Skip to content

[WIP] ivrix-bidi-hyphen-fix - #6

Draft
DananzMolt wants to merge 1950 commits into
mainfrom
ivrix-bidi-hyphen-fix
Draft

[WIP] ivrix-bidi-hyphen-fix#6
DananzMolt wants to merge 1950 commits into
mainfrom
ivrix-bidi-hyphen-fix

Conversation

@DananzMolt

Copy link
Copy Markdown
Owner

Parked by open-wip-prs.sh on 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

austinywang and others added 30 commits July 24, 2026 02:13
* 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
Tomer Danan and others added 30 commits July 26, 2026 15:46
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants