Author SHA1 Message Date
zach 2f6847ce0e Merge branch 'main' into fix/lockscreen-font
C++ / fmt (pull_request) Successful in 3s
JS/TS / fmt (pull_request) Successful in 14s
JS/TS / lint (pull_request) Successful in 20s
Python / static (pull_request) Successful in 58s
Rust / fmt (pull_request) Successful in 55s
C++ / build (pull_request) Successful in 2m31s
Rust / build (pull_request) Successful in 2m16s
Rust / clippy (pull_request) Successful in 1m33s
Python / verify (pull_request) Successful in 2m53s
C++ / clang-tidy (pull_request) Successful in 4m5s
2026-08-31 00:13:16 +02:00
zach e5e52802de test aur 2026-08-30 23:49:53 +02:00
zach 42f02f4dd2 test aur 2026-08-30 23:33:50 +02:00
zach 9df3a64815 fix: lowercase battery device state 2026-08-30 22:20:39 +02:00
Inorishio 9370284c6b changed old appearance vars for the lockscreen + warning hunting for the polkit
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 14s
JS/TS / lint (pull_request) Successful in 16s
Python / static (pull_request) Successful in 1m14s
Rust / fmt (pull_request) Successful in 1m8s
Rust / build (pull_request) Successful in 2m14s
C++ / build (pull_request) Successful in 2m36s
Rust / clippy (pull_request) Successful in 1m15s
Python / verify (pull_request) Successful in 3m19s
C++ / clang-tidy (pull_request) Successful in 4m1s
2026-08-30 16:35:20 +02:00
zach 5598f4f435 README.md 2026-08-23 17:01:27 +02:00
zach 7e02c87b91 README.md 2026-08-23 17:00:41 +02:00
zach d3e61efd86 Merge pull request 'Feat: Dashboard media widget update + anim tokens restructure' (#151) from feat/dashboard-media-widget into main
Reviewed-on: #151
2026-08-19 11:26:23 +02:00
zach de95b6c8f9 fix rounding tokens
C++ / fmt (pull_request) Successful in 7s
JS/TS / fmt (pull_request) Successful in 12s
JS/TS / lint (pull_request) Successful in 16s
Python / static (pull_request) Successful in 53s
Rust / fmt (pull_request) Successful in 39s
Rust / build (pull_request) Successful in 1m57s
C++ / build (pull_request) Successful in 3m51s
Rust / clippy (pull_request) Successful in 1m39s
Python / verify (pull_request) Successful in 3m4s
C++ / clang-tidy (pull_request) Successful in 5m25s
2026-08-19 00:38:41 +02:00
zach 4ff4afa279 Merge branch 'main' into feat/dashboard-media-widget
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 18s
JS/TS / lint (pull_request) Successful in 17s
Python / static (pull_request) Successful in 58s
Rust / fmt (pull_request) Successful in 1m4s
C++ / build (pull_request) Successful in 2m39s
Rust / build (pull_request) Successful in 2m22s
Rust / clippy (pull_request) Successful in 1m48s
Python / verify (pull_request) Successful in 3m29s
C++ / clang-tidy (pull_request) Successful in 7m7s
2026-08-19 00:36:42 +02:00
zach 8a83181f11 modular quick toggles 2026-08-19 00:34:49 +02:00
11 changed files with 261 additions and 91 deletions
@@ -40,7 +40,7 @@ Item {
anchors.centerIn: parent anchors.centerIn: parent
sourceComponent: BatteryIcon { sourceComponent: BatteryIcon {
devState: Battery.deviceStateString devState: Battery.deviceStateString.toLowerCase()
percentage: Battery.currentPerc percentage: Battery.currentPerc
} }
} }
+6 -6
View File
@@ -29,7 +29,7 @@ ColumnLayout {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
color: Colors.palette.m3secondary color: Colors.palette.m3secondary
font.bold: true font.bold: true
font.family: Appearance.font.family.clock font.family: Config.appearance.font.family.clock
font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale) font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale)
text: Time.hourStr text: Time.hourStr
} }
@@ -38,7 +38,7 @@ ColumnLayout {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
color: Colors.palette.m3primary color: Colors.palette.m3primary
font.bold: true font.bold: true
font.family: Appearance.font.family.clock font.family: Config.appearance.font.family.clock
font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale) font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale)
text: ":" text: ":"
} }
@@ -47,7 +47,7 @@ ColumnLayout {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
color: Colors.palette.m3secondary color: Colors.palette.m3secondary
font.bold: true font.bold: true
font.family: Appearance.font.family.clock font.family: Config.appearance.font.family.clock
font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale) font.pointSize: Math.floor(Tokens.font.size.extraLarge * 3 * root.centerScale)
text: Time.minuteStr text: Time.minuteStr
} }
@@ -58,7 +58,7 @@ ColumnLayout {
Layout.topMargin: -Tokens.padding.large * 2 Layout.topMargin: -Tokens.padding.large * 2
color: Colors.palette.m3tertiary color: Colors.palette.m3tertiary
font.bold: true font.bold: true
font.family: Appearance.font.family.mono font.family: Config.appearance.font.family.mono
font.pointSize: Math.floor(Tokens.font.size.extraLarge * root.centerScale) font.pointSize: Math.floor(Tokens.font.size.extraLarge * root.centerScale)
text: Time.format("dddd, d MMMM yyyy") text: Time.format("dddd, d MMMM yyyy")
} }
@@ -226,7 +226,7 @@ ColumnLayout {
anchors.right: parent.right anchors.right: parent.right
animateProp: "opacity" animateProp: "opacity"
color: Colors.palette.m3onSurfaceVariant color: Colors.palette.m3onSurfaceVariant
font.family: Appearance.font.family.mono font.family: Config.appearance.font.family.mono
horizontalAlignment: Qt.AlignHCenter horizontalAlignment: Qt.AlignHCenter
lineHeight: 1.2 lineHeight: 1.2
opacity: shouldBeVisible && !message.msg ? 1 : 0 opacity: shouldBeVisible && !message.msg ? 1 : 0
@@ -295,7 +295,7 @@ ColumnLayout {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
color: Colors.palette.m3error color: Colors.palette.m3error
font.family: Appearance.font.family.mono font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.small font.pointSize: Tokens.font.size.small
horizontalAlignment: Qt.AlignHCenter horizontalAlignment: Qt.AlignHCenter
opacity: 0 opacity: 0
+1 -1
View File
@@ -41,7 +41,7 @@ Item {
anchors.centerIn: parent anchors.centerIn: parent
animate: true animate: true
color: root.pam.passwd.active ? Colors.palette.m3secondary : Colors.palette.m3outline color: root.pam.passwd.active ? Colors.palette.m3secondary : Colors.palette.m3outline
font.family: Appearance.font.family.mono font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.normal font.pointSize: Tokens.font.size.normal
opacity: root.buffer ? 0 : 1 opacity: root.buffer ? 0 : 1
text: { text: {
+2 -2
View File
@@ -24,7 +24,7 @@ ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
color: Colors.palette.m3outline color: Colors.palette.m3outline
elide: Text.ElideRight elide: Text.ElideRight
font.family: Appearance.font.family.mono font.family: Config.appearance.font.family.mono
font.weight: 500 font.weight: 500
text: NotifServer.list.length > 0 ? qsTr("%1 notification%2").arg(NotifServer.list.length).arg(NotifServer.list.length === 1 ? "" : "s") : qsTr("Notifications") text: NotifServer.list.length > 0 ? qsTr("%1 notification%2").arg(NotifServer.list.length).arg(NotifServer.list.length === 1 ? "" : "s") : qsTr("Notifications")
} }
@@ -66,7 +66,7 @@ ColumnLayout {
CustomText { CustomText {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
color: Colors.palette.m3outlineVariant color: Colors.palette.m3outlineVariant
font.family: Appearance.font.family.mono font.family: Config.appearance.font.family.mono
font.pointSize: Tokens.font.size.large font.pointSize: Tokens.font.size.large
font.weight: 500 font.weight: 500
text: qsTr("No Notifications") text: qsTr("No Notifications")
@@ -1,101 +1,184 @@
import Quickshell.Bluetooth pragma ComponentBehavior: Bound
import Quickshell.Networking as QSNetwork
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell
import Quickshell.Bluetooth
import Quickshell.Networking as QSNetwork
import ZShell.Components import ZShell.Components
import qs.Components
import ZShell.Config import ZShell.Config
import qs.Components
import qs.Services
import qs.Helpers import qs.Helpers
import qs.Daemons import qs.Daemons
import qs.Services import qs.Modules.Settings
import qs.Modules.Bar.Popouts as BarPopouts
CustomRect { CustomRect {
id: root id: root
required property Item popouts readonly property bool needExtraRow: quickToggles.length > 6
required property var visibilities required property BarPopouts.Wrapper popouts
readonly property var quickToggles: {
const seenIds = new Set();
return Config.utilities.quickToggles.values.filter(item => {
if (!item.enabled)
return false;
if (seenIds.has(item.id)) {
return false;
}
if (item.id === "bluetooth" && !Bluetooth.defaultAdapter)
return false;
if (item.id === "wifi" && !QSNetwork.Networking.devices.values.some(n => n.type === QSNetwork.DeviceType.Wifi))
return false;
seenIds.add(item.id);
return true;
});
}
readonly property int splitIndex: Math.ceil(quickToggles.length / 2)
required property PersistentProperties visibilities
Layout.fillWidth: true
color: Colors.tPalette.m3surfaceContainer color: Colors.tPalette.m3surfaceContainer
implicitHeight: layout.implicitHeight + 18 * 2 implicitHeight: layout.implicitHeight + Tokens.padding.extraLargeIncreased
radius: Tokens.rounding.small radius: Tokens.rounding.small
ButtonRow { ColumnLayout {
id: layout id: layout
anchors.fill: parent anchors.fill: parent
anchors.margins: 18 anchors.margins: Tokens.padding.large
spacing: Tokens.spacing.extraSmall spacing: Tokens.spacing.medium
Toggle { CustomText {
checked: Network.wifiEnabled font.pointSize: Tokens.font.size.normal
icon: Network.wifiEnabled ? "wifi" : "wifi_off" text: qsTr("Quick Toggles")
visible: QSNetwork.Networking.devices.values.some(n => n.type === QSNetwork.DeviceType.Wifi)
onClicked: Network.toggleWifi()
} }
Toggle { QuickToggleRow {
id: toggle model: root.needExtraRow ? root.quickToggles.slice(0, root.splitIndex) : root.quickToggles
checked: !NotifServer.dnd
icon: NotifServer.dnd ? "notifications_off" : "notifications"
onClicked: NotifServer.dnd = !NotifServer.dnd
} }
Toggle { QuickToggleRow {
checked: !Audio.sourceMuted model: root.needExtraRow ? root.quickToggles.slice(root.splitIndex) : []
icon: Audio.sourceMuted ? "mic_off" : "mic" visible: root.needExtraRow
onClicked: {
const audio = Audio.source?.audio;
if (audio)
audio.muted = !audio.muted;
}
}
Toggle {
checked: !Audio.muted
icon: Audio.muted ? "volume_off" : "volume_up"
onClicked: {
const audio = Audio.sink?.audio;
if (audio)
audio.muted = !audio.muted;
}
}
Toggle {
checked: Bluetooth.defaultAdapter?.enabled ?? false
icon: Bluetooth.defaultAdapter?.enabled ? "bluetooth" : "bluetooth_disabled"
visible: Bluetooth.defaultAdapter ?? false
onClicked: {
const adapter = Bluetooth.defaultAdapter;
if (adapter)
adapter.enabled = !adapter.enabled;
}
}
Toggle {
checked: GameMode.enabled
icon: GameMode.enabled ? "videogame_asset" : "videogame_asset_off"
onClicked: GameMode.enabled = !GameMode.enabled
} }
} }
CustomShortcut { component QuickToggleRow: ButtonRow {
name: "toggle-dnd" property alias model: repeater.model
onPressed: { Layout.fillWidth: true
toggle.clicked(); spacing: Tokens.spacing.small
Repeater {
id: repeater
delegate: DelegateChooser {
role: "id"
DelegateChoice {
roleValue: "wifi"
delegate: Toggle {
checked: Network.wifiEnabled
icon: checked ? "wifi" : "wifi_off"
onClicked: Network.toggleWifi()
}
}
DelegateChoice {
roleValue: "bluetooth"
delegate: Toggle {
checked: Bluetooth.defaultAdapter?.enabled ?? false // qmllint disable unresolved-type
icon: "bluetooth"
onClicked: {
const adapter = Bluetooth.defaultAdapter; // qmllint disable unresolved-type
if (adapter)
adapter.enabled = !adapter.enabled;
}
}
}
DelegateChoice {
roleValue: "mic"
delegate: Toggle {
checked: !Audio.sourceMuted
icon: checked ? "mic" : "mic_off"
onClicked: {
const audio = Audio.source?.audio;
if (audio)
audio.muted = !audio.muted;
}
}
}
DelegateChoice {
roleValue: "audio"
delegate: Toggle {
checked: !Audio.muted
icon: checked ? "volume_up" : "volume_off"
onClicked: {
const audio = Audio.sink?.audio;
if (audio)
audio.muted = !audio.muted;
}
}
}
DelegateChoice {
roleValue: "gameMode"
delegate: Toggle {
checked: GameMode.enabled
icon: "gamepad"
onClicked: GameMode.enabled = !GameMode.enabled
}
}
DelegateChoice {
roleValue: "dnd"
delegate: Toggle {
checked: NotifServer.dnd
icon: "notifications_off"
onClicked: NotifServer.dnd = !NotifServer.dnd
}
}
DelegateChoice {
roleValue: "settings"
delegate: Toggle {
icon: "settings"
inactiveOnColor: Colors.palette.m3onSurfaceVariant
isToggle: false
onClicked: {
root.visibilities.sidebar = false;
root.visibilities.settings = true;
}
}
}
}
} }
} }
component Toggle: IconButton { component Toggle: IconButton {
fillWidth: true fillWidth: true
inactiveColor: Colors.layer(Colors.palette.m3surfaceContainerHighest, 2)
isRound: true isRound: true
isToggle: true isToggle: true
shapeMorph: true shapeMorph: true
@@ -1,13 +1,14 @@
import Quickshell import Quickshell
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import qs.Modules.Bar.Popouts as BarPopouts
import qs.Modules.Notifications.Sidebar.Utils.Cards import qs.Modules.Notifications.Sidebar.Utils.Cards
import ZShell.Config import ZShell.Config
Item { Item {
id: root id: root
required property Item popouts required property BarPopouts.Wrapper popouts
required property PersistentProperties props required property PersistentProperties props
required property var visibilities required property var visibilities
@@ -30,6 +31,7 @@ Item {
} }
Toggles { Toggles {
Layout.fillWidth: true
popouts: root.popouts popouts: root.popouts
visibilities: root.visibilities visibilities: root.visibilities
} }
@@ -1,15 +1,16 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import qs.Components
import ZShell.Config
import Quickshell
import QtQuick import QtQuick
import Quickshell
import ZShell.Config
import qs.Components
import qs.Modules.Bar.Popouts as BarPopouts
Item { Item {
id: root id: root
property real offsetScale: shouldBeActive ? 0 : 1 property real offsetScale: shouldBeActive ? 0 : 1
required property Item popouts required property BarPopouts.Wrapper popouts
readonly property PersistentProperties props: PersistentProperties { readonly property PersistentProperties props: PersistentProperties {
property string recordingConfirmDelete property string recordingConfirmDelete
property bool recordingListExpanded: false property bool recordingListExpanded: false
+2 -2
View File
@@ -130,7 +130,7 @@ Scope {
Layout.preferredWidth: Math.min(600, contentWidth) Layout.preferredWidth: Math.min(600, contentWidth)
font.bold: true font.bold: true
font.pointSize: 16 font.pointSize: 16
text: polkitAgent.flow?.message text: polkitAgent.flow?.message ?? ""
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
} }
@@ -168,7 +168,7 @@ Scope {
id: showPassCheckbox id: showPassCheckbox
Layout.alignment: Qt.AlignLeft Layout.alignment: Qt.AlignLeft
checked: polkitAgent.flow?.responseVisible checked: polkitAgent.flow?.responseVisible ?? false
text: "Show Password" text: "Show Password"
onCheckedChanged: { onCheckedChanged: {
@@ -9,6 +9,16 @@ import qs.Modules.Settings.Common
PageBase { PageBase {
id: root id: root
readonly property var builtinIcons: ({
audio: qsTr("Toggle audio"),
mic: qsTr("Toggle microphone"),
settings: qsTr("Open settings"),
dnd: qsTr("Toggle Do-Not-Disturb"),
gameMode: qsTr("Toggle game mode"),
bluetooth: qsTr("Toggle bluetooth"),
wifi: qsTr("Toggle Wi-Fi")
})
isSubPage: true isSubPage: true
title: qsTr("Sidebar") title: qsTr("Sidebar")
@@ -44,5 +54,59 @@ PageBase {
onMoved: v => Config.sidebar.dragThreshold = v onMoved: v => Config.sidebar.dragThreshold = v
} }
SectionHeader {
text: qsTr("Quick toggles")
}
ListEditor {
function labelFor(item: var): string {
const prettyName = root.builtinIcons[item.id];
if (prettyName)
return prettyName;
const label = item.id.replace(/([A-Z])/g, " $1");
return label.charAt(0).toUpperCase() + label.slice(1).toLowerCase();
}
function toggledFor(item: var): bool {
return item.enabled;
}
first: true
values: Config.utilities.quickToggles.values
z: 1
onItemMoved: (from, to) => Config.utilities.quickToggles.move(from, to)
onItemRemoved: index => Config.utilities.quickToggles.remove(index)
onItemToggled: (index, checked) => Config.utilities.quickToggles.at(index).enabled = checked
}
DialogSelectButton {
id: addItemContainer
acceptLabel: qsTr("Add")
enabled: Object.keys(model).length > 0
header: qsTr("Add new entry")
icon: "add"
label: qsTr("Add entry")
model: {
const present = new Set(Config.utilities.quickToggles.values.map(item => item.id));
return Object.keys(root.builtinIcons).filter(id => !present.has(id)).map(id => ({
id: id,
label: root.builtinIcons[id]
}));
}
rootParent: root.flickable
onAccepted: {
if (!selectedItem)
return;
Config.utilities.quickToggles.insert({
id: selectedItem,
enabled: true
});
}
}
} }
} }
+13
View File
@@ -1,5 +1,8 @@
#pragma once #pragma once
#include "configobject.hpp" #include "configobject.hpp"
#include "configlist.hpp"
#include <QVariantList> #include <QVariantList>
#include <QStringList> #include <QStringList>
#include <qqmlintegration.h> #include <qqmlintegration.h>
@@ -58,6 +61,16 @@ class Utilities : public ConfigObject {
CONFIG_SUBOBJECT(UtilitiesSizes, sizes) CONFIG_SUBOBJECT(UtilitiesSizes, sizes)
CONFIG_SUBOBJECT(Toasts, toasts) CONFIG_SUBOBJECT(Toasts, toasts)
CONFIG_SUBOBJECT(Vpn, vpn) CONFIG_SUBOBJECT(Vpn, vpn)
CONFIG_LIST(
EntryList,
quickToggles,
{LIST_ENTRY(dnd, true),
LIST_ENTRY(wifi, true),
LIST_ENTRY(settings, true),
LIST_ENTRY(bluetooth, true),
LIST_ENTRY(mic, true),
LIST_ENTRY(audio, true),
LIST_ENTRY(gameMode, true)})
public: public:
explicit Utilities(QObject* parent = nullptr) explicit Utilities(QObject* parent = nullptr)
+13 -6
View File
@@ -15,7 +15,6 @@ It includes a configurable top bar, launcher, notifications daemon + sidebar, wa
## Highlights ## Highlights
- Multi-window shell layout driven by `Drawers/Windows.qml` and panel/interactions wrappers - Multi-window shell layout driven by `Drawers/Windows.qml` and panel/interactions wrappers
- Configurable bar entries and popouts (audio, tray, network, power, resources, clock, active window)
- Launcher with app DB frequency tracking, action commands, fuzzy search modes, and wallpaper/scheme flows - Launcher with app DB frequency tracking, action commands, fuzzy search modes, and wallpaper/scheme flows
- Notification server with persistence (`~/.local/state/zshell/notifs.json`) and sidebar UI - Notification server with persistence (`~/.local/state/zshell/notifs.json`) and sidebar UI
- Dynamic Material 3 palette generation from wallpaper, optional template rendering, terminal sequence application - Dynamic Material 3 palette generation from wallpaper, optional template rendering, terminal sequence application
@@ -47,16 +46,25 @@ Core requirements:
- Hyprland (Wayland session integration) - Hyprland (Wayland session integration)
- Python 3 for scheme/wallpaper tooling - Python 3 for scheme/wallpaper tooling
Make sure to have the newest Quickshell version! As of writing, version `0.2.0.r136.gfb08ece-1`. Here's a list of package dependencies, names are from arch-packages so they
might differ slightly depending on your distro.
```
python python-pillow python-materialyoucolor libnotify cava
app2unit wl-clipboard dconf cliphist python-typer qt6-canvaspainter
python-build python-installer python-hatch python-hatch-vcs cmake ninja
```
Make sure to have the newest Quickshell version! As of writing, version `0.3.1.r1.g0fed22a-1`.
Used by major features (install as needed for your setup): Used by major features (install as needed for your setup):
- `app2unit` (launcher app execution) - `app2unit` (launcher app execution)
- `nmcli` (network integration) - `nmcli` (network integration)
- `brightnessctl`, `ddcutil` (brightness controls) - `brightnessctl`, `ddcutil` (brightness controls)
- `wl-copy`, `swappy` (picker/screenshot flow) - `wl-copy` (clipboard integration)
- `libqalculate` (launcher calculator)
- `PipeWire` + audio stack + `aubio`/`cava` paths for media visualization - `PipeWire` + audio stack + `aubio`/`cava` paths for media visualization
- `libqalculate` (launcher calculator)
- `gsettings` (optional GTK dark/light mode sync) - `gsettings` (optional GTK dark/light mode sync)
## Build and Install ## Build and Install
@@ -180,7 +188,7 @@ Important state/cache files:
- `~/.local/state/zshell/apps.sqlite` - `~/.local/state/zshell/apps.sqlite`
- `~/.cache/zshell/` - `~/.cache/zshell/`
Config is hot-reloaded and saved through `Config/Config.qml` serializers. Top-level sections include: Config is hot-reloaded and saved through the `Config` singleton. Top-level sections include:
- `general` - `general`
- `appearance` - `appearance`
@@ -265,7 +273,6 @@ Note: Template rendering (Jinja2) applies generated colors to `~/.config/zshell/
### `screenshot` — area picker ### `screenshot` — area picker
- `start` — open interactive area picker - `start` — open interactive area picker
- `start-freeze` — freeze screen then pick
### `wallpaper` — wallpaper management ### `wallpaper` — wallpaper management