Joplin (Wayland-native Electron) reasserts its own window right after it
maps, escaping the map-time `silent` workspace rule, so it opened on the
focused workspace instead of its app-home (ws 4 / HDMI-A-2).
Add a data-driven `reassert` placement flag: rules.lua emits a window.open
handler that re-applies the assignment by address (follow = false, silent)
for flagged apps. Only Joplin opts in; the static rule stays as a fallback
and the other app-homes are untouched. Document the flag in CLAUDE.md and
AGENTS.md.
Kill pure-black crust (->Void #070709) and add the figure/ground tokens
focusActive/focusField/emberField. Move active/selection states to violet
(calendar Today, keybinds active tab, battery + power profiles); stop spending
danger-red on non-danger states (mic + audio-default -> cyan); make the notif
bell state-driven (neutral/focus/danger); give critical notifications an ember
ground; bluetooth scanning -> cyan; k8s MEM -> blue (free the special violet).
Red is reserved for danger, violet for attention. Active window border now
violet (focus->iris) with a violet glow; urgent windows get a red border via a
window.urgent handler (cleared on focus). Inactive windows wear a dim-cyan HUD
border + faint cyan glow. Groups are structural blue; group-locked is amber.
Groupbar active tab is an info gradient (blue frame + cyan interior), keeping
violet scarce to the window border. Mirrors the regenerated apex dist conf.
Mirror the canonical apex v2 dist palette (apex-{neon,aeon}-colors.conf):
v2 neutrals (#070709 ground), rose #ff8899 -> #ff3b3b, dark-text group
bar on red->violet. Update hyprlock bubble + the shared lock/greeter
fragment ground to #070709 and the fail gradient off peach -> rose.
Migrate the shell palette to the v2 semantic spine and clean up
spec violations:
- Theme.qml: v2 neutrals (#070709 ground), split real blue (#5b8cff)
from info cyan, retire the peach token, add a focus attention violet.
- Weather: severity-driven coloring (cyan/amber/red) from weatherCode;
replaces decorative peach. Forecast highs=text, lows=subtext0.
- SystemPopout: performance profile + Reboot hold -> amber (peach retired).
- Workspaces: active indicator red -> focus violet (selection is
attention, not danger).
- SystemPill: Arch logo lavender -> real blue.
- Bar.qml: gate BluetoothPill instantiation behind hasBluetooth so
BlueZ DBus never spins up when the bluetooth tag is off.
- IdleScreen: route hardcoded hex through Theme tokens.
Bar pill shows charge % and a level/charging-tinted icon from UPower; popout
adds time-to-full/empty, battery health, charge rate, and power-profile
switching. Gated on Config.hasBattery / hasPowerProfiles, so it only mounts on
laptops.
kitty auto-loads dark/light/no-preference *-theme.auto.conf based on the
OS color scheme, which overrode the v2 `include themes/apex-neon.conf`.
8db3831 dropped the file from source but left it on already-deployed
machines. Add .chezmoiremove so apply purges it everywhere.
Bluetooth singleton has no devicesChanged signal, so the Connections
handlers never fired (QML warning on every reload). The pill now recounts
on Instantiator delegate create/destroy (covers pair/forget) in addition
to per-device connected toggles; the popout already re-sorts via its
open-only Timer, so the dead Connections is simply removed.
Gated on a new Config.hasBluetooth (bluetooth tag). Adds a bar pill
showing adapter state + connected count, and a full manager popout:
adapter power toggle, scan/discovery, and per-device pair / connect /
disconnect / trust / forget with battery. Built on Quickshell.Bluetooth
(BlueZ); mirrors the existing Kubernetes/System pill+popout pattern.
Top-level project overview (quick start, file-naming, the chezmoi data
model, layout, theming). Ignored via .chezmoiignore so it never applies
to $HOME.
Collapse redundant template tags into a cleaner data model:
- drop the desktop tag (use "not laptop"); replace hyprland/niri with
compositor = "hyprland"|"niri"; replace cs2/entertainment with an
apps list (gated via has); drop the dead waybar tag.
- move app->workspace->monitor routing into a portable [[data.placement]]
table keyed by monitor role (left/right/primary), resolved per machine
with fallback to the primary monitor. workspaces.lua.tmpl and
rules.lua.tmpl now generate the workspace/window rules from it, so
single-monitor machines work with no hardcoded monitor names.
Update CLAUDE.md / AGENTS.md / GEMINI.md schema references to match.
Bind workspaces 1/4/5 to HDMI-A-2 so Thunderbird, Joplin and Steam open on
a fixed monitor instead of the focused one, and append " silent" to every
app workspace rule so autostarting apps no longer steal focus or flip the
visible workspace at login. Also fix the Joplin class regex
(@joplin/app-desktop -> joplin-app-desktop) which never matched.
kitty.conf now includes themes/apex-neon.conf from the config root (a
../ include from kitty.d/ does not resolve under globinclude). Removed
dark-theme.auto.conf: kitty auto-loaded it by system appearance, and it
still carried the v1 palette (red cursor), overriding the apex theme.
refresh-apex-themes no longer emits gemini (deprecated for agy), hypr
conf themes (superseded by the hyprland.d.lua theme module), or the
sherlock/swaync/waybar/wezterm artifacts. Remove the orphaned outputs.
Drop the invalid -f flag from lock_cmd (a swaylock holdover; hyprlock
v0.9.5 has no fork flag and the pgrep guard already prevents duplicates),
and migrate all dpms calls to the Lua dispatch form required since
Hyprland 0.55: hyprctl dispatch 'hl.dsp.dpms({ action = ... })'.
Fix the launcher bind, which used pre-2.2 syntax (owlry -p
app,cmd,system,ssh) where -p is now --prompt, so the whole string was
passed as prompt text. Use bare 'owlry' for Super+Space and add Super+D
for the new dev profile (owlry --profile dev).
Replace the deprecated config.toml with an explicit, self-documenting
owlry.lua. Drop config.toml (ignored once owlry.lua exists; removed in
owlry 3.0). Disable the ssh provider (SSH is done from a terminal) and
keep filesearch out of the global set, enabling it only in a new dev
profile scoped to ~/Dev. Bump frecency_weight to 0.5, set tabs to
app/clipboard/emoji. Modernize the dmenu scripts to 'owlry dmenu'.
While the popout is open, poll both status and metrics at a fast cadence
(kubeRefreshOpenMs, 3s) instead of 2min/15s, and refresh immediately when
it opens — so opening the pill shows fresh data and updates live. Status
reverts to the slow 2min pill cadence once closed.
The sliding active indicator positioned itself via Repeater.itemAt(), which
returns null mid-rebuild; the fallback then stranded it at the top (looked
like workspace 1 was active while the font correctly showed another). Cells
are uniform, so compute the indicator's y arithmetically from the index
instead. Also make monitorWsData's sort deterministic: numbered workspaces
ascending, then named ones (e.g. joplin) alphabetically — parseInt alone
yields NaN and an unstable order for named workspaces.
no_hardware_cursors auto (2) still corrupts the cursor on the rotated
vertical monitor, and Hyprland has no per-monitor override, so force
software cursors (1) globally.
hl.animation rejects a spring leaf without a speed (Hyprland logged
"missing required field speed"), so windowsIn fell back to default. Add
speed so the snappy spring actually applies.
GamemodePill is instantiated per screen Variant, so it spawned one
long-lived gdbus monitor per monitor (3 on desktop). Move the watcher into
a shared Gamemode singleton so only one runs regardless of screen count.
swaylock broke and the stack already locks with hyprlock everywhere else;
point the desktop lockCommand and power menu at hyprlock and drop the
now-identical laptop/desktop branches.
The metrics poller spawned the heavy k8s-metrics script every 15s all day
even though its data only renders in the popout. Gate it on the popout
being open (triggeredOnStart fetches immediately on open) and slow the
pill's status poll from 30s to 120s.
no_hardware_cursors 2 (auto) uses hardware cursors where supported and
falls back to software only when needed, instead of forcing software
cursors globally.
These scratch HTML mockups and .server runtime files were tracked in the
source tree and would be applied into ~/.config. Remove them and ignore
the directory.
The pill and popout each picked an active MPRIS player independently, so
with multiple players open they disagreed and flipped. Add a Media
singleton that selects one active player with stickiness and bind both
components to it.
Feed the three kubectl outputs into one jq program that does app
aggregation, CPU/memory unit normalization, and quota math, replacing the
per-pod jq fork loops and eight quota jq reads (~40 forks down to ~4).
Replace the per-tick sh -c subprocess reads in the system popout with
Quickshell FileView reads (/proc/stat, /proc/meminfo, and once-resolved
hwmon/battery sysfs paths), dropping the 5s tick from ~7 process spawns to
~2. Wrap checkupdates in timeout 120 and guard against restarting an
in-flight run so a slow sync can no longer thrash or stall.
Lua config (hyprland.d.lua):
- keybinds: layout-aware binds now read the per-workspace layout via
cur_ws_layout() instead of the global hl.get_config("general.layout"),
fixing mouse-wheel/bracket scrolling and ratio keys on the lua:*-scroll
layouts.
- add state.lua shared module (ws_layouts) replacing the _G globals.
- layout: factor the 9 duplicated layout_msg bodies into scroll_msg/swap_msg
builders; drop a dead #state expression.
- rules: NO_BLUELIGHT window.open handler no longer leaks a rule per open
(one per class) and regex-escapes/nil-guards the class.
- monitors: quote non-numeric scale so scale="auto" renders.
- drop debug print() focus handler, local-next shadowing, stray {mouse=true}
on wheel binds (kept on drag/resize, which require it).
Quickshell:
- brightness OSD is now event-driven: Osd.qml gains an IpcHandler(target:osd)
and the keybind pushes the new level via `qs ipc call osd brightness`,
removing the always-on 500ms brightnessctl poll.
- GamemodePill watches GameMode's D-Bus signals via gdbus monitor instead of
polling gamemoded --status every 5s.
Cleanup:
- remove stock hyprland.lua.refactor/ boilerplate and the redundant,
partly-wrong hyprland_lua_api.md (both were deployed into ~/.config/hypr;
.luarc.json already points the LSP at /usr/share/hypr/stubs).
- refresh hypr/AGENTS.md (lua layout) and quickshell/CLAUDE.md (v0.3.0).
Replace nwg-hello with hyprlogin as the greetd greeter. Both hyprlock
and hyprlogin now share a single apex-neon theme fragment
(.chezmoitemplates/apex-neon-lock.conf), giving an identical look on
lock and login.
- Add system/ staging tree for /etc files (not auto-applied by chezmoi)
- Add system/install-greeter.sh to render templates and sudo-install to /etc
- Add apex-neon shared fragment: palette, font, animations, input-field,
clock/date bubbles, weather/location bubbles
- Add dot_config/hypr/themes/apex-neon-lock.conf.tmpl ($HOME copy for hyprlock)
- Rewrite hyprlock.conf.tmpl to source the shared fragment; move
notification bubble here (hyprlock-only, requires quickshell)
- Add Quickshell IpcHandler so `qs ipc call notifications count` works
- Session fixed to hyprland-uwsm.desktop; greetd config targets greetd.conf
Alt+. now cycles through all whitespace-split tokens from history
(most recent first, last arg first within each command) using env
var state. Alt+, opens a skim fuzzy picker over full history commands
and inserts the selected entry at the cursor.