aboutsummaryrefslogtreecommitdiffstats
AgeCommit message (Collapse)AuthorFilesLines
9 hoursfeat(window-switcher): mark desktops with waybar's iconsDanilo M.3-8/+62
The card's desktop line showed the workspace number. waybar draws the same eight desktops as CSS background images keyed by number, because its format-icons takes text rather than paths; those names now live in the Windows singleton and are resolved through the active icon theme, so a theme switch follows the drawer and the bar and the switcher agree. A named workspace, or one past eight, falls back to its number, and so does an icon the active theme cannot resolve.
10 hoursfeat(appearance): polish the Icons tab previews and applyDanilo M.4-129/+270
Cursor previews were extracted only on hover, so the row stayed blank until each card was visited. They are now batched on open: one python pass extracts four shapes per theme (arrow, hand, text, horizontal resize) through the theme's aliases and every storage form it may use (hyprcursor .hlc, a shape directory SVG, or a legacy Xcursor binary via xcur2png). The card rows are Flows so they wrap instead of running off the right edge, and the tab scrolls. Cards widened and names wrap to two lines. A size selector (16-64) applies the pointer size live, to gsettings, the GTK settings.ini files and environment.lua. gsettings alone does not reliably switch icon/cursor here: both gtk-3.0 and gtk-4.0 settings.ini carry the names, so apply now rewrites gtk-icon-theme-name / gtk-cursor-theme-name / gtk-cursor-theme-size there too. The symlinked `default` cursor alias is skipped in the scan and the current value resolved through it, so the real theme carries the current marker. The tab row is centred against the panel so it reads as drawer chrome rather than the first line of a tab's content.
10 hoursfix(appearance): wait for hyprsunset exit before restartDanilo M.1-1/+7
pkill only sends SIGTERM, and hyprsunset binds Hyprland's CTM manager exclusively, so the replacement started immediately after pkill raced the old process and exited with 'A CTM manager is already running'. Saving from the Sunset tab therefore killed the night-light daemon instead of restarting it. Bound the wait to 5s (50 x 0.1s) so a wedged process cannot hang the restart.
10 hoursfix(appearance): final review fixes for the Sunset, Idle and Icons tabsDanilo M.4-7/+31
xcur2png wrote its config file into the qs CWD, which is the repo root, so hovering a legacy Xcursor theme left a stray left_ptr.conf in the tree. Point -c at the per-theme cache dir instead. Hyprsunset.save() could run before refresh()'s two cat processes landed and write a header-only hyprsunset.conf, blanking the location. Guard on the same confLoaded/appLoaded flags Hypridle already uses. The idle save had no acknowledgement, which the spec requires. Add a notice after the restart and show it in the tab, and switch Hypridle's save guard to a loaded flag so a config whose first block is a listener is not mistaken for an unloaded one. classify() used else if, so a directory carrying both index.theme and cursors/ (Adwaita) was listed as a cursor theme only and never reached the icon picker. Make the two tests independent; selftest gains the dual-purpose case.
10 hoursdocs(appearance): fix stale Theme tab phrase and align the tab diagramDanilo M.1-9/+9
The five-tab shell's paragraph still said theme opened "the other tab", and the widened top border left the body and bottom border 6 columns short. Widen lines 7-14 to match and name the Theme tab.
10 hoursdocs(appearance): document the Sunset, Idle and Icons tabsDanilo M.1-1/+27
Records the IPC verbs, the shared hyprsunset.conf format, the preserved hypridle comments, and the preview mechanisms. The hardcoded icon theme in udt, waybar and rofi is tracked as a TODO in those repos.
11 hoursfeat(appearance): add the Icons tabDanilo M.2-0/+185
Icon themes preview four glyphs each from the GTK lookup; cursor themes preview an extracted left_ptr and change the real cursor on hover, reverted on leave and on close. Selection applies both.
11 hoursfix(appearance): show idle timeouts as m:ssDanilo M.1-1/+1
The spec calls for a seconds field shown as m:ss, but the first pass rendered the brief's '9m 30s' form. Zero-pad the seconds so 570 reads 9:30 and 600 reads 10:00.
11 hoursfeat(appearance): add the Idle tabDanilo M.2-0/+92
Rows for each listener with an enable toggle and a timeout field, the command shown read-only. Save rewrites the file and restarts hypridle.
11 hoursfeat(appearance): add the Sunset tabDanilo M.5-11/+302
Tab bar and Tab-key cycle become five entries, the Loader picks inline or file tabs, and show() refreshes the model. The tab edits the profiles in place, fetches sun times and previews through the daemon.
11 hoursfeat(appearance): add the icon and cursor theme modelDanilo M.2-1/+237
Lists themes from ~/.icons, ~/.local/share/icons and /usr/share/icons, distinguishing cursor themes by cursors/ or manifest.hl. Icon previews come from one GTK lookup process for every theme; cursor previews extract left_ptr from a hyprcursor .hlc via unzip or from an Xcursor theme via xcur2png. Application sets gsettings and the Qt configs, and rewrites the cursor env in environment.lua.
11 hoursfix(appearance): refuse to save hypridle before the config has loadedDanilo M.1-0/+1
save() could run before refresh()'s async cat completed, when prefix and tail are empty and listeners is empty, so it would write an empty file and restart hypridle with no listeners. The real config always has a general block, so an empty prefix means the load has not happened or failed.
11 hoursfeat(appearance): add the hypridle listener modelDanilo M.2-1/+157
Parses the general block and comments before the first listener and keeps them verbatim; only timeouts and the enabled flag are editable, commands are copied. Disabled listeners are written commented out, and the parser matches the closing brace at line start so the dpms command's inline brace does not truncate the block.
11 hoursfix(appearance): preserve the raw cache path on settings saveDanilo M.1-2/+4
appConfText() hardcoded path = ~/.config/hyprsunset-qt/sun.json, so any saveSettings() -- from save() or detect() -- silently reset a user-customized cache path while fetchSun() kept writing to the parsed location. Mirror sunset-qt's settings.py: hold the raw unexpanded string in cachePathRaw for serialization and derive the expanded cachePath for file access, so a no-op save is byte-identical and the two stay consistent.
11 hoursfeat(appearance): add the hyprsunset profile modelDanilo M.2-0/+310
Parses and serializes ~/.config/hypr/hyprsunset.conf in the exact format hyprsunset-qt writes, so both editors share the file. Location lives in hyprsunset-qt's own INI; fetching mirrors its curl calls and browser User-Agent. Selftest round-trips a fixture byte-for-byte.
15 hoursdocs: the old waybar notification module is archived, not just deadDanilo M.1-3/+3
The note pointed at waybar/scripts/notifications.py and its module by path. Both moved to ~/bin/archive/ with the rest of the stock waybar theme, whose scripts had no caller left once this shell took over notifications. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
15 hoursrefactor(theme): read the palette from ~/.cache/udtDanilo M.7-8/+8
unified-desktop-theme moved its generated files out of ~/.cache/wal, which pywal used to own and nothing writes any more. The palette udt-accent generates is now at ~/.cache/udt/udt-palette.qml. shared/Theme.qml is the only code change; every component reaches it through a symlink. The module READMEs cite the path, so they move with it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
28 hoursdocs: 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.
28 hoursdocs: 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.
29 hoursfix(notifications): check every img src and uncache previewDanilo M.2-7/+14
The sanitizer matched the first \bsrc anywhere in the tag, so a data-src="file:///tmp/decoy" before a remote src won the match and kept the whole tag while Qt, which ignores data-src, fetched the remote image. Scan every src assignment in the tag and keep it only if all of them are local. Also set cache: false on the balloon preview, since the daemon overwrites the same path on a replace and QQuickPixmapCache keys on URL, so a replaced notification could show the previous image.
29 hoursfix(notifications): allowlist local inline image sourcesDanilo M.1-5/+22
The http(s)-only regex let protocol-relative //host, ftp, data and entity-encoded schemes through, and the comment overstated what it removed. Replace it with a deny-by-default allowlist: an <img> is removed unless its src, entities decoded first, is a file: URL or a single leading slash. Protocol-relative //host is rejected while a /absolute/path is kept.
29 hoursfeat(notifications): strip remote inline image sourcesDanilo M.3-2/+10
A notification is untrusted input. Inline <img> now renders only for local sources; an http(s) source is removed before the RichText body is shown, so a remote sender cannot make the shell fetch a URL. The row and the balloon share the one sanitizer in the Notify singleton.
29 hoursfeat(notifications): draw the content image in the balloonDanilo M.1-1/+25
The daemon now publishes an image path; the balloon shows it below the text, scaled to the balloon width with a 240px cap. A daemon without the field leaves it hidden, so the renderer and the daemon can ship in either order.
30 hoursdocs: 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.
30 hoursdocs: 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.
30 hoursfeat(desktop): make the drawer notifications readableDanilo M.4-16/+49
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.
31 hourschore: ignore the superpowers SDD workspaceDanilo M.1-0/+1
The per-plan ledger, briefs and reports under .superpowers/ are scratch, not source. Committing the ignore rule keeps the tree clean for every clone and worktree instead of only this one, where it lived in the local info/exclude.
32 hoursfix(notifications): apply final review fixesDanilo M.5-4/+26
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.
33 hoursdocs(notifications): document the renderers and record the trapsDanilo M.2-0/+23
The daemon is a separate process and the files are the interface; suppression lives in the balloon shell so the drawer can list what DND held back; and the drawer flag is what stops a notification appearing as both a balloon and a row.
33 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.
33 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.
33 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.
33 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.
33 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.
33 hoursfeat(notifications): add the rofi action pickerDanilo M.1-0/+40
The renderer passes the action labels and keys as arguments, so the picker parses no JSON; a chosen label maps to its key and runs notifyctl action.
33 hoursfix(notifications): map the balloon window from the queue, not layoutDanilo M.1-3/+8
Deriving the window's visible from column.implicitHeight deadlocked it: a hidden window stops polishing, Column computes implicitHeight during polish, so once the column was empty the window never re-mapped and no later balloon could show, including at startup where the queue arrives asynchronously after the window is already hidden. The binding now reads Notify.queue membership, which a property binding re-evaluates whether or not the window is mapped, so a later notification re-maps it. Expired entries left in the queue keep a zero-height window mapped, which is harmless.
33 hoursfix(notifications): keep the balloon model stable across ticksDanilo M.2-14/+31
The Repeater model was a fresh filter of Notify.queue on every 250ms tick because the filter read now, so Qt Quick recreated every balloon delegate four times a second, reloading icons and resetting hover. Model is now Notify.queue itself, and each balloon drops itself at expiry through its own visible binding, driven by the shared tick. Removing required from the balloon's properties is part of the same fix: a required property makes QML create the delegate in its own context, where modelData and index are undefined, so notification: modelData silently arrived undefined once a live queue item was actually drawn.
33 hoursfeat(notifications): add the balloon shellDanilo M.7-0/+244
Reads the daemon's queue through the Notify singleton and draws a balloon per live notification, bottom-right of DP-1 over conky. Suppression is here, not in the daemon: dnd withholds low and normal, snooze withholds all, and the drawer still lists them.
33 hoursfeat(desktop): add the Notify singletonDanilo M.2-0/+124
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.
33 hoursdocs: 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.
33 hoursdocs: 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.
33 hoursdocs: correct the snooze check count to fiveDanilo M.1-1/+1
The check has five assertions, not four.
33 hoursdocs: 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.
34 hoursdocs: 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.
34 hoursdocs: 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.
34 hoursdocs: 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.
34 hoursdocs: 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.
34 hoursdocs: 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.
35 hoursfix(status): address final review findingsDanilo M.6-10/+23
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.
35 hoursdocs(status): document the module and record its trapsDanilo M.3-5/+111
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.