Skip to content

feat(matic): hyprland + keyd hotkeys - #749

Merged
shunkakinoki merged 50 commits into
mainfrom
feat/matic-hyprland-keyd
Feb 6, 2026
Merged

feat(matic): hyprland + keyd hotkeys#749
shunkakinoki merged 50 commits into
mainfrom
feat/matic-hyprland-keyd

Conversation

@shunkakinoki

@shunkakinoki shunkakinoki commented Feb 6, 2026

Copy link
Copy Markdown
Owner

Summary\n- add Hyprland config for matic with Super+T/S/G app launchers via gtk-launch\n- add keyd config (Caps Lock + Framework key -> Super, Right Shift double-tap -> Caps Lock)\n- wire agenix GitHub SSH key deployment for matic and add matic pubkey to galactica secrets\n\n## Testing\n- make build HOST=matic\n\n## Manual steps\n- On galactica: make rekey-galactica && git add named-hosts/galactica/keys && git commit -m "chore(agenix): rekey for matic" && git push\n- On matic: sudo make switch HOST=matic\n- Verify keyd/framework key mapping with: sudo keyd monitor


Summary by cubic

Switch matic to Hyprland with Super-based hotkeys and keyd mappings for Caps/Right Alt, plus mac-style Command on the Framework key. Also deploy the GitHub SSH key via agenix, import the GPG key for signing, and add matic’s pubkey to galactica.

  • New Features

    • Hyprland session with xwayland, portals, PipeWire audio, Bluetooth, and hyprexpo. Waybar, SwayNC, Wofi, Wlogout, hypridle/hyprlock, wob, and GTK Adwaita-dark configured.
    • Super app hotkeys for ghostty, chrome, slack, code, 1password, signal, and yazi.
    • keyd: Caps Lock and Right Alt → Super; Framework key → mac-style Command; Function row → media keys; Right Shift double-tap → Caps Lock.
    • Agenix: deploy GitHub SSH key to ~/.ssh/id_ed25519_github and import GPG key for commit signing; ensure ~/.ssh exists; add matic key to galactica’s decrypt set.
  • Migration

    • On galactica: make rekey-galactica; commit/push keys.
    • On matic: sudo make switch HOST=matic.
    • Verify keyd: sudo keyd monitor.

Written for commit 0d18047. Summary will update on new commits.

Copilot AI review requested due to automatic review settings February 6, 2026 09:01
@mesa-dot-dev

mesa-dot-dev Bot commented Feb 6, 2026

Copy link
Copy Markdown

You do not have enough credits to review this pull request. Please purchase more credits to continue.

@coderabbitai

coderabbitai Bot commented Feb 6, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR establishes a comprehensive Wayland-based desktop environment for the "matic" host, introducing Hyprland window manager with plugins, idle/lock screens, keyd key remapping, and desktop applications (SwayNC notifications, Waybar statusbar, Wofi launcher, Wlogout menu) alongside GTK dark theming, new secrets management, and configuration infrastructure refactoring.

Changes

