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