From dbdf726b8b253b4e66f418d1993fa58e3c633f37 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Tue, 9 Jun 2026 00:12:01 +0800 Subject: [PATCH 01/12] fix: reduce idle TUI CPU usage --- src/domain/resource.rs | 20 ++++++------ src/runner.rs | 72 +++++++++++++++++++++++++++++++++++------- 2 files changed, 72 insertions(+), 20 deletions(-) diff --git a/src/domain/resource.rs b/src/domain/resource.rs index ae21f684..b73e39d2 100644 --- a/src/domain/resource.rs +++ b/src/domain/resource.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, fmt}; +use std::{collections::HashMap, fmt, sync::LazyLock}; use regex::Regex; use serde::{Deserialize, Serialize}; @@ -6,6 +6,14 @@ use thiserror::Error; pub const FULL_DEPTH_WARNING_HINT: &str = "set --api-depth full or GZG_API_DEPTH=full"; +static GITHUB_RESOURCE_URL_RE: LazyLock = LazyLock::new(|| { + Regex::new(r"^https://github\.com/([^/\s]+)/([^/\s]+)/(pull|issues)/([0-9]+)(?:[/?#].*)?$") + .expect("valid GitHub URL regex") +}); +static OWNER_REPO_HASH_RE: LazyLock = LazyLock::new(|| { + Regex::new(r"^([^/\s#]+)/([^/\s#]+)#([0-9]+)$").expect("valid owner repo hash regex") +}); + #[derive(Debug, Error, PartialEq, Eq)] pub enum ResourceIdError { #[error("expected a GitHub PR/issue URL, owner/repo#number, or owner/repo number")] @@ -107,11 +115,7 @@ impl ResourceId { } fn parse_url(input: &str) -> Result, ResourceIdError> { - let re = Regex::new( - r"^https://github\.com/([^/\s]+)/([^/\s]+)/(pull|issues)/([0-9]+)(?:[/?#].*)?$", - ) - .expect("valid GitHub URL regex"); - let Some(caps) = re.captures(input) else { + let Some(caps) = GITHUB_RESOURCE_URL_RE.captures(input) else { return Ok(None); }; let kind_hint = match &caps[3] { @@ -128,9 +132,7 @@ impl ResourceId { } fn parse_owner_repo_hash(input: &str) -> Result, ResourceIdError> { - let re = - Regex::new(r"^([^/\s#]+)/([^/\s#]+)#([0-9]+)$").expect("valid owner repo hash regex"); - let Some(caps) = re.captures(input) else { + let Some(caps) = OWNER_REPO_HASH_RE.captures(input) else { return Ok(None); }; Ok(Some(Self { diff --git a/src/runner.rs b/src/runner.rs index 976b7f7e..b7ebaa66 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -655,19 +655,22 @@ async fn run_tui( last_refresh = Instant::now(); } } + let mut needs_redraw = true; loop { + let mut state_changed = false; if apply_completed_fetches(state, &mut fetch_rx) { + state_changed = true; if let Some(runtime) = &mut session_runtime { persist_session_now(state, runtime); } } - maybe_load_file_patches_for_active_files_tab( + state_changed |= maybe_load_file_patches_for_active_files_tab( state, fetch_source.clone(), &fetch_tx, &mut last_refresh, ); - handle_pending_control_requests( + state_changed |= handle_pending_control_requests( state, fetch_source.clone(), &fetch_tx, @@ -675,21 +678,27 @@ async fn run_tui( &mut session_runtime, &mut last_refresh, ); - state.advance_loading_frame(); - terminal.draw(|frame| render_app(frame, state))?; + if should_advance_loading_frame(state) { + state.advance_loading_frame(); + state_changed = true; + } + if needs_redraw || state_changed { + terminal.draw(|frame| render_app(frame, state))?; + needs_redraw = false; + } if state.should_quit { if let Some(runtime) = &mut session_runtime { persist_session_now(state, runtime); } return Ok(()); } - maybe_refresh_loading_active_resource( + needs_redraw |= maybe_refresh_loading_active_resource( state, fetch_source.clone(), &fetch_tx, &mut last_refresh, ); - maybe_auto_refresh( + needs_redraw |= maybe_auto_refresh( state, fetch_source.is_live_github(), refresh_interval, @@ -698,7 +707,11 @@ async fn run_tui( fetch_source.clone(), &fetch_tx, ); - for app_event in read_pending_app_events()? { + let app_events = read_pending_app_events()?; + if !app_events.is_empty() { + needs_redraw = true; + } + for app_event in app_events { let intent = apply_event(state, app_event); if handle_intent( state, @@ -714,13 +727,13 @@ async fn run_tui( } return Ok(()); } - maybe_refresh_loading_active_resource( + needs_redraw |= maybe_refresh_loading_active_resource( state, fetch_source.clone(), &fetch_tx, &mut last_refresh, ); - maybe_load_file_patches_for_active_files_tab( + needs_redraw |= maybe_load_file_patches_for_active_files_tab( state, fetch_source.clone(), &fetch_tx, @@ -736,6 +749,15 @@ async fn run_tui( } } +fn should_advance_loading_frame(state: &AppState) -> bool { + state.loading_message().is_some() + || state.file_patch_loading_message().is_some() + || state + .resource_tabs + .iter() + .any(|tab| is_loading_resource(&tab.resource)) +} + fn handle_pending_control_requests( state: &mut AppState, fetch_source: FetchSource, @@ -743,7 +765,8 @@ fn handle_pending_control_requests( control_rx: &mut UnboundedReceiver, session_runtime: &mut Option, last_refresh: &mut Instant, -) { +) -> bool { + let mut handled = false; while let Ok(request) = control_rx.try_recv() { let reply = handle_control_command( state, @@ -760,7 +783,9 @@ fn handle_pending_control_requests( persist_session_now(state, runtime); } } + handled = true; } + handled } fn handle_control_command( @@ -1609,7 +1634,8 @@ mod tests { maybe_load_file_patches_with_start, maybe_refresh_loading_active_resource, navigate_back, navigate_to_resource, parse_resource_args, prepare_restored_initial_fetch, resource_count_label, save_open_commands_to_session, session_state_persistable, - should_replace_empty_launch_tab, url_open_command, ClipboardPlatform, + should_advance_loading_frame, should_replace_empty_launch_tab, url_open_command, + ClipboardPlatform, }; struct FakeGateway { @@ -1902,6 +1928,30 @@ mod tests { ])); } + #[test] + fn idle_resource_does_not_advance_loading_frame() { + let state = AppState::new(issue_resource(1, "Idle issue")); + + assert!(!should_advance_loading_frame(&state)); + } + + #[test] + fn loading_resource_advances_loading_frame() { + let mut state = AppState::new(loading_resource_placeholder( + ResourceId::parse("owner/repo#1").unwrap(), + )); + + assert!(should_advance_loading_frame(&state)); + + state.replace_resource_reset_view(issue_resource(1, "Loaded issue")); + state.begin_loading( + ResourceId::parse("owner/repo#1").unwrap(), + "refreshing owner/repo#1 from GitHub", + ); + + assert!(should_advance_loading_frame(&state)); + } + #[test] fn session_resource_count_label_uses_singular_and_plural() { assert_eq!(resource_count_label(1), "1 resource"); From e91ea90d71894a3aeb17ace74275b43049a6c7a2 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Tue, 9 Jun 2026 00:22:34 +0800 Subject: [PATCH 02/12] fix: redraw on terminal resize --- src/runner.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/runner.rs b/src/runner.rs index b7ebaa66..36a7d667 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -1017,6 +1017,7 @@ fn event_to_app_event(event: Event) -> Option { match event { Event::Key(key) => Some(AppEvent::Key(key)), Event::Mouse(mouse) => Some(AppEvent::Mouse(mouse)), + Event::Resize(_, _) => Some(AppEvent::Tick), _ => None, } } From d0c683f4b7d286cfc14d7ca1f933c7fd8f821a21 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Tue, 9 Jun 2026 00:30:47 +0800 Subject: [PATCH 03/12] fix: preserve scrollbar fade redraws --- src/runner.rs | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/runner.rs b/src/runner.rs index 36a7d667..6d364ddc 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -682,9 +682,13 @@ async fn run_tui( state.advance_loading_frame(); state_changed = true; } + let scrollbar_was_fading = should_advance_scrollbar_fade(state); + if scrollbar_was_fading { + state_changed = true; + } if needs_redraw || state_changed { terminal.draw(|frame| render_app(frame, state))?; - needs_redraw = false; + needs_redraw = should_finish_scrollbar_fade_redraw(scrollbar_was_fading, state); } if state.should_quit { if let Some(runtime) = &mut session_runtime { @@ -758,6 +762,14 @@ fn should_advance_loading_frame(state: &AppState) -> bool { .any(|tab| is_loading_resource(&tab.resource)) } +fn should_advance_scrollbar_fade(state: &AppState) -> bool { + state.scrollbar_visible_frames > 0 +} + +fn should_finish_scrollbar_fade_redraw(scrollbar_was_fading: bool, state: &AppState) -> bool { + scrollbar_was_fading && !should_advance_scrollbar_fade(state) +} + fn handle_pending_control_requests( state: &mut AppState, fetch_source: FetchSource, @@ -1635,7 +1647,8 @@ mod tests { maybe_load_file_patches_with_start, maybe_refresh_loading_active_resource, navigate_back, navigate_to_resource, parse_resource_args, prepare_restored_initial_fetch, resource_count_label, save_open_commands_to_session, session_state_persistable, - should_advance_loading_frame, should_replace_empty_launch_tab, url_open_command, + should_advance_loading_frame, should_advance_scrollbar_fade, + should_finish_scrollbar_fade_redraw, should_replace_empty_launch_tab, url_open_command, ClipboardPlatform, }; @@ -1953,6 +1966,26 @@ mod tests { assert!(should_advance_loading_frame(&state)); } + #[test] + fn scrollbar_fade_keeps_redrawing_until_hidden() { + let mut state = AppState::new(issue_resource(1, "Scrollable issue")); + + assert!(!should_advance_scrollbar_fade(&state)); + + state.set_scroll_limit(10); + state.scroll_down(1); + + assert!(should_advance_scrollbar_fade(&state)); + + while should_advance_scrollbar_fade(&state) { + state.advance_scrollbar_visibility(); + } + + assert!(!should_advance_scrollbar_fade(&state)); + assert!(should_finish_scrollbar_fade_redraw(true, &state)); + assert!(!should_finish_scrollbar_fade_redraw(false, &state)); + } + #[test] fn session_resource_count_label_uses_singular_and_plural() { assert_eq!(resource_count_label(1), "1 resource"); From c3f0ff6e0098c50807c22ee6192478a3c60a268b Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Tue, 9 Jun 2026 00:38:43 +0800 Subject: [PATCH 04/12] fix: redraw session save errors --- src/runner.rs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/runner.rs b/src/runner.rs index 6d364ddc..0a5783b8 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -748,7 +748,7 @@ async fn run_tui( } } if let Some(runtime) = &mut session_runtime { - persist_session_when_due(state, runtime, Instant::now()); + needs_redraw |= persist_session_when_due(state, runtime, Instant::now()); } } } @@ -900,9 +900,9 @@ fn apply_control_setting(state: &mut AppState, key: &str, value: &str) -> Result } } -fn persist_session_now(state: &mut AppState, runtime: &mut SessionRuntime) { +fn persist_session_now(state: &mut AppState, runtime: &mut SessionRuntime) -> bool { if !session_state_persistable(state) { - return; + return false; } match session::save_session( &runtime.handle, @@ -915,23 +915,32 @@ fn persist_session_now(state: &mut AppState, runtime: &mut SessionRuntime) { runtime.snapshot = Some(snapshot); runtime.dirty = false; runtime.dirty_since = None; + false } Err(error) => { - state.last_error = Some(format!("failed to save ghzinga session: {error}")); + let message = format!("failed to save ghzinga session: {error}"); + let changed = state.last_error.as_deref() != Some(message.as_str()); + state.last_error = Some(message); + changed } } } -fn persist_session_when_due(state: &mut AppState, runtime: &mut SessionRuntime, now: Instant) { +fn persist_session_when_due( + state: &mut AppState, + runtime: &mut SessionRuntime, + now: Instant, +) -> bool { if !runtime.dirty { - return; + return false; } let Some(dirty_since) = runtime.dirty_since else { - return; + return false; }; if now.duration_since(dirty_since) >= SESSION_SAVE_DEBOUNCE { - persist_session_now(state, runtime); + return persist_session_now(state, runtime); } + false } fn session_state_persistable(state: &AppState) -> bool { From b384a280558c810f033bada02015ab8c90960603 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Tue, 9 Jun 2026 00:46:52 +0800 Subject: [PATCH 05/12] fix: continue post-render scrollbar fade --- src/runner.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/runner.rs b/src/runner.rs index 0a5783b8..98031319 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -688,7 +688,7 @@ async fn run_tui( } if needs_redraw || state_changed { terminal.draw(|frame| render_app(frame, state))?; - needs_redraw = should_finish_scrollbar_fade_redraw(scrollbar_was_fading, state); + needs_redraw = should_redraw_after_scrollbar_frame(scrollbar_was_fading, state); } if state.should_quit { if let Some(runtime) = &mut session_runtime { @@ -766,8 +766,8 @@ fn should_advance_scrollbar_fade(state: &AppState) -> bool { state.scrollbar_visible_frames > 0 } -fn should_finish_scrollbar_fade_redraw(scrollbar_was_fading: bool, state: &AppState) -> bool { - scrollbar_was_fading && !should_advance_scrollbar_fade(state) +fn should_redraw_after_scrollbar_frame(scrollbar_was_fading: bool, state: &AppState) -> bool { + scrollbar_was_fading || should_advance_scrollbar_fade(state) } fn handle_pending_control_requests( @@ -1657,7 +1657,7 @@ mod tests { navigate_to_resource, parse_resource_args, prepare_restored_initial_fetch, resource_count_label, save_open_commands_to_session, session_state_persistable, should_advance_loading_frame, should_advance_scrollbar_fade, - should_finish_scrollbar_fade_redraw, should_replace_empty_launch_tab, url_open_command, + should_redraw_after_scrollbar_frame, should_replace_empty_launch_tab, url_open_command, ClipboardPlatform, }; @@ -1985,14 +1985,15 @@ mod tests { state.scroll_down(1); assert!(should_advance_scrollbar_fade(&state)); + assert!(should_redraw_after_scrollbar_frame(false, &state)); while should_advance_scrollbar_fade(&state) { state.advance_scrollbar_visibility(); } assert!(!should_advance_scrollbar_fade(&state)); - assert!(should_finish_scrollbar_fade_redraw(true, &state)); - assert!(!should_finish_scrollbar_fade_redraw(false, &state)); + assert!(should_redraw_after_scrollbar_frame(true, &state)); + assert!(!should_redraw_after_scrollbar_frame(false, &state)); } #[test] From f4f2870d83dc3840eec4fb56df0495eb2f71041c Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Tue, 9 Jun 2026 00:55:55 +0800 Subject: [PATCH 06/12] fix: keep failed loading placeholders idle --- src/runner.rs | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/runner.rs b/src/runner.rs index 98031319..46c57037 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -754,12 +754,24 @@ async fn run_tui( } fn should_advance_loading_frame(state: &AppState) -> bool { - state.loading_message().is_some() - || state.file_patch_loading_message().is_some() - || state - .resource_tabs - .iter() - .any(|tab| is_loading_resource(&tab.resource)) + if state.loading_message().is_some() || state.file_patch_loading_message().is_some() { + return true; + } + if is_animating_loading_resource(&state.resource, state.last_error.as_deref()) { + return true; + } + let active_tab_id = state.active_resource_tab_id(); + state.resource_tabs.iter().any(|tab| { + tab.id != active_tab_id + && is_animating_loading_resource(&tab.resource, tab.last_error.as_deref()) + }) +} + +fn is_animating_loading_resource( + resource: &crate::domain::Resource, + last_error: Option<&str>, +) -> bool { + last_error.is_none() && is_loading_resource(resource) } fn should_advance_scrollbar_fade(state: &AppState) -> bool { @@ -1975,6 +1987,16 @@ mod tests { assert!(should_advance_loading_frame(&state)); } + #[test] + fn failed_loading_resource_does_not_advance_loading_frame() { + let mut state = AppState::new(loading_resource_placeholder( + ResourceId::parse("owner/repo#1").unwrap(), + )); + state.last_error = Some("network down".into()); + + assert!(!should_advance_loading_frame(&state)); + } + #[test] fn scrollbar_fade_keeps_redrawing_until_hidden() { let mut state = AppState::new(issue_resource(1, "Scrollable issue")); From c4870441a5935704eb8d18e36af8d9260b5b4a3c Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Tue, 9 Jun 2026 01:03:56 +0800 Subject: [PATCH 07/12] fix: redraw before resize-batched input --- src/runner.rs | 81 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 64 insertions(+), 17 deletions(-) diff --git a/src/runner.rs b/src/runner.rs index 46c57037..3c4f27fc 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -712,10 +712,15 @@ async fn run_tui( &fetch_tx, ); let app_events = read_pending_app_events()?; - if !app_events.is_empty() { + if app_events.requires_pre_event_redraw { + let scrollbar_was_fading = should_advance_scrollbar_fade(state); + terminal.draw(|frame| render_app(frame, state))?; + needs_redraw = should_redraw_after_scrollbar_frame(scrollbar_was_fading, state); + } + if !app_events.events.is_empty() { needs_redraw = true; } - for app_event in app_events { + for app_event in app_events.events { let intent = apply_event(state, app_event); if handle_intent( state, @@ -1033,9 +1038,22 @@ enum OpenResourceMode { ReplaceCurrent, } -fn read_pending_app_events() -> anyhow::Result> { +struct PendingAppEvents { + events: Vec, + requires_pre_event_redraw: bool, +} + +struct PendingAppEvent { + event: AppEvent, + requires_pre_event_redraw: bool, +} + +fn read_pending_app_events() -> anyhow::Result { if !event::poll(EVENT_POLL_TIMEOUT)? { - return Ok(Vec::new()); + return Ok(PendingAppEvents { + events: Vec::new(), + requires_pre_event_redraw: false, + }); } let mut events = Vec::with_capacity(MAX_PENDING_EVENTS_PER_FRAME); @@ -1043,14 +1061,35 @@ fn read_pending_app_events() -> anyhow::Result> { while events.len() < MAX_PENDING_EVENTS_PER_FRAME && event::poll(Duration::ZERO)? { events.push(event_to_app_event(event::read()?)); } - Ok(events.into_iter().flatten().collect()) + let mut requires_pre_event_redraw = false; + let events = events + .into_iter() + .flatten() + .map(|pending| { + requires_pre_event_redraw |= pending.requires_pre_event_redraw; + pending.event + }) + .collect(); + Ok(PendingAppEvents { + events, + requires_pre_event_redraw, + }) } -fn event_to_app_event(event: Event) -> Option { +fn event_to_app_event(event: Event) -> Option { match event { - Event::Key(key) => Some(AppEvent::Key(key)), - Event::Mouse(mouse) => Some(AppEvent::Mouse(mouse)), - Event::Resize(_, _) => Some(AppEvent::Tick), + Event::Key(key) => Some(PendingAppEvent { + event: AppEvent::Key(key), + requires_pre_event_redraw: false, + }), + Event::Mouse(mouse) => Some(PendingAppEvent { + event: AppEvent::Mouse(mouse), + requires_pre_event_redraw: false, + }), + Event::Resize(_, _) => Some(PendingAppEvent { + event: AppEvent::Tick, + requires_pre_event_redraw: true, + }), _ => None, } } @@ -1645,7 +1684,7 @@ mod tests { }; use crate::{ - app::{loading_resource_placeholder, AppIntent, AppState, Tab}, + app::{loading_resource_placeholder, AppEvent, AppIntent, AppState, Tab}, domain::{ Resource, ResourceId, ResourceKind, FILE_PATCH_CONTEXT_UNAVAILABLE_WARNING, FULL_DEPTH_WARNING_HINT, @@ -1664,13 +1703,13 @@ mod tests { use super::{ apply_control_setting, auto_refresh_due, clipboard_command, empty_launch_resource, - handle_control_open, handle_intent, has_command_help_arg, maybe_auto_refresh_with_start, - maybe_load_file_patches_with_start, maybe_refresh_loading_active_resource, navigate_back, - navigate_to_resource, parse_resource_args, prepare_restored_initial_fetch, - resource_count_label, save_open_commands_to_session, session_state_persistable, - should_advance_loading_frame, should_advance_scrollbar_fade, - should_redraw_after_scrollbar_frame, should_replace_empty_launch_tab, url_open_command, - ClipboardPlatform, + event_to_app_event, handle_control_open, handle_intent, has_command_help_arg, + maybe_auto_refresh_with_start, maybe_load_file_patches_with_start, + maybe_refresh_loading_active_resource, navigate_back, navigate_to_resource, + parse_resource_args, prepare_restored_initial_fetch, resource_count_label, + save_open_commands_to_session, session_state_persistable, should_advance_loading_frame, + should_advance_scrollbar_fade, should_redraw_after_scrollbar_frame, + should_replace_empty_launch_tab, url_open_command, ClipboardPlatform, }; struct FakeGateway { @@ -2018,6 +2057,14 @@ mod tests { assert!(!should_redraw_after_scrollbar_frame(false, &state)); } + #[test] + fn resize_event_requires_pre_event_redraw() { + let pending = event_to_app_event(crossterm::event::Event::Resize(120, 40)).unwrap(); + + assert!(matches!(pending.event, AppEvent::Tick)); + assert!(pending.requires_pre_event_redraw); + } + #[test] fn session_resource_count_label_uses_singular_and_plural() { assert_eq!(resource_count_label(1), "1 resource"); From 7fe0dae8dd09ebc45fcea0504db5d2e764e99a80 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Tue, 9 Jun 2026 11:05:19 +0800 Subject: [PATCH 08/12] test: refresh capture manifests --- captures/ghzinga-issue-88499/large/manifest.json | 4 ++-- captures/ghzinga-issue-88499/manifest.json | 4 ++-- captures/ghzinga-issue-88499/medium/manifest.json | 4 ++-- captures/ghzinga-issue-88499/mouse-smoke/manifest.json | 4 ++-- captures/ghzinga-issue-88499/narrow/manifest.json | 4 ++-- captures/ghzinga-pr-81834/large/manifest.json | 4 ++-- captures/ghzinga-pr-81834/manifest.json | 4 ++-- captures/ghzinga-pr-81834/medium/manifest.json | 4 ++-- captures/ghzinga-pr-81834/mouse-smoke/manifest.json | 4 ++-- captures/ghzinga-pr-81834/narrow/manifest.json | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/captures/ghzinga-issue-88499/large/manifest.json b/captures/ghzinga-issue-88499/large/manifest.json index 3faec0b6..98aa4258 100644 --- a/captures/ghzinga-issue-88499/large/manifest.json +++ b/captures/ghzinga-issue-88499/large/manifest.json @@ -4,7 +4,7 @@ "title": "openai-responses provider: 404 on previous_response_id when store=false (default)", "mode": "issue", "binary": "target/debug/gzg", - "git_commit": "1f9c11df009706d744e6c810d50bb886d8bebc9a", + "git_commit": "c4870441a5935704eb8d18e36af8d9260b5b4a3c", "config_path": "captures/ghzinga-issue-88499/capture-empty-config.toml", "requested_columns": 160, "requested_rows": 50, @@ -113,5 +113,5 @@ } ], "actual_tmux_size": "160x50", - "app_tree_hash": "0bb55716541e219b88eda9e1305244258debfd2a90d5e6d65b0958aafbe4840d" + "app_tree_hash": "76068ac0572ab7b3776e5985f52d1351f03d3094c1b405b9585a97292b50fdcf" } diff --git a/captures/ghzinga-issue-88499/manifest.json b/captures/ghzinga-issue-88499/manifest.json index 05817bd5..5c6ec28f 100644 --- a/captures/ghzinga-issue-88499/manifest.json +++ b/captures/ghzinga-issue-88499/manifest.json @@ -3,7 +3,7 @@ "title": "openai-responses provider: 404 on previous_response_id when store=false (default)", "mode": "issue", "binary": "target/debug/gzg", - "git_commit": "1f9c11df009706d744e6c810d50bb886d8bebc9a", + "git_commit": "c4870441a5935704eb8d18e36af8d9260b5b4a3c", "config_path": "captures/ghzinga-issue-88499/capture-empty-config.toml", "offline_fixture": "fixtures/issue-88499.json", "offline_resource_fixtures": [], @@ -24,5 +24,5 @@ "rows": 50 } ], - "app_tree_hash": "0bb55716541e219b88eda9e1305244258debfd2a90d5e6d65b0958aafbe4840d" + "app_tree_hash": "76068ac0572ab7b3776e5985f52d1351f03d3094c1b405b9585a97292b50fdcf" } diff --git a/captures/ghzinga-issue-88499/medium/manifest.json b/captures/ghzinga-issue-88499/medium/manifest.json index 878528ff..29b4cc7d 100644 --- a/captures/ghzinga-issue-88499/medium/manifest.json +++ b/captures/ghzinga-issue-88499/medium/manifest.json @@ -4,7 +4,7 @@ "title": "openai-responses provider: 404 on previous_response_id when store=false (default)", "mode": "issue", "binary": "target/debug/gzg", - "git_commit": "1f9c11df009706d744e6c810d50bb886d8bebc9a", + "git_commit": "c4870441a5935704eb8d18e36af8d9260b5b4a3c", "config_path": "captures/ghzinga-issue-88499/capture-empty-config.toml", "requested_columns": 120, "requested_rows": 36, @@ -113,5 +113,5 @@ } ], "actual_tmux_size": "120x36", - "app_tree_hash": "0bb55716541e219b88eda9e1305244258debfd2a90d5e6d65b0958aafbe4840d" + "app_tree_hash": "76068ac0572ab7b3776e5985f52d1351f03d3094c1b405b9585a97292b50fdcf" } diff --git a/captures/ghzinga-issue-88499/mouse-smoke/manifest.json b/captures/ghzinga-issue-88499/mouse-smoke/manifest.json index d499d094..c72a4442 100644 --- a/captures/ghzinga-issue-88499/mouse-smoke/manifest.json +++ b/captures/ghzinga-issue-88499/mouse-smoke/manifest.json @@ -5,7 +5,7 @@ "fixtures/issue-66943.json" ], "binary": "target/debug/gzg", - "git_commit": "1f9c11df009706d744e6c810d50bb886d8bebc9a", + "git_commit": "c4870441a5935704eb8d18e36af8d9260b5b4a3c", "config_path": "captures/ghzinga-issue-88499/mouse-smoke/capture-empty-config.toml", "command": "cd . && TERM=xterm-256color GZG_CONFIG_PATH=./captures/ghzinga-issue-88499/mouse-smoke/capture-empty-config.toml GZG_STATE_HOME=./captures/ghzinga-issue-88499/mouse-smoke/.capture-state GZG_CACHE_HOME=./captures/ghzinga-issue-88499/mouse-smoke/.capture-cache BROWSER=./captures/ghzinga-issue-88499/mouse-smoke/capture-open-url.sh GZG_COPY_COMMAND=./captures/ghzinga-issue-88499/mouse-smoke/capture-copy-url.sh ./target/debug/gzg https://github.com/openclaw/openclaw/issues/88499 --offline-fixture ./captures/ghzinga-issue-88499/mouse-smoke/navigation-fixture.json --offline-resource-fixture ./fixtures/issue-66943.json --no-restore --refresh-seconds 0", "actual_tmux_size": "120x36", @@ -120,5 +120,5 @@ "ansi": "70_mouse_quit_confirm.ansi" } ], - "app_tree_hash": "0bb55716541e219b88eda9e1305244258debfd2a90d5e6d65b0958aafbe4840d" + "app_tree_hash": "76068ac0572ab7b3776e5985f52d1351f03d3094c1b405b9585a97292b50fdcf" } diff --git a/captures/ghzinga-issue-88499/narrow/manifest.json b/captures/ghzinga-issue-88499/narrow/manifest.json index dc524792..6f233d86 100644 --- a/captures/ghzinga-issue-88499/narrow/manifest.json +++ b/captures/ghzinga-issue-88499/narrow/manifest.json @@ -4,7 +4,7 @@ "title": "openai-responses provider: 404 on previous_response_id when store=false (default)", "mode": "issue", "binary": "target/debug/gzg", - "git_commit": "1f9c11df009706d744e6c810d50bb886d8bebc9a", + "git_commit": "c4870441a5935704eb8d18e36af8d9260b5b4a3c", "config_path": "captures/ghzinga-issue-88499/capture-empty-config.toml", "requested_columns": 80, "requested_rows": 24, @@ -113,5 +113,5 @@ } ], "actual_tmux_size": "80x24", - "app_tree_hash": "0bb55716541e219b88eda9e1305244258debfd2a90d5e6d65b0958aafbe4840d" + "app_tree_hash": "76068ac0572ab7b3776e5985f52d1351f03d3094c1b405b9585a97292b50fdcf" } diff --git a/captures/ghzinga-pr-81834/large/manifest.json b/captures/ghzinga-pr-81834/large/manifest.json index e4f305cf..61717b54 100644 --- a/captures/ghzinga-pr-81834/large/manifest.json +++ b/captures/ghzinga-pr-81834/large/manifest.json @@ -4,7 +4,7 @@ "title": "feat(senseaudio): add SenseAudio TTS provider", "mode": "pr", "binary": "target/debug/gzg", - "git_commit": "1f9c11df009706d744e6c810d50bb886d8bebc9a", + "git_commit": "c4870441a5935704eb8d18e36af8d9260b5b4a3c", "config_path": "captures/ghzinga-pr-81834/capture-empty-config.toml", "requested_columns": 160, "requested_rows": 50, @@ -183,5 +183,5 @@ } ], "actual_tmux_size": "160x50", - "app_tree_hash": "0bb55716541e219b88eda9e1305244258debfd2a90d5e6d65b0958aafbe4840d" + "app_tree_hash": "76068ac0572ab7b3776e5985f52d1351f03d3094c1b405b9585a97292b50fdcf" } diff --git a/captures/ghzinga-pr-81834/manifest.json b/captures/ghzinga-pr-81834/manifest.json index 51649a2c..d40329ae 100644 --- a/captures/ghzinga-pr-81834/manifest.json +++ b/captures/ghzinga-pr-81834/manifest.json @@ -3,7 +3,7 @@ "title": "feat(senseaudio): add SenseAudio TTS provider", "mode": "pr", "binary": "target/debug/gzg", - "git_commit": "1f9c11df009706d744e6c810d50bb886d8bebc9a", + "git_commit": "c4870441a5935704eb8d18e36af8d9260b5b4a3c", "config_path": "captures/ghzinga-pr-81834/capture-empty-config.toml", "offline_fixture": null, "offline_resource_fixtures": [], @@ -24,5 +24,5 @@ "rows": 50 } ], - "app_tree_hash": "0bb55716541e219b88eda9e1305244258debfd2a90d5e6d65b0958aafbe4840d" + "app_tree_hash": "76068ac0572ab7b3776e5985f52d1351f03d3094c1b405b9585a97292b50fdcf" } diff --git a/captures/ghzinga-pr-81834/medium/manifest.json b/captures/ghzinga-pr-81834/medium/manifest.json index 75b56a08..19e29935 100644 --- a/captures/ghzinga-pr-81834/medium/manifest.json +++ b/captures/ghzinga-pr-81834/medium/manifest.json @@ -4,7 +4,7 @@ "title": "feat(senseaudio): add SenseAudio TTS provider", "mode": "pr", "binary": "target/debug/gzg", - "git_commit": "1f9c11df009706d744e6c810d50bb886d8bebc9a", + "git_commit": "c4870441a5935704eb8d18e36af8d9260b5b4a3c", "config_path": "captures/ghzinga-pr-81834/capture-empty-config.toml", "requested_columns": 120, "requested_rows": 36, @@ -183,5 +183,5 @@ } ], "actual_tmux_size": "120x36", - "app_tree_hash": "0bb55716541e219b88eda9e1305244258debfd2a90d5e6d65b0958aafbe4840d" + "app_tree_hash": "76068ac0572ab7b3776e5985f52d1351f03d3094c1b405b9585a97292b50fdcf" } diff --git a/captures/ghzinga-pr-81834/mouse-smoke/manifest.json b/captures/ghzinga-pr-81834/mouse-smoke/manifest.json index 3b40d5f6..f4fce845 100644 --- a/captures/ghzinga-pr-81834/mouse-smoke/manifest.json +++ b/captures/ghzinga-pr-81834/mouse-smoke/manifest.json @@ -5,7 +5,7 @@ "fixtures/issue-66943.json" ], "binary": "target/debug/gzg", - "git_commit": "1f9c11df009706d744e6c810d50bb886d8bebc9a", + "git_commit": "c4870441a5935704eb8d18e36af8d9260b5b4a3c", "config_path": "captures/ghzinga-pr-81834/mouse-smoke/capture-empty-config.toml", "command": "cd . && TERM=xterm-256color GZG_CONFIG_PATH=./captures/ghzinga-pr-81834/mouse-smoke/capture-empty-config.toml GZG_STATE_HOME=./captures/ghzinga-pr-81834/mouse-smoke/.capture-state GZG_CACHE_HOME=./captures/ghzinga-pr-81834/mouse-smoke/.capture-cache BROWSER=./captures/ghzinga-pr-81834/mouse-smoke/capture-open-url.sh GZG_COPY_COMMAND=./captures/ghzinga-pr-81834/mouse-smoke/capture-copy-url.sh ./target/debug/gzg 'openclaw/openclaw#81834' --offline-fixture ./captures/ghzinga-pr-81834/mouse-smoke/navigation-fixture.json --offline-resource-fixture ./fixtures/issue-66943.json --no-restore --refresh-seconds 0", "actual_tmux_size": "120x36", @@ -240,5 +240,5 @@ ], "load_full_fixture": "captures/ghzinga-pr-81834/mouse-smoke/load-full-fixture.json", "load_full_command": "cd . && TERM=xterm-256color GZG_CONFIG_PATH=./captures/ghzinga-pr-81834/mouse-smoke/capture-empty-config.toml GZG_STATE_HOME=./captures/ghzinga-pr-81834/mouse-smoke/.capture-state GZG_CACHE_HOME=./captures/ghzinga-pr-81834/mouse-smoke/.capture-cache ./target/debug/gzg 'openclaw/openclaw#81834' --offline-fixture ./captures/ghzinga-pr-81834/mouse-smoke/load-full-fixture.json --no-restore --refresh-seconds 0", - "app_tree_hash": "0bb55716541e219b88eda9e1305244258debfd2a90d5e6d65b0958aafbe4840d" + "app_tree_hash": "76068ac0572ab7b3776e5985f52d1351f03d3094c1b405b9585a97292b50fdcf" } diff --git a/captures/ghzinga-pr-81834/narrow/manifest.json b/captures/ghzinga-pr-81834/narrow/manifest.json index abc8cbf2..136c062e 100644 --- a/captures/ghzinga-pr-81834/narrow/manifest.json +++ b/captures/ghzinga-pr-81834/narrow/manifest.json @@ -4,7 +4,7 @@ "title": "feat(senseaudio): add SenseAudio TTS provider", "mode": "pr", "binary": "target/debug/gzg", - "git_commit": "1f9c11df009706d744e6c810d50bb886d8bebc9a", + "git_commit": "c4870441a5935704eb8d18e36af8d9260b5b4a3c", "config_path": "captures/ghzinga-pr-81834/capture-empty-config.toml", "requested_columns": 80, "requested_rows": 24, @@ -183,5 +183,5 @@ } ], "actual_tmux_size": "80x24", - "app_tree_hash": "0bb55716541e219b88eda9e1305244258debfd2a90d5e6d65b0958aafbe4840d" + "app_tree_hash": "76068ac0572ab7b3776e5985f52d1351f03d3094c1b405b9585a97292b50fdcf" } From c5fac92f3fd1c1dd1362104a6a1456d6965ecd58 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Tue, 9 Jun 2026 11:15:01 +0800 Subject: [PATCH 09/12] fix: preserve batched input order on resize --- src/runner.rs | 37 +++++++++++-------------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/src/runner.rs b/src/runner.rs index 3c4f27fc..7c1c8a74 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -712,16 +712,16 @@ async fn run_tui( &fetch_tx, ); let app_events = read_pending_app_events()?; - if app_events.requires_pre_event_redraw { - let scrollbar_was_fading = should_advance_scrollbar_fade(state); - terminal.draw(|frame| render_app(frame, state))?; - needs_redraw = should_redraw_after_scrollbar_frame(scrollbar_was_fading, state); - } if !app_events.events.is_empty() { needs_redraw = true; } - for app_event in app_events.events { - let intent = apply_event(state, app_event); + for pending_event in app_events.events { + if pending_event.requires_pre_event_redraw { + let scrollbar_was_fading = should_advance_scrollbar_fade(state); + terminal.draw(|frame| render_app(frame, state))?; + needs_redraw = should_redraw_after_scrollbar_frame(scrollbar_was_fading, state); + } + let intent = apply_event(state, pending_event.event); if handle_intent( state, intent, @@ -1039,8 +1039,7 @@ enum OpenResourceMode { } struct PendingAppEvents { - events: Vec, - requires_pre_event_redraw: bool, + events: Vec, } struct PendingAppEvent { @@ -1050,10 +1049,7 @@ struct PendingAppEvent { fn read_pending_app_events() -> anyhow::Result { if !event::poll(EVENT_POLL_TIMEOUT)? { - return Ok(PendingAppEvents { - events: Vec::new(), - requires_pre_event_redraw: false, - }); + return Ok(PendingAppEvents { events: Vec::new() }); } let mut events = Vec::with_capacity(MAX_PENDING_EVENTS_PER_FRAME); @@ -1061,19 +1057,8 @@ fn read_pending_app_events() -> anyhow::Result { while events.len() < MAX_PENDING_EVENTS_PER_FRAME && event::poll(Duration::ZERO)? { events.push(event_to_app_event(event::read()?)); } - let mut requires_pre_event_redraw = false; - let events = events - .into_iter() - .flatten() - .map(|pending| { - requires_pre_event_redraw |= pending.requires_pre_event_redraw; - pending.event - }) - .collect(); - Ok(PendingAppEvents { - events, - requires_pre_event_redraw, - }) + let events = events.into_iter().flatten().collect(); + Ok(PendingAppEvents { events }) } fn event_to_app_event(event: Event) -> Option { From 442461cb64b71e4e86ff671069b2da05fec472c6 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Tue, 9 Jun 2026 11:15:25 +0800 Subject: [PATCH 10/12] test: refresh capture manifests --- captures/ghzinga-issue-88499/large/manifest.json | 4 ++-- captures/ghzinga-issue-88499/manifest.json | 4 ++-- captures/ghzinga-issue-88499/medium/manifest.json | 4 ++-- captures/ghzinga-issue-88499/mouse-smoke/manifest.json | 4 ++-- captures/ghzinga-issue-88499/narrow/manifest.json | 4 ++-- captures/ghzinga-pr-81834/large/manifest.json | 4 ++-- captures/ghzinga-pr-81834/manifest.json | 4 ++-- captures/ghzinga-pr-81834/medium/manifest.json | 4 ++-- captures/ghzinga-pr-81834/mouse-smoke/manifest.json | 4 ++-- captures/ghzinga-pr-81834/narrow/manifest.json | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/captures/ghzinga-issue-88499/large/manifest.json b/captures/ghzinga-issue-88499/large/manifest.json index 98aa4258..15774ebe 100644 --- a/captures/ghzinga-issue-88499/large/manifest.json +++ b/captures/ghzinga-issue-88499/large/manifest.json @@ -4,7 +4,7 @@ "title": "openai-responses provider: 404 on previous_response_id when store=false (default)", "mode": "issue", "binary": "target/debug/gzg", - "git_commit": "c4870441a5935704eb8d18e36af8d9260b5b4a3c", + "git_commit": "c5fac92f3fd1c1dd1362104a6a1456d6965ecd58", "config_path": "captures/ghzinga-issue-88499/capture-empty-config.toml", "requested_columns": 160, "requested_rows": 50, @@ -113,5 +113,5 @@ } ], "actual_tmux_size": "160x50", - "app_tree_hash": "76068ac0572ab7b3776e5985f52d1351f03d3094c1b405b9585a97292b50fdcf" + "app_tree_hash": "56443dcfca40155fe6be910b62047b818a91ea9eed8e51f26992480f647d18a5" } diff --git a/captures/ghzinga-issue-88499/manifest.json b/captures/ghzinga-issue-88499/manifest.json index 5c6ec28f..3abe0edf 100644 --- a/captures/ghzinga-issue-88499/manifest.json +++ b/captures/ghzinga-issue-88499/manifest.json @@ -3,7 +3,7 @@ "title": "openai-responses provider: 404 on previous_response_id when store=false (default)", "mode": "issue", "binary": "target/debug/gzg", - "git_commit": "c4870441a5935704eb8d18e36af8d9260b5b4a3c", + "git_commit": "c5fac92f3fd1c1dd1362104a6a1456d6965ecd58", "config_path": "captures/ghzinga-issue-88499/capture-empty-config.toml", "offline_fixture": "fixtures/issue-88499.json", "offline_resource_fixtures": [], @@ -24,5 +24,5 @@ "rows": 50 } ], - "app_tree_hash": "76068ac0572ab7b3776e5985f52d1351f03d3094c1b405b9585a97292b50fdcf" + "app_tree_hash": "56443dcfca40155fe6be910b62047b818a91ea9eed8e51f26992480f647d18a5" } diff --git a/captures/ghzinga-issue-88499/medium/manifest.json b/captures/ghzinga-issue-88499/medium/manifest.json index 29b4cc7d..2d9494b4 100644 --- a/captures/ghzinga-issue-88499/medium/manifest.json +++ b/captures/ghzinga-issue-88499/medium/manifest.json @@ -4,7 +4,7 @@ "title": "openai-responses provider: 404 on previous_response_id when store=false (default)", "mode": "issue", "binary": "target/debug/gzg", - "git_commit": "c4870441a5935704eb8d18e36af8d9260b5b4a3c", + "git_commit": "c5fac92f3fd1c1dd1362104a6a1456d6965ecd58", "config_path": "captures/ghzinga-issue-88499/capture-empty-config.toml", "requested_columns": 120, "requested_rows": 36, @@ -113,5 +113,5 @@ } ], "actual_tmux_size": "120x36", - "app_tree_hash": "76068ac0572ab7b3776e5985f52d1351f03d3094c1b405b9585a97292b50fdcf" + "app_tree_hash": "56443dcfca40155fe6be910b62047b818a91ea9eed8e51f26992480f647d18a5" } diff --git a/captures/ghzinga-issue-88499/mouse-smoke/manifest.json b/captures/ghzinga-issue-88499/mouse-smoke/manifest.json index c72a4442..78e511a1 100644 --- a/captures/ghzinga-issue-88499/mouse-smoke/manifest.json +++ b/captures/ghzinga-issue-88499/mouse-smoke/manifest.json @@ -5,7 +5,7 @@ "fixtures/issue-66943.json" ], "binary": "target/debug/gzg", - "git_commit": "c4870441a5935704eb8d18e36af8d9260b5b4a3c", + "git_commit": "c5fac92f3fd1c1dd1362104a6a1456d6965ecd58", "config_path": "captures/ghzinga-issue-88499/mouse-smoke/capture-empty-config.toml", "command": "cd . && TERM=xterm-256color GZG_CONFIG_PATH=./captures/ghzinga-issue-88499/mouse-smoke/capture-empty-config.toml GZG_STATE_HOME=./captures/ghzinga-issue-88499/mouse-smoke/.capture-state GZG_CACHE_HOME=./captures/ghzinga-issue-88499/mouse-smoke/.capture-cache BROWSER=./captures/ghzinga-issue-88499/mouse-smoke/capture-open-url.sh GZG_COPY_COMMAND=./captures/ghzinga-issue-88499/mouse-smoke/capture-copy-url.sh ./target/debug/gzg https://github.com/openclaw/openclaw/issues/88499 --offline-fixture ./captures/ghzinga-issue-88499/mouse-smoke/navigation-fixture.json --offline-resource-fixture ./fixtures/issue-66943.json --no-restore --refresh-seconds 0", "actual_tmux_size": "120x36", @@ -120,5 +120,5 @@ "ansi": "70_mouse_quit_confirm.ansi" } ], - "app_tree_hash": "76068ac0572ab7b3776e5985f52d1351f03d3094c1b405b9585a97292b50fdcf" + "app_tree_hash": "56443dcfca40155fe6be910b62047b818a91ea9eed8e51f26992480f647d18a5" } diff --git a/captures/ghzinga-issue-88499/narrow/manifest.json b/captures/ghzinga-issue-88499/narrow/manifest.json index 6f233d86..63552264 100644 --- a/captures/ghzinga-issue-88499/narrow/manifest.json +++ b/captures/ghzinga-issue-88499/narrow/manifest.json @@ -4,7 +4,7 @@ "title": "openai-responses provider: 404 on previous_response_id when store=false (default)", "mode": "issue", "binary": "target/debug/gzg", - "git_commit": "c4870441a5935704eb8d18e36af8d9260b5b4a3c", + "git_commit": "c5fac92f3fd1c1dd1362104a6a1456d6965ecd58", "config_path": "captures/ghzinga-issue-88499/capture-empty-config.toml", "requested_columns": 80, "requested_rows": 24, @@ -113,5 +113,5 @@ } ], "actual_tmux_size": "80x24", - "app_tree_hash": "76068ac0572ab7b3776e5985f52d1351f03d3094c1b405b9585a97292b50fdcf" + "app_tree_hash": "56443dcfca40155fe6be910b62047b818a91ea9eed8e51f26992480f647d18a5" } diff --git a/captures/ghzinga-pr-81834/large/manifest.json b/captures/ghzinga-pr-81834/large/manifest.json index 61717b54..820ec7bd 100644 --- a/captures/ghzinga-pr-81834/large/manifest.json +++ b/captures/ghzinga-pr-81834/large/manifest.json @@ -4,7 +4,7 @@ "title": "feat(senseaudio): add SenseAudio TTS provider", "mode": "pr", "binary": "target/debug/gzg", - "git_commit": "c4870441a5935704eb8d18e36af8d9260b5b4a3c", + "git_commit": "c5fac92f3fd1c1dd1362104a6a1456d6965ecd58", "config_path": "captures/ghzinga-pr-81834/capture-empty-config.toml", "requested_columns": 160, "requested_rows": 50, @@ -183,5 +183,5 @@ } ], "actual_tmux_size": "160x50", - "app_tree_hash": "76068ac0572ab7b3776e5985f52d1351f03d3094c1b405b9585a97292b50fdcf" + "app_tree_hash": "56443dcfca40155fe6be910b62047b818a91ea9eed8e51f26992480f647d18a5" } diff --git a/captures/ghzinga-pr-81834/manifest.json b/captures/ghzinga-pr-81834/manifest.json index d40329ae..e61f5ad8 100644 --- a/captures/ghzinga-pr-81834/manifest.json +++ b/captures/ghzinga-pr-81834/manifest.json @@ -3,7 +3,7 @@ "title": "feat(senseaudio): add SenseAudio TTS provider", "mode": "pr", "binary": "target/debug/gzg", - "git_commit": "c4870441a5935704eb8d18e36af8d9260b5b4a3c", + "git_commit": "c5fac92f3fd1c1dd1362104a6a1456d6965ecd58", "config_path": "captures/ghzinga-pr-81834/capture-empty-config.toml", "offline_fixture": null, "offline_resource_fixtures": [], @@ -24,5 +24,5 @@ "rows": 50 } ], - "app_tree_hash": "76068ac0572ab7b3776e5985f52d1351f03d3094c1b405b9585a97292b50fdcf" + "app_tree_hash": "56443dcfca40155fe6be910b62047b818a91ea9eed8e51f26992480f647d18a5" } diff --git a/captures/ghzinga-pr-81834/medium/manifest.json b/captures/ghzinga-pr-81834/medium/manifest.json index 19e29935..5c222868 100644 --- a/captures/ghzinga-pr-81834/medium/manifest.json +++ b/captures/ghzinga-pr-81834/medium/manifest.json @@ -4,7 +4,7 @@ "title": "feat(senseaudio): add SenseAudio TTS provider", "mode": "pr", "binary": "target/debug/gzg", - "git_commit": "c4870441a5935704eb8d18e36af8d9260b5b4a3c", + "git_commit": "c5fac92f3fd1c1dd1362104a6a1456d6965ecd58", "config_path": "captures/ghzinga-pr-81834/capture-empty-config.toml", "requested_columns": 120, "requested_rows": 36, @@ -183,5 +183,5 @@ } ], "actual_tmux_size": "120x36", - "app_tree_hash": "76068ac0572ab7b3776e5985f52d1351f03d3094c1b405b9585a97292b50fdcf" + "app_tree_hash": "56443dcfca40155fe6be910b62047b818a91ea9eed8e51f26992480f647d18a5" } diff --git a/captures/ghzinga-pr-81834/mouse-smoke/manifest.json b/captures/ghzinga-pr-81834/mouse-smoke/manifest.json index f4fce845..7536c715 100644 --- a/captures/ghzinga-pr-81834/mouse-smoke/manifest.json +++ b/captures/ghzinga-pr-81834/mouse-smoke/manifest.json @@ -5,7 +5,7 @@ "fixtures/issue-66943.json" ], "binary": "target/debug/gzg", - "git_commit": "c4870441a5935704eb8d18e36af8d9260b5b4a3c", + "git_commit": "c5fac92f3fd1c1dd1362104a6a1456d6965ecd58", "config_path": "captures/ghzinga-pr-81834/mouse-smoke/capture-empty-config.toml", "command": "cd . && TERM=xterm-256color GZG_CONFIG_PATH=./captures/ghzinga-pr-81834/mouse-smoke/capture-empty-config.toml GZG_STATE_HOME=./captures/ghzinga-pr-81834/mouse-smoke/.capture-state GZG_CACHE_HOME=./captures/ghzinga-pr-81834/mouse-smoke/.capture-cache BROWSER=./captures/ghzinga-pr-81834/mouse-smoke/capture-open-url.sh GZG_COPY_COMMAND=./captures/ghzinga-pr-81834/mouse-smoke/capture-copy-url.sh ./target/debug/gzg 'openclaw/openclaw#81834' --offline-fixture ./captures/ghzinga-pr-81834/mouse-smoke/navigation-fixture.json --offline-resource-fixture ./fixtures/issue-66943.json --no-restore --refresh-seconds 0", "actual_tmux_size": "120x36", @@ -240,5 +240,5 @@ ], "load_full_fixture": "captures/ghzinga-pr-81834/mouse-smoke/load-full-fixture.json", "load_full_command": "cd . && TERM=xterm-256color GZG_CONFIG_PATH=./captures/ghzinga-pr-81834/mouse-smoke/capture-empty-config.toml GZG_STATE_HOME=./captures/ghzinga-pr-81834/mouse-smoke/.capture-state GZG_CACHE_HOME=./captures/ghzinga-pr-81834/mouse-smoke/.capture-cache ./target/debug/gzg 'openclaw/openclaw#81834' --offline-fixture ./captures/ghzinga-pr-81834/mouse-smoke/load-full-fixture.json --no-restore --refresh-seconds 0", - "app_tree_hash": "76068ac0572ab7b3776e5985f52d1351f03d3094c1b405b9585a97292b50fdcf" + "app_tree_hash": "56443dcfca40155fe6be910b62047b818a91ea9eed8e51f26992480f647d18a5" } diff --git a/captures/ghzinga-pr-81834/narrow/manifest.json b/captures/ghzinga-pr-81834/narrow/manifest.json index 136c062e..3801b122 100644 --- a/captures/ghzinga-pr-81834/narrow/manifest.json +++ b/captures/ghzinga-pr-81834/narrow/manifest.json @@ -4,7 +4,7 @@ "title": "feat(senseaudio): add SenseAudio TTS provider", "mode": "pr", "binary": "target/debug/gzg", - "git_commit": "c4870441a5935704eb8d18e36af8d9260b5b4a3c", + "git_commit": "c5fac92f3fd1c1dd1362104a6a1456d6965ecd58", "config_path": "captures/ghzinga-pr-81834/capture-empty-config.toml", "requested_columns": 80, "requested_rows": 24, @@ -183,5 +183,5 @@ } ], "actual_tmux_size": "80x24", - "app_tree_hash": "76068ac0572ab7b3776e5985f52d1351f03d3094c1b405b9585a97292b50fdcf" + "app_tree_hash": "56443dcfca40155fe6be910b62047b818a91ea9eed8e51f26992480f647d18a5" } From 79b504ea6e2ab4d768a3abf0aed9ab4903057f46 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Tue, 9 Jun 2026 11:25:26 +0800 Subject: [PATCH 11/12] fix: preserve resize batch redraw requests --- src/runner.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runner.rs b/src/runner.rs index 7c1c8a74..16926f22 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -719,7 +719,7 @@ async fn run_tui( if pending_event.requires_pre_event_redraw { let scrollbar_was_fading = should_advance_scrollbar_fade(state); terminal.draw(|frame| render_app(frame, state))?; - needs_redraw = should_redraw_after_scrollbar_frame(scrollbar_was_fading, state); + needs_redraw |= should_redraw_after_scrollbar_frame(scrollbar_was_fading, state); } let intent = apply_event(state, pending_event.event); if handle_intent( From 141dc91fde518da94589d854d2d1c963b7f02a13 Mon Sep 17 00:00:00 2001 From: Onur Solmaz <2453968+osolmaz@users.noreply.github.com> Date: Tue, 9 Jun 2026 11:25:58 +0800 Subject: [PATCH 12/12] test: refresh capture manifests --- captures/ghzinga-issue-88499/large/manifest.json | 4 ++-- captures/ghzinga-issue-88499/manifest.json | 4 ++-- captures/ghzinga-issue-88499/medium/manifest.json | 4 ++-- captures/ghzinga-issue-88499/mouse-smoke/manifest.json | 4 ++-- captures/ghzinga-issue-88499/narrow/manifest.json | 4 ++-- captures/ghzinga-pr-81834/large/manifest.json | 4 ++-- captures/ghzinga-pr-81834/manifest.json | 4 ++-- captures/ghzinga-pr-81834/medium/manifest.json | 4 ++-- captures/ghzinga-pr-81834/mouse-smoke/manifest.json | 4 ++-- captures/ghzinga-pr-81834/narrow/manifest.json | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/captures/ghzinga-issue-88499/large/manifest.json b/captures/ghzinga-issue-88499/large/manifest.json index 15774ebe..f264b5af 100644 --- a/captures/ghzinga-issue-88499/large/manifest.json +++ b/captures/ghzinga-issue-88499/large/manifest.json @@ -4,7 +4,7 @@ "title": "openai-responses provider: 404 on previous_response_id when store=false (default)", "mode": "issue", "binary": "target/debug/gzg", - "git_commit": "c5fac92f3fd1c1dd1362104a6a1456d6965ecd58", + "git_commit": "79b504ea6e2ab4d768a3abf0aed9ab4903057f46", "config_path": "captures/ghzinga-issue-88499/capture-empty-config.toml", "requested_columns": 160, "requested_rows": 50, @@ -113,5 +113,5 @@ } ], "actual_tmux_size": "160x50", - "app_tree_hash": "56443dcfca40155fe6be910b62047b818a91ea9eed8e51f26992480f647d18a5" + "app_tree_hash": "3d0ff248f59a801cfca8d8049b68091d5ade6f392d024eb3cb07ec1ffd4fd358" } diff --git a/captures/ghzinga-issue-88499/manifest.json b/captures/ghzinga-issue-88499/manifest.json index 3abe0edf..4cc2f9f8 100644 --- a/captures/ghzinga-issue-88499/manifest.json +++ b/captures/ghzinga-issue-88499/manifest.json @@ -3,7 +3,7 @@ "title": "openai-responses provider: 404 on previous_response_id when store=false (default)", "mode": "issue", "binary": "target/debug/gzg", - "git_commit": "c5fac92f3fd1c1dd1362104a6a1456d6965ecd58", + "git_commit": "79b504ea6e2ab4d768a3abf0aed9ab4903057f46", "config_path": "captures/ghzinga-issue-88499/capture-empty-config.toml", "offline_fixture": "fixtures/issue-88499.json", "offline_resource_fixtures": [], @@ -24,5 +24,5 @@ "rows": 50 } ], - "app_tree_hash": "56443dcfca40155fe6be910b62047b818a91ea9eed8e51f26992480f647d18a5" + "app_tree_hash": "3d0ff248f59a801cfca8d8049b68091d5ade6f392d024eb3cb07ec1ffd4fd358" } diff --git a/captures/ghzinga-issue-88499/medium/manifest.json b/captures/ghzinga-issue-88499/medium/manifest.json index 2d9494b4..0041beaf 100644 --- a/captures/ghzinga-issue-88499/medium/manifest.json +++ b/captures/ghzinga-issue-88499/medium/manifest.json @@ -4,7 +4,7 @@ "title": "openai-responses provider: 404 on previous_response_id when store=false (default)", "mode": "issue", "binary": "target/debug/gzg", - "git_commit": "c5fac92f3fd1c1dd1362104a6a1456d6965ecd58", + "git_commit": "79b504ea6e2ab4d768a3abf0aed9ab4903057f46", "config_path": "captures/ghzinga-issue-88499/capture-empty-config.toml", "requested_columns": 120, "requested_rows": 36, @@ -113,5 +113,5 @@ } ], "actual_tmux_size": "120x36", - "app_tree_hash": "56443dcfca40155fe6be910b62047b818a91ea9eed8e51f26992480f647d18a5" + "app_tree_hash": "3d0ff248f59a801cfca8d8049b68091d5ade6f392d024eb3cb07ec1ffd4fd358" } diff --git a/captures/ghzinga-issue-88499/mouse-smoke/manifest.json b/captures/ghzinga-issue-88499/mouse-smoke/manifest.json index 78e511a1..fb45ca83 100644 --- a/captures/ghzinga-issue-88499/mouse-smoke/manifest.json +++ b/captures/ghzinga-issue-88499/mouse-smoke/manifest.json @@ -5,7 +5,7 @@ "fixtures/issue-66943.json" ], "binary": "target/debug/gzg", - "git_commit": "c5fac92f3fd1c1dd1362104a6a1456d6965ecd58", + "git_commit": "79b504ea6e2ab4d768a3abf0aed9ab4903057f46", "config_path": "captures/ghzinga-issue-88499/mouse-smoke/capture-empty-config.toml", "command": "cd . && TERM=xterm-256color GZG_CONFIG_PATH=./captures/ghzinga-issue-88499/mouse-smoke/capture-empty-config.toml GZG_STATE_HOME=./captures/ghzinga-issue-88499/mouse-smoke/.capture-state GZG_CACHE_HOME=./captures/ghzinga-issue-88499/mouse-smoke/.capture-cache BROWSER=./captures/ghzinga-issue-88499/mouse-smoke/capture-open-url.sh GZG_COPY_COMMAND=./captures/ghzinga-issue-88499/mouse-smoke/capture-copy-url.sh ./target/debug/gzg https://github.com/openclaw/openclaw/issues/88499 --offline-fixture ./captures/ghzinga-issue-88499/mouse-smoke/navigation-fixture.json --offline-resource-fixture ./fixtures/issue-66943.json --no-restore --refresh-seconds 0", "actual_tmux_size": "120x36", @@ -120,5 +120,5 @@ "ansi": "70_mouse_quit_confirm.ansi" } ], - "app_tree_hash": "56443dcfca40155fe6be910b62047b818a91ea9eed8e51f26992480f647d18a5" + "app_tree_hash": "3d0ff248f59a801cfca8d8049b68091d5ade6f392d024eb3cb07ec1ffd4fd358" } diff --git a/captures/ghzinga-issue-88499/narrow/manifest.json b/captures/ghzinga-issue-88499/narrow/manifest.json index 63552264..77fa920c 100644 --- a/captures/ghzinga-issue-88499/narrow/manifest.json +++ b/captures/ghzinga-issue-88499/narrow/manifest.json @@ -4,7 +4,7 @@ "title": "openai-responses provider: 404 on previous_response_id when store=false (default)", "mode": "issue", "binary": "target/debug/gzg", - "git_commit": "c5fac92f3fd1c1dd1362104a6a1456d6965ecd58", + "git_commit": "79b504ea6e2ab4d768a3abf0aed9ab4903057f46", "config_path": "captures/ghzinga-issue-88499/capture-empty-config.toml", "requested_columns": 80, "requested_rows": 24, @@ -113,5 +113,5 @@ } ], "actual_tmux_size": "80x24", - "app_tree_hash": "56443dcfca40155fe6be910b62047b818a91ea9eed8e51f26992480f647d18a5" + "app_tree_hash": "3d0ff248f59a801cfca8d8049b68091d5ade6f392d024eb3cb07ec1ffd4fd358" } diff --git a/captures/ghzinga-pr-81834/large/manifest.json b/captures/ghzinga-pr-81834/large/manifest.json index 820ec7bd..abf20f75 100644 --- a/captures/ghzinga-pr-81834/large/manifest.json +++ b/captures/ghzinga-pr-81834/large/manifest.json @@ -4,7 +4,7 @@ "title": "feat(senseaudio): add SenseAudio TTS provider", "mode": "pr", "binary": "target/debug/gzg", - "git_commit": "c5fac92f3fd1c1dd1362104a6a1456d6965ecd58", + "git_commit": "79b504ea6e2ab4d768a3abf0aed9ab4903057f46", "config_path": "captures/ghzinga-pr-81834/capture-empty-config.toml", "requested_columns": 160, "requested_rows": 50, @@ -183,5 +183,5 @@ } ], "actual_tmux_size": "160x50", - "app_tree_hash": "56443dcfca40155fe6be910b62047b818a91ea9eed8e51f26992480f647d18a5" + "app_tree_hash": "3d0ff248f59a801cfca8d8049b68091d5ade6f392d024eb3cb07ec1ffd4fd358" } diff --git a/captures/ghzinga-pr-81834/manifest.json b/captures/ghzinga-pr-81834/manifest.json index e61f5ad8..ab9ff300 100644 --- a/captures/ghzinga-pr-81834/manifest.json +++ b/captures/ghzinga-pr-81834/manifest.json @@ -3,7 +3,7 @@ "title": "feat(senseaudio): add SenseAudio TTS provider", "mode": "pr", "binary": "target/debug/gzg", - "git_commit": "c5fac92f3fd1c1dd1362104a6a1456d6965ecd58", + "git_commit": "79b504ea6e2ab4d768a3abf0aed9ab4903057f46", "config_path": "captures/ghzinga-pr-81834/capture-empty-config.toml", "offline_fixture": null, "offline_resource_fixtures": [], @@ -24,5 +24,5 @@ "rows": 50 } ], - "app_tree_hash": "56443dcfca40155fe6be910b62047b818a91ea9eed8e51f26992480f647d18a5" + "app_tree_hash": "3d0ff248f59a801cfca8d8049b68091d5ade6f392d024eb3cb07ec1ffd4fd358" } diff --git a/captures/ghzinga-pr-81834/medium/manifest.json b/captures/ghzinga-pr-81834/medium/manifest.json index 5c222868..4e10b556 100644 --- a/captures/ghzinga-pr-81834/medium/manifest.json +++ b/captures/ghzinga-pr-81834/medium/manifest.json @@ -4,7 +4,7 @@ "title": "feat(senseaudio): add SenseAudio TTS provider", "mode": "pr", "binary": "target/debug/gzg", - "git_commit": "c5fac92f3fd1c1dd1362104a6a1456d6965ecd58", + "git_commit": "79b504ea6e2ab4d768a3abf0aed9ab4903057f46", "config_path": "captures/ghzinga-pr-81834/capture-empty-config.toml", "requested_columns": 120, "requested_rows": 36, @@ -183,5 +183,5 @@ } ], "actual_tmux_size": "120x36", - "app_tree_hash": "56443dcfca40155fe6be910b62047b818a91ea9eed8e51f26992480f647d18a5" + "app_tree_hash": "3d0ff248f59a801cfca8d8049b68091d5ade6f392d024eb3cb07ec1ffd4fd358" } diff --git a/captures/ghzinga-pr-81834/mouse-smoke/manifest.json b/captures/ghzinga-pr-81834/mouse-smoke/manifest.json index 7536c715..1fb45df5 100644 --- a/captures/ghzinga-pr-81834/mouse-smoke/manifest.json +++ b/captures/ghzinga-pr-81834/mouse-smoke/manifest.json @@ -5,7 +5,7 @@ "fixtures/issue-66943.json" ], "binary": "target/debug/gzg", - "git_commit": "c5fac92f3fd1c1dd1362104a6a1456d6965ecd58", + "git_commit": "79b504ea6e2ab4d768a3abf0aed9ab4903057f46", "config_path": "captures/ghzinga-pr-81834/mouse-smoke/capture-empty-config.toml", "command": "cd . && TERM=xterm-256color GZG_CONFIG_PATH=./captures/ghzinga-pr-81834/mouse-smoke/capture-empty-config.toml GZG_STATE_HOME=./captures/ghzinga-pr-81834/mouse-smoke/.capture-state GZG_CACHE_HOME=./captures/ghzinga-pr-81834/mouse-smoke/.capture-cache BROWSER=./captures/ghzinga-pr-81834/mouse-smoke/capture-open-url.sh GZG_COPY_COMMAND=./captures/ghzinga-pr-81834/mouse-smoke/capture-copy-url.sh ./target/debug/gzg 'openclaw/openclaw#81834' --offline-fixture ./captures/ghzinga-pr-81834/mouse-smoke/navigation-fixture.json --offline-resource-fixture ./fixtures/issue-66943.json --no-restore --refresh-seconds 0", "actual_tmux_size": "120x36", @@ -240,5 +240,5 @@ ], "load_full_fixture": "captures/ghzinga-pr-81834/mouse-smoke/load-full-fixture.json", "load_full_command": "cd . && TERM=xterm-256color GZG_CONFIG_PATH=./captures/ghzinga-pr-81834/mouse-smoke/capture-empty-config.toml GZG_STATE_HOME=./captures/ghzinga-pr-81834/mouse-smoke/.capture-state GZG_CACHE_HOME=./captures/ghzinga-pr-81834/mouse-smoke/.capture-cache ./target/debug/gzg 'openclaw/openclaw#81834' --offline-fixture ./captures/ghzinga-pr-81834/mouse-smoke/load-full-fixture.json --no-restore --refresh-seconds 0", - "app_tree_hash": "56443dcfca40155fe6be910b62047b818a91ea9eed8e51f26992480f647d18a5" + "app_tree_hash": "3d0ff248f59a801cfca8d8049b68091d5ade6f392d024eb3cb07ec1ffd4fd358" } diff --git a/captures/ghzinga-pr-81834/narrow/manifest.json b/captures/ghzinga-pr-81834/narrow/manifest.json index 3801b122..4cfa69d8 100644 --- a/captures/ghzinga-pr-81834/narrow/manifest.json +++ b/captures/ghzinga-pr-81834/narrow/manifest.json @@ -4,7 +4,7 @@ "title": "feat(senseaudio): add SenseAudio TTS provider", "mode": "pr", "binary": "target/debug/gzg", - "git_commit": "c5fac92f3fd1c1dd1362104a6a1456d6965ecd58", + "git_commit": "79b504ea6e2ab4d768a3abf0aed9ab4903057f46", "config_path": "captures/ghzinga-pr-81834/capture-empty-config.toml", "requested_columns": 80, "requested_rows": 24, @@ -183,5 +183,5 @@ } ], "actual_tmux_size": "80x24", - "app_tree_hash": "56443dcfca40155fe6be910b62047b818a91ea9eed8e51f26992480f647d18a5" + "app_tree_hash": "3d0ff248f59a801cfca8d8049b68091d5ade6f392d024eb3cb07ec1ffd4fd358" }