The Enter guard keyed off branch_picker_open/overflow_open alone, so a
stale flag while the panel was closed / loading / merging / showing a diff
would wrongly eat the global Enter. Add git_ready_popover_open() (panel
open + ready view + a popover flag) and guard on it instead.
The inline 新建分支 name input painted a static '|' and the host never woke
the loop for branch_create_focused, so the caret sat frozen. Blink it on the
shared commit-caret cadence (paint_menu_input), wake next_animation_deadline
for branch_create_focused, and seed/reset commit_caret_anchor_ms on focus +
keypress so it starts solid then blinks.
In list / merge mode (and the overflow menu) no input is focused, so Enter
skipped every input handler and fell through to the global chat send. Guard
it: while either ready-state popover is open, Enter is consumed (focused
inputs still submit via the handlers above).
A stale commit/remote/https focus could still route text + Enter to the
hidden input while the branch-picker dropdown was open (firing a hidden
commit / set-remote / login). Gate the three focus helpers on
!branch_picker_open so those inputs are inactive whenever the dropdown is
showing; focus resumes when it closes.
A stale focused Git input (commit / remote / https) could intercept the
merge-mode Escape, because the sub-mode handler ran after the input
defocus handlers. Move the sub-mode Escape ahead of them so create/merge
always exits to the branch list on the first Escape.
Merge mode had no exit: Escape was gated on branch_create_focused (false
in merge mode) and the painted 取消 hint was inert. Now:
- Escape is mode-based (branch_picker_open && mode != List → List), so it
steps both create AND merge sub-modes back to the branch list
- new GitPanelHit::BranchPickerCancel: in merge mode a click anywhere but a
branch row (incl. the 取消 hint) returns to the list
The 新建分支 / 合并分支 footer buttons were visual-only; this makes them
functional (TS GitPanelBranchPicker list/create/merge state machine):
- GitBranchPickerMode { List, Create, Merge } drives the dropdown body
- 新建分支 → inline name input (paint_menu_input) + Create button; typing
routes to branch_create_draft, Enter/Submit runs CreateBranch
(create_and_switch_branch); Escape returns to the list
- 合并分支 → merge mode listing non-current branches; a row merges it
- paint↔hit-test lockstep via shared branch_footer_rects / branch_create_rects
+ a backend-free label_px width heuristic
- input_active() now counts the branch-create field so shortcuts don't leak
All labels resolve through op-i18n (createAction/mergeAction/createPlaceholder
/createSubmit/cancel — present in all 15 locales).
The AI chat model-picker search captured typed chars but Delete (native
+ web), and on web also nudge/duplicate/reorder via input_active, still
reached the canvas and mutated/deleted the selection. Add the
chat_model_picker_open guard to native apply_delete and to web
apply_delete / input_active / apply_escape so the search field owns the
keyboard while open. Regression tests cover delete/backspace/nudge/
duplicate/reorder under an open picker.
Also clears the workspace clippy/fmt gate: allow should_implement_trait
on BuiltinAgentPresetKey::from_str (one call site, matches existing
op-ai-skills convention) and cargo fmt --all.