| Age | Commit message (Collapse) | Author | Files | Lines |
|
A settings page under Appearance > Fonts holds one pixel size for the
whole desktop and an override wherever a system needs its own, saved as
~/.config/keystone/fonts.json, all in px. At scale 1 the logical DPI is
96, so a point is 4/3 px, and each system is written in its own unit:
Theme.fontSize reads the json itself, so every shell follows live;
qt5ct/qt6ct and the GTK settings files are edited in place, size only,
plus gsettings; kitty, rofi and hyprlock read generated files under
~/.config/keystone that their configs include over designed fallbacks;
the groupbar is a keystone.lua line.
rofi and hyprlock are sections of designed sizes that scale together
from their body text, the smallest entry, each keeping its ratio unless
given its own size. The design is read from the fallbacks themselves,
var(kf-..., "Family N") in the rofi files and $kf_... = N in
hyprlock.conf, so a new entry appears on the page with no list to keep.
With no fonts.json the page starts from what the files say, base 16 and
an override per system at its current size, so the first save changes
nothing; seeded to 0.01px, since 0.1 scaled rofi's 72pt glyph to 72.2.
Each stepper shows px then pt in fixed columns, the system's own unit
highlighted.
Check: node desktop/modules/fonts/test_fonts.js
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
All three edit Hyprland's own config or its companion daemons, so they
sit with the shortcuts and options pages rather than under Desktop.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Keyboard shortcuts lists every bind hyprctl reports, joined to the
hl.bind call in ~/.config/hypr that made it: hyprctl gives every Lua
action as an opaque __lua number, so the action and file:line only
exist in the source. The join is by description, then combo, then the
longest literal head of a description built in a loop. List, Source
and Clashes tabs. A combo the source writes as a plain string (or from
file-level string locals) can be changed: hyprctl eval of a do-nothing
bind checks it first, refusing an unknown modifier or keysym, then the
call's first argument is rewritten and Hyprland autoreloads.
Options lists all 353 options from hyprctl descriptions, with values
from one batched getoption, since descriptions' own "current" field is
stale. Filters for everything, set in the config, and changed from the
default. A change is one line in hypr-theme's sections/keystone.lua,
required last, and is run through hyprctl eval first, which refuses an
unknown key or a wrongly shaped value without changing anything.
Both modules keep their own copy of the file text: FileView.text()
returns the old text right after setText(), and a second quick edit
built from it dropped the first. The AGENTS.md notes record that and
the hyprctl quirks found on the way.
Check: node desktop/modules/hyprland/test_hypr.js
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Settings > Desktop > Shells lists every directory holding a shell.qml,
running or stopped by `qs list --all`, with Restart (Start when it is not
running) per row and a Restart all. Every button runs reload.sh through
Quickshell.execDetached, which leaves the script outside the desktop
shell's process, so restarting desktop from its own settings window
works: the window closes and the new process carries on.
restart.sh wraps reload.sh and sends one notification naming the shells
that came back, or a critical one naming those that did not. Back means
qs lists an instance on the shell's config path under a pid it did not
have before, within 5s; the old instance can linger in the list for a
moment after qs kill. It checks the process, not the config, so a shell
that starts with a broken component still counts as back.
The rows reuse the ai module's AiRow rather than a copy of it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A process holding thousands of DRM fds makes the fdinfo scan a few
hundred KB. Parsing that in QML every 2s drove Qt's unmanaged-heap
counter to underflow, after which the V4 engine ran a GC on every
allocation and the shell sat at 100% CPU.
vram.sh does the scan and the per-client-id sum in awk and prints one
number, or nothing when no xe client is found, which the panel draws as
a dash. Perf.js loses vram() and its test; test_vram.sh carries the same
cases against the script, which takes a file argument instead of
scanning so the test can feed it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The Load button reads Unload on the loaded model and posts to the
router's /models/unload. An error from the router lands in modelError,
the same place a failed load reports, and the list is re-read either way.
The model comes back on the next request for it or when the canvas opens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The llama.cpp row offers Unload while a model is loaded. It posts to the
router's /models/unload, which frees the VRAM and leaves llama-server up,
so its web UI stays available; the engines themselves are still never
stopped from here. The answer is not read: the next poll shows the row
without a model, or with it if the unload failed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The list fills about 2s after the page opens and can show fewer than five
rows, which resized the CPU card and shifted every card below it. A
FontMetrics-sized Column keeps the height fixed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The rx/tx graph, the LAN address and the public IP with its pin move from
the perf page's NET card to the network page, where they belong; the NET
card is gone. Perf still samples and owns all of them: the graph opens on
the last minute of history, and a public IP that differs from the pin
still lights the perf tile's dot.
Below the graph, the five busiest processes. nethogs is installed but
cannot open any interface unprivileged, so the rates come from
`ss -tinpH`: per-socket byte counters diffed every 2s while the page is
open, summed by process name. That counts TCP only, and only the user's
own processes, so QUIC and system services are missing. The list is held
at five rows tall so the IP lines under it never move.
Perf.js gains sockets() and netProcs(), with tests in test_perf.js.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Seed 42 drew a third hand, the new one plus both still on the bench, on
every dress frame among sit-think, sit-listen and sit-wave, and on
leotard/sit-think. The comments name the seeds that replaced it so a
remake does not have to find them again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Naming a ledge in SIT drew a grey bar across the whole image, so SIT now
names nothing under her. Its "keep her sitting pose exactly" also froze
her arms, so the posed seated frames do without it: sit-point came out as
the plain sit with a smirk, and at seed 42 kept her old hand on her hip
too (seed 7 kept). dress/sit-read is hand made, a masked inpaint recorded
in the art's recipe.txt, since no edit would bend her forward, and
leotard/sit-read names the lowered gaze that ${LEOTARD} turned to the
viewer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Both images faded at once, so mid-swap each sat near half opacity and she
blinked see-through on every turn of the dj loop. The incoming frame now
fades in on top while the outgoing one stays opaque under it. With the
blink gone the loop could run faster: 300 ms a frame, from 450.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Records that Hyprland's typing binds fire under the canvas's exclusive
keyboard, which is why the canvas reports no typing of its own, and that
the seat fixes her right edge rather than her centre.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Edited from base-sitting, the original sitting rest pose dress/base came
from, so the legs already dangle and klein only changes the arms and
expression. The leotard set follows its dress frames as before.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
|
|
hopping counts as away, so a goal change during the exit (the chat card
closing mid-drop) moved the window before the drop had played. settled
already takes the latest goal.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
While the canvas shows its tall card she hops onto its top edge, through
the monitor hop: the exit plays, the window's margins move, the entrance
plays. The tall card's top is fixed, so nothing follows a moving edge.
Seated she ignores focus changes and balloons, and shows Mood.seatFrame
of whatever she would show standing.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The companion needs both to sit on the chat card, and should not reach
into the card for them. Card.tall now reads the canvas's, so there is one
definition of when the card is tall.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
Pure, so the seat's frame choice is tested with the rest of Mood rather
than by eye. Frames without a sitting version fall to the plain sit: a
standing frame on the card's edge would float above it.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
|
|
A table under RAM lists the top five processes with their CPU and
resident memory, sorted by either through a toggle in the card. The
toggle re-sorts the frame already held and is not saved.
The rows come from one `top -b -d2` that runs only while the page is
open and dies with it, checked with pgrep. ps was not used: its %CPU is
a lifetime average. top's first frame is the same lifetime figure, so it
is skipped. top marks no frame end but writes each frame in one burst, so
a frame is published once its output has been quiet for 150ms, and rows
appear about 2s after opening, not 4s as when waiting for the next
header.
SplitParser delivered every header after the first as "\ntop - ...", so
the header match is a regex that allows leading whitespace; AGENTS.md
records it. The parser is Perf.js procs(), covered in test_perf.js.
Columns are right-aligned at fixed widths with tabular figures.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A tray menu un-hovers the pills, so the island holds itself shown while
one is open. display() reports no close, so the hold ended only when
the pointer came back to the island. Quitting an app from its menu
left the pointer on the vanished menu, below the pills, and the island
stayed open until the next hover.
Menus now open through a QsMenuAnchor. Its closed signal fires on any
dismissal, an entry picked, Escape or a click elsewhere, and clears the
hold and restarts the hide timer. Checked by eye: menu placement
unchanged, Quit and a dismissal both retract the island, hovering back
keeps it shown.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
qs ipc has a show subcommand, and its parser takes the word wherever it
appears: `qs -p <dir> ipc call companion show` printed the target list
and exited 0 instead of calling the function. The companion, window
switcher and keybind reminder each had a show() no command line could
call. The binds use toggle and are unaffected. open() matches the
drawer's own.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A shell started with Prefs.companion false created the Figure with out
already true. A starting value fires no onOutChanged, so the drop never
ran and she was drawn in place. Hidden, her 200 ms clock does not tick,
so every timed step froze: an empty desktop left her on the watch frame
of the wallpaper blush, and a balloon left her on shoved, never mad.
Toggling her on made the bug look fixed until the next restart.
The body is now invisible while out and not animating, and the first
entrance after such a start begins from below like any other.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The stronger down prompt tucks her head with the hair hanging over her
face, which reads as the downbeat; the up frame keeps the earlier prompt,
since spelling out the horns still drew a peace sign. ${LEOTARD} insists
her eyes show, which parted the hair even with an override appended, so
the leotard line only swaps the skirt, as leotard/sing does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Whistle had the same censor-bar blindfold as dj and takes the same fix.
headbang-down edited from headbang-up came out nearly identical, so both
are made from base with opposite poses. klein will not draw an invisible
guitar, so the air guitar is a neon blue electric.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
With headphones, the blindfold came out as a flat censor bar across her
face, the same failure sing had, so dj takes sing's fix: blindfold off,
eyes closed. "Hiding her legs" drew a table on legs with her boots in
view; a solid front panel to the ground hides them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Twelve frames, dress and leotard. Prompts are a first draft for klein; the
user generates them, and until then each falls back to base.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Reads the sound module's Player, which already filters the MPRIS
duplicates. The track key is artist+title while playing, so a pause and
resume of the same track does not DJ again; a shell start with music on
DJs once, which is harmless. Frame loops are a small table keyed by the
frame Mood names, so dj and headbang need no special case in Mood.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
PhrasesPage takes its intro, key hint, add-button text and saved set, so
the artist set reuses it rather than a copy that would drift. Reset's
enabled state read Prefs.companionApps directly and would have tracked the
wrong set. ArtistsModule mirrors PhrasesModule on Prefs.companionArtists
and Lines.artists.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Artist keys match as a case-insensitive substring of the MPRIS artist, so
collaborations and feat. credits still hit; the cost is a short key
over-matching, marked as a ponytail ceiling. appMap/appList take their
defaults so the settings page can serve artists with the same helpers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A DJ burst ranks just below the headphones sing, so both arriving together
sings first. Music turns only the first idle stage into a groove: yawning
and lying down still win, so music does not keep her awake and the
screensaver handoff is unchanged. The groove pick is an input, keeping
pick() pure.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
She now reacts to the headphones connecting with a short singing pose:
eyes shut, blindfold gone, mouth wide open, headphones on, a few musical
notes. The frames are new klein edits in frames.txt (dress/sing from
dress/base, leotard/sing from dress/sing), so a post-self-destruct sing
has its own art.
Detection is the island's rule (StatusPill.qml): by model prefix, since
BlueZ gives no class that says "headphones". The device's BlueZ Name is
WH-CH720N, which the alias does not affect. The reaction is a one-shot
hold in Mood.js (singHold, 4 s), placed after wave like the other event
reactions. A 4 s startup grace keeps a shell start with the headphones
already on from counting as a connection, since Bluetooth reads empty
for about 2 s.
|
|
SUPER+\ now plays a short goodbye instead of dropping her at once: she cycles wave-l and wave-r around the existing wave for a sway, then the normal drop. One hide in four she also says a bye line, non-forced so the usual 30 s cooldown applies; the hold matches what showed, 2 s with a line and 1 s silent.
The sway is klein edits of dress/wave rather than base, so the arm stays up and only the hand moves; the leotard twins come from their dress frames. The leotard rerolled to seed 7 because the first seed left a faint alpha fringe that widened the canvas and made the flip jitter.
Fullscreen, the lock screen and the monitor hop keep their own exits and do not wave.
Also carries the pending onFocusedChanged guard: with no focused monitor, or already away, she moves at once rather than waiting for an exit that will never settle.
|
|
Her per-app lines were hardcoded in Lines.js under app:<class>. They now live in Prefs.companionApps once edited and fall back to Lines.js until then, read live by Companion.qml so a save needs no restart. The new Companion phrases page does full CRUD on apps and their phrases, with a draft, SaveBar and Reset to the built-in set, mirroring Ask presets. Companion settings move out of Desktop into their own Companion group.
|
|
The Prefer rows for cloud models never built: the shell's JS engine has
no Array.prototype.flatMap, and the Repeater's model threw a TypeError
(seen in qs log). The ids are now joined with reduce in the module.
Each tool row gains a cloud switch, applied on Save after the renames:
it writes or drops a top-level cloud = true, validated with tomllib and
replaced atomically, on name.toml or name.toml.off.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The canvas asks for its chat's model, else the preferred one, when it
opens and when the pick changes; a cloud id loads nothing. The paperclip
also closes the model panel, so it and the picker never stack. The plan's
Models.qml import is corrected to the one that resolves.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The canvas keeps the chat's model from the daemon (hello.chat_model,
conversation.model), falls back to the preferred one (set from shell.qml
later), and sends it as model with every ask, typed or spoken. The To .md
write stays without one: the daemon always writes with the local model.
Models.qml lists the daemon's models event, router models then cloud
ones, in a panel like History. It opens from the header label in chat,
from a Model button next to History outside chat, and with Ctrl+M. The
models are asked for on hello too, so the cloud marker is right before
the panel was ever opened. Moving a chat with turns to a cloud model
says once that its history goes to that provider.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
Settings > Desktop > Companion takes the seconds she stays in the
leotard after the self-destruct, 60 as before. Switched off, she keeps
it until she is hidden and shown again, which already put the dress
back early. Mood.outfit takes the time instead of reading T.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
A 1.5 px shiver side to side, 160 ms a cycle, for the blush3 half of
admire only; a click burst's blush3 stays still. Put back to rest on
stop, since an infinite animation stopped mid-swing keeps its offset.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
At the label's size it was hard to spot. An outline in the base colour
keeps it readable on a light image.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The trigger fires whenever she stops being away, which is a dodge and
fullscreen as well as the three appearances the spec named.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Shown on hover, and always on a listed one. It toggles the path in the
list file and leaves the rest of the file as it was.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Empty means the default under ~/.config/keystone, shown as the
placeholder.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Checked on switching workspace or closing a window, and whenever she
stops being away, which covers being shown, a remap, a hop to another
monitor, a dodge and fullscreen. The 3 minute cooldown keeps workspace
flicking from repeating it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The list is a plain text file whose path is a preference, the default
under ~/.config/keystone. Written through cat because an emptied list
is an empty file, which FileView.setText does not write.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|