| Age | Commit message (Collapse) | Author | Files | Lines |
|
The appearance shell existed because a wallpaper picker needed more room
than the 600px drawer, so it ran as its own process with a full-screen
overlay. The settings window now gives it that room, and its five tabs
become five settings pages in the desktop process: Wallpaper, Theme,
Icons & cursor, Sunset and Idle. appearance/ is gone, and with it a
process, a keepalive window and a layer blur rule.
The files move to desktop/modules/appearance/ unchanged in what they do.
Each tab gets a small *Module.qml; the window's refresh-on-open calls the
singletons' refresh(), which the panel's show() used to. The two tabs that
were inline Components in AppearancePanel become ThemeTab and
WallpaperTab, owning the notices the panel printed. Sunset, Idle and
Icons drop their own Flickable so the page scrolls them and search can
reveal a row; the wallpaper list virtualises its rows and scrolls itself,
so it takes the page's new viewHeight instead.
Search finds a colour scheme and an icon or cursor theme by name.
The appearance IPC target moves to desktop/shell.qml with the same verbs,
so udt-steps and any keybind change only the path. Git now follows the
appearance pages and autostart in the sidebar.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Search used to stop at the page: clicking a result opened it at the top
and left the setting to be found by eye. Now each settings module keeps
its rows as searchItems, {text, key}, reread on every window open since
the page itself is not loaded until shown: git runs gitcfg list, and
autostart watches its file. A row hit opens the page, and Page.reveal()
finds the row whose objectName is the key, waiting up to 3s for the
page's own data, scrolls to it and flashes it in the accent. The reveal
lives in Page, so a page only names its rows.
Git's long keyword list was a stand-in for exactly this and is cut to
"gitconfig". The autostart page takes its path from the module now that
both read the file.
Also corrects the AGENTS note on missed hot reloads: it blamed sed -i
replacing the file, but the Edit tool writes a new inode too and usually
reloads. The cause is unknown; the note now says how to detect it and
that a process that stops watching needs a restart.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The landing page's tiles repeated the sidebar. It now says what the shell
is and where things live, which is what a landing page is for.
The sidebar gains a search. A page is a Component loaded only when
opened, so its contents cannot be indexed without loading every page;
instead a module lists keywords for the settings buried in its page,
and the search matches those and the label. An entry matched by a
keyword shows which one, Enter opens the first match, Escape clears and
then steps out as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The global git config is set once and left alone, which is what the
settings window is for. The user row and its page stay in the drawer;
"More git settings" there now closes the drawer and opens git in the
window: Drawer.show hands any settings module to the window, so every
deep link, IPC included, lands in the right place.
Git was the only user of Module.up, the page back() returned to, so
up goes with it and back from a drawer page is always the grid.
Also gives autostart its rocket glyph, which the first commit wrote as
an empty string.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The drawer's tiles were turning into a full settings app. The drawer stays
the quick area, live state and things toggled often; pages set once and
left alone go to a settings window, a normal Hyprland toplevel listing
them down the left. A module joins with settings: true, which also drops
its tile. A gear tile and ipc call settings open|toggle reach it.
Autostart edits hypr-theme's autostart.lua directly, line by line: an
hl.exec_cmd line is an entry, behind -- it is disabled, everything else
passes through. Edits refuse when their line moved since the parse, and
writes are atomic because Hyprland reloads on every change.
A wrong entry only shows at the next login, so adding is guided rather
than a free text field: pick an installed app or type a command whose
program must resolve on PATH before Add enables, then a workspace and
silent, composed into a [workspace X silent] rule. Try runs the line
through hyprctl dispatch so the rule applies as at login. Existing rows
get no Try: they ran at login, and a second start stacks an instance.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
|
|
Reported from use: a note created with only a title then saved left a db.json
entry with no file beside it, and reopening it said the file was missing.
Cause: FileView.setText("") on a path that view has not loaded creates no
file at all and reports no error. The index write (always non-empty JSON)
was fine, so only the body was lost, which is why it looked like a missing
file rather than a failed save. Bodies are now written by a Process running
cat > <file> with the text on stdin: verified against empty and non-empty
payloads, consecutive writes on one Process, and a store with no db.json at
all. The exit code is the real outcome, so a refused write reports it.
Second report in the same flow: the editor stays editable after the first
click, so typing more and then pressing Commit & push dropped the edit, the
push carrying only what the first click wrote. The second click now writes
the current title and body before it commits and pushes.
|
|
The confirm was on the second click, after the first had already removed the
file and the entry, so the 'Confirm?' label promised a check that had
already happened and one stray click lost the note. The first click now only
arms the button; the second removes and pushes the removal.
|
|
A review of the branch found four ways the page could report success for
work that never happened, all reachable in ordinary use.
Writes: setText is asynchronous unless blockWrites, and its
saved()/saveFailed() do not fire for every write (a second setText to the
same path loses the signal, which stranded the callback), so a save was
reported before it landed and a failed db.json or body write was never
seen. Both views now block and carry the outcome, so writeDb and writeBody
know the result when they return; a failed create therefore leaves no index
entry. The create path also loads the body before the index, so a failed
body write cannot leave an entry with no text.
Commits: the sync chain swallowed every commit failure with '|| true', so a
failing pre-commit hook or a signing failure pushed an unchanged HEAD and
reported success. It now stages, commits only when something is staged, and
lets 'set -e' fail the chain, with stderr folded in for the whole chain
rather than just the push.
Duplicate notes: the 'awaiting commit' state lived only in a 3 second
timer, so a slower second click re-entered create() and appended a second
note. It is now a page property that outlives the timer, and the button
follows it.
Preview: the shared Switch writes checked itself, dropping the binding, so
the switch stopped following page.preview; resynced on change, the way
StatusRow.qml does.
Also validates a note's file name as digits before read, write or rm, and
clears the index error once the index reads again.
|
|
|
|
The editor, and the write and git path behind it. Save is two clicks and
each does one thing: the first writes the note and only then arms the
button as 'Commit & push?', so a quick second click cannot commit a note
the first has not written yet.
The write path refuses when db.json cannot be read as the index. That is
not hypothetical: git pull --rebase --autostash resolves a remote db.json
change against a local uncommitted one, the stash pop conflicts, git still
exits 0, and the file is left full of merge markers. Reading that as 'no
notes' and writing it back deleted every title in a probe; Notes.valid now
distinguishes it from an empty store and the write reports instead. parse
stays tolerant for display. Stamping lastpull also only follows a pull
that worked, and only when git is on at all.
Db reads that a write is based on go through reload() plus waitForJob():
blockLoading does not make reload() blocking, contrary to the note in
AGENTS.md, and reading text() straight after reload() returns the old
contents. A body is written with a plain setText, which needs no prior
load; only reading text() before a load would.
|
|
The module resolves notes.sh's own paths by sourcing its rc, so a changed
BASEDIR moves the page with it, and notes.sh's git test is the same one the
page uses. A Module is a plain QtObject with no default property, so the
config Process and the db.json FileView are typed properties, the shape
UpdatesModule uses for its own Process.
db.json is watched rather than read on open: the tile's count has to move
when a note is added from either side, the CLI included, which is the same
call mail makes for its unread count.
The list reads db.json.notes rather than walking notes/, because that is
where the titles are; entries sort newest first on the file timestamp and
a title search filters them.
|
|
The parsing, sorting and transforms the notes page needs, kept free of any
file or process so node can test them.
Ids and files stay strings: notes.sh writes them with jq --arg and looks a
note up with select(.id == $i), so a number would stop the CLI finding a
note the page wrote. sorted() copies rather than reorders, and an entry
whose file is missing or non-numeric sorts last and formats as blank
instead of NaN.
|
|
desktop-assistant is a text daemon now (its voice version is the tag
voice-v1), and the canvas in desktop/assistant/ is its front end.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The single Add line at the bottom took a full section.key and left the
user to type the section right. Each heading now carries a + that opens a
key and value row under it; git config --add writes the key into that
existing section, among its neighbours, rather than at the end of the
file. The typed text lives on the page, not in the fields, so a failed add
keeps it across the reload that rebuilds every row. A new section is made
in the editor.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Review findings. With a key holding the same value twice, --fixed-value
--replace-all rewrote both and merged two rows into one, and --unset
failed on both; gitcfg now counts the matches and refuses anything but
exactly one, pointing at the editor. A file git cannot parse made list
print nothing, which the page drew as an empty config; list now prints
git's error as an error= line and the page shows it. Escape in the Add
fields clears the typed text before it leaves the page, as a row's edit
is undone first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A "More git settings" button under the Git fields. The page emits
moreGit and the module turns it into Page.go("git"), since the page
itself has no reach into the drawer.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
Every global value as a row under its section, in file order: Enter saves,
Escape undoes, Remove drops that one value, and an Add row takes
section.key and a value. Each write goes through gitcfg and is followed by
a fresh list, so a row shows what git holds; a failure shows git's reason
under its row. "Edit in editor" opens ~/.gitconfig in Sublime Text.
No tile: up is "user", so back returns to the user page it opens from.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
NUL between entries and the first newline between name and value, so
values holding = or newlines survive. The key is after the last dot, so
color.branch.remote groups under color.branch.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
list, set, add and unset over git config --global. --fixed-value lets a
write touch one value of a multi-valued key. set and unset check the old
value is still there first: --replace-all with no match appends and exits
0, and the file may have changed in an editor since the page read it.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
The greeting row picks one at random from ~/.config/quickshell/greetings,
one per line with {user} for the login name, # comments and blank lines
skipped; without the file, five built-in ones. The row is rebuilt on every
drawer open, so the file is read then with no watch, and the last pick is
kept in User.js, a shared .pragma library, so the same greeting never
shows twice running.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Up and Down move through the list, wrapping, and light the row as hover
does; Enter takes it; Escape closes the list. With no list open the
keys pass on, so Escape still leaves the page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
There is no place database on this machine, and the user chose
Nominatim over a tz city list for coverage. It is asked 1s after typing
stops (its policy: one request a second, an identifying User-Agent,
nothing personal sent), from three characters, in the desktop's
language, and a reply to an older query is dropped. places() turns its
JSON into 'place, region, country' labels and treats an error page as
no suggestions; test_user.js covers it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The one-line preview became a section: the git name and email as
read-only fields that follow the Name and Email edits as they are
typed, and the signing key, which is git's too, moved under them. A
derived field is computed from the draft and never saved on its own.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Hover and attention effects were solid: a flat accent ring round the
user page's avatar, a flat quarter-disc in the hot corner, and thick
accent or white borders on window cards, keycaps and knobs. Glow.qml
is a RectangularShadow in the accent, fading out from the shape's edge
and in over 140ms, linked into desktop, window-switcher and keybinds
like Theme.qml, and all five places use it. Selection borders in the
appearance tabs stay: they mark state, not hover.
The avatar also gains the hover cue in the first place: the hand
cursor never showed under a hyprcursor-only theme, so without it the
picture gave no sign it could be clicked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The avatar had room to spare at 96px; 160 fills the page better. The
two ComboBoxes rendered in the default style's light colours on a dark
page; a palette takes the theme into the control and its popup, and the
background matches the text fields.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
With a surface and a border it read as one more notification in the
list under it. Only a faint hover tint remains.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The page showed AccountsService's icon, an older picture than the one
the rest of the desktop uses: hyprlock, the udt SDDM theme (pointed at
it by udt-accent) and the rofi power menu all read ~/.user-icon.jpg.
get now prefers that file, and set converts the pick to JPEG there,
through a temp file and a rename, before handing it to AccountsService
so both agree.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A key whose write half-failed (AccountsService took it, git did not)
reloads as unchanged and Save went dead. A key with an error now counts
as changed, and editing a field clears its error.
Processes were parented to the page, so leaving it mid-save killed
userctl between stores. They hang off the module now, passed in as
owner (not the module's id, which would bind to itself).
busy now clears when the reload lands, so Save cannot be clicked again
before saved is fresh, and the fields and avatar picker are disabled
while busy.
Avatar paths with # or % broke the preview under a bare file:// prefix;
each segment is now URI-encoded for the Image source (page and row),
while draft.avatar stays a plain path for userctl. Dropped a redundant
saved clear in load.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Name, email, avatar, language, location and the git signing key, each
saved through userctl to every store that holds it. Only changed fields
are sent; a field whose write failed keeps its edit and its error, so
the next Save retries it.
A failed userctl get reports a single error= line. The page shows it in
red under the avatar, leaves saved and draft untouched, and disables
Save until a later load succeeds.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
Avatar, Hello <login> and the real name, re-read whenever the grid view
shows again so an edit is visible on the way back from the page. Module
gains tiled, so a module can own a page without a tile.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
Parsing userctl's output and locale -a, and the git name preview, kept
out of QML so node can test them.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
busctl and kwriteconfig6 parse a value starting with '-' as an option, so
a location like '-x' failed; '--' before the value ends option parsing
(checked against both binaries).
get ran without pipefail, so a failing busctl gave empty fields and exit
0, which the page would show as a blank form. It now reads all fields
first and, on failure, prints one error= line and exits 1.
keys listed revoked, expired or invalid primaries, keys that cannot
sign, and could label a key with a revoked uid. It now skips those.
Unsetting the signing key uses --unset-all, since git exits 5 on a
multi-valued key with --unset.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
One writer for the stores other programs read: AccountsService (and with
it the passwd GECOS LibreOffice prefills from), KDE's emaildefaults and
the global git config, where the surname goes in as an initial. busctl
is asked for JSON because its plain output escapes non-ASCII as octal.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
The island's performance icon and panel are gone: the drawer page covers
them. With only one host left, the panel drops its three-column grid for
one card per row at a larger size, since the page scrolls; temperatures
get a card of their own as tiles. The files move from island/perf to
modules/perf.
A cache card ports conky's: ~/.cache and its five largest children,
measured with du each time the page opens (28ms over 2 GiB here) rather
than read from conky's 15-minute sampler file. The largest is yellow,
red past half the total, as conky had it.
The warning dot now shows only on the drawer tile.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A drawer row greets the login user and opens a page that edits name,
email, avatar, language and location, written where other programs
already read them: AccountsService (and through it the passwd GECOS
LibreOffice reads), KDE's emaildefaults and the global git config, with
the surname shortened to an initial for git.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The island spec put per-app volume on the drawer page, and it was never
built. Each application playing audio now gets a row with its name, what it
is playing (two browser tabs are otherwise identical rows), a click-to-mute
level and its own slider.
Streams are matched on PwNodeType.AudioOutStream. Quickshell reports isSink
true for a playback stream, so the obvious isStream && !isSink filter found
nothing while Firefox played. Their properties are empty until bound, so the
page carries a PwObjectTracker for them, which stops with the drawer.
speech-dispatcher-dummy is always present and never audible, and is hidden.
The output slider runs to 150%, the ceiling the volume keybind already uses
(wpctl -l 1.5), with a tick at 100%. Input and apps stop at 100%: a boosted
microphone only clips. The meter became an inline component shared by both,
and wheel-up never lowers a level already past its maximum.
Now playing is dropped from the page: the OSD already carries it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The first cut was small type in one long list: 471 Slackware upgrades
pushed everything below them out of sight, and the repo tag, the
versions and the name ran together across the row. Type is 2px larger
and rows taller. A section over ten rows starts folded behind a
clickable heading with its count. The version being moved to is bright,
the one being left dim, both in Inconsolata so digits line up. Every
other row is shaded to carry the eye across, and the repo is a pill in
one colour per repo, never red, green, yellow or the accent, which mean
something elsewhere.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The tile counts what can be installed now (Slackware and the binary
repo) and what only needs a build, and goes accent when there is
something to install. It runs updatesctl while the drawer is open and
not otherwise; a result over 30 minutes old is followed by a refresh
that runs nvchecker, never after a refresh, so a failing nvchecker
cannot loop.
The page has one section per next action: Slackware upgrades, new and
removed first, then upgrade here, awaiting build, upstream newer, app
updates, the order the work is done in. Each row names the package, the
repo it comes from and the one version jump its action covers. A source
that could not be read says so under the section it empties instead of
reading as nothing pending.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A git hash starting with a letter (3.1-a59215d) ordered behind its base,
because any trailing alphabetic chunk counted as a pre-release; only
alpha, beta, rc, pre and dev do now. Trailing zeros (1.14 against
1.14.0) compare equal instead of reading as an upstream bump. A bad line
in the slackpkg blacklist is skipped rather than crashing the script and
leaving the drawer with nothing. An AppImage keeps its key over a
flatpak of the same name, as the spec orders them, and flatpak list has
a timeout. A Slackware upgrade compares version and build, so an arch
change alone is not a row. The spec's prose now matches the code: the
upstream comparison includes the installed version.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The awaiting-build branch also required the build to be older than
upstream, which cannot fail there: a build newer than installed was
already taken as an upgrade, so the build is at most the installed
version, which is behind upstream whenever a row is emitted. Removing
it left every test passing, which is how it was found.
The blacklisted testing/ entry sat after the main one in the fixture, so
setdefault kept the main one and the mirror-side blacklist was never
exercised; it now comes first, and dropping the check fails the test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A fifo with no writer blocks cat at open() the way a hard NFS mount
blocks during a NAS outage. The repo section reports unreachable within
the timeout and the mirror sections still fill.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
Compared mirror against installed, as upgrade-all does, not against
slackpkg's local ChangeLog: `slackpkg update` replaces that without
installing anything, so a diff against it reads clean after an update
that was never applied. New and removed come from the mirror's
ChangeLog, the newest Added or Removed line deciding, so a package
added and later upgraded still counts as new. Blacklisted build tags
and directories are skipped, as slackpkg skips them.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
Each nvchecker stanza lands in one section: upgrade here when the binary
repo's PACKAGES.TXT holds a newer build than installed, awaiting build
when the recipe (the user's or SBo's) has caught up but the build has
not, upstream newer when the recipe lags, app updates for AppImages and
flatpaks. Built comes from PACKAGES.TXT rather than the .info, which is
why gamescope and kitty no longer read as installable: the repo still
held the old build. The repo is on a hard NFS mount, so it is read by a
child the timeout can kill.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
nvtable compares versions for equality, which cannot tell an installed
2.1.278 from an SBo recipe of 2.1.267 that is older than it. vercmp
orders numeric chunks as numbers and treats a trailing alphabetic chunk
as a pre-release, so a kernel suffix or git hash orders as newer than
its base and never reads as behind.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
The tile grid sits in a Flickable capped at three rows, so the fourth row
the new modules bring scrolls inside that box instead of eating the
notification space; a thin accent bar in the margin shows only when a row
is hidden. Tiles are reordered with the everyday ones first.
Performance shows the island's PerfPanel in a Page, with a tile line of CPU
and RAM and the warning dot. Perf.detailed is now a count of live PerfPanel
instances instead of a binding in the status pill: the island and the drawer
both host the panel, and a bool with two writers would let one clear it
under the other.
Clipboard reads cliphist when its page opens: search, copy on click or
Enter, delete per row, clear all behind a 3 second confirm, image entries as
thumbnails decoded once onto tmpfs. Entries reach sh as $1, never inside a
command string. A copy closes the drawer through a new Page.dismiss signal
the drawer connects like back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|