Compare commits

..

4 Commits

Author SHA1 Message Date
Emma Thorpe e78e52e18d docs: add a keybindings reference covering Sway/tmux/foot/zsh
Document every configured shortcut in lyrathorpe/home/KEYBINDINGS.md,
compiled from the rendered configs (so it includes the home-manager Sway
module defaults alongside the custom binds and modes), and link it from
the top-level README. Notes the Dvorak keysym caveat and the
laptop-only brightness keys.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 20:57:15 +01:00
Emma Thorpe e7e11c17b0 feat(tmux): apply dotfiles tmux config
From emmaisadev/dotfiles (tmux.conf): run a non-login shell
(default-command), drop the stock %/" split keys (the s/v vim splits
already come from reverseSplit), declare foot's terminal-features
(RGB/sync/clipboard/title/ccolour/cstyle), and raise the scrollback to
500000. Alt-arrow pane nav and mouse were already present. Also drop a
stale comment referencing the removed tty1 autostart.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 20:49:36 +01:00
Emma Thorpe b04391809e feat(sway): port keybindings, modes and tweaks from dotfiles
Adapted from emmaisadev/dotfiles (sway/config.d) into the Nix config:

- Screenshots to swappy: Print = drag a region, Shift+Print = focused
  window (a writeShellScript reusing the grimshot.sh tree/jq logic).
  Replaces the old plain full-screen grim->file.
- Workspace cycle Mod+z / Mod+x (prev/next).
- Media keys (playerctl) and mic mute (wpctl source).
- Re-home `focus mode_toggle` onto Mod+Alt+space (Mod+Space is the
  launcher now).
- Clipboard history: services.clipman stores copies; Mod+c picks one
  through a Catppuccin-themed fuzzel (programs.fuzzel).
- Binding modes: a layout submenu (Mod+y -> s/w/e, which also restores
  split-toggle that Mod+e gave up to nemo) and a power menu
  (Mod+Shift+x -> lock/exit/sleep/reboot/shutdown). Mod+l still locks
  immediately.
- Touchpad tap + natural scroll (laptops; inert on desktop).
- Solid Catppuccin base as the wallpaper (output * bg, no image).
- foot: term=xterm-256color and scrollback 100000 (colours unchanged).
- swaywm.nix: add slurp/swappy/jq/playerctl to the session packages.

