diff --git a/crates/op-host-web/src/widget_host/keyboard_edit_ops.rs b/crates/op-host-web/src/widget_host/keyboard_edit_ops.rs index 681923557..131c41112 100644 --- a/crates/op-host-web/src/widget_host/keyboard_edit_ops.rs +++ b/crates/op-host-web/src/widget_host/keyboard_edit_ops.rs @@ -434,12 +434,25 @@ impl WidgetHost { if !is_mod || !shift || alt { return false; } - if self.input_active() { + // The collaboration invite field must keep ownership of editor + // shortcuts while the user is typing. + if self.editor_state.editor_ui.collab_join_input_active() { return true; } if key.eq_ignore_ascii_case("k") { - self.apply_toggle_component_browser() + // K may close its own component browser even though that search + // surface counts as an active input. Every other focused input + // keeps ownership, matching the native settings/Git guards. + if self.editor_state.editor_ui.component_browser_open { + self.apply_toggle_component_browser() + } else if self.input_active() { + true + } else { + self.apply_toggle_component_browser() + } } else if key.eq_ignore_ascii_case("f") { + // F/H intentionally blur inputs covered by the import modal, with + // settings/Git/modal guards inside `apply_open_import`. self.apply_open_import(ImportSource::Figma) } else if key.eq_ignore_ascii_case("h") { self.apply_open_import(ImportSource::Html) diff --git a/crates/op-host-web/src/widget_host/keyboard_tests.rs b/crates/op-host-web/src/widget_host/keyboard_tests.rs index 6ff642e52..2186b8f91 100644 --- a/crates/op-host-web/src/widget_host/keyboard_tests.rs +++ b/crates/op-host-web/src/widget_host/keyboard_tests.rs @@ -54,6 +54,10 @@ fn keydown_shortcut_cmd_shift_k_toggles_component_browser() { host.editor_state .editor_ui .component_browser_kit_picker_open = true; + assert!( + host.input_active(), + "the open component browser owns keyboard input" + ); assert!(host.apply_keydown_shortcut("k", true, true, false)); assert!(!host.editor_state.editor_ui.component_browser_open); @@ -71,6 +75,32 @@ fn keydown_shortcut_cmd_shift_k_toggles_component_browser() { assert!(!host.editor_state_dirty); } +#[test] +fn keydown_component_shortcut_does_not_escape_settings_or_git_inputs() { + let mut host = WidgetHost::new(); + host.editor_state.editor_ui.agent_settings_open = true; + host.editor_state.editor_ui.agent_settings.focus = Some(SettingsFocus::McpPort); + host.editor_state_dirty = false; + + assert!(host.apply_keydown_shortcut("K", true, true, false)); + assert!(!host.editor_state.editor_ui.component_browser_open); + assert_eq!( + host.editor_state.editor_ui.agent_settings.focus, + Some(SettingsFocus::McpPort) + ); + assert!(!host.editor_state_dirty); + + host.editor_state.editor_ui.agent_settings.focus = None; + host.editor_state.editor_ui.agent_settings_open = false; + host.editor_state.editor_ui.git_panel.open = true; + host.editor_state.editor_ui.git_panel.commit_focused = true; + + assert!(host.apply_keydown_shortcut("K", true, true, false)); + assert!(!host.editor_state.editor_ui.component_browser_open); + assert!(host.editor_state.editor_ui.git_panel.commit_focused); + assert!(!host.editor_state_dirty); +} + #[test] fn keydown_import_shortcuts_select_their_source_without_stale_state() { let mut host = WidgetHost::new();