aboutsummaryrefslogtreecommitdiffstats
path: root/desktop
AgeCommit message (Collapse)AuthorFilesLines
8 hoursfeat(desktop): make the drawer notifications readableDanilo M.3-12/+44
Each row shows when the notification appeared ("Sep 15 14:32", from created), sits on its own rounded card with space between rows instead of reading as one list, and uses the balloon's 16/18/16 sizing rather than 13/11. The close target grows to a 32px hit area in the row and the balloon, since a 20px box was hard to hit.
10 hoursfix(notifications): apply final review fixesDanilo M.1-1/+6
Mirror the balloon's right-click close-all on the drawer row: the spec says the gestures are identical in both forms, and a right-click on a row previously did nothing. The right-button branch precedes the live guard so it behaves the same on history rows. Stop importing the Status singleton into the notification shell. Referencing it instantiated it, and its onPresentationChanged writes status.dnd and runs breaktimer.sh, so a read-only consumer was writing state and shelling out on every presentation toggle, and doubled the dndBeforePresentation race. Notify now reads "/run/user/<uid>/status.dnd" directly through a FileView, the same convention as the notifyd files; a missing file means off. AGENTS.md named Drawer.qml as the drawer's reserved space; it is desktop/NotificationList.qml.
10 hoursfeat(status): add the snooze rowDanilo M.2-0/+124
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.
10 hoursfix(desktop): leave history when deep-linking to a pageDanilo M.1-0/+3
show() is the IPC deep-link path and set only page/open, so if the history view was open, historyLoader stayed active alongside pageLoader and painted over the requested module page. Clear history in show(), as close() already does.
10 hoursfeat(desktop): add the notification history pageDanilo M.2-4/+92
Reachable only from the reserved space's History button, so it is a drawer view, not a module and not a tile. Rows are inert; clear-all empties the ring, since the daemon has no per-id history deletion.
11 hoursfix(desktop): publish the drawer flag at startupDanilo M.1-0/+5
onOpenChanged does not fire for the initial value, so a restart while the drawer was open left notifyd/drawer at "1" and every balloon stayed suppressed until a drawer open/close cycle. Write the starting state from Component.onCompleted so the file always reflects the drawer.
11 hoursfeat(desktop): fill the drawer's notification spaceDanilo M.3-6/+199
The reserved Item becomes the live queue with a History button, scrollable because the queue can hold 20. The drawer writes notifyd/drawer so the balloon shell stands down while this space is showing, which is what stops a notification appearing twice.
11 hoursfeat(desktop): add the Notify singletonDanilo M.1-0/+1
The daemon publishes its queue, history, drawer flag and snooze as files; this reads them for both renderers, the same files-are-the-interface convention the status registry set. Mutations and the rofi action picker run notifyctl through a Process, which is the one path back to the daemon.
13 hoursfix(status): address final review findingsDanilo M.4-6/+7
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.
13 hoursdocs(status): document the module and record its trapsDanilo M.1-0/+59
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.
13 hoursfix(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.
13 hoursfeat(desktop): add the status page and register the moduleDanilo M.2-0/+45
One row per mode. Adding a mode is one file in the singleton and one row here, which is what a registry buys over two separate toggles.
13 hoursfeat(desktop): add the status module, tile and rowDanilo M.3-0/+122
The module is thin because the singleton owns the modes: it is a tile and a page over Status, always active so the effects hold whether or not the drawer has been opened. Injected as st rather than mod, since a component property named the same as the enclosing object's id binds to itself and arrives undefined.
13 hoursfeat(desktop): add presentation mode and its effectsDanilo M.1-0/+4
Presentation mode sets DND, asserts a Wayland idle inhibitor and pauses breaktimer. The effects hang off the mode property rather than the setter, so a mode set with statusctl while the drawer is closed asserts them too. DND has two writers once presentation mode exists, so turning presentation off restores the value DND had before rather than clearing it, or an afternoon of hand-set DND would vanish when a talk ends. That prior value lives in the singleton, not in a file: it means nothing once presentation mode is off, and presentation mode does not survive a reboot. breaktimer owns its own state file and is driven only through its verbs. Two writers on that file would race with its daemon loop.
13 hoursrefactor(desktop): name the keepalive windowDanilo M.1-0/+5
IdleInhibitor needs a non-null window and this is the only one that lives for the whole session, so presentation mode attaches to it. Naming it is a prerequisite for that and changes nothing else.
14 hoursfeat(desktop): add the statusctl CLI and its checkDanilo M.2-0/+219
statusctl reads and writes the mode files directly rather than going through the shell, so it works while quickshell is down. Setting a mode that way records the state without firing its effects; the shell sees the change through its own watch and reasserts them. The watch listens on the directory, not the file: an atomic write replaces the file, so a watch held on the old inode dies with it. Same trap the mail watcher hit with Xapian, and the reason moved_to is in the event list. An unknown mode exits non-zero rather than reading as off, so a typo cannot masquerade as a mode that happens to be disabled. The watch loop runs inotifywait through process substitution, held open on fd 3, with the PID captured and trapped on EXIT/TERM/INT. Piped straight into the while loop, inotifywait would be a pipeline sibling rather than a child, so a plain kill on statusctl leaves it running, watching a directory nobody reads anymore. Task 7 wires this CLI into waybar's exec, and waybar kills and respawns exec children on every reload the same plain way, so every reload would otherwise leak another watcher for the rest of the session. Process substitution makes inotifywait a real child whose PID the trap can hold and kill. A SIGKILL still orphans it, since KILL cannot be trapped, but nothing in the described path sends one. A failed rename exits non-zero instead of being swallowed. Reporting success on a write that did not land would leave the caller and the shell disagreeing about the mode, with an orphan temp file as the only trace, and this script is the interface the whole registry is read and written through. Verified against an unwritable directory: exit 1, no orphan left, prior value intact. Reading uses a redirect rather than cat piped into tr, so that an unreadable file falling back to off is explicit rather than a side effect of a pipeline discarding cat's exit status. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A7ThHHh5iTYbVfp3rNAkw2
14 hoursfeat(desktop): add the status singleton with the dnd modeDanilo M.1-0/+1
Modes live as files under XDG_RUNTIME_DIR, one per mode, holding 0 or 1, with a missing file meaning off. That directory is tmpfs, so a reboot resets every mode and no cleanup code is needed. FileView covers both directions: atomicWrites for the write, watchChanges for the watch, so an external writer repaints the drawer with no polling. Both are already the documented defaults in 0.3.1 and are set explicitly anyway, because statusctl watches close_write,moved_to precisely because an atomic write lands as a rename. A future release flipping either default would break the watcher with no error, and a declaration is a stronger guarantee than a default. The documented behaviour is that a FileView fires its own fileChanged on setText, so the reparse compares before assigning and a self-write is a no-op rather than a loop. That has not been observed running yet: nothing consumes the singleton until the status module exists, so the guard stays unproven until then. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A7ThHHh5iTYbVfp3rNAkw2
15 hoursfix(desktop): stop bluetooth and wifi scans after 60sDanilo M.2-0/+20
Neither scan property self-terminates. BlueZ discovery runs until StartDiscovery is stopped, and the NetworkManager scanner repeats until disabled; the docs for both say as much and give no timeout or interval property. The scan button was the only writer of either property, and closing the drawer destroys the page without touching them, so a scan started and forgotten kept the radio busy for the rest of the session. Continuous wifi scanning also costs throughput on the connected link. Each page gets a Timer bound to the scan property rather than started by the button. Binding it that way means a manual stop cancels the clock, and reopening the page on a scan still running from an earlier visit re-arms a fresh 60s instead of leaving it running forever. Verified both files parse: the desktop shell hot-reloaded without dropping its process. The timeout firing at 60s is not verified here, since it needs a real scan on the radio. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A7ThHHh5iTYbVfp3rNAkw2
29 hoursfeat(desktop): show the module glyph in every page titleDanilo M.2-4/+27
Page.qml gains an icon rendered before the title, and the drawer binds it to the loaded module's icon so a module whose glyph changes (network follows the active link) keeps the header in step.
29 hoursfix(desktop): queue KDE Connect actions instead of interruptingDanilo M.3-8/+26
A second action while one was in flight killed the first process, whose onExited then fired under the new label and raised a false failure notification. Actions are now queued and launched on exit, so each exit reports under its own label. Also correct two comments that contradicted the code: the pairing banner is only noticed on the on-open poll, and the laptop glyph keys off the daemon's desktop type.
30 hoursfix(desktop): name the kdeconnect injected property kc, not modDanilo M.4-29/+29
The tile, page and row each took a required property named mod while the module object's id is also mod. In a Component scope the id resolves to the property's own unset self, so every page.mod read was undefined and the device list never rendered. Only a runtime TypeError showed it; the smoke check was clean because the drawer was closed. kc matches the net and bt convention and cannot collide with the id.
30 hoursdocs: record the KDE Connect trapsDanilo M.1-0/+5
No D-Bus module means shell-outs and no push; the daemon and the indicator are separate; the battery plugin is not on every device; and qdbus reads properties and methods through one call shape.
30 hoursdocs(desktop): document the kdeconnect moduleDanilo M.2-7/+53
Records that the phone-to-PC clipboard is the daemon's plugin and not the indicator's, so removing the indicator does not break it, and that polling stops when the drawer closes.
30 hoursfeat(desktop): share a file to a KDE Connect deviceDanilo M.2-0/+30
A QtQuick.Dialogs FileDialog held on the module, not the page, so a closed drawer does not destroy it mid-pick. The path is decoded from the file:// URL before it reaches kdeconnect-cli.
30 hoursfeat(desktop): KDE Connect pairingDanilo M.2-0/+54
Outgoing pair and unpair go through kdeconnect-cli; accepting and rejecting an incoming request go straight to the daemon, since the CLI has no verb for them. The verification key is shown on the row while a pairing is in flight so the two ends can be compared. An incoming request is not noticed while the drawer is closed, because the poll only runs when it is open; the daemon keeps the request, so it is there on the next open.
30 hoursfeat(desktop): KDE Connect device actionsDanilo M.1-0/+30
Ring, clipboard, mount and unpair, all shell-outs since there is no D-Bus module. Actions share one serialized Process and a failure notifies, for the case where the drawer has closed by the time the command returns. Mount runs --mount and then --get-mount-point in one shell, because --mount prints nothing and the path is a second call.
30 hoursfeat(desktop): KDE Connect module, tile and pageDanilo M.5-0/+359
State is polled from kdeconnect-state.sh because there is no D-Bus module to subscribe with. The poll runs only while the drawer is open: the tile is created when the panel loads and destroyed when it unloads, and it flips the module's polling flag. The laptop reports no battery plugin, so an absent charge is an empty field and renders as nothing, never as a zero.
30 hoursfeat(desktop): KDE Connect state scriptDanilo M.2-0/+218
Quickshell 0.3.1 has no generic D-Bus module, so device state comes from qdbus6 shell-outs. The script emits a tab line protocol for the QML side and is checked by a stub-daemon test, since the live daemon is not an oracle an agent can rely on. A failed daemon query exits non-zero with no output so the module keeps its last list, which is not the same as a valid empty device list.
31 hoursfix(desktop): disable bluetooth switches until the adapter is upDanilo M.1-0/+2
31 hoursfeat(desktop): a theme-coloured SwitchDanilo M.3-2/+47
31 hoursfeat(desktop): pin glyphs to Inconsolata Nerd FontDanilo M.7-11/+11
Glyphs are Nerd Font private-use codepoints, and relying on per-codepoint fontconfig fallback from Noto Sans let the same codepoint resolve to a different glyph in a different Nerd Font. A Theme.iconFamily now names the render family once, and every glyph Text uses it, while fontFamily stays Noto Sans for words and labels. The ethernet glyph moves from \uf6ff to \uef44, the fa-ethernet codepoint present in Inconsolata.
31 hoursfix(desktop): use the real ethernet glyph, not a gamepadDanilo M.1-1/+1
31 hoursfeat(desktop): globe network tile with per-link IPsDanilo M.4-30/+122
35 hoursfix(desktop): wire tile active state and clear connect errorsDanilo M.7-8/+24
35 hoursdocs: record the network and bluetooth trapsDanilo M.1-0/+3
The native backends start empty for about two seconds, device lists are ObjectModels, NMSettings is not in QML scope, and quickshell has no BlueZ agent. Each one cost a probe to learn; writing them down is cheaper than re-learning them.
35 hoursdocs(desktop): document the bluetooth moduleDanilo M.2-6/+46
35 hoursfeat(desktop): pair bluetooth devicesDanilo M.3-0/+117
Quickshell has no BlueZ agent and no generic D-Bus module, so a pairing that needs a passkey confirmed cannot be done from QML. bluetoothctl registers its own agent, so pairing is the one shell-out; adapter state, scanning, connecting and forgetting all stay native. The timeout is the ceiling: a device bluetoothctl cannot auto-confirm fails visibly rather than hanging the page.
35 hoursfix(desktop): scale bluetooth battery to a percentageDanilo M.1-1/+1
35 hoursfeat(desktop): bluetooth module, tile and pageDanilo M.5-0/+295
Native Quickshell.Bluetooth for adapter power, discoverable, scan, connect, disconnect, trust and forget. Pairing is separate, because quickshell ships no BlueZ agent. The adapter is null for the first ~2s, so every access is navigated safely rather than assuming a first paint.
35 hoursdocs(desktop): keep the network module docs self-consistentDanilo M.2-11/+11
35 hoursdocs(desktop): document the network moduleDanilo M.2-1/+37
35 hoursfeat(desktop): network wifi pageDanilo M.2-1/+211
Radio switch, scan, and a list ordered connected, known, strongest. A secured unknown network reveals an inline password field rather than opening a popup, since a QtQuick.Controls popup is a separate window that positions badly on this layer surface, the same reason the sound page expands in place. Connection failures come through the Network.connectionFailed signal and show inline; the same failure also notifies, for the case where the drawer has closed by the time it lands.
35 hoursfeat(desktop): network module, live tile and wired pageDanilo M.4-0/+228
Wired first because it is the simpler half and proves the module wiring. Native Quickshell.Networking, no polling: the singletons push, and alwaysActive is true with no Service.qml because there is nothing to gate. The tile shows one line per active link rather than picking a primary, since the API exposes no route metric and guessing wrong would be worse than showing both.
36 hoursfeat(sound): switch default device, control the levelDanilo M.2-24/+179
The sound page showed the default sink and source but gave no way to choose another device and the meter was inert. Each section now lists the real audio devices, streams and monitor nodes filtered out, behind an in-place dropdown that sets Pipewire.preferredDefaultAudioSink or preferredDefaultAudioSource. The meter takes the wheel, click and drag, and the state glyph beside the percentage toggles mute. The list expands in place rather than in a popup because a QtQuick.Controls popup is a separate window and does not position reliably on a layer surface. The meter accepts the wheel and prevents stealing so the page's ScrollView neither scrolls nor flicks instead.
36 hoursfix(desktop): honest alwaysActive docs and working Escape in confirmDanilo M.7-41/+51
37 hoursdocs: correct drawer exit behaviour and dead module commandsDanilo M.4-26/+33
The README claimed a click outside returns to the grid; it closes the drawer, and only Escape-with-a-page or the back arrow return to the grid. The three moved module READMEs' run and IPC commands still named the deleted volume-osd, mail-overview and vm-manager paths, so point them at qs -p desktop and ipc call drawer open sound|mail|vm.
37 hoursdocs: the desktop shell and what the merge changedDanilo M.4-0/+153
Five components become three, and the Theme symlink count with them. Two new notes: a pragma Singleton in a subdirectory resolves through a plain directory import with no qmldir, and Virsh.sampling gates only the stats poll, not the lifecycle stream that keeps the tile correct while the page is closed.
37 hoursfeat(desktop): move vm-manager in as the vm moduleDanilo M.7-0/+975
Virsh.qml is unchanged; VmPanel's body becomes the page and the stat row becomes a Flow, because four stats with 150px bars do not fit a 600px drawer as one row. The page starts and stops Virsh.sampling, so the 2s poll runs only while something is looking at it; the lifecycle event stream still runs always, which is what keeps the tile's dots current.
37 hoursfix(desktop): start the mail Accounts singleton at shell launchDanilo M.1-0/+7
The singleton was referenced only inside lazy Components, so its qtmaildir.conf watcher and unread-count refresh did not start until the drawer first opened, contradicting alwaysActive and the old MailPanel, which shell.qml instantiated directly. Naming Accounts at the Module top level creates it at shell load; the page keeps its own refresh on open.
37 hoursfeat(desktop): move mail-overview in as the mail moduleDanilo M.10-0/+1291
Accounts.qml is unchanged and MailPanel's body becomes the page. The three scripts move with it, which changes the absolute paths in autostart and in the waybar module; both are outside this repo and listed in the plan's final task. Button.qml, byte-identical here and in vm-manager, lands at the shell root as the single copy.