Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
be9f566
chore: bump ghostty pin to prompt selection editing
DananzMolt Jul 28, 2026
a696dc8
chore: bump ghostty pin to arrow-driven selection editing
DananzMolt Jul 28, 2026
6f8a7db
chore: bump ghostty pin to cursor hiding during selection
DananzMolt Jul 28, 2026
f7b88ac
build: name dev builds ivrix-dev in the Dock and app switcher
DananzMolt Jul 28, 2026
9afd626
chore: bump ghostty pin to Shift+arrow selection
DananzMolt Jul 28, 2026
fa9cf9f
chore: bump ghostty pin to Shift selection space fix
DananzMolt Jul 28, 2026
1c347b0
chore: bump ghostty pin to Shift selection anchor-space fix
DananzMolt Jul 28, 2026
cba26cf
chore: bump ghostty pin to RTL mouse position mapping
DananzMolt Jul 28, 2026
03b48df
chore: bump ghostty pin to shift+arrow keybinding fix
DananzMolt Jul 28, 2026
618615d
release: Ivrix 1.1.0
DananzMolt Jul 28, 2026
072609e
feat(update): point the Update Available button at the Ivrix repo
DananzMolt Jul 28, 2026
bce2cd6
release: Ivrix 1.1.1 — the update button updates Ivrix
DananzMolt Jul 28, 2026
8a6f491
fix(release): guard the empty sign_update arg array on bash 3.2
DananzMolt Jul 28, 2026
9ca680f
chore: bump ghostty pin to RTL drag selection fix
DananzMolt Jul 29, 2026
acfd11e
chore: bump ghostty pin to row-width match for mouse inversion
DananzMolt Jul 29, 2026
40521de
chore: bump ghostty pin to bidi mouse instrumentation
DananzMolt Jul 29, 2026
8d06186
chore: bump ghostty pin to logical mouse reporting
DananzMolt Jul 29, 2026
90eb2db
chore: bump ghostty pin to arrow-swap gating
DananzMolt Jul 29, 2026
2ab9ea4
chore: bump ghostty pin to restore arrow mirroring in applications
DananzMolt Jul 29, 2026
6da2d35
chore: bump ghostty pin to arrow hint mirroring
DananzMolt Jul 29, 2026
44dae74
chore: bump ghostty pin to drop mouse debug logging
DananzMolt Jul 29, 2026
0808e7c
release: Ivrix 1.1.2 — RTL fixes for selection and arrows in applicat…
DananzMolt Jul 29, 2026
1c0bf8f
feat(ivrix): toggle the terminal text direction from a keyboard shortcut
DananzMolt Jul 31, 2026
9b2bcea
feat(ivrix): send ASCII quotes when typing on a Hebrew keyboard layout
DananzMolt Jul 31, 2026
1476117
feat(ivrix): show the shortcut in the titlebar direction button tooltip
DananzMolt Jul 31, 2026
dea8417
chore: bump ghostty pin to mark-gated selection editing
DananzMolt Jul 31, 2026
4148426
docs: draft the OSC 133 input-marking ask for Claude Code
DananzMolt Jul 31, 2026
5bdaca0
release: Ivrix 1.1.3 — a shortcut for the direction toggle, ASCII quo…
DananzMolt Jul 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,117 @@

All notable changes to cmux are documented here.

## Ivrix [1.1.3] - 2026-07-31

Typing ergonomics for a Hebrew keyboard: a shortcut for the direction toggle,
and shell quotes that survive the Hebrew layout.

### Added
- **A keyboard shortcut for the text direction.** Flipping between left-to-right
and right-to-left meant reaching for the titlebar button. `Ctrl+Cmd+H` now does
it, and so does View > Toggle Text Direction. Rebind it in Settings > Keyboard
Shortcuts or as `shortcuts.bindings.toggleTextDirection` in `cmux.json`. The
titlebar button's tooltip names the shortcut, and follows a rebind.
- **ASCII quotes when typing on a Hebrew layout.** A Hebrew layout puts geresh
and gershayim (`׳` `״`) on the apostrophe and quote keys, so `echo "hi"` typed
in Hebrew reached the shell as `echo ״hi״` and the shell never saw a quote at
all. Those two characters are now sent as ASCII `'` and `"`. Only for keys you
actually press: pasted text, dictation, and anything inserted programmatically
are untouched. Turn it off in Settings > Terminal to type acronyms such as
צה״ל, which need the real gershayim.

### Changed
- **Selection editing now asks whether input is marked, not which screen is up.**
Selecting text at a prompt and typing over it was refused outright whenever an
application had taken over the screen. That was a proxy for the real
requirement, which is `OSC 133` marking saying which cells are the edit buffer;
without it there is nothing to count arrow keys and deletes against. It now
checks for the marking directly. An application that emits no marks is refused
exactly as before, so nothing changes today: Claude Code, for one, takes the
screen and emits no marks, and its composer stays copy-only until it does.

