refactor rice into config

This commit is contained in:
atagen 2025-07-21 12:26:28 +10:00
parent 08c13ea2bc
commit 9a1217044c
64 changed files with 225 additions and 230 deletions

View file

@ -0,0 +1,15 @@
{
pkgs,
lib,
...
}:
{
hm.home.packages = builtins.attrValues {
inherit (pkgs)
avizo
playerctl
;
};
hm.quickServices."avizo-service" = "${lib.getExe' pkgs.avizo "avizo-service"}";
}

119
graphical/binds.nix Normal file
View file

@ -0,0 +1,119 @@
{
pkgs,
lib,
config,
inputs,
...
}:
let
inherit (lib)
range
nameValuePair
mapAttrs'
mergeAttrsList
;
inherit (builtins) listToAttrs replaceStrings;
inherit (config.hm.lib.niri) actions;
hBinds = {
H = "left";
L = "right";
};
vBinds = {
J = "down";
K = "up";
};
makeDirBind =
mods: cmd: keys:
mapAttrs' (
key: dir:
nameValuePair "${mods}+${key}" {
action = actions."${replaceStrings [ "$DIR" ] [ "${dir}" ] "${cmd}"}";
}
) keys;
makeWsBind =
mods: cmd:
listToAttrs (
map (num: {
name = "${mods}+${builtins.toString num}";
value = {
action."${cmd}" = num;
};
}) (range 1 6)
);
tagctl = lib.getExe' inputs.niri-tag.packages.${pkgs.system}.unstable "tagctl";
makeTagBind =
mods: cmd:
listToAttrs (
map (num: {
name = "${mods}+${builtins.toString num}";
value = {
action.spawn = [
tagctl
cmd
(builtins.toString num)
];
};
}) (range 1 6)
);
in
{
hm.programs.niri.settings.binds = mergeAttrsList [
{
"Mod+D".action.spawn = [
"qs"
"ipc"
"call"
"launch"
"toggle"
];
"Mod+F".action.spawn = "firefox";
"Mod+E".action.spawn = "nautilus";
"Mod+Return".action.spawn = "kitty";
"Mod+Shift+E".action.spawn = "wlogout";
"Mod+Equal".action.spawn = "bitwarden";
"Mod+Shift+Q".action = actions.close-window;
"Mod+Shift+S".action = actions.screenshot;
"Mod+R".action = actions.switch-preset-column-width;
"Mod+Shift+R".action = actions.maximize-column;
"XF86AudioRaiseVolume".action.spawn = [
"volumectl"
"-u"
"up"
];
"XF86AudioLowerVolume".action.spawn = [
"volumectl"
"-u"
"down"
];
"XF86AudioMute".action.spawn = [
"volumectl"
"toggle-mute"
];
"XF86AudioStop".action.spawn = [
"playerctl"
"stop"
];
"XF86AudioPlay".action.spawn = [
"playerctl"
"play-pause"
];
"XF86AudioNext".action.spawn = [
"playerctl"
"next"
];
"XF86AudioPrev".action.spawn = [
"playerctl"
"previous"
];
"Mod+Space".action = actions.toggle-window-floating;
}
(makeDirBind "Mod" "focus-window-$DIR" vBinds)
(makeDirBind "Mod" "focus-column-or-monitor-$DIR" hBinds)
(makeDirBind "Mod+Shift" "move-column-$DIR-or-to-monitor-$DIR" hBinds)
(makeDirBind "Mod+Ctrl" "consume-or-expel-window-$DIR" hBinds)
(makeDirBind "Mod+Ctrl" "move-window-$DIR" vBinds)
(makeTagBind "Mod" "toggle-tag")
(makeTagBind "Mod+Shift" "toggle")
(makeTagBind "Mod+Ctrl" "exclusive-tag")
];
}

40
graphical/boot.nix Normal file
View file

@ -0,0 +1,40 @@
{ config, ... }:
let
inherit (config) rice;
in
{
boot.tmp.useTmpfs = true;
boot.initrd.systemd.enable = true;
boot.loader.limine = {
enable = true;
style =
let
pal = rice.palette.shortHex;
in
{
wallpapers = [ rice.bg.src ];
interface = {
brandingColor = 1;
branding = "welcome to quiver";
};
graphicalTerminal =
let
getPal = p: builtins.attrValues p |> builtins.concatStringsSep ";";
in
{
palette = getPal pal.normal;
brightPalette = getPal pal.bright;
marginGradient = 0;
margin = 256;
foreground = pal.util.fg;
background = "20" + pal.util.bg;
brightForeground = pal.bright.yellow;
brightBackground = pal.util.bg;
};
backdrop = pal.util.bg;
};
maxGenerations = 5;
};
}

93
graphical/browser.nix Normal file
View file

@ -0,0 +1,93 @@
{
lib,
...
}:
let
officialAddon = name: {
install_url = lib.strings.concatStrings [
"https://addons.mozilla.org/firefox/downloads/latest/"
name
"/latest.xpi"
];
installation_mode = "force_installed";
};
lock-false = {
Value = false;
Status = "locked";
};
lock-true = {
Value = true;
Status = "locked";
};
in
{
hm.programs.firefox = {
enable = true;
policies = {
DisableTelemetry = true;
DisableFirefoxStudies = true;
EnableTrackingProtection = {
Value = true;
Locked = true;
Cryptomining = true;
Fingerprinting = true;
};
DisablePocket = true;
DisableFirefoxAccounts = true;
DisableAccounts = true;
DisableFirefoxScreenshots = true;
DisableAppUpdate = true;
DisablePrivateBrowsing = true;
DontCheckDefaultBrowser = true;
OverrideFirstRunPage = "";
OverridePostUpdatePage = "";
DisplayBookmarksToolbar = "newtab"; # alternatives: "always" or "newtab"
DisplayMenuBar = "default-off"; # alternatives: "always", "never" or "default-on"
SearchBar = "unified"; # alternative: "separate"
ExtensionSettings = {
"*".installation_mode = "blocked"; # blocks all addons except the ones specified below
"uBlock0@raymondhill.net" = officialAddon "ublock-origin";
"addon@darkreader.org" = officialAddon "darkreader";
"vimium-c@gdh1995.cn" = officialAddon "vimium-c";
"{b86e4813-687a-43e6-ab65-0bde4ab75758}" = officialAddon "localcdn-fork-of-decentraleyes";
"jid1-5Fs7iTLscUaZBgwr@jetpack" = officialAddon "happy-bonobo-disable-webrtc";
"{446900e4-71c2-419f-a6a7-df9c091e268b}" = officialAddon "bitwarden-password-mananger";
};
Preferences = {
"extensions.pocket.enabled" = lock-false;
"extensions.screenshots.disabled" = lock-true;
"browser.topsites.contile.enabled" = lock-false;
"browser.formfill.enable" = lock-false;
"browser.search.suggest.enabled" = lock-false;
"browser.search.suggest.enabled.private" = lock-false;
"browser.urlbar.suggest.searches" = lock-false;
"browser.urlbar.showSearchSuggestionsFirst" = lock-false;
"browser.newtabpage.activity-stream.feeds.section.topstories" = lock-false;
"browser.newtabpage.activity-stream.feeds.snippets" = lock-false;
"browser.newtabpage.activity-stream.section.highlights.includePocket" = lock-false;
"browser.newtabpage.activity-stream.section.highlights.includeBookmarks" = lock-false;
"browser.newtabpage.activity-stream.section.highlights.includeDownloads" = lock-false;
"browser.newtabpage.activity-stream.section.highlights.includeVisited" = lock-false;
"browser.newtabpage.activity-stream.showSponsored" = lock-false;
"browser.newtabpage.activity-stream.system.showSponsored" = lock-false;
"browser.newtabpage.activity-stream.showSponsoredTopSites" = lock-false;
};
};
profiles.default = {
id = 0;
name = "Default";
settings = {
"browser.startup.homepage" = "about:blank";
"browser.shell.didSkipDefaultBrowserCheckOnFirstRun" = true;
"browser.policies.applied" = true;
"widget.use-xdg-desktop-portal.file-picker" = 1;
"widget.use-xdg-desktop-portal.mime-handler" = 1;
};
};
};
}

