| Age | Commit message (Collapse) | Author | Files | Lines |
|
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.
|
|
A drawer tile and page for bash-notes: browse, write, edit and delete
notes in the page, sharing notes.sh's store so both tools work on the
same notes.
Git follows notes.sh's shape, pull before write, commit and push after,
with two departures the page needs: an unconfirmed Save leaves db.json
uncommitted (write and commit are two clicks here, one invocation there),
so the pulls carry --autostash, and lastpull is stamped after the pull
rather than before it, which notes.sh does and then ignores the failed
pull that follows.
|
|
The Do tile's hover and press fills were Qt.alpha colours, and the
Glow sits beneath the tile (z: -1), so the translucent fill let it
wash out the icon and the label. Qt.tint over Theme.surface keeps the
fill opaque, so the glow reads as a glow around a solid tile rather
than through it.
|
|
The canvas: a To .md ended any other way than its own Stop (the footer
Stop, a question, a drop, a daemon restart, an older daemon refusing the
command) left the card at Working forever. The footer Stop goes through
stopOp(), asking and dropping end the operation first, a disconnect ends
it, and an error while nothing else runs is taken as its outcome. Each
fileops run gets its own Process, so a stopped run's late answer is never
read as the next one's. To .md takes one PDF, as one file is written.
Compress... no longer flips back when file(1) refines a kind, long errors
are elided, and a desktop with no documents folder gets
Documents/Assistant rather than ~/Assistant.
fileops: an animated GIF to PNG or JPEG left one file per frame under
names the rename never found; the first frame is taken. JPEG flattens
transparency onto white. Zipping a folder holding the output folder read
its own growing archive without end; only regular files outside it are
added, which also skips FIFOs and dangling links. qpdf warnings no longer
fail a merge. A folder's free name keeps its dots, a lone .gz or .xz is
decompressed rather than refused, and a dotfile's archive is not hidden.
Tools die with fileops even when it is killed outright, a killed run's
leftovers are swept on the next run, a stop between fork and bookkeeping
cannot miss the child, and a late stop cannot add a second answer.
Error lines lose bsdtar's doubled prefix and its delayed-exit noise.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The actions sat as buttons bunched at the left of the card. Each group is
now a grid of equal tiles across the card's width, icon over label,
about 150 px a column, with a small Ask or Do caption. Icons are Font
Awesome and Octicons codepoints checked in Inconsolata Nerd Font's cmap;
a saved preset without one gets a fallback. The lists referenced from
QML are var, since a library's top-level const is not reliably exposed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Conversions, extraction, merge and compression on the staged files from
the file state, one at a time, with Stop; Compress… opens its formats.
To .md goes through the daemon's write, the rest through fileops. The
card keeps the result line, Saved name with Open and Folder, or the
error, so another operation can follow on the same files. Results land in
Documents/Assistant.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
One CLI for converting, extracting, merging and compressing staged files,
answering with one JSON line. Results are written under a temporary name
and renamed to the first free one, so a failure or a stop leaves nothing
half written, and a stop kills the tool's whole process group. This
ffmpeg has no libx264 and no AAC encoder, so MP4 is H.264 through VAAPI
with MP3 audio, and audio extraction copies AAC, MP3 and Opus tracks as
they are. The test builds its inputs and checks every result's type.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Local conversions, extraction, merge and compression through a small
fileops CLI, results in Documents/Assistant, and To .md through the
daemon's new write command.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A script with no suffix staged as 'other' and was refused. A staged file
the name says nothing about is now handed to file(1) once, and its mime
type gives the kind, so a shell script is code and gets Explain and
Review. The daemon makes its own call on the content.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A row of canned questions matching every staged file's kind, plus Compare
for two or more files: one click asks it, Shift+click puts it in the input
to edit first. Defaults live in Presets.js and the list in use in Prefs, so
a future drawer settings page can edit it; a saved list that does not
validate reads as the defaults. Each prompt asks for an answer in the
document's language. Code and configuration get their own kind, so a
script is offered Explain and Review rather than Summarize. Button's
clicked signal carries the mouse event for the Shift check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Canned questions per staged kind in the file state, stored in Prefs with
built-in defaults so a future settings page can edit them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Checked by hand: resume rule, island drop and its refusal message, the file
state, Chat about it, the picker, and the Escape order.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A drop offering no local file:// URL was refused silently: the bubble
vanished and nothing happened. Measured on the first report: the file
manager offers a trashed file as trash:, with no path to read. The bubble
now shows the reason for a moment, Trash named as such, and the refusal is
logged with the URL schemes and offered formats (never the paths).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
From review. A shell reload reconnected with lastActive at 0, so the next
SUPER+Home started a new conversation thirty seconds into one; hello with
turns now counts as activity. show() could send new under a reply still
running, which stops it unsaved; it waits while busy. ask() cleared the
chips before the daemon accepted them, so a refused file (unreadable, too
big, a model without vision) dropped them and collapsed the card to the
entry line; the user event clears them now, and chatting holds the layout
while a PDF is read.
In the picker, folders ignore name filters and sorted first, so filter
then Enter opened a folder; folders hide while filtering. Folder URLs are
encoded, so a '#' in a name no longer cuts the path, and glob characters
typed in the filter are taken literally. The card says the assistant is
not running in every state, panels sit under the chat header instead of
over it, and History and the picker no longer open together. Dropped
file://localhost/ URLs are accepted, other hosts refused, and the island
receptor keeps the sliver's width at rest so it takes no clicks beside it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A DropArea over the sliver grows into a bubble while a file drag is over it and hands file:// paths to the canvas.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
|
|
The card has three states: an entry line, a file state for staged files with Chat about it, and the chat. SUPER+Home resumes a conversation active in the last 10 minutes and otherwise starts a new one. The paperclip opens a picker drawn in the card, since a system dialog would open beneath the overlay that holds the keyboard. Button gains a font alias for the paperclip glyph, and the canvas a socketPath property so a check can point it at a scratch daemon.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
|
|
Files.js decides what the card offers for a staged file; the daemon decides what chat accepts, so the text and image lists match its own. Dropped URLs become paths only for file:// ones.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
|
|
Four tasks: file kinds, the three card states with staging and the
in-card picker, the island receptacle, and the handover. The card files
ran in a scratch harness against the attachment daemon first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A system file dialog would open under the overlay that holds the keyboard,
so the picker is drawn in the card, over the transcript, from
Qt.labs.folderlistmodel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Three card states: an entry line, a file state offering what to do with
dropped files, and the chat. A drop always starts a new task, and asking
about a file is one of the choices rather than the only one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
All nine visual checks passed by hand.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
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>
|
|
From review of the canvas. Escape stopped working in three ways, each
leaving the keyboard grabbed with nothing to receive it: the history
field hiding took focus with it, a disabled input could not hold focus
while the daemon was down, and a click in a streaming reply focused text
the next flush destroyed. The card now gives focus back to the input
whenever it falls out, and the input is read-only rather than disabled.
Only an error the daemon marks as ending the reply (turn) closes the live
row; a command error, possibly another client's, used to freeze the reply
mid-stream. The running tool comes from state events, hello included.
Scrolling up during a stream no longer snaps back, since following stops
when a movement starts. History snippets go through Notify.sanitize like
any model output, links open only for http, https and mailto, and a line
opening with inline triple-backtick code is no longer taken as a fence.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Inside the desktop process rather than its own shell: it needs no keepalive
window of its own and restarts with the rest of the desktop.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
|
|
The socket, the turn model and the window live in Assistant.qml, outside
the window's loader, so closing the card loses nothing and a reply keeps
streaming while it is closed. Tokens reach the model on a 50 ms tick
rather than per token. Prose goes through Notify.sanitize, because model
output can quote a search result and a remote image in it would be
fetched on render. Code blocks get a copy button once their fence closes.
The history panel lists conversations and searches them through the
daemon's full-text index.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
|
|
Qt's markdown renders a fence but gives no per-block hook, so a copy
button per code block needs the fences found first. An unclosed fence is
open code, which is every fence while a reply streams; a fence inside a
list item keeps its indent out of the copied text.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
|
|
Five tasks: the markdown splitter, the canvas components, hosting them in
the desktop shell, the switch-over (voice overlay out, hypr-theme bind,
autostart and blur), and the user's visual check. Every file was run in a
scratch harness against the real daemon before planning.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A Luma-style overlay on SUPER+Home, inside the desktop process, as a pure
view over desktop-assistant's socket. It replaces the voice overlay in
assistant/ and stands in for llamachat while it is tested.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Shows album art and a scrolling "artist · album · title" while a
player is playing or paused, using the sound module's Player so the
browser proxy duplicates are already filtered. Hovering swaps the text
for a progress bar, swapped in place rather than growing the pill;
MPRIS does not push position, so it is re-read every second while
hovered, and a click seeks only when the player reports canSeek and a
known length. The wheel steps tracks one per notch, as the layout flag
does, so a touchpad does not skip several.
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>
|
|
Page gains go(name), wired to the drawer's show(). Module gains up, the
page back() returns to, empty for the grid as before. The back arrow now
calls back() instead of clearing the page itself, so the arrow, Escape
and the mouse's back button agree. For the git page, opened from the
user page.
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>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A drawer page, reached from the user page, listing every global git key and
editing, adding and removing values through git config itself, so the file
keeps its comments and quoting. --fixed-value lets each write target one
value of a multi-valued key.
Co-Authored-By: Claude Opus 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>
|