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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion config/hyprland/hyprland.conf
Original file line number Diff line number Diff line change
Expand Up @@ -439,4 +439,3 @@ bind = CTRL ALT SHIFT SUPER, L, exec, hyprlock
# Notification Toggle
# =============================================================================
bind = $mod SHIFT, D, exec, hyprpanel toggleWindow notifications-center

6 changes: 6 additions & 0 deletions config/keyd/app.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# keyd-application-mapper normalizes classes/titles to lowercase
# with punctuation collapsed to '-'. Slack's class becomes `slack`.
[slack]
leftmeta = layer(control)
prog1 = layer(control)
f13 = layer(control)
4 changes: 0 additions & 4 deletions config/keyd/default.conf
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@ rightshift = capslock
# xremap then converts Hyper+key → Ctrl+key for apps.
# Keys excluded from xremap (e.g. 3/4/5) pass through as Hyper to Hyprland.

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

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

Expand Down
11 changes: 10 additions & 1 deletion config/keyd/default.nix
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
_: {
{
username,
...
}:
{
services.keyd.enable = true;

# Optional: silence the setgid warning (nice to have, not required for functionality)
Expand All @@ -14,4 +18,9 @@ _: {
];

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

home-manager.users.${username} = {
xdg.configFile."keyd/app.conf".source = ./app.conf;
services."keyd-application-mapper".enable = true;
};
}
28 changes: 20 additions & 8 deletions home-manager/modules/xremap/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -80,17 +80,23 @@ let
value = "${ctrlPrefix}${key}";
}) keys
);
# Framework+key Ctrl+key for all apps (macOS-style shortcuts)
# Framework+key -> Ctrl+key for all apps (macOS-style shortcuts)
globalRemap = mkRemap remapKeys;
# Ghostty: Framework+C/V → Ctrl+Shift+C/V (terminal convention: Ctrl+C = SIGINT)
ghosttyRemap = globalRemap // {
# Terminal/editor clipboard convention: Framework+C/V -> Ctrl+Shift+C/V so
# copy/paste does not collide with SIGINT or app-specific bare Ctrl+C handlers.
terminalClipboardRemap = globalRemap // {
"C-c" = "C-Shift-c";
"${hyperPrefix}c" = "C-Shift-c";
"${hyperPrefix}v" = "C-Shift-v";
};
Comment on lines +87 to 91

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.

high

The terminalClipboardRemap definition includes a remap for the bare C-c key (line 88). This causes the physical Ctrl+C key to send Ctrl+Shift+C.

In terminal applications, this effectively breaks the ability to send the SIGINT (interrupt) signal, which is usually bound to Ctrl+C. If your goal is to have Cmd+C (Framework+C) perform a copy while keeping Ctrl+C as the interrupt signal, you should remove the C-c entry. This is particularly relevant given the comment on line 119 which acknowledges the Ctrl+C = SIGINT convention.

  terminalClipboardRemap = globalRemap // {
    "${hyperPrefix}c" = "C-Shift-c";
    "${hyperPrefix}v" = "C-Shift-v";
  };

Comment thread
coderabbitai[bot] marked this conversation as resolved.
# Slack: same mapping as global but isolated so Slack-specific workarounds
# (modifier leak, thread mark-as-read on bare `c`/`Esc`) can be tuned here
# without affecting other apps. See commits e60e0df, 95679b8, 48ad8f5.
codeEditorAppIds = [
"cursor"
"Cursor"
"code"
"Code"
];
Comment on lines +92 to +97

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

On Wayland, Electron-based applications like VS Code and Cursor often use specific app_id strings such as code-url-handler or cursor-url-handler rather than just code or cursor. Adding these variants ensures the remaps are applied correctly when running natively on Wayland. You can verify the exact identifiers using hyprctl clients while the apps are running.

  codeEditorAppIds = [
    "cursor"
    "Cursor"
    "cursor-url-handler"
    "code"
    "Code"
    "code-url-handler"
  ];

Comment on lines +92 to +97

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:

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

Repository: shunkakinoki/dotfiles

Length of output: 106


🏁 Script executed:

git ls-files | grep -i xremap | head -20

Repository: shunkakinoki/dotfiles

Length of output: 104


🏁 Script executed:

find . -name "*.nix" -path "*xremap*" | head -20

Repository: shunkakinoki/dotfiles

Length of output: 106


🏁 Script executed:

cat -n home-manager/modules/xremap/default.nix | head -140

