# The house glyph set: the durable artifact this pipeline exists to carry. # # Every entry is a mark rather than a letter. Letters, digits and punctuation are # the base face's job and are meant to differ per slot; marks carry meaning, and # the meaning does not change between a terminal, a webview and an egui panel, so # neither should the drawing. # # Sizes are never absolute units, so a glyph is refitted to the next base rather # than redrawn for it. Every dimension is # # band extent x band + base stroke x weight # # and both terms are measured off the base itself (see `quasi-type params`): # # band the symbol band, read off `+`: the extent the base fits its own # symbols into, horizontally and vertically. # stroke `-`'s bbox height, the base's horizontal stroke weight. # stem `|`'s bbox width, the base's vertical stroke weight. # advance the cell width. Marks centre on advance/2, not on the band. # # A dimension with no weight term is written as a bare number. # # WHY THE WEIGHT TERM EXISTS, measured 2026-08-16 across Plex Mono's two weights. # The base redraws its symbols heavier in Bold rather than only thickening them # in place: `•` runs 244 to 308 units wide, `×` 424 to 460, `↑` 414 to 478, and # `✓`, `❌` and `◊` all grow too. What holds still is `█` and `░`, and those are # cell-fill primitives rather than symbols. `+` is the other pattern: identical # bbox in both weights, heavier bars inside it. # # So the set follows the base in both, and which one a mark follows is a property # of the mark rather than a setting: # # solid marks (the triangles) have no stroke to thicken, so they grow. # stroked marks (the X, the box, the return arrow) grow a little and thicken, # which is the pattern `x` and `+` follow in the base. # # Sizing off the band alone would leave a mark at Regular's size inside a Bold # face, reading light among its neighbours. That was this file's first rule and # it was wrong: it generalised from the block elements, which are the one thing # in the base that does not answer weight at all. # # ON A VARIABLE BASE, the weight term stops being sampled at two points and # becomes the axis itself. A cut from a variable base keeps that axis, so every # mark is drawn once per master — the axis default and each end — and the # differences ship as `gvar` deltas. Nothing here changes: the recipe is already # parametric in the base's measurements, and a master is the same recipe read at # another location. # # What it puts on every recipe, including the generators the box-drawing set will # need, is one rule: **a mark's point count and contour order cannot depend on # the measurements**. Deltas are per point, so two masters of one glyph have to # be the same polygon at different sizes. A recipe that grew a contour past some # threshold would interpolate into a different shape halfway along the axis. The # pipeline refuses that case rather than shipping it, and a generator that emits # a fixed topology never meets it. # # Adding a glyph is an edit here plus a re-run against every face that consumes # the set. Nothing downstream needs touching. [set] # Bumped to 2 on 2026-08-17: `←` and four more triangles. A face's identity is # (base, set version), so a face carrying a set that grew is a different face # and says so in its own name table. version = 2 name = "quasi house glyph set" # --------------------------------------------------------------------------- # Sort carets. Decision 591fca15 unified the three renderers on the triangles; # until this set shipped, a webview drawing one fell back per glyph to whatever # the OS ordered first, so the "unified" caret was three platforms' opinions. # --------------------------------------------------------------------------- [[glyph]] codepoint = 0x25B2 name = "uni25B2" role = "sort caret, ascending" source = "decision 591fca15" shape = "triangle" direction = "up" # Across the base edge, off the band's width. span = { band = 0.75, weight = 0.62 } # Apex distance as a ratio of span. 0.866 is equilateral, at every weight. depth = 0.866 anchor = "band-center" [[glyph]] codepoint = 0x25BC name = "uni25BC" role = "sort caret, descending" source = "decision 591fca15" shape = "triangle" direction = "down" span = { band = 0.75, weight = 0.62 } depth = 0.866 anchor = "band-center" # The rest of the triangle run, on the same footing as `←` below: nothing in # Alloy emits them, and ratatui's scroll and selection markers reach for them, # so a face drawing two of the run and leaving four to a fallback is a row of # markers at two weights. Same recipe as the carets above and the same # proportions, rotated; `▾` takes the small separators'. # # `▶` and `▸` differ in size and `▶` and `►` do not differ at all here. Unicode # separates TRIANGLE from POINTER by intent rather than by shape, and a house set # that drew them differently would be inventing a distinction no consumer makes. # --------------------------------------------------------------------------- [[glyph]] codepoint = 0x25B6 name = "uni25B6" role = "scroll marker, right" source = "alloy widget ceiling, measured 2026-08-17" purpose = "coverage" shape = "triangle" direction = "right" span = { band = 0.75, weight = 0.62 } depth = 0.866 anchor = "band-center" [[glyph]] codepoint = 0x25C4 name = "uni25C4" role = "scroll marker, left" source = "alloy widget ceiling, measured 2026-08-17" purpose = "coverage" shape = "triangle" direction = "left" span = { band = 0.75, weight = 0.62 } depth = 0.866 anchor = "band-center" [[glyph]] codepoint = 0x25BA name = "uni25BA" role = "scroll marker, right pointer" source = "alloy widget ceiling, measured 2026-08-17" purpose = "coverage" shape = "triangle" direction = "right" span = { band = 0.75, weight = 0.62 } depth = 0.866 anchor = "band-center" [[glyph]] codepoint = 0x25BE name = "uni25BE" role = "scroll marker, down small" source = "alloy widget ceiling, measured 2026-08-17" purpose = "coverage" shape = "triangle" direction = "down" span = { band = 0.50, weight = 0.50 } depth = 0.75 anchor = "band-center" # --------------------------------------------------------------------------- # Starship segment separators and error state. # --------------------------------------------------------------------------- [[glyph]] codepoint = 0x25B8 name = "uni25B8" role = "starship, segment separator" shape = "triangle" direction = "right" # Unicode calls these SMALL, and they sit inline in a prompt. Span is the # vertical extent for a horizontal mark, so it comes off the band's height. span = { band = 0.50, weight = 0.50 } depth = 0.75 anchor = "band-center" [[glyph]] codepoint = 0x25C2 name = "uni25C2" role = "starship, segment separator" shape = "triangle" direction = "left" span = { band = 0.50, weight = 0.50 } depth = 0.75 anchor = "band-center" [[glyph]] codepoint = 0x2718 name = "uni2718" role = "starship, error state" shape = "cross" width = { band = 0.70, weight = 0.50 } height = { band = 0.70, weight = 0.50 } # Calibrated against the base's own cross rather than by eye: HEAVY BALLOT X is # heavier than `×` and not far heavier, so the number is read off the base each # time rather than carried over. A flat 1.5x read as a blob. # # RECALIBRATED 2026-08-17, adopting Atkinson, and this is the coefficient that # does not survive a base change. It was 0.80 against Plex Mono, whose `×` # (U+00D7) fills 38.2% of its own box in Regular and 53.4% in Bold, and ours # filled 40.7% and 55.5% — the margin the rule asks for. Atkinson's bar is # thinner against its own cross, so 0.80 came out *under* the base at every # weight (29.0% against 30.0%, 53.6% against 56.9%), which is a ballot X lighter # than the multiplication sign beside it. At 0.95 of the base's bar it fills # 33.7% and 59.1% against the base's 30.0% and 56.9%. stroke = 0.95 # --------------------------------------------------------------------------- # Helix whitespace renders. Both sit low in the cell, next to lowercase. # --------------------------------------------------------------------------- [[glyph]] codepoint = 0x2423 name = "uni2423" role = "helix, space render" shape = "open-box" # RECALIBRATED 2026-08-17, adopting Atkinson. The weight terms were 0.30 and the # box closed up: Atkinson's stem runs 54 units to 158 across `wght` 200-800 # against Plex's 1.8x from Regular to Bold, so at the heavy end two risers of # the base's own stroke took 278 of a 413-unit box and left a 135-unit counter. # Measured 0.906 fill of its own bounding box, which rasterises as a blob rather # than as a box, and the proof sheet is where it was seen (`quasi-type proof`). # # The rule the numbers now hold, and the one to keep when the next base arrives: # AN OPEN BOX HAS TO STAY OPEN. Its counter is what makes it read as a box, so # the box grows with the base's stroke rather than the stroke eating the box. # The aperture cannot be held exactly constant — the band term does not scale # with the stroke — so it is fitted at the heavy end, where it is scarce: the # counter is 50% of the box's width at `wght` 800 and 74% at 200. width = { band = 0.72, weight = 1.35 } height = { band = 0.38, weight = 0.60 } # Above the baseline, as a ratio of the band's height. bottom = 0.04 # A stroked mark: its bars are the base's own, unmultiplied. stroke = 1.0 [[glyph]] codepoint = 0x23CE name = "uni23CE" role = "helix, newline render" shape = "return-arrow" # Height of the riser's flat cap, as a ratio of the band's height. top = 0.92 # Centre of the shaft, as a ratio of the band's height above its floor. shaft = 0.26 # The head is solid, so it takes a weight term. Its coefficient is larger than # the triangles' because the head is measured against the shaft it sits on # rather than against the band: at { 0.36, 0.30 } the Bold head cleared its own # stroke by 38 units and read as a nub rather than an arrow. head_span = { band = 0.20, weight = 1.35 } head_depth = { band = 0.26, weight = 0.20 } stroke = 1.0 # --------------------------------------------------------------------------- # The arrows. Alloy's own surfaces emit four of these (docs/FONTS.md, measured # against the built image), and a base can ship a full text face without one: # Plex Mono draws all four, Atkinson Hyperlegible Mono draws none. # # One recipe, five entries. The head is the same drawing as the sort caret two # sections up, which is the point of a house set: a triangle in a status line # and a triangle on an arrow should not be two people's triangles. # # `←` is here on a different footing from the other four, and it is worth saying # which: nothing in Alloy emits it, but ratatui draws scrollbar ends with the # whole U+2190-U+2193 run, so a face carrying three of the four leaves the # fourth to a fallback — one arrow in a row of four at another face's weight and # another face's baseline. Measured 2026-08-17 against Alloy's widget ceiling # while adopting Atkinson; the old base drew all four and hid it. # --------------------------------------------------------------------------- [[glyph]] codepoint = 0x2190 name = "uni2190" role = "leftwards arrow" source = "alloy widget ceiling, measured 2026-08-17" purpose = "coverage" shape = "arrow" direction = "left" length = 0.92 head_span = 0.42 head_depth = 0.9 stroke = 1.0 [[glyph]] codepoint = 0x2191 name = "uni2191" role = "upwards arrow" source = "alloy docs/FONTS.md, the measured surface" purpose = "coverage" shape = "arrow" direction = "up" length = 0.92 head_span = 0.42 head_depth = 0.9 stroke = 1.0 [[glyph]] codepoint = 0x2193 name = "uni2193" role = "downwards arrow" source = "alloy docs/FONTS.md, the measured surface" purpose = "coverage" shape = "arrow" direction = "down" length = 0.92 head_span = 0.42 head_depth = 0.9 stroke = 1.0 [[glyph]] codepoint = 0x2192 name = "uni2192" role = "rightwards arrow" source = "alloy docs/FONTS.md, the measured surface" purpose = "coverage" shape = "arrow" direction = "right" length = 0.92 head_span = 0.42 head_depth = 0.9 stroke = 1.0 # Two heads on one shaft. The only member of the set that is not a single # gesture, and the reason `both_ends` exists rather than a second shape. # # Its head is smaller than the single-ended arrows above, and that is the # double end paying for itself rather than a taste call: at their proportions # two heads take 82% of the length and meet in the middle, so the mark reads as # a bowtie and not as an arrow with a shaft. Measured off the drawing, not # guessed — see `an_arrow_with_two_heads_still_has_a_shaft`. [[glyph]] codepoint = 0x2195 name = "uni2195" role = "up-down arrow" source = "alloy docs/FONTS.md, the measured surface" purpose = "coverage" shape = "arrow" direction = "up" both_ends = true length = 0.98 head_span = 0.34 head_depth = 0.75 stroke = 1.0 # --------------------------------------------------------------------------- # The cell tier. Box drawing and block elements, generated rather than authored: # their 160 recipes are already written down in Unicode's own character names, # and they must be cell-exact to tile, which is arithmetic rather than drawing. # See `src/cells` for the geometry and `scripts/derive-cell-table.py` for how # the table is derived. Asked for by block so this file stays a record of what # somebody chose rather than 160 stanzas of transcription. # # They are here because a base can be chosen for the quality of its text and # ship neither block: Atkinson Hyperlegible Mono has 0/128 box drawing and 0/32 # block elements, measured. Fallback cannot cover the gap, because a terminal # takes its baseline from the primary face alone, so borders arriving from a # fallback face with unrelated cell metrics draw off the line — broken borders # rather than tofu. # --------------------------------------------------------------------------- [[generated]] block = "box-drawing" [[generated]] block = "block-elements"