quickshell lets gooo

This commit is contained in:
zastian@mrthoddata.com
2025-06-17 18:54:28 +01:00
parent 8d618a8ae3
commit 040bc459a4
124 changed files with 11667 additions and 83 deletions

View File

@@ -0,0 +1,37 @@
pragma Singleton
import "root:/utils/scripts/fuzzysort.js" as Fuzzy
import Quickshell
import Quickshell.Io
Singleton {
id: root
readonly property list<DesktopEntry> list: DesktopEntries.applications.values.filter(a => !a.noDisplay).sort((a, b) => a.name.localeCompare(b.name))
readonly property list<var> preppedApps: list.map(a => ({
name: Fuzzy.prepare(a.name),
comment: Fuzzy.prepare(a.comment),
entry: a
}))
function fuzzyQuery(search: string): var { // Idk why list<DesktopEntry> doesn't work
return Fuzzy.go(search, preppedApps, {
all: true,
keys: ["name", "comment"],
scoreFn: r => r[0].score > 0 ? r[0].score * 0.9 + r[1].score * 0.1 : 0
}).map(r => r.obj.entry);
}
function launch(entry: DesktopEntry): void {
launchProc.entry = entry;
launchProc.startDetached();
}
Process {
id: launchProc
property DesktopEntry entry
command: ["app2unit", "--", `${entry?.id}.desktop`]
}
}

View File

@@ -0,0 +1,25 @@
pragma Singleton
import Quickshell
import Quickshell.Services.Pipewire
Singleton {
id: root
readonly property PwNode sink: Pipewire.defaultAudioSink
readonly property PwNode source: Pipewire.defaultAudioSource
readonly property bool muted: sink?.audio?.muted ?? false
readonly property real volume: sink?.audio?.volume ?? 0
function setVolume(volume: real): void {
if (sink?.ready && sink?.audio) {
sink.audio.muted = false;
sink.audio.volume = volume;
}
}
PwObjectTracker {
objects: [Pipewire.defaultAudioSink, Pipewire.defaultAudioSource]
}
}

View File

@@ -0,0 +1,22 @@
pragma Singleton
import Quickshell
import Quickshell.Io
Singleton {
id: root
property real bpm
Process {
running: true
command: ["/usr/lib/caelestia/beat_detector", "--no-log", "--no-stats", "--no-visual"]
stdout: SplitParser {
onRead: data => {
const match = data.match(/BPM: ([0-9]+\.[0-9])/);
if (match)
root.bpm = parseFloat(match[1]);
}
}
}
}

View File

@@ -0,0 +1,96 @@
pragma Singleton
import Quickshell
import Quickshell.Io
import QtQuick
Singleton {
id: root
property bool powered
property bool discovering
readonly property list<Device> devices: []
Process {
running: true
command: ["bluetoothctl"]
stdout: SplitParser {
onRead: {
getInfo.running = true;
getDevices.running = true;
}
}
}
Process {
id: getInfo
running: true
command: ["bluetoothctl", "show"]
stdout: StdioCollector {
onStreamFinished: {
root.powered = text.includes("Powered: yes");
root.discovering = text.includes("Discovering: yes");
}
}
}
Process {
id: getDevices
running: true
command: ["fish", "-c", `
for a in (bluetoothctl devices)
if string match -q 'Device *' $a
bluetoothctl info $addr (string split ' ' $a)[2]
echo
end
end`]
stdout: StdioCollector {
onStreamFinished: {
const devices = text.trim().split("\n\n").map(d => ({
name: d.match(/Name: (.*)/)[1],
alias: d.match(/Alias: (.*)/)[1],
address: d.match(/Device ([0-9A-Z:]{17})/)[1],
icon: d.match(/Icon: (.*)/)[1],
connected: d.includes("Connected: yes"),
paired: d.includes("Paired: yes"),
trusted: d.includes("Trusted: yes")
}));
const rDevices = root.devices;
const destroyed = rDevices.filter(rd => !devices.find(d => d.address === rd.address));
for (const device of destroyed)
rDevices.splice(rDevices.indexOf(device), 1).forEach(d => d.destroy());
for (const device of devices) {
const match = rDevices.find(d => d.address === device.address);
if (match) {
match.lastIpcObject = device;
} else {
rDevices.push(deviceComp.createObject(root, {
lastIpcObject: device
}));
}
}
}
}
}
component Device: QtObject {
required property var lastIpcObject
readonly property string name: lastIpcObject.name
readonly property string alias: lastIpcObject.alias
readonly property string address: lastIpcObject.address
readonly property string icon: lastIpcObject.icon
readonly property bool connected: lastIpcObject.connected
readonly property bool paired: lastIpcObject.paired
readonly property bool trusted: lastIpcObject.trusted
}
Component {
id: deviceComp
Device {}
}
}

