Commit dd581a08003d

Vincent Demeester <vincent@sbr.pm>
2025-12-10 14:18:21
feat(kyushu): Implement unified color-scheme switching
- Enable seamless light/dark mode across GTK, Emacs, and niri - Reduce eye strain with automatic daily switching (7am/7pm) - Sync theme changes in real-time via dbus notifications - Provide manual control via keybindings (Mod+Shift+T, C-c t t) Signed-off-by: Vincent Demeester <vincent@sbr.pm>
1 parent 3dae98e
Changed files (7)
dots
.config
home
common
pkgs
systems
kyushu
tools
emacs
dots/.config/niri/config.kdl
@@ -197,6 +197,7 @@ binds {
 
     Mod+T { spawn "bash" "-c" "notify-send --icon=clock --category=info --urgency=critical \"$(date +\"%I:%M\")\""; }
     Mod+B { spawn "bash" "-c" "notify-send --icon=battery --category=info --urgency=critical \"$(acpi)\""; }
+    Mod+Shift+T hotkey-overlay-title="Toggle Color Scheme" { spawn "toggle-color-scheme"; }
 
     Mod+Shift+B { spawn "pkill" "-USR1" "waybar"; }
 
home/common/services/color-scheme-timer.nix
@@ -0,0 +1,89 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+
+with lib;
+
+let
+  cfg = config.services.color-scheme-timer;
+in
+{
+  options.services.color-scheme-timer = {
+    enable = mkEnableOption "automatic color scheme switching based on time of day";
+
+    latitude = mkOption {
+      type = types.str;
+      default = "48.87";
+      description = "Latitude for calculating sunrise/sunset times";
+    };
+
+    longitude = mkOption {
+      type = types.str;
+      default = "2.33";
+      description = "Longitude for calculating sunrise/sunset times";
+    };
+
+    lightTime = mkOption {
+      type = types.str;
+      default = "07:00";
+      description = "Time to switch to light mode (HH:MM format)";
+    };
+
+    darkTime = mkOption {
+      type = types.str;
+      default = "19:00";
+      description = "Time to switch to dark mode (HH:MM format)";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.user.services.color-scheme-light = {
+      Unit = {
+        Description = "Switch to light color scheme";
+      };
+      Service = {
+        Type = "oneshot";
+        ExecStart = "${pkgs.toggle-color-scheme}/bin/toggle-color-scheme light";
+      };
+    };
+
+    systemd.user.services.color-scheme-dark = {
+      Unit = {
+        Description = "Switch to dark color scheme";
+      };
+      Service = {
+        Type = "oneshot";
+        ExecStart = "${pkgs.toggle-color-scheme}/bin/toggle-color-scheme dark";
+      };
+    };
+
+    systemd.user.timers.color-scheme-light = {
+      Unit = {
+        Description = "Timer for switching to light color scheme";
+      };
+      Timer = {
+        OnCalendar = cfg.lightTime;
+        Persistent = true;
+      };
+      Install = {
+        WantedBy = [ "timers.target" ];
+      };
+    };
+
+    systemd.user.timers.color-scheme-dark = {
+      Unit = {
+        Description = "Timer for switching to dark color scheme";
+      };
+      Timer = {
+        OnCalendar = cfg.darkTime;
+        Persistent = true;
+      };
+      Install = {
+        WantedBy = [ "timers.target" ];
+      };
+    };
+  };
+}
pkgs/toggle-color-scheme/default.nix
@@ -0,0 +1,6 @@
+{ writeShellApplication, dconf }:
+writeShellApplication {
+  name = "toggle-color-scheme";
+  runtimeInputs = [ dconf ];
+  text = builtins.readFile ./toggle-color-scheme.sh;
+}
pkgs/toggle-color-scheme/toggle-color-scheme.sh
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Toggle or set color scheme
+# Usage: toggle-color-scheme [light|dark]
+# Without argument: toggles between light and dark
+# With argument: sets to specified scheme
+
+# Get current color scheme from dconf
+current_scheme=$(dconf read /org/gnome/desktop/interface/color-scheme | tr -d "'")
+
+# Determine new scheme
+if [ $# -eq 0 ]; then
+    # No argument: toggle
+    if [[ "$current_scheme" == "prefer-dark" ]]; then
+        new_scheme="prefer-light"
+    else
+        new_scheme="prefer-dark"
+    fi
+    echo "Toggling from $current_scheme to $new_scheme"
+else
+    # Argument provided: set to specific scheme
+    case "$1" in
+        light)
+            new_scheme="prefer-light"
+            ;;
+        dark)
+            new_scheme="prefer-dark"
+            ;;
+        *)
+            echo "Invalid argument: $1"
+            echo "Usage: $0 [light|dark]"
+            exit 1
+            ;;
+    esac
+    echo "Setting color scheme to $new_scheme"
+fi
+
+# Set new color scheme via dconf
+dconf write /org/gnome/desktop/interface/color-scheme "'$new_scheme'"
+
+# Trigger niri screen transition for smooth visual effect
+if command -v niri &> /dev/null; then
+    niri msg action do-screen-transition
+fi
+
+echo "Color scheme is now $new_scheme"
pkgs/default.nix
@@ -24,6 +24,7 @@ in
   gh-restart-failed = pkgs.callPackage ../tools/gh-restart-failed { };
   arr = pkgs.callPackage ../tools/arr { };
   download-kiwix-zim = pkgs.callPackage ../tools/download-kiwix-zim { };
