Rebase to flake parts #8
This commit is contained in:
@@ -0,0 +1,14 @@
|
|||||||
|
{ self, inputs, ... }: {
|
||||||
|
flake.nixosModules.desktop = {
|
||||||
|
imports = [
|
||||||
|
self.nixosModules.desktopShellDmsOptions
|
||||||
|
self.nixosModules.desktopHyprland
|
||||||
|
self.nixosModules.desktopNiri
|
||||||
|
self.nixosModules.desktopPlasma
|
||||||
|
self.nixosModules.desktopOptions
|
||||||
|
self.nixosModules.desktopGui
|
||||||
|
self.nixosModules.desktopWallpapers
|
||||||
|
self.nixosModules.desktopWaydroid
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
{ ... }: {
|
||||||
|
flake.nixosModules.desktopGui =
|
||||||
|
{ config, lib, pkgs, self, inputs, options, ... }:
|
||||||
|
let
|
||||||
|
pi5Greeter = self.lib.pi5NiriKdl;
|
||||||
|
cfg = config.chiasson.desktop;
|
||||||
|
guiEnabled = cfg.hyprland.enable || cfg.niri.enable || cfg.plasma.enable;
|
||||||
|
hmAvailable = lib.hasAttrByPath [ "home-manager" "sharedModules" ] options;
|
||||||
|
dm = cfg.displayManager;
|
||||||
|
variant = dm.variant or "sddm";
|
||||||
|
useGreeter = variant == "dankgreeter";
|
||||||
|
sddmTheme =
|
||||||
|
if dm.sddm.theme.package == null then
|
||||||
|
"breeze"
|
||||||
|
else
|
||||||
|
"${dm.sddm.theme.package}/share/sddm/themes/${dm.sddm.theme.id}";
|
||||||
|
effectiveDefaultSession =
|
||||||
|
if cfg.defaultSession != null then
|
||||||
|
cfg.defaultSession
|
||||||
|
else if cfg.hyprland.enable then
|
||||||
|
"hyprland"
|
||||||
|
else if cfg.niri.enable then
|
||||||
|
"niri"
|
||||||
|
else
|
||||||
|
"plasma";
|
||||||
|
greeterCompositor =
|
||||||
|
if effectiveDefaultSession == "niri" then
|
||||||
|
"niri"
|
||||||
|
else if effectiveDefaultSession == "hyprland" then
|
||||||
|
"hyprland"
|
||||||
|
else
|
||||||
|
"niri";
|
||||||
|
enabledUsers = config.chiasson.users.enabled or [ ];
|
||||||
|
greeterConfigHome =
|
||||||
|
if dm.greeter.configHome != null then
|
||||||
|
dm.greeter.configHome
|
||||||
|
else if enabledUsers != [ ] then
|
||||||
|
"/home/${builtins.head enabledUsers}"
|
||||||
|
else
|
||||||
|
null;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# Unconditional imports only — conditional imports that peek at `config` recurse during merge.
|
||||||
|
imports = [
|
||||||
|
inputs.dms.nixosModules.greeter
|
||||||
|
];
|
||||||
|
|
||||||
|
config = lib.mkMerge [
|
||||||
|
(lib.mkIf guiEnabled {
|
||||||
|
services.xserver.enable = true;
|
||||||
|
|
||||||
|
# Chromium/Electron (Edge, Vesktop, etc.) only add native Wayland + IME flags when this is
|
||||||
|
# set; nixpkgs wrappers gate on NIXOS_OZONE_WL and WAYLAND_DISPLAY. Helps PipeWire/desktop
|
||||||
|
# portal screen capture on wlroots-like sessions. Harmless on X11 (flags stay off).
|
||||||
|
environment.sessionVariables.NIXOS_OZONE_WL = lib.mkDefault "1";
|
||||||
|
|
||||||
|
xdg.portal.enable = true;
|
||||||
|
xdg.portal.extraPortals =
|
||||||
|
[ pkgs.xdg-desktop-portal-gtk ]
|
||||||
|
++ lib.optionals cfg.plasma.enable [ pkgs.kdePackages.xdg-desktop-portal-kde ];
|
||||||
|
})
|
||||||
|
(lib.mkIf (guiEnabled && !useGreeter) {
|
||||||
|
services.displayManager.sddm = {
|
||||||
|
enable = true;
|
||||||
|
wayland.enable = dm.sddm.wayland.enable;
|
||||||
|
theme = sddmTheme;
|
||||||
|
inherit (dm.sddm) enableHidpi settings;
|
||||||
|
extraPackages = lib.optionals (dm.sddm.theme.package != null) (
|
||||||
|
with pkgs.kdePackages; [
|
||||||
|
qtdeclarative
|
||||||
|
qtsvg
|
||||||
|
]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
services.displayManager.defaultSession = effectiveDefaultSession;
|
||||||
|
})
|
||||||
|
(lib.mkIf (guiEnabled && useGreeter) {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = greeterConfigHome != null;
|
||||||
|
message = "DankGreeter needs chiasson.desktop.displayManager.greeter.configHome or a non-empty chiasson.users.enabled list.";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
services.displayManager.defaultSession = effectiveDefaultSession;
|
||||||
|
|
||||||
|
programs.dank-material-shell.greeter = {
|
||||||
|
enable = true;
|
||||||
|
compositor = {
|
||||||
|
name = greeterCompositor;
|
||||||
|
}
|
||||||
|
// lib.optionalAttrs (cfg.niri.raspberryPi5DrmWorkaround && greeterCompositor == "niri") {
|
||||||
|
customConfig = lib.mkDefault pi5Greeter.dankGreeterCompositorConfig;
|
||||||
|
};
|
||||||
|
configHome = greeterConfigHome;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
(lib.mkIf (cfg.defaultPackages.enabled && guiEnabled) {
|
||||||
|
environment.systemPackages = cfg.defaultPackages.packages;
|
||||||
|
})
|
||||||
|
(lib.mkIf (cfg.extraPackages != [ ] && guiEnabled) {
|
||||||
|
environment.systemPackages = cfg.extraPackages;
|
||||||
|
})
|
||||||
|
(lib.mkIf (guiEnabled && cfg.keyring.enable) {
|
||||||
|
services.gnome.gnome-keyring.enable = true;
|
||||||
|
security.pam.services.login.enableGnomeKeyring = true;
|
||||||
|
services.xserver.updateDbusEnvironment = lib.mkDefault true;
|
||||||
|
# Electron apps (Element, Slack, etc.) use libsecret via keytar; without it they report
|
||||||
|
# "unsupported keyring" even if gnome-keyring is enabled.
|
||||||
|
environment.systemPackages = [ pkgs.libsecret ];
|
||||||
|
})
|
||||||
|
(lib.mkIf (guiEnabled && cfg.keyring.enable && !useGreeter) {
|
||||||
|
security.pam.services.sddm.enableGnomeKeyring = true;
|
||||||
|
})
|
||||||
|
(lib.mkIf (guiEnabled && cfg.keyring.enable && useGreeter) {
|
||||||
|
security.pam.services.greetd.enableGnomeKeyring = true;
|
||||||
|
})
|
||||||
|
(lib.mkIf (guiEnabled && cfg.keyring.enable && hmAvailable) {
|
||||||
|
"home-manager".sharedModules = [
|
||||||
|
({ lib, pkgs, ... }: {
|
||||||
|
services.gnome-keyring.enable = lib.mkDefault true;
|
||||||
|
home.packages = [ pkgs.gcr ];
|
||||||
|
})
|
||||||
|
];
|
||||||
|
})
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
{ self, ... }: {
|
||||||
|
flake.nixosModules.desktopHyprland =
|
||||||
|
{ config, options, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.chiasson.desktop;
|
||||||
|
hmAvailable = lib.hasAttrByPath [ "home-manager" "sharedModules" ] options;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.chiasson.desktop.hyprland = {
|
||||||
|
enable = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Hyprland session + HM wiring.";
|
||||||
|
};
|
||||||
|
settings = lib.mkOption {
|
||||||
|
type = lib.types.attrs;
|
||||||
|
default = { };
|
||||||
|
description = "Extra `wayland.windowManager.hyprland.settings` merged with defaults.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkMerge [
|
||||||
|
(lib.mkIf cfg.hyprland.enable {
|
||||||
|
programs.hyprland.enable = true;
|
||||||
|
})
|
||||||
|
(lib.mkIf (cfg.hyprland.enable && hmAvailable) {
|
||||||
|
"home-manager".sharedModules = [ self.homeManagerModules.desktopHyprland ];
|
||||||
|
})
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
flake.homeManagerModules.desktopHyprland = {
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
osConfig ? { },
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
hyprlandEnabled = osConfig.chiasson.desktop.hyprland.enable or false;
|
||||||
|
# nixpkgs hyprland-plugins pin is stale for current Hyprland — override to a known-good rev.
|
||||||
|
hyprbarsPatched =
|
||||||
|
let
|
||||||
|
hyprlandPluginsSrc = pkgs.fetchFromGitHub {
|
||||||
|
owner = "hyprwm";
|
||||||
|
repo = "hyprland-plugins";
|
||||||
|
rev = "b85a56b9531013c79f2f3846fd6ee2ff014b8960";
|
||||||
|
hash = "sha256-xwNa+1D8WPsDnJtUofDrtyDCZKZotbUymzV/R5s+M0I=";
|
||||||
|
};
|
||||||
|
in
|
||||||
|
pkgs.hyprlandPlugins.hyprbars.overrideAttrs (_: {
|
||||||
|
version = "unstable-2026-02-23";
|
||||||
|
src = "${hyprlandPluginsSrc}/hyprbars";
|
||||||
|
});
|
||||||
|
in
|
||||||
|
{
|
||||||
|
config = lib.mkIf hyprlandEnabled {
|
||||||
|
wayland.windowManager.hyprland = {
|
||||||
|
enable = true;
|
||||||
|
# null = use NixOS Hyprland/xdg-desktop-portal-hyprland (same versions as the session).
|
||||||
|
package = null;
|
||||||
|
portalPackage = null;
|
||||||
|
plugins = [ hyprbarsPatched ];
|
||||||
|
extraConfig = ''
|
||||||
|
source = ~/.config/hypr/colors.conf
|
||||||
|
'';
|
||||||
|
|
||||||
|
settings = lib.mkMerge [
|
||||||
|
{
|
||||||
|
monitor = [ ",preferred,auto,auto" ];
|
||||||
|
general = {
|
||||||
|
gaps_in = 8;
|
||||||
|
gaps_out = 4;
|
||||||
|
border_size = 2;
|
||||||
|
allow_tearing = false;
|
||||||
|
};
|
||||||
|
cursor.no_hardware_cursors = true;
|
||||||
|
env = [
|
||||||
|
"XCURSOR_THEME,phinger-cursors-dark"
|
||||||
|
"XCURSOR_SIZE,32"
|
||||||
|
];
|
||||||
|
input = {
|
||||||
|
kb_layout = "ca";
|
||||||
|
kb_variant = "";
|
||||||
|
numlock_by_default = true;
|
||||||
|
};
|
||||||
|
binds.scroll_event_delay = 50;
|
||||||
|
exec-once = [
|
||||||
|
"nm-applet --indicator &"
|
||||||
|
"sleep 1 && hyprctl reload"
|
||||||
|
];
|
||||||
|
|
||||||
|
# Default keybinds
|
||||||
|
bind =
|
||||||
|
[
|
||||||
|
"SUPER,T,exec,kitty -e fish"
|
||||||
|
"ControlSuper,T,exec,konsole"
|
||||||
|
"SUPER,D,exec,rofi -show drun"
|
||||||
|
"ControlSuper,D,exec,rofi -show window"
|
||||||
|
"SUPER,E,exec,dolphin"
|
||||||
|
"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
|
||||||
|
decoration = {
|
||||||
|
rounding = 10;
|
||||||
|
active_opacity = 0.95;
|
||||||
|
inactive_opacity = 0.85;
|
||||||
|
shadow = {
|
||||||
|
enabled = true;
|
||||||
|
range = 5;
|
||||||
|
render_power = 8;
|
||||||
|
};
|
||||||
|
blur = {
|
||||||
|
enabled = true;
|
||||||
|
new_optimizations = true;
|
||||||
|
xray = false;
|
||||||
|
size = 2;
|
||||||
|
passes = 4;
|
||||||
|
vibrancy = 10;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
misc = {
|
||||||
|
disable_hyprland_logo = true;
|
||||||
|
disable_splash_rendering = true;
|
||||||
|
};
|
||||||
|
plugin.hyprbars = {
|
||||||
|
enabled = true;
|
||||||
|
bar_height = 30;
|
||||||
|
bar_blur = true;
|
||||||
|
bar_padding = 10;
|
||||||
|
bar_button_padding = 7;
|
||||||
|
bar_precedence_over_border = true;
|
||||||
|
bar_part_of_window = true;
|
||||||
|
bar_title_enabled = true;
|
||||||
|
bar_text_size = 10;
|
||||||
|
bar_text_font = "Sans";
|
||||||
|
bar_text_align = "center";
|
||||||
|
bar_buttons_alignment = "left";
|
||||||
|
icon_on_hover = true;
|
||||||
|
"hyprbars-button" = [
|
||||||
|
"rgb(ed6a5f), 12, , hyprctl dispatch killactive, rgb(460804)"
|
||||||
|
"rgb(f6be50), 12, , hyprctl dispatch movetoworkspacesilent special:minimized, rgb(90591d)"
|
||||||
|
"rgb(61c555), 12, , hyprctl dispatch fullscreen 1, rgb(2a6218)"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
windowrule = [
|
||||||
|
"sync_fullscreen 0, match:class ^(?i)microsoft-edge|Spotify|org.kde.gwenview|zen-beta$"
|
||||||
|
"opacity 1.0 override 0.95 override, match:class ^(?i)microsoft-edge$"
|
||||||
|
"opacity 1.0 override 1.00 override, match:class ^(?i)com.stremio.stremio$"
|
||||||
|
"opacity 1.0 override 0.85 override, match:class ^(?i)zen-beta$"
|
||||||
|
"no_screen_share on, match:class ^(?i)(microsoft-edge|zen-beta)$, match:title ^(?i).*(scotiabank|paypal).*"
|
||||||
|
"no_screen_share on, match:class ^(?i)microsoft-edge$, match:initial_title ^(?i).*(?i)personal 2.*edge.*$"
|
||||||
|
"hyprbars:no_bar on, match:class ^(?i)(microsoft-edge|Cursor|Flow|looking-glass-client|localsend_app)$"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
(osConfig.chiasson.desktop.hyprland.settings or { })
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
{ self, inputs, ... }:
|
||||||
|
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;
|
||||||
|
|
||||||
|
window-rules = [
|
||||||
|
{
|
||||||
|
matches = [
|
||||||
|
{
|
||||||
|
app-id = "^$";
|
||||||
|
title = "^$";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
open-floating = true;
|
||||||
|
open-focused = false;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
#TODO[epic=Binds] Go over binds again
|
||||||
|
binds = {
|
||||||
|
"Mod+T"."spawn-sh" = "${pkgs.lib.getExe pkgs.kitty} -e fish"; #TODO[epic=Binds] This should only be set if having kitty
|
||||||
|
"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" = "dolphin"; #TODO[epic=Binds] This should only be set if having dolphin
|
||||||
|
"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+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"
|
||||||
|
];
|
||||||
|
|
||||||
|
Print.screenshot = _: { };
|
||||||
|
"Ctrl+Print"."screenshot-screen" = _: { };
|
||||||
|
"Alt+Print"."screenshot-window" = _: { };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
mergeNiriSettings =
|
||||||
|
pkgs: niriCfg:
|
||||||
|
let
|
||||||
|
lib = pkgs.lib;
|
||||||
|
pi5 = self.lib.pi5NiriKdl;
|
||||||
|
rpi5Extra = lib.optionalString (niriCfg.raspberryPi5DrmWorkaround or false) pi5.drmExtraConfig;
|
||||||
|
userExtra = niriCfg.extraSettings or { };
|
||||||
|
extraConfigMerged = rpi5Extra + (userExtra.extraConfig or "");
|
||||||
|
in
|
||||||
|
lib.recursiveUpdate (niriBaseSettings pkgs) (
|
||||||
|
userExtra
|
||||||
|
// lib.optionalAttrs (rpi5Extra != "" || (userExtra.extraConfig or "") != "") {
|
||||||
|
extraConfig = extraConfigMerged;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
flake.homeManagerModules.desktopNiri =
|
||||||
|
{ lib, pkgs, osConfig ? { }, ... }:
|
||||||
|
let
|
||||||
|
niriOs = osConfig.chiasson.desktop.niri or { };
|
||||||
|
niriEnabled = osConfig.chiasson.desktop.niri.enable or false;
|
||||||
|
mergedSettings = mergeNiriSettings pkgs niriOs;
|
||||||
|
niriConfigPkg = inputs.wrapper-modules.wrappers.niri.wrap {
|
||||||
|
inherit pkgs;
|
||||||
|
settings = mergedSettings;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
config = lib.mkIf niriEnabled {
|
||||||
|
xdg.configFile."niri/config.kdl".source = "${niriConfigPkg}/niri-config.kdl";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
flake.nixosModules.desktopNiri =
|
||||||
|
{ config, options, lib, pkgs, self, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.chiasson.desktop;
|
||||||
|
hmAvailable = lib.hasAttrByPath [ "home-manager" "sharedModules" ] options;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.chiasson.desktop.niri = {
|
||||||
|
enable = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Niri compositor session + NixOS packages.";
|
||||||
|
};
|
||||||
|
|
||||||
|
raspberryPi5DrmWorkaround = lib.mkEnableOption ''
|
||||||
|
Pi 5 + RP1 DSI: Niri DRM debug rules (renderD128, ignore card1/2) + matching DankGreeter niri config.
|
||||||
|
Opt-in only — HDMI-only / other Pi setups do not need this.
|
||||||
|
'';
|
||||||
|
|
||||||
|
extraSettings = lib.mkOption {
|
||||||
|
type = lib.types.attrs;
|
||||||
|
default = { };
|
||||||
|
description = ''
|
||||||
|
Merged into shared defaults → `config.kdl` via wrapper-modules. Put raw KDL in `extraConfig`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkMerge [
|
||||||
|
{
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = !cfg.niri.raspberryPi5DrmWorkaround || cfg.niri.enable;
|
||||||
|
message = "chiasson.desktop.niri.raspberryPi5DrmWorkaround requires chiasson.desktop.niri.enable.";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
(lib.mkIf cfg.niri.enable {
|
||||||
|
programs.niri.enable = true;
|
||||||
|
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) {
|
||||||
|
"home-manager".sharedModules = [ self.homeManagerModules.desktopNiri ];
|
||||||
|
})
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,218 @@
|
|||||||
|
{ ... }: {
|
||||||
|
flake.nixosModules.desktopOptions =
|
||||||
|
{ config, options, lib, pkgs, self, inputs, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.chiasson.desktop;
|
||||||
|
hmAvailable = lib.hasAttrByPath [ "home-manager" "sharedModules" ] options;
|
||||||
|
guiEnabled = cfg.hyprland.enable || cfg.niri.enable || cfg.plasma.enable;
|
||||||
|
dmsEnabled = cfg.shell == "dms";
|
||||||
|
sddmIni = pkgs.formats.ini { };
|
||||||
|
# Pixie SDDM theme — Qt6 main; upstream has a qt5 branch if you need it.
|
||||||
|
pixieSddm = pkgs.stdenvNoCC.mkDerivation {
|
||||||
|
pname = "pixie-sddm";
|
||||||
|
version = "0-unstable-2026-03-29";
|
||||||
|
src = pkgs.fetchFromGitHub {
|
||||||
|
owner = "xCaptaiN09";
|
||||||
|
repo = "pixie-sddm";
|
||||||
|
rev = "12a5f459ebd6d699be42c188c10976c8bb7076d7";
|
||||||
|
hash = "sha256-lmE/49ySuAZDh5xLochWqfSw9qWrIV+fYaK5T2Ckck8=";
|
||||||
|
};
|
||||||
|
dontConfigure = true;
|
||||||
|
dontBuild = true;
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p "$out/share/sddm/themes/pixie"
|
||||||
|
cp -r "$src"/. "$out/share/sddm/themes/pixie/"
|
||||||
|
'';
|
||||||
|
meta = {
|
||||||
|
description = "Pixel / Material 3 inspired SDDM theme (Qt6)";
|
||||||
|
homepage = "https://github.com/xCaptaiN09/pixie-sddm";
|
||||||
|
license = lib.licenses.mit;
|
||||||
|
platforms = lib.platforms.linux;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.chiasson.desktop = {
|
||||||
|
defaultSession = lib.mkOption {
|
||||||
|
type = lib.types.nullOr (lib.types.enum [
|
||||||
|
"hyprland"
|
||||||
|
"niri"
|
||||||
|
"plasma"
|
||||||
|
]);
|
||||||
|
default = null;
|
||||||
|
example = "niri";
|
||||||
|
description = ''
|
||||||
|
DM session preference; `null` picks from which compositor flags are enabled. Turn on only
|
||||||
|
the compositor you use so SDDM does not drag in extras.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
shell = lib.mkOption {
|
||||||
|
type = lib.types.nullOr (lib.types.enum [ "dms" ]);
|
||||||
|
default = null;
|
||||||
|
example = "dms";
|
||||||
|
description = "Desktop shell overlay (e.g. DMS). Extend the enum when you add another.";
|
||||||
|
};
|
||||||
|
|
||||||
|
displayManager = {
|
||||||
|
variant = lib.mkOption {
|
||||||
|
type = lib.types.nullOr (lib.types.enum [
|
||||||
|
"sddm"
|
||||||
|
"dankgreeter"
|
||||||
|
]);
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
SDDM vs DankGreeter (greetd + DMS — [docs](https://danklinux.com/docs/dankgreeter/nixos-flake)).
|
||||||
|
`null`: Plasma-only → SDDM; Hyprland/Niri + `desktop.shell = "dms"` → DankGreeter; else SDDM.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
greeter = {
|
||||||
|
configHome = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
example = "/home/olivier";
|
||||||
|
description = ''
|
||||||
|
Whose DMS files to mirror into the greeter cache. `null` → first entry in `chiasson.users.enabled`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
displayManager.sddm = {
|
||||||
|
wayland.enable = lib.mkEnableOption ''
|
||||||
|
SDDM greeter on Wayland (nicer on HiDPI; turn off if the greeter glitches on your GPU).
|
||||||
|
'' // {
|
||||||
|
default = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
enableHidpi = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Passed through to `services.displayManager.sddm.enableHidpi`.";
|
||||||
|
};
|
||||||
|
|
||||||
|
theme = {
|
||||||
|
package = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.package;
|
||||||
|
default = pixieSddm;
|
||||||
|
description = ''
|
||||||
|
Package providing `share/sddm/themes/<id>`. Default: bundled [Pixie](https://github.com/xCaptaiN09/pixie-sddm).
|
||||||
|
`null` → Breeze. Other nixpkgs examples: `catppuccin-sddm`, `sddm-sugar-dark`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
id = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "pixie";
|
||||||
|
description = ''
|
||||||
|
Subdir under `share/sddm/themes/` (default `pixie`). Match your theme package (e.g. catppuccin ids).
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
settings = lib.mkOption {
|
||||||
|
type = sddmIni.type;
|
||||||
|
default = { };
|
||||||
|
description = "Extra `services.displayManager.sddm.settings` (INI).";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
plasma.enable = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Plasma 6 session bits for this flake.";
|
||||||
|
};
|
||||||
|
|
||||||
|
defaultPackages = {
|
||||||
|
enabled = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Shared desktop utility packages (ntfs3g, cifs-utils, …).";
|
||||||
|
};
|
||||||
|
packages = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.package;
|
||||||
|
default = with pkgs; [
|
||||||
|
ntfs3g
|
||||||
|
cifs-utils
|
||||||
|
usbutils
|
||||||
|
xhost
|
||||||
|
];
|
||||||
|
description = "Packages merged when `defaultPackages.enabled` is true.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
extraPackages = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.package;
|
||||||
|
default = [ ];
|
||||||
|
description = "Extra packages on GUI hosts.";
|
||||||
|
};
|
||||||
|
|
||||||
|
homeManager = {
|
||||||
|
bundleWisdom = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Add `wisdom` (baseline + bash) to HM `sharedModules`. Other slices still go in per-user `extraModules`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
keyring = {
|
||||||
|
enable = lib.mkEnableOption ''
|
||||||
|
gnome-keyring + pam (login + sddm or greetd) + HM user service + `gcr` + `services.xserver.updateDbusEnvironment`.
|
||||||
|
niri/hyprland: `dbus-update-activation-environment` at compositor start so libsecret/Electron see `WAYLAND_DISPLAY`.
|
||||||
|
'' // {
|
||||||
|
default = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkMerge [
|
||||||
|
{
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = cfg.defaultSession != "hyprland" || cfg.hyprland.enable;
|
||||||
|
message = "chiasson.desktop.defaultSession = \"hyprland\" requires chiasson.desktop.hyprland.enable = true.";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = cfg.defaultSession != "niri" || cfg.niri.enable;
|
||||||
|
message = "chiasson.desktop.defaultSession = \"niri\" requires chiasson.desktop.niri.enable = true.";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = cfg.defaultSession != "plasma" || cfg.plasma.enable;
|
||||||
|
message = "chiasson.desktop.defaultSession = \"plasma\" requires chiasson.desktop.plasma.enable = true.";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion =
|
||||||
|
cfg.displayManager.variant != "dankgreeter" || cfg.hyprland.enable || cfg.niri.enable;
|
||||||
|
message = "chiasson.desktop.displayManager.variant = \"dankgreeter\" requires chiasson.desktop.hyprland or chiasson.desktop.niri.";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = cfg.displayManager.variant != "dankgreeter" || cfg.shell == "dms";
|
||||||
|
message = "DankGreeter expects chiasson.desktop.shell = \"dms\" for DMS/matugen sync; use SDDM or enable DMS.";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
(lib.mkIf guiEnabled {
|
||||||
|
chiasson.desktop.displayManager.variant = lib.mkDefault (
|
||||||
|
if cfg.plasma.enable && !(cfg.hyprland.enable || cfg.niri.enable) then
|
||||||
|
"sddm"
|
||||||
|
else if (cfg.hyprland.enable || cfg.niri.enable) && cfg.shell == "dms" then
|
||||||
|
"dankgreeter"
|
||||||
|
else
|
||||||
|
"sddm"
|
||||||
|
);
|
||||||
|
})
|
||||||
|
(lib.mkIf (guiEnabled && hmAvailable && cfg.homeManager.bundleWisdom) {
|
||||||
|
"home-manager".sharedModules = [ self.homeManagerModules.wisdom ];
|
||||||
|
})
|
||||||
|
(lib.mkIf (dmsEnabled && hmAvailable) {
|
||||||
|
"home-manager".sharedModules = [ self.homeManagerModules.desktopShellDms ];
|
||||||
|
})
|
||||||
|
(lib.mkIf (hmAvailable && (dmsEnabled || cfg.niri.enable)) {
|
||||||
|
"home-manager".extraSpecialArgs = { inherit inputs; };
|
||||||
|
})
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{ ... }: {
|
||||||
|
flake.nixosModules.desktopPlasma =
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.chiasson.desktop;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
config = lib.mkIf cfg.plasma.enable {
|
||||||
|
services.desktopManager.plasma6.enable = true;
|
||||||
|
environment.etc."xdg/menus/applications.menu".text =
|
||||||
|
builtins.readFile "${pkgs.kdePackages.plasma-workspace}/etc/xdg/menus/plasma-applications.menu";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
{ inputs, ... }: {
|
||||||
|
flake.nixosModules.desktopShellDmsOptions = { lib, ... }: {
|
||||||
|
options.chiasson.desktop.shells.dms = {
|
||||||
|
enableGpuTemp = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
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 {
|
||||||
|
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.";
|
||||||
|
};
|
||||||
|
extraRightBarWidgets = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.attrs;
|
||||||
|
default = [ ];
|
||||||
|
description = "Extra right-bar widgets (prepended).";
|
||||||
|
};
|
||||||
|
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;
|
||||||
|
example = [ "sudo" "nixos-rebuild" "switch" "--flake" ".#14900k" ];
|
||||||
|
description = "Command used by DMS nix-monitor widget for rebuild actions.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
flake.homeManagerModules.desktopShellDms = {
|
||||||
|
lib,
|
||||||
|
osConfig ? { },
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
cfg = lib.attrByPath [ "chiasson" "desktop" "shells" "dms" ] { } osConfig;
|
||||||
|
selectedShell = lib.attrByPath [ "chiasson" "desktop" "shell" ] null osConfig;
|
||||||
|
dmsEnabled = selectedShell == "dms";
|
||||||
|
hostName = lib.attrByPath [ "networking" "hostName" ] "nixos" osConfig;
|
||||||
|
rebuildCommand =
|
||||||
|
if (cfg.rebuildCommand or null) != null then
|
||||||
|
cfg.rebuildCommand
|
||||||
|
else
|
||||||
|
[ "sudo" "nixos-rebuild" "switch" "--flake" ".#${hostName}" ];
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./home-manager/default.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,80 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"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": "NixOS-V2",
|
||||||
|
"type": "widget",
|
||||||
|
"capabilities": ["dankbar-widget"],
|
||||||
|
"component": "./RbwLockToggle.qml",
|
||||||
|
"settings": "./RbwLockToggleSettings.qml",
|
||||||
|
"icon": "lock_open",
|
||||||
|
"permissions": ["settings_read"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"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": "NixOS-V2",
|
||||||
|
"type": "widget",
|
||||||
|
"capabilities": ["dankbar-widget"],
|
||||||
|
"component": "./WvkbdToggle.qml",
|
||||||
|
"settings": "./WvkbdToggleSettings.qml",
|
||||||
|
"icon": "keyboard",
|
||||||
|
"permissions": ["settings_read"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
/**
|
||||||
|
* Vesktop / Vencord theme — matugen + DMS wallpaper colors.
|
||||||
|
* Derived from pywal-vencord-style mapping (NixOS-New templates/colors-discord.css).
|
||||||
|
*/
|
||||||
|
|
||||||
|
* {
|
||||||
|
/* Surfaces */
|
||||||
|
--background-primary: {{colors.background.default.hex}};
|
||||||
|
--background-secondary: {{colors.surface.default.hex}};
|
||||||
|
--background-tertiary: {{colors.surface_dim.default.hex}};
|
||||||
|
|
||||||
|
--background-primary-alt: {{colors.background.default.hex}};
|
||||||
|
--background-secondary-alt: {{colors.surface.default.hex}};
|
||||||
|
--background-tertiary-alt: {{colors.surface_dim.default.hex}};
|
||||||
|
|
||||||
|
--channeltextarea-background: {{colors.surface.default.hex}};
|
||||||
|
--custom-channel-members-bg: {{colors.surface.default.hex}};
|
||||||
|
|
||||||
|
--profile-gradient-primary-color: {{colors.surface.default.hex}};
|
||||||
|
--profile-gradient-secondary-color: {{colors.surface_variant.default.hex}};
|
||||||
|
|
||||||
|
--__header-bar-background: {{colors.surface.default.hex}} !important;
|
||||||
|
|
||||||
|
--bg-base-tertiary: {{colors.background.default.hex}};
|
||||||
|
|
||||||
|
--card-primary-bg: color-mix(in srgb, {{colors.surface.default.hex}}, black 20%);
|
||||||
|
--input-background: color-mix(in srgb, {{colors.surface.default.hex}}, black 20%);
|
||||||
|
--autocomplete-bg: color-mix(in srgb, {{colors.surface.default.hex}}, black 20%);
|
||||||
|
--background-nested-floating: color-mix(in srgb, {{colors.surface.default.hex}}, black 20%);
|
||||||
|
--background-floating: color-mix(in srgb, {{colors.surface.default.hex}}, black 20%);
|
||||||
|
--scrollbar-auto-track: color-mix(in srgb, {{colors.surface.default.hex}}, black 20%);
|
||||||
|
--scrollbar-thin-track: color-mix(in srgb, {{colors.surface.default.hex}}, black 20%);
|
||||||
|
|
||||||
|
--border-subtle: color-mix(in srgb, {{colors.surface.default.hex}}, black 20%);
|
||||||
|
--background-base-lowest: color-mix(in srgb, {{colors.surface.default.hex}}, black 20%);
|
||||||
|
--background-surface-high: color-mix(in srgb, {{colors.surface.default.hex}}, black 20%);
|
||||||
|
|
||||||
|
--button-secondary-background: color-mix(in srgb, {{colors.surface.default.hex}}, black 30%);
|
||||||
|
--background-surface-higher: color-mix(in srgb, {{colors.surface.default.hex}}, black 30%);
|
||||||
|
--background-base-lower: color-mix(in srgb, {{colors.surface.default.hex}}, black 35%);
|
||||||
|
|
||||||
|
--background-message-hover: color-mix(in srgb, {{colors.surface.default.hex}}, black 40%);
|
||||||
|
--button-secondary-background-hover: color-mix(in srgb, {{colors.surface.default.hex}}, black 40%);
|
||||||
|
--background-base-low: color-mix(in srgb, {{colors.surface.default.hex}}, black 40%);
|
||||||
|
--background-surface-highest: color-mix(in srgb, {{colors.surface.default.hex}}, black 40%);
|
||||||
|
--chat-background-default: color-mix(in srgb, {{colors.surface.default.hex}}, black 45%);
|
||||||
|
|
||||||
|
--button-secondary-background-active: color-mix(in srgb, {{colors.surface.default.hex}}, black 60%);
|
||||||
|
|
||||||
|
--primary-630: {{colors.surface_variant.default.hex}};
|
||||||
|
|
||||||
|
/* Muted / secondary chrome */
|
||||||
|
--scrollbar-auto-thumb: {{colors.on_surface_variant.default.hex}};
|
||||||
|
--scrollbar-thin-thumb: {{colors.on_surface_variant.default.hex}};
|
||||||
|
--interactive-muted: {{colors.on_surface_variant.default.hex}};
|
||||||
|
--text-muted: {{colors.on_surface_variant.default.hex}};
|
||||||
|
--background-modifier-hover: color-mix(in srgb, {{colors.on_surface_variant.default.hex}}, black 40%);
|
||||||
|
--background-modifier-active: color-mix(in srgb, {{colors.on_surface_variant.default.hex}}, black 20%);
|
||||||
|
--background-modifier-accent: {{colors.secondary_container.default.hex}};
|
||||||
|
--background-accent: {{colors.secondary.default.hex}};
|
||||||
|
|
||||||
|
--input-border: {{colors.outline.default.hex}};
|
||||||
|
--border-normal: {{colors.outline.default.hex}};
|
||||||
|
--icon-secondary: {{colors.on_surface_variant.default.hex}};
|
||||||
|
--icon-tertiary: {{colors.on_surface_variant.default.hex}};
|
||||||
|
--channel-icon: {{colors.on_surface_variant.default.hex}};
|
||||||
|
--channels-default: {{colors.on_surface_variant.default.hex}};
|
||||||
|
--header-primary: {{colors.on_surface.default.hex}};
|
||||||
|
--__lottieIconColor: {{colors.on_surface_variant.default.hex}};
|
||||||
|
--interactive-normal: {{colors.on_surface_variant.default.hex}};
|
||||||
|
|
||||||
|
/* Selection / highlights */
|
||||||
|
--red-400: {{colors.error.default.hex}};
|
||||||
|
--background-modifier-selected: {{colors.secondary_container.default.hex}};
|
||||||
|
|
||||||
|
--notice-background-positive: color-mix(in srgb, {{colors.tertiary.default.hex}}, black 75%);
|
||||||
|
--notice-text-positive: {{colors.tertiary.default.hex}};
|
||||||
|
|
||||||
|
/* Danger */
|
||||||
|
--status-danger: {{colors.error.default.hex}};
|
||||||
|
--button-outline-danger-border: {{colors.error.default.hex}};
|
||||||
|
--button-outline-danger-text: {{colors.error.default.hex}};
|
||||||
|
--button-danger-background: {{colors.error.default.hex}};
|
||||||
|
|
||||||
|
--yellow-300: {{colors.error.default.hex}};
|
||||||
|
|
||||||
|
/* Brand / accents */
|
||||||
|
--brand-experiment: {{colors.primary.default.hex}};
|
||||||
|
--brand-experiment-360: {{colors.primary.default.hex}};
|
||||||
|
--brand-experiment-500: {{colors.primary.default.hex}};
|
||||||
|
--profile-gradient-button-color: {{colors.primary.default.hex}};
|
||||||
|
|
||||||
|
--green-360: {{colors.primary.default.hex}};
|
||||||
|
|
||||||
|
/* Foreground */
|
||||||
|
--text-normal: {{colors.on_surface.default.hex}};
|
||||||
|
--interactive-active: {{colors.on_surface.default.hex}};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Status indicators (legacy Discord hex fills) */
|
||||||
|
rect[fill="#d83a41"] {
|
||||||
|
fill: {{colors.error.default.hex}} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
rect[fill="#cc954c"] {
|
||||||
|
fill: {{colors.secondary.default.hex}} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
rect[fill="#40a258"] {
|
||||||
|
fill: {{colors.primary.default.hex}} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper_ef3116 {
|
||||||
|
background: {{colors.background.default.hex}} !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar_c38106 {
|
||||||
|
background: {{colors.background.default.hex}} !important;
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
{ inputs, ... }: {
|
||||||
|
flake.nixosModules.desktopWallpapers =
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.chiasson.desktop.wallpapers;
|
||||||
|
d = config.chiasson.desktop;
|
||||||
|
guiEnabled = d.hyprland.enable || d.niri.enable || d.plasma.enable;
|
||||||
|
|
||||||
|
wallpaperDir = pkgs.stdenvNoCC.mkDerivation {
|
||||||
|
pname = "nixos-v2-wallpapers";
|
||||||
|
version = "0.1";
|
||||||
|
src = builtins.path {
|
||||||
|
path = cfg.source;
|
||||||
|
name = "wallpapers-src";
|
||||||
|
};
|
||||||
|
dontConfigure = true;
|
||||||
|
dontBuild = true;
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p "$out/share/wallpapers"
|
||||||
|
find "$src" -mindepth 1 -maxdepth 1 ! -name .git -exec cp -a {} "$out/share/wallpapers/" \;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
installPath = "${wallpaperDir}/share/wallpapers";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.chiasson.desktop.wallpapers = {
|
||||||
|
enable = lib.mkEnableOption ''
|
||||||
|
Copy `source` into the store; sets `NIXOS_V2_WALLPAPERS` and `/etc/wallpapers`.
|
||||||
|
'' // {
|
||||||
|
default = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
source = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
default = inputs.wallpapers;
|
||||||
|
description = ''
|
||||||
|
Directory copied into the store. Default: `inputs.wallpapers`
|
||||||
|
(`git+https://git.chiasson.cloud/Olivier/wallpapers`, pinned in `flake.lock`).
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf (guiEnabled && cfg.enable) {
|
||||||
|
environment = {
|
||||||
|
variables.NIXOS_V2_WALLPAPERS = installPath;
|
||||||
|
sessionVariables.NIXOS_V2_WALLPAPERS = installPath;
|
||||||
|
etc."wallpapers".source = installPath;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
{ ... }: {
|
||||||
|
flake.nixosModules.desktopWaydroid =
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
# `virtualisation.waydroid.package` defaults to `waydroid-nftables` when `networking.nftables.enable`
|
||||||
|
# is true — required on recent kernels where legacy `ip_tables` / `waydroid-net.sh` (iptables) fails
|
||||||
|
# with `waydroid session start` (nixpkgs#459520).
|
||||||
|
let
|
||||||
|
cfg = config.chiasson.desktop.waydroid;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.chiasson.desktop.waydroid = {
|
||||||
|
enable = lib.mkEnableOption ''
|
||||||
|
Waydroid + synced base props / nav mode. Needs Wayland; desktop hosts only. This module also
|
||||||
|
sets `networking.nftables.enable` (default) so NixOS uses `waydroid-nftables` — needed on newer
|
||||||
|
kernels where `waydroid-net.sh` / iptables fails. For **Google Play** apps (e.g. Hot Wheels
|
||||||
|
Showcase), run `sudo waydroid init -s GAPPS -f` once after the first `nixos-rebuild` (or reset
|
||||||
|
the container if you already initialized without GAPPS); then sign in and install from the Play
|
||||||
|
Store. If `dnsmasq` reports port 53 in use, free that port (Waydroid needs it).
|
||||||
|
'';
|
||||||
|
|
||||||
|
width = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
default = 1920;
|
||||||
|
description = "Waydroid rendering width (`persist.waydroid.width`).";
|
||||||
|
};
|
||||||
|
|
||||||
|
height = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
default = 1080;
|
||||||
|
description = "Waydroid rendering height (`persist.waydroid.height`).";
|
||||||
|
};
|
||||||
|
|
||||||
|
multiWindows = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Enable Waydroid multi-window integration.";
|
||||||
|
};
|
||||||
|
|
||||||
|
navigationMode = lib.mkOption {
|
||||||
|
type = lib.types.enum [ "3button" "gestures" ];
|
||||||
|
default = "gestures";
|
||||||
|
description = "Maps to Waydroid `navigation_mode` secure prop (3button=0, gestures=2).";
|
||||||
|
};
|
||||||
|
|
||||||
|
extraBaseProperties = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf lib.types.str;
|
||||||
|
default = { };
|
||||||
|
description = ''
|
||||||
|
Extra `key = value` pairs written into `/var/lib/waydroid/waydroid_base.prop` on activation
|
||||||
|
(same mechanism as width/height). Use for upstream tweaks — e.g. NVIDIA hosts often need
|
||||||
|
`ro.hardware.gralloc=default` and `ro.hardware.egl=swiftshader`; Linux 5.18+ may need
|
||||||
|
`sys.use_memfd=true` ([NixOS Waydroid wiki](https://wiki.nixos.org/wiki/Waydroid)).
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
networking.nftables.enable = lib.mkDefault true;
|
||||||
|
|
||||||
|
virtualisation.waydroid.enable = true;
|
||||||
|
|
||||||
|
system.activationScripts.waydroidProps = {
|
||||||
|
text = ''
|
||||||
|
PROP_FILE="/var/lib/waydroid/waydroid_base.prop"
|
||||||
|
|
||||||
|
if [ ! -f "$PROP_FILE" ]; then
|
||||||
|
echo "waydroid: $PROP_FILE not found yet, skipping prop sync."
|
||||||
|
echo "waydroid: run 'sudo waydroid init' once, then rebuild."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
set_prop() {
|
||||||
|
key="$1"
|
||||||
|
value="$2"
|
||||||
|
|
||||||
|
if ${lib.getExe pkgs.gnugrep} -q "^''${key}=" "$PROP_FILE"; then
|
||||||
|
${pkgs.gnused}/bin/sed -i "s|^''${key}=.*|''${key}=''${value}|" "$PROP_FILE"
|
||||||
|
else
|
||||||
|
printf "%s=%s\n" "$key" "$value" >> "$PROP_FILE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
set_prop "persist.waydroid.multi_windows" "${if cfg.multiWindows then "true" else "false"}"
|
||||||
|
set_prop "persist.waydroid.width" "${toString cfg.width}"
|
||||||
|
set_prop "persist.waydroid.height" "${toString cfg.height}"
|
||||||
|
${lib.concatStringsSep "\n" (
|
||||||
|
lib.mapAttrsToList (k: v: ''
|
||||||
|
set_prop ${lib.escapeShellArg k} ${lib.escapeShellArg v}'') cfg.extraBaseProperties
|
||||||
|
)}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.waydroid-navigation-mode = {
|
||||||
|
description = "Set Waydroid Android navigation mode";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
after = [ "waydroid-container.service" ];
|
||||||
|
wants = [ "waydroid-container.service" ];
|
||||||
|
path = [ config.virtualisation.waydroid.package ];
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
script = ''
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
nav_mode="${if cfg.navigationMode == "gestures" then "2" else "0"}"
|
||||||
|
|
||||||
|
if ! systemctl -q is-active waydroid-container.service; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
for _ in $(seq 1 30); do
|
||||||
|
if waydroid shell settings put secure navigation_mode "$nav_mode" >/dev/null 2>&1; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "waydroid: warning: could not set navigation_mode=$nav_mode (container not ready?)" >&2
|
||||||
|
exit 0
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# Export large Jellyfin media trees to nix-server. Local path must already exist
|
||||||
|
# (e.g. /mnt/test/jellyfin/{movies,tv}). On nix-server this is mounted at /mnt/nixdesk-jellyfin.
|
||||||
|
#
|
||||||
|
# After deploy: ensure Jellyfin can read files over NFS — typical fix:
|
||||||
|
# chmod -R a+rX /mnt/test/jellyfin
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
# Avoid UID/GID mismatches across machines: map all NFS writes from nix-server to a single
|
||||||
|
# local system user/group on this server.
|
||||||
|
users.groups.nfsmedia = { gid = 990; };
|
||||||
|
users.users.nfsmedia = {
|
||||||
|
isSystemUser = true;
|
||||||
|
uid = 990;
|
||||||
|
group = "nfsmedia";
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.tmpfiles.settings."14900k-jellyfin-media-dirs" = {
|
||||||
|
"/mnt/test/jellyfin"."d" = { mode = "2775"; user = "nfsmedia"; group = "nfsmedia"; };
|
||||||
|
"/mnt/test/jellyfin/movies"."d" = { mode = "2775"; user = "nfsmedia"; group = "nfsmedia"; };
|
||||||
|
"/mnt/test/jellyfin/tv"."d" = { mode = "2775"; user = "nfsmedia"; group = "nfsmedia"; };
|
||||||
|
};
|
||||||
|
|
||||||
|
# Fixed ports so the firewall can allow NFS v3 helpers (see networking.firewall below).
|
||||||
|
services.nfs.server = {
|
||||||
|
enable = true;
|
||||||
|
mountdPort = 4000;
|
||||||
|
lockdPort = 4001;
|
||||||
|
statdPort = 4002;
|
||||||
|
exports = ''
|
||||||
|
/mnt/test/jellyfin 192.168.2.238(rw,sync,no_subtree_check,crossmnt,root_squash,all_squash,anonuid=990,anongid=990)
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = [
|
||||||
|
111 # portmapper
|
||||||
|
2049
|
||||||
|
4000
|
||||||
|
4001
|
||||||
|
4002
|
||||||
|
];
|
||||||
|
networking.firewall.allowedUDPPorts = [
|
||||||
|
111
|
||||||
|
2049
|
||||||
|
4000
|
||||||
|
4001
|
||||||
|
4002
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
./_private/peripherals.nix
|
./_private/peripherals.nix
|
||||||
# ./_private/printing-epson.nix
|
# ./_private/printing-epson.nix
|
||||||
./_private/displays.nix
|
./_private/displays.nix
|
||||||
|
./_private/jellyfin-nfs-export.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
sops = {
|
sops = {
|
||||||
@@ -37,7 +38,24 @@
|
|||||||
group = "users";
|
group = "users";
|
||||||
mode = "0400";
|
mode = "0400";
|
||||||
};
|
};
|
||||||
|
services.cloudflare-warp.enable = true;
|
||||||
|
|
||||||
|
# Intel iGPU video acceleration (VA-API / QSV via oneVPL).
|
||||||
|
# This fixes common NixOS issues like `vaInitialize failed` and missing QSV encoders in apps.
|
||||||
|
hardware.graphics = {
|
||||||
|
enable = true;
|
||||||
|
extraPackages = with pkgs; [
|
||||||
|
intel-media-driver # iHD (Gen8+)
|
||||||
|
vpl-gpu-rt # oneVPL runtime (QSV)
|
||||||
|
libvdpau-va-gl
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
environment.sessionVariables = {
|
||||||
|
LIBVA_DRIVER_NAME = "iHD";
|
||||||
|
# Force VA-API to use the Intel iGPU render node (otherwise libva may pick NVIDIA and iHD fails).
|
||||||
|
LIBVA_DRM_DEVICE = "/dev/dri/renderD128";
|
||||||
|
};
|
||||||
|
|
||||||
chiasson.system.caching.attic = {
|
chiasson.system.caching.attic = {
|
||||||
enable = true;
|
enable = true;
|
||||||
@@ -86,7 +104,20 @@
|
|||||||
palera1n.enable = true;
|
palera1n.enable = true;
|
||||||
uconsoleKernelBuilder.enable = true;
|
uconsoleKernelBuilder.enable = true;
|
||||||
|
|
||||||
extraPackages = [ pkgs.sops pkgs.nodejs_22 ];
|
extraPackages = with pkgs; [
|
||||||
|
sops
|
||||||
|
nodejs_22
|
||||||
|
ffmpeg
|
||||||
|
bento4
|
||||||
|
yt-dlp
|
||||||
|
|
||||||
|
# Native install (avoid flatpak sandbox issues for QSV/VAAPI).
|
||||||
|
handbrake
|
||||||
|
|
||||||
|
# Diagnostics
|
||||||
|
libva-utils # vainfo
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
networking = {
|
networking = {
|
||||||
hostName = "nixdesk";
|
hostName = "nixdesk";
|
||||||
@@ -102,6 +133,7 @@
|
|||||||
self.homeManagerModules.wisdomTerminalsKitty
|
self.homeManagerModules.wisdomTerminalsKitty
|
||||||
self.homeManagerModules.wisdomBrowsersEdge
|
self.homeManagerModules.wisdomBrowsersEdge
|
||||||
self.homeManagerModules.wisdomBrowsersFlow
|
self.homeManagerModules.wisdomBrowsersFlow
|
||||||
|
self.homeManagerModules.wisdomBrowsersOrion
|
||||||
self.homeManagerModules.wisdomEditorsCursor
|
self.homeManagerModules.wisdomEditorsCursor
|
||||||
self.homeManagerModules.wisdomEditorsObsidian
|
self.homeManagerModules.wisdomEditorsObsidian
|
||||||
self.homeManagerModules.wisdomShellYazi
|
self.homeManagerModules.wisdomShellYazi
|
||||||
@@ -135,6 +167,7 @@
|
|||||||
|
|
||||||
browsers.edge.enable = true;
|
browsers.edge.enable = true;
|
||||||
browsers.flow.enable = false;
|
browsers.flow.enable = false;
|
||||||
|
browsers.orion.enable = true;
|
||||||
|
|
||||||
editors.cursor.enable = true;
|
editors.cursor.enable = true;
|
||||||
editors.obsidian.enable = true;
|
editors.obsidian.enable = true;
|
||||||
|
|||||||
@@ -0,0 +1,213 @@
|
|||||||
|
{ ... }: {
|
||||||
|
flake.nixosModules.systemServiceImmich =
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.chiasson.system.services.immich;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.chiasson.system.services.immich = with lib; {
|
||||||
|
enable = mkEnableOption "Immich stack (server + machine-learning + redis + postgres).";
|
||||||
|
|
||||||
|
version = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "release";
|
||||||
|
description = "Immich image tag to deploy.";
|
||||||
|
};
|
||||||
|
|
||||||
|
host = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "0.0.0.0";
|
||||||
|
description = "Host interface to bind Immich server.";
|
||||||
|
};
|
||||||
|
|
||||||
|
port = mkOption {
|
||||||
|
type = types.int;
|
||||||
|
default = 2283;
|
||||||
|
description = "Host TCP port mapped to Immich server port 2283.";
|
||||||
|
};
|
||||||
|
|
||||||
|
openFirewall = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Open firewall for Immich server port.";
|
||||||
|
};
|
||||||
|
|
||||||
|
timezone = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "UTC";
|
||||||
|
description = "Timezone passed to Immich services via TZ.";
|
||||||
|
};
|
||||||
|
|
||||||
|
uploadLocation = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "/var/lib/immich/library";
|
||||||
|
description = "Host path used for Immich uploads/library.";
|
||||||
|
};
|
||||||
|
|
||||||
|
environmentFiles = mkOption {
|
||||||
|
type = types.listOf types.path;
|
||||||
|
default = [ ];
|
||||||
|
description = ''
|
||||||
|
Docker `--env-file` paths for **immich-database** and **immich-server** (after inline `-e`).
|
||||||
|
Use a sops template with `POSTGRES_PASSWORD` and `DB_PASSWORD` (same value) when using
|
||||||
|
`sops.placeholder` for the DB secret; then set `postgres.password` to empty.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
postgres = {
|
||||||
|
image = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23";
|
||||||
|
description = "Immich recommended Postgres image with vector extensions.";
|
||||||
|
};
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "postgres";
|
||||||
|
description = "Immich Postgres username.";
|
||||||
|
};
|
||||||
|
|
||||||
|
password = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "";
|
||||||
|
description = ''
|
||||||
|
Immich Postgres password (inline `POSTGRES_PASSWORD` / `DB_PASSWORD`).
|
||||||
|
Leave empty when using `environmentFiles` with those keys from a sops template.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
database = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "immich";
|
||||||
|
description = "Immich Postgres database name.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion =
|
||||||
|
cfg.postgres.password != "" || cfg.environmentFiles != [ ];
|
||||||
|
message =
|
||||||
|
"chiasson.system.services.immich: set postgres.password or environmentFiles (e.g. sops-rendered POSTGRES_PASSWORD + DB_PASSWORD).";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
virtualisation = {
|
||||||
|
docker.enable = true;
|
||||||
|
oci-containers = {
|
||||||
|
backend = "docker";
|
||||||
|
containers = {
|
||||||
|
immich-redis = {
|
||||||
|
image = "docker.io/valkey/valkey:9@sha256:3b55fbaa0cd93cf0d9d961f405e4dfcc70efe325e2d84da207a0a8e6d8fde4f9";
|
||||||
|
extraOptions = [ "--network=immich-network" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
immich-database = {
|
||||||
|
image = cfg.postgres.image;
|
||||||
|
environment =
|
||||||
|
{
|
||||||
|
POSTGRES_USER = cfg.postgres.user;
|
||||||
|
POSTGRES_DB = cfg.postgres.database;
|
||||||
|
POSTGRES_INITDB_ARGS = "--data-checksums";
|
||||||
|
}
|
||||||
|
// lib.optionalAttrs (cfg.postgres.password != "") {
|
||||||
|
POSTGRES_PASSWORD = cfg.postgres.password;
|
||||||
|
};
|
||||||
|
environmentFiles = cfg.environmentFiles;
|
||||||
|
volumes = [ "immich-postgres:/var/lib/postgresql/data" ];
|
||||||
|
extraOptions = [
|
||||||
|
"--network=immich-network"
|
||||||
|
"--shm-size=128mb"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
immich-machine-learning = {
|
||||||
|
image = "ghcr.io/immich-app/immich-machine-learning:${cfg.version}";
|
||||||
|
environment = {
|
||||||
|
TZ = cfg.timezone;
|
||||||
|
};
|
||||||
|
volumes = [ "immich-model-cache:/cache" ];
|
||||||
|
extraOptions = [ "--network=immich-network" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
immich-server = {
|
||||||
|
image = "ghcr.io/immich-app/immich-server:${cfg.version}";
|
||||||
|
dependsOn = [
|
||||||
|
"immich-redis"
|
||||||
|
"immich-database"
|
||||||
|
"immich-machine-learning"
|
||||||
|
];
|
||||||
|
ports = [ "${cfg.host}:${toString cfg.port}:2283" ];
|
||||||
|
environment =
|
||||||
|
{
|
||||||
|
TZ = cfg.timezone;
|
||||||
|
DB_HOSTNAME = "immich-database";
|
||||||
|
DB_USERNAME = cfg.postgres.user;
|
||||||
|
DB_DATABASE_NAME = cfg.postgres.database;
|
||||||
|
REDIS_HOSTNAME = "immich-redis";
|
||||||
|
IMMICH_MACHINE_LEARNING_URL = "http://immich-machine-learning:3003";
|
||||||
|
UPLOAD_LOCATION = "/data";
|
||||||
|
}
|
||||||
|
// lib.optionalAttrs (cfg.postgres.password != "") { DB_PASSWORD = cfg.postgres.password; };
|
||||||
|
environmentFiles = cfg.environmentFiles;
|
||||||
|
volumes = [
|
||||||
|
"${cfg.uploadLocation}:/data"
|
||||||
|
"/etc/localtime:/etc/localtime:ro"
|
||||||
|
];
|
||||||
|
extraOptions = [
|
||||||
|
"--network=immich-network"
|
||||||
|
"--pull=always"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.immich-network = {
|
||||||
|
description = "Create Docker network for Immich";
|
||||||
|
after = [ "docker.service" ];
|
||||||
|
requires = [ "docker.service" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
script = ''
|
||||||
|
${pkgs.docker}/bin/docker network inspect immich-network >/dev/null 2>&1 || \
|
||||||
|
${pkgs.docker}/bin/docker network create immich-network
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."docker-immich-redis" = {
|
||||||
|
after = [ "immich-network.service" ];
|
||||||
|
requires = [ "immich-network.service" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."docker-immich-database" = {
|
||||||
|
after = [ "immich-network.service" ];
|
||||||
|
requires = [ "immich-network.service" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."docker-immich-machine-learning" = {
|
||||||
|
after = [ "immich-network.service" ];
|
||||||
|
requires = [ "immich-network.service" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services."docker-immich-server" = {
|
||||||
|
after = [
|
||||||
|
"immich-network.service"
|
||||||
|
"docker-immich-redis.service"
|
||||||
|
"docker-immich-database.service"
|
||||||
|
"docker-immich-machine-learning.service"
|
||||||
|
];
|
||||||
|
requires = [
|
||||||
|
"immich-network.service"
|
||||||
|
"docker-immich-redis.service"
|
||||||
|
"docker-immich-database.service"
|
||||||
|
"docker-immich-machine-learning.service"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = lib.optionals cfg.openFirewall [ cfg.port ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{ ... }: {
|
||||||
|
flake.homeManagerModules.wisdomBrowsersChrome =
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
root = config.chiasson.home;
|
||||||
|
cfg = config.chiasson.home.browsers.chrome;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.chiasson.home.browsers.chrome.enable = lib.mkEnableOption ''
|
||||||
|
Chrome (unfree, needs `allowUnfree`); skipped if nixpkgs has no build for this platform.
|
||||||
|
'';
|
||||||
|
|
||||||
|
config = lib.mkIf (root.enable && cfg.enable) {
|
||||||
|
home.packages = lib.optional (
|
||||||
|
lib.meta.availableOn pkgs.stdenv.hostPlatform pkgs.google-chrome
|
||||||
|
) pkgs.google-chrome;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{ ... }: {
|
||||||
|
flake.homeManagerModules.wisdomBrowsersEdge =
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
root = config.chiasson.home;
|
||||||
|
cfg = config.chiasson.home.browsers.edge;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.chiasson.home.browsers.edge.enable = lib.mkEnableOption "Edge (unfree); skipped if unavailable on this platform.";
|
||||||
|
|
||||||
|
config = lib.mkIf (root.enable && cfg.enable) {
|
||||||
|
home.packages = lib.optional (
|
||||||
|
lib.meta.availableOn pkgs.stdenv.hostPlatform pkgs.microsoft-edge
|
||||||
|
) pkgs.microsoft-edge;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
{ ... }: {
|
||||||
|
flake.homeManagerModules.wisdomBrowsersFlow =
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
root = config.chiasson.home;
|
||||||
|
cfg = config.chiasson.home.browsers.flow;
|
||||||
|
|
||||||
|
flow-browser =
|
||||||
|
let
|
||||||
|
pname = "flow-browser";
|
||||||
|
version = "0.11.0";
|
||||||
|
|
||||||
|
suffix = if pkgs.stdenv.hostPlatform.isAarch64 then "arm64" else "x86_64";
|
||||||
|
|
||||||
|
hash =
|
||||||
|
if pkgs.stdenv.hostPlatform.isAarch64 then
|
||||||
|
"sha256-rTRKbNyVRJAw7ZyDR6kx+XJ4rWmErZqA0b6LP9t5eOA="
|
||||||
|
else
|
||||||
|
"sha256-/Tca4uUBfgbZQEeXdYkCz6CWxqvCl40CQpACFry1k9s=";
|
||||||
|
|
||||||
|
src = pkgs.fetchurl {
|
||||||
|
url = "https://github.com/MultiboxLabs/flow-browser/releases/download/v${version}/flow-browser-${version}-${suffix}.AppImage";
|
||||||
|
inherit hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
appimageContents = pkgs.appimageTools.extractType2 { inherit pname version src; };
|
||||||
|
in
|
||||||
|
pkgs.appimageTools.wrapType2 {
|
||||||
|
inherit pname version src;
|
||||||
|
|
||||||
|
nativeBuildInputs = [ pkgs.makeWrapper ];
|
||||||
|
|
||||||
|
extraInstallCommands = ''
|
||||||
|
wrapProgram $out/bin/${pname} \
|
||||||
|
--add-flags "\''${NIXOS_OZONE_WL:+\''${WAYLAND_DISPLAY:+--ozone-platform-hint=auto --enable-features=WaylandWindowDecorations --enable-wayland-ime=true}}"
|
||||||
|
|
||||||
|
install -m 444 -D ${appimageContents}/flow-browser.desktop -t $out/share/applications
|
||||||
|
substituteInPlace $out/share/applications/flow-browser.desktop \
|
||||||
|
--replace-fail 'Exec=AppRun --ozone-platform-hint=auto' 'Exec=${pname} --ozone-platform-hint=auto'
|
||||||
|
|
||||||
|
install -m 444 -D ${appimageContents}/usr/share/icons/hicolor/512x512/apps/flow-browser.png \
|
||||||
|
$out/share/icons/hicolor/512x512/apps/flow-browser.png
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
description = "Chromium-based browser (upstream AppImage)";
|
||||||
|
homepage = "https://github.com/MultiboxLabs/flow-browser";
|
||||||
|
license = lib.licenses.gpl3Plus;
|
||||||
|
sourceProvenance = with lib.sourceTypes; [ binaryNativeCode ];
|
||||||
|
platforms = [ "x86_64-linux" "aarch64-linux" ];
|
||||||
|
mainProgram = pname;
|
||||||
|
maintainers = [ ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.chiasson.home.browsers.flow.enable = lib.mkEnableOption ''
|
||||||
|
[Flow](https://github.com/MultiboxLabs/flow-browser) — upstream AppImage wrapped for NixOS.
|
||||||
|
'';
|
||||||
|
|
||||||
|
config = lib.mkIf (root.enable && cfg.enable) {
|
||||||
|
home.packages = lib.optional (
|
||||||
|
lib.meta.availableOn pkgs.stdenv.hostPlatform flow-browser
|
||||||
|
) flow-browser;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
{ ... }: {
|
||||||
|
flake.homeManagerModules.wisdomBrowsersOrion =
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
root = config.chiasson.home;
|
||||||
|
cfg = config.chiasson.home.browsers.orion;
|
||||||
|
|
||||||
|
pname = "oriongtk";
|
||||||
|
version = "0.3.0";
|
||||||
|
flatpakBundle = pkgs.fetchurl {
|
||||||
|
url = "https://cdn.kagi.com/downloads/oriongtk.${version}.flatpak";
|
||||||
|
hash = "sha256-0NOWPS2Yv5NpnTxqsiMvshHFyTyDotPi964/2og/bCw=";
|
||||||
|
};
|
||||||
|
|
||||||
|
appId = "com.kagi.OrionGtk";
|
||||||
|
|
||||||
|
oriongtk = pkgs.runCommand "oriongtk-${version}"
|
||||||
|
{
|
||||||
|
nativeBuildInputs = [ pkgs.makeWrapper ];
|
||||||
|
passthru = {
|
||||||
|
inherit pname version;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
''
|
||||||
|
mkdir -p "$out/bin"
|
||||||
|
makeWrapper ${pkgs.flatpak}/bin/flatpak "$out/bin/${pname}" \
|
||||||
|
--add-flags "run" \
|
||||||
|
--add-flags ${lib.escapeShellArg appId}
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.chiasson.home.browsers.orion.enable = lib.mkEnableOption ''
|
||||||
|
[Orion](https://orionbrowser.com/) (Kagi) — installs the upstream Flatpak bundle and provides `oriongtk`.
|
||||||
|
'';
|
||||||
|
|
||||||
|
config = lib.mkIf (root.enable && cfg.enable) {
|
||||||
|
home.packages = [ oriongtk ];
|
||||||
|
|
||||||
|
home.activation.oriongtkFlatpak = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
if [ ! -x "${pkgs.flatpak}/bin/flatpak" ]; then
|
||||||
|
echo "oriongtk: flatpak missing; enable Flatpak (e.g. services.flatpak on NixOS)." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "oriongtk: ensuring ${appId} from ${flatpakBundle} (user)"
|
||||||
|
# `--or-update` still exits non-zero when the same ref is already installed from this bundle;
|
||||||
|
# `--reinstall` is idempotent for HM switches (uninstall first only if present).
|
||||||
|
${pkgs.flatpak}/bin/flatpak --user install \
|
||||||
|
--assumeyes \
|
||||||
|
--noninteractive \
|
||||||
|
--reinstall \
|
||||||
|
--bundle \
|
||||||
|
${lib.escapeShellArg (builtins.toString flatpakBundle)}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
{ inputs, ... }: {
|
||||||
|
flake.homeManagerModules.wisdomBrowsersZen =
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
root = config.chiasson.home;
|
||||||
|
cfg = config.chiasson.home.browsers.zen;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ inputs.zen-browser.homeModules.beta ];
|
||||||
|
|
||||||
|
options.chiasson.home.browsers.zen.enable = lib.mkEnableOption "Zen Browser + locked-down policies / extensions.";
|
||||||
|
|
||||||
|
config = lib.mkIf (root.enable && cfg.enable) {
|
||||||
|
programs.zen-browser = {
|
||||||
|
enable = true;
|
||||||
|
policies = {
|
||||||
|
PasswordManagerEnabled = false;
|
||||||
|
AutofillCreditCardEnabled = false;
|
||||||
|
AutofillAddressEnabled = false;
|
||||||
|
DisableAppUpdate = true;
|
||||||
|
DisableFeedbackCommands = true;
|
||||||
|
DisableFirefoxStudies = true;
|
||||||
|
DisablePocket = true;
|
||||||
|
DisableTelemetry = true;
|
||||||
|
OfferToSaveLogins = false;
|
||||||
|
EnableTrackingProtection = {
|
||||||
|
Value = true;
|
||||||
|
Locked = true;
|
||||||
|
Cryptomining = true;
|
||||||
|
Fingerprinting = true;
|
||||||
|
};
|
||||||
|
ExtensionSettings = {
|
||||||
|
"{446900e4-71c2-419f-a6a7-df9c091e268b}" = {
|
||||||
|
install_url = "https://addons.mozilla.org/firefox/downloads/latest/bitwarden-password-manager/latest.xpi";
|
||||||
|
installation_mode = "normal_installed";
|
||||||
|
};
|
||||||
|
"uBlock0@raymondhill.net" = {
|
||||||
|
install_url = "https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi";
|
||||||
|
installation_mode = "normal_installed";
|
||||||
|
};
|
||||||
|
"{762f9885-5a13-4abd-9c77-433dcd38b8fd}" = {
|
||||||
|
install_url = "https://addons.mozilla.org/firefox/downloads/latest/return-youtube-dislikes/latest.xpi";
|
||||||
|
installation_mode = "normal_installed";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
home.packages = [
|
||||||
|
(pkgs.writeShellApplication {
|
||||||
|
name = "extract-firefox-extension";
|
||||||
|
runtimeInputs = with pkgs; [
|
||||||
|
wget
|
||||||
|
unzip
|
||||||
|
jq
|
||||||
|
];
|
||||||
|
text = ''
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "usage: $0 <firefox-addon-url>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
PLUGIN_URL="$1"
|
||||||
|
TEMP_DIR="extension-id-$(date +%s)"
|
||||||
|
mkdir "$TEMP_DIR" || exit 1
|
||||||
|
cd "$TEMP_DIR" || exit 1
|
||||||
|
|
||||||
|
DOWNLOAD_URL=$(echo "$PLUGIN_URL" \
|
||||||
|
| sed -E 's|https://addons.mozilla.org/firefox/downloads/file/[0-9]+/([^/]+)-[^/]+\.xpi|\1|' \
|
||||||
|
| tr '_' '-' \
|
||||||
|
| awk '{print "https://addons.mozilla.org/firefox/downloads/latest/" $1 "/latest.xpi"}')
|
||||||
|
|
||||||
|
wget -q "$DOWNLOAD_URL" -O latest.xpi || { cd ..; rm -rf "$TEMP_DIR"; exit 1; }
|
||||||
|
unzip -q latest.xpi -d unpacked || { cd ..; rm -rf "$TEMP_DIR"; exit 1; }
|
||||||
|
|
||||||
|
jq -r '.browser_specific_settings.gecko.id' unpacked/manifest.json || { cd ..; rm -rf "$TEMP_DIR"; exit 1; }
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
rm -rf "$TEMP_DIR"
|
||||||
|
'';
|
||||||
|
})
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
{ ... }: {
|
||||||
|
flake.homeManagerModules.wisdomDesktopGtkQtTheming =
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
root = config.chiasson.home;
|
||||||
|
cfg = config.chiasson.home.desktop.theming;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.chiasson.home.desktop.theming = {
|
||||||
|
enable = lib.mkEnableOption ''
|
||||||
|
WhiteSur GTK + icon themes, Phinger cursor, and Qt via the KDE platform theme — same idea as
|
||||||
|
the old `home-shared.nix` stack for Hyprland/Niri (no full Plasma session required). Optional
|
||||||
|
`dank-colors.css` import for DMS/matugen GTK accents.
|
||||||
|
'';
|
||||||
|
|
||||||
|
matugenGtkColors = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Add `@import url("dank-colors.css");` to gtk3/gtk4 extraCss. DMS/matugen writes
|
||||||
|
`~/.config/gtk-3.0` / `gtk-4.0` `dank-colors.css` when dynamic theming is on — disable if
|
||||||
|
you use this module without DMS.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
gtkTheme = {
|
||||||
|
name = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "WhiteSur-Dark";
|
||||||
|
};
|
||||||
|
package = lib.mkOption {
|
||||||
|
type = lib.types.package;
|
||||||
|
default = pkgs.whitesur-gtk-theme;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
iconTheme = {
|
||||||
|
name = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "WhiteSur-dark";
|
||||||
|
};
|
||||||
|
package = lib.mkOption {
|
||||||
|
type = lib.types.package;
|
||||||
|
default = pkgs.whitesur-icon-theme;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
cursor = {
|
||||||
|
name = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "phinger-cursors-dark";
|
||||||
|
};
|
||||||
|
package = lib.mkOption {
|
||||||
|
type = lib.types.package;
|
||||||
|
default = pkgs.phinger-cursors;
|
||||||
|
};
|
||||||
|
size = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
default = 32;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
qt = {
|
||||||
|
platformTheme = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "kde";
|
||||||
|
description = ''
|
||||||
|
`QT_QPA_PLATFORMTHEME` (e.g. `kde` for Qt/KDE integration, matches previous flake).
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
extraPackages = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.package;
|
||||||
|
default = [ pkgs.whitesur-kde pkgs.qt6Packages.qt6ct ];
|
||||||
|
description = "Extra packages (KDE look-and-feel + qt6ct GUI).";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf (root.enable && cfg.enable) (lib.mkMerge [
|
||||||
|
{
|
||||||
|
home.packages = cfg.qt.extraPackages;
|
||||||
|
|
||||||
|
home.sessionVariables = {
|
||||||
|
QT_QPA_PLATFORMTHEME = cfg.qt.platformTheme;
|
||||||
|
};
|
||||||
|
|
||||||
|
home.pointerCursor = {
|
||||||
|
name = cfg.cursor.name;
|
||||||
|
package = cfg.cursor.package;
|
||||||
|
size = cfg.cursor.size;
|
||||||
|
gtk.enable = true;
|
||||||
|
x11.enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
gtk = {
|
||||||
|
enable = true;
|
||||||
|
theme = {
|
||||||
|
name = cfg.gtkTheme.name;
|
||||||
|
package = cfg.gtkTheme.package;
|
||||||
|
};
|
||||||
|
iconTheme = {
|
||||||
|
name = cfg.iconTheme.name;
|
||||||
|
package = cfg.iconTheme.package;
|
||||||
|
};
|
||||||
|
cursorTheme = {
|
||||||
|
name = cfg.cursor.name;
|
||||||
|
package = cfg.cursor.package;
|
||||||
|
size = cfg.cursor.size;
|
||||||
|
};
|
||||||
|
gtk3.extraConfig = {
|
||||||
|
gtk-application-prefer-dark-theme = 1;
|
||||||
|
};
|
||||||
|
gtk4.extraConfig = {
|
||||||
|
gtk-application-prefer-dark-theme = 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
(lib.mkIf cfg.matugenGtkColors {
|
||||||
|
gtk.gtk3.extraCss = ''
|
||||||
|
@import url("dank-colors.css");
|
||||||
|
'';
|
||||||
|
gtk.gtk4.extraCss = ''
|
||||||
|
@import url("dank-colors.css");
|
||||||
|
'';
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
{ ... }: {
|
||||||
|
flake.homeManagerModules.wisdomDesktopScreenshot =
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
root = config.chiasson.home;
|
||||||
|
cfg = config.chiasson.home.desktop.screenshot;
|
||||||
|
hyprlandHm = lib.attrByPath [ "wayland" "windowManager" "hyprland" ] { } config;
|
||||||
|
hyprlandHmEnabled = hyprlandHm.enable or false;
|
||||||
|
keyOk = cfg.swiftshareApiKeyFile != null && cfg.swiftshareApiKeyFile != "";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.chiasson.home.desktop.screenshot.enable = lib.mkEnableOption ''
|
||||||
|
grim/slurp/swappy + SwiftShare helpers; Hyprland binds if HM Hyprland is on.
|
||||||
|
'';
|
||||||
|
|
||||||
|
options.chiasson.home.desktop.screenshot.swiftshareApiKeyFile = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
File with SwiftShare API key (sops path is fine). Required when screenshot module is on.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkMerge [
|
||||||
|
(lib.mkIf (root.enable && cfg.enable) {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = keyOk;
|
||||||
|
message = "chiasson.home.desktop.screenshot: set chiasson.home.desktop.screenshot.swiftshareApiKeyFile to your SwiftShare API key file path.";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
})
|
||||||
|
(lib.mkIf (root.enable && cfg.enable && keyOk) (
|
||||||
|
let
|
||||||
|
apiKeyFile = cfg.swiftshareApiKeyFile;
|
||||||
|
in
|
||||||
|
lib.mkMerge [
|
||||||
|
{
|
||||||
|
home.packages = with pkgs; [
|
||||||
|
grim
|
||||||
|
slurp
|
||||||
|
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
|
||||||
|
'')
|
||||||
|
];
|
||||||
|
}
|
||||||
|
(lib.mkIf hyprlandHmEnabled {
|
||||||
|
wayland.windowManager.hyprland.settings = {
|
||||||
|
bind = [
|
||||||
|
", Print, exec, grim -g \"$(slurp)\" - | wl-copy"
|
||||||
|
"Control, Print, exec, grim -g \"$(slurp)\" - | swappy -f -"
|
||||||
|
"SUPER, Print, exec, swiftshare-screenshot"
|
||||||
|
];
|
||||||
|
windowrule = [
|
||||||
|
"float on, opacity 1.0 override, match:class ^(swappy)$"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
})
|
||||||
|
]
|
||||||
|
))
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
{ inputs, ... }: {
|
||||||
|
flake.homeManagerModules.wisdomEditorsCursor =
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
root = config.chiasson.home;
|
||||||
|
cfg = config.chiasson.home.editors.cursor;
|
||||||
|
cursorPkgs = inputs.cursor.packages.${pkgs.stdenv.hostPlatform.system} or { };
|
||||||
|
cursorPkg =
|
||||||
|
if cursorPkgs ? cursor then
|
||||||
|
cursorPkgs.cursor
|
||||||
|
else if cursorPkgs ? default then
|
||||||
|
cursorPkgs.default
|
||||||
|
else
|
||||||
|
null;
|
||||||
|
# NixOS-New `home-shared.nix`: `cursor-cli` alongside the AppImage. nixpkgs now names this
|
||||||
|
# `cursor-agent`; keep both for compatibility across pins.
|
||||||
|
defaultAgentPkg =
|
||||||
|
if pkgs ? cursor-agent then
|
||||||
|
pkgs.cursor-agent
|
||||||
|
else if pkgs ? cursor-cli then
|
||||||
|
pkgs.cursor-cli
|
||||||
|
else
|
||||||
|
null;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.chiasson.home.editors.cursor = {
|
||||||
|
enable = lib.mkEnableOption "Cursor editor from the `cursor` flake input.";
|
||||||
|
setAsDefaultEditor = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "`EDITOR` / `VISUAL` → `cursor --wait`.";
|
||||||
|
};
|
||||||
|
agent = {
|
||||||
|
enable = lib.mkEnableOption ''
|
||||||
|
Cursor Agent CLI (`cursor-agent` in current nixpkgs; older pins used `cursor-cli`).
|
||||||
|
'' // {
|
||||||
|
default = true;
|
||||||
|
};
|
||||||
|
package = lib.mkOption {
|
||||||
|
type = with lib.types; nullOr package;
|
||||||
|
default = defaultAgentPkg;
|
||||||
|
defaultText = "pkgs.cursor-agent or pkgs.cursor-cli or null";
|
||||||
|
description = ''
|
||||||
|
Package providing the `cursor-agent` CLI. Set to `null` to omit the CLI while keeping the GUI app.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf (root.enable && cfg.enable && cursorPkg != null) {
|
||||||
|
home.packages =
|
||||||
|
[ cursorPkg ]
|
||||||
|
++ lib.optionals (cfg.agent.enable && cfg.agent.package != null) [ cfg.agent.package ];
|
||||||
|
home.sessionVariables = lib.mkIf cfg.setAsDefaultEditor {
|
||||||
|
EDITOR = "cursor --wait";
|
||||||
|
VISUAL = "cursor --wait";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
{ ... }: {
|
||||||
|
flake.homeManagerModules.wisdomFilebrowsersDolphin =
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
root = config.chiasson.home;
|
||||||
|
cfg = root.filebrowsers.dolphin;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.chiasson.home.filebrowsers.dolphin.enable = lib.mkEnableOption "Dolphin + declarative dolphinrc.";
|
||||||
|
|
||||||
|
config = lib.mkIf (root.enable && cfg.enable) {
|
||||||
|
home.packages = [ pkgs.kdePackages.dolphin ];
|
||||||
|
|
||||||
|
xdg.configFile."dolphinrc".text = ''
|
||||||
|
[ContentDisplay]
|
||||||
|
UsePermissionsFormat=CombinedFormat
|
||||||
|
|
||||||
|
[General]
|
||||||
|
StartupPath=~
|
||||||
|
DoubleClickViewAction=show_hidden_files
|
||||||
|
ShowFullPath=true
|
||||||
|
ShowFullPathInTitlebar=true
|
||||||
|
ShowStatusBar=FullWidth
|
||||||
|
UseTabForSwitchingSplitView=true
|
||||||
|
Version=202
|
||||||
|
ViewPropsTimestamp=2025,11,17,23,21,57.762
|
||||||
|
|
||||||
|
[KFileDialog Settings]
|
||||||
|
Places Icons Auto-resize=false
|
||||||
|
Places Icons Static Size=22
|
||||||
|
|
||||||
|
[MainWindow]
|
||||||
|
MenuBar=Disabled
|
||||||
|
ToolBarsMovable=Disabled
|
||||||
|
|
||||||
|
[PreviewSettings]
|
||||||
|
Plugins=appimagethumbnail,audiothumbnail,blenderthumbnail,comicbookthumbnail,cursorthumbnail,djvuthumbnail,ebookthumbnail,exrthumbnail,directorythumbnail,fontthumbnail,imagethumbnail,jpegthumbnail,kraorathumbnail,windowsexethumbnail,windowsimagethumbnail,mobithumbnail,opendocumentthumbnail,gsthumbnail,rawthumbnail,svgthumbnail,ffmpegthumbs
|
||||||
|
|
||||||
|
[IconsMode]
|
||||||
|
IconSize=48
|
||||||
|
PreviewSize=48
|
||||||
|
TextLines=2
|
||||||
|
UseThumbnails=true
|
||||||
|
|
||||||
|
[DetailsMode]
|
||||||
|
FontWeight=50
|
||||||
|
HighlightEntireRow=true
|
||||||
|
|
||||||
|
[ViewProperties]
|
||||||
|
Mode=1
|
||||||
|
ColumnWidths=50,50,50,50,50,50,50,50,50,50
|
||||||
|
SortColumn=0
|
||||||
|
SortOrder=0
|
||||||
|
SortFoldersFirst=true
|
||||||
|
SortHiddenLast=false
|
||||||
|
SortCaseSensitively=false
|
||||||
|
ShowPreviews=true
|
||||||
|
ShowInGroups=false
|
||||||
|
ShowFoldersFirst=true
|
||||||
|
ShowHiddenFilesLast=false
|
||||||
|
NaturalSorting=true
|
||||||
|
|
||||||
|
[ContextMenu]
|
||||||
|
ShowCopyToMenu=true
|
||||||
|
ShowMoveToMenu=true
|
||||||
|
|
||||||
|
[Search]
|
||||||
|
Location=Everywhere
|
||||||
|
|
||||||
|
[SettingsWindow]
|
||||||
|
SidebarWidth=180
|
||||||
|
SplitterState=AAAA/wAAAAD9AAAAAAAAAAAAAAABAAAAAQAAAAEAAAAAQAAAAEAAAAA=
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
{ ... }: {
|
||||||
|
flake.homeManagerModules.wisdomHardwareUconsoleGamepad =
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
root = config.chiasson.home;
|
||||||
|
cfg = config.chiasson.home.hardware.uconsoleGamepad;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.chiasson.home.hardware.uconsoleGamepad.enable = lib.mkEnableOption ''
|
||||||
|
uConsole gamepad antimicrox profile + Hyprland exec-once when HM Hyprland is on.
|
||||||
|
'';
|
||||||
|
|
||||||
|
config = lib.mkIf (root.enable && cfg.enable) (lib.mkMerge [
|
||||||
|
{
|
||||||
|
home.packages = [ pkgs.antimicrox ];
|
||||||
|
home.file.".config/antimicrox/uconsole.gamecontroller.amgp".text = ''
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<gamecontroller configversion="19" appversion="3.5.1">
|
||||||
|
<sdlname>Clockwork Pi DevTerm</sdlname>
|
||||||
|
<uniqueID>030000fdaf1e00002400000010010000785536</uniqueID>
|
||||||
|
<stickAxisAssociation index="2" xAxis="3" yAxis="4"/>
|
||||||
|
<stickAxisAssociation index="1" xAxis="1" yAxis="2"/>
|
||||||
|
<vdpadButtonAssociations index="1">
|
||||||
|
<vdpadButtonAssociation axis="0" button="12" direction="1"/>
|
||||||
|
<vdpadButtonAssociation axis="0" button="13" direction="4"/>
|
||||||
|
<vdpadButtonAssociation axis="0" button="14" direction="8"/>
|
||||||
|
<vdpadButtonAssociation axis="0" button="15" direction="2"/>
|
||||||
|
</vdpadButtonAssociations>
|
||||||
|
<names>
|
||||||
|
<controlstickname index="2">Stick 2</controlstickname>
|
||||||
|
<controlstickname index="1">Stick 1</controlstickname>
|
||||||
|
</names>
|
||||||
|
<sets>
|
||||||
|
<set index="1">
|
||||||
|
<trigger index="6">
|
||||||
|
<throttle>positivehalf</throttle>
|
||||||
|
</trigger>
|
||||||
|
<trigger index="5">
|
||||||
|
<throttle>positivehalf</throttle>
|
||||||
|
</trigger>
|
||||||
|
<button index="2">
|
||||||
|
<slots>
|
||||||
|
<slot>
|
||||||
|
<code>0x1000022</code>
|
||||||
|
<mode>keyboard</mode>
|
||||||
|
</slot>
|
||||||
|
</slots>
|
||||||
|
</button>
|
||||||
|
</set>
|
||||||
|
</sets>
|
||||||
|
</gamecontroller>
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
(lib.mkIf (config.wayland.windowManager.hyprland.enable or false) {
|
||||||
|
wayland.windowManager.hyprland.settings.exec-once = lib.mkAfter [
|
||||||
|
"antimicrox --hidden --no-tray --profile ${config.home.homeDirectory}/.config/antimicrox/uconsole.gamecontroller.amgp &"
|
||||||
|
];
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user