aboutsummaryrefslogtreecommitdiffstats
path: root/docs
AgeCommit message (Collapse)AuthorFilesLines
10 daysdocs(plan): calendar panel implementation planDanilo M.1-0/+2202
Ten tasks: protocol corrections to the spec, calctl in two halves with their CLI tests, the pure Cal.js with its node test, the service, the editor, the panel, the island wiring, a pass with the user and docs. Both test suites were run against the plan's own code before committing, and a read-only list against the live calendars measured 0.11s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
10 daysdocs(spec): calendar panel with event editingDanilo M.1-0/+153
Sub-project 3. The clock pill opens a month grid beside the selected day's agenda, and an editor that writes the vdirsyncer calendars through a small Python tool, calctl, rather than khal: khal 0.11 has no JSON output and keeps a cache that is a second view of the same files. Recurrence stops at a plain FREQ and UNTIL, and the form owns one alarm; anything richer in an existing event reads as custom and survives an edit untouched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
10 daysfix(island): review findings in the controls panelDanilo M.1-2/+3
Edit mode let a right-click and the wheel through to the tile, so a right-click opened the drawer and scroll changed volume, against the spec. The drag area now takes both buttons and the wheel. A scroll up on a level already above the cap, such as a mic set to 120% elsewhere, snapped it down to the cap. The cap now only stops a step up. Prefs logs a failed write, which printErrors off had silenced: the new order would otherwise vanish at the next restart with no trace. Its parsed object is renamed from data to doc, so it cannot shadow a base property of that name. The spec now says what was built: JSON.parse rather than a JsonAdapter, and the helper list as it is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
10 daysdocs(spec): controls panel uses named icons and a plain dragDanilo M.1-4/+15
Planning found two places where the spec diverged from the island it extends. Icons go through the icon theme like every other island icon, and drag is one MouseArea per tile with a pure drop-slot helper, which is less code than DragHandler and DropArea and can be tested under node. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
10 daysdocs(plan): controls panel implementation planDanilo M.1-0/+1063
Nine tasks: two spec corrections found while planning, the pure helpers with their node test, the preferences singleton, the tile, the panel, the island and IPC wiring, the SUPER release bind with its fallback, a visual pass with the user, and docs. The plan's test was run against the plan's Controls.js before committing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
10 daysdocs(spec): controls panel and preferences storeDanilo M.1-0/+147
Sub-project 2. A grid of seven toggles opened from the status pill and by a bare SUPER release, with an edit mode to reorder, hide and show tiles. The visible list lives in a JSON file under the state home, so a tile added to the code later starts hidden rather than appearing unasked. The drawer keeps the detail: right-click on a tile opens its page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07fix(island): neutral idle sliver, flag scroll via MouseAreaDanilo M.1-1/+2
The sliver was the accent at rest, and udt had snapped the accent to the same colour as the mic indicator (#36d190), so an idle sliver read as a live microphone. It is Theme.overlay at rest now; captures keep their colours whatever the wallpaper does. The flag's WheelHandler never fired. MouseArea.onWheel, which SoundPage already uses, does; scroll now switches layout, one step per notch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07docs(plan): island core implementation planDanilo M.2-6/+1471
Twelve tasks from pure logic to waybar removal. The spec is corrected on two points found while reading the code: Quickshell's PwNode has no running state, so mic and screen detection poll pw-dump like the waybar script did, and the VM dot rule now agrees with its own table on saved VMs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07docs(spec): island flag follows whichever keyboard is mainDanilo M.1-1/+4
The same keyboard appears as a different device over USB and over its dongle, so the main device is resolved per event, never cached. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07docs(spec): island core designDanilo M.1-0/+213
Sub-project 1 of the move toward a Modesty-style integrated desktop: a top-centre island on DP-1 hidden behind a sliver, a clock pill and a status pill that slide out of it, a sound panel, and the removal of waybar. Records the decisions taken while brainstorming: the island floats over windows with no exclusive zone, privacy capture recolours the sliver rather than revealing the pills, the clock pill flicks the desktop icon, app icon and app name on workspace change and app open but not on focus change, the volume icon has no gestures beyond opening its panel, and the flag images move into this repo. Modesty is GPLv3, so only its ideas are used. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05fix(ai): count a running sd-cli as the sd engineDanilo M.1-1/+5
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>
2026-10-05feat(ai): state sweep for the AI stackDanilo M.1-3/+3
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>
2026-10-05docs(ai): implementation plan for the AI moduleDanilo M.1-0/+836
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05docs(ai): design for the AI stack drawer moduleDanilo M.1-0/+94
Engines (llama.cpp, sd.cpp) read-only, apps get start/stop through their existing controls. Records that /slots?model= autoloads an unloaded model unless autoload=false is passed, found when a design probe reloaded one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25docs(keybinds): implementation planDanilo M.1-0/+587
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25docs(keybinds): design for a live keybind reminderDanilo M.1-0/+64
The list comes from hyprctl binds -j on every opening, so it is always what Hyprland has loaded, loop-generated binds included. Labels come from each bind's description option, since hyprctl reports Lua actions as an opaque __lua handle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-18docs(status): implementation plan for breaktimer controlDanilo M.1-0/+1259
Nine tasks over two repos. The daemon half gains a sourced config file and a config verb that prints the effective settings, which is what makes the config testable without running a thirty minute work block. The shell half reads the daemon's three runtime files, adds a row and a tile line. Three assumptions were probed rather than trusted while writing it, each with a precedent for biting here: padStart exists in this QML engine (matchAll does not), a Connections resync fires correctly on a singleton property, and the config source line leaves unnamed variables alone. The qs verification steps redirect rather than pipe. Piped output is lost when timeout kills the process, which reads as a silent success, and that failure happened twice while probing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18docs(status): spec breaktimer control and a config fileDanilo M.1-0/+221
The daemon already publishes its phase, state and countdown as files under XDG_RUNTIME_DIR, and the status module already shells out to it for presentation mode. Reading those files makes the traffic two-way for the cost of three FileViews, with no new interface to invent. Into status/ rather than a module of its own: breaktimer state is one more thing the desktop is doing, which is what that page already is, and Status.qml is already where breaktimer.sh is called from. The config file is the other half, and stands alone. Every tunable is a shell variable in a tracked script, so the installed copy and the committed one have already drifted over three sound paths pointing into a home directory. One sourced file fixes that with no parser and no format. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15docs: add the appearance sunset/idle/icons implementation planDanilo M.1-0/+1750
Seven tasks: the hyprsunset model, the hypridle model, the icon/cursor model, then the tab bar and one task per tab, finishing with docs and the cross-repo unhardcode TODOs. Models are built and self-tested first so every commit loads. Records the brace-in-command parsing trap and the sunset-qt byte-for-byte serializer shape.
2026-09-15docs: add the appearance sunset/idle/icons designDanilo M.1-0/+214
Design for three new appearance tabs: a hyprsunset-qt-equivalent profile editor, a hypridle timeout/enable editor that preserves the file's comments, and an icon/cursor selector with live previews. Records the preview mechanisms probed on this box (GTK icon lookup, hyprcursor .hlc unzip, xcur2png) and scopes the hardcoded-theme propagation out as follow-ups.
2026-09-15docs: add the notification images implementation plansDanilo M.2-0/+1218
Two plans, one per subsystem: the notifyd daemon (hint parsing, PNG encode, theme-name resolution, the image field and its cleanup) and the quickshell renderers (the balloon preview and remote inline source stripping). Either can ship first; the renderer tolerates a daemon without the field.
2026-09-15docs: add the notification images designDanilo M.1-0/+201
The daemon drops every image hint today, so a screenshot and an application image never arrive. libnotify 0.8.8 sends the content image via -i/--icon (the image-path hint) while -n/--app-icon is app_icon. The spec adds the image contract field, daemon hint handling and theme-name resolution, the balloon preview, and a local-only inline image policy.
2026-09-15docs: add the notification renderers implementation planDanilo M.1-0/+1380
Plan two of two: the quickshell side of the notification daemon. The Notify singleton reads the daemon's published files, a new notifications/ component draws balloons and owns suppression, the drawer's reserved Item becomes the live queue with a history page, and the Status page gains a snooze row. Suppression lives in the balloon shell so the drawer can list what DND held back, and the drawer flag is the single no-double-show mechanism. Per-id history removal is a tracked TODO in the notifyd repo rather than an untracked spec gap.
2026-09-15docs: fix the expire_timeout direction in the spec and planDanilo M.2-20/+27
The freedesktop spec says -1 means the server decides and 0 means never; the spec and plan had them reversed, which the shipped code inherited and the live handover test caught. Corrected to match the spec, libnotify's -1 default and dunst.
2026-09-15docs: correct the snooze check count to fiveDanilo M.1-1/+1
The check has five assertions, not four.
2026-09-15docs: fix the runtime dir collision in the check scriptDanilo M.1-1/+2
The daemon binary was built at $tmp/notifyd, the same name RuntimeDir() gives under XDG_RUNTIME_DIR, so the two collided. The runtime dir moves to $tmp/run, which is what the shipped script does.
2026-09-15docs: give the plan's test files the GPLv2 headerDanilo M.1-0/+44
The test snippets omitted the header the global constraints require on every .go file, which a task review flagged against the first one shipped. The snippets now carry it, so later tasks do not repeat the gap.
2026-09-15docs: record the notifyd repo as createdDanilo M.1-4/+4
Public under the Linux cgit section, cloned to ~/Programming/GIT/notifyd, so Task 1 Step 1 is done and the executor starts at Step 2.
2026-09-15docs: add the notifyd implementation planDanilo M.1-0/+1780
Plan one of two for the notification daemon: the Go daemon and notifyctl in their own repo. The renderer plan is separate because the daemon is working, testable software on its own, exercised through notifyctl. The policy is pure and tested without a bus; the store's expiry, dismissal and eviction are separate lifetimes, which the store tests pin; and test-notifyctl.sh runs the daemon and CLI on a private session bus against a temporary runtime directory.
2026-09-15docs: separate a notification's balloon and drawer lifetimesDanilo M.1-24/+57
The freedesktop expire_timeout is the notification's lifetime, and --wait and -b clients block until the daemon closes it. So the daemon emits NotificationClosed at expiry and frees the client, but keeps the entry in the live queue: the drawer lists it until it is dismissed or evicted, inert because its client is gone. History holds only what was dismissed or evicted. The reserved space is therefore scrollable, since the live queue can hold 20 at once, and a row past its expiry is read-only.
2026-09-15docs: add the notification daemon designDanilo M.1-0/+276
The second of the two specs the status registry named. A Go daemon owns org.freedesktop.Notifications and holds the state; quickshell renders it. The daemon and notifyctl live in a separate repo, the balloon shell and the drawer's notification centre live here. Rendering is split because quickshell has no generic D-Bus module, so something outside QML has to own the bus name. Files are the interface in the direction the renderers read, the same convention the registry set, and notifyctl is the one surface back. DND suppression lives in the balloon shell, not the daemon: the drawer's reserved space lists every live notification including the suppressed ones, because a list the user opened is not an interruption. This supersedes the registry spec's assumption that the daemon would read dnd.
2026-09-15fix(status): address final review findingsDanilo M.1-3/+5
F1: after a click the shared Switch writes checked directly, dropping StatusRow's declarative binding, so an external mode change from waybar or statusctl no longer moved the switch. Resync via onValueChanged. F2: the spec claimed hyprctl clients counts idle inhibitors, which is false for a layer-surface inhibitor; replace with the behavioral hypridle marker check. F3: README said the check covers both watch states; it exercises the activated report and the absent-file down report. F4: give breakProc an onExited check so a failed breaktimer.sh verb is visible instead of silent. F5: drop StatusTile's unused required st property and its injection. F6: comment the startup-order limit on dndBeforePresentation.
2026-09-15docs(status): document the module and record its trapsDanilo M.1-5/+35
A FileView fires its own fileChanged on setText, so a handler that writes in response to a change loops unless it compares first. IdleInhibitor has no window of its own and does nothing without one, so the singleton is handed the keepalive window by shell.qml. Both were read from the documentation while designing and confirmed while implementing.
2026-09-15fix(desktop): keep statusctl quiet when the mode file is absentDanilo M.1-6/+6
read_mode() failed on a missing mode file with a shell redirection error, 'statusctl: line 56: .../status.dnd: No such file or directory', printed to stderr on every get/toggle before the file existed and polluting waybar's exec stderr. The '2>/dev/null' sat on the 'tr' command, but the error is the parent shell's input redirection failure, which that redirect cannot suppress. Guard on '[[ -e "$file" ]]' before reading; a missing file still reads as 0, which was always the contract. Mirror the fix into the plan so plan and script agree.
2026-09-15docs: correct the check count and mirror the statusctl hardeningDanilo M.1-4/+13
Two corrections to the plan, both found by running it rather than reading it. The test script the plan specifies makes nine assertions; the prose around it claimed eight, in both the task step and the final check. An implementer running it would see a passing suite that disagrees with its own expected output, which reads as a failure of either the test or the plan. My arithmetic. The plan's statusctl also swallowed a failed rename and read the mode file through cat piped into tr, so a write that did not land still reported success and an unreadable file fell back to off by accident of pipeline semantics rather than by intent. Review of the implemented script caught both. The plan now carries the same fixes, so a future run of it does not reintroduce them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A7ThHHh5iTYbVfp3rNAkw2
2026-09-15docs: set atomicWrites explicitly in the plan's ModeFileDanilo M.1-1/+11
Review of the first task caught that the commit message credited atomicWrites while the code never set it. The property is already the documented default in 0.3.1, so the behaviour was right, but statusctl watches close_write,moved_to specifically because an atomic write arrives as a rename, and leaning on a default for something the CLI depends on is weaker than declaring it. Both ModeFile blocks in the plan now set it, so task four does not reintroduce the gap when it adds the second mode. Worth recording: the review asserted atomicWrites defaults to false in 0.3.1 and concluded the shipped writes were not atomic. The v0.3.1 and v0.3.0 documentation both read "If true (default)", so that conclusion was wrong, though the underlying observation about the unset property was right. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A7ThHHh5iTYbVfp3rNAkw2
2026-09-15docs: add the status registry implementation planDanilo M.1-0/+1216
Eight tasks against the registry spec: the singleton with the dnd mode, the statusctl CLI and its check, naming the keepalive window, presentation mode and its three effects, the module and tile, the page and registration, installing the CLI and swapping the waybar module, then the README and traps. The plan carries the facts probed while designing so the implementer does not re-probe them: the runtime directory is tmpfs, elogind runs here with the pam caveat that stops it guaranteeing logout cleanup, the compositor advertises zwp_idle_inhibit_manager_v1, inotifywait is present, and Inconsolata Nerd Font lives under ~/.fonts rather than either share directory. That last one cost a failed cmap search that read as a missing glyph when it was a bad search path. Two claims stay marked unverified because they come from documentation and have not been observed running: that a FileView fires its own fileChanged on setText, and that IdleInhibitor does nothing without a non-null window. The plan handles both and tells the implementer to confirm them and record what actually happens. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A7ThHHh5iTYbVfp3rNAkw2
2026-09-15docs: add the status registry designDanilo M.1-0/+229
Desktop modes as state: do not disturb and presentation mode, with one place that owns them. First of two specs; the notification daemon that replaces dunst is the second and consumes the registry's dnd mode rather than owning it. The registry comes first because DND belongs to the desktop rather than to the notification daemon, and because the daemon is the larger and riskier component. The design that needed deciding was not the toggle but the coupling. Presentation mode sets DND, so DND has two writers, and turning presentation mode off has to restore what the user had rather than clearing it. That rule is why this is a registry and not two booleans. State is one file per mode in XDG_RUNTIME_DIR, matching the convention breaktimer already writes on this machine. That directory is /run/user/1000, a tmpfs, so a reboot clears every mode with no cleanup code. Logout without reboot is less certain and the design does not lean on it: elogind runs here and its pam module documents removing the runtime directory at last logout, but the same manual says the module does nothing when the system was not booted with elogind as init, which on Slackware it is not. If removal does not happen, a mode survives a logout, which is the same behaviour as surviving a shell restart. FileView covers both directions: atomicWrites for the write, watchChanges for the watch, no shell-out. External watchers need close_write,moved_to because an atomic write arrives as a rename, the same trap the mail watcher hit with Xapian. Confirmed while designing: the compositor advertises zwp_idle_inhibit_manager_v1 and waybar's built-in idle_inhibitor already drives it here, so the inhibitor is asserted over Wayland rather than D-Bus. That module is replaced rather than demoted, because it owns its inhibitor object and cannot display state owned by anything else; left alongside the registry it would assert a second inhibitor and idle would resume only when both released. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A7ThHHh5iTYbVfp3rNAkw2
2026-09-14docs: KDE Connect module design and planDanilo M.2-2/+1226
Absorbs the kdeconnect-indicator into the drawer: status, battery, pairing, ping, clipboard send, file share, refresh and mount. State is polled from a qdbus6 helper script while the drawer is open, since quickshell 0.3.1 has no generic D-Bus module. The spec notes the phone-to-PC clipboard is the daemon's plugin, not the indicator's, so removing the indicator does not touch it.
2026-09-14docs: KDE Connect state protocol carries the pair flagsDanilo M.1-8/+14
The pairing flow needs isPairRequested, isPairRequestedByPeer and the verification key, so the polled line carries them; the key is only queried with a pairing in flight. A failed daemon query is a non-zero exit with no output, which is how the module tells it apart from a valid empty list. Pairing failure comes from the kdeconnect-cli exit code, not the pairingFailed signal, since there is no generic D-Bus module to watch it.
2026-09-14docs: KDE Connect module designDanilo M.1-0/+217
Status plus basic actions, absorbed from the indicator. State comes from qdbus6 over kdeconnectd, since quickshell 0.3.1 has no generic DBus module. Polled while the drawer is open, driven by the tile lifecycle. Notes the indicator is not what serves the phone-to-PC clipboard, and the FileDialog focus risk under an Exclusive layer surface.
2026-09-14docs: the network tile is a globe and reads the local IPDanilo M.1-4/+13
The tile no longer swaps a wired/wifi icon, and the local IP comes from ip -j because Quickshell.Networking exposes only the MAC. Recorded in the spec so it matches the code.
2026-09-14docs: correct three plan details found during executionDanilo M.1-14/+32
The Connections block needs a typed property on a QtObject, the bluetooth README lines belong to Task 6 so every commit stays self-consistent, and Pairing has no status property. Each was found by a task review and fixed in the plan as well as the code.
2026-09-14docs: plan the network and bluetooth modulesDanilo M.2-5/+1350
Eight tasks, each leaving the tree working: network wired page, wifi section, README, bluetooth module and page, pairing, README, then the waybar removal and the trap notes. The spec's failure section is corrected in the same commit: it claimed forget and disconnect failures would surface inline, but neither backend exposes a failure signal for them, so that is a ceiling rather than a feature. Verified by probe before writing any task: the singletons start empty for about two seconds, the device lists are ObjectModels, signalStrength is 0..1, scanner and discovering are writable, and NMSettings is not in QML scope, which drops the hidden-network join the spec had left conditional.
2026-09-14docs: design for network and bluetooth modulesDanilo M.1-0/+160
Two drawer modules covering wired, wifi and bluetooth, using the native Quickshell.Networking and Quickshell.Bluetooth backends rather than nmcli or bluetoothctl. The one exception is pairing: quickshell ships no BlueZ agent and no generic D-Bus module, so a device needing passkey confirmation is paired through bluetoothctl, whose own agent handles it. The wifi module became Network so a connected eth0 does not mask the wifi state; the tile shows both links when both are up because the API exposes no route metric. Replaces waybar's network and bluetooth indicators, per the drawer being the single place they are managed.
2026-09-14docs: correct the plan's no-arg ipc open and the head pipeDanilo M.1-6/+20
Quickshell 0.3.1 IPC requires every declared argument, so the plan's `ipc call drawer open` with no page fails; the page-less grid entry is the zero-argument `toggle`. Also records that `... | head` races timeout and kills the shell early, so verification uses a file redirect.
2026-09-14docs: a subdirectory singleton needs no qmldir, drop it from the planDanilo M.1-71/+54
The plan claimed a pragma Singleton outside the config root is invisible until a qmldir names it, and had Task 1 create one plus Tasks 6, 7 and 8 append a line each. That premise was wrong. Tested: a pragma Singleton in modules/sub/, reached by a plain import "modules/sub", resolves with no qmldir anywhere. The control was a reference to a genuinely undefined type, which warns "ReferenceError: <name> is not defined"; the singleton case produced no such warning and the binding evaluated. This matches what AGENTS.md already documented about Theme.qml, which the code review pointed out. Four registration steps removed and the remaining steps renumbered. The AGENTS.md note Task 9 adds now records the tested behaviour rather than the invented one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We9zcHEH8ZWHPcQwMaJRNv
2026-09-14docs: warn that the config task's tilde paths must be typed absoluteDanilo M.1-0/+9
The paths are written with a tilde because this file is committed and the repo forbids home paths in committed files. But neither Hyprland's exec_cmd nor waybar's exec goes through a shell, so a tilde there never expands and fails silently, which reads as a broken keybind rather than a bad path. The two rules genuinely conflict here, so the plan now says which form goes where instead of shipping lines that would not run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We9zcHEH8ZWHPcQwMaJRNv
2026-09-14docs: implementation plan for the desktop shellDanilo M.1-0/+2817
Ten tasks: skeleton, contract, chrome, drawer, then the four modules in increasing order of risk, then documentation, then the six config edits outside this repo. Appearance goes first among the modules despite being last in the grid, because it is nine lines of substance and puts something in the grid before the three migrations start. Sound, mail and vm follow in that order: sound splits one 302-line file into three, mail is the gentlest move and carries the only automated oracle in the project, and vm is the largest and the one whose polling behaviour actually changes. Each task leaves the repo working, because several delete a component and a half-finished deletion is painful to unpick. Paths in the config task are written with a tilde: the gitleaks hook rejected the absolute form, correctly, since this file is committed and the live Hyprland config it describes is not. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We9zcHEH8ZWHPcQwMaJRNv
2026-09-14docs: settle the vm tile's cold state on drawer openDanilo M.1-3/+4
The vm module's service is lazy, so the tile had no state to show before its page had been visited. One `virsh list` when the drawer opens fixes that for the cost of a single call; the per-VM polling for guest-agent stats and managed-save detection still waits for the page, which is where the expense actually is. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01We9zcHEH8ZWHPcQwMaJRNv