mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Add model availability NUX tooltips (#13021)
- override startup tooltips with model availability NUX and persist per-model show counts in config - stop showing each model after four exposures and fall back to normal tooltips
This commit is contained in:
@@ -8,6 +8,7 @@ use codex_protocol::config_types::Personality;
|
||||
use codex_protocol::config_types::TrustLevel;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use tokio::task;
|
||||
@@ -75,6 +76,27 @@ pub fn status_line_items_edit(items: &[String]) -> ConfigEdit {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn model_availability_nux_count_edits(shown_count: &HashMap<String, u32>) -> Vec<ConfigEdit> {
|
||||
let mut shown_count_entries: Vec<_> = shown_count.iter().collect();
|
||||
shown_count_entries.sort_unstable_by(|(left, _), (right, _)| left.cmp(right));
|
||||
|
||||
let mut edits = vec![ConfigEdit::ClearPath {
|
||||
segments: vec!["tui".to_string(), "model_availability_nux".to_string()],
|
||||
}];
|
||||
for (model_slug, count) in shown_count_entries {
|
||||
edits.push(ConfigEdit::SetPath {
|
||||
segments: vec![
|
||||
"tui".to_string(),
|
||||
"model_availability_nux".to_string(),
|
||||
model_slug.clone(),
|
||||
],
|
||||
value: value(i64::from(*count)),
|
||||
});
|
||||
}
|
||||
|
||||
edits
|
||||
}
|
||||
|
||||
// TODO(jif) move to a dedicated file
|
||||
mod document_helpers {
|
||||
use crate::config::types::McpServerConfig;
|
||||
@@ -799,6 +821,12 @@ impl ConfigEditsBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_model_availability_nux_count(mut self, shown_count: &HashMap<String, u32>) -> Self {
|
||||
self.edits
|
||||
.extend(model_availability_nux_count_edits(shown_count));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn replace_mcp_servers(mut self, servers: &BTreeMap<String, McpServerConfig>) -> Self {
|
||||
self.edits
|
||||
.push(ConfigEdit::ReplaceMcpServers(servers.clone()));
|
||||
@@ -963,6 +991,25 @@ model_reasoning_effort = "high"
|
||||
assert_eq!(contents, "enabled = true\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_model_availability_nux_count_writes_shown_count() {
|
||||
let tmp = tempdir().expect("tmpdir");
|
||||
let codex_home = tmp.path();
|
||||
let shown_count = HashMap::from([("gpt-foo".to_string(), 4)]);
|
||||
|
||||
ConfigEditsBuilder::new(codex_home)
|
||||
.set_model_availability_nux_count(&shown_count)
|
||||
.apply_blocking()
|
||||
.expect("persist");
|
||||
|
||||
let contents =
|
||||
std::fs::read_to_string(codex_home.join(CONFIG_TOML_FILE)).expect("read config");
|
||||
let expected = r#"[tui.model_availability_nux]
|
||||
gpt-foo = 4
|
||||
"#;
|
||||
assert_eq!(contents, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_skill_config_writes_disabled_entry() {
|
||||
let tmp = tempdir().expect("tmpdir");
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::config::types::McpServerDisabledReason;
|
||||
use crate::config::types::McpServerTransportConfig;
|
||||
use crate::config::types::MemoriesConfig;
|
||||
use crate::config::types::MemoriesToml;
|
||||
use crate::config::types::ModelAvailabilityNuxConfig;
|
||||
use crate::config::types::Notice;
|
||||
use crate::config::types::NotificationMethod;
|
||||
use crate::config::types::Notifications;
|
||||
@@ -276,6 +277,9 @@ pub struct Config {
|
||||
/// Show startup tooltips in the TUI welcome screen.
|
||||
pub show_tooltips: bool,
|
||||
|
||||
/// Persisted startup availability NUX state for model tooltips.
|
||||
pub model_availability_nux: ModelAvailabilityNuxConfig,
|
||||
|
||||
/// Start the TUI in the specified collaboration mode (plan/default).
|
||||
|
||||
/// Controls whether the TUI uses the terminal's alternate screen buffer.
|
||||
@@ -2213,6 +2217,11 @@ impl Config {
|
||||
.unwrap_or_default(),
|
||||
animations: cfg.tui.as_ref().map(|t| t.animations).unwrap_or(true),
|
||||
show_tooltips: cfg.tui.as_ref().map(|t| t.show_tooltips).unwrap_or(true),
|
||||
model_availability_nux: cfg
|
||||
.tui
|
||||
.as_ref()
|
||||
.map(|t| t.model_availability_nux.clone())
|
||||
.unwrap_or_default(),
|
||||
tui_alternate_screen: cfg
|
||||
.tui
|
||||
.as_ref()
|
||||
@@ -2401,6 +2410,7 @@ mod tests {
|
||||
use crate::config::types::McpServerTransportConfig;
|
||||
use crate::config::types::MemoriesConfig;
|
||||
use crate::config::types::MemoriesToml;
|
||||
use crate::config::types::ModelAvailabilityNuxConfig;
|
||||
use crate::config::types::NotificationMethod;
|
||||
use crate::config::types::Notifications;
|
||||
use crate::config_loader::RequirementSource;
|
||||
@@ -2539,6 +2549,51 @@ phase_2_model = "gpt-5"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_toml_deserializes_model_availability_nux() {
|
||||
let toml = r#"
|
||||
[tui.model_availability_nux]
|
||||
"gpt-foo" = 2
|
||||
"gpt-bar" = 4
|
||||
"#;
|
||||
let cfg: ConfigToml =
|
||||
toml::from_str(toml).expect("TOML deserialization should succeed for TUI NUX");
|
||||
|
||||
assert_eq!(
|
||||
cfg.tui.expect("tui config should deserialize"),
|
||||
Tui {
|
||||
notifications: Notifications::default(),
|
||||
notification_method: NotificationMethod::default(),
|
||||
animations: true,
|
||||
show_tooltips: true,
|
||||
alternate_screen: AltScreenMode::default(),
|
||||
status_line: None,
|
||||
theme: None,
|
||||
model_availability_nux: ModelAvailabilityNuxConfig {
|
||||
shown_count: HashMap::from([
|
||||
("gpt-bar".to_string(), 4),
|
||||
("gpt-foo".to_string(), 2),
|
||||
]),
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_config_defaults_model_availability_nux() {
|
||||
let cfg = Config::load_from_base_config_with_overrides(
|
||||
ConfigToml::default(),
|
||||
ConfigOverrides::default(),
|
||||
tempdir().expect("tempdir").path().to_path_buf(),
|
||||
)
|
||||
.expect("load config");
|
||||
|
||||
assert_eq!(
|
||||
cfg.model_availability_nux,
|
||||
ModelAvailabilityNuxConfig::default()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_toml_deserializes_permissions_network() {
|
||||
let toml = r#"
|
||||
@@ -2673,6 +2728,7 @@ theme = "dracula"
|
||||
alternate_screen: AltScreenMode::Auto,
|
||||
status_line: None,
|
||||
theme: None,
|
||||
model_availability_nux: ModelAvailabilityNuxConfig::default(),
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -4884,6 +4940,7 @@ model_verbosity = "high"
|
||||
tui_notification_method: Default::default(),
|
||||
animations: true,
|
||||
show_tooltips: true,
|
||||
model_availability_nux: ModelAvailabilityNuxConfig::default(),
|
||||
analytics_enabled: Some(true),
|
||||
feedback_enabled: true,
|
||||
tui_alternate_screen: AltScreenMode::Auto,
|
||||
@@ -5011,6 +5068,7 @@ model_verbosity = "high"
|
||||
tui_notification_method: Default::default(),
|
||||
animations: true,
|
||||
show_tooltips: true,
|
||||
model_availability_nux: ModelAvailabilityNuxConfig::default(),
|
||||
analytics_enabled: Some(true),
|
||||
feedback_enabled: true,
|
||||
tui_alternate_screen: AltScreenMode::Auto,
|
||||
@@ -5136,6 +5194,7 @@ model_verbosity = "high"
|
||||
tui_notification_method: Default::default(),
|
||||
animations: true,
|
||||
show_tooltips: true,
|
||||
model_availability_nux: ModelAvailabilityNuxConfig::default(),
|
||||
analytics_enabled: Some(false),
|
||||
feedback_enabled: true,
|
||||
tui_alternate_screen: AltScreenMode::Auto,
|
||||
@@ -5247,6 +5306,7 @@ model_verbosity = "high"
|
||||
tui_notification_method: Default::default(),
|
||||
animations: true,
|
||||
show_tooltips: true,
|
||||
model_availability_nux: ModelAvailabilityNuxConfig::default(),
|
||||
analytics_enabled: Some(true),
|
||||
feedback_enabled: true,
|
||||
tui_alternate_screen: AltScreenMode::Auto,
|
||||
|
||||
@@ -667,6 +667,14 @@ impl fmt::Display for NotificationMethod {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default, JsonSchema)]
|
||||
#[schemars(deny_unknown_fields)]
|
||||
pub struct ModelAvailabilityNuxConfig {
|
||||
/// Number of times a startup availability NUX has been shown per model slug.
|
||||
#[serde(default, flatten)]
|
||||
pub shown_count: HashMap<String, u32>,
|
||||
}
|
||||
|
||||
/// Collection of settings that are specific to the TUI.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema)]
|
||||
#[schemars(deny_unknown_fields)]
|
||||
@@ -716,6 +724,10 @@ pub struct Tui {
|
||||
/// Use `/theme` in the TUI or see `$CODEX_HOME/themes` for custom themes.
|
||||
#[serde(default)]
|
||||
pub theme: Option<String>,
|
||||
|
||||
/// Startup tooltip availability NUX state persisted by the TUI.
|
||||
#[serde(default)]
|
||||
pub model_availability_nux: ModelAvailabilityNuxConfig,
|
||||
}
|
||||
|
||||
const fn default_true() -> bool {
|
||||
|
||||
Reference in New Issue
Block a user