Skip to main content

max / shop

Strip historical narrative from documentation Remove what a doc used to say, when it changed, the incidents that justified a rule, finished migration narration, and counts and versions that rot. State the rules in the present tense instead. Keep every instruction, prohibition and threshold, and keep the measurements that make a rule actionable. Public-facing docs keep their explanatory voice.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session
https://claude.ai/code/session_01MptwXZ8k65v19rFmdGAyki
Author: Max Johnson <me@maxj.phd> · 2026-08-31 01:52 UTC
Signed with PGP, not checked
Commit: f0f50f5d52afcfa2c51c9ebd7f48be8f92ae0826
Parent: 2372f87
22 files changed, +141 insertions, -203 deletions
M ATTRIBUTIONS.md +2 -3
@@ -3,13 +3,12 @@
3 3 Third-party code adapted into this workspace. Pinned to the version read at
4 4 adaptation time; refresh when the source is re-consulted.
5 5
6 - None yet — this file will populate as files are lifted from external sources.
6 + None yet. This file populates as files are lifted from external sources.
7 7
8 8 ## Reference reads (not adapted, but consulted during design)
9 9
10 10 - [rio](https://github.com/raphamorim/rio) v0.4.12 — MIT. Architectural read
11 - notes at `~/Wiki/rio-source-notes.md`. Rio's file structure and design
12 - choices inform shop's crate layout; no rio code is currently vendored.
11 + notes at `~/Wiki/rio-source-notes.md`. No rio code is vendored.
13 12 - [foot](https://codeberg.org/dnkl/foot) — MIT. Spirit reference for the
14 13 "Wayland-only, no built-in multiplexing" design.
15 14 - [alacritty_terminal](https://github.com/alacritty/alacritty) — Apache-2.0.
M CONTRIBUTING.md +3 -4
@@ -1,6 +1,6 @@
1 1 # Contributing
2 2
3 - Solo project. External contributions aren't accepted yet — the design is still
3 + Solo project. External contributions aren't accepted yet; the design is still
4 4 moving. When that changes this file will grow.
5 5
6 6 ## Working in this repo
@@ -19,11 +19,10 @@
19 19
20 20 ## Adding a component crate
21 21
22 - Internal crates are scaffolded when they start, not up front. When you begin
23 - `shop-vt`:
22 + Internal crates are scaffolded when they start, not up front:
24 23
25 24 ```
26 - cargo new --lib crates/shop-vt
25 + cargo new --lib crates/<name>
27 26 ```
28 27
29 28 Then add it to `Cargo.toml`'s `[workspace] members`, add the `[lints]
M README.md +11 -15
@@ -98,7 +98,7 @@
98 98 scrollback_lines = 10000
99 99
100 100 # Which chords shop keeps for itself. Shop sees a key before the program
101 - # inside it does, so anything bound here never reaches the shell -- or tmux.
101 + # inside it does, so anything bound here never reaches the shell, or tmux.
102 102 # Every default can be moved, and `none` gives the key back entirely.
103 103 [keys]
104 104 scroll_page_up = "shift+page_up"
@@ -146,8 +146,7 @@
146 146
147 147 The alt screen has none, by definition: a program that takes the whole window
148 148 repaints it, so a row leaving the top is overdraw rather than history. A wheel
149 - turn inside vim or htop does nothing for now — translating it into arrow keys
150 - is a separate question.
149 + turn inside vim or htop does nothing.
151 150
152 151 A resize rewraps scrollback. Each history row records whether it ran off the
153 152 right edge, so a run of those rows plus the one ending it is a logical line the
@@ -205,11 +204,8 @@
205 204 the first build in a fresh checkout needs network and the rest do not. A font in
206 205 the repo would be a second source of truth that nothing rebuilds.
207 206
208 - It replaced a nerd-patched IosevkaTerm, 13 MB committed in `assets/` against 70
209 - KB cut here. What that costs is braille and the legacy-computing sextants, which
210 - now come from fallback; nothing shop draws for itself emits either. What it buys
211 - is that the marks are the same drawings the rest of the house uses, rather than
212 - whichever face the OS happened to order first.
207 + Braille and the legacy-computing sextants are not in it and come from fallback;
208 + nothing shop draws for itself emits either.
213 209
214 210 The face is variable, `wght` 200 to 800 in one file, and **its own default is the
215 211 light end**. Shop names the weight it draws at (`shop_font::WEIGHT`) rather than
@@ -228,14 +224,14 @@
228 224 row of mixed scripts wander.
229 225
230 226 Ligatures are a different thing and shop does not do them. `>=` drawn as one
231 - connected glyph is the font's business, not the buffer's — the two characters
232 - still occupy two cells — and a ligature makes a cursor sitting between them land
227 + connected glyph is the font's business, not the buffer's: the two characters
228 + still occupy two cells, and a ligature makes a cursor sitting between them land
233 229 visually inside a glyph. Column-accurate editing wins that trade.
234 230
235 231 Colour emoji are not drawn either, and that is a decision rather than a gap.
236 232 Drawing them means a second atlas in colour, glyphs that carry their own colour
237 233 instead of taking the cell's, and bitmap strikes at a fixed size that is never
238 - the cell's — a colour pipeline running the whole length of the renderer to put
234 + the cell's: a colour pipeline running the whole length of the renderer to put
239 235 pictures in a terminal. An emoji still takes the columns it is owed, so text
240 236 around one stays where it belongs and a line of somebody else's output is read
241 237 exactly as it was written. The cell is simply empty.
@@ -251,8 +247,8 @@
251 247 selection; both are actions like any other and ship unbound, because every
252 248 default binding is a key the program inside shop never sees.
253 249
254 - What opens it is the `[emit] runner`. Shop is the bottom of the stack -- it *is*
255 - the thing that runs programs -- so it cannot shell out without answering "and
250 + What opens it is the `[emit] runner`. Shop is the bottom of the stack: it *is*
251 + the thing that runs programs, so it cannot shell out without answering "and
256 252 where does the output appear?", and that answer is yours. One key, and shop
257 253 carries no code for any particular multiplexer. Shop does not read `$TMUX` and
258 254 quietly behave differently; a window that opens somewhere surprising should be
@@ -261,8 +257,8 @@
261 257 The region goes to a file and `{file}` is substituted into each argument, rather
262 258 than being piped: stdin does not survive `tmux display-popup` or a detached
263 259 window, so a pipe would work for exactly one runner. The file lands in
264 - `$XDG_RUNTIME_DIR` and is not deleted -- an editor can outlive the terminal that
265 - opened it.
260 + `$XDG_RUNTIME_DIR` and is not deleted, because an editor can outlive the
261 + terminal that opened it.
266 262
267 263 It only goes one way. Kakoune's `|` replaces the selection with the program's
268 264 output; shop's primitive is `>`. The scrollback is a transcript of something
@@ -1,28 +1,26 @@
1 1 //! Cut the bundled face, rather than commit one.
2 2 //!
3 - //! shop used to carry a 13 MB IosevkaTerm in `assets/`, committed. The house
4 - //! face is cut from a pinned base by `quasi-type`, and cutting it here is what
5 - //! keeps the glyph set and the shipped face from drifting apart: a face in the
6 - //! repo would be a second source of truth that nothing rebuilds.
3 + //! The house face is cut from a pinned base by `quasi-type`, and cutting it
4 + //! here is what keeps the glyph set and the shipped face from drifting apart: a
5 + //! face in the repo would be a second source of truth that nothing rebuilds.
7 6 //!
8 7 //! The base is downloaded once into `OUT_DIR` and checksummed against
9 8 //! `quasi-type`'s pins, so a warm target directory needs no network.
10 9 //!
11 10 //! A cold one reaches raw.githubusercontent.com, which rate-limits by IP and
12 - //! took Alloy's image build down on 2026-08-17. `QUASI_TYPE_MIRROR` in the
13 - //! environment names a base URL that is tried first, with the pinned url as the
14 - //! fallback; every file is addressed under it by the sha256 the pin already
15 - //! carries, so a mirror is a second source rather than a second thing to trust.
16 - //! Nothing here reads it -- it is `quasi-type`'s own knob, and setting it is
17 - //! what lets a build host cover this fetch without shop changing.
11 + //! can fail a container image build. `QUASI_TYPE_MIRROR` in the environment
12 + //! names a base URL that is tried first, with the pinned url as the fallback;
13 + //! every file is addressed under it by the sha256 the pin already carries, so a
14 + //! mirror is a second source rather than a second thing to trust. Nothing here
15 + //! reads it: it is `quasi-type`'s own knob, and setting it is what lets a build
16 + //! host cover this fetch without shop changing.
18 17
19 18 fn main() {
20 19 println!("cargo:rerun-if-changed=build.rs");
21 20
22 21 let out = std::path::PathBuf::from(std::env::var("OUT_DIR").expect("cargo sets OUT_DIR"));
23 22 // cut_native writes the ttf and the OFL text and hands back what has to be
24 - // exported beside them. It used to be written out here, and every other
25 - // native consumer would have copied it from here.
23 + // exported beside them.
26 24 let written = match quasi_type::cut_native(
27 25 &out,
28 26 &out.join("bases"),