## Ivrix [1.1.2] - 2026-07-29

Right-to-left fixes for selecting and moving around inside full-screen
applications.

### Fixed
- **Selecting Hebrew inside a TUI highlighted the wrong text.** An application
that takes over the screen addresses cells in its own column order and knows
nothing about the reordering applied when drawing, so it was being told the
column the pointer was physically over and acted on a different cell. Mouse
reporting now sends the logical column.
- **Dragging a selection across Hebrew.** Only the press position was mapped
back through the row's order; the end that follows the pointer, the
autoscroll past the window edge, and the prompt click target were not, so a
drag anchored a logical cell to a visual one and landed mirrored.
- **Row width mismatch** between the renderer's map and the inverse used for
the mouse, which shifted every column on rows where the two differed.
- **Arrow hints now agree with the arrow keys.** With `bidi-direction = rtl`
the arrow keys mirror on right-to-left rows, but an application's own hint
such as `press <-` sits in a Latin run, so UAX #9 left the glyph alone and it
named the opposite of the key that performs it. Horizontal arrows now mirror
on those rows too. Turn off with `bidi-mirror-arrows = false`.

## Ivrix [1.1.1] - 2026-07-28

### Fixed
- **The "Update Available" button now updates Ivrix.** It ran Sparkle against
upstream cmux's release feed, so an Ivrix install was offered upstream's
releases; accepting one replaced Ivrix with cmux and lost the Hebrew build.
The feed now names this repository, and the app carries its own signing key
rather than upstream's, so only Ivrix releases can be offered or installed.
Releases publish a signed `appcast.xml`, without which the feed returned
nothing and no update was ever shown.

Installs of 1.1.0 and earlier carry the old feed and cannot be reached by
this fix. Download 1.1.1 once by hand; updates work from there on.

### Changed
- `scripts/build-ivrix.sh` refuses to build if the updater feed still points
upstream or the signing key is missing or upstream's.

## Ivrix [1.1.0] - 2026-07-28

Selection and text editing at the prompt, and the bidi fixes needed to make
them correct in Hebrew.

### Added
- **Selection editing at the prompt.** Select text and type to replace it, or
press backspace/delete to remove it, the way a text editor behaves. A
terminal has no protocol for this, so the terminal places the shell's cursor
with arrow keys and deletes the selected positions itself. Needs `OSC 133`
input marking; controlled by `selection-edit-at-prompt`.
- **Keyboard selection.** `Shift+Left/Right` selects by character and
`Shift+Option+Left/Right` by word. Direction is resolved per row, so on a
Hebrew line `Shift+Left` extends forward through the text, matching the way
the plain arrows already move.
- **Cursor hides during selection**, since the selection is what the next
keystroke acts on. Controlled by `cursor-hide-while-selecting`.

