fix(terminal): show the effort level alone, close toolbar lists on an outside click, read per-model effort

This commit is contained in:
l0ng-ai
2026-10-01 15:27:59 +08:00
parent b5a3aae4a9
commit 74dafeca48
2 changed files with 66 additions and 9 deletions
+45 -1
View File
@@ -214,12 +214,34 @@ fn claude_readout(payload: &serde_json::Value, body: &mut serde_json::Value) {
if let Some(effort) = std::env::var("CLAUDE_CODE_EFFORT_LEVEL")
.ok()
.filter(|e| !e.is_empty())
.or_else(|| str_of(settings.get("effortLevel")))
.or_else(|| configured_effort(&settings, model.as_deref()))
{
body["effort"] = effort.into();
}
}
/// The effort level Claude's settings give a model: the one set for that
/// model if there is one, the general one otherwise.
fn configured_effort(settings: &serde_json::Value, model: Option<&str>) -> Option<String> {
let level = |v: Option<&serde_json::Value>| {
v.and_then(|v| v.get("effortLevel"))
.and_then(|v| v.as_str())
.filter(|s| !s.is_empty())
.map(str::to_string)
};
let per_model = settings.get("modelSettings");
model
.and_then(|m| {
let per_model = per_model?;
level(
per_model
.get(m)
.or_else(|| per_model.get(m.trim_end_matches("[1m]"))),
)
})
.or_else(|| level(Some(settings)))
}
fn claude_settings() -> serde_json::Value {
let dir = std::env::var_os("CLAUDE_CONFIG_DIR")
.filter(|d| !d.is_empty())
@@ -2103,6 +2125,28 @@ mod tests {
);
}
#[test]
fn an_effort_set_for_the_model_wins_over_the_general_one() {
let settings = serde_json::json!({
"effortLevel": "high",
"modelSettings": { "claude-opus-5-5": { "effortLevel": "medium" } },
});
assert_eq!(
configured_effort(&settings, Some("claude-opus-5-5")).as_deref(),
Some("medium")
);
assert_eq!(
configured_effort(&settings, Some("claude-opus-5-5[1m]")).as_deref(),
Some("medium")
);
assert_eq!(
configured_effort(&settings, Some("claude-sonnet-5-5")).as_deref(),
Some("high")
);
assert_eq!(configured_effort(&settings, None).as_deref(), Some("high"));
assert_eq!(configured_effort(&serde_json::json!({}), None), None);
}
#[test]
fn other_agents_carry_no_readout() {
let stdin = r#"{"session_id":"s","permission_mode":"plan"}"#;
+21 -8
View File
@@ -1526,6 +1526,10 @@ impl TerminalView {
.left_0()
.bottom_full()
.mb(px(8.))
.on_mouse_down(MouseButton::Left, |_, window, cx| {
window.prevent_default();
cx.stop_propagation();
})
.min_w(px(180.))
.p(px(5.))
.flex()
@@ -1582,6 +1586,16 @@ impl TerminalView {
div()
.relative()
.flex_none()
// A click anywhere else closes the list, as a menu's would.
// The button and the list stop their own clicks short of this.
.when(open, |s| {
s.on_mouse_down_out(cx.listener(|this, _: &MouseDownEvent, _w, cx| {
if let Some(c) = this.composer.as_mut() {
c.picker = None;
}
cx.notify();
}))
})
.child(
tool(id)
.gap(px(5.))
@@ -1643,16 +1657,15 @@ impl TerminalView {
});
let current_effort = c.effort.clone().or_else(|| readout.effort.clone());
let effort = claude.then(|| {
// "Effort" in the toolbar's grey, the level in the text's ink.
// The level alone, as the model button shows the model alone;
// the tooltip and the list's title say what it is.
let name = match current_effort.as_deref() {
Some(level) => effort_label(level),
None => t(L10nKey::ComposerEffort).to_string(),
};
let label = h_flex()
.gap(px(5.))
.text_color(ink.opacity(0.45))
.child(t(L10nKey::ComposerEffort))
.children(
current_effort
.as_deref()
.map(|level| div().text_color(ink).child(effort_label(level))),
)
.child(name)
.child(
Icon::new(IconName::ChevronDown)
.size(px(8.))