18
graphical/chat.nix Normal file
View file

@ -0,0 +1,18 @@
{ config, pkgs, ... }:
{
hm.programs.firefox.webapps = {
"Microsoft-Teams" = {
url = "https://teams.microsoft.com";
extraSettings = config.hm.programs.firefox.profiles.default.settings;
name = "Microsoft Teams";
icon = ../assets/ms_teams.png;
};
"Facebook-Messenger" = {
url = "https://www.messenger.com";
extraSettings = config.hm.programs.firefox.profiles.default.settings;
name = "Facebook Messenger";
icon = ../assets/fb_msg.png;
};
};
hm.home.packages = [ pkgs.cinny-desktop ];
}

77
graphical/dev.nix Normal file
View file

@ -0,0 +1,77 @@
{
pkgs,
...
}:
let
getPkgs = builtins.attrValues;
in
{
hm.home.packages =
getPkgs {
inherit (pkgs)
direnv
;
}
++ [
# from https://gist.github.com/mikeboiko/58ab730afd65bca0a125bc12b6f4670d
(pkgs.writeTextFile {
name = "rbw-helper";
text = ''
declare -A params
if [ "x$1" == "xget" ]; then
read line
while [ -n "$line" ]; do
key=$\{line%%=*}
value=$\{line#*=}
params[$key]=$value
read line
done
if [ "x$\{params['protocol']}" != "xhttps" ]; then
exit
fi
if [ -z "$\{params["host"]}" ]; then
exit
fi
rbw ls > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "Please login to rbw to use git credential helper" > /dev/stderr
exit
fi
user=`rbw get --full $\{params["host"]} | grep "Username:" | cut -d' ' -f2-`
pass=`rbw get $\{params["host"]}`
if [ "x$user" == "x" ] || [ "x$pass" == "x" ]; then
echo "Couldn't find host in rbw DB." > /dev/stderr
exit
fi
echo username=$user
echo password=$pass
fi
'';
executable = true;
destination = "/bin/git-credential-rw";
})
];
hm.programs.direnv = {
enable = true;
nix-direnv = {
enable = true;
};
enableFishIntegration = true;
};
hm.programs.git = {
enable = true;
userName = "atagen";
userEmail = "boss@atagen.co";
};
}

9
graphical/documents.nix Normal file
View file

@ -0,0 +1,9 @@
{ pkgs, ... }:
{
hm.home.packages = builtins.attrValues {
inherit (pkgs)
libreoffice
thunderbird
;
};
}

10
graphical/fm.nix Normal file
View file

@ -0,0 +1,10 @@
{ pkgs, ... }:
{
hm.home.packages = builtins.attrValues {
inherit (pkgs)
file-roller
nautilus
;
};
}

14
graphical/gfx-env.nix Normal file
View file

@ -0,0 +1,14 @@
{
...
}:
{
environment.sessionVariables = {
NIXOS_OZONE_WL = "1";
GBM_BACKEND = "nvidia-drm";
NVD_BACKEND = "direct";
__GLX_VENDOR_LIBRARY_NAME = "nvidia";
LIBVA_DRIVER_NAME = "nvidia";
__GL_GSYNC_ALLOWED = "1";
__GL_VRR_ALLOWED = "1";
};
}

5
graphical/hw.nix Normal file
View file

@ -0,0 +1,5 @@
{ ... }:
{
hardware.enableRedistributableFirmware = true;
hardware.enableAllFirmware = true;
}

6
graphical/input.nix Normal file
View file

@ -0,0 +1,6 @@
{ ... }:
{
services.libinput.enable = true;
console.useXkbConfig = true;
services.xserver.xkb.options = "caps:swapescape";
}

View file

@ -0,0 +1,38 @@
{
pkgs,
...
}:
{
xdg.autostart.enable = true;
xdg.portal.enable = true;
hm.xdg = {
enable = true;
portal =
let
gtk = pkgs.xdg-desktop-portal-gtk;
gnome = pkgs.xdg-desktop-portal-gnome;
in
{
enable = true;
config = {
common = {
default = [
"gnome"
];
};
};
extraPortals = [
gnome
gtk
];
};
};
environment.pathsToLink = [
"/share/xdg-desktop-portal"
"/share/applications"
];
security.polkit.enable = true;
}

11
graphical/kernel.nix Normal file
View file

@ -0,0 +1,11 @@
{ pkgs, inputs, ... }:
{
imports = [
inputs.nyx.nixosModules.default
];
services.scx = {
enable = true;
scheduler = "scx_bpfland";
};
boot.kernelPackages = pkgs.linuxPackages_cachyos;
}

45
graphical/logout.nix Normal file
View file

@ -0,0 +1,45 @@
{
pkgs,
config,
...
}:
let
inherit (config) rice;
pal = rice.palette.hex;
in
{
hm.programs.wlogout = {
enable = true;
package = pkgs.wleave;
layout = builtins.fromJSON (builtins.readFile ./logout/layout);
style =
with pal;
(builtins.replaceStrings
[
"/usr/share/wlogout"
"/etc/wlogout"
"#WINBG"
"#BTNCOL"
"#BTNBG"
"#BTNFOCUSBG"
"#WINLOGO"
]
[
"${pkgs.wlogout}/share/wlogout"
"${pkgs.wlogout}/etc/wlogout"
util.bg
bright.yellow
util.bg
normal.black
(builtins.path {
name = "winlogo";
path = ../assets/winlogo.png;
sha256 = "7c1ff96b553c7a7ca3a7b7cf8efe830ab7feea92355aed288a10ee7347c24108";
})
]
(builtins.readFile ./logout/style.css)
);
};
hm.home.packages = [ pkgs.wleave ];
}

38
graphical/logout/layout Normal file
View file

@ -0,0 +1,38 @@
[
{
"label" : "lock",
"action" : "loginctl lock-session",
"text" : "Lock",
"keybind" : "l"
},
{
"label" : "logout",
"action" : "loginctl terminate-user $USER",
"text" : "Logout",
"keybind" : "e"
},
{
"label" : "suspend",
"action" : "systemctl suspend",
"text" : "Suspend",
"keybind" : "s"
},
{
"label" : "shutdown",
"action" : "systemctl poweroff",
"text" : "Shutdown",
"keybind" : "d"
},
{
"label" : "reboot",
"action" : "systemctl reboot",
"text" : "Reboot",
"keybind" : "r"
},
{
"label" : "windows",
"action" : "systemctl reboot --boot-loader-entry=auto-windows",
"text" : "Windows",
"keybind" : "w"
}
]

View file

@ -0,0 +1,48 @@
* {
background-image: none;
}
window {
background-color: #WINBG;
}
button {
color: #BTNCOL;
background-color: #BTNBG;
border-style: solid;
border-width: 2px;
background-repeat: no-repeat;
background-position: center;
background-size: 25%;
}
button:focus, button:active, button:hover {
background-color: #BTNFOCUSBG;
outline-style: none;
}
#lock {
background-image: image(url("/usr/share/wlogout/assets/lock.png"), url("/usr/local/share/wlogout/assets/lock.png"));
}
#logout {
background-image: image(url("/usr/share/wlogout/assets/logout.png"), url("/usr/local/share/wlogout/assets/logout.png"));
}
#suspend {
background-image: image(url("/usr/share/wlogout/assets/suspend.png"), url("/usr/local/share/wlogout/assets/suspend.png"));
}
#hibernate {
background-image: image(url("/usr/share/wlogout/assets/hibernate.png"), url("/usr/local/share/wlogout/assets/hibernate.png"));
}
#shutdown {
background-image: image(url("/usr/share/wlogout/assets/shutdown.png"), url("/usr/local/share/wlogout/assets/shutdown.png"));
}
#reboot {
background-image: image(url("/usr/share/wlogout/assets/reboot.png"), url("/usr/local/share/wlogout/assets/reboot.png"));
}
#windows {
background-image: image(url("#WINLOGO"))
}

