me like nix
0

Configure Feed

Select the types of activity you want to include in your feed.

Replace Quickshell with Noctalia

author
Sean Aye
date (Jun 15, 2026, 10:14 PM -0400) commit 6d76d1d0 parent fe3abe41 change-id npmnnlqm
+65 -3497
+32
flake.lock
··· 726 726 "type": "github" 727 727 } 728 728 }, 729 + "nixpkgs_10": { 730 + "locked": { 731 + "lastModified": 1781074563, 732 + "narHash": "sha256-d34lhgOet4IqYMnCxbIvwFBMOyTV6PT4TyNEOP0/ZhU=", 733 + "rev": "9ae611a455b90cf061d8f332b977e387bda8e1ca", 734 + "type": "tarball", 735 + "url": "https://releases.nixos.org/nixos/unstable/nixos-26.11pre1014179.9ae611a455b9/nixexprs.tar.xz" 736 + }, 737 + "original": { 738 + "type": "tarball", 739 + "url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz" 740 + } 741 + }, 729 742 "nixpkgs_2": { 730 743 "locked": { 731 744 "lastModified": 1748162331, ··· 850 863 "type": "github" 851 864 } 852 865 }, 866 + "noctalia": { 867 + "inputs": { 868 + "nixpkgs": "nixpkgs_10" 869 + }, 870 + "locked": { 871 + "lastModified": 1781575000, 872 + "narHash": "sha256-ezt2BbBFs4YOEyFCaXxgmJejiEOBWs8kbkOH9TYz1JQ=", 873 + "owner": "noctalia-dev", 874 + "repo": "noctalia", 875 + "rev": "800bb1051a504d770cd71cf80bb529929d7a3572", 876 + "type": "github" 877 + }, 878 + "original": { 879 + "owner": "noctalia-dev", 880 + "repo": "noctalia", 881 + "type": "github" 882 + } 883 + }, 853 884 "opencodex": { 854 885 "inputs": { 855 886 "nixpkgs": [ ··· 888 919 "nixos-hardware": "nixos-hardware", 889 920 "nixos-raspberrypi": "nixos-raspberrypi", 890 921 "nixpkgs": "nixpkgs_9", 922 + "noctalia": "noctalia", 891 923 "opencodex": "opencodex", 892 924 "trmnl-rs": "trmnl-rs", 893 925 "zen-browser": "zen-browser"
+9 -2
flake.nix
··· 2 2 description = "My NixOS Flake Configuration"; 3 3 4 4 nixConfig = { 5 - extra-substituters = [ "https://niri.cachix.org" ]; 6 - extra-trusted-public-keys = [ "niri.cachix.org-1:Wv0OmO7PsuocRKzfDoJ3mulSl7Z6oezYhGhR+3W2964=" ]; 5 + extra-substituters = [ 6 + "https://niri.cachix.org" 7 + "https://noctalia.cachix.org" 8 + ]; 9 + extra-trusted-public-keys = [ 10 + "niri.cachix.org-1:Wv0OmO7PsuocRKzfDoJ3mulSl7Z6oezYhGhR+3W2964=" 11 + "noctalia.cachix.org-1:pCOR47nnMEo5thcxNDtzWpOxNFQsBRglJzxWPp3dkU4=" 12 + ]; 7 13 }; 8 14 9 15 inputs = { ··· 17 23 }; 18 24 catppuccin.url = "github:catppuccin/nix"; 19 25 niri.url = "github:sodiboo/niri-flake"; 26 + noctalia.url = "github:noctalia-dev/noctalia"; 20 27 nixarr.url = "github:rasmus-kirk/nixarr"; 21 28 zen-browser = { 22 29 url = "github:0xc000022070/zen-browser-flake";
-8
modules/_config/quickshell/components/Anim.qml
··· 1 - import QtQuick 2 - import "../config" as Config 3 - 4 - NumberAnimation { 5 - duration: Config.Appearance.anim.durations.normal 6 - easing.type: Easing.BezierSpline 7 - easing.bezierCurve: Config.Appearance.anim.curves.standard 8 - }
-8
modules/_config/quickshell/components/CAnim.qml
··· 1 - import QtQuick 2 - import "../config" as Config 3 - 4 - ColorAnimation { 5 - duration: Config.Appearance.anim.durations.normal 6 - easing.type: Easing.BezierSpline 7 - easing.bezierCurve: Config.Appearance.anim.curves.standard 8 - }
-33
modules/_config/quickshell/components/StateLayer.qml
··· 1 - import QtQuick 2 - import "../config" as Config 3 - 4 - Rectangle { 5 - id: stateLayer 6 - 7 - property alias hoverEnabled: mouseArea.hoverEnabled 8 - property alias containsMouse: mouseArea.containsMouse 9 - property alias acceptedButtons: mouseArea.acceptedButtons 10 - property alias cursorShape: mouseArea.cursorShape 11 - 12 - signal clicked(var mouse) 13 - signal entered() 14 - signal exited() 15 - 16 - color: Config.Colours.overlay0 17 - opacity: mouseArea.containsMouse ? (mouseArea.pressed ? 0.16 : 0.08) : 0.0 18 - radius: parent.radius 19 - 20 - Behavior on opacity { 21 - Anim { duration: Config.Appearance.anim.durations.small } 22 - } 23 - 24 - MouseArea { 25 - id: mouseArea 26 - anchors.fill: parent 27 - hoverEnabled: true 28 - cursorShape: Qt.PointingHandCursor 29 - onClicked: mouse => stateLayer.clicked(mouse) 30 - onEntered: stateLayer.entered() 31 - onExited: stateLayer.exited() 32 - } 33 - }
-7
modules/_config/quickshell/components/StyledRect.qml
··· 1 - import QtQuick 2 - import "../config" as Config 3 - 4 - Rectangle { 5 - color: Config.Colours.surface0 6 - radius: Config.Appearance.rounding.normal 7 - }
-8
modules/_config/quickshell/components/StyledText.qml
··· 1 - import QtQuick 2 - import "../config" as Config 3 - 4 - Text { 5 - color: Config.Colours.text 6 - font.family: Config.Appearance.font.family 7 - font.pixelSize: Config.Appearance.font.size.normal 8 - }
-5
modules/_config/quickshell/components/qmldir
··· 1 - Anim 1.0 Anim.qml 2 - CAnim 1.0 CAnim.qml 3 - StyledRect 1.0 StyledRect.qml 4 - StyledText 1.0 StyledText.qml 5 - StateLayer 1.0 StateLayer.qml
-66
modules/_config/quickshell/config/Appearance.qml
··· 1 - pragma Singleton 2 - 3 - import QtQuick 4 - 5 - QtObject { 6 - readonly property QtObject rounding: QtObject { 7 - readonly property int small: 4 8 - readonly property int normal: 8 9 - readonly property int large: 12 10 - readonly property int full: 999 11 - } 12 - 13 - readonly property QtObject spacing: QtObject { 14 - readonly property int small: 4 15 - readonly property int normal: 8 16 - readonly property int large: 12 17 - } 18 - 19 - readonly property QtObject padding: QtObject { 20 - readonly property int small: 4 21 - readonly property int normal: 8 22 - readonly property int large: 12 23 - } 24 - 25 - readonly property QtObject font: QtObject { 26 - readonly property string family: "BerkeleyMono Nerd Font" 27 - readonly property QtObject size: QtObject { 28 - readonly property int small: 11 29 - readonly property int normal: 14 30 - readonly property int large: 18 31 - } 32 - } 33 - 34 - readonly property QtObject anim: QtObject { 35 - // Legacy aliases (match old short_/normal/long_ naming) 36 - readonly property int short_: durations.small 37 - readonly property int normal: durations.normal 38 - readonly property int long_: durations.large 39 - 40 - // Legacy easing type (for any remaining references) 41 - readonly property int type: Easing.BezierSpline 42 - 43 - readonly property QtObject durations: QtObject { 44 - readonly property int small: 200 45 - readonly property int normal: 400 46 - readonly property int large: 600 47 - readonly property int extraLarge: 1000 48 - readonly property int expressiveFastSpatial: 350 49 - readonly property int expressiveDefaultSpatial: 500 50 - } 51 - 52 - readonly property QtObject curves: QtObject { 53 - readonly property var emphasized: [0.05, 0, 2/15, 0.06, 1/6, 0.4, 5/24, 0.82, 0.25, 1, 1, 1] 54 - readonly property var emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1] 55 - readonly property var emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1] 56 - readonly property var standard: [0.2, 0, 0, 1, 1, 1] 57 - readonly property var standardAccel: [0.3, 0, 1, 1, 1, 1] 58 - readonly property var standardDecel: [0, 0, 0, 1, 1, 1] 59 - readonly property var expressiveFastSpatial: [0.42, 1.67, 0.21, 0.9, 1, 1] 60 - readonly property var expressiveDefaultSpatial: [0.38, 1.21, 0.22, 1, 1, 1] 61 - readonly property var expressiveEffects: [0.34, 0.8, 0.34, 1, 1, 1] 62 - } 63 - } 64 - 65 - readonly property real barOpacity: 0.85 66 - }
-34
modules/_config/quickshell/config/Colours.qml
··· 1 - pragma Singleton 2 - 3 - import QtQuick 4 - 5 - QtObject { 6 - // Catppuccin Frappe palette 7 - readonly property color rosewater: "#f2d5cf" 8 - readonly property color flamingo: "#eebebe" 9 - readonly property color pink: "#f4b8e4" 10 - readonly property color mauve: "#ca9ee6" 11 - readonly property color red: "#e78284" 12 - readonly property color maroon: "#ea999c" 13 - readonly property color peach: "#ef9f76" 14 - readonly property color yellow: "#e5c890" 15 - readonly property color green: "#a6d189" 16 - readonly property color teal: "#81c8be" 17 - readonly property color sky: "#99d1db" 18 - readonly property color sapphire: "#85c1dc" 19 - readonly property color blue: "#8caaee" 20 - readonly property color lavender: "#babbf1" 21 - 22 - readonly property color text: "#c6d0f5" 23 - readonly property color subtext1: "#b5bfe2" 24 - readonly property color subtext0: "#a5adce" 25 - readonly property color overlay2: "#949cbb" 26 - readonly property color overlay1: "#838ba7" 27 - readonly property color overlay0: "#737994" 28 - readonly property color surface2: "#626880" 29 - readonly property color surface1: "#51576d" 30 - readonly property color surface0: "#414559" 31 - readonly property color base: "#303446" 32 - readonly property color mantle: "#292c3c" 33 - readonly property color crust: "#232634" 34 - }
-2
modules/_config/quickshell/config/qmldir
··· 1 - singleton Colours 1.0 Colours.qml 2 - singleton Appearance 1.0 Appearance.qml
-238
modules/_config/quickshell/modules/dashboard/Dashboard.qml
··· 1 - import QtQuick 2 - import QtQuick.Layouts 3 - import Quickshell 4 - import "../../config" as Config 5 - import "../../components" 6 - import "../../services" as Services 7 - import "tabs" 8 - 9 - PanelWindow { 10 - id: topTrigger 11 - 12 - required property var modelData 13 - screen: modelData 14 - 15 - anchors { 16 - top: true 17 - left: true 18 - right: true 19 - } 20 - margins.left: 60 21 - margins.right: 8 22 - margins.top: 0 23 - implicitHeight: 2 24 - exclusiveZone: 0 25 - color: "transparent" 26 - 27 - property bool dashboardOpen: false 28 - property int activeTab: 0 29 - 30 - MouseArea { 31 - anchors.fill: parent 32 - hoverEnabled: true 33 - onEntered: { closeTimer.stop(); topTrigger.dashboardOpen = true } 34 - onExited: closeTimer.restart() 35 - } 36 - 37 - onDashboardOpenChanged: { 38 - if (dashboardOpen) { 39 - contentRect._opening = true 40 - fadeInTimer.start() 41 - } else { 42 - fadeInTimer.stop() 43 - contentRect._opening = false 44 - contentRect.opacity = 0 45 - activeTab = 0 46 - } 47 - } 48 - 49 - Timer { 50 - id: closeTimer 51 - interval: 400 52 - onTriggered: topTrigger.dashboardOpen = false 53 - } 54 - 55 - PopupWindow { 56 - id: dashPanel 57 - anchor.window: topTrigger 58 - anchor.onAnchoring: { 59 - anchor.rect.x = (topTrigger.width - implicitWidth) / 2 60 - anchor.rect.y = topTrigger.height + 4 61 - } 62 - 63 - visible: topTrigger.dashboardOpen || contentRect.opacity > 0 64 - implicitWidth: Math.min(topTrigger.width, 900) 65 - implicitHeight: contentCol.implicitHeight + 32 66 - color: "transparent" 67 - 68 - Timer { 69 - id: fadeInTimer 70 - interval: 16 71 - onTriggered: contentRect.opacity = 1 72 - } 73 - 74 - Rectangle { 75 - id: contentRect 76 - anchors.fill: parent 77 - color: Config.Colours.base 78 - radius: Config.Appearance.rounding.large 79 - border.width: 1 80 - border.color: Config.Colours.surface0 81 - opacity: 0 82 - 83 - property bool _opening: false 84 - Behavior on opacity { 85 - Anim { 86 - duration: contentRect._opening ? Config.Appearance.anim.durations.normal : Config.Appearance.anim.durations.small 87 - easing.bezierCurve: contentRect._opening ? Config.Appearance.anim.curves.standardDecel : Config.Appearance.anim.curves.standardAccel 88 - } 89 - } 90 - 91 - HoverHandler { 92 - onHoveredChanged: { 93 - if (hovered) closeTimer.stop() 94 - else closeTimer.restart() 95 - } 96 - } 97 - 98 - ColumnLayout { 99 - id: contentCol 100 - anchors.fill: parent 101 - anchors.margins: 16 102 - spacing: 0 103 - 104 - // Tab bar 105 - RowLayout { 106 - id: tabBar 107 - Layout.fillWidth: true 108 - spacing: 0 109 - 110 - Repeater { 111 - model: [ 112 - { icon: "\uf0e4", label: "Dashboard" }, 113 - { icon: "\uf001", label: "Media" }, 114 - { icon: "\uf2db", label: "Performance" }, 115 - { icon: "\uf0c2", label: "Weather" } 116 - ] 117 - delegate: Rectangle { 118 - required property var modelData 119 - required property int index 120 - Layout.fillWidth: true 121 - height: 36 122 - radius: Config.Appearance.rounding.normal 123 - color: topTrigger.activeTab === index 124 - ? Config.Colours.surface0 125 - : (tabMouse.containsMouse ? Config.Colours.surface0 : "transparent") 126 - opacity: topTrigger.activeTab === index ? 1.0 : (tabMouse.containsMouse ? 0.7 : 0.5) 127 - Behavior on color { CAnim {} } 128 - Behavior on opacity { Anim { duration: Config.Appearance.anim.durations.small } } 129 - 130 - RowLayout { 131 - anchors.centerIn: parent 132 - spacing: 6 133 - Text { 134 - color: topTrigger.activeTab === index ? Config.Colours.blue : Config.Colours.subtext0 135 - font.family: Config.Appearance.font.family 136 - font.pixelSize: 14 137 - text: modelData.icon 138 - } 139 - Text { 140 - color: topTrigger.activeTab === index ? Config.Colours.text : Config.Colours.subtext0 141 - font.family: Config.Appearance.font.family 142 - font.pixelSize: Config.Appearance.font.size.small 143 - text: modelData.label 144 - } 145 - } 146 - 147 - MouseArea { 148 - id: tabMouse 149 - anchors.fill: parent 150 - hoverEnabled: true 151 - cursorShape: Qt.PointingHandCursor 152 - onClicked: topTrigger.activeTab = index 153 - } 154 - } 155 - } 156 - } 157 - 158 - // Tab indicator 159 - Rectangle { 160 - Layout.fillWidth: true 161 - height: 2 162 - color: "transparent" 163 - Layout.topMargin: 4 164 - Layout.bottomMargin: 8 165 - 166 - Rectangle { 167 - height: 2 168 - radius: 1 169 - color: Config.Colours.blue 170 - width: parent.width / 4 - 16 171 - x: (parent.width / 4) * topTrigger.activeTab + 8 172 - Behavior on x { 173 - Anim { 174 - duration: Config.Appearance.anim.durations.normal 175 - easing.bezierCurve: Config.Appearance.anim.curves.emphasized 176 - } 177 - } 178 - } 179 - } 180 - 181 - // Tab content 182 - Item { 183 - id: tabContent 184 - Layout.fillWidth: true 185 - clip: true 186 - implicitHeight: { 187 - if (topTrigger.activeTab === 0) return dashTab.implicitHeight 188 - if (topTrigger.activeTab === 1) return mediaTab.implicitHeight 189 - if (topTrigger.activeTab === 2) return perfTab.implicitHeight 190 - return weatherTab.implicitHeight 191 - } 192 - 193 - DashTab { 194 - id: dashTab 195 - width: parent.width 196 - visible: topTrigger.activeTab === 0 197 - opacity: visible ? 1 : 0 198 - isOpen: topTrigger.dashboardOpen 199 - Behavior on opacity { Anim { duration: Config.Appearance.anim.durations.small } } 200 - } 201 - 202 - MediaTab { 203 - id: mediaTab 204 - width: parent.width 205 - visible: topTrigger.activeTab === 1 206 - opacity: visible ? 1 : 0 207 - Behavior on opacity { Anim { duration: Config.Appearance.anim.durations.small } } 208 - } 209 - 210 - PerformanceTab { 211 - id: perfTab 212 - width: parent.width 213 - visible: topTrigger.activeTab === 2 214 - opacity: visible ? 1 : 0 215 - Behavior on opacity { Anim { duration: Config.Appearance.anim.durations.small } } 216 - } 217 - 218 - WeatherTab { 219 - id: weatherTab 220 - width: parent.width 221 - visible: topTrigger.activeTab === 3 222 - opacity: visible ? 1 : 0 223 - Behavior on opacity { Anim { duration: Config.Appearance.anim.durations.small } } 224 - } 225 - } 226 - } 227 - 228 - Keys.onEscapePressed: topTrigger.dashboardOpen = false 229 - } 230 - 231 - // Click outside to close 232 - MouseArea { 233 - anchors.fill: parent 234 - z: -1 235 - onClicked: { closeTimer.stop(); topTrigger.dashboardOpen = false } 236 - } 237 - } 238 - }
-1
modules/_config/quickshell/modules/dashboard/qmldir
··· 1 - Dashboard 1.0 Dashboard.qml
-364
modules/_config/quickshell/modules/dashboard/tabs/DashTab.qml
··· 1 - import QtQuick 2 - import QtQuick.Layouts 3 - import "../../../config" as Config 4 - import "../../../components" 5 - import "../../../services" as Services 6 - 7 - Item { 8 - id: root 9 - 10 - property bool isOpen: false 11 - implicitHeight: mainLayout.implicitHeight 12 - 13 - property int calYear: new Date().getFullYear() 14 - property int calMonth: new Date().getMonth() 15 - property var calModel: calendarDays() 16 - 17 - onIsOpenChanged: if (isOpen) resetMonth() 18 - 19 - function calendarDays() { 20 - var firstDay = new Date(calYear, calMonth, 1).getDay() 21 - var daysInMonth = new Date(calYear, calMonth + 1, 0).getDate() 22 - var prevDays = new Date(calYear, calMonth, 0).getDate() 23 - var today = new Date() 24 - var isCurrentMonth = (calYear === today.getFullYear() && calMonth === today.getMonth()) 25 - var days = [] 26 - for (var i = 0; i < 42; i++) { 27 - var dayNum = i - firstDay + 1 28 - if (dayNum < 1) 29 - days.push({ day: prevDays + dayNum, inMonth: false, isToday: false }) 30 - else if (dayNum > daysInMonth) 31 - days.push({ day: dayNum - daysInMonth, inMonth: false, isToday: false }) 32 - else 33 - days.push({ day: dayNum, inMonth: true, isToday: isCurrentMonth && dayNum === today.getDate() }) 34 - } 35 - return days 36 - } 37 - 38 - function prevMonth() { 39 - if (calMonth === 0) { calMonth = 11; calYear-- } 40 - else calMonth-- 41 - calModel = calendarDays() 42 - } 43 - 44 - function nextMonth() { 45 - if (calMonth === 11) { calMonth = 0; calYear++ } 46 - else calMonth++ 47 - calModel = calendarDays() 48 - } 49 - 50 - function resetMonth() { 51 - var now = new Date() 52 - calYear = now.getFullYear() 53 - calMonth = now.getMonth() 54 - calModel = calendarDays() 55 - } 56 - 57 - // Resource bar component 58 - component ResourceBar: ColumnLayout { 59 - property string icon 60 - property string label 61 - property string valueText 62 - property real pct: 0 63 - property color barColor 64 - 65 - Layout.fillWidth: true 66 - spacing: 2 67 - 68 - // Animated value that smoothly transitions between updates 69 - property real animatedPct: pct 70 - Behavior on animatedPct { Anim { duration: Config.Appearance.anim.durations.large } } 71 - 72 - RowLayout { 73 - Layout.fillWidth: true 74 - Text { color: barColor; font.family: Config.Appearance.font.family; font.pixelSize: 12; text: icon } 75 - Text { color: Config.Colours.subtext0; font.family: Config.Appearance.font.family; font.pixelSize: 12; text: label } 76 - Item { Layout.fillWidth: true } 77 - Text { color: Config.Colours.text; font.family: Config.Appearance.font.family; font.pixelSize: 12; text: valueText } 78 - } 79 - 80 - Item { 81 - Layout.fillWidth: true 82 - height: 4 83 - Rectangle { anchors.fill: parent; radius: 2; color: Config.Colours.surface1 } 84 - Rectangle { 85 - width: parent.width * (animatedPct / 100) 86 - height: parent.height; radius: 2 87 - color: barColor 88 - } 89 - } 90 - } 91 - 92 - RowLayout { 93 - id: mainLayout 94 - width: parent.width 95 - spacing: 16 96 - 97 - // Left: DateTime 98 - ColumnLayout { 99 - Layout.preferredWidth: 80 100 - Layout.fillHeight: true 101 - Layout.alignment: Qt.AlignVCenter 102 - spacing: 0 103 - 104 - Text { 105 - id: dashHour 106 - Layout.alignment: Qt.AlignHCenter 107 - color: Config.Colours.blue 108 - font.family: Config.Appearance.font.family 109 - font.pixelSize: 48 110 - font.bold: true 111 - text: Qt.formatDateTime(new Date(), "HH") 112 - } 113 - 114 - Text { 115 - Layout.alignment: Qt.AlignHCenter 116 - color: Config.Colours.blue 117 - font.family: Config.Appearance.font.family 118 - font.pixelSize: 14 119 - text: "\u2022\u2022\u2022" 120 - } 121 - 122 - Text { 123 - id: dashMin 124 - Layout.alignment: Qt.AlignHCenter 125 - color: Config.Colours.blue 126 - font.family: Config.Appearance.font.family 127 - font.pixelSize: 48 128 - font.bold: true 129 - text: Qt.formatDateTime(new Date(), "mm") 130 - } 131 - 132 - Text { 133 - Layout.alignment: Qt.AlignHCenter 134 - Layout.topMargin: 4 135 - color: Config.Colours.subtext0 136 - font.family: Config.Appearance.font.family 137 - font.pixelSize: Config.Appearance.font.size.small 138 - text: Qt.formatDateTime(new Date(), "dddd") 139 - } 140 - 141 - Text { 142 - Layout.alignment: Qt.AlignHCenter 143 - color: Config.Colours.overlay0 144 - font.family: Config.Appearance.font.family 145 - font.pixelSize: Config.Appearance.font.size.small 146 - text: Qt.formatDateTime(new Date(), "MMMM d, yyyy") 147 - } 148 - 149 - Timer { 150 - interval: 1000 151 - running: root.isOpen 152 - repeat: true 153 - onTriggered: { 154 - var now = new Date() 155 - dashHour.text = Qt.formatDateTime(now, "HH") 156 - dashMin.text = Qt.formatDateTime(now, "mm") 157 - } 158 - } 159 - } 160 - 161 - Rectangle { width: 1; Layout.fillHeight: true; color: Config.Colours.surface1 } 162 - 163 - // Center: Calendar 164 - ColumnLayout { 165 - Layout.fillWidth: true 166 - Layout.fillHeight: true 167 - spacing: 4 168 - 169 - RowLayout { 170 - Layout.fillWidth: true 171 - 172 - Text { 173 - color: Config.Colours.overlay0 174 - font.family: Config.Appearance.font.family 175 - font.pixelSize: 16 176 - text: "\uf053" 177 - MouseArea { anchors.fill: parent; cursorShape: Qt.PointingHandCursor; onClicked: root.prevMonth() } 178 - } 179 - Item { Layout.fillWidth: true } 180 - Text { 181 - color: Config.Colours.text 182 - font.family: Config.Appearance.font.family 183 - font.pixelSize: Config.Appearance.font.size.normal 184 - font.bold: true 185 - text: new Date(root.calYear, root.calMonth, 1).toLocaleDateString(Qt.locale(), "MMMM yyyy") 186 - MouseArea { anchors.fill: parent; cursorShape: Qt.PointingHandCursor; onClicked: root.resetMonth() } 187 - } 188 - Item { Layout.fillWidth: true } 189 - Text { 190 - color: Config.Colours.overlay0 191 - font.family: Config.Appearance.font.family 192 - font.pixelSize: 16 193 - text: "\uf054" 194 - MouseArea { anchors.fill: parent; cursorShape: Qt.PointingHandCursor; onClicked: root.nextMonth() } 195 - } 196 - } 197 - 198 - Row { 199 - Layout.fillWidth: true 200 - Repeater { 201 - model: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"] 202 - delegate: Text { 203 - required property string modelData 204 - width: parent.width / 7 205 - horizontalAlignment: Text.AlignHCenter 206 - color: Config.Colours.overlay0 207 - font.family: Config.Appearance.font.family 208 - font.pixelSize: 11 209 - text: modelData 210 - } 211 - } 212 - } 213 - 214 - Grid { 215 - Layout.fillWidth: true 216 - columns: 7 217 - rowSpacing: 2 218 - 219 - Repeater { 220 - model: root.calModel 221 - delegate: Item { 222 - required property var modelData 223 - width: parent.width / 7 224 - height: 28 225 - 226 - Rectangle { 227 - anchors.centerIn: parent 228 - width: 24; height: 24; radius: 12 229 - color: modelData.isToday ? Config.Colours.blue : "transparent" 230 - } 231 - 232 - Text { 233 - anchors.centerIn: parent 234 - color: modelData.isToday ? Config.Colours.crust 235 - : (modelData.inMonth ? Config.Colours.text : Config.Colours.overlay0) 236 - font.family: Config.Appearance.font.family 237 - font.pixelSize: 12 238 - font.bold: modelData.isToday 239 - text: modelData.day 240 - } 241 - } 242 - } 243 - } 244 - } 245 - 246 - Rectangle { width: 1; Layout.fillHeight: true; color: Config.Colours.surface1 } 247 - 248 - // Right: Weather + Resources + Power 249 - ColumnLayout { 250 - Layout.preferredWidth: 200 251 - Layout.fillHeight: true 252 - spacing: 10 253 - 254 - // Weather 255 - RowLayout { 256 - Layout.fillWidth: true 257 - spacing: 8 258 - visible: Services.Weather.weather !== "" 259 - 260 - Text { 261 - color: Config.Colours.yellow 262 - font.family: Config.Appearance.font.family 263 - font.pixelSize: 20 264 - text: "\uf0eb" 265 - } 266 - Text { 267 - Layout.fillWidth: true 268 - color: Config.Colours.text 269 - font.family: Config.Appearance.font.family 270 - font.pixelSize: Config.Appearance.font.size.normal 271 - text: Services.Weather.weather 272 - wrapMode: Text.WordWrap 273 - } 274 - } 275 - 276 - Rectangle { 277 - Layout.fillWidth: true; height: 1; color: Config.Colours.surface1 278 - visible: Services.Weather.weather !== "" 279 - } 280 - 281 - // Resources 282 - Text { 283 - color: Config.Colours.text 284 - font.family: Config.Appearance.font.family 285 - font.pixelSize: Config.Appearance.font.size.normal 286 - font.bold: true 287 - text: "Resources" 288 - } 289 - 290 - ResourceBar { 291 - icon: "\uf2db"; label: "CPU" 292 - valueText: Services.SystemUsage.cpuUsage + "%" 293 - pct: Services.SystemUsage.cpuUsage 294 - barColor: Config.Colours.peach 295 - } 296 - 297 - ResourceBar { 298 - icon: "\uefc5"; label: "Memory" 299 - valueText: Services.SystemUsage.memUsage + "%" 300 - pct: Services.SystemUsage.memUsage 301 - barColor: Config.Colours.mauve 302 - } 303 - 304 - ResourceBar { 305 - icon: "\uf0a0"; label: "Storage" 306 - valueText: Services.Storage.usagePercent + "%" 307 - pct: Services.Storage.usagePercent 308 - barColor: Config.Colours.sapphire 309 - } 310 - 311 - ResourceBar { 312 - icon: "\uf2c9"; label: "Temp" 313 - valueText: Services.SystemUsage.temperature + "\u00b0C" 314 - pct: Math.min(Services.SystemUsage.temperature, 100) 315 - barColor: Config.Colours.red 316 - } 317 - 318 - // Power profile 319 - Rectangle { 320 - Layout.fillWidth: true; height: 1; color: Config.Colours.surface1 321 - } 322 - 323 - RowLayout { 324 - Layout.fillWidth: true 325 - spacing: 8 326 - 327 - Text { 328 - color: { 329 - if (Services.PowerProfile.profile === "performance") return Config.Colours.peach 330 - if (Services.PowerProfile.profile === "power-saver") return Config.Colours.green 331 - return Config.Colours.blue 332 - } 333 - font.family: Config.Appearance.font.family 334 - font.pixelSize: 14 335 - text: { 336 - if (Services.PowerProfile.profile === "performance") return "\uf0e7" 337 - if (Services.PowerProfile.profile === "power-saver") return "\uf06c" 338 - return "\uf24e" 339 - } 340 - } 341 - Text { 342 - color: Config.Colours.subtext0 343 - font.family: Config.Appearance.font.family 344 - font.pixelSize: 12 345 - text: { 346 - if (Services.PowerProfile.profile === "performance") return "Performance" 347 - if (Services.PowerProfile.profile === "power-saver") return "Power Saver" 348 - return "Balanced" 349 - } 350 - } 351 - Item { Layout.fillWidth: true } 352 - Text { 353 - color: Config.Colours.text 354 - font.family: Config.Appearance.font.family 355 - font.pixelSize: 12 356 - text: Services.Battery.percent + "%" 357 - visible: Services.Battery.hasBattery 358 - } 359 - } 360 - 361 - Item { Layout.fillHeight: true } 362 - } 363 - } 364 - }
-242
modules/_config/quickshell/modules/dashboard/tabs/MediaTab.qml
··· 1 - import QtQuick 2 - import QtQuick.Layouts 3 - import "../../../config" as Config 4 - import "../../../components" 5 - import "../../../services" as Services 6 - 7 - Item { 8 - id: root 9 - 10 - implicitHeight: mediaLayout.implicitHeight 11 - 12 - function formatTime(seconds) { 13 - if (!seconds || seconds <= 0) return "0:00" 14 - var m = Math.floor(seconds / 60) 15 - var s = Math.floor(seconds % 60) 16 - return m + ":" + (s < 10 ? "0" : "") + s 17 - } 18 - 19 - ColumnLayout { 20 - id: mediaLayout 21 - width: parent.width 22 - spacing: 16 23 - 24 - // No player state 25 - ColumnLayout { 26 - Layout.fillWidth: true 27 - Layout.alignment: Qt.AlignHCenter 28 - Layout.topMargin: 32 29 - Layout.bottomMargin: 32 30 - visible: !Services.Mpris.hasPlayer 31 - spacing: 8 32 - 33 - Text { 34 - Layout.alignment: Qt.AlignHCenter 35 - color: Config.Colours.overlay0 36 - font.family: Config.Appearance.font.family 37 - font.pixelSize: 32 38 - text: "\uf001" 39 - } 40 - Text { 41 - Layout.alignment: Qt.AlignHCenter 42 - color: Config.Colours.subtext0 43 - font.family: Config.Appearance.font.family 44 - font.pixelSize: Config.Appearance.font.size.normal 45 - text: "No media playing" 46 - } 47 - Text { 48 - Layout.alignment: Qt.AlignHCenter 49 - color: Config.Colours.overlay0 50 - font.family: Config.Appearance.font.family 51 - font.pixelSize: Config.Appearance.font.size.small 52 - text: "Play something to see controls here" 53 - } 54 - } 55 - 56 - // Player content 57 - RowLayout { 58 - Layout.fillWidth: true 59 - visible: Services.Mpris.hasPlayer 60 - spacing: 16 61 - 62 - // Album art 63 - Rectangle { 64 - width: 140; height: 140 65 - radius: Config.Appearance.rounding.large 66 - color: Config.Colours.surface0 67 - clip: true 68 - 69 - Image { 70 - anchors.fill: parent 71 - source: Services.Mpris.artUrl 72 - fillMode: Image.PreserveAspectCrop 73 - visible: status === Image.Ready 74 - sourceSize.width: 140 75 - sourceSize.height: 140 76 - } 77 - 78 - Text { 79 - anchors.centerIn: parent 80 - visible: Services.Mpris.artUrl === "" || albumArt.status !== Image.Ready 81 - color: Config.Colours.overlay0 82 - font.family: Config.Appearance.font.family 83 - font.pixelSize: 40 84 - text: "\uf001" 85 - 86 - property var albumArt: parent.children[0] 87 - } 88 - } 89 - 90 - // Track info + controls 91 - ColumnLayout { 92 - Layout.fillWidth: true 93 - Layout.fillHeight: true 94 - spacing: 8 95 - 96 - // Title 97 - Text { 98 - Layout.fillWidth: true 99 - color: Config.Colours.text 100 - font.family: Config.Appearance.font.family 101 - font.pixelSize: Config.Appearance.font.size.large 102 - font.bold: true 103 - text: Services.Mpris.title || "Unknown" 104 - elide: Text.ElideRight 105 - maximumLineCount: 1 106 - } 107 - 108 - // Artist - Album 109 - Text { 110 - Layout.fillWidth: true 111 - color: Config.Colours.subtext0 112 - font.family: Config.Appearance.font.family 113 - font.pixelSize: Config.Appearance.font.size.normal 114 - text: { 115 - var parts = [] 116 - if (Services.Mpris.artist) parts.push(Services.Mpris.artist) 117 - if (Services.Mpris.album) parts.push(Services.Mpris.album) 118 - return parts.join(" \u2022 ") || "Unknown" 119 - } 120 - elide: Text.ElideRight 121 - maximumLineCount: 1 122 - } 123 - 124 - Item { Layout.fillHeight: true } 125 - 126 - // Progress bar 127 - Item { 128 - Layout.fillWidth: true 129 - height: 4 130 - 131 - property real animatedFraction: Services.Mpris.length > 0 ? root._trackedPos / Services.Mpris.length : 0 132 - Behavior on animatedFraction { 133 - Anim { 134 - duration: Config.Appearance.anim.durations.large 135 - easing.bezierCurve: Config.Appearance.anim.curves.standardDecel 136 - } 137 - } 138 - 139 - Rectangle { 140 - anchors.fill: parent 141 - radius: 2 142 - color: Config.Colours.surface1 143 - } 144 - Rectangle { 145 - width: parent.width * parent.animatedFraction 146 - height: parent.height 147 - radius: 2 148 - color: Config.Colours.blue 149 - } 150 - } 151 - 152 - // Time labels 153 - RowLayout { 154 - Layout.fillWidth: true 155 - Text { 156 - color: Config.Colours.overlay0 157 - font.family: Config.Appearance.font.family 158 - font.pixelSize: 10 159 - text: formatTime(root._trackedPos) 160 - } 161 - Item { Layout.fillWidth: true } 162 - Text { 163 - color: Config.Colours.overlay0 164 - font.family: Config.Appearance.font.family 165 - font.pixelSize: 10 166 - text: formatTime(Services.Mpris.length) 167 - } 168 - } 169 - 170 - // Playback controls 171 - RowLayout { 172 - Layout.alignment: Qt.AlignHCenter 173 - spacing: 24 174 - 175 - // Previous 176 - Text { 177 - color: Services.Mpris.canPrev ? Config.Colours.text : Config.Colours.overlay0 178 - font.family: Config.Appearance.font.family 179 - font.pixelSize: 20 180 - text: "\uf04a" 181 - MouseArea { 182 - anchors.fill: parent; anchors.margins: -8 183 - cursorShape: Qt.PointingHandCursor 184 - enabled: Services.Mpris.canPrev 185 - onClicked: Services.Mpris.previous() 186 - } 187 - } 188 - 189 - // Play/Pause 190 - Rectangle { 191 - width: 44; height: 44 192 - radius: 22 193 - color: Config.Colours.blue 194 - Text { 195 - anchors.centerIn: parent 196 - color: Config.Colours.crust 197 - font.family: Config.Appearance.font.family 198 - font.pixelSize: 20 199 - text: Services.Mpris.isPlaying ? "\uf04c" : "\uf04b" 200 - } 201 - MouseArea { 202 - anchors.fill: parent; cursorShape: Qt.PointingHandCursor 203 - onClicked: Services.Mpris.togglePlaying() 204 - } 205 - } 206 - 207 - // Next 208 - Text { 209 - color: Services.Mpris.canNext ? Config.Colours.text : Config.Colours.overlay0 210 - font.family: Config.Appearance.font.family 211 - font.pixelSize: 20 212 - text: "\uf04e" 213 - MouseArea { 214 - anchors.fill: parent; anchors.margins: -8 215 - cursorShape: Qt.PointingHandCursor 216 - enabled: Services.Mpris.canNext 217 - onClicked: Services.Mpris.next() 218 - } 219 - } 220 - } 221 - } 222 - } 223 - } 224 - 225 - // Position tracking for smooth progress 226 - property real _trackedPos: Services.Mpris.position 227 - 228 - Timer { 229 - interval: 1000 230 - running: Services.Mpris.isPlaying && root.visible 231 - repeat: true 232 - onTriggered: root._trackedPos += 1.0 233 - } 234 - 235 - Connections { 236 - target: Services.Mpris 237 - function onPositionChanged() { 238 - if (Math.abs(Services.Mpris.position - root._trackedPos) > 2) 239 - root._trackedPos = Services.Mpris.position 240 - } 241 - } 242 - }
-190
modules/_config/quickshell/modules/dashboard/tabs/PerformanceTab.qml
··· 1 - import QtQuick 2 - import QtQuick.Layouts 3 - import "../../../config" as Config 4 - import "../../../components" 5 - import "../../../services" as Services 6 - 7 - Item { 8 - id: root 9 - 10 - implicitHeight: perfLayout.implicitHeight 11 - 12 - ColumnLayout { 13 - id: perfLayout 14 - width: parent.width 15 - spacing: 12 16 - 17 - // CPU 18 - Rectangle { 19 - Layout.fillWidth: true 20 - implicitHeight: cpuCol.implicitHeight + 24 21 - radius: Config.Appearance.rounding.normal 22 - color: Config.Colours.surface0 23 - 24 - ColumnLayout { 25 - id: cpuCol 26 - anchors.fill: parent 27 - anchors.margins: 12 28 - spacing: 8 29 - 30 - RowLayout { 31 - Layout.fillWidth: true 32 - Text { color: Config.Colours.peach; font.family: Config.Appearance.font.family; font.pixelSize: 18; text: "\uf2db" } 33 - Text { color: Config.Colours.text; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.normal; font.bold: true; text: "CPU" } 34 - Item { Layout.fillWidth: true } 35 - Text { color: Config.Colours.peach; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.large; font.bold: true; text: Services.SystemUsage.cpuUsage + "%" } 36 - } 37 - 38 - Item { 39 - Layout.fillWidth: true; height: 8 40 - property real animatedPct: Services.SystemUsage.cpuUsage 41 - Behavior on animatedPct { Anim { duration: Config.Appearance.anim.durations.large } } 42 - Rectangle { anchors.fill: parent; radius: 4; color: Config.Colours.surface1 } 43 - Rectangle { 44 - width: parent.width * parent.animatedPct / 100 45 - height: parent.height; radius: 4; color: Config.Colours.peach 46 - } 47 - } 48 - } 49 - } 50 - 51 - // Memory 52 - Rectangle { 53 - Layout.fillWidth: true 54 - implicitHeight: memCol.implicitHeight + 24 55 - radius: Config.Appearance.rounding.normal 56 - color: Config.Colours.surface0 57 - 58 - ColumnLayout { 59 - id: memCol 60 - anchors.fill: parent 61 - anchors.margins: 12 62 - spacing: 8 63 - 64 - RowLayout { 65 - Layout.fillWidth: true 66 - Text { color: Config.Colours.mauve; font.family: Config.Appearance.font.family; font.pixelSize: 18; text: "\uefc5" } 67 - Text { color: Config.Colours.text; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.normal; font.bold: true; text: "Memory" } 68 - Item { Layout.fillWidth: true } 69 - Text { color: Config.Colours.mauve; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.large; font.bold: true; text: Services.SystemUsage.memUsage + "%" } 70 - } 71 - 72 - Item { 73 - Layout.fillWidth: true; height: 8 74 - property real animatedPct: Services.SystemUsage.memUsage 75 - Behavior on animatedPct { Anim { duration: Config.Appearance.anim.durations.large } } 76 - Rectangle { anchors.fill: parent; radius: 4; color: Config.Colours.surface1 } 77 - Rectangle { 78 - width: parent.width * parent.animatedPct / 100 79 - height: parent.height; radius: 4; color: Config.Colours.mauve 80 - } 81 - } 82 - } 83 - } 84 - 85 - // Temperature 86 - Rectangle { 87 - Layout.fillWidth: true 88 - implicitHeight: tempCol.implicitHeight + 24 89 - radius: Config.Appearance.rounding.normal 90 - color: Config.Colours.surface0 91 - 92 - ColumnLayout { 93 - id: tempCol 94 - anchors.fill: parent 95 - anchors.margins: 12 96 - spacing: 8 97 - 98 - RowLayout { 99 - Layout.fillWidth: true 100 - Text { color: Config.Colours.red; font.family: Config.Appearance.font.family; font.pixelSize: 18; text: "\uf2c9" } 101 - Text { color: Config.Colours.text; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.normal; font.bold: true; text: "Temperature" } 102 - Item { Layout.fillWidth: true } 103 - Text { color: Config.Colours.red; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.large; font.bold: true; text: Services.SystemUsage.temperature + "\u00b0C" } 104 - } 105 - 106 - Item { 107 - Layout.fillWidth: true; height: 8 108 - property real animatedPct: Math.min(Services.SystemUsage.temperature, 100) 109 - Behavior on animatedPct { Anim { duration: Config.Appearance.anim.durations.large } } 110 - Rectangle { anchors.fill: parent; radius: 4; color: Config.Colours.surface1 } 111 - Rectangle { 112 - width: parent.width * parent.animatedPct / 100 113 - height: parent.height; radius: 4; color: Config.Colours.red 114 - } 115 - } 116 - } 117 - } 118 - 119 - // Storage 120 - Rectangle { 121 - Layout.fillWidth: true 122 - implicitHeight: storageCol.implicitHeight + 24 123 - radius: Config.Appearance.rounding.normal 124 - color: Config.Colours.surface0 125 - 126 - ColumnLayout { 127 - id: storageCol 128 - anchors.fill: parent 129 - anchors.margins: 12 130 - spacing: 8 131 - 132 - RowLayout { 133 - Layout.fillWidth: true 134 - Text { color: Config.Colours.sapphire; font.family: Config.Appearance.font.family; font.pixelSize: 18; text: "\uf0a0" } 135 - Text { color: Config.Colours.text; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.normal; font.bold: true; text: "Storage" } 136 - Item { Layout.fillWidth: true } 137 - Text { color: Config.Colours.subtext0; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.small; text: Services.Storage.usedStr + " / " + Services.Storage.totalStr } 138 - Text { color: Config.Colours.sapphire; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.large; font.bold: true; text: Services.Storage.usagePercent + "%" } 139 - } 140 - 141 - Item { 142 - Layout.fillWidth: true; height: 8 143 - property real animatedPct: Services.Storage.usagePercent 144 - Behavior on animatedPct { Anim { duration: Config.Appearance.anim.durations.large } } 145 - Rectangle { anchors.fill: parent; radius: 4; color: Config.Colours.surface1 } 146 - Rectangle { 147 - width: parent.width * parent.animatedPct / 100 148 - height: parent.height; radius: 4; color: Config.Colours.sapphire 149 - } 150 - } 151 - } 152 - } 153 - 154 - // Battery 155 - Rectangle { 156 - Layout.fillWidth: true 157 - visible: Services.Battery.hasBattery 158 - implicitHeight: batCol.implicitHeight + 24 159 - radius: Config.Appearance.rounding.normal 160 - color: Config.Colours.surface0 161 - 162 - ColumnLayout { 163 - id: batCol 164 - anchors.fill: parent 165 - anchors.margins: 12 166 - spacing: 8 167 - 168 - RowLayout { 169 - Layout.fillWidth: true 170 - Text { color: Config.Colours.green; font.family: Config.Appearance.font.family; font.pixelSize: 18; text: Services.Battery.status === "Charging" ? "\uf0e7" : "\uf241" } 171 - Text { color: Config.Colours.text; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.normal; font.bold: true; text: "Battery" } 172 - Item { Layout.fillWidth: true } 173 - Text { color: Config.Colours.subtext0; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.small; text: Services.Battery.status } 174 - Text { color: Config.Colours.green; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.large; font.bold: true; text: Services.Battery.percent + "%" } 175 - } 176 - 177 - Item { 178 - Layout.fillWidth: true; height: 8 179 - property real animatedPct: Services.Battery.percent 180 - Behavior on animatedPct { Anim { duration: Config.Appearance.anim.durations.large } } 181 - Rectangle { anchors.fill: parent; radius: 4; color: Config.Colours.surface1 } 182 - Rectangle { 183 - width: parent.width * parent.animatedPct / 100 184 - height: parent.height; radius: 4; color: Config.Colours.green 185 - } 186 - } 187 - } 188 - } 189 - } 190 - }
-165
modules/_config/quickshell/modules/dashboard/tabs/WeatherTab.qml
··· 1 - import QtQuick 2 - import QtQuick.Layouts 3 - import Quickshell.Io 4 - import "../../../config" as Config 5 - import "../../../components" 6 - import "../../../services" as Services 7 - 8 - Item { 9 - id: root 10 - 11 - implicitHeight: weatherLayout.implicitHeight 12 - 13 - property string _condition: "" 14 - property string _feelsLike: "" 15 - property string _humidity: "" 16 - property string _wind: "" 17 - property bool _fetched: false 18 - 19 - onVisibleChanged: { 20 - if (visible && !_fetched) { 21 - weatherDetailProc.running = true 22 - _fetched = true 23 - } 24 - } 25 - 26 - Process { 27 - id: weatherDetailProc 28 - command: ["sh", "-c", "curl -sf 'wttr.in/?format=%c|%C|%t|%f|%h|%w' | tr -d '+'"] 29 - stdout: SplitParser { 30 - onRead: data => { 31 - if (!data) return 32 - var parts = data.trim().split("|") 33 - if (parts.length >= 6) { 34 - root._condition = parts[1] || "" 35 - root._feelsLike = parts[3] || "" 36 - root._humidity = parts[4] || "" 37 - root._wind = parts[5] || "" 38 - } 39 - } 40 - } 41 - } 42 - 43 - ColumnLayout { 44 - id: weatherLayout 45 - width: parent.width 46 - spacing: 16 47 - 48 - // No data 49 - ColumnLayout { 50 - Layout.fillWidth: true 51 - Layout.alignment: Qt.AlignHCenter 52 - Layout.topMargin: 32 53 - Layout.bottomMargin: 32 54 - visible: Services.Weather.weather === "" 55 - spacing: 8 56 - 57 - Text { 58 - Layout.alignment: Qt.AlignHCenter 59 - color: Config.Colours.overlay0 60 - font.family: Config.Appearance.font.family 61 - font.pixelSize: 32 62 - text: "\uf0c2" 63 - } 64 - Text { 65 - Layout.alignment: Qt.AlignHCenter 66 - color: Config.Colours.subtext0 67 - font.family: Config.Appearance.font.family 68 - font.pixelSize: Config.Appearance.font.size.normal 69 - text: "Weather data unavailable" 70 - } 71 - } 72 - 73 - // Current weather 74 - ColumnLayout { 75 - Layout.fillWidth: true 76 - visible: Services.Weather.weather !== "" 77 - spacing: 16 78 - 79 - // Large display 80 - RowLayout { 81 - Layout.fillWidth: true 82 - spacing: 16 83 - 84 - Text { 85 - color: Config.Colours.yellow 86 - font.family: Config.Appearance.font.family 87 - font.pixelSize: 48 88 - text: "\uf0eb" 89 - } 90 - ColumnLayout { 91 - spacing: 4 92 - Text { 93 - color: Config.Colours.text 94 - font.family: Config.Appearance.font.family 95 - font.pixelSize: 28 96 - font.bold: true 97 - text: Services.Weather.weather 98 - } 99 - Text { 100 - color: Config.Colours.subtext0 101 - font.family: Config.Appearance.font.family 102 - font.pixelSize: Config.Appearance.font.size.normal 103 - text: root._condition || "Loading..." 104 - } 105 - } 106 - } 107 - 108 - Rectangle { Layout.fillWidth: true; height: 1; color: Config.Colours.surface1 } 109 - 110 - // Detail cards 111 - RowLayout { 112 - Layout.fillWidth: true 113 - spacing: 12 114 - 115 - // Feels Like 116 - Rectangle { 117 - Layout.fillWidth: true 118 - implicitHeight: 80 119 - radius: Config.Appearance.rounding.normal 120 - color: Config.Colours.surface0 121 - 122 - ColumnLayout { 123 - anchors.centerIn: parent 124 - spacing: 4 125 - Text { Layout.alignment: Qt.AlignHCenter; color: Config.Colours.peach; font.family: Config.Appearance.font.family; font.pixelSize: 20; text: "\uf2c9" } 126 - Text { Layout.alignment: Qt.AlignHCenter; color: Config.Colours.text; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.normal; font.bold: true; text: root._feelsLike || "--" } 127 - Text { Layout.alignment: Qt.AlignHCenter; color: Config.Colours.overlay0; font.family: Config.Appearance.font.family; font.pixelSize: 10; text: "Feels like" } 128 - } 129 - } 130 - 131 - // Humidity 132 - Rectangle { 133 - Layout.fillWidth: true 134 - implicitHeight: 80 135 - radius: Config.Appearance.rounding.normal 136 - color: Config.Colours.surface0 137 - 138 - ColumnLayout { 139 - anchors.centerIn: parent 140 - spacing: 4 141 - Text { Layout.alignment: Qt.AlignHCenter; color: Config.Colours.sapphire; font.family: Config.Appearance.font.family; font.pixelSize: 20; text: "\uf043" } 142 - Text { Layout.alignment: Qt.AlignHCenter; color: Config.Colours.text; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.normal; font.bold: true; text: root._humidity || "--" } 143 - Text { Layout.alignment: Qt.AlignHCenter; color: Config.Colours.overlay0; font.family: Config.Appearance.font.family; font.pixelSize: 10; text: "Humidity" } 144 - } 145 - } 146 - 147 - // Wind 148 - Rectangle { 149 - Layout.fillWidth: true 150 - implicitHeight: 80 151 - radius: Config.Appearance.rounding.normal 152 - color: Config.Colours.surface0 153 - 154 - ColumnLayout { 155 - anchors.centerIn: parent 156 - spacing: 4 157 - Text { Layout.alignment: Qt.AlignHCenter; color: Config.Colours.teal; font.family: Config.Appearance.font.family; font.pixelSize: 20; text: "\uf72e" } 158 - Text { Layout.alignment: Qt.AlignHCenter; color: Config.Colours.text; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.normal; font.bold: true; text: root._wind || "--" } 159 - Text { Layout.alignment: Qt.AlignHCenter; color: Config.Colours.overlay0; font.family: Config.Appearance.font.family; font.pixelSize: 10; text: "Wind" } 160 - } 161 - } 162 - } 163 - } 164 - } 165 - }
-4
modules/_config/quickshell/modules/dashboard/tabs/qmldir
··· 1 - DashTab 1.0 DashTab.qml 2 - MediaTab 1.0 MediaTab.qml 3 - PerformanceTab 1.0 PerformanceTab.qml 4 - WeatherTab 1.0 WeatherTab.qml
-373
modules/_config/quickshell/modules/rightpanel/RightPanel.qml
··· 1 - import QtQuick 2 - import QtQuick.Layouts 3 - import Quickshell 4 - import Quickshell.Io 5 - import "../../config" as Config 6 - import "../../components" 7 - import "../../services" as Services 8 - 9 - PanelWindow { 10 - id: rightTrigger 11 - 12 - required property var modelData 13 - screen: modelData 14 - 15 - anchors { 16 - right: true 17 - top: true 18 - bottom: true 19 - } 20 - margins.right: 0 21 - margins.top: 8 22 - margins.bottom: 8 23 - implicitWidth: 2 24 - exclusiveZone: 0 25 - color: "transparent" 26 - 27 - property bool panelOpen: false 28 - 29 - MouseArea { 30 - anchors.fill: parent 31 - hoverEnabled: true 32 - onEntered: { closeTimer.stop(); rightTrigger.panelOpen = true } 33 - onExited: closeTimer.restart() 34 - } 35 - 36 - onPanelOpenChanged: { 37 - if (panelOpen) { 38 - contentRect._opening = true 39 - fadeInTimer.start() 40 - } else { 41 - fadeInTimer.stop() 42 - contentRect._opening = false 43 - contentRect.opacity = 0 44 - } 45 - } 46 - 47 - Timer { 48 - id: closeTimer 49 - interval: 400 50 - onTriggered: rightTrigger.panelOpen = false 51 - } 52 - 53 - PopupWindow { 54 - id: rightPanel 55 - anchor.window: rightTrigger 56 - anchor.onAnchoring: { 57 - anchor.rect.x = -(implicitWidth + 8) 58 - anchor.rect.y = (rightTrigger.height - implicitHeight) / 2 59 - } 60 - 61 - visible: rightTrigger.panelOpen || contentRect.opacity > 0 62 - implicitWidth: 280 63 - implicitHeight: panelContent.implicitHeight + 32 64 - color: "transparent" 65 - 66 - Timer { 67 - id: fadeInTimer 68 - interval: 16 69 - onTriggered: contentRect.opacity = 1 70 - } 71 - 72 - Rectangle { 73 - id: contentRect 74 - anchors.fill: parent 75 - color: Config.Colours.base 76 - radius: Config.Appearance.rounding.large 77 - border.width: 1 78 - border.color: Config.Colours.surface0 79 - opacity: 0 80 - 81 - property bool _opening: false 82 - Behavior on opacity { 83 - Anim { 84 - duration: contentRect._opening ? Config.Appearance.anim.durations.normal : Config.Appearance.anim.durations.small 85 - easing.bezierCurve: contentRect._opening ? Config.Appearance.anim.curves.standardDecel : Config.Appearance.anim.curves.standardAccel 86 - } 87 - } 88 - 89 - HoverHandler { 90 - onHoveredChanged: { 91 - if (hovered) closeTimer.stop() 92 - else closeTimer.restart() 93 - } 94 - } 95 - 96 - ColumnLayout { 97 - id: panelContent 98 - anchors.fill: parent 99 - anchors.margins: 16 100 - spacing: 12 101 - 102 - // Header 103 - Text { 104 - color: Config.Colours.text 105 - font.family: Config.Appearance.font.family 106 - font.pixelSize: Config.Appearance.font.size.large 107 - font.bold: true 108 - text: "Quick Settings" 109 - } 110 - 111 - Rectangle { Layout.fillWidth: true; height: 1; color: Config.Colours.surface1 } 112 - 113 - // Volume 114 - ColumnLayout { 115 - Layout.fillWidth: true 116 - spacing: 6 117 - 118 - RowLayout { 119 - Layout.fillWidth: true 120 - spacing: 8 121 - Text { color: Config.Colours.maroon; font.family: Config.Appearance.font.family; font.pixelSize: 16; text: Services.Audio.muted ? "\uf026" : "\uf028" } 122 - Text { color: Config.Colours.text; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.normal; text: "Volume" } 123 - Item { Layout.fillWidth: true } 124 - Text { color: Config.Colours.text; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.normal; font.bold: true; text: Services.Audio.muted ? "Muted" : Services.Audio.volume + "%" } 125 - } 126 - 127 - Item { 128 - Layout.fillWidth: true 129 - height: 6 130 - property real animatedPct: Services.Audio.volume 131 - Behavior on animatedPct { Anim { duration: Config.Appearance.anim.durations.large } } 132 - Rectangle { anchors.fill: parent; radius: 3; color: Config.Colours.surface1 } 133 - Rectangle { 134 - width: parent.width * parent.animatedPct / 100 135 - height: parent.height; radius: 3; color: Config.Colours.maroon 136 - } 137 - } 138 - 139 - Rectangle { 140 - Layout.fillWidth: true 141 - height: 28 142 - radius: Config.Appearance.rounding.normal 143 - color: muteMouse.containsMouse ? Config.Colours.surface1 : Config.Colours.surface0 144 - Behavior on color { CAnim {} } 145 - Text { anchors.centerIn: parent; color: Config.Colours.subtext0; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.small; text: Services.Audio.muted ? "Unmute" : "Mute" } 146 - MouseArea { id: muteMouse; anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor; onClicked: muteProc.running = true } 147 - } 148 - } 149 - 150 - Rectangle { Layout.fillWidth: true; height: 1; color: Config.Colours.surface1 } 151 - 152 - // Brightness 153 - ColumnLayout { 154 - Layout.fillWidth: true 155 - spacing: 6 156 - 157 - RowLayout { 158 - Layout.fillWidth: true 159 - spacing: 8 160 - Text { color: Config.Colours.yellow; font.family: Config.Appearance.font.family; font.pixelSize: 16; text: Services.Brightness.brightness > 50 ? "\uf0eb" : "\uf0ec" } 161 - Text { color: Config.Colours.text; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.normal; text: "Brightness" } 162 - Item { Layout.fillWidth: true } 163 - Text { color: Config.Colours.text; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.normal; font.bold: true; text: Services.Brightness.brightness + "%" } 164 - } 165 - 166 - Item { 167 - id: brightnessSlider 168 - Layout.fillWidth: true 169 - height: 14 170 - property real animatedPct: Services.Brightness.brightness 171 - Behavior on animatedPct { Anim { duration: Config.Appearance.anim.durations.large } } 172 - 173 - function setBrightnessFromX(x) { 174 - const pct = Math.max(1, Math.min(100, Math.round(x / width * 100))) 175 - brightnessProc.percent = pct 176 - brightnessProc.running = true 177 - } 178 - 179 - Rectangle { 180 - anchors.verticalCenter: parent.verticalCenter 181 - width: parent.width 182 - height: 6 183 - radius: 3 184 - color: Config.Colours.surface1 185 - } 186 - Rectangle { 187 - anchors.verticalCenter: parent.verticalCenter 188 - width: parent.width * parent.animatedPct / 100 189 - height: 6 190 - radius: 3 191 - color: Config.Colours.yellow 192 - } 193 - MouseArea { 194 - anchors.fill: parent 195 - hoverEnabled: true 196 - cursorShape: Qt.PointingHandCursor 197 - onPressed: mouse => brightnessSlider.setBrightnessFromX(mouse.x) 198 - onPositionChanged: mouse => { if (pressed) brightnessSlider.setBrightnessFromX(mouse.x) } 199 - } 200 - } 201 - } 202 - 203 - Rectangle { Layout.fillWidth: true; height: 1; color: Config.Colours.surface1 } 204 - 205 - // Network 206 - RowLayout { 207 - Layout.fillWidth: true 208 - spacing: 8 209 - 210 - Text { 211 - color: Config.Colours.green 212 - font.family: Config.Appearance.font.family 213 - font.pixelSize: 16 214 - text: { 215 - if (!Services.Network.connected) return "\uf071" 216 - if (Services.Network.isEthernet) return "\udb80\ude00" 217 - return "\uf1eb" 218 - } 219 - } 220 - 221 - ColumnLayout { 222 - spacing: 2 223 - Text { 224 - color: Config.Colours.text 225 - font.family: Config.Appearance.font.family 226 - font.pixelSize: Config.Appearance.font.size.normal 227 - text: Services.Network.connected ? Services.Network.name : "Disconnected" 228 - } 229 - Text { 230 - color: Config.Colours.subtext0 231 - font.family: Config.Appearance.font.family 232 - font.pixelSize: Config.Appearance.font.size.small 233 - text: { 234 - if (!Services.Network.connected) return "No connection" 235 - if (Services.Network.isEthernet) return "Wired connection" 236 - return "Signal: " + Services.Network.strength + "%" 237 - } 238 - } 239 - } 240 - } 241 - 242 - Rectangle { Layout.fillWidth: true; height: 1; color: Config.Colours.surface1; visible: Services.Battery.hasBattery } 243 - 244 - // Battery + Power Profile 245 - RowLayout { 246 - Layout.fillWidth: true 247 - spacing: 8 248 - visible: Services.Battery.hasBattery 249 - 250 - Text { 251 - color: { 252 - if (Services.Battery.percent <= 20) return Config.Colours.red 253 - if (Services.Battery.status === "Charging") return Config.Colours.green 254 - return Config.Colours.green 255 - } 256 - font.family: Config.Appearance.font.family 257 - font.pixelSize: 16 258 - text: { 259 - if (Services.Battery.status === "Charging") return "\uf0e7" 260 - if (Services.Battery.percent > 75) return "\uf240" 261 - if (Services.Battery.percent > 50) return "\uf241" 262 - if (Services.Battery.percent > 25) return "\uf242" 263 - if (Services.Battery.percent > 10) return "\uf243" 264 - return "\uf244" 265 - } 266 - } 267 - 268 - ColumnLayout { 269 - spacing: 2 270 - Text { 271 - color: Config.Colours.text 272 - font.family: Config.Appearance.font.family 273 - font.pixelSize: Config.Appearance.font.size.normal 274 - text: Services.Battery.percent > 0 ? Services.Battery.percent + "%" : "No battery" 275 - } 276 - Text { 277 - color: Config.Colours.subtext0 278 - font.family: Config.Appearance.font.family 279 - font.pixelSize: Config.Appearance.font.size.small 280 - text: Services.Battery.status 281 - } 282 - } 283 - 284 - } 285 - 286 - Rectangle { Layout.fillWidth: true; height: 1; color: Config.Colours.surface1 } 287 - 288 - // Power Profile 289 - RowLayout { 290 - Layout.fillWidth: true 291 - spacing: 8 292 - 293 - Text { 294 - color: { 295 - if (Services.PowerProfile.profile === "performance") return Config.Colours.peach 296 - if (Services.PowerProfile.profile === "power-saver") return Config.Colours.green 297 - return Config.Colours.blue 298 - } 299 - font.family: Config.Appearance.font.family 300 - font.pixelSize: 16 301 - text: { 302 - if (Services.PowerProfile.profile === "performance") return "\uf0e7" 303 - if (Services.PowerProfile.profile === "power-saver") return "\uf06c" 304 - return "\uf24e" 305 - } 306 - } 307 - 308 - ColumnLayout { 309 - spacing: 2 310 - Text { 311 - color: Config.Colours.text 312 - font.family: Config.Appearance.font.family 313 - font.pixelSize: Config.Appearance.font.size.normal 314 - text: { 315 - if (Services.PowerProfile.profile === "performance") return "Performance" 316 - if (Services.PowerProfile.profile === "power-saver") return "Power Saver" 317 - return "Balanced" 318 - } 319 - } 320 - Text { 321 - color: Config.Colours.subtext0 322 - font.family: Config.Appearance.font.family 323 - font.pixelSize: Config.Appearance.font.size.small 324 - text: "Power Profile" 325 - } 326 - } 327 - 328 - Item { Layout.fillWidth: true } 329 - 330 - Rectangle { 331 - width: 32; height: 32 332 - radius: Config.Appearance.rounding.full 333 - color: profileMouse.containsMouse ? Config.Colours.surface1 : Config.Colours.surface0 334 - Behavior on color { CAnim {} } 335 - 336 - Text { 337 - anchors.centerIn: parent 338 - color: Config.Colours.subtext0 339 - font.family: Config.Appearance.font.family 340 - font.pixelSize: 14 341 - text: "\uf2f1" 342 - } 343 - 344 - MouseArea { 345 - id: profileMouse 346 - anchors.fill: parent 347 - hoverEnabled: true 348 - cursorShape: Qt.PointingHandCursor 349 - onClicked: Services.PowerProfile.cycleProfile() 350 - } 351 - } 352 - } 353 - } 354 - 355 - Keys.onEscapePressed: rightTrigger.panelOpen = false 356 - } 357 - 358 - // Click outside to close 359 - MouseArea { 360 - anchors.fill: parent 361 - z: -1 362 - onClicked: rightTrigger.panelOpen = false 363 - } 364 - } 365 - 366 - // Volume/brightness control processes 367 - Process { id: muteProc; command: ["wpctl", "set-mute", "@DEFAULT_AUDIO_SINK@", "toggle"] } 368 - Process { 369 - id: brightnessProc 370 - property int percent: Services.Brightness.brightness 371 - command: ["brightnessctl", "set", percent + "%"] 372 - } 373 - }
-1
modules/_config/quickshell/modules/rightpanel/qmldir
··· 1 - RightPanel 1.0 RightPanel.qml
-214
modules/_config/quickshell/modules/sidebar/Sidebar.qml
··· 1 - import QtQuick 2 - import QtQuick.Layouts 3 - import Quickshell 4 - import "../../config" as Config 5 - import "../../services" as Services 6 - import "components" 7 - import "popouts" 8 - 9 - PanelWindow { 10 - id: sidebar 11 - 12 - required property var modelData 13 - screen: modelData 14 - 15 - anchors { 16 - left: true 17 - top: true 18 - bottom: true 19 - } 20 - margins.left: 0 21 - margins.top: 0 22 - margins.bottom: 0 23 - implicitWidth: 48 24 - color: "transparent" 25 - 26 - property string activePopout: "" 27 - property real popoutAnchorY: height / 2 28 - 29 - function togglePopout(name) { 30 - if (activePopout === name) 31 - activePopout = "" 32 - else 33 - activePopout = name 34 - } 35 - 36 - function openPopoutAt(name, item) { 37 - var pos = item.mapToItem(barBg, 0, item.height / 2) 38 - popoutAnchorY = pos.y 39 - togglePopout(name) 40 - } 41 - 42 - Rectangle { 43 - id: barBg 44 - anchors.fill: parent 45 - radius: Config.Appearance.rounding.large 46 - color: Qt.rgba( 47 - Config.Colours.mantle.r, 48 - Config.Colours.mantle.g, 49 - Config.Colours.mantle.b, 50 - Config.Appearance.barOpacity 51 - ) 52 - 53 - ColumnLayout { 54 - anchors.fill: parent 55 - anchors.leftMargin: 4 56 - anchors.rightMargin: 4 57 - anchors.topMargin: 10 58 - anchors.bottomMargin: 10 59 - spacing: Config.Appearance.spacing.normal 60 - 61 - // NixOS Logo 62 - LogoButton { 63 - id: logoButton 64 - Layout.alignment: Qt.AlignHCenter 65 - onClicked: sidebar.openPopoutAt("session", logoButton) 66 - } 67 - 68 - // Top spacer 69 - Item { Layout.fillHeight: true } 70 - 71 - // Vertical Clock 72 - VerticalClock { 73 - Layout.alignment: Qt.AlignHCenter 74 - onClicked: sidebar.togglePopout("dashboard") 75 - } 76 - 77 - // Weather 78 - Text { 79 - Layout.alignment: Qt.AlignHCenter 80 - font.family: "Noto Color Emoji" 81 - font.pixelSize: 14 82 - text: Services.Weather.icon 83 - visible: Services.Weather.icon !== "" 84 - } 85 - Text { 86 - Layout.alignment: Qt.AlignHCenter 87 - color: Config.Colours.subtext0 88 - font.family: Config.Appearance.font.family 89 - font.pixelSize: 10 90 - text: Services.Weather.temp 91 - visible: Services.Weather.temp !== "" 92 - } 93 - 94 - // Bottom spacer 95 - Item { Layout.fillHeight: true } 96 - 97 - // Tray pill 98 - Rectangle { 99 - Layout.fillWidth: true 100 - implicitHeight: trayLayout.implicitHeight + Config.Appearance.padding.normal * 2 101 - radius: Config.Appearance.rounding.full 102 - color: Config.Colours.surface0 103 - clip: true 104 - visible: tray.count > 0 105 - 106 - ColumnLayout { 107 - id: trayLayout 108 - anchors.horizontalCenter: parent.horizontalCenter 109 - anchors.verticalCenter: parent.verticalCenter 110 - spacing: Config.Appearance.spacing.small 111 - 112 - Tray { id: tray } 113 - } 114 - } 115 - 116 - // Status icons pill 117 - Rectangle { 118 - Layout.fillWidth: true 119 - implicitHeight: statusCol.implicitHeight + Config.Appearance.padding.normal * 2 120 - radius: Config.Appearance.rounding.full 121 - color: Config.Colours.surface0 122 - clip: true 123 - 124 - ColumnLayout { 125 - id: statusCol 126 - anchors.horizontalCenter: parent.horizontalCenter 127 - anchors.verticalCenter: parent.verticalCenter 128 - spacing: Config.Appearance.spacing.small 129 - 130 - // Volume 131 - SidebarIcon { 132 - id: volumeIcon 133 - Layout.alignment: Qt.AlignHCenter 134 - iconText: Services.Audio.muted ? "\uf026" : (Services.Audio.volume > 50 ? "\uf028" : "\uf027") 135 - iconColor: Config.Colours.maroon 136 - onClicked: sidebar.openPopoutAt("audio", volumeIcon) 137 - } 138 - 139 - // Network 140 - SidebarIcon { 141 - id: networkIcon 142 - Layout.alignment: Qt.AlignHCenter 143 - iconText: { 144 - if (!Services.Network.connected) return "\uf071" 145 - if (Services.Network.isEthernet) return "\udb80\ude00" 146 - return "\uf1eb" 147 - } 148 - iconColor: Config.Colours.green 149 - onClicked: sidebar.openPopoutAt("network", networkIcon) 150 - } 151 - 152 - // Battery / Power Profile 153 - SidebarIcon { 154 - id: batteryIcon 155 - Layout.alignment: Qt.AlignHCenter 156 - visible: Services.Battery.hasBattery 157 - iconText: { 158 - if (Services.Battery.status === "Charging") return "\uf0e7" 159 - var p = Services.Battery.percent 160 - if (p > 75) return "\uf240" 161 - if (p > 50) return "\uf241" 162 - if (p > 25) return "\uf242" 163 - if (p > 10) return "\uf243" 164 - return "\uf244" 165 - } 166 - iconColor: { 167 - if (Services.Battery.status === "Charging") return Config.Colours.green 168 - if (Services.Battery.percent <= 20) return Config.Colours.red 169 - return Config.Colours.green 170 - } 171 - onClicked: sidebar.openPopoutAt("powerprofile", batteryIcon) 172 - } 173 - } 174 - } 175 - 176 - } 177 - } 178 - 179 - // Popout windows 180 - DashboardPopout { 181 - show: sidebar.activePopout === "dashboard" 182 - parentWindow: sidebar 183 - anchorY: sidebar.height / 2 184 - onClose: sidebar.activePopout = "" 185 - } 186 - 187 - SessionPopout { 188 - show: sidebar.activePopout === "session" 189 - parentWindow: sidebar 190 - anchorY: sidebar.popoutAnchorY 191 - onClose: sidebar.activePopout = "" 192 - } 193 - 194 - AudioPopout { 195 - show: sidebar.activePopout === "audio" 196 - parentWindow: sidebar 197 - anchorY: sidebar.popoutAnchorY 198 - onClose: sidebar.activePopout = "" 199 - } 200 - 201 - NetworkPopout { 202 - show: sidebar.activePopout === "network" 203 - parentWindow: sidebar 204 - anchorY: sidebar.popoutAnchorY 205 - onClose: sidebar.activePopout = "" 206 - } 207 - 208 - PowerProfilePopout { 209 - show: sidebar.activePopout === "powerprofile" 210 - parentWindow: sidebar 211 - anchorY: sidebar.popoutAnchorY 212 - onClose: sidebar.activePopout = "" 213 - } 214 - }
-39
modules/_config/quickshell/modules/sidebar/components/LogoButton.qml
··· 1 - import QtQuick 2 - import "../../../config" as Config 3 - import "../../../components" 4 - 5 - Item { 6 - id: root 7 - signal clicked() 8 - 9 - width: 24 10 - height: 24 11 - 12 - Rectangle { 13 - anchors.centerIn: parent 14 - width: parent.width + 8 15 - height: parent.height + 8 16 - radius: Config.Appearance.rounding.normal 17 - color: logoMouse.containsMouse 18 - ? Qt.rgba(Config.Colours.blue.r, Config.Colours.blue.g, Config.Colours.blue.b, 0.15) 19 - : "transparent" 20 - Behavior on color { CAnim {} } 21 - } 22 - 23 - Text { 24 - anchors.centerIn: parent 25 - color: Config.Colours.blue 26 - font.family: Config.Appearance.font.family 27 - font.pixelSize: 22 28 - text: "\uf313" 29 - } 30 - 31 - MouseArea { 32 - id: logoMouse 33 - anchors.fill: parent 34 - anchors.margins: -4 35 - hoverEnabled: true 36 - cursorShape: Qt.PointingHandCursor 37 - onClicked: root.clicked() 38 - } 39 - }
-39
modules/_config/quickshell/modules/sidebar/components/PowerButton.qml
··· 1 - import QtQuick 2 - import "../../../config" as Config 3 - import "../../../components" 4 - 5 - Item { 6 - id: root 7 - signal clicked() 8 - 9 - width: 24 10 - height: 24 11 - 12 - Rectangle { 13 - anchors.centerIn: parent 14 - width: parent.width + 8 15 - height: parent.height + 8 16 - radius: Config.Appearance.rounding.full 17 - color: powerMouse.containsMouse 18 - ? Qt.rgba(Config.Colours.red.r, Config.Colours.red.g, Config.Colours.red.b, 0.2) 19 - : "transparent" 20 - Behavior on color { CAnim {} } 21 - } 22 - 23 - Text { 24 - anchors.centerIn: parent 25 - color: Config.Colours.red 26 - font.family: Config.Appearance.font.family 27 - font.pixelSize: 18 28 - text: "\u23fb" 29 - } 30 - 31 - MouseArea { 32 - id: powerMouse 33 - anchors.fill: parent 34 - anchors.margins: -4 35 - hoverEnabled: true 36 - cursorShape: Qt.PointingHandCursor 37 - onClicked: root.clicked() 38 - } 39 - }
-42
modules/_config/quickshell/modules/sidebar/components/SidebarIcon.qml
··· 1 - import QtQuick 2 - import "../../../config" as Config 3 - import "../../../components" 4 - 5 - Item { 6 - id: root 7 - 8 - property string iconText: "" 9 - property color iconColor: Config.Colours.text 10 - signal clicked() 11 - 12 - width: 22 13 - height: 22 14 - 15 - Rectangle { 16 - anchors.centerIn: parent 17 - width: parent.width + 4 18 - height: parent.height + 4 19 - radius: Config.Appearance.rounding.normal 20 - color: iconMouse.containsMouse 21 - ? Qt.rgba(root.iconColor.r, root.iconColor.g, root.iconColor.b, 0.15) 22 - : "transparent" 23 - Behavior on color { CAnim {} } 24 - } 25 - 26 - Text { 27 - anchors.centerIn: parent 28 - color: root.iconColor 29 - font.family: Config.Appearance.font.family 30 - font.pixelSize: 14 31 - text: root.iconText 32 - } 33 - 34 - MouseArea { 35 - id: iconMouse 36 - anchors.fill: parent 37 - anchors.margins: -2 38 - hoverEnabled: true 39 - cursorShape: Qt.PointingHandCursor 40 - onClicked: root.clicked() 41 - } 42 - }
-15
modules/_config/quickshell/modules/sidebar/components/Tray.qml
··· 1 - import QtQuick 2 - import QtQuick.Layouts 3 - import Quickshell.Services.SystemTray 4 - 5 - ColumnLayout { 6 - property alias count: repeater.count 7 - 8 - spacing: 4 9 - 10 - Repeater { 11 - id: repeater 12 - model: SystemTray.items.values 13 - delegate: TrayItem {} 14 - } 15 - }
-59
modules/_config/quickshell/modules/sidebar/components/TrayItem.qml
··· 1 - import QtQuick 2 - import Quickshell 3 - import Quickshell.Widgets 4 - import Quickshell.Io 5 - import "../../../config" as Config 6 - import "../../../components" 7 - 8 - Item { 9 - id: trayItem 10 - 11 - required property var modelData 12 - 13 - width: 22 14 - height: 22 15 - 16 - Rectangle { 17 - anchors.centerIn: parent 18 - width: parent.width + 4 19 - height: parent.height + 4 20 - radius: Config.Appearance.rounding.normal 21 - color: trayMouse.containsMouse 22 - ? Qt.rgba(Config.Colours.text.r, Config.Colours.text.g, Config.Colours.text.b, 0.1) 23 - : "transparent" 24 - Behavior on color { CAnim {} } 25 - } 26 - 27 - IconImage { 28 - implicitSize: 16 29 - anchors.centerIn: parent 30 - source: Quickshell.iconPath(trayItem.modelData.icon, true) || trayItem.modelData.icon 31 - } 32 - 33 - MouseArea { 34 - id: trayMouse 35 - anchors.fill: parent 36 - anchors.margins: -2 37 - acceptedButtons: Qt.LeftButton | Qt.RightButton 38 - hoverEnabled: true 39 - cursorShape: Qt.PointingHandCursor 40 - onClicked: mouse => { 41 - if (mouse.button === Qt.RightButton) { 42 - if (trayItem.modelData.hasMenu) { 43 - trayItem.modelData.display(trayItem.Window.window, trayItem.width, trayItem.height / 2) 44 - } 45 - } else { 46 - if (trayItem.modelData.id.toLowerCase().indexOf("steam") !== -1) { 47 - steamProc.running = true 48 - } else { 49 - trayItem.modelData.activate() 50 - } 51 - } 52 - } 53 - } 54 - 55 - Process { 56 - id: steamProc 57 - command: ["sh", "-c", "WID=$(niri msg --json windows | jq -r '.[] | select(.app_id | test(\"steam\"; \"i\")) | .id' | head -n1); if [ -n \"$WID\" ]; then niri msg action focus-window --id \"$WID\"; else steam steam://open/games; fi"] 58 - } 59 - }
-35
modules/_config/quickshell/modules/sidebar/components/VerticalClock.qml
··· 1 - import QtQuick 2 - import "../../../config" as Config 3 - 4 - Item { 5 - id: root 6 - signal clicked() 7 - 8 - width: 40 9 - implicitHeight: clockText.implicitHeight 10 - 11 - Text { 12 - id: clockText 13 - anchors.horizontalCenter: parent.horizontalCenter 14 - horizontalAlignment: Text.AlignHCenter 15 - color: Config.Colours.blue 16 - font.family: Config.Appearance.font.family 17 - font.pixelSize: 16 18 - font.bold: true 19 - lineHeight: 1.0 20 - text: Qt.formatDateTime(new Date(), "HH\nmm") 21 - } 22 - 23 - Timer { 24 - interval: 1000 25 - running: true 26 - repeat: true 27 - onTriggered: clockText.text = Qt.formatDateTime(new Date(), "HH\nmm") 28 - } 29 - 30 - MouseArea { 31 - anchors.fill: parent 32 - cursorShape: Qt.PointingHandCursor 33 - onClicked: root.clicked() 34 - } 35 - }
-6
modules/_config/quickshell/modules/sidebar/components/qmldir
··· 1 - LogoButton 1.0 LogoButton.qml 2 - VerticalClock 1.0 VerticalClock.qml 3 - SidebarIcon 1.0 SidebarIcon.qml 4 - Tray 1.0 Tray.qml 5 - TrayItem 1.0 TrayItem.qml 6 - PowerButton 1.0 PowerButton.qml
-98
modules/_config/quickshell/modules/sidebar/popouts/AudioPopout.qml
··· 1 - import QtQuick 2 - import QtQuick.Layouts 3 - import Quickshell 4 - import Quickshell.Wayland 5 - import Quickshell.Io 6 - import "../../../config" as Config 7 - import "../../../components" 8 - import "../../../services" as Services 9 - 10 - PopupWindow { 11 - id: audioPopout 12 - 13 - property bool show: false 14 - property var parentWindow 15 - property real anchorY: 0 16 - signal close() 17 - 18 - visible: show || contentRect.opacity > 0 19 - anchor.window: parentWindow 20 - anchor.onAnchoring: { 21 - anchor.rect.x = parentWindow.width + 8 22 - anchor.rect.y = Math.max(0, anchorY - implicitHeight / 2) 23 - } 24 - 25 - implicitWidth: 220 26 - implicitHeight: audioContent.implicitHeight + 32 27 - color: "transparent" 28 - 29 - onShowChanged: { 30 - if (show) { contentRect._opening = true; fadeIn.start() } 31 - else { fadeIn.stop(); contentRect._opening = false; contentRect.opacity = 0 } 32 - } 33 - Timer { id: fadeIn; interval: 16; onTriggered: contentRect.opacity = 1 } 34 - 35 - Rectangle { 36 - id: contentRect 37 - anchors.fill: parent 38 - color: Config.Colours.base 39 - radius: Config.Appearance.rounding.large 40 - border.width: 1 41 - border.color: Config.Colours.surface0 42 - opacity: 0 43 - property bool _opening: false 44 - Behavior on opacity { 45 - Anim { 46 - duration: contentRect._opening ? Config.Appearance.anim.durations.normal : Config.Appearance.anim.durations.small 47 - easing.bezierCurve: contentRect._opening ? Config.Appearance.anim.curves.standardDecel : Config.Appearance.anim.curves.standardAccel 48 - } 49 - } 50 - 51 - ColumnLayout { 52 - id: audioContent 53 - anchors.fill: parent 54 - anchors.margins: 16 55 - spacing: 12 56 - 57 - Text { 58 - color: Config.Colours.text 59 - font.family: Config.Appearance.font.family 60 - font.pixelSize: Config.Appearance.font.size.normal 61 - font.bold: true 62 - text: "Volume" 63 - } 64 - 65 - RowLayout { 66 - Layout.fillWidth: true 67 - spacing: 8 68 - Text { color: Config.Colours.maroon; font.family: Config.Appearance.font.family; font.pixelSize: 20; text: Services.Audio.muted ? "\uf026" : "\uf028" } 69 - Text { color: Config.Colours.text; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.large; font.bold: true; text: Services.Audio.muted ? "Muted" : Services.Audio.volume + "%" } 70 - } 71 - 72 - Item { 73 - Layout.fillWidth: true 74 - height: 6 75 - property real animatedPct: Services.Audio.volume 76 - Behavior on animatedPct { Anim { duration: Config.Appearance.anim.durations.large } } 77 - Rectangle { anchors.fill: parent; radius: 3; color: Config.Colours.surface1 } 78 - Rectangle { width: parent.width * (parent.animatedPct / 100); height: parent.height; radius: 3; color: Config.Colours.maroon } 79 - } 80 - 81 - Rectangle { 82 - Layout.fillWidth: true 83 - height: 32 84 - radius: Config.Appearance.rounding.normal 85 - color: muteMouse.containsMouse ? Config.Colours.surface1 : Config.Colours.surface0 86 - Behavior on color { CAnim {} } 87 - Text { anchors.centerIn: parent; color: Config.Colours.subtext0; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.small; text: Services.Audio.muted ? "Unmute" : "Mute" } 88 - MouseArea { id: muteMouse; anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor; onClicked: muteProc.running = true } 89 - } 90 - 91 - Process { id: muteProc; command: ["wpctl", "set-mute", "@DEFAULT_AUDIO_SINK@", "toggle"] } 92 - } 93 - 94 - Keys.onEscapePressed: audioPopout.close() 95 - } 96 - 97 - MouseArea { anchors.fill: parent; z: -1; onClicked: audioPopout.close() } 98 - }
-279
modules/_config/quickshell/modules/sidebar/popouts/DashboardPopout.qml
··· 1 - import QtQuick 2 - import QtQuick.Layouts 3 - import Quickshell 4 - import Quickshell.Wayland 5 - import "../../../config" as Config 6 - import "../../../components" 7 - import "../../../services" as Services 8 - 9 - PopupWindow { 10 - id: dashPopout 11 - 12 - property bool show: false 13 - property var parentWindow 14 - property real anchorY: 0 15 - signal close() 16 - 17 - visible: show || contentRect.opacity > 0 18 - anchor.window: parentWindow 19 - anchor.onAnchoring: { 20 - anchor.rect.x = parentWindow.width + 8 21 - anchor.rect.y = Math.max(0, anchorY - implicitHeight / 2) 22 - } 23 - 24 - implicitWidth: 280 25 - implicitHeight: dashContent.implicitHeight + 32 26 - color: "transparent" 27 - 28 - onShowChanged: { 29 - if (show) { contentRect._opening = true; fadeIn.start(); resetMonth() } 30 - else { fadeIn.stop(); contentRect._opening = false; contentRect.opacity = 0 } 31 - } 32 - Timer { id: fadeIn; interval: 16; onTriggered: contentRect.opacity = 1 } 33 - 34 - property int calYear: new Date().getFullYear() 35 - property int calMonth: new Date().getMonth() 36 - 37 - function calendarDays() { 38 - var firstDay = new Date(calYear, calMonth, 1).getDay() 39 - var daysInMonth = new Date(calYear, calMonth + 1, 0).getDate() 40 - var prevDays = new Date(calYear, calMonth, 0).getDate() 41 - var today = new Date() 42 - var isCurrentMonth = (calYear === today.getFullYear() && calMonth === today.getMonth()) 43 - var days = [] 44 - for (var i = 0; i < 42; i++) { 45 - var dayNum = i - firstDay + 1 46 - if (dayNum < 1) 47 - days.push({ day: prevDays + dayNum, inMonth: false, isToday: false }) 48 - else if (dayNum > daysInMonth) 49 - days.push({ day: dayNum - daysInMonth, inMonth: false, isToday: false }) 50 - else 51 - days.push({ day: dayNum, inMonth: true, isToday: isCurrentMonth && dayNum === today.getDate() }) 52 - } 53 - return days 54 - } 55 - 56 - function prevMonth() { 57 - if (calMonth === 0) { calMonth = 11; calYear-- } 58 - else calMonth-- 59 - calModel = calendarDays() 60 - } 61 - 62 - function nextMonth() { 63 - if (calMonth === 11) { calMonth = 0; calYear++ } 64 - else calMonth++ 65 - calModel = calendarDays() 66 - } 67 - 68 - function resetMonth() { 69 - var now = new Date() 70 - calYear = now.getFullYear() 71 - calMonth = now.getMonth() 72 - calModel = calendarDays() 73 - } 74 - 75 - property var calModel: calendarDays() 76 - 77 - Rectangle { 78 - id: contentRect 79 - anchors.fill: parent 80 - color: Config.Colours.base 81 - radius: Config.Appearance.rounding.large 82 - border.width: 1 83 - border.color: Config.Colours.surface0 84 - opacity: 0 85 - property bool _opening: false 86 - Behavior on opacity { 87 - Anim { 88 - duration: contentRect._opening ? Config.Appearance.anim.durations.normal : Config.Appearance.anim.durations.small 89 - easing.bezierCurve: contentRect._opening ? Config.Appearance.anim.curves.standardDecel : Config.Appearance.anim.curves.standardAccel 90 - } 91 - } 92 - 93 - ColumnLayout { 94 - id: dashContent 95 - anchors.fill: parent 96 - anchors.margins: 16 97 - spacing: 12 98 - 99 - // Time + Date 100 - ColumnLayout { 101 - Layout.fillWidth: true 102 - spacing: 2 103 - 104 - Text { 105 - id: dashTime 106 - color: Config.Colours.blue 107 - font.family: Config.Appearance.font.family 108 - font.pixelSize: 32 109 - font.bold: true 110 - text: Qt.formatDateTime(new Date(), "HH:mm") 111 - Timer { 112 - interval: 1000 113 - running: dashPopout.show 114 - repeat: true 115 - onTriggered: dashTime.text = Qt.formatDateTime(new Date(), "HH:mm") 116 - } 117 - } 118 - 119 - Text { 120 - color: Config.Colours.subtext0 121 - font.family: Config.Appearance.font.family 122 - font.pixelSize: Config.Appearance.font.size.small 123 - text: Qt.formatDateTime(new Date(), "dddd, MMMM d") 124 - } 125 - } 126 - 127 - Rectangle { Layout.fillWidth: true; height: 1; color: Config.Colours.surface1 } 128 - 129 - // Calendar 130 - Column { 131 - Layout.fillWidth: true 132 - spacing: 4 133 - 134 - RowLayout { 135 - width: parent.width 136 - 137 - Text { 138 - color: Config.Colours.overlay0 139 - font.family: Config.Appearance.font.family 140 - font.pixelSize: 14 141 - text: "\uf053" 142 - MouseArea { anchors.fill: parent; cursorShape: Qt.PointingHandCursor; onClicked: dashPopout.prevMonth() } 143 - } 144 - Item { Layout.fillWidth: true } 145 - Text { 146 - color: Config.Colours.text 147 - font.family: Config.Appearance.font.family 148 - font.pixelSize: Config.Appearance.font.size.normal 149 - font.bold: true 150 - text: new Date(dashPopout.calYear, dashPopout.calMonth, 1).toLocaleDateString(Qt.locale(), "MMMM yyyy") 151 - MouseArea { anchors.fill: parent; cursorShape: Qt.PointingHandCursor; onClicked: dashPopout.resetMonth() } 152 - } 153 - Item { Layout.fillWidth: true } 154 - Text { 155 - color: Config.Colours.overlay0 156 - font.family: Config.Appearance.font.family 157 - font.pixelSize: 14 158 - text: "\uf054" 159 - MouseArea { anchors.fill: parent; cursorShape: Qt.PointingHandCursor; onClicked: dashPopout.nextMonth() } 160 - } 161 - } 162 - 163 - Row { 164 - spacing: 0 165 - Repeater { 166 - model: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"] 167 - delegate: Text { 168 - required property string modelData 169 - width: (dashContent.width - 32) / 7 170 - horizontalAlignment: Text.AlignHCenter 171 - color: Config.Colours.overlay0 172 - font.family: Config.Appearance.font.family 173 - font.pixelSize: 10 174 - text: modelData 175 - } 176 - } 177 - } 178 - 179 - Grid { 180 - columns: 7 181 - rowSpacing: 2 182 - 183 - Repeater { 184 - model: dashPopout.calModel 185 - delegate: Item { 186 - required property var modelData 187 - width: (dashContent.width - 32) / 7 188 - height: 24 189 - 190 - Rectangle { 191 - anchors.centerIn: parent 192 - width: 22; height: 22; radius: 11 193 - color: modelData.isToday ? Config.Colours.blue : "transparent" 194 - } 195 - 196 - Text { 197 - anchors.centerIn: parent 198 - color: modelData.isToday ? Config.Colours.crust 199 - : (modelData.inMonth ? Config.Colours.text : Config.Colours.overlay0) 200 - font.family: Config.Appearance.font.family 201 - font.pixelSize: 11 202 - font.bold: modelData.isToday 203 - text: modelData.day 204 - } 205 - } 206 - } 207 - } 208 - } 209 - 210 - Rectangle { 211 - Layout.fillWidth: true; height: 1; color: Config.Colours.surface1 212 - visible: Services.Weather.weather !== "" 213 - } 214 - 215 - // Weather 216 - RowLayout { 217 - Layout.fillWidth: true 218 - spacing: 8 219 - visible: Services.Weather.weather !== "" 220 - 221 - Text { 222 - color: Config.Colours.yellow 223 - font.family: Config.Appearance.font.family 224 - font.pixelSize: 16 225 - text: "\uf0eb" 226 - } 227 - Text { 228 - Layout.fillWidth: true 229 - color: Config.Colours.text 230 - font.family: Config.Appearance.font.family 231 - font.pixelSize: Config.Appearance.font.size.normal 232 - text: Services.Weather.weather 233 - wrapMode: Text.WordWrap 234 - } 235 - } 236 - 237 - Rectangle { Layout.fillWidth: true; height: 1; color: Config.Colours.surface1 } 238 - 239 - // System 240 - ColumnLayout { 241 - Layout.fillWidth: true 242 - spacing: 6 243 - 244 - Text { 245 - color: Config.Colours.text 246 - font.family: Config.Appearance.font.family 247 - font.pixelSize: Config.Appearance.font.size.normal 248 - font.bold: true 249 - text: "System" 250 - } 251 - 252 - Repeater { 253 - model: { 254 - var items = [ 255 - { icon: "\uf2db", label: "CPU", value: Services.SystemUsage.cpuUsage + "%", color: Config.Colours.peach }, 256 - { icon: "\uefc5", label: "Memory", value: Services.SystemUsage.memUsage + "%", color: Config.Colours.mauve }, 257 - { icon: "\uf2c9", label: "Temp", value: Services.SystemUsage.temperature + "\u00b0C", color: Config.Colours.red } 258 - ] 259 - if (Services.Battery.hasBattery) 260 - items.push({ icon: "\uf241", label: "Battery", value: Services.Battery.percent + "%", color: Config.Colours.green }) 261 - return items 262 - } 263 - delegate: RowLayout { 264 - required property var modelData 265 - Layout.fillWidth: true 266 - Text { color: modelData.color; font.family: Config.Appearance.font.family; font.pixelSize: 12; text: modelData.icon } 267 - Text { color: Config.Colours.subtext0; font.family: Config.Appearance.font.family; font.pixelSize: 12; text: modelData.label } 268 - Item { Layout.fillWidth: true } 269 - Text { color: Config.Colours.text; font.family: Config.Appearance.font.family; font.pixelSize: 12; text: modelData.value } 270 - } 271 - } 272 - } 273 - } 274 - 275 - Keys.onEscapePressed: dashPopout.close() 276 - } 277 - 278 - MouseArea { anchors.fill: parent; z: -1; onClicked: dashPopout.close() } 279 - }
-91
modules/_config/quickshell/modules/sidebar/popouts/NetworkPopout.qml
··· 1 - import QtQuick 2 - import QtQuick.Layouts 3 - import Quickshell 4 - import Quickshell.Wayland 5 - import "../../../config" as Config 6 - import "../../../components" 7 - import "../../../services" as Services 8 - 9 - PopupWindow { 10 - id: networkPopout 11 - 12 - property bool show: false 13 - property var parentWindow 14 - property real anchorY: 0 15 - signal close() 16 - 17 - visible: show || contentRect.opacity > 0 18 - anchor.window: parentWindow 19 - anchor.onAnchoring: { 20 - anchor.rect.x = parentWindow.width + 8 21 - anchor.rect.y = Math.max(0, anchorY - implicitHeight / 2) 22 - } 23 - 24 - implicitWidth: 220 25 - implicitHeight: netContent.implicitHeight + 32 26 - color: "transparent" 27 - 28 - onShowChanged: { 29 - if (show) { contentRect._opening = true; fadeIn.start() } 30 - else { fadeIn.stop(); contentRect._opening = false; contentRect.opacity = 0 } 31 - } 32 - Timer { id: fadeIn; interval: 16; onTriggered: contentRect.opacity = 1 } 33 - 34 - Rectangle { 35 - id: contentRect 36 - anchors.fill: parent 37 - color: Config.Colours.base 38 - radius: Config.Appearance.rounding.large 39 - border.width: 1 40 - border.color: Config.Colours.surface0 41 - opacity: 0 42 - property bool _opening: false 43 - Behavior on opacity { 44 - Anim { 45 - duration: contentRect._opening ? Config.Appearance.anim.durations.normal : Config.Appearance.anim.durations.small 46 - easing.bezierCurve: contentRect._opening ? Config.Appearance.anim.curves.standardDecel : Config.Appearance.anim.curves.standardAccel 47 - } 48 - } 49 - 50 - ColumnLayout { 51 - id: netContent 52 - anchors.fill: parent 53 - anchors.margins: 16 54 - spacing: 12 55 - 56 - Text { 57 - color: Config.Colours.text 58 - font.family: Config.Appearance.font.family 59 - font.pixelSize: Config.Appearance.font.size.normal 60 - font.bold: true 61 - text: "Network" 62 - } 63 - 64 - RowLayout { 65 - Layout.fillWidth: true 66 - spacing: 8 67 - 68 - Text { 69 - color: Config.Colours.green 70 - font.family: Config.Appearance.font.family 71 - font.pixelSize: 20 72 - text: { 73 - if (!Services.Network.connected) return "\uf071" 74 - if (Services.Network.isEthernet) return "\udb80\ude00" 75 - return "\uf1eb" 76 - } 77 - } 78 - 79 - ColumnLayout { 80 - spacing: 2 81 - Text { color: Config.Colours.text; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.normal; text: Services.Network.connected ? Services.Network.name : "Disconnected" } 82 - Text { color: Config.Colours.subtext0; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.small; text: { if (!Services.Network.connected) return "No connection"; if (Services.Network.isEthernet) return "Wired connection"; return "Signal: " + Services.Network.strength + "%" } } 83 - } 84 - } 85 - } 86 - 87 - Keys.onEscapePressed: networkPopout.close() 88 - } 89 - 90 - MouseArea { anchors.fill: parent; z: -1; onClicked: networkPopout.close() } 91 - }
-163
modules/_config/quickshell/modules/sidebar/popouts/PowerProfilePopout.qml
··· 1 - import QtQuick 2 - import QtQuick.Layouts 3 - import Quickshell 4 - import "../../../config" as Config 5 - import "../../../components" 6 - import "../../../services" as Services 7 - 8 - PopupWindow { 9 - id: profilePopout 10 - 11 - property bool show: false 12 - property var parentWindow 13 - property real anchorY: 0 14 - signal close() 15 - 16 - visible: show || contentRect.opacity > 0 17 - anchor.window: parentWindow 18 - anchor.onAnchoring: { 19 - anchor.rect.x = parentWindow.width + 8 20 - anchor.rect.y = Math.max(0, anchorY - implicitHeight / 2) 21 - } 22 - 23 - implicitWidth: 240 24 - implicitHeight: profileContent.implicitHeight + 32 25 - color: "transparent" 26 - 27 - onShowChanged: { 28 - if (show) { contentRect._opening = true; fadeIn.start() } 29 - else { fadeIn.stop(); contentRect._opening = false; contentRect.opacity = 0 } 30 - } 31 - Timer { id: fadeIn; interval: 16; onTriggered: contentRect.opacity = 1 } 32 - 33 - Rectangle { 34 - id: contentRect 35 - anchors.fill: parent 36 - color: Config.Colours.base 37 - radius: Config.Appearance.rounding.large 38 - border.width: 1 39 - border.color: Config.Colours.surface0 40 - opacity: 0 41 - property bool _opening: false 42 - Behavior on opacity { 43 - Anim { 44 - duration: contentRect._opening ? Config.Appearance.anim.durations.normal : Config.Appearance.anim.durations.small 45 - easing.bezierCurve: contentRect._opening ? Config.Appearance.anim.curves.standardDecel : Config.Appearance.anim.curves.standardAccel 46 - } 47 - } 48 - 49 - ColumnLayout { 50 - id: profileContent 51 - anchors.fill: parent 52 - anchors.margins: 16 53 - spacing: 12 54 - 55 - // Battery info 56 - RowLayout { 57 - Layout.fillWidth: true 58 - spacing: 8 59 - 60 - Text { 61 - color: { 62 - if (Services.Battery.percent <= 20) return Config.Colours.red 63 - if (Services.Battery.status === "Charging") return Config.Colours.green 64 - return Config.Colours.green 65 - } 66 - font.family: Config.Appearance.font.family 67 - font.pixelSize: 20 68 - text: { 69 - if (Services.Battery.status === "Charging") return "\uf0e7" 70 - if (Services.Battery.percent > 75) return "\uf240" 71 - if (Services.Battery.percent > 50) return "\uf241" 72 - if (Services.Battery.percent > 25) return "\uf242" 73 - if (Services.Battery.percent > 10) return "\uf243" 74 - return "\uf244" 75 - } 76 - } 77 - 78 - ColumnLayout { 79 - spacing: 2 80 - Text { 81 - color: Config.Colours.text 82 - font.family: Config.Appearance.font.family 83 - font.pixelSize: Config.Appearance.font.size.normal 84 - text: Services.Battery.percent > 0 ? Services.Battery.percent + "%" : "No battery detected" 85 - } 86 - Text { 87 - color: Config.Colours.subtext0 88 - font.family: Config.Appearance.font.family 89 - font.pixelSize: Config.Appearance.font.size.small 90 - text: { 91 - if (Services.PowerProfile.profile === "power-saver") return "Power profile: Power Saver" 92 - if (Services.PowerProfile.profile === "performance") return "Power profile: Performance" 93 - if (Services.PowerProfile.profile === "balanced") return "Power profile: Balanced" 94 - return "Power profile: " + Services.PowerProfile.profile 95 - } 96 - } 97 - } 98 - } 99 - 100 - Rectangle { Layout.fillWidth: true; height: 1; color: Config.Colours.surface1 } 101 - 102 - // Profile buttons 103 - RowLayout { 104 - Layout.fillWidth: true 105 - Layout.alignment: Qt.AlignHCenter 106 - spacing: 12 107 - 108 - Repeater { 109 - model: [ 110 - { id: "performance", icon: "\uf0e7", label: "Perf", color: Config.Colours.peach }, 111 - { id: "balanced", icon: "\uf24e", label: "Bal", color: Config.Colours.blue }, 112 - { id: "power-saver", icon: "\uf06c", label: "Saver", color: Config.Colours.green } 113 - ] 114 - 115 - delegate: Rectangle { 116 - required property var modelData 117 - width: 56; height: 56 118 - radius: Config.Appearance.rounding.full 119 - color: { 120 - if (Services.PowerProfile.profile === modelData.id) 121 - return Qt.rgba(modelData.color.r, modelData.color.g, modelData.color.b, 0.25) 122 - return profileMouse.containsMouse ? Config.Colours.surface1 : Config.Colours.surface0 123 - } 124 - border.width: Services.PowerProfile.profile === modelData.id ? 2 : 0 125 - border.color: modelData.color 126 - Behavior on color { CAnim {} } 127 - 128 - ColumnLayout { 129 - anchors.centerIn: parent 130 - spacing: 2 131 - Text { 132 - Layout.alignment: Qt.AlignHCenter 133 - color: modelData.color 134 - font.family: Config.Appearance.font.family 135 - font.pixelSize: 18 136 - text: modelData.icon 137 - } 138 - Text { 139 - Layout.alignment: Qt.AlignHCenter 140 - color: Config.Colours.subtext0 141 - font.family: Config.Appearance.font.family 142 - font.pixelSize: 9 143 - text: modelData.label 144 - } 145 - } 146 - 147 - MouseArea { 148 - id: profileMouse 149 - anchors.fill: parent 150 - hoverEnabled: true 151 - cursorShape: Qt.PointingHandCursor 152 - onClicked: Services.PowerProfile.setProfile(modelData.id) 153 - } 154 - } 155 - } 156 - } 157 - } 158 - 159 - Keys.onEscapePressed: profilePopout.close() 160 - } 161 - 162 - MouseArea { anchors.fill: parent; z: -1; onClicked: profilePopout.close() } 163 - }
-107
modules/_config/quickshell/modules/sidebar/popouts/SessionPopout.qml
··· 1 - import QtQuick 2 - import QtQuick.Layouts 3 - import Quickshell 4 - import Quickshell.Wayland 5 - import Quickshell.Io 6 - import "../../../config" as Config 7 - import "../../../components" 8 - 9 - PopupWindow { 10 - id: sessionPopout 11 - 12 - property bool show: false 13 - property var parentWindow 14 - property real anchorY: 0 15 - signal close() 16 - 17 - visible: show || contentRect.opacity > 0 18 - anchor.window: parentWindow 19 - anchor.onAnchoring: { 20 - anchor.rect.x = parentWindow.width + 8 21 - anchor.rect.y = Math.max(0, anchorY - implicitHeight / 2) 22 - } 23 - 24 - implicitWidth: content.implicitWidth + 48 25 - implicitHeight: content.implicitHeight + 48 26 - color: "transparent" 27 - 28 - onShowChanged: { 29 - if (show) { contentRect._opening = true; fadeIn.start() } 30 - else { fadeIn.stop(); contentRect._opening = false; contentRect.opacity = 0 } 31 - } 32 - Timer { id: fadeIn; interval: 16; onTriggered: contentRect.opacity = 1 } 33 - 34 - Rectangle { 35 - id: contentRect 36 - anchors.fill: parent 37 - color: Config.Colours.base 38 - radius: Config.Appearance.rounding.large 39 - border.width: 1 40 - border.color: Config.Colours.surface0 41 - opacity: 0 42 - property bool _opening: false 43 - Behavior on opacity { 44 - Anim { 45 - duration: contentRect._opening ? Config.Appearance.anim.durations.normal : Config.Appearance.anim.durations.small 46 - easing.bezierCurve: contentRect._opening ? Config.Appearance.anim.curves.standardDecel : Config.Appearance.anim.curves.standardAccel 47 - } 48 - } 49 - 50 - ColumnLayout { 51 - id: content 52 - anchors.centerIn: parent 53 - spacing: 12 54 - 55 - Text { 56 - Layout.alignment: Qt.AlignHCenter 57 - color: Config.Colours.text 58 - font.family: Config.Appearance.font.family 59 - font.pixelSize: Config.Appearance.font.size.large 60 - text: "Session" 61 - } 62 - 63 - Grid { 64 - columns: 2 65 - spacing: 8 66 - 67 - Repeater { 68 - model: [ 69 - { label: "Logout", icon: "\udb81\ude99", color: Config.Colours.yellow, cmd: "loginctl terminate-user $USER" }, 70 - { label: "Suspend", icon: "\udb81\udf86", color: Config.Colours.blue, cmd: "systemctl suspend" }, 71 - { label: "Reboot", icon: "\udb80\udd30", color: Config.Colours.peach, cmd: "systemctl reboot" }, 72 - { label: "Shutdown", icon: "\u23fb", color: Config.Colours.red, cmd: "systemctl poweroff" } 73 - ] 74 - 75 - delegate: Rectangle { 76 - required property var modelData 77 - width: 80; height: 80 78 - radius: Config.Appearance.rounding.normal 79 - color: sessionMouse.containsMouse ? Config.Colours.surface1 : Config.Colours.surface0 80 - Behavior on color { CAnim {} } 81 - 82 - ColumnLayout { 83 - anchors.centerIn: parent 84 - spacing: 4 85 - Text { Layout.alignment: Qt.AlignHCenter; color: modelData.color; font.family: Config.Appearance.font.family; font.pixelSize: 24; text: modelData.icon } 86 - Text { Layout.alignment: Qt.AlignHCenter; color: Config.Colours.subtext0; font.family: Config.Appearance.font.family; font.pixelSize: Config.Appearance.font.size.small; text: modelData.label } 87 - } 88 - 89 - MouseArea { 90 - id: sessionMouse 91 - anchors.fill: parent 92 - hoverEnabled: true 93 - cursorShape: Qt.PointingHandCursor 94 - onClicked: { sessionPopout.close(); execProc.command = ["sh", "-c", modelData.cmd]; execProc.running = true } 95 - } 96 - } 97 - } 98 - } 99 - 100 - Process { id: execProc } 101 - } 102 - 103 - Keys.onEscapePressed: sessionPopout.close() 104 - } 105 - 106 - MouseArea { anchors.fill: parent; z: -1; onClicked: sessionPopout.close() } 107 - }
-5
modules/_config/quickshell/modules/sidebar/popouts/qmldir
··· 1 - DashboardPopout 1.0 DashboardPopout.qml 2 - SessionPopout 1.0 SessionPopout.qml 3 - AudioPopout 1.0 AudioPopout.qml 4 - NetworkPopout 1.0 NetworkPopout.qml 5 - PowerProfilePopout 1.0 PowerProfilePopout.qml
-1
modules/_config/quickshell/modules/sidebar/qmldir
··· 1 - Sidebar 1.0 Sidebar.qml
-33
modules/_config/quickshell/services/Audio.qml
··· 1 - pragma Singleton 2 - 3 - import QtQuick 4 - import Quickshell 5 - import Quickshell.Io 6 - 7 - QtObject { 8 - readonly property int volume: _volume 9 - readonly property bool muted: _muted 10 - 11 - property int _volume: 0 12 - property bool _muted: false 13 - 14 - property var _proc: Process { 15 - command: ["sh", "-c", "wpctl get-volume @DEFAULT_AUDIO_SINK@"] 16 - stdout: SplitParser { 17 - onRead: data => { 18 - if (!data) return 19 - _muted = data.indexOf("[MUTED]") !== -1 20 - var match = data.match(/Volume:\s+([\d.]+)/) 21 - if (match) _volume = Math.round(parseFloat(match[1]) * 100) 22 - } 23 - } 24 - Component.onCompleted: running = true 25 - } 26 - 27 - property var _timer: Timer { 28 - interval: 2000 29 - running: true 30 - repeat: true 31 - onTriggered: _proc.running = true 32 - } 33 - }
-58
modules/_config/quickshell/services/Battery.qml
··· 1 - pragma Singleton 2 - 3 - import QtQuick 4 - import Quickshell 5 - import Quickshell.Io 6 - 7 - QtObject { 8 - readonly property int percent: _percent 9 - readonly property string status: _status 10 - readonly property bool hasBattery: _hasBattery 11 - 12 - property int _percent: 0 13 - property string _status: "Unknown" 14 - property bool _hasBattery: false 15 - 16 - property var _detectProc: Process { 17 - command: ["sh", "-c", "test -d /sys/class/power_supply/BAT1 && echo yes || echo no"] 18 - stdout: SplitParser { 19 - onRead: data => { 20 - if (!data) return 21 - _hasBattery = data.trim() === "yes" 22 - } 23 - } 24 - Component.onCompleted: running = true 25 - } 26 - 27 - property var _capProc: Process { 28 - command: ["sh", "-c", "cat /sys/class/power_supply/BAT1/capacity 2>/dev/null || echo 0"] 29 - stdout: SplitParser { 30 - onRead: data => { 31 - if (!data) return 32 - _percent = parseInt(data.trim()) || 0 33 - } 34 - } 35 - Component.onCompleted: running = true 36 - } 37 - 38 - property var _statusProc: Process { 39 - command: ["sh", "-c", "cat /sys/class/power_supply/BAT1/status 2>/dev/null || echo Unknown"] 40 - stdout: SplitParser { 41 - onRead: data => { 42 - if (!data) return 43 - _status = data.trim() 44 - } 45 - } 46 - Component.onCompleted: running = true 47 - } 48 - 49 - property var _timer: Timer { 50 - interval: 2000 51 - running: true 52 - repeat: true 53 - onTriggered: { 54 - _capProc.running = true 55 - _statusProc.running = true 56 - } 57 - } 58 - }
-29
modules/_config/quickshell/services/Brightness.qml
··· 1 - pragma Singleton 2 - 3 - import QtQuick 4 - import Quickshell 5 - import Quickshell.Io 6 - 7 - QtObject { 8 - readonly property int brightness: _brightness 9 - 10 - property int _brightness: 0 11 - 12 - property var _proc: Process { 13 - command: ["sh", "-c", "brightnessctl -m | cut -d, -f4 | tr -d '%'"] 14 - stdout: SplitParser { 15 - onRead: data => { 16 - if (!data) return 17 - _brightness = parseInt(data.trim()) || 0 18 - } 19 - } 20 - Component.onCompleted: running = true 21 - } 22 - 23 - property var _timer: Timer { 24 - interval: 2000 25 - running: true 26 - repeat: true 27 - onTriggered: _proc.running = true 28 - } 29 - }
-63
modules/_config/quickshell/services/Mpris.qml
··· 1 - pragma Singleton 2 - 3 - import QtQuick 4 - import Quickshell 5 - import Quickshell.Services.Mpris as MprisService 6 - 7 - QtObject { 8 - id: root 9 - 10 - property MprisService.MprisPlayer _trackedPlayer: null 11 - 12 - readonly property MprisService.MprisPlayer activePlayer: { 13 - // If tracked player is valid and playing, prefer it 14 - if (_trackedPlayer && _trackedPlayer.isPlaying) return _trackedPlayer 15 - // Otherwise find any playing player 16 - var players = MprisService.Mpris.players.values 17 - for (var i = 0; i < players.length; i++) { 18 - if (players[i].isPlaying) return players[i] 19 - } 20 - // Fall back to tracked or first available 21 - if (_trackedPlayer) return _trackedPlayer 22 - return players.length > 0 ? players[0] : null 23 - } 24 - 25 - readonly property string title: activePlayer ? activePlayer.trackTitle : "" 26 - readonly property string artist: activePlayer ? activePlayer.trackArtist : "" 27 - readonly property string album: activePlayer ? activePlayer.trackAlbum : "" 28 - readonly property string artUrl: activePlayer ? activePlayer.trackArtUrl : "" 29 - readonly property bool isPlaying: activePlayer ? activePlayer.isPlaying : false 30 - readonly property real position: activePlayer ? activePlayer.position : 0 31 - readonly property real length: activePlayer ? activePlayer.length : 0 32 - readonly property bool hasPlayer: activePlayer !== null 33 - readonly property bool canNext: activePlayer ? activePlayer.canGoNext : false 34 - readonly property bool canPrev: activePlayer ? activePlayer.canGoPrevious : false 35 - 36 - // Track player changes reactively via Instantiator 37 - property var _tracker: Instantiator { 38 - model: MprisService.Mpris.players 39 - delegate: QtObject { 40 - required property MprisService.MprisPlayer modelData 41 - 42 - property var _conn: Connections { 43 - target: modelData 44 - 45 - Component.onCompleted: { 46 - if (root._trackedPlayer === null || modelData.isPlaying) { 47 - root._trackedPlayer = modelData 48 - } 49 - } 50 - 51 - function onPlaybackStateChanged() { 52 - if (modelData.isPlaying) { 53 - root._trackedPlayer = modelData 54 - } 55 - } 56 - } 57 - } 58 - } 59 - 60 - function togglePlaying() { if (activePlayer) activePlayer.togglePlaying() } 61 - function next() { if (activePlayer) activePlayer.next() } 62 - function previous() { if (activePlayer) activePlayer.previous() } 63 - }
-53
modules/_config/quickshell/services/Network.qml
··· 1 - pragma Singleton 2 - 3 - import QtQuick 4 - import Quickshell 5 - import Quickshell.Io 6 - 7 - QtObject { 8 - readonly property bool connected: _connected 9 - readonly property bool isEthernet: _isEthernet 10 - readonly property string name: _name 11 - readonly property int strength: _strength 12 - 13 - property bool _connected: false 14 - property bool _isEthernet: false 15 - property string _name: "" 16 - property int _strength: 0 17 - 18 - property var _proc: Process { 19 - command: ["sh", "-c", "eth=$(nmcli -t -f TYPE,STATE,CONNECTION dev | grep '^ethernet:connected:' | head -1); if [ -n \"$eth\" ]; then echo \"ethernet:$(echo $eth | cut -d: -f3)\"; else nmcli -t -f ACTIVE,SSID,SIGNAL dev wifi | grep '^yes' | head -1; fi"] 20 - stdout: SplitParser { 21 - onRead: data => { 22 - if (!data || data.trim() === "") { 23 - _connected = false 24 - _isEthernet = false 25 - _name = "Disconnected" 26 - _strength = 0 27 - return 28 - } 29 - var trimmed = data.trim() 30 - if (trimmed.startsWith("ethernet:")) { 31 - _connected = true 32 - _isEthernet = true 33 - _name = trimmed.split(":")[1] || "Ethernet" 34 - _strength = 100 35 - } else { 36 - var parts = trimmed.split(":") 37 - _connected = true 38 - _isEthernet = false 39 - _name = parts[1] || "" 40 - _strength = parseInt(parts[2]) || 0 41 - } 42 - } 43 - } 44 - Component.onCompleted: running = true 45 - } 46 - 47 - property var _timer: Timer { 48 - interval: 2000 49 - running: true 50 - repeat: true 51 - onTriggered: _proc.running = true 52 - } 53 - }
-65
modules/_config/quickshell/services/PowerProfile.qml
··· 1 - pragma Singleton 2 - 3 - import QtQuick 4 - import Quickshell 5 - import Quickshell.Io 6 - 7 - QtObject { 8 - readonly property string profile: _profile 9 - readonly property string backend: _backend 10 - 11 - property string _profile: "" 12 - property string _backend: "" 13 - 14 - function setProfile(name) { 15 - if (_backend === "system76") { 16 - var s76profile = name === "power-saver" ? "battery" : name 17 - _setProc.command = ["system76-power", "profile", s76profile] 18 - } else { 19 - _setProc.command = ["powerprofilesctl", "set", name] 20 - } 21 - _setProc.running = true 22 - _profile = name 23 - } 24 - 25 - function cycleProfile() { 26 - var next = "balanced" 27 - if (_profile === "balanced") next = "performance" 28 - else if (_profile === "performance") next = "power-saver" 29 - else next = "balanced" 30 - setProfile(next) 31 - } 32 - 33 - property var _backendProc: Process { 34 - command: ["sh", "-c", "command -v system76-power >/dev/null 2>&1 && echo system76 || echo ppd"] 35 - stdout: SplitParser { 36 - onRead: data => { 37 - if (!data) return 38 - _backend = data.trim() 39 - } 40 - } 41 - Component.onCompleted: running = true 42 - } 43 - 44 - property var _profileProc: Process { 45 - command: ["sh", "-c", "if command -v system76-power >/dev/null 2>&1; then p=$(system76-power profile 2>/dev/null | grep -oiE 'performance|balanced|battery' | tr '[:upper:]' '[:lower:]'); [ \"$p\" = \"battery\" ] && p=\"power-saver\"; echo \"${p:-unknown}\"; else powerprofilesctl get 2>/dev/null || echo unknown; fi"] 46 - stdout: SplitParser { 47 - onRead: data => { 48 - if (!data) return 49 - _profile = data.trim() 50 - } 51 - } 52 - Component.onCompleted: running = true 53 - } 54 - 55 - property var _setProc: Process { 56 - id: _setProc 57 - } 58 - 59 - property var _timer: Timer { 60 - interval: 2000 61 - running: true 62 - repeat: true 63 - onTriggered: _profileProc.running = true 64 - } 65 - }
-36
modules/_config/quickshell/services/Storage.qml
··· 1 - pragma Singleton 2 - 3 - import QtQuick 4 - import Quickshell 5 - import Quickshell.Io 6 - 7 - QtObject { 8 - readonly property int usagePercent: _usagePercent 9 - readonly property string usedStr: _usedStr 10 - readonly property string totalStr: _totalStr 11 - 12 - property int _usagePercent: 0 13 - property string _usedStr: "" 14 - property string _totalStr: "" 15 - 16 - property var _proc: Process { 17 - command: ["sh", "-c", "df -h / | tail -1"] 18 - stdout: SplitParser { 19 - onRead: data => { 20 - if (!data) return 21 - var parts = data.trim().split(/\s+/) 22 - _totalStr = parts[1] || "" 23 - _usedStr = parts[2] || "" 24 - _usagePercent = parseInt((parts[4] || "0").replace("%", "")) || 0 25 - } 26 - } 27 - Component.onCompleted: running = true 28 - } 29 - 30 - property var _timer: Timer { 31 - interval: 30000 32 - running: true 33 - repeat: true 34 - onTriggered: _proc.running = true 35 - } 36 - }
-71
modules/_config/quickshell/services/SystemUsage.qml
··· 1 - pragma Singleton 2 - 3 - import QtQuick 4 - import Quickshell 5 - import Quickshell.Io 6 - 7 - QtObject { 8 - readonly property int cpuUsage: _cpuUsage 9 - readonly property int memUsage: _memUsage 10 - readonly property int temperature: _temperature 11 - 12 - property int _cpuUsage: 0 13 - property int _memUsage: 0 14 - property int _temperature: 0 15 - property var _lastCpuIdle: 0 16 - property var _lastCpuTotal: 0 17 - 18 - property var _cpuProc: Process { 19 - command: ["sh", "-c", "head -1 /proc/stat"] 20 - stdout: SplitParser { 21 - onRead: data => { 22 - if (!data) return 23 - var p = data.trim().split(/\s+/) 24 - var idle = parseInt(p[4]) + parseInt(p[5]) 25 - var total = p.slice(1, 8).reduce((a, b) => a + parseInt(b), 0) 26 - if (_lastCpuTotal > 0) { 27 - _cpuUsage = Math.round(100 * (1 - (idle - _lastCpuIdle) / (total - _lastCpuTotal))) 28 - } 29 - _lastCpuTotal = total 30 - _lastCpuIdle = idle 31 - } 32 - } 33 - Component.onCompleted: running = true 34 - } 35 - 36 - property var _memProc: Process { 37 - command: ["sh", "-c", "free | grep Mem"] 38 - stdout: SplitParser { 39 - onRead: data => { 40 - if (!data) return 41 - var parts = data.trim().split(/\s+/) 42 - var total = parseInt(parts[1]) || 1 43 - var used = parseInt(parts[2]) || 0 44 - _memUsage = Math.round(100 * used / total) 45 - } 46 - } 47 - Component.onCompleted: running = true 48 - } 49 - 50 - property var _tempProc: Process { 51 - command: ["sh", "-c", "cat /sys/class/thermal/thermal_zone0/temp 2>/dev/null || echo 0"] 52 - stdout: SplitParser { 53 - onRead: data => { 54 - if (!data) return 55 - _temperature = Math.round(parseInt(data.trim()) / 1000) 56 - } 57 - } 58 - Component.onCompleted: running = true 59 - } 60 - 61 - property var _timer: Timer { 62 - interval: 2000 63 - running: true 64 - repeat: true 65 - onTriggered: { 66 - _cpuProc.running = true 67 - _memProc.running = true 68 - _tempProc.running = true 69 - } 70 - } 71 - }
-41
modules/_config/quickshell/services/Weather.qml
··· 1 - pragma Singleton 2 - 3 - import QtQuick 4 - import Quickshell 5 - import Quickshell.Io 6 - 7 - QtObject { 8 - readonly property string weather: _weather 9 - readonly property string icon: _icon 10 - readonly property string temp: _temp 11 - 12 - property string _weather: "" 13 - property string _icon: "" 14 - property string _temp: "" 15 - 16 - property var _proc: Process { 17 - command: ["sh", "-c", "curl -sf 'wttr.in/?format=%c|%t&m' | tr -d '+'"] 18 - stdout: SplitParser { 19 - onRead: data => { 20 - if (!data) return 21 - var raw = data.trim() 22 - var parts = raw.split("|") 23 - if (parts.length >= 2) { 24 - _icon = parts[0].trim() 25 - _temp = parts[1].trim() 26 - _weather = _icon + " " + _temp 27 - } else { 28 - _weather = raw 29 - } 30 - } 31 - } 32 - Component.onCompleted: running = true 33 - } 34 - 35 - property var _timer: Timer { 36 - interval: 900000 37 - running: true 38 - repeat: true 39 - onTriggered: _proc.running = true 40 - } 41 - }
-9
modules/_config/quickshell/services/qmldir
··· 1 - singleton Audio 1.0 Audio.qml 2 - singleton Brightness 1.0 Brightness.qml 3 - singleton Network 1.0 Network.qml 4 - singleton SystemUsage 1.0 SystemUsage.qml 5 - singleton Battery 1.0 Battery.qml 6 - singleton PowerProfile 1.0 PowerProfile.qml 7 - singleton Weather 1.0 Weather.qml 8 - singleton Mpris 1.0 Mpris.qml 9 - singleton Storage 1.0 Storage.qml
-21
modules/_config/quickshell/shell.qml
··· 1 - import Quickshell 2 - import "modules/sidebar" 3 - import "modules/dashboard" 4 - import "modules/rightpanel" 5 - 6 - ShellRoot { 7 - Variants { 8 - model: Quickshell.screens 9 - Sidebar {} 10 - } 11 - 12 - Variants { 13 - model: Quickshell.screens 14 - Dashboard {} 15 - } 16 - 17 - Variants { 18 - model: Quickshell.screens 19 - RightPanel {} 20 - } 21 - }
+2
modules/base.nix
··· 51 51 }; 52 52 53 53 # Services 54 + services.power-profiles-daemon.enable = true; 54 55 services.udisks2.enable = true; 56 + services.upower.enable = true; 55 57 services.tailscale.enable = true; 56 58 57 59 # Docker
+19 -74
modules/desktop.nix
··· 104 104 inputs.mako-tui.packages.${pkgs.stdenv.hostPlatform.system}.default 105 105 bemoji 106 106 networkmanager_dmenu 107 - quickshell 108 107 kaleidux 109 108 (import ../packages/cclip.nix { inherit pkgs; }) 110 109 pavucontrol ··· 264 263 "systemctl" 265 264 "--user" 266 265 "restart" 267 - "quickshell.service" 266 + "noctalia.service" 268 267 ]; 269 268 "XF86AudioPlay".action.spawn = [ 270 269 "playerctl" ··· 382 381 flavor = "frappe"; 383 382 }; 384 383 384 + programs.noctalia = { 385 + enable = true; 386 + systemd.enable = true; 387 + settings = { 388 + shell = { 389 + launch_apps_as_systemd_services = true; 390 + }; 391 + theme = { 392 + mode = "dark"; 393 + source = "builtin"; 394 + builtin = "Catppuccin"; 395 + }; 396 + wallpaper = { 397 + enabled = false; 398 + }; 399 + }; 400 + }; 401 + 385 402 # Wallpaper downloads 386 403 home.activation.downloadWallpapers = 387 404 config.lib.dag.entryAfter [ "writeBoundary" ] '' ··· 404 421 transition = { type = "fade" } 405 422 ''; 406 423 407 - # Quickshell status bar 408 - xdg.configFile."quickshell" = { 409 - source = ./_config/quickshell; 410 - recursive = true; 411 - }; 412 - 413 424 systemd.user.services.kaleidux = { 414 425 Unit = { 415 426 Description = "Kaleidux dynamic wallpaper daemon"; ··· 420 431 ExecStart = "${kaleidux}/bin/kaleidux-daemon"; 421 432 Restart = "on-failure"; 422 433 RestartSec = 2; 423 - }; 424 - Install = { 425 - WantedBy = [ "graphical-session.target" ]; 426 - }; 427 - }; 428 - 429 - systemd.user.services.quickshell = { 430 - Unit = { 431 - Description = "QuickShell status bar"; 432 - After = [ "graphical-session.target" ]; 433 - PartOf = [ "graphical-session.target" ]; 434 - }; 435 - Service = { 436 - ExecStart = "${pkgs.quickshell}/bin/quickshell"; 437 - Restart = "on-failure"; 438 - RestartSec = 2; 439 - }; 440 - Install = { 441 - WantedBy = [ "graphical-session.target" ]; 442 - }; 443 - }; 444 - 445 - systemd.user.services.quickshell-reload = { 446 - Unit = { 447 - Description = "Reload QuickShell on wake or display change"; 448 - After = [ 449 - "quickshell.service" 450 - "graphical-session.target" 451 - ]; 452 - PartOf = [ "graphical-session.target" ]; 453 - }; 454 - Service = { 455 - Type = "simple"; 456 - ExecStart = "${pkgs.writeShellScript "quickshell-reload" '' 457 - LOCKFILE="/tmp/quickshell-reload.lock" 458 - 459 - do_restart() { 460 - ( 461 - ${pkgs.util-linux}/bin/flock -xn 200 || exit 0 462 - sleep 2 463 - ${pkgs.systemd}/bin/systemctl --user restart quickshell.service 464 - sleep 3 465 - ) 200>"$LOCKFILE" 466 - } 467 - 468 - # Sleep/wake monitor 469 - ${pkgs.dbus}/bin/dbus-monitor --system \ 470 - "type='signal',interface='org.freedesktop.login1.Manager',member='PrepareForSleep'" 2>/dev/null | \ 471 - while IFS= read -r line; do 472 - if [[ "$line" == *"boolean false"* ]]; then 473 - do_restart 474 - fi 475 - done & 476 - 477 - # Display hotplug monitor 478 - ${pkgs.systemd}/bin/udevadm monitor --property --subsystem-match=drm 2>/dev/null | \ 479 - while IFS= read -r line; do 480 - if [[ "$line" == *"HOTPLUG=1"* ]]; then 481 - do_restart 482 - fi 483 - done & 484 - 485 - wait 486 - ''}"; 487 - Restart = "on-failure"; 488 - RestartSec = 5; 489 434 }; 490 435 Install = { 491 436 WantedBy = [ "graphical-session.target" ];
+1
modules/hosts/framework16.nix
··· 34 34 imports = [ 35 35 inputs.catppuccin.homeModules.catppuccin 36 36 inputs.niri.homeModules.niri 37 + inputs.noctalia.homeModules.default 37 38 inputs.zen-browser.homeModules.beta 38 39 inputs.agenix.homeManagerModules.default 39 40 hm.sean
+1
modules/hosts/ideapad5.nix
··· 33 33 imports = [ 34 34 inputs.catppuccin.homeModules.catppuccin 35 35 inputs.niri.homeModules.niri 36 + inputs.noctalia.homeModules.default 36 37 inputs.zen-browser.homeModules.beta 37 38 inputs.agenix.homeManagerModules.default 38 39 hm.sean
+1
modules/hosts/mira.nix
··· 45 45 imports = [ 46 46 inputs.catppuccin.homeModules.catppuccin 47 47 inputs.niri.homeModules.niri 48 + inputs.noctalia.homeModules.default 48 49 inputs.zen-browser.homeModules.beta 49 50 inputs.agenix.homeManagerModules.default 50 51 hm.sean