331 lines
9.2 KiB
Nix
331 lines
9.2 KiB
Nix
{ config, lib, pkgs, inputs, ... }:
|
|
|
|
let
|
|
# Compile your custom wvkbd build
|
|
custom_wvkbd = pkgs.wvkbd.overrideAttrs (oldAttrs: {
|
|
# Pin the package version to the exact commit from your flake input
|
|
src = inputs.wvkbd-src;
|
|
version = "custom-git";
|
|
|
|
# Choose your preferred layout layout (e.g., mobintl, standard, cyrillic)
|
|
makeFlags = (oldAttrs.makeFlags or []) ++ [ "LAYOUT=mobintl" ];
|
|
|
|
# Optional: Swap out the layout with your own config.h file if you have one
|
|
preBuild = ''
|
|
cp ${./programs/layout.custom.h} layout.mobintl.h
|
|
cp ${./programs/config.custom.h} config.mobintl.h
|
|
cp ${./programs/keymap.custom.h} keymap.mobintl.h
|
|
'';
|
|
});
|
|
in
|
|
{
|
|
imports = [
|
|
./programs
|
|
./desktop
|
|
];
|
|
|
|
home.username = "jashton";
|
|
home.homeDirectory = "/home/jashton";
|
|
home.file.".zshrc".text = ''
|
|
# Zsh is configured globally at the NixOS system level.
|
|
# This file exists solely to prevent the zsh-newuser-install prompt.
|
|
'';
|
|
|
|
home.sessionVariables = {
|
|
EDITOR = "nvim";
|
|
NIXOS_OZONE_WL = "1"; # Forces Electron/Chromium apps to use Wayland natively
|
|
GTK_IM_MODULE = "wayland"; # Forces GTK apps to use Wayland text-input protocols
|
|
QT_IM_MODULE = "wayland"; # Forces Qt apps to use Wayland text-input protocols
|
|
TEXT_INPUT_PRESENT = "1"; # Explicitly signals virtual keyboard capability to the shell
|
|
JAVA_HOME = "${pkgs.jdk21}";
|
|
};
|
|
|
|
home.sessionPath = [
|
|
"$HOME/.cargo/bin"
|
|
];
|
|
|
|
home.packages = with pkgs; [
|
|
android-tools
|
|
antigravity
|
|
asciinema
|
|
claude-code
|
|
discord
|
|
eclipses.eclipse-java
|
|
ffmpeg
|
|
gemini-cli
|
|
kicad
|
|
libreoffice
|
|
librewolf
|
|
pdftk
|
|
picom
|
|
prismlauncher
|
|
scrot
|
|
signal-desktop
|
|
spotify
|
|
thunderbird
|
|
tor
|
|
tor-browser
|
|
vlc
|
|
wtype
|
|
xwayland-satellite
|
|
yt-dlp
|
|
zathura
|
|
|
|
custom_wvkbd
|
|
|
|
(pkgs.writeShellScriptBin "jroot" ''
|
|
d="$PWD"
|
|
while [ "$d" != "/" ]; do
|
|
if [ -f "$d/.classpath" ]; then printf '%s\n' "$d"; exit 0; fi
|
|
d="$(dirname "$d")"
|
|
done
|
|
echo "jroot: no .classpath found at or above $PWD" >&2
|
|
exit 1
|
|
'')
|
|
|
|
(pkgs.writeShellScriptBin "jbuild" ''
|
|
set -euo pipefail
|
|
if [ $# -lt 1 ]; then
|
|
echo "usage: jbuild <package.MainClass> [args...]" >&2
|
|
exit 1
|
|
fi
|
|
|
|
ROOT="$(jroot)"
|
|
cd "$ROOT"
|
|
mkdir -p bin
|
|
|
|
# Set the target directory, defaulting to the current directory (.) if $1 is empty
|
|
TARGET_DIR="$1"
|
|
SOURCES_FILE="SOURCES"
|
|
|
|
# Fix the find command to safely search the target directory
|
|
find "$TARGET_DIR" -name '*.java' > "$SOURCES_FILE"
|
|
|
|
if [ ! -s "$SOURCES_FILE" ]; then
|
|
# Fix the error message to reference the directory, not the output text file
|
|
echo "jbuild: no .java files found under $TARGET_DIR" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Source roots for every top-level package (assignments/assign04 -> package assign04, etc.).
|
|
# Needed so javac can resolve cross-package imports (eg. assign04 -> timing) and
|
|
# transitively compile whatever those imports require, even though TARGET_DIR only
|
|
# covers one subtree.
|
|
SOURCEPATH="assignments:labs:timing:playground:src"
|
|
|
|
javac -g --release 21 -encoding UTF-8 -d bin -cp "lib/*" -sourcepath "$SOURCEPATH" @"$SOURCES_FILE"
|
|
'')
|
|
|
|
(pkgs.writeShellScriptBin "jclean" ''
|
|
set -euo pipefail
|
|
ROOT="$(jroot)"
|
|
rm -rf "$ROOT/bin"
|
|
echo "cleaned $ROOT/bin"
|
|
'')
|
|
|
|
(pkgs.writeShellScriptBin "jrun" ''
|
|
set -euo pipefail
|
|
if [ $# -lt 3 ]; then
|
|
echo "usage: jrun <folder> <package> <MainClass> [args...]" >&2
|
|
exit 1
|
|
fi
|
|
ROOT="$(jroot)"
|
|
cd "$ROOT"
|
|
jbuild "$1"
|
|
MAIN="$2.$3"
|
|
shift 3
|
|
java -cp "bin:lib/*" "$MAIN" "$@"
|
|
'')
|
|
|
|
(pkgs.writeShellScriptBin "jtest" ''
|
|
set -euo pipefail
|
|
if [ "$#" -lt 1 ]; then
|
|
echo "usage: jtest <package.MainClass> [args...]"
|
|
exit 1
|
|
fi
|
|
|
|
# sort -V so 1.10.2 beats 1.9.0; plain tail -n1 sorts those the wrong way.
|
|
JUNIT="$(ls -1 "$HOME"/dev/jars/junit-platform-console-standalone-*.jar 2>/dev/null | sort -V | tail -n1 || true)"
|
|
if [ -z "$JUNIT" ]; then
|
|
echo "jtest: no junit-platform-console-standalone jar in ~/dev/jars" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# --debug suspends the test JVM on start and waits for nvim-dap to attach
|
|
# on :5005 (see the "Attach to jtest" config in nvim.nix), so breakpoints
|
|
# set before running jtest --debug are hit from the very first test.
|
|
DEBUG_ARGS=()
|
|
if [[ "$1" = "debug" ]]; then
|
|
DEBUG_ARGS=(-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005)
|
|
shift
|
|
echo "jtest: waiting for debugger to attach on 127.0.0.1:5005..." >&2
|
|
fi
|
|
|
|
ROOT="$(jroot)"
|
|
cd "$ROOT"
|
|
jbuild "$1"
|
|
if [ $# -eq 0 ]; then
|
|
exec java "''${DEBUG_ARGS[@]}" -jar "$JUNIT" execute --class-path "bin:lib/*" --scan-class-path --details=tree
|
|
fi
|
|
ARGS=""
|
|
for pkg in "$@"; do
|
|
ARGS="$ARGS --select-package $pkg"
|
|
done
|
|
exec java "''${DEBUG_ARGS[@]}" -jar "$JUNIT" execute --class-path "bin:lib/*" $ARGS --details=tree
|
|
'')
|
|
|
|
(pkgs.writeShellScriptBin "fkill" ''
|
|
set -eu
|
|
if [ $# -lt 1 ]; then
|
|
echo "usage: fkill <process-name>" >&2
|
|
echo "eg. fkill firefox" >&2
|
|
exit 1
|
|
fi
|
|
|
|
PROGRAM="$1"
|
|
|
|
if ! pgrep -x -- "$PROGRAM" >/dev/null 2>&1; then
|
|
echo "fkill: no process named '$PROGRAM'" >&2
|
|
exit 1
|
|
fi
|
|
|
|
pgrep -a -x -- "$PROGRAM"
|
|
printf 'Kill these? [y/N] '
|
|
read -r reply
|
|
case "$reply" in
|
|
[yY]*) pkill -x -- "$PROGRAM" ;;
|
|
*) echo "aborted" ;;
|
|
esac
|
|
'')
|
|
|
|
(pkgs.writeShellScriptBin "find-open-port" ''
|
|
set -u
|
|
if [ $# -lt 1 ]; then
|
|
echo "Please provide an application name, project directory, or other identifier."
|
|
echo ' Usage: find-open-port $HOME/dev/some-project'
|
|
echo " Usage: find-open-port some-app"
|
|
|
|
exit 1
|
|
fi
|
|
|
|
APP_NAME="$1"
|
|
REGISTRY_FILE="$HOME/.dotfiles/ports.registry"
|
|
|
|
MIN_PORT=8000
|
|
MAX_PORT=8999
|
|
MAX_RETRIES=50
|
|
|
|
mkdir -p "$(dirname "$REGISTRY_FILE")"
|
|
|
|
if [ -f "$REGISTRY_FILE" ] && grep -q "^$APP_NAME : " "$REGISTRY_FILE"; then
|
|
echo "Already registered:"
|
|
grep "^$APP_NAME : " "$REGISTRY_FILE"
|
|
exit 0
|
|
fi
|
|
|
|
echo "Searching for available port between $MIN_PORT and $MAX_PORT for $APP_NAME..."
|
|
|
|
for ((i=1; i<=MAX_RETRIES; i++)); do
|
|
RANDOM_PORT=$(shuf -i $MIN_PORT-$MAX_PORT -n 1)
|
|
|
|
if ! ss -tuln | grep -E -q ":$RANDOM_PORT\b"; then
|
|
if [ -f "$REGISTRY_FILE" ] && grep -q "\b$RANDOM_PORT\b" "$REGISTRY_FILE"; then
|
|
continue
|
|
fi
|
|
|
|
echo "$APP_NAME : $RANDOM_PORT" >> "$REGISTRY_FILE"
|
|
echo -e "\nSuccessfully wrote '$APP_NAME : $RANDOM_PORT' to $REGISTRY_FILE"
|
|
exit 0
|
|
fi
|
|
done
|
|
|
|
echo -e "\nError: Could not find an open port between $MIN_PORT and $MAX_PORT after $MAX_RETRIES attempts."
|
|
exit 1
|
|
'')
|
|
];
|
|
|
|
home.activation = {
|
|
# 1. Create the persistent directories
|
|
createWorkspaceDirs = lib.hm.dag.entryAfter ["writeBoundary"] ''
|
|
$DRY_RUN_CMD mkdir -p $HOME/dev
|
|
$DRY_RUN_CMD mkdir -p $HOME/downloads
|
|
$DRY_RUN_CMD mkdir -p $HOME/life
|
|
$DRY_RUN_CMD mkdir -p $HOME/media/pics
|
|
$DRY_RUN_CMD mkdir -p $HOME/media/videos
|
|
$DRY_RUN_CMD mkdir -p $HOME/school
|
|
'';
|
|
};
|
|
|
|
systemd.user.services.lan-mouse = {
|
|
Unit = {
|
|
Description = "Lan Mouse Daemon";
|
|
After = [ "graphical-session.target" ];
|
|
PartOf = [ "graphical-session.target" ];
|
|
};
|
|
Install = {
|
|
WantedBy = [ "graphical-session.target" ];
|
|
};
|
|
Service = {
|
|
# The --daemon flag ensures it runs silently in the background without opening the GUI
|
|
ExecStart = "${pkgs.lan-mouse}/bin/lan-mouse --daemon";
|
|
};
|
|
};
|
|
|
|
programs.opencode = {
|
|
enable = true;
|
|
|
|
settings = {
|
|
provider = {
|
|
ollama = {
|
|
npm = "@ai-sdk/openai-compatible";
|
|
name = "Ollama (Local)";
|
|
options = {
|
|
baseURL = "http://localhost:11434/v1";
|
|
};
|
|
models = {
|
|
"qwen3-coder-next:q4_K_M" = {
|
|
name = "Qwen3 Coder Next";
|
|
};
|
|
"qwen3.6:35b-a3b-q4_K_M" = {
|
|
name = "Qwen3.6 35B Thinker";
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
programs.ssh = {
|
|
enable = true;
|
|
enableDefaultConfig = false;
|
|
settings = {
|
|
"*" = {
|
|
forwardAgent = false;
|
|
addKeysToAgent = "no";
|
|
compression = false;
|
|
serverAliveInterval = 0;
|
|
serverAliveCountMax = 3;
|
|
hashKnownHosts = false;
|
|
userKnownHostsFile = "~/.ssh/known_hosts";
|
|
controlMaster = "no";
|
|
controlPath = "~/.ssh/master-%r@%n:%p";
|
|
controlPersist = "no";
|
|
};
|
|
"heliotrope" = {
|
|
hostname = "100.77.235.110";
|
|
};
|
|
"lavender" = {
|
|
hostname = "100.99.189.49";
|
|
};
|
|
"violet" = {
|
|
hostname = "100.101.100.84";
|
|
};
|
|
"lilac" = {
|
|
hostname = "100.82.51.88";
|
|
};
|
|
};
|
|
};
|
|
|
|
home.stateVersion = "26.05"; # DO NOT CHANGE
|
|
}
|