Fix wallpaper persistence, tray icons, Booru bugs, session launcher

- Persist last-set wallpaper and restore it via exec-once, since
  hyprpaper/mpvpaper start every session with nothing loaded
- Fix Vesktop's system tray icon rendering as a broken texture: only
  resolve tray icons through the icon theme when they're actual names,
  not raw pixmap-sourced image:// URLs
- Launch Hyprland via start-hyprland (its official watchdog launcher)
  instead of the raw binary, removing the startup warning and adding
  hang recovery
- Fix Booru panel: Lewds toggle now actually persists, tag autocomplete
  no longer drops/races overlapping requests, and pagination wraps
  instead of overflowing off-screen
- Remove the redundant gap between windows and sidebars/tiles
  (gaps_in/gaps_out, and duplicate flyout margins stacked on top of
  Hyprland's automatic exclusive-zone inset)
This commit is contained in:
Nexoniarz 2026-08-30 15:48:06 +02:00
commit ea7fefbba8
7 changed files with 96 additions and 4 deletions

View file

@ -1100,6 +1100,16 @@ Rectangle {
}
var url = "https://danbooru.donmai.us/autocomplete.json?search%5Bquery%5D="
+ encodeURIComponent(partial) + "&search%5Btype%5D=tag_query&limit=8";
// If a previous keystroke's request is still in flight (curl
// hasn't returned yet), reassigning .command and setting
// .running = true on an already-running Process is a no-op
// — running:true -> true is not a change, so nothing
// restarts and the newer query silently never gets sent.
// That's the "sometimes works, sometimes not" flakiness:
// explicitly stop it first so the new request actually
// fires.
if (autocompleteProc.running) autocompleteProc.running = false;
autocompleteProc.requestedPartial = partial;
autocompleteProc.command = ["curl", "-s", "-A", booruCol.userAgent, url];
autocompleteProc.running = true;
}
@ -1211,8 +1221,15 @@ Rectangle {
}
Process {
id: autocompleteProc
property string requestedPartial: ""
stdout: StdioCollector {
onStreamFinished: {
// Drop stale responses: even with the running-guard
// above, a slow request can still resolve after a
// faster, newer one — without this check whichever
// curl happens to finish last wins, regardless of
// which one actually matches what's typed now.
if (autocompleteProc.requestedPartial !== booruCol.currentPartialTag()) return;
try {
var d = JSON.parse(text);
booruCol.suggestions = Array.isArray(d) ? d : [];
@ -1354,7 +1371,20 @@ Rectangle {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: { booruSettings.lewds = booruCol.lewds ? "0" : "1"; booruCol.doSearch(); }
onClicked: {
// save() here, not just the property assignment:
// this was the actual persistence bug — toggling
// Lewds never wrote to disk on its own, only the
// source-switcher below called save(). So Lewds
// only ever "stuck" as a side effect of also
// switching source afterward, and turning it
// back off never stuck at all — reopening Booru
// always reloaded whatever was last written that
// way instead of the toggle's actual state.
booruSettings.lewds = booruCol.lewds ? "0" : "1";
booruSettings.save();
booruCol.doSearch();
}
}
}
}
@ -1468,7 +1498,16 @@ Rectangle {
}
// -- Pagination --
Row {
// Flow, not Row: Row lays out children at their natural
// width regardless of the width assigned to the Row
// itself, so when Prev + "Page" + the page field + "of
// N" + Next added up to more than booruCol.width (which
// happened often enough — "of N" varies in width with
// page count, and it was already close to the edge) the
// Next button rendered past the flyout's right edge,
// off the visible screen. Flow wraps overflow onto a
// second line instead of spilling past the edge.
Flow {
visible: !booruCol.searching && (booruCol.page > 1 || booruCol.hasNextPage)
width: booruCol.width
spacing: 8

View file

@ -76,7 +76,19 @@ PanelWindow {
delegate: IconImage {
id: trayIcon
implicitSize: 18
source: Quickshell.iconPath(modelData.icon, "")
// Only resolve through the icon theme when this is
// actually a themed icon name (e.g. Steam's
// "steam_tray_mono"). Apps that only ever supply a raw
// IconPixmap over the SNI protocol and no IconName at
// all (confirmed via busctl: Vesktop's tray item has no
// IconName property) get an already-usable image:// URL
// back from modelData.icon instead — running that
// through iconPath(), a name/path resolver, mangled it
// into nothing, which is what rendered as a broken-
// texture checkerboard instead of the actual icon.
source: modelData.icon.includes("://")
? modelData.icon
: Quickshell.iconPath(modelData.icon, "")
MouseArea {
anchors.fill: parent
hoverEnabled: true

View file

@ -29,6 +29,10 @@ source = ~/.config/hypr/display.conf
# TTY) since only one agent can be registered at a time.
exec-once = mako
exec-once = hyprpaper
# Reapplies whatever wallpaper was last set — hyprpaper/mpvpaper start every
# session with nothing loaded, and wallpaper-set never persisted anything
# on its own (see modules/scripts/wallpaper-set.sh, wallpaper-restore.sh).
exec-once = wallpaper-restore
exec-once = quickshell
# GTK/Qt apps native-Wayland where possible, X11 fallback otherwise

View file

@ -11,7 +11,13 @@
services.greetd = {
enable = true;
settings.default_session = {
command = "${pkgs.tuigreet}/bin/tuigreet --time --remember --remember-session --cmd Hyprland";
# start-hyprland, not the raw Hyprland binary: it's Hyprland's own
# watchdog-wrapped launcher (ships alongside Hyprland in the same
# package) that also auto-restarts the compositor if it hangs.
# Launching the raw binary directly works but logs a startup warning
# ("Hyprland is being launched without start-hyprland. This is highly
# advised against.") and skips that recovery.
command = "${pkgs.tuigreet}/bin/tuigreet --time --remember --remember-session --cmd start-hyprland";
user = "greeter";
};
};

View file

@ -8,6 +8,7 @@ let
showDesktopToggle = pkgs.writeShellScriptBin "show-desktop-toggle" (builtins.readFile ./scripts/show-desktop-toggle.sh);
wallpaperSet = pkgs.writeShellScriptBin "wallpaper-set" (builtins.readFile ./scripts/wallpaper-set.sh);
wallpaperDelete = pkgs.writeShellScriptBin "wallpaper-delete" (builtins.readFile ./scripts/wallpaper-delete.sh);
wallpaperRestore = pkgs.writeShellScriptBin "wallpaper-restore" (builtins.readFile ./scripts/wallpaper-restore.sh);
displaySet = pkgs.writeShellScriptBin "display-set" (builtins.readFile ./scripts/display-set.sh);
hyprThemeSet = pkgs.writeShellScriptBin "hypr-theme-set" (builtins.readFile ./scripts/hypr-theme-set.sh);
gtkThemeSet = pkgs.writeShellScriptBin "gtk-theme-set" (builtins.readFile ./scripts/gtk-theme-set.sh);
@ -31,6 +32,7 @@ in
showDesktopToggle
wallpaperSet
wallpaperDelete
wallpaperRestore
displaySet
hyprThemeSet
gtkThemeSet

View file

@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Reapplies the last-set wallpaper on login/reboot. hyprpaper and mpvpaper
# both start every session with nothing loaded, and wallpaper-set only ever
# ran through live IPC with no persistence of its own — without this the
# desktop silently reverted to blank on every fresh login. Run via
# exec-once in hyprland.conf, after hyprpaper.
set -euo pipefail
state_file="$HOME/.config/hypr/wallpaper.conf"
[ -f "$state_file" ] || exit 0
# hyprpaper's IPC socket isn't guaranteed ready the instant exec-once fires
# it, since exec-once entries all start back-to-back with no ordering
# guarantee beyond process spawn order.
for _ in $(seq 1 50); do
hyprctl hyprpaper listactive > /dev/null 2>&1 && break
sleep 0.1
done
bash "$state_file"

View file

@ -58,3 +58,12 @@ case "${image,,}" in
hyprctl hyprpaper wallpaper ",$image,$fit_mode" > /dev/null
;;
esac
# Persist so the wallpaper survives a reboot/relogin. hyprpaper/mpvpaper
# both start every session with nothing loaded — this was previously only
# ever set at runtime via IPC, so the desktop silently reverted to blank
# every fresh login. wallpaper-restore (exec-once in hyprland.conf, after
# hyprpaper starts) re-runs the exact command saved here.
state_file="$HOME/.config/hypr/wallpaper.conf"
mkdir -p "$(dirname "$state_file")"
printf 'wallpaper-set %q %q %q %q %q\n' "$image" "$fit_mode" "$muted" "$speed" "$auto_pause" > "$state_file"