Commit graph

183 commits

Author SHA1 Message Date
f436389ad7 quickshell: click a notification to jump to its app
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 20:29:19 +01:00
bcd685f3e3 quickshell: skip hidden dirs in file search, log launcher opens
fd drops --hidden, so ~/.cache, ~/.local/share/Steam, .git trees and
friends are out of the search entirely — most of the files under $HOME
and none of them worth matching by name. The .git/.cache/.steam excludes
go with it; node_modules is the one junk tree that isn't hidden.

Also log the three points that can swallow a Super+R: the IPC handler
when no bar is registered, the toggle itself, and the focus grab closing
a panel the user didn't close. journalctl --user -u quickshell.
2026-08-01 20:26:55 +01:00
a8eb76782c quickshell: stop launcher rows fading to random opacities
The results ListView had add/remove transitions animating opacity and
scale on the delegate root. The model is re-diffed on every keystroke, so
a row part-way through a fade frequently survived the next pass as a move
instead of a remove — and move animates x,y only, leaving the
half-finished opacity permanently unrestored. Rows accumulated stuck at
arbitrary opacities depending on typing speed, which read as the result
colours randomly fading.

Dropped both fades. x,y stay animated because the view re-asserts them on
every layout, so an interrupted slide self-corrects; opacity and scale
have no such backstop. Comment records that a future fade belongs on a
child item, not the root the view recycles.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 19:42:37 +01:00
4b4ae52c65 quickshell: fix pinned tiles never launching
launchPin passed a bare DesktopEntry to activate(), but activate()
started dispatching on a `kind` field when the results list became
heterogeneous (apps + files + web). A bare entry has no `kind`, so every
pin click fell through to the no-op branch: the tiles rendered, hovered
and dragged correctly and simply never launched. Regression from
"launcher searches files and the web".

Wrap via appItem() so the pin takes the same path as a search result, and
make activate()'s fallthrough console.warn instead of returning quietly —
the silence is the only reason this survived a release.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 19:32:31 +01:00
309c54d31b qs-ask: answer via Claude Code on the subscription, not an API key
No API key and no separate billing: qs-ask now shells out to `claude -p`,
which draws on the existing subscription already authenticated on this
host. The trade is latency — Claude Code boots a Node process per
question, ~6s measured against the raw API's ~1s — so the launcher now
races Wikipedia and Claude instead of chaining them. Wikipedia fills the
card in under a second and Claude supersedes it on arrival; chaining
would have left the card blank for six seconds on every question.

Isolation, verified: --safe-mode drops CLAUDE.md, skills, hooks, plugins
and MCP while leaving auth working, which matters for correctness and not
just speed — the global CLAUDE.md here asks for caveman-mode replies and
that would have leaked into answers. --no-session-persistence writes no
transcript, and a dedicated empty cwd keeps launcher questions out of any
project's history or auto-memory. Confirmed no project namespace, no
transcript and no memory write after a run.

Not --bare, which looks right and is a trap: its auth is strictly
ANTHROPIC_API_KEY, so it cannot use the subscription at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 17:34:50 +01:00
958dc9b333 quickshell: Claude-backed launcher answers via the mediaserver
Adds qs-ask on the mediaserver — the Anthropic key lives there and nowhere
else, so neither desktop holds a credential and there's one place to
rotate it. The launcher pipes the question over the same SSH path the
server monitor already uses; the query goes over stdin because
`ssh host cmd arg` would re-parse arg through the remote shell.

Claude answers first, Wikipedia is the fallback. The script is the
feature gate: with no key it exits silently, which is the same signal as
a failed call or an UNKNOWN reply, so the Wikipedia path stays the
default with zero configuration. The card names whichever answered.

Haiku 4.5, no thinking and no effort (effort errors on Haiku) — a
one-sentence fact needs no reasoning tokens. ~$0.0005 per query.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 17:14:04 +01:00
5c3d84d0c9 quickshell: answer "where"/"when"/"who" questions, pick the right article
"where is K2" had no scoring signal at all — "where" is a stopword and
"k2" fell under the 3-char term floor, so every sentence tied at zero and
the card fell back to the opening line. Fixes:

- answer-type hints: a "where" question scores sentences carrying the
  vocabulary of place, "when" of dates, "who" of attribution. Stems, not
  whole words — the Telephone intro says "granted a United States
  patent", never "invented". "is a"/"was a" deliberately excluded, since
  they match the definitional opening of every article.
- term floor down to 2 chars so "K2" and "UK" count.
- fetch 3 candidates and choose: an exact title match wins, and a
  disambiguated title loses unless the query asked for that sense.
  Search rank alone put "Chernobyl (miniseries)" above the city.

