Plan the desktop integration work
Six areas an OS is expected to own that this desktop still delegates: preview, GTK accent cohesion, storage, launcher search, secrets, and backups. Ordered so each is useful the day it lands. Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
This commit is contained in:
@@ -0,0 +1,137 @@
|
|||||||
|
# Desktop integration: the parts an OS owns that we still hand to someone else
|
||||||
|
|
||||||
|
Written 2026-08-19, after the default-applications work landed.
|
||||||
|
|
||||||
|
Settings is now a superpowered control panel, and files open in the right
|
||||||
|
applications. What remains are the jobs a desktop environment is expected to own
|
||||||
|
but that this one currently delegates, ignores, or leaves to whatever GNOME
|
||||||
|
component happens to be installed.
|
||||||
|
|
||||||
|
Six items, ordered so each one is useful the day it lands rather than only at the
|
||||||
|
end. Effort labels are relative to each other, not to the clock.
|
||||||
|
|
||||||
|
## What the probe found
|
||||||
|
|
||||||
|
Two of these are far cheaper than assumed:
|
||||||
|
|
||||||
|
- **`sushi` 50 is already installed** and owns `org.gnome.NautilusPreviewer`, so
|
||||||
|
Quick Look is a wiring-and-declaring job, not a build.
|
||||||
|
- **Vicinae script commands already exist and are repo-managed**
|
||||||
|
(`config/local/share/vicinae/scripts/panama/`, linked by
|
||||||
|
`setup/scripts/link-vicinae-scripts`), so launcher search is a generator on top
|
||||||
|
of a mechanism that works.
|
||||||
|
|
||||||
|
And two need software this machine does not have: `restic` (backups) is not
|
||||||
|
installed, and nothing enumerates the keyring beyond lock state.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 1 — Finish the file-opening story (small)
|
||||||
|
|
||||||
|
Curating defaults only helps if the applications are actually present. A fresh
|
||||||
|
install seeds `org.gnome.Loupe`, `org.gnome.Papers` and `org.gnome.Decibels`
|
||||||
|
only when they exist; otherwise it correctly leaves the role alone and the
|
||||||
|
machine falls back to installation order — the exact problem we just fixed.
|
||||||
|
|
||||||
|
- Declare Loupe, Papers, Decibels and sushi in the dependency list so
|
||||||
|
`declared-dependencies-contract` fails when a curated handler is missing.
|
||||||
|
- Wire preview: confirm space-to-preview from the file manager, and decide
|
||||||
|
whether preview is worth a compositor-level keybind for the focused selection.
|
||||||
|
- Extend the family contract to assert every seeded candidate is installable.
|
||||||
|
|
||||||
|
**Done when** a fresh machine gets the same handlers this one has, and the
|
||||||
|
contract fails loudly if a curated application is not a declared dependency.
|
||||||
|
|
||||||
|
## Phase 2 — Make GTK applications wear our accent (small)
|
||||||
|
|
||||||
|
The named accents theme our own surfaces; Loupe, Papers, Nautilus and every other
|
||||||
|
libadwaita application still render in GNOME blue. GNOME exposes
|
||||||
|
`org.gnome.desktop.interface accent-color` as an enum (blue, teal, green,
|
||||||
|
yellow, orange, red, pink, purple, slate).
|
||||||
|
|
||||||
|
- Give each named accent a nearest-GNOME-enum member in `Theme.qml`, beside the
|
||||||
|
dark/light pair it already carries.
|
||||||
|
- Apply it alongside the existing GTK theme write, so one accent change moves
|
||||||
|
both worlds.
|
||||||
|
- Contract: every named accent maps to a member the enum actually publishes,
|
||||||
|
read from `gsettings range` rather than a hardcoded list.
|
||||||
|
|
||||||
|
**Done when** switching accent visibly recolors Papers and Nautilus.
|
||||||
|
|
||||||
|
## Phase 3 — Disks and storage (medium)
|
||||||
|
|
||||||
|
There is no way to see what is using the disk, and removable media is handled by
|
||||||
|
`udiskie` in the tray with no surface in Settings. `udisks2` is installed.
|
||||||
|
|
||||||
|
- Helper `panama-disks`: JSON snapshot of filesystems, sizes, usage, mount
|
||||||
|
points, removable devices, and SMART health where the device exposes it.
|
||||||
|
- Page: usage bars per filesystem, removable devices with mount / unmount /
|
||||||
|
eject, and "open in Files".
|
||||||
|
- **Deliberately out of scope: partitioning and formatting.** A settings pane is
|
||||||
|
the wrong place to hand someone a way to erase a disk in two clicks; that stays
|
||||||
|
with GNOME Disks, which we can launch.
|
||||||
|
|
||||||
|
**Done when** the page answers "what is filling my drive" and "is it safe to pull
|
||||||
|
this USB stick out".
|
||||||
|
|
||||||
|
## Phase 4 — Launcher-native settings search (small)
|
||||||
|
|
||||||
|
Settings has a search index. The launcher has script commands. They do not know
|
||||||
|
about each other, so finding a setting means opening Settings first.
|
||||||
|
|
||||||
|
- Generate one script command per settings page from the same schema that feeds
|
||||||
|
in-app search, with the page's keywords, so typing "night light" in the
|
||||||
|
launcher opens that page directly.
|
||||||
|
- Generator gets a `--check` staleness mode, matching `panama-settings-docs`.
|
||||||
|
- Contract: every page has a command, every command resolves to a real page.
|
||||||
|
|
||||||
|
**Open question for Gabriel:** the existing launcher commands are titled
|
||||||
|
"Panama: Open Settings", which the debranding pass did not touch. Keep the prefix
|
||||||
|
so desktop commands group together in the launcher, or drop it to match the rest?
|
||||||
|
|
||||||
|
**Done when** the launcher is the fastest way to reach any setting.
|
||||||
|
|
||||||
|
## Phase 5 — Keychain and secrets (medium, security-sensitive)
|
||||||
|
|
||||||
|
`panama-keyring` knows whether the keyring is locked and can unlock it. Nothing
|
||||||
|
can see what is *in* it, so managing a stored credential means installing
|
||||||
|
Seahorse.
|
||||||
|
|
||||||
|
- Extend the helper to enumerate collections and items through libsecret,
|
||||||
|
returning **attributes and labels only**.
|
||||||
|
- Page: unlock state, lock/unlock, items grouped by collection, delete, change
|
||||||
|
password.
|
||||||
|
- Rules that are non-negotiable and get their own contracts: a secret value never
|
||||||
|
appears in a snapshot, never reaches a command line (argv is world-readable via
|
||||||
|
`/proc`), never lands in a log or an error message, and is revealed only on an
|
||||||
|
explicit per-item request.
|
||||||
|
|
||||||
|
**Done when** a stored password can be found, inspected and removed without
|
||||||
|
leaving Settings, and the contracts prove no value leaks on the way.
|
||||||
|
|
||||||
|
## Phase 6 — Backups (large, staged)
|
||||||
|
|
||||||
|
The one genuinely missing safety net. `restic` is not installed.
|
||||||
|
|
||||||
|
- **6a** — destination and repository setup, plus a manual snapshot. Repository
|
||||||
|
password lives in the keyring (which is why this follows phase 5), never in the
|
||||||
|
repo and never on argv.
|
||||||
|
- **6b** — a systemd user timer, with the page showing schedule, last run, and
|
||||||
|
failures.
|
||||||
|
- **6c** — restore: browse a snapshot and copy files out.
|
||||||
|
|
||||||
|
Blast radius: writes to a destination the user chooses. Nothing in this phase
|
||||||
|
modifies or deletes existing data; restore copies out rather than over.
|
||||||
|
|
||||||
|
**Done when** losing the home directory is an inconvenience rather than a
|
||||||
|
catastrophe.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Order and reasoning
|
||||||
|
|
||||||
|
Phases 1 and 2 finish work already in flight and are visible immediately. Phase 3
|
||||||
|
is self-contained and answers a question people actually ask. Phase 4 is small
|
||||||
|
but changes daily habit. Phase 5 must precede 6 because 6 needs somewhere safe to
|
||||||
|
keep a repository password. Phase 6 is the largest and is staged so each part
|
||||||
|
stands alone.
|
||||||
Reference in New Issue
Block a user