Skipped from the dotfiles: named workspaces + app auto-assign, the foot
--server/footclient setup, and pactl/MX-Master/lxqt device-specific bits.
All in the shared files, so every Sway host gets it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 20:49:36 +01:00
Emma Thorpe 2cbcc3d7f1 feat(sway): add the nemo file manager (themed, Mod+e)
Install nemo and bind it to Mod+e (mkForce, overriding the module
default `layout toggle split`). Add a home-manager `gtk` block so GTK
apps match the desktop: the Catppuccin Mocha GTK theme
(catppuccin-gtk, mocha/blue), with Adwaita icons + cursor as before.
Under Sway GTK reads ~/.config/gtk-*/settings.ini directly (no XSettings
daemon), so this themes nemo without extra env.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 20:30:32 +01:00
6 changed files with 342 additions and 5 deletions
+5
View File
@@ -28,6 +28,11 @@ sudo nixos-rebuild switch --flake .#<configuration>
darwin-rebuild switch --flake .#lyrathorpe-mac
```
## Keybindings
All Sway / tmux / foot / zsh keyboard shortcuts are documented in
[`lyrathorpe/home/KEYBINDINGS.md`](./lyrathorpe/home/KEYBINDINGS.md).
## Login / greeter
Graphical (Sway) hosts log in through a Wayland greeter — `greetd` running
+172
View File
@@ -0,0 +1,172 @@
# Keybindings reference
Every keyboard shortcut configured across this desktop, and where it is defined.
Everything here is managed declaratively through Nix — edit the listed file and
rebuild, never the generated dotfiles.
| Area | Defined in |
| --- | --- |
| Sway (compositor) | [`sway.nix`](./sway.nix) `config.keybindings` + `config.modes`, plus the home-manager Sway module's built-in defaults |
| tmux | [`shell.nix`](./shell.nix) `programs.tmux` |
| zsh line editor | [`shell.nix`](./shell.nix) `programs.zsh.historySubstringSearch` |
| foot (terminal) | foot package defaults — only colours are themed (in `sway.nix`) |
**Conventions**
- **Super** is the `Mod4` / logo (Windows/Command) key; **Alt** is `Mod1`.
- Letter keys are **keysyms** (the character produced), not physical positions.
The keyboard is **Dvorak** (`us`/`dvorak`), so e.g. "Super+s" is whatever key
types `s` in Dvorak.
- Shortcuts apply to every Sway host (MBP, T400, Mac Pro); brightness keys are
laptop-only, as noted.
---
## Sway
### Applications & session
| Shortcut | Action |
| --- | --- |
| `Super`+`Return` | Open a terminal (foot) |
| `Super`+`Space` | App launcher (sway-launcher-desktop in a floating foot) |
| `Super`+`d` | App launcher (same as above; module default) |
| `Super`+`e` | File manager (nemo) |
| `Super`+`c` | Clipboard history picker (clipman → fuzzel) |
| `Super`+`l` | Lock screen (swaylock) |
| `Super`+`Shift`+`q` | Close the focused window |
| `Super`+`Shift`+`c` | Reload the Sway config |
| `Super`+`Shift`+`e` | Exit Sway (asks for confirmation) |
### Focus
| Shortcut | Action |
| --- | --- |
| `Super`+`←`/`↓`/`↑`/`→` | Move focus by direction |
| `Super`+`h`/`j`/`k` | Move focus left / down / up (vim-style) |
| `Super`+`a` | Focus the parent container |
| `Super`+`Alt`+`Space` | Toggle focus between tiling and floating |
> Note: vim focus-right would be `Super`+`l`, but that is bound to **lock** here;
> use `Super`+`→`.
### Moving windows
| Shortcut | Action |
| --- | --- |
| `Super`+`Shift`+`←`/`↓`/`↑`/`→` | Move the window by direction |
| `Super`+`Shift`+`h`/`j`/`k`/`l` | Move the window left / down / up / right |
| `Super`+`Shift`+`Space` | Toggle the window floating |
Mouse (with `Super` held): left-drag moves a window, right-drag resizes it.
### Layout
| Shortcut | Action |
| --- | --- |
| `Super`+`b` | Split horizontally |
| `Super`+`v` | Split vertically |
| `Super`+`s` | Stacking layout |
| `Super`+`w` | Tabbed layout |
| `Super`+`f` | Toggle fullscreen |
| `Super`+`y` | **Layout submenu**: `s` stacking · `w` tabbed · `e` toggle split · `Return`/`Esc` exit |
> The layout submenu's `e` (toggle split) is the home for that action since
> `Super`+`e` now opens the file manager.
### Workspaces
| Shortcut | Action |
| --- | --- |
| `Super`+`1``0` | Switch to workspace 1…10 |
| `Super`+`Shift`+`1``0` | Move the window to workspace 1…10 |
| `Super`+`z` | Previous workspace |
| `Super`+`x` | Next workspace |
### Scratchpad
| Shortcut | Action |
| --- | --- |
| `Super`+`Shift`+`-` | Move the window to the scratchpad |
| `Super`+`-` | Show / cycle the scratchpad |
### Modes (submenus)
| Shortcut | Action |
| --- | --- |
| `Super`+`r` | **Resize mode**: arrow keys resize; `Return`/`Esc` exit |
| `Super`+`y` | **Layout mode** (see Layout above) |
| `Super`+`Shift`+`x` | **Power menu**: `l` lock · `e` log out · `s` sleep · `r` reboot · `Shift`+`s` shutdown · `Return`/`Esc` exit |
### Screenshots
| Shortcut | Action |
| --- | --- |
| `Print` | Select a region → swappy (annotate/save) |
| `Shift`+`Print` | Focused window → swappy |
### Audio & media
| Shortcut | Action |
| --- | --- |
| `XF86AudioRaiseVolume` / `XF86AudioLowerVolume` | Volume ±5% (wpctl) |
| `XF86AudioMute` | Toggle output mute |
| `XF86AudioMicMute` | Toggle microphone mute |
| `XF86AudioPlay` | Play/pause (playerctl) |
| `XF86AudioNext` / `XF86AudioPrev` | Next / previous track |
### Brightness — laptops only
| Shortcut | Action |
| --- | --- |
| `XF86MonBrightnessUp` / `XF86MonBrightnessDown` | Backlight ±5% (brightnessctl) |
Present only on portable hosts (T400, MBP); desktops have no internal backlight.
---
## tmux
Prefix is **`Ctrl`+`b`** (default). Copy mode uses **vi** keys.
| Shortcut | Action |
| --- | --- |
| `Ctrl`+`b` then `v` | Split into left/right panes |
| `Ctrl`+`b` then `s` | Split into top/bottom panes |
| `Alt`+`←`/`→`/`↑`/`↓` | Switch pane by direction (no prefix needed) |
| `Ctrl`+`b` then `[` | Enter copy mode (then vi motions; `Space`/`Enter` to select/copy) |
| `Ctrl`+`b` then `z` | Zoom / unzoom the focused pane |
| `Ctrl`+`b` then `c` | New window |
| `Ctrl`+`b` then `n` / `p` | Next / previous window |
| `Ctrl`+`b` then `d` | Detach |
| Mouse | Enabled — click to focus, drag borders, scroll, select |
> The stock split keys `%` and `"` are unbound; use `v` / `s` above. `Ctrl`+`b`
> then `s` is therefore a split, not the session tree.
---
## foot (terminal)
Only colours are themed; these are foot's default key bindings.
| Shortcut | Action |
| --- | --- |
| `Ctrl`+`Shift`+`c` / `Ctrl`+`Shift`+`v` | Copy / paste (clipboard) |
| `Shift`+`Insert` | Paste primary selection |
| `Ctrl`+`Shift`+`r` | Search scrollback |
| `Ctrl`+`+` / `Ctrl`+`-` / `Ctrl`+`0` | Font larger / smaller / reset |
| `Ctrl`+`Shift`+`u` | URL mode (jump to/open links) |
| `Ctrl`+`Shift`+`n` | Spawn a new terminal |
| `Shift`+`PageUp` / `Shift`+`PageDown` | Scroll back / forward |
---
## zsh
| Shortcut | Action |
| --- | --- |
| `↑` / `↓` | History **substring** search — type a fragment first, then the arrows cycle matching past commands |
Bound for both CSI and SS3 cursor sequences, so it works in foot, iTerm2 and
the Linux TTY alike.
+20
View File
@@ -11,6 +11,7 @@
home.packages = [
pkgs.element-desktop
pkgs.legcord
pkgs.nemo # file manager (launched via Mod+e, see ./sway.nix)
#pkgs.plex-desktop
#pkgs.plexamp
];
@@ -20,6 +21,25 @@
XDG_CURRENT_DESKTOP = "sway";
};
# Theme GTK apps (nemo, etc.) to match the Catppuccin Mocha desktop. Under
# Sway there is no XSettings daemon, so GTK reads these from the generated
# ~/.config/gtk-{3,4}.0/settings.ini directly. The Mocha theme is dark by
# design, so no separate prefer-dark hint is needed.
gtk = {
enable = true;
theme = {
name = "catppuccin-mocha-blue-standard";
package = pkgs.catppuccin-gtk.override {
accents = [ "blue" ];
variant = "mocha";
};
};
iconTheme = {
name = "Adwaita";
package = pkgs.adwaita-icon-theme;
};
};
home.pointerCursor = {
gtk.enable = true;
x11 = {
+21 -4
View File
@@ -31,9 +31,7 @@
theme = "robbyrussell";
};
syntaxHighlighting.enable = true;
# Prefix the prompt with the hostname over SSH. The graphical autostart
# (exec sway on tty1) lives in ./desktop.nix so it never runs on headless
# hosts.
# Prefix the prompt with the hostname over SSH.
initContent = lib.mkOrder 1500 ''
if [ "$SSH_CLIENT" ] || [ "$SSH_TTY" ]; then
export PS1="%M $PS1"
@@ -50,14 +48,33 @@
terminal = "tmux-direct";
newSession = true;
keyMode = "vi";
historyLimit = 50000;
historyLimit = 500000;
mouse = true;
# `reverseSplit = true` already binds s -> vertical and v -> horizontal
# split (the dotfiles' vim-style splits).
extraConfig = ''
# Run a non-login shell in new panes/windows.
set -g default-command "''${SHELL}"
# Drop the stock split keys in favour of the s/v binds above.
unbind %
unbind '"'
# Alt+Arrow pane navigation
bind -n M-Left select-pane -L
bind -n M-Right select-pane -R
bind -n M-Up select-pane -U
bind -n M-Down select-pane -D
# Tell tmux which capabilities the foot terminal supports, so truecolor,
# synchronised output, the system clipboard (OSC 52), window titles and
# cursor styling all pass through.
set -as terminal-features ",foot*:RGB"
set -as terminal-features ",foot*:sync"
set -as terminal-features ",foot*:clipboard"
set -as terminal-features ",foot*:title"
set -as terminal-features ",foot*:ccolour"
set -as terminal-features ",foot*:cstyle"
'';
};
}
+120 -1
View File
@@ -19,6 +19,19 @@ let
# where a consumer needs it -- Sway/i3status/dunst want "#", foot/swaylock do
# not.
ctp = import ../catppuccin-mocha.nix;
# Focused-window screenshot -> swappy editor (the dotfiles' grimshot.sh logic).
# Full store paths so it needs nothing on PATH.
screenshotWindow = pkgs.writeShellScript "screenshot-window" ''
${pkgs.grim}/bin/grim -g "$(${pkgs.sway}/bin/swaymsg -t get_tree \
| ${pkgs.jq}/bin/jq -r '.. | select(.focused?) | .rect | "\(.x),\(.y) \(.width)x\(.height)"')" \
- | ${pkgs.swappy}/bin/swappy -f -
'';
# Binding-mode names. The string is both the `modes` attr key and what the
# bar's mode indicator shows, so the keys are spelled out in the label.
layoutMode = "layout: [s]tacking [w]tabbed [e]split";
systemMode = "system: [l]ock [e]xit [s]leep [r]eboot [Shift+s]shutdown";
in
{
wayland.windowManager.sway = {
@@ -39,6 +52,15 @@ in
xkb_layout = "us";
xkb_variant = "dvorak";
};
# Touchpads (laptops): tap-to-click and natural scrolling. Inert on the
# desktop hosts, which have no touchpad.
input."type:touchpad" = {
tap = "enabled";
natural_scroll = "enabled";
};
# Solid Catppuccin Mocha base as the wallpaper (no image dependency).
output."*".bg = "#${ctp.base} solid_color";
# Window borders -- Catppuccin Mocha (blue accent on the focused window).
colors = {
@@ -79,6 +101,33 @@ in
}
];
# Binding modes (submenus). Entered from keybindings below; each action
# returns to the default mode. mkOptionDefault-merged with the module's
# built-in "resize" mode.
modes = {
# Layout submenu (Mod+y). Mirrors Sway's default s/w/e layout keys --
# notably it restores split-toggle, which moved off Mod+e when that
# became the nemo launcher.
${layoutMode} = {
"s" = "layout stacking, mode default";
"w" = "layout tabbed, mode default";
"e" = "layout toggle split, mode default";
"Return" = "mode default";
"Escape" = "mode default";
};
# Power menu (Mod+Shift+x). Lock reuses the themed swaylock; the rest go
# through systemd/logind (allowed for the active local session).
${systemMode} = {
"l" = "exec ${pkgs.swaylock}/bin/swaylock -f, mode default";
"e" = "exec ${pkgs.sway}/bin/swaymsg exit, mode default";
"s" = "exec systemctl suspend, mode default";
"r" = "exec systemctl reboot, mode default";
"Shift+s" = "exec systemctl poweroff, mode default";
"Return" = "mode default";
"Escape" = "mode default";
};
};
bars = [
{
position = "top";
@@ -131,11 +180,42 @@ in
# Mod+Space (focus mode_toggle); a plain value would conflict with it
# at equal priority. Mod+d also still runs the launcher (module default).
"${modifier}+space" = lib.mkForce "exec ${menu}";
# File manager. mkForce overrides the module default (layout toggle split).
"${modifier}+e" = lib.mkForce "exec ${pkgs.nemo}/bin/nemo";
"${modifier}+l" = "exec ${pkgs.swaylock}/bin/swaylock -f";
"Print" = "exec ${pkgs.grim}/bin/grim ~/screenshot-$(date +%F-%H%M%S).png";
# Cycle workspaces.
"${modifier}+z" = "workspace prev";
"${modifier}+x" = "workspace next";
# focus mode_toggle (tiling <-> floating focus) -- re-homed off
# Mod+Space, which is now the launcher.
"${modifier}+Mod1+space" = "focus mode_toggle";
# Enter the binding-mode submenus defined above.
"${modifier}+y" = "mode \"${layoutMode}\"";
"${modifier}+Shift+x" = "mode \"${systemMode}\"";
# Clipboard history: pick a past entry through fuzzel (clipman stores
# it -- see services.clipman below).
"${modifier}+c" =
"exec ${pkgs.clipman}/bin/clipman pick -t CUSTOM --tool-args=\"${pkgs.fuzzel}/bin/fuzzel --dmenu\"";
# Screenshots -> swappy editor: Print = drag a region, Shift+Print =
# the focused window.
"Print" =
"exec ${pkgs.grim}/bin/grim -g \"$(${pkgs.slurp}/bin/slurp)\" - | ${pkgs.swappy}/bin/swappy -f -";
"Shift+Print" = "exec ${screenshotWindow}";
"XF86AudioRaiseVolume" = "exec ${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+";
"XF86AudioLowerVolume" = "exec ${pkgs.wireplumber}/bin/wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-";
"XF86AudioMute" = "exec ${pkgs.wireplumber}/bin/wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle";
"XF86AudioMicMute" = "exec ${pkgs.wireplumber}/bin/wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle";
# Media keys (MPRIS via playerctl).
"XF86AudioPlay" = "exec ${pkgs.playerctl}/bin/playerctl play-pause";
"XF86AudioNext" = "exec ${pkgs.playerctl}/bin/playerctl next";
"XF86AudioPrev" = "exec ${pkgs.playerctl}/bin/playerctl previous";
}
# Screen backlight: laptops only (no internal backlight on a desktop).
// lib.optionalAttrs portable {
@@ -156,6 +236,13 @@ in
# "<text> <cursor>" (man foot.ini(5): "ff0000 00ff00" => green cursor, red
# text). Only colors-dark is needed; we never set initial-color-theme=light.
settings = {
main = {
# Advertise as xterm-256color so remote hosts without foot's terminfo
# still behave (tmux re-adds foot's RGB/sync/etc. features -- see
# shell.nix). The [main] section is the unheadered top of foot.ini.
term = "xterm-256color";
};
scrollback.lines = 100000;
"colors-dark" = {
background = ctp.base;
foreground = ctp.text;
@@ -182,6 +269,38 @@ in
};
};
# Clipboard history: a user service runs `wl-paste --watch clipman store`,
# bound to the Wayland session, so copies persist and Mod+c (above) can pick
# an old entry through fuzzel.
services.clipman.enable = true;
# fuzzel: the dmenu picker used by clipman, themed Catppuccin Mocha to match
# (fuzzel colours are RRGGBBAA -- 8 hex digits).
programs.fuzzel = {
enable = true;
settings = {
main = {
font = "Noto Sans:size=12";
prompt = "\"clipboard \"";
};
border = {
width = 2;
radius = 8;
};
colors = {
background = "${ctp.base}f0";
text = "${ctp.text}ff";
prompt = "${ctp.subtext0}ff";
input = "${ctp.text}ff";
match = "${ctp.blue}ff";
selection = "${ctp.surface1}ff";
selection-text = "${ctp.text}ff";
selection-match = "${ctp.blue}ff";
border = "${ctp.blue}ff";
};
};
};
programs.swaylock = {
enable = true;
# Catppuccin Mocha (swaylock colours are hex without "#").
+4
View File
@@ -37,6 +37,10 @@ in
brightnessctl
foot
grim
slurp # region selection for screenshots
swappy # screenshot annotation/save
jq # used by the focused-window screenshot bind
playerctl # MPRIS media keys
sway-launcher-desktop
pavucontrol
];