nushell, helium + pwas, niri session management

This commit is contained in:
atagen 2026-03-03 00:26:21 +11:00
parent cb72b47661
commit 3b3bfb6b39
21 changed files with 816 additions and 464 deletions

View file

@ -1,19 +1,30 @@
{ ... }:
{ pkgs, ... }:
{
user.packages = [
];
# 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;
# };
# };
programs.pwas =
let
papirusIcon = app: "${pkgs.papirus-icon-theme}/share/icons/Papirus-Dark/16x16/apps/${app}.svg";
in
{
cinny = {
name = "Cinny";
url = "https://chat.lobotomise.me";
icon = papirusIcon "in.cinny.Cinny";
description = "Cinny, a Matrix client";
};
discord = {
name = "Discord";
url = "https://discord.com/app";
icon = papirusIcon "discord";
description = "Discord Web";
};
fb-messenger = {
name = "FB Messenger";
url = "https://m.me";
icon = papirusIcon "facebook-facebook.com.svg";
description = "Facebook Messenger";
};
};
xdg.mime.defaultApplications = {
"x-scheme-handler/matrix" = "Cinny-webapp.desktop";
};
}

View file

@ -57,10 +57,10 @@ spawn-at-startup "systemctl" "--user" "start" "startup-sound.service"
window-rule {
match app-id="Bitwarden"
open-floating true
default-column-width { proportion 0.16667; }
default-column-width { proportion 0.2; }
}
window-rule {
match app-id="feishin"
match app-id="chrome-listen.lobotomise.me__-Default"
open-floating true
default-column-width { proportion 0.16667; }
default-column-width { proportion 0.2; }
}

View file

@ -17,14 +17,58 @@ in
inherit (pkgs) wl-clipboard quickshell;
};
imports = [ inputs.stasis.nixosModules.default ];
services.stasis.enable = true;
imports = [
inputs.stasis.nixosModules.default
inputs.stash.nixosModules.default
];
services.stasis = {
enable = true;
extraPathPackages = [ (config.programs.niri.package) ];
extraConfig = ''
default:
dpms_off:
timeout 300
command "niri msg action power-off-monitors"
end
suspend:
timeout 600
command "systemctl suspend"
end
end
'';
};
services.stash-clipboard = {
enable = true;
excludedApps = [ "Bitwarden" ];
};
quick.services = {
noti = "${getExe pkgs.swaynotificationcenter}";
shell = "${getExe pkgs.quickshell}";
pwManager = "${getExe config.apps.passwordManager}";
music = "${getExe config.apps.streamPlayer}";
# music = "${getExe config.apps.streamPlayer}";
};
user.systemd.services.music = {
environment.PATH = lib.mkForce "/run/current-system/sw/bin:/run/current-system/sw/sbin:/etc/profiles/per-user/${mainUser}/bin:/etc/profiles/per-user/${mainUser}/sbin";
unitConfig = {
Description = "airdrome";
Requires = [
"graphical-session.target"
];
After = [
"graphical-session.target"
"niri.target"
];
PartOf = [ "graphical-session.target" ];
};
serviceConfig = {
ExecStart = "${getExe config.apps.streamPlayer}";
Type = "forking";
};
wantedBy = [ "graphical-session.target" ];
};
environment.files."/home/${mainUser}/.config/quickshell" = {

View file

@ -3,6 +3,7 @@
inputs,
getFlakePkg',
lib,
pkgs,
config,
...
}:
@ -61,7 +62,15 @@ in
settings =
let
session = {
command = "niri-session";
command =
let
niri-session-direct = pkgs.writeShellScript "niri-session-direct" ''
systemctl --user import-environment
dbus-update-activation-environment --all
exec ${lib.getExe niri} --session
'';
in
"${niri-session-direct}";
user = "${mainUser}";
};
in
@ -78,13 +87,18 @@ in
services.niri-tag = {
enable = true;
prepopulate = 10;
strict = true;
scratchpads = {
# TODO FIXME these could use the getExe ?
"feishin" = 99;
"chrome-listen.lobotomise.me__-Default" = 99;
"Bitwarden" = 101;
};
};
services.niri-s76-bridge.enable = true;
# niri runs directly from greetd (session-1.scope), not as a user service
systemd.user.services.niri.wantedBy = lib.mkForce [ ];
systemd.user.services.niri.enable = lib.mkForce false;
}

View file

@ -9,5 +9,5 @@ scope "apps" {
noteTaking = obsidian;
ebookReader = foliate;
pdfReader = zathura;
calculator = mate.mate-calc;
calculator = mate-calc;
}

View file

