| Age | Commit message (Collapse) | Author | Files | Lines |
|
Measured: after shared/Notify.qml changed, a reload triggered by editing
Balloons.qml left the notifications shell on the old singleton, while a
fresh instance read the new config correctly.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A bare TextInput defaults activeFocusOnTab to false, unlike the Controls
TextField some pages use, so Tab skipped every field built from one: the
settings search, Field, the qtmaildir, git, autostart, assistant, OBS,
user, VM and network rows, and the calendar editor. Multi-line TextEdits
stay out of the chain.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Typing replaces a Field's value binding, so Discard left the typed text
showing over the reverted draft, and a rejected timeout kept displaying.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Edits notifyd's config.json as a draft behind the SaveBar and runs
notifyctl reload after the write; a stopped daemon is reported as saved
but not applied, since it reads the file at its next start. Muting offers
the apps already seen in the queue and history.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Screen and corner come from the shared config. Mute and the DND rule go
through NotifyConfig.withheld, so the settings page's node check covers the
same decision the balloon makes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
One resolver for both shells and the settings page, with the defaults that
reproduce today's behaviour, and the mute/DND decision beside it so the
node check covers it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Ten tasks across notifyd (config loader, timeouts, caps, reload) and this
repo (shared resolver with a node check, balloon placement and mute, the
settings module).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
One config file in ~/.config/notifyd read by the daemon (timeouts, caps,
picked up by notifyctl reload) and by both shells (placement, mute, DND).
Snooze stays out: the status tile already covers it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The thumbnail list scrolled a fraction of a thumbnail per wheel notch,
which is ListView's default step and slow with rows this tall. A
MouseArea over the list (acceptedButtons: Qt.NoButton, so clicks and
hovers still reach the thumbnails) now moves one row per notch, and
keeps a touchpad's pixel delta as it is.
An open folder with many images scrolled its header out of view, so
folding it meant scrolling back to the top. A copy of the header is now
pinned over the list while the topmost row belongs to a folder whose own
header has scrolled off; clicking it folds that folder and positions the
view at its header. The header row became an inline component so the
list and the pinned copy draw the same thing.
Loads clean; not yet confirmed by eye.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Hyprland draws no decorations, so the settings window had no way to fill
the monitor and no visible close (only Ctrl+Q and Escape). It now carries
a titlebar row of its own: "Keystone Settings" centred, and maximize and
close at the right. Maximize toggles FloatingWindow.maximized, which
Hyprland honours and undoes.
A corner button floating over the page was tried first and covered the
pages' Save buttons; a row of its own keeps clear of every page header
without each page knowing about it.
The buttons are Rectangles rather than the directory's Button.qml,
because this file imports QtQuick.Controls and that import shadows it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A headphones icon in the status pill, shown while Sony headphones are
connected, opens a panel in the capsule: battery, noise control tiles
(ANC, ambient, off), ambient level, focus on voice, the headphones' own
sink volume, power off and refresh.
Battery and volume need nothing new: BlueZ's Battery1 already reports the
charge, and volume is AVRCP through PipeWire, so the slider drives the
headphones' bluez_output node rather than the default sink. Only the mode
needs Sony's own protocol, which sonyctl speaks over RFCOMM with the
Python standard library. The headphones advertise the v2 MDR service on
channel 18 and answer an 8-byte init reply, which is the v2 shape.
What was measured on a WH-CH720N, and differs from what reading
Gadgetbridge alone suggested: the ambient subtype is 17, while 15 (what
Gadgetbridge sends to models without wind-noise reduction) is acked and
never answered; the set payload is 7 bytes, with no wind byte. Noise
cancelling has no strength, only on or off; a level sent with it is
ignored and the previous ambient level kept. Focus on voice reads back
as set. A connect right after the previous run closed fails with EBUSY,
so sonyctl retries it briefly.
Each call is about 2s, so the panel runs one at a time and sends the
ambient level only once the slider or wheel settles. The mode is re-read
on every open because the headphones' own button changes it too. Power
off was not exercised, to keep the headphones on during development.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Closing a window from the expose now glides the rest into their new
rects instead of jumping. The Repeater's plain array model rebuilt every
card on any change, leaving nothing to animate; ScriptModel diffs by
identity, so only the closed card goes. The Behaviors stay off until the
fade-in settles, or every card would fly in from the corner on open.
The scatter keeps its centre weighting but spaces windows further apart
(GAP 60 -> 200 real px) and may fill 95% of the screen rather than 92%.
Measured with node at 7 windows: about 45px on screen between cards.
The bottom 28px are reserved so the lowest card's name stays on screen.
Each card shows the app's desktop-entry name below its preview, falling
back to the app id, and the app icon moved to a larger overlay in the
preview's bottom-left corner; the hover title starts right of it.
Arrow keys select a card and Enter focuses it. The scatter has no grid,
so Expose.step picks the nearest centre on the arrow's side with
sideways drift counted double, and the first press picks the top-left
card; test_expose.js covers it. The selection is held as the window,
not an index, so a close that shifts the list does not move it, and
hovering selects too, so pointer and keys never show two highlights.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
qtmaildir now takes --account, --thread and --message, and hands them to
a running window over its socket, so the drawer and the notifications can
open the exact thing they show instead of just launching the client.
mail-open.sh is the one place that turns an account and a thread into
selectors, shared by both callers. It looks up the thread's newest unread
message at click time rather than when the row or notification was built:
by then it may have been read, and an empty lookup falls back to the whole
thread instead of a miss in qtmaildir's status bar.
The notification never had a working click: dunstify is not installed, so
mail-notify.sh always took its notify-send fallback, which carried no
action. It now uses notify-send -A NAME=Text, one action per listed thread
(keyed by thread id, which is hex and so never holds the "=" separator)
plus the account as default. The round trip through notifyd was checked:
notifyctl action makes notify-send print the picked name.
On the page, the account header opens the account and a thread row its
newest unread message. Rows use TapHandler and HoverHandler because a
MouseArea cannot fill a Column. Launches are detached: a first launch is
the client itself, and the old reused Process would have killed it on the
next click. Opening anything now closes the drawer rather than going back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The page shows a summary; btop is the full picture when the summary is
not enough. The button sits in the header's actions slot, so it stays
put while the panel scrolls, and closes the drawer on launch since the
terminal takes over from there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The repository had no name of its own, only the framework's. It is now
Keystone, in the README, AGENTS.md, the settings window, notes commit
messages, calctl's PRODID and the user page's User-Agent.
Its files move off quickshell's name too:
~/.config/quickshell/greetings -> ~/.config/keystone/
~/.config/quickshell/obs-scene-icons.json -> ~/.config/keystone/
$XDG_STATE_HOME/quickshell/desktop.json -> $XDG_STATE_HOME/keystone/
~/.config/quickshell is Quickshell's own default config directory (a
bare qs loads its shell.qml), so sharing it was a collision rather than
a home. There is no fallback to the old paths: the files are moved once.
The layer namespaces become keystone-*, and the Hyprland blur rules in
hypr-theme are renamed to match in the same change. A running shell
keeps its old namespaces until restarted.
Left alone: the retired standalone mail and vm shells' namespaces,
which their READMEs keep as history; Quickshell's own app id, binary
and process name; and the repository directory.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The landing page was a paragraph of About text. It now carries the
project's name, Keystone, a short About, and a system card: the
Slackware facts of conky-theme-udt's card (version, package count,
kernel, install age, and the slackpkg ChangeLog age, green under a day,
yellow under a week, red past it), the machine, and base library and
development tool versions, in two columns. The page scrolls once it
outgrows the window.
Everything comes from one sh process per open of the window, so there
is no cache file and no dependency on conky's sampler. Library and tool
versions are read from the installed package names in
/var/log/packages: the version must start with a digit, so gcc does not
also match gcc-g++, and stops at an underscore, which drops qt6's
snapshot suffix. A package that is not installed shows a dash.
The shortcut walkthrough left the About text: the drawer, island and
assistant are described in their own READMEs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A Wayland window capture is chosen in the portal picker, which only OBS's
source dialog can open. In a scene whose only source is a PipeWire capture,
the panel gets a Window button that opens that dialog. A window scene with
more than one source needs a per-scene designation the pill does not have yet.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
At rest the pill is a record dot with the elapsed time; a click morphs it
into a panel, as the status pill does for sound, with Record/Stop,
Pause/Resume, Show/Hide webcam and the scene list. The user preferred it
to every control inline: the pill stays small, and scenes are picked by
name rather than stepped through, so obsStep is gone. OBS quitting with
the panel open clears it, or the island would stay held open.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Six tasks: logic helpers with their check, the openssl auth helper, the
Obs singleton, the pill, island wiring with the sliver pulse, docs.
Records two deviations from the spec found while planning: QML's
WebSocket hides the close code, so a wrong password is logged as
'closed before Identified'; responses route on the echoed requestType.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A pill on the island that exists while OBS runs: record, pause, scene,
webcam visibility, and a pulsing sliver while recording. Talks to
obs-websocket directly from QML (QtWebSockets is installed), reading the
password from OBS's own config so no copy exists. Auth stays on because
obs-websocket listens on every interface.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Per-core load, the network rate, VRAM and the fan used to run only while
a PerfPanel existed, and closing the page cleared them. Opening the
drawer therefore always showed a fresh first sample and an empty network
graph, which made the page not very useful as a glance at what just
happened. Everything now samples for the whole session, so the graph
opens on the last minute of history.
The cost is the VRAM fdinfo scan, ~14ms every 2s, now unconditional; a
ponytail note beside it says to gate it on the panel again if that shows
up. The LAN address and the public IP check run once at startup instead
of on each page open.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The Mail, Assistant and Ask presets pages got a Save and Discard pair in
the page header; the rest still had their own buttons wherever their
page put them: User's Save at the bottom, Wallpaper's Apply and Reset
beside the mock screens, Icons' Apply and Reset under the cursor cards,
HyprSunset's and HyprIdle's Save + Restart in the page. They now all sit
in the header SaveBar, which takes the page's own verbs (Apply and
Reset, Save + Restart) and a busy state.
HyprSunset and HyprIdle had no notion of an unsaved change, so their
button was always live. They now count as changed when what save()
would write differs from what was read, which enables Save only then,
gives Discard (a reload) something to drop, and marks them in the
sidebar; a window open no longer reloads them over unsaved edits. The
staged cursor moved from IconsTab into the Icons singleton so the
header and the sidebar can see it.
Git and autostart act a row at a time, and a theme applies on click, so
those have no bar.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The qtmaildir, Assistant and Ask presets pages wrote a field on Enter,
and a switch or chip the moment it was clicked, so a half-finished
change reached the file and a slip could not be taken back. They now
edit a draft as fields are typed, and a Save and Discard pair in the
page header (SaveBar, through Page.actions) writes or drops it. Save
waits while a field holds a value that cannot be written, and a draft
over a file that changed on disk meanwhile is refused at Save rather
than written over it. A dot in the sidebar marks a page with unsaved
edits; drafts survive moving between pages. The four Mail pages share
one draft, since they are one file. Tool switches are staged too and
renamed on Save; model Load, Prefer and Restart stay immediate, being
actions rather than settings.
Live editing needed two rules. A field is no longer bound to its value,
which would reset the cursor and eat a typed trailing space; it follows
the value only when the draft moves under it. And a Repeater's model is
now the set of row keys (or a count), assigned only when that set
changes: an array rebuilt from the draft on each keystroke recreates
every row, including the one being typed in, which loses its focus.
Git, autostart's rows, HyprSunset, HyprIdle and User keep their own
buttons.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
QtQuick.Dialogs' ColorDialog opened light on the dark desktop. It paints
from the Qt Quick palette, which qt6ct leaves at Qt's default here: its
custom_palette is off and the dark comes from the kvantum-dark widget
style, which Qt Quick does not use. Qt.labs.platform's ColorDialog falls
back to the widget QColorDialog in a QApplication, which this process
already is for the tray menus, and widgets take the kvantum style like
every other application.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
An account's chip colour and each tag colour were #rrggbb typed by
hand. A swatch beside each now shows the colour and opens Qt's own
ColorDialog, which writes the choice back as #rrggbb; typing still
works. The add-tag row has one too, filling its field. Setting.qml
gains a colour flag rather than a second component, since a colour
key is an ordinary text key with a swatch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
qtmaildir is configured through qtmaildir.conf alone, which it reads at
startup and never writes. Four settings pages under a new Mail group
edit it: Mail ([general], [sync], [completion]), Compose, Accounts (a
card per account, add and remove) and Tags & shortcuts ([tagcolors] and
[keys]). Fields save on Enter and unset when emptied, with qtmaildir's
defaults as placeholders and numbers range-checked; the pages say when
qtmaildir is running and a change waits for its restart.
Ini.js edits the file line by line so its comments survive. Headers are
only whole [name] lines: a section is never cut at the next "[", since
values such as [Gmail]/Bozze hold brackets, the trap AGENTS.md records.
The shortcut actions are copied from qtmaildir's KeyMap::knownActions()
rather than exported by it, since a CLI flag there would have meant
translations and a changelog entry; the field also takes free text, and
the copy is marked as a ponytail ceiling.
The settings sidebar now clips, which a list long enough to scroll
showed it did not: entries drew over the search box.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Load set the preference as well as swapping the model in, so loading a
model from the settings page for any reason made it the assistant's
model too: after loading Qwen there, the canvas kept Qwen instead of
bringing the preferred Gemma back. Load and Prefer are now separate
buttons: Load swaps a model in now and leaves the preference alone, and
Prefer only records which model the canvas loads when it opens.
The canvas header also showed a stale model name after a swap; it now
takes the model from the daemon's ctx event, which carries it from
desktop-assistant f9d4644 on.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A preferred model that only took effect from the settings page did
nothing the next time another client had swapped it out. On this
desktop one model and one client run at a time, so opening the canvas
is when the assistant's model should come back: if the preferred one is
neither loaded nor loading, it is loaded, and the canvas header says so
until the router reports it in. The daemon still never asks for a model;
with no preference set nothing changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The daemon answers with whichever model llama-server has loaded and
never asks for one, so it never evicts a model another client is using.
The Assistant settings page now lists the router's models with their
state. Load saves one as the preferred model, in Prefs, and asks the
router to load it now, polling /v1/models until it reports loaded;
Prefer marks the loaded model as preferred without swapping. The swap
happens only on that click, so the daemon's rule holds.
AGENTS.md records the router's load endpoint and its two refusals, both
measured: it lives on the server root, not under /v1.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Ten pages in one flat list had stopped reading as a list. A module now
names its group, and the sidebar sorts by it and draws a small heading
where it changes: Account (user, git), Appearance (wallpaper, theme,
icons), Desktop (HyprSunset, HyprIdle, autostart) and Assistant (the
daemon, Ask presets). Groups keep the order their first page is
declared in, so shell.qml's module order still decides.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|