[WIP] ivrix-bidi-hyphen-fix - #6
Draft
DananzMolt wants to merge 1950 commits into
Draft
Conversation
* Add regression test for same-display window cutoff * Fit every restored main window to visible displays * test: preserve visible frames across display replacement * fix: preserve visible frames on replacement displays --------- Co-authored-by: cmux reload-cloud <cmux-reload-cloud@users.noreply.github.com>
* test: cover Dock session persistence * feat: persist Dock sessions * test: distinguish Dock snapshot scopes
…-regression # Conflicts: # docs/ghostty-fork.md
…-resumability Retry cloud provider reconnects and refine shared rails
Add a [LTR|RTL] segmented control to the window toolbar that flips the terminal print direction live: - TerminalTextDirectionSettings: persisted ltr/rtl + change notification; emits the ghostty bidi-direction config line. - Bake bidi-direction into the surface config; reload live on change (same path as copy-on-select). - WindowToolbarController: segmented control, action sets the setting, syncs on focus + change notification. - Localize the 4 new strings across all supported locales.
The WindowToolbarController NSToolbar is dead code (never instantiated), so the toggle never appeared. Add the direction button to TitlebarControlsView.controlsGroup (the real sidebar/bell/+/back-forward row), reacting to TerminalTextDirectionSettings.didChangeNotification; icon flips text.alignleft/right.
…lease repo at DananzMolt/Ivrix A single `codesign --deep` does not sign nested executables under Contents/Resources/bin, so notarization rejected them as unsigned. Sign inside-out per Apple's docs: helpers, plugins, frameworks, then the main bundle without --deep. Also updates RELEASE_REPO after the cmux -> Ivrix repo rename.
Rebases the 5 Ivrix bidi commits onto the ghostty revision that latest cmux pins (af4dfb43), and fixes the uucode bidi_class table collision introduced by upstream's own bidi work. Pin: DananzMolt/ghostty @ ivrix-bidi-sync (1ac5659c0)
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.
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.
Fixes two reordering bugs on RTL rows: - A neutral that resolved LTR (the hyphen in "max-height", or a plain space between two Latin words) was assigned embedding level 0 instead of 2. Level 0 sits below the paragraph level, so UAX manaflow-ai#9 L2 ended its level-1 run there and reordered the two halves separately: "max-height" rendered as "max <hebrew> -height". - Number separators were never resolved as weak types, so "1.5" rendered as "5.1", "3,000" as "000,3", and "192.168.1.1" came out fully reversed. Claude-Session: https://claude.ai/code/session_01TPRwsvsc1t88Wg5LaJ3rXb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Parked by
open-wip-prs.shon 20260802-234157.1 commit(s) that existed only on this machine. Opened so the work can be
continued elsewhere and the local worktree freed.
Resume:
git fetch origin && git switch ivrix-bidi-hyphen-fix