13
graphical/media.nix Normal file
View file

@ -0,0 +1,13 @@
{ pkgs, ... }:
{
hm.home.packages = builtins.attrValues {
inherit (pkgs)
mpv
imv
resonance
zathura
feishin
nicotine-plus
;
};
}

15
graphical/network.nix Normal file
View file

@ -0,0 +1,15 @@
{ ... }:
{
networking.networkmanager.enable = true;
systemd.services.NetworkManager-wait-online.enable = true;
services.resolved = {
enable = true;
fallbackDns = [
"103.1.206.179"
"168.138.8.38"
"168.138.12.137"
];
dnssec = "false";
};
services.mullvad-vpn.enable = true;
}

View file

@ -0,0 +1,14 @@
{ pkgs, ... }:
{
hm.programs.rbw = {
enable = true;
settings = {
email = "boss@atagen.co";
pinentry = pkgs.pinentry-gnome3;
base_url = "https://v.atagen.co";
};
};
hm.programs.git.extraConfig.credential.helper = "rbw";
hm.home.packages = [ pkgs.bitwarden ];
}

View file

@ -0,0 +1,32 @@
{ pkgs, config, ... }:
let
inherit (config) rice;
in
{
# for quickshell
qt.enable = true;
hm.home.packages = [
pkgs.gtk-engine-murrine
];
hm.fonts.fontconfig.enable = true;
hm.qt = {
enable = true;
style.name = "adwaita-dark";
platformTheme.name = "adwaita";
};
hm.gtk = {
enable = true;
theme = {
inherit (rice.gtk-theme) package name;
};
iconTheme = {
inherit (rice.icons) package name;
};
font = {
inherit (rice.fonts.sans) size package name;
};
};
}

View file

@ -0,0 +1,59 @@
{
config,
lib,
...
}:
{
config.hm.options = {
quickServices =
with lib;
mkOption {
type = with types; attrsOf str;
default = { };
};
quickOneShots =
with lib;
mkOption {
type = with types; attrsOf str;
default = { };
};
# for specifying an additional systemd target
extraTarget =
with lib;
mkOption {
type = with types; listOf str;
default = [ ];
};
};
config.hm.config = {
systemd.user.services =
builtins.mapAttrs (name: cmd: {
Unit = {
Description = "${name}";
Requires = [ "graphical-session.target" ] ++ config.hm.extraTarget;
After = [ "graphical-session.target" ] ++ config.hm.extraTarget;
};
Service = {
ExecStart = cmd;
};
Install = {
WantedBy = [ "graphical-session.target" ] ++ config.hm.extraTarget;
};
}) config.hm.quickServices
// builtins.mapAttrs (name: cmd: {
Unit = {
Description = "${name}";
After = [ "graphical-session.target" ] ++ config.extraTarget;
};
Service = {
ExecStart = cmd;
Type = "oneshot";
};
Install = {
WantedBy = [ "graphical-session.target" ] ++ config.extraTarget;
};
}) config.hm.quickOneShots;
};
}

106
graphical/rice.nix Normal file
View file