View File

@@ -0,0 +1,111 @@
pragma Singleton
pragma ComponentBehavior: Bound
import "root:/widgets"
import Quickshell
import Quickshell.Io
import QtQuick
Singleton {
id: root
property list<var> ddcMonitors: []
readonly property list<Monitor> monitors: variants.instances
function getMonitorForScreen(screen: ShellScreen): var {
return monitors.find(m => m.modelData === screen);
}
function increaseBrightness(): void {
const focusedName = Hyprland.focusedMonitor.name;
const monitor = monitors.find(m => focusedName === m.modelData.name);
if (monitor)
monitor.setBrightness(monitor.brightness + 0.1);
}
function decreaseBrightness(): void {
const focusedName = Hyprland.focusedMonitor.name;
const monitor = monitors.find(m => focusedName === m.modelData.name);
if (monitor)
monitor.setBrightness(monitor.brightness - 0.1);
}
reloadableId: "brightness"
onMonitorsChanged: {
ddcMonitors = [];
ddcProc.running = true;
}
Variants {
id: variants
model: Quickshell.screens
Monitor {}
}
Process {
id: ddcProc
command: ["ddcutil", "detect", "--brief"]
stdout: StdioCollector {
onStreamFinished: root.ddcMonitors = text.trim().split("\n\n").filter(d => d.startsWith("Display ")).map(d => ({
model: d.match(/Monitor:.*:(.*):.*/)[1],
busNum: d.match(/I2C bus:[ ]*\/dev\/i2c-([0-9]+)/)[1]
}))
}
}
Process {
id: setProc
}
CustomShortcut {
name: "brightnessUp"
onPressed: root.increaseBrightness()
}
CustomShortcut {
name: "brightnessDown"
onPressed: root.decreaseBrightness()
}
component Monitor: QtObject {
id: monitor
required property ShellScreen modelData
readonly property bool isDdc: root.ddcMonitors.some(m => m.model === modelData.model)
readonly property string busNum: root.ddcMonitors.find(m => m.model === modelData.model)?.busNum ?? ""
property real brightness
readonly property Process initProc: Process {
stdout: StdioCollector {
onStreamFinished: {
const [, , , current, max] = text.split(" ");
monitor.brightness = parseInt(current) / parseInt(max);
}
}
}
function setBrightness(value: real): void {
value = Math.max(0, Math.min(1, value));
const rounded = Math.round(value * 100);
if (Math.round(brightness * 100) === rounded)
return;
brightness = value;
setProc.command = isDdc ? ["ddcutil", "-b", busNum, "setvcp", "10", rounded] : ["brightnessctl", "s", `${rounded}%`];
setProc.startDetached();
}
onBusNumChanged: {
initProc.command = isDdc ? ["ddcutil", "-b", busNum, "getvcp", "10", "--brief"] : ["sh", "-c", `echo "a b c $(brightnessctl g) $(brightnessctl m)"`];
initProc.running = true;
}
Component.onCompleted: {
initProc.command = isDdc ? ["ddcutil", "-b", busNum, "getvcp", "10", "--brief"] : ["sh", "-c", `echo "a b c $(brightnessctl g) $(brightnessctl m)"`];
initProc.running = true;
}
}
}

View File

