Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -537,14 +537,33 @@ nix-flake-update: nix-connect ## Update flake.lock file.
.PHONY: nix-format
nix-format: nix-format-clear-cache ## Format Nix files.
@echo "🧹 Formatting Nix files..."
@$(NIX_EXEC) fmt -- --clear-cache
@if $(NIX_EXEC) fmt -- --clear-cache; then \
:; \
else \
nixfmt_bin=$$(command -v nixfmt 2>/dev/null || true); \
if [ -z "$$nixfmt_bin" ]; then \
set -- /nix/store/*-nixfmt-*/bin/nixfmt; \
if [ -x "$$1" ]; then \
nixfmt_bin=$$1; \
fi; \
fi; \
if [ -z "$$nixfmt_bin" ]; then \
echo "❌ nix fmt failed and no local nixfmt fallback is available"; \
exit 1; \
fi; \
echo "⚠️ nix fmt unavailable; falling back to $$nixfmt_bin"; \
git ls-files -z '*.nix' | xargs -0 -r "$$nixfmt_bin"; \
fi
@echo "✅ Formatting complete"

.PHONY: nix-format-clear-cache
nix-format-clear-cache: ## Clear Nix format cache.
@echo "🧹 Clearing Nix cache..."
@$(NIX_EXEC) fmt -- --clear-cache
@echo "✅ Cache cleared"
@if $(NIX_EXEC) fmt -- --clear-cache >/dev/null 2>&1; then \
echo "✅ Cache cleared"; \
else \
echo "⚠️ nix fmt cache clear unavailable; continuing without it"; \
fi

.PHONY: nix-format-check
nix-format-check: ## Check Nix file formatting.
Expand Down
2,512 changes: 699 additions & 1,813 deletions bun.lock

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions config/hyprpanel/activate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -euo pipefail

SOURCE_JSON="$1"
TARGET_DIR="$HOME/.config/hyprpanel"
TARGET_JSON="$TARGET_DIR/config.json"
TMP_JSON="$TARGET_DIR/config.json.tmp"

mkdir -p "$TARGET_DIR"

if [ -L "$TARGET_JSON" ]; then
rm -f "$TARGET_JSON"
fi

if [ -f "$TARGET_JSON" ] && cmp -s "$SOURCE_JSON" "$TARGET_JSON"; then
exit 0
fi