@ -0,0 +1,106 @@
{
pkgs,
userPkgs,
config,
inputs,
...
}:
{
hm.home.packages =
let
inherit (config.rice) icons fonts cursor;
in
fonts.pkgs
++ icons.pkgs
++ [
cursor.package
];
rice = {
fonts =
let
sans = {
name = "Inria Sans";
size = 12;
package = pkgs.inriafonts;
};
serif = {
name = "Inria Serif";
size = 12;
package = pkgs.inriafonts;
};
monospace = {
name = "Fira Code";
size = 10;
package = pkgs.fira-code;
};
emoji = {
name = "Twitter Color Emoji";
size = 12;
package = pkgs.twemoji-color-font;
};
in
{
inherit
sans
serif
monospace
emoji
;
pkgs = [
sans.package
serif.package
monospace.package
emoji.package
pkgs.meslo-lgs-nf
];
};
icons =
let
package = pkgs.papirus-icon-theme;
in
{
inherit package;
name = "Papirus-Dark";
pkgs = [
package
];
};
gtk-theme = {
name = "nix-rice";
package = pkgs.callPackage userPkgs.gtk-theme { palette = config.rice.palette.shortHex; };
};
borders = {
thickness = 6;
rounding = 0;
gaps_in = 32;
gaps_out = 72;
};
bg = {
src = builtins.path {
name = "wallpaper.jpg";
path = ./rice/wallpaper.jpg;
sha256 = "2db3f9d0397fbd4746ada297bd14c0c7d3e22c7d4e894968fcfece90bbfb902a";
};
};
cursor = {
package = pkgs.afterglow-cursors-recolored.override {
themeVariants = [ "Dracula" ];
draculaColorVariants = [ "Orange" ];
};
name = "Afterglow-Recolored-Dracula-Orange";
};
plymouth = {
theme = "starship";
font = "${config.rice.fonts.sans.package}/share/fonts/truetype/InriaSans-Regular.ttf";
themePackages = [
inputs.hudcore.packages.${pkgs.system}.default
];
};
};
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

76
graphical/shell.nix Normal file
View file

@ -0,0 +1,76 @@
{
pkgs,
lib,
inputs,
mainUser,
config,
...
}:
let
inherit (lib) getExe getExe';
inherit (config) rice;
in
{
# quickshell stuff
environment.systemPackages =
builtins.attrValues {
inherit (pkgs.kdePackages) qtbase qtdeclarative;
inherit (pkgs) wl-clipboard;
}
++ [
(inputs.quickshell.packages.${pkgs.system}.default.override {
withHyprland = false;
withI3 = false;
})
];
hm.systemd.user.services.quickshell = {
Unit.PartOf = [ "graphical-session.target" ];
Unit.After = [
"graphical-session.target"
"niri.service"
];
Install.WantedBy = [ "graphical-session.target" ];
Service = {
ExecStart = "${getExe' (inputs.quickshell.packages.${pkgs.system}.default.override {
withHyprland = false;
withI3 = false;
}) "qs"}";
};
};
hm.quickServices = {
"swaync" = "${getExe pkgs.swaynotificationcenter}";
"swaybg" = "${getExe pkgs.swaybg} -m fill -i ${rice.bg.src}";
"swayidle" =
let
niri = inputs.niri.packages.${pkgs.system}.niri-unstable;
systemctl = getExe' pkgs.systemd "systemctl";
in
''
${lib.getExe pkgs.swayidle} -w \
timeout 1800 '${systemctl} suspend' \
timeout 600 '${niri} msg action power-off-monitors'
'';
};
hm.systemd.user.targets.tray = {
Unit = {
Description = "Home Manager System Tray";
Requires = [ "graphical-session.target" ];
};
};
imports = [ inputs.arbys.nixosModules.arbys ];
environment = {
arbys = {
enable = true;
clobber = true;
};
files."/home/${mainUser}/.config/quickshell" = {
source = "/home/${mainUser}/.nix/graphical/shell/quickshell";
uid = 1000;
gid = 100;
};
};
}

View file

@ -0,0 +1,133 @@
pragma ComponentBehavior: Bound
import QtQuick
Rectangle {
id: base
anchors {
fill: parent
margins: 2
}
color: colours[0]
required property string format
required property var colours
property var clock: genClock(format)
property var date: new Date()
property string time: date.toLocaleString(Qt.locale())
property int cols: getColSum(clock)
property real colWidth: (base.width - topgrid.spacing - base.anchors.margins) / cols
property var keys: {
"a": [(date.getHours() > 11) | 0, -1],
"H": [-1].concat(binarise(date.getHours(), 5)),
"h": binarise(date.getHours() % 12, 4),
"m": binarise(date.getMinutes(), 6),
"s": binarise(date.getSeconds(), 6)
}
function genClock(format) {
return format.split('').map(k => keys[k]);
}
function getColSum(clock) {
return clock.map(x => Math.ceil(x.length / 2)).reduce((acc, el) => acc + el);
}
function binarise(n, p) {
return n.toString(2) // to base-2 string
.padStart(p, 0) // zero pad
.split('') // split to array
.slice(-p) // take only desired bits, lsb first
.map(x => parseInt(x)); // map to int
}
Grid {
id: topgrid
rows: 1
columns: base.clock.length
spacing: 2
anchors.fill: parent
Repeater {
model: base.clock.length
Grid {
id: inner
required property int index
property var bits: base.clock[index]
property int cols: bits.length / 2
width: base.colWidth * cols
height: base.height
rows: 2
columns: cols
spacing: 1
function calcBitSize() {
let cell = inner.width - inner.spacing * 2;
let def = (cell / inner.cols);
return (def > inner.height / 2) ? inner.height / 2 : def;
}
Repeater {
model: inner.bits.length
Rectangle {
required property int index
height: inner.calcBitSize()
width: height
color: "transparent"
Rectangle {
property int bit: inner.bits[parent.index]
property string on: base.colours[1]
property string off: base.colours[0]
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
height: parent.height
width: parent.width
border.width: 1
border.color: bit == -1 ? off : on
color: (bit == -1 || !bit) ? off : on
Behavior on color {
ColorAnimation {
duration: 150
}
}
radius: height
//
// height: bit ? parent.height / 3 * 2 : parent.height
// Behavior on height {
// NumberAnimation {
// duration: 50
// }
// }
// width: bit ? 1 : height
// Behavior on width {
// NumberAnimation {
// duration: 50
// }
// }
// radius: bit ? 0 : height
// Behavior on radius {
// NumberAnimation {
// duration: 50
// }
// }
}
}
}
}
}
Timer {
interval: 1000
running: true
repeat: true
onTriggered: {
base.date = new Date();
}
}
}
}

View file

