| Age | Commit message (Collapse) | Author | Files | Lines |
|
Parsing userctl's output and locale -a, and the git name preview, kept
out of QML so node can test them.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
busctl and kwriteconfig6 parse a value starting with '-' as an option, so
a location like '-x' failed; '--' before the value ends option parsing
(checked against both binaries).
get ran without pipefail, so a failing busctl gave empty fields and exit
0, which the page would show as a blank form. It now reads all fields
first and, on failure, prints one error= line and exits 1.
keys listed revoked, expired or invalid primaries, keys that cannot
sign, and could label a key with a revoked uid. It now skips those.
Unsetting the signing key uses --unset-all, since git exits 5 on a
multi-valued key with --unset.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
One writer for the stores other programs read: AccountsService (and with
it the passwd GECOS LibreOffice prefills from), KDE's emaildefaults and
the global git config, where the surname goes in as an initial. busctl
is asked for JSON because its plain output escapes non-ASCII as octal.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
The island's performance icon and panel are gone: the drawer page covers
them. With only one host left, the panel drops its three-column grid for
one card per row at a larger size, since the page scrolls; temperatures
get a card of their own as tiles. The files move from island/perf to
modules/perf.
A cache card ports conky's: ~/.cache and its five largest children,
measured with du each time the page opens (28ms over 2 GiB here) rather
than read from conky's 15-minute sampler file. The largest is yellow,
red past half the total, as conky had it.
The warning dot now shows only on the drawer tile.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A drawer row greets the login user and opens a page that edits name,
email, avatar, language and location, written where other programs
already read them: AccountsService (and through it the passwd GECOS
LibreOffice reads), KDE's emaildefaults and the global git config, with
the surname shortened to an initial for git.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The island spec put per-app volume on the drawer page, and it was never
built. Each application playing audio now gets a row with its name, what it
is playing (two browser tabs are otherwise identical rows), a click-to-mute
level and its own slider.
Streams are matched on PwNodeType.AudioOutStream. Quickshell reports isSink
true for a playback stream, so the obvious isStream && !isSink filter found
nothing while Firefox played. Their properties are empty until bound, so the
page carries a PwObjectTracker for them, which stops with the drawer.
speech-dispatcher-dummy is always present and never audible, and is hidden.
The output slider runs to 150%, the ceiling the volume keybind already uses
(wpctl -l 1.5), with a tick at 100%. Input and apps stop at 100%: a boosted
microphone only clips. The meter became an inline component shared by both,
and wheel-up never lowers a level already past its maximum.
Now playing is dropped from the page: the OSD already carries it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The first cut was small type in one long list: 471 Slackware upgrades
pushed everything below them out of sight, and the repo tag, the
versions and the name ran together across the row. Type is 2px larger
and rows taller. A section over ten rows starts folded behind a
clickable heading with its count. The version being moved to is bright,
the one being left dim, both in Inconsolata so digits line up. Every
other row is shaded to carry the eye across, and the repo is a pill in
one colour per repo, never red, green, yellow or the accent, which mean
something elsewhere.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The tile counts what can be installed now (Slackware and the binary
repo) and what only needs a build, and goes accent when there is
something to install. It runs updatesctl while the drawer is open and
not otherwise; a result over 30 minutes old is followed by a refresh
that runs nvchecker, never after a refresh, so a failing nvchecker
cannot loop.
The page has one section per next action: Slackware upgrades, new and
removed first, then upgrade here, awaiting build, upstream newer, app
updates, the order the work is done in. Each row names the package, the
repo it comes from and the one version jump its action covers. A source
that could not be read says so under the section it empties instead of
reading as nothing pending.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A git hash starting with a letter (3.1-a59215d) ordered behind its base,
because any trailing alphabetic chunk counted as a pre-release; only
alpha, beta, rc, pre and dev do now. Trailing zeros (1.14 against
1.14.0) compare equal instead of reading as an upstream bump. A bad line
in the slackpkg blacklist is skipped rather than crashing the script and
leaving the drawer with nothing. An AppImage keeps its key over a
flatpak of the same name, as the spec orders them, and flatpak list has
a timeout. A Slackware upgrade compares version and build, so an arch
change alone is not a row. The spec's prose now matches the code: the
upstream comparison includes the installed version.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The awaiting-build branch also required the build to be older than
upstream, which cannot fail there: a build newer than installed was
already taken as an upgrade, so the build is at most the installed
version, which is behind upstream whenever a row is emitted. Removing
it left every test passing, which is how it was found.
The blacklisted testing/ entry sat after the main one in the fixture, so
setdefault kept the main one and the mirror-side blacklist was never
exercised; it now comes first, and dropping the check fails the test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A fifo with no writer blocks cat at open() the way a hard NFS mount
blocks during a NAS outage. The repo section reports unreachable within
the timeout and the mirror sections still fill.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
Compared mirror against installed, as upgrade-all does, not against
slackpkg's local ChangeLog: `slackpkg update` replaces that without
installing anything, so a diff against it reads clean after an update
that was never applied. New and removed come from the mirror's
ChangeLog, the newest Added or Removed line deciding, so a package
added and later upgraded still counts as new. Blacklisted build tags
and directories are skipped, as slackpkg skips them.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
Each nvchecker stanza lands in one section: upgrade here when the binary
repo's PACKAGES.TXT holds a newer build than installed, awaiting build
when the recipe (the user's or SBo's) has caught up but the build has
not, upstream newer when the recipe lags, app updates for AppImages and
flatpaks. Built comes from PACKAGES.TXT rather than the .info, which is
why gamescope and kitty no longer read as installable: the repo still
held the old build. The repo is on a hard NFS mount, so it is read by a
child the timeout can kill.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
nvtable compares versions for equality, which cannot tell an installed
2.1.278 from an SBo recipe of 2.1.267 that is older than it. vercmp
orders numeric chunks as numbers and treats a trailing alphabetic chunk
as a pre-release, so a kernel suffix or git hash orders as newer than
its base and never reads as behind.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
The tile grid sits in a Flickable capped at three rows, so the fourth row
the new modules bring scrolls inside that box instead of eating the
notification space; a thin accent bar in the margin shows only when a row
is hidden. Tiles are reordered with the everyday ones first.
Performance shows the island's PerfPanel in a Page, with a tile line of CPU
and RAM and the warning dot. Perf.detailed is now a count of live PerfPanel
instances instead of a binding in the status pill: the island and the drawer
both host the panel, and a bool with two writers would let one clear it
under the other.
Clipboard reads cliphist when its page opens: search, copy on click or
Enter, delete per row, clear all behind a 3 second confirm, image entries as
thumbnails decoded once onto tmpfs. Entries reach sh as $1, never inside a
command string. A copy closes the drawer through a new Page.dismiss signal
the drawer connects like back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A new registry mode, status.quiet, with a row on the status page. Both tools
will play their sounds through notify-sound, which asks statusctl and plays
nothing while quiet is on, so their popups stay and only the sound goes.
Presentation sets quiet and restores it afterwards, like dnd and nolock: a
sound during a call or a game is what quiet is for.
notify-sound ignores any argument it has no sound for, because the opencode
notifier runs its command on every event, session_started and user_message
included.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
AGENTS.md describes the hybrid model (desktop/ hosts drawer and island) and
gains the gotchas found while building the island: UseQApplication for tray
menus, WheelHandler vs MouseArea, accent colliding with green, xkb layout
switching, and the tray popup un-hovering the island. The Blur waybar
measurement becomes history. READMEs drop their present-tense waybar
references.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The island replaces both bars: clock and VM dots in the clock pill, volume,
tray, layout flag and privacy in the status pill and its sliver, mail count
in the drawer. The icon switch no longer regenerates or reloads waybar, and
the mail pill's watcher script goes with it. Workspaces and the taskbar have
no persistent view by decision: the flick and the expose cover them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Accounts counted only at shell start and when the mail page opened, so the
drawer tile drifted behind the inbox for the rest of the session: it read 14
while notmuch and the waybar pill both said 16. The waybar pill never drifted
because waybar-mail.sh watches the database; the drawer had no watcher.
Accounts now runs its own inotifywait -m on the xapian directory (the
directory, because Xapian replaces files on commit) and refreshes 300ms after
a commit burst settles. The watcher respawns after 5s if it exits, since a
dead one reproduces the drift with no visible error.
This is the prerequisite for removing waybar: the drawer becomes the only
place the unread count is shown.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Engines are read-only, since stopping llama-server or sd-server takes
its web UI down too. Apps start and stop through their own controls,
detached so a start outlives a quickshell reload. Fanfictioner has no
start (a run needs a story) and stops by pid, children first, so its
sd-cli runs go with it and nothing else does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
fanfictioner runs sd-cli one-shot, not through sd-server, so the sd row
read down while a CLI run held VRAM and generated. A running sd-cli now
reads up and busy, its model marked (sd-cli), and outranks the web UI.
The model is the first --diffusion-model/-m: sd-cli's prompt follows and
is free text that may contain "-m".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Six TSV lines per sweep: llama and sd engines, four apps. llama's slot
probe passes autoload=false, since a plain /slots?model= loads the model
into VRAM. sd-server has no progress endpoint, so busy is its log
having been written in the last 5s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Qt's atomic write builds the file as an unnamed O_TMPFILE. When the
target does not exist yet it links that straight into place, and the
only event naming the file is IN_CREATE: no close_write, no moved_to.
statusctl watch did not listen for create, so the first write of each
mode in a session went unseen. On a fresh session that is the write
that matters: a Teams call started an hour in left the waybar pill
reading off until some later rename happened to redraw it.
The test reproduces the event with a hard link, which is what the
linkat from Qt's temp file looks like to inotify.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
It was a hand-installed copy, which can drift from the tested source
silently. Replaced with a symlink after confirming the two were
identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The auto-detector now sets presentation while Teams holds a mic stream,
alongside the game checks. Teams runs for the whole session, so its
process alone is no signal and would have kept lock, DND and the
breaktimer off all day. A call is Teams holding a source-output, which
pactl lists tagged with application.process.binary = "teams-for-linux";
verified matching during a live call.
It reuses status.gaming rather than status.presentation, so a call
ending clears only the automatic half and never a manual toggle. The
statusctl tooltip for that state said "game running", which is now
wrong, so it reads "auto: game or call". The file and waybar class keep
the gaming name: renaming them touches four places for no behaviour
change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The crossfade only brought a loaded cover to the front when the image's
source compared equal to the wanted URL. It never did for a title with
spaces or brackets: the URL carries the title as a percent-encoded
fragment cache buster, and QUrl reads it back decoded, so "#Foo%20Bar"
came back as "#Foo Bar". The load finished, loaded() bailed, and the old
cover stayed. Confirmed with a standalone qml test.
The slot being loaded is now tracked by index instead. Stale status
changes cannot match: only the back image is ever reloaded, and a newer
want resets it before assigning.
Seen working on screen by the user.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The OSD background held one Image, so a track change swapped covers in a
single frame. Two images now take turns: the new cover loads into the
hidden one and is brought to the front only once it reports Ready, then
both fade over 300ms. Waiting for Ready is what keeps a slow or remote
art server from fading through black.
The blur, scrim, sheen and grain are grouped under one item whose
opacity follows artOn. It goes false when the player has no art or the
cover for the current track errors, so the panel fades back to its plain
translucent look instead of snapping, or keeping a stale cover that
reads as a fault. It goes true again only when a new cover has loaded,
so returning from plain never flashes the previous one.
The back image's source is cleared before being set, so a URL it
already holds still reloads and reports a status. onWantChanged misses
the value at load, hence the Component.onCompleted call.
Not yet seen on screen: the shell reloaded it cleanly but the fade was
not watched through a track change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The art background is opaque, so Hyprland's blur behind the OSD cannot
show through it; making the art translucent to let it through would
also thin the scrim that keeps light covers legible. Fake the frosted
glass instead: a faint top-down sheen in Theme.text and a fixed grain,
painted once by a Canvas and masked to the panel's rounded corners.
The grain is a Canvas rather than a ShaderEffect because a shader needs
a compiled .qsb beside it; the Canvas repaints only on resize.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
When the current track has art, the volume OSD now draws it across the
whole panel, blurred (MultiEffect blur 0.5, default blurMax) and masked
to the panel's rounded corners, since clip only cuts to the bounding box.
The thumbnail in the track row stays sharp on top.
A dark scrim (Theme.base at 0.82) sits over the art. The palette is dark
and the text light, so a light cover made the panel unreadable, and more
blur did not fix that; only darkening did. The scrim is a sibling of the
MultiEffect, not a child: nested inside it, it did not paint at all, which
is why raising its alpha first appeared to change nothing.
Without art the panel keeps its translucent, compositor-frosted look.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A Loader keeps the height of its last item after active goes false. When
a browser tab playing media was closed, its MPRIS player vanished,
Player.active dropped and the window shrank to 100px, but the Loader
still reserved 46px plus the Column spacing on top. The volume row was
pushed down and clipped at the bottom edge, which is the "stuck" OSD a
hot reload used to cure: a fresh Loader starts at zero height.
Hiding the Loader while inactive makes the Column skip it. Both the
retained height and the collapse were confirmed with headless qml6 runs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
libvirt reports "running" as soon as QEMU is up, long before the guest
has booted, so the panel had no way to say when a VM is actually usable.
After a successful start, a detached script polls the VM's sshd and
sends a notification once it accepts connections, or a warning after
five minutes.
Host and port come from `ssh -G <vm>`: a Host alias named after the VM
may put sshd on a non-default port (buildsystem listens on 2290, and a
probe of port 22 never succeeded). Without an alias `ssh -G` echoes the
name back, and the guest agent's IPv4 address on port 22 is used. The
probe is a plain `nc -z`, so no authentication or key is involved.
Detached rather than a Process so several VMs can wait at once and the
wait survives a shell reload. Only starts issued from the panel are
watched; a VM started from virt-manager or the CLI is not.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
status.gaming is written by the shell's game detector and presentation is
the OR of it and the manual file, but statusctl read only the manual half.
A game therefore asserted the presentation effects while the waybar pill
still read deactivated.
Report gaming as a third state, for presentation only. The watch follows
status.gaming as well as its own file, and emits only on a real change so
an unchanged rewrite draws nothing.
Toggle is refused while a game holds the mode: the detector reasserts the
file within its poll, so the write would not stick. set stays allowed, the
manual half is worth setting for when the game exits.
gaming stays out of MODES. The detector owns that file and nothing here
may write it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
PCSX2 joins gamescope, Steam and DuckStation in the game detector, so
presentation is set automatically while a PS2 game runs and the idle
lock, DND and breaktimer stay out of the way.
The binary is /usr/bin/pcsx2-qt, the same -qt shape as duckstation-qt,
so it takes the same pgrep -x exact name match and cannot self-match the
way the bracketed steam pattern has to guard against.
Verified against the real emulator rather than a stand-in: with
pcsx2-qt running, status.gaming reads 1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Game detection probed only gamescope and Steam, so a standalone
DuckStation session never turned presentation on. Add a third probe for
the duckstation-qt process.
pgrep -x rather than the bracketed pgrep -f the Steam path needs: this
is an exact match on the process name, so it cannot match the sh running
the check, and the binary name is under the 15 character comm limit so
it is not truncated.
|
|
A failed pairing showed the raw bluetoothctl output as a plain red line that
only cleared on the next attempt, and the desktop notification is suppressed
while the drawer is open, so the failure was easy to miss and impossible to
dismiss.
Show it in a red card with a close control that clears pairing.error. It has
no timeout, and clearing the source means the next identical failure shows
again rather than staying hidden.
|
|
BluetoothRow only read BluetoothDevice.battery, which is BlueZ's
org.bluez.Battery1. A gamepad never publishes that interface: a Sony pad
exposes its charge as a kernel power_supply instead, so the percentage never
appeared.
Read every power_supply whose name ends in a MAC on a 30s timer while
anything is connected, keyed by address. BlueZ still wins when it has a
value.
|
|
Presentation is worth having automatically while a game runs: it is a
fullscreen app that already holds the screen, and the idle lock, DND and
breaktimer are all unwanted during it.
Detection is a 5s poll for a gamescope process or a Steam game binary,
either one enough. The Steam pattern is bracketed so pgrep -f cannot match
the sh running the check and report a game forever.
The result goes to its own status.gaming file, and presentation becomes the
OR of it and status.presentation. That separation is the point: a game
ending clears only the gaming half, so a presentation set by hand survives
a game session instead of being clobbered on exit.
|
|
The drawer absorbed the indicator's device actions but not the settings
window, so there was no path to the full KDE Connect app. Add an Open app
button beside Refresh, launching kdeconnect-app through execDetached so the
window outlives a quickshell reload.
|
|
The hand-rolled buttons only reacted to hover, so a press gave no visual
confirmation until the action's outcome appeared. kdeconnect made this
obvious: its row buttons shell out to kdeconnect-cli, and nothing on
screen acknowledged the click.
Button.qml now fills solid accent while the mouse is held (red for
danger), with the label flipped to base for contrast. TransportButton
gets the same treatment, and the window-switcher close button lightens
on press. Toggles, tiles and tabs are left alone: their own state change
is already the feedback.
|
|
|
|
The one rule worth writing down is which side writes: the daemon owns its
files and the shell only calls verbs.
|
|
Below every mode in the priority chain, so an active mode still owns the line
and breaktimer replaces only the idle 'All clear'.
It does not count toward activeCount. A running breaktimer is not a mode the
user switched on, and accenting the tile for an ordinary working day would
read as a mode stuck on.
|
|
Into the status module rather than a module of its own: break state is one
more thing the desktop is doing, which is what that page already is, and
Status.qml was already where breaktimer.sh is called from.
The paused state shows no number. The countdown is frozen while paused and a
frozen number reads as a bug, so the row says what it is instead.
|
|
The OSD showed only "Output"/"Input", not which device was in use, so
switching between, say, the analogue output and a USB DAC gave no
visible confirmation beyond the volume itself.
The name falls back description -> nickname -> name, the same order
SoundPage.devLabel uses, and elides. The panel height grows to fit the
new line.
|
|
dnd and presentation were the only modes, and presentation was the sole
holder of the Wayland idle inhibitor. That inhibitor is the only thing
that stops hypridle's loginctl lock-session at the 600s timeout, so
screen locking is on by default and there was no way to turn it off on
its own.
nolock is stored as "auto-lock disabled" so the file keeps the registry's
"missing means off" rule, and the row inverts it so the switch reads as
the default: lock on. Presentation now drives nolock the same way it
drives dnd, holding the prior value in memory and restoring it on exit,
so it needs no second inhibitor and the inhibitor has a single holder.
The page sets the disable permanently or for a number of minutes. The
deadline and its timer live in the singleton rather than the row because
the drawer page is destroyed on close while the effect has to outlive
it. The timer is shell-lifetime, the same class of limit as
dndBeforePresentation: a restart during a timed disable leaves the lock
off until it is toggled, which is safe because the user opted into the
disable.
The mode governs idle-triggered locking only. The SUPER+l bind and the
lock on suspend are separate paths and stay live.
|
|
statusctl watch reported class "down" for a missing state file and waybar's presentation rule drew a warning for it. That fires on every fresh session, before anything has written a file, which is the normal off state the registry's own invariant describes. The state was meant to flag a dead registry, but a crashed shell leaves a stale present file, not a missing one, so it never caught that case. get already read a missing file as 0; watch now matches it and the down state is gone.
|
|
unified-desktop-theme moved its generated files out of ~/.cache/wal, which
pywal used to own and nothing writes any more. The palette udt-accent
generates is now at ~/.cache/udt/udt-palette.qml.
shared/Theme.qml is the only code change; every component reaches it through
a symlink. The module READMEs cite the path, so they move with it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
A switch and a free-text minutes field driving notify-snooze.sh. Snooze is a
file the balloon shell reads, so the row only writes it; the switch follows
the file, including a snooze that ends while the page is open.
|