It is the system settings app for this desktop, so it is named the way one is. The window is "Settings", the titlebar is "Settings", the wordmark above the search field is gone, the sidebar's last row is "About", and the status line reads "Desktop is healthy". Prose followed the same rule. Forty-odd strings explained what "Panama" does -- "Panama never animates while idle", "Restore Panama defaults", "Panama looks in ~/Pictures/Wallpapers" -- which is how a product describes itself, not how a settings panel describes a setting. They now say what happens. No user-visible "Panama" remains anywhere in the app. Two consequences worth naming: The SUPER+I shortcut's description is user-visible, because the Shortcuts page is generated from it, so that is renamed too. Rebindings are keyed by shipped chord rather than description, so no existing override is orphaned by this. Three contracts matched the window by title and one matched that shortcut by description; all four are updated. There are no Hyprland window rules keyed on the title, so nothing about the window's placement changes. The desktop entry is now Name=Settings, but the FILE keeps its panama-settings name, as does the icon: the dock pins applications by desktop id, and renaming the file would silently unpin it. dock-pins-contract covers exactly that. The dated design docs under docs/superpowers keep the old name. They are a record of what was decided when, and editing them to agree with the present would make them lie about the past. Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
110 lines
4.6 KiB
Markdown
110 lines
4.6 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.
|
|
|
|
## 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.
|
|
|
|
## 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 copy of the Quickshell config shares the live shell's ID.** Quickshell
|
|
derives the Shell ID from config *content*, not path, so
|
|
`cp -a config/dot/quickshell $tmp && qs -p $tmp kill` kills the running
|
|
desktop, and `qs -p $tmp ipc call …` can drive it. Harnesses that point at a
|
|
single distinct `.qml` file are safe; copying the whole directory is not.
|
|
|
|
## 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.
|