Compare commits
10
Commits
bf2a4cc67d
...
a2d55bf983
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2d55bf983
|
||
|
|
726681e5c2 | ||
|
|
cdbad8910c
|
||
|
|
85d5fa453a | ||
|
|
ec10527c0d | ||
|
|
e5fb44c367 | ||
|
|
66164d9671
|
||
|
|
0c69e71585
|
||
|
|
0aa5c4021e
|
||
|
|
1ca1d04e8d
|
Generated
+162
-5
@@ -21,6 +21,27 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"flake-parts": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": [
|
||||||
|
"hermes-agent",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1772408722,
|
||||||
|
"narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"flake-utils": {
|
"flake-utils": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
@@ -39,6 +60,29 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"hermes-agent": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-parts": "flake-parts",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"npm-lockfile-fix": "npm-lockfile-fix",
|
||||||
|
"pyproject-build-systems": "pyproject-build-systems",
|
||||||
|
"pyproject-nix": "pyproject-nix",
|
||||||
|
"uv2nix": "uv2nix"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1784633374,
|
||||||
|
"narHash": "sha256-kaRIIOZwlfLI3dwI5TgnyuLyG5RsuwM6jsuw3s5m5wc=",
|
||||||
|
"owner": "NousResearch",
|
||||||
|
"repo": "hermes-agent",
|
||||||
|
"rev": "64702f8f91661149128ca1a721f7a0fd4c22113b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NousResearch",
|
||||||
|
"repo": "hermes-agent",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"home-manager": {
|
"home-manager": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -103,16 +147,16 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1783703440,
|
"lastModified": 1775036866,
|
||||||
"narHash": "sha256-O3/YajjWo001VUIgD8BwaRdSNLUFe7nZ1qV5TwhRBcw=",
|
"narHash": "sha256-ZojAnPuCdy657PbTq5V0Y+AHKhZAIwSIT2cb8UgAz/U=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "8f0500b9660505dc3cb647775fe9a978a74b5283",
|
"rev": "6201e203d09599479a3b3450ed24fa81537ebc4e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"ref": "nixos-26.05",
|
"ref": "nixos-unstable",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
@@ -133,12 +177,100 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1783703440,
|
||||||
|
"narHash": "sha256-O3/YajjWo001VUIgD8BwaRdSNLUFe7nZ1qV5TwhRBcw=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "8f0500b9660505dc3cb647775fe9a978a74b5283",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-26.05",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"npm-lockfile-fix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"hermes-agent",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1775903712,
|
||||||
|
"narHash": "sha256-2GV79U6iVH4gKAPWYrxUReB0S41ty/Y3dBLquU8AlaA=",
|
||||||
|
"owner": "jeslie0",
|
||||||
|
"repo": "npm-lockfile-fix",
|
||||||
|
"rev": "c6093acb0c0548e0f9b8b3d82918823721930fe8",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "jeslie0",
|
||||||
|
"repo": "npm-lockfile-fix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pyproject-build-systems": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"hermes-agent",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"pyproject-nix": [
|
||||||
|
"hermes-agent",
|
||||||
|
"pyproject-nix"
|
||||||
|
],
|
||||||
|
"uv2nix": [
|
||||||
|
"hermes-agent",
|
||||||
|
"uv2nix"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1772555609,
|
||||||
|
"narHash": "sha256-3BA3HnUvJSbHJAlJj6XSy0Jmu7RyP2gyB/0fL7XuEDo=",
|
||||||
|
"owner": "pyproject-nix",
|
||||||
|
"repo": "build-system-pkgs",
|
||||||
|
"rev": "c37f66a953535c394244888598947679af231863",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "pyproject-nix",
|
||||||
|
"repo": "build-system-pkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pyproject-nix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"hermes-agent",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1772865871,
|
||||||
|
"narHash": "sha256-/ZTSg97aouL0SlPHaokA4r3iuH9QzHVuWPACD2CUCFY=",
|
||||||
|
"owner": "pyproject-nix",
|
||||||
|
"repo": "pyproject.nix",
|
||||||
|
"rev": "e537db02e72d553cea470976b9733581bcf5b3ed",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "pyproject-nix",
|
||||||
|
"repo": "pyproject.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"awsvpnclient": "awsvpnclient",
|
"awsvpnclient": "awsvpnclient",
|
||||||
|
"hermes-agent": "hermes-agent",
|
||||||
"home-manager": "home-manager",
|
"home-manager": "home-manager",
|
||||||
"home-manager-deck": "home-manager-deck",
|
"home-manager-deck": "home-manager-deck",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs_2",
|
||||||
"nixpkgs-deck": "nixpkgs-deck",
|
"nixpkgs-deck": "nixpkgs-deck",
|
||||||
"zen-browser": "zen-browser"
|
"zen-browser": "zen-browser"
|
||||||
}
|
}
|
||||||
@@ -158,6 +290,31 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"uv2nix": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"hermes-agent",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"pyproject-nix": [
|
||||||
|
"hermes-agent",
|
||||||
|
"pyproject-nix"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1773039484,
|
||||||
|
"narHash": "sha256-+boo33KYkJDw9KItpeEXXv8+65f7hHv/earxpcyzQ0I=",
|
||||||
|
"owner": "pyproject-nix",
|
||||||
|
"repo": "uv2nix",
|
||||||
|
"rev": "b68be7cfeacbed9a3fa38a2b5adc0cfb81d9bb1f",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "pyproject-nix",
|
||||||
|
"repo": "uv2nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"zen-browser": {
|
"zen-browser": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"home-manager": "home-manager_2",
|
"home-manager": "home-manager_2",
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
url = "github:ymatsiuk/awsvpnclient";
|
url = "github:ymatsiuk/awsvpnclient";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
hermes-agent.url = "github:NousResearch/hermes-agent";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs =
|
outputs =
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
../../modules/nixos/steam
|
../../modules/nixos/steam
|
||||||
../../modules/nixos/awsvpn
|
../../modules/nixos/awsvpn
|
||||||
../../modules/nixos/tailscale
|
../../modules/nixos/tailscale
|
||||||
|
../../modules/nixos/wake-on-lan
|
||||||
|
../../modules/nixos/hermes
|
||||||
];
|
];
|
||||||
|
|
||||||
# Bootloader
|
# Bootloader
|
||||||
@@ -102,6 +104,7 @@
|
|||||||
shell = pkgs.zsh;
|
shell = pkgs.zsh;
|
||||||
openssh.authorizedKeys.keys = [
|
openssh.authorizedKeys.keys = [
|
||||||
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHtBfMUaNtpUYOi0dFsGsAv/ypw535+SouH7fn8eYDvr writterdeck"
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHtBfMUaNtpUYOi0dFsGsAv/ypw535+SouH7fn8eYDvr writterdeck"
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG8115XYzQTL/ZfAerfm2carlSsIDHN6qM/K5V+AQ0St Generated By Termius"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -115,6 +118,8 @@
|
|||||||
hyprland.enable = true;
|
hyprland.enable = true;
|
||||||
steam.enable = true;
|
steam.enable = true;
|
||||||
tailscale.enable = true;
|
tailscale.enable = true;
|
||||||
|
wake-on-lan.enable = true;
|
||||||
|
hermes.enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
##------------------------------
|
##------------------------------
|
||||||
@@ -134,6 +139,8 @@
|
|||||||
ollama-rocm
|
ollama-rocm
|
||||||
glib
|
glib
|
||||||
gsettings-desktop-schemas
|
gsettings-desktop-schemas
|
||||||
|
zip
|
||||||
|
yarn
|
||||||
];
|
];
|
||||||
|
|
||||||
virtualisation.docker.enable = true; # docker, buildx
|
virtualisation.docker.enable = true; # docker, buildx
|
||||||
@@ -213,7 +220,7 @@
|
|||||||
# broadly on every network interface by the firewall.
|
# broadly on every network interface by the firewall.
|
||||||
services.openssh = {
|
services.openssh = {
|
||||||
enable = true;
|
enable = true;
|
||||||
openFirewall = false;
|
openFirewall = true;
|
||||||
settings = {
|
settings = {
|
||||||
PasswordAuthentication = false;
|
PasswordAuthentication = false;
|
||||||
KbdInteractiveAuthentication = false;
|
KbdInteractiveAuthentication = false;
|
||||||
|
|||||||
@@ -86,7 +86,9 @@
|
|||||||
|
|
||||||
nvim.enable = true;
|
nvim.enable = true;
|
||||||
pi.enable = true;
|
pi.enable = true;
|
||||||
tmux.enable = true;
|
tmux = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
openspec.enable = true;
|
openspec.enable = true;
|
||||||
power-profiles.enable = true;
|
power-profiles.enable = true;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# Cage intentionally has no IPC API, so status-line clients cannot query its
|
||||||
|
# active XKB group. When CAGE_XKB_STATE_FILE is set, keep that file synchronized
|
||||||
|
# with the zero-based effective layout index.
|
||||||
|
--- a/seat.c
|
||||||
|
+++ b/seat.c
|
||||||
|
@@ -12,6 +12,7 @@
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <linux/input-event-codes.h>
|
||||||
|
+#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <wayland-server-core.h>
|
||||||
|
@@ -245,8 +246,29 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
+write_xkb_layout_state(struct wlr_keyboard *keyboard)
|
||||||
|
+{
|
||||||
|
+ const char *path = getenv("CAGE_XKB_STATE_FILE");
|
||||||
|
+ if (path == NULL || path[0] == '\0' || keyboard->xkb_state == NULL) {
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ FILE *state_file = fopen(path, "w");
|
||||||
|
+ if (state_file == NULL) {
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ xkb_layout_index_t layout =
|
||||||
|
+ xkb_state_serialize_layout(keyboard->xkb_state, XKB_STATE_LAYOUT_EFFECTIVE);
|
||||||
|
+ fprintf(state_file, "%u\n", (unsigned int)layout);
|
||||||
|
+ fclose(state_file);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void
|
||||||
|
handle_modifier_event(struct wlr_keyboard *keyboard, struct cg_seat *seat)
|
||||||
|
{
|
||||||
|
+ write_xkb_layout_state(keyboard);
|
||||||
|
+
|
||||||
|
wlr_seat_set_keyboard(seat->seat, keyboard);
|
||||||
|
wlr_seat_keyboard_notify_modifiers(seat->seat, &keyboard->modifiers);
|
||||||
|
|
||||||
|
@@ -371,6 +393,8 @@
|
||||||
|
wl_signal_add(&cg_group->wlr_group->keyboard.events.modifiers, &cg_group->modifiers);
|
||||||
|
cg_group->modifiers.notify = handle_keyboard_group_modifiers;
|
||||||
|
|
||||||
|
+ write_xkb_layout_state(&cg_group->wlr_group->keyboard);
|
||||||
|
+
|
||||||
|
return;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
+71
-12
@@ -1,25 +1,52 @@
|
|||||||
{
|
{ config
|
||||||
config,
|
, pkgs
|
||||||
pkgs,
|
, lib
|
||||||
lib,
|
, ...
|
||||||
...
|
|
||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
mainFont = "Berkeley Mono Nerd Font Mono";
|
mainFont = "Berkeley Mono Nerd Font Mono";
|
||||||
fontSize = 14;
|
fontSize = 14;
|
||||||
|
|
||||||
|
# Cage has no IPC for querying its active XKB group. This deck-only build
|
||||||
|
# writes that group to CAGE_XKB_STATE_FILE for the tmux indicator below.
|
||||||
|
cageWithLayoutState = pkgs.cage.overrideAttrs (oldAttrs: {
|
||||||
|
patches = (oldAttrs.patches or [ ]) ++ [ ./cage-xkb-state.patch ];
|
||||||
|
});
|
||||||
|
|
||||||
|
keyboardLayout = pkgs.writeShellApplication {
|
||||||
|
name = "keyboard-layout";
|
||||||
|
runtimeInputs = [ pkgs.coreutils ];
|
||||||
|
text = ''
|
||||||
|
runtime_dir="''${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
|
||||||
|
state_file="''${CAGE_XKB_STATE_FILE:-$runtime_dir/writter-deck-keyboard-layout}"
|
||||||
|
layout=0
|
||||||
|
|
||||||
|
if [[ -r "$state_file" ]]; then
|
||||||
|
IFS= read -r layout < "$state_file" || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$layout" in
|
||||||
|
0) printf 'US' ;;
|
||||||
|
1) printf 'PT' ;;
|
||||||
|
*) printf '?' ;;
|
||||||
|
esac
|
||||||
|
'';
|
||||||
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
../../modules/home-manager/oh-my-posh
|
../../modules/home-manager/oh-my-posh
|
||||||
../../modules/home-manager/zshrc
|
../../modules/home-manager/zshrc
|
||||||
../../modules/home-manager/bat
|
../../modules/home-manager/bat
|
||||||
|
../../modules/home-manager/cedilla
|
||||||
../../modules/home-manager/opencode
|
../../modules/home-manager/opencode
|
||||||
../../modules/home-manager/nvim
|
../../modules/home-manager/nvim
|
||||||
../../modules/home-manager/pi
|
../../modules/home-manager/pi
|
||||||
../../modules/home-manager/tmux
|
../../modules/home-manager/tmux
|
||||||
../../modules/home-manager/eza
|
../../modules/home-manager/eza
|
||||||
../../modules/home-manager/zoxide
|
../../modules/home-manager/zoxide
|
||||||
|
../../modules/home-manager/tailscale-daemon
|
||||||
];
|
];
|
||||||
|
|
||||||
home = {
|
home = {
|
||||||
@@ -38,27 +65,48 @@ in
|
|||||||
enable = true;
|
enable = true;
|
||||||
fastfetch = false;
|
fastfetch = false;
|
||||||
};
|
};
|
||||||
tmux.enable = true;
|
tmux = {
|
||||||
|
enable = true;
|
||||||
|
extraConfig = ''
|
||||||
|
# Cage does not expose the XKB group through Wayland. The deck-only
|
||||||
|
# Cage patch updates the state file consumed by this status segment.
|
||||||
|
set -g status-interval 1
|
||||||
|
set -ag status-right "#[fg=#89b4fa] KBD:#(${keyboardLayout}/bin/keyboard-layout)"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
cedilla = {
|
||||||
|
enable = true;
|
||||||
|
# Foot/libxkbcommon handles ~/.XCompose directly; the deck has no NixOS
|
||||||
|
# fcitx5 service at /run/current-system/sw.
|
||||||
|
startFcitx5 = false;
|
||||||
|
};
|
||||||
oh-my-posh.enable = true;
|
oh-my-posh.enable = true;
|
||||||
pi.enable = true;
|
pi.enable = true;
|
||||||
nvim.enable = true;
|
nvim.enable = true;
|
||||||
eza.enable = true;
|
eza.enable = true;
|
||||||
bat.enable = true;
|
bat.enable = true;
|
||||||
zoxide.enable = true;
|
zoxide.enable = true;
|
||||||
|
tailscale-daemon = {
|
||||||
|
enable = true;
|
||||||
|
acceptRoutes = true;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
home.packages = with pkgs; [
|
home.packages = with pkgs; [
|
||||||
cage
|
cageWithLayoutState
|
||||||
|
keyboardLayout
|
||||||
foot
|
foot
|
||||||
|
wlr-randr
|
||||||
git
|
git
|
||||||
ripgrep
|
ripgrep
|
||||||
fd
|
fd
|
||||||
mosh
|
mosh
|
||||||
tailscale
|
|
||||||
fastfetch
|
fastfetch
|
||||||
|
lazygit
|
||||||
stow
|
stow
|
||||||
tmux
|
bluetuith
|
||||||
];
|
];
|
||||||
|
# `tailscale`/`ts` shell helpers come from the tailscale-daemon module.
|
||||||
|
|
||||||
# Non-NixOS host: fixes locales, ld paths, XDG desktop entries
|
# Non-NixOS host: fixes locales, ld paths, XDG desktop entries
|
||||||
targets.genericLinux.enable = true;
|
targets.genericLinux.enable = true;
|
||||||
@@ -122,10 +170,21 @@ in
|
|||||||
bash = {
|
bash = {
|
||||||
enable = true;
|
enable = true;
|
||||||
profileExtra = ''
|
profileExtra = ''
|
||||||
if [ -z "$WAYLAND_DISPLAY" ] && [ "$XDG_VTNR" = "1" ]; then
|
# Auto-launch cage + foot when logging in on tty1 (skip over SSH).
|
||||||
|
# If cage exits, you drop back to the login shell on tty1 instead
|
||||||
|
# of being stuck in a restart loop.
|
||||||
|
if [ "$(tty 2>/dev/null)" = "/dev/tty1" ] \
|
||||||
|
&& [ -z "$WAYLAND_DISPLAY" ] \
|
||||||
|
&& [ -z "$CAGE_RUNNING" ]; then
|
||||||
|
export CAGE_RUNNING=1
|
||||||
export WLR_RENDERER=pixman
|
export WLR_RENDERER=pixman
|
||||||
export XDG_RUNTIME_DIR=''${XDG_RUNTIME_DIR:-/run/user/$(id -u)}
|
export XDG_RUNTIME_DIR=''${XDG_RUNTIME_DIR:-/run/user/$(id -u)}
|
||||||
exec cage -- foot
|
export XKB_DEFAULT_LAYOUT="us,us"
|
||||||
|
export XKB_DEFAULT_VARIANT=",intl"
|
||||||
|
export XKB_DEFAULT_OPTIONS="grp:alt_shift_toggle"
|
||||||
|
export CAGE_XKB_STATE_FILE="$XDG_RUNTIME_DIR/writter-deck-keyboard-layout"
|
||||||
|
rm -f "$CAGE_XKB_STATE_FILE"
|
||||||
|
exec ${cageWithLayoutState}/bin/cage -- ${pkgs.foot}/bin/foot
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
@@ -160,7 +219,7 @@ in
|
|||||||
serverAliveInterval = 30;
|
serverAliveInterval = 30;
|
||||||
};
|
};
|
||||||
"pc-ts" = {
|
"pc-ts" = {
|
||||||
hostname = "jpporta-nixos";
|
hostname = "jpporta-nixos.taild23e4.ts.net";
|
||||||
user = "jpporta";
|
user = "jpporta";
|
||||||
serverAliveInterval = 30;
|
serverAliveInterval = 30;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,36 +20,31 @@
|
|||||||
size = 10.3;
|
size = 10.3;
|
||||||
normal = {
|
normal = {
|
||||||
family = "BerkeleyMono Nerd Font Mono";
|
family = "BerkeleyMono Nerd Font Mono";
|
||||||
|
style = "Regular";
|
||||||
};
|
};
|
||||||
bold = {
|
bold = {
|
||||||
family = "BerkeleyMono Nerd Font Mono";
|
family = "BerkeleyMono Nerd Font Mono";
|
||||||
|
style = "ExtraBold";
|
||||||
};
|
};
|
||||||
italic = {
|
italic = {
|
||||||
family = "BerkeleyMono Nerd Font Mono";
|
family = "BerkeleyMono Nerd Font Mono";
|
||||||
|
style = "Oblique";
|
||||||
};
|
};
|
||||||
bold_italic = {
|
bold_italic = {
|
||||||
family = "BerkeleyMono Nerd Font Mono";
|
family = "BerkeleyMono Nerd Font Mono";
|
||||||
|
style = "ExtraBold Oblique";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
window = {
|
window = {
|
||||||
decorations = "None";
|
decorations = "None";
|
||||||
opacity = 0.8;
|
opacity = 0.8;
|
||||||
blur = true;
|
blur = false;
|
||||||
dynamic_padding = true;
|
dynamic_padding = true;
|
||||||
padding = {
|
padding = {
|
||||||
x = 10;
|
x = 10;
|
||||||
y = 16;
|
y = 16;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
keyboard = {
|
|
||||||
bindings = [
|
|
||||||
{
|
|
||||||
key = "Return";
|
|
||||||
mods = "Shift";
|
|
||||||
chars = "\u001B\r";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,16 @@
|
|||||||
{ lib, config, ... }:
|
{ lib, config, ... }:
|
||||||
{
|
{
|
||||||
|
options.custom.cedilla = {
|
||||||
|
enable = lib.mkEnableOption "enable cedilla - apply Compose configuration for cedilla";
|
||||||
|
|
||||||
options.custom = {
|
startFcitx5 = lib.mkOption {
|
||||||
cedilla.enable = lib.mkEnableOption "enable cedilla - apply Compose configuration for cedilla";
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Start the NixOS fcitx5 user service. Disable this when only the Compose
|
||||||
|
rules are needed, for example on a non-NixOS Wayland host.
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf config.custom.cedilla.enable {
|
config = lib.mkIf config.custom.cedilla.enable {
|
||||||
@@ -13,7 +21,7 @@
|
|||||||
<dead_acute> <C> : "Ç" Ccedilla
|
<dead_acute> <C> : "Ç" Ccedilla
|
||||||
'';
|
'';
|
||||||
|
|
||||||
systemd.user.services.fcitx5 = {
|
systemd.user.services.fcitx5 = lib.mkIf config.custom.cedilla.startFcitx5 {
|
||||||
Unit = {
|
Unit = {
|
||||||
Description = "Fcitx5 input method";
|
Description = "Fcitx5 input method";
|
||||||
PartOf = [ "graphical-session.target" ];
|
PartOf = [ "graphical-session.target" ];
|
||||||
|
|||||||
@@ -12,6 +12,13 @@
|
|||||||
plugin = [
|
plugin = [
|
||||||
"@dietrichgebert/ponytail"
|
"@dietrichgebert/ponytail"
|
||||||
];
|
];
|
||||||
|
mcp = {
|
||||||
|
n8n = {
|
||||||
|
type = "remote";
|
||||||
|
url = "https://n8n.joaoporta.com/mcp-server/http";
|
||||||
|
enabled = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -29,14 +29,14 @@ let
|
|||||||
}
|
}
|
||||||
|
|
||||||
listener {
|
listener {
|
||||||
timeout = 300
|
timeout = 4
|
||||||
on-timeout = loginctl lock-session
|
on-timeout = loginctl lock-session
|
||||||
}
|
}
|
||||||
|
|
||||||
listener {
|
listener {
|
||||||
timeout = 330
|
timeout = 5
|
||||||
on-timeout = hyprctl dispatch dpms off
|
on-timeout = hyprctl dispatch 'hl.dsp.dpms({ action = "off" })'
|
||||||
on-resume = hyprctl dispatch dpms on
|
on-resume = hyprctl dispatch 'hl.dsp.dpms({ action = "on" })'
|
||||||
}
|
}
|
||||||
# No suspend listener.
|
# No suspend listener.
|
||||||
'';
|
'';
|
||||||
@@ -45,7 +45,7 @@ let
|
|||||||
general {
|
general {
|
||||||
lock_cmd = pidof hyprlock || hyprlock
|
lock_cmd = pidof hyprlock || hyprlock
|
||||||
before_sleep_cmd = loginctl lock-session
|
before_sleep_cmd = loginctl lock-session
|
||||||
after_sleep_cmd = hyprctl dispatch dpms on
|
after_sleep_cmd = hyprctl dispatch 'hl.dsp.dpms({ action = "on" })'
|
||||||
}
|
}
|
||||||
|
|
||||||
listener {
|
listener {
|
||||||
@@ -55,8 +55,8 @@ let
|
|||||||
|
|
||||||
listener {
|
listener {
|
||||||
timeout = 660
|
timeout = 660
|
||||||
on-timeout = hyprctl dispatch dpms off
|
on-timeout = hyprctl dispatch 'hl.dsp.dpms({ action = "off" })'
|
||||||
on-resume = hyprctl dispatch dpms on
|
on-resume = hyprctl dispatch 'hl.dsp.dpms({ action = "on" })'
|
||||||
}
|
}
|
||||||
|
|
||||||
listener {
|
listener {
|
||||||
@@ -74,7 +74,7 @@ let
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
STATE_FILE="''${XDG_STATE_HOME:-$HOME/.local/state}/power-profile"
|
STATE_FILE="''${XDG_STATE_HOME:-$HOME/.local/state}/power-profile"
|
||||||
ACTIVE_LINK="''${XDG_CONFIG_HOME:-$HOME/.config}/hypr/hypridle.conf"
|
ACTIVE_LINK="''${XDG_CONFIG_HOME:-$HOME/.config}/hypr/hypridle-active.conf"
|
||||||
|
|
||||||
PROFILES_NORMAL="normal"
|
PROFILES_NORMAL="normal"
|
||||||
PROFILES_CAFFE="caffeinated"
|
PROFILES_CAFFE="caffeinated"
|
||||||
@@ -199,6 +199,7 @@ in
|
|||||||
home.packages = [
|
home.packages = [
|
||||||
powerProfileScript
|
powerProfileScript
|
||||||
waybarScript
|
waybarScript
|
||||||
|
pkgs.hypridle
|
||||||
];
|
];
|
||||||
|
|
||||||
# We replace the default hypridle.service with our own service that is
|
# We replace the default hypridle.service with our own service that is
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.custom.tailscale-daemon;
|
||||||
|
|
||||||
|
# The writter-deck is a non-NixOS host that can't create a real tailscale0
|
||||||
|
# interface (missing TUN drivers). Userspace networking avoids that need.
|
||||||
|
#
|
||||||
|
# Layout:
|
||||||
|
# - Socket: %t/tailscaled/tailscaled.sock (per-session tmpfs)
|
||||||
|
# - State: $HOME/.local/state/tailscale/... (persistent)
|
||||||
|
#
|
||||||
|
# In systemd specifiers:
|
||||||
|
# %t → $XDG_RUNTIME_DIR (=/run/user/$UID) — works in systemd 249+
|
||||||
|
# %S → $XDG_CONFIG_HOME (! NOT $XDG_STATE_HOME) — works in systemd 249+
|
||||||
|
#
|
||||||
|
# Because %S points at the wrong place, we hard-code the persistent state
|
||||||
|
# path under $HOME/.local/state/, which is the XDG_STATE_HOME default.
|
||||||
|
homeDir = config.home.homeDirectory;
|
||||||
|
stateDir = "${homeDir}/.local/state/tailscale";
|
||||||
|
stateFile = "${stateDir}/tailscaled.state";
|
||||||
|
|
||||||
|
# Becomes "$XDG_RUNTIME_DIR/tailscaled/tailscaled.sock" at runtime.
|
||||||
|
socketFile = "%t/tailscaled/tailscaled.sock";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.custom.tailscale-daemon = {
|
||||||
|
enable = lib.mkEnableOption ''
|
||||||
|
Tailscale daemon (tailscaled) running as a systemd user service with
|
||||||
|
userspace networking. For non-NixOS hosts without TUN device drivers.
|
||||||
|
'';
|
||||||
|
|
||||||
|
acceptRoutes = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
If true, run `tailscale up --accept-routes` automatically on first
|
||||||
|
start. Otherwise you'll need to authenticate manually once.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
acceptDns = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Whether to accept DNS settings from the coordination server.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
# Create the persistent state dir up front so the systemd service can
|
||||||
|
# write to it immediately.
|
||||||
|
home.activation.createTailscaleDirs = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
|
||||||
|
run mkdir -p ${lib.escapeShellArg stateDir}
|
||||||
|
run chmod 0700 ${lib.escapeShellArg stateDir}
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Export the socket path (systemd-249-expanded) so any tool (systray,
|
||||||
|
# scripts) picks the user-scope daemon automatically. Note: this
|
||||||
|
# contains a literal `%t` which systemd will expand when the daemon
|
||||||
|
# or its consumers run.
|
||||||
|
home.sessionVariables.TAILSCALED_SOCKET = "/run/user/$(id -u)/tailscaled/tailscaled.sock";
|
||||||
|
|
||||||
|
home.packages = [
|
||||||
|
pkgs.tailscale
|
||||||
|
|
||||||
|
# Wrapper so the CLI client can find the user-scope socket without flags.
|
||||||
|
(pkgs.writeShellApplication {
|
||||||
|
name = "ts";
|
||||||
|
runtimeInputs = [ pkgs.tailscale ];
|
||||||
|
text = ''
|
||||||
|
exec tailscale --socket="/run/user/$(id -u)/tailscaled/tailscaled.sock" "$@"
|
||||||
|
'';
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
systemd.user.services.tailscaled = {
|
||||||
|
Unit = {
|
||||||
|
Description = "Tailscale daemon (userspace networking, no TUN device)";
|
||||||
|
# No "After=default.target" — it would deadlock since default.target
|
||||||
|
# is what brings us up. Just wait for the runtime dir to exist.
|
||||||
|
RequiresMountsFor = [ "%t" ];
|
||||||
|
# Survive transient network blips / flap.
|
||||||
|
StartLimitIntervalSec = 60;
|
||||||
|
StartLimitBurst = 5;
|
||||||
|
};
|
||||||
|
Service = {
|
||||||
|
Type = "notify";
|
||||||
|
ExecStart = lib.escapeShellArgs [
|
||||||
|
"${pkgs.tailscale}/bin/tailscaled"
|
||||||
|
"--tun=userspace-networking"
|
||||||
|
"--state=${stateFile}"
|
||||||
|
"--socket=${socketFile}"
|
||||||
|
];
|
||||||
|
# systemd-managed runtime dir:
|
||||||
|
# $XDG_RUNTIME_DIR/tailscaled (RuntimeDirectory, mode 0700)
|
||||||
|
# State is in $HOME/.local/state/tailscale and managed by us, not
|
||||||
|
# via StateDirectory, so we don't need to fight protectHome.
|
||||||
|
RuntimeDirectory = "tailscaled";
|
||||||
|
RuntimeDirectoryMode = "0700";
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = 5;
|
||||||
|
# Light sandboxing. systemd user services can't drop capabilities
|
||||||
|
# the way the system unit can, so we keep restrictions to a
|
||||||
|
# conservative subset that doesn't fight journald.
|
||||||
|
PrivateTmp = true;
|
||||||
|
ProtectSystem = "strict";
|
||||||
|
ProtectHome = true;
|
||||||
|
ReadWritePaths = [ "%t/tailscaled" "${stateDir}" ];
|
||||||
|
};
|
||||||
|
Install.WantedBy = [ "default.target" "graphical-session.target" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
# One-shot: authenticate on first start if not already logged in.
|
||||||
|
# Idempotent: tailscale up is a no-op once you're logged in.
|
||||||
|
systemd.user.services.tailscale-up = {
|
||||||
|
Unit = {
|
||||||
|
Description = "Tailscale: authenticate and connect";
|
||||||
|
After = [ "tailscaled.service" ];
|
||||||
|
Requires = [ "tailscaled.service" ];
|
||||||
|
};
|
||||||
|
Service = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
ExecStart = lib.escapeShellArgs ([
|
||||||
|
"${pkgs.tailscale}/bin/tailscale"
|
||||||
|
"--socket=${socketFile}"
|
||||||
|
"up"
|
||||||
|
(if cfg.acceptRoutes then "--accept-routes" else "--no-accept-routes")
|
||||||
|
(if cfg.acceptDns then "--accept-dns=true" else "--accept-dns=false")
|
||||||
|
]);
|
||||||
|
# Don't fail the whole stack if auth isn't completed yet.
|
||||||
|
SuccessExitStatus = [ 0 1 ];
|
||||||
|
};
|
||||||
|
Install.WantedBy = [ "default.target" "graphical-session.target" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,9 +1,47 @@
|
|||||||
|
|
||||||
{ lib, config, pkgs, ... }:
|
|
||||||
{
|
{
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
options.custom = {
|
{
|
||||||
tmux.enable = lib.mkEnableOption "enable tmux - terminal multiplexer";
|
options.custom.tmux = {
|
||||||
|
enable = lib.mkEnableOption "enable tmux - terminal multiplexer";
|
||||||
|
|
||||||
|
defaultShell = lib.mkOption {
|
||||||
|
type = lib.types.package;
|
||||||
|
default = pkgs.zsh;
|
||||||
|
defaultText = lib.literalExpression "pkgs.zsh";
|
||||||
|
description = ''
|
||||||
|
Shell used by tmux for new windows and panes. Resolved to its
|
||||||
|
store path so it works the same on NixOS and non-NixOS hosts.
|
||||||
|
'';
|
||||||
|
example = lib.literalExpression "pkgs.bash";
|
||||||
|
};
|
||||||
|
|
||||||
|
defaultTerminal = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "tmux-256color";
|
||||||
|
description = "Default terminal type reported to applications inside tmux.";
|
||||||
|
};
|
||||||
|
|
||||||
|
catppuccinFlavor = lib.mkOption {
|
||||||
|
type = lib.types.enum [
|
||||||
|
"latte"
|
||||||
|
"frappe"
|
||||||
|
"macchiato"
|
||||||
|
"mocha"
|
||||||
|
];
|
||||||
|
default = "mocha";
|
||||||
|
description = "Catppuccin flavor used by the tmux theme plugin.";
|
||||||
|
};
|
||||||
|
|
||||||
|
extraConfig = lib.mkOption {
|
||||||
|
type = lib.types.lines;
|
||||||
|
default = "";
|
||||||
|
description = "Extra lines appended verbatim to ~/.config/tmux/tmux.conf.";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf config.custom.tmux.enable {
|
config = lib.mkIf config.custom.tmux.enable {
|
||||||
@@ -11,6 +49,132 @@
|
|||||||
tmux
|
tmux
|
||||||
];
|
];
|
||||||
|
|
||||||
xdg.configFile."tmux/tmux.conf".source = config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/ndots/modules/home-manager/tmux/tmux.conf";
|
xdg.configFile."tmux/tmux.conf".text = ''
|
||||||
|
##### Terminal & Color Support #####
|
||||||
|
|
||||||
|
# Kitty = truecolor
|
||||||
|
set -g default-terminal "${config.custom.tmux.defaultTerminal}"
|
||||||
|
set -ga terminal-overrides ",*:Tc"
|
||||||
|
|
||||||
|
# Key line:
|
||||||
|
set -g pane-border-style "bg=default"
|
||||||
|
set -g pane-active-border-style "bg=default"
|
||||||
|
set -g status-style "bg=default"
|
||||||
|
set -g window-style "bg=default"
|
||||||
|
set -g window-active-style "bg=default"
|
||||||
|
set -g default-shell ${config.custom.tmux.defaultShell}/bin/${
|
||||||
|
config.custom.tmux.defaultShell.meta.mainProgram or (lib.getName config.custom.tmux.defaultShell)
|
||||||
|
}
|
||||||
|
set -g extended-keys on
|
||||||
|
set -g extended-keys-format csi-u
|
||||||
|
|
||||||
|
# Reduce escape sequence delay (fixes lag in nvim/zsh)
|
||||||
|
set -sg escape-time 0
|
||||||
|
|
||||||
|
# Prefix to C-a
|
||||||
|
# set -g prefix C-a
|
||||||
|
# unbind C-b
|
||||||
|
# bind-key C-a send-prefix
|
||||||
|
|
||||||
|
# Mouse follow
|
||||||
|
set -g mouse on
|
||||||
|
|
||||||
|
# Split with better keys
|
||||||
|
# unbind %
|
||||||
|
# bind | split-window -h -c "#{pane_current_path}"
|
||||||
|
# unbind '"'
|
||||||
|
# bind - split-window -v -c "#{pane_current_path}"
|
||||||
|
|
||||||
|
# Easy config reload
|
||||||
|
unbind r
|
||||||
|
bind r source-file ~/.config/tmux/tmux.conf
|
||||||
|
|
||||||
|
# Move between panes with tmux-navigator
|
||||||
|
is_vim="ps -o state= -o comm= -t '#{pane_tty}' \
|
||||||
|
| grep -iqE '^[^TXZ ]+ +(\\S+\\/)?g?(view|l?n?vim?x?|fzf)(diff)?$'"
|
||||||
|
bind-key -n 'C-h' if-shell "$is_vim" 'send-keys M-h' 'select-pane -L'
|
||||||
|
bind-key -n 'C-j' if-shell "$is_vim" 'send-keys M-j' 'select-pane -D'
|
||||||
|
bind-key -n 'C-k' if-shell "$is_vim" 'send-keys M-k' 'select-pane -U'
|
||||||
|
bind-key -n 'C-l' if-shell "$is_vim" 'send-keys M-l' 'select-pane -R'
|
||||||
|
|
||||||
|
# No automatic window renaming
|
||||||
|
set-option -g allow-rename off
|
||||||
|
|
||||||
|
# Bell
|
||||||
|
set -g visual-activity off
|
||||||
|
set -g visual-bell off
|
||||||
|
set -g visual-silence off
|
||||||
|
setw -g monitor-activity off
|
||||||
|
set -g bell-action none
|
||||||
|
|
||||||
|
# Status bar
|
||||||
|
set -g status-position top
|
||||||
|
set -g status-justify left
|
||||||
|
|
||||||
|
# Start index on 1
|
||||||
|
set -g base-index 1
|
||||||
|
set -g pane-base-index 1
|
||||||
|
set-window-option -g pane-base-index 1
|
||||||
|
set-option -g renumber-windows on
|
||||||
|
|
||||||
|
# Resize panes
|
||||||
|
bind -r J resize-pane -D 5
|
||||||
|
bind -r K resize-pane -U 5
|
||||||
|
bind -r L resize-pane -R 5
|
||||||
|
bind -r H resize-pane -L 5
|
||||||
|
bind -r m resize-pane -Z
|
||||||
|
# Resize dragging
|
||||||
|
unbind -T copy-mode-vi MouseDragEnd1Pane # don't exit copy mode after dragging with mouse
|
||||||
|
|
||||||
|
# Vim motions
|
||||||
|
set-window-option -g mode-keys vi
|
||||||
|
|
||||||
|
# Selection
|
||||||
|
bind-key -T copy-mode-vi 'v' send -X begin-selection # start selecting text with "v"
|
||||||
|
bind-key -T copy-mode-vi 'y' send -X copy-selection # copy text with "y"
|
||||||
|
|
||||||
|
set -g @plugin "dweidner/tmux-theme"
|
||||||
|
set -g @plugin 'tmux-plugins/tmux-yank'
|
||||||
|
set -g @plugin 'tmux-plugins/tmux-cpu'
|
||||||
|
set -g @plugin 'tmux-plugins/tmux-battery'
|
||||||
|
set -g @plugin 'catppuccin/tmux#v2.1.1' # See https://github.com/catppuccin/tmux/tags for additional tags
|
||||||
|
set -g @plugin 'b0o/tmux-autoreload'
|
||||||
|
set -g @plugin 'tmux-plugins/tpm'
|
||||||
|
|
||||||
|
bind-key -T copy-mode-vi v send-keys -X begin-selection
|
||||||
|
bind-key -T copy-mode-vi C-v send-keys -X rectangle-toggle
|
||||||
|
bind-key -T copy-mode-vi y send-keys -X copy-selection-and-cancel
|
||||||
|
|
||||||
|
# Catppuccin config
|
||||||
|
set -g @catppuccin_status_background none
|
||||||
|
set -g @catppuccin_flavor '${config.custom.tmux.catppuccinFlavor}'
|
||||||
|
set -g @catppuccin_window_status_style "basic"
|
||||||
|
|
||||||
|
set -g status-right-length 100
|
||||||
|
set -g status-left ""
|
||||||
|
set -g status-left-length 200
|
||||||
|
set -g status-right "#{E:@catppuccin_status_application}"
|
||||||
|
set -agF status-right "#{E:@catppuccin_status_cpu}"
|
||||||
|
set -ag status-right "#{E:@catppuccin_status_session}"
|
||||||
|
set -ag status-right "#{E:@catppuccin_status_uptime}"
|
||||||
|
set -agF status-right "#{E:@catppuccin_status_battery}"
|
||||||
|
|
||||||
|
|
||||||
|
# Neovim delay fix
|
||||||
|
set -sg escape-time 0
|
||||||
|
|
||||||
|
# Tmux keep session
|
||||||
|
set-option -g detach-on-destroy off
|
||||||
|
|
||||||
|
# Focus events
|
||||||
|
set-option -g focus-events on
|
||||||
|
bind-key z resize-pane -Z \; if-shell "tmux list-panes -F '#F' | grep -q Z" "set -g status off" "set -g status on"
|
||||||
|
|
||||||
|
# Clipboard
|
||||||
|
set -g set-clipboard on
|
||||||
|
|
||||||
|
run '~/.tmux/plugins/tpm/tpm'
|
||||||
|
''
|
||||||
|
+ config.custom.tmux.extraConfig;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,11 +50,35 @@
|
|||||||
nix-update = "sudo nix-channel --update && sudo nixos-rebuild switch";
|
nix-update = "sudo nix-channel --update && sudo nixos-rebuild switch";
|
||||||
hms = "cd ~/ndots && git add . && home-manager switch --flake .#jpporta-deck";
|
hms = "cd ~/ndots && git add . && home-manager switch --flake .#jpporta-deck";
|
||||||
s = "sesh connect $(sesh list | fzf)";
|
s = "sesh connect $(sesh list | fzf)";
|
||||||
|
rot90 = "wlr-randr --output HDMI-A-1 --transform 90";
|
||||||
|
rot0 = "wlr-randr --output HDMI-A-1 --transform normal";
|
||||||
|
rotl = "wlr-randr --output HDMI-A-1 --transform 270";
|
||||||
|
rot180 = "wlr-randr --output HDMI-A-1 --transform 180";
|
||||||
|
# Tailscale shortcuts (writter-deck only really needs these, but
|
||||||
|
# they don't hurt anyone else either). `ts` is the wrapper that
|
||||||
|
# points the CLI at the user-scope socket from the tailscale-daemon
|
||||||
|
# module.
|
||||||
|
ts = "tailscale --socket=/tmp/ts-$UID/tailscaled.sock";
|
||||||
|
ts-status = "ts status";
|
||||||
|
ts-ping = "ts ping -c 3 jpporta-nixos";
|
||||||
};
|
};
|
||||||
|
|
||||||
initContent = lib.mkIf config.custom.zsh.fastfetch ''
|
initContent = lib.mkMerge [
|
||||||
fastfetch
|
(lib.mkOrder 700 ''
|
||||||
'';
|
# Home Manager normally defines ZSH in .zshenv. Keep it available
|
||||||
|
# when .zshrc is sourced directly as well.
|
||||||
|
export ZSH="${config.programs.zsh.oh-my-zsh.package}/share/oh-my-zsh"
|
||||||
|
'')
|
||||||
|
(lib.mkOrder 1000 ''
|
||||||
|
${lib.optionalString config.custom.zsh.fastfetch "fastfetch"}
|
||||||
|
# Auto-rotate screen to portrait when running inside cage/foot
|
||||||
|
# on the writer-deck. Skipped over SSH and other non-Wayland sessions.
|
||||||
|
if [ -n "$WAYLAND_DISPLAY" ] && [ -n "$CAGE_RUNNING" ] && [ "$ROTATED" != "1" ]; then
|
||||||
|
export ROTATED=1
|
||||||
|
wlr-randr --output HDMI-A-1 --transform 90 >/dev/null 2>&1
|
||||||
|
fi
|
||||||
|
'')
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
inputs,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
cfg = config.custom.hermes;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.custom.hermes.enable = lib.mkEnableOption "Hermes Agent CLI";
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
environment.systemPackages = [
|
||||||
|
inputs.hermes-agent.packages.${pkgs.system}.default
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.custom.wake-on-lan;
|
||||||
|
|
||||||
|
iface = "enp14s0";
|
||||||
|
|
||||||
|
mac = "d8:43:ae:5a:ae:12";
|
||||||
|
|
||||||
|
wake-jpporta-nixos = pkgs.writeShellScriptBin "wake-jpporta-nixos" ''
|
||||||
|
exec ${pkgs.wakeonlan}/bin/wakeonlan -i "''${1:-255.255.255.255}" ${mac}
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.custom.wake-on-lan = {
|
||||||
|
enable = lib.mkEnableOption "Wake-on-LAN on the wired NIC (magic-packet mode)";
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
# Keep `wol g` applied on the NIC after the link is up (boot path).
|
||||||
|
systemd.services."wol-${iface}" = {
|
||||||
|
description = "Enable Wake-on-LAN (magic packet) on ${iface}";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
wants = [
|
||||||
|
"network-online.target"
|
||||||
|
"NetworkManager.service"
|
||||||
|
];
|
||||||
|
after = [
|
||||||
|
"network-online.target"
|
||||||
|
"NetworkManager.service"
|
||||||
|
"NetworkManager-wait-online.service"
|
||||||
|
];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
ExecStart = "${pkgs.ethtool}/bin/ethtool -s ${iface} wol g";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Re-apply `wol g` after resume from suspend / hibernate so NetworkManager
|
||||||
|
# cannot silently drop the WOL flag on link renegotiation.
|
||||||
|
systemd.services."wol-${iface}-resume" = {
|
||||||
|
description = "Re-apply Wake-on-LAN (magic packet) on ${iface} after resume";
|
||||||
|
wantedBy = [
|
||||||
|
"suspend.target"
|
||||||
|
"hibernate.target"
|
||||||
|
"hybrid-sleep.target"
|
||||||
|
];
|
||||||
|
after = [
|
||||||
|
"systemd-suspend.service"
|
||||||
|
"systemd-hibernate.service"
|
||||||
|
"systemd-hybrid-sleep.service"
|
||||||
|
];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStart = "${pkgs.ethtool}/bin/ethtool -s ${iface} wol g";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
environment.systemPackages = [
|
||||||
|
pkgs.ethtool
|
||||||
|
pkgs.wakeonlan
|
||||||
|
wake-jpporta-nixos
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-07-17
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
## Context
|
||||||
|
|
||||||
|
`jpporta-nixos` is a desktop machine with a wired Ethernet interface (`enp14s0`, MAC `d8:43:ae:5a:ae:12`) managed by NetworkManager. The user wants to power the machine on remotely via Wake-on-LAN so it can be reached over Tailscale + SSH without manual intervention.
|
||||||
|
|
||||||
|
WOL requires three layers of configuration:
|
||||||
|
|
||||||
|
1. **Firmware (BIOS/UEFI):** "Wake on LAN" must be enabled on the NIC, and the machine must remain in a low-power state after shutdown (most desktop boards default to this; some servers default to "power on after AC loss" which would defeat the purpose).
|
||||||
|
2. **Kernel/driver (NixOS):** The NIC driver must be told to keep listening for magic packets while the system is off. For most Intel NICs this means `ethtool -s <iface> wol g` (magic packet mode).
|
||||||
|
3. **Network:** Magic packets are sent to the layer-2 broadcast address (`ff:ff:ff:ff:ff:ff`) and rely on the NIC matching its own MAC. The host cannot route or filter them above the NIC, so no userspace daemon is required to *receive* a packet. A userspace tool (`wakeonlan`) is required to *send* one.
|
||||||
|
|
||||||
|
NixOS already provides `networking.wakeOnLan.enable`, but that option is hard-coded to `eth0` and is incompatible with predictable interface names (`enp14s0`). The NixOS-supported path for a named, predictable interface is a custom systemd unit that invokes `ethtool` against the specific interface. This is the approach used here because it survives reboots, resumes, and NetworkManager reconnects.
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
**Goals:**
|
||||||
|
- Persist `wol g` on `enp14s0` across reboots and resume-from-suspend.
|
||||||
|
- Provide `ethtool` and `wakeonlan` on `$PATH` so the user can inspect and send magic packets.
|
||||||
|
- Provide a convenience script `wake-jpporta-nixos` that sends a WOL packet to this machine's MAC.
|
||||||
|
- Wrap the configuration in a reusable module (`custom.wake-on-lan.enable`) consistent with existing patterns.
|
||||||
|
- Document the out-of-band BIOS / router configuration steps.
|
||||||
|
|
||||||
|
**Non-Goals:**
|
||||||
|
- Setting BIOS options (firmware is out of reach from NixOS).
|
||||||
|
- Configuring router port-forwarding for WOL-over-internet (router is out of scope).
|
||||||
|
- Listening for / relaying WOL packets on this host — that is the NIC's job once `wol g` is set.
|
||||||
|
- Changing the network interface naming scheme or replacing NetworkManager.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
1. **Per-interface systemd unit over `networking.wakeOnLan.enable`.**
|
||||||
|
- The top-level `networking.wakeOnLan.enable` is hard-coded to `eth0`, which doesn't match the predictable interface name `enp14s0` used here.
|
||||||
|
- Alternative considered: `networking.interfaces.enp14s0.wakeOnLan.enable = true`. This option does exist in modern NixOS, but on this machine it interacts awkwardly with NetworkManager (which re-loads NIC settings on reconnect) and the underlying implementation still relies on an ethtool invocation that may be racy with NetworkManager bringing the link up.
|
||||||
|
- **Decision:** Use a dedicated systemd `oneshot` service with a `network-online.target` dependency. This makes the WOL setting idempotent, runs after NetworkManager is up, and can also be triggered on resume-from-suspend by adding it to the `suspend.target` resume path. Concretely we ship a unit `wol-enp14s0.service` that runs `ethtool -s enp14s0 wol g`.
|
||||||
|
|
||||||
|
2. **Package set: `ethtool` + `iputils` (which provides `wakeonlan`).**
|
||||||
|
- `pkgs.ethtool` is the standard tool for inspecting and setting NIC driver options, including WOL modes.
|
||||||
|
- `pkgs.iputils` provides `wakeonlan`, the simplest CLI for sending a magic packet to a target MAC.
|
||||||
|
- Alternative considered: `pkgs.wol` (the separate, older `wol` program). `wakeonlan` from iputils is more widely used, has a simpler interface (`wakeonlan <mac>`), and is what most WOL guides assume.
|
||||||
|
|
||||||
|
3. **Convenience wrapper script `wake-jpporta-nixos`.**
|
||||||
|
- Hard-codes the MAC of this machine's wired NIC (`d8:43:ae:5a:ae:12`) into a small `writeShellScriptBin`-packaged script.
|
||||||
|
- Rationale: avoids having to remember or look up the MAC every time. The wrapper accepts an optional broadcast IP argument so the user can target `192.168.0.255` from off-LAN once the router is configured to forward UDP 9 to that broadcast.
|
||||||
|
|
||||||
|
4. **Module pattern: `modules/nixos/wake-on-lan/default.nix` with `custom.wake-on-lan.enable`.**
|
||||||
|
- Mirrors the existing pattern used by `modules/nixos/tailscale/` and `modules/nixos/hyprland/`.
|
||||||
|
- Defaults to disabled. Host (`jpporta-nixos`) opts in via `custom.wake-on-lan.enable = true`.
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
- **[Risk]** `ethtool -s ... wol g` only takes effect while the kernel NIC driver is loaded. Some BIOS/firmware combinations fully power down the NIC on shutdown, making WOL impossible regardless of the OS setting.
|
||||||
|
- **Mitigation:** Document this clearly in the change notes. The user must verify with `ethtool enp14s0` after a `systemctl poweroff` that `Supports Wake-on: pumbg` and `Wake-on: g` are still present.
|
||||||
|
|
||||||
|
- **[Risk]** Some desktop boards default to "Power on after AC loss = Power on" which would make the machine boot after any power outage regardless of WOL — undermining the use case.
|
||||||
|
- **Mitigation:** Document the BIOS setting ("After Power Loss" → "Power Off" or "Stay Off") in the change notes.
|
||||||
|
|
||||||
|
- **[Risk]** NetworkManager may re-apply link settings on resume, overwriting `wol g`.
|
||||||
|
- **Mitigation:** The systemd unit is wired to run on resume via a `path`/`service` pair (or a manual `systemctl restart wol-enp14s0.service` hook). For the initial implementation we trigger it on boot and rely on the user to re-run if it ever drifts; the follow-up task in `tasks.md` adds the resume hook.
|
||||||
|
|
||||||
|
- **[Trade-off]** Hard-coding the MAC and interface name in the Nix module means the module is specific to `jpporta-nixos`. We accept this trade-off because the module is only enabled on this host and the alternative (parameterizing) would add ceremony for a single user.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
## Why
|
||||||
|
|
||||||
|
The user wants to power on `jpporta-nixos` remotely via Wake-on-LAN (WOL). This is useful when the machine is shut down but the user wants to access it from another device (e.g., over Tailscale + SSH) without manually pressing the power button. Currently the machine has no WOL configuration: nothing keeps the NIC in a low-power listening state, and no tooling exists on the host to inspect or send WOL packets.
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- Enable Wake-on-LAN on the wired Ethernet interface (`enp14s0`) so the NIC keeps listening for magic packets while the system is powered off.
|
||||||
|
- Add `ethtool` so the user can inspect the current WOL settings and supported modes on the NIC.
|
||||||
|
- Add `wol` (the `iputils`-style `wakeonlan` command) so WOL packets can be sent from this host to other machines, and from other machines back to this one (using its MAC `d8:43:ae:5a:ae:12`).
|
||||||
|
- Add a small helper script (`wake-jpporta-nixos`) that sends a WOL magic packet to this machine's MAC address, so it can be invoked by name.
|
||||||
|
- Document the manual BIOS-level steps that are required for WOL to work (these cannot be set from NixOS).
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### New Capabilities
|
||||||
|
- `wake-on-lan`: Configures Wake-on-LAN on the host's wired NIC and provides tooling/scripts to inspect and send magic packets.
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
- None
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
- **Code:** A new optional module `modules/nixos/wake-on-lan/` will be added and imported into `hosts/jpporta-nixos/configuration.nix`. The module will expose a `custom.wake-on-lan.enable` toggle consistent with the project's existing module pattern (see `modules/nixos/tailscale/`).
|
||||||
|
- **System:** A systemd unit will be installed that runs `ethtool -s enp14s0 wol g` on boot and on resume, persisting the WOL setting across reboots and sleep states.
|
||||||
|
- **User:** Two new commands will be on `$PATH`: `ethtool` and `wakeonlan`, plus a convenience wrapper `wake-jpporta-nixos`.
|
||||||
|
- **Hardware:** Requires BIOS-level "Wake on LAN" to be enabled on the motherboard and a stable AC power source. The user must configure these out-of-band.
|
||||||
|
- **Network:** To wake the machine from outside the LAN, the router must forward UDP port 9 (or 7) to the broadcast address. This is a router-side concern, not configurable from NixOS.
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Persistent Wake-on-LAN on the wired NIC
|
||||||
|
The system SHALL keep the wired Ethernet interface (`enp14s0`) configured to wake on magic-packet while powered off.
|
||||||
|
|
||||||
|
#### Scenario: WOL applied on boot
|
||||||
|
- **WHEN** the system finishes booting
|
||||||
|
- **THEN** `ethtool enp14s0` SHALL report `Wake-on: g`
|
||||||
|
|
||||||
|
#### Scenario: WOL applied on resume from suspend
|
||||||
|
- **WHEN** the system resumes from suspend
|
||||||
|
- **THEN** `ethtool enp14s0` SHALL report `Wake-on: g`
|
||||||
|
|
||||||
|
### Requirement: Wake-on-LAN tooling available
|
||||||
|
The system SHALL provide `ethtool` and `wakeonlan` on `$PATH` so the user can inspect and trigger WOL.
|
||||||
|
|
||||||
|
#### Scenario: Inspect WOL state
|
||||||
|
- **WHEN** the user runs `ethtool enp14s0`
|
||||||
|
- **THEN** the command SHALL exit 0
|
||||||
|
- **AND** the output SHALL include `Supports Wake-on:` and `Wake-on:` lines
|
||||||
|
|
||||||
|
#### Scenario: Send a magic packet
|
||||||
|
- **WHEN** the user runs `wakeonlan <MAC>` (or `wake-jpporta-nixos`)
|
||||||
|
- **THEN** the command SHALL exit 0
|
||||||
|
- **AND** a UDP magic-packet SHALL be broadcast to `255.255.255.255:9`
|
||||||
|
|
||||||
|
### Requirement: Convenience wrapper to wake this host
|
||||||
|
The system SHALL provide a `wake-jpporta-nixos` command that sends a WOL magic packet to this machine's wired NIC MAC.
|
||||||
|
|
||||||
|
#### Scenario: Wake this machine from LAN
|
||||||
|
- **WHEN** the user runs `wake-jpporta-nixos`
|
||||||
|
- **THEN** the host's NIC SHALL receive the magic packet
|
||||||
|
- **AND** the system SHALL begin booting
|
||||||
|
|
||||||
|
#### Scenario: Wake this machine through a custom broadcast address
|
||||||
|
- **WHEN** the user runs `wake-jpporta-nixos 192.168.0.255`
|
||||||
|
- **THEN** the magic packet SHALL be sent to the `192.168.0.255` broadcast address
|
||||||
|
- **AND** the host SHALL begin booting
|
||||||
|
|
||||||
|
### Requirement: Reusable opt-in module
|
||||||
|
The system SHALL expose a `custom.wake-on-lan.enable` option that, when enabled, configures all of the above.
|
||||||
|
|
||||||
|
#### Scenario: Module disabled
|
||||||
|
- **WHEN** `custom.wake-on-lan.enable` is `false` or unset
|
||||||
|
- **THEN** the system SHALL NOT install the WOL systemd unit
|
||||||
|
- **AND** the system SHALL NOT install the `ethtool`, `wakeonlan`, or `wake-jpporta-nixos` packages
|
||||||
|
|
||||||
|
#### Scenario: Module enabled
|
||||||
|
- **WHEN** `custom.wake-on-lan.enable` is `true`
|
||||||
|
- **THEN** the WOL systemd unit SHALL be active
|
||||||
|
- **AND** the `wake-jpporta-nixos` command SHALL be on `$PATH`
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
## 1. Create the WOL NixOS module
|
||||||
|
|
||||||
|
- [x] 1.1 Create `modules/nixos/wake-on-lan/default.nix` with a `custom.wake-on-lan.enable` option.
|
||||||
|
- [x] 1.2 Define a systemd `oneshot` service `wol-enp14s0.service` that runs `ethtool -s enp14s0 wol g`.
|
||||||
|
- [x] 1.3 Wire the unit's `after`/`wants` to `network-online.target` and `NetworkManager.service` so it runs after the link is up.
|
||||||
|
- [x] 1.4 Add the unit to the resume path so `wol g` is re-applied after suspend/resume (use `systemd-suspend.service` or a `path` unit hook).
|
||||||
|
|
||||||
|
## 2. Add packages and the convenience wrapper
|
||||||
|
|
||||||
|
- [x] 2.1 Add `pkgs.ethtool` and `pkgs.wakeonlan` to `environment.systemPackages` when the module is enabled. *(Note: `pkgs.iputils` does not provide `wakeonlan` in nixpkgs — see implementation note.)*
|
||||||
|
- [x] 2.2 Create a `wake-jpporta-nixos` shell script via `pkgs.writeShellScriptBin` that invokes `wakeonlan -i ${1:-255.255.255.255} d8:43:ae:5a:ae:12`.
|
||||||
|
- [x] 2.3 Add the wrapper to `environment.systemPackages`.
|
||||||
|
|
||||||
|
## 3. Wire the module into the host
|
||||||
|
|
||||||
|
- [x] 3.1 Add `../../modules/nixos/wake-on-lan` to `imports` in `hosts/jpporta-nixos/configuration.nix`.
|
||||||
|
- [x] 3.2 Add `custom.wake-on-lan.enable = true;` under the existing `custom = { ... };` block in the host config.
|
||||||
|
|
||||||
|
## 4. Verify and document
|
||||||
|
|
||||||
|
- [x] 4.1 Rebuild the system with `sudo nixos-rebuild switch --flake .#jpporta-nixos`.
|
||||||
|
- [x] 4.2 Run `ethtool enp14s0` and confirm `Supports Wake-on:` lists `g` and `Wake-on: g`.
|
||||||
|
- [x] 4.3 Run `systemctl status wol-enp14s0.service` and confirm the unit is `active (exited)`.
|
||||||
|
- [x] 4.4 Run `wake-jpporta-nixos` once while the machine is up to confirm the script runs without error (it will be a no-op when the host is already up).
|
||||||
|
- [x] 4.5 Shut the machine down, then run `wake-jpporta-nixos` from another machine on the same LAN and confirm `jpporta-nixos` powers on.
|
||||||
|
- [x] 4.6 Document the BIOS-level prerequisite ("Wake on LAN" enabled, "After Power Loss" → Power Off / Stay Off) in the change notes or a README so it's not lost.
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# Power Profile Management
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
This capability provides configurable power management profiles that control system behavior such as screen blanking, locking, and suspension.
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Manage Power Profiles
|
||||||
|
The system SHALL provide three distinct power management profiles: `caffeinated`, `headless`, and `normal`.
|
||||||
|
|
||||||
|
#### Scenario: Caffeinated Profile
|
||||||
|
- **WHEN** the `caffeinated` profile is active
|
||||||
|
- **THEN** the system SHALL NOT blank the screen.
|
||||||
|
- **AND** the system SHALL NOT lock the screen.
|
||||||
|
- **AND** the system SHALL NOT suspend.
|
||||||
|
|
||||||
|
#### Scenario: Headless Profile
|
||||||
|
- **WHEN** the `headless` profile is active
|
||||||
|
- **THEN** the system SHALL blank the screen after a timeout.
|
||||||
|
- **AND** the system SHALL lock the screen when it blanks.
|
||||||
|
- **AND** the system SHALL NOT suspend.
|
||||||
|
|
||||||
|
#### Scenario: Normal Profile
|
||||||
|
- **WHEN** the `normal` profile is active
|
||||||
|
- **THEN** the system SHALL blank the screen after a default timeout.
|
||||||
|
- **AND** the system SHALL lock the screen when it blanks.
|
||||||
|
- **AND** the system SHALL suspend after a longer default timeout.
|
||||||
|
|
||||||
|
### Requirement: Cycle Through Power Profiles
|
||||||
|
The system SHALL allow the user to cycle through the power profiles using a keybinding.
|
||||||
|
|
||||||
|
#### Scenario: Cycle Forward
|
||||||
|
- **WHEN** the user presses the "next profile" keybinding
|
||||||
|
- **THEN** the active power profile SHALL change from `normal` to `caffeinated`, `caffeinated` to `headless`, and `headless` to `normal`.
|
||||||
|
|
||||||
|
#### Scenario: Cycle Backward
|
||||||
|
- **WHEN** the user presses the "previous profile" keybinding
|
||||||
|
- **THEN** the active power profile SHALL change from `normal` to `headless`, `headless` to `caffeinated`, and `caffeinated` to `normal`.
|
||||||
|
|
||||||
|
### Requirement: Visual Indicator for Current Profile
|
||||||
|
The system SHALL display the current power profile mode in the Waybar status bar.
|
||||||
|
|
||||||
|
#### Scenario: Display Caffeinated Icon
|
||||||
|
- **WHEN** the `caffeinated` profile is active
|
||||||
|
- **THEN** a "caffeinated" icon SHALL be visible in the Waybar status bar.
|
||||||
|
|
||||||
|
#### Scenario: Display Headless Icon
|
||||||
|
- **WHEN** the `headless` profile is active
|
||||||
|
- **THEN** a "headless" icon SHALL be visible in the Waybar status bar.
|
||||||
|
|
||||||
|
#### Scenario: Display Normal Icon
|
||||||
|
- **WHEN** the `normal` profile is active
|
||||||
|
- **THEN** a "normal" icon SHALL be visible in the Waybar status bar.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/nix/store/kldv63c3f6aqvk95rbwp7jf9ms7qjbb9-home-manager-generation
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
/nix/store/z1w7hmcjbxlj4zif3lwb8ifkn66rjy60-home-manager-generation
|
||||||
Reference in New Issue
Block a user