diff --git a/dotfiles/claude/settings.json b/dotfiles/claude/settings.json index 061938ad..c7f0a98d 100644 --- a/dotfiles/claude/settings.json +++ b/dotfiles/claude/settings.json @@ -15,6 +15,6 @@ "superpowers@superpowers-marketplace": true, "agent-browser@agent-browser": true }, - "skipDangerousModePermissionPrompt": true, - "effortLevel": "high" + "effortLevel": "high", + "skipDangerousModePermissionPrompt": true } diff --git a/dotfiles/codex/config.toml b/dotfiles/codex/config.toml index b93ab8af..9afc7ce2 100644 --- a/dotfiles/codex/config.toml +++ b/dotfiles/codex/config.toml @@ -88,6 +88,12 @@ trust_level = "trusted" [projects."/home/imalison/Projects/rofi-systemd"] trust_level = "trusted" +[projects."/home/imalison/Projects/map-quiz"] +trust_level = "trusted" + +[projects."/run/media/imalison/NETDEBUGUSB"] +trust_level = "trusted" + [notice] hide_gpt5_1_migration_prompt = true "hide_gpt-5.1-codex-max_migration_prompt" = true diff --git a/dotfiles/config/taffybar/end-widget-colors.css b/dotfiles/config/taffybar/end-widget-colors.css index 0476e336..b72ecf85 100644 --- a/dotfiles/config/taffybar/end-widget-colors.css +++ b/dotfiles/config/taffybar/end-widget-colors.css @@ -38,56 +38,53 @@ /* --- Color rotation rules --- */ +.outer-pad.end-widget, +.outer-pad.sni-tray { + background-image: none; + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.10), + inset 0 0 0 1px rgba(255, 255, 255, 0.10), + 0 10px 24px rgba(0, 0, 0, 0.30); +} + /* Slot 1: indigo */ .outer-pad.end-widget.end-slot-1 { background-color: @end-color-1-bg; - background-image: none; border-color: @end-color-1-border; color: @end-color-1-fg; - box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06), 0 10px 24px rgba(0, 0, 0, 0.30); } /* Slot 2: purple */ .outer-pad.end-widget.end-slot-2 { background-color: @end-color-2-bg; - background-image: none; border-color: @end-color-2-border; color: @end-color-2-fg; - box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06), 0 10px 24px rgba(0, 0, 0, 0.30); } /* Slot 3: emerald */ .outer-pad.end-widget.end-slot-3 { background-color: @end-color-3-bg; - background-image: none; border-color: @end-color-3-border; color: @end-color-3-fg; - box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06), 0 10px 24px rgba(0, 0, 0, 0.30); } /* Slot 4: teal */ .outer-pad.end-widget.end-slot-4 { background-color: @end-color-4-bg; - background-image: none; border-color: @end-color-4-border; color: @end-color-4-fg; - box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06), 0 10px 24px rgba(0, 0, 0, 0.30); } /* Slot 5: rose */ .outer-pad.end-widget.end-slot-5 { background-color: @end-color-5-bg; - background-image: none; border-color: @end-color-5-border; color: @end-color-5-fg; - box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06), 0 10px 24px rgba(0, 0, 0, 0.30); } /* --- SNI tray (center widget, not part of the rotation) --- */ .outer-pad.sni-tray { background-color: rgb(65, 70, 100); - background-image: none; border-color: rgb(110, 115, 160); - box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06), 0 10px 24px rgba(0, 0, 0, 0.30); } diff --git a/dotfiles/config/taffybar/end-widget-solid.css b/dotfiles/config/taffybar/end-widget-solid.css deleted file mode 100644 index eda2dbb0..00000000 --- a/dotfiles/config/taffybar/end-widget-solid.css +++ /dev/null @@ -1,50 +0,0 @@ -/* Final pass overrides for end-widget pill chrome. - * - * This is loaded after the main bar/theme CSS so the end-widget palette stays - * vivid even if earlier rules or theme rendering make the pills read too - * transparent on some hosts. - */ - -.outer-pad.end-widget.end-slot-1 { - background-color: rgb(50, 60, 160); - background-image: none; - border-color: rgb(90, 100, 210); -} - -.outer-pad.end-widget.end-slot-2 { - background-color: rgb(110, 45, 160); - background-image: none; - border-color: rgb(155, 85, 210); -} - -.outer-pad.end-widget.end-slot-3 { - background-color: rgb(25, 130, 75); - background-image: none; - border-color: rgb(55, 190, 115); -} - -.outer-pad.end-widget.end-slot-4 { - background-color: rgb(20, 120, 140); - background-image: none; - border-color: rgb(50, 175, 200); -} - -.outer-pad.end-widget.end-slot-5 { - background-color: rgb(160, 40, 70); - background-image: none; - border-color: rgb(210, 80, 115); -} - -.outer-pad.sni-tray { - background-color: rgb(65, 70, 100); - background-image: none; - border-color: rgb(110, 115, 160); -} - -.outer-pad.end-widget, -.outer-pad.sni-tray { - box-shadow: - inset 0 1px 0 rgba(255, 255, 255, 0.10), - inset 0 0 0 1px rgba(255, 255, 255, 0.10), - 0 10px 24px rgba(0, 0, 0, 0.30); -} diff --git a/dotfiles/config/taffybar/log-levels.yaml b/dotfiles/config/taffybar/log-levels.yaml index e3eac692..1951b061 100644 --- a/dotfiles/config/taffybar/log-levels.yaml +++ b/dotfiles/config/taffybar/log-levels.yaml @@ -10,8 +10,9 @@ # System.Taffybar.DBus.Toggle: DEBUG # Graphics.UI.GIGtkStrut: DEBUG # Temporary startup debugging for tray duplication. -StatusNotifier.Tray: DEBUG -StatusNotifier.Host.Service: DEBUG -System.Taffybar.Widget.SNITray.PrioritizedCollapsible: DEBUG +# Enable these selectively when investigating tray churn: +# StatusNotifier.Tray: DEBUG +# StatusNotifier.Host.Service: DEBUG +# System.Taffybar.Widget.SNITray.PrioritizedCollapsible: DEBUG System.Taffybar.LogLevels: INFO -System.Taffybar.Context: DEBUG +# System.Taffybar.Context: DEBUG diff --git a/dotfiles/config/taffybar/taffybar.css b/dotfiles/config/taffybar/taffybar.css index f552fbbf..07de69ed 100644 --- a/dotfiles/config/taffybar/taffybar.css +++ b/dotfiles/config/taffybar/taffybar.css @@ -1,6 +1,5 @@ @import url("theme.css"); @import url("end-widget-colors.css"); -@import url("end-widget-solid.css"); /* Widget/layout styling for taffybar. * @@ -93,7 +92,7 @@ } /* Per-widget color overrides now live in end-widget-colors.css, - which rotates through a 5-color palette via :nth-child(). */ + which rotates through a 5-color palette via semantic end-slot classes. */ .outer-pad.mpris .icon { font-family: "Iosevka Nerd Font"; diff --git a/dotfiles/config/taffybar/taffybar.hs b/dotfiles/config/taffybar/taffybar.hs index af2d07ea..d06e2c32 100644 --- a/dotfiles/config/taffybar/taffybar.hs +++ b/dotfiles/config/taffybar/taffybar.hs @@ -250,10 +250,7 @@ defaultCssFiles = ["taffybar.css"] cssFilesByHostname :: [(String, [FilePath])] cssFilesByHostname = - [ ("imalison-home", ["taffybar.css"]), - ("ryzen-shine", ["ryzen-shine.css"]), - ("stevie-nixos", ["taffybar.css"]) - ] + [("ryzen-shine", ["ryzen-shine.css"])] laptopHosts :: [String] laptopHosts = diff --git a/dotfiles/emacs.d/elpaca-installer.el b/dotfiles/emacs.d/elpaca-installer.el index 8e7bc681..d497e8b2 100644 --- a/dotfiles/emacs.d/elpaca-installer.el +++ b/dotfiles/emacs.d/elpaca-installer.el @@ -77,6 +77,30 @@ (directory-file-name source))) (t (delete-directory build 'recursive)))))))) + +(defun elpaca-installer--repair-source-dir-aliases () + "Create compatibility symlinks for legacy repos ending in `.el'." + (when (file-directory-p elpaca-sources-directory) + (dolist (entry (directory-files elpaca-sources-directory t directory-files-no-dot-files-regexp)) + (when-let* (((file-directory-p entry)) + (name (file-name-nondirectory (directory-file-name entry))) + ((string-suffix-p ".el" name)) + (alias-name (substring name 0 (- (length name) 3))) + (alias (expand-file-name alias-name elpaca-sources-directory)) + (target (ignore-errors + (directory-file-name (file-truename entry))))) + (cond + ((and (file-symlink-p alias) + (equal (ignore-errors (directory-file-name (file-truename alias))) + target)) + nil) + ((file-symlink-p alias) + (delete-file alias) + (make-symbolic-link target alias)) + ((file-exists-p alias) + nil) + (t + (make-symbolic-link target alias))))))) ;; Elpaca now expects package sources under `sources/`. Preserve older local ;; installs that still use `repos/` so startup can recover without recloning. (when (and (file-directory-p elpaca-legacy-repos-directory) @@ -87,6 +111,7 @@ (not (file-exists-p elpaca-legacy-repos-directory))) (make-symbolic-link (directory-file-name elpaca-sources-directory) (directory-file-name elpaca-legacy-repos-directory))) +(elpaca-installer--repair-source-dir-aliases) (elpaca-installer--repair-build-source-layout) (let* ((repo (expand-file-name "elpaca/" elpaca-sources-directory)) (build (expand-file-name "elpaca/" elpaca-builds-directory)) diff --git a/nixos/desktop.nix b/nixos/desktop.nix index 7da6fb9f..7b733d0a 100644 --- a/nixos/desktop.nix +++ b/nixos/desktop.nix @@ -43,6 +43,12 @@ makeEnable config "myModules.desktop" true { # This is for the benefit of VSCODE running natively in wayland environment.sessionVariables.NIXOS_OZONE_WL = "1"; + system.activationScripts.playwrightChromeCompat.text = lib.optionalString (pkgs.stdenv.hostPlatform.system == "x86_64-linux") '' + # Playwright's Chrome channel lookup expects the FHS path below. + mkdir -p /opt/google/chrome + ln -sfn ${pkgs.google-chrome}/bin/google-chrome-stable /opt/google/chrome/chrome + ''; + services.gnome.at-spi2-core.enable = true; services.gnome.gnome-keyring.enable = true; diff --git a/nixos/flake.lock b/nixos/flake.lock index 540af461..f2a12914 100644 --- a/nixos/flake.lock +++ b/nixos/flake.lock @@ -93,11 +93,11 @@ "quickshell": "quickshell" }, "locked": { - "lastModified": 1775660122, - "narHash": "sha256-qMKB06TE0MY1anDQKBrzZEpktNPyvMxQQzTEEwWAA6I=", + "lastModified": 1775801889, + "narHash": "sha256-q1LGwhQbNOurIAClh5YwKVU2kJ5lTCxRYZf48bAb9IM=", "owner": "caelestia-dots", "repo": "shell", - "rev": "aa2b08dd45963dc9558de94dbff5e1615e347d02", + "rev": "0e07176ff149d02391531c802b51c28e73185f30", "type": "github" }, "original": { @@ -116,11 +116,11 @@ ] }, "locked": { - "lastModified": 1775765772, - "narHash": "sha256-TH45EzDoSo3WY3cZWI+EVwzw8jC8A3tMJoCDulqrjc4=", + "lastModified": 1775966787, + "narHash": "sha256-wq1rMcMxMK9ZBg8TsJkXxli9K4ey+C2qKKmRYphhbek=", "owner": "sadjow", "repo": "claude-code-nix", - "rev": "3da0500f8f10aa8ad2da90a081b0c1d16add10f5", + "rev": "e128b713477ef9732a0b5d9bed155ff37c0bb5a4", "type": "github" }, "original": { @@ -139,11 +139,11 @@ ] }, "locked": { - "lastModified": 1774977969, - "narHash": "sha256-MbCh0ayUhnP8Z/83tTPR9iTzNxQkfleedmlcgsHh1LA=", + "lastModified": 1775882637, + "narHash": "sha256-e2jaiMpQr2N/AxhRqt1NA0DU7jC9Sv0pvNnpCuyXxuw=", "owner": "sadjow", "repo": "codex-cli-nix", - "rev": "8be597476146c75f440708f9a7ad50ae489641c4", + "rev": "d4223ed9ee52efe572c1612579c3c6a80bc4d8db", "type": "github" }, "original": { @@ -658,11 +658,11 @@ ] }, "locked": { - "lastModified": 1775762219, - "narHash": "sha256-e7BhggoWhg3Ok7dDI5kY1XZzORBQc0Rclcs3IWzux3w=", + "lastModified": 1776030105, + "narHash": "sha256-b4cNpWPDSH+/CTTiw8++yGh1UYG2kQNrbIehV2iGoeo=", "owner": "nix-community", "repo": "home-manager", - "rev": "c975a66a56306b38eaa3108f54bbc11e213f42f6", + "rev": "49088dc2e7a876e338e510c5f5f60f659819c650", "type": "github" }, "original": { @@ -1167,11 +1167,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1775723846, - "narHash": "sha256-L0mX3HCoE/N0K97SLNc1QoW8vp/WMm60Y6SLEyLvyWc=", + "lastModified": 1775987564, + "narHash": "sha256-4aslB/s2klFFzJYtHcGEZ3W7TfHX44ZMTpks0QN6se8=", "owner": "NixOS", "repo": "nix", - "rev": "84acfc03f6af30042714d82d79eebb799b64f7a7", + "rev": "efd639861b6526fb691694fbc2a204c6c3e209ed", "type": "github" }, "original": { @@ -1188,11 +1188,11 @@ ] }, "locked": { - "lastModified": 1775759356, - "narHash": "sha256-xvoCaRhS2F4rrS58GGtlucW6ItQ5eMxyj81237HpvpQ=", + "lastModified": 1775987221, + "narHash": "sha256-G+vUCy7vlJSYHzWlDg02ruR0G4jYODxz9gRnF32/ZDI=", "owner": "nixified-ai", "repo": "flake", - "rev": "82458a692a23f4df4d782c8e5d9a31fba0edb28f", + "rev": "bef7be03aed7e49bb280cc5900788224b87e93f0", "type": "github" }, "original": { @@ -1351,11 +1351,11 @@ }, "nixpkgs_4": { "locked": { - "lastModified": 1775423009, - "narHash": "sha256-vPKLpjhIVWdDrfiUM8atW6YkIggCEKdSAlJPzzhkQlw=", + "lastModified": 1775710090, + "narHash": "sha256-ar3rofg+awPB8QXDaFJhJ2jJhu+KqN/PRCXeyuXR76E=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "68d8aa3d661f0e6bd5862291b5bb263b2a6595c9", + "rev": "4c1018dae018162ec878d42fec712642d214fdfa", "type": "github" }, "original": { @@ -1910,11 +1910,11 @@ ] }, "locked": { - "lastModified": 1755152326, - "narHash": "sha256-47gGXt1TxzfQx/6X895t/T8Ozj+7fmQdU1i4cwyxcSg=", + "lastModified": 1771531632, + "narHash": "sha256-ZM8V9Dp4HSxaQKuB7kckU7PaAgMwsYbfHnKnPqr0Rbw=", "owner": "IvanMalison", "repo": "xmonad-contrib", - "rev": "1a8da46855ca83e11cfb31cbbaed980ed7a8dfcc", + "rev": "b71f9e4c9dfdbb71c1b55f30edb0da524b0ed251", "type": "github" }, "original": { diff --git a/nixos/home-manager.nix b/nixos/home-manager.nix index 3fa60d71..8c586548 100644 --- a/nixos/home-manager.nix +++ b/nixos/home-manager.nix @@ -185,6 +185,9 @@ in { programs.ssh = { enable = true; enableDefaultConfig = false; + extraConfig = '' + Include /home/imalison/config/dotfiles/ssh/config + ''; matchBlocks = { "*" = { forwardAgent = true; @@ -201,6 +204,8 @@ in { }; }; + home.file.".ssh/config".force = true; + services.gpg-agent = { enable = true; defaultCacheTtl = 8 * 60 * 60; diff --git a/nixos/secrets.nix b/nixos/secrets.nix index 2e41aa4c..c5df4648 100644 --- a/nixos/secrets.nix +++ b/nixos/secrets.nix @@ -1,61 +1,94 @@ -{ inputs, pkgs, ... }: { - home-manager.users.imalison = ({ config, ... }: { - imports = [ inputs.agenix.homeManagerModules.default ]; - age.identityPaths = [ "${config.home.homeDirectory}/.ssh/id_ed25519" ]; +{ + inputs, + pkgs, + ... +}: { + home-manager.users.imalison = {config, ...}: { + imports = [inputs.agenix.homeManagerModules.default]; + age.identityPaths = ["${config.home.homeDirectory}/.ssh/id_ed25519"]; home.packages = [ inputs.agenix.packages."${pkgs.stdenv.hostPlatform.system}".default ]; age.secrets.gpg-keys.file = ./secrets/gpg-keys.age; age.secrets.gpg-passphrase.file = ./secrets/gpg-passphrase.age; + age.secrets.gws-client-secret.file = ./secrets/gws-client-secret.json.age; + + home.sessionVariables.GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE = "${config.xdg.configHome}/gws/client_secret.json"; + systemd.user.services.import-gpg-key = { Unit = { Description = "Import GPG private key"; - After = [ "agenix.service" ]; + After = ["agenix.service"]; # 3 total retries StartLimitIntervalSec = 0; StartLimitBurst = 3; }; - Install.WantedBy = [ "default.target" ]; + Install.WantedBy = ["default.target"]; Service = { Type = "oneshot"; RestartSec = 5; Restart = "on-failure"; - ExecStart = - let - replace = builtins.replaceStrings [ "$XDG_RUNTIME_DIR" ] [ "\${XDG_RUNTIME_DIR}" ]; - path = replace config.age.secrets.gpg-keys.path; - passphrasePath = replace config.age.secrets.gpg-passphrase.path; - importScript = pkgs.writeShellScript "import-gpg-key" '' - set -eu + ExecStart = let + replace = builtins.replaceStrings ["$XDG_RUNTIME_DIR"] ["\${XDG_RUNTIME_DIR}"]; + path = replace config.age.secrets.gpg-keys.path; + passphrasePath = replace config.age.secrets.gpg-passphrase.path; + importScript = pkgs.writeShellScript "import-gpg-key" '' + set -eu - normalized_key_file="$(mktemp)" - trap 'rm -f "$normalized_key_file"' EXIT + normalized_key_file="$(mktemp)" + trap 'rm -f "$normalized_key_file"' EXIT - # Some historical exports omitted the required blank line after the - # armor header. GnuPG imports the keys but exits non-zero, which - # leaves the unit in a failed state. - awk ' - pending_blank { - if ($0 != "") { - print "" - } - pending_blank = 0 + # Some historical exports omitted the required blank line after the + # armor header. GnuPG imports the keys but exits non-zero, which + # leaves the unit in a failed state. + awk ' + pending_blank { + if ($0 != "") { + print "" } - { print } - /^-----BEGIN PGP PRIVATE KEY BLOCK-----$/ { - pending_blank = 1 - } - ' ${path} > "$normalized_key_file" + pending_blank = 0 + } + { print } + /^-----BEGIN PGP PRIVATE KEY BLOCK-----$/ { + pending_blank = 1 + } + ' ${path} > "$normalized_key_file" - exec ${pkgs.gnupg}/bin/gpg \ - --batch \ - --pinentry-mode loopback \ - --passphrase-file ${passphrasePath} \ - --import "$normalized_key_file" - ''; - in "${importScript}"; + exec ${pkgs.gnupg}/bin/gpg \ + --batch \ + --pinentry-mode loopback \ + --passphrase-file ${passphrasePath} \ + --import "$normalized_key_file" + ''; + in "${importScript}"; }; }; - }); + + systemd.user.services.link-gws-client-secret = { + Unit = { + Description = "Link gws client secret"; + After = ["agenix.service"]; + }; + + Install.WantedBy = ["default.target"]; + Service = { + Type = "oneshot"; + ExecStart = let + replace = builtins.replaceStrings ["$XDG_RUNTIME_DIR"] ["\${XDG_RUNTIME_DIR}"]; + secretPath = replace config.age.secrets.gws-client-secret.path; + linkScript = pkgs.writeShellScript "link-gws-client-secret" '' + set -eu + + config_dir="${config.xdg.configHome}/gws" + target="${secretPath}" + link_path="$config_dir/client_secret.json" + + mkdir -p "$config_dir" + ln -sfn "$target" "$link_path" + ''; + in "${linkScript}"; + }; + }; + }; } diff --git a/nixos/secrets/secrets.nix b/nixos/secrets/secrets.nix index 31c54fd7..85ecbb69 100644 --- a/nixos/secrets/secrets.nix +++ b/nixos/secrets/secrets.nix @@ -1,8 +1,9 @@ -let keys = (import ../keys.nix); -in -{ +let + keys = import ../keys.nix; +in { "gpg-keys.age".publicKeys = keys.agenixKeys; "gpg-passphrase.age".publicKeys = keys.agenixKeys; + "gws-client-secret.json.age".publicKeys = keys.agenixKeys; "cache-priv-key.pem.age".publicKeys = keys.agenixKeys; "gitea-runner-token.biskcomp.age".publicKeys = keys.agenixKeys; "gitea-runner-token.ryzen-shine.age".publicKeys = keys.agenixKeys;