Skip to content

[twarp 06] tab rename: bind ⌘⌥R to rename the active tab#65

Merged
timomak merged 4 commits into
masterfrom
twarp-06-tab-rename-impl
May 26, 2026
Merged

[twarp 06] tab rename: bind ⌘⌥R to rename the active tab#65
timomak merged 4 commits into
masterfrom
twarp-06-tab-rename-impl

Conversation

@timomak
Copy link
Copy Markdown
Owner

@timomak timomak commented May 21, 2026

Implements feature 06 (tab rename shortcut). Specs merged in #64.

What changed

Two files, Rust only:

  • app/src/workspace/mod.rs — attach the default chord cmdorctrl-alt-r to the existing, previously-unbound workspace:rename_active_tab EditableBinding. This is the entire headline feature: it lights up the keyboard path into the inline tab-rename flow that double-click already triggers. Same editor, same pre-selected title, same Enter-commits / Escape-cancels / blur-commits semantics (PRODUCT §1–§4). No new action, handler, state, render path, persistence, telemetry, or feature flag. The binding stays editable, so it remains remappable on the keybindings settings page and surfaces ⌘⌥R on the Rename Tab menu entry via CustomAction::RenameTab (PRODUCT §10).
  • app/src/workspace/view.rs — add a zero-tab bounds guard to rename_tab, which indexed self.tabs[index] before rename_tab_internal's existing guard ran. This guarantees PRODUCT §7 (the shortcut is a no-op, no crash, when there's no tab to rename) and also hardens the pre-existing Rename Tab menu path.

Conflict analysis (TECH §2)

  • macOS (⌘⌥R, the primary target): conflict-free. Grep confirms no cmd-alt-r binding exists anywhere in app/ or crates/ (the cmdorctrl-alt-right / ctrl-alt-right hits are the arrow key — a different chord).
  • Linux/Windows (⌃⌥R): theoretical shadow, provably dead. The same ctrl-alt-r chord is bound to TerminalAction::ResumeConversation (terminal/view/init.rs:180), but that binding requires the CAN_RESUME_CONVERSATION_KEY context flag. That flag is gated on FeatureFlag::AIResumeButton and an active conversation in error state (terminal/view.rs:25950). Post-AI-removal (feature 02 / twarp 2c-d), no conversation is ever created and was_manually_cancelled is hardcoded false — the in-tree comment literally reads "resume button cannot fire." So the flag is never set and the shadow can't occur. The binding is remappable as the escape hatch regardless. Removing the defunct ResumeConversation binding is feature-02 cleanup, out of scope here.

Edge cases verified (TECH §3)

  • Zero tabs (§7): guarded (see above).
  • Re-press while renaming (§5): benign by inspection — rename_tab_internal clears and re-insert_selected_texts into the single shared tab_rename_editor; no second editor.
  • Vertical tabs (§9): both the horizontal (tab.rs) and vertical (vertical_tabs.rs) render paths key off is_tab_being_renamed(), so the inline editor renders in both layouts. No code change needed.

