* fix: keep tray order stable when tooltipTitle changes dynamically
Revert the earlier simplistic key approach: keep getTrayItemKey()
unchanged (id::tooltipTitle) so duplicate-id disambiguation (#1542)
is preserved and persisted id::tooltipTitle entries keep matching.
Instead make persisted-key lookups tolerant of dynamic tooltipTitle:
resolveOrderIndex() in sortByPreferredOrder() does exact match first,
falling back to prefix matching an id::* entry only when the id has a
single instance. findSavedTrayKey()/isTrayIdHidden() replace direct
SessionData.isHiddenTrayId() calls and showTrayId() now receives the
actual saved key.
Prefix fallback is gated by trayItemCountForId() to single-instance
ids only, so multiple items sharing an id always match exactly and
hiding/ordering one never affects the others.
* fix: bail on ambiguous saved tray keys in fallback
* tray: use occurrence index as stable key, add one-time session migration
* tray: pin stable key per item instance so it never flips when siblings come and go
* tray: assign unique per-id sequential keys to avoid collision after sibling exit
* tray: reuse free key slots so a restarted singleton keeps its plain id
* lock: keep the fingerprint reader usable across suspend
The reader stopped responding after the lid was closed and opened, after a long
idle period, and after suspend.
The shell held a PAM session for as long as the screen was locked, so a suspend
always landed on a running verify. fprintd then suspends the device under the
verify, pam_fprintd's ReleaseDevice fails with "device is still busy", and the
claim is never released, so every later Claim fails until fprintd restarts. The
reader is now released when the monitors blank and armed again when they come
back, so a suspend has nothing left to wedge.
The PAM profile drops to max-tries=1 and sets an explicit timeout. pam_fprintd
only counts a wrong finger, so one finished session is now one wrong scan and
maxFprintTries means the number of scans it names rather than five times that.
A timeout expiry arrives as PamResult.Error, exactly like a device fault, so a
session that ran at least as long as the configured timeout is treated as a
planned renewal instead of spending the error budget and painting an error.
A suspend that still lands while the screen is on wedges as before. Recovery now
waits out fprintd's idle timeout instead of climbing to it, which brings the
reader back about 37 seconds after resume instead of about 98.
SessionService gains lidOpened from UPower, because logind can ignore the lid on
AC and resume alone is not enough. The lock PAM state machine is fixed alongside:
PamResult.Failed was dropped, a pending retry outlived the contexts that owned
it, and an availability check reset the wrong-scan counter.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Q5LQHivuucTLXwLkvZMfcn
* lock: time the fingerprint session from the verify, not the claim
pam_fprintd's timeout runs from the verify, but the session age was measured
from start(), which folds the claim into it. A slow claim plus a verify that
failed before its own timeout could add up past the timeout and be taken for a
planned renewal, skipping the backoff. A session that never reached a verify had
the same problem.
The age now starts at the message pam_fprintd sends when the verify begins, so a
session that never verified can never qualify as an expiry, and a slow claim no
longer ages a fault. The message is translated and quickshell clears it before
completed, so only its timing is used, never its text.
That message arrives after pam_fprintd has already armed its deadline, because
VerifyStart is asynchronous, so the age read here runs short of the real timeout
and a slack makes it up. The bias is deliberate. A fault misread as a timeout
costs one extra retry, while a timeout misread as a fault parks the reader behind
a backoff that grows to a minute, which on an idle lock screen would leave it
unusable most of the time.
Also corrects the comment that claimed a timeout is reported without a message.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Q5LQHivuucTLXwLkvZMfcn
---------
Co-authored-by: Claude Opus 5 <[email protected]>
Autostart apps (e.g. KeePassXC) that call RegisterStatusNotifierItem
before the watcher owns its name fail silently and never re-register.
Until now the recovery scan only ran on suspend/resume or watcher
owner change, so first-boot orphans were never rescued.
Schedule a one-time delayed scan when the watcher first appears.
recoverTrayItems() already deduplicates via registeredConnIDs, so this
cannot double-register apps that registered normally.
Pressing Enter in the name field fires both the text field's accepted
signal and the dialog's own Return handler, so submitAndClose() runs
twice. The first call renames and hides the window, which clears the
field; the second call then renames to an empty string and unsets the
workspace name. The OK button only fires once, so it was unaffected.
Ignore submits once the window is hidden.
Claude-Session: https://claude.ai/code/session_01791wgvD8EqJaAeURYXYbT5
Signed-off-by: Thomas Kroll <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
* matugen: support multiple injected palettes with settings ui
Run user-configured external commands on each theme build and merge their
JSON output into matugen's import data under a namespace, so templates can
reference e.g. {{mypalette.color0}} alongside dank16. Multiple palettes are
supported, each under its own namespace. Command output may be nested JSON
(pywal/wallust shape). A settings page manages the list; a legacy
single-object config file is still read.
* settings: map PaletteInjectTab in search index
* matugen: bound palette-inject command with timeout and WaitDelay
* fix palette column spacing (review suggested
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
* remove wallpaper kind guard (review suggestion)
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
* fixup! remove wallpaper kind guard (review suggestion)
---------
Co-authored-by: bbedward <[email protected]>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
* feat(keybinds): redesign cheatsheet with category navigation, M3 styling, and click-to-execute
- Add category sidebar with Material Symbols icons and badge counts
- Move search field to top of sidebar with Ctrl+F shortcut
- Implement 2-column masonry layout for keybind items
- Deduplicate duplicate actions and stack multi-key combos vertically
- Collapse consecutive workspace keybindings (1-9) into range entries
- Add media & hardware icons for volume, mic, brightness, and PrtSc keycaps
- Preserve Niri window filter properties and distinguish same-app Alt-Tab binds
- Enable click-to-execute for keybind actions across supported compositors
* fix(keybinds): preserve niri action properties and disable execution on range rows
* fix(keybinds): convert niri action properties to CLI flags and disable IPC execution for window switcher binds
* fix(keybinds): pass skip-confirmation as boolean switch in niri executeAction
* fix(keybinds): handle niri spawn-sh actions in executeAction
- A new minimal and fun island instance (dot) that gets out of your way to be tossed anywhere on your screen
- Dankisland freedom is here too! It can now also be used in anywhere mode and used horizontally or vertically as you see fit!