Compare commits

...

6 Commits

Author SHA1 Message Date
Olivier fa62ba2b8c Refactor desktop home configuration and introduce caching module
- Simplified `desktop-home-base.nix` by removing legacy git and application settings, delegating them to a new `desktop-home-defaults.nix` module for better organization.
- Added a caching configuration for `chiasson.system.caching.attic` in `desktop-home-base.nix`, enabling caching features with customizable options.
- Updated host-specific configurations to remove direct caching settings, ensuring a cleaner and more modular setup.
2026-06-22 12:29:16 -03:00
Olivier 3b5c031619 Add rbw SSH socket support in home-manager modules
- Introduced a new module `rbw-ssh-socket.nix` to manage SSH_AUTH_SOCK for rbw.
- Updated `default.nix` to utilize the new SSH socket variable for session management.
- Refactored Bash and Fish shell configurations to use the new module for setting SSH_AUTH_SOCK, improving maintainability and consistency across shell environments.
2026-06-22 12:28:25 -03:00
Olivier 1524100ff5 Add DMS plugins for rbw lock and wvkbd toggle
- Introduced new DMS plugins: `rbw-lock-toggle` for managing Bitwarden vault state and `wvkbd-toggle` for showing/hiding the on-screen keyboard.
- Updated `flake.lock` and `flake.nix` to include the new plugins and their configurations.
- Removed legacy options and files related to the rbw lock toggle, streamlining the DMS configuration.
- Enhanced the home-manager module to support the new plugins and their integration into the DMS environment.
2026-06-22 12:26:25 -03:00
Olivier 662d24a89c Add Hyprland and Niri configuration files
- Introduced `binds.nix` for Hyprland, defining keybindings for various applications and workspace management.
- Added `base-settings.nix` and `window-rules.nix` for Niri, establishing keyboard input settings and window management rules.
- Updated `default.nix` for both Hyprland and Niri to import new configuration files, enhancing modularity and maintainability.
2026-06-22 11:48:59 -03:00
Olivier 41ed709f4a Update Moonfin Flatpak URL and hash to version 2.2.0 2026-06-22 11:45:19 -03:00
Olivier 3fdb859ff8 Refactor DMS configuration and enhance Obsidian integration
- Replaced legacy Obsidian snippet directory options with a unified `obsidianVaults` option for better management.
- Updated DMS configuration to streamline widget handling and removed deprecated options.
- Introduced new templates for Hyprland colors and Obsidian themes to improve user customization.
- Added scripts for SwiftShare upload functionality, enhancing screenshot capabilities within the DMS environment.
2026-06-21 15:56:27 -03:00
36 changed files with 1164 additions and 1437 deletions
Generated
+34
View File
@@ -329,6 +329,22 @@
"type": "github"
}
},
"dms-plugin-rbw-lock-toggle": {
"flake": false,
"locked": {
"lastModified": 1782075393,
"narHash": "sha256-pTA8X2EWSnytBjwVph6G5QQbAH2RitCBm5XKbyeuXps=",
"ref": "refs/heads/main",
"rev": "f602df31706cde2176053faa284f8a2cecdd10ce",
"revCount": 1,
"type": "git",
"url": "https://git.chiasson.cloud/dms-plugins/rbw-lock-toggle"
},
"original": {
"type": "git",
"url": "https://git.chiasson.cloud/dms-plugins/rbw-lock-toggle"
}
},
"dms-plugin-unified-taskbar": {
"flake": false,
"locked": {
@@ -393,6 +409,22 @@
"type": "github"
}
},
"dms-plugin-wvkbd-toggle": {
"flake": false,
"locked": {
"lastModified": 1782075446,
"narHash": "sha256-dQOypewXssYUSW2MPHx42EUlLPJMR8zetcr98t8LeTE=",
"ref": "refs/heads/main",
"rev": "fd13bb27628cd39c393fd898c1c9957ce44eff9f",
"revCount": 1,
"type": "git",
"url": "https://git.chiasson.cloud/dms-plugins/wvkbd-toggle"
},
"original": {
"type": "git",
"url": "https://git.chiasson.cloud/dms-plugins/wvkbd-toggle"
}
},
"flake-compat": {
"locked": {
"lastModified": 1733328505,
@@ -1087,10 +1119,12 @@
"dms-plugin-nix-package-runner": "dms-plugin-nix-package-runner",
"dms-plugin-ocr-scanner": "dms-plugin-ocr-scanner",
"dms-plugin-official": "dms-plugin-official",
"dms-plugin-rbw-lock-toggle": "dms-plugin-rbw-lock-toggle",
"dms-plugin-unified-taskbar": "dms-plugin-unified-taskbar",
"dms-plugin-wallpaper-carousel": "dms-plugin-wallpaper-carousel",
"dms-plugin-web-search": "dms-plugin-web-search",
"dms-plugin-widget-group": "dms-plugin-widget-group",
"dms-plugin-wvkbd-toggle": "dms-plugin-wvkbd-toggle",
"flake-parts": "flake-parts",
"home-manager": "home-manager",
"import-tree": "import-tree",
+10
View File
@@ -103,6 +103,16 @@
flake = false;
};
dms-plugin-wvkbd-toggle = {
url = "git+https://git.chiasson.cloud/dms-plugins/wvkbd-toggle";
flake = false;
};
dms-plugin-rbw-lock-toggle = {
url = "git+https://git.chiasson.cloud/dms-plugins/rbw-lock-toggle";
flake = false;
};
dgop = {
url = "github:AvengeMedia/dgop";
inputs.nixpkgs.follows = "nixpkgs";
+77
View File
@@ -0,0 +1,77 @@
{ ... }: {
flake.lib.hyprlandBinds =
{ lib, dmsEnabled ? false }:
let
workspaceBinds =
(builtins.map (i: "Super, ${toString i}, workspace, ${toString i}") (builtins.genList (n: n + 1) 9))
++ (builtins.map (
i: "ControlSuper, ${toString i}, focusworkspaceoncurrentmonitor, ${toString i}"
) (builtins.genList (n: n + 1) 9))
++ (builtins.map (i: "ShiftSuper, ${toString i}, movetoworkspacesilent, ${toString i}") (
builtins.genList (n: n + 1) 9
));
in
{
bind =
[
"SUPER,T,exec,kitty"
"ControlSuper,T,exec,konsole"
"SUPER,D,exec,rofi -show drun"
"ControlSuper,D,exec,rofi -show window"
"SUPER,E,exec,nemo"
"Super, Q, killactive"
"ControlSuper, Q, exec, hyprctl kill"
"Super, F, fullscreen, 0"
"Super, G, fullscreen, 1"
"ShiftSuper, F, fullscreenstate, 0 3"
"Super, Minus, splitratio, -0.1"
"Super, Equal, splitratio, 0.1"
"AltSuper, Space, togglefloating"
"Super, P, pin"
]
++ lib.optionals dmsEnabled [
"Super, Space, exec, dms ipc call spotlight toggle"
"Super, I, exec, dms ipc call settings focusOrToggle"
"Super, N, exec, dms ipc call notepad toggle"
"ShiftSuper, N, exec, dms ipc call notifications toggle"
"Super, M, exec, dms ipc call processlist focusOrToggle"
"Super, L, exec, dms ipc call lock lock"
"ShiftSuper, V, exec, dms ipc call clipboard toggle"
]
++ [
"Super, Tab, cyclenext"
"Super, Tab, bringactivetotop"
"Super, left, movefocus, l"
"Super, right, movefocus, r"
"Super, up, movefocus, u"
"Super, down, movefocus, d"
"ShiftSuper, left, movewindow, l"
"ShiftSuper, right, movewindow, r"
"ShiftSuper, up, movewindow, u"
"ShiftSuper, down, movewindow, d"
]
++ workspaceBinds;
bindm = [
" , mouse:282, movewindow"
"Super, mouse:272, movewindow"
"Super, mouse:273, resizewindow"
];
bindl = [
"Super, mouse_up, splitratio, -0.1"
"Super, mouse_down, splitratio, 0.1"
" , XF86AudioPlay, exec, playerctl play-pause"
" , XF86AudioPrev, exec, playerctl previous"
" , XF86AudioNext, exec, playerctl next"
" , XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"
" , XF86MonBrightnessDown, exec, brightnessctl s 10%-"
" , XF86MonBrightnessUp, exec, brightnessctl s +10%"
];
bindel = [
" , XF86AudioRaiseVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+"
" , XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"
];
};
}
+8 -63
View File
@@ -1,4 +1,8 @@
{ self, ... }: {
imports = [
./binds.nix
];
flake.nixosModules.desktopHyprland =
{ config, options, lib, pkgs, ... }:
let
@@ -38,6 +42,7 @@
}:
let
hyprlandEnabled = osConfig.chiasson.desktop.hyprland.enable or false;
dmsEnabled = (osConfig.chiasson.desktop.shell or null) == "dms";
keyringEnabled = osConfig.chiasson.desktop.keyring.enable or false;
# nixpkgs hyprland-plugins pin is stale for current Hyprland — override to a known-good rev.
hyprbarsPatched =
@@ -95,69 +100,9 @@
"nm-applet --indicator &"
"sleep 1 && hyprctl reload"
];
# Default keybinds
bind =
[
"SUPER,T,exec,kitty"
"ControlSuper,T,exec,konsole"
"SUPER,D,exec,rofi -show drun"
"ControlSuper,D,exec,rofi -show window"
"SUPER,E,exec,nemo"
"Super, Q, killactive"
"ControlSuper, Q, exec, hyprctl kill"
"Super, F, fullscreen, 0"
"Super, G, fullscreen, 1"
"ShiftSuper, F, fullscreenstate, 0 3"
"Super, Minus, splitratio, -0.1"
"Super, Equal, splitratio, 0.1"
"AltSuper, Space, togglefloating"
"Super, P, pin"
"Super, Space, exec, dms ipc call spotlight toggle"
"Super, I, exec, dms ipc call settings focusOrToggle"
"Super, N, exec, dms ipc call notepad toggle"
"ShiftSuper, N, exec, dms ipc call notifications toggle"
"Super, M, exec, dms ipc call processlist focusOrToggle"
"Super, L, exec, dms ipc call lock lock"
"ShiftSuper, V, exec, dms ipc call clipboard toggle"
"Super, Tab, cyclenext"
"Super, Tab, bringactivetotop"
"Super, left, movefocus, l"
"Super, right, movefocus, r"
"Super, up, movefocus, u"
"Super, down, movefocus, d"
"ShiftSuper, left, movewindow, l"
"ShiftSuper, right, movewindow, r"
"ShiftSuper, up, movewindow, u"
"ShiftSuper, down, movewindow, d"
]
++ (builtins.map (i: "Super, ${toString i}, workspace, ${toString i}") (builtins.genList (n: n + 1) 9))
++ (builtins.map (i: "ControlSuper, ${toString i}, focusworkspaceoncurrentmonitor, ${toString i}") (builtins.genList (n: n + 1) 9))
++ (builtins.map (i: "ShiftSuper, ${toString i}, movetoworkspacesilent, ${toString i}") (builtins.genList (n: n + 1) 9));
bindm = [
" , mouse:282, movewindow"
"Super, mouse:272, movewindow"
"Super, mouse:273, resizewindow"
];
bindl = [
"Super, mouse_up, splitratio, -0.1"
"Super, mouse_down, splitratio, 0.1"
" , XF86AudioPlay, exec, playerctl play-pause"
" , XF86AudioPrev, exec, playerctl previous"
" , XF86AudioNext, exec, playerctl next"
" , XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"
" , XF86MonBrightnessDown, exec, brightnessctl s 10%-"
" , XF86MonBrightnessUp, exec, brightnessctl s +10%"
];
bindel = [
" , XF86AudioRaiseVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+"
" , XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"
];
# Decoration / blur
}
(self.lib.hyprlandBinds { inherit lib dmsEnabled; })
{
decoration = {
rounding = 10;
active_opacity = 0.95;
+18
View File
@@ -0,0 +1,18 @@
{ self, ... }: {
flake.lib.niriBaseSettings =
pkgs:
{
input.keyboard = {
xkb.layout = "ca";
xkb.variant = "";
};
input."focus-follows-mouse" = _: {
props."max-scroll-amount" = "45%";
content = { };
};
input."warp-mouse-to-focus" = _: { };
layout.gaps = 5;
prefer-no-csd = true;
window-rules = self.lib.niriWindowRules;
};
}
+104
View File
@@ -0,0 +1,104 @@
{ ... }: {
flake.lib.niriBinds =
{ pkgs, dmsEnabled ? false }:
let
lib = pkgs.lib;
in
{
"Mod+T"."spawn-sh" = lib.getExe pkgs.kitty;
"Mod+Control+T"."spawn-sh" = "konsole";
"Mod+D"."spawn-sh" = "rofi -show drun";
"Mod+Control+O"."spawn-sh" = "rofi -show window";
"Mod+E"."spawn-sh" = "nemo";
}
// lib.optionalAttrs dmsEnabled {
"Mod+Space"."spawn-sh" = "dms ipc call spotlight toggle";
"Mod+V"."spawn-sh" = "dms ipc call clipboard toggle";
"Mod+N"."spawn-sh" = "dms ipc call notepad toggle";
"Mod+Shift+N"."spawn-sh" = "dms ipc call notifications toggle";
"Mod+M"."spawn-sh" = "dms ipc call processlist toggle";
"Mod+B"."spawn-sh" = ''dms ipc call bar toggle name "Main Bar"; dms ipc call bar toggle name "Bar 2"'';
"Mod+Shift+B"."spawn-sh" = ''dms ipc call bar toggle name "Bar 2"'';
"Mod+A"."spawn-sh" = "dms ipc call ephemera toggle";
"Mod+numbersign"."spawn-sh" = "dms ipc call wallpaperCarousel toggle";
"Mod+L"."spawn-sh" = "dms ipc call lock lock";
}
// {
"Mod+Q"."close-window" = _: { };
"Mod+F"."maximize-column" = _: { };
"Mod+Shift+F"."fullscreen-window" = _: { };
"Mod+O"."toggle-overview" = _: { };
"Mod+Shift+NumberSign"."show-hotkey-overlay" = _: { };
"Mod+Shift+E".quit = _: { };
"Mod+Left"."focus-column-or-monitor-left" = _: { };
"Mod+Down"."focus-window-or-monitor-down" = _: { };
"Mod+Up"."focus-window-or-monitor-up" = _: { };
"Mod+Right"."focus-column-or-monitor-right" = _: { };
"Mod+Shift+WheelScrollDown"."focus-workspace-down" = _: { };
"Mod+Shift+WheelScrollUp"."focus-workspace-up" = _: { };
"Mod+WheelScrollDown"."focus-column-or-monitor-right" = _: { };
"Mod+WheelScrollUp"."focus-column-or-monitor-left" = _: { };
"Mod+Shift+Left"."move-column-left-or-to-monitor-left" = _: { };
"Mod+Shift+Down"."move-window-down" = _: { };
"Mod+Shift+Up"."move-window-up" = _: { };
"Mod+Shift+Right"."move-column-right-or-to-monitor-right" = _: { };
"Mod+Page_Up"."focus-workspace-up" = _: { };
"Mod+Page_Down"."focus-workspace-down" = _: { };
"Mod+Shift+Page_Up"."move-column-to-workspace-up" = _: { };
"Mod+Shift+Page_Down"."move-column-to-workspace-down" = _: { };
"Mod+R"."switch-preset-column-width" = _: { };
"Mod+BracketLeft"."consume-or-expel-window-left" = _: { };
"Mod+BracketRight"."consume-or-expel-window-right" = _: { };
"Mod+Comma"."consume-window-into-column" = _: { };
"Mod+Period"."expel-window-from-column" = _: { };
"Mod+Alt+Space"."toggle-window-floating" = _: { };
"Mod+1"."focus-workspace" = 1;
"Mod+2"."focus-workspace" = 2;
"Mod+3"."focus-workspace" = 3;
"Mod+4"."focus-workspace" = 4;
"Mod+5"."focus-workspace" = 5;
"Mod+6"."focus-workspace" = 6;
"Mod+7"."focus-workspace" = 7;
"Mod+8"."focus-workspace" = 8;
"Mod+9"."focus-workspace" = 9;
"Mod+Ctrl+1"."move-column-to-workspace" = 1;
"Mod+Ctrl+2"."move-column-to-workspace" = 2;
"Mod+Ctrl+3"."move-column-to-workspace" = 3;
"Mod+Ctrl+4"."move-column-to-workspace" = 4;
"Mod+Ctrl+5"."move-column-to-workspace" = 5;
"Mod+Ctrl+6"."move-column-to-workspace" = 6;
"Mod+Ctrl+7"."move-column-to-workspace" = 7;
"Mod+Ctrl+8"."move-column-to-workspace" = 8;
"Mod+Ctrl+9"."move-column-to-workspace" = 9;
"XF86AudioRaiseVolume".spawn = [
"wpctl"
"set-volume"
"@DEFAULT_AUDIO_SINK@"
"0.05+"
];
"XF86AudioLowerVolume".spawn = [
"wpctl"
"set-volume"
"@DEFAULT_AUDIO_SINK@"
"0.05-"
];
"XF86AudioMute".spawn = [
"wpctl"
"set-mute"
"@DEFAULT_AUDIO_SINK@"
"toggle"
];
"XF86MonBrightnessUp".spawn = [ "brightnessctl" "set" "+5%" ];
"XF86MonBrightnessDown".spawn = [ "brightnessctl" "set" "5%-" ];
Print.screenshot = _: { };
"Ctrl+Print"."screenshot-screen" = _: { };
"Alt+Print"."screenshot-window" = _: { };
};
}
+24 -172
View File
@@ -1,168 +1,36 @@
{ self, inputs, ... }:
{ self, inputs, ... }: {
imports = [
./window-rules.nix
./base-settings.nix
./binds.nix
];
flake.homeManagerModules.desktopNiri =
{ config, lib, pkgs, osConfig ? { }, ... }:
let
# Keep defaults in this let — a bare niri-settings.nix next to this file would get picked up by import-tree.
niriBaseSettings =
pkgs:
{
input.keyboard = {
xkb.layout = "ca";
xkb.variant = "";
};
input."focus-follows-mouse" = _: {
props."max-scroll-amount" = "45%";
content = { };
};
input."warp-mouse-to-focus" = _: { };
layout.gaps = 5;
prefer-no-csd = true;
window-rules = [
{
matches = [
{
app-id = "^$";
title = "^$";
}
];
open-floating = true;
open-focused = false;
}
{
matches = [
{ title = "^(Picture-in-Picture|Picture in picture)$"; }
];
open-floating = true;
open-focused = false;
default-column-width = { fixed = 640; };
default-window-height = { fixed = 360; };
default-floating-position = _: {
props = {
x = 32;
y = 32;
relative-to = "bottom-right";
};
};
}
{
matches = [
{ app-id = "^Minecraft\\*"; }
{ app-id = "^Minecraft$"; }
];
tiled-state = true;
}
];
#TODO[epic=Binds] Go over binds again
binds = {
"Mod+T"."spawn-sh" = pkgs.lib.getExe pkgs.kitty; # shell from kitty HM config
"Mod+Control+T"."spawn-sh" = "konsole"; #TODO[epic=Binds] This should only be set if having konsole
"Mod+D"."spawn-sh" = "rofi -show drun"; #TODO[epic=Binds] This should only be set if having rofi
"Mod+Space"."spawn-sh" = "dms ipc call spotlight toggle"; #TODO[epic=Binds] This should only be set if having dms
"Mod+E"."spawn-sh" = "nemo"; #TODO[epic=Binds] This should only be set if having nemo
"Mod+Control+O"."spawn-sh" = "rofi -show window"; #TODO[epic=Binds] This should only be set if having rofi
"Mod+V"."spawn-sh" = "dms ipc call clipboard toggle"; #TODO[epic=Binds] This should only be set if having dms
"Mod+N"."spawn-sh" = "dms ipc call notepad toggle"; #TODO[epic=Binds] This should only be set if having dms
"Mod+Shift+N"."spawn-sh" = "dms ipc call notifications toggle"; #TODO[epic=Binds] This should only be set if having dms
"Mod+M"."spawn-sh" = "dms ipc call processlist toggle"; #TODO[epic=Binds] This should only be set if having dms
"Mod+B"."spawn-sh" = "dms ipc call bar toggle name \"Main Bar\"; dms ipc call bar toggle name \"Bar 2\""; #TODO[epic=Binds] This should only be set if having dms
"Mod+Shift+B"."spawn-sh" = "dms ipc call bar toggle name \"Bar 2\""; #TODO[epic=Binds] This should only be set if having dms
"Mod+A"."spawn-sh" = "dms ipc call ephemera toggle"; #TODO[epic=Binds] This should only be set if having dms
"Mod+numbersign"."spawn-sh" = "dms ipc call wallpaperCarousel toggle"; # Mod+# — TODO[epic=Binds] This should only be set if having dms
"Mod+L"."spawn-sh" = "dms ipc call lock lock"; #TODO[epic=Binds] This should only be set if having dms
"Mod+Q"."close-window" = _: { };
"Mod+F"."maximize-column" = _: { };
"Mod+Shift+F"."fullscreen-window" = _: { };
"Mod+O"."toggle-overview" = _: { };
"Mod+Shift+NumberSign"."show-hotkey-overlay" = _: { };
"Mod+Shift+E".quit = _: { };
"Mod+Left"."focus-column-or-monitor-left" = _: { };
"Mod+Down"."focus-window-or-monitor-down" = _: { };
"Mod+Up"."focus-window-or-monitor-up" = _: { };
"Mod+Right"."focus-column-or-monitor-right" = _: { };
"Mod+Shift+WheelScrollDown"."focus-workspace-down" = _: { };
"Mod+Shift+WheelScrollUp"."focus-workspace-up" = _: { };
"Mod+WheelScrollDown"."focus-column-or-monitor-right" = _: { };
"Mod+WheelScrollUp"."focus-column-or-monitor-left" = _: { };
"Mod+Shift+Left"."move-column-left-or-to-monitor-left" = _: { };
"Mod+Shift+Down"."move-window-down" = _: { };
"Mod+Shift+Up"."move-window-up" = _: { };
"Mod+Shift+Right"."move-column-right-or-to-monitor-right" = _: { };
"Mod+Page_Up"."focus-workspace-up" = _: { };
"Mod+Page_Down"."focus-workspace-down" = _: { };
"Mod+Shift+Page_Up"."move-column-to-workspace-up" = _: { };
"Mod+Shift+Page_Down"."move-column-to-workspace-down" = _: { };
"Mod+R"."switch-preset-column-width" = _: { };
"Mod+BracketLeft"."consume-or-expel-window-left" = _: { };
"Mod+BracketRight"."consume-or-expel-window-right" = _: { };
"Mod+Comma"."consume-window-into-column" = _: { };
"Mod+Period"."expel-window-from-column" = _: { };
"Mod+Alt+Space"."toggle-window-floating" = _: { };
#"Mod+Shift+V"."switch-focus-between-floating-and-tiling" = _: { }; #TODO[epic=Binds] Find free bind that is not in the way and not overcomplicated to remember
"Mod+1"."focus-workspace" = 1;
"Mod+2"."focus-workspace" = 2;
"Mod+3"."focus-workspace" = 3;
"Mod+4"."focus-workspace" = 4;
"Mod+5"."focus-workspace" = 5;
"Mod+6"."focus-workspace" = 6;
"Mod+7"."focus-workspace" = 7;
"Mod+8"."focus-workspace" = 8;
"Mod+9"."focus-workspace" = 9;
"Mod+Ctrl+1"."move-column-to-workspace" = 1;
"Mod+Ctrl+2"."move-column-to-workspace" = 2;
"Mod+Ctrl+3"."move-column-to-workspace" = 3;
"Mod+Ctrl+4"."move-column-to-workspace" = 4;
"Mod+Ctrl+5"."move-column-to-workspace" = 5;
"Mod+Ctrl+6"."move-column-to-workspace" = 6;
"Mod+Ctrl+7"."move-column-to-workspace" = 7;
"Mod+Ctrl+8"."move-column-to-workspace" = 8;
"Mod+Ctrl+9"."move-column-to-workspace" = 9;
"XF86AudioRaiseVolume".spawn = [
"wpctl"
"set-volume"
"@DEFAULT_AUDIO_SINK@"
"0.05+"
];
"XF86AudioLowerVolume".spawn = [
"wpctl"
"set-volume"
"@DEFAULT_AUDIO_SINK@"
"0.05-"
];
"XF86AudioMute".spawn = [
"wpctl"
"set-mute"
"@DEFAULT_AUDIO_SINK@"
"toggle"
];
# Backlight: relies on `pkgs.brightnessctl` being on PATH (provided by `desktopGui` when
# any GUI session is enabled) and the user being in the `video` group (catalog default).
"XF86MonBrightnessUp".spawn = [ "brightnessctl" "set" "+5%" ];
"XF86MonBrightnessDown".spawn = [ "brightnessctl" "set" "5%-" ];
Print.screenshot = _: { };
"Ctrl+Print"."screenshot-screen" = _: { };
"Alt+Print"."screenshot-window" = _: { };
};
};
home = config.home.homeDirectory;
niriOs = osConfig.chiasson.desktop.niri or { };
niriEnabled = osConfig.chiasson.desktop.niri.enable or false;
keyringEnabled = osConfig.chiasson.desktop.keyring.enable or false;
dmsEnabled = (osConfig.chiasson.desktop.shell or null) == "dms";
keyringNiriStartupKdl = ''
spawn-at-startup "dbus-update-activation-environment" "--systemd" "--all"
'';
niriBaseSettings =
pkgs: dmsEnabled:
self.lib.niriBaseSettings pkgs // {
binds = self.lib.niriBinds { inherit pkgs dmsEnabled; };
};
mergeNiriSettings =
pkgs: niriCfg: keyringEnable:
pkgs: niriCfg: keyringEnable: dmsEnabled:
let
lib = pkgs.lib;
pi5 = self.lib.pi5NiriKdl;
rpi5Extra = lib.optionalString (niriCfg.raspberryPi5DrmWorkaround or false) pi5.drmExtraConfig;
base = niriBaseSettings pkgs;
base = niriBaseSettings pkgs dmsEnabled;
userExtra = niriCfg.extraSettings or { };
keyringExtra = lib.optionalString keyringEnable keyringNiriStartupKdl;
extraConfigMerged = keyringExtra + rpi5Extra + (userExtra.extraConfig or "");
@@ -185,31 +53,18 @@ let
extraConfig = extraConfigMerged;
}
);
in
{
flake.homeManagerModules.desktopNiri =
{ config, lib, pkgs, osConfig ? { }, ... }:
let
home = config.home.homeDirectory;
niriOs = osConfig.chiasson.desktop.niri or { };
niriEnabled = osConfig.chiasson.desktop.niri.enable or false;
keyringEnabled = osConfig.chiasson.desktop.keyring.enable or false;
dmsEnabled = (osConfig.chiasson.desktop.shell or null) == "dms";
dmsWindowRulesPath = "${home}/.config/niri/dms/windowrules.kdl";
# wrapper-modules emits quoted KDL (`"include" "…"`) which DMS's parser ignores.
# config.kdl is a store symlink, so a relative include would not resolve to the real
# ~/.config/niri/dms/windowrules.kdl that DMS writes at runtime.
dmsWindowRulesInclude = lib.optionalString dmsEnabled ''
include "${dmsWindowRulesPath}"
'';
baseMergedSettings = mergeNiriSettings pkgs niriOs keyringEnabled;
baseMergedSettings = mergeNiriSettings pkgs niriOs keyringEnabled dmsEnabled;
mergedSettings = lib.recursiveUpdate baseMergedSettings {
extraConfig = dmsWindowRulesInclude + (baseMergedSettings.extraConfig or "");
};
niriConfigPkg = inputs.wrapper-modules.wrappers.niri.wrap {
inherit pkgs;
settings = mergedSettings;
# User-owned rules path is not available during the Nix build.
disableConfigValidation = dmsEnabled;
};
in
@@ -219,7 +74,6 @@ in
xdg.configFile."niri/config.kdl".source = "${niriConfigPkg}/niri-config.kdl";
}
(lib.mkIf dmsEnabled {
# Seed empty rules file so niri can load the include before DMS first deploy.
home.activation.dmsNiriWindowRulesSeed = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
rulesDir="$HOME/.config/niri/dms"
rulesFile="$rulesDir/windowrules.kdl"
@@ -283,8 +137,6 @@ in
programs.niri.package = pkgs.niri;
programs.xwayland.enable = true;
xdg.portal.extraPortals = [ pkgs.xdg-desktop-portal-gnome ];
# Niri resolves `xwayland-satellite` from PATH to provide XWayland + `$DISPLAY` for X11
# clients (Steam, etc.). See https://github.com/YaLTeR/niri/issues/452
environment.systemPackages = [ pkgs.xwayland-satellite ];
})
(lib.mkIf (cfg.niri.enable && hmAvailable) {
+37
View File
@@ -0,0 +1,37 @@
{ ... }: {
flake.lib.niriWindowRules = [
{
matches = [
{
app-id = "^$";
title = "^$";
}
];
open-floating = true;
open-focused = false;
}
{
matches = [
{ title = "^(Picture-in-Picture|Picture in picture)$"; }
];
open-floating = true;
open-focused = false;
default-column-width = { fixed = 640; };
default-window-height = { fixed = 360; };
default-floating-position = _: {
props = {
x = 32;
y = 32;
relative-to = "bottom-right";
};
};
}
{
matches = [
{ app-id = "^Minecraft\\*"; }
{ app-id = "^Minecraft$"; }
];
tiled-state = true;
}
];
}
@@ -0,0 +1,44 @@
# Builds matugen config.toml text for DMS dynamic theming.
{
lib,
home,
hyprlandEnabled,
ohMyPoshEnabled,
discordEnabled,
obsidianSnippetDirs,
}:
let
templateBlock = name: input: output: ''
[templates.${name}]
input_path = '${input}'
output_path = '${output}'
'';
in
''
[config]
''
+ lib.optionalString hyprlandEnabled (
templateBlock "hyprland"
"${home}/.config/matugen/templates/hyprland-colors.conf"
"${home}/.config/hypr/colors.conf"
)
+ lib.optionalString ohMyPoshEnabled (
templateBlock "ohmyposh"
"${home}/.config/matugen/templates/ohmyposh-theme.omp.json"
"${home}/.config/oh-my-posh/theme.omp.json"
)
+ lib.optionalString discordEnabled (
templateBlock "dank-discord"
"${home}/.config/matugen/templates/dank-discord.css"
"${home}/.config/vesktop/themes/dank-discord.css"
)
+ lib.optionalString (obsidianSnippetDirs != [ ]) (
lib.concatStringsSep "\n" (
lib.lists.imap0 (i: snippetDir:
templateBlock "Obsidian${toString i}"
"${home}/.config/matugen/templates/obsidian-minimal-matugen-colors.css"
"${snippetDir}/matugen.css"
) obsidianSnippetDirs
)
)
+5 -41
View File
@@ -6,32 +6,17 @@
default = true;
description = "GPU temp in DMS bar.";
};
obsidianSnippetsDir = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "Legacy single Obsidian snippets dir for matugen.";
};
obsidianConfigDirs = lib.mkOption {
obsidianVaults = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Vault `.obsidian/` paths for matugen.";
};
obsidianSnippetsDirs = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Explicit `.obsidian/snippets` paths.";
example = [ "/mnt/zimaos/Obsidian/Home/.obsidian" ];
description = "Vault `.obsidian/` dirs; matugen writes `<vault>/snippets/matugen.css`.";
};
extraRightBarWidgets = lib.mkOption {
type = lib.types.listOf lib.types.attrs;
default = [ ];
description = "Extra right-bar widgets merged into the first-run DMS settings seed.";
};
enableWvkbdToggle = lib.mkEnableOption ''
wvkbd DMS plugin + bar toggle (touch / uConsole).
'';
enableRbwLockToggle = lib.mkEnableOption ''
rbw vault lock/unlock button in the DMS bar (Bitwarden CLI via rbw).
'';
rebuildCommand = lib.mkOption {
type = lib.types.nullOr (lib.types.listOf lib.types.str);
default = null;
@@ -53,7 +38,8 @@
type = lib.types.bool;
default = true;
description = ''
Install bundled third-party DMS plugins from flake inputs on all DMS hosts.
Install bundled DMS plugins from flake inputs (third-party + chiasson).
Disable individual plugins in DMS settings; set this to false to skip all bundled plugins.
'';
};
};
@@ -81,29 +67,7 @@
];
config = lib.mkIf dmsEnabled {
dms.enable = true;
dms.enableGpuTemp = cfg.enableGpuTemp or true;
dms.obsidianSnippetsDir = cfg.obsidianSnippetsDir or null;
dms.obsidianConfigDirs = cfg.obsidianConfigDirs or [ ];
dms.obsidianSnippetsDirs = cfg.obsidianSnippetsDirs or [ ];
dms.enableWvkbdToggle = cfg.enableWvkbdToggle or false;
dms.enableRbwLockToggle = cfg.enableRbwLockToggle or false;
dms.extraRightBarWidgets =
(lib.optionals (cfg.enableWvkbdToggle or false) [
{
id = "wvkbdToggle";
enabled = true;
}
])
++ (lib.optionals (cfg.enableRbwLockToggle or false) [
{
id = "rbwLockToggle";
enabled = true;
}
])
++ (cfg.extraRightBarWidgets or [ ]);
programs.nix-monitor.rebuildCommand = rebuildCommand;
dms.bundleThirdPartyPlugins = cfg.bundleThirdPartyPlugins or true;
};
};
}
@@ -7,11 +7,13 @@
...
}: let
home = config.home.homeDirectory;
cfg = config.dms;
gpuTempEnabled = cfg.enableGpuTemp;
dmsEnabled = lib.attrByPath [ "chiasson" "desktop" "shell" ] null osConfig == "dms";
dmsOs = lib.attrByPath [ "chiasson" "desktop" "shells" "dms" ] { } osConfig;
gpuTempEnabled = dmsOs.enableGpuTemp or true;
hyprlandEnabled = lib.attrByPath [ "chiasson" "desktop" "hyprland" "enable" ] false osConfig;
discordEnabled = lib.attrByPath [ "chiasson" "home" "apps" "discord" "enable" ] false config;
ohMyPoshEnabled = config.programs.oh-my-posh.enable or false;
obsidianSnippetDirs = map (v: v + "/snippets") (dmsOs.obsidianVaults or [ ]);
matugenOutputDirs =
lib.optionals hyprlandEnabled [ "${home}/.config/hypr" ]
++ lib.optionals ohMyPoshEnabled [ "${home}/.config/oh-my-posh" ]
@@ -20,44 +22,44 @@
"${home}/.config/qt6ct/colors"
"${home}/.config/qt5ct/colors"
];
ensureMatugenOutputDirsScript = pkgs.writeShellScript "dms-ensure-matugen-output-dirs" ''
set -euo pipefail
${lib.concatStringsSep "\n" (map (dir: "mkdir -p ${lib.escapeShellArg dir}") matugenOutputDirs)}
${lib.optionalString ohMyPoshEnabled ''
ensureMatugenBody =
lib.concatStringsSep "\n" (map (dir: "mkdir -p ${lib.escapeShellArg dir}") matugenOutputDirs)
+ lib.concatStringsSep "\n" (map (dir: ''
snippetDir=${lib.escapeShellArg dir}
parentDir="$(dirname "$snippetDir")"
if [ -d "$snippetDir" ]; then
:
elif [ ! -d "$parentDir" ] || [ ! -w "$parentDir" ]; then
echo "dms: skipping Obsidian snippet dir '$snippetDir' (parent missing or not writable)" >&2
else
mkdir -p "$snippetDir"
fi
'') obsidianSnippetDirs)
+ lib.optionalString ohMyPoshEnabled ''
dest="$HOME/.config/oh-my-posh/theme.omp.json"
template="$HOME/.config/matugen/templates/ohmyposh-theme.omp.json"
# matugen writes output_path in place; HM symlinks into the store are read-only and go stale.
if [ -L "$dest" ] || { [ -e "$dest" ] && ! [ -f "$dest" ]; }; then
rm -f "$dest"
fi
if [ ! -f "$dest" ]; then
# Seed from our matugen template (same generation closure), not programs.oh-my-posh.package
# which can reference a GC'd store path after profile churn.
cp -L "$template" "$dest"
fi
''}
'';
obsidianSnippetsDir = cfg.obsidianSnippetsDir; # legacy single path
obsidianSnippetDirs =
let
snippetsFromConfigDirs = map (d: "${d}/snippets") cfg.obsidianConfigDirs;
explicitSnippetsDirs =
cfg.obsidianSnippetsDirs
++ lib.optionals (obsidianSnippetsDir != null) [obsidianSnippetsDir];
in
lib.unique (snippetsFromConfigDirs ++ explicitSnippetsDirs);
# Clears Quickshell QML bytecode cache so bar plugins reload (stale *.qmlc keeps old UI).
dmsRestartOnceScript = pkgs.writeShellScript "dms-rbw-plugin-restart-once" ''
ensureMatugenOutputDirsScript = pkgs.writeShellScript "dms-ensure-matugen-output-dirs" ''
set -euo pipefail
rt="''${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
exec 9>"$rt/dms-rbw-plugin-restart.lock"
${pkgs.util-linux}/bin/flock -n 9 || exit 0
cache="''${XDG_CACHE_HOME:-''$HOME/.cache}/quickshell/qmlcache"
rm -rf "$cache"
${pkgs.systemd}/bin/systemctl --user try-restart dms.service 2>/dev/null || true
${ensureMatugenBody}
'';
dmsRestartOnceScript = pkgs.writeShellScript "dms-plugin-restart-once" (
builtins.replaceStrings
[ "@FLOCK@" "@SYSTEMCTL@" ]
[
"${pkgs.util-linux}/bin/flock"
"${pkgs.systemd}/bin/systemctl"
]
(builtins.readFile ./scripts/dms-restart-once.sh)
);
jsonFormat = pkgs.formats.json { };
dmsConfigDir = "${config.xdg.configHome}/DankMaterialShell";
defaultSeedDir = lib.attrByPath [ "chiasson" "desktop" "shells" "dms" "defaultSeedDir" ] null osConfig;
@@ -75,11 +77,45 @@
"settings.json" = jsonFormat.generate "dms-settings-seed.json" (
import ../_private/default-settings.nix {
inherit lib gpuTempEnabled;
extraRightBarWidgets = cfg.extraRightBarWidgets;
extraRightBarWidgets = dmsOs.extraRightBarWidgets or [ ];
}
);
};
dmsSeedBody = lib.concatMapStringsSep "\n" (
fileName:
let
dest = "${dmsConfigDir}/${fileName}";
src = dmsSeedSources.${fileName};
in
''
dest=${lib.escapeShellArg dest}
mkdir -p "$(dirname "$dest")"
if [ -L "$dest" ]; then
${pkgs.coreutils}/bin/cp --remove-destination -L "$dest" "$dest"
fi
if [ ! -e "$dest" ]; then
${pkgs.coreutils}/bin/cp ${lib.escapeShellArg src} "$dest"
chmod u+w "$dest"
fi
''
) (lib.attrNames dmsSeedSources);
dmsSeedConfigScript = pkgs.writeShellScript "dms-seed-config" ''
set -euo pipefail
${dmsSeedBody}
'';
matugenConfigText = import ../_private/matugen-config.nix {
inherit
lib
home
hyprlandEnabled
ohMyPoshEnabled
discordEnabled
obsidianSnippetDirs
;
};
# Directory name must match each plugin's `id` in plugin.json.
thirdPartyPlugins = {
dankVault = inputs.dms-plugin-dank-vault;
@@ -98,6 +134,8 @@
wallpaperCarousel = inputs.dms-plugin-wallpaper-carousel;
ephemera = inputs.dms-plugin-ephemera;
appShortcut = inputs.dms-plugin-app-shortcut;
wvkbdToggle = inputs.dms-plugin-wvkbd-toggle;
rbwLockToggle = inputs.dms-plugin-rbw-lock-toggle;
};
shipThirdPartyPlugin =
@@ -108,106 +146,13 @@
};
in {
options = {
dms.enable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "DMS Home Manager config from this flake.";
};
dms.enableGpuTemp = lib.mkOption {
type = lib.types.bool;
default = true;
description = "GPU temp widget in the bar.";
};
# <vault>/.obsidian/snippets — unset skips Obsidian matugen output
dms.obsidianSnippetsDir = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
example = "/home/olivier/Documents/Notes/.obsidian/snippets";
description = "Snippet dir for `matugen.css` (legacy; prefer `obsidianConfigDirs`).";
};
# Each path is a vaults `.obsidian/` — we write `<path>/snippets/matugen.css`.
dms.obsidianConfigDirs = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
example = [
"/mnt/zimaos/Obsidian/Home/.obsidian"
];
description = "Vault `.obsidian` dirs for matugen CSS.";
};
# Or pass `.obsidian/snippets` paths yourself.
dms.obsidianSnippetsDirs = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [];
example = [
"/mnt/zimaos/Obsidian/Home/.obsidian/snippets"
];
description = "Explicit snippet directories for matugen CSS.";
};
# Prepended to the bars rightWidgets (shows first).
dms.extraRightBarWidgets = lib.mkOption {
type = lib.types.listOf lib.types.attrs;
default = [];
example = [
{ id = "wvkbdToggle"; enabled = true; }
{ id = "rbwLockToggle"; enabled = true; }
];
description = "Extra `{ id, enabled, }` widgets merged into the first-run settings seed.";
};
dms.enableWvkbdToggle = lib.mkEnableOption ''
Ship wvkbd-toggle plugin into `~/.config/DankMaterialShell/plugins/` (toggle from NixOS `desktop.shells.dms`).
'';
dms.enableRbwLockToggle = lib.mkEnableOption ''
Ship rbw-lock-toggle into `~/.config/DankMaterialShell/plugins/rbwLockToggle/` (directory name matches plugin id; Bitwarden vault lock/unlock in the bar).
'';
dms.bundleThirdPartyPlugins = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Install bundled third-party DMS plugins (DankVault, Calculator, Home Assistant, )
into `~/.config/DankMaterialShell/plugins/` from flake inputs.
'';
};
};
imports = [
inputs.dms.homeModules.dank-material-shell
inputs.nix-monitor.homeManagerModules.default
];
config = lib.mkIf cfg.enable (lib.mkMerge [
(lib.mkIf cfg.enableWvkbdToggle {
xdg.configFile."DankMaterialShell/plugins/wvkbd-toggle/plugin.json".source =
../plugins/wvkbd-toggle/plugin.json;
xdg.configFile."DankMaterialShell/plugins/wvkbd-toggle/WvkbdToggle.qml".source =
../plugins/wvkbd-toggle/WvkbdToggle.qml;
xdg.configFile."DankMaterialShell/plugins/wvkbd-toggle/WvkbdToggleSettings.qml".source =
../plugins/wvkbd-toggle/WvkbdToggleSettings.qml;
})
(lib.mkIf cfg.enableRbwLockToggle {
# Directory name matches plugin id (same idea as NixMonitor / ideapad wvkbdToggle).
xdg.configFile."DankMaterialShell/plugins/rbwLockToggle/plugin.json" = {
source = ../plugins/rbw-lock-toggle/plugin.json;
onChange = "${dmsRestartOnceScript}";
};
xdg.configFile."DankMaterialShell/plugins/rbwLockToggle/RbwLockToggleSettings.qml" = {
source = ../plugins/rbw-lock-toggle/RbwLockToggleSettings.qml;
onChange = "${dmsRestartOnceScript}";
};
xdg.configFile."DankMaterialShell/plugins/rbwLockToggle/RbwLockToggle.qml" = {
source = ../plugins/rbw-lock-toggle/RbwLockToggle.qml;
onChange = "${dmsRestartOnceScript}";
};
})
(lib.mkIf cfg.bundleThirdPartyPlugins {
config = lib.mkIf dmsEnabled (lib.mkMerge [
(lib.mkIf (dmsOs.bundleThirdPartyPlugins or true) {
xdg.configFile =
lib.mapAttrs (id: src: shipThirdPartyPlugin id src) thirdPartyPlugins;
})
@@ -272,25 +217,7 @@ in {
};
# Detach store symlinks so UI edits survive rebuilds; seed defaults only on a fresh profile.
home.activation.dmsSeedConfig = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
${lib.concatMapStringsSep "\n" (
fileName:
let
dest = "${dmsConfigDir}/${fileName}";
src = dmsSeedSources.${fileName};
in ''
dest=${lib.escapeShellArg dest}
mkdir -p "$(dirname "$dest")"
if [ -L "$dest" ]; then
${pkgs.coreutils}/bin/cp --remove-destination -L "$dest" "$dest"
fi
if [ ! -e "$dest" ]; then
${pkgs.coreutils}/bin/cp ${lib.escapeShellArg src} "$dest"
chmod u+w "$dest"
fi
''
) (lib.attrNames dmsSeedSources)}
'';
home.activation.dmsSeedConfig = lib.hm.dag.entryAfter [ "writeBoundary" ] "${dmsSeedConfigScript}";
# matugen won't create missing output_path parents; run before DMS theme generation.
systemd.user.services.dms.serviceConfig.ExecStartPre =
@@ -299,478 +226,13 @@ in {
# DMS / matugen custom templates.
#
# DMS docs (custom templates): https://danklinux.com/docs/dankmaterialshell/application-themes#custom-matugen-templates
xdg.configFile."matugen/config.toml".text =
''
[config]
''
+ lib.optionalString hyprlandEnabled ''
[templates.hyprland]
input_path = '${home}/.config/matugen/templates/hyprland-colors.conf'
output_path = '${home}/.config/hypr/colors.conf'
''
+ lib.optionalString ohMyPoshEnabled ''
[templates.ohmyposh]
input_path = '${home}/.config/matugen/templates/ohmyposh-theme.omp.json'
output_path = '${home}/.config/oh-my-posh/theme.omp.json'
''
+ lib.optionalString discordEnabled ''
[templates.dank-discord]
input_path = '${home}/.config/matugen/templates/dank-discord.css'
output_path = '${home}/.config/vesktop/themes/dank-discord.css'
''
+ lib.optionalString (obsidianSnippetDirs != []) (
lib.concatStringsSep "\n" (
lib.lists.imap0 (i: snippetDir: ''
[templates.Obsidian${toString i}]
input_path = '${home}/.config/matugen/templates/obsidian-minimal-matugen-colors.css'
output_path = '${snippetDir}/matugen.css'
'') obsidianSnippetDirs
)
)
;
# Obsidian snippet dirs for matugen: mkdir here instead of tmpfiles (no chmod fights on weird mounts).
home.activation.dmsEnsureObsidianSnippetDirs = lib.hm.dag.entryAfter ["writeBoundary"] (
let
snippetDirs = lib.unique obsidianSnippetDirs;
escapedSnippetDirs = map lib.escapeShellArg snippetDirs;
in
''
for snippetDir in ${lib.concatStringsSep " " escapedSnippetDirs}; do
parentDir="$(dirname "$snippetDir")"
if [ -d "$snippetDir" ]; then
continue
fi
if [ ! -d "$parentDir" ] || [ ! -w "$parentDir" ]; then
echo "dms: skipping Obsidian snippet dir '$snippetDir' (parent missing or not writable)" >&2
continue
fi
mkdir -p "$snippetDir"
done
''
);
xdg.configFile."matugen/config.toml".text = matugenConfigText;
xdg.configFile."matugen/templates/dank-discord.css".source = ../templates/dank-discord.css;
# matugen template → Hyprland colors file sourced at runtime.
# Your Hyprland module includes: `source = ~/.config/hypr/colors.conf`
# (see `modules/desktop/hyprland/home-manager/appearance.nix`).
xdg.configFile."matugen/templates/hyprland-colors.conf".text = ''
# Hyprland colors generated by DMS/matugen
# Keep `.default` so DMS can swap to `.dark` when "Always use Dark Theme" is enabled.
general {
col.active_border = rgb({{colors.primary.default.hex_stripped}}) rgb({{colors.secondary.default.hex_stripped}}) rgb({{colors.tertiary.default.hex_stripped}})
col.inactive_border = rgba({{colors.surface_variant.default.hex_stripped}}ee)
}
decoration {
shadow {
color = rgba({{colors.shadow.default.hex_stripped}}cc)
}
}
# Hyprbars colors
plugin {
hyprbars {
bar_color = rgba({{colors.surface.default.hex_stripped}}ff)
col.text = rgba({{colors.on_surface.default.hex_stripped}}ff)
}
}
'';
# matugen → Oh My Posh JSON; hex in the base theme swapped for matugen tokens.
xdg.configFile."matugen/templates/ohmyposh-theme.omp.json".text = ''
{
"$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json",
"blocks": [
{
"alignment": "left",
"segments": [
{
"background": "#3A456E",
"foreground": "#ffbebc",
"leading_diamond": "\ue0b6",
"style": "diamond",
"template": "\u007B\u007B .UserName \u007D\u007D@\u007B\u007B .HostName \u007D\u007D <#ffffff>on</>",
"type": "session"
},
{
"type": "os",
"style": "diamond",
"background": "#3A456E",
"foreground": "#ffffff",
"trailing_diamond": "\ue0b4",
"template": " \u007B\u007B .Icon \u007D\u007D "
},
{"type": "session",
"style": "diamond",
"background": "#3A456E",
"foreground": "#ffbebc",
"template": "\u007B\u007B if .SSHSession \u007D\u007Dvia SSH\u007B\u007B end \u007D\u007D",
"trailing_diamond": "\ue0b4",
"type": "session"
},
{
"type": "text",
"style": "diamond",
"background": "#3A456E",
"foreground": "#ffbebc",
"template": "\u007B\u007B if .Env.IN_NIX_SHELL \u007D\u007Din nix-shell\u007B\u007B end \u007D\u007D",
"trailing_diamond": "\ue0b4"
},
{
"background": "#3A456E",
"foreground": "#bc93ff",
"leading_diamond": "\ue0b6",
"properties": {
"time_format": "Monday <#ffffff>at</> 3:04 PM"
},
"style": "diamond",
"trailing_diamond": "\ue0b4",
"template": "\u007B\u007B .CurrentDate | date .Format \u007D\u007D",
"type": "time"
},
{
"background": "#3A456E",
"foreground": "#43CCEA",
"foreground_templates": [
"\u007B\u007B if or (.Working.Changed) (.Staging.Changed) \u007D\u007D#FF9248\u007B\u007B end \u007D\u007D",
"\u007B\u007B if and (gt .Ahead 0) (gt .Behind 0) \u007D\u007D#ff4500\u007B\u007B end \u007D\u007D",
"\u007B\u007B if gt .Ahead 0 \u007D\u007D#B388FF\u007B\u007B end \u007D\u007D",
"\u007B\u007B if gt .Behind 0 \u007D\u007D#B388FF\u007B\u007B end \u007D\u007D"
],
"leading_diamond": " \ue0b6",
"options": {
"branch_template": "\u007B\u007B trunc 25 .Branch \u007D\u007D",
"fetch_status": true,
"fetch_upstream_icon": true
},
"style": "diamond",
"template": " \u007B\u007B .UpstreamIcon \u007D\u007D\u007B\u007B .HEAD \u007D\u007D\u007B\u007Bif .BranchStatus \u007D\u007D \u007B\u007B .BranchStatus \u007D\u007D\u007B\u007B end \u007D\u007D\u007B\u007B if .Working.Changed \u007D\u007D \uf044 \u007B\u007B .Working.String \u007D\u007D\u007B\u007B end \u007D\u007D\u007B\u007B if and (.Working.Changed) (.Staging.Changed) \u007D\u007D |\u007B\u007B end \u007D\u007D\u007B\u007B if .Staging.Changed \u007D\u007D \uf046 \u007B\u007B .Staging.String \u007D\u007D\u007B\u007B end \u007D\u007D\u007B\u007B if gt .StashCount 0 \u007D\u007D \ueb4b \u007B\u007B .StashCount \u007D\u007D\u007B\u007B end \u007D\u007D ",
"trailing_diamond": "\ue0b4",
"type": "git"
},
{
"background": "#3A456E",
"foreground": "#E4F34A",
"leading_diamond": " \ue0b6",
"options": {
"fetch_version": false
},
"style": "diamond",
"template": "\ue235 \u007B\u007B if .Error \u007D\u007D\u007B\u007B .Error \u007D\u007D\u007B\u007B else \u007D\u007D\u007B\u007B if .Venv \u007D\u007D\u007B\u007B .Venv \u007D\u007D \u007B\u007B end \u007D\u007D\u007B\u007B .Full \u007D\u007D\u007B\u007B end \u007D\u007D",
"trailing_diamond": "\ue0b4",
"type": "python"
},
{
"background": "#3A456E",
"foreground": "#7FD5EA",
"leading_diamond": " \ue0b6",
"options": {
"fetch_version": false
},
"style": "diamond",
"template": "\ue626\u007B\u007B if .Error \u007D\u007D\u007B\u007B .Error \u007D\u007D\u007B\u007B else \u007D\u007D\u007B\u007B .Full \u007D\u007D\u007B\u007B end \u007D\u007D",
"trailing_diamond": "\ue0b4",
"type": "go"
},
{
"background": "#3A456E",
"foreground": "#42E66C",
"leading_diamond": " \ue0b6",
"options": {
"fetch_version": false
},
"style": "diamond",
"template": "\ue718\u007B\u007B if .PackageManagerIcon \u007D\u007D\u007B\u007B .PackageManagerIcon \u007D\u007D \u007B\u007B end \u007D\u007D\u007B\u007B .Full \u007D\u007D",
"trailing_diamond": "\ue0b4",
"type": "node"
},
{
"background": "#3A456E",
"foreground": "#E64747",
"leading_diamond": " \ue0b6",
"options": {
"fetch_version": false
},
"style": "diamond",
"template": "\ue791\u007B\u007B if .Error \u007D\u007D\u007B\u007B .Error \u007D\u007D\u007B\u007B else \u007D\u007D\u007B\u007B .Full \u007D\u007D\u007B\u007B end \u007D\u007D",
"trailing_diamond": "\ue0b4",
"type": "ruby"
},
{
"background": "#3A456E",
"foreground": "#E64747",
"leading_diamond": " \ue0b6",
"options": {
"fetch_version": false
},
"style": "diamond",
"template": "\ue738\u007B\u007B if .Error \u007D\u007D\u007B\u007B .Error \u007D\u007D\u007B\u007B else \u007D\u007D\u007B\u007B .Full \u007D\u007D\u007B\u007B end \u007D\u007D",
"trailing_diamond": "\ue0b4",
"type": "java"
},
{
"background": "#3A456E",
"foreground": "#9B6BDF",
"leading_diamond": " \ue0b6",
"options": {
"fetch_version": false
},
"style": "diamond",
"template": "\ue624\u007B\u007B if .Error \u007D\u007D\u007B\u007B .Error \u007D\u007D\u007B\u007B else \u007D\u007D\u007B\u007B .Full \u007D\u007D\u007B\u007B end \u007D\u007D ",
"trailing_diamond": "\ue0b4",
"type": "julia"
},
{
"type": "php",
"style": "diamond",
"foreground": "#ffffff",
"background": "#4063D8",
"leading_diamond": " \ue0b6",
"options": {
"fetch_version": false
},
"template": "\ue73d \u007B\u007B .Full \u007D\u007D ",
"trailing_diamond": "\ue0b4"
},
{
"background": "#3A456E",
"foreground": "#9B6BDF",
"foreground_templates": [
"\u007B\u007Bif eq \"Charging\" .State.String\u007D\u007D#40c4ff\u007B\u007Bend\u007D\u007D",
"\u007B\u007Bif eq \"Discharging\" .State.String\u007D\u007D#ff5722\u007B\u007Bend\u007D\u007D",
"\u007B\u007Bif eq \"Full\" .State.String\u007D\u007D#4caf50\u007B\u007Bend\u007D\u007D"
],
"leading_diamond": " \ue0b6",
"options": {
"charged_icon": "• ",
"charging_icon": "\u21e1 ",
"discharging_icon": "\u21e3 "
},
"style": "diamond",
"template": "\u007B\u007B if not .Error \u007D\u007D\u007B\u007B .Icon \u007D\u007D\u007B\u007B .Percentage \u007D\u007D\u007B\u007B end \u007D\u007D\u007B\u007B .Error \u007D\u007D",
"trailing_diamond": "\ue0b4",
"type": "battery"
}
],
"type": "prompt"
},
{
"alignment": "right",
"segments": [
{
"background": "#3A456E",
"foreground": "#AEA4BF",
"leading_diamond": "\ue0b6",
"options": {
"style": "austin",
"threshold": 150
},
"style": "diamond",
"template": "\u007B\u007B .FormattedMs \u007D\u007D",
"trailing_diamond": "\ue0b4 ",
"type": "executiontime"
}
],
"type": "prompt"
},
{
"alignment": "left",
"newline": true,
"segments": [
{
"background": "#3A456E",
"foreground": "#3EC669",
"leading_diamond": "\ue0b6",
"properties": {
"style": "agnoster_full",
"cycle": [
"#3EC669,#3A456E",
"#43CCEA,#3A456E",
"#E4F34A,#3A456E",
"#9B6BDF,#3A456E"
],
"cycle_folder_separator": true
},
"style": "diamond",
"template": "\ue5ff \u007B\u007B .Path \u007D\u007D",
"trailing_diamond": "\ue0b4",
"type": "path"
},
{
"background": "#3A456E",
"foreground": "#ffbebc",
"leading_diamond": "\ue0b6",
"style": "diamond",
"template": "\ue602",
"trailing_diamond": "\ue0b4",
"type": "text"
}
],
"type": "prompt"
}
],
"final_space": true,
"version": 4
}
'';
# matugen template → Obsidian CSS snippet (Minimal-compatible variables).
#
# Upstream template:
# https://raw.githubusercontent.com/Simorg2002/obsidian-matugen-template/refs/heads/main/obsidian-minimal-matugen-colors.css
xdg.configFile."matugen/templates/obsidian-minimal-matugen-colors.css".text = ''
.theme-dark {
--accent-h:calc({{colors.primary.dark.hue}}/255*360);
--accent-s:calc({{colors.primary.dark.saturation}}/255*100%);
--accent-l:calc({{colors.primary.dark.lightness}}/255*100%);
--text-normal:{{colors.on_surface.dark.hex}};
--text-muted:{{colors.on_surface_variant.dark.hex}};
--text-faint:{{colors.outline.dark.hex}};
--background-primary:{{colors.surface.dark.hex}}; /* editor and right ribbon */
--background-secondary:{{colors.surface_dim.dark.hex}}; /* left ribbon */
--background-modifier-hover:{{colors.secondary_container.dark.hex}};
--background-modifier-active-hover:{{colors.secondary.dark.hex}};
--background-modifier-message:{{colors.surface_dim.dark.hex}};
--background-modifier-form-field:{{colors.primary_container.dark.hex}};
--ribbon-background:{{colors.surface.dark.hex}};
--divider-color:{{colors.secondary_container.dark.hex}};
--scrollbar-thumb-bg:{{colors.on_secondary_container.dark.hex}};
--status-bar-border-color:{{colors.secondary_container.dark.hex}};
--status-bar-background:{{colors.surface.dark.hex}};
--titlebar-background:{{colors.surface.dark.hex}};
--titlebar-text-color:{{colors.on_surface.dark.hex}};
--nav-item-color:{{colors.on_surface_variant.dark.hex}};
--nav-item-color-active:{{colors.on_primary_container.dark.hex}};
--nav-item-background-active:{{colors.primary_container.dark.hex}};
--nav-item-color-hover:{{colors.on_secondary_container.dark.hex}};
--nav-item-background-hover:{{colors.secondary_container.dark.hex}};
--tab-background-active:{{colors.primary_container.dark.hex}};
--tab-text-color-active:{{colors.on_primary_container.dark.hex}};
--tab-text-color:{{colors.on_surface_variant.dark.hex}};
--icon-color:{{colors.on_surface.dark.hex}};
--h1-color:{{colors.primary.dark.hex}};
--h2-color: color-mix(in srgb, {{colors.primary.dark.hex}}, {{colors.tertiary.dark.hex}});
--h3-color:{{colors.tertiary.dark.hex}};
--h4-color: color-mix(in srgb, {{colors.primary.dark.hex}}, {{colors.secondary.dark.hex}});
--h5-color:{{colors.secondary.dark.hex}};
--h6-color: color-mix(in srgb, {{colors.tertiary.dark.hex}}, {{colors.secondary.dark.hex}});
}
/* add parts not accessible without specific selector?*/
.theme-dark .sidebar-toggle-button.mod-left {
color:{{colors.on_surface.dark.hex}};
background:{{colors.surface.dark.hex}};
}
.theme-dark .sidebar-toggle-button.mod-right {
color:{{colors.on_surface.dark.hex}};
background:{{colors.surface.dark.hex}};
}
.theme-dark .workspace-tab-header-container{
background:{{colors.surface.dark.hex}};
}
/* copy for light mode */
.theme-light {
--accent-h:calc({{colors.primary.light.hue}}/255*360);
--accent-s:calc({{colors.primary.light.saturation}}/255*100%);
--accent-l:calc({{colors.primary.light.lightness}}/255*100%);
--text-normal:{{colors.on_surface.light.hex}};
--text-muted:{{colors.on_surface_variant.light.hex}};
--text-faint:{{colors.outline.light.hex}};
--background-primary:{{colors.surface.light.hex}}; /* editor and right ribbon */
--background-secondary:{{colors.surface_dim.light.hex}}; /* left ribbon */
--background-modifier-hover:{{colors.secondary_container.light.hex}};
--background-modifier-active-hover:{{colors.secondary.light.hex}};
--background-modifier-message:{{colors.surface_dim.light.hex}};
--background-modifier-form-field:{{colors.primary_container.light.hex}};
--ribbon-background:{{colors.surface.light.hex}};
--divider-color:{{colors.secondary_container.light.hex}};
--scrollbar-thumb-bg:{{colors.on_secondary_container.light.hex}};
--status-bar-border-color:{{colors.secondary_container.light.hex}};
--status-bar-background:{{colors.surface.light.hex}};
--titlebar-background:{{colors.surface.light.hex}};
--titlebar-text-color:{{colors.on_surface.light.hex}};
--nav-item-color:{{colors.on_surface_variant.light.hex}};
--nav-item-color-active:{{colors.on_primary_container.light.hex}};
--nav-item-background-active:{{colors.primary_container.light.hex}};
--nav-item-color-hover:{{colors.on_secondary_container.light.hex}};
--nav-item-background-hover:{{colors.secondary_container.light.hex}};
--tab-background-active:{{colors.primary_container.light.hex}};
--tab-text-color-active:{{colors.on_primary_container.light.hex}};
--tab-text-color:{{colors.on_surface_variant.light.hex}};
--icon-color:{{colors.on_surface.light.hex}};
--h1-color:{{colors.primary.light.hex}};
--h2-color: color-mix(in srgb, {{colors.primary.light.hex}}, {{colors.tertiary.light.hex}});
--h3-color:{{colors.tertiary.light.hex}};
--h4-color: color-mix(in srgb, {{colors.primary.light.hex}}, {{colors.secondary.light.hex}});
--h5-color:{{colors.secondary.light.hex}};
--h6-color: color-mix(in srgb, {{colors.tertiary.light.hex}}, {{colors.secondary.light.hex}});
}
/* add parts not accessible without specific selector?*/
.theme-light .sidebar-toggle-button.mod-left {
color:{{colors.on_surface.light.hex}};
background:{{colors.surface.light.hex}};
}
.theme-light .sidebar-toggle-button.mod-right {
color:{{colors.on_surface.light.hex}};
background:{{colors.surface.light.hex}};
}
.theme-light .workspace-tab-header-container{
background:{{colors.surface.light.hex}};
}
/* Hide window controls (frameless titlebar buttons) */
div[aria-label="Close window"],
div[aria-label="Minimize"],
div[aria-label="Restore down"],
div[aria-label="Maximize"]{
display: none !important;
}
/* Remove the extra right-side spacing reserved for those buttons */
.is-hidden-frameless:not(.is-fullscreen) .workspace-tabs.mod-top-right-space .workspace-tab-header-container{
padding-right: 0px !important;
}
.is-hidden-frameless:not(.is-fullscreen) .workspace-tabs.mod-top-right-space .workspace-tab-header-container:after{
display: none !important;
}
'';
xdg.configFile."matugen/templates/hyprland-colors.conf".source = ../templates/hyprland-colors.conf;
xdg.configFile."matugen/templates/ohmyposh-theme.omp.json".source = ../templates/ohmyposh-theme.omp.json;
xdg.configFile."matugen/templates/obsidian-minimal-matugen-colors.css".source =
../templates/obsidian-minimal-matugen-colors.css;
}
(lib.mkIf ohMyPoshEnabled {
# Same path as matugen `[templates.ohmyposh]`; seeded as a real file by `ensureMatugenOutputDirsScript`.
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
rt="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
exec 9>"$rt/dms-plugin-restart.lock"
@FLOCK@ -n 9 || exit 0
cache="${XDG_CACHE_HOME:-$HOME/.cache}/quickshell/qmlcache"
rm -rf "$cache"
@SYSTEMCTL@ --user try-restart dms.service 2>/dev/null || true
@@ -1,80 +0,0 @@
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common
import qs.Services
import qs.Widgets
import qs.Modules.Plugins
PluginComponent {
id: root
property bool vaultUnlocked: false
function refreshLockState() {
if (!statusProcess.running)
statusProcess.exec(["rbw", "unlocked"]);
}
function toggleLock() {
if (root.vaultUnlocked) {
Quickshell.execDetached(["rbw", "lock"]);
} else {
Quickshell.execDetached(["rbw", "unlock"]);
}
}
pillClickAction: () => root.toggleLock()
Component.onCompleted: refreshLockState()
Timer {
interval: 2500
repeat: true
running: true
onTriggered: root.refreshLockState()
}
Process {
id: statusProcess
command: ["rbw", "unlocked"]
onExited: (exitCode, _exitStatus) => {
root.vaultUnlocked = exitCode === 0;
}
}
horizontalBarPill: Component {
MouseArea {
implicitWidth: iconH.implicitWidth
implicitHeight: iconH.implicitHeight
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.toggleLock()
DankIcon {
id: iconH
name: root.vaultUnlocked ? "lock_open_right" : "lock"
size: root.iconSize
color: parent.containsMouse ? Theme.primary : Theme.surfaceText
}
}
}
verticalBarPill: Component {
MouseArea {
implicitWidth: iconV.implicitWidth
implicitHeight: iconV.implicitHeight
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.toggleLock()
DankIcon {
id: iconV
name: root.vaultUnlocked ? "lock_open_right" : "lock"
size: root.iconSize
color: parent.containsMouse ? Theme.primary : Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
}
@@ -1,16 +0,0 @@
import QtQuick
import qs.Common
import qs.Modules.Plugins
PluginSettings {
id: root
pluginId: "rbwLockToggle"
StyledText {
width: parent.width
text: "Shows rbw vault state with a closed lock (locked) or open lock (unlocked). Click to run `rbw unlock` (pinentry) or `rbw lock`. Requires `rbw` on PATH in the DMS session and a working pinentry."
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
wrapMode: Text.WordWrap
}
}
@@ -1,13 +0,0 @@
{
"id": "rbwLockToggle",
"name": "Bitwarden (rbw) lock",
"description": "Bar control for rbw vault: locked/unlocked padlock; click to unlock or lock",
"version": "1.0.0",
"author": "chiasson.cloud",
"type": "widget",
"capabilities": ["dankbar-widget"],
"component": "./RbwLockToggle.qml",
"settings": "./RbwLockToggleSettings.qml",
"icon": "lock_open",
"permissions": ["settings_read"]
}
@@ -1,51 +0,0 @@
import QtQuick
import Quickshell
import qs.Common
import qs.Services
import qs.Widgets
import qs.Modules.Plugins
PluginComponent {
id: root
function toggleKeyboard() {
Quickshell.execDetached(["sh", "-c", "pkill -SIGRTMIN -x wvkbd-mobintl"]);
}
pillClickAction: () => root.toggleKeyboard()
horizontalBarPill: Component {
MouseArea {
implicitWidth: icon.implicitWidth
implicitHeight: icon.implicitHeight
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.toggleKeyboard()
DankIcon {
id: icon
name: "keyboard"
size: Theme.iconSize
color: parent.containsMouse ? Theme.primary : Theme.surfaceText
}
}
}
verticalBarPill: Component {
MouseArea {
implicitWidth: iconV.implicitWidth
implicitHeight: iconV.implicitHeight
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.toggleKeyboard()
DankIcon {
id: iconV
name: "keyboard"
size: Theme.iconSize
color: parent.containsMouse ? Theme.primary : Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
}
@@ -1,16 +0,0 @@
import QtQuick
import qs.Common
import qs.Modules.Plugins
PluginSettings {
id: root
pluginId: "wvkbdToggle"
StyledText {
width: parent.width
text: "Click the keyboard icon in the bar to show or hide the on-screen keyboard (wvkbd)."
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
wrapMode: Text.WordWrap
}
}
@@ -1,13 +0,0 @@
{
"id": "wvkbdToggle",
"name": "Virtual Keyboard Toggle",
"description": "Bar button to show/hide the wvkbd on-screen keyboard (for touch/tablet)",
"version": "1.0.0",
"author": "chiasson.cloud",
"type": "widget",
"capabilities": ["dankbar-widget"],
"component": "./WvkbdToggle.qml",
"settings": "./WvkbdToggleSettings.qml",
"icon": "keyboard",
"permissions": ["settings_read"]
}
@@ -0,0 +1,21 @@
# Hyprland colors generated by DMS/matugen
# Keep `.default` so DMS can swap to `.dark` when "Always use Dark Theme" is enabled.
general {
col.active_border = rgb({{colors.primary.default.hex_stripped}}) rgb({{colors.secondary.default.hex_stripped}}) rgb({{colors.tertiary.default.hex_stripped}})
col.inactive_border = rgba({{colors.surface_variant.default.hex_stripped}}ee)
}
decoration {
shadow {
color = rgba({{colors.shadow.default.hex_stripped}}cc)
}
}
# Hyprbars colors
plugin {
hyprbars {
bar_color = rgba({{colors.surface.default.hex_stripped}}ff)
col.text = rgba({{colors.on_surface.default.hex_stripped}}ff)
}
}
@@ -0,0 +1,140 @@
.theme-dark {
--accent-h:calc({{colors.primary.dark.hue}}/255*360);
--accent-s:calc({{colors.primary.dark.saturation}}/255*100%);
--accent-l:calc({{colors.primary.dark.lightness}}/255*100%);
--text-normal:{{colors.on_surface.dark.hex}};
--text-muted:{{colors.on_surface_variant.dark.hex}};
--text-faint:{{colors.outline.dark.hex}};
--background-primary:{{colors.surface.dark.hex}}; /* editor and right ribbon */
--background-secondary:{{colors.surface_dim.dark.hex}}; /* left ribbon */
--background-modifier-hover:{{colors.secondary_container.dark.hex}};
--background-modifier-active-hover:{{colors.secondary.dark.hex}};
--background-modifier-message:{{colors.surface_dim.dark.hex}};
--background-modifier-form-field:{{colors.primary_container.dark.hex}};
--ribbon-background:{{colors.surface.dark.hex}};
--divider-color:{{colors.secondary_container.dark.hex}};
--scrollbar-thumb-bg:{{colors.on_secondary_container.dark.hex}};
--status-bar-border-color:{{colors.secondary_container.dark.hex}};
--status-bar-background:{{colors.surface.dark.hex}};
--titlebar-background:{{colors.surface.dark.hex}};
--titlebar-text-color:{{colors.on_surface.dark.hex}};
--nav-item-color:{{colors.on_surface_variant.dark.hex}};
--nav-item-color-active:{{colors.on_primary_container.dark.hex}};
--nav-item-background-active:{{colors.primary_container.dark.hex}};
--nav-item-color-hover:{{colors.on_secondary_container.dark.hex}};
--nav-item-background-hover:{{colors.secondary_container.dark.hex}};
--tab-background-active:{{colors.primary_container.dark.hex}};
--tab-text-color-active:{{colors.on_primary_container.dark.hex}};
--tab-text-color:{{colors.on_surface_variant.dark.hex}};
--icon-color:{{colors.on_surface.dark.hex}};
--h1-color:{{colors.primary.dark.hex}};
--h2-color: color-mix(in srgb, {{colors.primary.dark.hex}}, {{colors.tertiary.dark.hex}});
--h3-color:{{colors.tertiary.dark.hex}};
--h4-color: color-mix(in srgb, {{colors.primary.dark.hex}}, {{colors.secondary.dark.hex}});
--h5-color:{{colors.secondary.dark.hex}};
--h6-color: color-mix(in srgb, {{colors.tertiary.dark.hex}}, {{colors.secondary.dark.hex}});
}
/* add parts not accessible without specific selector?*/
.theme-dark .sidebar-toggle-button.mod-left {
color:{{colors.on_surface.dark.hex}};
background:{{colors.surface.dark.hex}};
}
.theme-dark .sidebar-toggle-button.mod-right {
color:{{colors.on_surface.dark.hex}};
background:{{colors.surface.dark.hex}};
}
.theme-dark .workspace-tab-header-container{
background:{{colors.surface.dark.hex}};
}
/* copy for light mode */
.theme-light {
--accent-h:calc({{colors.primary.light.hue}}/255*360);
--accent-s:calc({{colors.primary.light.saturation}}/255*100%);
--accent-l:calc({{colors.primary.light.lightness}}/255*100%);
--text-normal:{{colors.on_surface.light.hex}};
--text-muted:{{colors.on_surface_variant.light.hex}};
--text-faint:{{colors.outline.light.hex}};
--background-primary:{{colors.surface.light.hex}}; /* editor and right ribbon */
--background-secondary:{{colors.surface_dim.light.hex}}; /* left ribbon */
--background-modifier-hover:{{colors.secondary_container.light.hex}};
--background-modifier-active-hover:{{colors.secondary.light.hex}};
--background-modifier-message:{{colors.surface_dim.light.hex}};
--background-modifier-form-field:{{colors.primary_container.light.hex}};
--ribbon-background:{{colors.surface.light.hex}};
--divider-color:{{colors.secondary_container.light.hex}};
--scrollbar-thumb-bg:{{colors.on_secondary_container.light.hex}};
--status-bar-border-color:{{colors.secondary_container.light.hex}};
--status-bar-background:{{colors.surface.light.hex}};
--titlebar-background:{{colors.surface.light.hex}};
--titlebar-text-color:{{colors.on_surface.light.hex}};
--nav-item-color:{{colors.on_surface_variant.light.hex}};
--nav-item-color-active:{{colors.on_primary_container.light.hex}};
--nav-item-background-active:{{colors.primary_container.light.hex}};
--nav-item-color-hover:{{colors.on_secondary_container.light.hex}};
--nav-item-background-hover:{{colors.secondary_container.light.hex}};
--tab-background-active:{{colors.primary_container.light.hex}};
--tab-text-color-active:{{colors.on_primary_container.light.hex}};
--tab-text-color:{{colors.on_surface_variant.light.hex}};
--icon-color:{{colors.on_surface.light.hex}};
--h1-color:{{colors.primary.light.hex}};
--h2-color: color-mix(in srgb, {{colors.primary.light.hex}}, {{colors.tertiary.light.hex}});
--h3-color:{{colors.tertiary.light.hex}};
--h4-color: color-mix(in srgb, {{colors.primary.light.hex}}, {{colors.secondary.light.hex}});
--h5-color:{{colors.secondary.light.hex}};
--h6-color: color-mix(in srgb, {{colors.tertiary.light.hex}}, {{colors.secondary.light.hex}});
}
/* add parts not accessible without specific selector?*/
.theme-light .sidebar-toggle-button.mod-left {
color:{{colors.on_surface.light.hex}};
background:{{colors.surface.light.hex}};
}
.theme-light .sidebar-toggle-button.mod-right {
color:{{colors.on_surface.light.hex}};
background:{{colors.surface.light.hex}};
}
.theme-light .workspace-tab-header-container{
background:{{colors.surface.light.hex}};
}
/* Hide window controls (frameless titlebar buttons) */
div[aria-label="Close window"],
div[aria-label="Minimize"],
div[aria-label="Restore down"],
div[aria-label="Maximize"]{
display: none !important;
}
/* Remove the extra right-side spacing reserved for those buttons */
.is-hidden-frameless:not(.is-fullscreen) .workspace-tabs.mod-top-right-space .workspace-tab-header-container{
padding-right: 0px !important;
}
.is-hidden-frameless:not(.is-fullscreen) .workspace-tabs.mod-top-right-space .workspace-tab-header-container:after{
display: none !important;
}
@@ -0,0 +1,236 @@
{
"$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json",
"blocks": [
{
"alignment": "left",
"segments": [
{
"background": "#3A456E",
"foreground": "#ffbebc",
"leading_diamond": "\ue0b6",
"style": "diamond",
"template": "\u007B\u007B .UserName \u007D\u007D@\u007B\u007B .HostName \u007D\u007D <#ffffff>on</>",
"type": "session"
},
{
"type": "os",
"style": "diamond",
"background": "#3A456E",
"foreground": "#ffffff",
"trailing_diamond": "\ue0b4",
"template": " \u007B\u007B .Icon \u007D\u007D "
},
{"type": "session",
"style": "diamond",
"background": "#3A456E",
"foreground": "#ffbebc",
"template": "\u007B\u007B if .SSHSession \u007D\u007Dvia SSH\u007B\u007B end \u007D\u007D",
"trailing_diamond": "\ue0b4",
"type": "session"
},
{
"type": "text",
"style": "diamond",
"background": "#3A456E",
"foreground": "#ffbebc",
"template": "\u007B\u007B if .Env.IN_NIX_SHELL \u007D\u007Din nix-shell\u007B\u007B end \u007D\u007D",
"trailing_diamond": "\ue0b4"
},
{
"background": "#3A456E",
"foreground": "#bc93ff",
"leading_diamond": "\ue0b6",
"properties": {
"time_format": "Monday <#ffffff>at</> 3:04 PM"
},
"style": "diamond",
"trailing_diamond": "\ue0b4",
"template": "\u007B\u007B .CurrentDate | date .Format \u007D\u007D",
"type": "time"
},
{
"background": "#3A456E",
"foreground": "#43CCEA",
"foreground_templates": [
"\u007B\u007B if or (.Working.Changed) (.Staging.Changed) \u007D\u007D#FF9248\u007B\u007B end \u007D\u007D",
"\u007B\u007B if and (gt .Ahead 0) (gt .Behind 0) \u007D\u007D#ff4500\u007B\u007B end \u007D\u007D",
"\u007B\u007B if gt .Ahead 0 \u007D\u007D#B388FF\u007B\u007B end \u007D\u007D",
"\u007B\u007B if gt .Behind 0 \u007D\u007D#B388FF\u007B\u007B end \u007D\u007D"
],
"leading_diamond": " \ue0b6",
"options": {
"branch_template": "\u007B\u007B trunc 25 .Branch \u007D\u007D",
"fetch_status": true,
"fetch_upstream_icon": true
},
"style": "diamond",
"template": " \u007B\u007B .UpstreamIcon \u007D\u007D\u007B\u007B .HEAD \u007D\u007D\u007B\u007Bif .BranchStatus \u007D\u007D \u007B\u007B .BranchStatus \u007D\u007D\u007B\u007B end \u007D\u007D\u007B\u007B if .Working.Changed \u007D\u007D \uf044 \u007B\u007B .Working.String \u007D\u007D\u007B\u007B end \u007D\u007D\u007B\u007B if and (.Working.Changed) (.Staging.Changed) \u007D\u007D |\u007B\u007B end \u007D\u007D\u007B\u007B if .Staging.Changed \u007D\u007D \uf046 \u007B\u007B .Staging.String \u007D\u007D\u007B\u007B end \u007D\u007D\u007B\u007B if gt .StashCount 0 \u007D\u007D \ueb4b \u007B\u007B .StashCount \u007D\u007D\u007B\u007B end \u007D\u007D ",
"trailing_diamond": "\ue0b4",
"type": "git"
},
{
"background": "#3A456E",
"foreground": "#E4F34A",
"leading_diamond": " \ue0b6",
"options": {
"fetch_version": false
},
"style": "diamond",
"template": "\ue235 \u007B\u007B if .Error \u007D\u007D\u007B\u007B .Error \u007D\u007D\u007B\u007B else \u007D\u007D\u007B\u007B if .Venv \u007D\u007D\u007B\u007B .Venv \u007D\u007D \u007B\u007B end \u007D\u007D\u007B\u007B .Full \u007D\u007D\u007B\u007B end \u007D\u007D",
"trailing_diamond": "\ue0b4",
"type": "python"
},
{
"background": "#3A456E",
"foreground": "#7FD5EA",
"leading_diamond": " \ue0b6",
"options": {
"fetch_version": false
},
"style": "diamond",
"template": "\ue626\u007B\u007B if .Error \u007D\u007D\u007B\u007B .Error \u007D\u007D\u007B\u007B else \u007D\u007D\u007B\u007B .Full \u007D\u007D\u007B\u007B end \u007D\u007D",
"trailing_diamond": "\ue0b4",
"type": "go"
},
{
"background": "#3A456E",
"foreground": "#42E66C",
"leading_diamond": " \ue0b6",
"options": {
"fetch_version": false
},
"style": "diamond",
"template": "\ue718\u007B\u007B if .PackageManagerIcon \u007D\u007D\u007B\u007B .PackageManagerIcon \u007D\u007D \u007B\u007B end \u007D\u007D\u007B\u007B .Full \u007D\u007D",
"trailing_diamond": "\ue0b4",
"type": "node"
},
{
"background": "#3A456E",
"foreground": "#E64747",
"leading_diamond": " \ue0b6",
"options": {
"fetch_version": false
},
"style": "diamond",
"template": "\ue791\u007B\u007B if .Error \u007D\u007D\u007B\u007B .Error \u007D\u007D\u007B\u007B else \u007D\u007D\u007B\u007B .Full \u007D\u007D\u007B\u007B end \u007D\u007D",
"trailing_diamond": "\ue0b4",
"type": "ruby"
},
{
"background": "#3A456E",
"foreground": "#E64747",
"leading_diamond": " \ue0b6",
"options": {
"fetch_version": false
},
"style": "diamond",
"template": "\ue738\u007B\u007B if .Error \u007D\u007D\u007B\u007B .Error \u007D\u007D\u007B\u007B else \u007D\u007D\u007B\u007B .Full \u007D\u007D\u007B\u007B end \u007D\u007D",
"trailing_diamond": "\ue0b4",
"type": "java"
},
{
"background": "#3A456E",
"foreground": "#9B6BDF",
"leading_diamond": " \ue0b6",
"options": {
"fetch_version": false
},
"style": "diamond",
"template": "\ue624\u007B\u007B if .Error \u007D\u007D\u007B\u007B .Error \u007D\u007D\u007B\u007B else \u007D\u007D\u007B\u007B .Full \u007D\u007D\u007B\u007B end \u007D\u007D ",
"trailing_diamond": "\ue0b4",
"type": "julia"
},
{
"type": "php",
"style": "diamond",
"foreground": "#ffffff",
"background": "#4063D8",
"leading_diamond": " \ue0b6",
"options": {
"fetch_version": false
},
"template": "\ue73d \u007B\u007B .Full \u007D\u007D ",
"trailing_diamond": "\ue0b4"
},
{
"background": "#3A456E",
"foreground": "#9B6BDF",
"foreground_templates": [
"\u007B\u007Bif eq \"Charging\" .State.String\u007D\u007D#40c4ff\u007B\u007Bend\u007D\u007D",
"\u007B\u007Bif eq \"Discharging\" .State.String\u007D\u007D#ff5722\u007B\u007Bend\u007D\u007D",
"\u007B\u007Bif eq \"Full\" .State.String\u007D\u007D#4caf50\u007B\u007Bend\u007D\u007D"
],
"leading_diamond": " \ue0b6",
"options": {
"charged_icon": "• ",
"charging_icon": "\u21e1 ",
"discharging_icon": "\u21e3 "
},
"style": "diamond",
"template": "\u007B\u007B if not .Error \u007D\u007D\u007B\u007B .Icon \u007D\u007D\u007B\u007B .Percentage \u007D\u007D\u007B\u007B end \u007D\u007D\u007B\u007B .Error \u007D\u007D",
"trailing_diamond": "\ue0b4",
"type": "battery"
}
],
"type": "prompt"
},
{
"alignment": "right",
"segments": [
{
"background": "#3A456E",
"foreground": "#AEA4BF",
"leading_diamond": "\ue0b6",
"options": {
"style": "austin",
"threshold": 150
},
"style": "diamond",
"template": "\u007B\u007B .FormattedMs \u007D\u007D",
"trailing_diamond": "\ue0b4 ",
"type": "executiontime"
}
],
"type": "prompt"
},
{
"alignment": "left",
"newline": true,
"segments": [
{
"background": "#3A456E",
"foreground": "#3EC669",
"leading_diamond": "\ue0b6",
"properties": {
"style": "agnoster_full",
"cycle": [
"#3EC669,#3A456E",
"#43CCEA,#3A456E",
"#E4F34A,#3A456E",
"#9B6BDF,#3A456E"
],
"cycle_folder_separator": true
},
"style": "diamond",
"template": "\ue5ff \u007B\u007B .Path \u007D\u007D",
"trailing_diamond": "\ue0b4",
"type": "path"
},
{
"background": "#3A456E",
"foreground": "#ffbebc",
"leading_diamond": "\ue0b6",
"style": "diamond",
"template": "\ue602",
"trailing_diamond": "\ue0b4",
"type": "text"
}
],
"type": "prompt"
}
],
"final_space": true,
"version": 4
}
@@ -6,8 +6,8 @@
{
appId = "org.moonfin.linux";
bundle = pkgs.fetchurl {
url = "https://github.com/Moonfin-Client/Moonfin-Core/releases/download/2.1.0/Moonfin_Linux_v2.1.0.flatpak";
hash = "sha256-dJcI/bzKS/+SCkHarSBesei5CKCm93SjRJBp09Jn810=";
url = "https://github.com/Moonfin-Client/Moonfin-Core/releases/download/2.2.0/Moonfin_Linux_v2.2.0.flatpak";
hash = "sha256-66YaGJQEK3u+SAmtsXLbCH4ZF8gL/Jl1S5PRGDfZ5fE=";
};
}
];
-10
View File
@@ -47,15 +47,6 @@
LIBVA_DRM_DEVICE = "/dev/dri/renderD128";
};
chiasson.system.caching.attic = {
enable = true;
cacheName = "nixos-new";
endpoint = "http://192.168.2.238:8080/";
publicKey = "nixos-new:8NySIcT0HP7KvGQKgBRWoWESxxRA8BVYo8S85UNpNX0=";
tokenFile = config.sops.secrets."caching/attic/token".path;
push.enable = true;
userCli.enable = true;
};
chiasson.desktop = {
niri.enable = true;
@@ -69,7 +60,6 @@
"--flake"
".#14900k"
];
enableRbwLockToggle = true;
defaultSeedDir = ./_private/dms-defaults;
};
};
+13 -80
View File
@@ -1,93 +1,26 @@
# Shared Home Manager selection for all desktop hosts (14900k, t2mbp, ideapad, uConsole).
# Wisdom slices resolved here (NixOS has `self`); toggles select what actually installs.
{ self, inputs, ... }:
{ self, ... }:
{
flake.nixosModules.desktopHomeBase =
{ self, lib, pkgs, ... }:
{ config, lib, ... }:
{
chiasson.system.chromiumHevc.enable = lib.mkDefault true;
chiasson.system.caching.attic = lib.mkDefault {
enable = true;
cacheName = "nixos-new";
endpoint = "http://192.168.2.238:8080/";
publicKey = "nixos-new:8NySIcT0HP7KvGQKgBRWoWESxxRA8BVYo8S85UNpNX0=";
tokenFile = config.sops.secrets."caching/attic/token".path;
push.enable = true;
userCli.enable = true;
};
chiasson.users.extraModules.olivier =
self.lib.wisdomCatalogExtraModules self
++ [
(
{ lib, pkgs, ... }:
let
aarch64 = pkgs.stdenv.hostPlatform.isAarch64;
in
{
programs.git = {
enable = lib.mkDefault true;
settings.user = {
name = "OlivierChiasson";
email = "olivierchiasson@hotmail.fr";
};
};
#TODO[epic=Moderate] Check if git-crypt should belong in desktop-home-base.
home.packages = [ pkgs.git-crypt ];
chiasson.home = {
# Catalog of `chiasson.home.*` toggles — host `home.nix` sets `enable = true` to override `mkDefault false`.
shell = {
fish.enable = lib.mkDefault true;
ohMyPosh.enable = lib.mkDefault true;
yazi.enable = lib.mkDefault true;
};
terminals.kitty.enable = lib.mkDefault true;
filebrowsers.nemo.enable = lib.mkDefault true;
browsers = {
chrome.enable = lib.mkDefault false;
chromiumHevc = {
enable = lib.mkDefault true;
packages = lib.mkDefault (
if aarch64 then
[ "chromium" ]
else
[ "google-chrome" ]
);
} // lib.optionalAttrs (!aarch64) {
# Chromium + NVIDIA VA-API → frame pool errors in Jellyfin; gecko may fare better on NVIDIA.
vaapi.gpu = lib.mkDefault "intel";
};
edge.enable = lib.mkDefault false;
flow.enable = lib.mkDefault false;
orion.enable = lib.mkDefault false;
zen.enable = lib.mkDefault false;
};
editors = {
cursor.enable = lib.mkDefault true;
kate.enable = lib.mkDefault false;
obsidian.enable = lib.mkDefault true;
};
apps = {
discord.enable = lib.mkDefault false;
localsend.enable = lib.mkDefault true;
pokeclicker.enable = lib.mkDefault false;
spotify = {
enable = lib.mkDefault false;
openDiscoveryFirewall = lib.mkDefault false;
};
};
desktop = {
screenshot = {
enable = lib.mkDefault true;
swiftshareApiKeyFile = "/run/secrets/swiftshare/API_KEY"; #TODO[epic=sops] redo this by passing sops file output directly
};
# WhiteSur GTK/icons, Phinger cursor, DMS matugen for GTK (+ qt6ct for Qt apps).
theming.enable = lib.mkDefault true;
};
hardware.uconsoleGamepad.enable = lib.mkDefault false;
};
}
)
self.homeManagerModules.desktopHomeDefaults
];
};
}
+77
View File
@@ -0,0 +1,77 @@
# Shared HM defaults for desktop hosts (git identity, wisdom catalog toggles).
{ ... }: {
flake.homeManagerModules.desktopHomeDefaults =
{ lib, pkgs, ... }:
let
aarch64 = pkgs.stdenv.hostPlatform.isAarch64;
in
{
programs.git = {
enable = lib.mkDefault true;
settings.user = {
name = "OlivierChiasson";
email = "olivierchiasson@hotmail.fr";
};
};
#TODO[epic=Moderate] Check if git-crypt should belong in desktop-home-base.
home.packages = [ pkgs.git-crypt ];
chiasson.home = {
shell = {
fish.enable = lib.mkDefault true;
ohMyPosh.enable = lib.mkDefault true;
yazi.enable = lib.mkDefault true;
};
terminals.kitty.enable = lib.mkDefault true;
filebrowsers.nemo.enable = lib.mkDefault true;
browsers = {
chrome.enable = lib.mkDefault false;
chromiumHevc = {
enable = lib.mkDefault true;
packages = lib.mkDefault (
if aarch64 then
[ "chromium" ]
else
[ "google-chrome" ]
);
} // lib.optionalAttrs (!aarch64) {
vaapi.gpu = lib.mkDefault "intel";
};
edge.enable = lib.mkDefault false;
flow.enable = lib.mkDefault false;
orion.enable = lib.mkDefault false;
zen.enable = lib.mkDefault false;
};
editors = {
cursor.enable = lib.mkDefault true;
kate.enable = lib.mkDefault false;
obsidian.enable = lib.mkDefault true;
};
apps = {
discord.enable = lib.mkDefault false;
localsend.enable = lib.mkDefault true;
pokeclicker.enable = lib.mkDefault false;
spotify = {
enable = lib.mkDefault false;
openDiscoveryFirewall = lib.mkDefault false;
};
};
desktop = {
screenshot = {
enable = lib.mkDefault true;
swiftshareApiKeyFile = "/run/secrets/swiftshare/API_KEY"; #TODO[epic=sops] redo this by passing sops file output directly
};
theming.enable = lib.mkDefault true;
};
hardware.uconsoleGamepad.enable = lib.mkDefault false;
};
};
}
-13
View File
@@ -51,17 +51,6 @@
hardware.firmware = [ pkgs.chromeos-sc7180-unredistributable-firmware ];
hardware.enableRedistributableFirmware = true;
# ─────────────────────── Attic (substitution + push + CLI token) ───────
chiasson.system.caching.attic = {
enable = true;
cacheName = "nixos-new";
endpoint = "http://192.168.2.238:8080/";
publicKey = "nixos-new:8NySIcT0HP7KvGQKgBRWoWESxxRA8BVYo8S85UNpNX0=";
tokenFile = config.sops.secrets."caching/attic/token".path;
push.enable = true;
userCli.enable = true;
};
# ─────────────────────── System bits ───────────────────────
chiasson.system = {
audio.enable = true;
@@ -90,8 +79,6 @@
defaultSession = "niri";
shell = "dms";
shells.dms = {
enableWvkbdToggle = true;
enableRbwLockToggle = true;
# Cross-build on the 14900k via binfmt and push back over LAN — much faster than
# rebuilding aarch64 closure on the Snapdragon. Mirrors the old NixOS-New flow:
# ssh out to nixdesk, run nixos-rebuild --target-host pointing back at us.
-14
View File
@@ -54,17 +54,6 @@
# Dynamic function row on the Touch Bar (`tiny-dfr`; needs `DRM_APPLETBDRM` — see t2linux kernel opts).
hardware.apple.touchBar.enable = true;
# ─────────────────────── Attic (pull + push + CLI token) ────────────────────
chiasson.system.caching.attic = {
enable = true;
cacheName = "nixos-new";
endpoint = "http://192.168.2.238:8080/";
publicKey = "nixos-new:8NySIcT0HP7KvGQKgBRWoWESxxRA8BVYo8S85UNpNX0=";
tokenFile = config.sops.secrets."caching/attic/token".path;
push.enable = true;
userCli.enable = true;
};
# ─────────────────────── Display Server & Desktop ──────────────────────────
chiasson.desktop = {
niri = {
@@ -79,9 +68,6 @@
};
defaultSession = "niri";
shell = "dms";
shells.dms = {
enableRbwLockToggle = true;
};
};
chiasson.system = {
-13
View File
@@ -45,16 +45,6 @@
chiasson.system.librepods.enable = true;
chiasson.system.palera1n.enable = true;
# ─────────────────────── Attic (pull + push + CLI token) ────────────────────
chiasson.system.caching.attic = {
enable = true;
cacheName = "nixos-new";
endpoint = "http://192.168.2.238:8080/";
publicKey = "nixos-new:8NySIcT0HP7KvGQKgBRWoWESxxRA8BVYo8S85UNpNX0=";
tokenFile = config.sops.secrets."caching/attic/token".path;
push.enable = true;
userCli.enable = true;
};
# ─────────────────────── Display Server & Desktop ──────────────────────────
chiasson.desktop = {
@@ -66,9 +56,6 @@
};
defaultSession = "niri";
shell = "dms";
shells.dms = {
enableWvkbdToggle = true;
};
};
chiasson.system = {
audio.enable = true;
+30
View File
@@ -0,0 +1,30 @@
{ ... }: {
flake.lib.rbwSshSocket =
let
bashSnippet = ''
if [ -z "''${SSH_AUTH_SOCK:-}" ]; then
if [ -n "''${XDG_RUNTIME_DIR:-}" ]; then
export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/rbw/ssh-agent-socket"
else
export SSH_AUTH_SOCK="/run/user/$(id -u)/rbw/ssh-agent-socket"
fi
fi
'';
fishSnippet =
pkgs: ''
if test -z "$SSH_AUTH_SOCK"
if test -n "$XDG_RUNTIME_DIR"
set -gx SSH_AUTH_SOCK "$XDG_RUNTIME_DIR/rbw/ssh-agent-socket"
else
set -l _hm_uid (${pkgs.coreutils}/bin/id -u)
set -gx SSH_AUTH_SOCK "/run/user/$_hm_uid/rbw/ssh-agent-socket"
end
end
'';
in
{
inherit bashSnippet fishSnippet;
sessionVariable = "$XDG_RUNTIME_DIR/rbw/ssh-agent-socket";
activationPath = "/run/user/$(id -u)/rbw/ssh-agent-socket";
};
}
+2 -2
View File
@@ -56,14 +56,14 @@
}
{
home.packages = with pkgs; [ rbw pinentry-gtk2 ];
home.sessionVariables.SSH_AUTH_SOCK = "$XDG_RUNTIME_DIR/rbw/ssh-agent-socket";
home.sessionVariables.SSH_AUTH_SOCK = self.lib.rbwSshSocket.sessionVariable;
home.file = inventory.mkIdentityFiles selectedHosts;
programs.ssh.enable = lib.mkIf cfg.manageSshConfig false;
home.activation.rbwSshConfig = lib.mkIf cfg.manageSshConfig (lib.hm.dag.entryAfter [ "writeBoundary" ] ''
mkdir -p "$HOME/.ssh"
chmod 700 "$HOME/.ssh"
RBW_SSH_SOCK="/run/user/$(id -u)/rbw/ssh-agent-socket"
RBW_SSH_SOCK="${self.lib.rbwSshSocket.activationPath}"
cat > "$HOME/.ssh/config" <<'EOF'
${sshConfigTemplate}
EOF
-10
View File
@@ -1,15 +1,5 @@
# HM side of the flake; option tree is `chiasson.home.*` (docs/conventions.md).
{ self, inputs, ... }: {
imports = [
./apps/discord.nix
./apps/localsend.nix
./apps/pokeclicker
./apps/spotify.nix
./browsers/orion.nix
./desktop/screenshot.nix
./hardware/uconsole-gamepad.nix
];
# Root module: chiasson.home.enable + bash. Other `wisdom*` slices auto-wire via
# `lib.wisdomCatalogExtraModules`; hosts flip `chiasson.home.*.enable` rather than re-importing.
flake.homeManagerModules.wisdom =
+10 -156
View File
@@ -7,6 +7,8 @@
hyprlandHm = lib.attrByPath [ "wayland" "windowManager" "hyprland" ] { } config;
hyprlandHmEnabled = hyprlandHm.enable or false;
keyOk = cfg.swiftshareApiKeyFile != null && cfg.swiftshareApiKeyFile != "";
uploadScript = builtins.readFile ./scripts/swiftshare-upload.sh;
screenshotScript = builtins.readFile ./scripts/swiftshare-screenshot.sh;
in
{
options.chiasson.home.desktop.screenshot.enable = lib.mkEnableOption ''
@@ -33,6 +35,10 @@
(lib.mkIf (root.enable && cfg.enable && keyOk) (
let
apiKeyFile = cfg.swiftshareApiKeyFile;
swiftshareUpload = pkgs.writeShellScriptBin "swiftshare-upload" (
builtins.replaceStrings [ "@API_KEY_FILE@" ] [ apiKeyFile ] uploadScript
);
swiftshareScreenshot = pkgs.writeShellScriptBin "swiftshare-screenshot" screenshotScript;
in
lib.mkMerge [
{
@@ -42,162 +48,10 @@
swappy
wl-clipboard
libnotify
(writeShellScriptBin "swiftshare-upload" ''
#!${pkgs.bash}/bin/bash
set -euo pipefail
COPY_URL=0
if [ "$#" -ge 1 ] && [ "$1" = "--copy-url" ]; then
COPY_URL=1
shift
fi
APP_NAME=""
if [ "$#" -ge 2 ] && [ "$1" = "--app-name" ]; then
APP_NAME="$2"
shift 2
fi
API_KEY_FILE=${lib.escapeShellArg apiKeyFile}
if [ -r "$API_KEY_FILE" ]; then
SWIFTSHARE_API_KEY="$(tr -d '\n' < "$API_KEY_FILE")"
fi
if [ -z "''${SWIFTSHARE_API_KEY:-}" ]; then
${pkgs.libnotify}/bin/notify-send "SwiftShare upload" "SwiftShare API key missing (expected readable: $API_KEY_FILE)"
echo "Error: SwiftShare API key missing (expected readable: $API_KEY_FILE)" >&2
exit 1
fi
IMAGE_FILE=""
RESPONSE_FILE=""
cleanup() {
if [ -n "$RESPONSE_FILE" ] && [ -f "$RESPONSE_FILE" ]; then
rm -f "$RESPONSE_FILE"
fi
}
trap cleanup EXIT
if [ "$#" -ge 1 ] && [ "$1" != "-" ]; then
IMAGE_FILE="$1"
if [ "''${IMAGE_FILE#'/'}" = "''${IMAGE_FILE}" ]; then
IMAGE_FILE="$(${pkgs.coreutils}/bin/readlink -f "''${IMAGE_FILE}")"
fi
if [ ! -f "$IMAGE_FILE" ]; then
echo "Error: file not found: $IMAGE_FILE" >&2
exit 1
fi
else
APP_NAME="''${APP_NAME:-screenshot}"
APP_NAME="''${APP_NAME%% *}"
APP_NAME="''${APP_NAME//[^A-Za-z0-9]/}"
APP_NAME="''${APP_NAME,,}"
if [ -z "$APP_NAME" ]; then
APP_NAME="screenshot"
fi
IMAGE_FILE="$(${pkgs.coreutils}/bin/mktemp --suffix=.png "''${TMPDIR:-/tmp}/''${APP_NAME}_XXXXXX")"
cat > "$IMAGE_FILE"
fi
if [ ! -s "$IMAGE_FILE" ]; then
${pkgs.libnotify}/bin/notify-send "SwiftShare" "Empty capture (maybe canceled) not uploading"
echo "Empty image file, not uploading." >&2
exit 0
fi
RESPONSE_FILE="$(mktemp)"
set +e
HTTP_STATUS="$(${pkgs.curl}/bin/curl -sS -o "''${RESPONSE_FILE}" -w '%{http_code}' \
-X POST "https://swiftshare.cloud/api/upload/sharex" \
-F "upload=@''${IMAGE_FILE}" \
-F "apiKey=''${SWIFTSHARE_API_KEY}")"
CURL_EXIT=$?
set -e
RESPONSE="$(cat "''${RESPONSE_FILE}")"
if [ "''${CURL_EXIT}" -ne 0 ]; then
${pkgs.libnotify}/bin/notify-send "SwiftShare upload failed" "Network or HTTP error (curl exit ''${CURL_EXIT})"
echo "SwiftShare upload failed (curl exit ''${CURL_EXIT})." >&2
echo "Response body:" >&2
echo "''${RESPONSE}" >&2
exit 1
fi
if ! echo "''${HTTP_STATUS}" | grep -qE '^2[0-9][0-9]$'; then
ERROR_MSG="$(${pkgs.jq}/bin/jq -r '.error // empty' <<< "''${RESPONSE}")"
if [ -z "''${ERROR_MSG}" ] || [ "''${ERROR_MSG}" = "null" ]; then
ERROR_MSG="Failed to upload file"
fi
${pkgs.libnotify}/bin/notify-send "SwiftShare upload failed (''${HTTP_STATUS})" "''${ERROR_MSG}"
echo "SwiftShare upload failed (HTTP ''${HTTP_STATUS}): ''${ERROR_MSG}" >&2
exit 1
fi
URL="$(${pkgs.jq}/bin/jq -r '.url // empty' <<< "''${RESPONSE}")"
THUMBNAIL="$(${pkgs.jq}/bin/jq -r '.thumbnail // empty' <<< "''${RESPONSE}")"
if [ -z "$URL" ] || [ "$URL" = "null" ]; then
${pkgs.libnotify}/bin/notify-send "SwiftShare upload failed" "Could not parse URL from response"
echo "Upload failed. Raw response:" >&2
echo "''${RESPONSE}" >&2
exit 1
fi
echo "$URL"
if [ -n "$THUMBNAIL" ] && [ "$THUMBNAIL" != "null" ]; then
echo "$THUMBNAIL"
fi
if [ "$COPY_URL" = "1" ]; then
${pkgs.wl-clipboard}/bin/wl-copy <<< "$URL"
fi
if [ -n "$IMAGE_FILE" ] && [ -f "$IMAGE_FILE" ]; then
${pkgs.libnotify}/bin/notify-send \
-a "SwiftShare" \
-i "$IMAGE_FILE" \
-h string:image-path:"$IMAGE_FILE" \
"SwiftShare upload" "Uploaded image: $URL"
else
${pkgs.libnotify}/bin/notify-send "SwiftShare upload" "Uploaded image: $URL"
fi
'')
(writeShellScriptBin "swiftshare-screenshot" ''
#!${pkgs.bash}/bin/bash
set -euo pipefail
LOG_DIR="$HOME/.local/state/swiftshare"
mkdir -p "$LOG_DIR"
LOG_FILE="$LOG_DIR/screenshot.log"
APP_CLASS="$(${pkgs.hyprland}/bin/hyprctl activewindow -j 2>/dev/null | ${pkgs.jq}/bin/jq -r '.class // .initialClass // empty' 2>/dev/null || true)"
APP_CLASS="''${APP_CLASS%% *}"
APP_CLASS="''${APP_CLASS//[^A-Za-z0-9]/}"
APP_CLASS="''${APP_CLASS,,}"
if [ -z "$APP_CLASS" ]; then
APP_CLASS="screenshot"
fi
GEOM="$(${pkgs.slurp}/bin/slurp)"
SLURP_EXIT=$?
if [ "$SLURP_EXIT" -ne 0 ] || [ -z "$GEOM" ]; then
${pkgs.libnotify}/bin/notify-send "SwiftShare" "Capture canceled"
{
echo "==== $(date) ==== capture canceled (slurp exit $SLURP_EXIT, geom='$GEOM')"
} >>"$LOG_FILE" 2>&1
exit 0
fi
{
echo "==== $(date) ===="
echo "Geometry: $GEOM"
${pkgs.grim}/bin/grim -g "$GEOM" - | ${pkgs.swappy}/bin/swappy -f - -o - | swiftshare-upload --copy-url --app-name "$APP_CLASS"
} >>"$LOG_FILE" 2>&1
'')
curl
jq
swiftshareUpload
swiftshareScreenshot
];
}
(lib.mkIf hyprlandHmEnabled {
@@ -0,0 +1,31 @@
#!/usr/bin/env bash
set -euo pipefail
LOG_DIR="$HOME/.local/state/swiftshare"
mkdir -p "$LOG_DIR"
LOG_FILE="$LOG_DIR/screenshot.log"
APP_CLASS="$(hyprctl activewindow -j 2>/dev/null | jq -r '.class // .initialClass // empty' 2>/dev/null || true)"
APP_CLASS="${APP_CLASS%% *}"
APP_CLASS="${APP_CLASS//[^A-Za-z0-9]/}"
APP_CLASS="${APP_CLASS,,}"
if [ -z "$APP_CLASS" ]; then
APP_CLASS="screenshot"
fi
GEOM="$(slurp)"
SLURP_EXIT=$?
if [ "$SLURP_EXIT" -ne 0 ] || [ -z "$GEOM" ]; then
notify-send "SwiftShare" "Capture canceled"
{
echo "==== $(date) ==== capture canceled (slurp exit $SLURP_EXIT, geom='$GEOM')"
} >>"$LOG_FILE" 2>&1
exit 0
fi
{
echo "==== $(date) ===="
echo "Geometry: $GEOM"
grim -g "$GEOM" - | swappy -f - -o - | swiftshare-upload --copy-url --app-name "$APP_CLASS"
} >>"$LOG_FILE" 2>&1
@@ -0,0 +1,121 @@
#!/usr/bin/env bash
# API key path substituted at build time (@API_KEY_FILE@).
set -euo pipefail
COPY_URL=0
if [ "$#" -ge 1 ] && [ "$1" = "--copy-url" ]; then
COPY_URL=1
shift
fi
APP_NAME=""
if [ "$#" -ge 2 ] && [ "$1" = "--app-name" ]; then
APP_NAME="$2"
shift 2
fi
API_KEY_FILE=@API_KEY_FILE@
if [ -r "$API_KEY_FILE" ]; then
SWIFTSHARE_API_KEY="$(tr -d '\n' < "$API_KEY_FILE")"
fi
if [ -z "${SWIFTSHARE_API_KEY:-}" ]; then
notify-send "SwiftShare upload" "SwiftShare API key missing (expected readable: $API_KEY_FILE)"
echo "Error: SwiftShare API key missing (expected readable: $API_KEY_FILE)" >&2
exit 1
fi
IMAGE_FILE=""
RESPONSE_FILE=""
cleanup() {
if [ -n "$RESPONSE_FILE" ] && [ -f "$RESPONSE_FILE" ]; then
rm -f "$RESPONSE_FILE"
fi
}
trap cleanup EXIT
if [ "$#" -ge 1 ] && [ "$1" != "-" ]; then
IMAGE_FILE="$1"
if [ "${IMAGE_FILE#/}" = "${IMAGE_FILE}" ]; then
IMAGE_FILE="$(readlink -f "${IMAGE_FILE}")"
fi
if [ ! -f "$IMAGE_FILE" ]; then
echo "Error: file not found: $IMAGE_FILE" >&2
exit 1
fi
else
APP_NAME="${APP_NAME:-screenshot}"
APP_NAME="${APP_NAME%% *}"
APP_NAME="${APP_NAME//[^A-Za-z0-9]/}"
APP_NAME="${APP_NAME,,}"
if [ -z "$APP_NAME" ]; then
APP_NAME="screenshot"
fi
IMAGE_FILE="$(mktemp --suffix=.png "${TMPDIR:-/tmp}/${APP_NAME}_XXXXXX")"
cat > "$IMAGE_FILE"
fi
if [ ! -s "$IMAGE_FILE" ]; then
notify-send "SwiftShare" "Empty capture (maybe canceled) not uploading"
echo "Empty image file, not uploading." >&2
exit 0
fi
RESPONSE_FILE="$(mktemp)"
set +e
HTTP_STATUS="$(curl -sS -o "${RESPONSE_FILE}" -w '%{http_code}' \
-X POST "https://swiftshare.cloud/api/upload/sharex" \
-F "upload=${IMAGE_FILE}" \
-F "apiKey=${SWIFTSHARE_API_KEY}")"
CURL_EXIT=$?
set -e
RESPONSE="$(cat "${RESPONSE_FILE}")"
if [ "${CURL_EXIT}" -ne 0 ]; then
notify-send "SwiftShare upload failed" "Network or HTTP error (curl exit ${CURL_EXIT})"
echo "SwiftShare upload failed (curl exit ${CURL_EXIT})." >&2
echo "Response body:" >&2
echo "${RESPONSE}" >&2
exit 1
fi
if ! echo "${HTTP_STATUS}" | grep -qE '^2[0-9][0-9]$'; then
ERROR_MSG="$(jq -r '.error // empty' <<< "${RESPONSE}")"
if [ -z "${ERROR_MSG}" ] || [ "${ERROR_MSG}" = "null" ]; then
ERROR_MSG="Failed to upload file"
fi
notify-send "SwiftShare upload failed (${HTTP_STATUS})" "${ERROR_MSG}"
echo "SwiftShare upload failed (HTTP ${HTTP_STATUS}): ${ERROR_MSG}" >&2
exit 1
fi
URL="$(jq -r '.url // empty' <<< "${RESPONSE}")"
THUMBNAIL="$(jq -r '.thumbnail // empty' <<< "${RESPONSE}")"
if [ -z "$URL" ] || [ "$URL" = "null" ]; then
notify-send "SwiftShare upload failed" "Could not parse URL from response"
echo "Upload failed. Raw response:" >&2
echo "${RESPONSE}" >&2
exit 1
fi
echo "$URL"
if [ -n "$THUMBNAIL" ] && [ "$THUMBNAIL" != "null" ]; then
echo "$THUMBNAIL"
fi
if [ "$COPY_URL" = "1" ]; then
wl-copy <<< "$URL"
fi
if [ -n "$IMAGE_FILE" ] && [ -f "$IMAGE_FILE" ]; then
notify-send \
-a "SwiftShare" \
-i "$IMAGE_FILE" \
-h string:image-path:"$IMAGE_FILE" \
"SwiftShare upload" "Uploaded image: $URL"
else
notify-send "SwiftShare upload" "Uploaded image: $URL"
fi
+4 -19
View File
@@ -1,9 +1,10 @@
{ ... }: {
{ self, ... }: {
flake.homeManagerModules.wisdomShellBash =
{ config, lib, ... }:
let
root = config.chiasson.home;
cfg = config.chiasson.home.shell.bash;
rbw = self.lib.rbwSshSocket;
in
{
options.chiasson.home.shell.bash.enable = lib.mkEnableOption "Bash + rbw SSH socket env in profile/init." // {
@@ -13,24 +14,8 @@
config = lib.mkIf (root.enable && cfg.enable) {
programs.bash = {
enable = true;
profileExtra = ''
if [ -z "''${SSH_AUTH_SOCK:-}" ]; then
if [ -n "''${XDG_RUNTIME_DIR:-}" ]; then
export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/rbw/ssh-agent-socket"
else
export SSH_AUTH_SOCK="/run/user/$(id -u)/rbw/ssh-agent-socket"
fi
fi
'';
initExtra = ''
if [ -z "''${SSH_AUTH_SOCK:-}" ]; then
if [ -n "''${XDG_RUNTIME_DIR:-}" ]; then
export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/rbw/ssh-agent-socket"
else
export SSH_AUTH_SOCK="/run/user/$(id -u)/rbw/ssh-agent-socket"
fi
fi
'';
profileExtra = rbw.bashSnippet;
initExtra = rbw.bashSnippet;
};
};
};
+3 -10
View File
@@ -1,9 +1,10 @@
{ ... }: {
{ self, ... }: {
flake.homeManagerModules.wisdomShellFish =
{ config, lib, pkgs, ... }:
let
root = config.chiasson.home;
cfg = config.chiasson.home.shell.fish;
rbw = self.lib.rbwSshSocket;
in
{
options.chiasson.home.shell.fish = {
@@ -19,21 +20,13 @@
};
config = lib.mkIf (root.enable && cfg.enable) {
# `fishPlugins.grc` only installs the plugin; the wrapper invokes the `grc` binary.
home.packages = [ pkgs.grc ];
programs.fish = {
enable = true;
interactiveShellInit = ''
set fish_greeting ""
if test -z "$SSH_AUTH_SOCK"
if test -n "$XDG_RUNTIME_DIR"
set -gx SSH_AUTH_SOCK "$XDG_RUNTIME_DIR/rbw/ssh-agent-socket"
else
set -l _hm_uid (${pkgs.coreutils}/bin/id -u)
set -gx SSH_AUTH_SOCK "/run/user/$_hm_uid/rbw/ssh-agent-socket"
end
end
${rbw.fishSnippet pkgs}
'';
plugins = [
{