collate recent changes back to master

This commit is contained in:
atagen 2025-06-18 01:16:11 +10:00
commit 92ed39cb83
258 changed files with 5169 additions and 16379 deletions

71
home/util/ez.nix Normal file
View file

@ -0,0 +1,71 @@
{
config,
lib,
...
}:
{
options = {
ezServices =
with lib;
mkOption {
type = with types; attrsOf str;
default = { };
};
ezOneShots =
with lib;
mkOption {
type = with types; attrsOf str;
default = { };
};
ezConf =
with lib;
mkOption {
type = with types; attrsOf path;
default = { };
};
# for specifying an additional systemd target
extraTarget =
with lib;
mkOption {
type = with types; listOf str;
default = [ ];
};
};
config = {
systemd.user.services =
builtins.mapAttrs (name: cmd: {
Unit = {
Description = "${name}";
Requires = [ "graphical-session.target" ] ++ config.extraTarget;
After = [ "graphical-session.target" ] ++ config.extraTarget;
};
Service = {
ExecStart = cmd;
};
Install = {
WantedBy = [ "graphical-session.target" ] ++ config.extraTarget;
};
}) config.ezServices
// 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.ezOneShots;
xdg.configFile = builtins.mapAttrs (_name: value: {
enable = true;
source = value;
}) config.ezConf;
};
}

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
{
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 = {
programs.firefox.profiles = make-app-profiles (enumerate config.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.programs.firefox.package}"
"--name"
"${name}"
"--app-id"
"${name}"
"--class"
"${name}"
"-P"
"${config.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.programs.firefox.webapps;
};
}

252
home/util/local-webapp.nix Normal file
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.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.localWebApps;
in
{
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 = {
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;
};
}

10
home/util/name.nix Normal file
View file

@ -0,0 +1,10 @@
{
mainUser,
...
}:
{
config.home = {
username = mainUser;
homeDirectory = "/home/${mainUser}";
};
}