Skip to content

iOS: add Return shortcut to terminal accessory bar - #6101

Merged
lawrencecchen merged 7 commits into
mainfrom
feat-ios-return-key
Jun 15, 2026
Merged

lawrencecchen merged 7 commits into
mainfrom
feat-ios-return-key

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jun 14, 2026 •

Copy link
Copy Markdown
Contributor

What

Adds a Return/Enter key to the iOS terminal keyboard accessory bar, alongside Tab and Esc. Tapping it sends a carriage return (CR, byte 0x0D), which is what Enter sends in a terminal.

Where it sits

In defaultConfigurableOrder the new key is placed immediately after Esc (which already sits right after Tab), so the three most common terminal keys (Tab, Esc, Return) are adjacent on a fresh install. It is user-configurable, so it can be hidden/reordered like the other shortcuts.

Identifier stability

The new TerminalInputAccessoryAction.returnKey case is appended last in the enum. The persisted identifier is the enum's Int rawValue, stored as builtin.<rawValue>, so appending keeps every existing built-in's raw value (and thus every user's persisted bar order/enabled set) unchanged. A UI-test accessibility id terminal.inputAccessory.return is also added for consistency.

Switch sites touched (all in GhosttySurfaceView.swift)

  • enum case returnKey (appended last)
  • title(isMacRemote:) -> ⏎
  • accessibilityIdentifier -> terminal.inputAccessory.return
  • output -> Data([0x0D]) (CR)
  • defaultConfigurableOrder -> inserted after .escape
  • settingsDisplayName -> "Return"

Dispatch needs no change: the perform path reads action.output generically, and isUserConfigurable returns true by default. All other switches over the enum (icons, alternate/command output, armed/sticky, ResolvedToolbarItem, the config resolve, the reducer/migration) are either default-covered or enum-agnostic.

Tests

Added at the ios/cmuxPackage layer (TerminalAccessoryConfigurationTests.swift) because the enum is iOS-only (GhosttySurfaceView.swift imports UIKit) and is not reachable from host swift-test:

  • fresh install shows Return adjacent to Tab/Esc (Tab, Esc, Return in order)
  • Return is enabled by default, is user-configurable, and sends CR (0x0D)
  • Return's storage key is stable (builtin.<rawValue>, round-trips, raw value is the max across cases)

Host-testable reducer tests in CmuxMobileTerminalKit still pass (78 tests). iOS compilation is validated by the Xcode build.

🤖 Generated with Claude Code


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Note

Low Risk
Localized iOS terminal UI and UserDefaults toolbar migration with broad test coverage; no auth, networking, or shared desktop behavior changes.

Overview
Adds a Return/Enter shortcut to the iOS terminal keyboard accessory bar. Tapping ⏎ sends carriage return (0x0D) through the existing output path; the new returnKey enum case is appended last so persisted builtin.<rawValue> IDs for other shortcuts stay unchanged.

Default layout places Return immediately after Esc (Tab → Esc → Return). It is user-configurable like other shortcuts, with localized settings label and accessibility id terminal.inputAccessory.return. output is made public for tests.

Migration refactors v3 “fold in post-ship shortcuts” into foldNewlyConfigurableV3, chaining ⇧ and Return insertion with anchor-based placement. Saved v3, v2→v3, and v1→v3 layouts get Return force-enabled after Esc once (idempotent; respects users who already hid it). Tests cover defaults, persistence, chained folds, and upgrade paths.

Reviewed by Cursor Bugbot for commit de3d8d6. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Adds a Return/Enter key to the iOS terminal accessory bar next to Tab and Esc. It sends CR (0x0D) and is auto-inserted for existing installs without changing user layouts.

  • New Features

    • Added returnKey in TerminalInputAccessoryAction; default order Tab → Esc → Return; label "⏎"; accessibility id terminal.inputAccessory.return; sends CR.
    • Enabled by default and user-configurable; case appended last so builtin.<rawValue> ids stay stable; output is public; settings name localized (en/ja).
    • Updated .github/swift-file-length-budget.tsv to match GhosttySurfaceView.swift.
  • Migration

    • One-shot fold inserts and enables Return after Esc for saved v3/v2/v1 configs; persists under v3 keys.
    • Idempotent and respects user choice; chains with the prior ⇧ fold.

Written for commit de3d8d6. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

New Features

  • Added a Return key to the terminal input accessory keyboard, enabled by default and positioned directly after Escape (immediately following Tab/ Escape in the default layout).
  • Return is user-configurable, shown as “Return”, and sends a carriage return (0x0D) when pressed.

Bug Fixes

  • Improved upgrade handling so existing shortcut layouts reliably surface Return in the correct spot without disrupting prior user choices.

Tests

  • Added/extended coverage for default ordering, persistence, carriage-return output, and migration/rewiring behavior.

Documentation

  • Added localized “Return” label (including Japanese).

Adds a Return/Enter key alongside Tab and Esc on the iOS terminal
keyboard accessory bar. It sends a carriage return (CR, 0x0D), which is
what Enter sends in a terminal.

The new case is appended last in TerminalInputAccessoryAction so existing
persisted raw values stay stable (the persisted key is builtin.<rawValue>).
Its default on-bar position is curated in defaultConfigurableOrder,
placed immediately after Esc so Tab/Esc/Return are adjacent.

Handles every exhaustive switch over the enum: title, accessibilityIdentifier,
output (CR byte), and settingsDisplayName. Dispatch flows through the generic
action.output path (no perform-switch change needed). isUserConfigurable
returns true by default so it appears in the bar and editor.

Tests live at the ios/cmuxPackage layer (the enum is iOS-only, imports
UIKit, so it is not reachable from host swift-test): assert Return is
shown by default adjacent to Esc, sends CR, and that its storage key is
stable. Host-testable reducer tests in CmuxMobileTerminalKit still pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 14, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jun 15, 2026 10:01pm
cmux-staging Building Building Preview, Comment Jun 15, 2026 10:01pm

@coderabbitai

coderabbitai Bot commented Jun 14, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new .returnKey case to TerminalInputAccessoryAction with full wiring (glyph, accessibility identifier, carriage-return output, default positioning, localization), refactors the schema migration path in TerminalAccessoryConfiguration to use a generalized folding helper that force-shows newly-configurable built-ins across v1/v2/v3 upgrades, and provides comprehensive test coverage for feature behavior and migration idempotency.

Changes

returnKey Action with Generalized Migration Support

Layer / File(s) Summary
returnKey enum case, properties, default ordering, and localization
Packages/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttySurfaceView.swift, ios/cmux/Resources/Localizable.xcstrings
Adds .returnKey appended to the public enum for raw-value stability, extends title(isMacRemote:) to return ⏎, accessibilityIdentifier to terminal.inputAccessory.return, output to emit 0x0D bytes, defaultConfigurableOrder to position it after .escape, and settingsDisplayName to "Return". English and Japanese localization strings are added for the display name.
Generalized folding helper for newly-configurable items
Packages/CmuxMobileTerminal/Sources/CmuxMobileTerminal/TerminalAccessoryConfiguration.swift
Refactors v3/v2/v1 schema-loading paths to use a shared foldNewlyConfigurableV3 helper that force-shows newly-configurable built-ins (shift, return) exactly once when absent, preserving existing enabled/disabled state. The helper iterates over newly-configurable ids with their canonical anchors and applies the folding routine to both persisted order and enabled set across all upgrade paths.
Tests for folding chaining and idempotency
Packages/CmuxMobileTerminalKit/Tests/CmuxMobileTerminalKitTests/ToolbarLayoutMigrationFoldTests.swift
Adds test coverage for chaining two foldingNewlyConfigurable operations (shift then return), verifying that each fold inserts at its own anchor when applied sequentially, that re-running becomes a no-op once both ids are present, and that hidden items are not re-shown or moved by subsequent folds.
Default ordering, output, storage stability, and migration coverage
ios/cmuxPackage/Tests/cmuxFeatureTests/TerminalAccessoryConfigurationTests.swift
Updates fresh-install default-order assertions for Tab/Esc/Return adjacency, adds tests verifying return key visibility, output (0x0D), user-configurability, and storageKey format stability. Extends migration coverage with comprehensive tests for v3 layouts gaining return force-enabled, layouts with missing shift/return folding at correct anchors, persistent user-hidden state across reloads, fold persistence via v3 keys, and v2→v3 upgrades surfacing return force-enabled.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • manaflow-ai/cmux#5532: Introduced defaultConfigurableOrder logic that this PR extends by inserting .returnKey after .escape.
  • manaflow-ai/cmux#6104: Modifies the same v3 toolbar migration folding machinery; this PR generalizes the folding framework to handle multiple newly-configurable items.
  • manaflow-ai/cmux#5579: Also modifies TerminalAccessoryConfiguration and default built-in placement machinery that this PR extends with generalized folding support.

Poem

🐇 A key returns with carriage and grace,
Folding through migrations with proper space!
⏎ After Escape it finds its place,
Raw values stable, idempotent base—
From v1 to v3, a smooth upgrade race!


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Cmux Source Artifacts ❌ Error PR adds 26 files to .claude/ and .agents/ scratch directories, which are explicitly listed in source-control-artifacts.md as prohibited from source control. Remove .claude/ and .agents/ directories from the commit, or add these directory patterns to .gitignore if they're meant to be intentional repo fixtures.
Docstring Coverage ⚠️ Warning Docstring coverage is 35.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (19 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding a Return shortcut to the iOS terminal accessory bar. It directly corresponds to the primary objective of the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed All production Swift changes maintain proper actor isolation. The returnKey enum case is added to TerminalInputAccessoryAction, which is already explicitly marked Sendable. TerminalAccessoryConfigu...
Cmux Swift Blocking Runtime ✅ Passed The PR adds a Return key to the iOS terminal accessory bar with no blocking or timing-based synchronization. All changes are pure data/enum additions: enum case, title, accessibility identifier, ou...
Cmux Expensive Synchronous Load ✅ Passed PR adds Return key to iOS terminal keyboard. No expensive synchronous loaders added; only UserDefaults reads/writes and pure data transformation for migration logic—appropriate for @MainActor confi...
Cmux Cache Substitution Correctness ✅ Passed PR correctly reads fresh from UserDefaults (not cached), applies migrations as pure transformations, and immediately persists normalized layout back to UserDefaults to ensure migrations run at most...
Cmux No Hacky Sleeps ✅ Passed Custom check "cmux no hacky sleeps" applies only to non-Swift TypeScript, JavaScript, shell, or build/runtime scripts. This PR contains only Swift code, localization resources, and configuration me...
Cmux Algorithmic Complexity ✅ Passed All collections in the new migration logic are fixed-size (≤31 toolbar items from enum). Folds execute once per app launch in init(), not in hot UI/socket paths. Explicit bounds from code structure...
Cmux Swift Concurrency ✅ Passed The Return key feature introduces no new legacy async patterns; returnKey is a simple enum case with output/title. New files use @Observable (modern Observation framework, not Combine) and Dispatch...
Cmux Swift @Concurrent ✅ Passed All new Swift code is synchronous with no concurrency concerns. The new foldNewlyConfigurableV3 helper is a lightweight private static function performing simple array manipulation with no async/ac...
Cmux Swift File And Package Boundaries ✅ Passed PR adds only 17 lines to GhosttySurfaceView.swift (3,684 lines, already oversized) and 66 lines to TerminalAccessoryConfiguration.swift (324 lines). Both changes fall well below the thresholds: 17-...
Cmux Swift Logging ✅ Passed No logging violations found. The PR adds a returnKey enum case with simple property returns (title, accessibility ID, output bytes, display name); no print, debugPrint, dump, NSLog, or unauthorized...
Cmux User-Facing Error Privacy ✅ Passed PR adds a Return key to iOS terminal bar with only generic user-facing text (⏎ symbol, "Return" label, Japanese "リターン"). No vendor names, credentials, errors, or implementation details exposed.
Cmux Full Internationalization ✅ Passed User-facing text for Return key uses proper String(localized:defaultValue:) API with complete translations (en, ja) in Localizable.xcstrings matching all catalog locales.
Cmux Swiftui State Layout ✅ Passed PR introduces no SwiftUI state layout violations: GhosttySurfaceView adds enum case/switches without state; TerminalAccessoryConfiguration uses modern @Observable pattern, not @Published/@StateObje...
Cmux Architecture Rethink ✅ Passed PR adds returnKey enum case with clean, single-owner architecture: synchronous deterministic migration in init, one source of truth (TerminalAccessoryConfiguration), generic output dispatch path, n...
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR is iOS-only, adds keyboard accessory shortcut enum case, not auxiliary windows; no NSWindow/NSPanel/NSWindowController/SwiftUI Window/WindowGroup code present.
Description check ✅ Passed The PR description comprehensively covers what changed, where it sits, identifier stability, all switch sites touched, dispatch considerations, and test coverage details.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-ios-return-key

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: def28d1211

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

defaultLeadingActions + [
.tab,
.escape,
.returnKey,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Enable Return during the v3 upgrade path

For users who have already launched a v3 toolbar config, adding .returnKey to defaultConfigurableOrder only appends it to displayOrder; TerminalAccessoryConfiguration.init loads the existing non-nil cmux.terminal.toolbar.enabled.v3 set, and TerminalAccessoryLayoutReducer.load keeps that set unchanged, so the new Return button is hidden after upgrade unless the user resets or manually enables it. If this shortcut is meant to be added to the terminal bar for existing users, the v3 migration/load path needs to force-enable this new ID or bump the persisted schema.

Useful? React with 👍 / 👎.

switch self {
case .escape: return String(localized: "terminal.shortcut.name.escape", defaultValue: "Escape")
case .tab: return String(localized: "terminal.shortcut.name.tab", defaultValue: "Tab")
case .returnKey: return String(localized: "terminal.shortcut.name.return", defaultValue: "Return")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add catalog entries for the Return label

This introduces the new user-facing key terminal.shortcut.name.return, but I could not find it in ios/cmux/Resources/Localizable.xcstrings (the sibling shortcut keys there have en and ja entries). In Japanese settings, the shortcuts editor will fall back to the English default value, which violates the repo's requirement that every user-facing string be localized in the catalog for all supported locales.

Useful? React with 👍 / 👎.

@greptile-apps

greptile-apps Bot commented Jun 14, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds a Return/Enter shortcut (returnKey) to the iOS terminal keyboard accessory bar, sending CR (0x0D) via the existing generic output dispatch path. The case is appended last in TerminalInputAccessoryAction to keep all existing builtin.<rawValue> storage keys stable, and a shared foldNewlyConfigurableV3 helper inserts Return after Esc on first launch for every upgrade path (v3, v2, v1), then becomes a no-op once persisted.

  • GhosttySurfaceView.swift: returnKey enum case added last; ⏎ bar-button title (symbol, consistent with ⇧/⌘/arrows); Data([0x0D]) CR output; placed after .escape in defaultConfigurableOrder; settingsDisplayName routes through String(localized:) with matching en + ja xcstrings entries.
  • TerminalAccessoryConfiguration.swift: Previous per-path ⇧ fold refactored into foldNewlyConfigurableV3, which chains ⇧ then Return idempotently and is applied on all three legacy upgrade paths; enabled: nil is correctly threaded through unchanged when no fold fires.
  • Tests: iOS-layer tests cover default Tab→Esc→Return adjacency, CR output, raw-value stability, v3/v2 migration fold-in, one-shot idempotence, and user-hide persistence; host-testable fold unit tests cover chaining and the hidden-id no-op path.

Confidence Score: 5/5

Safe to merge — the change is confined to the iOS terminal accessory bar, uses the existing generic output dispatch path, and the one-time migration fold is idempotent and well-tested across all upgrade paths.

The enum case is appended last so no existing stored raw values shift. The migration fold correctly handles nil enabled sets, chains with the prior ⇧ fold, and re-persists under v3 keys so it runs once. The ⏎ symbol title is consistent with the established pattern for non-text symbols, and settingsDisplayName is fully localized with en + ja catalog entries. Test coverage is thorough: default ordering, CR output, raw-value stability, v3/v2 migration, idempotence, and user-hide persistence are all exercised.

No files require special attention.

Important Files Changed

Filename Overview
Packages/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttySurfaceView.swift Adds returnKey enum case appended last (raw-value-stable), ⏎ bar title (symbol, consistent with ⇧/⌘/arrows), Data([0x0D]) CR output (public for tests), and inserts the case into defaultConfigurableOrder right after .escape. Switch coverage is complete; settingsDisplayName routes through String(localized:) with a matching xcstrings entry.
Packages/CmuxMobileTerminal/Sources/CmuxMobileTerminal/TerminalAccessoryConfiguration.swift Refactors the per-upgrade-version ⇧ fold into a shared foldNewlyConfigurableV3 helper that chains ⇧ then Return idempotently, and applies it on all three legacy upgrade paths (v3, v2, v1). Logic correctly threads enabled: nil through unchanged when no fold fires, and re-persists under v3 keys so the fold runs at most once.
ios/cmux/Resources/Localizable.xcstrings Adds terminal.shortcut.name.return with en (Return) and ja (リターン) translations, consistent with all other adjacent shortcut-name keys.
Packages/CmuxMobileTerminalKit/Tests/CmuxMobileTerminalKitTests/ToolbarLayoutMigrationFoldTests.swift Adds three new fold tests: chaining two folds independently, idempotence after both ids are present, and respecting an already-hidden id.
ios/cmuxPackage/Tests/cmuxFeatureTests/TerminalAccessoryConfigurationTests.swift Comprehensive iOS-layer tests covering default order, CR output, raw-value stability, v3/v2 migration fold-in, one-shot behavior, and user-hide persistence.
.github/swift-file-length-budget.tsv Budget line for GhosttySurfaceView.swift bumped from 3672 to 3734 to account for the 62-line addition.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[TerminalAccessoryConfiguration init] --> B{v3 keys present?}
    B -- yes --> C[Load order + enabled from UserDefaults]
    B -- no --> D{v2 keys present?}
    D -- yes --> E[widenedToV3]
    D -- no --> F{v1 keys present?}
    F -- yes --> G[migratedOrder → widenedToV3]
    F -- no --> H[Fresh install: empty order, nil enabled]
    C --> I[foldNewlyConfigurableV3]
    E --> I
    G --> I
    I --> J{⇧ absent from order?}
    J -- yes --> K[Insert ⇧ after command/alt/ctrl, force-enable]
    J -- no --> L[No-op for ⇧]
    K --> M{Return absent from order?}
    L --> M
    M -- yes --> N[Insert Return after Esc/Tab/modifiers, force-enable]
    M -- no --> O[No-op for Return]
    N --> P[Persist under v3 keys]
    O --> P
    H --> P
    P --> Q[reducer.load → displayOrder + enabledSet]
Loading

Reviews (7): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile

@@ -482,6 +493,7 @@ public enum TerminalInputAccessoryAction: Int, CaseIterable, Sendable {
switch self {
case .escape: return String(localized: "terminal.shortcut.name.escape", defaultValue: "Escape")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Missing xcstrings entry for terminal.shortcut.name.return

settingsDisplayName calls String(localized: "terminal.shortcut.name.return", defaultValue: "Return"), but ios/cmux/Resources/Localizable.xcstrings has no entry for this key — unlike every other nearby shortcut name (.escape → terminal.shortcut.name.escape, .tab → terminal.shortcut.name.tab, etc., each with en + ja translations). On a Japanese-locale device, users will see the raw English word "Return" in the settings display because the runtime falls back to defaultValue. Add "terminal.shortcut.name.return" to the catalog with at least en and ja entries.

Rule Used: Flag production user-facing text that is not fully... (source)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@ios/cmuxPackage/Tests/cmuxFeatureTests/TerminalAccessoryConfigurationTests.swift`:
- Around line 71-83: In the returnKeyStableIdentifier() test function, replace
the dynamic string construction that rebuilds the expected value from the same
enum case with a hardcoded literal string (such as "builtin.29") to ensure the
test catches any unintended changes to the persisted identifier. Additionally,
remove the maxRaw assertion and related lines that only verify the current
ordering rather than the stability of the stored key itself. This ensures the
test will fail if enum reordering accidentally changes the persisted identifier.

In
`@Packages/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttySurfaceView.swift`:
- Line 496: The localization key `terminal.shortcut.name.return` introduced in
the case statement for `.returnKey` is missing its corresponding entry in the
string catalog. Add an entry for this key to the Resources/Localizable.xcstrings
file with the value "Return" and provide translations for all supported locales
that your application targets. Without this catalog entry, users in non-English
locales will see the default English fallback text instead of proper
translations for the shortcuts editor label.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7e2c5930-35f9-4417-85d0-6e8b9882a89b

📥 Commits

Reviewing files that changed from the base of the PR and between 8bb9b69 and def28d1.

📒 Files selected for processing (2)
  • Packages/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttySurfaceView.swift
  • ios/cmuxPackage/Tests/cmuxFeatureTests/TerminalAccessoryConfigurationTests.swift

Comment on lines +71 to +83
@Test("Return's persisted identifier is stable")
func returnKeyStableIdentifier() {
// The persisted key is `builtin.<rawValue>`; Return is appended last in the
// enum so existing built-ins keep their raw values. Lock the storage key so
// a future reorder of the enum cannot silently shift it.
let stored = TerminalInputAccessoryAction.returnKey.itemID.storageKey
#expect(stored == "builtin.\(TerminalInputAccessoryAction.returnKey.rawValue)")
let parsed = ToolbarItemID(storageKey: stored)
#expect(parsed == id(.returnKey))
// Appended last: its raw value is the max across all cases.
let maxRaw = TerminalInputAccessoryAction.allCases.map(\.rawValue).max()
#expect(TerminalInputAccessoryAction.returnKey.rawValue == maxRaw)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Pin the concrete persisted ID instead of recomputing it.

stored == "builtin.\(TerminalInputAccessoryAction.returnKey.rawValue)" just rebuilds the expected value from the same enum case, so inserting/reordering cases before .returnKey can still change the persisted identifier without failing this test. The maxRaw assertion only proves .returnKey is currently last, not that its stored key stayed stable. Lock the current literal (builtin.29) and drop the maxRaw check.

🔧 Suggested test change
         let stored = TerminalInputAccessoryAction.returnKey.itemID.storageKey
-        `#expect`(stored == "builtin.\(TerminalInputAccessoryAction.returnKey.rawValue)")
+        `#expect`(stored == "builtin.29")
         let parsed = ToolbarItemID(storageKey: stored)
         `#expect`(parsed == id(.returnKey))
-        // Appended last: its raw value is the max across all cases.
-        let maxRaw = TerminalInputAccessoryAction.allCases.map(\.rawValue).max()
-        `#expect`(TerminalInputAccessoryAction.returnKey.rawValue == maxRaw)

Based on Packages/CmuxMobileTerminalKit/Sources/CmuxMobileTerminalKit/ToolbarItemID.swift, the persisted contract is the concrete builtin.<rawValue> string, so the regression test needs to pin that exact value rather than recomputing it from the same enum case.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@ios/cmuxPackage/Tests/cmuxFeatureTests/TerminalAccessoryConfigurationTests.swift`
around lines 71 - 83, In the returnKeyStableIdentifier() test function, replace
the dynamic string construction that rebuilds the expected value from the same
enum case with a hardcoded literal string (such as "builtin.29") to ensure the
test catches any unintended changes to the persisted identifier. Additionally,
remove the maxRaw assertion and related lines that only verify the current
ordering rather than the stability of the stored key itself. This ensures the
test will fail if enum reordering accidentally changes the persisted identifier.

switch self {
case .escape: return String(localized: "terminal.shortcut.name.escape", defaultValue: "Escape")
case .tab: return String(localized: "terminal.shortcut.name.tab", defaultValue: "Tab")
case .returnKey: return String(localized: "terminal.shortcut.name.return", defaultValue: "Return")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add the matching string-catalog entry for terminal.shortcut.name.return.

Line 496 introduces a new user-facing localization key, but this review cohort does not include the corresponding Resources/Localizable.xcstrings entry. That leaves the shortcuts editor falling back to English "Return" anywhere the catalog is missing instead of shipping a translated label for every supported locale. As per coding guidelines, "Flag production changes that introduce new Swift localization keys not backed by matching Resources/*.xcstrings entries with translated values for every supported locale" and "All user-facing strings must be localized."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Packages/CmuxMobileTerminal/Sources/CmuxMobileTerminal/GhosttySurfaceView.swift`
at line 496, The localization key `terminal.shortcut.name.return` introduced in
the case statement for `.returnKey` is missing its corresponding entry in the
string catalog. Add an entry for this key to the Resources/Localizable.xcstrings
file with the value "Return" and provide translations for all supported locales
that your application targets. Without this catalog entry, users in non-English
locales will see the default English fallback text instead of proper
translations for the shortcuts editor label.

Source: Coding guidelines

cmux-lawrence and others added 2 commits June 14, 2026 18:07
… can read it

CI ios-simulator failed: the cmuxFeatureTests module imports CmuxMobileTerminal
non-@testable, so the internal output property was inaccessible. title,
isUserConfigurable, itemID, and settingsDisplayName are already public; output is
the byte payload an action sends, part of the same public contract.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The touched file is already well over any reasonable size; splitting it is a
separate refactor and stored properties cannot move to an extension. Accept the
incremental growth as known debt so the budget guard reflects reality.
cmux-lawrence added 2 commits June 14, 2026 19:01
Autoreview P3: terminal.shortcut.name.return was missing from
Localizable.xcstrings while sibling shortcut names have en+ja, so Japanese
users would see the English fallback and the localization audit would flag it.
# Conflicts:
#	.github/swift-file-length-budget.tsv
Adding .returnKey to defaultConfigurableOrder only reaches fresh installs
and resets: an upgrading user's persisted v3 config keeps its saved enabled
set verbatim, so the appended Return item stayed hidden.

Add a one-shot fold (mirroring the existing Shift fold) that inserts and
enables Return next to Esc when it is absent from a persisted config, keyed
off Return's absence from the saved order so it runs exactly once and a user
who later hides it stays hidden. Apply it on the v3, v2, and v1 load paths,
generalizing the inline Shift fold into a shared foldNewlyConfigurableV3
helper that chains both folds.

Tests: pure chained-fold + idempotence + respect-hidden assertions in the
host-testable CmuxMobileTerminalKit migration tests; iOS-layer config tests
(ios/cmuxPackage) for the v3 Return fold, the combined Shift+Return fold,
later-hide persistence, no re-fold of a hidden Return, one-shot re-persist,
and a v2 upgrade surfacing Return.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lawrencecchen

Copy link
Copy Markdown
Contributor Author

Addressed the P2 autoreview finding: existing users' saved toolbar configs now surface the new Return key.

How it works. The layout reducer appends newly-configurable ids to the saved order but preserves the saved enabled set verbatim, so an appended Return stayed hidden after an upgrade. Added a one-shot fold mirroring the existing Shift fold (ToolbarLayoutMigration.foldingNewlyConfigurable): on load, if Return is absent from the persisted order, it is inserted immediately after Esc (falling back to Tab, then the modifiers) and added to the enabled set.

Keying. The fold keys off Return's absence from the saved order, not its enabled state. Once it runs, Return is persisted into the v3 order, so every later launch takes the no-op path. A user who then hides Return keeps it hidden across reloads. Generalized the inline Shift fold into a shared foldNewlyConfigurableV3 helper that chains the Shift and Return folds; applied on the v3, v2, and v1 load paths (the v2/v1 widening force-enables only the pinned modifiers/zoom, never post-v3 shortcuts, so Return needs the explicit fold there too).

Tests.

  • Host-testable (CmuxMobileTerminalKit / ToolbarLayoutMigrationFoldTests): pure chained-fold inserts each id after its own anchor, idempotence on re-run, and respect-an-existing-hidden-id. swift test green (85 tests).
  • iOS-layer (ios/cmuxPackage / TerminalAccessoryConfigurationTests, builds for iOS): existing v3 config without Return gains it force-enabled after Esc; combined Shift+Return fold; later-hide persists across reload; a hidden Return is not re-folded; one-shot re-persist under v3 keys; and a v2 upgrade surfacing Return. (.returnKey lives in the iOS-only GhosttySurfaceView, so the config-layer assertions live at the iOS layer; pure itemID-ordering assertions live in CmuxMobileTerminalKit.)

File-length budget guard green.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
ios/cmuxPackage/Tests/cmuxFeatureTests/TerminalAccessoryConfigurationTests.swift (1)

72-83: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Pin the persisted Return key to a fixed literal contract value.

Line 78 recomputes the expected key from rawValue, and Lines 82-83 only assert “is max,” so this test can still pass after raw-value drift. Assert the concrete persisted key literal directly and remove the max-raw check.

🔧 Suggested update
         let stored = TerminalInputAccessoryAction.returnKey.itemID.storageKey
-        `#expect`(stored == "builtin.\(TerminalInputAccessoryAction.returnKey.rawValue)")
+        `#expect`(stored == "builtin.29") // pin concrete persisted contract value
         let parsed = ToolbarItemID(storageKey: stored)
         `#expect`(parsed == id(.returnKey))
-        // Appended last: its raw value is the max across all cases.
-        let maxRaw = TerminalInputAccessoryAction.allCases.map(\.rawValue).max()
-        `#expect`(TerminalInputAccessoryAction.returnKey.rawValue == maxRaw)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@ios/cmuxPackage/Tests/cmuxFeatureTests/TerminalAccessoryConfigurationTests.swift`
around lines 72 - 83, In the returnKeyStableIdentifier() test function, replace
the dynamic computation of the storage key using rawValue on line 78 with a
hardcoded literal string that represents the fixed contract value for the Return
key's persisted identifier. Remove the max-raw assertion at lines 82-83 (the
allCases map and max check) since it only validates relative positioning rather
than ensuring stability of the actual persisted value. The test should directly
assert the concrete literal string value instead of deriving it from the enum's
raw value or checking relative orderings.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In
`@ios/cmuxPackage/Tests/cmuxFeatureTests/TerminalAccessoryConfigurationTests.swift`:
- Around line 72-83: In the returnKeyStableIdentifier() test function, replace
the dynamic computation of the storage key using rawValue on line 78 with a
hardcoded literal string that represents the fixed contract value for the Return
key's persisted identifier. Remove the max-raw assertion at lines 82-83 (the
allCases map and max check) since it only validates relative positioning rather
than ensuring stability of the actual persisted value. The test should directly
assert the concrete literal string value instead of deriving it from the enum's
raw value or checking relative orderings.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c8ee2424-51f4-4939-8319-958441ef32e2

📥 Commits

Reviewing files that changed from the base of the PR and between 24a89bc and 51c760b.

📒 Files selected for processing (3)
  • Packages/CmuxMobileTerminal/Sources/CmuxMobileTerminal/TerminalAccessoryConfiguration.swift
  • Packages/CmuxMobileTerminalKit/Tests/CmuxMobileTerminalKitTests/ToolbarLayoutMigrationFoldTests.swift
  • ios/cmuxPackage/Tests/cmuxFeatureTests/TerminalAccessoryConfigurationTests.swift

@lawrencecchen
lawrencecchen merged commit 58076fd into main Jun 15, 2026
27 of 31 checks passed

This branch was successfully deployed

1 active deployment
Preview – cmux — de3d8d6f Deployed Jun 15, 2026 by vercel[bot]
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.

1 participant