feat(shell-core): thread localised strings through LayerPanel + AIChatPanel paint

Theme + locale toggle infrastructure landed in ed36df56, but the
visible chrome strings were still hardcoded so flipping the Globe
icon didn't actually change anything. Now:

- LayerPanel resolves '页面' / '图层' from doc.t() at construction
  and stores as String fields; paint reads those instead of
  hardcoded literals.
- AIChatPlaceholder resolves 'New Chat' / '用 AI 开始设计' /
  '用 Agent 设计…' the same way; paint_examples takes the hint
  label as a parameter.

TopBar 'untitled' label was already wired (for_document uses
doc.t). 67 lib tests still pass.
This commit is contained in:
Kayshen-X 2026-05-10 18:40:46 +08:00
parent 91d9e99a94
commit 9506341da0
2 changed files with 26 additions and 8 deletions

View file

@ -88,6 +88,12 @@ pub struct AIChatPlaceholder<'a> {
/// [`jian_core::anim::blink_visible`]. `0` = host hasn't
/// installed a clock yet (caret stays solid).
pub now_ms: u64,
/// Localised chrome strings — resolved at construction time
/// from `Document::t` so the panel reflows when the user
/// flips the TopBar Globe icon.
pub label_new_chat: String,
pub label_start_with_ai: String,
pub label_input_placeholder: String,
}
impl<'a> AIChatPlaceholder<'a> {
@ -103,6 +109,9 @@ impl<'a> AIChatPlaceholder<'a> {
theme: doc.theme(),
state: &doc.chat,
now_ms,
label_new_chat: doc.t("chat.new_chat").to_string(),
label_start_with_ai: doc.t("chat.start_with_ai").to_string(),
label_input_placeholder: doc.t("chat.input_placeholder").to_string(),
}
}
@ -207,7 +216,7 @@ impl<'a> Widget for AIChatPlaceholder<'a> {
);
// "New Chat" label.
let title = TextLayout::single_run(
"New Chat",
&self.label_new_chat,
"system-ui",
13.0,
to_jian_color(self.theme.foreground),
@ -246,7 +255,7 @@ impl<'a> Widget for AIChatPlaceholder<'a> {
1.4,
);
let title = TextLayout::single_run(
"New Chat",
&self.label_new_chat,
"system-ui",
14.0,
to_jian_color(self.theme.foreground),
@ -282,7 +291,7 @@ impl<'a> Widget for AIChatPlaceholder<'a> {
};
if self.state.messages.is_empty() {
paint_examples(cx, &self.theme, rect);
paint_examples(cx, &self.theme, rect, &self.label_start_with_ai);
} else {
paint_messages(cx, &self.theme, body_rect, &self.state.messages);
}
@ -311,7 +320,10 @@ impl<'a> Widget for AIChatPlaceholder<'a> {
// Input text — buffer if non-empty, placeholder otherwise.
let (text, color) = if self.state.input.is_empty() {
("用 Agent 设计…", self.theme.muted_foreground)
(
self.label_input_placeholder.as_str(),
self.theme.muted_foreground,
)
} else {
(self.state.input.as_str(), self.theme.foreground)
};
@ -400,9 +412,9 @@ impl<'a> Widget for AIChatPlaceholder<'a> {
}
}
fn paint_examples(cx: &mut PaintCx<'_>, theme: &Theme, rect: Rect) {
fn paint_examples(cx: &mut PaintCx<'_>, theme: &Theme, rect: Rect, hint_label: &str) {
let hint = TextLayout::single_run(
"用 AI 开始设计",
hint_label,
"system-ui",
12.0,
to_jian_color(theme.muted_foreground),

View file

@ -49,6 +49,8 @@ pub struct LayerPanel {
pub pages: Vec<PageItem>,
pub items: Vec<LayerItem>,
pub theme: Theme,
pub pages_label: String,
pub layers_label: String,
}
impl LayerPanel {
@ -77,6 +79,8 @@ impl LayerPanel {
pages,
items,
theme: doc.theme(),
pages_label: doc.t("layer_panel.pages").to_string(),
layers_label: doc.t("layer_panel.layers").to_string(),
}
}
@ -86,6 +90,8 @@ impl LayerPanel {
pages: Vec::new(),
items: Vec::new(),
theme: Theme::dark(),
pages_label: "Pages".to_string(),
layers_label: "Layers".to_string(),
}
}
@ -187,7 +193,7 @@ impl Widget for LayerPanel {
let mut y = rect.origin.y + 8.0;
// Pages section header.
paint_section_header(cx, &self.theme, rect.origin.x, y, rect.size.x, "页面");
paint_section_header(cx, &self.theme, rect.origin.x, y, rect.size.x, &self.pages_label);
// "+" add-page affordance, top-right of header row.
let plus_x = rect.origin.x + rect.size.x - ROW_PAD_X - 12.0;
let plus_y = y + (SECTION_HEADER_HEIGHT - 14.0) / 2.0;
@ -232,7 +238,7 @@ impl Widget for LayerPanel {
y += SECTION_GAP;
// Layers section header.
paint_section_header(cx, &self.theme, rect.origin.x, y, rect.size.x, "图层");
paint_section_header(cx, &self.theme, rect.origin.x, y, rect.size.x, &self.layers_label);
y += SECTION_HEADER_HEIGHT;
// Layer rows.