GNOME's Multitasking panel asked one workspace question worth reproducing, and it is not which workspace goes on which screen. It is whether the second screen participates at all: workspaces on the primary display only, or each screen with its own. Ten rows of per-workspace assignment would be more powerful and worse. Off is Hyprland's own behaviour and emits nothing. On pins workspaces 1 to 10 -- however many ALT+1..ALT+0 actually reach, read from keybinds.lua rather than written down twice -- to whichever output is recorded as primary. With no primary recorded, nothing is pinned: guessing one would move every workspace onto whichever output happened to sort first, and this machine is in exactly that state. Applying is a reload, which is the part that shaped the design. Hyprland reads workspace rules at config time and will not remove one afterwards -- a rule written with an empty monitor keeps its old binding, which was checked rather than assumed. Only a reload clears them, so the config is the only honest source and the page cannot pretend a change has landed before one happens. Hence a service that reads `hyprctl workspacerules` back rather than inferring success from having written the preference, and a Reload row that exists only while the two disagree. Verified end to end against the live compositor and put back: off emits nothing, on emits ten rules naming the primary, and turning it off clears them. The settings file came back byte-identical. Claude-Session: https://claude.ai/code/session_01Q84axqUE5inJhf5Jz9CFy1
161 lines
8.0 KiB
Markdown
161 lines
8.0 KiB
Markdown
# Panama
|
|
|
|
Formerly Sunhat. A personal config for Fedora, with the intention of helping a
|
|
user set up their Fedora system with one command.
|
|
|
|
```sh
|
|
git clone https://git.gbrown.org/gib/Panama.git ~/.local/share/Panama
|
|
~/.local/share/Panama/install
|
|
```
|
|
|
|
`install` asks its questions first and then runs the stages in `setup/scripts/`
|
|
in order, without stopping again:
|
|
|
|
| Script | Does |
|
|
|---|---|
|
|
| `interview` | Every prompt, before anything is installed. Answers last one run and are never written to a durable path |
|
|
| `install-packages` | Repos (RPM Fusion, Terra, Hyprland COPR), the package lists in `setup/packages/`, then whichever optional categories were chosen |
|
|
| `link-dotfiles` | Symlinks `config/dot/<name>` → `~/.config/<name>`, and seeds the wallpaper, cursor theme and Firefox chrome |
|
|
| `change-settings` | Copies `config/copy/` over `/`, applies gsettings, enables user services |
|
|
| `link-vicinae-scripts` | Publishes the Vicinae script commands |
|
|
| `setup-identity` | git config, `gh auth login`, an SSH key — whichever were asked for |
|
|
| `install-hardware` | NVIDIA, Secure Boot enrollment, Fedora's extras, firmware — each only if it was asked for. Last, because enrollment and firmware are consumed at the next boot |
|
|
|
|
The run ends with a health summary from `panama-doctor`, which reports what is
|
|
actually running rather than what was attempted. It never fails the install: on a
|
|
fresh machine it legitimately reports things as not yet configured.
|
|
|
|
### Optional applications
|
|
|
|
Every machine gets the lists in `setup/packages/`. The interview also offers the
|
|
categories in `setup/packages/extras/` as a checklist, so a work laptop need not
|
|
acquire emulators and a desktop need not skip Steam. Nothing is preselected.
|
|
|
|
A category is one file. A bare line is a dnf package and a `flatpak:` line is a
|
|
Flathub ID, because the applications in a category do not all come from one
|
|
place. Adding a category is adding a file — the menu is read from the directory,
|
|
not written down anywhere.
|
|
|
|
Existing configs are moved to `config/old/` rather than overwritten.
|
|
|
|
## The desktop
|
|
|
|
Hyprland, with a shell written from scratch. It began as a replacement for a
|
|
GNOME session — Forge for tiling, Dash-to-Dock, Openbar, Vitals — and was built
|
|
to reproduce it closely enough that muscle memory transferred: same keybinds,
|
|
same panel contents, same dock, same Tokyo Night Moon palette.
|
|
|
|
That is history now rather than a second option. Panama installs and configures
|
|
one desktop, and the GNOME session it grew out of is neither installed nor
|
|
configured here. What each piece replaced is recorded in
|
|
[`config/dot/hypr/DESKTOP-PARITY.md`](config/dot/hypr/DESKTOP-PARITY.md) and in
|
|
the comments of the components themselves, because knowing what a thing was
|
|
modelled on explains why it behaves the way it does.
|
|
|
|
GNOME is not gone from the machine: `gnome-control-center` is a declared
|
|
dependency, and Panama's own Settings hands off to it for the panels it
|
|
deliberately does not own — Online Accounts, Color, Sound, Network, Keyboard,
|
|
Privacy, Wellbeing, Accessibility, and System for users, date and time, region
|
|
and remote desktop. The allow-list in
|
|
[`services/SystemSettings.qml`](config/dot/quickshell/services/SystemSettings.qml)
|
|
is what decides; anything not on it is a panel Panama owns itself.
|
|
|
|
| Piece | What it is |
|
|
|---|---|
|
|
| `config/dot/hypr/` | Compositor config. **Lua, not hyprlang** — see its README |
|
|
| `config/dot/quickshell/` | The shell: bar, dock, Continuum overview, Settings, Screen Intelligence, focus sessions, quick settings, notifications, screenshot UI |
|
|
| `config/dot/vicinae/` | Raycast-style launcher, themed. Its commands live in `config/local/share/vicinae/` — script commands, and one compiled extension that adds web search with live suggestions |
|
|
| `config/dot/uwsm/` | Session environment (see the uwsm caveat in the hypr README) |
|
|
| `config/dot/wofi/` | Fallback launcher, in case the shell fails to start |
|
|
| `config/dot/xdg-desktop-portal/` | Portal backend routing |
|
|
|
|
**Start here: [`config/dot/hypr/README.md`](config/dot/hypr/README.md)** — it
|
|
covers the Lua migration, the uwsm environment gotcha, the HDR decision, the
|
|
full keymap, and troubleshooting.
|
|
|
|
Log in as **"Hyprland (uwsm-managed)"**, not plain "Hyprland".
|
|
|
|
## Layout
|
|
|
|
```
|
|
bin/ Small user-facing commands on PATH; `panama` is the entry point
|
|
config/
|
|
bash/ .bashrc, aliases, env (env is gitignored)
|
|
copy/ Files copied verbatim over / (needs sudo)
|
|
dot/ Symlinked into ~/.config
|
|
firefox/ Vendored Firefox chrome, linked into the browser profile
|
|
local/ Icons, the cursor theme, and the launcher's commands and
|
|
extensions, linked into ~/.local/share
|
|
old/ Backups of whatever was replaced (gitignored)
|
|
wallpapers/ Copied into ~/Pictures/Wallpapers when absent
|
|
setup/
|
|
apps/ Applications built from source, one file each
|
|
lib/ Shared by more than one stage; the extras catalog reader
|
|
packages/ One package per line; extras/ holds the optional categories
|
|
scripts/ Run in order by ./install
|
|
tests/ Contracts. See below
|
|
docs/ Settings reference, and the design specs behind the work
|
|
```
|
|
|
|
## Tests
|
|
|
|
129 of them, under `tests/`. Run the lot, or a subset by pattern:
|
|
|
|
```sh
|
|
panama test # everything
|
|
panama test dock # just the ones matching "dock"
|
|
tests/setup/interview-contract # or one directly; they are plain executables
|
|
```
|
|
|
|
They are called contracts rather than unit tests because that is what they are:
|
|
each one pins a decision that was expensive to get right and is cheap to undo by
|
|
accident. Most read or measure the real thing — launching a shell to measure a
|
|
surface's geometry, standing stub commands on `PATH` to see what a stage would
|
|
have installed, running a script against a throwaway `HOME` — rather than
|
|
asserting things about source text, because the bugs worth catching here have all
|
|
been ones that source text looked fine for.
|
|
|
|
```
|
|
tests/setup/ The installer: the interview, package lists, hardware, extras
|
|
tests/quickshell/ The shell and its settings pages
|
|
tests/hypr/ The compositor config
|
|
```
|
|
|
|
## The `panama` command
|
|
|
|
```sh
|
|
panama update # review, commit and sync this repo
|
|
panama edit # open it in Neovim
|
|
panama doctor # what is actually running, not what was installed
|
|
panama test # every contract, or a subset by pattern
|
|
panama upgrade # re-run ./install from anywhere
|
|
panama apps # choose applications to install, by category
|
|
panama app # applications no repository carries; build one by name
|
|
```
|
|
|
|
`panama apps` is the optional-application catalog, opened after the fact. The
|
|
interview offers the same categories during `./install`, whole; this picks a
|
|
category and then the applications inside it, so a machine can acquire Slack in
|
|
March without having wanted Discord in January. Both read
|
|
`setup/lib/extras-catalog`, so the two cannot describe different catalogues.
|
|
|
|
A category is one file under `setup/packages/extras/`. A bare line is a dnf
|
|
package, a `flatpak:` line is a Flathub id, `| Name` gives the menu something
|
|
readable, and an indented line belongs to the entry above it — which is how OBS
|
|
carries its sixteen plugin extensions as one thing to tick.
|
|
|
|
`panama app` is deliberately not part of `./install`. Everything else Panama
|
|
installs comes from dnf or Flathub; these are built from source because no
|
|
packaged form exists, and a source build is slow, wants the network throughout,
|
|
and depends on an upstream that moves. That is the failure the interview exists
|
|
to prevent, so asking for one is something you do on purpose — and it is also
|
|
how you rebuild when a new version ships. Nothing is pinned: each build takes
|
|
the current upstream and reports a failure rather than working around it.
|
|
|
|
Adding one is adding a file to `setup/apps/`, and the file has to say why the
|
|
exception exists.
|
|
|
|
None of the scripts in this repository carry a `.sh` extension. A shebang and
|
|
the executable bit already select the interpreter, and the extension only
|
|
becomes something to keep in sync — which it did not stay.
|