feat: make the clipboard work across tmux, ssh and hosts
CI / lint (push) Successful in 31s
CI / eval (push) Successful in 2m1s

tmux copy bindings piped to xclip, which is X11-only and never worked
under Hyprland or over SSH, and set-clipboard was never enabled, so the
vi-mode `y` binding was dead too. Route both through OSC 52 instead.
terminal-features has to name the outer terminal explicitly: tmux skips
OSC 52 silently when the terminfo entry does not claim the capability,
which is the common case for an SSH session on xterm-256color.

Neovim only falls back to OSC 52 when it finds no clipboard tool, but
wl-clipboard is installed on every host, so over SSH it wrote the remote
clipboard. Pin the OSC 52 provider for SSH sessions only.

Moonlight and Sunshine have no clipboard channel in the protocol, so
nothing at the terminal layer can help there. Add kdeconnect on all three
hosts for that, and start its indicator from Hyprland — kdeconnectd is
DBus-activated and nothing else brings it up at login.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Miguel Palhas
2026-08-18 08:14:17 +01:00
parent c79436d340
commit d417a27f2d
7 changed files with 44 additions and 3 deletions
@@ -219,6 +219,9 @@ in
hl.exec_cmd("hyprctl setcursor Nordzy-cursors ${toString cfg.cursorSize}") hl.exec_cmd("hyprctl setcursor Nordzy-cursors ${toString cfg.cursorSize}")
hl.exec_cmd("hyprsunset") hl.exec_cmd("hyprsunset")
hl.exec_cmd("noctalia") hl.exec_cmd("noctalia")
-- kdeconnectd is only DBus-activated; nothing starts it at login,
-- so clipboard sync stays dead until the indicator runs.
hl.exec_cmd("kdeconnect-indicator")
end) end)
-- layer rules -- layer rules
+9
View File
@@ -12,5 +12,14 @@
programs.nvchad = { programs.nvchad = {
enable = true; enable = true;
# Neovim only auto-picks OSC 52 when it finds no clipboard tool, but
# wl-clipboard is on every host — so over SSH it writes the *remote*
# clipboard instead. Locally wl-copy stays: kitty refuses OSC 52 reads.
extraConfig = ''
if vim.env.SSH_TTY and vim.env.SSH_TTY ~= "" then
vim.g.clipboard = "osc52"
end
'';
}; };
} }
+19 -3
View File
@@ -101,6 +101,20 @@ setw -g mode-keys vi
bind -T copy-mode-vi v send -X begin-selection bind -T copy-mode-vi v send -X begin-selection
bind -T copy-mode-vi y send -X copy-selection-and-cancel bind -T copy-mode-vi y send -X copy-selection-and-cancel
# -------------------------------------------------------------------
# Clipboard (OSC 52)
# -------------------------------------------------------------------
set -g set-clipboard on
# tmux silently skips OSC 52 unless the outer terminal's terminfo claims the
# capability. kitty's does; SSH sessions usually land on a bare
# xterm-256color that does not, so declare both.
set -ag terminal-features ",xterm-kitty:clipboard"
set -ag terminal-features ",xterm-256color:clipboard"
# Let nvim's OSC 52 provider reach the outer terminal through tmux.
set -g allow-passthrough on
# tmux-resurrect settings # tmux-resurrect settings
set -g @resurrect-capture-pane-contents 'on' set -g @resurrect-capture-pane-contents 'on'
@@ -142,7 +156,9 @@ bind-key -T copy-mode-vi WheelDownPane send-keys -X scroll-down
bind-key -T copy-mode WheelUpPane send-keys -X scroll-up bind-key -T copy-mode WheelUpPane send-keys -X scroll-up
bind-key -T copy-mode WheelDownPane send-keys -X scroll-down bind-key -T copy-mode WheelDownPane send-keys -X scroll-down
# Clipboard integration (drag-to-copy) # Clipboard integration (drag-to-copy). No external command: set-clipboard
bind-key -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-and-cancel "xclip -in -selection clipboard" # above routes this through OSC 52, which works on Wayland and over SSH.
bind-key -T copy-mode MouseDragEnd1Pane send-keys -X copy-pipe-and-cancel "xclip -in -selection clipboard" # xclip did neither.
bind-key -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-and-cancel
bind-key -T copy-mode MouseDragEnd1Pane send-keys -X copy-pipe-and-cancel
# End agent-deck configuration # End agent-deck configuration
+1
View File
@@ -17,6 +17,7 @@
../common/features/docker.nix ../common/features/docker.nix
../common/features/appimage.nix ../common/features/appimage.nix
../common/features/fonts.nix ../common/features/fonts.nix
../common/features/kdeconnect.nix
../common/features/nix-ld.nix ../common/features/nix-ld.nix
../common/features/bluetooth.nix ../common/features/bluetooth.nix
../common/features/ledger.nix ../common/features/ledger.nix
+10
View File
@@ -0,0 +1,10 @@
{ pkgs, ... }:
{
# Clipboard (and file) sync between hosts over the LAN. Moonlight/Sunshine
# carry no clipboard channel at all, so this is the only path between the
# streaming client and its host.
programs.kdeconnect = {
enable = true;
package = pkgs.kdePackages.kdeconnect-kde;
};
}
+1
View File
@@ -13,6 +13,7 @@
../common/features/pipewire.nix ../common/features/pipewire.nix
../common/features/docker.nix ../common/features/docker.nix
../common/features/fonts.nix ../common/features/fonts.nix
../common/features/kdeconnect.nix
../common/features/nix-ld.nix ../common/features/nix-ld.nix
../common/features/bluetooth.nix ../common/features/bluetooth.nix
../common/features/ledger.nix ../common/features/ledger.nix
+1
View File
@@ -14,6 +14,7 @@
../common/features/remote-desktop.nix ../common/features/remote-desktop.nix
../common/features/docker.nix ../common/features/docker.nix
../common/features/fonts.nix ../common/features/fonts.nix
../common/features/kdeconnect.nix
../common/features/nix-ld.nix ../common/features/nix-ld.nix
../common/features/appimage.nix ../common/features/appimage.nix
../common/features/home ../common/features/home