Repository: shunkakinoki/dotfiles

Length of output: 4914


🌐 Web query:

VS Code default keybindings Ctrl+Shift+C terminal

💡 Result:

In Visual Studio Code, the default keybinding Ctrl+Shift+C (⇧⌘C on macOS) when the integrated terminal is focused is "Terminal: Copy Selection" (command ID: workbench.action.terminal.copySelection). This copies the currently selected text in the terminal to the system clipboard. It is included in terminal.integrated.commandsToSkipShell by default, so it is handled by VS Code rather than passed to the shell. Note that globally (outside the terminal), Ctrl+Shift+C is bound to "Open New External Terminal" (workbench.action.terminal.openNativeConsole). This behavior follows platform standards for terminals: on Linux, Ctrl+Shift+C is standard for copy in terminals (to avoid conflicting with Ctrl+C for interrupt), while Windows uses Ctrl+C.

Citations:


🌐 Web query:

xremap application filtering scope integrated terminal

💡 Result:

xremap supports application filtering using the application field in modmap and keymap configurations. This allows key remappings to apply only to specific applications (using only) or exclude them (using not). Application names are matched exactly or via regex (e.g., /regex/). For integrated terminals: - xremap detects the host application's class/name, not the shell inside. - VSCode integrated terminal uses VSCode's class: "code.Code" (X11) or similar on Wayland (e.g., "code" or "Code"). - Standalone terminals: "Gnome-terminal" (X11), "gnome-terminal-server" (GNOME Wayland), "Kitty", "Alacritty", "foot" (app_id on wlroots). To find exact names: - X11: wmctrl -x -l (use 3rd column, full or last part). - GNOME Wayland: busctl --user call org.gnome.Shell /com/k0kubun/Xremap com.k0kubun.Xremap WMClasses (requires xremap-gnome extension). - KDE Wayland: Trigger a filter; logs "class". - Sway: swaymsg -t get_tree (app_id). - Fallback: Config with application: {not: []} or window: {not: []}; press key to log active app/window. Example config for terminals: shared: terminals: &terminals - Gnome-terminal - Kitty - code.Code # VSCode incl. integrated terminal keymap: - application: only: *terminals remap: # terminal-specific remaps xremap integrates with X11/Wayland via feature flags (e.g., --features x11, --features gnome). Filters evaluate via WMClient querying current_application.

Citations:


Remove VS Code/Cursor from terminal clipboard remapping—it breaks editor copy behavior.

When the Code Editors remapping applies terminalClipboardRemap app-wide, Framework+C emits Ctrl+Shift+C even in the editor pane, which opens a new external terminal instead of copying. VS Code's editor copy is Ctrl+C, not Ctrl+Shift+C. While Ctrl+Shift+C correctly triggers terminal copy when the integrated terminal is focused, xremap operates at the window level and cannot distinguish between the integrated terminal and editor panes within the same VS Code window.

Either remove lines 92–97 and 124–128, or configure terminal-specific copy/paste through VS Code's keybindings with terminal focus conditions instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@home-manager/modules/xremap/default.nix` around lines 92 - 97, The
codeEditorAppIds list includes VS Code/Cursor ("cursor","Cursor","code","Code")
which causes terminalClipboardRemap to apply to the whole VS Code window and
breaks editor copy behavior; remove those entries from codeEditorAppIds (and
corresponding occurrences where terminalClipboardRemap is applied, e.g., the
block referenced by terminalClipboardRemap usage) so the remap no longer targets
VS Code/Cursor windows, or alternatively stop applying terminalClipboardRemap
app-wide and instead rely on VS Code keybindings with terminal focus conditions
to handle terminal copy/paste.