Testing

  • cargo fmt --check ✅ · clippy --workspace --all-targets --tests -D warnings ✅ · clang-format ✅
  • wgslfmt — N/A (no .wgsl files in the diff).
  • No new unit tests: the change adds zero new logic (attaches a chord to an already-tested action; adds a bounds guard mirroring the adjacent one). Per TECH.md the manual smoke test in PRODUCT.md is the canonical pre-merge check.
  • Relying on GitHub CI (nextest) for the full suite. (A local cargo test run surfaced 5 failures, all pre-existing test-isolation artifacts in unrelated subsystems — autoupdate/telemetry/settings — that pass under nextest's process isolation; none are touched by this change.)

Smoke test

See roadmap/06-tab-rename/PRODUCT.md → Smoke test. Headline: open 3 tabs, focus tab 2, press ⌘⌥R, type a name, Enter → tab 2 renamed; ⌘⌥R then Escape → reverts; works with a terminal running top focused; settings page + Rename Tab menu show ⌘⌥R.

Specs

  • PRODUCT: roadmap/06-tab-rename/PRODUCT.md
  • TECH: roadmap/06-tab-rename/TECH.md

🤖 Generated with Claude Code

timomak and others added 4 commits May 21, 2026 13:20
Attach the default key chord `cmdorctrl-alt-r` (⌘⌥R on macOS,
Ctrl+Alt+R on Linux/Windows) to the existing, previously-unbound
`workspace:rename_active_tab` EditableBinding. This lights up the
keyboard path into the inline tab-rename flow that double-click
already triggers — same editor, same pre-selected text, same
Enter-commits / Escape-cancels semantics. No new action, handler,
state, render path, persistence, telemetry, or feature flag. The
binding stays editable, so it remains remappable on the keybindings
settings page and surfaces ⌘⌥R on the Rename Tab menu entry via
CustomAction::RenameTab.

Also add a zero-tab bounds guard to `rename_tab`, which indexed
`self.tabs[index]` before `rename_tab_internal`'s existing guard
ran. This guarantees PRODUCT §7 (the shortcut is a no-op with no
crash when there is no tab to rename) and also hardens the existing
Rename Tab menu path.

Conflict check (TECH §2): `cmd-alt-r` is unbound on macOS, so the
⌘⌥R default is conflict-free on the primary target. On Linux/Windows
the same `ctrl-alt-r` chord is also bound to ResumeConversation, but
that binding requires the CAN_RESUME_CONVERSATION_KEY context flag,
which is gated on FeatureFlag::AIResumeButton AND an active
conversation in error state. Post-AI-removal (twarp 2c-d) no
conversation is ever created and `was_manually_cancelled` is
hardcoded false, so the flag is provably never set — the shadow is
dead. The binding is remappable as the escape hatch regardless.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reconcile STATUS to git (spec #64 merged) and record the impl PR.
Tick the single sub-phase and note the resolved §2 conflict check
(macOS ⌘⌥R conflict-free; Linux/Windows ⌃⌥R shadow dead post-AI-removal).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…g_to_keystroke

The previous approach (`EditableBinding::with_key_binding("cmdorctrl-alt-r")`
on `workspace:rename_active_tab`) panicked the app at startup:

  thread 'main' panicked at app/src/app_menus.rs:122:13:
  action should have a name: RenameTab

Root cause: `with_custom_action` and `with_key_binding` write the *same*
`EditableBinding::trigger` field. Calling `with_key_binding` last overwrote
`Trigger::Custom(RenameTab)` with a keystroke trigger. The macOS menu builds
its "Rename Tab" item via `description_for_custom_action(RenameTab)`, which
only resolves a binding whose `trigger`/`original_trigger` is
`Trigger::Custom(RenameTab)`. With that clobbered the lookup returned `None`,
tripping `default_name`'s `debug_assert!`.

Fix: revert the `with_key_binding` call (so the binding keeps its custom
trigger) and assign the default chord in `custom_tag_to_keystroke` instead —
the same `CustomAction -> Keystroke` map used by every other menu shortcut
(NewTab, Copy, ShowSettings, ...). It feeds the runtime converter on both
macOS (`register_default_keystroke_triggers_for_custom_actions`) and
Linux/Windows (`convert_custom_triggers_to_keystroke_triggers`), so ⌘⌥R
(Ctrl+Alt+R) fires RenameActiveTab while the menu still resolves correctly.

Verified by launching warp-oss: process survives the startup window
(menu build) with no panic on stderr. fmt + clippy clean. The zero-tab
guard from the first commit is unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…chanism

The merged spec prescribed `EditableBinding::with_key_binding(...)`, which
panics the menu builder (it clobbers `Trigger::Custom(RenameTab)`). Update §1
to document the correct mechanism — register the default chord in
`custom_tag_to_keystroke` — and to require launching the app for validation,
since the panic escaped build/clippy/test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@timomak
Copy link
Copy Markdown
Owner Author

timomak commented May 26, 2026

Update: fixed a startup panic found in manual testing

Running cargo run surfaced a launch-time panic that build/clippy/cargo test did not catch:

thread 'main' panicked at app/src/app_menus.rs:122:13:
action should have a name: RenameTab

Cause: EditableBinding::with_custom_action and with_key_binding write the same trigger field. The .with_key_binding("cmdorctrl-alt-r") overwrote Trigger::Custom(RenameTab), and the macOS menu's description_for_custom_action(RenameTab) lookup requires that custom trigger — so it returned None and tripped default_name's debug_assert!.

Fix (commits 7c986b5, bfb8ae1): revert the with_key_binding call and register the default chord in custom_tag_to_keystroke (app/src/util/bindings.rs) instead — the same CustomAction → Keystroke map every other menu shortcut uses (NewTab, Copy, ShowSettings…). It feeds the runtime converter on macOS and Linux/Windows, so ⌘⌥R / Ctrl+Alt+R fires RenameActiveTab while the menu still resolves. TECH.md §1 updated to document the correct mechanism and to require app-launch validation.

Re-verified: launched warp-oss — process survives the startup/menu-build window with no panic on stderr; fmt + clippy clean. The zero-tab guard is unchanged.

Please still run the PRODUCT.md smoke test (⌘⌥R rename / Escape revert / terminal-focused) before merging.

@timomak timomak merged commit 61af322 into master May 26, 2026
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