### Fixed
- **Hyphenated words and numbers no longer scatter on a Hebrew line.** A
neutral character resolving left-to-right inside a right-to-left paragraph
was given embedding level 0 instead of 2, which cut the paragraph run in two
and reordered each half on its own: `max-height` rendered as
`max <hebrew> -height`. Two Latin words separated by a space broke the same
way.
- **Numbers keep their digit order.** Weak types (UAX #9 W1-W7) were never
resolved, so number separators were treated as neutral: `1.5` rendered as
`5.1`, `3,000` as `000,3`, and `192.168.1.1` came out fully reversed.
- **Backgrounds, selection highlight and decorations paint at the visual
column.** Only glyphs went through the bidi map, so on a Hebrew row the text
sat in one place and everything drawn around it sat in another.
- **The mouse lands on the cell under the pointer in Hebrew.** Clicks were
handing a screen column straight in as a logical cell index, so dragging
selected text the user had not dragged over.
- **Wide characters cost one arrow key, not two,** when clicking to move the
cursor past CJK or emoji.

### Changed
- Dev builds identify themselves as `ivrix-dev` rather than the upstream name.
- The app now carries its own version rather than inheriting cmux's.

## Ivrix [1.0.0] - 2026-07-28

Ivrix is a Hebrew-first fork of cmux. This release rebases the fork onto the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ public struct TerminalCatalogSection: SettingCatalogSection {
userDefaultsKey: HebrewFontFace.settingsPath
)

/// Rewrite Hebrew geresh/gershayim to ASCII `'`/`"` while typing.
public let hebrewAsciiQuotes = DefaultsKey<Bool>(
id: "terminal.hebrewAsciiQuotes",
defaultValue: true,
userDefaultsKey: "terminal.hebrewAsciiQuotes"
)

public let copyOnSelect = DefaultsKey<Bool>(
id: "terminal.copyOnSelect",
defaultValue: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ extension ShortcutAction {
case .attachTextBoxFile: return ShortcutStroke(key: "a", command: true, shift: true, option: true)
case .sendCtrlFToTerminal: return nil
case .clearScreenKeepScrollback: return ShortcutStroke(key: "k", command: true, shift: true)
case .toggleTextDirection: return ShortcutStroke(key: "h", command: true, control: true)
case .toggleRightSidebar: return ShortcutStroke(key: "b", command: true, option: true)
case .fileExplorerOpenSelection: return ShortcutStroke(key: "\r")
case .fileExplorerOpenSelectionFinderAlias: return ShortcutStroke(key: "↓", command: true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ public enum ShortcutAction: String, CaseIterable, Sendable, Hashable, SettingCod
case sendCtrlFToTerminal
/// Clears the focused terminal's visible screen while preserving scrollback.
case clearScreenKeepScrollback
/// Ivrix: flips the terminal print direction between LTR and RTL, the same
/// state the titlebar direction control writes.
case toggleTextDirection

// MARK: Panes
case focusLeft
Expand Down Expand Up @@ -194,7 +197,7 @@ extension ShortcutAction {
.newWorkspaceGroup, .groupSelectedWorkspaces, .toggleFocusedWorkspaceGroupCollapsed,
.reopenClosedBrowserPanel, .newSurface, .toggleTerminalCopyMode,
.focusTextBoxInput, .cycleTextBoxSubmitAction, .attachTextBoxFile, .sendCtrlFToTerminal,
.clearScreenKeepScrollback:
.clearScreenKeepScrollback, .toggleTextDirection:
return .navigation
case .focusLeft, .focusRight, .focusUp, .focusDown, .splitRight, .splitDown,
.toggleSplitZoom, .equalizeSplits, .splitBrowserRight, .splitBrowserDown,
Expand Down Expand Up @@ -399,6 +402,8 @@ extension ShortcutAction {
return String(localized: "shortcut.sendCtrlFToTerminal.label", defaultValue: "Send Ctrl-F to Terminal")
case .clearScreenKeepScrollback:
return String(localized: "shortcut.clearScreenKeepScrollback.label", defaultValue: "Clear Screen (Keep Scrollback)")
case .toggleTextDirection:
return String(localized: "shortcut.toggleTextDirection.label", defaultValue: "Toggle Text Direction (LTR/RTL)")
case .focusLeft: return "Focus Pane Left"
case .focusRight: return "Focus Pane Right"
case .focusUp: return "Focus Pane Up"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ extension Array where Element == CuratedSettingEntry {
synonyms: "terminal.scrollSpeed scroll speed multiplier wheel mouse trackpad sensitivity faster slower"
),
.init(section: .terminal, id: "copy-on-select", title: "Copy on Selection", synonyms: "terminal.copyOnSelect copy on selection select clipboard mouse double click triple click iterm"),
.init(section: .terminal, id: "hebrew-ascii-quotes", title: "ASCII Quotes on Hebrew Layout", synonyms: "terminal.hebrewAsciiQuotes hebrew ascii quotes geresh gershayim apostrophe quote punctuation rtl ivrix shell quoting layout"),
.init(section: .terminal, id: "agent-auto-resume", title: "Resume Agent Sessions on Reopen", synonyms: "terminal.autoResumeAgentSessions auto resume restore reopen relaunch quit sessions agents claude code codex opencode rovo dev rovodev toggle"),
.init(section: .terminal, id: "agent-hibernation", title: "Agent Hibernation", synonyms: "terminal.agentHibernation.enabled idle hibernate suspend background agents claude code codex opencode live terminals"),
.init(section: .terminal, id: "agent-hibernation-idle", title: "Hibernate After Idle Seconds", synonyms: "terminal.agentHibernation.idleSeconds idle seconds timeout delay hibernate suspend"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public struct TerminalSection: View {
@State private var scrollBar: DefaultsValueModel<Bool>
@State private var copyOnSelect: DefaultsValueModel<Bool>
@State private var hebrewFont: DefaultsValueModel<HebrewFontFace>
@State private var hebrewAsciiQuotes: DefaultsValueModel<Bool>
@State private var autoResume: DefaultsValueModel<Bool>
@State private var hibernation: DefaultsValueModel<Bool>
@State private var idleSeconds: DefaultsValueModel<Double>
Expand Down Expand Up @@ -51,6 +52,7 @@ public struct TerminalSection: View {
_scrollBar = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.terminal.showScrollBar))
_copyOnSelect = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.terminal.copyOnSelect))
_hebrewFont = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.terminal.hebrewFont))
_hebrewAsciiQuotes = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.terminal.hebrewAsciiQuotes))
_autoResume = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.terminal.autoResumeAgentSessions))
_hibernation = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.terminal.agentHibernationEnabled))
_idleSeconds = State(initialValue: DefaultsValueModel(store: defaultsStore, key: catalog.terminal.agentHibernationIdleSeconds))
Expand Down Expand Up @@ -80,6 +82,7 @@ public struct TerminalSection: View {
scrollBar,
copyOnSelect,
hebrewFont,
hebrewAsciiQuotes,
autoResume,
hibernation,
idleSeconds,
Expand Down Expand Up @@ -339,6 +342,25 @@ public struct TerminalSection: View {
.accessibilityIdentifier("SettingsHebrewFontPicker")
}
SettingsCardDivider()
SettingsCardRow(
configurationReview: .json("terminal.hebrewAsciiQuotes"),
String(localized: "settings.terminal.hebrewAsciiQuotes", defaultValue: "ASCII Quotes on Hebrew Layout"),
subtitle: hebrewAsciiQuotes.current
? String(
localized: "settings.terminal.hebrewAsciiQuotes.subtitleOn",
defaultValue: "Typing geresh or gershayim (׳ ״) on a Hebrew layout sends ASCII ' and \" instead, so shell quoting works."
)
: String(
localized: "settings.terminal.hebrewAsciiQuotes.subtitleOff",
defaultValue: "Hebrew punctuation is sent as typed. Needed for acronyms such as צה״ל; shell quoting will not work from a Hebrew layout."
)
) {
Toggle("", isOn: Binding(get: { hebrewAsciiQuotes.current }, set: { hebrewAsciiQuotes.set($0) }))
.labelsHidden()
.controlSize(.small)
.accessibilityIdentifier("SettingsTerminalHebrewAsciiQuotesToggle")
}
SettingsCardDivider()
SettingsCardRow(
configurationReview: .json("terminal.scrollSpeed"),
String(localized: "settings.terminal.scrollSpeed", defaultValue: "Scroll Speed"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ struct SettingsRowAnchorResolutionTests {
"terminal.rendererRealization.maxWarmRenderers",
"terminal.autoResumeAgentSessions",
"terminal.copyOnSelect",
"terminal.hebrewAsciiQuotes",
"terminal.resumeCommands",
"terminal.sessionContentAlignment",
"terminal.sessionContentMaxWidth",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,21 @@ public struct UpdateFeedResolver: Sendable {
/// The appcast URL used when the `Info.plist` feed URL is missing or empty.
public let fallbackFeedURL: String

/// The appcast this fork updates from.
///
/// This has to be Ivrix's own feed, not the upstream project's. An Ivrix
/// build that queries upstream is offered upstream's releases, and because
/// the two are different applications, installing one replaces Ivrix with
/// cmux and loses the Hebrew build entirely. A wrong value here is worse
/// than no updater at all, so it must never silently fall back upstream.
public static let ivrixFallbackFeedURL =
"https://github.com/DananzMolt/Ivrix/releases/latest/download/appcast.xml"

/// Creates a resolver.
///
/// - Parameter fallbackFeedURL: The appcast URL to fall back to when the build-time
/// feed URL is absent. Defaults to the project's latest-release appcast.
public init(fallbackFeedURL: String = "https://github.com/manaflow-ai/cmux/releases/latest/download/appcast.xml") {
public init(fallbackFeedURL: String = UpdateFeedResolver.ivrixFallbackFeedURL) {
self.fallbackFeedURL = fallbackFeedURL
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,16 @@ import Testing
#expect(resolution.isNightly)
#expect(!resolution.usedFallback)
}

@Test func defaultFallbackPointsAtIvrixNotUpstream() {
// An Ivrix build that falls back to upstream's appcast is offered
// upstream's releases, and installing one replaces Ivrix with cmux.
// The default therefore has to be this fork's own feed.
let resolver = UpdateFeedResolver()
let resolution = resolver.resolve(infoFeedURL: nil)
#expect(resolution.usedFallback)
#expect(resolution.url == UpdateFeedResolver.ivrixFallbackFeedURL)
#expect(resolution.url.contains("DananzMolt/Ivrix"))
#expect(!resolution.url.contains("manaflow-ai/cmux"))
}
}
2 changes: 1 addition & 1 deletion Resources/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@
<key>SUEnableAutomaticChecks</key>
<true/>
<key>SUFeedURL</key>
<string>https://github.com/manaflow-ai/cmux/releases/latest/download/appcast.xml</string>
<string>https://github.com/DananzMolt/Ivrix/releases/latest/download/appcast.xml</string>
<key>SUScheduledCheckInterval</key>
<integer>86400</integer>
<key>SUSendProfileInfo</key>
Expand Down
Loading