+  toggle-color-scheme = pkgs.callPackage ./toggle-color-scheme { };
   homepage = pkgs.callPackage ./homepage { inherit globals; };
 
   chmouzies-ai = pkgs.callPackage ./chmouzies/ai.nix { };
systems/kyushu/home.nix
@@ -3,6 +3,7 @@
   imports = [
     ../../home/common/dev/containers.nix
     ../../home/common/dev/tektoncd.nix
+    ../../home/common/services/color-scheme-timer.nix
   ];
   nixpkgs.config.allowUnfree = true;
 
@@ -55,6 +56,16 @@
     gh-restart-failed
     arr
     claude-hooks
+    toggle-color-scheme
   ];
 
+  # Automatic color scheme switching
+  services.color-scheme-timer = {
+    enable = true;
+    latitude = "48.87"; # Paris coordinates
+    longitude = "2.33";
+    lightTime = "07:00"; # Switch to light mode at 7am
+    darkTime = "19:00"; # Switch to dark mode at 7pm
+  };
+
 }
tools/emacs/init.el
@@ -145,10 +145,64 @@ It is shared with iOS and replace the deprecated `org-journal-file' below.")
 				  (agenda-structure . (variable-pitch light 2.2))
 				  (agenda-date . (variable-pitch regular 1.3))
 				  (t . (regular 1.15))))
-  ;; Default modus-operandi on GUI and modus-vivendi on CLI
+
+  ;; Color scheme management - sync with system dconf settings
+  (defvar vde/themes-plist
+    '(:dark modus-vivendi :light modus-operandi)
+    "Themes to use for dark and light modes.")
+
+  (defun vde/color-scheme-get-system ()
+    "Get current system color scheme from dconf."
+    (when (eq system-type 'gnu/linux)
+      (let* ((raw-scheme (shell-command-to-string
+                          "/run/current-system/sw/bin/dconf read /org/gnome/desktop/interface/color-scheme"))
+             (scheme (string-trim raw-scheme)))
+        (if (string-match-p "prefer-dark" scheme)
+            :dark
+          :light))))
+
+  (defun vde/color-scheme-set-emacs (scheme)
+    "Set Emacs theme based on SCHEME (:dark or :light)."
+    (let ((theme (plist-get vde/themes-plist scheme)))
+      (when theme
+        (mapc #'disable-theme custom-enabled-themes)
+        (load-theme theme :no-confirm))))
+
+  (defun vde/color-scheme-sync ()
+    "Sync Emacs theme with system color scheme."
+    (interactive)
+    (let ((scheme (vde/color-scheme-get-system)))
+      (when scheme
+        (vde/color-scheme-set-emacs scheme))))
+
+  (defun vde/color-scheme-toggle ()
+    "Toggle system color scheme and sync Emacs."
+    (interactive)
+    (when (eq system-type 'gnu/linux)
+      (start-process "toggle-color-scheme" nil "toggle-color-scheme")
+      ;; Wait a bit for the system to update, then sync
+      (run-with-timer 0.5 nil #'vde/color-scheme-sync)))
+
+  ;; Initial theme setup
   (if (display-graphic-p)
-      (load-theme 'modus-operandi :no-confirm)
-    (load-theme 'modus-vivendi :no-confirm)))
+      (vde/color-scheme-sync)
+    (load-theme 'modus-vivendi :no-confirm))
+
+  ;; Watch for dbus signals when color-scheme changes
+  (when (and (eq system-type 'gnu/linux)
+             (require 'dbus nil t))
+    (dbus-register-signal
+     :session
+     "ca.desrt.dconf"
+     "/ca/desrt/dconf/Writer/user"
+     "ca.desrt.dconf.Writer"
+     "Notify"
+     (lambda (&rest args)
+       (when (string-match-p "color-scheme" (format "%s" args))
+         (vde/color-scheme-sync)))))
+
+  ;; Keybinding for toggling color scheme
+  (global-set-key (kbd "C-c t t") #'vde/color-scheme-toggle))
 
 (setopt load-prefer-newer t)              ; Always load newer compiled files
 (setopt ad-redefinition-action 'accept)   ; Silence advice redefinition warnings