Cohort / File(s) Summary
Hyprland Window Manager
config/hyprland/default.nix, config/hyprland/hyprland.conf, config/hyprland/hypridle.conf, config/hyprland/hyprlock.conf
Adds complete Hyprland desktop environment setup with window manager configuration, plugin integration (hyprexpo), idle/lock screen behavior, and Dracula-inspired theming.
Keyd Key Remapping
config/keyd/default.nix, config/keyd/default.conf
Enables keyd service with systemd capabilities and adds key mapping configuration for Caps Lock, Right Alt, and macOS-style command layer bindings.
Desktop UI Stack
config/swaync/*, config/waybar/*, config/wofi/*, config/wlogout/*
Adds complete configuration for notification center (SwayNC), statusbar (Waybar), application launcher (Wofi), and logout menu (Wlogout) with CSS styling and JSON/layout configs.
GTK and Ghostty Theming
config/gtk/default.nix, config/ghostty/config
Enables dark GTK theme (Adwaita-dark) with dconf settings and adds Ctrl+C/V clipboard keybinds to Ghostty terminal.
Desktop Package Additions
home-manager/packages/default.nix
Adds 21 desktop-related packages including brightnessctl, cliphist, grim, waybar, wob, wofi, swappy, and notification/portal components.
Matic Host Configuration
named-hosts/matic/default.nix, named-hosts/matic/secrets.nix, named-hosts/galactica/secrets.nix
Introduces new "matic" host with keyd import, agenix/age-based SSH and GPG key management, activation hooks, GPG/SSH agent configuration, and Hyprland desktop session setup.
Configuration Infrastructure
config/default.nix, home-manager/default.nix, config/codex/config.toml
Wraps config/default.nix as a function to conditionally include desktop modules (gtk, hyprland, swaync, waybar, wlogout, wofi) based on isDesktop flag; updates config imports; adds codex model settings.
Build Configuration
Makefile
Adds Hyprland Cachix substituter and trusted key; introduces systemctl-waybar target with kill/restart sequence for Waybar service.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

enhancement

Poem

🐰 A desktop takes shape, all wavy and bright,
With Hyprland dancing in Wayland's soft light,
Keyd taps, Waybar glows, and SwayNC shines sweet,
The matic host wakes with secrets so neat!
Configuration hops forward with care. 🌙✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'feat(matic): hyprland + keyd hotkeys' directly and clearly summarizes the primary changes: adding Hyprland and keyd configuration for the matic host.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The PR description is directly related to the changeset, detailing Hyprland configuration, keyd mappings, agenix SSH key deployment, and testing/migration steps that align with the file changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/matic-hyprland-keyd

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @shunkakinoki, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly upgrades the matic host's user experience and security posture. It transitions the desktop environment from GNOME to the more modern and customizable Hyprland, coupled with keyd for enhanced keyboard control and hotkey management. Furthermore, it streamlines the secure management of sensitive credentials by integrating agenix for automated GitHub SSH key deployment, ensuring that matic can access necessary resources securely and efficiently.

Highlights

  • Hyprland Desktop Environment: The matic host has been configured to transition from GNOME to Hyprland as its primary desktop environment, including the setup of essential components like XDG portals for better application integration.
  • Custom Keyd Mappings: New keyd configurations have been introduced to remap Caps Lock and the Framework key to act as the Super key, and to enable Caps Lock functionality via a double-tap of the Right Shift key.
  • Secure SSH Key Deployment with Agenix: Agenix integration has been set up for matic to securely deploy the GitHub SSH key, ensuring that the key is placed in the correct location (~/.ssh/id_ed25519_github) with appropriate permissions, and that the SSH directory exists prior to deployment.
  • New Package Additions: The home-manager package list for desktop Linux environments has been expanded to include xclip, emote, and vscode.
Changelog
  • config/hyprland/default.nix
    • Added a NixOS module to manage Hyprland configuration files via xdg.configFile.
  • config/hyprland/hyprland.conf
    • Introduced the core Hyprland configuration, defining the Super key and setting up Super+T/S/G hotkeys for ghostty, slack, and google-chrome respectively.
  • config/keyd/default.conf
    • Defined custom keyboard mappings, reassigning Caps Lock and the Framework key to Super, and implementing a double-tap Right Shift for Caps Lock.
  • config/keyd/default.nix
    • Enabled the keyd service in NixOS and configured it to use the custom default.conf, including optional setgid capability bounding.
  • home-manager/packages/default.nix
    • Expanded the list of default packages for desktop Linux systems to include xclip, emote, and vscode.
  • named-hosts/galactica/secrets.nix
    • Updated the galactica host's secrets definition to include matic's SSH public key, allowing matic to decrypt shared secrets.
  • named-hosts/matic/default.nix
    • Integrated the keyd configuration module.
    • Transitioned the desktop environment from GNOME to Hyprland, configuring programs.hyprland, programs.dconf, and xdg.portal with Hyprland-specific components.
    • Enhanced home-manager configuration to include the Hyprland module and set up agenix for deploying the GitHub SSH key to ~/.ssh/id_ed25519_github with appropriate permissions, including a pre-activation step to ensure the SSH directory exists.
  • named-hosts/matic/secrets.nix
    • Created a new secrets file for matic, defining its own SSH public key and configuring the deployment of the shared GitHub SSH key.
Activity
  • No specific activity (comments, reviews, progress) has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@mesa-dot-dev

mesa-dot-dev Bot commented Feb 6, 2026

Copy link
Copy Markdown

Mesa Description

TL;DR

Switched the matic host from GNOME to Hyprland with comprehensive configurations for a modern Wayland desktop, including custom hotkeys via keyd, extensive styling for Waybar, SwayNC, Wofi, and Wlogout, and enhanced security with agenix for GitHub SSH and GPG key deployment.

What changed?

  • Hyprland Desktop Environment:
    • named-hosts/matic/default.nix updated to switch to Hyprland and enable related services (geoclue2, localtimed, PipeWire, Bluetooth).
    • config/hyprland/default.nix, hyprland.conf, hypridle.conf, hyprlock.conf added for complete Hyprland setup, including hyprexpo, Dracula theme, hotkeys for app launching and window management, idle behavior, and a lock screen.
    • config/default.nix now conditionally includes desktop-specific configurations for GTK, Hyprland, SwayNC, Waybar, Wlogout, and Wofi.
    • home-manager/packages/default.nix expanded with numerous Linux desktop utilities (e.g., brightnessctl, grim, slurp, swappy, waybar, wofi).
  • Keyd Custom Keybindings:
    • config/keyd/default.nix and default.conf added to enable and configure the keyd service, mapping Caps Lock and Right Alt to Super, the Framework key to mac-style Command, and Right Shift double-tap to Caps Lock.
    • home-manager/programs/fish/functions/fish_user_key_bindings.fish updated to support clipboard paste with keyd Framework key.
  • Agenix Secrets Management:
    • named-hosts/galactica/secrets.nix updated to include matic's SSH public key for shared secret decryption.
    • named-hosts/matic/default.nix expanded to manage GitHub SSH and GPG keys via agenix, including GPG agent configuration.
    • named-hosts/matic/secrets.nix updated for host-specific encrypted secrets.
  • Desktop Component Styling and Configuration:
    • config/gtk/default.nix added to configure GTK with the 'Adwaita-dark' theme.
    • New configuration and styling files (config.json/style.css/default.nix) added for swaync (notification center), waybar (status bar), wlogout (logout menu), and wofi (app launcher), all adopting a Dracula-inspired aesthetic.
    • config/ghostty/config added standard Ctrl+C/Ctrl+V keybindings.
  • Infrastructure & Dependencies:
    • Makefile updated to add hyprland.cachix.org as a Nix substituter and a new systemctl-waybar target.
    • flake.lock updated for dependency refreshes.
    • config/codex/config.toml added new model and model_reasoning_effort options.

Description generated by Mesa. Update settings

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces Hyprland and keyd configuration for the matic host, and sets up agenix for SSH key deployment. The changes are generally well-structured. I've added a couple of comments with suggestions to improve maintainability by refactoring the agenix configuration to be more idiomatic and by centralizing SSH public key definitions to avoid duplication.

Comment on lines +236 to +252
age.identityPaths = [ "/home/${username}/.ssh/id_ed25519" ];
age.secrets = builtins.mapAttrs (
name: value:
{
file = value.file;
}
// (
if name == "keys/id_github.age" then
{
# Deploy GitHub SSH key to ~/.ssh/ with correct permissions
path = "/home/${username}/.ssh/id_ed25519_github";
mode = "0600";
}
else
{ }
)
) (import ./secrets.nix);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This block can be improved in two ways:

  1. Use ${config.home.homeDirectory} instead of hardcoding /home/${username} to make paths more robust.
  2. Use lib.optionalAttrs for conditionally adding attributes. This is more idiomatic and readable than if ... then ... else {} with attribute set merging.
          age.identityPaths = [ "${config.home.homeDirectory}/.ssh/id_ed25519" ];
          age.secrets = builtins.mapAttrs (
            name: value:
              {
                file = value.file;
              }
              // lib.optionalAttrs (name == "keys/id_github.age") {
                # Deploy GitHub SSH key to ~/.ssh/ with correct permissions
                path = "${config.home.homeDirectory}/.ssh/id_ed25519_github";
                mode = "0600";
              }
          ) (import ./secrets.nix);

Comment on lines +5 to +14
let
# Galactica's SSH public key
galactica = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEKze2jlpV7SyTKA2ezqbumpCiDn+5Sj4z5SxrqfzesX shunkakinoki@gmail.com";
# Matic's SSH public key
matic = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIQqWYSDaaoazVNOrAimCpUxNgaLe9Von237zIoox3E5 skakinoki@matic";
# All machines that can decrypt shared secrets
allMachines = [
galactica
matic
];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The public SSH keys for galactica and matic are defined here, and also in named-hosts/galactica/secrets.nix. This duplication can make key management difficult. Consider creating a central file (e.g., lib/pubkeys.nix) to store all public keys and then import them where needed. This would centralize key definitions and simplify maintenance.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 8 files

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds Hyprland window manager support and keyboard customization for the matic host (Framework 13" AMD AI 300 laptop). It configures Caps Lock and Framework keys to act as Super modifiers via keyd, sets up Hyprland with basic app launcher hotkeys (Super+T/S/G), and deploys GitHub SSH keys using agenix.

Changes:

  • Switch from GNOME to Hyprland as the desktop environment for matic
  • Add keyd configuration for keyboard remapping (Caps Lock/Framework key → Super, Right Shift double-tap → Caps Lock)
  • Configure Hyprland with minimal setup and three app launcher hotkeys
  • Enable agenix secret management for GitHub SSH key deployment from galactica to matic
  • Add utility packages (xclip, emote, vscode) for Linux desktop environment

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
named-hosts/matic/secrets.nix Defines agenix secrets for matic, including SSH key sharing from galactica
named-hosts/galactica/secrets.nix Adds matic's public key to galactica's authorized machines list
named-hosts/matic/default.nix Switches from GNOME to Hyprland, adds keyd module, configures agenix deployment in home-manager
config/keyd/default.nix NixOS module to enable keyd service with custom configuration
config/keyd/default.conf Keyd configuration for keyboard remapping
config/hyprland/default.nix Home-manager module to deploy Hyprland configuration
config/hyprland/hyprland.conf Minimal Hyprland configuration with app launcher hotkeys
home-manager/packages/default.nix Adds xclip, emote, and vscode packages for Linux desktop

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

services.xserver.enable = true;
services.displayManager.gdm.enable = true;
services.desktopManager.gnome.enable = true;
services.displayManager.defaultSession = "hyprland";

Copilot AI Feb 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The defaultSession is set to "hyprland", but with withUWSM = true, the session might be named "hyprland-uwsm" instead. UWSM (Universal Wayland Session Manager) typically registers a separate session file. Verify the correct session name after building, or remove the defaultSession line to allow manual selection at login. You can check available sessions in /usr/share/wayland-sessions/ or /usr/share/xsessions/.

Suggested change
services.displayManager.defaultSession = "hyprland";

Copilot uses AI. Check for mistakes.
Comment thread config/keyd/default.conf
f13 = layer(meta)

# Right Shift double-tap -> Caps Lock (tap twice)
rightshift = overload(shift, oneshot(rshift))

Copilot AI Feb 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The keyd configuration for rightshift double-tap is incorrect. The syntax overload(shift, oneshot(rshift)) is invalid because overload's second argument should be a key, not a layer operation. For a double-tap to capslock functionality, the correct syntax should be rightshift = overload(rshift, rightshift) which makes rightshift activate the rshift layer when held and emit rightshift when tapped. Combined with the [rshift] layer mapping rightshift to capslock, this creates the double-tap effect.

Suggested change
rightshift = overload(shift, oneshot(rshift))
rightshift = overload(rshift, rightshift)

Copilot uses AI. Check for mistakes.
home.activation.ensureSshDirectory = config.lib.dag.entryBefore [ "writeBoundary" ] ''
$DRY_RUN_CMD mkdir -p $VERBOSE_ARG ${config.home.homeDirectory}/.ssh
$DRY_RUN_CMD chmod $VERBOSE_ARG 700 ${config.home.homeDirectory}/.ssh
'';

Copilot AI Feb 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The agenix configuration is missing manual secret deployment activation scripts. Looking at the kyber configuration (named-hosts/kyber/default.nix:74-94), there's additional setup including ensureAgenixDirectory and deployAgenixSecrets activation hooks. Without these, the GitHub SSH key may not be deployed properly during home-manager activation, especially on first run. Consider adding similar activation hooks to ensure the secret is properly decrypted and deployed.

Suggested change
'';
'';
# Ensure agenix-related directory exists (mirrors kyber host setup)
home.activation.ensureAgenixDirectory = config.lib.dag.entryBefore [ "writeBoundary" ] ''
AGENIX_DIR="${config.home.homeDirectory}/.local/share/agenix"
$DRY_RUN_CMD mkdir -p $VERBOSE_ARG "$AGENIX_DIR"
$DRY_RUN_CMD chmod $VERBOSE_ARG 700 "$AGENIX_DIR"
'';
# Manually deploy agenix secrets during home-manager activation
home.activation.deployAgenixSecrets = config.lib.dag.entryAfter [ "ensureAgenixDirectory" ] ''
if [ -x "${pkgs.agenix}/bin/agenix" ]; then
$DRY_RUN_CMD ${pkgs.agenix}/bin/agenix -r
fi
'';

Copilot uses AI. Check for mistakes.
Comment thread config/hyprland/hyprland.conf Outdated
# Global hotkeys (Caps Lock / Framework key mapped to Super)
bind = $mod, T, exec, gtk-launch ghostty
bind = $mod, S, exec, gtk-launch slack
bind = $mod, G, exec, gtk-launch google-chrome

Copilot AI Feb 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Hyprland configuration is missing essential window management keybindings. Users won't be able to close windows, switch workspaces, move windows, resize windows, or perform other basic window management tasks. Consider adding at least the following: window close (e.g., $mod+Q), workspace switching (e.g., $mod+1-9), window focus movement (e.g., $mod+arrow keys), window movement between workspaces, and window resize mode. Without these, the desktop environment will be difficult to use effectively.

Suggested change
bind = $mod, G, exec, gtk-launch google-chrome
bind = $mod, G, exec, gtk-launch google-chrome
# --------------------------------------------------------
# Essential window management keybindings
# --------------------------------------------------------
# Close focused window
bind = $mod, Q, killactive
# Workspace switching ($mod + 1-9)
bind = $mod, 1, workspace, 1
bind = $mod, 2, workspace, 2
bind = $mod, 3, workspace, 3
bind = $mod, 4, workspace, 4
bind = $mod, 5, workspace, 5
bind = $mod, 6, workspace, 6
bind = $mod, 7, workspace, 7
bind = $mod, 8, workspace, 8
bind = $mod, 9, workspace, 9
# Move focused window to workspace ($mod + Shift + 1-9)
bind = $mod SHIFT, 1, movetoworkspace, 1
bind = $mod SHIFT, 2, movetoworkspace, 2
bind = $mod SHIFT, 3, movetoworkspace, 3
bind = $mod SHIFT, 4, movetoworkspace, 4
bind = $mod SHIFT, 5, movetoworkspace, 5
bind = $mod SHIFT, 6, movetoworkspace, 6
bind = $mod SHIFT, 7, movetoworkspace, 7
bind = $mod SHIFT, 8, movetoworkspace, 8
bind = $mod SHIFT, 9, movetoworkspace, 9
# Move focus between windows with arrow keys
bind = $mod, left, movefocus, l
bind = $mod, right, movefocus, r
bind = $mod, up, movefocus, u
bind = $mod, down, movefocus, d
# Resize mode: enter with $mod+R, then use arrows; exit with Escape or R
bind = $mod, R, submap, resize
submap = resize
bind = , left, resizeactive, -10 0
bind = , right, resizeactive, 10 0
bind = , up, resizeactive, 0 -10
bind = , down, resizeactive, 0 10
# Exit resize mode
bind = , escape, submap, reset
bind = , R, submap, reset
# Return to the default keymap
submap = reset

Copilot uses AI. Check for mistakes.
Comment thread config/hyprland/hyprland.conf Outdated
Comment on lines +7 to +9
bind = $mod, T, exec, gtk-launch ghostty
bind = $mod, S, exec, gtk-launch slack
bind = $mod, G, exec, gtk-launch google-chrome

Copilot AI Feb 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The gtk-launch commands assume specific .desktop file names (ghostty, slack, google-chrome) that may not match the actual desktop file names installed by the packages. For example, ghostty might install as com.mitchellh.ghostty.desktop. Verify the actual desktop file names after installation by checking /usr/share/applications/ or ~/.local/share/applications/, and update the hyprland.conf accordingly if needed. You can test with: ls /usr/share/applications/ | grep -E 'ghostty|slack|google-chrome'

Suggested change
bind = $mod, T, exec, gtk-launch ghostty
bind = $mod, S, exec, gtk-launch slack
bind = $mod, G, exec, gtk-launch google-chrome
bind = $mod, T, exec, gtk-launch com.mitchellh.ghostty.desktop
bind = $mod, S, exec, gtk-launch slack.desktop
bind = $mod, G, exec, gtk-launch google-chrome.desktop

Copilot uses AI. Check for mistakes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@config/keyd/default.nix`:
- Around line 3-12: Reorder the top-level attributes so they are alphabetical
(place environment before services, services before systemd, systemd before
users) and replace the manual file drop-in
environment.etc."keyd/default.conf".source with the declarative
services.keyd.keyboards configuration (use services.keyd.keyboards.<name>.device
and services.keyd.keyboards.<name>.extraConfig to move the current default.conf
contents into the module-managed keymap), while keeping users.groups.keyd and
systemd.services.keyd.serviceConfig (CapabilityBoundingSet/AmbientCapabilities)
as-is to preserve the setgid workarounds.

In `@named-hosts/matic/default.nix`:
- Around line 98-105: Update services.displayManager.defaultSession from
"hyprland" to "hyprland-uwsm" so it matches the session name generated when
programs.hyprland.withUWSM = true; locate the
services.displayManager.defaultSession setting and replace the value, ensuring
programs.hyprland.withUWSM remains true so the registered session
hyprland-uwsm.desktop is selected by the display manager.
- Around line 219-231: The import of the home-manager module is passing an empty
config (config = { }) which breaks evaluation because submodules reference
config.home.*; replace the empty object with the enclosing home-manager config
by passing the actual config through (e.g. change config = { } to config =
config) so the imported module sees the real home config values; update the
imports block where the home-manager is imported (the expression that sets
inputs and overrides host/isDesktop) to forward the parent config rather than an
empty object.
🧹 Nitpick comments (4)
home-manager/packages/default.nix (1)

117-117: Consider adding wl-clipboard alongside or instead of xclip for Wayland.

Since matic uses Hyprland (a Wayland compositor), xclip only works under X11/XWayland. For native Wayland clipboard support, wl-clipboard (wl-copy/wl-paste) is the standard tool. You may want to add it here or in the desktop section.

named-hosts/matic/secrets.nix (1)

19-20: Naming mismatch between secret key and source file — verify intent.

The secret is named "keys/id_github.age" but the source is ../galactica/keys/id_ed25519.age. The comments explain the relationship, but the naming difference (id_github vs id_ed25519) could confuse future maintainers. Consider aligning the names or adding a brief inline note on line 20 clarifying that id_ed25519 is the GitHub key on galactica.

config/hyprland/default.nix (1)

1-7: Simple and modular — consider dropping the unused config parameter.

config is declared in the function signature but never referenced in the body. You can simplify to { ... }:.

Suggested diff
-{ config, ... }:
+{ ... }:
 {
   xdg.configFile."hypr/hyprland.conf" = {
     source = ./hyprland.conf;
     force = true;
   };
 }
named-hosts/matic/default.nix (1)

237-252: Fragile secret-name matching via string comparison.

The if name == "keys/id_github.age" check is brittle — if the key in secrets.nix is renamed or a new key is added that also needs custom deployment, this silently falls through to the else branch with no path or permissions set.

Consider matching on a structured attribute (e.g., a type or target field in secrets.nix) or defining the deployment config explicitly per-secret rather than relying on name-based dispatch within mapAttrs.

♻️ Example: explicit per-secret config instead of name-matching
-          age.secrets = builtins.mapAttrs (
-            name: value:
-            {
-              file = value.file;
-            }
-            // (
-              if name == "keys/id_github.age" then
-                {
-                  # Deploy GitHub SSH key to ~/.ssh/ with correct permissions
-                  path = "/home/${username}/.ssh/id_ed25519_github";
-                  mode = "0600";
-                }
-              else
-                { }
-            )
-          ) (import ./secrets.nix);
+          age.secrets."keys/id_github.age" = {
+            file = (import ./secrets.nix)."keys/id_github.age".file;
+            path = "/home/${username}/.ssh/id_ed25519_github";
+            mode = "0600";
+          };

Comment thread config/keyd/default.nix
Comment on lines +3 to +12
services.keyd.enable = true;

# Optional: silence the setgid warning (nice to have, not required for functionality)
users.groups.keyd = { };
systemd.services.keyd.serviceConfig = {
CapabilityBoundingSet = [ "CAP_SETGID" ];
AmbientCapabilities = [ "CAP_SETGID" ];
};

environment.etc."keyd/default.conf".source = ./default.conf;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

NixOS keyd module services.keyd.keyboards configuration

💡 Result:

In NixOS, services.keyd.keyboards is an attribute set where each attribute name becomes a config file under /etc/keyd/<name>.conf (e.g. default/etc/keyd/default.conf). (mynixos.com)

Each keyboard entry is a submodule with (mainly) these options:

  • services.keyd.keyboards.<name>.ids: list of device IDs as shown by keyd monitor (default is ["*"]; you can also exclude devices with a leading -). (mynixos.com)
  • services.keyd.keyboards.<name>.settings: an attrset that maps to INI sections like [main], [control], etc. (this becomes the file content excluding the [ids] section). (mynixos.com)
  • services.keyd.keyboards.<name>.extraConfig: raw text appended to the end of the generated file (useful for things that must be declared after other layers). (mynixos.com)

Example configuration.nix:

{
  services.keyd = {
    enable = true;

    keyboards = {
      default = {
        ids = [ "*" ];
        settings = {
          main = {
            capslock = "overload(control, esc)";
          };
        };
      };

      externalKeyboard = {
        ids = [ "1ea7:0907" ];
        settings = {
          main = { esc = "capslock"; };
        };
      };
    };
  };
}

This structure matches the NixOS option example and will generate the corresponding /etc/keyd/*.conf files. (mynixos.com)

Citations:


🏁 Script executed:

#!/bin/bash
# Search for any references to services.keyd in the codebase
rg 'services\.keyd' --type nix -C 2

Repository: shunkakinoki/dotfiles

Length of output: 570


🏁 Script executed:

#!/bin/bash
# Search for keyd module documentation or implementation in nixpkgs
web_search_result=$(rg -i 'keyd' --type nix | head -20)
echo "=== Keyd references in repo ==="
echo "$web_search_result"

# Search for any documentation or comments about group/capability setup
rg -i 'setgid|CAP_SETGID' --type nix -C 3

Repository: shunkakinoki/dotfiles

Length of output: 1076


🌐 Web query:

NixOS nixpkgs keyd service module automatic group creation capabilities

💡 Result:

In current nixpkgs/NixOS, the services.keyd module does not have “automatic group creation” knobs: it only exposes services.keyd.enable, services.keyd.keyboards.*, and services.keyd.package options (i.e., nothing like group, user, extraGroups, etc.). [1]

That matters because keyd itself uses a keyd Unix group for its application-specific remapping helper (“Add yourself to the keyd group”). NixOS does not implicitly create/manage that group via the services.keyd module, so if you need it you must create it yourself and add users to it. [2]

Example NixOS config:

{
  services.keyd.enable = true;

  users.groups.keyd = {};                 # create the group
  users.users.<you>.extraGroups = [ "keyd" ];
}

Sources: MyNixOS option index for services.keyd (shows available options only) [1]; upstream keyd documentation about the keyd group [2].


Sort top-level attributes alphabetically and consider using services.keyd.keyboards for declarative config management.

The top-level attributes in this module should be ordered alphabetically: environment before services, services before systemd, and systemd before users.

Separately, NixOS's services.keyd module provides services.keyd.keyboards for declaratively defining key mappings and device IDs, which automatically generates /etc/keyd/*.conf files. This would eliminate the need for environment.etc."keyd/default.conf".source. The keyboards option also supports extraConfig for raw keyd syntax (e.g., overload, oneshot), so you don't lose fine-grained control. The manual users.groups.keyd and capability configuration remain necessary—they are not handled automatically by the module.

🤖 Prompt for AI Agents
In `@config/keyd/default.nix` around lines 3 - 12, Reorder the top-level
attributes so they are alphabetical (place environment before services, services
before systemd, systemd before users) and replace the manual file drop-in
environment.etc."keyd/default.conf".source with the declarative
services.keyd.keyboards configuration (use services.keyd.keyboards.<name>.device
and services.keyd.keyboards.<name>.extraConfig to move the current default.conf
contents into the module-managed keymap), while keeping users.groups.keyd and
systemd.services.keyd.serviceConfig (CapabilityBoundingSet/AmbientCapabilities)
as-is to preserve the setgid workarounds.

Comment on lines +98 to +105
services.displayManager.defaultSession = "hyprland";
services.desktopManager.gnome.enable = false;

programs.hyprland = {
enable = true;
xwayland.enable = true;
withUWSM = true;
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Check for withUWSM references in the codebase
rg -n 'withUWSM' --type nix

Repository: shunkakinoki/dotfiles

Length of output: 125


🏁 Script executed:

# Check session registration and naming patterns
rg -n 'hyprland.*session\|sessionName\|defaultSession' --type nix -A 2 -B 2

Repository: shunkakinoki/dotfiles

Length of output: 47


🏁 Script executed:

# Check the nixpkgs revision to understand available options
git ls-files | grep -E '(flake.nix|flake.lock)' | head -5

Repository: shunkakinoki/dotfiles

Length of output: 85


🏁 Script executed:

# Examine the file in question for full context
cat -n named-hosts/matic/default.nix | sed -n '90,120p'

Repository: shunkakinoki/dotfiles

Length of output: 1136


🏁 Script executed:

# Check the nixpkgs revision in flake.nix
cat flake.nix

Repository: shunkakinoki/dotfiles

Length of output: 6990


🏁 Script executed:

# Search for UWSM-related session naming patterns
rg -n 'uwsm|UWSM|hyprland-uwsm' --type nix -i

Repository: shunkakinoki/dotfiles

Length of output: 125


🏁 Script executed:

# Check home-manager modules for Hyprland configuration reference
fd 'hyprland' --type f --extension nix | head -10

Repository: shunkakinoki/dotfiles

Length of output: 47


🏁 Script executed:

# Search for any references to session names or display manager setup
rg -n 'gdm|displayManager' --type nix -B 2 -A 2 | head -40

Repository: shunkakinoki/dotfiles

Length of output: 511


🌐 Web query:

Hyprland withUWSM nixpkgs session name registration

💡 Result:

In nixpkgs/NixOS, session registration for Hyprland+UWSM is done by generating a display-manager .desktop entry.

1) Let the Hyprland module register it (most common)

{
  programs.hyprland = {
    enable = true;
    withUWSM = true;
  };
}

This generates a desktop entry named hyprland-uwsm.desktop, which shows up in display managers (often labeled like “Hyprland (UWSM)”). [1]

2) Register/override the session explicitly via UWSM (if you need to control the name)

Use programs.uwsm.waylandCompositors, which creates the desktop entry used by display managers: [2]

{
  programs.uwsm.enable = true;

  programs.uwsm.waylandCompositors.hyprland = {
    prettyName = "Hyprland (UWSM)";
    comment = "Hyprland compositor managed by UWSM";
    binPath = "/run/current-system/sw/bin/Hyprland";
  };
}

3) Setting the “default session” name

If your display manager supports it, the session you want is typically:

  • desktop file: hyprland-uwsm.desktop [1]
  • session name (often without suffix): hyprland-uwsm

So you’d usually try:

services.displayManager.defaultSession = "hyprland-uwsm";

Sources: Hyprland wiki (NixOS + UWSM) notes withUWSM generates hyprland-uwsm.desktop [1]; nixpkgs option docs explain programs.uwsm.waylandCompositors generates the DM desktop entry [2].

[1] https://wiki.hypr.land/Useful-Utilities/Systemd-start/
[2] https://mynixos.com/nixpkgs/option/programs.uwsm.waylandCompositors


🌐 Web query:

NixOS Hyprland UWSM session defaultSession hyprland-uwsm

💡 Result:

To make the UWSM-managed Hyprland session the default on NixOS, you need two things:

  1. Have Nix generate the UWSM desktop entry
programs.hyprland = {
  enable = true;
  withUWSM = true;
};

This creates the display-manager session file hyprland-uwsm.desktop. [1][2]

  1. Set the display manager default session to that entry’s name
services.displayManager.defaultSession = "hyprland-uwsm";

(Usually you use the session name without the .desktop suffix.)

If you’re using GDM and it still boots plain “Hyprland”

GDM often remembers the last session per user (via AccountsService), which can override defaultSession. This is a common gotcha specifically with GDM + hyprland-uwsm. [3]

If you want, tell me which display manager you’re using (GDM/SDDM/greetd/etc.) and whether you use auto-login, and I’ll give the exact fix for that DM.

Sources: [1] [2] [3]


Change defaultSession to "hyprland-uwsm" to match the session registered with withUWSM = true.

When programs.hyprland.withUWSM = true is set, NixOS generates a display-manager session entry named hyprland-uwsm.desktop, not hyprland.desktop. The current services.displayManager.defaultSession = "hyprland" will not match this session name, causing GDM to fail to find the session or fall back to another option.

Update to:

services.displayManager.defaultSession = "hyprland-uwsm";
🤖 Prompt for AI Agents
In `@named-hosts/matic/default.nix` around lines 98 - 105, Update
services.displayManager.defaultSession from "hyprland" to "hyprland-uwsm" so it
matches the session name generated when programs.hyprland.withUWSM = true;
locate the services.displayManager.defaultSession setting and replace the value,
ensuring programs.hyprland.withUWSM remains true so the registered session
hyprland-uwsm.desktop is selected by the display manager.

Comment on lines +219 to +231
imports = [
(import ../../home-manager {
inherit username;
# Override host detection for matic (isDesktop = true)
inputs = inputs // {
host = (import ../../lib/host.nix) // {
isDesktop = true;
};
};
lib = inputs.nixpkgs.lib;
pkgs = pkgs;
config = { };
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -name "default.nix" -path "*/home-manager/*" | head -20

Repository: shunkakinoki/dotfiles

Length of output: 874


🏁 Script executed:

# First, let's locate the file and understand the directory structure
git ls-files | grep -E "(home-manager|named-hosts)" | head -30

Repository: shunkakinoki/dotfiles

Length of output: 1354


🏁 Script executed:

cat -n home-manager/default.nix

Repository: shunkakinoki/dotfiles

Length of output: 1241


🏁 Script executed:

# Check how programs uses config
cat -n home-manager/programs/default.nix

Repository: shunkakinoki/dotfiles

Length of output: 1475


🏁 Script executed:

# Check services module
cat -n home-manager/services/default.nix

Repository: shunkakinoki/dotfiles

Length of output: 1045


🏁 Script executed:

# Check ssh-agent service since it receives config
cat -n home-manager/services/ssh-agent/default.nix

Repository: shunkakinoki/dotfiles

Length of output: 1278


🏁 Script executed:

# Check a few individual programs to see if they use config
cat -n home-manager/programs/ssh/default.nix

Repository: shunkakinoki/dotfiles

Length of output: 2182


🏁 Script executed:

# Check git program to see if it uses config
cat -n home-manager/programs/git/default.nix

Repository: shunkakinoki/dotfiles

Length of output: 2753


🏁 Script executed:

# Check what the config module does
cat -n config/default.nix

Repository: shunkakinoki/dotfiles

Length of output: 495


🏁 Script executed:

# Check if config is actually used anywhere in home-manager/default.nix or referenced in its submodules
rg "config\." home-manager/default.nix

Repository: shunkakinoki/dotfiles

Length of output: 47


🏁 Script executed:

# Search for uses of config in the programs and services modules
rg "config\." home-manager/programs/ home-manager/services/ | head -20

Repository: shunkakinoki/dotfiles

Length of output: 2218


🏁 Script executed:

# Check the actual context in named-hosts/matic/default.nix around lines 219-231
sed -n '210,240p' named-hosts/matic/default.nix

Repository: shunkakinoki/dotfiles

Length of output: 1014


🏁 Script executed:

# Let's check what the context is around the import call and understand the module structure better
sed -n '200,245p' named-hosts/matic/default.nix

Repository: shunkakinoki/dotfiles

Length of output: 1522


🏁 Script executed:

# Let's verify which programs and services actually depend on having config pre-populated
rg "config\.home\." home-manager/programs/ | head -30

Repository: shunkakinoki/dotfiles

Length of output: 649


🏁 Script executed:

# Check if there are other similar import patterns in the codebase
rg "import .*home-manager.*config" --type nix

Repository: shunkakinoki/dotfiles

Length of output: 47


🏁 Script executed:

# Check if config is used during module evaluation in home-manager/default.nix
# Look at the programs and services that receive config
sed -n '1,30p' home-manager/programs/fnm/default.nix

Repository: shunkakinoki/dotfiles

Length of output: 821


🏁 Script executed:

# Let's check the full fnm module to understand how config is used
cat -n home-manager/programs/fnm/default.nix

Repository: shunkakinoki/dotfiles

Length of output: 1716


The empty config = { } passed to the home-manager import will cause module evaluation failures.

The imported home-manager module and its submodules directly reference config values during evaluation. For example, home-manager/programs/fnm/default.nix accesses config.home.homeDirectory at line 8 in a let binding, and home-manager/programs/fish/default.nix references config.home.username and config.home.homeDirectory within template strings. Similarly, services like cliproxyapi depend on config.home.homeDirectory and config.home.username.

Passing an empty object means these references will fail or return undefined, breaking the module evaluation. Provide the actual config object from the enclosing home-manager context instead.

🤖 Prompt for AI Agents
In `@named-hosts/matic/default.nix` around lines 219 - 231, The import of the
home-manager module is passing an empty config (config = { }) which breaks
evaluation because submodules reference config.home.*; replace the empty object
with the enclosing home-manager config by passing the actual config through
(e.g. change config = { } to config = config) so the imported module sees the
real home config values; update the imports block where the home-manager is
imported (the expression that sets inputs and overrides host/isDesktop) to
forward the parent config rather than an empty object.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@config/hyprland/hyprland.conf`:
- Around line 167-173: The hotkey bindings using gtk-launch (targets: ghostty,
google-chrome, slack, cursor, 1password, signal-desktop) and other runtime tools
referenced elsewhere (grim, slurp, swappy, wob, brightnessctl, cliphist,
wl-paste, wofi, hypridle, hyprlock) must be present in the runtime environment;
verify each binary or .desktop entry exists and add missing packages to your Nix
configuration (environment.systemPackages or home-manager packages) or provide
the corresponding .desktop files so gtk-launch can find them, updating package
lists where you see the symbols gtk-launch, ghostty, google-chrome, slack,
cursor, 1password, signal-desktop, grim, slurp, swappy, wob, brightnessctl,
cliphist, wl-paste, wofi, hypridle, and hyprlock.

In `@config/wlogout/style.css`:
- Around line 31-49: The CSS uses hardcoded absolute icon paths for selectors
`#lock`, `#logout`, `#suspend`, `#reboot`, and `#shutdown` which will not exist on NixOS;
update style.css so the background-image references are not
/usr/share/wlogout/icons/... but instead use one of: (A) template the file
during packaging to substitute the Nix store path into the URL (use your build’s
substitute mechanism to inject /nix/store/.../share/wlogout/icons/), (B) change
the URLs to relative paths pointing to icons copied next to style.css (e.g.
./icons/...), or (C) document/configure a runtime FHS wrapper/symlink so
/usr/share/wlogout/icons/ resolves; apply the chosen method consistently for the
`#lock`, `#logout`, `#suspend`, `#reboot` and `#shutdown` selectors and update any
packaging/build step that writes this style.css accordingly.
🧹 Nitpick comments (5)
config/gtk/default.nix (2)

8-28: Consider sorting the gtk attribute set alphabetically.

Per coding guidelines, attribute sets should be sorted alphabetically. The current order is enable, theme, iconTheme, cursorTheme, gtk3, gtk4 — reordering to cursorTheme, enable, gtk3, gtk4, iconTheme, theme would comply.

♻️ Suggested reorder
   gtk = lib.mkIf pkgs.stdenv.isLinux {
-    enable = true;
-    theme = {
-      name = "Adwaita-dark";
-      package = pkgs.gnome-themes-extra;
-    };
-    iconTheme = {
-      name = "Adwaita";
-      package = pkgs.adwaita-icon-theme;
-    };
     cursorTheme = {
       name = "Adwaita";
       size = 24;
     };
+    enable = true;
     gtk3.extraConfig = {
       gtk-application-prefer-dark-theme = 1;
     };
     gtk4.extraConfig = {
       gtk-application-prefer-dark-theme = 1;
     };
+    iconTheme = {
+      name = "Adwaita";
+      package = pkgs.adwaita-icon-theme;
+    };
+    theme = {
+      name = "Adwaita-dark";
+      package = pkgs.gnome-themes-extra;
+    };
   };

As per coding guidelines, "Sort attribute sets alphabetically in Nix files".


1-6: Unused config parameter.

config is declared in the function arguments but never referenced in the body. Consider removing it to keep the interface clean, or replace with ... if it's needed for forward compatibility.

♻️ Proposed fix
 {
-  config,
   pkgs,
   lib,
   ...
 }:
config/hyprland/default.nix (1)

3-14: Attribute sets are not sorted alphabetically.

Per coding guidelines, attribute sets should be sorted alphabetically. The entries should be ordered: hypridle.conf, hyprland.conf, hyprlock.conf.

♻️ Suggested reorder
 { config, ... }:
 {
-  xdg.configFile."hypr/hyprland.conf" = {
-    source = ./hyprland.conf;
-    force = true;
-  };
   xdg.configFile."hypr/hypridle.conf" = {
     source = ./hypridle.conf;
     force = true;
   };
+  xdg.configFile."hypr/hyprland.conf" = {
+    source = ./hyprland.conf;
+    force = true;
+  };
   xdg.configFile."hypr/hyprlock.conf" = {
     source = ./hyprlock.conf;
     force = true;
   };
 }

As per coding guidelines, "Sort attribute sets alphabetically in Nix files".

config/hyprland/hyprland.conf (1)

47-48: wob pipe setup doesn't guard against stale processes on Hyprland restart.

If Hyprland is restarted (e.g., config reload), exec-once won't re-run, but if the session is fully restarted, old tail -f / wob processes from a previous session may still hold the pipe. Consider wrapping this in a script that kills previous instances first, e.g., pkill wob; rm -f /tmp/wobpipe && mkfifo /tmp/wobpipe && tail -f /tmp/wobpipe | wob.

named-hosts/matic/default.nix (1)

226-247: Host override expression is duplicated.

The inputs // { host = (import ../../lib/host.nix) // { isDesktop = true; }; } expression appears identically in both extraSpecialArgs (Lines 228–232) and the home-manager import call (Lines 243–247). Extract it into a let binding to keep it DRY.

♻️ Suggested refactor
+    # Define once in the enclosing let/in or at the module level
+    # maticInputs = inputs // {
+    #   host = (import ../../lib/host.nix) // { isDesktop = true; };
+    # };
+
     home-manager.extraSpecialArgs = {
-      inputs = inputs // {
-        host = (import ../../lib/host.nix) // {
-          isDesktop = true;
-        };
-      };
+      inputs = maticInputs;
     };
     ...
           (import ../../home-manager {
             inherit username;
-            inputs = inputs // {
-              host = (import ../../lib/host.nix) // {
-                isDesktop = true;
-              };
-            };
+            inputs = maticInputs;
             lib = inputs.nixpkgs.lib;
             pkgs = pkgs;
-            config = { };
+            inherit config;
           })

Comment thread config/hyprland/hyprland.conf Outdated
Comment thread config/wlogout/style.css
Comment on lines +31 to +49
#lock {
background-image: image(url("/usr/share/wlogout/icons/lock.png"));
}

#logout {
background-image: image(url("/usr/share/wlogout/icons/logout.png"));
}

#suspend {
background-image: image(url("/usr/share/wlogout/icons/suspend.png"));
}

#reboot {
background-image: image(url("/usr/share/wlogout/icons/reboot.png"));
}

#shutdown {
background-image: image(url("/usr/share/wlogout/icons/shutdown.png"));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check repository structure for Nix files
git ls-files | grep -E '\.(nix|flake)' | head -20

Repository: shunkakinoki/dotfiles

Length of output: 574


🏁 Script executed:

# Search for wlogout configuration in the Nix setup
rg -i 'wlogout' --type nix

Repository: shunkakinoki/dotfiles

Length of output: 276


🏁 Script executed:

# Check the actual config/wlogout directory structure
ls -la config/wlogout/ 2>/dev/null || echo "Directory listing failed"

Repository: shunkakinoki/dotfiles

Length of output: 352


🏁 Script executed:

# Check if there's a flake.nix at the root
cat flake.nix 2>/dev/null | head -100

Repository: shunkakinoki/dotfiles

Length of output: 2541


🏁 Script executed:

# Examine the wlogout default.nix configuration
cat -n config/wlogout/default.nix

Repository: shunkakinoki/dotfiles

Length of output: 336


🏁 Script executed:

# Check the style.css file to see the icon references
cat -n config/wlogout/style.css

Repository: shunkakinoki/dotfiles

Length of output: 1400


🏁 Script executed:

# Search for any FHS wrapper or symlink setup for wlogout
rg -i 'fhs|flatpak|nix-shell' config/wlogout/

Repository: shunkakinoki/dotfiles

Length of output: 47


🏁 Script executed:

# Check if there's a wrapper or setup in home-manager packages
rg -A 5 -B 5 'wlogout' home-manager/packages/default.nix

Repository: shunkakinoki/dotfiles

Length of output: 153


🏁 Script executed:

# Check if wlogout package in nixpkgs has any special setup or environment variables
rg -i 'wlogout' --type nix home-manager/ | head -20

Repository: shunkakinoki/dotfiles

Length of output: 108


🏁 Script executed:

# Look for any environment setup or symlinks in the repository
rg -i 'environment\.' --type nix -A 3 | grep -i 'wlogout\|icon\|/usr/share' | head -20

Repository: shunkakinoki/dotfiles

Length of output: 47


🏁 Script executed:

# Check if there's documentation about wlogout icon paths in the repo
find . -name "*.md" -exec grep -l 'wlogout\|icon' {} \; 2>/dev/null

Repository: shunkakinoki/dotfiles

Length of output: 47


🏁 Script executed:

# Search for any existing icon file references or copies in the config directory
find config/wlogout -type f -name '*.png' 2>/dev/null || echo "No PNG files found"

Repository: shunkakinoki/dotfiles

Length of output: 47


Hardcoded /usr/share/wlogout/icons/ paths will not resolve on NixOS and result in missing button icons.

On NixOS, wlogout is installed in the Nix store (e.g., /nix/store/<hash>-wlogout-<version>/share/wlogout/icons/), not in /usr/share/. These paths at lines 32, 36, 40, 44, and 48 will fail to resolve at runtime, leaving all five buttons without their icon images.

Fix by either:

  1. Templating the CSS from Nix with store-interpolated paths using substitute or similar
  2. Copying the icons into the config directory alongside style.css and using relative paths
  3. Configuring wlogout to use a symlink or FHS wrapper that makes /usr/share/wlogout/icons/ available
🤖 Prompt for AI Agents
In `@config/wlogout/style.css` around lines 31 - 49, The CSS uses hardcoded
absolute icon paths for selectors `#lock`, `#logout`, `#suspend`, `#reboot`, and
`#shutdown` which will not exist on NixOS; update style.css so the
background-image references are not /usr/share/wlogout/icons/... but instead use
one of: (A) template the file during packaging to substitute the Nix store path
into the URL (use your build’s substitute mechanism to inject
/nix/store/.../share/wlogout/icons/), (B) change the URLs to relative paths
pointing to icons copied next to style.css (e.g. ./icons/...), or (C)
document/configure a runtime FHS wrapper/symlink so /usr/share/wlogout/icons/
resolves; apply the chosen method consistently for the `#lock`, `#logout`, `#suspend`,
`#reboot` and `#shutdown` selectors and update any packaging/build step that writes
this style.css accordingly.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@named-hosts/matic/default.nix`:
- Around line 304-326: The importGpgKey activation uses a fragile runtime path
for GPG_SECRET_FILE; change it to use a build-time Nix store path like
deployAgenixSecrets does by setting GPG_SECRET_FILE to builtins.toString
../galactica/keys/gpg.age (or the same input used for id_ed25519.age) instead of
"${config.home.homeDirectory}/dotfiles/..."; update the GPG_SECRET_FILE
assignment in home.activation.importGpgKey, keep the rest of the import logic
(GPG_TEMP_FILE, rage decrypt, gpg --import) as-is, and ensure the value is
quoted so the script reads the Nix store path at runtime reliably.
- Around line 315-319: The decrypted GPG private key is written to
$GPG_TEMP_FILE with default permissions; ensure it's created with restrictive
permissions and always removed on failure: after running ${pkgs.rage}/bin/rage
-d -i ... -o "$GPG_TEMP_FILE" (the command that produces GPG_TEMP_FILE)
immediately set chmod 600 on "$GPG_TEMP_FILE" and register a trap to rm -f
"$GPG_TEMP_FILE" on EXIT (so cleanup happens even if activation is interrupted),
then proceed to call ${pkgs.gnupg}/bin/gpg --batch --import and finally rm -f
"$GPG_TEMP_FILE" as currently done.
🧹 Nitpick comments (3)
named-hosts/galactica/secrets.nix (1)

16-26: Attribute set is not sorted alphabetically.

"keys/id_ed25519.age" (line 17) precedes "keys/gpg.age" (line 23), but gpg sorts before id_ed25519. Swap the two blocks to comply with the alphabetical ordering guideline.

Proposed fix
 {
-  # SSH key for GitHub authentication (shared with all machines)
-  "keys/id_ed25519.age" = {
-    file = ./keys/id_ed25519.age;
-    publicKeys = allMachines;
-  };
-
   # GPG key (shared with all machines for commit signing)
   "keys/gpg.age" = {
     file = ./keys/gpg.age;
     publicKeys = allMachines;
   };
+
+  # SSH key for GitHub authentication (shared with all machines)
+  "keys/id_ed25519.age" = {
+    file = ./keys/id_ed25519.age;
+    publicKeys = allMachines;
+  };
 }

As per coding guidelines: "Sort attribute sets alphabetically in Nix files".

named-hosts/matic/secrets.nix (1)

10-19: Attribute set is not sorted alphabetically — same pattern as in galactica/secrets.nix.

"keys/gpg.age" should precede "keys/id_github.age".

Proposed fix
 {
+  "keys/gpg.age" = {
+    file = ../galactica/keys/gpg.age;
+    publicKeys = allMachines;
+  };
+
   "keys/id_github.age" = {
     file = ../galactica/keys/id_ed25519.age;
     publicKeys = allMachines;
   };
-
-  "keys/gpg.age" = {
-    file = ../galactica/keys/gpg.age;
-    publicKeys = allMachines;
-  };
 }

As per coding guidelines: "Sort attribute sets alphabetically in Nix files".

named-hosts/matic/default.nix (1)

256-271: keys/gpg.age is registered in age.secrets but the importGpgKey hook decrypts it manually from a different path.

The age.secrets mapping on line 256 registers both keys/id_github.age and keys/gpg.age with agenix. However, the importGpgKey activation hook (line 306) reads the GPG secret from a hardcoded repo path rather than from the agenix-managed secret location. This means the agenix-managed gpg.age secret is effectively unused.

Consider either:

  1. Removing keys/gpg.age from age.secrets if manual decryption is the intended approach, or
  2. Having importGpgKey read from config.age.secrets."keys/gpg.age".path to use the agenix-managed secret.

Comment on lines +304 to +326
home.activation.importGpgKey = config.lib.dag.entryAfter [ "linkGeneration" ] ''
$VERBOSE_ECHO "🔑 Starting GPG key import process..."
GPG_SECRET_FILE="${config.home.homeDirectory}/dotfiles/named-hosts/galactica/keys/gpg.age"
GPG_TEMP_FILE="${config.home.homeDirectory}/.config/agenix/gpg.key"

# Create agenix directory if it doesn't exist
mkdir -p "${config.home.homeDirectory}/.config/agenix"

if [[ -f "$GPG_SECRET_FILE" ]]; then
# Check if key is already imported
if ! ${pkgs.gnupg}/bin/gpg --list-secret-keys 2>/dev/null | grep -q "C2E97FCFF482925D"; then
echo "Importing GPG key from agenix..."
# Try to decrypt - will fail silently if SSH key isn't authorized
if ${pkgs.rage}/bin/rage -d -i ${config.home.homeDirectory}/.ssh/id_ed25519 -o "$GPG_TEMP_FILE" "$GPG_SECRET_FILE" 2>/dev/null; then
${pkgs.gnupg}/bin/gpg --batch --import "$GPG_TEMP_FILE" 2>/dev/null
rm -f "$GPG_TEMP_FILE"
echo "✅ GPG key imported successfully"
fi
else
$VERBOSE_ECHO "ℹ️ GPG key already imported"
fi
fi
'';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

GPG secret file path is a fragile runtime assumption — inconsistent with the SSH key approach.

deployAgenixSecrets (line 291) correctly uses builtins.toString ../galactica/keys/id_ed25519.age which resolves to a Nix store path at build time, guaranteeing the encrypted file is available. However, importGpgKey (line 306) references ${config.home.homeDirectory}/dotfiles/named-hosts/galactica/keys/gpg.age — a runtime path that assumes the dotfiles repo is cloned at ~/dotfiles. On a fresh deployment or if the repo is elsewhere, this will silently fail.

Use the same builtins.toString pattern for consistency and reliability:

Proposed fix
          home.activation.importGpgKey = config.lib.dag.entryAfter [ "linkGeneration" ] ''
            $VERBOSE_ECHO "🔑 Starting GPG key import process..."
-           GPG_SECRET_FILE="${config.home.homeDirectory}/dotfiles/named-hosts/galactica/keys/gpg.age"
+           GPG_SECRET_FILE="${builtins.toString ../galactica/keys/gpg.age}"
            GPG_TEMP_FILE="${config.home.homeDirectory}/.config/agenix/gpg.key"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
home.activation.importGpgKey = config.lib.dag.entryAfter [ "linkGeneration" ] ''
$VERBOSE_ECHO "🔑 Starting GPG key import process..."
GPG_SECRET_FILE="${config.home.homeDirectory}/dotfiles/named-hosts/galactica/keys/gpg.age"
GPG_TEMP_FILE="${config.home.homeDirectory}/.config/agenix/gpg.key"
# Create agenix directory if it doesn't exist
mkdir -p "${config.home.homeDirectory}/.config/agenix"
if [[ -f "$GPG_SECRET_FILE" ]]; then
# Check if key is already imported
if ! ${pkgs.gnupg}/bin/gpg --list-secret-keys 2>/dev/null | grep -q "C2E97FCFF482925D"; then
echo "Importing GPG key from agenix..."
# Try to decrypt - will fail silently if SSH key isn't authorized
if ${pkgs.rage}/bin/rage -d -i ${config.home.homeDirectory}/.ssh/id_ed25519 -o "$GPG_TEMP_FILE" "$GPG_SECRET_FILE" 2>/dev/null; then
${pkgs.gnupg}/bin/gpg --batch --import "$GPG_TEMP_FILE" 2>/dev/null
rm -f "$GPG_TEMP_FILE"
echo "✅ GPG key imported successfully"
fi
else
$VERBOSE_ECHO "ℹ️ GPG key already imported"
fi
fi
'';
home.activation.importGpgKey = config.lib.dag.entryAfter [ "linkGeneration" ] ''
$VERBOSE_ECHO "🔑 Starting GPG key import process..."
GPG_SECRET_FILE="${builtins.toString ../galactica/keys/gpg.age}"
GPG_TEMP_FILE="${config.home.homeDirectory}/.config/agenix/gpg.key"
# Create agenix directory if it doesn't exist
mkdir -p "${config.home.homeDirectory}/.config/agenix"
if [[ -f "$GPG_SECRET_FILE" ]]; then
# Check if key is already imported
if ! ${pkgs.gnupg}/bin/gpg --list-secret-keys 2>/dev/null | grep -q "C2E97FCFF482925D"; then
echo "Importing GPG key from agenix..."
# Try to decrypt - will fail silently if SSH key isn't authorized
if ${pkgs.rage}/bin/rage -d -i ${config.home.homeDirectory}/.ssh/id_ed25519 -o "$GPG_TEMP_FILE" "$GPG_SECRET_FILE" 2>/dev/null; then
${pkgs.gnupg}/bin/gpg --batch --import "$GPG_TEMP_FILE" 2>/dev/null
rm -f "$GPG_TEMP_FILE"
echo "✅ GPG key imported successfully"
fi
else
$VERBOSE_ECHO "ℹ️ GPG key already imported"
fi
fi
'';
🤖 Prompt for AI Agents
In `@named-hosts/matic/default.nix` around lines 304 - 326, The importGpgKey
activation uses a fragile runtime path for GPG_SECRET_FILE; change it to use a
build-time Nix store path like deployAgenixSecrets does by setting
GPG_SECRET_FILE to builtins.toString ../galactica/keys/gpg.age (or the same
input used for id_ed25519.age) instead of
"${config.home.homeDirectory}/dotfiles/..."; update the GPG_SECRET_FILE
assignment in home.activation.importGpgKey, keep the rest of the import logic
(GPG_TEMP_FILE, rage decrypt, gpg --import) as-is, and ensure the value is
quoted so the script reads the Nix store path at runtime reliably.

Comment on lines +315 to +319
echo "Importing GPG key from agenix..."
# Try to decrypt - will fail silently if SSH key isn't authorized
if ${pkgs.rage}/bin/rage -d -i ${config.home.homeDirectory}/.ssh/id_ed25519 -o "$GPG_TEMP_FILE" "$GPG_SECRET_FILE" 2>/dev/null; then
${pkgs.gnupg}/bin/gpg --batch --import "$GPG_TEMP_FILE" 2>/dev/null
rm -f "$GPG_TEMP_FILE"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Decrypted GPG private key written to temp file without restrictive permissions.

rage -o "$GPG_TEMP_FILE" writes with the default umask. If the activation is interrupted between the decrypt (line 317) and the rm (line 319), the private key persists unencrypted and potentially world-readable. Set permissions explicitly:

Proposed fix
                if ${pkgs.rage}/bin/rage -d -i ${config.home.homeDirectory}/.ssh/id_ed25519 -o "$GPG_TEMP_FILE" "$GPG_SECRET_FILE" 2>/dev/null; then
+                 chmod 600 "$GPG_TEMP_FILE"
                  ${pkgs.gnupg}/bin/gpg --batch --import "$GPG_TEMP_FILE" 2>/dev/null
                  rm -f "$GPG_TEMP_FILE"
                  echo "✅ GPG key imported successfully"
                fi

Alternatively, consider wrapping in a trap 'rm -f "$GPG_TEMP_FILE"' EXIT to ensure cleanup on any failure path.

🤖 Prompt for AI Agents
In `@named-hosts/matic/default.nix` around lines 315 - 319, The decrypted GPG
private key is written to $GPG_TEMP_FILE with default permissions; ensure it's
created with restrictive permissions and always removed on failure: after
running ${pkgs.rage}/bin/rage -d -i ... -o "$GPG_TEMP_FILE" (the command that
produces GPG_TEMP_FILE) immediately set chmod 600 on "$GPG_TEMP_FILE" and
register a trap to rm -f "$GPG_TEMP_FILE" on EXIT (so cleanup happens even if
activation is interrupted), then proceed to call ${pkgs.gnupg}/bin/gpg --batch
--import and finally rm -f "$GPG_TEMP_FILE" as currently done.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="config/hyprland/hyprland.conf">

<violation number="1" location="config/hyprland/hyprland.conf:148">
P2: `windowrule` (V1) doesn’t accept `class:` selectors, so these rules won’t match any windows. Use a plain class regex for V1 rules or revert to `windowrulev2`.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread config/hyprland/hyprland.conf Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="config/hyprland/hyprland.conf">

<violation number="1" location="config/hyprland/hyprland.conf:159">
P2: This rule enables a slide animation for wofi even though the intention is to prevent animations. Use `noanim` so the launcher doesn’t animate.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

layerrule = ignore_alpha 0.0, match:namespace swaync-notification-window

# Prevent wofi animation
layerrule = animation slide, match:namespace ^(wofi)$

@cubic-dev-ai cubic-dev-ai Bot Feb 6, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This rule enables a slide animation for wofi even though the intention is to prevent animations. Use noanim so the launcher doesn’t animate.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At config/hyprland/hyprland.conf, line 159:

<comment>This rule enables a slide animation for wofi even though the intention is to prevent animations. Use `noanim` so the launcher doesn’t animate.</comment>

<file context>
@@ -145,20 +143,20 @@ misc {
 
 # Prevent wofi animation
-layerrule = noanim, ^(wofi)$
+layerrule = animation slide, match:namespace ^(wofi)$
 
 # =============================================================================
</file context>
Suggested change
layerrule = animation slide, match:namespace ^(wofi)$
layerrule = noanim on, match:namespace ^(wofi)$
Fix with Cubic

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@named-hosts/matic/default.nix`:
- Around line 286-299: The shell heredoc in the deployAgenixSecrets fragment
(and similar blocks ensureSshDirectory, ensureAgenixDirectory, importGpgKey)
uses unquoted Nix interpolations like ${config.home.homeDirectory} and
$SECRET_FILE which can be split if paths contain spaces; fix by quoting all path
expansions in the shell snippets (use "${config.home.homeDirectory}",
"\"$SECRET_FILE\"" or ideally SECRET_FILE already set then reference as
"$SECRET_FILE"), and quote targets passed to commands (e.g., the rage -i/-o
arguments and chmod target) so every interpolated/expanded path is wrapped in
double quotes to prevent word-splitting and globbing.
- Around line 308-309: The mkdir line that creates
"${config.home.homeDirectory}/.config/agenix" should not run during dry runs and
is redundant; either prefix it with $DRY_RUN_CMD (i.e. change the invocation in
the activation hook to use $DRY_RUN_CMD mkdir -p) or remove the line entirely
because ensureAgenixDirectory already creates that directory before
writeBoundary and importGpgKey runs after linkGeneration—update the activation
hook accordingly and keep ensureAgenixDirectory, writeBoundary, linkGeneration,
and importGpgKey order in mind when removing the redundant mkdir.
🧹 Nitpick comments (8)
config/waybar/style.css (1)

76-98: #battery.charging duplicates the base #battery color — intentional clarity or oversight?

Both #battery (Line 77) and #battery.charging (Line 97) set color: @green``. This is harmless and arguably self-documenting, but if the base battery color ever changes, this rule would mask the update for the charging state.

Makefile (1)

790-796: Consider guarding the Waybar restart with a Hyprland availability check.

This target runs on all Linux hosts via the systemctl umbrella (line 752), but pkill waybar and hyprctl are only relevant on Hyprland-enabled hosts (matic). On other Linux hosts it's harmless (due to || true) but prints misleading "Restarting waybar…" / "waybar restarted" messages.

An optional improvement to reduce noise on non-Hyprland hosts:

♻️ Suggested guard
 .PHONY: systemctl-waybar
 systemctl-waybar: ## Restart waybar.
-	`@echo` "🔄 Restarting waybar..."
-	`@pkill` waybar 2>/dev/null || true
-	`@sleep` 1
-	`@hyprctl` dispatch exec waybar 2>/dev/null || true
-	`@echo` "✅ waybar restarted"
+	`@if` command -v hyprctl >/dev/null 2>&1; then \
+		echo "🔄 Restarting waybar..."; \
+		pkill waybar 2>/dev/null || true; \
+		sleep 1; \
+		hyprctl dispatch exec waybar 2>/dev/null || true; \
+		echo "✅ waybar restarted"; \
+	fi
named-hosts/matic/default.nix (2)

336-342: GPG agent cache TTL of 3 years effectively disables passphrase re-prompting.

defaultCacheTtl and maxCacheTtl are both set to ~3 years (94608000s). This means a compromised session or stolen machine with an active gpg-agent would have unlimited access to the signing key without re-authentication. A common balanced value is 1–8 hours (3600–28800s).

If this is intentional for your workflow, feel free to ignore — just flagging the trade-off.


253-270: age.secrets mapping relies on a magic string to identify the GitHub key.

The name == "keys/id_github.age" check on line 261 must exactly match the attribute name exported by ./secrets.nix. If that name changes, the match silently fails and the SSH key deploys without the correct path/permissions. Consider defining the secrets explicitly rather than mapping over the import, or extracting the key name to a shared variable.

flake.nix (1)

54-60: Inputs are not sorted alphabetically.

Per coding guidelines, attribute sets in Nix files should be sorted alphabetically. hyprland and hyprland-plugins should appear after home-manager and before mk-shell-bin, not after nixos-hardware.

Also, hyprland-plugins is unpinned (tracks the default branch). Since hyprland is pinned to v0.53.3, a future hyprland-plugins update could introduce incompatible plugin ABIs. Consider pinning to a compatible tag or commit.

Suggested reorder (move block after line 11)
     home-manager = {
       url = "github:nix-community/home-manager";
       inputs.nixpkgs.follows = "nixpkgs";
     };
+    hyprland = {
+      url = "github:hyprwm/Hyprland/v0.53.3";
+    };
+    hyprland-plugins = {
+      url = "github:hyprwm/hyprland-plugins";
+      inputs.hyprland.follows = "hyprland";
+    };
     mk-shell-bin = {
     nixos-hardware = {
       url = "github:NixOS/nixos-hardware/master";
     };
-    hyprland = {
-      url = "github:hyprwm/Hyprland/v0.53.3";
-    };
-    hyprland-plugins = {
-      url = "github:hyprwm/hyprland-plugins";
-      inputs.hyprland.follows = "hyprland";
-    };
   };

As per coding guidelines: "Sort attribute sets alphabetically in Nix files" and "Always pin package versions in flake.lock".

config/hyprland/default.nix (1)

11-19: Attributes not sorted alphabetically; consider a brief comment for systemd.enable = false.

Per coding guidelines, attribute sets should be sorted alphabetically. Within the hyprland block, the order should be: enable, extraConfig, package, systemd. Also, disabling systemd integration is a deliberate choice — a one-line comment would clarify why (e.g., session services launched via exec-once in the conf instead).

Suggested reorder and comment
   wayland.windowManager.hyprland = {
     enable = true;
-    package = inputs.hyprland.packages.${pkgs.stdenv.hostPlatform.system}.hyprland;
-    systemd.enable = false;
     extraConfig = ''
       plugin = ${hyprexpoPlugin}/lib/libhyprexpo.so
     ''
     + builtins.readFile ./hyprland.conf;
+    package = inputs.hyprland.packages.${pkgs.stdenv.hostPlatform.system}.hyprland;
+    # Session services are started via exec-once in hyprland.conf
+    systemd.enable = false;
   };

As per coding guidelines: "Sort attribute sets alphabetically in Nix files" and "Document complex configurations with comments in Nix files".

config/hyprland/hyprland.conf (2)

159-175: $mod, V for VS Code is placed under the "Launcher & Clipboard" heading instead of "App Launch Hotkeys".

Line 173 (bind = $mod, V, exec, code) is an app-launch binding, not a launcher or clipboard action. Consider moving it up to the "App Launch Hotkeys" section (lines 159–168) for consistency.


289-300: Hyprexpo plugin config looks reasonable.

Note that the bg_col uses a raw rgb(282a36) literal rather than the $background variable defined at line 4. Using the variable would keep the Dracula palette centralized.

Suggested tweak
     hyprexpo {
         columns = 3
         gap_size = 5
-        bg_col = rgb(282a36)
+        bg_col = $background
         workspace_method = first 1
         gesture_distance = 300
     }

Comment on lines +286 to +299
home.activation.deployAgenixSecrets = config.lib.dag.entryAfter [ "writeBoundary" ] ''
# Decrypt and deploy GitHub SSH key if it doesn't exist
if [[ ! -f "${config.home.homeDirectory}/.ssh/id_ed25519_github" ]]; then
echo "Deploying GitHub SSH key from agenix..."
SECRET_FILE="${builtins.toString ../galactica/keys/id_ed25519.age}"
if [[ -f "$SECRET_FILE" ]]; then
$DRY_RUN_CMD ${pkgs.rage}/bin/rage -d -i ${config.home.homeDirectory}/.ssh/id_ed25519 "$SECRET_FILE" -o ${config.home.homeDirectory}/.ssh/id_ed25519_github
$DRY_RUN_CMD chmod $VERBOSE_ARG 0600 ${config.home.homeDirectory}/.ssh/id_ed25519_github
echo "✅ GitHub SSH key deployed successfully"
else
echo "⚠️ Warning: Secret file not found at $SECRET_FILE"
fi
fi
'';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Unquoted Nix-interpolated paths in shell commands risk word splitting.

Several config.home.homeDirectory interpolations are unquoted in the shell script (lines 292–293), while others (line 288) are properly quoted. If the home directory ever contains spaces or special characters, the unquoted paths will break.

Proposed fix — quote all path interpolations consistently
-            if [[ ! -f "${config.home.homeDirectory}/.ssh/id_ed25519_github" ]]; then
+            if [[ ! -f "${config.home.homeDirectory}/.ssh/id_ed25519_github" ]]; then
               echo "Deploying GitHub SSH key from agenix..."
               SECRET_FILE="${builtins.toString ../galactica/keys/id_ed25519.age}"
               if [[ -f "$SECRET_FILE" ]]; then
-                $DRY_RUN_CMD ${pkgs.rage}/bin/rage -d -i ${config.home.homeDirectory}/.ssh/id_ed25519 "$SECRET_FILE" -o ${config.home.homeDirectory}/.ssh/id_ed25519_github
-                $DRY_RUN_CMD chmod $VERBOSE_ARG 0600 ${config.home.homeDirectory}/.ssh/id_ed25519_github
+                $DRY_RUN_CMD ${pkgs.rage}/bin/rage -d -i "${config.home.homeDirectory}/.ssh/id_ed25519" "$SECRET_FILE" -o "${config.home.homeDirectory}/.ssh/id_ed25519_github"
+                $DRY_RUN_CMD chmod $VERBOSE_ARG 0600 "${config.home.homeDirectory}/.ssh/id_ed25519_github"
                 echo "✅ GitHub SSH key deployed successfully"
               else

The same quoting inconsistency also affects ensureSshDirectory (lines 274–275), ensureAgenixDirectory (lines 280–281), and importGpgKey (lines 305, 309, 316).

🤖 Prompt for AI Agents
In `@named-hosts/matic/default.nix` around lines 286 - 299, The shell heredoc in
the deployAgenixSecrets fragment (and similar blocks ensureSshDirectory,
ensureAgenixDirectory, importGpgKey) uses unquoted Nix interpolations like
${config.home.homeDirectory} and $SECRET_FILE which can be split if paths
contain spaces; fix by quoting all path expansions in the shell snippets (use
"${config.home.homeDirectory}", "\"$SECRET_FILE\"" or ideally SECRET_FILE
already set then reference as "$SECRET_FILE"), and quote targets passed to
commands (e.g., the rage -i/-o arguments and chmod target) so every
interpolated/expanded path is wrapped in double quotes to prevent word-splitting
and globbing.

Comment on lines +308 to +309
# Create agenix directory if it doesn't exist
mkdir -p "${config.home.homeDirectory}/.config/agenix"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

mkdir -p missing $DRY_RUN_CMD prefix — will create the directory even during dry runs.

Other activation hooks (ensureSshDirectory, ensureAgenixDirectory) consistently use $DRY_RUN_CMD mkdir -p, but this one does not.

Proposed fix
            # Create agenix directory if it doesn't exist
-           mkdir -p "${config.home.homeDirectory}/.config/agenix"
+           $DRY_RUN_CMD mkdir -p $VERBOSE_ARG "${config.home.homeDirectory}/.config/agenix"

Note: This mkdir is also redundant since ensureAgenixDirectory (line 279) already creates this directory before writeBoundary, and importGpgKey runs after linkGeneration. Consider removing it entirely if the ordering is guaranteed.

🤖 Prompt for AI Agents
In `@named-hosts/matic/default.nix` around lines 308 - 309, The mkdir line that
creates "${config.home.homeDirectory}/.config/agenix" should not run during dry
runs and is redundant; either prefix it with $DRY_RUN_CMD (i.e. change the
invocation in the activation hook to use $DRY_RUN_CMD mkdir -p) or remove the
line entirely because ensureAgenixDirectory already creates that directory
before writeBoundary and importGpgKey runs after linkGeneration—update the
activation hook accordingly and keep ensureAgenixDirectory, writeBoundary,
linkGeneration, and importGpgKey order in mind when removing the redundant
mkdir.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@config/ghostty/config`:
- Around line 19-20: The current keybind entries bind bare Ctrl+C to
copy_to_clipboard, which intercepts SIGINT; change the bindings to use the
performable: prefix so the copy action only runs when applicable and otherwise
the key is passed through — update the entries referencing the keybind lines
(e.g., the existing "keybind = ctrl+c=copy_to_clipboard" rule) to "keybind =
ctrl+c=performable:copy_to_clipboard" while leaving "keybind =
ctrl+v=paste_from_clipboard" as-is or apply performable: if desired.

In `@config/hyprland/hyprland.conf`:
- Line 48: The exec-once setup creates /tmp/wobpipe and runs wob but nothing
writes to it, so the OSD never appears; update the volume and brightness key
handlers (the commands that call wpctl and brightnessctl) to send the current
value into /tmp/wobpipe after each adjustment (e.g., query the new
volume/brightness and echo that formatted value into /tmp/wobpipe), or remove
the exec-once wob/fifo setup entirely if you don't want an OSD; specifically
modify the commands that invoke wpctl and brightnessctl to append a write to
/tmp/wobpipe so wob receives updates.
- Around line 161-167: The binding "bind = $mod, C, exec, cursor" will fail
because the desktop binary isn't available on Linux; replace it with the
installed CLI binary name from the package set (e.g., use "cursor-cli") or point
the binding to the correct executable provided by the Linux package; update the
bind line that references "cursor" so it calls the available binary (reference:
the bind line with "$mod, C, exec, cursor" and the package "cursor-cli") and
keep the existing gtk-launch entries for 1password and signal-desktop as-is.
🧹 Nitpick comments (8)
named-hosts/matic/default.nix (2)

252-269: Hardcoded /home/${username} vs config.home.homeDirectory inconsistency.

Lines 253 and 263 use /home/${username} while activation hooks (lines 272+) use config.home.homeDirectory. Both resolve to the same value here (line 68 sets home = "/home/${username}"), but using the config attribute consistently would be more robust.


326-341: Attribute sets not sorted alphabetically.

Per coding guidelines, attribute sets in Nix files should be sorted alphabetically. The services.gpg-agent block has attributes out of order.

Proposed fix
          services.gpg-agent = {
+           defaultCacheTtl = 94608000; # 3 years
            enable = true;
            enableSshSupport = false;
-           pinentry.package = pkgs.pinentry-tty;
-           defaultCacheTtl = 94608000; # 3 years
            maxCacheTtl = 94608000; # 3 years
+           pinentry.package = pkgs.pinentry-tty;
          };

Also, defaultCacheTtl and maxCacheTtl of ~3 years means the GPG passphrase is effectively never re-prompted. This is a deliberate convenience trade-off worth documenting if intentional.

As per coding guidelines: "Sort attribute sets alphabetically in Nix files".

home-manager/packages/default.nix (1)

117-117: xclip may be redundant given wl-clipboard for Wayland desktops.

xclip (X11 clipboard) is added to the general Linux list, while wl-clipboard is in the Linux+Desktop (Wayland) list. If "matic" runs Hyprland (Wayland-only), xclip won't function natively — it only works under XWayland. This is fine if other non-desktop Linux hosts still use X11, but worth confirming the intent.

config/hyprland/default.nix (2)

1-29: Add brief comments explaining non-obvious configuration choices.

Per coding guidelines, complex configurations should be documented. A few choices here would benefit from a short inline comment:

  • Line 14: Why is systemd.enable = false? (e.g., using a login manager instead, or manual session start)
  • Line 23/27: Why force = true on the XDG config files?

This helps future maintainers (or yourself) understand the intent quickly. Based on learnings, document all major changes in Nix configurations.


1-6: Unused config and inputs parameters.

Neither config (line 2) nor inputs (line 3) is referenced in the module body — only pkgs is used. While listing them is harmless (standard home-manager module pattern), inputs in particular is non-standard and implies it's passed via specialArgs. If it's not needed here, consider removing it to keep the interface clean.

Proposed cleanup
 {
-  config,
-  inputs,
   pkgs,
   ...
 }:
config/hyprland/hyprland.conf (3)

183-183: Inconsistent use of SUPER instead of $mod.

Line 183 uses the literal SUPER CTRL, F while every other binding uses $mod. Since $mod = SUPER (line 19), this works, but it's inconsistent and will break if $mod is ever changed.

Fix
-bind = SUPER CTRL, F, fullscreen, 0
+bind = $mod CTRL, F, fullscreen, 0

169-175: Section header "Launcher & Clipboard" is misleading — $mod, V launches VS Code.

bind = $mod, V, exec, code (line 172) is an app-launch binding, not a launcher or clipboard action. Consider moving it to the "App Launch Hotkeys" section (lines 158–167) for clarity, or renaming this section.


282-283: Hardcoded eDP-1 display name in scale toggle may not match all setups.

The monitor line (53) uses auto-detection (, preferred, auto, 1.5), but the F9 toggle (lines 282–283) hardcodes eDP-1. If Hyprland names the display differently, the toggle will fail. Consider querying the monitor name dynamically:

Dynamic monitor name approach
-bindl = , F9, exec, hyprctl -j monitors | grep -q '"scale": 1.5' && hyprctl keyword monitor "eDP-1, preferred, auto, 1.0" || hyprctl keyword monitor "eDP-1, preferred, auto, 1.5"
+bindl = , F9, exec, MON=$(hyprctl -j monitors | jq -r '.[0].name'); hyprctl -j monitors | grep -q '"scale": 1.5' && hyprctl keyword monitor "$MON, preferred, auto, 1.0" || hyprctl keyword monitor "$MON, preferred, auto, 1.5"

Comment thread config/ghostty/config
Comment on lines +19 to +20
keybind = ctrl+c=copy_to_clipboard
keybind = ctrl+v=paste_from_clipboard

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Ghostty performable keybind prefix syntax documentation

💡 Result:

Ghostty keybind “prefix” syntax is documented under Keybindings → Trigger Prefixes. A keybind is:

keybind = <prefixes>:<trigger>=<action>

Prefixes are written before the trigger, separated by : (and you can stack multiple): e.g.

keybind = global:unconsumed:ctrl+a=reload_config [1]

performable: prefix

performable: means only consume the key if the action can actually run (otherwise Ghostty acts like the binding doesn’t exist, so the key falls through to the app in the terminal). Example:
keybind = performable:ctrl+c=copy_to_clipboard [1][2]

Notes:

  • It has no effect with global: or all: prefixed keybinds. [1][2]
  • For key sequences, if the action isn’t performable it resets the sequence (as if no keybind was set). [1][2]
  • performable: keybinds won’t show as menu shortcuts. [1][2]

Related docs

  • Keybindings / Trigger Prefixes (includes performable:): [1]
  • Option reference for the same prefixes (with “Available since” info): [2]
  • Trigger sequences (if you mean “prefix key” sequences like ctrl+a>n=...): [5]

🌐 Web query:

Ghostty ctrl+c copy clipboard keybind SIGINT terminal

💡 Result:

To make Ctrl+C act as Copy only when there’s a selection, and otherwise pass through to the PTY as SIGINT (interrupt), bind it with Ghostty’s performable: prefix:

# ~/.config/ghostty/ghostty.conf
keybind = performable:ctrl+c=copy_to_clipboard
  • copy_to_clipboard copies the current selection. (ghostty.org)
  • performable: means Ghostty only consumes the keypress if the action can be performed (i.e., there is something selected). If not, Ghostty behaves as if the keybind isn’t set—so Ctrl+C is sent to the running program, producing SIGINT in shells/programs like sleep 100. (ghostty.org)

Tip: you can inspect what’s currently bound (defaults and overrides) with:

ghostty +list-keybinds --default

(ghostty.org)

Citations:


🏁 Script executed:

# Check the keyd config mentioned in the review to understand the Framework key mapping
find . -name "default.conf" -path "*/keyd/*" | head -5

Repository: shunkakinoki/dotfiles

Length of output: 91


🏁 Script executed:

# Search for Ghostty documentation or config examples in the repo
find . -name "ghostty" -o -name "*ghostty*" -type d 2>/dev/null | head -10

Repository: shunkakinoki/dotfiles

Length of output: 81


🏁 Script executed:

# Check the keyd config to understand the Framework key to Ctrl+C mapping context
cat -n ./config/keyd/default.conf | head -40

Repository: shunkakinoki/dotfiles

Length of output: 954


🏁 Script executed:

# Check the current Ghostty config around the mentioned lines
cat -n ./config/ghostty/config | head -30

Repository: shunkakinoki/dotfiles

Length of output: 960


ctrl+c=copy_to_clipboard intercepts SIGINT — use performable: prefix instead.

Binding ctrl+c unconditionally to copy_to_clipboard prevents sending interrupt signals to running processes. The keyd cmd_mac layer (Framework+C → Ctrl+C) will still work, but users cannot send SIGINT directly via bare Ctrl+C from the keyboard.

Ghostty's performable: prefix triggers the action only when it can be performed (i.e., text is selected); otherwise the key passes through to the terminal. This preserves both copy on selection and SIGINT when needed.

Proposed fix
-keybind = ctrl+c=copy_to_clipboard
-keybind = ctrl+v=paste_from_clipboard
+keybind = performable:ctrl+c=copy_to_clipboard
+keybind = performable:ctrl+v=paste_from_clipboard
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
keybind = ctrl+c=copy_to_clipboard
keybind = ctrl+v=paste_from_clipboard
keybind = performable:ctrl+c=copy_to_clipboard
keybind = performable:ctrl+v=paste_from_clipboard
🤖 Prompt for AI Agents
In `@config/ghostty/config` around lines 19 - 20, The current keybind entries bind
bare Ctrl+C to copy_to_clipboard, which intercepts SIGINT; change the bindings
to use the performable: prefix so the copy action only runs when applicable and
otherwise the key is passed through — update the entries referencing the keybind
lines (e.g., the existing "keybind = ctrl+c=copy_to_clipboard" rule) to "keybind
= ctrl+c=performable:copy_to_clipboard" while leaving "keybind =
ctrl+v=paste_from_clipboard" as-is or apply performable: if desired.

exec-once = hypridle

# Volume/brightness OSD
exec-once = rm -f /tmp/wobpipe && mkfifo /tmp/wobpipe && tail -f /tmp/wobpipe | wob

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

wob OSD pipe is created but never fed — no visual feedback for volume/brightness.

The named pipe /tmp/wobpipe is created and wob reads from it, but all volume commands (lines 257–264) use wpctl directly and brightness commands (lines 276–280) use brightnessctl directly — neither pipes output to /tmp/wobpipe. The OSD bar will never appear.

Either pipe the current value after each adjustment, or remove the wob setup if you don't want an OSD.

Example fix for volume keys (apply similar pattern to brightness)
 # F2: Volume down
-bindel = , XF86AudioLowerVolume, exec, wpctl set-volume `@DEFAULT_AUDIO_SINK`@ 5%-
+bindel = , XF86AudioLowerVolume, exec, wpctl set-volume `@DEFAULT_AUDIO_SINK`@ 5%- && wpctl get-volume `@DEFAULT_AUDIO_SINK`@ | awk '{print int($2*100)}' > /tmp/wobpipe
-bindel = , F2, exec, wpctl set-volume `@DEFAULT_AUDIO_SINK`@ 5%-
+bindel = , F2, exec, wpctl set-volume `@DEFAULT_AUDIO_SINK`@ 5%- && wpctl get-volume `@DEFAULT_AUDIO_SINK`@ | awk '{print int($2*100)}' > /tmp/wobpipe
🤖 Prompt for AI Agents
In `@config/hyprland/hyprland.conf` at line 48, The exec-once setup creates
/tmp/wobpipe and runs wob but nothing writes to it, so the OSD never appears;
update the volume and brightness key handlers (the commands that call wpctl and
brightnessctl) to send the current value into /tmp/wobpipe after each adjustment
(e.g., query the new volume/brightness and echo that formatted value into
/tmp/wobpipe), or remove the exec-once wob/fifo setup entirely if you don't want
an OSD; specifically modify the commands that invoke wpctl and brightnessctl to
append a write to /tmp/wobpipe so wob receives updates.

Comment on lines +161 to +167
bind = $mod, T, exec, ghostty
bind = $mod, G, exec, google-chrome-stable
bind = $mod, S, exec, slack
bind = $mod, C, exec, cursor
bind = $mod, P, exec, gtk-launch 1password
bind = $mod, M, exec, gtk-launch signal-desktop
bind = $mod, N, exec, ghostty -e yazi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's understand the repository structure
git ls-files | head -30

Repository: shunkakinoki/dotfiles

Length of output: 665


🏁 Script executed:

# Search for nix configuration files that define packages
fd -e nix | head -20

Repository: shunkakinoki/dotfiles

Length of output: 574


🏁 Script executed:

# Look for the packages/dependencies configuration
rg -n 'cursor|1password|signal-desktop' --type nix | head -30

Repository: shunkakinoki/dotfiles

Length of output: 454


🏁 Script executed:

# Check the specific file for context
cat -n config/hyprland/hyprland.conf | sed -n '155,175p'

Repository: shunkakinoki/dotfiles

Length of output: 1116


🏁 Script executed:

# Check what the cursor config module does
cat -n config/cursor/default.nix

Repository: shunkakinoki/dotfiles

Length of output: 221


🏁 Script executed:

# Check home-manager packages to see full context
cat -n home-manager/packages/default.nix | sed -n '20,35p'

Repository: shunkakinoki/dotfiles

Length of output: 321


🏁 Script executed:

# Check for .desktop files or xdg configuration
rg -n '\.desktop|xdgData|desktopItems' --type nix | head -20

Repository: shunkakinoki/dotfiles

Length of output: 152


🏁 Script executed:

# Search for 1password and signal-desktop configuration
rg -n '_1password-gui|signal-desktop' --type nix -B2 -A2

Repository: shunkakinoki/dotfiles

Length of output: 601


🏁 Script executed:

# Check home-manager configuration for xdg.desktopEntries or similar
rg -n 'xdg\.|desktopEntries' --type nix | head -20

Repository: shunkakinoki/dotfiles

Length of output: 1696


🏁 Script executed:

# Look for how packages are imported and if there's a desktop environment setup
cat -n home-manager/packages/default.nix | sed -n '1,30p'

Repository: shunkakinoki/dotfiles

Length of output: 629


🏁 Script executed:

# Check if there's any configuration for gtk or application launching
rg -n 'gtk-launch|desktopDatabase|xdg-utils' --type nix

Repository: shunkakinoki/dotfiles

Length of output: 47


🏁 Script executed:

# Verify the context around isDesktop and Linux packages
cat -n home-manager/packages/default.nix | sed -n '110,145p'

Repository: shunkakinoki/dotfiles

Length of output: 776


🏁 Script executed:

# Check if cursor is defined anywhere else in the nix config
rg -n '^cursor\s*=' --type nix

Repository: shunkakinoki/dotfiles

Length of output: 47


🏁 Script executed:

# Check if there's a custom desktop entry or application launcher for cursor
rg -n 'cursor' home-manager/packages/default.nix -B2 -A2

Repository: shunkakinoki/dotfiles

Length of output: 124


🏁 Script executed:

# Verify if nix-darwin homebrew config is used for Linux systems
cat -n nix-darwin/config/homebrew.nix | sed -n '70,85p'

Repository: shunkakinoki/dotfiles

Length of output: 478


🏁 Script executed:

# Check the system-specific configuration to understand if this is Linux or macOS
rg -n 'isDesktop|stdenv.isLinux' --type nix config/hyprland/ home-manager/ named-hosts/ | head -10

Repository: shunkakinoki/dotfiles

Length of output: 1263


🏁 Script executed:

# Check if cursor is wrapped or aliased anywhere
rg -n 'cursor' --type nix home-manager/ config/ | grep -v 'cursor-cli' | grep -v '.cursor'

Repository: shunkakinoki/dotfiles

Length of output: 47


🏁 Script executed:

# Verify the system this hyprland config is for - look at named-hosts
ls -la named-hosts/

Repository: shunkakinoki/dotfiles

Length of output: 410


🏁 Script executed:

# Check which host system uses hyprland
rg -n 'hyprland' --type nix named-hosts/

Repository: shunkakinoki/dotfiles

Length of output: 386


🏁 Script executed:

# Verify package names for 1password and signal-desktop
rg -n 'lib.optionals.*isDesktop' home-manager/packages/default.nix -A 30

Repository: shunkakinoki/dotfiles

Length of output: 594


🏁 Script executed:

# Double-check: is there any wrapping or aliasing of cursor-cli to cursor?
rg -n 'makeWrapper|symlinkJoin' --type nix | grep cursor

Repository: shunkakinoki/dotfiles

Length of output: 47


🏁 Script executed:

# Check what _1password-gui and signal-desktop actually provide
rg -n 'desktop|1password|signal' home-manager/ config/ named-hosts/ | grep -i 'desktop' | head -10

Repository: shunkakinoki/dotfiles

Length of output: 973


🏁 Script executed:

# Verify the exact package names and if they have .desktop files
cat -n home-manager/packages/default.nix | sed -n '115,150p'

Repository: shunkakinoki/dotfiles

Length of output: 755


🏁 Script executed:

# Final verification: check if this is the matic host (Linux) or another host
cat -n named-hosts/matic/default.nix | sed -n '100,130p'

Repository: shunkakinoki/dotfiles

Length of output: 1224


🏁 Script executed:

# Confirm what exact packages are included for desktop Linux systems
cat -n home-manager/packages/default.nix | sed -n '1,10p'

Repository: shunkakinoki/dotfiles

Length of output: 275


Fix the cursor binding — the desktop app binary is not available.

The binding on line 164 will fail at runtime. Only cursor-cli is in the Linux packages (home-manager/packages/default.nix), not the cursor desktop editor binary. The cursor entry in nix-darwin/config/homebrew.nix is for macOS and not applicable here.

The gtk-launch 1password and gtk-launch signal-desktop bindings should work correctly—both _1password-gui and signal-desktop are present in the Linux desktop packages and will provide the required .desktop files.

🤖 Prompt for AI Agents
In `@config/hyprland/hyprland.conf` around lines 161 - 167, The binding "bind =
$mod, C, exec, cursor" will fail because the desktop binary isn't available on
Linux; replace it with the installed CLI binary name from the package set (e.g.,
use "cursor-cli") or point the binding to the correct executable provided by the
Linux package; update the bind line that references "cursor" so it calls the
available binary (reference: the bind line with "$mod, C, exec, cursor" and the
package "cursor-cli") and keep the existing gtk-launch entries for 1password and
signal-desktop as-is.

@shunkakinoki
shunkakinoki merged commit 35a44b5 into main Feb 6, 2026
25 checks passed
@shunkakinoki
shunkakinoki deleted the feat/matic-hyprland-keyd branch February 6, 2026 18:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants