168 lines
7.8 KiB
Markdown
168 lines
7.8 KiB
Markdown
# Settings
|
|
|
|
The control centre for everything Panama owns. Anything the system owns —
|
|
hardware, accounts, printers — is delegated to GNOME Settings and labelled as
|
|
such rather than half-reimplemented.
|
|
|
|
## System Health
|
|
|
|
The stable internal `services` route renders **System Health**. It is reachable
|
|
from the Settings sidebar and its live 54px footer, the degraded-only bar
|
|
indicator, and Vicinae's **Panama: Check System Health** command. Healthy scans
|
|
reserve no bar space and produce no notification.
|
|
|
|
`services/Health.qml` owns the last accepted redacted snapshot. It invokes
|
|
`scripts/panama-doctor` for scans and bounded repairs, `wl-copy` only for an
|
|
explicit **Copy Report**, and bounded `notify-send` only when an external repair
|
|
fails. For a concise terminal view, run:
|
|
|
|
```bash
|
|
~/.config/quickshell/scripts/panama-doctor --summary
|
|
```
|
|
|
|
The helper diagnoses Panama-owned desktop services, dependencies, links, and
|
|
configured integrations. It does not read secret values, clipboard or
|
|
notification contents, calendar events, SSIDs, addresses, or arbitrary command
|
|
output. Its repair interface is an authored allow-list: it never installs a
|
|
package, runs `sudo`, deletes user data, or repairs a service Panama does not
|
|
own. A repair remains degraded until a fresh scan observes recovery.
|
|
|
|
The final card is the ownership boundary. Network configuration and the exact
|
|
Users, Sharing, Colour profiles, and Digital wellbeing handoffs open GNOME
|
|
Settings because Fedora's system services own those areas.
|
|
|
|
## Adding a setting
|
|
|
|
One schema entry. That is the whole job.
|
|
|
|
```qml
|
|
// config/PreferenceSchema.qml
|
|
{
|
|
key: "blurSize", type: "int", def: 8, min: 1, max: 20, step: 1,
|
|
unit: "px", group: "effects",
|
|
label: "Blur radius",
|
|
detail: "Larger is softer and costs more frame time",
|
|
hypr: { path: ["decoration", "blur", "size"], option: "decoration:blur:size", readAs: "int" }
|
|
}
|
|
```
|
|
|
|
```qml
|
|
// the page
|
|
SliderRow { setting: "blurSize" }
|
|
```
|
|
|
|
Persistence, validation, clamping, reset, search indexing, and — with a `hypr`
|
|
block — live application to the compositor and the startup replay all derive
|
|
from that entry. There is nothing else to register.
|
|
|
|
If it is compositor-backed, add the matching `prefs.get("blurSize", 8)` in
|
|
`hypr/looks.lua` so the Hyprland config still stands alone with no settings
|
|
file.
|
|
|
|
## Setting ownership
|
|
|
|
Every preference has **one primary page**, derived from its schema `group` and
|
|
the route in `services/SettingsSearch.qml`. Search results always open that
|
|
owner. A control may appear on a second page only when the same adaptation is
|
|
part of another established mental model; otherwise use a labelled handoff to
|
|
the owner instead of duplicating it.
|
|
|
|
### Intentional mirrors
|
|
|
|
| Setting | Primary page | Mirror | Why the mirror earns its place |
|
|
|---|---|---|---|
|
|
| `animationsEnabled` | Appearance | Accessibility | Reduced motion belongs both to visual polish and motion accessibility. |
|
|
| `cursorInactiveTimeout` | Mouse | Accessibility | Pointer visibility is configured with pointer behaviour but affects motor and visual access. |
|
|
| `cursorSize` | Accessibility | Mouse | Large cursors are an accessibility adaptation that users also look for beside pointer controls. |
|
|
| `inactiveOpacity` | Appearance | Accessibility | Window translucency is an appearance choice with a direct readability impact. |
|
|
| `lockMinutes` | Power | Privacy | Idle timing owns the mechanism; privacy owns the expectation that the unattended desktop locks. |
|
|
| `lockOnSleep` | Power | Privacy | Suspend owns the transition; privacy owns whether waking requires authentication. |
|
|
|
|
Mirrors must remain the same schema-backed control, never a second preference
|
|
or a copied default. Additions to this table require a concrete discoverability
|
|
reason and an update to `tests/quickshell/settings-ownership-contract.sh`.
|
|
|
|
Window border colour follows the same ownership rule. The inactive border is a
|
|
**scheme-relative role** owned by `ColorScheme.qml`: it changes only to retain
|
|
neutral contrast in light and dark modes. The focused Prism border is the
|
|
accent role owned by the visual theme (and, eventually, an accent picker).
|
|
`ColorScheme.qml` must never write the focused border, so changing schemes
|
|
cannot erase a user-selected accent.
|
|
|
|
## The rows
|
|
|
|
| Component | For |
|
|
|---|---|
|
|
| `SettingsPage` | The page scaffold: title, lede, optional pinned `header` |
|
|
| `ToggleRow { setting }` | A boolean |
|
|
| `SliderRow { setting }` | A number; `zeroLabel` renders 0 as "Never"/"Instant"/"None" |
|
|
| `ChoiceRow { setting }` | An enum, as a segmented control |
|
|
| `ActionRow` | A button: opens a GNOME panel, runs a one-shot |
|
|
| `TextRow` | A genuinely read-only fact |
|
|
|
|
`TextRow` is for facts, not for settings that were merely expensive to wire.
|
|
Before Stage 3 more than half of all rows were static text standing in for
|
|
controls; that is the failure this vocabulary exists to prevent.
|
|
|
|
Rows write through `SystemSettings.commitPreference(key, value)`, which routes
|
|
compositor-backed keys through apply-and-verify and local keys straight to the
|
|
store. A row never needs to know which kind it holds.
|
|
|
|
## Things that will bite you
|
|
|
|
**`readAs` describes the answer, not the setting.** `hyprctl getoption` returns
|
|
the value in a different JSON field per type — `int`, `bool`, `float`, `str`,
|
|
and `css` for gaps (a four-value box). Declaring the wrong one does not fail
|
|
loudly: it makes every write to that key look *rejected*, and the user sees an
|
|
error for a change that worked. `tests/quickshell/schema-hypr-shape-contract.sh`
|
|
asks the compositor for the real shape of every mapped option.
|
|
|
|
**Never trust an exit code from `hyprctl`.** `keyword` refuses to work on a
|
|
Lua-configured Hyprland, prints the refusal to stdout, and exits 0. `eval` exits
|
|
0 on syntax and runtime errors too. The only trustworthy signal that a write
|
|
landed is reading the value back.
|
|
|
|
**The Settings window is tiled.** `implicitWidth` is a hint; the layout decides,
|
|
and it ranges from a half-screen split to the full display. `SliderRow` stacks
|
|
its control under the label below 520px. Test narrow.
|
|
|
|
**Binding an anchor to `undefined` does not reliably release it.** Switching
|
|
layouts that way left a slider anchored to both edges with the label squeezed
|
|
into what was left. Position explicitly instead.
|
|
|
|
**Inside a `SettingsCard`, `parent` is the card's internal Column.** So
|
|
`parent.modelData` in a nested `Repeater` is undefined and the rows silently
|
|
never appear — you get a card with a heading and nothing under it. Address the
|
|
outer model through an explicit `id`.
|
|
|
|
**A `TapHandler` declared as a child of `SettingRow` lands in the trailing
|
|
slot**, because that is the row's default property, so only the right-hand edge
|
|
becomes clickable. Use `activatable: true` with `onActivated` for a whole-row
|
|
target.
|
|
|
|
**A content-identical Quickshell entry can share the live shell's ID.**
|
|
Quickshell derives the Shell ID from config *content*, not path. Runtime
|
|
harnesses therefore create a distinct semantic entry file, address that exact
|
|
file with `qs -p`, and discover its PID from the exact Config path in
|
|
`qs list --all`. They terminate only that recorded PID with `kill`; never use
|
|
`qs kill` from a copied configuration.
|
|
|
|
## Where state lives
|
|
|
|
| File | Holds |
|
|
|---|---|
|
|
| `~/.config/panama/settings.json` | Everything in the schema. Read by the shell *and* by `hypr/prefs.lua` |
|
|
| `$XDG_STATE_HOME/panama/panama-home.json` | Home accessory favourites and aliases |
|
|
| `$XDG_STATE_HOME/panama/backups/` | Settings snapshots |
|
|
| `$XDG_STATE_HOME/panama/hypridle.conf` | Generated idle config |
|
|
|
|
`SystemSettings.restoreDefaults()` spans all of them. A reset that silently
|
|
skipped one would be worse than having no reset, because nothing would say so.
|
|
|
|
## Not stored by Panama
|
|
|
|
Timezone and network time are read from and written to `timedatectl` directly.
|
|
They belong to the machine and are shared with sessions that never see Panama's
|
|
file; storing a copy would create a second answer to a question the system
|
|
already answers.
|