mirror of
https://github.com/openai/codex.git
synced 2026-04-24 14:45:27 +00:00
make dollar-mention always clarify item category (skill, app, plugin) (#14147)
#### What ###### Context + Problem With the introduction of plugins, we now have one more type of `$`-mentionable item in the TUI's popup menu on `$`. Apps, skills, and plugins can all have the same user-facing name, and we attempt to distinguish with a category tag suffix, like `[App]`. This has a few problems: - We decide to show tags by the text that will be inserted into the conversation, not the actual user-visible text, so two visibly-identical entries can have no clarifying category tag suffix - The category tag is a suffix and commonly gets cut off by long descriptions - The skill category tag is currently only displayed on repo skills as `[Repo]`, which is confusing to most users - The plugin category tag is currently `[<marketplace-name>]`, which is also confusing to most users ###### Solution - **Always** show a **prefix** category tag that is `[Skill]`, `[App]`, or `[Plugin]`. No conditional rendering or copy. Before: <img width="801" height="153" alt="image" src="https://github.com/user-attachments/assets/448e06e7-2af8-4c14-9804-ed1ca17cf514" /> After: <img width="800" height="118" alt="image" src="https://github.com/user-attachments/assets/57895b41-06fe-4d92-887b-68704c5a15fd" /> I also feel this clarifies the results at-a-glance while you scroll: https://github.com/user-attachments/assets/cbdd5840-53d9-4656-812c-6e816755e1fd ### Tests Added + updated tests (including snapshots), tested locally
This commit is contained in:
@@ -3579,8 +3579,7 @@ impl ChatComposer {
|
||||
insert_text: format!("${skill_name}"),
|
||||
search_terms,
|
||||
path: Some(skill.path_to_skills_md.to_string_lossy().into_owned()),
|
||||
category_tag: (skill.scope == codex_protocol::protocol::SkillScope::Repo)
|
||||
.then(|| "[Repo]".to_string()),
|
||||
category_tag: Some("[Skill]".to_string()),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3631,8 +3630,7 @@ impl ChatComposer {
|
||||
insert_text: format!("${plugin_name}"),
|
||||
search_terms,
|
||||
path: Some(format!("plugin://{}", plugin.config_name)),
|
||||
category_tag: (!marketplace_name.is_empty())
|
||||
.then(|| format!("[{marketplace_name}]")),
|
||||
category_tag: Some("[Plugin]".to_string()),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3660,16 +3658,6 @@ impl ChatComposer {
|
||||
}
|
||||
}
|
||||
|
||||
let mut counts: HashMap<String, usize> = HashMap::new();
|
||||
for mention in &mentions {
|
||||
*counts.entry(mention.insert_text.clone()).or_insert(0) += 1;
|
||||
}
|
||||
for mention in &mut mentions {
|
||||
if counts.get(&mention.insert_text).copied().unwrap_or(0) <= 1 {
|
||||
mention.category_tag = None;
|
||||
}
|
||||
}
|
||||
|
||||
mentions
|
||||
}
|
||||
|
||||
@@ -5343,6 +5331,60 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mention_popup_type_prefixes_snapshot() {
|
||||
snapshot_composer_state_with_width("mention_popup_type_prefixes", 72, false, |composer| {
|
||||
composer.set_connectors_enabled(true);
|
||||
composer.set_text_content("$goog".to_string(), Vec::new(), Vec::new());
|
||||
composer.set_skill_mentions(Some(vec![SkillMetadata {
|
||||
name: "google-calendar-skill".to_string(),
|
||||
description: "Find availability and plan event changes".to_string(),
|
||||
short_description: None,
|
||||
interface: Some(codex_core::skills::model::SkillInterface {
|
||||
display_name: Some("Google Calendar".to_string()),
|
||||
short_description: None,
|
||||
icon_small: None,
|
||||
icon_large: None,
|
||||
brand_color: None,
|
||||
default_prompt: None,
|
||||
}),
|
||||
dependencies: None,
|
||||
policy: None,
|
||||
permission_profile: None,
|
||||
path_to_skills_md: PathBuf::from("/tmp/repo/google-calendar/SKILL.md"),
|
||||
scope: codex_protocol::protocol::SkillScope::Repo,
|
||||
}]));
|
||||
composer.set_plugin_mentions(Some(vec![PluginCapabilitySummary {
|
||||
config_name: "google-calendar@debug".to_string(),
|
||||
display_name: "Google Calendar".to_string(),
|
||||
description: Some(
|
||||
"Connect Google Calendar for scheduling, availability, and event management."
|
||||
.to_string(),
|
||||
),
|
||||
has_skills: false,
|
||||
mcp_server_names: vec!["google-calendar".to_string()],
|
||||
app_connector_ids: Vec::new(),
|
||||
}]));
|
||||
composer.set_connector_mentions(Some(ConnectorsSnapshot {
|
||||
connectors: vec![AppInfo {
|
||||
id: "google_calendar".to_string(),
|
||||
name: "Google Calendar".to_string(),
|
||||
description: Some("Look up events and availability".to_string()),
|
||||
logo_url: None,
|
||||
logo_url_dark: None,
|
||||
distribution_channel: None,
|
||||
branding: None,
|
||||
app_metadata: None,
|
||||
labels: None,
|
||||
install_url: Some("https://example.test/google-calendar".to_string()),
|
||||
is_accessible: true,
|
||||
is_enabled: true,
|
||||
plugin_display_names: Vec::new(),
|
||||
}],
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_connector_mentions_excludes_disabled_apps_from_mention_popup() {
|
||||
let (tx, _rx) = unbounded_channel::<AppEvent>();
|
||||
|
||||
@@ -98,14 +98,26 @@ impl SkillPopup {
|
||||
.map(|(idx, indices, _score)| {
|
||||
let mention = &self.mentions[idx];
|
||||
let name = truncate_text(&mention.display_name, MENTION_NAME_TRUNCATE_LEN);
|
||||
let description = mention.description.clone().unwrap_or_default();
|
||||
let description = match (
|
||||
mention.category_tag.as_deref(),
|
||||
mention.description.as_deref(),
|
||||
) {
|
||||
(Some(tag), Some(description)) if !description.is_empty() => {
|
||||
Some(format!("{tag} {description}"))
|
||||
}
|
||||
(Some(tag), _) => Some(tag.to_string()),
|
||||
(None, Some(description)) if !description.is_empty() => {
|
||||
Some(description.to_string())
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
GenericDisplayRow {
|
||||
name,
|
||||
name_prefix_spans: Vec::new(),
|
||||
match_indices: indices,
|
||||
display_shortcut: None,
|
||||
description: Some(description).filter(|desc| !desc.is_empty()),
|
||||
category_tag: mention.category_tag.clone(),
|
||||
description,
|
||||
category_tag: None,
|
||||
is_disabled: false,
|
||||
disabled_reason: None,
|
||||
wrap_indent: None,
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/chat_composer.rs
|
||||
expression: terminal.backend()
|
||||
---
|
||||
" "
|
||||
"› $goog "
|
||||
" "
|
||||
" "
|
||||
" Google Calendar [Skill] Find availability and plan event changes "
|
||||
" Google Calendar [Plugin] Connect Google Calendar for scheduling, ava…"
|
||||
" Google Calendar [App] Look up events and availability "
|
||||
" "
|
||||
" Press enter to insert or esc to close "
|
||||
@@ -8,6 +8,6 @@ expression: terminal.backend()
|
||||
" "
|
||||
" "
|
||||
" "
|
||||
" Sample Plugin Plugin that includes the Figma MCP server and Skills for common workflows "
|
||||
" Sample Plugin [Plugin] Plugin that includes the Figma MCP server and Skills for common workflows "
|
||||
" "
|
||||
" Press enter to insert or esc to close "
|
||||
|
||||
Reference in New Issue
Block a user