@@ -0,0 +1,19 @@
pragma Singleton
import "root:/config"
import Quickshell
import Quickshell.Io
Singleton {
id: root
property list<int> values
Process {
running: true
command: ["sh", "-c", `printf '[general]\nframerate=60\nbars=${Config.dashboard.visualiserBars}\n[output]\nchannels=mono\nmethod=raw\nraw_target=/dev/stdout\ndata_format=ascii\nascii_max_range=100' | cava -p /dev/stdin`]
stdout: SplitParser {
onRead: data => root.values = data.slice(0, -1).split(";").map(v => parseInt(v, 10))
}
}
}

View File

@@ -0,0 +1,143 @@
pragma Singleton
import "root:/config"
import "root:/utils"
import Quickshell
import Quickshell.Io
import QtQuick
Singleton {
id: root
readonly property list<string> colourNames: ["rosewater", "flamingo", "pink", "mauve", "red", "maroon", "peach", "yellow", "green", "teal", "sky", "sapphire", "blue", "lavender"]
property bool showPreview
property bool endPreviewOnNextChange
property bool light
readonly property Colours palette: showPreview ? preview : current
readonly property Colours current: Colours {}
readonly property Colours preview: Colours {}
readonly property Transparency transparency: Transparency {}
function alpha(c: color, layer: bool): color {
if (!transparency.enabled)
return c;
c = Qt.rgba(c.r, c.g, c.b, layer ? transparency.layers : transparency.base);
if (layer)
c.hsvValue = Math.max(0, Math.min(1, c.hslLightness + (light ? -0.2 : 0.2))); // TODO: edit based on colours (hue or smth)
return c;
}
function on(c: color): color {
if (c.hslLightness < 0.5)
return Qt.hsla(c.hslHue, c.hslSaturation, 0.9, 1);
return Qt.hsla(c.hslHue, c.hslSaturation, 0.1, 1);
}
function load(data: string, isPreview: bool): void {
const colours = isPreview ? preview : current;
const scheme = JSON.parse(data);
light = scheme.mode === "light";
for (const [name, colour] of Object.entries(scheme.colours)) {
const propName = colourNames.includes(name) ? name : `m3${name}`;
if (colours.hasOwnProperty(propName))
colours[propName] = `#${colour}`;
}
if (!isPreview || (isPreview && endPreviewOnNextChange)) {
showPreview = false;
endPreviewOnNextChange = false;
}
}
function setMode(mode: string): void {
Quickshell.execDetached(["caelestia", "scheme", "set", "--notify", "-m", mode]);
}
FileView {
path: `${Paths.state}/scheme.json`
watchChanges: true
onFileChanged: reload()
onLoaded: root.load(text(), false)
}
component Transparency: QtObject {
readonly property bool enabled: false
readonly property real base: 0.78
readonly property real layers: 0.58
}
component Colours: QtObject {
property color m3primary_paletteKeyColor: "#7870AB"
property color m3secondary_paletteKeyColor: "#78748A"
property color m3tertiary_paletteKeyColor: "#976A7D"
property color m3neutral_paletteKeyColor: "#79767D"
property color m3neutral_variant_paletteKeyColor: "#797680"
property color m3background: "#141318"
property color m3onBackground: "#E5E1E9"
property color m3surface: "#141318"
property color m3surfaceDim: "#141318"
property color m3surfaceBright: "#3A383E"
property color m3surfaceContainerLowest: "#0E0D13"
property color m3surfaceContainerLow: "#1C1B20"
property color m3surfaceContainer: "#201F25"
property color m3surfaceContainerHigh: "#2B292F"
property color m3surfaceContainerHighest: "#35343A"
property color m3onSurface: "#E5E1E9"
property color m3surfaceVariant: "#48454E"
property color m3onSurfaceVariant: "#C9C5D0"
property color m3inverseSurface: "#E5E1E9"
property color m3inverseOnSurface: "#312F36"
property color m3outline: "#938F99"
property color m3outlineVariant: "#48454E"
property color m3shadow: "#000000"
property color m3scrim: "#000000"
property color m3surfaceTint: "#C8BFFF"
property color m3primary: "#C8BFFF"
property color m3onPrimary: "#30285F"
property color m3primaryContainer: "#473F77"
property color m3onPrimaryContainer: "#E5DEFF"
property color m3inversePrimary: "#5F5791"
property color m3secondary: "#C9C3DC"
property color m3onSecondary: "#312E41"
property color m3secondaryContainer: "#484459"
property color m3onSecondaryContainer: "#E5DFF9"
property color m3tertiary: "#ECB8CD"
property color m3onTertiary: "#482536"
property color m3tertiaryContainer: "#B38397"
property color m3onTertiaryContainer: "#000000"
property color m3error: "#EA8DC1"
property color m3onError: "#690005"
property color m3errorContainer: "#93000A"
property color m3onErrorContainer: "#FFDAD6"
property color m3primaryFixed: "#E5DEFF"
property color m3primaryFixedDim: "#C8BFFF"
property color m3onPrimaryFixed: "#1B1149"
property color m3onPrimaryFixedVariant: "#473F77"
property color m3secondaryFixed: "#E5DFF9"
property color m3secondaryFixedDim: "#C9C3DC"
property color m3onSecondaryFixed: "#1C192B"
property color m3onSecondaryFixedVariant: "#484459"
property color m3tertiaryFixed: "#FFD8E7"
property color m3tertiaryFixedDim: "#ECB8CD"
property color m3onTertiaryFixed: "#301121"
property color m3onTertiaryFixedVariant: "#613B4C"
property color rosewater: "#B8C4FF"
property color flamingo: "#DBB9F8"
property color pink: "#F3B3E3"
property color mauve: "#D0BDFE"
property color red: "#F8B3D1"
property color maroon: "#F6B2DA"
property color peach: "#E4B7F4"
property color yellow: "#C3C0FF"
property color green: "#ADC6FF"
property color teal: "#D4BBFC"
property color sky: "#CBBEFF"
property color sapphire: "#BDC2FF"
property color blue: "#C7BFFF"
property color lavender: "#EAB5ED"
}
}

