Super+D holds the microphone open, releasing it transcribes on the GPU and types the result wherever the cursor is. Roughly 150ms for a normal utterance once the model is resident, measured rather than hoped for. Getting there meant discarding two approaches. Fedora 44 cannot install any GPU-capable Whisper for Python -- openai-whisper needs a numba that needs an llvmlite that does not exist for 3.14, and faster-whisper needs a ctranslate2 nobody packaged. The whisper-cpp package IS built with HIP but ships libraries with no binary and no bindings, and hand-writing ctypes for a large by-value struct is a segfault waiting for a version bump. So a container, as suggested. Vulkan rather than ROCm, and upstream's image rather than one built here. ROCm is seven gigabytes and serves AMD alone; Vulkan compute runs on the AMD, Intel and NVIDIA machines this config is used on, in a twentieth of the space. The Vulkan tag already contains whisper-server, so there is no Containerfile to keep working -- an earlier draft of this commit had one, and it was strictly worse. Two bugs found by using it rather than by reading it. Whisper describes silence as the literal text "[BLANK_AUDIO]", and the first working version pasted that string into the clipboard; a transcription that is nothing but such markers is now discarded. And the server answers with a line per segment, which typed into a window is an Enter press -- sending the half-written message, submitting the form. Whitespace is collapsed to one line. Neither the image nor the model is installed by ./install. Together they are over two gigabytes that want the network, and Settings offers both as one action instead. Nothing starts at login either: whisper-server holds the model from the moment it starts, so the first press of the key is what brings it up. The contract pins both text bugs, that the server stays on loopback, and that it does not start at login. Reverting the [BLANK_AUDIO] guard did not fail it at first -- the check was still correct, it had simply stopped being called -- so it now checks the call site too. Claude-Session: https://claude.ai/code/session_01Q84axqUE5inJhf5Jz9CFy1
8.2 KiB
Panama
Formerly Sunhat. A personal config for Fedora, with the intention of helping a user set up their Fedora system with one command.
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 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
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/containers/ |
Container definitions systemd runs as units — currently the speech-to-text server behind dictation |
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 — 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
containers/ Quadlets, linked into ~/.config/containers/systemd
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
130 of them, under tests/. Run the lot, or a subset by pattern:
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
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.