cp -f "$SOURCE_JSON" "$TMP_JSON"
chmod 644 "$TMP_JSON"
mv -f "$TMP_JSON" "$TARGET_JSON"
27 changes: 21 additions & 6 deletions config/hyprpanel/default.nix
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
_: {
programs.hyprpanel = {
enable = true;
systemd.enable = false;
settings = {
{
lib,
pkgs,
...
}:
let
hyprpanelSettings = {
# Bar layout: workspaces left, clock center, modules right
"bar.layouts" = {
"0" = {
Expand Down Expand Up @@ -536,6 +538,19 @@ _: {
"theme.notification.labelicon" = "#88c0d0";
"theme.notification.close_button.background" = "#88c0d0";
"theme.notification.close_button.label" = "#1a1b26";
};
};
hyprpanelConfig = (pkgs.formats.json { }).generate "hyprpanel-config" hyprpanelSettings;
in
{
programs.hyprpanel = {
enable = true;
systemd.enable = false;

# HyprPanel writes back to config.json at runtime and fails on store symlinks.
settings = { };
};

home.activation.hyprpanelConfig = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
$DRY_RUN_CMD ${pkgs.bash}/bin/bash "${./activate.sh}" "${hyprpanelConfig}"
'';
}
4 changes: 4 additions & 0 deletions config/keyd/default.conf
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ rightshift = capslock
# xremap then converts Hyper+key → Ctrl+key for apps.
# Keys excluded from xremap (e.g. 3/4/5) pass through as Hyper to Hyprland.

# Framework+C should behave like a real Ctrl+C in Slack/Electron. Emit it
# directly from keyd instead of sending Hyper+C through xremap first.
c = C-c

# Framework+Tab → Super+Tab for hyprshell window switcher
tab = M-tab

Expand Down
16 changes: 15 additions & 1 deletion config/rofi/default.nix
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{ pkgs, ... }:
{
config,
pkgs,
...
}:
let
gnome-control-center-wrapper = pkgs.writeShellScript "gnome-control-center-wrapper" (
builtins.readFile (
Expand Down Expand Up @@ -129,5 +133,15 @@ in
];
noDisplay = false;
};
decafinate = {
name = "Decafinate (AC Only)";
exec = "${config.home.homeDirectory}/.local/scripts/decafinate";
icon = "battery-full-charged";
categories = [
"System"
"Utility"
];
noDisplay = false;
};
};
}
2 changes: 1 addition & 1 deletion dotagents
Submodule dotagents updated 2 files
+3 −3 .ruler/mcp.json
+3 −0 SKILLS.txt
167 changes: 167 additions & 0 deletions home-manager/modules/local-scripts/decafinate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#!/usr/bin/env bash

set -euo pipefail

UNIT_NAME="decafinate"
UNIT_FILE="${UNIT_NAME}.service"

notify_local() {
local message="$1"

if [[ -x "$HOME/.local/scripts/notify-local" ]]; then
"$HOME/.local/scripts/notify-local" "Decafinate" "$message" >/dev/null 2>&1 || true
fi
}

print_usage() {
cat <<'EOF'
Usage: decafinate [toggle|start|stop|status]

Manage an AC-only keep-awake session.

Commands:
toggle Start the session if inactive, otherwise stop it. Default.
start Start the session if AC power is connected.
stop Stop the session.
status Show whether the session is active.
EOF
}

ac_online() {
local ac_path
shopt -s nullglob

for ac_path in /sys/class/power_supply/AC*/online; do
if grep -q 1 "$ac_path"; then
return 0
fi
done

return 1
}

require_ac_path() {
if ! compgen -G "/sys/class/power_supply/AC*/online" >/dev/null; then
echo "No AC power status path found under /sys/class/power_supply/AC*/online" >&2
notify_local "No AC power status path found."
exit 1
fi
}

service_active() {
systemctl --user is-active --quiet "$UNIT_FILE"
}

print_status() {
if service_active; then
echo "decafinate is active"
return 0
fi

echo "decafinate is inactive"
return 1
}

start_service() {
require_ac_path

if service_active; then
echo "decafinate is already active"
return 0
fi

if ! ac_online; then
echo "AC power is not connected; refusing to inhibit sleep on battery." >&2
notify_local "AC power is not connected. Decafinate was not started."
return 1
fi

local systemd_inhibit
local bash_bin

systemd_inhibit="$(command -v systemd-inhibit)"
bash_bin="$(command -v bash)"

if [[ -z "$systemd_inhibit" || -z "$bash_bin" ]]; then
echo "Required commands not found: systemd-inhibit and bash must be available." >&2
return 1
fi

echo "Starting decafinate. The laptop will stay awake while AC is connected."
notify_local "Keeping the laptop awake while AC is connected."

systemctl --user reset-failed "$UNIT_FILE" >/dev/null 2>&1 || true

# shellcheck disable=SC2016
systemd-run --user \
--unit="$UNIT_NAME" \
--collect \
--property=Restart=no \
--property=Type=simple \
"$systemd_inhibit" \
--what=idle:sleep:handle-lid-switch \
--who="decafinate" \
--why="Manual AC-only keep-awake session" \
"$bash_bin" -lc '
set -euo pipefail

ac_online() {
local ac_path
shopt -s nullglob

for ac_path in /sys/class/power_supply/AC*/online; do
if grep -q 1 "$ac_path"; then
return 0
fi
done

return 1
}

while ac_online; do
sleep 15
done

echo "AC power disconnected; ending decafinate session."
if [[ -x "$HOME/.local/scripts/notify-local" ]]; then
"$HOME/.local/scripts/notify-local" "Decafinate" "AC power disconnected. Ending keep-awake session." >/dev/null 2>&1 || true
fi
' >/dev/null
}

stop_service() {
if ! service_active; then
echo "decafinate is already inactive"
return 0
fi

systemctl --user stop "$UNIT_FILE"
echo "Stopped decafinate"
notify_local "Stopped the keep-awake session."
}

case "${1:-toggle}" in
help|--help|-h)
print_usage
;;
toggle)
if service_active; then
stop_service
else
start_service
fi
;;
start)
start_service
;;
stop)
stop_service
;;
status)
print_status
;;
*)
print_usage >&2
exit 1
;;
esac
5 changes: 5 additions & 0 deletions home-manager/modules/local-scripts/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ _: {
force = true;
source = ./clipboard-paste.sh;
};
home.file.".local/scripts/decafinate" = {
executable = true;
force = true;
source = ./decafinate.sh;
};
home.file.".local/scripts/notify-local" = {
executable = true;
force = true;
Expand Down
10 changes: 8 additions & 2 deletions home-manager/modules/uv-globals/install-uv-globals.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ PYTHON_VERSION=$(tomlq -r '.project["requires-python"]' "$PYPROJECT" 2>/dev/null
PYTHON_VERSION=${PYTHON_VERSION:-3.13}

DEPS=$(tomlq -r '.["dependency-groups"].tools[]' "$PYPROJECT" 2>/dev/null)
PRERELEASE_PKGS=$(tomlq -r '.tool["uv-globals"].prerelease[]?' "$PYPROJECT" 2>/dev/null || true)

if [ -z "$DEPS" ]; then
echo "No dependencies found in pyproject.toml"
Expand All @@ -56,16 +57,21 @@ echo "$DEPS" | while read -r pkg; do
req_version=$(echo "$pkg" | sed -n 's/.*>=\([0-9][0-9.]*\).*/\1/p')
installed_version=$(echo "$INSTALLED" | sed -n "s/^${name} v\([0-9][0-9.]*\).*/\1/p")

extra_flags=""
if echo "$PRERELEASE_PKGS" | grep -qx "$name"; then
extra_flags="--prerelease=allow"
fi

if [ -n "$installed_version" ] && [ -n "$req_version" ]; then
if printf '%s\n%s\n' "$req_version" "$installed_version" | sort -V | head -n1 | grep -qx "$req_version"; then
echo "$name $installed_version already installed, skipping"
else
echo "Installing $pkg..."
uv tool install "$pkg" --python "$PYTHON_VERSION" --force 2>/dev/null || echo "Failed to install $pkg, skipping..."
uv tool install "$pkg" --python "$PYTHON_VERSION" --force $extra_flags 2>/dev/null || echo "Failed to install $pkg, skipping..."
fi
else
echo "Installing $pkg..."
uv tool install "$pkg" --python "$PYTHON_VERSION" --force 2>/dev/null || echo "Failed to install $pkg, skipping..."
uv tool install "$pkg" --python "$PYTHON_VERSION" --force $extra_flags 2>/dev/null || echo "Failed to install $pkg, skipping..."
fi

# Symlink each tool's python3 for per-tool access: `python3-<tool> -m <tool>`
Expand Down
1 change: 1 addition & 0 deletions home-manager/modules/xremap/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ let
globalRemap = mkRemap remapKeys;
# Ghostty: Framework+C/V → Ctrl+Shift+C/V (terminal convention: Ctrl+C = SIGINT)
ghosttyRemap = globalRemap // {
"C-c" = "C-Shift-c";
"${hyperPrefix}c" = "C-Shift-c";
"${hyperPrefix}v" = "C-Shift-v";
};
Expand Down
2 changes: 2 additions & 0 deletions home-manager/programs/fish/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
coxeh = "_coxeh_function";
coxel = "_coxel_function";
coxelh = "_coxelh_function";
decaf = "_decaf_function";
dev = "_dev_function";
fch = "_fzf_chrome_history";
fdp = "_fzf_directory_picker --allow-cd --prompt-name Projects ~/";
Expand Down Expand Up @@ -224,6 +225,7 @@
"_coxeh_function"
"_coxel_function"
"_coxelh_function"
"_decaf_function"
"_dev_function"
"_fish_shortcuts"
"_fzf_chrome_history"
Expand Down
9 changes: 9 additions & 0 deletions home-manager/programs/fish/functions/_decaf_function.fish
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function _decaf_function --description "Keep the laptop awake while AC power is connected"
set -l script "$HOME/.local/scripts/decafinate"
if not test -x "$script"
echo "decafinate script not found at $script"
return 1
end

bash "$script" $argv
end
Loading
Loading