aboutsummaryrefslogtreecommitdiffstats
AgeCommit message (Collapse)AuthorFilesLines
33 hoursvm: show docker containers beside the VMsDanilo M.5-11/+379
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>
2 daysisland: hide the media pill while the island is retractedDanilo M.1-2/+6
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>
2 dayssbbuilder: list the default manifests, and keep them on the first editDanilo M.5-6/+37
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>
2 dayssettings: a keyword hit stays out when a row matches tooDanilo M.1-5/+6
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>
2 daysSwitch: never set checked itself, report the new state insteadDanilo M.13-42/+18
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>
2 daysdocs: the sbbuilder settings pagesDanilo M.2-0/+35
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 dayssbbuilder: fixes from reviewDanilo M.6-26/+66
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>
2 dayssbbuilder: Desktop and Build VM settings pagesDanilo M.3-0/+326
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>
2 dayssbbuilder: the two config draftsDanilo M.2-0/+229
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>
2 daysqtmaildir: Setting and BoolSetting take the draft as a storeDanilo M.2-9/+13
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>
2 dayssbbuilder: a line editor for sb-builder's TOML configsDanilo M.2-0/+247
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>
2 daysdocs: plan the sb-builder settings pagesDanilo M.1-0/+1157
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 daysdocs: spec the sb-builder settings pagesDanilo M.1-0/+92
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>
4 daysassistant: count seconds on the running tool chipDanilo M.1-4/+14
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.
4 daysassistant: grey out the mic when the model cannot hearDanilo M.6-15/+37
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>
4 daysassistant: a mic button asks by voiceDanilo M.5-7/+385
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>
4 daysdocs: spec the assistant canvas micDanilo M.1-0/+90
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>
4 daysnotifications: quiet mode silences notification sounds tooDanilo M.3-2/+18
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>
4 daysnotifications: write gsettings only when the theme changedDanilo M.2-8/+12
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>
4 daysdesktop: report save results in the SaveBar, not at the page's footDanilo M.16-58/+58
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>
4 daysdocs: notification soundsDanilo M.2-0/+16
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 daysdesktop: notification sounds on the settings pageDanilo M.2-7/+129
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>
4 daysnotifications: play a sound for a new balloonDanilo M.1-0/+34
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>
4 daysnotifications: resolve the sound keys and choose a balloon's soundDanilo M.2-2/+35
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 daysdocs: plan notification soundsDanilo M.1-0/+696
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 daysdocs: notification sound theme mirrors gsettingsDanilo M.1-3/+18
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>
4 daysdocs: spec notification soundsDanilo M.1-0/+123
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>
4 daysdesktop: tell muted apps from suggestions, explain the limitsDanilo M.1-4/+27
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>
4 daysdesktop: notification page follows the file, saves once, keeps nested keysDanilo M.1-8/+19
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>
4 daysdocs: a symlinked singleton needs a restart, not a forced reloadDanilo M.1-2/+7
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>
4 daysdesktop: Tab moves between the hand-rolled text fieldsDanilo M.15-0/+27
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>
4 daysdesktop: rebind the notification page's fields after an editDanilo M.1-2/+12
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>
4 daysdocs: the notification settings page and config.jsonDanilo M.3-4/+28
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 daysdesktop: notifications settings pageDanilo M.3-0/+251
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>
4 daysnotifications: place, mute and DND balloons from config.jsonDanilo M.2-9/+13
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>
4 daysnotifications: read notifyd's config.json in the Notify singletonDanilo M.1-0/+23
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4 daysnotifications: resolve notifyd's config.json in a shared libraryDanilo M.4-0/+116
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>
4 daysdocs: plan the notification settings pageDanilo M.1-0/+1153
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>
4 daysdocs: spec the notification settings pageDanilo M.1-0/+104
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>
5 dayswallpaper: row-per-notch wheel and a sticky folder headerDanilo M.1-30/+86
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>
5 dayssettings: titlebar row with maximize and closeDanilo M.2-1/+56
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>
5 daysisland: headphones panel for Sony noise controlDanilo M.6-8/+804
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>
5 dayswindow-switcher: animate, respace and label the expose, add arrow keysDanilo M.5-26/+126
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>
5 daysmail: open accounts and messages in qtmaildir from the drawer and notificationsDanilo M.7-55/+153
qtmaildir now takes --account, --thread and --message, and hands them to a running window over its socket, so the drawer and the notifications can open the exact thing they show instead of just launching the client. mail-open.sh is the one place that turns an account and a thread into selectors, shared by both callers. It looks up the thread's newest unread message at click time rather than when the row or notification was built: by then it may have been read, and an empty lookup falls back to the whole thread instead of a miss in qtmaildir's status bar. The notification never had a working click: dunstify is not installed, so mail-notify.sh always took its notify-send fallback, which carried no action. It now uses notify-send -A NAME=Text, one action per listed thread (keyed by thread id, which is hex and so never holds the "=" separator) plus the account as default. The round trip through notifyd was checked: notifyctl action makes notify-send print the picked name. On the page, the account header opens the account and a thread row its newest unread message. Rows use TapHandler and HoverHandler because a MouseArea cannot fill a Column. Launches are detached: a first launch is the client itself, and the old reused Process would have killed it on the next click. Opening anything now closes the drawer rather than going back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5 daysperf: open btop in kitty from the page headerDanilo M.1-0/+10
The page shows a summary; btop is the full picture when the summary is not enough. The button sits in the header's actions slot, so it stays put while the panel scrolls, and closes the drawer on launch since the terminal takes over from there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5 daysName the project Keystone, and move its paths and namespacesDanilo M.29-43/+44
The repository had no name of its own, only the framework's. It is now Keystone, in the README, AGENTS.md, the settings window, notes commit messages, calctl's PRODID and the user page's User-Agent. Its files move off quickshell's name too: ~/.config/quickshell/greetings -> ~/.config/keystone/ ~/.config/quickshell/obs-scene-icons.json -> ~/.config/keystone/ $XDG_STATE_HOME/quickshell/desktop.json -> $XDG_STATE_HOME/keystone/ ~/.config/quickshell is Quickshell's own default config directory (a bare qs loads its shell.qml), so sharing it was a collision rather than a home. There is no fallback to the old paths: the files are moved once. The layer namespaces become keystone-*, and the Hyprland blur rules in hypr-theme are renamed to match in the same change. A running shell keeps its old namespaces until restarted. Left alone: the retired standalone mail and vm shells' namespaces, which their READMEs keep as history; Quickshell's own app id, binary and process name; and the repository directory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5 dayssettings: system card on the landing pageDanilo M.2-36/+230
The landing page was a paragraph of About text. It now carries the project's name, Keystone, a short About, and a system card: the Slackware facts of conky-theme-udt's card (version, package count, kernel, install age, and the slackpkg ChangeLog age, green under a day, yellow under a week, red past it), the machine, and base library and development tool versions, in two columns. The page scrolls once it outgrows the window. Everything comes from one sh process per open of the window, so there is no cache file and no dependency on conky's sampler. Library and tool versions are read from the installed package names in /var/log/packages: the version must start with a digit, so gcc does not also match gcc-g++, and stops at an underscore, which drops qt6's snapshot suffix. A package that is not installed shows a dash. The shortcut walkthrough left the About text: the drawer, island and assistant are described in their own READMEs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 daysisland: open the window source's properties from the pillDanilo M.3-6/+30
A Wayland window capture is chosen in the portal picker, which only OBS's source dialog can open. In a scene whose only source is a PipeWire capture, the panel gets a Window button that opens that dialog. A window scene with more than one source needs a per-scene designation the pill does not have yet.
6 daysisland: let record events own the OBS stateDanilo M.1-4/+13
6 daysisland: keep a paused OBS take as recordingDanilo M.1-2/+6