View File

@@ -0,0 +1,113 @@
pragma Singleton
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import QtQuick
Singleton {
id: root
readonly property list<Client> clients: []
readonly property var workspaces: Hyprland.workspaces
readonly property var monitors: Hyprland.monitors
property Client activeClient: null
readonly property HyprlandWorkspace activeWorkspace: focusedMonitor?.activeWorkspace ?? null
readonly property HyprlandMonitor focusedMonitor: Hyprland.focusedMonitor
readonly property int activeWsId: activeWorkspace?.id ?? 1
property point cursorPos
function reload() {
Hyprland.refreshWorkspaces();
Hyprland.refreshMonitors();
getClients.running = true;
getActiveClient.running = true;
}
function dispatch(request: string): void {
Hyprland.dispatch(request);
}
Component.onCompleted: reload()
Connections {
target: Hyprland
function onRawEvent(event: HyprlandEvent): void {
if (!event.name.endsWith("v2"))
root.reload();
}
}
Process {
id: getClients
command: ["hyprctl", "-j", "clients"]
stdout: StdioCollector {
onStreamFinished: {
const clients = JSON.parse(text);
const rClients = root.clients;
const destroyed = rClients.filter(rc => !clients.find(c => c.address === rc.address));
for (const client of destroyed)
rClients.splice(rClients.indexOf(client), 1).forEach(c => c.destroy());
for (const client of clients) {
const match = rClients.find(c => c.address === client.address);
if (match) {
match.lastIpcObject = client;
} else {
rClients.push(clientComp.createObject(root, {
lastIpcObject: client
}));
}
}
}
}
}
Process {
id: getActiveClient
command: ["hyprctl", "-j", "activewindow"]
stdout: StdioCollector {
onStreamFinished: {
const client = JSON.parse(text);
const rClient = root.activeClient;
if (client.address) {
if (rClient)
rClient.lastIpcObject = client;
else
root.activeClient = clientComp.createObject(root, {
lastIpcObject: client
});
} else if (rClient) {
rClient.destroy();
root.activeClient = null;
}
}
}
}
component Client: QtObject {
required property var lastIpcObject
readonly property string address: lastIpcObject.address
readonly property string wmClass: lastIpcObject.class
readonly property string title: lastIpcObject.title
readonly property string initialClass: lastIpcObject.initialClass
readonly property string initialTitle: lastIpcObject.initialTitle
readonly property int x: lastIpcObject.at[0]
readonly property int y: lastIpcObject.at[1]
readonly property int width: lastIpcObject.size[0]
readonly property int height: lastIpcObject.size[1]
readonly property HyprlandWorkspace workspace: Hyprland.workspaces.values.find(w => w.id === lastIpcObject.workspace.id) ?? null
readonly property bool floating: lastIpcObject.floating
readonly property bool fullscreen: lastIpcObject.fullscreen
readonly property int pid: lastIpcObject.pid
readonly property int focusHistoryId: lastIpcObject.focusHistoryID
}
Component {
id: clientComp
Client {}
}
}