# Slack-specific Framework->Ctrl behavior is handled by keyd's
# application mapper on hosts that enable ~/.config/keyd/app.conf.
slackRemap = globalRemap;
in
{
Expand All @@ -109,10 +115,16 @@ in
keypress_delay_ms = 10;
keymap = [
{
# Ghostty: Framework+C/V Ctrl+Shift+C/V (terminal convention: Ctrl+C = SIGINT)
# Ghostty: Framework+C/V -> Ctrl+Shift+C/V (terminal convention: Ctrl+C = SIGINT)
name = "Framework Command (Ghostty)";
application.only = [ "com.mitchellh.ghostty" ];
remap = ghosttyRemap;
remap = terminalClipboardRemap;
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
}
{
# Cursor/VS Code: terminal-style clipboard shortcut, not bare Ctrl+C.
name = "Framework Command (Code Editors)";
application.only = codeEditorAppIds;
remap = terminalClipboardRemap;
}
Comment on lines +123 to 128

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.

high

Applying terminalClipboardRemap to GUI editors like VS Code and Cursor is likely incorrect. These applications use Ctrl+C/V for clipboard operations by default on Linux, whereas Ctrl+Shift+C/V often trigger different actions (e.g., opening a new terminal in VS Code).

Since globalRemap (applied on line 141) already maps Hyper+C/V to Ctrl+C/V, editors will automatically receive macOS-style copy/paste behavior without this specific block. Using the terminal-specific remap here will likely break standard editor shortcuts and the physical Ctrl+C behavior.

{
# Slack: isolated block so modifier-leak / thread mark-as-read
Expand Down
2 changes: 2 additions & 0 deletions home-manager/services/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ let
dockerPostgres = ./docker-postgres;
dotfilesUpdater = import ./dotfiles-updater { inherit pkgs; };
gasTown = import ./gas-town { inherit pkgs; };
keydApplicationMapper = ./keyd-application-mapper;
makeUpdater = import ./make-updater { inherit pkgs; };
neversslKeepalive = import ./neverssl-keepalive { inherit pkgs; };
obsidian = import ./obsidian { inherit config pkgs inputs; };
Expand All @@ -37,6 +38,7 @@ in
dockerPostgres
dotfilesUpdater
gasTown
keydApplicationMapper
makeUpdater
neversslKeepalive
obsidian
Expand Down
47 changes: 47 additions & 0 deletions home-manager/services/keyd-application-mapper/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services."keyd-application-mapper";
in
{
options.services."keyd-application-mapper" = {
enable = lib.mkEnableOption "keyd application mapper user service";
};

config = lib.mkIf (pkgs.stdenv.isLinux && cfg.enable) {
assertions = [
{
assertion = config.xdg.configFile ? "keyd/app.conf";
message = "services.keyd-application-mapper requires xdg.configFile.\"keyd/app.conf\"";
}
];

systemd.user.services.keyd-application-mapper = {
Unit = {
Description = "keyd application mapper";
After = [ "graphical-session.target" ];
PartOf = [ "graphical-session.target" ];
};
Service = {
Type = "simple";
# Force a unit restart on switch when app.conf changes.
Environment = [
"KEYD_APP_CONF_HASH=${builtins.hashFile "sha256" config.xdg.configFile."keyd/app.conf".source}"
];
# User managers can start before refreshed supplementary groups
# are visible in the login session. Enter the keyd group
# explicitly so the mapper can always reach /var/run/keyd.socket.
ExecStart = "${pkgs.bash}/bin/bash -lc 'exec /run/wrappers/bin/sg keyd -c \"KEYD_BIN=${pkgs.keyd}/bin/keyd ${pkgs.keyd}/bin/keyd-application-mapper\"'";
Restart = "on-failure";
RestartSec = 3;
};
Install = {
WantedBy = [ "graphical-session.target" ];
};
};
};
}
16 changes: 4 additions & 12 deletions named-hosts/matic/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import ../../hosts/nixos {
"video"
"audio"
"docker"
"keyd"
];
modules = [
# Framework 13" AMD AI 300 hardware support
Expand All @@ -22,6 +23,9 @@ import ../../hosts/nixos {
# Hardware configuration
./hardware-configuration.nix

# Shared keyd config and application mapper wiring
../../config/keyd/default.nix

# Kolide launcher
./kolide.nix

Expand Down Expand Up @@ -86,18 +90,6 @@ import ../../hosts/nixos {
ExecStop = "${pkgs.e2fsprogs}/bin/chattr -i /";
};
};
# Keyd configuration (Linux desktop only)
services.keyd.enable = true;
users.groups.keyd = { };
systemd.services.keyd.serviceConfig = {
CapabilityBoundingSet = [ "CAP_SETGID" ];
AmbientCapabilities = [ "CAP_SETGID" ];
};
systemd.services.keyd.restartTriggers = [
(builtins.hashFile "sha256" ../../config/keyd/default.conf)
];
environment.etc."keyd/default.conf".source = ../../config/keyd/default.conf;

# Input remapping (xremap)
hardware.uinput.enable = true;
services.udev.extraRules = ''
Expand Down
Loading