4runr-os 2.9.48 → 2.9.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -343,7 +343,9 @@ pub struct SetupPortalState {
343
343
  pub detecting: bool,
344
344
  pub detection_result: Option<DetectionResult>,
345
345
  pub error: Option<String>,
346
- pub list_state: ratatui::widgets::ListState, // CRITICAL: Must persist across frames
346
+ pub list_state: ratatui::widgets::ListState,
347
+ /// Last option we drew; clear terminal only when this differs from selected_option (reduces flicker).
348
+ pub last_rendered_option: Option<GatewayOption>,
347
349
  }
348
350
 
349
351
  #[derive(Debug, Clone)]
@@ -375,6 +377,7 @@ impl Default for SetupPortalState {
375
377
  detection_result: None,
376
378
  error: None,
377
379
  list_state,
380
+ last_rendered_option: None,
378
381
  }
379
382
  }
380
383
  }
@@ -591,15 +591,23 @@ fn main() -> Result<()> {
591
591
  let current_screen = app.state.navigation.current_screen().clone();
592
592
  let is_portal_active = matches!(current_screen, crate::screens::Screen::ConnectionPortal | crate::screens::Screen::SetupPortal);
593
593
 
594
- // Clear terminal only on screen switch, not on every render
595
- // This prevents flashing while still ensuring clean transitions
594
+ // Clear on screen switch, or on Setup Portal only when selection just changed
595
+ // (avoids flicker from clearing every frame while still fixing list/details mismatch)
596
596
  let is_switching = match (&previous_screen, &current_screen) {
597
597
  (Some(prev), current) => prev != current,
598
- (None, _) => true, // First render
598
+ (None, _) => true,
599
+ };
600
+
601
+ let setup_selection_changed = match &current_screen {
602
+ crate::screens::Screen::SetupPortal => {
603
+ let last = app.state.setup_portal.last_rendered_option.as_ref();
604
+ let current = &app.state.setup_portal.selected_option;
605
+ last != Some(current)
606
+ }
599
607
  _ => false,
600
608
  };
601
-
602
- if is_switching {
609
+
610
+ if is_switching || setup_selection_changed {
603
611
  terminal.clear()?;
604
612
  }
605
613
 
@@ -144,7 +144,7 @@ fn render_header(f: &mut Frame, area: Rect, state: &AppState) {
144
144
 
145
145
  // Line 1: Brand + version + mode + uptime - Bug 3 fix: Use "4Runr." with dot (matches brand logo)
146
146
  // Use npm package version (2.9.24) - matches package.json
147
- const PACKAGE_VERSION: &str = "2.9.48";
147
+ const PACKAGE_VERSION: &str = "2.9.50";
148
148
  let brand_line = Line::from(vec![
149
149
  Span::styled("4Runr.", Style::default().fg(BRAND_PURPLE).add_modifier(Modifier::BOLD)),
150
150
  Span::styled(" AI AGENT OS", Style::default().fg(BRAND_VIOLET)),
@@ -146,6 +146,9 @@ pub fn render(f: &mut Frame, state: &mut AppState) {
146
146
  } else {
147
147
  render_actions(f, chunks[3], state);
148
148
  }
149
+
150
+ // So main loop only clears terminal when selection actually changed (reduces flicker).
151
+ state.setup_portal.last_rendered_option = Some(state.setup_portal.selected_option.clone());
149
152
  }
150
153
 
151
154
  fn render_header(f: &mut Frame, area: Rect, state: &AppState) {
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "4runr-os",
3
- "version": "2.9.48",
3
+ "version": "2.9.50",
4
4
  "type": "module",
5
- "description": "4Runr AI Agent OS - Secure terminal interface for AI agents. v2.9.48: Setup Portal - stateless list (single source of truth), per-frame R# debug. v2.9.47: ListState sync. ⚠️ Pre-MVP / Development Phase",
5
+ "description": "4Runr AI Agent OS - Secure terminal interface for AI agents. v2.9.50: Setup Portal - clear only when selection changes (fixes flicker, keeps list/details in sync). v2.9.49: Per-frame clear. ⚠️ Pre-MVP / Development Phase",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
8
8
  "4runr": "dist/index.js",