@ -2,7 +2,7 @@
pkgs,
...
}:
with pkgs.mate;
with pkgs;
scope "apps" {
fm = caja;
archive = engrampa;

View file

@ -15,7 +15,6 @@
};
};
extraPortals = [
pkgs.xdg-desktop-portal-gtk
pkgs.xdg-desktop-portal-gnome
];
};

View file

@ -1,23 +1,25 @@
{
pkgs,
lib,
inputs,
mainUser,
...
}:
let
claude-code =
clodTools = with pkgs; [
bash
procps
ripgrep
socat
bubblewrap
];
mkClod =
{
confDir ? null,
suffix ? null,
}:
let
version = "2.1.52";
runtimeDeps = lib.makeBinPath (
[
pkgs.procps
pkgs.ripgrep
]
++ lib.optionals pkgs.stdenv.hostPlatform.isLinux [
pkgs.bubblewrap
pkgs.socat
]
);
version = "2.1.62";
runtimeDeps = lib.makeBinPath clodTools;
patchScript = pkgs.writeScript "patch-claude-src" ''
#!${pkgs.python3}/bin/python3
@ -110,7 +112,7 @@ let
open(sys.argv[1], "wb").write(data)
'';
in
pkgs.writeShellScriptBin "claude" ''
pkgs.writeShellScriptBin "claude${lib.optionalString (suffix != null) "-${suffix}"}" ''
set -euo pipefail
export DISABLE_AUTOUPDATER=1
export DISABLE_INSTALLATION_CHECKS=1
@ -119,6 +121,7 @@ let
CACHE="''${XDG_CACHE_HOME:-$HOME/.cache}/claude-code"
BIN="$CACHE/claude-${version}"
${lib.optionalString (confDir != null) "export CLAUDE_CONFIG_DIR=\"$HOME/${confDir}\""}
if [ ! -x "$BIN" ]; then
mkdir -p "$CACHE"
@ -132,12 +135,48 @@ let
exec "$BIN" "$@"
'';
claude-code = mkClod { };
claude-koss = mkClod {
suffix = "koss";
confDir = ".clod-koss";
};
in
(scope "apps.slopcode" <| claude-code)
(scope "apps" {
"slop" = claude-code;
"temp-slop" = claude-koss;
})
// {
# required for loaderslop
# required for loader
programs.nix-ld = {
enable = true;
libraries = [ pkgs.stdenv.cc.cc.lib ];
};
# experiment with our own sandboxing
# security.yoke.wrappers =
# let
# basePaths = [
# "wrx=/home/${mainUser}/.claude.json:/home/${mainUser}/.claude-code:/home/${mainUser}/.cache/claude-code:$PWD/.claude"
# "rx=/"
# ];
# base = {
# package = claude-code;
# executable = "claude";
# retainEnv = true;
# unrestrictTcp = true;
# extraPackages = clodTools;
# };
# in
# {
# clod-cuck = base // {
# pathRules = basePaths + [ "rx=$PWD" ];
# };
# clod = base // {
# pathRules = basePaths ++ [
# "wrx=/home/${mainUser}"
# ];
# addPwd = true;
# unrestrictSockets = true;
# unrestrictSignals = true;
# };
# };
}

View file

@ -1,12 +1,27 @@
{
config,
pkgs,
...
}:
with pkgs;
scope "apps" {
(scope "apps" {
videoPlayer = mpv;
imageViewer = imv;
musicPlayer = resonance;
streamPlayer = feishin;
streamPlayer = config.programs.pwas.airdrome.package;
soulSeek = nicotine-plus;
}
})
// (scope "programs.pwas.airdrome" {
name = "Airdrome";
url = "https://listen.lobotomise.me";
icon = builtins.fetchurl {
name = "airdrome.svg";
url = "https://raw.githubusercontent.com/JPGuillemin/Airdrome/refs/heads/master/public/icon.svg";
sha256 = "sha256:1chmza1cbfg028ilz4dqg583s3121iw4fhc136v9f0zf44h76y7m";
};
description = "Airdrome, a Navidrome client";
categories = [
"Music"
"Network"
];
})

View file

@ -36,6 +36,7 @@ scope "options.quick" {
};
serviceConfig = {
ExecStart = cmd;
Restart = "always";
};
wantedBy = [ "graphical-session.target" ];
}) config.quick.services

View file

@ -14,6 +14,7 @@ scope "user.systemd.services.startup-sound"
"graphical-session.target"
"niri.target"
"sound.target"
"shell.service"
];
PartOf = [ "graphical-session.target" ];
};

View file

