Skip to main content

max / alloy

Revert "Swap the terminal from rio to shop" This reverts commit 0771ea5caa5a43c9bd7b4ba3ad65667c3d9c8079.
Author: Max Johnson <me@maxj.phd> · 2026-07-31 20:46 UTC
Signed with PGP, not checked
Commit: 3c89165fae34e5e3ce049c039a7948d7fe11560a
Parent: 0771ea5
13 files changed, +49 insertions, -111 deletions
M Containerfile +4 -55
@@ -31,53 +31,7 @@
31 31 # greeter's console palette; skelgen emits that table now, along with the rest
32 32 # of the desktop skeleton, so there is one implementation of the ANSI mapping
33 33 # instead of three.
34 - # git, and the two libraries shop links, on top of the toolchain. The console
35 - # needs none of them; shop is Wayland and GPU facing, so it links
36 - # libwayland-client and libxkbcommon at build time and will not compile
37 - # without their headers. `ldd` on the built binary is the check if this list
38 - # ever looks wrong.
39 - RUN dnf install -y cargo rust git wayland-devel libxkbcommon-devel pkgconf \
40 - && dnf clean all
41 -
42 - # =====================================================================
43 - # shop, Alloy's terminal.
44 - # =====================================================================
45 - # Built from source rather than installed from a repo, because there is no
46 - # repo: shop is first-party and packaged nowhere. Owning an RPM spec, a COPR
47 - # and a release cadence for a binary whose only consumer is this image buys
48 - # nothing, and Alloy publishes no other artifact (distribution is builders,
49 - # not images).
50 - #
51 - # Pinned by revision, not by branch. An unpinned build is a different image
52 - # every day for the same Containerfile, and the terminal is the one component
53 - # where "it worked yesterday" has no recovery path from inside the session.
54 - # Bump this deliberately.
55 - #
56 - # `cargo install` rather than a second COPY-and-build stage: shop is a separate
57 - # repo with its own workspace, so there is no local tree to copy and no
58 - # existing pin to imitate. `--locked` builds the dependency graph shop
59 - # committed rather than whatever resolves that day, matching the console's
60 - # `--locked` below.
61 - #
62 - # Above the console's source on purpose. This layer is keyed on the revision
63 - # alone, so console edits — the thing that changes most often — reuse it
64 - # instead of rebuilding a terminal that did not change. Moving it below the
65 - # `COPY crates/` would defeat the dependency-cache split that follows.
66 - ARG SHOP_REV=1a32ff7809fa8fff429f2dacc8c8be63caa20a4c
67 - RUN cargo install \
68 - --git https://makenot.work/git/max/shop.git \
69 - --rev "${SHOP_REV}" \
70 - --locked \
71 - --root /shop \
72 - shop
73 -
74 - # The binary exists and runs. Every other way this fails is silent at build
75 - # time and total at first login: sway binds $mod+Return to `shop` and the
76 - # launcher spawns `${TERMINAL:-shop}`, so a missing binary is a session with
77 - # no way to reach a shell and no way to edit the config that would fix it,
78 - # short of a VT. Same reasoning as the console's stub assertions below.
79 - RUN test -x /shop/bin/shop \
80 - || { echo "shop did not install; the session would have no terminal" >&2; exit 1; }
34 + RUN dnf install -y cargo rust && dnf clean all
81 35
82 36 WORKDIR /src
83 37
@@ -396,8 +350,8 @@
396 350 libnotify \
397 351 satty \
398 352 swww \
399 - # Editor, shell, prompt. The terminal is not here: shop is built from
400 - # source in the rust-build stage and copied in below.
353 + # Terminal, editor, shell, prompt
354 + rio \
401 355 helix \
402 356 nushell \
403 357 starship \
@@ -834,7 +788,7 @@
834 788 gnome-session \
835 789 # foot, a second terminal nothing in Alloy invokes. It arrives as a
836 790 # weak dependency of sway-config-upstream, whose config binds it as
837 - # sway's default terminal; Alloy ships its own config and binds shop
791 + # sway's default terminal; Alloy ships its own config and binds rio
838 792 # (docs/STACK.md#terminal), so nothing here ever launches it. Left in,
839 793 # it puts three of the eleven entries in the new launcher (Foot, Foot
840 794 # Client, Foot Server), which is how an unused package stops being
@@ -1322,11 +1276,6 @@
1322 1276 # something the operator dropped in.
1323 1277 COPY --from=rust-build /src/target/release/alloy /usr/bin/alloy
1324 1278
1325 - # The terminal, from the same stage. /usr/bin for the same reason as the
1326 - # console: shipped software, and /usr/local is not part of a bootc image's
1327 - # immutable tree.
1328 - COPY --from=rust-build /shop/bin/shop /usr/bin/shop
1329 -
1330 1279 # The themes that binary refuses to run without. Kept next to it so the
1331 1280 # two move together: shipping the console without these is not a degraded
1332 1281 # console, it is one that exits on launch.
@@ -17,7 +17,7 @@
17 17 Large, declarative iconography lived on GUI surfaces (egui lockscreen ornament, launcher app icons, settings headers, marquee-app heroes). Those surfaces were dropped in the pivot, so the hero tier lost most of its homes. It survives only where a graphical surface remains:
18 18
19 19 - **swaylock background**: the adopted lockscreen can display a background image; a hero plate belongs here.
20 - - **First-boot splash**: renderable via shop's kitty-graphics protocol, or shown before the session starts.
20 + - **First-boot splash**: renderable via rio's kitty-graphics protocol, or shown before the session starts.
21 21 - **Brand assets** outside the running system (repo social card, README).
22 22
23 23 Everything else that used to carry a hero illustration is now terminal chrome and uses the functional glyph tier instead. The hero tier is therefore **deferred**: the style below is preserved so the identity is not lost and so the surviving surfaces have a spec, but no hero illustrations are on the critical path for the TUI work.
M docs/SHELL.md +1 -1
@@ -12,7 +12,7 @@
12 12
13 13 The pick that keeps the ecosystem tax bounded:
14 14
15 - - **Login shell:** `nu`. What the user sees when they open shop, when they SSH in, when they open a scratch pane.
15 + - **Login shell:** `nu`. What the user sees when they open rio, when they SSH in, when they open a scratch pane.
16 16 - **`/bin/sh`:** bash, unchanged. Every `curl | sh` invokes this. Untouched.
17 17 - **`/bin/bash`:** bash, unchanged. Every `#!/bin/bash` script runs here. Untouched.
18 18 - **`/etc/shells`:** contains both nu and bash. `chsh -s /bin/bash` reverts a user to bash cleanly and reversibly.
M docs/STACK.md +15 -22
@@ -50,7 +50,7 @@
50 50
51 51 ## Launcher
52 52
53 - **`alloy-menu`: fzf over desktop entries, run in shop.** Bound `Mod+D`. A shell script in `usr/bin/`, about forty lines, that reads `.desktop` files from the system, user and Flatpak paths, pipes names through fzf, and hands the chosen `Exec` line to `swaymsg exec` so the app outlives the terminal that launched it.
53 + **`alloy-menu`: fzf over desktop entries, run in rio.** Bound `Mod+D`. A shell script in `usr/bin/`, about forty lines, that reads `.desktop` files from the system, user and Flatpak paths, pipes names through fzf, and hands the chosen `Exec` line to `swaymsg exec` so the app outlives the terminal that launched it.
54 54
55 55 The split with `Mod+Return` is the design. Typing a command is what a shell is for, and a shell is one keystroke away, so the launcher does not list every binary on `PATH`: doing that buries the browser under coreutils. It lists the graphical apps that have a name, an icon and a binary nobody remembers. Two keys, two jobs.
56 56
@@ -113,39 +113,32 @@
113 113
114 114 ## Terminal
115 115
116 - **shop.** Rust, WGPU-backed, Wayland-only, TOML config, kitty graphics protocol. Alloy's own, built from source into the image. Adopted 2026-07-31, replacing rio.
116 + **rio.** Rust, WGPU-backed, TOML config, kitty and sixel and iTerm2 graphics protocols. Ships in Fedora repos.
117 117
118 - The pick is still driven by the yazi commitment above. Yazi is the TUI primary, "where keyboard-driven Alloy users will live by default." A terminal without graphics protocols neuters the primary tool, so the question isn't "are previews a nice-to-have" but "does the primary tool run at full capability." shop implements the kitty graphics protocol, which is the one yazi wants.
118 + The pick is driven by the yazi commitment above. Yazi is the TUI primary, "where keyboard-driven Alloy users will live by default." A terminal without graphics protocols neuters the primary tool, so the question isn't "are previews a nice-to-have" but "does the primary tool run at full capability." That reframes the axis and makes rio the honest answer.
119 119
120 - rio satisfied that much too, and was the pick until shop met its daily-drive bar. What an adopted terminal could not satisfy is the rest:
121 -
122 - - **The terminal is a surface Alloy designs.** Every other surface in the image renders from one makeover theme through one intent table. An adopted terminal renders from sixteen hex values transcribed into its own config, and the two drift the moment either side moves. shop takes a theme by id and resolves the slots itself, from the same `makeover::ansi_intent` table the kernel cmdline and setvtrgb render from. There is nothing left to transcribe, which is why `templates/etc/skel/.config/shop/config.toml.in` carries no colour at all.
123 - - **First-party.** The console is authored. The terminal is the other surface a user is inside all day, and adopting it put the most-used window in the image on someone else's roadmap.
124 - - **Wayland-only.** The rest of the stack already assumes it. A terminal carrying an X11 path carries it for nobody here.
125 -
126 - Secondary alignments, unchanged by the swap:
120 + Secondary alignments:
127 121 - **TOML config.** No scripting language dependency, consistent with Alloy's rejection of Lua/yuck/similar config surfaces elsewhere in the stack.
122 + - **Design-forward posture.** Rio treats the terminal as a displayed surface, not just a fast text renderer. That matches Alloy's design-system stance.
128 123 - **No multiplexing.** Tabs, splits, panes are sway's job. A terminal that also tiles is duplicated budget. Alloy therefore ships no multiplexer either: tmux and zellij are not in the image, and a terminal's own docs should not send an Alloy user looking for them. A window is a window, and `Mod+H/J/K/L` moves between them.
129 124
130 - **yazi should run with kitty graphics under shop, not sixel.** shop implements kitty graphics and no sixel, so there is no compat path to fall back to inside the terminal: it is inline previews or chafa half-blocks.
125 + **yazi runs with kitty graphics under rio, not sixel.** Both work in rio; kitty is the modern protocol, sixel is the compat path. Yazi picks the graphics protocol at runtime by detecting terminal capability from `TERM` / `TERM_PROGRAM`, so the config work is small:
131 126
132 - - `yazi.toml` default: leave image previews on with `image_filter = "triangle"` and `image_quality = 75`.
133 - - **Open, and the one thing to check first.** Yazi picks its graphics protocol by runtime detection. rio identified itself with `TERM_PROGRAM=rio`; shop sets `TERM=xterm-256color` and no `TERM_PROGRAM` at all, so yazi has one fewer signal than it had, and `xterm-256color` on its own advertises no graphics capability. Verify by opening an image directory in yazi under shop and confirming inline previews render. If they do not, the fix is on shop's side or in `yazi.toml`, not in yazi.
127 + - Rio sets `TERM=xterm-256color` and identifies itself via `TERM_PROGRAM=rio`. Yazi's runtime detection picks kitty graphics from that.
128 + - `yazi.toml` default: leave image previews on with `image_filter = "triangle"` and `image_quality = 75`. No explicit backend key needed.
129 + - Verify at v0 packaging time by opening an image directory in yazi under rio and confirming inline preview renders (not falling back to chafa/half-block).
134 130
135 - Cost of authoring rather than adopting, stated plainly:
131 + (Reduce to a real `yazi.toml` file when the v0 stack packaging lands. Documented here so the pick's implication isn't lost.)
136 132
137 - - **No repo to ship from.** shop is packaged nowhere, so the image builds it from source at a pinned revision (`SHOP_REV` in the Containerfile). An image build needs network to fetch it, and bumping the pin is a deliberate act rather than something `dnf update` does.
138 - - **It is new.** Written in 2026-07. The daily-drive bar it had to clear was selection and clipboard plus real key handling, both landed 2026-07-31. Still absent: scrollback, and with it the scroll wheel and scrollback search; sixel; OSC 8 hyperlinks; an I-beam pointer over the grid.
139 - - **The complexity was the objection, and it was a fair one.** Terminals are among the most complex userland software: ANSI, terminfo, graphics protocols, IME, OSC, performance under load. This section previously rejected writing one on exactly that ground. The scope was taken on deliberately, with a stated bar and a fallback, rather than by deciding the objection was wrong.
140 -
141 - Risk accepted, and the fallback is unchanged: **alacritty**, if shop proves unworkable. The graphics gap becomes a knowing loss then, exactly as it would have if rio had failed.
133 + Risk accepted: rio is ~2 years mature vs. alacritty's ~7. If rio proves flaky in real use, the fallback is alacritty, and the graphics gap becomes a knowing loss. Naming this trade-off here rather than pretending rio is drama-free.
142 134
143 135 Rejected:
144 - - **rio.** The prior pick, and a good one for an adopted terminal: Rust, WGPU, TOML, all three graphics protocols, in Fedora repos. Replaced for the reasons above, none of which are faults of rio's.
145 - - **alacritty.** Upstream has firmly refused sixel/kitty graphics for years: a definitional stance, not a pending PR. Kills yazi previews permanently. Kept as the fallback anyway, with that loss named.
136 + - **alacritty.** Upstream has firmly refused sixel/kitty graphics for years: a definitional stance, not a pending PR. Kills yazi previews permanently.
146 137 - **wezterm.** Lua config violates the "no scripting-language configs" line held elsewhere in the stack. Its central pitch (built-in multiplexing, tabs, SSH client) duplicates sway's tiling and is spent budget in this stack.
147 138 - **cosmic-term.** Uses `alacritty_terminal` as the backend (the same graphics gap as alacritty) and additionally couples Alloy's default terminal to libcosmic's visual identity, which the design-system stance rejects.
148 139
140 + No custom egui terminal: terminals are among the most complex userland software (ANSI, terminfo, sixel/kitty graphics, IME, OSC, performance under load). Wrong scope.
141 +
149 142 ## Browser
150 143
151 144 **Helium baked into the image as the default, unconfigured.** Helium is ungoogled-chromium with the behavior Alloy used to hand-build on top of Firefox already set upstream: ads, trackers, cookie banners and third-party cookies blocked by default, fingerprinting tampered with, no analytics, and no network request at all on first launch. Alloy ships no policy file, no preference seed, no chrome CSS and no extension pin for it. Shipping nothing is the reason it is the default, not an omission next to it. The packaging rules this follows are in the wiki note `alloy-packaging-policy`; the short form is that an app earns a recommendation by not needing configuration, which is the same test that picked helix over a configured vim.
@@ -452,7 +445,7 @@
452 445 Pango consumers are not a cell grid and keep the plain family, where the fuller icons are free.
453 446
454 447 Applied at:
455 - - shop: nothing to apply. It bundles `IosevkaTerm Nerd Font Mono` into the binary and has no font setting, so the terminal cannot be pointed at a font the image does not carry. This is also why it renders nerd-font glyphs with no system font install.
448 + - Rio, all five `fonts.*.family` entries: `IosevkaTerm Nerd Font Mono`.
456 449 - fontconfig `monospace` alias: `IosevkaTerm Nerd Font Mono`, for everything else that asks for mono.
457 450 - `templates/etc/skel/.config/mpv/mpv.conf.in`, `templates/etc/skel/.config/imv/config.in`, the sway bar and title font, mako, swaylock, satty: the plain `IosevkaTerm Nerd Font`, all pango.
458 451
@@ -7,7 +7,7 @@
7 7 the previous one stays on disk to roll back to.
8 8 - **sway as the compositor.** i3-style manual tiling on Wayland. Workspaces,
9 9 splits, tabs, and stacks. No scrolling columns, no animation.
10 - - **A terminal-first stack.** shop for the terminal, nushell for the shell,
10 + - **A terminal-first stack.** rio for the terminal, nushell for the shell,
11 11 helix for the editor, yazi for files, bottom for processes, zathura for PDFs,
12 12 imv for images, mpv for video.
13 13 - **One authored program, the `alloy` console.** Network, audio, displays,
@@ -23,7 +23,7 @@
23 23
24 24 Mod+Return
25 25
26 - That is shop, running nushell. It is where you will spend most of your time.
26 + That is rio, running nushell. It is where you will spend most of your time.
27 27
28 28 If you would rather find a graphical app by name than type a binary:
29 29
@@ -43,7 +43,7 @@
43 43 the timezone did.
44 44
45 45 **Applications** holds config files, listed by the app they configure rather
46 - than by path. You pick `yazi`, not `~/.config/yazi/yazi.toml`. Editing here is
46 + than by path. You pick `rio`, not `~/.config/rio/config.toml`. Editing here is
47 47 schema-driven: fields carry types, ranges, allowed values, and their own help
48 48 text, and the file keeps its comments and key order when it is written back.
49 49
@@ -7,9 +7,9 @@
7 7
8 8 yazi
9 9
10 - A two-pane file manager in the terminal, with previews. Under shop it renders
10 + A two-pane file manager in the terminal, with previews. Under rio it renders
11 11 images inline using the kitty graphics protocol rather than falling back to
12 - coloured blocks, which is most of what the terminal is asked for here.
12 + coloured blocks, which is most of the reason rio is the terminal.
13 13
14 14 `?` lists its keys. `q` leaves.
15 15
@@ -111,6 +111,6 @@
111 111 ## Terminal, editor, and everything else
112 112
113 113 Each adopted tool has its own themed config under `~/.config/`, rendered from
114 - the same palette: shop, helix, yazi, mako, swaylock, satty, bottom, zathura,
114 + the same palette: rio, helix, yazi, mako, swaylock, satty, bottom, zathura,
115 115 imv, mpv, swayosd, and the sway config itself. Editing one is fine; the theme
116 116 system will notice and stop overwriting it.
@@ -24,13 +24,9 @@
24 24
25 25 ### Nothing opens when I press Mod+Return
26 26
27 - Check the config's terminal name. `~/.config/sway/config` sets `$term shop`,
28 - and if that line has been edited to something the image does not carry, the
29 - binding runs nothing and says nothing.
30 -
31 - `shop` is built into the image rather than installed from a repo, so the
32 - package manager cannot tell you whether it is there. `command -v shop` from a
33 - virtual console can.
27 + Check the config's terminal name. Fedora packages the terminal as `rio` but
28 + installs its binary as `rioterm`, and a config naming `rio` leaves you with no
29 + way to open a terminal at all.
34 30
35 31 Two ways out that do not need a terminal: `Mod+D` opens the launcher, and
36 32 `Ctrl+Alt+F2` gets you a virtual console where you can edit
@@ -65,9 +65,9 @@
65 65 [ -n "$cmd" ] || exit 0
66 66
67 67 # TUI entries get a terminal to live in. $TERMINAL so this stays true if the
68 - # terminal pick ever changes; shop is the STACK.md default.
68 + # terminal pick ever changes; rioterm is the STACK.md default.
69 69 if [ "$needs_term" = "1" ]; then
70 - cmd="${TERMINAL:-shop} -e $cmd"
70 + cmd="${TERMINAL:-rioterm} -e $cmd"
71 71 fi
72 72
73 73 # swaymsg runs it through sh, so a full Exec line keeps its arguments. Outside