View File

@@ -0,0 +1,77 @@
pragma Singleton
import Quickshell
import Quickshell.Io
import QtQuick
Singleton {
id: root
readonly property list<AccessPoint> networks: []
readonly property AccessPoint active: networks.find(n => n.active) ?? null
reloadableId: "network"
Process {
running: true
command: ["nmcli", "m"]
stdout: SplitParser {
onRead: getNetworks.running = true
}
}
Process {
id: getNetworks
running: true
command: ["nmcli", "-g", "ACTIVE,SIGNAL,FREQ,SSID,BSSID", "d", "w"]
stdout: StdioCollector {
onStreamFinished: {
const PLACEHOLDER = "STRINGWHICHHOPEFULLYWONTBEUSED";
const rep = new RegExp("\\\\:", "g");
const rep2 = new RegExp(PLACEHOLDER, "g");
const networks = text.trim().split("\n").map(n => {
const net = n.replace(rep, PLACEHOLDER).split(":");
return {
active: net[0] === "yes",
strength: parseInt(net[1]),
frequency: parseInt(net[2]),
ssid: net[3],
bssid: net[4].replace(rep2, ":")
};
});
const rNetworks = root.networks;
const destroyed = rNetworks.filter(rn => !networks.find(n => n.frequency === rn.frequency && n.ssid === rn.ssid && n.bssid === rn.bssid));
for (const network of destroyed)
rNetworks.splice(rNetworks.indexOf(network), 1).forEach(n => n.destroy());
for (const network of networks) {
const match = rNetworks.find(n => n.frequency === network.frequency && n.ssid === network.ssid && n.bssid === network.bssid);
if (match) {
match.lastIpcObject = network;
} else {
rNetworks.push(apComp.createObject(root, {
lastIpcObject: network
}));
}
}
}
}
}
component AccessPoint: QtObject {
required property var lastIpcObject
readonly property string ssid: lastIpcObject.ssid
readonly property string bssid: lastIpcObject.bssid
readonly property int strength: lastIpcObject.strength
readonly property int frequency: lastIpcObject.frequency
readonly property bool active: lastIpcObject.active
}
Component {
id: apComp
AccessPoint {}
}
}

View File

