diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-21 01:55:59 +0300 |
|---|---|---|
| committer | Can Celik <ogulcancelik@gmail.com> | 2026-05-21 03:10:52 +0300 |
| commit | 7515695a1996e9b406a3cf1eef43208c6c248929 (patch) | |
| tree | c0fd480d20b17a0309fad7a1933aa64d043dafc3 /src | |
| parent | 6172275252f5f724d874163462f5394800f8d55b (diff) | |
fix: keep cjk ime agent allow-list safe
refs #149
Diffstat (limited to 'src')
| -rw-r--r-- | src/app/mod.rs | 3 | ||||
| -rw-r--r-- | src/app/state.rs | 4 | ||||
| -rw-r--r-- | src/config/model.rs | 3 | ||||
| -rw-r--r-- | src/main.rs | 1 | ||||
| -rw-r--r-- | src/server/headless.rs | 29 | ||||
| -rw-r--r-- | src/server/render_stream.rs | 4 |
6 files changed, 40 insertions, 4 deletions
diff --git a/src/app/mod.rs b/src/app/mod.rs index 5cc40ecc..4c4845a1 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -428,6 +428,7 @@ impl App { prompt_new_tab_name: config.ui.prompt_new_tab_name, show_agent_labels_on_pane_borders: config.ui.show_agent_labels_on_pane_borders, reveal_hidden_cursor_for_cjk_ime: config.experimental.reveal_hidden_cursor_for_cjk_ime, + cjk_ime_agent_filter_configured: !config.experimental.cjk_ime_agents.is_empty(), cjk_ime_agents: parse_cjk_ime_agents(&config.experimental.cjk_ime_agents), cjk_ime_cursor_shape: config.experimental.cjk_ime_cursor_shape.to_decscusr(), kitty_graphics_enabled: config.experimental.kitty_graphics, @@ -907,6 +908,8 @@ impl App { } self.state.reveal_hidden_cursor_for_cjk_ime = config.experimental.reveal_hidden_cursor_for_cjk_ime; + self.state.cjk_ime_agent_filter_configured = + !config.experimental.cjk_ime_agents.is_empty(); self.state.cjk_ime_agents = parse_cjk_ime_agents(&config.experimental.cjk_ime_agents); self.state.cjk_ime_cursor_shape = config.experimental.cjk_ime_cursor_shape.to_decscusr(); diff --git a/src/app/state.rs b/src/app/state.rs index 1566fdeb..6924dc65 100644 --- a/src/app/state.rs +++ b/src/app/state.rs @@ -969,7 +969,8 @@ pub struct AppState { /// the pane requested `?25l`. See `[experimental] reveal_hidden_cursor_for_cjk_ime`. pub reveal_hidden_cursor_for_cjk_ime: bool, /// Restrict cursor reveal to focused panes whose detected agent matches - /// one of these. Empty means apply to any focused pane. + /// one of these. When false, apply to any focused pane. + pub cjk_ime_agent_filter_configured: bool, pub cjk_ime_agents: Vec<crate::detect::Agent>, /// DECSCUSR shape parameter (1–6) for the IME anchor cursor. pub cjk_ime_cursor_shape: u8, @@ -1233,6 +1234,7 @@ impl AppState { prompt_new_tab_name: true, show_agent_labels_on_pane_borders: false, reveal_hidden_cursor_for_cjk_ime: false, + cjk_ime_agent_filter_configured: false, cjk_ime_agents: Vec::new(), cjk_ime_cursor_shape: 2, // steady_block kitty_graphics_enabled: false, diff --git a/src/config/model.rs b/src/config/model.rs index ba52d579..40d425a3 100644 --- a/src/config/model.rs +++ b/src/config/model.rs @@ -265,7 +265,8 @@ pub struct ExperimentalConfig { pub reveal_hidden_cursor_for_cjk_ime: bool, /// Restrict `reveal_hidden_cursor_for_cjk_ime` to focused panes whose /// detected agent matches one of these names (case-insensitive). Empty - /// list means apply to any focused pane. Unknown agent names are ignored. + /// list means apply to any focused pane. Unknown agent names are ignored; + /// if the list contains no valid names, the reveal does not apply. /// Accepted names: pi, claude, codex, gemini, cursor, cline, opencode, /// copilot, kimi, kiro, droid, amp, grok, hermes. Default: empty. pub cjk_ime_agents: Vec<String>, diff --git a/src/main.rs b/src/main.rs index 440a87a5..17052159 100644 --- a/src/main.rs +++ b/src/main.rs @@ -209,6 +209,7 @@ const DEFAULT_CONFIG: &str = r##"# herdr configuration # reveal_hidden_cursor_for_cjk_ime = false # Optional allow-list: only reveal for focused panes whose detected agent # matches one of these names. Empty means apply to any focused pane. +# If the list contains no valid names, the reveal does not apply. # Accepted: pi, claude, codex, gemini, cursor, cline, opencode, copilot, # kimi, kiro, droid, amp, grok, hermes. # cjk_ime_agents = [] diff --git a/src/server/headless.rs b/src/server/headless.rs index f56dbdeb..539892e0 100644 --- a/src/server/headless.rs +++ b/src/server/headless.rs @@ -2804,6 +2804,7 @@ mod tests { state.reveal_hidden_cursor_for_cjk_ime = true; // Filter only Claude, but the test pane has no detected agent, so the // reveal must not apply. + state.cjk_ime_agent_filter_configured = true; state.cjk_ime_agents = vec![crate::detect::Agent::Claude]; let mut ws = crate::workspace::Workspace::test_new("test"); let pane_id = ws.tabs[0].root_pane; @@ -2828,6 +2829,34 @@ mod tests { } #[tokio::test] + async fn virtual_render_skips_reveal_when_agent_filter_has_no_valid_entries() { + let mut state = AppState::test_new(); + state.reveal_hidden_cursor_for_cjk_ime = true; + state.cjk_ime_agent_filter_configured = true; + state.cjk_ime_agents = Vec::new(); + let mut ws = crate::workspace::Workspace::test_new("test"); + let pane_id = ws.tabs[0].root_pane; + ws.insert_test_runtime( + pane_id, + crate::terminal::TerminalRuntime::test_with_screen_bytes(20, 5, b"left\x1b[?25l"), + ); + + state.workspaces = vec![ws]; + state.active = Some(0); + state.selected = 0; + state.mode = crate::app::Mode::Terminal; + + let area = Rect::new(0, 0, 80, 24); + let (_buffer, cursor) = + crate::server::render_stream::render_virtual(&mut state, area, true); + + assert!( + cursor.as_ref().is_none_or(|cursor| !cursor.visible), + "agent filter with no valid entries should suppress reveal; got {cursor:?}", + ); + } + + #[tokio::test] async fn virtual_render_omits_focused_pane_cursor_while_mobile_switcher_open() { let mut state = AppState::test_new(); let mut ws = crate::workspace::Workspace::test_new("test"); diff --git a/src/server/render_stream.rs b/src/server/render_stream.rs index 6dd4ad48..b6e50444 100644 --- a/src/server/render_stream.rs +++ b/src/server/render_stream.rs @@ -327,9 +327,9 @@ fn focused_terminal_cursor(app_state: &AppState) -> Option<CursorState> { // Determine whether the IME-anchor reveal applies to this focused pane. // The master switch must be on, and either no agent filter is configured // (apply to any pane) or the focused pane's detected agent matches the - // allow-list. + // allow-list. A configured list with no valid entries reveals nothing. let reveal = app_state.reveal_hidden_cursor_for_cjk_ime - && (app_state.cjk_ime_agents.is_empty() || { + && (!app_state.cjk_ime_agent_filter_configured || { let detected = app_state .workspaces .get(ws_idx) |