@ -5,13 +5,36 @@
...
}:
let
inherit (lib) mkIf;
browser = lib.getExe config.apps.browser;
inherit (lib)
mkOption
mkAliasOptionModule
types
mapAttrsToList
attrNames
filterAttrs
replaceStrings
getExe
toLower
;
inherit (types)
str
strMatching
package
path
nullOr
listOf
attrsOf
submodule
either
;
browser = getExe config.apps.browser;
webAppLauncher = pkgs.writeShellScript "web-app-launcher" ''
browser="${browser}"
browser_exec=""
for path in ~/.local ~/.nix-profile /usr; do
for path in ~/.local ~/.nix-profile /usr /nix/var/nix/profiles/system/etc/profiles/per-user/$USER; do
if [ -f "$path/share/applications/$browser.desktop" ]; then
browser_exec=$(sed -n 's/^Exec=\([^ ]*\).*/\1/p' "$path/share/applications/$browser.desktop" 2>/dev/null | head -1)
break
@ -45,88 +68,112 @@ let
'';
# Create web app package
createWebApp =
mkWebApp =
{
name,
url,
icon,
description ? "",
categories ? [
"Network"
"Chat"
"InstantMessaging"
],
description,
categories,
}:
let
pkgName = "${lib.replaceStrings [ " " ] [ "-" ] (lib.toLower name)}-web-app";
cleanName = replaceStrings [ " " "\n" "\t" ] [ "-" "-" "-" ] name |> toLower;
pname = "${cleanName}-webapp";
in
{
name = pkgName;
value = pkgs.stdenv.mkDerivation {
pname = pkgName;
version = "1.0";
dontUnpack = true;
pkgs.stdenv.mkDerivation {
inherit pname;
version = "1.0";
dontUnpack = true;
nativeBuildInputs = [
pkgs.copyDesktopItems
pkgs.makeWrapper
];
nativeBuildInputs = [
pkgs.copyDesktopItems
pkgs.makeWrapper
];
installPhase = ''
runHook preInstall
makeWrapper ${webAppLauncher} $out/bin/${pkgName} \
--add-flags "${url}" \
--add-flags "${pkgName}"
runHook postInstall
'';
installPhase = ''
runHook preInstall
makeWrapper ${webAppLauncher} $out/bin/${pname} \
--add-flags "${url}" \
--add-flags "${pname}"
runHook postInstall
'';
desktopItems = [
(pkgs.makeDesktopItem {
inherit icon categories;
name = pkgName;
exec = "${pkgName} %U";
desktopItems = [
(pkgs.makeDesktopItem (
{
name = pname;
exec = "${pname} %U";
desktopName = name;
comment = description;
startupNotify = true;
startupWMClass = pkgName;
})
];
startupWMClass = pname;
}
// filterAttrs (_: v: v != null) {
inherit icon categories;
comment = description;
}
))
];
meta.mainProgram = pname;
};
innerOpts = {
options = {
name = mkOption {
type = str;
};
url = mkOption {
type = strMatching "[hH][tT][tT][pP][sS]?://[^\n\r[:space:]]+";
};
icon = mkOption {
type = nullOr (either str path);
default = null;
};
description = mkOption {
type = nullOr str;
default = null;
};
categories = mkOption {
type = nullOr (listOf str);
default = null;
};
};
};
apps = builtins.listToAttrs (
map createWebApp [
{
name = "Cinny";
url = "https://chat.lobotomise.me";
icon = "cinny";
description = "Cinny, a Matrix client";
}
{
name = "Discord";
url = "https://discord.com/app";
icon = "discord";
description = "Discord Web";
}
{
name = "FB Messenger";
url = "https://m.me";
icon = "facebook";
description = "Facebook Messenger";
}
]
pwaModule = submodule (
{ config, ... }:
{
imports =
let
names = innerOpts.options |> attrNames;
in
map (optName: mkAliasOptionModule [ optName ] [ "settings" optName ]) names;
options = {
settings = mkOption {
type = submodule innerOpts;
default = { };
};
package = mkOption {
type = package;
readOnly = true;
default = mkWebApp config.settings;
};
};
}
);
in
{
config = {
environment.systemPackages = builtins.attrValues apps;
xdg.mime.defaultApplications = {
"x-scheme-handler/matrix" = "cinny-web-app.desktop";
options = {
programs.pwas = mkOption {
description = "PWA Applications";
type = attrsOf pwaModule;
default = { };
};
};
environment.etc."web-apps-setup".text = ''
mkdir -p /home/*/.local/share/web-apps
'';
config = {
environment.systemPackages = mapAttrsToList (_: v: v.package) config.programs.pwas;
};
}