Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions crates/moon-core/src/config/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,13 @@ pub struct WindowLayout {
/// discard the complete window layout.
#[serde(default, deserialize_with = "de_lenient")]
pub strategies_params_full: Option<bool>,
/// Strategies: whether section and field rows carry the localized human name under
/// Moonbot's own identifier.
///
/// `None` keeps the Strategies-owned default. Read leniently so a malformed hand edit cannot
/// discard the complete window layout.
#[serde(default, deserialize_with = "de_lenient")]
pub strategies_human_labels: Option<bool>,
/// Global "Assets" window geometry (singleton), so it reopens in its previous position.
#[serde(default)]
pub assets_window: Option<GeomRect>,
Expand Down
13 changes: 10 additions & 3 deletions crates/moon-ui-gpui/src/strategies/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1127,7 +1127,7 @@ impl StrategiesView {
differ,
param_entries::ParamLabels {
orphans: &orphans,
section_title: &|raw| section_display_title(raw),
section_title: &|raw| section_display_title(raw, self.prefs.human_labels),
},
);
ParamsBody::Full(Rc::new(flat))
Expand Down Expand Up @@ -1292,7 +1292,10 @@ impl StrategiesView {
// Title and field total come from the body; the multi selection-count branch keeps
// priority exactly as before the body could also be a full-mode list.
let (title, field_total) = match &body {
ParamsBody::Section(s) => (section_display_title(&s.title), s.fields.len()),
ParamsBody::Section(s) => (
section_display_title(&s.title, self.prefs.human_labels),
s.fields.len(),
),
ParamsBody::Full(f) => (t!("strat.params_full_title").to_string(), f.field_count),
};
let count = if multi {
Expand Down Expand Up @@ -1694,10 +1697,14 @@ impl StrategiesView {
.any(|(core, id)| self.field_edits.contains_key(&(*core, *id, f.name.clone())));
let field_name = f.name.clone();
let row_id = editor_state_id(keys, &field_name);
// The human name is a preference (`StrategiesPrefs::human_labels`); off, the row keeps
// only the name the core speaks, while the help tooltip is unaffected.
let (field_tooltip, field_label) = match field_keys(&field_name) {
Some((help, label)) => (
help.map(|key| t!(key).to_string()),
label.map(|key| t!(key).to_string()),
label
.filter(|_| self.prefs.human_labels)
.map(|key| t!(key).to_string()),
),
None => (None, None),
};
Expand Down
62 changes: 40 additions & 22 deletions crates/moon-ui-gpui/src/strategies/sections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,28 +109,31 @@ pub(super) fn section_label_key(raw_title: &str) -> Option<&'static str> {
///
/// Args:
/// raw_title: Section title exactly as the streamed schema produced it.
/// human_labels: The `StrategiesPrefs::human_labels` preference; off, the gloss is dropped.
///
/// Returns:
/// `"<raw> · <localized>"` when a label exists, or `raw_title` unchanged.
pub(super) fn section_display_title(raw_title: &str) -> String {
match section_label_key(raw_title) {
/// `"<raw> · <localized>"` when a label exists and labels are on, or `raw_title` unchanged.
pub(super) fn section_display_title(raw_title: &str, human_labels: bool) -> String {
match section_label_key(raw_title).filter(|_| human_labels) {
Some(key) => format!("{raw_title} · {}", t!(key)),
None => raw_title.to_string(),
}
}

/// Two-line caption for a table-of-contents row: the schema title, then its human name under it.
///
/// A section with no label keeps one line, so an unrecognised section looks exactly as it did.
/// A section with no label keeps one line, so an unrecognised section looks exactly as it did —
/// and so does every section once the human-labels preference is off.
///
/// Args:
/// raw_title: Section title exactly as the streamed schema produced it.
/// human_labels: The `StrategiesPrefs::human_labels` preference; off, no second line.
/// muted: Colour of the localized second line.
/// cx: Application context providing active text metrics.
///
/// Returns:
/// A width-owning column that truncates each line on its own.
fn section_caption(raw_title: &str, muted: Hsla, cx: &App) -> impl IntoElement {
fn section_caption(raw_title: &str, human_labels: bool, muted: Hsla, cx: &App) -> impl IntoElement {
v_flex()
.flex_1()
.min_w_0()
Expand All @@ -141,18 +144,21 @@ fn section_caption(raw_title: &str, muted: Hsla, cx: &App) -> impl IntoElement {
.truncate()
.child(raw_title.to_string()),
)
.when_some(section_label_key(raw_title), |col, key| {
col.child(
div()
.w_full()
.min_w_0()
.truncate()
.text_size(design::t_caption(cx))
.line_height(design::line_px(cx, 12.0))
.text_color(muted)
.child(t!(key).to_string()),
)
})
.when_some(
section_label_key(raw_title).filter(|_| human_labels),
|col, key| {
col.child(
div()
.w_full()
.min_w_0()
.truncate()
.text_size(design::t_caption(cx))
.line_height(design::line_px(cx, 12.0))
.text_color(muted)
.child(t!(key).to_string()),
)
},
)
}

impl StrategiesView {
Expand All @@ -175,9 +181,11 @@ impl StrategiesView {
.iter()
.map(|section| {
let raw = design::ui_body_text_width(cx, &section.title, 400.0);
let label = section_label_key(&section.title).map_or(0.0, |key| {
design::ui_caption_text_width(cx, &t!(key).to_string(), 400.0)
});
let label = section_label_key(&section.title)
.filter(|_| self.prefs.human_labels)
.map_or(0.0, |key| {
design::ui_caption_text_width(cx, &t!(key).to_string(), 400.0)
});
raw.max(label)
})
.reduce(f32::max)
Expand Down Expand Up @@ -306,7 +314,12 @@ impl StrategiesView {
.text_color(moon(p.text))
// The count badge beside it cannot shrink, so the caption owns the width
// and degrades to an ellipsis instead of painting over the badge.
.child(section_caption(&sec.title, moon(p.text_muted), cx))
.child(section_caption(
&sec.title,
self.prefs.human_labels,
moon(p.text_muted),
cx,
))
.child(
h_flex().ml_auto().flex_none().child(
MoonBadge::new(n.to_string())
Expand Down Expand Up @@ -376,7 +389,12 @@ impl StrategiesView {
.text_color(moon(tcol))
// The pane is user-resizable down to a width no section name fits, so each line
// of the caption degrades to an ellipsis rather than spilling into the splitter.
.child(section_caption(&sec.title, moon(p.text_muted), cx))
.child(section_caption(
&sec.title,
self.prefs.human_labels,
moon(p.text_muted),
cx,
))
.on_click(cx.listener(move |this, _, _, cx| {
if this.selected_section != i {
this.selected_section = i;
Expand Down
24 changes: 21 additions & 3 deletions crates/moon-ui-gpui/src/strategies/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ pub(super) struct StrategiesPrefs {
pub(super) tree_text_step: f32,
/// Whether the parameters pane shows every section at once instead of one.
pub(super) params_full: bool,
/// Whether section and field rows carry the localized human name under Moonbot's identifier.
pub(super) human_labels: bool,
}

impl Default for StrategiesPrefs {
Expand All @@ -69,13 +71,15 @@ impl Default for StrategiesPrefs {
/// does not silently hide strategies that were visible before the preference existed, the tree
/// text step ships at zero so the pane renders at exactly the theme base, and full mode stays
/// off for the same reason as active-only: an upgrade must not silently change what the pane
/// shows.
/// shows. Human labels ship ON: they arrived on by construction, so keeping them is what
/// leaves the pane unchanged.
fn default() -> Self {
Self {
group_by_venue: true,
active_only: false,
tree_text_step: STRATEGIES_TREE_TEXT_STEP_DEFAULT,
params_full: false,
human_labels: true,
}
}
}
Expand Down Expand Up @@ -161,12 +165,26 @@ const PARAMS_FULL: PrefRow = PrefRow {
store: |layout, value| layout.strategies_params_full = Some(value),
};

/// Show the localized human name under Moonbot's own identifier on section and field rows.
///
/// Off, a section reads `Main` instead of `Main · Основные` and a field row keeps only the name
/// the core speaks; the help tooltip stays either way.
const HUMAN_LABELS: PrefRow = PrefRow {
id: "human-labels",
group: DISPLAY_GROUP,
label: "strat.settings.human_labels",
read: |prefs| prefs.human_labels,
set: |prefs, value| prefs.human_labels = value,
saved: |layout| layout.strategies_human_labels,
store: |layout, value| layout.strategies_human_labels = Some(value),
};

/// Every preference, in the order `restore` resolves them. Persistence covers all of them wherever
/// their control lives.
const PREF_ROWS: [&PrefRow; 3] = [&GROUP_BY_VENUE, &ACTIVE_ONLY, &PARAMS_FULL];
const PREF_ROWS: [&PrefRow; 4] = [&GROUP_BY_VENUE, &ACTIVE_ONLY, &PARAMS_FULL, &HUMAN_LABELS];

/// The subset the settings popup renders, in display order.
const POPUP_ROWS: [&PrefRow; 1] = [&GROUP_BY_VENUE];
const POPUP_ROWS: [&PrefRow; 2] = [&GROUP_BY_VENUE, &HUMAN_LABELS];

impl StrategiesView {
/// Apply and persist one Strategies display preference.
Expand Down
1 change: 1 addition & 0 deletions crates/moon-ui-gpui/src/strategies/settings/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ fn saved_preferences_restore_independently() {
active_only: true,
tree_text_step: 0.0,
params_full: false,
human_labels: true,
}
);
}
Expand Down
4 changes: 4 additions & 0 deletions locales/strategies.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ strat.settings.params_full:
ru: "Показывать все разделы параметров сразу"
en: "Show every parameter section at once"
es: "Mostrar todas las secciones de parámetros a la vez"
strat.settings.human_labels:
ru: "Подписывать разделы и поля на моём языке"
en: "Caption sections and fields in my language"
es: "Rotular secciones y campos en mi idioma"
strat.params_mode_sections:
ru: "по разделам"
en: "by section"
Expand Down