From 4a38a2f0d9b03be0698386fe62c9511d92b2252f Mon Sep 17 00:00:00 2001 From: rope Date: Fri, 31 Jul 2026 12:22:11 +0100 Subject: [PATCH] quickshell: Super+C keybind cheat sheet; spotify-player visualizer Co-Authored-By: Claude Opus 5 --- home-manager/fred.nix | 3 +- settings/hyprland.nix | 3 + settings/quickshell.nix | 164 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 1 deletion(-) diff --git a/home-manager/fred.nix b/home-manager/fred.nix index f95b8a3..a58ff7c 100644 --- a/home-manager/fred.nix +++ b/home-manager/fred.nix @@ -54,9 +54,10 @@ in # spotify-player — native librespot TUI replacing the Electron Spotify app. # stylix's spotify-player target writes the colour theme. # MPRIS (quickshell's media card), notifications and streaming are all on by - # default on Linux, so no settings needed here. + # default on Linux, so only the off-by-default visualiser needs setting. programs.spotify-player = lib.mkIf isDesktop { enable = true; + settings.enable_audio_visualization = true; }; # Upstream ships no .desktop file (TUI only), so the launcher can't find it. diff --git a/settings/hyprland.nix b/settings/hyprland.nix index 1558805..6cd6922 100644 --- a/settings/hyprland.nix +++ b/settings/hyprland.nix @@ -360,6 +360,9 @@ in -- Color picker — copies hex to clipboard hl.bind(mod .. " + A", hl.dsp.exec_cmd("hyprpicker -a")) + -- Keybind cheat sheet (quickshell overlay) + hl.bind(mod .. " + C", hl.dsp.exec_cmd("${qs} ipc call cheatsheet toggle")) + -- Mouse window manipulation hl.bind(mod .. " + mouse:272", hl.dsp.window.drag(), { mouse = true }) hl.bind(mod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true }) diff --git a/settings/quickshell.nix b/settings/quickshell.nix index 44cecb0..8c8db0a 100644 --- a/settings/quickshell.nix +++ b/settings/quickshell.nix @@ -254,6 +254,7 @@ in singleton Commands 1.0 Commands.qml Bar 1.0 Bar.qml Lock 1.0 Lock.qml + Cheatsheet 1.0 Cheatsheet.qml ''; }; @@ -523,6 +524,162 @@ in ''; }; + "quickshell/Cheatsheet.qml" = { + onChange = qsRestart; + text = '' + import Quickshell + import Quickshell.Wayland + import QtQuick + + // Keybind cheat sheet — Super+C routes here via shell.qml's + // "cheatsheet" IpcHandler. The list is hand-maintained: the real + // binds live in settings/hyprland.nix's Lua config and the + // spotify-player ones are that app's built-in defaults, so both + // need updating here too when they change. + Scope { + id: root + property bool open: false + + readonly property var sections: [ + { title: "Apps", binds: [ + ["Super + T", "Terminal"], + ["Super + E", "Files"], + ["Super + Z", "Zen browser"], + ["Super + R", "Launcher"], + ["Super + I", "Audio settings"], + ["Super + A", "Colour picker"], + ["Super + C", "This cheat sheet"], + ]}, + { title: "Windows", binds: [ + ["Super + Q", "Close window"], + ["Super + V", "Toggle floating"], + ["Super + F", "Fullscreen"], + ["Super + P", "Pseudo tile"], + ["Super + S", "Toggle split"], + ["Super + drag", "Move window"], + ["Super + right-drag", "Resize window"], + ]}, + { title: "Focus & workspaces", binds: [ + ["Super + ←↓↑→", "Focus window"], + ["Super + H/J/K", "Focus left/down/up"], + ["Super + Shift + ←↓↑→", "Move window"], + ["Super + 0-9", "Go to workspace"], + ["Super + Shift + 0-9", "Send to workspace"], + ["Super + scroll", "Cycle workspaces"], + ]}, + { title: "Session", binds: [ + ["Super + L", "Power menu"], + ["Super + Shift + E", "Exit Hyprland"], + ["Super + Shift + S", "Screenshot region"], + ["Print", "Screenshot screen"], + ]}, + { title: "Spotify Player", binds: [ + ["?", "Built-in help"], + ["space", "Play / pause"], + ["n / p", "Next / previous"], + ["+ / -", "Volume"], + ["< / >", "Seek"], + ["D", "Switch device"], + ["/", "Search"], + ["z", "Queue"], + ["l", "Lyrics"], + ["g l", "Library"], + ["tab", "Next pane"], + ["q", "Quit"], + ]}, + ] + + PanelWindow { + visible: root.open + WlrLayershell.namespace: "quickshell-cheatsheet" + WlrLayershell.layer: WlrLayer.Overlay + // Exclusive works here (unlike the bar): this window uses no + // HyprlandFocusGrab, so nothing fights it for focus. + WlrLayershell.keyboardFocus: root.open + ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None + exclusionMode: ExclusionMode.Ignore + anchors { top: true; left: true; right: true; bottom: true } + color: "transparent" + + // Click anywhere outside the card to dismiss. + MouseArea { + anchors.fill: parent + onClicked: root.open = false + Rectangle { anchors.fill: parent; color: "#99000000" } + } + + Item { + anchors.fill: parent + focus: true + Keys.onPressed: e => { + if (e.key === Qt.Key_Escape || e.key === Qt.Key_C + || e.key === Qt.Key_Q) + root.open = false; + } + + Rectangle { + anchors.centerIn: parent + width: grid.width + Theme.panelGap * 6 + height: grid.height + Theme.panelGap * 6 + radius: Theme.radius + color: Theme.cardBg + border.width: Theme.borderWidth + border.color: Theme.base02 + + // Swallow clicks on the card so it doesn't close. + MouseArea { anchors.fill: parent } + + Row { + id: grid + anchors.centerIn: parent + spacing: Theme.panelGap * 4 + + Repeater { + model: root.sections + Column { + required property var modelData + spacing: 4 + + Text { + text: modelData.title + font.family: Theme.fontFamily + font.pixelSize: 15 + font.bold: true + color: Theme.base0D + bottomPadding: 4 + } + + Repeater { + model: modelData.binds + Row { + required property var modelData + spacing: 10 + + Text { + width: 150 + text: modelData[0] + font.family: Theme.fontFamily + font.pixelSize: 13 + color: Theme.base0A + } + Text { + text: modelData[1] + font.family: Theme.fontFamily + font.pixelSize: 13 + color: Theme.base05 + } + } + } + } + } + } + } + } + } + } + ''; + }; + "quickshell/Commands.qml" = { onChange = qsRestart; text = '' @@ -574,6 +731,13 @@ in function powermenu(): void { if (root.mainBar) root.mainBar.toggleSession(); } } + // Keybind cheat sheet — Super+C in hyprland.nix. + Cheatsheet { id: cheatsheet } + IpcHandler { + target: "cheatsheet" + function toggle(): void { cheatsheet.open = !cheatsheet.open; } + } + // Soft reload, used by the nix onChange hook — keeps the // process and its DBus services (tray host) alive. IpcHandler {