@ -0,0 +1,222 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Wayland
import Quickshell.Widgets
import Quickshell.Io
Singleton {
id: topLevel
required property real width
Timer {
id: closeTimer
interval: 400
running: false
repeat: false
onTriggered: launcherData.active = false
}
PersistentProperties {
id: launcherData
property bool open: false
property bool active: false
property real curWidth: 0
onOpenChanged: {
if (open) {
curWidth = topLevel.width;
launcherData.active = true;
} else {
curWidth = 0;
closeTimer.start();
}
}
Behavior on curWidth {
NumberAnimation {
duration: 400
easing.type: Easing.InOutQuad
}
}
}
IpcHandler {
target: "launch"
function open(): void {
launcherData.open = true;
}
function close(): void {
launcherData.open = false;
}
function toggle(): void {
launcherData.open = !launcherData.open;
}
}
LazyLoader {
id: loader
activeAsync: launcherData.active
PanelWindow {
id: launcherBase
anchors {
top: true
bottom: true
right: true
}
color: "transparent"
implicitWidth: topLevel.width
WlrLayershell.layer: WlrLayer.Overlay
focusable: true
exclusionMode: ExclusionMode.Ignore
WlrLayershell.namespace: "shell:launcher"
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
// launcherData.curWidth
Rectangle {
color: "#ffab5b"
anchors {
fill: parent
topMargin: parent.height / 3
bottomMargin: anchors.topMargin
leftMargin: topLevel.width - launcherData.curWidth
}
bottomLeftRadius: 10
// topLeftRadius: 10
Rectangle {
color: "#272a2a"
anchors {
fill: parent
topMargin: 3
bottomMargin: 3
leftMargin: 3
}
bottomLeftRadius: 10
// topLeftRadius: 10
// implicitWidth: topLevel.width
TextField {
id: searchField
anchors {
top: parent.top
left: parent.left
right: parent.right
topMargin: 10
leftMargin: 10
}
font {
family: "Inria Sans"
pointSize: 12
}
color: "#202e2f"
height: 24
background: Rectangle {
color: "#caccce"
radius: 5
}
focus: true
Keys.forwardTo: [list]
Keys.onEscapePressed: launcherData.open = false
onAccepted: {
if (list.currentItem) {
list.currentItem.clicked(null);
}
}
}
Rectangle {
id: emptyScrollbar
anchors {
left: parent.left
top: searchField.bottom
bottom: list.bottom
topMargin: 6
bottomMargin: 4
leftMargin: 4
}
color: "#3a5299ff"
width: 3
radius: 10
}
ListView {
id: list
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
top: searchField.bottom
topMargin: 4
leftMargin: 12
bottomMargin: 12
}
clip: true
cacheBuffer: 0
model: ScriptModel {
values: DesktopEntries.applications.values.map(x => x).filter(entry => {
const search = searchField.text.toLowerCase();
const name = entry.name.toLowerCase();
return search.length ? name.indexOf(search) > -1 : true;
})
}
onModelChanged: list.currentIndex = 0
highlight: Rectangle {
anchors {
left: parent.left
right: parent.right
}
color: "#7f5299ff"
border.color: "#928cc9"
border.width: 2
}
spacing: 0
delegate: MouseArea {
id: clickableEntry
required property DesktopEntry modelData
onClicked: {
modelData.execute();
launcherData.open = false;
}
implicitHeight: 24
implicitWidth: ListView.view.width
RowLayout {
id: rowEntry
anchors {
left: parent.left
leftMargin: 4
verticalCenter: parent.verticalCenter
}
IconImage {
asynchronous: true
implicitSize: 18
source: Quickshell.iconPath(clickableEntry.modelData.icon)
}
Text {
font {
family: "Inria Sans"
pointSize: 12
}
color: "#ffab5b"
text: clickableEntry.modelData.name
Layout.alignment: Qt.AlignBottom
}
}
}
}
}
}
}
}
}

View file

@ -0,0 +1,18 @@
pragma Singleton
import Quickshell
import Quickshell.Services.Notifications
Singleton {
NotificationServer {
id: notifications
actionsSupported: true
bodyHyperlinksSupported: true
// bodyImagesSupported: true
bodyMarkupSupported: true
imageSupported: true
onNotification: noti => {
}
}
}

View file

@ -0,0 +1,84 @@
import Quickshell
import QtQuick
import QtQuick.Particles
PanelWindow {
id: particleRoot
anchors {
top: true
bottom: true
left: true
right: true
}
color: "transparent"
exclusionMode: ExclusionMode.Ignore
screen: Quickshell.screens.find(s => s.name == "DP-1")
property var mousePos: [width/2, height/2]
MouseArea {
id: mouse
anchors.fill: parent
hoverEnabled: true
onPositionChanged: {
particleRoot.mousePos = [mouse.x, mouse.y];
}
}
ParticleSystem {
id: sys
running: true
anchors.fill: parent
ItemParticle {
system: sys
delegate: Component {
Rectangle {
color: "black"
width: 7
height: width
radius: width
Rectangle {
color: "white"
width: 5
height: width
radius: width
}
}
}
}
Emitter {
system: sys
// x: particleRoot.mousePos[0]-10
// y: particleRoot.mousePos[1]-10
x: particleRoot.width/2
y: particleRoot.height/2
height: 60
width: 60
emitRate: 200
lifeSpan: 3000
startTime: 0
velocity: CumulativeDirection {
AngleDirection {
angle: 0
angleVariation: 360
magnitude: 10
magnitudeVariation: 0.2
}
}
}
Attractor {
pointX: particleRoot.mousePos[0]
pointY: particleRoot.mousePos[1]
strength: 200
affectedParameter: Attractor.Position
proportionalToDistance: Attractor.InverseLinear
}
}
}

View file

@ -0,0 +1,34 @@
pragma Singleton
import Quickshell
Singleton {
property var c: {
"bg": "#1b2021",
"fg": "#cecbca",
"black": "#272a2a",
"black_b": "#202e2f",
"red": "#c43325",
"red_b": "#c46056",
"green": "#8cc992",
"green_b": "#c2dab0",
"yellow": "#ffb852",
"yellow_b": "#ffab5b",
"blue": "#5299ff",
"blue_b": "#92beff",
"magenta": "#645ac9",
"magenta_b": "#928cc9",
"cyan": "#5abfc9",
"cyan_b": "#8cc4c9",
"white": "#b0c2da",
"white_b": "#caccce",
}
}

View file

