+20
-1
README.md
+20
-1
README.md
···
10
10
11
11
<br />
12
12
13
-

13
+

14
+
15
+
<details>
16
+
<summary>more previews</summary>
17
+
18
+
<p align="center">
19
+
light mode
20
+
<img src="./docs/src/images/lightmode.png" width="800px" />
21
+
</p>
22
+
23
+
<p align="center">
24
+
wezterm + chromium
25
+
<img src="./docs/src/images/blur.png" width="800px" />
26
+
</p>
27
+
28
+
<p align="center">
29
+
neovim
30
+
<img src="./docs/src/images/nvim.png" width="800px" />
31
+
</p>
32
+
</details>
14
33
15
34

16
35
-8
docs/src/README.md
-8
docs/src/README.md
···
8
8
<img alt="nixos-unstable" src="https://img.shields.io/badge/NixOS-unstable-blue.svg?style=for-the-badge&labelColor=303446&logo=NixOS&logoColor=white&color=91D7E3" />
9
9
</div>
10
10
11
-
<br />
12
-
13
-

14
-
15
-

16
-
17
11
### Foreword
18
12
19
13
This repository contains my **personal** configuration for my systems, so its really important that you know it's **personal** and not everything will fit your needs.
···
28
22
- Sensible defaults, so you can get started quickly
29
23
- Docs kind of
30
24
- [Catppuccin](https://github.com/catppuccin/catppuccin) everywhere.
31
-
32
-
[](https://star-history.com/#isabelroses/dotfiles&Date)
+1
docs/src/SUMMARY.md
+1
docs/src/SUMMARY.md
docs/src/images/blur.png
docs/src/images/blur.png
This is a binary file and will not be displayed.
docs/src/images/blur.webp
docs/src/images/blur.webp
This is a binary file and will not be displayed.
docs/src/images/lightmode.png
docs/src/images/lightmode.png
This is a binary file and will not be displayed.
docs/src/images/lightmode.webp
docs/src/images/lightmode.webp
This is a binary file and will not be displayed.
docs/src/images/main.png
docs/src/images/main.png
This is a binary file and will not be displayed.
docs/src/images/main.webp
docs/src/images/main.webp
This is a binary file and will not be displayed.
docs/src/images/nvim.png
docs/src/images/nvim.png
This is a binary file and will not be displayed.
docs/src/images/nvim.webp
docs/src/images/nvim.webp
This is a binary file and will not be displayed.
+5
docs/src/misc/previews.md
+5
docs/src/misc/previews.md
+45
-45
flake.lock
+45
-45
flake.lock
···
24
24
]
25
25
},
26
26
"locked": {
27
-
"lastModified": 1767967164,
28
-
"narHash": "sha256-Cx4VETh9dGoQYDtWhre7g66d7SAr+h1h6f+SSHxVrck=",
27
+
"lastModified": 1767720192,
28
+
"narHash": "sha256-gA2dpQr/REM9D4SUdMwioAATnXmOZz/eVIladxEISRA=",
29
29
"owner": "catppuccin",
30
30
"repo": "nix",
31
-
"rev": "e973584280e3b0e1d5b5a1a5e9948dc222c54af7",
31
+
"rev": "bbda4b492ba33c1ca04385792c93dda1a3a28f64",
32
32
"type": "github"
33
33
},
34
34
"original": {
···
39
39
},
40
40
"crane": {
41
41
"locked": {
42
-
"lastModified": 1767461147,
43
-
"narHash": "sha256-TH/xTeq/RI+DOzo+c+4F431eVuBpYVwQwBxzURe7kcI=",
42
+
"lastModified": 1766774972,
43
+
"narHash": "sha256-8qxEFpj4dVmIuPn9j9z6NTbU+hrcGjBOvaxTzre5HmM=",
44
44
"owner": "ipetkov",
45
45
"repo": "crane",
46
-
"rev": "7d59256814085fd9666a2ae3e774dc5ee216b630",
46
+
"rev": "01bc1d404a51a0a07e9d8759cd50a7903e218c82",
47
47
"type": "github"
48
48
},
49
49
"original": {
···
91
91
"flake-compat": {
92
92
"flake": false,
93
93
"locked": {
94
-
"lastModified": 1767039857,
95
-
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
96
-
"owner": "NixOS",
94
+
"lastModified": 1761588595,
95
+
"narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=",
96
+
"owner": "edolstra",
97
97
"repo": "flake-compat",
98
-
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
98
+
"rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5",
99
99
"type": "github"
100
100
},
101
101
"original": {
102
-
"owner": "NixOS",
102
+
"owner": "edolstra",
103
103
"repo": "flake-compat",
104
104
"type": "github"
105
105
}
···
111
111
]
112
112
},
113
113
"locked": {
114
-
"lastModified": 1767609335,
115
-
"narHash": "sha256-feveD98mQpptwrAEggBQKJTYbvwwglSbOv53uCfH9PY=",
114
+
"lastModified": 1765835352,
115
+
"narHash": "sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw=",
116
116
"owner": "hercules-ci",
117
117
"repo": "flake-parts",
118
-
"rev": "250481aafeb741edfe23d29195671c19b36b6dca",
118
+
"rev": "a34fae9c08a15ad73f295041fec82323541400a9",
119
119
"type": "github"
120
120
},
121
121
"original": {
···
174
174
]
175
175
},
176
176
"locked": {
177
-
"lastModified": 1767971841,
178
-
"narHash": "sha256-TwDXF4MkmjI9c3Sly9FOWXf4sPbre6ZujG87v39G1Ig=",
177
+
"lastModified": 1767048910,
178
+
"narHash": "sha256-KLFTeA/xquN+F3XHLAXcserk0L0nijbhzuldxNDF1eE=",
179
179
"owner": "nix-community",
180
180
"repo": "home-manager",
181
-
"rev": "0e4217b2c4827e71e2e612accccb01981c16afda",
181
+
"rev": "d99b4ca5debaa082c7d76015aa2b7f3fc7e8b5f7",
182
182
"type": "github"
183
183
},
184
184
"original": {
···
212
212
]
213
213
},
214
214
"locked": {
215
-
"lastModified": 1768007596,
216
-
"narHash": "sha256-8es5mAWk+UMlo9O3pEbdLhXVk5CTs3yE7S1531aD1ZQ=",
215
+
"lastModified": 1767057212,
216
+
"narHash": "sha256-zmCTjuCxfA/iMWoM3dlqI6Jr7IxfdhxPQKps05mRH/4=",
217
217
"owner": "isabelroses",
218
218
"repo": "izlix",
219
-
"rev": "450899c93ac2b101b1db2061f3bddbd369728815",
219
+
"rev": "5ef2fec7c95c61c67337164df84bf89b50d862b8",
220
220
"type": "github"
221
221
},
222
222
"original": {
···
233
233
]
234
234
},
235
235
"locked": {
236
-
"lastModified": 1767756498,
237
-
"narHash": "sha256-a7SYG/6uUeeyfg1Xiq/sXpRb2RXs+kQ0GKrKMVlMw+4=",
236
+
"lastModified": 1766546564,
237
+
"narHash": "sha256-JShJrgMHSssMX425ubPK4hS5d4I9tqcb8+oB9qRnO/4=",
238
238
"owner": "isabelroses",
239
239
"repo": "nvim",
240
-
"rev": "725ef38a0ea9b5bd8218aad272bd7458d430b2be",
240
+
"rev": "790c8cb2ce8e8b51515b1bbc16226d445a8e2d4d",
241
241
"type": "github"
242
242
},
243
243
"original": {
···
256
256
"rust-overlay": "rust-overlay"
257
257
},
258
258
"locked": {
259
-
"lastModified": 1767697030,
260
-
"narHash": "sha256-0iVZ99H3kR5h6Lhw8kDDuUc5C/k6iismeWgCS1qWTQ4=",
259
+
"lastModified": 1767013031,
260
+
"narHash": "sha256-p8ANXBakAtfX/aEhLbU6w0tuQe3nrBvLdHbKirJP7ug=",
261
261
"owner": "nix-community",
262
262
"repo": "lanzaboote",
263
-
"rev": "657469e8f036334db768daaf7732b1174676054b",
263
+
"rev": "c2a82339373daee8cbbcad5f51f22ae6b71069e0",
264
264
"type": "github"
265
265
},
266
266
"original": {
···
292
292
},
293
293
"nixpkgs": {
294
294
"locked": {
295
-
"lastModified": 1767966113,
296
-
"narHash": "sha256-mb9aE5Y/wRCZz0cTB9EOe4BEKWpXAAl5Yai4TFrEf1E=",
297
-
"rev": "5f02c91314c8ba4afe83b256b023756412218535",
295
+
"lastModified": 1766996594,
296
+
"narHash": "sha256-Ot0xSLsfRXy6IlMvnc8hSLHKNn/7R8yFDc7uI5Cjpr4=",
297
+
"rev": "0744ef1b047f07d31d9962d757ffe38ec14a4d41",
298
298
"type": "tarball",
299
-
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.05pre924980.5f02c91314c8/nixexprs.tar.xz?lastModified=1767966113&rev=5f02c91314c8ba4afe83b256b023756412218535"
299
+
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.05pre918100.0744ef1b047f/nixexprs.tar.xz?lastModified=1766996594&rev=0744ef1b047f07d31d9962d757ffe38ec14a4d41"
300
300
},
301
301
"original": {
302
302
"type": "tarball",
···
313
313
]
314
314
},
315
315
"locked": {
316
-
"lastModified": 1767281941,
317
-
"narHash": "sha256-6MkqajPICgugsuZ92OMoQcgSHnD6sJHwk8AxvMcIgTE=",
316
+
"lastModified": 1765911976,
317
+
"narHash": "sha256-t3T/xm8zstHRLx+pIHxVpQTiySbKqcQbK+r+01XVKc0=",
318
318
"owner": "cachix",
319
319
"repo": "pre-commit-hooks.nix",
320
-
"rev": "f0927703b7b1c8d97511c4116eb9b4ec6645a0fa",
320
+
"rev": "b68b780b69702a090c8bb1b973bab13756cc7a27",
321
321
"type": "github"
322
322
},
323
323
"original": {
···
353
353
]
354
354
},
355
355
"locked": {
356
-
"lastModified": 1767495280,
357
-
"narHash": "sha256-hEEgtE/RSRigw8xscchGymf/t1nluZwTfru4QF6O1CQ=",
356
+
"lastModified": 1766976750,
357
+
"narHash": "sha256-w+o3AIBI56tzfMJRqRXg9tSXnpQRN5hAT15o2t9rxYw=",
358
358
"owner": "oxalica",
359
359
"repo": "rust-overlay",
360
-
"rev": "cb24c5cc207ba8e9a4ce245eedd2d37c3a988bc1",
360
+
"rev": "9fe44e7f05b734a64a01f92fc51ad064fb0a884f",
361
361
"type": "github"
362
362
},
363
363
"original": {
···
396
396
]
397
397
},
398
398
"locked": {
399
-
"lastModified": 1767848616,
400
-
"narHash": "sha256-rAVR2ucz5vn9TOjvj5hVyEl0MnXrwIJo3ee12+p0EZE=",
399
+
"lastModified": 1766896955,
400
+
"narHash": "sha256-BbAUnNjaBmfR7Mvho9BN0RfvDi5fpP19wd/Hs6DMX8k=",
401
401
"owner": "Mic92",
402
402
"repo": "sops-nix",
403
-
"rev": "994c9f014e5ce073de4bca7ebe21cf1cf90f2b0a",
403
+
"rev": "861c32b27cce26c4bb828dfd21bd23df0dba7df2",
404
404
"type": "github"
405
405
},
406
406
"original": {
···
418
418
"systems": "systems"
419
419
},
420
420
"locked": {
421
-
"lastModified": 1767502559,
422
-
"narHash": "sha256-om0IPjW850vhhIrNZ5tiXjsYuqyoI44IdE+I9AwZ96I=",
421
+
"lastModified": 1767117828,
422
+
"narHash": "sha256-K9sL2WwDQOltBiT111uU5pmfFjB6VmApaaHgTdXZqpg=",
423
423
"owner": "Gerg-L",
424
424
"repo": "spicetify-nix",
425
-
"rev": "806c1fdeb7af3e013215d14f5d9f06685fa6650f",
425
+
"rev": "b164eb4d18bed0f4be47c1033b477d3e81646752",
426
426
"type": "github"
427
427
},
428
428
"original": {
···
453
453
]
454
454
},
455
455
"locked": {
456
-
"lastModified": 1768006445,
457
-
"narHash": "sha256-WQ1yhsbYQM+EvhaWPSuS4UZPD4SjsskMWNG6azbEAgM=",
456
+
"lastModified": 1767055973,
457
+
"narHash": "sha256-sXhH2gl0SfcUVIhPvyBqhLOd4R20SrRypLl5EjzW3OI=",
458
458
"owner": "tgirlcloud",
459
459
"repo": "pkgs",
460
-
"rev": "ae2de15f76978b6564c3eecdbe68d5ed0d0d2500",
460
+
"rev": "ba3f5e451bd5fb41c147e62802c68e7729ac1d63",
461
461
"type": "github"
462
462
},
463
463
"original": {
+1
-2
home/isabel/gui/chromium.nix
+1
-2
home/isabel/gui/chromium.nix
···
1
1
{
2
2
lib,
3
3
pkgs,
4
-
config,
5
4
...
6
5
}:
7
6
let
···
188
187
};
189
188
};
190
189
191
-
xdg.configFile = mkIf (pkgs.stdenv.hostPlatform.isLinux && config.programs.chromium.enable) {
190
+
xdg.configFile = mkIf pkgs.stdenv.hostPlatform.isLinux {
192
191
"chromium/NativeMessagingHosts/ff2mpv.json".source =
193
192
"${pkgs.ff2mpv-rust}/etc/chromium/native-messaging-hosts/ff2mpv.json";
194
193
};
+31
-17
home/isabel/gui/hyprland.nix
+31
-17
home/isabel/gui/hyprland.nix
···
215
215
};
216
216
217
217
layerrule = [
218
-
"blur on, ignore_alpha 0, match:namespace vicinae"
219
-
"no_anim on, match:namespace vicinae"
218
+
"blur,vicinae"
219
+
"ignorealpha 0, vicinae"
220
+
"noanim, vicinae"
220
221
];
221
222
222
223
general = {
···
224
225
gaps_out = 8;
225
226
gaps_workspaces = 0;
226
227
border_size = 2;
228
+
no_border_on_floating = true;
227
229
228
230
"col.active_border" = "$pink";
229
231
"col.inactive_border" = "$surface1";
···
292
294
disable_autoreload = true; # autoreload is unnecessary on nixos, because the config is readonly anyway
293
295
};
294
296
295
-
windowrule = [
296
-
"float on, match:title ^(nm-connection-editor)$"
297
-
"float on, match:title ^(Network)$"
298
-
"float on, match:title ^(xdg-desktop-portal-gtk)$"
299
-
"float on, match:class gay.vaskel.soteria"
300
-
"float on, match:title ^(Picture-in-Picture)$"
301
-
"float on, match:class ^(download)$"
297
+
windowrulev2 = [
298
+
"float, title:^(nm-connection-editor)$"
299
+
"float, title:^(Network)$"
300
+
"float, title:^(xdg-desktop-portal-gtk)$"
301
+
"float, class:gay.vaskel.soteria"
302
+
"float, title:^(Picture-in-Picture)$"
303
+
"float, class:^(download)$"
302
304
303
-
"center on, float on, size (monitor_w*0.4) (monitor_h*0.6), match:initial_title (Open Files)"
304
-
"center on, float on, size (monitor_w*0.4) (monitor_h*0.6), match:class .blueman-manager-wrapped"
305
-
"center on, float on, size (monitor_w*0.4) (monitor_h*0.6), match:class com.saivert.pwvucontrol"
306
-
"float on, size 800 600, match:title Bitwarden"
305
+
"center(1), initialTitle:(Open Files)"
306
+
"float, initialTitle:(Open Files)"
307
+
"size 40% 60%, initialTitle:(Open Files)"
307
308
308
-
"workspace 6, match:class discord" # move discord to workspace 6
309
-
"workspace 7, match:class spotify" # move spotify to workspace 7
309
+
"center(1), class:.blueman-manager-wrapped"
310
+
"float, class:.blueman-manager-wrapped"
311
+
"size 40% 60%, class:.blueman-manager-wrapped"
312
+
313
+
"center(1), class:com.saivert.pwvucontrol"
314
+
"float, class:com.saivert.pwvucontrol"
315
+
"size 40% 60%, class:com.saivert.pwvucontrol"
316
+
317
+
# we can't just use the tag because we want to capture the popup window
318
+
"float, title:Bitwarden"
319
+
"size 800 600, title:Bitwarden"
320
+
# "no_screenshare on, tag:bitwarden"
321
+
322
+
"workspace 6, class:discord" # move discord to workspace 6
323
+
"workspace 7, class:spotify" # move spotify to workspace 7
310
324
311
325
# throw sharing indicators away
312
-
"workspace special silent, match:title ^(Firefox โ Sharing Indicator)$"
313
-
"workspace special silent, match:title ^(.*is sharing (your screen|a window).)$"
326
+
"workspace special silent, title:^(Firefox โ Sharing Indicator)$"
327
+
"workspace special silent, title:^(.*is sharing (your screen|a window)\.)$"
314
328
];
315
329
};
316
330
-186
home/isabel/gui/quickshell/components/MediaPlayers.qml
-186
home/isabel/gui/quickshell/components/MediaPlayers.qml
···
1
-
import QtQuick
2
-
import QtQuick.Layouts
3
-
import Quickshell.Widgets
4
-
import Quickshell.Services.Mpris
5
-
import "root:/data"
6
-
import "root:/services"
7
-
8
-
ColumnLayout {
9
-
id: root
10
-
11
-
Layout.fillWidth: true
12
-
spacing: 8
13
-
visible: Media.players.length > 0
14
-
15
-
RowLayout {
16
-
Layout.fillWidth: true
17
-
spacing: 8
18
-
19
-
Text {
20
-
text: "Now Playing"
21
-
color: Settings.colors.foreground
22
-
font {
23
-
pixelSize: 14
24
-
weight: Font.Bold
25
-
}
26
-
}
27
-
28
-
Item { Layout.fillWidth: true }
29
-
30
-
// Player selector (if multiple players)
31
-
Row {
32
-
spacing: 4
33
-
visible: Media.players.length > 1
34
-
35
-
Repeater {
36
-
model: Media.players
37
-
38
-
Rectangle {
39
-
required property MprisPlayer modelData
40
-
required property int index
41
-
width: 8
42
-
height: 8
43
-
radius: 4
44
-
color: Media.selectedPlayer === modelData ? Settings.colors.accent : Settings.colors.backgroundLightest
45
-
46
-
MouseArea {
47
-
anchors.fill: parent
48
-
cursorShape: Qt.PointingHandCursor
49
-
onClicked: Media.selectedPlayer = modelData
50
-
}
51
-
}
52
-
}
53
-
}
54
-
}
55
-
56
-
// Current Player
57
-
ListView {
58
-
id: mediaPlayerList
59
-
Layout.fillWidth: true
60
-
Layout.preferredHeight: contentHeight
61
-
model: Media.players
62
-
spacing: 8
63
-
clip: true
64
-
interactive: false
65
-
66
-
delegate: Rectangle {
67
-
required property MprisPlayer modelData
68
-
required property int index
69
-
width: ListView.view.width
70
-
height: 90
71
-
radius: 8
72
-
color: Settings.colors.backgroundLighter
73
-
opacity: Media.selectedPlayer === modelData ? 1.0 : 0.6
74
-
75
-
MouseArea {
76
-
anchors.fill: parent
77
-
onClicked: Media.selectedPlayer = modelData
78
-
}
79
-
80
-
RowLayout {
81
-
anchors {
82
-
fill: parent
83
-
margins: 10
84
-
}
85
-
spacing: 10
86
-
87
-
// Album Art
88
-
ClippingWrapperRectangle {
89
-
radius: 6
90
-
Layout.preferredWidth: 70
91
-
Layout.preferredHeight: 70
92
-
93
-
Image {
94
-
anchors.fill: parent
95
-
source: modelData.trackArtUrl
96
-
fillMode: Image.PreserveAspectCrop
97
-
}
98
-
}
99
-
100
-
// Track Info & Controls
101
-
ColumnLayout {
102
-
Layout.fillWidth: true
103
-
Layout.fillHeight: true
104
-
spacing: 2
105
-
106
-
RowLayout {
107
-
Layout.fillWidth: true
108
-
spacing: 4
109
-
110
-
Text {
111
-
text: modelData.trackTitle ?? "Unknown"
112
-
color: Settings.colors.foreground
113
-
font {
114
-
pixelSize: 13
115
-
weight: Font.Medium
116
-
}
117
-
elide: Text.ElideRight
118
-
Layout.fillWidth: true
119
-
}
120
-
121
-
// Player app name badge
122
-
Rectangle {
123
-
visible: Media.players.length > 1
124
-
Layout.preferredHeight: 16
125
-
Layout.preferredWidth: appNameText.width + 8
126
-
radius: 4
127
-
color: Settings.colors.backgroundLightest
128
-
129
-
Text {
130
-
id: appNameText
131
-
anchors.centerIn: parent
132
-
text: modelData.identity ?? ""
133
-
color: Settings.colors.foreground
134
-
opacity: 0.7
135
-
font.pixelSize: 9
136
-
}
137
-
}
138
-
}
139
-
140
-
Text {
141
-
text: modelData.trackArtist ?? ""
142
-
color: Settings.colors.foreground
143
-
opacity: 0.7
144
-
font.pixelSize: 11
145
-
elide: Text.ElideRight
146
-
Layout.fillWidth: true
147
-
}
148
-
149
-
Item { Layout.fillHeight: true }
150
-
151
-
// Playback Controls
152
-
RowLayout {
153
-
spacing: 14
154
-
Layout.alignment: Qt.AlignLeft
155
-
156
-
IconButton {
157
-
icon: "media-skip-backward-symbolic"
158
-
size: 14
159
-
onClicked: modelData.previous()
160
-
}
161
-
162
-
IconButton {
163
-
icon: modelData.playbackState === MprisPlaybackState.Playing
164
-
? "media-playback-pause-symbolic"
165
-
: "media-playback-start-symbolic"
166
-
size: 18
167
-
onClicked: {
168
-
if (modelData.playbackState === MprisPlaybackState.Playing) {
169
-
modelData.pause();
170
-
} else {
171
-
modelData.play();
172
-
}
173
-
}
174
-
}
175
-
176
-
IconButton {
177
-
icon: "media-skip-forward-symbolic"
178
-
size: 14
179
-
onClicked: modelData.next()
180
-
}
181
-
}
182
-
}
183
-
}
184
-
}
185
-
}
186
-
}
+528
-5
home/isabel/gui/quickshell/components/Noti.qml
+528
-5
home/isabel/gui/quickshell/components/Noti.qml
···
1
1
import QtQuick
2
2
import QtQuick.Layouts
3
3
import QtQuick.Controls
4
+
import QtQuick.Controls.Basic
4
5
import Quickshell
5
6
import Quickshell.Widgets
6
-
import Quickshell.Services.Notifications as QsNotifications
7
+
import Quickshell.Io
8
+
import Quickshell.Services.Notifications
9
+
import Quickshell.Services.Mpris
10
+
import Quickshell.Services.Pipewire
7
11
import "root:/data"
8
12
import "root:/services"
9
13
···
98
102
}
99
103
100
104
delegate: Rectangle {
101
-
required property QsNotifications.Notification modelData
105
+
required property Notification modelData
102
106
width: ListView.view.width
103
107
height: 72
104
108
radius: 8
···
168
172
visible: Media.players.length > 0
169
173
}
170
174
171
-
// Media Players Section
172
-
MediaPlayers {}
175
+
// Media Players Section (Multiple Players)
176
+
ColumnLayout {
177
+
Layout.fillWidth: true
178
+
spacing: 8
179
+
visible: Media.players.length > 0
180
+
181
+
RowLayout {
182
+
Layout.fillWidth: true
183
+
spacing: 8
184
+
185
+
Text {
186
+
text: "Now Playing"
187
+
color: Settings.colors.foreground
188
+
font {
189
+
pixelSize: 14
190
+
weight: Font.Bold
191
+
}
192
+
}
193
+
194
+
Item { Layout.fillWidth: true }
195
+
196
+
// Player selector (if multiple players)
197
+
Row {
198
+
spacing: 4
199
+
visible: Media.players.length > 1
200
+
201
+
Repeater {
202
+
model: Media.players
203
+
204
+
Rectangle {
205
+
required property MprisPlayer modelData
206
+
required property int index
207
+
width: 8
208
+
height: 8
209
+
radius: 4
210
+
color: Media.selectedPlayer === modelData ? Settings.colors.accent : Settings.colors.backgroundLightest
211
+
212
+
MouseArea {
213
+
anchors.fill: parent
214
+
cursorShape: Qt.PointingHandCursor
215
+
onClicked: Media.selectedPlayer = modelData
216
+
}
217
+
}
218
+
}
219
+
}
220
+
}
221
+
222
+
// Current Player
223
+
ListView {
224
+
id: mediaPlayerList
225
+
Layout.fillWidth: true
226
+
Layout.preferredHeight: contentHeight
227
+
model: Media.players
228
+
spacing: 8
229
+
clip: true
230
+
interactive: false
231
+
232
+
delegate: Rectangle {
233
+
required property MprisPlayer modelData
234
+
required property int index
235
+
width: ListView.view.width
236
+
height: 90
237
+
radius: 8
238
+
color: Settings.colors.backgroundLighter
239
+
opacity: Media.selectedPlayer === modelData ? 1.0 : 0.6
240
+
241
+
MouseArea {
242
+
anchors.fill: parent
243
+
onClicked: Media.selectedPlayer = modelData
244
+
}
245
+
246
+
RowLayout {
247
+
anchors {
248
+
fill: parent
249
+
margins: 10
250
+
}
251
+
spacing: 10
252
+
253
+
// Album Art
254
+
ClippingWrapperRectangle {
255
+
radius: 6
256
+
Layout.preferredWidth: 70
257
+
Layout.preferredHeight: 70
258
+
259
+
Image {
260
+
anchors.fill: parent
261
+
source: modelData.trackArtUrl
262
+
fillMode: Image.PreserveAspectCrop
263
+
}
264
+
}
265
+
266
+
// Track Info & Controls
267
+
ColumnLayout {
268
+
Layout.fillWidth: true
269
+
Layout.fillHeight: true
270
+
spacing: 2
271
+
272
+
RowLayout {
273
+
Layout.fillWidth: true
274
+
spacing: 4
275
+
276
+
Text {
277
+
text: modelData.trackTitle ?? "Unknown"
278
+
color: Settings.colors.foreground
279
+
font {
280
+
pixelSize: 13
281
+
weight: Font.Medium
282
+
}
283
+
elide: Text.ElideRight
284
+
Layout.fillWidth: true
285
+
}
286
+
287
+
// Player app name badge
288
+
Rectangle {
289
+
visible: Media.players.length > 1
290
+
Layout.preferredHeight: 16
291
+
Layout.preferredWidth: appNameText.width + 8
292
+
radius: 4
293
+
color: Settings.colors.backgroundLightest
294
+
295
+
Text {
296
+
id: appNameText
297
+
anchors.centerIn: parent
298
+
text: modelData.identity ?? ""
299
+
color: Settings.colors.foreground
300
+
opacity: 0.7
301
+
font.pixelSize: 9
302
+
}
303
+
}
304
+
}
305
+
306
+
Text {
307
+
text: modelData.trackArtist ?? ""
308
+
color: Settings.colors.foreground
309
+
opacity: 0.7
310
+
font.pixelSize: 11
311
+
elide: Text.ElideRight
312
+
Layout.fillWidth: true
313
+
}
314
+
315
+
Item { Layout.fillHeight: true }
316
+
317
+
// Playback Controls
318
+
RowLayout {
319
+
spacing: 14
320
+
Layout.alignment: Qt.AlignLeft
321
+
322
+
IconButton {
323
+
icon: "media-skip-backward-symbolic"
324
+
size: 14
325
+
onClicked: modelData.previous()
326
+
}
327
+
328
+
IconButton {
329
+
icon: modelData.playbackState === MprisPlaybackState.Playing
330
+
? "media-playback-pause-symbolic"
331
+
: "media-playback-start-symbolic"
332
+
size: 18
333
+
onClicked: {
334
+
if (modelData.playbackState === MprisPlaybackState.Playing) {
335
+
modelData.pause();
336
+
} else {
337
+
modelData.play();
338
+
}
339
+
}
340
+
}
341
+
342
+
IconButton {
343
+
icon: "media-skip-forward-symbolic"
344
+
size: 14
345
+
onClicked: modelData.next()
346
+
}
347
+
}
348
+
}
349
+
}
350
+
}
351
+
}
352
+
}
173
353
174
354
// Separator
175
355
Rectangle {
···
179
359
}
180
360
181
361
// Quick Settings Section
182
-
QuickSettings {}
362
+
ColumnLayout {
363
+
Layout.fillWidth: true
364
+
spacing: 12
365
+
366
+
Text {
367
+
text: "Quick Settings"
368
+
color: Settings.colors.foreground
369
+
font {
370
+
pixelSize: 14
371
+
weight: Font.Bold
372
+
}
373
+
}
374
+
375
+
// Connection Status Row (Ethernet indicator)
376
+
RowLayout {
377
+
Layout.fillWidth: true
378
+
spacing: 8
379
+
visible: Networking.ethernetConnected
380
+
381
+
MyIcon {
382
+
icon: "network-wired-symbolic"
383
+
size: 16
384
+
}
385
+
386
+
Text {
387
+
text: "Ethernet: " + Networking.ethernetDevice
388
+
color: Settings.colors.foreground
389
+
font.pixelSize: 12
390
+
}
391
+
392
+
Item { Layout.fillWidth: true }
393
+
394
+
Rectangle {
395
+
width: 8
396
+
height: 8
397
+
radius: 4
398
+
color: Settings.colors.success
399
+
}
400
+
}
401
+
402
+
// Toggle Buttons Row
403
+
RowLayout {
404
+
Layout.fillWidth: true
405
+
spacing: 8
406
+
407
+
// WiFi Toggle
408
+
QuickSettingButton {
409
+
icon: Networking.wifiEnabled
410
+
? (Networking.activeWifi?.icon ?? "network-wireless-acquiring-symbolic")
411
+
: "network-wireless-disabled-symbolic"
412
+
label: "WiFi"
413
+
active: Networking.wifiEnabled
414
+
onClicked: Networking.toggleWifi()
415
+
onPressAndHold: networkPanel.visible = !networkPanel.visible
416
+
}
417
+
418
+
// Bluetooth Toggle
419
+
QuickSettingButton {
420
+
icon: Bluetooth.icon
421
+
label: "Bluetooth"
422
+
active: Bluetooth.powered
423
+
onClicked: Bluetooth.toggle()
424
+
onPressAndHold: bluetoothPanel.visible = !bluetoothPanel.visible
425
+
}
426
+
427
+
// Do Not Disturb Toggle
428
+
QuickSettingButton {
429
+
id: dndButton
430
+
property bool dndEnabled: false
431
+
icon: dndEnabled ? "notifications-disabled-symbolic" : "preferences-system-notifications-symbolic"
432
+
label: "DND"
433
+
active: dndEnabled
434
+
onClicked: dndEnabled = !dndEnabled
435
+
}
436
+
}
437
+
438
+
// Volume Slider
439
+
PwObjectTracker {
440
+
objects: [Pipewire.defaultAudioSink]
441
+
}
442
+
443
+
StyledSlider {
444
+
icon: {
445
+
const muted = Pipewire.defaultAudioSink?.audio?.muted ?? false;
446
+
const vol = Pipewire.defaultAudioSink?.audio?.volume ?? 0;
447
+
if (muted) return "audio-volume-muted-symbolic";
448
+
if (vol > 0.66) return "audio-volume-high-symbolic";
449
+
if (vol > 0.33) return "audio-volume-medium-symbolic";
450
+
if (vol > 0) return "audio-volume-low-symbolic";
451
+
return "audio-volume-muted-symbolic";
452
+
}
453
+
value: Pipewire.defaultAudioSink?.audio?.volume ?? 0
454
+
iconClickable: true
455
+
onIconClicked: {
456
+
if (Pipewire.defaultAudioSink?.audio) {
457
+
Pipewire.defaultAudioSink.audio.muted = !Pipewire.defaultAudioSink.audio.muted;
458
+
}
459
+
}
460
+
onMoved: (val) => {
461
+
if (Pipewire.defaultAudioSink?.audio) {
462
+
Pipewire.defaultAudioSink.audio.volume = val;
463
+
}
464
+
}
465
+
}
466
+
467
+
// Brightness Slider
468
+
BrightnessHelper { id: brightnessHelper }
469
+
470
+
StyledSlider {
471
+
visible: brightnessHelper.available
472
+
icon: "display-brightness-symbolic"
473
+
value: brightnessHelper.brightness
474
+
from: 0.05
475
+
accentColor: Settings.colors.warning
476
+
onMoved: (val) => brightnessHelper.setBrightness(val)
477
+
}
478
+
479
+
// Network Panel (expandable)
480
+
Rectangle {
481
+
id: networkPanel
482
+
Layout.fillWidth: true
483
+
Layout.preferredHeight: visible ? networkListView.contentHeight + 48 : 0
484
+
visible: false
485
+
radius: 8
486
+
color: Settings.colors.backgroundLighter
487
+
clip: true
488
+
489
+
Behavior on Layout.preferredHeight {
490
+
NumberAnimation { duration: 150; easing.type: Easing.OutCubic }
491
+
}
492
+
493
+
ColumnLayout {
494
+
anchors.fill: parent
495
+
anchors.margins: 12
496
+
spacing: 8
497
+
498
+
RowLayout {
499
+
Layout.fillWidth: true
500
+
501
+
Text {
502
+
text: "Networks"
503
+
color: Settings.colors.foreground
504
+
font {
505
+
pixelSize: 13
506
+
weight: Font.Medium
507
+
}
508
+
}
509
+
510
+
Item { Layout.fillWidth: true }
511
+
512
+
IconButton {
513
+
icon: "view-refresh-symbolic"
514
+
size: 14
515
+
onClicked: Networking.reload()
516
+
}
517
+
}
518
+
519
+
ListView {
520
+
id: networkListView
521
+
Layout.fillWidth: true
522
+
Layout.preferredHeight: Math.min(contentHeight, 150)
523
+
model: Networking.networks
524
+
spacing: 4
525
+
clip: true
526
+
527
+
delegate: PanelListItem {
528
+
required property var modelData
529
+
icon: modelData.icon
530
+
text: modelData.ssid || "Unknown"
531
+
badge: modelData.strength + "%"
532
+
active: modelData.active
533
+
}
534
+
}
535
+
}
536
+
}
537
+
538
+
// Bluetooth Panel (expandable)
539
+
Rectangle {
540
+
id: bluetoothPanel
541
+
Layout.fillWidth: true
542
+
Layout.preferredHeight: visible ? bluetoothListView.contentHeight + 64 : 0
543
+
visible: false
544
+
radius: 8
545
+
color: Settings.colors.backgroundLighter
546
+
clip: true
547
+
548
+
Behavior on Layout.preferredHeight {
549
+
NumberAnimation { duration: 150; easing.type: Easing.OutCubic }
550
+
}
551
+
552
+
ColumnLayout {
553
+
anchors.fill: parent
554
+
anchors.margins: 12
555
+
spacing: 8
556
+
557
+
RowLayout {
558
+
Layout.fillWidth: true
559
+
560
+
Text {
561
+
text: "Bluetooth Devices"
562
+
color: Settings.colors.foreground
563
+
font {
564
+
pixelSize: 13
565
+
weight: Font.Medium
566
+
}
567
+
}
568
+
569
+
Item { Layout.fillWidth: true }
570
+
571
+
// Scan button
572
+
IconButton {
573
+
icon: Bluetooth.adapter?.discovering ? "process-stop-symbolic" : "view-refresh-symbolic"
574
+
size: 14
575
+
onClicked: {
576
+
if (Bluetooth.adapter?.discovering) {
577
+
Bluetooth.stopDiscovery();
578
+
} else {
579
+
Bluetooth.startDiscovery();
580
+
}
581
+
}
582
+
}
583
+
584
+
Text {
585
+
text: Bluetooth.connected ? Bluetooth.connectedDevice?.name ?? "" : ""
586
+
color: Settings.colors.accent
587
+
font.pixelSize: 11
588
+
visible: Bluetooth.connected
589
+
}
590
+
}
591
+
592
+
ListView {
593
+
id: bluetoothListView
594
+
Layout.fillWidth: true
595
+
Layout.preferredHeight: Math.min(contentHeight, 150)
596
+
model: Bluetooth.devices
597
+
spacing: 4
598
+
clip: true
599
+
600
+
delegate: PanelListItem {
601
+
required property var modelData
602
+
icon: modelData.icon || "bluetooth-symbolic"
603
+
text: modelData.name
604
+
badge: modelData.connected ? "Connected" : (modelData.paired ? "Paired" : "")
605
+
active: modelData.connected
606
+
onClicked: {
607
+
if (modelData.connected) {
608
+
modelData.disconnect();
609
+
} else {
610
+
modelData.connect();
611
+
}
612
+
}
613
+
}
614
+
}
615
+
616
+
Text {
617
+
text: {
618
+
if (!Bluetooth.adapter) return "No Bluetooth adapter";
619
+
if (Bluetooth.devices?.count === 0) return "No devices found";
620
+
return "";
621
+
}
622
+
color: Settings.colors.foreground
623
+
opacity: 0.5
624
+
font.pixelSize: 12
625
+
visible: !Bluetooth.adapter || Bluetooth.devices?.count === 0
626
+
Layout.alignment: Qt.AlignHCenter
627
+
}
628
+
}
629
+
}
630
+
}
183
631
}
184
632
}
633
+
}
634
+
}
635
+
636
+
// Quick Setting Button Component
637
+
component QuickSettingButton: Rectangle {
638
+
id: qsButton
639
+
Layout.fillWidth: true
640
+
Layout.preferredHeight: 64
641
+
radius: 8
642
+
color: active ? Settings.colors.accent : Settings.colors.backgroundLighter
643
+
644
+
property string icon: ""
645
+
property string label: ""
646
+
property bool active: false
647
+
648
+
signal clicked()
649
+
signal pressAndHold()
650
+
651
+
ColumnLayout {
652
+
anchors.centerIn: parent
653
+
spacing: 6
654
+
655
+
MyIcon {
656
+
icon: qsButton.icon
657
+
size: 20
658
+
Layout.alignment: Qt.AlignHCenter
659
+
invert: active
660
+
}
661
+
662
+
Text {
663
+
text: qsButton.label
664
+
color: qsButton.active ? Settings.colors.background : Settings.colors.foreground
665
+
font.pixelSize: 11
666
+
Layout.alignment: Qt.AlignHCenter
667
+
}
668
+
}
669
+
670
+
MouseArea {
671
+
anchors.fill: parent
672
+
cursorShape: Qt.PointingHandCursor
673
+
onClicked: qsButton.clicked()
674
+
onPressAndHold: qsButton.pressAndHold()
675
+
}
676
+
}
677
+
678
+
// Brightness Helper Component
679
+
component BrightnessHelper: QtObject {
680
+
id: brightnessObj
681
+
property real brightness: 1.0
682
+
property bool available: false
683
+
684
+
function setBrightness(value) {
685
+
brightness = value;
686
+
setBrightnessProc.command = ["brightnessctl", "set", Math.round(value * 100) + "%"];
687
+
setBrightnessProc.running = true;
688
+
}
689
+
690
+
Component.onCompleted: getBrightness.running = true
691
+
692
+
property Process getBrightness: Process {
693
+
id: getBrightness
694
+
command: ["bash", "-c", "brightnessctl -m 2>/dev/null | cut -d, -f4 | tr -d '%'"]
695
+
stdout: SplitParser {
696
+
onRead: {
697
+
const val = parseInt(data.trim());
698
+
if (!isNaN(val)) {
699
+
brightnessObj.brightness = val / 100;
700
+
brightnessObj.available = true;
701
+
}
702
+
}
703
+
}
704
+
}
705
+
706
+
property Process setBrightnessProc: Process {
707
+
id: setBrightnessProc
185
708
}
186
709
}
187
710
}
-387
home/isabel/gui/quickshell/components/QuickSettings.qml
-387
home/isabel/gui/quickshell/components/QuickSettings.qml
···
1
-
import QtQuick
2
-
import QtQuick.Layouts
3
-
import QtQuick.Controls.Basic
4
-
import Quickshell.Io
5
-
import Quickshell.Services.Pipewire
6
-
import "root:/data"
7
-
import "root:/services"
8
-
9
-
ColumnLayout {
10
-
id: root
11
-
12
-
Layout.fillWidth: true
13
-
spacing: 12
14
-
15
-
Text {
16
-
text: "Quick Settings"
17
-
color: Settings.colors.foreground
18
-
font {
19
-
pixelSize: 14
20
-
weight: Font.Bold
21
-
}
22
-
}
23
-
24
-
// Connection Status Row (Ethernet indicator)
25
-
RowLayout {
26
-
Layout.fillWidth: true
27
-
spacing: 8
28
-
visible: Networking.ethernetConnected
29
-
30
-
MyIcon {
31
-
icon: "network-wired-symbolic"
32
-
size: 16
33
-
}
34
-
35
-
Text {
36
-
text: "Ethernet: " + Networking.ethernetDevice
37
-
color: Settings.colors.foreground
38
-
font.pixelSize: 12
39
-
}
40
-
41
-
Item { Layout.fillWidth: true }
42
-
43
-
Rectangle {
44
-
width: 8
45
-
height: 8
46
-
radius: 4
47
-
color: Settings.colors.success
48
-
}
49
-
}
50
-
51
-
// Network Panel (expandable)
52
-
Rectangle {
53
-
id: networkPanel
54
-
Layout.fillWidth: true
55
-
Layout.preferredHeight: visible ? Math.min(networkColumn.implicitHeight + 20, 350) : 0
56
-
visible: false
57
-
radius: 8
58
-
color: Settings.colors.backgroundLighter
59
-
clip: true
60
-
61
-
Behavior on Layout.preferredHeight {
62
-
NumberAnimation { duration: 150; easing.type: Easing.OutCubic }
63
-
}
64
-
65
-
ColumnLayout {
66
-
id: networkColumn
67
-
anchors.fill: parent
68
-
anchors.margins: 12
69
-
spacing: 8
70
-
71
-
RowLayout {
72
-
Layout.fillWidth: true
73
-
74
-
Text {
75
-
text: "Networks"
76
-
color: Settings.colors.foreground
77
-
font {
78
-
pixelSize: 13
79
-
weight: Font.Medium
80
-
}
81
-
}
82
-
83
-
Item { Layout.fillWidth: true }
84
-
85
-
IconButton {
86
-
icon: "view-refresh-symbolic"
87
-
size: 14
88
-
onClicked: Networking.reload()
89
-
}
90
-
}
91
-
92
-
ScrollView {
93
-
Layout.fillWidth: true
94
-
Layout.preferredHeight: Math.min(networkListView.contentHeight, 280)
95
-
clip: true
96
-
97
-
ListView {
98
-
id: networkListView
99
-
width: parent.width
100
-
model: Networking.networks
101
-
spacing: 4
102
-
103
-
delegate: PanelListItem {
104
-
required property var modelData
105
-
width: ListView.view.width
106
-
icon: modelData.icon
107
-
text: modelData.ssid || "Unknown"
108
-
badge: modelData.strength + "%"
109
-
active: modelData.active
110
-
onClicked: {
111
-
if (!modelData.active) {
112
-
Networking.connectToNetwork(modelData.ssid);
113
-
}
114
-
}
115
-
}
116
-
}
117
-
}
118
-
119
-
Text {
120
-
text: Networking.networks?.count === 0 ? "No networks found" : ""
121
-
color: Settings.colors.foreground
122
-
opacity: 0.5
123
-
font.pixelSize: 12
124
-
visible: Networking.networks?.count === 0
125
-
Layout.alignment: Qt.AlignHCenter
126
-
}
127
-
128
-
Item { Layout.fillHeight: true }
129
-
}
130
-
}
131
-
132
-
// Bluetooth Panel (expandable)
133
-
Rectangle {
134
-
id: bluetoothPanel
135
-
Layout.fillWidth: true
136
-
Layout.preferredHeight: visible ? Math.min(bluetoothColumn.implicitHeight + 20, 350) : 0
137
-
visible: false
138
-
radius: 8
139
-
color: Settings.colors.backgroundLighter
140
-
clip: true
141
-
142
-
Behavior on Layout.preferredHeight {
143
-
NumberAnimation { duration: 150; easing.type: Easing.OutCubic }
144
-
}
145
-
146
-
ColumnLayout {
147
-
id: bluetoothColumn
148
-
anchors.fill: parent
149
-
anchors.margins: 12
150
-
spacing: 8
151
-
152
-
RowLayout {
153
-
Layout.fillWidth: true
154
-
155
-
Text {
156
-
text: "Bluetooth Devices"
157
-
color: Settings.colors.foreground
158
-
font {
159
-
pixelSize: 13
160
-
weight: Font.Medium
161
-
}
162
-
}
163
-
164
-
Item { Layout.fillWidth: true }
165
-
166
-
// Scan button
167
-
IconButton {
168
-
icon: Bluetooth.adapter?.discovering ? "process-stop-symbolic" : "view-refresh-symbolic"
169
-
size: 14
170
-
onClicked: {
171
-
if (Bluetooth.adapter?.discovering) {
172
-
Bluetooth.stopDiscovery();
173
-
} else {
174
-
Bluetooth.startDiscovery();
175
-
}
176
-
}
177
-
}
178
-
179
-
Text {
180
-
text: Bluetooth.connected ? Bluetooth.connectedDevice?.name ?? "" : ""
181
-
color: Settings.colors.accent
182
-
font.pixelSize: 11
183
-
visible: Bluetooth.connected
184
-
}
185
-
}
186
-
187
-
ScrollView {
188
-
Layout.fillWidth: true
189
-
Layout.preferredHeight: Math.min(bluetoothListView.contentHeight, 280)
190
-
clip: true
191
-
192
-
ListView {
193
-
id: bluetoothListView
194
-
width: parent.width
195
-
model: Bluetooth.devices
196
-
spacing: 8
197
-
clip: true
198
-
199
-
delegate: PanelListItem {
200
-
required property var modelData
201
-
width: ListView.view.width
202
-
icon: modelData.icon || "bluetooth-symbolic"
203
-
text: modelData.name
204
-
badge: modelData.connected ? "Connected" : (modelData.paired ? "Paired" : "")
205
-
active: modelData.connected
206
-
onClicked: {
207
-
if (modelData.connected) {
208
-
modelData.disconnect();
209
-
} else if (modelData.paired) {
210
-
modelData.connect();
211
-
} else {
212
-
// Pair and connect
213
-
modelData.pair();
214
-
}
215
-
}
216
-
}
217
-
}
218
-
}
219
-
220
-
Text {
221
-
text: {
222
-
if (!Bluetooth.adapter) return "No Bluetooth adapter";
223
-
if (Bluetooth.devices?.count === 0) return "No devices found";
224
-
return "";
225
-
}
226
-
color: Settings.colors.foreground
227
-
opacity: 0.5
228
-
font.pixelSize: 12
229
-
visible: !Bluetooth.adapter || Bluetooth.devices?.count === 0
230
-
Layout.alignment: Qt.AlignHCenter
231
-
}
232
-
233
-
Item { Layout.fillHeight: true }
234
-
}
235
-
}
236
-
237
-
// Toggle Buttons Row
238
-
RowLayout {
239
-
Layout.fillWidth: true
240
-
spacing: 8
241
-
242
-
// WiFi Toggle
243
-
QuickSettingButton {
244
-
icon: Networking.wifiEnabled
245
-
? (Networking.activeWifi?.icon ?? "network-wireless-acquiring-symbolic")
246
-
: "network-wireless-disabled-symbolic"
247
-
label: "WiFi"
248
-
active: Networking.wifiEnabled
249
-
onClicked: Networking.toggleWifi()
250
-
onPressAndHold: networkPanel.visible = !networkPanel.visible
251
-
}
252
-
253
-
// Bluetooth Toggle
254
-
QuickSettingButton {
255
-
icon: Bluetooth.icon
256
-
label: "Bluetooth"
257
-
active: Bluetooth.powered
258
-
onClicked: Bluetooth.toggle()
259
-
onPressAndHold: bluetoothPanel.visible = !bluetoothPanel.visible
260
-
}
261
-
262
-
// Do Not Disturb Toggle
263
-
QuickSettingButton {
264
-
icon: Notifications.dndEnabled ? "notifications-disabled-symbolic" : "preferences-system-notifications-symbolic"
265
-
label: "DND"
266
-
active: Notifications.dndEnabled
267
-
onClicked: Notifications.dndEnabled = !Notifications.dndEnabled
268
-
}
269
-
}
270
-
271
-
// Volume Slider
272
-
PwObjectTracker {
273
-
objects: [Pipewire.defaultAudioSink]
274
-
}
275
-
276
-
StyledSlider {
277
-
icon: {
278
-
const muted = Pipewire.defaultAudioSink?.audio?.muted ?? false;
279
-
const vol = Pipewire.defaultAudioSink?.audio?.volume ?? 0;
280
-
if (muted) return "audio-volume-muted-symbolic";
281
-
if (vol > 0.66) return "audio-volume-high-symbolic";
282
-
if (vol > 0.33) return "audio-volume-medium-symbolic";
283
-
if (vol > 0) return "audio-volume-low-symbolic";
284
-
return "audio-volume-muted-symbolic";
285
-
}
286
-
value: Pipewire.defaultAudioSink?.audio?.volume ?? 0
287
-
iconClickable: true
288
-
onIconClicked: {
289
-
if (Pipewire.defaultAudioSink?.audio) {
290
-
Pipewire.defaultAudioSink.audio.muted = !Pipewire.defaultAudioSink.audio.muted;
291
-
}
292
-
}
293
-
onMoved: (val) => {
294
-
if (Pipewire.defaultAudioSink?.audio) {
295
-
Pipewire.defaultAudioSink.audio.volume = val;
296
-
}
297
-
}
298
-
}
299
-
300
-
// Brightness Slider
301
-
BrightnessHelper { id: brightnessHelper }
302
-
303
-
StyledSlider {
304
-
visible: brightnessHelper.available
305
-
icon: "display-brightness-symbolic"
306
-
value: brightnessHelper.brightness
307
-
from: 0.05
308
-
accentColor: Settings.colors.warning
309
-
onMoved: (val) => brightnessHelper.setBrightness(val)
310
-
}
311
-
312
-
// Quick Setting Button Component
313
-
component QuickSettingButton: Rectangle {
314
-
id: qsButton
315
-
Layout.fillWidth: true
316
-
Layout.preferredHeight: 64
317
-
radius: 8
318
-
color: active ? Settings.colors.accent : Settings.colors.backgroundLighter
319
-
320
-
property string icon: ""
321
-
property string label: ""
322
-
property bool active: false
323
-
324
-
signal clicked()
325
-
signal pressAndHold()
326
-
327
-
ColumnLayout {
328
-
anchors.centerIn: parent
329
-
spacing: 6
330
-
331
-
MyIcon {
332
-
icon: qsButton.icon
333
-
size: 20
334
-
Layout.alignment: Qt.AlignHCenter
335
-
invert: active
336
-
}
337
-
338
-
Text {
339
-
text: qsButton.label
340
-
color: qsButton.active ? Settings.colors.background : Settings.colors.foreground
341
-
font.pixelSize: 11
342
-
Layout.alignment: Qt.AlignHCenter
343
-
}
344
-
}
345
-
346
-
MouseArea {
347
-
anchors.fill: parent
348
-
cursorShape: Qt.PointingHandCursor
349
-
onClicked: qsButton.clicked()
350
-
onPressAndHold: qsButton.pressAndHold()
351
-
}
352
-
}
353
-
354
-
// Brightness Helper Component
355
-
component BrightnessHelper: QtObject {
356
-
id: brightnessObj
357
-
property real brightness: 1.0
358
-
property bool available: false
359
-
360
-
function setBrightness(value) {
361
-
brightness = value;
362
-
setBrightnessProc.command = ["brightnessctl", "set", Math.round(value * 100) + "%"];
363
-
setBrightnessProc.running = true;
364
-
}
365
-
366
-
Component.onCompleted: getBrightness.running = true
367
-
368
-
property Process getBrightness: Process {
369
-
id: getBrightness
370
-
command: ["bash", "-c", "brightnessctl -m 2>/dev/null | cut -d, -f4 | tr -d '%'"]
371
-
stdout: SplitParser {
372
-
onRead: {
373
-
const val = parseInt(data.trim());
374
-
if (!isNaN(val)) {
375
-
brightnessObj.brightness = val / 100;
376
-
brightnessObj.available = true;
377
-
}
378
-
}
379
-
}
380
-
}
381
-
382
-
property Process setBrightnessProc: Process {
383
-
id: setBrightnessProc
384
-
}
385
-
}
386
-
}
387
-
-5
home/isabel/gui/quickshell/data/Settings.qml
-5
home/isabel/gui/quickshell/data/Settings.qml
+8
-8
home/isabel/gui/quickshell/modules/Osd.qml
+8
-8
home/isabel/gui/quickshell/modules/Osd.qml
···
4
4
import Quickshell.Widgets
5
5
import Quickshell.Wayland
6
6
import Quickshell.Services.Pipewire
7
-
import Quickshell.Services.Notifications as QsNotifications
7
+
import Quickshell.Services.Notifications
8
8
import "root:/data"
9
9
import "root:/components"
10
-
import "root:/services"
11
10
12
11
Scope {
13
12
id: root
···
54
53
osdHideTimer.restart();
55
54
}
56
55
57
-
function showNotification(notification: QsNotifications.Notification): void {
56
+
function showNotification(notification: Notification): void {
58
57
root.currentNotification = notification;
59
58
root.notificationVisible = true;
60
59
notificationHideTimer.restart();
···
72
71
onTriggered: root.notificationVisible = false
73
72
}
74
73
75
-
// Connect to the shared notification service
76
-
Connections {
77
-
target: Notifications
78
-
function onNewNotification(notification) {
74
+
// Notification Server
75
+
NotificationServer {
76
+
id: notificationServer
77
+
onNotification: (notification) => {
78
+
notification.tracked = true;
79
79
root.showNotification(notification);
80
80
}
81
+
actionsSupported: true
81
82
}
82
83
83
84
Variants {
···
273
274
}
274
275
}
275
276
}
276
-
-10
home/isabel/gui/quickshell/services/Networking.qml
-10
home/isabel/gui/quickshell/services/Networking.qml
···
45
45
checkStatus.running = true;
46
46
}
47
47
48
-
function connectToNetwork(ssid) {
49
-
connectProc.command = ["nmcli", "d", "wifi", "connect", ssid];
50
-
connectProc.running = true;
51
-
}
52
-
53
48
QtObject {
54
49
id: internal
55
50
property bool wifiEnabled: true
···
107
102
Process {
108
103
id: toggleWifiProc
109
104
command: ["nmcli", "radio", "wifi", internal.wifiEnabled ? "off" : "on"]
110
-
onExited: checkStatus.running = true
111
-
}
112
-
113
-
Process {
114
-
id: connectProc
115
105
onExited: checkStatus.running = true
116
106
}
117
107
+1
-12
home/isabel/gui/quickshell/services/Notifications.qml
+1
-12
home/isabel/gui/quickshell/services/Notifications.qml
···
3
3
import Quickshell
4
4
import Quickshell.Io
5
5
import Quickshell.Services.Notifications
6
-
import "root:/data"
7
6
8
7
Singleton {
9
8
id: root
10
9
11
-
property bool dndEnabled: false
12
-
property list<Notification> list: notifactionServer.trackedNotifications.values.filter(
13
-
notification => notification.tracked && !root.dndEnabled && !Settings.notificationBlacklist.includes(notification.appName)
14
-
)
15
-
16
-
signal newNotification(Notification notification)
10
+
property list<Notification> list: notifactionServer.trackedNotifications.values.filter(notification => notification.tracked)
17
11
18
12
NotificationServer {
19
13
id: notifactionServer
20
14
onNotification: (notification) => {
21
15
notification.tracked = true
22
-
// Only emit signal if not in DND and not blacklisted
23
-
if (!root.dndEnabled && !Settings.notificationBlacklist.includes(notification.appName)) {
24
-
root.newNotification(notification)
25
-
}
26
16
}
27
17
28
18
actionsSupported: true
···
38
28
}
39
29
}
40
30
}
41
-
+3
-3
justfile
+3
-3
justfile
···
29
29
deploy host *args:
30
30
#!/usr/bin/env bash
31
31
set -euo pipefail
32
-
before=$(ssh -q {{ host }} 'readlink /run/current-system')
32
+
before=$(ssh {{ host }} 'readlink /run/current-system')
33
33
just builder switch --target-host {{ host }} --use-substitutes {{ args }}
34
34
35
35
if [[ -n "${DEPLOY_SUMMARY:-}" ]]; then
36
36
{
37
37
echo "===== {{ host }} ====="
38
-
ssh -q {{ host }} lix diff "$before"
38
+
ssh {{ host }} lix diff "$before"
39
39
echo
40
40
} >> "$DEPLOY_SUMMARY"
41
41
else
···
60
60
echo
61
61
echo "===== DEPLOYMENT SUMMARY ====="
62
62
cat "$DEPLOY_SUMMARY"
63
-
rm "$DEPLOY_SUMMARY"
63
+
@rm "$DEPLOY_SUMMARY"
64
64
65
65
# rebuild the boot
66
66
[group('rebuild')]
-2
modules/nixos/hardware/gpu/nvidia.nix
-2
modules/nixos/hardware/gpu/nvidia.nix
+29
modules/nixos/networking/default.nix
+29
modules/nixos/networking/default.nix
···
50
50
"2620:fe::fe"
51
51
];
52
52
53
+
# hosts = {
54
+
# "2a01:4f8:c010:d56::2" = [ "github.com" ];
55
+
# "2a01:4f8:c010:d56::3" = [ "api.github.com" ];
56
+
# "2a01:4f8:c010:d56::4" = [ "codeload.github.com" ];
57
+
# "2a01:4f8:c010:d56::8" = [ "uploads.github.com" ];
58
+
# "2606:50c0:8000::133" = [
59
+
# "objects.githubusercontent.com"
60
+
# "www.objects.githubusercontent.com"
61
+
# "release-assets.githubusercontent.com"
62
+
# "gist.githubusercontent.com"
63
+
# "repository-images.githubusercontent.com"
64
+
# "camo.githubusercontent.com"
65
+
# "private-user-images.githubusercontent.com"
66
+
# "avatars0.githubusercontent.com"
67
+
# "avatars1.githubusercontent.com"
68
+
# "avatars2.githubusercontent.com"
69
+
# "avatars3.githubusercontent.com"
70
+
# "cloud.githubusercontent.com"
71
+
# "desktop.githubusercontent.com"
72
+
# ];
73
+
# "2606:50c0:8000::154" = [
74
+
# "support-assets.githubassets.com"
75
+
# "github.githubassets.com"
76
+
# "opengraph.githubassets.com"
77
+
# "github-registry-files.githubusercontent.com"
78
+
# "github-cloud.githubusercontent.com"
79
+
# ];
80
+
# };
81
+
53
82
enableIPv6 = true;
54
83
};
55
84
}
+1
-1
modules/nixos/system/scheduler.nix
+1
-1
modules/nixos/system/scheduler.nix
+3
-3
secrets/services/piper.yaml
+3
-3
secrets/services/piper.yaml
···
1
-
env: ENC[AES256_GCM,data:/bJSpMnpO/P5pen9RKdsRF47veCmkRstBFUwbdyu+fp3UDayUrL2u+/397XQQxBQYl266wJOQB7sjJ5lsOI/yRiM/89yv4zow7Qh/akPnsYyhxiLAjdIdJHFzh6tQMBZiHEHNF16edhejKMgIz0qOIvIQZu0yHX6ozvWfc7KKzYH3CUohbhIjgjqbJfzP/YMFNnqu6Aa4mlksprCeVPg/ZX3vf4z/DS9vIhmUbkc91064gjytMq3EFZvVt01aD3Wror/ApBCtCZ1Pt8zAbH/mrQd2Dsx9pOKueqYhTGcF6nsU5fxkKG62K0nK3xGOS2vOpl4k9m220k5BF1CbSKbWXFBafvtvorYbBBK+N4caBKI6DQz66/eA75D0DwA0KbBKadboqzRZOWFsvqQwIuiqnkzJottJZ2swGbyVjhLsAQEmZI/b6Ie0I0=,iv:Z1gnEHoJ8eBDWn1IorUn/pT+ghxrB7+nk9boHx7efWg=,tag:zNJRN/JeWP98TDxfFDdElw==,type:str]
1
+
env: ENC[AES256_GCM,data:scYfFEXl6esjrYaTLbFbeqQVnOw6h2ZzZVQ2EUsCxnYzaI+rhCTVqkrqXgowFAILWwQOiYM7B5ukgqTytiB5h6C2Bh/xa/VdMOPdY3EXqRed7NzZC96LZt3+dKDKKDbkw1wyddWvX/phQFk/cHq6BR5CQZfQ7nPXRr2ofZGoK/3m+GhouyJq2I4RHHRGxv0WXYniHkR9zgdc+myg1KsFphu9XaoR4WN/uaGACv59pguMJWRcBzr+wKPxdWtrzb4vTIVQiF8wpTSdWBmnwmKq29y/pBSNivDdhWB5N1/VHGFDRge5RzxXCg98Hv4fegebx4+YPiVlwoC2peGSi/q8EeNLFES+waywaKmbQ1r54AAFqGnXIIp16Ib5lCUa+VwY2jQl9c+QZOcNEJkVZ6jWmuoUguO1q2y4WbLvcaWksjRXJHs/NOsQoF4=,iv:b8O+3GksPP4hTuH57IxgAeYe6MAiYGO83nJKn7MVn6Q=,tag:V1dFrgABjL6SXN7af1DtgA==,type:str]
2
2
sops:
3
3
age:
4
4
- recipient: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMQDiHbMSinj8twL9cTgPOfI6OMexrTZyHX27T8gnMj2
···
71
71
lm9UxzoNda1OtptX52t1sWVYkx1pwvlTbTmuNy/8uVufZVekdPBz+pywuJbSJlh2
72
72
bH8K6Q==
73
73
-----END AGE ENCRYPTED FILE-----
74
-
lastmodified: "2026-01-07T01:01:47Z"
75
-
mac: ENC[AES256_GCM,data:ThIfvJcDdWqprQ0i66bTy29QOcPzWjIYCB8aYhdeEIPzBOjQDKgVh3YNXVaMC7/2tBJmC/IQbxvJ/pIPP/UICSxFvqS+Ta6wipRlTs2A23gaoaVXwj7nY8oMwUH4w04NbWuNFavQ/gnKYBEOmqDBsLrZ6+v5IPxfxx/fRapMKMM=,iv:OYGE7Ja0X6WNHswwYwQ8VsDSlb3U/sm47tsTnJ5wspc=,tag:5Pcfd0/eSL36rFNp8sH6qA==,type:str]
74
+
lastmodified: "2025-11-14T16:08:46Z"
75
+
mac: ENC[AES256_GCM,data:jd968uS+JcdIg1eDyo1ssHSVw/cxXVwuIDE6YdJrZCHpCf/XSHIWvlu6+L3LILixhV45eMdI81GKCMR3uBnXaXf6SOEPWZ2BJc9cT+p1seAPR1surmVA8d2GGKSWuGmi7mFW30kLsDJhRk1pybKpIHf/Jxdgx0cuh95DEoOtoPg=,iv:+XUddLha4hj68zCcnHiH5aEJU6buzKV+FNUO8ZXQofY=,tag:wlmooDkxQJyPD883cwRdSA==,type:str]
76
76
unencrypted_suffix: _unencrypted
77
77
version: 3.11.0