Commit Graph
13 Commits
Author SHA1 Message Date
Gabriel Brown 669a228286 Install the desktop before anything allowed to fail, and let Terra be installed twice
Two bugs, one story: ./install on a machine that had been half set up before
produced no Hyprland at all, and said so in one line among twenty minutes of
scrollback.

Terra bootstraps itself with --repofrompath, which defines a throwaway repo id
just long enough to install terra-release. Run it again on a machine that
already has terra-release and dnf5 refuses the whole transaction -- the
throwaway id collides with the real one. That step sits above everything, so
set -e ended the stage before a single package was considered. It is skipped
now when terra-release is already installed.

The rest is the reason one failed repo cost the desktop. Hyprland was installed
near the bottom of the stage, below a codec swap, two group updates and a
GStreamer glob, any one of which can fail for reasons outside this repository.
It now installs directly after the packages it needs and before anything
optional, and everything fragile below it runs through a soft helper that logs
and continues rather than ending the run. What was stepped over is listed at
the end, because tolerating a failure only beats aborting on it if somebody is
told.

A missing Hyprland is still fatal, and now says so in words.

Also removes the leftover disabled solopasha/hyprland COPR, which would mix
with lionheartp's the moment anyone enabled it while debugging.

Fixes the usage widget reading 1500%: the endpoint reports percentages, not
0..1 fractions. Clamped as well, and the widget answers a click now -- it set
interactive:false, which disables the mouse area its own handler needed.
2026-08-22 09:36:54 -04:00
Gabriel Brown 8b96d907a1 Show how much of the subscription is gone, without risking the session
The last of Section F, and the only thing in Panama that reads an
authentication token, so most of the design is about that rather than
about the number.

It never refreshes the token and never writes to the credentials file.
That token expires roughly hourly and Claude Code refreshes it on
demand; if this refreshed it too, two processes would be rotating one
credential, and a rotation invalidates the other holder's copy. The
failure mode is being silently signed out of Claude Code by a status
widget, which no bar indicator is worth. So it reads the token, uses it
while valid, and reports "waiting for Claude Code to refresh" when not
-- which covers the case that matters, because while you are using
Claude Code the token is fresh, and while you are not there is nothing
to watch.

The token never reaches argv either: curl takes the Authorization
header on stdin through --config, because a header passed as an
argument sits in /proc/<pid>/cmdline for the length of the request.
Same rule the password and MOK paths already follow. And it never
reaches the output: the record carries percentages and timestamps and
nothing else. Both are pinned, and both were checked by sabotaging the
collector to pass -H and watching the contract name it.

Off by default. It is a coding-tool readout, not something a
general-purpose desktop shows without being asked, and it hides unless
the collector has real numbers rather than displaying "unknown".
2026-08-22 08:33:39 -04:00
Gabriel Brown 3c359f3f7e Notice the battery, and the machine it is or is not in
Panama had no idea whether it was running on a laptop. No upower, no
battery, no lid, no AC: hypridle.conf says "This is a desktop" in its
own header, and that was true of the code as well as the machine.

panama-hw answers hardware questions one at a time, exits 0 or 1, and
prints nothing, so scripts, services and contracts all ask the same
way. The definition the rest of the laptop work hangs on is one line:
clamshell is lid-closed AND an external monitor. A machine with no
mains supply at all reports as being on wall power, because a desktop
cannot run out of it.

The battery service follows Vitals: sysfs through FileView, an
availability flag, and no subprocess on the timer. Globbing is the one
thing QML cannot do -- a battery is BAT0 or BAT1 or CMB0, mains is AC
or ADP1 or ACAD -- so panama-battery resolves the names once and the
shell reads the files directly after. Nothing falls back to a
plausible zero: a desktop shows no indicator, no card, and no charge
limit control where the firmware has no ceiling.

Also repairs two contracts that were already failing and had not been
noticed, because only the full suite runs them. The dependency
scanner treated line-initial variable assignments, case labels,
comments and heredoc bodies as commands, and `count`, `host`, `cancel`
and `import` are all real binaries on Fedora, so `command -v` could
not filter them out. It now drops comments and heredoc bodies and
requires a command to be followed by whitespace. Verified it still
catches a genuinely undeclared dependency rather than passing quietly.
The launcher command contract had not been told about the fourteen
commands added earlier today.
2026-08-21 21:43:14 -04:00
Gabriel Brown 2528edfddc Stop every popover double-counting the bar height
The control center fix applies to five more surfaces: the date menu,
the clipboard panel, the activity panel, notification toasts, and the
signal glass all opened 48 pixels under the bar while their own
expression asked for 12.

wlr-layer-shell has three behaviors and only the middle one is subtle.
A positive exclusiveZone reserves space; a negative one ignores what
others reserved; zero reserves nothing but RESPECTS what others
reserved. Every popover here uses zero, so the compositor had already
placed them below the bar before their own margin applied, and adding
Theme.barHeight counted the bar twice.

It is not a crash or a warning -- the surface simply opens lower than
written -- so a contract now holds the rule mechanically: a surface
with exclusiveZone 0 may not name Theme.barHeight in a margin. It also
checks the premise it rests on, and fails if the bar ever stops
reserving its own height rather than quietly checking the wrong thing.

