4runr-os 2.9.52 → 2.9.54

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.
@@ -2,7 +2,7 @@ use anyhow::Result;
2
2
  use crossterm::cursor;
3
3
  use crossterm::event::{self, Event, KeyEventKind, MouseEventKind};
4
4
  use crossterm::execute;
5
- use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen, Clear, ClearType};
5
+ use crossterm::terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen, Clear, ClearType, size as terminal_size};
6
6
  use ratatui::prelude::*;
7
7
  use std::time::{Duration, Instant};
8
8
 
@@ -597,6 +597,13 @@ fn main() -> Result<()> {
597
597
  (None, _) => true,
598
598
  };
599
599
 
600
+ // When opening Setup Portal in windowed mode, terminal size can be stale → corruption/duplicates.
601
+ // Force OS to report current size and redraw once so layout uses correct dimensions.
602
+ let switching_to_setup = is_switching && current_screen == crate::screens::Screen::SetupPortal;
603
+ if switching_to_setup {
604
+ let _ = terminal_size(); // sync terminal dimensions (e.g. after restore-down)
605
+ }
606
+
600
607
  if is_switching {
601
608
  terminal.clear()?;
602
609
  }
@@ -605,6 +612,11 @@ fn main() -> Result<()> {
605
612
  terminal.draw(|f| app.render(f))?;
606
613
  let render_duration = render_start.elapsed().as_millis() as u64;
607
614
  app.record_render(render_duration);
615
+
616
+ // Second frame when opening Setup Portal: ensures correct layout when windowed (no stale size).
617
+ if switching_to_setup {
618
+ app.request_immediate_render("setup_portal_second_frame");
619
+ }
608
620
 
609
621
  // Update previous screen tracking
610
622
  previous_screen = Some(current_screen);
@@ -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.52";
147
+ const PACKAGE_VERSION: &str = "2.9.54";
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)),
@@ -151,6 +151,9 @@ fn render_content(f: &mut Frame, area: Rect, state: &mut AppState, render_id: u6
151
151
  }
152
152
 
153
153
  fn render_options_list(f: &mut Frame, area: Rect, state: &mut AppState, _render_id: u64) {
154
+ // Clear this rect first so no duplicate rows from previous frame
155
+ f.render_widget(Clear, area);
156
+
154
157
  let options = vec![
155
158
  ("Local Bundle", GatewayOption::LocalBundle),
156
159
  ("4Runr Server", GatewayOption::CloudServer),
@@ -202,6 +205,9 @@ fn render_options_list(f: &mut Frame, area: Rect, state: &mut AppState, _render_
202
205
  }
203
206
 
204
207
  fn render_option_details(f: &mut Frame, area: Rect, state: &AppState, _render_id: u64) {
208
+ // Clear this rect first so no leftover from previous option
209
+ f.render_widget(Clear, area);
210
+
205
211
  let selected_option = &state.setup_portal.selected_option;
206
212
  let detection_result = &state.setup_portal.detection_result;
207
213
 
@@ -297,21 +303,27 @@ fn render_option_details(f: &mut Frame, area: Rect, state: &AppState, _render_id
297
303
  let inner = block.inner(area);
298
304
  f.render_widget(block, area);
299
305
 
300
- // Fill the inner area with background explicitly
301
- let fill = Block::default().style(Style::default().bg(BG_PANEL));
302
- f.render_widget(fill, inner);
303
-
304
- // Now render paragraph (content only, no block)
305
- let paragraph = Paragraph::new(content)
306
- .wrap(Wrap { trim: true })
307
- .style(Style::default().bg(BG_PANEL));
308
-
309
306
  // Validate area before rendering
310
307
  let terminal_size = f.size();
311
308
  if inner.width == 0 || inner.height == 0 ||
312
309
  inner.x >= terminal_size.width || inner.y >= terminal_size.height {
313
310
  return;
314
311
  }
312
+
313
+ // Pad content to at least inner height so every row is painted (no leftover text)
314
+ let min_lines = inner.height as usize;
315
+ let mut padded: Vec<Line> = content.into_iter().collect();
316
+ while padded.len() < min_lines {
317
+ padded.push(Line::from(""));
318
+ }
319
+
320
+ // Fill inner with background then paragraph so every cell is overwritten
321
+ let fill = Block::default().style(Style::default().bg(BG_PANEL));
322
+ f.render_widget(fill, inner);
323
+
324
+ let paragraph = Paragraph::new(padded)
325
+ .wrap(Wrap { trim: true })
326
+ .style(Style::default().bg(BG_PANEL));
315
327
 
316
328
  f.render_widget(paragraph, inner);
317
329
  }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "4runr-os",
3
- "version": "2.9.52",
3
+ "version": "2.9.54",
4
4
  "type": "module",
5
- "description": "4Runr AI Agent OS - Secure terminal interface for AI agents. v2.9.52: Setup Portal - block+fill+widget render (no flicker, no corruption). v2.9.51: No clear on nav, removed debug logs. ⚠️ Pre-MVP / Development Phase",
5
+ "description": "4Runr AI Agent OS - Secure terminal interface for AI agents. v2.9.54: Setup Portal - fix open-while-windowed (size sync + second frame). v2.9.53: Clear + padded details. ⚠️ Pre-MVP / Development Phase",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
8
8
  "4runr": "dist/index.js",