| Age | Commit message (Collapse) | Author | Files | Lines |
|
Desktop 2 is the editor and desktop 3 the terminal; the map had them the
other way round, matching waybar's stylesheet before it was corrected.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
Rows for each listener with an enable toggle and a timeout field, the
command shown read-only. Save rewrites the file and restarts hypridle.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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>
|
|
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>
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
The check has five assertions, not four.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|