@ -0,0 +1,212 @@
// components
import "bink" as Bink
import "launcher" as Launcher
// singletons
import "title"
import "tags"
import "rice"
import Quickshell
import Quickshell.Wayland
import QtQuick
import QtQuick.Controls
ShellRoot {
// rhs main
Variants {
model: Quickshell.screens.filter(s => s.name == "DP-2")
delegate: PanelWindow {
id: bink
property var modelData
anchors {
top: true
right: true
}
implicitHeight: 22
implicitWidth: 115
color: "transparent"
Rectangle {
anchors.fill: parent
bottomLeftRadius: 10
color: Colours.c.black
}
Rectangle {
anchors {
leftMargin: 8
bottomMargin: 3
fill: parent
}
color: Colours.c.black
bottomLeftRadius: 10
Bink.Bink {
format: "ahms"
colours: ["transparent", Colours.c.yellow_b]
}
}
exclusionMode: ExclusionMode.Ignore
WlrLayershell.layer: WlrLayer.Top
screen: modelData
}
}
// centre main
Variants {
model: Quickshell.screens.filter(s => s.name == "DP-2")
delegate: PanelWindow {
id: windowTitle
property var modelData
anchors {
top: true
}
TextMetrics {
id: textInfo
font {
family: "Inria Sans"
pointSize: 12
}
elideWidth: 550
elide: Qt.ElideMiddle
text: Title.currentWindow
}
implicitHeight: textInfo.height + 6
implicitWidth: (textInfo.width > textInfo.elideWidth ? textInfo.elideWidth : textInfo.width) + 24
Behavior on implicitWidth {
NumberAnimation {
duration: 150
easing.type: Easing.InOutQuad
}
}
color: "transparent"
Rectangle {
anchors {
fill: parent
}
color: Colours.c.black
bottomLeftRadius: 10
bottomRightRadius: 10
Rectangle {
anchors {
fill: parent
leftMargin: 12
rightMargin: 12
topMargin: 4
}
color: "transparent"
Text {
font {
family: "Inria Sans"
pointSize: 12
}
color: Colours.c.yellow_b
text: textInfo.elidedText
}
}
}
exclusionMode: ExclusionMode.Ignore
WlrLayershell.layer: WlrLayer.Top
screen: modelData
}
}
// bottom middle main
Variants {
model: Quickshell.screens.filter(s => s.name == "DP-2")
delegate: PanelWindow {
id: tags
property var modelData
anchors {
bottom: true
}
implicitHeight: 22
implicitWidth: Tags.keys.length * 13 + 24
// implicitWidth:.width + 24
color: "transparent"
Rectangle {
anchors {
fill: parent
}
color: Colours.c.black
topLeftRadius: 10
topRightRadius: 10
Rectangle {
anchors {
fill: parent
leftMargin: 12
rightMargin: 12
topMargin: 3
}
Row {
anchors {
fill: parent
}
spacing: 1
Repeater {
model: Tags.keys
delegate: Column {
id: baseCol
required property var modelData
spacing: 3
Rectangle {
property var tag: Tags.tags[baseCol.modelData]
width: 12
height: width
radius: width
color: tag.urgent ? Colours.c.red_b : (tag.enabled) ? Colours.c.yellow_b : Colours.c.black
Behavior on color {
ColorAnimation {
duration: 300
}
}
border {
width: 1
color: tag.urgent ? Colours.c.red_b : Colours.c.yellow_b
Behavior on color {
ColorAnimation {
duration: 300
}
}
}
}
Rectangle {
property var tag: Tags.tags[baseCol.modelData]
anchors.horizontalCenter: parent.horizontalCenter
width: 1
height: 1
color: tag.urgent ? Colours.c.red_b : (tag.occupied) ? Colours.c.yellow_b : Colours.c.black
Behavior on color {
ColorAnimation {
duration: 300
}
}
}
}
}
}
color: "transparent"
} // inner container
}// outer visible rect
exclusionMode: ExclusionMode.Ignore
WlrLayershell.layer: WlrLayer.Top
screen: modelData
}//invisible rect
}
// pops up on current monitor
Launcher.Launcher {
width: 190
}
}

View file

@ -0,0 +1,80 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
Singleton {
id: data
property var tags: {}
property var keys: []
Timer {
id: reconnectTimer
running: false
repeat: true
onTriggered: () => sock.connected = true
interval: 5000
}
Socket {
id: sock
connected: true
onConnectionStateChanged: () => {
reconnectTimer.running = sock.connected ? false : true
if (!sock.connected) {
data.tags = {}
data.keys = []
}
}
path: Quickshell.env("XDG_RUNTIME_DIR") + "/niri-tag-events.sock"
parser: SplitParser {
onRead: tag_data => {
let event = JSON.parse(tag_data);
let ensure = (d, t) => {
if (!d[t]) {
d[t] = {
occupied: false,
urgent: false,
enabled: false,
id: t
};
}
return d;
};
if (event.TagEmpty) {
data.tags = ensure(data.tags, event.TagEmpty);
data.tags[event.TagEmpty].occupied = false;
} else if (event.TagOccupied) {
data.tags = ensure(data.tags, event.TagOccupied);
data.tags[event.TagOccupied].occupied = true;
} else if (event.TagUrgent) {
data.tags = ensure(data.tags, event.TagUrgent);
data.tags[event.TagUrgent].urgent = true;
} else if (event.TagEnabled) {
data.tags = ensure(data.tags, event.TagEnabled);
data.tags[event.TagEnabled].enabled = true;
} else if (event.TagDisabled) {
data.tags = ensure(data.tags, event.TagDisabled);
data.tags[event.TagDisabled].enabled = false;
} else if (event.TagExclusive) {
data.tags = ensure(data.tags, event.TagExclusive);
for (const [k] of Object.keys(data.tags)) {
data.tags[k].enabled = false;
}
data.tags[event.TagExclusive].enabled = true;
} else if (event.TagFullState) {
data["tags"] = event.TagFullState;
for (const [k, v] of Object.entries(data.tags)) {
data.tags[k].id = k;
}
}
data.keys = Object.keys(data.tags);
data.tagsChanged();
data.keysChanged();
}
}
}
}

View file

@ -0,0 +1,52 @@
pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Io
Singleton {
id: data
property var currentWindow: " "
property var currentId: 0
property var windows: {}
Process {
id: proc
command: ["niri", "msg", "-j", "event-stream"]
running: true
stdout: SplitParser {
onRead: niri_data => {
var event = JSON.parse(niri_data);
if (event.WindowsChanged) {
let new_data = event.WindowsChanged.windows.reduce((acc, el) => {
acc[el.id] = el.title == "" ? el.app_id : el.title;
return acc;
}, {});
data.windows = new_data;
let focused = event.WindowsChanged.windows.find(w => w.is_focused);
if (focused === undefined) {
data.currentId = 0;
}
} else if (event.WindowOpenedOrChanged) {
let target = event.WindowOpenedOrChanged.window;
data.windows[target.id] = target.title == "" ? target.app_id : target.title;
if (target.is_focused) {
data.currentId = target.id;
}
} else if (event.WindowClosed) {
delete data[event.WindowClosed.id];
if (Object.keys(data).length == 0) {
data.currentId = 0;
}
} else if (event.WindowFocusChanged) {
if (event.WindowFocusChanged.id === null) {
data.currentId = 0;
} else {
data.currentId = event.WindowFocusChanged.id;
}
}
data.currentWindow = data.currentId == 0 ? " " : data.windows[data.currentId];
}
}
}
}

9
graphical/sound.nix Normal file
View file

@ -0,0 +1,9 @@
_: {
security.rtkit.enable = true;
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
};
}

17
graphical/udisks.nix Normal file
View file