@@ -0,0 +1,108 @@
pragma Singleton
pragma ComponentBehavior: Bound
import "root:/widgets"
import "root:/config"
import Quickshell
import Quickshell.Io
import Quickshell.Services.Notifications
import QtQuick
Singleton {
id: root
readonly property list<Notif> list: []
readonly property list<Notif> popups: list.filter(n => n.popup)
NotificationServer {
id: server
keepOnReload: false
actionsSupported: true
bodyHyperlinksSupported: true
bodyImagesSupported: true
bodyMarkupSupported: true
imageSupported: true
onNotification: notif => {
notif.tracked = true;
root.list.push(notifComp.createObject(root, {
popup: true,
notification: notif
}));
}
}
CustomShortcut {
name: "clearNotifs"
description: "Clear all notifications"
onPressed: {
for (const notif of root.list)
notif.popup = false;
}
}
IpcHandler {
target: "notifs"
function clear(): void {
for (const notif of root.list)
notif.popup = false;
}
}
component Notif: QtObject {
id: notif
property bool popup
readonly property date time: new Date()
readonly property string timeStr: {
const diff = Time.date.getTime() - time.getTime();
const m = Math.floor(diff / 60000);
const h = Math.floor(m / 60);
if (h < 1 && m < 1)
return "now";
if (h < 1)
return `${m}m`;
return `${h}h`;
}
required property Notification notification
readonly property string summary: notification.summary
readonly property string body: notification.body
readonly property string appIcon: notification.appIcon
readonly property string appName: notification.appName
readonly property string image: notification.image
readonly property var urgency: notification.urgency // Idk why NotificationUrgency doesn't work
readonly property list<NotificationAction> actions: notification.actions
readonly property Timer timer: Timer {
running: true
interval: notif.notification.expireTimeout > 0 ? notif.notification.expireTimeout : Config.notifs.defaultExpireTimeout
onTriggered: {
if (Config.notifs.expire)
notif.popup = false;
}
}
readonly property Connections conn: Connections {
target: notif.notification.Retainable
function onDropped(): void {
root.list.splice(root.list.indexOf(notif), 1);
}
function onAboutToDestroy(): void {
notif.destroy();
}
}
}
Component {
id: notifComp
Notif {}
}
}

View File

@@ -0,0 +1,97 @@
pragma Singleton
import "root:/widgets"
import Quickshell
import Quickshell.Io
import Quickshell.Services.Mpris
Singleton {
id: root
readonly property list<MprisPlayer> list: Mpris.players.values
readonly property MprisPlayer active: manualActive ?? list.find(p => p.identity === "Spotify") ?? list[0] ?? null
property MprisPlayer manualActive
CustomShortcut {
name: "mediaToggle"
description: "Toggle media playback"
onPressed: {
const active = root.active;
if (active && active.canTogglePlaying)
active.togglePlaying();
}
}
CustomShortcut {
name: "mediaPrev"
description: "Previous track"
onPressed: {
const active = root.active;
if (active && active.canGoPrevious)
active.previous();
}
}
CustomShortcut {
name: "mediaNext"
description: "Next track"
onPressed: {
const active = root.active;
if (active && active.canGoNext)
active.next();
}
}
CustomShortcut {
name: "mediaStop"
description: "Stop media playback"
onPressed: root.active?.stop()
}
IpcHandler {
target: "mpris"
function getActive(prop: string): string {
const active = root.active;
return active ? active[prop] ?? "Invalid property" : "No active player";
}
function list(): string {
return root.list.map(p => p.identity).join("\n");
}
function play(): void {
const active = root.active;
if (active?.canPlay)
active.play();
}
function pause(): void {
const active = root.active;
if (active?.canPause)
active.pause();
}
function playPause(): void {
const active = root.active;
if (active?.canTogglePlaying)
active.togglePlaying();
}
function previous(): void {
const active = root.active;
if (active?.canGoPrevious)
active.previous();
}
function next(): void {
const active = root.active;
if (active?.canGoNext)
active.next();
}
function stop(): void {
root.active?.stop();
}
}
}

View File

