summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--_sources/generated.json20
-rw-r--r--_sources/generated.nix14
-rw-r--r--accounts/gkleen@sif/default.nix27
-rw-r--r--accounts/gkleen@sif/niri/default.nix60
-rw-r--r--accounts/gkleen@sif/niri/mako.nix8
-rw-r--r--accounts/gkleen@sif/niri/swayosd.nix65
-rw-r--r--accounts/gkleen@sif/niri/waybar.nix49
-rw-r--r--accounts/gkleen@sif/systemd.nix3
-rw-r--r--flake.lock26
-rw-r--r--hosts/sif/default.nix2
-rw-r--r--nvfetcher.toml6
-rw-r--r--overlays/keepassxc/database-open-dialog.patch126
-rw-r--r--overlays/keepassxc/default.nix8
-rw-r--r--overlays/swayosd.nix27
-rwxr-xr-xoverlays/worktime/worktime/__main__.py91
15 files changed, 449 insertions, 83 deletions
diff --git a/_sources/generated.json b/_sources/generated.json
index e82d7fe6..72f913ec 100644
--- a/_sources/generated.json
+++ b/_sources/generated.json
@@ -365,6 +365,26 @@
365 }, 365 },
366 "version": "0.2.1" 366 "version": "0.2.1"
367 }, 367 },
368 "swayosd": {
369 "cargoLocks": null,
370 "date": "2025-01-27",
371 "extract": null,
372 "name": "swayosd",
373 "passthru": null,
374 "pinned": false,
375 "src": {
376 "deepClone": false,
377 "fetchSubmodules": false,
378 "leaveDotGit": false,
379 "name": null,
380 "rev": "993180b5e7db1dfc453a556bf208f05b04283c8f",
381 "sha256": "sha256-qwtGkRJlCYu+dO3xCmnRexX+E4QvXRAHXUslLO7mrAI=",
382 "sparseCheckout": [],
383 "type": "git",
384 "url": "https://github.com/ErikReider/SwayOSD"
385 },
386 "version": "993180b5e7db1dfc453a556bf208f05b04283c8f"
387 },
368 "tomorrow-night-paradise-theme": { 388 "tomorrow-night-paradise-theme": {
369 "cargoLocks": null, 389 "cargoLocks": null,
370 "date": "2012-06-04", 390 "date": "2012-06-04",
diff --git a/_sources/generated.nix b/_sources/generated.nix
index c1a0c6a0..e25f1bda 100644
--- a/_sources/generated.nix
+++ b/_sources/generated.nix
@@ -224,6 +224,20 @@
224 sha256 = "sha256-7d/0fepOvdswuBGJCCMULB2kXOFBLP78yqX4NmByCF8="; 224 sha256 = "sha256-7d/0fepOvdswuBGJCCMULB2kXOFBLP78yqX4NmByCF8=";
225 }; 225 };
226 }; 226 };
227 swayosd = {
228 pname = "swayosd";
229 version = "993180b5e7db1dfc453a556bf208f05b04283c8f";
230 src = fetchgit {
231 url = "https://github.com/ErikReider/SwayOSD";
232 rev = "993180b5e7db1dfc453a556bf208f05b04283c8f";
233 fetchSubmodules = false;
234 deepClone = false;
235 leaveDotGit = false;
236 sparseCheckout = [ ];
237 sha256 = "sha256-qwtGkRJlCYu+dO3xCmnRexX+E4QvXRAHXUslLO7mrAI=";
238 };
239 date = "2025-01-27";
240 };
227 tomorrow-night-paradise-theme = { 241 tomorrow-night-paradise-theme = {
228 pname = "tomorrow-night-paradise-theme"; 242 pname = "tomorrow-night-paradise-theme";
229 version = "70225a5bf90d495e13a9260bfdc268632ece0801"; 243 version = "70225a5bf90d495e13a9260bfdc268632ece0801";
diff --git a/accounts/gkleen@sif/default.nix b/accounts/gkleen@sif/default.nix
index b47e25a4..58cfb425 100644
--- a/accounts/gkleen@sif/default.nix
+++ b/accounts/gkleen@sif/default.nix
@@ -80,7 +80,7 @@ let
80 ]; 80 ];
81 }; 81 };
82 82
83 lockCommand = "${config.systemd.package}/bin/systemctl --user start gtklock.service"; 83 lockCommand = "${lib.getExe' config.systemd.package "systemctl"} --user start gtklock.service";
84in { 84in {
85 imports = with flake.nixosModules.userProfiles.${userName}; [ 85 imports = with flake.nixosModules.userProfiles.${userName}; [
86 mpv yt-dlp (args: import ./xcompose.nix (inputs // args)) 86 mpv yt-dlp (args: import ./xcompose.nix (inputs // args))
@@ -319,6 +319,7 @@ in {
319 }; 319 };
320 device_config = [ 320 device_config = [
321 { loop_file = "/nix/store/*-etc-metadata.erofs"; is_mounted = false; ignore = true; } 321 { loop_file = "/nix/store/*-etc-metadata.erofs"; is_mounted = false; ignore = true; }
322 { mount_path = "/run/nixos-etc-metadata"; ignore = true; }
322 { mount_path = "/run/nixos-etc-metadata.*"; ignore = true; } 323 { mount_path = "/run/nixos-etc-metadata.*"; ignore = true; }
323 ]; 324 ];
324 icon_names.media = ["drive-removable-media-symbolic"]; 325 icon_names.media = ["drive-removable-media-symbolic"];
@@ -358,31 +359,17 @@ in {
358 enable = true; 359 enable = true;
359 events = [ 360 events = [
360 { event = "before-sleep"; command = lockCommand; } 361 { event = "before-sleep"; command = lockCommand; }
361 # { event = "after-resume"; command = "${cfg.wayland.windowManager.hyprland.package}/bin/hyprctl dispatch dpms on"; }
362 { event = "lock"; command = lockCommand; } 362 { event = "lock"; command = lockCommand; }
363 ]; 363 ];
364 timeouts = [ 364 timeouts = [
365 # { timeout = 300;
366 # command = "${cfg.wayland.windowManager.hyprland.package}/bin/hyprctl dispatch dpms off";
367 # }
368 { timeout = 330; command = lockCommand; } 365 { timeout = 330; command = lockCommand; }
369 ]; 366 ];
370 extraArgs = [ 367 extraArgs = [
368 "-w"
371 "idlehint" "30" 369 "idlehint" "30"
372 ]; 370 ];
373 }; 371 };
374 poweralertd.enable = true; 372 poweralertd.enable = true;
375 avizo = {
376 enable = true;
377 settings.default = {
378 time = "1.0";
379 background = "rgba(0, 0, 0, 0.8)";
380 border-color = "rgba(0, 0, 0, 1)";
381 bar-fg-color = "rgba(160, 160, 160, 1)";
382 bar-bg-color = "rgba(32, 32, 32, 0.96)";
383 # y-offset = "0.25";
384 };
385 };
386 }; 373 };
387 374
388 home.pointerCursor = { 375 home.pointerCursor = {
@@ -508,6 +495,10 @@ in {
508 [Unit] 495 [Unit]
509 Before=graphical-session-pre.target 496 Before=graphical-session-pre.target
510 ''; 497 '';
498 "pdfpc/pdfpcrc".text = ''
499 mouse 8 prev
500 mouse 9 next
501 '';
511 }; 502 };
512 503
513 xdg.dataFile = { 504 xdg.dataFile = {
@@ -625,7 +616,6 @@ in {
625 --property 'CPUAccounting=yes' --property 'CPUQuotaPeriodSec=50ms' \ 616 --property 'CPUAccounting=yes' --property 'CPUQuotaPeriodSec=50ms' \
626 --property 'Environment=DSCP=46' \ 617 --property 'Environment=DSCP=46' \
627 -- ${pkgs.dscp}/bin/dscp ${pkgs.google-chrome}/bin/google-chrome-stable \ 618 -- ${pkgs.dscp}/bin/dscp ${pkgs.google-chrome}/bin/google-chrome-stable \
628 --force-device-scale-factor=1.5 \
629 --class=Rainbow \ 619 --class=Rainbow \
630 --kiosk "https://web.openrainbow.com" \ 620 --kiosk "https://web.openrainbow.com" \
631 --user-data-dir=''${HOME}/.config/google-chrome-rainbow 621 --user-data-dir=''${HOME}/.config/google-chrome-rainbow
@@ -634,6 +624,9 @@ in {
634 url = "https://web.openrainbow.com/rb/2.139.17/assets/skins/rainbow/images/homepage/logo__rainbow.svg"; 624 url = "https://web.openrainbow.com/rb/2.139.17/assets/skins/rainbow/images/homepage/logo__rainbow.svg";
635 hash = "sha256-5fmo8rDqVDpzkGaPjk4Y+SsSZpAsY7VUQSFW6WdHwuU="; 625 hash = "sha256-5fmo8rDqVDpzkGaPjk4Y+SsSZpAsY7VUQSFW6WdHwuU=";
636 }; 626 };
627 settings = {
628 StartupWMClass = "Rainbow";
629 };
637 }; 630 };
638 }; 631 };
639 632
diff --git a/accounts/gkleen@sif/niri/default.nix b/accounts/gkleen@sif/niri/default.nix
index bf124211..bfc32254 100644
--- a/accounts/gkleen@sif/niri/default.nix
+++ b/accounts/gkleen@sif/niri/default.nix
@@ -2,11 +2,10 @@
2let 2let
3 niri = config.programs.niri.package; 3 niri = config.programs.niri.package;
4 terminal = lib.getExe config.programs.kitty.package; 4 terminal = lib.getExe config.programs.kitty.package;
5 lightctl = lib.getExe' config.services.avizo.package "lightctl";
6 volumectl = lib.getExe' config.services.avizo.package "volumectl";
7 makoctl = lib.getExe' config.services.mako.package "makoctl"; 5 makoctl = lib.getExe' config.services.mako.package "makoctl";
8 loginctl = lib.getExe' hostConfig.systemd.package "loginctl"; 6 loginctl = lib.getExe' hostConfig.systemd.package "loginctl";
9 systemctl = lib.getExe' hostConfig.systemd.package "systemctl"; 7 systemctl = lib.getExe' hostConfig.systemd.package "systemctl";
8 swayosd-client = lib.getExe' config.services.swayosd.package "swayosd-client";
10 9
11 focus_or_spawn = pkgs.writeShellApplication { 10 focus_or_spawn = pkgs.writeShellApplication {
12 name = "focus-or-spawn"; 11 name = "focus-or-spawn";
@@ -75,7 +74,7 @@ let
75 jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET" 74 jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET"
76 ''; 75 '';
77 }; 76 };
78 with-adjacent-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_adjacent_workspace) "^pwctl|kpxc|bmgr|edit|term$"; 77 with-adjacent-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_adjacent_workspace) "^pwctl|eff|kpxc|bmgr|edit|term$";
79 focus-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}''; 78 focus-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}'';
80 move-column-to-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}}}}''; 79 move-column-to-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}}}}'';
81 80
@@ -122,6 +121,7 @@ in {
122 imports = [ 121 imports = [
123 ./waybar.nix 122 ./waybar.nix
124 ./mako.nix 123 ./mako.nix
124 ./swayosd.nix
125 ]; 125 ];
126 126
127 config = { 127 config = {
@@ -163,10 +163,15 @@ in {
163 hotkey-overlay.skip-at-startup = true; 163 hotkey-overlay.skip-at-startup = true;
164 164
165 input = { 165 input = {
166 keyboard.xkb = { 166 keyboard = {
167 layout = "us,us"; 167 repeat-delay = 300;
168 variant = "dvp,"; 168 repeat-rate = 50;
169 options = "compose:caps,grp:win_space_toggle"; 169
170 xkb = {
171 layout = "us,us";
172 variant = "dvp,";
173 options = "compose:caps,grp:win_space_toggle";
174 };
170 }; 175 };
171 176
172 workspace-auto-back-and-forth = true; 177 workspace-auto-back-and-forth = true;
@@ -202,6 +207,10 @@ in {
202 207
203 debug.render-drm-device = "/dev/dri/by-path/pci-0000:00:02.0-render"; 208 debug.render-drm-device = "/dev/dri/by-path/pci-0000:00:02.0-render";
204 209
210 animations = {
211 slowdown = 0.5;
212 };
213
205 layout = { 214 layout = {
206 gaps = 8; 215 gaps = 8;
207 struts = { left = 0; right = 0; top = 0; bottom = 0; }; 216 struts = { left = 0; right = 0; top = 0; bottom = 0; };
@@ -255,6 +264,7 @@ in {
255 "003" = { name = "bmgr"; open-on-output = "eDP-1"; }; 264 "003" = { name = "bmgr"; open-on-output = "eDP-1"; };
256 "004" = { name = "term"; open-on-output = "eDP-1"; }; 265 "004" = { name = "term"; open-on-output = "eDP-1"; };
257 "005" = { name = "edit"; open-on-output = "eDP-1"; }; 266 "005" = { name = "edit"; open-on-output = "eDP-1"; };
267 "006" = { name = "eff"; open-on-output = "eDP-1"; };
258 "101".name = "comm"; 268 "101".name = "comm";
259 "102".name = "web"; 269 "102".name = "web";
260 # "104".name = "read"; 270 # "104".name = "read";
@@ -264,19 +274,25 @@ in {
264 }; 274 };
265 275
266 window-rules = [ 276 window-rules = [
267 # { 277 {
268 # geometry-corner-radius = 278 matches = [ { is-floating = true; } ];
269 # let 279 geometry-corner-radius =
270 # allCorners = r: { bottom-left = r; bottom-right = r; top-left = r; top-right = r; }; 280 let
271 # in allCorners 4.; 281 allCorners = r: { bottom-left = r; bottom-right = r; top-left = r; top-right = r; };
272 # clip-to-geometry = true; 282 in allCorners 8.;
273 # } 283 clip-to-geometry = true;
284 }
274 { 285 {
275 matches = [ { app-id = "^com\.saivert\.pwvucontrol$"; } ]; 286 matches = [ { app-id = "^com\.saivert\.pwvucontrol$"; } ];
276 open-on-workspace = "pwctl"; 287 open-on-workspace = "pwctl";
277 open-maximized = true; 288 open-maximized = true;
278 } 289 }
279 { 290 {
291 matches = [ { app-id = "^com\.github\.wwmm\.easyeffects$"; } ];
292 open-on-workspace = "eff";
293 open-maximized = true;
294 }
295 {
280 matches = [ { app-id = "^\.blueman-manager-wrapped$"; } ]; 296 matches = [ { app-id = "^\.blueman-manager-wrapped$"; } ];
281 open-on-workspace = "bmgr"; 297 open-on-workspace = "bmgr";
282 open-maximized = true; 298 open-maximized = true;
@@ -303,6 +319,7 @@ in {
303 { app-id = "^org\.keepassxc\.KeePassXC$"; title = ".*Passkey credentials$"; } 319 { app-id = "^org\.keepassxc\.KeePassXC$"; title = ".*Passkey credentials$"; }
304 ]; 320 ];
305 open-focused = true; 321 open-focused = true;
322 open-floating = true;
306 } 323 }
307 { 324 {
308 matches = [ { app-id = "^kitty-scratch$"; } ]; 325 matches = [ { app-id = "^kitty-scratch$"; } ];
@@ -332,6 +349,7 @@ in {
332 matches = [ 349 matches = [
333 { app-id = "^thunderbird$"; } 350 { app-id = "^thunderbird$"; }
334 { app-id = "^Element$"; } 351 { app-id = "^Element$"; }
352 { app-id = "^Rainbow$"; }
335 ]; 353 ];
336 open-on-workspace = "comm"; 354 open-on-workspace = "comm";
337 } 355 }
@@ -380,6 +398,7 @@ in {
380 matches = [ 398 matches = [
381 { app-id = "^Gimp-"; title = "^Quit GIMP$"; } 399 { app-id = "^Gimp-"; title = "^Quit GIMP$"; }
382 { app-id = "^org\.kde\.polkit-kde-authentication-agent-1$"; } 400 { app-id = "^org\.kde\.polkit-kde-authentication-agent-1$"; }
401 { app-id = "^xdg-desktop-portal-gtk$"; }
383 ]; 402 ];
384 open-floating = true; 403 open-floating = true;
385 } 404 }
@@ -570,27 +589,27 @@ in {
570 }; 589 };
571 590
572 "XF86MonBrightnessUp" = { 591 "XF86MonBrightnessUp" = {
573 action = spawn lightctl "-d" "-e4" "-n1" "up"; 592 action = spawn swayosd-client "--brightness" "raise";
574 allow-when-locked = true; 593 allow-when-locked = true;
575 }; 594 };
576 "XF86MonBrightnessDown" = { 595 "XF86MonBrightnessDown" = {
577 action = spawn lightctl "-d" "-e4" "-n1" "down"; 596 action = spawn swayosd-client "--brightness" "lower";
578 allow-when-locked = true; 597 allow-when-locked = true;
579 }; 598 };
580 "XF86AudioRaiseVolume" = { 599 "XF86AudioRaiseVolume" = {
581 action = spawn volumectl "-d" "-u" "up"; 600 action = spawn swayosd-client "--output-volume" "raise";
582 allow-when-locked = true; 601 allow-when-locked = true;
583 }; 602 };
584 "XF86AudioLowerVolume" = { 603 "XF86AudioLowerVolume" = {
585 action = spawn volumectl "-d" "-u" "down"; 604 action = spawn swayosd-client "--output-volume" "lower";
586 allow-when-locked = true; 605 allow-when-locked = true;
587 }; 606 };
588 "XF86AudioMute" = { 607 "XF86AudioMute" = {
589 action = spawn volumectl "-d" "toggle-mute"; 608 action = spawn swayosd-client "--output-volume" "mute-toggle";
590 allow-when-locked = true; 609 allow-when-locked = true;
591 }; 610 };
592 "XF86AudioMicMute" = { 611 "XF86AudioMicMute" = {
593 action = spawn volumectl "-d" "-m" "toggle-mute"; 612 action = spawn swayosd-client "--input-volume" "mute-toggle";
594 allow-when-locked = true; 613 allow-when-locked = true;
595 }; 614 };
596 615
@@ -600,6 +619,7 @@ in {
600 "Mod+Comma".action = spawn makoctl "restore"; 619 "Mod+Comma".action = spawn makoctl "restore";
601 620
602 "Mod+Control+A".action = focus-or-spawn-action-app_id "com.saivert.pwvucontrol" "pwctl" "pwvucontrol"; 621 "Mod+Control+A".action = focus-or-spawn-action-app_id "com.saivert.pwvucontrol" "pwctl" "pwvucontrol";
622 "Mod+Control+O".action = focus-or-spawn-action-app_id "com.github.wwmm.easyeffects" "eff" "easyeffects";
603 "Mod+Control+P".action = focus-or-spawn-action-app_id "org.keepassxc.KeePassXC" "kpxc" "keepassxc"; 623 "Mod+Control+P".action = focus-or-spawn-action-app_id "org.keepassxc.KeePassXC" "kpxc" "keepassxc";
604 "Mod+Control+B".action = focus-or-spawn-action-app_id ".blueman-manager-wrapped" "bmgr" "blueman-manager"; 624 "Mod+Control+B".action = focus-or-spawn-action-app_id ".blueman-manager-wrapped" "bmgr" "blueman-manager";
605 "Mod+Control+Return".action = focus-or-spawn-action-app_id "kitty-scratch" "term" "kitty" "--app-id" "kitty-scratch"; 625 "Mod+Control+Return".action = focus-or-spawn-action-app_id "kitty-scratch" "term" "kitty" "--app-id" "kitty-scratch";
diff --git a/accounts/gkleen@sif/niri/mako.nix b/accounts/gkleen@sif/niri/mako.nix
index 8fbc81c1..0a10555a 100644
--- a/accounts/gkleen@sif/niri/mako.nix
+++ b/accounts/gkleen@sif/niri/mako.nix
@@ -30,6 +30,14 @@
30 [mode=silent] 30 [mode=silent]
31 invisible=1 31 invisible=1
32 ''; 32 '';
33 package = pkgs.symlinkJoin {
34 name = "${pkgs.mako.name}-wrapped";
35 paths = with pkgs; [ mako ];
36 inherit (pkgs.mako) meta;
37 postBuild = ''
38 rm -r $out/share/dbus-1
39 '';
40 };
33 }; 41 };
34 systemd.user.services.mako = { 42 systemd.user.services.mako = {
35 Unit = { 43 Unit = {
diff --git a/accounts/gkleen@sif/niri/swayosd.nix b/accounts/gkleen@sif/niri/swayosd.nix
new file mode 100644
index 00000000..984927c2
--- /dev/null
+++ b/accounts/gkleen@sif/niri/swayosd.nix
@@ -0,0 +1,65 @@
1{ pkgs, ... }:
2{
3 config = {
4 services.swayosd = {
5 enable = true;
6 topMargin = 0.946154;
7 stylePath = pkgs.runCommand "style.css" {
8 src = pkgs.writeText "style.scss" ''
9 window#osd {
10 padding: 12px 20px;
11 border-radius: 999px;
12 border: none;
13 background: rgba(0, 0, 0, 0.87);
14
15 #container {
16 margin: 16px;
17 }
18
19 image,
20 label {
21 color: rgb(255, 255, 255);
22
23 &:disabled {
24 opacity: 1;
25 color: rgb(84, 84, 84);
26 }
27 }
28
29 progressbar {
30 min-height: 6px;
31 border-radius: 999px;
32 background: transparent;
33 border: none;
34
35 trough, progress {
36 min-height: inherit;
37 border-radius: inherit;
38 border: none;
39 }
40
41 trough {
42 background: rgb(127, 127, 127);
43 }
44 progress {
45 background: rgb(255, 255, 255);
46 }
47
48 &:disabled {
49 opacity: 1;
50
51 trough {
52 background: rgb(19, 19, 19);
53 }
54 progress {
55 background: rgb(38, 38, 38);
56 }
57 }
58 }
59 }
60 '';
61 buildInputs = with pkgs; [sass];
62 } "scss -C --sourcemap=none --style=compact $src $out";
63 };
64 };
65}
diff --git a/accounts/gkleen@sif/niri/waybar.nix b/accounts/gkleen@sif/niri/waybar.nix
index 79c429f8..3f1f8119 100644
--- a/accounts/gkleen@sif/niri/waybar.nix
+++ b/accounts/gkleen@sif/niri/waybar.nix
@@ -1,5 +1,7 @@
1{ lib, pkgs, ... }: 1{ lib, config, pkgs, ... }:
2{ 2let
3 swayosd-client = lib.getExe' config.services.swayosd.package "swayosd-client";
4in {
3 config = { 5 config = {
4 programs.waybar = { 6 programs.waybar = {
5 enable = true; 7 enable = true;
@@ -22,7 +24,7 @@
22 output = [ "eDP-1" "DP-2" "DP-3" ]; 24 output = [ "eDP-1" "DP-2" "DP-3" ];
23 modules-left = [ "niri/workspaces" ]; 25 modules-left = [ "niri/workspaces" ];
24 modules-center = [ "niri/window" ]; 26 modules-center = [ "niri/window" ];
25 modules-right = [ # "custom/worktime" "custom/worktime-today" 27 modules-right = [ "custom/worktime" "custom/worktime-today"
26 "custom/weather" 28 "custom/weather"
27 "custom/keymap" 29 "custom/keymap"
28 "privacy" "tray" "wireplumber" "backlight" "battery" "idle_inhibitor" "custom/mako" "clock" ]; 30 "privacy" "tray" "wireplumber" "backlight" "battery" "idle_inhibitor" "custom/mako" "clock" ];
@@ -59,7 +61,7 @@
59 text = f"<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>" # noqa: E501 61 text = f"<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>" # noqa: E501
60 if is_silent: 62 if is_silent:
61 text = f"<span color=\"#ffffff\">{text}</span>" 63 text = f"<span color=\"#ffffff\">{text}</span>"
62 print(json.dumps({'text': text}, separators=(',', ':')), flush=True) # noqa: E501 64 print(json.dumps({'text': text, 'tooltip': ', '.join(modes)}, separators=(',', ':')), flush=True) # noqa: E501
63 65
64 async def on_properties_changed(interface_name, changed_properties, invalidated_properties): # noqa: E501 66 async def on_properties_changed(interface_name, changed_properties, invalidated_properties): # noqa: E501
65 if "Modes" not in invalidated_properties: 67 if "Modes" not in invalidated_properties:
@@ -120,16 +122,16 @@
120 }; 122 };
121 "custom/worktime" = { 123 "custom/worktime" = {
122 interval = 60; 124 interval = 60;
123 exec = lib.getExe pkgs.worktime; 125 exec = "${lib.getExe pkgs.worktime} time --waybar";
124 tooltip = false; 126 return-type = "json";
125 }; 127 };
126 "custom/worktime-today" = { 128 "custom/worktime-today" = {
127 interval = 60; 129 interval = 60;
128 exec = "${lib.getExe pkgs.worktime} today"; 130 exec = "${lib.getExe pkgs.worktime} today --waybar";
129 tooltip = false; 131 return-type = "json";
130 }; 132 };
131 "niri/workspaces" = { 133 "niri/workspaces" = {
132 ignore = ["pwctl" "kpxc" "bmgr" "edit" "term"]; 134 ignore = ["eff" "pwctl" "kpxc" "bmgr" "edit" "term"];
133 }; 135 };
134 "niri/window" = { 136 "niri/window" = {
135 separate-outputs = true; 137 separate-outputs = true;
@@ -190,8 +192,8 @@
190 icon-size = iconSize; 192 icon-size = iconSize;
191 tooltip-format = "{percent}%"; 193 tooltip-format = "{percent}%";
192 format-icons = ["&#xf00da;" "&#xf00db;" "&#xf00dc;" "&#xf00dd;" "&#xf00de;" "&#xf00df;" "&#xf00e0;"]; 194 format-icons = ["&#xf00da;" "&#xf00db;" "&#xf00dc;" "&#xf00dd;" "&#xf00de;" "&#xf00df;" "&#xf00e0;"];
193 on-scroll-up = "lightctl -d -e4 -n1 up"; 195 on-scroll-up = "${swayosd-client} --brightness raise";
194 on-scroll-down = "lightctl -d -e4 -n1 down"; 196 on-scroll-down = "${swayosd-client} --brightness lower";
195 }; 197 };
196 wireplumber = { 198 wireplumber = {
197 format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>"; 199 format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>";
@@ -200,9 +202,9 @@
200 format-icons = ["&#xf057f;" "&#xf0580;" "&#xf057e;"]; 202 format-icons = ["&#xf057f;" "&#xf0580;" "&#xf057e;"];
201 format-muted = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">&#xf075f;</span>"; 203 format-muted = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">&#xf075f;</span>";
202 # ignored-sinks = ["Easy Effects Sink"]; 204 # ignored-sinks = ["Easy Effects Sink"];
203 on-scroll-up = "volumectl -d -u up"; 205 on-scroll-up = "${swayosd-client} --output-volume raise";
204 on-scroll-down = "volumectl -d -u down"; 206 on-scroll-down = "${swayosd-client} --output-volume lower";
205 on-click = "volumectl -d toggle-mute"; 207 on-click = "${swayosd-client} --output-volume mute-toggle";
206 }; 208 };
207 } 209 }
208 { 210 {
@@ -241,7 +243,7 @@
241 243
242 * { 244 * {
243 border: none; 245 border: none;
244 font-family: "Fira Sans Nerd Font"; 246 font-family: "Fira Sans";
245 font-size: 10pt; 247 font-size: 10pt;
246 min-height: 0; 248 min-height: 0;
247 } 249 }
@@ -252,10 +254,10 @@
252 } 254 }
253 255
254 .modules-left { 256 .modules-left {
255 margin-left: 8px; 257 margin-left: 12px;
256 } 258 }
257 .modules-right { 259 .modules-right {
258 margin-right: 8px; 260 margin-right: 12px;
259 } 261 }
260 262
261 .module { 263 .module {
@@ -280,11 +282,12 @@
280 color: @grey; 282 color: @grey;
281 margin: 0 5px; 283 margin: 0 5px;
282 } 284 }
283 #custom-weather, #custom-worktime-today { 285 #custom-weather {
284 margin-right: 3px; 286 margin-right: 3px;
285 } 287 }
286 #custom-keymap, #custom-weather { 288 #custom-keymap {
287 margin-left: 3px; 289 margin-left: 3px;
290 margin-right: 3px;
288 } 291 }
289 292
290 #tray { 293 #tray {
@@ -320,6 +323,12 @@
320 #idle_inhibitor.activated { 323 #idle_inhibitor.activated {
321 color: @white; 324 color: @white;
322 } 325 }
326 #custom-worktime.running, #custom-worktime-today.running {
327 color: @white;
328 }
329 #custom-worktime.over, #custom-worktime-today.over {
330 color: @orange;
331 }
323 332
324 #idle_inhibitor { 333 #idle_inhibitor {
325 padding-top: 1px; 334 padding-top: 1px;
@@ -327,7 +336,7 @@
327 336
328 #privacy { 337 #privacy {
329 color: @red; 338 color: @red;
330 margin: -1px 2px 0px 5px; 339 margin: -1px 4px 0px 3px;
331 } 340 }
332 #clock { 341 #clock {
333 /* margin-right: 5px; */ 342 /* margin-right: 5px; */
diff --git a/accounts/gkleen@sif/systemd.nix b/accounts/gkleen@sif/systemd.nix
index d3a1a4c7..a89b46c2 100644
--- a/accounts/gkleen@sif/systemd.nix
+++ b/accounts/gkleen@sif/systemd.nix
@@ -344,6 +344,9 @@ in {
344 Unit = { 344 Unit = {
345 PartOf = lib.mkForce ["tray.target"]; 345 PartOf = lib.mkForce ["tray.target"];
346 }; 346 };
347 Install = {
348 WantedBy = lib.mkForce ["tray.target"];
349 };
347 }; 350 };
348 } // listToAttrs (map ({host, port}: nameValuePair "proxy-to-autossh-socks@${toString port}" { 351 } // listToAttrs (map ({host, port}: nameValuePair "proxy-to-autossh-socks@${toString port}" {
349 Unit = { 352 Unit = {
diff --git a/flake.lock b/flake.lock
index 1764d172..876ebecf 100644
--- a/flake.lock
+++ b/flake.lock
@@ -397,11 +397,11 @@
397 "xwayland-satellite-unstable": "xwayland-satellite-unstable" 397 "xwayland-satellite-unstable": "xwayland-satellite-unstable"
398 }, 398 },
399 "locked": { 399 "locked": {
400 "lastModified": 1737840481, 400 "lastModified": 1737961005,
401 "narHash": "sha256-WjW3cdrmh1sGMT3CBqCFzT9BOktTa1u9ldoWEqTj7xk=", 401 "narHash": "sha256-b4hqJNgyx8lnngz7NFcJ1W+59xQnMQYF0EK5g0IOy7c=",
402 "owner": "sodiboo", 402 "owner": "sodiboo",
403 "repo": "niri-flake", 403 "repo": "niri-flake",
404 "rev": "8fc9dba8df75d9d004d9369b513b81180788ec15", 404 "rev": "e98ae62893568dd31e7a7e4e75e1dbbf23f759a0",
405 "type": "github" 405 "type": "github"
406 }, 406 },
407 "original": { 407 "original": {
@@ -472,11 +472,11 @@
472 ] 472 ]
473 }, 473 },
474 "locked": { 474 "locked": {
475 "lastModified": 1737257306, 475 "lastModified": 1737861961,
476 "narHash": "sha256-lEGgpA4kGafc76+Amnz+gh1L/cwUS2pePFlf22WEyh8=", 476 "narHash": "sha256-LIRtMvAwLGb8pBoamzgEF67oKlNPz4LuXiRPVZf+TpE=",
477 "owner": "Mic92", 477 "owner": "Mic92",
478 "repo": "nix-index-database", 478 "repo": "nix-index-database",
479 "rev": "744d330659e207a1883d2da0141d35e520eb87bd", 479 "rev": "79b7b8eae3243fc5aa9aad34ba6b9bbb2266f523",
480 "type": "github" 480 "type": "github"
481 }, 481 },
482 "original": { 482 "original": {
@@ -630,11 +630,11 @@
630 }, 630 },
631 "nixpkgs-stable_2": { 631 "nixpkgs-stable_2": {
632 "locked": { 632 "locked": {
633 "lastModified": 1737672001, 633 "lastModified": 1737885640,
634 "narHash": "sha256-YnHJJ19wqmibLQdUeq9xzE6CjrMA568KN/lFPuSVs4I=", 634 "narHash": "sha256-GFzPxJzTd1rPIVD4IW+GwJlyGwBDV1Tj5FLYwDQQ9sM=",
635 "owner": "NixOS", 635 "owner": "NixOS",
636 "repo": "nixpkgs", 636 "repo": "nixpkgs",
637 "rev": "035f8c0853c2977b24ffc4d0a42c74f00b182cd8", 637 "rev": "4e96537f163fad24ed9eb317798a79afc85b51b7",
638 "type": "github" 638 "type": "github"
639 }, 639 },
640 "original": { 640 "original": {
@@ -678,11 +678,11 @@
678 }, 678 },
679 "nixpkgs_2": { 679 "nixpkgs_2": {
680 "locked": { 680 "locked": {
681 "lastModified": 1737842646, 681 "lastModified": 1737885589,
682 "narHash": "sha256-Bw3D+zXAGxcaS32BgXv3A/uLDEXn6/a18cX41USsv+M=", 682 "narHash": "sha256-Zf0hSrtzaM1DEz8//+Xs51k/wdSajticVrATqDrfQjg=",
683 "owner": "nixos", 683 "owner": "NixOS",
684 "repo": "nixpkgs", 684 "repo": "nixpkgs",
685 "rev": "34995559351f3b61c122e5566f1903d500e9b890", 685 "rev": "852ff1d9e153d8875a83602e03fdef8a63f0ecf8",
686 "type": "github" 686 "type": "github"
687 }, 687 },
688 "original": { 688 "original": {
diff --git a/hosts/sif/default.nix b/hosts/sif/default.nix
index 14f08ed5..32651e14 100644
--- a/hosts/sif/default.nix
+++ b/hosts/sif/default.nix
@@ -589,7 +589,7 @@ in {
589 }; 589 };
590 590
591 nvidia = { 591 nvidia = {
592 open = true; 592 open = false;
593 modesetting.enable = true; 593 modesetting.enable = true;
594 powerManagement.enable = true; 594 powerManagement.enable = true;
595 # prime = { 595 # prime = {
diff --git a/nvfetcher.toml b/nvfetcher.toml
index e7ba37e5..ecaebba0 100644
--- a/nvfetcher.toml
+++ b/nvfetcher.toml
@@ -110,4 +110,8 @@ fetch.pypi = "yt_dlp"
110 110
111[mako] 111[mako]
112src.git = "https://github.com/emersion/mako" 112src.git = "https://github.com/emersion/mako"
113fetch.git = "https://github.com/emersion/mako" \ No newline at end of file 113fetch.git = "https://github.com/emersion/mako"
114
115[swayosd]
116src.git = "https://github.com/ErikReider/SwayOSD"
117fetch.git = "https://github.com/ErikReider/SwayOSD"
diff --git a/overlays/keepassxc/database-open-dialog.patch b/overlays/keepassxc/database-open-dialog.patch
new file mode 100644
index 00000000..4916dc1b
--- /dev/null
+++ b/overlays/keepassxc/database-open-dialog.patch
@@ -0,0 +1,126 @@
1diff -u3 -r source.orig/src/browser/BrowserService.cpp source/src/browser/BrowserService.cpp
2--- source.orig/src/browser/BrowserService.cpp 2025-01-27 20:55:04.128198171 +0100
3+++ source/src/browser/BrowserService.cpp 2025-01-27 21:16:07.068959077 +0100
4@@ -249,7 +249,7 @@
5 return result;
6 }
7
8- auto dialogResult = MessageBox::warning(m_currentDatabaseWidget,
9+ auto dialogResult = MessageBox::warning(nullptr,
10 tr("KeePassXC - Create a new group"),
11 tr("A request for creating a new group \"%1\" has been received.\n"
12 "Do you want to create this group?\n")
13@@ -422,7 +422,7 @@
14
15 m_dialogActive = true;
16 updateWindowState();
17- BrowserAccessControlDialog accessControlDialog(m_currentDatabaseWidget);
18+ BrowserAccessControlDialog accessControlDialog{};
19
20 connect(m_currentDatabaseWidget, SIGNAL(databaseLockRequested()), &accessControlDialog, SLOT(reject()));
21
22@@ -512,7 +512,7 @@
23 QString id;
24
25 do {
26- QInputDialog keyDialog(m_currentDatabaseWidget);
27+ QInputDialog keyDialog{};
28 connect(m_currentDatabaseWidget, SIGNAL(databaseLockRequested()), &keyDialog, SLOT(reject()));
29 keyDialog.setWindowTitle(tr("KeePassXC - New key association request"));
30 keyDialog.setLabelText(tr("You have received an association request for the following database:\n%1\n\n"
31@@ -535,7 +535,7 @@
32
33 contains = db->metadata()->customData()->contains(CustomData::BrowserKeyPrefix + id);
34 if (contains) {
35- dialogResult = MessageBox::warning(m_currentDatabaseWidget,
36+ dialogResult = MessageBox::warning(nullptr,
37 tr("KeePassXC - Overwrite existing key?"),
38 tr("A shared encryption key with the name \"%1\" "
39 "already exists.\nDo you want to overwrite it?")
40@@ -595,7 +595,7 @@
41 const auto existingEntries = getPasskeyEntriesWithUserHandle(rpId, userId, keyList);
42
43 raiseWindow();
44- BrowserPasskeysConfirmationDialog confirmDialog(m_currentDatabaseWidget);
45+ BrowserPasskeysConfirmationDialog confirmDialog{};
46 confirmDialog.registerCredential(username, rpId, existingEntries, timeout);
47
48 auto dialogResult = confirmDialog.exec();
49@@ -612,7 +612,7 @@
50 // If no entry is selected, show the import dialog for manual entry selection
51 auto selectedEntry = confirmDialog.getSelectedEntry();
52 if (!selectedEntry) {
53- PasskeyImporter passkeyImporter(m_currentDatabaseWidget);
54+ PasskeyImporter passkeyImporter{};
55 const auto result = passkeyImporter.showImportDialog(db,
56 nullptr,
57 origin,
58@@ -683,7 +683,7 @@
59 const auto timeout = publicKeyOptions["timeout"].toInt();
60
61 raiseWindow();
62- BrowserPasskeysConfirmationDialog confirmDialog(m_currentDatabaseWidget);
63+ BrowserPasskeysConfirmationDialog confirmDialog{};
64 confirmDialog.authenticateCredential(entries, rpId, timeout);
65 auto dialogResult = confirmDialog.exec();
66 if (dialogResult == QDialog::Accepted) {
67@@ -760,7 +760,7 @@
68
69 // Ask confirmation if entry already contains a Passkey
70 if (entry->hasPasskey()) {
71- if (MessageBox::question(m_currentDatabaseWidget,
72+ if (MessageBox::question(nullptr,
73 tr("KeePassXC - Update passkey"),
74 tr("Entry already has a passkey.\nDo you want to overwrite the passkey in %1 - %2?")
75 .arg(entry->title(), passkeyUtils()->getUsernameFromEntry(entry)),
76@@ -873,7 +873,7 @@
77 MessageBox::Button dialogResult = MessageBox::No;
78 if (!browserSettings()->alwaysAllowUpdate()) {
79 raiseWindow();
80- dialogResult = MessageBox::question(m_currentDatabaseWidget,
81+ dialogResult = MessageBox::question(nullptr,
82 tr("KeePassXC - Update Entry"),
83 tr("Do you want to update the information in %1 - %2?")
84 .arg(QUrl(entryParameters.siteUrl).host(), username),
85@@ -909,7 +909,7 @@
86 return false;
87 }
88
89- auto dialogResult = MessageBox::warning(m_currentDatabaseWidget,
90+ auto dialogResult = MessageBox::warning(nullptr,
91 tr("KeePassXC - Delete entry"),
92 tr("A request for deleting entry \"%1\" has been received.\n"
93 "Do you want to delete the entry?\n")
94@@ -1536,7 +1536,7 @@
95 }
96 }
97
98- BrowserEntrySaveDialog browserEntrySaveDialog(m_currentDatabaseWidget);
99+ BrowserEntrySaveDialog browserEntrySaveDialog{};
100 int openDatabaseCount = browserEntrySaveDialog.setItems(databaseWidgets, m_currentDatabaseWidget);
101 if (openDatabaseCount > 1) {
102 int res = browserEntrySaveDialog.exec();
103diff -u3 -r source.orig/src/fdosecrets/objects/Prompt.cpp source/src/fdosecrets/objects/Prompt.cpp
104--- source.orig/src/fdosecrets/objects/Prompt.cpp 2025-01-27 20:55:04.135942791 +0100
105+++ source/src/fdosecrets/objects/Prompt.cpp 2025-01-27 21:01:37.166710935 +0100
106@@ -313,7 +313,7 @@
107 if (!entries.isEmpty()) {
108 QString app = tr("%1 (PID: %2)").arg(client->name()).arg(client->pid());
109 auto ac = new AccessControlDialog(
110- findWindow(m_windowId), entries, app, client->processInfo(), AuthOption::Remember);
111+ nullptr, entries, app, client->processInfo(), AuthOption::Remember);
112 connect(ac, &AccessControlDialog::finished, this, &UnlockPrompt::itemUnlockFinished);
113 connect(ac, &AccessControlDialog::finished, ac, &AccessControlDialog::deleteLater);
114 ac->open();
115diff -u3 -r source.orig/src/gui/DatabaseTabWidget.cpp source/src/gui/DatabaseTabWidget.cpp
116--- source.orig/src/gui/DatabaseTabWidget.cpp 2025-01-27 20:55:04.134589500 +0100
117+++ source/src/gui/DatabaseTabWidget.cpp 2025-01-27 21:07:09.785284837 +0100
118@@ -41,7 +41,7 @@
119 : QTabWidget(parent)
120 , m_dbWidgetStateSync(new DatabaseWidgetStateSync(this))
121 , m_dbWidgetPendingLock(nullptr)
122- , m_databaseOpenDialog(new DatabaseOpenDialog(this))
123+ , m_databaseOpenDialog(new DatabaseOpenDialog())
124 , m_databaseOpenInProgress(false)
125 {
126 auto* tabBar = new QTabBar(this);
diff --git a/overlays/keepassxc/default.nix b/overlays/keepassxc/default.nix
new file mode 100644
index 00000000..25429a66
--- /dev/null
+++ b/overlays/keepassxc/default.nix
@@ -0,0 +1,8 @@
1{ final, prev, ... }:
2{
3 keepassxc = prev.keepassxc.overrideAttrs (oldAttrs: {
4 patches = (oldAttrs.patches or []) ++ [
5 ./database-open-dialog.patch
6 ];
7 });
8}
diff --git a/overlays/swayosd.nix b/overlays/swayosd.nix
new file mode 100644
index 00000000..61c865e7
--- /dev/null
+++ b/overlays/swayosd.nix
@@ -0,0 +1,27 @@
1{ final, prev, sources, ... }: {
2 swayosd = prev.swayosd.overrideAttrs (oldAttrs: rec {
3 inherit (sources.swayosd) version src;
4 cargoDeps = prev.rustPlatform.fetchCargoTarball {
5 inherit (oldAttrs) pname;
6 inherit version src;
7 hash = "sha256-Anrk8p76HKZcNavYdi9l1oYahduLrb7Lf7knQK7Hy5E=";
8 };
9 nativeBuildInputs = with final; [
10 wrapGAppsHook4
11 pkg-config
12 meson
13 rustc
14 cargo
15 ninja
16 rustPlatform.cargoSetupHook
17 ];
18 buildInputs = with final; [
19 gtk4-layer-shell
20 libevdev
21 libinput
22 libpulseaudio
23 udev
24 sassc
25 ];
26 });
27}
diff --git a/overlays/worktime/worktime/__main__.py b/overlays/worktime/worktime/__main__.py
index 362c8da4..9335afdc 100755
--- a/overlays/worktime/worktime/__main__.py
+++ b/overlays/worktime/worktime/__main__.py
@@ -23,7 +23,7 @@ import argparse
23from copy import deepcopy 23from copy import deepcopy
24 24
25import sys 25import sys
26from sys import stderr 26from sys import stderr, stdout
27 27
28from tabulate import tabulate 28from tabulate import tabulate
29 29
@@ -38,6 +38,7 @@ from collections import defaultdict
38 38
39import jsonpickle 39import jsonpickle
40from hashlib import blake2s 40from hashlib import blake2s
41import json
41 42
42class TogglAPISection(Enum): 43class TogglAPISection(Enum):
43 TOGGL = '/api/v9' 44 TOGGL = '/api/v9'
@@ -518,7 +519,14 @@ def format_days(worktime, days, date_format=None):
518 return ', '.join(map(lambda group: ','.join(map(format_group, group)), groups)) 519 return ', '.join(map(lambda group: ','.join(map(format_group, group)), groups))
519 520
520 521
521def worktime(**args): 522def tooltip_timedelta(td):
523 if td < timedelta(seconds = 0):
524 return "-" + tooltip_timedelta(-td)
525 mm, ss = divmod(td.total_seconds(), 60)
526 hh, mm = divmod(mm, 60)
527 return "%d:%02d:%02d" % (hh, mm, ss)
528
529def worktime(pull_forward_cutoff, waybar, **args):
522 worktime = Worktime(**args) 530 worktime = Worktime(**args)
523 531
524 def format_worktime(worktime): 532 def format_worktime(worktime):
@@ -562,7 +570,9 @@ def worktime(**args):
562 else: 570 else:
563 return f"({difference_string})" 571 return f"({difference_string})"
564 572
565 if worktime.time_pulled_forward >= timedelta(minutes = 15): 573 out_class = "running" if worktime.running_entry else "stopped"
574 tooltip = tooltip_timedelta(worktime.time_to_work - worktime.time_worked)
575 if worktime.time_pulled_forward >= min(pull_forward_cutoff, timedelta(seconds = 1)):
566 worktime_no_pulled_forward = deepcopy(worktime) 576 worktime_no_pulled_forward = deepcopy(worktime)
567 worktime_no_pulled_forward.time_to_work -= worktime_no_pulled_forward.time_pulled_forward 577 worktime_no_pulled_forward.time_to_work -= worktime_no_pulled_forward.time_pulled_forward
568 worktime_no_pulled_forward.time_pulled_forward = timedelta() 578 worktime_no_pulled_forward.time_pulled_forward = timedelta()
@@ -570,11 +580,20 @@ def worktime(**args):
570 difference_string = format_worktime(worktime) 580 difference_string = format_worktime(worktime)
571 difference_string_no_pulled_forward = format_worktime(worktime_no_pulled_forward) 581 difference_string_no_pulled_forward = format_worktime(worktime_no_pulled_forward)
572 582
573 print(f"{difference_string_no_pulled_forward}…{difference_string}") 583 tooltip = tooltip_timedelta(worktime_no_pulled_forward.time_to_work - worktime_no_pulled_forward.time_worked) + "…" + tooltip
584 if worktime.time_pulled_forward >= pull_forward_cutoff:
585 out_text = f"{difference_string_no_pulled_forward}…{difference_string}"
586 else:
587 out_text = format_worktime(worktime)
574 else: 588 else:
575 print(format_worktime(worktime)) 589 out_text = format_worktime(worktime)
576 590
577def time_worked(now, **args): 591 if waybar:
592 json.dump({"text": out_text, "class": out_class, "tooltip": tooltip}, stdout)
593 else:
594 print(out_text)
595
596def time_worked(now, waybar, **args):
578 then = now.replace(hour = 0, minute = 0, second = 0, microsecond = 0) 597 then = now.replace(hour = 0, minute = 0, second = 0, microsecond = 0)
579 if now.time() == time(): 598 if now.time() == time():
580 now = now + timedelta(days = 1) 599 now = now + timedelta(days = 1)
@@ -584,6 +603,9 @@ def time_worked(now, **args):
584 603
585 worked = now.time_worked - then.time_worked 604 worked = now.time_worked - then.time_worked
586 605
606 out_text = None
607 out_class = "stopped"
608 tooltip = tooltip_timedelta(worked)
587 if args['do_round']: 609 if args['do_round']:
588 total_minutes_difference = 5 * ceil(worked / timedelta(minutes = 5)) 610 total_minutes_difference = 5 * ceil(worked / timedelta(minutes = 5))
589 (hours_difference, minutes_difference) = divmod(abs(total_minutes_difference), 60) 611 (hours_difference, minutes_difference) = divmod(abs(total_minutes_difference), 60)
@@ -602,15 +624,25 @@ def time_worked(now, **args):
602 difference = target_time - worked 624 difference = target_time - worked
603 clockout_difference = 5 * ceil(difference / timedelta(minutes = 5)) 625 clockout_difference = 5 * ceil(difference / timedelta(minutes = 5))
604 clockout_time = now.now + difference 626 clockout_time = now.now + difference
627 exact_clockout_time = clockout_time
605 clockout_time += (5 - clockout_time.minute % 5) * timedelta(minutes = 1) 628 clockout_time += (5 - clockout_time.minute % 5) * timedelta(minutes = 1)
606 clockout_time = clockout_time.replace(second = 0, microsecond = 0) 629 clockout_time = clockout_time.replace(second = 0, microsecond = 0)
607 630
608 if now.running_entry and clockout_time and clockout_difference >= 0: 631 if now.running_entry and clockout_time and clockout_difference >= 0:
609 print(f"{difference_string}/{clockout_time:%H:%M}") 632 out_class = "running"
633 out_text = f"{difference_string}/{clockout_time:%H:%M}"
634 tooltip = f"{tooltip_timedelta(worked)}/{exact_clockout_time:%H:%M}"
610 else: 635 else:
611 print(difference_string) 636 if now.running_entry:
637 out_class = "over"
638 out_text = difference_string
612 else: 639 else:
613 print(worked) 640 out_text = str(worked)
641
642 if waybar:
643 json.dump({"text": out_text, "class": out_class, "tooltip": tooltip}, stdout)
644 else:
645 print(out_text)
614 646
615def diff(now, **args): 647def diff(now, **args):
616 now = now.replace(hour = 0, minute = 0, second = 0, microsecond = 0) 648 now = now.replace(hour = 0, minute = 0, second = 0, microsecond = 0)
@@ -798,6 +830,38 @@ def classification(classification_name, table, table_format, **args):
798def main(): 830def main():
799 def isotime(s): 831 def isotime(s):
800 return datetime.fromisoformat(s).replace(tzinfo=tzlocal()) 832 return datetime.fromisoformat(s).replace(tzinfo=tzlocal())
833 def duration_minutes(s):
834 return timedelta(minutes = float(s))
835
836 def set_default_subparser(self, name, args=None, positional_args=0):
837 """default subparser selection. Call after setup, just before parse_args()
838 name: is the name of the subparser to call by default
839 args: if set is the argument list handed to parse_args()
840
841 , tested with 2.7, 3.2, 3.3, 3.4
842 it works with 2.6 assuming argparse is installed
843 """
844 subparser_found = False
845 for arg in sys.argv[1:]:
846 if arg in ['-h', '--help']: # global help if no subparser
847 break
848 else:
849 for x in self._subparsers._actions:
850 if not isinstance(x, argparse._SubParsersAction):
851 continue
852 for sp_name in x._name_parser_map.keys():
853 if sp_name in sys.argv[1:]:
854 subparser_found = True
855 if not subparser_found:
856 # insert default in last position before global positional
857 # arguments, this implies no global options are specified after
858 # first positional argument
859 if args is None:
860 sys.argv.insert(len(sys.argv) - positional_args, name)
861 else:
862 args.insert(len(args) - positional_args, name)
863
864 argparse.ArgumentParser.set_default_subparser = set_default_subparser
801 865
802 config = Worktime.config() 866 config = Worktime.config()
803 867
@@ -807,9 +871,13 @@ def main():
807 parser.add_argument('--no-running', dest = 'include_running', action = 'store_false') 871 parser.add_argument('--no-running', dest = 'include_running', action = 'store_false')
808 parser.add_argument('--no-force-day-to-work', dest = 'force_day_to_work', action = 'store_false') 872 parser.add_argument('--no-force-day-to-work', dest = 'force_day_to_work', action = 'store_false')
809 subparsers = parser.add_subparsers(help = 'Subcommands') 873 subparsers = parser.add_subparsers(help = 'Subcommands')
810 parser.set_defaults(cmd = worktime) 874 worktime_parser = subparsers.add_parser('time_worked', aliases = ['time', 'worked'])
811 time_worked_parser = subparsers.add_parser('time_worked', aliases = ['time', 'worked', 'today']) 875 worktime_parser.add_argument('--pull-forward-cutoff', dest = 'pull_forward_cutoff', metavar = 'MINUTES', type = duration_minutes, default = timedelta(minutes = 15))
876 worktime_parser.add_argument('--waybar', action='store_true')
877 worktime_parser.set_defaults(cmd = worktime)
878 time_worked_parser = subparsers.add_parser('today')
812 time_worked_parser.add_argument('--no-round', dest = 'do_round', action = 'store_false') 879 time_worked_parser.add_argument('--no-round', dest = 'do_round', action = 'store_false')
880 time_worked_parser.add_argument('--waybar', action='store_true')
813 time_worked_parser.set_defaults(cmd = time_worked) 881 time_worked_parser.set_defaults(cmd = time_worked)
814 diff_parser = subparsers.add_parser('diff') 882 diff_parser = subparsers.add_parser('diff')
815 diff_parser.set_defaults(cmd = diff) 883 diff_parser.set_defaults(cmd = diff)
@@ -827,6 +895,7 @@ def main():
827 classification_parser.add_argument('--table', action = 'store_true') 895 classification_parser.add_argument('--table', action = 'store_true')
828 classification_parser.add_argument('--table-format', dest='table_format', type=str, default='fancy_grid') 896 classification_parser.add_argument('--table-format', dest='table_format', type=str, default='fancy_grid')
829 classification_parser.set_defaults(cmd = partial(classification, classification_name=classification_name)) 897 classification_parser.set_defaults(cmd = partial(classification, classification_name=classification_name))
898 parser.set_default_subparser('time_worked')
830 args = parser.parse_args() 899 args = parser.parse_args()
831 900
832 args.cmd(**vars(args)) 901 args.cmd(**vars(args))