@ -0,0 +1,17 @@
{ lib, pkgs, ... }:
{
services.udisks2.enable = true;
hm.services.udiskie.enable = true;
# fix reliance on nonexistent graphical-session-pre.target
hm.systemd.user.services.udiskie = lib.mkForce {
Unit = {
Description = "udiskie mount daemon";
After = [ ];
PartOf = [ "graphical-session.target" ];
};
Service.ExecStart = [ "${pkgs.udiskie}/bin/udiskie --appindicator" ];
Install.WantedBy = [ "graphical-session.target" ];
};
}

14
graphical/webapps.nix Normal file
View file

@ -0,0 +1,14 @@
{ config, ... }:
{
imports = [
./webapps/firefox-webapp.nix
];
hm.programs.firefox.webapps = {
"Open-WebUI" = {
url = "http://127.0.0.1:8088";
extraSettings = config.hm.programs.firefox.profiles.default.settings;
name = "Open-WebUI";
icon = ../assets/openwebui.png;
};
};
}

View file

@ -0,0 +1,260 @@
{
config,
lib,
...
}:
let
inherit (builtins) getAttr stringLength substring;
inherit (lib)
mkOption
getExe
listToAttrs
attrsToList
imap
;
inherit (lib.attrsets)
mapAttrs
mapAttrs'
nameValuePair
;
inherit (lib.strings) concatStringsSep toUpper;
enumerate =
a:
listToAttrs (
imap (
id:
{
name,
value,
}:
{
inherit name;
value = value // {
inherit id;
};
}
) (attrsToList a)
);
make-app-profiles =
cfg:
mapAttrs' (
name: cfg:
nameValuePair "home-manager-webapp-${name}" {
inherit (cfg) id;
userChrome = ''
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
browser {
margin-right: 0px; margin-bottom: 0px;
}
#TabsToolbar {
visibility: collapse !important;
}
#nav-bar {
margin-top: 0;
margin-bottom: -42px;
z-index: -100;
}
#main-window[windowtype="navigator:browser"] {
background-color: transparent !important;
}
.tab-background[selected="true"] {
background: ${cfg.backgroundColor} !important;
}
'';
settings = cfg.extraSettings // {
"browser.startup.homepage" = builtins.toString cfg.url;
"browser.sessionstore.resume_session_once" = false;
"browser.sessionstore.resume_from_crash" = false;
"browser.cache.disk.enable" = false;
"browser.cache.disk.capacity" = 0;
"browser.cache.disk.filesystem_reported" = 1;
"browser.cache.disk.smart_size.enabled" = false;
"browser.cache.disk.smart_size.first_run" = false;
"browser.cache.disk.smart_size.use_old_max" = false;
"browser.ctrlTab.previews" = true;
"browser.tabs.warnOnClose" = false;
"plugin.state.flash" = 2;
"toolkit.legacyUserProfileCustomizations.stylesheets" = true;
"browser.tabs.drawInTitlebar" = false;
"browser.tabs.inTitlebar" = 0;
"browser.contentblocking.category" = "strict";
"network.cookie.lifetimePolicy" = 0;
"layout.css.prefers-color-scheme.content-override" = getAttr cfg.theme {
dark = 0;
light = 1;
system = 2;
};
};
}
) cfg;
in
{
config.hm.options.programs.firefox.webapps = mkOption {
default = { };
type =
with lib.types;
attrsOf (submodule {
options = {
####################
# Firefox settings #
####################
url = mkOption {
type = str;
description = "The URL of the webapp to launch.";
};
# id = mkOption {
# type = int;
# description = "The Firefox profile ID to set.";
# };
hidden = mkOption {
type = bool;
description = "Hide this webapp from the list of applications (but still generate a .desktop file).";
default = false;
};
extraArgs = mkOption {
type = listOf str;
default = [ ];
description = "Extra args to launch Firefox with.";
};
extraSettings = mkOption {
type = attrsOf (either bool (either int str));
default = { };
description = "Additional Firefox profile settings.";
};
backgroundColor = mkOption {
type = str;
default = "rgba(0, 0, 0, 0)";
description = "The background color to use for loading pages.";
};
theme = mkOption {
type = enum [
"dark"
"light"
"system"
];
default = "system";
description = "The application CSS theme to use, if supported.";
};
#########################
# Desktop file settings #
#########################
# Copied from xdg.desktopEntries, with slight modification for default settings
name = mkOption {
type = nullOr str;
default = null;
description = "Specific name of the application. Defaults to the capitalized attribute name.";
};
mimeType = mkOption {
description = "The MIME type(s) supported by this application.";
type = nullOr (listOf str);
default = [
"text/html"
"text/xml"
"application/xhtml_xml"
];
};
# Copied verbatim from xdg.desktopEntries.
genericName = mkOption {
type = nullOr str;
default = null;
description = "Generic name of the application.";
};
comment = mkOption {
type = nullOr str;
default = null;
description = "Tooltip for the entry.";
};
categories = mkOption {
type = nullOr (listOf str);
default = null;
description = "Categories in which the entry should be shown in a menu.";
};
icon = mkOption {
type = nullOr (either str path);
default = null;
description = "Icon to display in file manager, menus, etc.";
};
prefersNonDefaultGPU = mkOption {
type = nullOr bool;
default = null;
description = ''
If true, the application prefers to be run on a more
powerful discrete GPU if available.
'';
};
};
});
description = "Websites to create special site-specific Firefox instances for.";
};
config.hm.config = {
programs.firefox.profiles = make-app-profiles (enumerate config.hm.programs.firefox.webapps);
xdg.desktopEntries = mapAttrs (name: cfg: {
inherit (cfg)
genericName
comment
categories
icon
mimeType
prefersNonDefaultGPU
;
name =
if cfg.name == null then
(toUpper (substring 0 1 name)) + (substring 1 (stringLength name) name)
else
cfg.name;
startupNotify = true;
terminal = false;
type = "Application";
exec = concatStringsSep " " (
[
"${getExe config.hm.programs.firefox.package}"
"--name"
"${name}"
"--app-id"
"${name}"
"--class"
"${name}"
"-P"
"${config.hm.programs.firefox.profiles."home-manager-webapp-${name}".path}"
"--no-remote"
]
++ cfg.extraArgs
++ [ "${cfg.url}" ]
);
settings = {
X-MultipleArgs = "false"; # Consider enabling, don't know what this does
StartupWMClass = "${name}";
NoDisplay = lib.boolToString cfg.hidden;
};
}) config.hm.programs.firefox.webapps;
};
}

View file