@@ -0,0 +1,196 @@
pragma Singleton
import Quickshell
import Quickshell.Io
import QtQuick
Singleton {
id: root
property real cpuPerc
property real cpuTemp
property real gpuPerc
property real gpuTemp
property int memUsed
property int memTotal
readonly property real memPerc: memTotal > 0 ? memUsed / memTotal : 0
property int storageUsed
property int storageTotal
property real storagePerc: storageTotal > 0 ? storageUsed / storageTotal : 0
property int lastCpuIdle
property int lastCpuTotal
function formatKib(kib: int): var {
const mib = 1024;
const gib = 1024 ** 2;
const tib = 1024 ** 3;
if (kib >= tib)
return {
value: kib / tib,
unit: "TiB"
};
if (kib >= gib)
return {
value: kib / gib,
unit: "GiB"
};
if (kib >= mib)
return {
value: kib / mib,
unit: "MiB"
};
return {
value: kib,
unit: "KiB"
};
}
Timer {
running: true
interval: 3000
repeat: true
onTriggered: {
stat.reload();
meminfo.reload();
storage.running = true;
cpuTemp.running = true;
gpuUsage.running = true;
gpuTemp.running = true;
}
}
FileView {
id: stat
path: "/proc/stat"
onLoaded: {
const data = text().match(/^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/);
if (data) {
const stats = data.slice(1).map(n => parseInt(n, 10));
const total = stats.reduce((a, b) => a + b, 0);
const idle = stats[3];
const totalDiff = total - root.lastCpuTotal;
const idleDiff = idle - root.lastCpuIdle;
root.cpuPerc = totalDiff > 0 ? (1 - idleDiff / totalDiff) : 0;
root.lastCpuTotal = total;
root.lastCpuIdle = idle;
}
}
}
FileView {
id: meminfo
path: "/proc/meminfo"
onLoaded: {
const data = text();
root.memTotal = parseInt(data.match(/MemTotal: *(\d+)/)[1], 10) || 1;
root.memUsed = (root.memTotal - parseInt(data.match(/MemAvailable: *(\d+)/)[1], 10)) || 0;
}
}
Process {
id: storage
running: true
command: ["sh", "-c", "df | grep '^/dev/' | awk '{print $1, $3, $4}'"]
stdout: StdioCollector {
onStreamFinished: {
const deviceMap = new Map();
for (const line of text.trim().split("\n")) {
if (line.trim() === "")
continue;
const parts = line.trim().split(/\s+/);
if (parts.length >= 3) {
const device = parts[0];
const used = parseInt(parts[1], 10) || 0;
const avail = parseInt(parts[2], 10) || 0;
// Only keep the entry with the largest total space for each device
if (!deviceMap.has(device) || (used + avail) > (deviceMap.get(device).used + deviceMap.get(device).avail)) {
deviceMap.set(device, {
used: used,
avail: avail
});
}
}
}
let totalUsed = 0;
let totalAvail = 0;
for (const [device, stats] of deviceMap) {
totalUsed += stats.used;
totalAvail += stats.avail;
}
root.storageUsed = totalUsed;
root.storageTotal = totalUsed + totalAvail;
}
}
}
Process {
id: cpuTemp
running: true
command: ["sh", "-c", "cat /sys/class/thermal/thermal_zone*/temp"]
stdout: StdioCollector {
onStreamFinished: {
const temps = text.trim().split(" ");
const sum = temps.reduce((acc, d) => acc + parseInt(d, 10), 0);
root.cpuTemp = sum / temps.length / 1000;
}
}
}
Process {
id: gpuUsage
running: true
command: ["sh", "-c", "cat /sys/class/drm/card*/device/gpu_busy_percent"]
stdout: StdioCollector {
onStreamFinished: {
const percs = text.trim().split("\n");
const sum = percs.reduce((acc, d) => acc + parseInt(d, 10), 0);
root.gpuPerc = sum / percs.length / 100;
}
}
}
Process {
id: gpuTemp
running: true
command: ["sensors"]
stdout: StdioCollector {
onStreamFinished: {
let eligible = false;
let sum = 0;
let count = 0;
for (const line of text.trim().split("\n")) {
if (line === "Adapter: PCI adapter")
eligible = true;
else if (line === "")
eligible = false;
else if (eligible) {
const match = line.match(/^(temp[0-9]+|GPU core|edge)+:\s+\+([0-9]+\.[0-9]+)°C/);
if (match) {
sum += parseFloat(match[2]);
count++;
}
}
}
root.gpuTemp = count > 0 ? sum / count : 0;
}
}
}
}

View File

@@ -0,0 +1,20 @@
pragma Singleton
import Quickshell
Singleton {
property alias enabled: clock.enabled
readonly property date date: clock.date
readonly property int hours: clock.hours
readonly property int minutes: clock.minutes
readonly property int seconds: clock.seconds
function format(fmt: string): string {
return Qt.formatDateTime(clock.date, fmt);
}
SystemClock {
id: clock
precision: SystemClock.Seconds
}
}

View File