Measured after: date menu and clipboard at 12px, control center at 2px,
each matching what its code asks for.

Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
2026-08-19 11:31:53 -04:00
Gabriel Brown d96863b687 Convert British spellings to American across the repo
colour -> color, behaviour -> behavior, centre -> center, favourite ->
favorite, and about twenty other pairs, applied consistently across
comments, docs, error/UI copy, and a handful of QML identifiers that
used the British spelling as their actual name: SystemSettings'
serialiseValue/serialiseTable/normaliseGradient, Displays'
normaliseModes, Wallpaper's normalisePolicy, SettingsBackup's
serialiseHomeState, DateTime's ntpSynchronised property, Clipboard's
_normalise helper, and ShortcutCapture's cancelled signal (with its
onCancelled handler in ShortcutsPage.qml). Every call site and the two
tests that assert on the literal source text (settings-ownership and
settings-backup-live contracts) were updated in lockstep.

Left untouched: config/dot/espanso/match/packages/misspell-en/ is a
vendored third-party autocorrect dictionary -- its entries are typo
corrections, not our prose, and rewriting them would fight the
package's own purpose (and any future re-sync from upstream).

The already-American `favorites` property (Home page pinned
accessories) was never actually misspelled -- only nearby comments and
error strings said "favourites" -- so no data migration was needed
there.

Claude-Session: https://claude.ai/code/session_01E6TJUAh41HaP25MVHWkhRZ
2026-08-19 08:07:55 -04:00
Gabriel Brown 8cf03d4529 Remove dead shell components and fix stale docs
NotificationCenter.qml, CalendarPopup.qml, and RecordingIndicator.qml
were never instantiated anywhere -- shell.qml builds NotificationList,
DateMenu, and CaptureOverlay in their place. Verified with a repo-wide
grep before deleting; updated the two stale comments in Notifs.qml
that still pointed at NotificationCenter.

DESKTOP-PARITY.md still described Wi-Fi QR sharing as deliberately
omitted, though it was since built. The System Health colour-profile
handoff pointed at GNOME's colour panel as if it worked, but the
daemon that actually loads an ICC profile doesn't run in this session,
so it silently does nothing -- reworded the card to say so. Displays
carried two verify-timer attempt counters that were incremented but
never read anywhere.

Claude-Session: https://claude.ai/code/session_01E6TJUAh41HaP25MVHWkhRZ
2026-08-18 21:23:32 -04:00
Gabriel Brown e6b4d3c1a1 Stop lists and sliders from losing input under the user
WifiList and the notification toasts built their model from a plain
computed array, so any background property tick (a scan result, an
unrelated notification arriving) reassigned the whole array and the
Repeater destroyed and recreated every delegate -- including one with
an open, focused password field or an in-progress reply. Switched
both to a ScriptModel, which diffs by identity instead of resetting.

ValueSlider had its pointer-to-value mapping offset by 16px (the
hit-area margin was applied with the wrong sign), so 0% was
unreachable and every click landed to the right of where it was
placed -- affects every slider in the shell. SliderRow used -1 as a
sentinel for "nothing pending," which collides with legitimate
negative preference values like pointer sensitivity.

Dock intellihide read the globally focused workspace instead of each
monitor's own, so an empty workspace on one screen could hide the
dock on another; ActivityPanel rebuilt every row once a second during
a recording because the elapsed-time read lived in the model
construction instead of each row's own binding.

Claude-Session: https://claude.ai/code/session_01E6TJUAh41HaP25MVHWkhRZ
2026-08-18 21:22:58 -04:00
Gabriel Brown 8be3fc2fdd Right-click a bar widget to open its settings
Four places in the entire shell could reach Settings. The bar, where a
person looks first, was not one of them -- and Pill has routed
right-click to a secondaryActivated signal all along, which nothing
connected, so the gesture did nothing on every widget in the bar.

Each widget now opens the page that owns its settings: the clock and the
calendar reminder open Date & Time, weather opens Home, the vitals
readout opens Appearance, the status glyphs open Network & Devices, the
media readout opens Sound, and the privacy indicator opens Privacy &
Security. Left-click behaviour is untouched.

Two routing bugs found while picking those destinations, both of the
same kind and both invisible from the code, since each page reads
perfectly well on its own:

  weather routed to Appearance while every weather control lives on
  Home, so searching "temperature unit" opened a page without it.

  vitals routed to Appearance, but the refresh interval sat on Home
  while the toggles it governs sat on Appearance -- one concept split
  across two pages, which is exactly what the ownership rule forbids.
  The interval now sits beside the toggles and Home's stub card is gone.

The jump contract guards the failure mode these share. openSettings()
falls back to Home for an unknown page, sensibly and completely
silently, so a typo or a later rename turns a right-click into "opens
the wrong page" with nothing logged. It also fails a Pill-based bar
widget that leaves right-click unconnected, since that is how the
gesture came to be inert everywhere in the first place.

A third instance of the routing bug is still open: followMouse and
pointerSensitivity sit in the input group, which routes to Keyboard,
while both render on Mouse. Fixing it is a two-line group change in
PreferenceSchema.qml, which codex currently owns, so the contract that
catches all three lands with that fix rather than red.

Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
2026-08-18 14:46:04 -04:00
Gabriel Brown d855a26bd9 Add quiet System Health entry points 2026-08-18 10:19:03 -04:00
Gabriel Brown 5940d6b4bd Build the Panama Control Center 2026-08-17 13:44:18 -04:00
Gabriel Brown f27a1dca11 Add the quiet calendar capsule 2026-08-17 11:41:58 -04:00
Gabriel Brown 6a1bec849d Route the interactive Panama date menu 2026-08-17 11:32:57 -04:00
Gabriel Brown 5248883e4b Build the Panama Hyprland desktop 2026-08-17 10:32:55 -04:00