@ -0,0 +1,252 @@
{
config,
pkgs,
lib,
...
}:
let
inherit (lib)
mkOption
mkForce
getExe
getExe'
listToAttrs
flatten
mapAttrsToList
mapAttrs
mapAttrs'
nameValuePair
toLower
replaceStrings
concatMapStringsSep
;
partOf = cfg: "${replaceStrings [ " " ] [ "-" ] (toLower cfg.name)}.target";
# make a firefox webapp + hidden .desktop entry for the client app
make-firefox =
cfg:
mapAttrs' (
name: cfg:
nameValuePair "${name}-client" {
inherit (cfg) name;
url = "http://127.0.0.1:${builtins.toString cfg.port}";
extraSettings = config.hm.programs.firefox.profiles.default.settings;
hidden = true;
}
) cfg;
# make a systemd service for running the frontend
make-systemd-service =
cfg:
mapAttrs' (
name: cfg:
if (cfg.service != null) then
nameValuePair "${cfg.name}-frontend" {
Unit = {
Description = "${cfg.name} Frontend";
WantedBy = mkForce [ ];
};
Service = cfg.service;
}
else
nameValuePair "" { }
) cfg;
# modify systemd units to be PartOf this target
modify-systemd-services =
cfg:
listToAttrs (
flatten (
mapAttrsToList (
name: cfg:
(map (req: {
name = "${req}";
value = {
Unit = {
PartOf = partOf cfg;
};
};
}) cfg.requires.services)
) cfg
)
);
modify-quadlets =
cfg:
listToAttrs (
flatten (
mapAttrsToList (
name: cfg:
(map (req: {
name = "${req}";
value = {
extraConfig.Unit.PartOf = partOf cfg;
};
}) cfg.requires.containers)
) cfg
)
);
# make a systemd target to collate dependencies
make-systemd-target =
cfg:
mapAttrs (name: cfg: {
Unit = {
Description = "${cfg.name} Target";
WantedBy = mkForce [ ];
Requires =
(map (req: req + ".service") cfg.requires.services)
++ (map (req: "podman-" + req + ".service") cfg.requires.containers);
};
}) cfg;
# make desktop shortcuts and a script which will handle starting everything
make-xdg =
cfg:
mapAttrs (name: cfg: {
inherit (cfg) name icon genericName;
type = "Application";
exec = "${
let
notify-send = "${getExe' pkgs.libnotify "notify-send"} -a \"${cfg.name}\"";
systemctl = "${getExe' pkgs.systemd "systemctl"}";
dex = "${getExe pkgs.dex}";
podman = "${getExe pkgs.podman}";
makeContainerCheck =
container: ''[ "$(${podman} inspect -f {{.State.Health.Status}} ${container})" == "healthy" ]'';
# makeContainerCheck = container: ''
# [ ${podman} inspect -f {{.State.Status}} ${container})" != "running" ]
# '';
containerChecks =
if (cfg.requires.containers != [ ]) then
''
container_checks() {
if ''
+ (concatMapStringsSep " && " makeContainerCheck cfg.requires.containers)
+ ''
; then
return 0
else
return 1
fi
}
''
else
''
container_checks() {
return 0
}
'';
in
pkgs.writeShellScript "${name}" ''
set -euo pipefail
exit_error() {
${notify-send} -w "Failure" $1
exit 1
}
${containerChecks}
${notify-send} "Launching ${name} backend.." "Please be patient."
${systemctl} --user start ${name}.target || exit_error "Failed to launch!"
checks=0
until container_checks; do
sleep 2
checks=$((checks+1))
if [ $((checks%10)) -eq 0 ]; then
${notify-send} "Waiting for backend."
fi
if [ $checks -ge 60 ]; then
${systemctl} --no-block --user stop ${name}.target
exit_error "Failed to launch!"
fi
done
${notify-send} "Launching ${name}.."
${dex} -w ~/.nix-profile/share/applications/${name}-client.desktop
${notify-send} "Goodbye" "Shutting down."
${systemctl} --user stop ${name}.target
exit 0
''
}";
}) cfg;
cfg = config.hm.localWebApps;
in
{
config.hm.options.localWebApps = mkOption {
default = { };
type =
with lib.types;
attrsOf (submodule {
options = {
name = mkOption {
type = str;
description = "Display name of the webapp.";
};
genericName = mkOption {
type = nullOr str;
description = "Generic name of the webapp.";
default = null;
};
icon = mkOption {
type = nullOr (either str path);
description = "Path to a file to use for application icon.";
default = null;
};
requires = mkOption {
type = nullOr (submodule {
options = {
containers = mkOption {
type = listOf str;
default = [ ];
};
services = mkOption {
type = listOf str;
default = [ ];
};
};
});
default = null;
description = "Containers or services this app requires.";
};
service = mkOption {
type = nullOr (submodule {
options = {
execStartPre = mkOption {
type = nullOr str;
default = null;
};
execStart = mkOption {
type = nullOr str;
default = null;
};
execStop = mkOption {
type = nullOr str;
default = null;
};
};
});
default = null;
description = "Submodule containing exec[StartPre/Start/Stop] commands for any required systemd service";
};
port = mkOption {
type = int;
description = "Local port the webapp should host on.";
};
};
});
};
config.hm.config = {
programs.firefox.webapps = make-firefox cfg;
systemd.user.targets = make-systemd-target cfg;
systemd.user.services = (make-systemd-service cfg) // (modify-systemd-services cfg);
services.podman.containers = modify-quadlets cfg;
xdg.desktopEntries = make-xdg cfg;
};
}

88
graphical/wm.nix Normal file
View file

@ -0,0 +1,88 @@
{
pkgs,
mainUser,
inputs,
config,
...
}:
let
inherit (config) rice;
in
{
imports = [
inputs.niri.nixosModules.niri
inputs.niri-tag.nixosModules.niri-tag
];
hm.programs.niri.settings = {
input = {
warp-mouse-to-focus.enable = true;
};
cursor = {
hide-after-inactive-ms = 5000;
hide-when-typing = true;
size = 16;
theme = rice.cursor.name;
};
layout = {
always-center-single-column = true;
gaps = 24;
default-column-width.proportion = 0.5;
preset-column-widths = map (p: { proportion = p; }) [
(2.0 / 3.0)
0.5
(1.0 / 3.0)
];
focus-ring =
let
pal = rice.palette.hex;
in
{
active = {
color = pal.bright.yellow;
};
inactive = {
color = pal.normal.black;
};
};
};
prefer-no-csd = true;
hotkey-overlay.skip-at-startup = true;
window-rules =
let
v = 10.0;
in
[
{
geometry-corner-radius = {
bottom-left = v;
bottom-right = 0.0;
top-left = 0.0;
top-right = v;
};
clip-to-geometry = true;
}
];
};
services.greetd = {
enable = true;
restart = false;
settings =
let
session = {
command = "niri-session";
user = "${mainUser}";
};
in
{
default_session = session;
initial_session = session;
};
};
programs.niri = {
enable = true;
package = inputs.niri.packages.${pkgs.system}.niri-unstable;
};
services.niri-tag.enable = true;
}