@@ -0,0 +1,12 @@
pragma Singleton
import Quickshell
Singleton {
property var screens: ({})
property var panels: ({})
function getForActive(): PersistentProperties {
return Object.entries(screens).find(s => s[0].slice(s[0].indexOf('"') + 1, s[0].lastIndexOf('"')) === Hyprland.focusedMonitor.name)[1];
}
}

View File

@@ -0,0 +1,117 @@
pragma Singleton
import "root:/utils/scripts/fuzzysort.js" as Fuzzy
import "root:/utils"
import Quickshell
import Quickshell.Io
import QtQuick
Singleton {
id: root
readonly property string currentNamePath: `${Paths.state}/wallpaper/path.txt`.slice(7)
readonly property string path: `${Paths.pictures}/Wallpapers`.slice(7)
readonly property list<string> extensions: ["jpg", "jpeg", "png", "webp", "tif", "tiff"]
readonly property list<Wallpaper> list: wallpapers.instances
property bool showPreview: false
readonly property string current: showPreview ? previewPath : actualCurrent
property string previewPath
property string actualCurrent
readonly property list<var> preppedWalls: list.map(w => ({
name: Fuzzy.prepare(w.name),
path: Fuzzy.prepare(w.path),
wall: w
}))
function fuzzyQuery(search: string): var {
return Fuzzy.go(search, preppedWalls, {
all: true,
keys: ["name", "path"],
scoreFn: r => r[0].score * 0.9 + r[1].score * 0.1
}).map(r => r.obj.wall);
}
function setWallpaper(path: string): void {
actualCurrent = path;
setWall.path = path;
setWall.startDetached();
}
function preview(path: string): void {
previewPath = path;
showPreview = true;
getPreviewColoursProc.running = true;
}
function stopPreview(): void {
showPreview = false;
Colours.endPreviewOnNextChange = true;
}
reloadableId: "wallpapers"
IpcHandler {
target: "wallpaper"
function get(): string {
return root.actualCurrent;
}
function set(path: string): void {
root.setWallpaper(path);
}
function list(): string {
return root.list.map(w => w.path).join("\n");
}
}
FileView {
path: root.currentNamePath
watchChanges: true
onFileChanged: reload()
onLoaded: root.actualCurrent = text().trim()
}
Process {
id: getPreviewColoursProc
command: ["caelestia", "wallpaper", "-p", root.previewPath]
stdout: StdioCollector {
onStreamFinished: {
Colours.load(text, true);
Colours.showPreview = true;
}
}
}
Process {
id: setWall
property string path
command: ["caelestia", "wallpaper", "-f", path]
}
Process {
running: true
command: ["find", root.path, "-type", "d", "-path", '*/.*', "-prune", "-o", "-not", "-name", '.*', "-type", "f", "-print"]
stdout: StdioCollector {
onStreamFinished: wallpapers.model = text.trim().split("\n").filter(w => root.extensions.includes(w.slice(w.lastIndexOf(".") + 1))).sort()
}
}
Variants {
id: wallpapers
Wallpaper {}
}
component Wallpaper: QtObject {
required property string modelData
readonly property string path: modelData
readonly property string name: path.slice(path.lastIndexOf("/") + 1, path.lastIndexOf("."))
}
}

View File

@@ -0,0 +1,49 @@
pragma Singleton
import "root:/config"
import "root:/utils"
import Quickshell
import Quickshell.Io
import QtQuick
Singleton {
id: root
property string loc
property string icon
property string description
property real temperature
function reload(): void {
if (Config.dashboard.weatherLocation)
loc = Config.dashboard.weatherLocation;
else
ipProc.running = true;
}
onLocChanged: wttrProc.running = true
Component.onCompleted: reload()
Process {
id: ipProc
command: ["curl", "ipinfo.io"]
stdout: StdioCollector {
onStreamFinished: root.loc = JSON.parse(text).loc
}
}
Process {
id: wttrProc
command: ["curl", `https://wttr.in/${root.loc}?format=j1`]
stdout: StdioCollector {
onStreamFinished: {
const json = JSON.parse(text).current_condition[0];
root.icon = Icons.getWeatherIcon(json.weatherCode);
root.description = json.weatherDesc[0].value;
root.temperature = parseFloat(json.temp_C);
}
}
}
}