Verified 11/11 on live responses by running the generated QML functions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 16:26:10 +01:00
95637d91c8 quickshell: instant answers in the launcher
Wikipedia search+extract in one request gives the article for a
natural-language query, then the intro's sentences are ranked so the card
leads with the one that actually answers: +3 per query term, +4 for a
digit when a number was asked for, ties to the earliest sentence. Title
words are dropped from the terms unless they're the attribute asked
about, which is what makes "mount everest height" return 8,848.86 m
instead of a sentence restating the subject.

Fires on a 600ms debounce, only for query shapes that aren't app
searches, cached per query — Wikipedia throttles bursts, so restraint is
correctness here. Every triggered query goes to Wikipedia.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 16:06:00 +01:00
03fda2a6bb quickshell: launcher searches files and the web
Results become one flat list — apps, then files, then a web fallback —
so a single set of arrow keys walks all of it and activate() dispatches
on the item kind.

Files come from one fd run per settled keystroke, scoped to $HOME and
capped with --max-results so fd exits as soon as it has enough; no index
and no daemon, so nothing is ever stale. Spaces in the query become
gaps ("report 2024" finds report-2024.pdf) and regex metacharacters are
escaped so a query can't make fd bail.

Web row is last and always there, with bangs (!yt !gh !no !np !w) to
retarget it. Suppressed for arithmetic so Enter keeps copying the sum.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 15:45:43 +01:00
a15e4d5a32 quickshell: pins as a 3-up tile grid with drag reorder
Pinned apps move out of the results list into their own tile grid, three
across, wrapping to further rows. Drag a tile onto another slot to
reorder; the id list is rewritten on release rather than mid-drag, so
nothing shifts under the cursor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 15:14:19 +01:00
c212b06d15 quickshell: launcher calculator + app pinning, knobless monitor meters
Type arithmetic in the launcher ("3+3") and a result card appears above
the list; click or Enter copies it. Right-click any result to pin it to
the top — pins persist in ~/.local/state and outrank ties in search.

CPU/RAM rows in the server card use a new knobless MeterBar instead of
PillSlider, which read as draggable controls.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 14:32:07 +01:00
6f32d56c5b quickshell: blend status colours toward the theme accent
Fixed hues stayed legible but read as pasted on. Mix each 25% toward
base0D so they land in the wallpaper's family: err #e0525f -> #b86872,
ok #68c17c -> #5ebb88. statusHarmony is the one knob.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 13:42:59 +01:00
e2c413d997 quickshell: derive surfaces from base00, fix status colours
Stylix base01/base02 come off the wallpaper and land off-hue (purple card
over a slate bar). Tint base00 instead: #1c1f26 -> #2f3440 -> #434a5b.

base08-base0C collapse to the same cyan on monochrome wallpapers, so
warning and critical were indistinguishable. Replace with fixed err/warn/
ok/info/alt tokens; base0D stays the themed accent.

