me like nix
0

Configure Feed

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

1import Quickshell 2import Quickshell.Wayland 3import Quickshell.Io 4import QtQuick 5import QtQuick.Layouts 6 7ShellRoot { 8 Variants { 9 model: Quickshell.screens 10 11 PanelWindow { 12 id: root 13 screen: modelData 14 15 // Catppuccin Frappe palette 16 readonly property color colBase: "#303446" 17 readonly property color colMantle: "#292c3c" 18 readonly property color colSurface0: "#414559" 19 readonly property color colText: "#c6d0f5" 20 readonly property color colSubtext0: "#a5adce" 21 readonly property color colOverlay0: "#737994" 22 readonly property color colBlue: "#8caaee" 23 readonly property color colGreen: "#a6d189" 24 readonly property color colPeach: "#ef9f76" 25 readonly property color colMauve: "#ca9ee6" 26 readonly property color colRed: "#e78284" 27 readonly property color colYellow: "#e5c890" 28 readonly property color colMaroon: "#ea999c" 29 readonly property color colLavender: "#babbf1" 30 readonly property color colSky: "#99d1db" 31 readonly property color colSapphire: "#85c1dc" 32 33 readonly property string fontFamily: "BerkeleyMono Nerd Font" 34 readonly property int fontSize: 14 35 36 // Nerd Font icons (using Unicode escapes) 37 // Volume 38 readonly property string iconVolHigh: "\uf028" 39 readonly property string iconVolLow: "\uf027" 40 readonly property string iconVolMute: "\uf6a9" 41 // Network 42 readonly property string iconWifi: "\uf1eb" 43 readonly property string iconEthernet: "\udb80\ude00" 44 // Power profiles 45 readonly property string iconBolt: "\uf0e7" 46 readonly property string iconBalance: "\uf24e" 47 readonly property string iconLeaf: "\uf06c" 48 // CPU 49 readonly property string iconCpu: "\uf2db" 50 // Memory 51 readonly property string iconMem: "\uefc5" 52 // Temperature 53 readonly property string iconTempLow: "\uf2cb" 54 readonly property string iconTempMed: "\uf2c9" 55 readonly property string iconTempHigh: "\uf2c7" 56 // Backlight 57 readonly property string iconSun: "\uf185" 58 // Battery 59 readonly property string iconBatEmpty: "\uf244" 60 readonly property string iconBatQuarter: "\uf243" 61 readonly property string iconBatHalf: "\uf242" 62 readonly property string iconBatThreeQ: "\uf241" 63 readonly property string iconBatFull: "\uf240" 64 readonly property string iconBatCharge: "\uf0e7" 65 // Power 66 readonly property string iconPower: "\u23fb" 67 68 // System data 69 property int cpuUsage: 0 70 property int memUsage: 0 71 property int temperature: 0 72 property var lastCpuIdle: 0 73 property var lastCpuTotal: 0 74 property string networkName: "" 75 property int networkStrength: 0 76 property bool networkConnected: false 77 property bool networkIsEthernet: false 78 property int volume: 0 79 property bool volumeMuted: false 80 property int brightness: 0 81 property int batteryPercent: 0 82 property string batteryStatus: "" 83 property string powerProfile: "" 84 property string weather: "" 85 86 anchors { 87 top: true 88 left: true 89 right: true 90 } 91 margins.top: 0 92 margins.bottom: 0 93 margins.left: 0 94 margins.right: 0 95 implicitHeight: 30 96 color: Qt.rgba(0.188, 0.204, 0.275, 0.85) 97 98 // CPU process 99 Process { 100 id: cpuProc 101 command: ["sh", "-c", "head -1 /proc/stat"] 102 stdout: SplitParser { 103 onRead: data => { 104 if (!data) return 105 var p = data.trim().split(/\s+/) 106 var idle = parseInt(p[4]) + parseInt(p[5]) 107 var total = p.slice(1, 8).reduce((a, b) => a + parseInt(b), 0) 108 if (root.lastCpuTotal > 0) { 109 root.cpuUsage = Math.round(100 * (1 - (idle - root.lastCpuIdle) / (total - root.lastCpuTotal))) 110 } 111 root.lastCpuTotal = total 112 root.lastCpuIdle = idle 113 } 114 } 115 Component.onCompleted: running = true 116 } 117 118 // Memory process 119 Process { 120 id: memProc 121 command: ["sh", "-c", "free | grep Mem"] 122 stdout: SplitParser { 123 onRead: data => { 124 if (!data) return 125 var parts = data.trim().split(/\s+/) 126 var total = parseInt(parts[1]) || 1 127 var used = parseInt(parts[2]) || 0 128 root.memUsage = Math.round(100 * used / total) 129 } 130 } 131 Component.onCompleted: running = true 132 } 133 134 // Temperature 135 Process { 136 id: tempProc 137 command: ["sh", "-c", "cat /sys/class/thermal/thermal_zone0/temp 2>/dev/null || echo 0"] 138 stdout: SplitParser { 139 onRead: data => { 140 if (!data) return 141 root.temperature = Math.round(parseInt(data.trim()) / 1000) 142 } 143 } 144 Component.onCompleted: running = true 145 } 146 147 // Network - check ethernet first, then wifi 148 Process { 149 id: netProc 150 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"] 151 stdout: SplitParser { 152 onRead: data => { 153 if (!data || data.trim() === "") { 154 root.networkConnected = false 155 root.networkIsEthernet = false 156 root.networkName = "Disconnected" 157 root.networkStrength = 0 158 return 159 } 160 var trimmed = data.trim() 161 if (trimmed.startsWith("ethernet:")) { 162 root.networkConnected = true 163 root.networkIsEthernet = true 164 root.networkName = trimmed.split(":")[1] || "Ethernet" 165 root.networkStrength = 100 166 } else { 167 var parts = trimmed.split(":") 168 root.networkConnected = true 169 root.networkIsEthernet = false 170 root.networkName = parts[1] || "" 171 root.networkStrength = parseInt(parts[2]) || 0 172 } 173 } 174 } 175 Component.onCompleted: running = true 176 } 177 178 // Volume 179 Process { 180 id: volProc 181 command: ["sh", "-c", "wpctl get-volume @DEFAULT_AUDIO_SINK@"] 182 stdout: SplitParser { 183 onRead: data => { 184 if (!data) return 185 root.volumeMuted = data.indexOf("[MUTED]") !== -1 186 var match = data.match(/Volume:\s+([\d.]+)/) 187 if (match) { 188 root.volume = Math.round(parseFloat(match[1]) * 100) 189 } 190 } 191 } 192 Component.onCompleted: running = true 193 } 194 195 // Brightness 196 Process { 197 id: brightProc 198 command: ["sh", "-c", "brightnessctl -m | cut -d, -f4 | tr -d '%'"] 199 stdout: SplitParser { 200 onRead: data => { 201 if (!data) return 202 root.brightness = parseInt(data.trim()) || 0 203 } 204 } 205 Component.onCompleted: running = true 206 } 207 208 // Battery 209 Process { 210 id: batProc 211 command: ["sh", "-c", "cat /sys/class/power_supply/BAT1/capacity 2>/dev/null || echo 0"] 212 stdout: SplitParser { 213 onRead: data => { 214 if (!data) return 215 root.batteryPercent = parseInt(data.trim()) || 0 216 } 217 } 218 Component.onCompleted: running = true 219 } 220 221 Process { 222 id: batStatusProc 223 command: ["sh", "-c", "cat /sys/class/power_supply/BAT1/status 2>/dev/null || echo Unknown"] 224 stdout: SplitParser { 225 onRead: data => { 226 if (!data) return 227 root.batteryStatus = data.trim() 228 } 229 } 230 Component.onCompleted: running = true 231 } 232 233 // Power profile 234 Process { 235 id: powerProc 236 command: ["sh", "-c", "powerprofilesctl get 2>/dev/null || echo unknown"] 237 stdout: SplitParser { 238 onRead: data => { 239 if (!data) return 240 root.powerProfile = data.trim() 241 } 242 } 243 Component.onCompleted: running = true 244 } 245 246 // Weather 247 Process { 248 id: weatherProc 249 command: ["sh", "-c", "curl -sf 'wttr.in/?format=%c+%t' | tr -d '+'"] 250 stdout: SplitParser { 251 onRead: data => { 252 if (!data) return 253 root.weather = data.trim() 254 } 255 } 256 Component.onCompleted: running = true 257 } 258 259 // Weather timer (refresh every 15 minutes) 260 Timer { 261 interval: 900000 262 running: true 263 repeat: true 264 onTriggered: weatherProc.running = true 265 } 266 267 // Update timer 268 Timer { 269 interval: 2000 270 running: true 271 repeat: true 272 onTriggered: { 273 cpuProc.running = true 274 memProc.running = true 275 tempProc.running = true 276 netProc.running = true 277 volProc.running = true 278 brightProc.running = true 279 batProc.running = true 280 batStatusProc.running = true 281 powerProc.running = true 282 } 283 } 284 285 // Center section - absolutely positioned 286 RowLayout { 287 anchors.centerIn: parent 288 spacing: 12 289 290 Text { 291 color: root.colText 292 font { family: root.fontFamily; pixelSize: root.fontSize } 293 text: root.weather 294 visible: root.weather !== "" 295 } 296 297 Text { 298 id: clockText 299 color: root.colBlue 300 font { family: root.fontFamily; pixelSize: root.fontSize } 301 text: Qt.formatDateTime(new Date(), "dd-MM-yyyy HH:mm") 302 Timer { 303 interval: 1000 304 running: true 305 repeat: true 306 onTriggered: clockText.text = Qt.formatDateTime(new Date(), "dd-MM-yyyy HH:mm") 307 } 308 } 309 } 310 311 // Right section - anchored to right edge 312 RowLayout { 313 anchors.right: parent.right 314 anchors.rightMargin: 12 315 anchors.verticalCenter: parent.verticalCenter 316 spacing: 12 317 318 // Volume 319 Text { 320 color: root.colMaroon 321 font { family: root.fontFamily; pixelSize: root.fontSize } 322 text: root.volume + "% " + (root.volumeMuted ? root.iconVolMute : (root.volume > 50 ? root.iconVolHigh : root.iconVolLow)) 323 MouseArea { 324 anchors.fill: parent 325 cursorShape: Qt.PointingHandCursor 326 onClicked: volClickProc.running = true 327 } 328 } 329 330 Process { 331 id: volClickProc 332 command: ["pavucontrol"] 333 } 334 335 Rectangle { width: 1; height: 16; color: root.colOverlay0 } 336 337 // Network 338 Text { 339 color: root.colGreen 340 font { family: root.fontFamily; pixelSize: root.fontSize } 341 text: { 342 if (!root.networkConnected) return "Disconnected \u26a0" 343 if (root.networkIsEthernet) return root.networkName + " " + root.iconEthernet 344 return root.networkName + " (" + root.networkStrength + "%) " + root.iconWifi 345 } 346 } 347 348 Rectangle { width: 1; height: 16; color: root.colOverlay0 } 349 350 // Power Profile 351 Text { 352 color: root.colText 353 font { family: root.fontFamily; pixelSize: root.fontSize } 354 text: { 355 if (root.powerProfile === "performance") return root.iconBolt 356 if (root.powerProfile === "balanced") return root.iconBalance 357 if (root.powerProfile === "power-saver") return root.iconLeaf 358 return root.iconBalance 359 } 360 } 361 362 Rectangle { width: 1; height: 16; color: root.colOverlay0 } 363 364 // CPU 365 Text { 366 color: root.colPeach 367 font { family: root.fontFamily; pixelSize: root.fontSize } 368 text: root.cpuUsage + "% " + root.iconCpu 369 } 370 371 Rectangle { width: 1; height: 16; color: root.colOverlay0 } 372 373 // Memory 374 Text { 375 color: root.colMauve 376 font { family: root.fontFamily; pixelSize: root.fontSize } 377 text: root.memUsage + "% " + root.iconMem 378 } 379 380 Rectangle { width: 1; height: 16; color: root.colOverlay0 } 381 382 // Temperature 383 Text { 384 color: root.colRed 385 font { family: root.fontFamily; pixelSize: root.fontSize } 386 text: { 387 var icon = root.temperature >= 80 ? root.iconTempHigh : (root.temperature >= 50 ? root.iconTempMed : root.iconTempLow) 388 return root.temperature + "\u00b0C " + icon 389 } 390 } 391 392 Rectangle { width: 1; height: 16; color: root.colOverlay0 } 393 394 // Backlight 395 Text { 396 color: root.colYellow 397 font { family: root.fontFamily; pixelSize: root.fontSize } 398 text: root.brightness + "% " + root.iconSun 399 } 400 401 Rectangle { width: 1; height: 16; color: root.colOverlay0 } 402 403 // Battery 404 Text { 405 color: { 406 if (root.batteryStatus === "Charging") return root.colGreen 407 if (root.batteryPercent <= 15) return root.colRed 408 if (root.batteryPercent <= 30) return root.colRed 409 return root.colGreen 410 } 411 font { family: root.fontFamily; pixelSize: root.fontSize } 412 text: { 413 var icon 414 if (root.batteryStatus === "Charging") { 415 icon = root.iconBatCharge 416 } else { 417 var icons = [root.iconBatEmpty, root.iconBatQuarter, root.iconBatHalf, root.iconBatThreeQ, root.iconBatFull] 418 var idx = Math.min(Math.floor(root.batteryPercent / 25), 4) 419 icon = icons[idx] 420 } 421 return root.batteryPercent + "% " + icon 422 } 423 } 424 425 Rectangle { width: 1; height: 16; color: root.colOverlay0 } 426 427 // Power button 428 Text { 429 color: root.colRed 430 font { family: root.fontFamily; pixelSize: root.fontSize } 431 text: root.iconPower 432 } 433 } 434 } 435 } 436} 437