Hebrew fallback: Ivrix Mono He (uniform advance) + bundle Resources/Fonts - #2
Conversation
Pin: DananzMolt/ghostty @ ivrix-bidi-sync (26e85d068)
Resources/Info.plist sets ATSApplicationFontsPath = Fonts, but nothing copied that directory during a normal Xcode build - only build-ivrix.sh did, at release packaging time. So Debug builds shipped a dangling font path and silently fell back to system fonts for both the Latin mono and the Hebrew face, making dev-time font work impossible to verify. Adds Resources/Fonts as a folder reference in the app's Resources build phase so every build bundles it.
Miriam Mono CLM drew Hebrew letters at 0.482em. Maple Mono NF's Latin x-height is 0.550em and its cap height 0.730em, so Hebrew rendered smaller than the surrounding lowercase Latin - the reason it read as thin and unclear rather than any quality of the typeface itself. Rubik draws Hebrew at 0.572em (1.04x that x-height, 0.78x cap) with a 0.606em advance against the 0.600em cell, so it fills the grid without crowding it. It is proportional rather than monospace, which is fine here: it is a fallback face and the terminal snaps glyphs to the cell regardless. Measured alternatives: Heebo (0.581em height but 0.644em advance, 7% over the cell), Noto Sans Hebrew (0.615em advance), Miriam Libre (0.613em), Assistant (0.552em height, barely above the old font), Alef (0.530em, still under the Latin x-height). Covers all 27 Hebrew letters plus 17 nikud marks. Regular/Bold/Italic/ BoldItalic instantiated from the upstream variable font. Bundles the OFL text alongside, as the license requires.
Rubik is proportional, and the terminal gives every Hebrew letter exactly one cell. Yod advances 0.213em inside a 0.600em cell, so a third of the cell was empty; across the alphabet Rubik's advances vary 24%. The result was per-letter gaps that read as broken text. This is not fixable by picking a different proportional face. Measured advance variation across the Hebrew alphabet: Miriam Mono CLM 0.0% (0.600 flat - true monospace) Alef 18.1% Miriam Libre 21.5% Heebo 23.3% Rubik 24.2% Noto Sans Hebrew 24.5% Assistant 24.6% Miriam Mono CLM was the only uniform one, but it drew Hebrew at 0.482em, below Maple Mono's 0.550em Latin x-height, which is what made it look small and thin in the first place. Ivrix Mono He takes Rubik, subsets it to the Hebrew block, and centres every spacing glyph on a uniform 0.600em advance, keeping Rubik's 0.572em height. Combining marks (nikud) keep their zero advance so they stack on the base letter instead of consuming a cell. Rubik carries no Reserved Font Name, so the OFL permits the derivative; the name table records the origin and modification, and the OFL text ships alongside. Subsetting also drops the files from 212KB to 10KB each, since Maple Mono owns Latin. Verified in the running app: letters now sit on an even rhythm with no inter-letter gaps.
|
Follow-up after testing in the app: plain Rubik was wrong. The terminal gives every Hebrew letter exactly one cell, and Rubik is proportional — yod advances 0.213em inside a 0.600em cell, so a third of the cell sat empty. Advances vary 24% across the alphabet, producing per-letter gaps that read as broken text. No proportional face fixes this. Measured advance variation:
Miriam Mono was the only uniform option but drew Hebrew at 0.482em — under Maple Mono's 0.550em Latin x-height — which is exactly what made it look small. Ivrix Mono He takes Rubik, subsets it to the Hebrew block, and centres every spacing glyph on a uniform 0.600em advance while keeping Rubik's 0.572em height. Nikud keep zero advance so they stack rather than consume a cell. Subsetting drops each file 212KB → 10KB. Verified in the running app (not just rasterization this time): |
Swaps the source face for Ivrix Mono He from Rubik to Miriam Libre, kept at the same 0.572em letter height and uniform 0.600em advance, so this is purely a change of letterforms. Picked from a 13-face sampler rendered at terminal size with every candidate normalised to the same cell and height, so the comparison was of shape alone. Miriam Libre is a Hebrew-first design rather than a Hebrew extension of a Latin family, and its 0.549em maximum ink width leaves more room inside the 0.600em cell than Heebo (0.617em) or IBM Plex Sans Hebrew (0.604em). Miriam Libre ships no italic, so the oblique styles are synthesised with a 12 degree shear - the same approach Miriam Mono CLM took with its Oblique cuts. Nikud keep their zero advance. No Reserved Font Name in the upstream OFL, so the derivative is permitted; the name table records origin and modification and the OFL text ships alongside.
scripts/notarize-ivrix.sh only works off a published GitHub release, so there was no way to cut a signed DMG from a branch build. This packages whatever scripts/build-ivrix.sh just staged. Notarizes twice on purpose: the app is notarized and stapled so it still launches cleanly once dragged out of the image, and the DMG is notarized and stapled so the image itself opens without a Gatekeeper prompt. Signing is inside-out, reusing the fix from notarize-ivrix.sh: a single codesign --deep leaves the nested executables under Contents/Resources/bin unsigned and notarization rejects them.
Pin: DananzMolt/ghostty @ ivrix-bidi-sync (51ac56c55)
Pin: DananzMolt/ghostty @ ivrix-bidi-sync (90b1ddc83)
Pin: DananzMolt/ghostty @ ivrix-bidi-sync (7ac8c8854)
Pin: DananzMolt/ghostty @ ivrix-bidi-sync (eb3e384b8)
Pin: DananzMolt/ghostty @ ivrix-bidi-sync (d6d379ef2)
Bundles twelve Hebrew faces and adds a picker under Settings > Terminal, so trying a different face no longer means rebuilding the app. Every face is the same source put through scripts/make-hebrew-font.py: subset to the Hebrew block and normalised to a uniform 0.600em advance matching the Latin cell. Switching therefore changes letterforms only, never the grid, and none of them can reintroduce the per-letter gaps that a proportional face produces in a terminal. HebrewFontFace lives in CmuxSettings next to SessionContentAlignment so the settings window and the terminal share one definition rather than duplicating the list. The catalog key also makes the face selectable from ~/.config/cmux/cmux.json. The settings UI writes UserDefaults directly, so the change has to be bridged: KeyboardShortcutSettingsFileStore now posts the reload notification for terminal.hebrewFont the same way it does for terminal.copyOnSelect. Without that the face would only apply on the next launch. Faces are registered in the settings search index with their names as keywords, so searching 'noto' or 'alef' finds the row. Adds ~400KB total across twelve families - Maple Mono alone is 9MB - and each ships its upstream OFL text with the origin recorded in the font's name table, as the licences require.
The settings window writes UserDefaults through its own UserDefaultsSettingsStore, not the managed-defaults path that posts TerminalHebrewFontSettings.didChangeNotification. Picking a face therefore persisted the choice but left every open terminal on the old font until the next launch. Watch UserDefaults directly as well. didChangeNotification fires for any key, so the handler compares against the face the surface already applied; without that comparison every unrelated settings write would rebuild the font grid on every surface. The notification path stays for setFace() and for edits made through ~/.config/cmux/cmux.json.
Pin: DananzMolt/ghostty @ ivrix-bidi-sync (06a5d7a55)
Pin: DananzMolt/ghostty @ ivrix-bidi-sync (f7479c629)
Binds Cmd+Z to readline's undo (Ctrl+_, 0x1f), which shells and readline-style TUIs act on. Ghostty unescapes text bindings before writing, so the control byte is sent rather than the literal characters. This undoes an edit on the line being typed - it cannot un-run a command already submitted and does not touch terminal output. Anything that ignores 0x1f does nothing, which is what the key did before. Note: cmux policy is that new shortcuts also appear in KeyboardShortcutSettings so they can be remapped. This is a ghostty keybind, so it works but is not yet remappable there.
Documents the fork's Hebrew work: right-to-left rendering and the direction toggle, bidi-aware cursor placement, UAX manaflow-ai#9 glyph mirroring, direction-aware arrow keys, the twelve selectable Hebrew faces, and Cmd+Z undo. Records the constraints too - the uniform-advance normalisation the faces need, and what Cmd+Z can and cannot undo.
The README was still upstream cmux's, so nothing on the repo said what this fork is for or would surface for anyone searching for a Hebrew terminal. Leads with what Ivrix does differently - per-line RTL rendering, a bidi-aware cursor, mirrored glyphs, direction-aware arrows, and the bundled Hebrew faces - then hands off to cmux's own feature tour, which Ivrix inherits unchanged. Upstream cmux and Ghostty are credited at the top and in the footer, and the fonts' OFL attribution is noted. Includes a Hebrew line and Hebrew search terms: someone looking for this is at least as likely to search in Hebrew as in English.
The inherited section still offered a cmux download badge, so someone skimming the page could install upstream cmux and wonder where the Hebrew support went. Labels that section as upstream and points back to the Ivrix release.
Replaces the Hebrew fallback face and fixes the reason it could never be checked during development.
Font
Miriam Mono CLMdrew Hebrew at 0.482em. Maple Mono NF's Latin x-height is 0.550em (cap 0.730em), so Hebrew rendered smaller than the surrounding lowercase Latin. That size mismatch, not the typeface's design, is why it read as thin and unclear.Rubikdraws Hebrew at 0.572em (1.04x that x-height) with a 0.606em advance against the 0.600em cell — it fills the grid without crowding it. Proportional rather than monospace, which is fine for a fallback face since the terminal snaps glyphs to the cell anyway.Measured and rejected:
Covers all 27 Hebrew letters + 17 nikud. Regular/Bold/Italic/BoldItalic instantiated from the upstream variable font with correct family naming. OFL text bundled as the license requires.
Bundling fix (separate commit)
Resources/Info.plistsetsATSApplicationFontsPath = Fonts, but nothing copied that directory during a normal Xcode build — onlybuild-ivrix.shdid, at release packaging. Debug builds therefore shipped a dangling font path and silently fell back to system fonts for both the Latin mono and the Hebrew face, so no dev build ever rendered with the intended fonts. AddedResources/Fontsas a folder reference in the Resources build phase.Verified: app builds, bundle now contains all 9 font files + license.
Not verified
Rendering was confirmed by direct font rasterization at terminal size, not through Ghostty's renderer — the tagged dev app hung at launch behind what appears to be a first-launch dialog for its bundle ID.