max / alloy
13 files changed,
+49 insertions,
-111 deletions
| @@ -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. |
| @@ -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. |
| @@ -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 |