From 1bd6b4c41a1ed5ed412eae9a59b95c4ee25e9860 Mon Sep 17 00:00:00 2001 From: canvrno-oai Date: Fri, 12 Jun 2026 16:29:40 -0700 Subject: [PATCH] Promote TUI unified mentions in composer to default mentions feature (#27499) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary This PR promotes Mentions 2.0 (unified TUI mention popup) to stable and enables it by default. - Keep `mentions_v2` as a temporary rollback path to the legacy split popups (`--disable mentions_v2`). - Add feature-default and snapshot coverage for the default experience. ## Prior work - [#19068 — Unified mentions in TUI](https://github.com/openai/codex/pull/19068) - [#22375 — Use plugin/list to get plugins for mentions](https://github.com/openai/codex/pull/22375) - [#23363 — Unified mentions tweaks and rendering polish](https://github.com/openai/codex/pull/23363) ## Test plan - Launch Codex without any feature overrides. - Type `@` in the TUI composer. - Confirm the unified mentions menu opens and displays filesystem, plugin, and skill results. --- codex-rs/features/src/lib.rs | 6 ++--- codex-rs/features/src/tests.rs | 6 ++--- codex-rs/tui/src/bottom_pane/chat_composer.rs | 22 +++++++++++++++++++ .../tui/src/bottom_pane/mentions_v2/mod.rs | 5 +++++ ..._tests__default_unified_mention_popup.snap | 13 +++++++++++ codex-rs/tui/src/chatwidget/skills.rs | 9 +++++++- ...skills_menu_default_mentions_shortcut.snap | 11 ++++++++++ .../chatwidget/tests/popups_and_settings.rs | 9 ++++++++ 8 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__default_unified_mention_popup.snap create mode 100644 codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__skills_menu_default_mentions_shortcut.snap diff --git a/codex-rs/features/src/lib.rs b/codex-rs/features/src/lib.rs index 1b13d41b4..c907849cb 100644 --- a/codex-rs/features/src/lib.rs +++ b/codex-rs/features/src/lib.rs @@ -191,7 +191,7 @@ pub enum Feature { SkillMcpDependencyInstall, /// Removed compatibility flag for deleted skill env var dependency prompting. SkillEnvVarDependencyPrompt, - /// Enable the unified mention popup prototype. + /// Enable the unified mention popup used by default in the TUI. MentionsV2, /// Allow request_user_input in Default collaboration mode. DefaultModeRequestUserInput, @@ -1119,8 +1119,8 @@ pub const FEATURES: &[FeatureSpec] = &[ FeatureSpec { id: Feature::MentionsV2, key: "mentions_v2", - stage: Stage::UnderDevelopment, - default_enabled: false, + stage: Stage::Stable, + default_enabled: true, }, FeatureSpec { id: Feature::Steer, diff --git a/codex-rs/features/src/tests.rs b/codex-rs/features/src/tests.rs index fd39cab26..4f02815e4 100644 --- a/codex-rs/features/src/tests.rs +++ b/codex-rs/features/src/tests.rs @@ -310,9 +310,9 @@ fn auth_elicitation_is_under_development() { } #[test] -fn mentions_v2_is_under_development_and_disabled_by_default() { - assert_eq!(Feature::MentionsV2.stage(), Stage::UnderDevelopment); - assert_eq!(Feature::MentionsV2.default_enabled(), false); +fn mentions_v2_is_stable_and_enabled_by_default() { + assert_eq!(Feature::MentionsV2.stage(), Stage::Stable); + assert_eq!(Feature::MentionsV2.default_enabled(), true); assert_eq!(feature_for_key("mentions_v2"), Some(Feature::MentionsV2)); } diff --git a/codex-rs/tui/src/bottom_pane/chat_composer.rs b/codex-rs/tui/src/bottom_pane/chat_composer.rs index 5745d1735..0f4f89d5a 100644 --- a/codex-rs/tui/src/bottom_pane/chat_composer.rs +++ b/codex-rs/tui/src/bottom_pane/chat_composer.rs @@ -6354,6 +6354,28 @@ mod tests { ); } + #[test] + fn default_unified_mention_popup_snapshot() { + snapshot_composer_state( + "default_unified_mention_popup", + /*enhanced_keys_supported*/ false, + |composer| { + let features = codex_features::Features::with_defaults(); + composer + .set_mentions_v2_enabled(features.enabled(codex_features::Feature::MentionsV2)); + composer.set_text_content("@sa".to_string(), Vec::new(), Vec::new()); + composer.set_plugin_mentions(Some(vec![PluginCapabilitySummary { + config_name: "sample@test".to_string(), + display_name: "Sample Plugin".to_string(), + description: Some("Plugin with skills and an MCP server".to_string()), + has_skills: true, + mcp_server_names: vec!["sample".to_string()], + app_connector_ids: Vec::new(), + }])); + }, + ); + } + #[test] fn mention_popup_type_prefixes_snapshot() { snapshot_composer_state_with_width( diff --git a/codex-rs/tui/src/bottom_pane/mentions_v2/mod.rs b/codex-rs/tui/src/bottom_pane/mentions_v2/mod.rs index 8e33c3e87..ca6e280cb 100644 --- a/codex-rs/tui/src/bottom_pane/mentions_v2/mod.rs +++ b/codex-rs/tui/src/bottom_pane/mentions_v2/mod.rs @@ -1,3 +1,8 @@ +//! The unified mention popup used by default in the TUI. +//! +//! The `mentions_v2` feature flag remains temporarily as a rollback path: disabling it restores +//! the legacy split mention and file-search popups. + mod candidate; mod filter; mod footer; diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__default_unified_mention_popup.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__default_unified_mention_popup.snap new file mode 100644 index 000000000..797e4113f --- /dev/null +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__chat_composer__tests__default_unified_mention_popup.snap @@ -0,0 +1,13 @@ +--- +source: tui/src/bottom_pane/chat_composer.rs +expression: terminal.backend() +--- +" " +"› @sa " +" " +" Sample Plugin Plugin with skills and an MCP server Plugin" +" " +" " +" " +" " +" enter insert · esc close · ←/→ switch search modes [All Results] Filesystem Only Plugins " diff --git a/codex-rs/tui/src/chatwidget/skills.rs b/codex-rs/tui/src/chatwidget/skills.rs index 687ce5641..53b70be5c 100644 --- a/codex-rs/tui/src/chatwidget/skills.rs +++ b/codex-rs/tui/src/chatwidget/skills.rs @@ -33,10 +33,17 @@ impl ChatWidget { } pub(crate) fn open_skills_menu(&mut self) { + let list_shortcut = if self.config.features.enabled(Feature::MentionsV2) { + '@' + } else { + '$' + }; let items = vec![ SelectionItem { name: "List skills".to_string(), - description: Some("Tip: press $ to open this list directly.".to_string()), + description: Some(format!( + "Tip: press {list_shortcut} to open this list directly." + )), actions: vec![Box::new(|tx| { tx.send(AppEvent::OpenSkillsList); })], diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__skills_menu_default_mentions_shortcut.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__skills_menu_default_mentions_shortcut.snap new file mode 100644 index 000000000..f63ef4958 --- /dev/null +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__skills_menu_default_mentions_shortcut.snap @@ -0,0 +1,11 @@ +--- +source: tui/src/chatwidget/tests/popups_and_settings.rs +expression: popup +--- + Skills + Choose an action + +› 1. List skills Tip: press @ to open this list directly. + 2. Enable/Disable Skills Enable or disable skills. + + Press enter to confirm or esc to go back diff --git a/codex-rs/tui/src/chatwidget/tests/popups_and_settings.rs b/codex-rs/tui/src/chatwidget/tests/popups_and_settings.rs index 86dc5fb4e..729b5faca 100644 --- a/codex-rs/tui/src/chatwidget/tests/popups_and_settings.rs +++ b/codex-rs/tui/src/chatwidget/tests/popups_and_settings.rs @@ -2575,6 +2575,15 @@ async fn personality_selection_popup_snapshot() { assert_chatwidget_snapshot!("personality_selection_popup", popup); } +#[tokio::test] +async fn skills_menu_default_mentions_shortcut_snapshot() { + let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; + chat.open_skills_menu(); + + let popup = render_bottom_popup(&chat, /*width*/ 80); + assert_chatwidget_snapshot!("skills_menu_default_mentions_shortcut", popup); +} + #[tokio::test] async fn model_picker_hides_show_in_picker_false_models_from_cache() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("test-visible-model")).await;