Also fixes Theme.base06, which was referenced but never declared.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 13:32:26 +01:00
dd5040ca79 cheatsheet: fix spotify-player search keys 2026-07-31 12:28:42 +01:00
4a38a2f0d9 quickshell: Super+C keybind cheat sheet; spotify-player visualizer
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 12:22:11 +01:00
a7a9ac6198 add spotify-player, drop need for Electron Spotify
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 09:05:52 +01:00
d8d8400519 quickshell: drop vinyl sheen streaks 2026-07-30 16:07:38 +01:00
41267747d0 quickshell: add sheen streaks so art-less discs read as spinning 2026-07-30 16:04:29 +01:00
2b73b1e470 quickshell: tone down vinyl fallback, gate ring on playback, widen stream match 2026-07-30 16:00:29 +01:00
88ab7944f4 quickshell: fall back to the page favicon when no artwork 2026-07-30 15:56:03 +01:00
02ebdc36ce quickshell: source badge from xesam:url, vinyl fallback label 2026-07-30 15:47:08 +01:00
8dbdb42841 quickshell: spinning vinyl media art with cava spectrum ring 2026-07-30 15:42:00 +01:00
7e8275a654 quickshell: fix media, power-profile and mute icon names 2026-07-30 15:18:55 +01:00
c7d518ab6a quickshell: swap icon font to Lucide 2026-07-30 09:29:26 +01:00
876dc3fc51 quickshell: match caelestia volume, battery and brightness glyphs 2026-07-29 22:48:31 +01:00
1b59058b91 ghostty: keep warm daemon alive with no windows
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 22:45:02 +01:00
734d18791c quickshell: match caelestia icon axes and wifi glyphs 2026-07-29 22:10:21 +01:00
4eeb496efe stylix: switch to caelestia fonts (Rubik + CaskaydiaCove NF) 2026-07-29 21:46:13 +01:00
ec7ae50cf0 quickshell: use Rubik for panel text 2026-07-29 17:45:08 +01:00
df2fe84c5d quickshell: drop bar dividers, space groups instead 2026-07-28 22:21:49 +01:00
7bfd1e25c3 quickshell: segment bar groups, pin server glance 2026-07-28 22:10:47 +01:00
4df4062806 quickshell: restyle volume/brightness OSD 2026-07-28 22:03:32 +01:00
608902915f quickshell: glance adds temp + wan up/down
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 20:37:05 +01:00
72864463d0 server monitor: qs-stats stream adds cpu temp + wan throughput
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 20:32:43 +01:00
87165b5a62 quickshell: glance captions under values
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 20:18:32 +01:00
bf1ed23d4f quickshell: glance gets cpu/ram captions, ms under ping
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 20:14:28 +01:00
ee86678cb5 gaming: drop arctis-sound-manager, ANC via direct HID script
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 19:35:25 +01:00
027858ff1e gaming: arctis-sound-manager + quickshell ANC switcher
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 19:21:37 +01:00
1df7ebb302 quickshell: escape always closes launcher
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 18:38:43 +01:00
dfe8c9ecdf quickshell: launcher v2 — categories, animated reshuffle, bigger rows
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 16:22:30 +01:00
ce14f695c6 quickshell: server glance under workspaces, sliders get knobs
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 16:08:45 +01:00
36e9b00782 quickshell: server monitor sizing, glance matches clock, bigger card type
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 15:58:06 +01:00
39b88591e8 quickshell: server glance bars -> numbers
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 15:54:24 +01:00
01b771b17d quickshell: fix ping path, no wrapper on NixOS
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 15:45:22 +01:00
d135331a30 quickshell: ssh server monitor widget + btop-style dropdown
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 15:40:28 +01:00
5c71cc3a1f quickshell: per-icon tray menu cache, kill the switch flash
One shared QsMenuOpener meant tray-to-tray switching reassigned the
handle and reloaded over DBus — the model went briefly empty, and no
sizing gate downstream could hide that. Each tray icon now owns its
opener, assigned when the icon appears, so every menu (Steam's slow
one included) is fetched once at startup and held. Switching swaps one
populated model for another in a single binding pass; contextMenu just
repoints activeOpener. Hover prefetch deleted — nothing left to
prefetch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 15:24:55 +01:00
d866cae4f7 quickshell: size the tray menu from the model, not the laid-out Card
Every previous attempt read the panel height off menuItems, which by
definition lags the data: the Repeater must build delegates and the
Column re-measure before the Card reports its real size. Reading it
during that gap is the flash, and no amount of gating on the model
closes it, because the gap is between the model and the layout.

fullHeight now sums the rows from menuOpener.children directly. That is
exact — cardSpacing is 0, so the sum plus 2*cardPad is precisely what
the Card lays out — and it lands in the same binding pass as the data,
so size and content never disagree.

Row metrics moved to menuRowHeight/menuSepHeight, shared by the sum and
the delegate so they cannot drift. The previous-height hold stays for
the DBus fetch itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 15:16:10 +01:00
31c584e7d1 quickshell: close the tray-to-tray morph gap
Gating the held height on the model alone was not enough. The model
populating and the layout catching up are different frames: the
Repeater still has to build delegates and the Column re-measure, so
menuLoaded went true while the Card was still empty-sized and the panel
collapsed for a frame before regrowing.

Only tray-to-tray showed it, because that is the one path that
reassigns the menu handle — coming from a non-tray widget leaves the
handle unchanged, so nothing reloads.

menuReady now also requires the height to have grown past an empty Card
(2*cardPad = 16; the smallest real row is 28, so it cannot misfire).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 15:10:55 +01:00
cca2d62763 quickshell: hold tray menu height so switching morphs instead of regrowing
Switching to a tray menu whose items were not currently loaded
collapsed fullHeight at the instant of the switch, so the chrome
morphed DOWN to a stub and replayed the vertical grow when the items
landed, rather than moving across from the previous panel.

An empty Card is not height 0 but 2*cardPad, so loaded-ness has to come
from the model: menuLoaded tracks menuOpener.children. While a newly
assigned menu is loading, fullHeight holds the last loaded height, so
the morph stays continuous and the correction on arrival is small —
nil when it is the same menu as last time.

ui.nix radius knob at 20.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 15:05:05 +01:00
196b71bf25 quickshell: stop discarding the tray menu on close
contextMenu nulled menuOpener.menu whenever it hid, throwing away the
loaded items. The only thing that refills them is the hover prefetch,
which fires on enter — and the cursor is already on the icon you just
clicked, so closing and reopening never re-triggered it. The reopen
re-fetched over DBus and the panel grew to its empty height, then
jumped when the items arrived. Moving to another widget and back
worked only because it produced a fresh hover-enter.

Keeping the handle assigned makes reopening the same item free, and
since assigning an unchanged QML property is a no-op it also covers
switching away and back. openFor() reassigns for a different item.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 14:58:54 +01:00