| Age | Commit message (Collapse) | Author | Files | Lines |
|
After a 33-minute suspend the desktop shell sat at ~45% of a core: its
main thread ran flat out for about a second every two seconds, in step
with the Perf 2s timer. Stack samples were almost all QV4 GC
(GCStateMachine::transition, MarkStack::drain) entered from allocData via
tryForceGCCompletion, under the regex loop in Perf.vram(), a 177-line
parse that should take microseconds. The main thread also posted ~1000
events a second to its own eventfd, against a few dozen in the other
shells. Reading: Qt 6.11's incremental collector, the default since 6.8,
never completed a cycle after resume, so every allocation forced a full
mark.
QV4_GC_TIMELIMIT=0 is Qt's documented switch back to non-incremental
collection. With it, after a 31-minute suspend the shell stayed at 0.3%
and ~37 wakeups a second. The live autostart.lua sets it too; keep the
two in step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A reply streams into the canvas state while the card is closed, but
nothing said when it was done, so working elsewhere meant checking back.
Now a finished or failed turn sends a notification whose Open action
brings the card back on that conversation. A stopped reply does not
notify: stopping it was the user's own act.
notify-send -A blocks until the notification is acted on or dismissed and
prints the picked key, so one Process carries it. A newer reply restarts
that Process and its stack tag replaces the balloon, which drops the
older wait: only the latest reply's Open works.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The name and size were one string, so sizes started wherever the name
ended and a column of them could not be scanned. The size is now its own
right-anchored Text in the main text colour, and a long name elides in the
middle instead of pushing it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Update runs `docker compose -p <project> -f <files> up -d --pull always`
with the files read from the com.docker.compose.project.config_files label,
so only stacks compose itself made get the button: a plain `docker run`
container has no recipe to recreate it from, and the supabase CLI labels
the project and nothing else. It has its own Process because a pull runs
for minutes and must not be clobbered by a start or stop meanwhile.
Published TCP ports become localhost:<port> buttons opened by the default
browser. Docker cannot say which port is a web UI, so every published one
gets a button and a port ending in 443 is guessed https.
Prune images runs `docker image prune -a -f` behind a confirm step, since a
re-pull can be gigabytes. The images it would delete are listed under the
header from `docker image ls`'s {{.Containers}} count, which docker 29
fills in; images of stopped containers are kept. That count reads 0 for
images only used by `--rm` containers, such as the test-build ones, so the
list is the warning.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Same class as matchAll and fromEntries: a TypeError that only reaches the
log. In the docker Update button it threw after the busy flag was set, so
the button read "Updating…" forever with no process behind it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The Theme page applied a scheme on click, so a stray click reinstalled the
whole theme, and it was the one settings page without the header's
commit point. Clicking now stages the scheme (outlined yellow, "selected"),
and Reset / Apply in the header drop or apply it, as the wallpaper page
does. The applied notice and any failure moved into the SaveBar, and the
sidebar marks the page dirty while a scheme is staged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Shows the live scheme, accent and both wallpapers, the scheme and
wallpapers linking to their own pages, then every udt consumer in a
software and a hardware list, each with a Re-apply button running
`install.sh <name>`, and Re-apply all in the header. The list comes from
`install.sh --list`, so a consumer added in unified-desktop-theme shows up
here with no edit. One run at a time, and none during a scheme switch.
QML's JS engine has no Object.fromEntries either: it threw a TypeError
that only reached the log and left the page without its list. Noted in
AGENTS.md beside String.matchAll.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Docker.qml mirrors Virsh: the docker CLI, an event stream that runs for the
whole session, and `docker stats --no-stream` on the 2s timer only while the
page is up. Containers are grouped by compose project, since a stack is a
dozen containers that start and stop together; a container outside any
project is a group of one. Verbs are start, stop, restart and resume, by
container id, so no compose file is needed and nothing removes a container.
The event stream is filtered to lifecycle events: health checks emit exec_*
events every few seconds per container, which would make the stream a poll.
It dies with the daemon, so it is retried every 10s.
An inactive Loader keeps its last height and a Column still reserves it, so
collapsing one card left an empty box where its detail had been. Both the
container and VM detail loaders now hide while inactive; the VM one had the
same bug, unseen with a single VM.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The pill faded to opacity 0 and slid off-screen with the rest of the
island, but its visible stayed true for as long as a player was playing
or paused. Its marquee animation is gated on pill.visible, so a title
too long to fit kept scrolling off-screen all session, and the island's
layer repainted every frame. Measured: the desktop shell sat at 42% of
a core while idle (253 CPU-minutes over 10 hours), and 0.3% after.
Opacity alone does not stop animations; visible does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The helper replaces its default [manifests] table with the file's, so
adding one manifest to a file without the table dropped neovim's, and
the page showed no rows for the default that was in effect. With no
table in the file the page now lists the helper's defaults, and the
first add, remove or URL edit writes them all before its own change.
Toml.js gains sections(), to tell a missing table from an empty one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Searching "zoom" listed Mail twice: once for the module keyword, which
opens the page at the top, and once for the Message zoom row, which
scrolls to it and flashes it. The keyword hit looked like a reveal that
failed. It now shows only when none of the page's rows match.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The shared Switch flipped its own checked on click, which replaced the
caller's binding with a constant. Every use binds checked, so after a
click the switch stopped following its source: Discard on a settings
draft reverted the value and left the switch showing the click. Four
status rows and the headphones panel each carried a resync workaround
for it.
toggled now carries the new state and the switch moves only when the
caller's binding does. Handlers that read checked as the new value take
the argument instead; the workarounds are gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Toml: a value followed by anything but a comment was parsed as if the
line ended there, so a multi-line string (which opens with an empty one)
or `x = "a" junk` read as valid and set() could write a duplicate or
mangle it. entries() now skips such lines and set() refuses them with the
existing "cannot edit" error. The ponytail comment names the ceilings.
Store: a failed put left Save enabled with a stale draft; it now marks the
key invalid. An empty manifest URL used to unset, which removed the row
being typed in; it now marks the row invalid instead (Setting clears the
mark just before set, so the one made inside set stays). Noted that list
entries with spaces cannot be entered here.
Sbb: refresh() ran an ssh fetch for the desktop page too and let two
fetches overlap, or one run during a save. It is split into reloadDesktop()
and fetch(false), and fetch() returns while busy. A successful config-get
clears a stale VM error, and a missing text reads as empty. The put now
writes and records the draft as sent, so typing during the save is not
marked saved.
SbbVmModule: fields are read-only while busy, a Reload button sits beside
the status line, and the manifest URL field says Remove deletes the row.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
Under SB Builder in the settings window. The desktop page edits
sb-builder's config.toml in place; VM control is a switch because an
emptied field means the default domain, not "". The VM page is
read-only until the helper answers config-get.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The desktop config is a watched local file; the VM one goes through the
helper's config-get and config-set over ssh, so the helper validates what
lands in root's file. Field text is typed after the key's default, the
same check sb-builder makes on load.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
The sb-builder pages need the same fields over another file. The store
defaults to Qtm, so the Mail pages are unchanged.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
Same shape as qtmaildir's Ini.js, so a file the user annotated keeps its
comments: unset comments a key out, set reactivates it. A value spanning
lines throws instead of gaining a duplicate key, which TOML rejects.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Two pages under SB Builder in the settings window: the GUI's config.toml
edited in place, and the helper's config on the VM through new
config-get/config-set commands, so the helper keeps validating what is
written to it. The sb-builder half is built in that repo.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A lit chip alone looks frozen on a clone that takes a minute; the running tool now shows the elapsed seconds and resets when it finishes.
|
|
AssistantModule keeps each router model's input_modalities and exposes
hears: the model loading, else the first not unloaded (the daemon's
rule), reports audio. Unknown inputs or a router that is down count as
yes; the daemon's error covers the rest. The model list is now read on
every canvas open, not only when a preferred model is set, so hears is
current when the card shows.
Checked: the rule against the router's live /models (Gemma resident:
on; Qwen resident or loading: off; router down: on). Not yet seen by
eye after a desktop shell reload.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Each recording is a pw-record Process of its own, made from a Component
like the Do row's runs, so a stopped recording's late exit cannot be
read as the next one's. Stopping sets running to false (SIGTERM), which
lets pw-record write the WAV header; the audio ask goes out from the
process's exit, not from the click.
Recording stops itself at 59 s: the daemon refuses over 60 s by file
size, and the stop lands a little after the tick. Escape cancels a
recording before it does anything else, and closing the card or losing
the daemon cancels it too. A pw-record that ends on its own says
"recording failed".
Checked: a scratch harness saw transcribing, thinking, idle for a 2 s
recording; by hand, every case in the spec passed with no pw-record
left behind, a desktop shell reload mid-recording included.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A mic button records with pw-record and sends the WAV's path as an
audio ask; the daemon transcribes it. pw-record writes a right header
when stopped by SIGTERM, but one stray kept recording in a trial, so
every path out stops it and the checks look for a stray each time.
Recording stops at 59 s: the daemon refuses over 60 s by file size.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Quiet muted only the Claude and opencode hooks, which play their own
sounds and check the flag themselves. With notification sounds it now
means no sound at all while the balloons still show, so the balloon
player reads status.quiet the way Notify reads status.dnd, without
instantiating Status. A per-app quiet list was left out: the mute list
already covers an app that should make no sound.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
From the final review. Every save ran gsettings set, so saving a timeout
on a first run, with no soundTheme in the file, silently reset a system
theme set elsewhere to freedesktop; the page now writes it only when the
draft's theme differs from the file's, and flags any other mismatch as
before. An exit code outside 0-3, a save killed mid-run, now reads as an
error rather than a partial success. The player's comment no longer
claims the balloon's expiry check, which it does not need.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A save's outcome sat at the bottom of the page, out of sight on a long
one, while "unsaved changes" was already in the header. SaveBar gains
notice, shown once the page is clean, and error, shown in red over the
unsaved mark, since a refused or failed write leaves the draft dirty.
Wired for notifications, HyprIdle, Sunset, Wallpaper and the four Mail
pages, with their longer messages shortened to fit the header. Messages
that are not save results stay in their pages: the icon theme's
immediate apply, Sunset's location detection, the assistant restart.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A sound per urgency chosen with the widget file dialog, previewed with
pw-play, and the sound theme for apps' sound-name hints. Save writes the
theme to gsettings as well, and the page reads gsettings back on every
open so a change made elsewhere is shown, with a button to adopt it,
rather than overwritten.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
pw-play, once per new or replaced notification, only when the balloon
would show: the drawer, snooze, mute and DND that withhold the balloon
silence it too. A restart records the queue without playing it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Save sets the system sound theme too, and the page reads it back on every
open so a change made elsewhere shows instead of being overwritten.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A sound per urgency in config.json, played by the balloon shell under the
same checks that decide the balloon. notifyd resolves the apps' sound-file,
sound-name and suppress-sound hints into two published fields, looking
names up in a configurable sound theme.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Muted and suggested apps were the same chip in two unlabelled rows. Each
row now has a caption; muted chips are red, suggestions dimmed and cut to
the eight most recent, newest first. The suggestions were already bounded
by the live and history caps. Limits gains a line saying what the two
numbers do, like the one under Timeouts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
From the final review. An edit broke the draft's binding to Notify.config
and Discard assigned a plain value, so a later change to the file read as
unsaved edits; discard and refresh now restore the binding. Save stays
disabled while the write runs, since a second click killed the first
mid-write. Unknown keys inside timeouts and balloons are merged rather
than replaced, as the spec asks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Measured: after shared/Notify.qml changed, a reload triggered by editing
Balloons.qml left the notifications shell on the old singleton, while a
fresh instance read the new config correctly.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A bare TextInput defaults activeFocusOnTab to false, unlike the Controls
TextField some pages use, so Tab skipped every field built from one: the
settings search, Field, the qtmaildir, git, autostart, assistant, OBS,
user, VM and network rows, and the calendar editor. Multi-line TextEdits
stay out of the chain.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Typing replaces a Field's value binding, so Discard left the typed text
showing over the reverted draft, and a rejected timeout kept displaying.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Edits notifyd's config.json as a draft behind the SaveBar and runs
notifyctl reload after the write; a stopped daemon is reported as saved
but not applied, since it reads the file at its next start. Muting offers
the apps already seen in the queue and history.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Screen and corner come from the shared config. Mute and the DND rule go
through NotifyConfig.withheld, so the settings page's node check covers the
same decision the balloon makes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
One resolver for both shells and the settings page, with the defaults that
reproduce today's behaviour, and the mute/DND decision beside it so the
node check covers it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Ten tasks across notifyd (config loader, timeouts, caps, reload) and this
repo (shared resolver with a node check, balloon placement and mute, the
settings module).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
One config file in ~/.config/notifyd read by the daemon (timeouts, caps,
picked up by notifyctl reload) and by both shells (placement, mute, DND).
Snooze stays out: the status tile already covers it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The thumbnail list scrolled a fraction of a thumbnail per wheel notch,
which is ListView's default step and slow with rows this tall. A
MouseArea over the list (acceptedButtons: Qt.NoButton, so clicks and
hovers still reach the thumbnails) now moves one row per notch, and
keeps a touchpad's pixel delta as it is.
An open folder with many images scrolled its header out of view, so
folding it meant scrolling back to the top. A copy of the header is now
pinned over the list while the topmost row belongs to a folder whose own
header has scrolled off; clicking it folds that folder and positions the
view at its header. The header row became an inline component so the
list and the pinned copy draw the same thing.
Loads clean; not yet confirmed by eye.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Hyprland draws no decorations, so the settings window had no way to fill
the monitor and no visible close (only Ctrl+Q and Escape). It now carries
a titlebar row of its own: "Keystone Settings" centred, and maximize and
close at the right. Maximize toggles FloatingWindow.maximized, which
Hyprland honours and undoes.
A corner button floating over the page was tried first and covered the
pages' Save buttons; a row of its own keeps clear of every page header
without each page knowing about it.
The buttons are Rectangles rather than the directory's Button.qml,
because this file imports QtQuick.Controls and that import shadows it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A headphones icon in the status pill, shown while Sony headphones are
connected, opens a panel in the capsule: battery, noise control tiles
(ANC, ambient, off), ambient level, focus on voice, the headphones' own
sink volume, power off and refresh.
Battery and volume need nothing new: BlueZ's Battery1 already reports the
charge, and volume is AVRCP through PipeWire, so the slider drives the
headphones' bluez_output node rather than the default sink. Only the mode
needs Sony's own protocol, which sonyctl speaks over RFCOMM with the
Python standard library. The headphones advertise the v2 MDR service on
channel 18 and answer an 8-byte init reply, which is the v2 shape.
What was measured on a WH-CH720N, and differs from what reading
Gadgetbridge alone suggested: the ambient subtype is 17, while 15 (what
Gadgetbridge sends to models without wind-noise reduction) is acked and
never answered; the set payload is 7 bytes, with no wind byte. Noise
cancelling has no strength, only on or off; a level sent with it is
ignored and the previous ambient level kept. Focus on voice reads back
as set. A connect right after the previous run closed fails with EBUSY,
so sonyctl retries it briefly.
Each call is about 2s, so the panel runs one at a time and sends the
ambient level only once the slider or wheel settles. The mode is re-read
on every open because the headphones' own button changes it too. Power
off was not exercised, to keep the headphones on during development.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Closing a window from the expose now glides the rest into their new
rects instead of jumping. The Repeater's plain array model rebuilt every
card on any change, leaving nothing to animate; ScriptModel diffs by
identity, so only the closed card goes. The Behaviors stay off until the
fade-in settles, or every card would fly in from the corner on open.
The scatter keeps its centre weighting but spaces windows further apart
(GAP 60 -> 200 real px) and may fill 95% of the screen rather than 92%.
Measured with node at 7 windows: about 45px on screen between cards.
The bottom 28px are reserved so the lowest card's name stays on screen.
Each card shows the app's desktop-entry name below its preview, falling
back to the app id, and the app icon moved to a larger overlay in the
preview's bottom-left corner; the hover title starts right of it.
Arrow keys select a card and Enter focuses it. The scatter has no grid,
so Expose.step picks the nearest centre on the arrow's side with
sideways drift counted double, and the first press picks the top-left
card; test_expose.js covers it. The selection is held as the window,
not an index, so a close that shifts the list does not move it, and
hovering selects too, so pointer and keys never show two highlights.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|