diff --git a/crates/tui/locales/en.json b/crates/tui/locales/en.json index e999dcfd2..300da0a5a 100644 --- a/crates/tui/locales/en.json +++ b/crates/tui/locales/en.json @@ -396,7 +396,7 @@ "SetupOperateNeedsActionSaved": "Operate/Fleet still needs action; recorded for setup report.", "SetupHotbarBindingsLabel": "Hotbar bindings:", "SetupHotbarActionsLabel": "Bindable actions:", - "SetupHotbarReviewHint": "Press H to customize slots; Enter records this Hotbar snapshot.", + "SetupHotbarReviewHint": "Enter records this Hotbar snapshot. Press H to customize slots.", "SetupHotbarReviewed": "Hotbar setup state recorded.", "SetupToolsMcpServersLabel": "MCP servers:", "SetupToolsMcpSkillsLabel": "Skills:", @@ -408,7 +408,7 @@ "SetupRemoteBridgesLabel": "Chat bridges:", "SetupRemoteProvidersLabel": "Providers:", "SetupRemoteModeLabel": "Remote mode:", - "SetupRemoteReviewHint": "Press R to preview; Enter records this Remote snapshot.", + "SetupRemoteReviewHint": "Enter records this Remote snapshot. Press R to preview.", "SetupRemotePreviewTitle": "Remote runtime on-ramp", "SetupRemoteReviewed": "Remote runtime on-ramp recorded.", "SetupPersistenceHomeLabel": "Home:", @@ -424,7 +424,7 @@ "SetupProviderModelReviewed": "Provider/model readiness recorded.", "SetupProviderModelNeedsActionSaved": "Provider/model still needs action; recorded for setup report.", "SetupRuntimePostureBoundary": "Runtime posture is enforced config; constitution guidance never changes it silently.", - "SetupRuntimePostureReviewHint": "Enter records this posture; press M for work mode or C for config.", + "SetupRuntimePostureReviewHint": "Enter records this Runtime snapshot. Press M for work mode or C for config.", "SetupRuntimePostureReviewed": "Runtime posture reviewed; no config changed.", "SetupRuntimePresetSelectedLabel": "Selected preset:", "SetupRuntimePresetDiffLabel": "Config diff:", @@ -486,7 +486,6 @@ "CtxMenuHelp": "Help", "CtxMenuHelpDesc": "keybindings and commands", "FanoutCounts": "{done} done · {running} running · {failed} failed · {pending} pending", - "ModePickerPrompt": "Choose how CodeWhale should operate:", "AppModeAgent": "Act", "AppModeAuto": "Auto", "AppModeYolo": "YOLO (deprecated)", diff --git a/crates/tui/locales/es-419.json b/crates/tui/locales/es-419.json index af5d49620..b82190abd 100644 --- a/crates/tui/locales/es-419.json +++ b/crates/tui/locales/es-419.json @@ -484,7 +484,6 @@ "CtxMenuHelp": "Ayuda", "CtxMenuHelpDesc": "atajos de teclado y comandos", "FanoutCounts": "{done} completado · {running} ejecutando · {failed} falló · {pending} pendiente", - "ModePickerPrompt": "Elige cómo debe funcionar CodeWhale:", "AppModeAgent": "Agente", "AppModeAuto": "Automático", "AppModeYolo": "YOLO", diff --git a/crates/tui/locales/ja.json b/crates/tui/locales/ja.json index 479097a72..b0891a476 100644 --- a/crates/tui/locales/ja.json +++ b/crates/tui/locales/ja.json @@ -484,7 +484,6 @@ "CtxMenuHelp": "ヘルプ", "CtxMenuHelpDesc": "キー操作とコマンド", "FanoutCounts": "{done} 完了 · {running} 実行中 · {failed} 失敗 · {pending} 保留", - "ModePickerPrompt": "CodeWhale の動作方法を選択してください:", "AppModeAgent": "エージェント", "AppModeAuto": "自動", "AppModeYolo": "YOLO", diff --git a/crates/tui/locales/pt-BR.json b/crates/tui/locales/pt-BR.json index 7770cc1b1..fb3a0395c 100644 --- a/crates/tui/locales/pt-BR.json +++ b/crates/tui/locales/pt-BR.json @@ -484,7 +484,6 @@ "CtxMenuHelp": "Ajuda", "CtxMenuHelpDesc": "atalhos de teclado e comandos", "FanoutCounts": "{done} concluído · {running} em execução · {failed} falhou · {pending} pendente", - "ModePickerPrompt": "Escolha como o CodeWhale deve operar:", "AppModeAgent": "Agente", "AppModeAuto": "Automático", "AppModeYolo": "YOLO", diff --git a/crates/tui/locales/vi.json b/crates/tui/locales/vi.json index c4ae74de0..c9af16d32 100644 --- a/crates/tui/locales/vi.json +++ b/crates/tui/locales/vi.json @@ -484,7 +484,6 @@ "CtxMenuHelp": "Trợ giúp", "CtxMenuHelpDesc": "phím tắt và lệnh", "FanoutCounts": "{done} hoàn thành · {running} đang chạy · {failed} thất bại · {pending} chờ", - "ModePickerPrompt": "Chọn cách CodeWhale hoạt động:", "AppModeAgent": "Tác nhân", "AppModeAuto": "Tự động", "AppModeYolo": "YOLO", diff --git a/crates/tui/locales/zh-Hans.json b/crates/tui/locales/zh-Hans.json index 2e5d6839f..bb761d118 100644 --- a/crates/tui/locales/zh-Hans.json +++ b/crates/tui/locales/zh-Hans.json @@ -484,7 +484,6 @@ "CtxMenuHelp": "帮助", "CtxMenuHelpDesc": "快捷键和命令", "FanoutCounts": "{done} 已完成 · {running} 运行中 · {failed} 失败 · {pending} 等待中", - "ModePickerPrompt": "选择 CodeWhale 的运行方式:", "AppModeAgent": "智能体", "AppModeAuto": "自动", "AppModeYolo": "YOLO", diff --git a/crates/tui/locales/zh-Hant.json b/crates/tui/locales/zh-Hant.json index 8081e68d0..493673917 100644 --- a/crates/tui/locales/zh-Hant.json +++ b/crates/tui/locales/zh-Hant.json @@ -8,7 +8,6 @@ "TranslationFailed": "翻譯失敗", "FooterBalancePrefix": "餘額", "FanoutCounts": "{done} 已完成 · {running} 運行中 · {failed} 失敗 · {pending} 等待中", - "ModePickerPrompt": "選擇 CodeWhale 的運作方式:", "AppModeAgent": "智能體", "AppModeAuto": "自動", "AppModeYolo": "YOLO", diff --git a/crates/tui/src/localization.rs b/crates/tui/src/localization.rs index 4216878ea..a9fb8d837 100644 --- a/crates/tui/src/localization.rs +++ b/crates/tui/src/localization.rs @@ -574,8 +574,7 @@ pub enum MessageId { // Agent fanout card. FanoutCounts, - // App mode picker (prompt, names, hints) and composer vim indicator. - ModePickerPrompt, + // App mode picker (names, hints) and composer vim indicator. AppModeAgent, AppModeAuto, AppModeYolo, @@ -1201,7 +1200,6 @@ pub const ALL_MESSAGE_IDS: &[MessageId] = &[ MessageId::CtxMenuHelp, MessageId::CtxMenuHelpDesc, MessageId::FanoutCounts, - MessageId::ModePickerPrompt, MessageId::AppModeAgent, MessageId::AppModeAuto, MessageId::AppModeYolo, @@ -1624,11 +1622,10 @@ mod tests { #[test] fn mode_picker_strings_are_translated_in_non_english_locales() { - // The picker prompt and the three mode hints are full sentences; every - // shipped non-English locale must provide a real translation rather than - // leaking the English string through the fallback chain. + // The mode hints are full sentences; every shipped non-English locale + // must provide a real translation rather than leaking the English + // string through the fallback chain. let sentences = [ - MessageId::ModePickerPrompt, MessageId::AppModeAgentHint, MessageId::AppModeAutoHint, MessageId::AppModePlanHint, diff --git a/crates/tui/src/tui/history.rs b/crates/tui/src/tui/history.rs index b34508d28..79e6838b4 100644 --- a/crates/tui/src/tui/history.rs +++ b/crates/tui/src/tui/history.rs @@ -835,8 +835,13 @@ impl ExploringCell { } else { "running" }; - lines.push(render_tool_header_with_summary( - "Workspace", + // Search-only exploration cards read with the `find` verb so a + // completed grep renders `find done · Searching for …` instead of the + // incoherent `read done · Searching …` (#4145). Read/list or mixed + // cards keep the neutral `read` verb the Workspace card has always used. + let family = exploring_card_family(&self.entries); + lines.push(render_tool_header_with_family_and_summary( + family, header_summary.as_deref(), header_state, status, @@ -1710,6 +1715,24 @@ fn exploring_header_summary(entries: &[ExploringEntry]) -> Option { } } +/// Choose the verb family for an exploring card's header. A card whose entries +/// are all searches reads with the `find` verb so the completed action agrees +/// with its `Searching for …` labels (#4145); every other exploration mix keeps +/// the neutral `read` verb the Workspace card uses. The search signal is the +/// English label prefix produced by `exploring_label` in `tool_routing`. +fn exploring_card_family(entries: &[ExploringEntry]) -> crate::tui::widgets::tool_card::ToolFamily { + use crate::tui::widgets::tool_card::ToolFamily; + let all_search = !entries.is_empty() + && entries + .iter() + .all(|entry| entry.label.starts_with("Searching")); + if all_search { + ToolFamily::Find + } else { + ToolFamily::Read + } +} + fn render_compact_kv(label: &str, value: &str, style: Style, width: u16) -> Vec> { render_card_detail_line(Some(label.trim_end_matches(':')), value, style, width) } diff --git a/crates/tui/src/tui/history/tests.rs b/crates/tui/src/tui/history/tests.rs index 675b0a3ac..f88adaa83 100644 --- a/crates/tui/src/tui/history/tests.rs +++ b/crates/tui/src/tui/history/tests.rs @@ -1333,6 +1333,56 @@ fn generic_tool_cell_renders_rlm_with_rlm_label_not_swarm() { ); } +#[test] +fn exploring_card_search_reads_as_find_not_read() { + // #4145: a completed grep grouped under the exploration card must not + // render `read done · Searching …`; the header verb has to agree with the + // `Searching for …` label. + let cell = super::ExploringCell { + entries: vec![super::ExploringEntry { + label: "Searching for `TranscriptScroll`".to_string(), + status: ToolStatus::Success, + }], + }; + let header: String = cell.lines_with_motion(80, true)[0] + .spans + .iter() + .map(|s| s.content.as_ref()) + .collect::(); + assert!( + header.contains("find done"), + "search card header should read `find done`: {header:?}" + ); + assert!( + !header.contains("read done"), + "search card must not pair `read done` with a search label: {header:?}" + ); + assert!( + header.contains("Searching for `TranscriptScroll`"), + "search label should remain intact: {header:?}" + ); +} + +#[test] +fn exploring_card_read_keeps_read_verb() { + // The fix only re-verbs search-only cards — a plain read stays `read`. + let cell = super::ExploringCell { + entries: vec![super::ExploringEntry { + label: "Reading src/foo.rs".to_string(), + status: ToolStatus::Success, + }], + }; + let header: String = cell.lines_with_motion(80, true)[0] + .spans + .iter() + .map(|s| s.content.as_ref()) + .collect::(); + assert!( + header.contains("read done"), + "read card header should read `read done`: {header:?}" + ); +} + // === Reasoning treatment tests (v0.6.6 UI redesign) === #[test] diff --git a/crates/tui/src/tui/setup/mod.rs b/crates/tui/src/tui/setup/mod.rs index 234784468..b31409ec8 100644 --- a/crates/tui/src/tui/setup/mod.rs +++ b/crates/tui/src/tui/setup/mod.rs @@ -6137,7 +6137,7 @@ mod tests { assert!(text.contains("configured_slots=2")); assert!(text.contains("Bindable actions:")); assert!(text.contains("13 bindable actions")); - assert!(text.contains("Press H to customize slots; Enter records this Hotbar snapshot.")); + assert!(text.contains("Enter records this Hotbar snapshot. Press H to customize slots.")); } #[test] @@ -6271,7 +6271,7 @@ mod tests { assert!(text.contains("feishu")); assert!(text.contains("Remote mode:")); assert!(text.contains("--apply not implemented")); - assert!(text.contains("Press R to preview; Enter records this Remote snapshot.")); + assert!(text.contains("Enter records this Remote snapshot. Press R to preview.")); } #[test]