diff --git a/justfile b/justfile new file mode 100644 index 0000000..72c83f1 --- /dev/null +++ b/justfile @@ -0,0 +1,8 @@ +# Repo helpers. Run `just` to list recipes. + +default: + @just --list + +# Sync live DMS config into this host's seed dir. Pass --dry-run, --settings-only, or a host module name. +sync-dms *args='': + ./modules/desktop/shells/dms/_private/sync-seed.sh {{args}} diff --git a/modules/deploy/navi.nix b/modules/deploy/navi.nix index d034e62..618db95 100644 --- a/modules/deploy/navi.nix +++ b/modules/deploy/navi.nix @@ -116,16 +116,6 @@ in "x86_64-linux" "aarch64-linux" ]) { - devShells.default = pkgs.mkShell { - packages = [ inputs.navi.packages.${system}.default ]; - shellHook = '' - echo "Navi fleet deploy (from repo root):" - echo " navi apply --on # build + switch one host" - echo " navi apply-local --node 14900k --sudo # switch this machine locally (needs root), --node if hostname differs" - echo " navi tui # interactive fleet dashboard" - ''; - }; - apps = { navi = { type = "app"; diff --git a/modules/desktop/shells/dms/_private/default-settings.nix b/modules/desktop/shells/dms/_private/default-settings.nix index f21b921..22bd508 100644 --- a/modules/desktop/shells/dms/_private/default-settings.nix +++ b/modules/desktop/shells/dms/_private/default-settings.nix @@ -1,6 +1,8 @@ # Generic defaults written once to ~/.config/DankMaterialShell/settings.json on first login. -# Hosts can override via `chiasson.desktop.shells.dms.defaultSettingsFile` (see 14900k). +# Hosts can override via `chiasson.desktop.shells.dms.defaultSeedDir` (see 14900k). # After seeding, DMS settings are owned by the UI — rebuilds do not overwrite them. +# +# To refresh host seeds from live DMS config: `just sync-dms` { lib, gpuTempEnabled ? true, diff --git a/modules/desktop/shells/dms/_private/sync-seed.sh b/modules/desktop/shells/dms/_private/sync-seed.sh new file mode 100755 index 0000000..5f359de --- /dev/null +++ b/modules/desktop/shells/dms/_private/sync-seed.sh @@ -0,0 +1,119 @@ +#!/usr/bin/env bash +# Sync live DMS config into modules/hosts//_private/dms-defaults/ (see defaultSeedDir). +set -euo pipefail + +usage() { + cat <&2 + return 1 +} + +sync_json() { + local source="$1" dest="$2" label="$3" + + [[ -f "$source" ]] || { echo "skip: $label not found" >&2; return 0; } + + jq empty "$source" + if [[ "$DRY_RUN" -eq 1 ]]; then + echo " $source -> $dest" + return 0 + fi + + local tmp + tmp="$(mktemp)" + jq . "$source" >"$tmp" + mv "$tmp" "$dest" + echo "Updated $dest" + UPDATED+=("$dest") +} + +DRY_RUN=0 +SETTINGS_ONLY=0 +HOST="" +UPDATED=() + +while [[ $# -gt 0 ]]; do + case "$1" in + -n | --dry-run) DRY_RUN=1; shift ;; + --settings-only) SETTINGS_ONLY=1; shift ;; + -h | --help) usage; exit 0 ;; + -*) echo "error: unknown option: $1" >&2; usage >&2; exit 1 ;; + *) HOST="$1"; shift ;; + esac +done + +REPO_ROOT="${CHIASSON_NIX_ROOT:-$(find_repo_root "$PWD" || true)}" +[[ -n "$REPO_ROOT" ]] || { echo "error: run from chiasson-nix repo root" >&2; exit 1; } +command -v jq >/dev/null || { echo "error: jq required" >&2; exit 1; } + +if [[ -z "$HOST" ]]; then + SYSTEM_HOSTNAME="$(hostname -s 2>/dev/null || hostname)" + HOST="$(resolve_host_module "$SYSTEM_HOSTNAME" "$REPO_ROOT")" + [[ "$HOST" == "$SYSTEM_HOSTNAME" ]] \ + && echo "Using host module: $HOST" \ + || echo "Using host module: $HOST (hostname: $SYSTEM_HOSTNAME)" +fi + +HOST_PRIVATE="${REPO_ROOT}/modules/hosts/${HOST}/_private" +SEED_DIR="${HOST_PRIVATE}/dms-defaults" +DMS_CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}/DankMaterialShell" + +[[ -d "$HOST_PRIVATE" ]] || { echo "error: missing $HOST_PRIVATE" >&2; exit 1; } +[[ -f "${DMS_CONFIG}/settings.json" ]] || { + echo "error: missing ${DMS_CONFIG}/settings.json" >&2 + exit 1 +} + +[[ "$DRY_RUN" -eq 1 ]] && echo "Would sync into $SEED_DIR:" +[[ "$DRY_RUN" -eq 0 ]] && mkdir -p "$SEED_DIR" + +sync_json "${DMS_CONFIG}/settings.json" "${SEED_DIR}/settings.json" settings.json +[[ "$SETTINGS_ONLY" -eq 0 ]] \ + && sync_json "${DMS_CONFIG}/plugin_settings.json" "${SEED_DIR}/plugin_settings.json" plugin_settings.json + +[[ "$DRY_RUN" -eq 1 || ${#UPDATED[@]} -eq 0 ]] && exit 0 +git -C "$REPO_ROOT" diff --stat -- "${UPDATED[@]}" 2>/dev/null || true diff --git a/modules/desktop/shells/dms/default.nix b/modules/desktop/shells/dms/default.nix index 14cd2a8..9e220de 100644 --- a/modules/desktop/shells/dms/default.nix +++ b/modules/desktop/shells/dms/default.nix @@ -38,13 +38,15 @@ example = [ "sudo" "nixos-rebuild" "switch" "--flake" ".#14900k" ]; description = "Command used by DMS nix-monitor widget for rebuild actions."; }; - defaultSettingsFile = lib.mkOption { + defaultSeedDir = lib.mkOption { type = lib.types.nullOr lib.types.path; default = null; - example = ./dms-default-settings.json; + example = ./dms-defaults; description = '' - Host-specific first-run `settings.json` seed. When set, replaces the bundled - `default-settings.nix` template on fresh profiles. + Host-specific first-run DMS config seed directory. When set, copies + `settings.json` (required) and `plugin_settings.json` (optional) from this + directory into `~/.config/DankMaterialShell/` on fresh profiles. + Replaces the bundled `default-settings.nix` template for `settings.json`. ''; }; bundleThirdPartyPlugins = lib.mkOption { @@ -101,7 +103,6 @@ ]) ++ (cfg.extraRightBarWidgets or [ ]); programs.nix-monitor.rebuildCommand = rebuildCommand; - dms.defaultSettingsFile = cfg.defaultSettingsFile or null; dms.bundleThirdPartyPlugins = cfg.bundleThirdPartyPlugins or true; }; }; diff --git a/modules/desktop/shells/dms/home-manager/default.nix b/modules/desktop/shells/dms/home-manager/default.nix index 5663b18..9b166b2 100644 --- a/modules/desktop/shells/dms/home-manager/default.nix +++ b/modules/desktop/shells/dms/home-manager/default.nix @@ -59,16 +59,26 @@ ''; jsonFormat = pkgs.formats.json { }; - dmsSeedSettings = - if cfg.defaultSettingsFile != null then - builtins.fromJSON (builtins.readFile cfg.defaultSettingsFile) + dmsConfigDir = "${config.xdg.configHome}/DankMaterialShell"; + defaultSeedDir = lib.attrByPath [ "chiasson" "desktop" "shells" "dms" "defaultSeedDir" ] null osConfig; + + dmsSeedSources = + if defaultSeedDir != null then + { + "settings.json" = defaultSeedDir + "/settings.json"; + } + // lib.optionalAttrs (builtins.pathExists (defaultSeedDir + "/plugin_settings.json")) { + "plugin_settings.json" = defaultSeedDir + "/plugin_settings.json"; + } else - import ../_private/default-settings.nix { - inherit lib gpuTempEnabled; - extraRightBarWidgets = cfg.extraRightBarWidgets; + { + "settings.json" = jsonFormat.generate "dms-settings-seed.json" ( + import ../_private/default-settings.nix { + inherit lib gpuTempEnabled; + extraRightBarWidgets = cfg.extraRightBarWidgets; + } + ); }; - dmsSeedFile = jsonFormat.generate "dms-settings-seed.json" dmsSeedSettings; - dmsSettingsPath = "${config.xdg.configHome}/DankMaterialShell/settings.json"; # Directory name must match each plugin's `id` in plugin.json. thirdPartyPlugins = { @@ -158,15 +168,6 @@ in { Ship rbw-lock-toggle into `~/.config/DankMaterialShell/plugins/rbwLockToggle/` (directory name matches plugin id; Bitwarden vault lock/unlock in the bar). ''; - dms.defaultSettingsFile = lib.mkOption { - type = lib.types.nullOr lib.types.path; - default = null; - description = '' - Host-specific first-run `settings.json` seed. When set, replaces the bundled - `default-settings.nix` template on fresh profiles. - ''; - }; - dms.bundleThirdPartyPlugins = lib.mkOption { type = lib.types.bool; default = true; @@ -270,22 +271,25 @@ in { session = { }; }; - # Detach store symlink so UI edits survive rebuilds; seed defaults only on a fresh profile. - home.activation.dmsMaterializeSettings = lib.hm.dag.entryBefore [ "writeBoundary" ] '' - settingsFile=${lib.escapeShellArg dmsSettingsPath} - if [ -L "$settingsFile" ]; then - mkdir -p "$(dirname "$settingsFile")" - ${pkgs.coreutils}/bin/cp --remove-destination -L "$settingsFile" "$settingsFile" - fi - ''; - - home.activation.dmsSeedSettings = lib.hm.dag.entryAfter [ "writeBoundary" ] '' - settingsFile=${lib.escapeShellArg dmsSettingsPath} - mkdir -p "$(dirname "$settingsFile")" - if [ ! -e "$settingsFile" ]; then - ${pkgs.coreutils}/bin/cp ${lib.escapeShellArg dmsSeedFile} "$settingsFile" - chmod u+w "$settingsFile" - fi + # Detach store symlinks so UI edits survive rebuilds; seed defaults only on a fresh profile. + home.activation.dmsSeedConfig = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + ${lib.concatMapStringsSep "\n" ( + fileName: + let + dest = "${dmsConfigDir}/${fileName}"; + src = dmsSeedSources.${fileName}; + in '' + dest=${lib.escapeShellArg dest} + mkdir -p "$(dirname "$dest")" + if [ -L "$dest" ]; then + ${pkgs.coreutils}/bin/cp --remove-destination -L "$dest" "$dest" + fi + if [ ! -e "$dest" ]; then + ${pkgs.coreutils}/bin/cp ${lib.escapeShellArg src} "$dest" + chmod u+w "$dest" + fi + '' + ) (lib.attrNames dmsSeedSources)} ''; # matugen won't create missing output_path parents; run before DMS theme generation. diff --git a/modules/devshell.nix b/modules/devshell.nix new file mode 100644 index 0000000..17c08b2 --- /dev/null +++ b/modules/devshell.nix @@ -0,0 +1,31 @@ +{ inputs, lib, ... }: +{ + perSystem = + { + pkgs, + system, + ... + }: + lib.optionalAttrs (lib.elem system [ + "x86_64-linux" + "aarch64-linux" + ]) { + devShells.default = pkgs.mkShell { + packages = [ + inputs.navi.packages.${system}.default + pkgs.just + pkgs.jq + ]; + shellHook = '' + echo "Repo helpers (from repo root):" + echo " just # list justfile recipes" + echo " just sync-dms [--dry-run] # copy live DMS config into host seed dir" + echo "" + echo "Navi fleet deploy:" + echo " navi apply --on # build + switch one host" + echo " navi apply-local --node 14900k --sudo # switch locally (needs root)" + echo " navi tui # interactive fleet dashboard" + ''; + }; + }; +} diff --git a/modules/hosts/14900k/_private/dms-defaults/plugin_settings.json b/modules/hosts/14900k/_private/dms-defaults/plugin_settings.json new file mode 100644 index 0000000..134a7ae --- /dev/null +++ b/modules/hosts/14900k/_private/dms-defaults/plugin_settings.json @@ -0,0 +1,278 @@ +{ + "dankDesktopWeather": { + "enabled": true + }, + "nixMonitor": { + "enabled": true + }, + "newClock": { + "enabled": true, + "clockStyle": "digital", + "showSeconds": true, + "showDate": true + }, + "rbwLockToggle": { + "enabled": true + }, + "ambientSound": { + "enabled": true, + "whenDoneActions": [], + "soundVolumes": { + "acoustic-guitar": 100, + "warm-piano": 100, + "lofi-beats": 100 + } + }, + "bongoCat": { + "enabled": true, + "selectedDevicePath": "/dev/input/event4" + }, + "calculator": { + "enabled": true, + "trigger": "=" + }, + "dankGifSearch": { + "enabled": false + }, + "homeAssistantMonitor": { + "enabled": true, + "hassUrl": "https://home.chiasson.cloud/", + "hassTokenPath": "/run/secrets/home-assistant/auth-token", + "showButtonsOnStatusBar": true, + "showAttributes": false + }, + "unifiedTaskbar": { + "enabled": true, + "allMonitors": false, + "reverseMonitorOrder": false, + "groupByApp": false, + "compactMode": false, + "filledPills": false, + "iconPadding": 4, + "itemSpacing": 2 + }, + "widgetGroup": { + "enabled": true, + "variants": [ + { + "icon": "widgets", + "label": "", + "display": "both", + "expandDir": "right", + "targets": [ + "rbwLockToggle" + ], + "mainTarget": "", + "mainClickButton": "right", + "mainMarkerColor": "primary", + "expandIndicatorPosition": "", + "showArrow": false, + "showArrowOnlyOnHover": false, + "hideMain": false, + "id": "variant_1781997418516", + "name": "Test", + "autoCollapse": false, + "autoCollapseSeconds": 5, + "autoCollapseOnLeave": false + }, + { + "icon": "bug_report", + "label": "", + "display": "both", + "expandDir": "right", + "targets": [ + "nixMonitor", + "cpuUsage", + "memUsage", + "diskUsage", + "cpuTemp", + "gpuTemp" + ], + "mainTarget": "cpuUsage", + "mainClickButton": "right", + "mainMarkerColor": "primary", + "expandIndicatorPosition": "", + "showArrow": false, + "showArrowOnlyOnHover": false, + "hideMain": false, + "id": "variant_1781999113601", + "name": "SysInfo", + "autoCollapse": false, + "autoCollapseSeconds": 5, + "autoCollapseOnLeave": false + } + ] + }, + "desktopWidgetToggle": { + "enabled": false, + "activeGroupIds": [], + "activeGroupId": "", + "groups": [ + { + "id": "g1", + "name": "Group 1", + "icon": "widgets", + "widgets": [], + "overrideIndividual": false + }, + { + "id": "g_1781998217464_d0ngn3bo3", + "name": "New Group", + "icon": "widgets", + "widgets": [], + "overrideIndividual": true, + "widgetOverrides": {} + } + ], + "conflictMode": "single" + }, + "dropdownMenu": { + "enabled": true, + "variants": [ + { + "icon": "expand_circle_down", + "text": "", + "items": [ + { + "type": "popout", + "icon": "", + "label": "Home Assistant", + "display": "both", + "widgetId": "homeAssistantMonitor" + } + ], + "id": "variant_1781998267426", + "name": "Main Drop" + } + ] + }, + "ocrScanner": { + "enabled": true + }, + "obsidianSearch": { + "enabled": true, + "vaultPath": "/mnt/zimaos/Obsidian/Home/", + "isFlatpak": true + }, + "dankObsidian": { + "enabled": false + }, + "dankPinentry": { + "enabled": true + }, + "dankVault": { + "enabled": true, + "backend": "rbw" + }, + "nixPackageRunner": { + "enabled": true, + "runInTerminal": false + }, + "dmsconky": { + "enabled": true + }, + "discordVoice": { + "enabled": false + }, + "emojiLauncher": { + "enabled": true, + "trigger": ":", + "pasteOnSelect": true, + "recentEmojis": "🃏" + }, + "webSearch": { + "enabled": true, + "searchEngines": [], + "disabledEngines": [], + "trigger": "#" + }, + "wallpaperCarousel": { + "enabled": true, + "cacheSize": 30, + "holdDelay": 1206 + }, + "ephemera": { + "enabled": true, + "provider": "gemini", + "model": "gemini-3-flash-preview" + }, + "aiAssistant": { + "enabled": true, + "providers": { + "openai": { + "baseUrl": "https://api.openai.com", + "model": "gpt-5.2", + "apiKey": "", + "saveApiKey": false, + "apiKeyEnvVar": "", + "temperature": 0.7, + "maxTokens": 4096, + "timeout": 30 + }, + "anthropic": { + "baseUrl": "https://api.anthropic.com", + "model": "claude-sonnet-4-5", + "apiKey": "", + "saveApiKey": false, + "apiKeyEnvVar": "", + "temperature": 0.7, + "maxTokens": 4096, + "timeout": 30 + }, + "gemini": { + "baseUrl": "https://generativelanguage.googleapis.com", + "model": "gemini-3-flash-preview", + "apiKey": "", + "saveApiKey": false, + "apiKeyEnvVar": "", + "temperature": 0.7, + "maxTokens": 4096, + "timeout": 30, + "geminiWebSearch": false + }, + "inception": { + "baseUrl": "https://api.inceptionlabs.ai/v1", + "model": "mercury-2", + "apiKey": "", + "saveApiKey": false, + "apiKeyEnvVar": "", + "temperature": 0.75, + "maxTokens": 8192, + "timeout": 30, + "inceptionReasoningEffort": "medium", + "inceptionReasoningSummary": true, + "inceptionReasoningSummaryWait": false + }, + "ollama": { + "baseUrl": "http://localhost:11434", + "model": "", + "apiKey": "", + "saveApiKey": false, + "apiKeyEnvVar": "", + "temperature": 0.7, + "maxTokens": 4096, + "timeout": 30 + }, + "custom": { + "baseUrl": "https://api.openai.com", + "model": "gpt-5.2", + "apiKey": "", + "saveApiKey": false, + "apiKeyEnvVar": "", + "temperature": 0.7, + "maxTokens": 4096, + "timeout": 30 + } + }, + "provider": "openai", + "baseUrl": "https://api.openai.com", + "model": "gpt-5.2", + "apiKey": "", + "saveApiKey": false, + "apiKeyEnvVar": "", + "temperature": 0.7, + "maxTokens": 4096, + "timeout": 30, + "geminiWebSearch": false + } +} diff --git a/modules/hosts/14900k/_private/dms-default-settings.json b/modules/hosts/14900k/_private/dms-defaults/settings.json similarity index 99% rename from modules/hosts/14900k/_private/dms-default-settings.json rename to modules/hosts/14900k/_private/dms-defaults/settings.json index d041e7a..8cf7113 100644 --- a/modules/hosts/14900k/_private/dms-default-settings.json +++ b/modules/hosts/14900k/_private/dms-defaults/settings.json @@ -1152,4 +1152,4 @@ }, "launcherPluginOrder": [], "configVersion": 5 -} \ No newline at end of file +} diff --git a/modules/hosts/14900k/configuration.nix b/modules/hosts/14900k/configuration.nix index 3014e89..07022da 100644 --- a/modules/hosts/14900k/configuration.nix +++ b/modules/hosts/14900k/configuration.nix @@ -70,7 +70,7 @@ ".#14900k" ]; enableRbwLockToggle = true; - defaultSettingsFile = ./_private/dms-default-settings.json; + defaultSeedDir = ./_private/dms-defaults; }; };