4runr-os 2.9.38 → 2.9.40

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.
@@ -646,6 +646,12 @@ fn main() -> Result<()> {
646
646
  }
647
647
  }
648
648
  }
649
+ Event::Resize(_width, _height) => {
650
+ // Terminal was resized - force immediate re-render with correct dimensions
651
+ // This fixes the initial render issue where portal dimensions might be wrong
652
+ terminal.clear()?;
653
+ app.request_immediate_render("terminal_resize");
654
+ }
649
655
  _ => {}
650
656
  }
651
657
  }
@@ -33,6 +33,20 @@ pub fn render(f: &mut Frame, state: &AppState) {
33
33
 
34
34
  let area = f.size();
35
35
 
36
+ // CRITICAL: Validate terminal size before rendering
37
+ // This prevents character corruption when terminal size is wrong on initial render
38
+ if area.width == 0 || area.height == 0 {
39
+ // Terminal not ready - skip render
40
+ return;
41
+ }
42
+
43
+ // Ensure minimum size for portal (60x22)
44
+ if area.width < 60 || area.height < 22 {
45
+ // Render "too small" message
46
+ render_too_small_connection(f, area);
47
+ return;
48
+ }
49
+
36
50
  // CRITICAL: Clear the ENTIRE screen first - this is a standalone screen, not an overlay
37
51
  f.render_widget(Clear, area);
38
52
 
@@ -481,3 +495,40 @@ fn render_actions(f: &mut Frame, area: Rect, state: &AppState) {
481
495
 
482
496
  f.render_widget(paragraph, area);
483
497
  }
498
+
499
+ fn render_too_small_connection(f: &mut Frame, area: Rect) {
500
+ // Clear screen
501
+ f.render_widget(Clear, area);
502
+
503
+ // Render background
504
+ let bg = Block::default()
505
+ .style(Style::default().bg(BG_PANEL));
506
+ f.render_widget(bg, area);
507
+
508
+ // Center the message
509
+ let msg = vec![
510
+ Line::from(""),
511
+ Line::from(Span::styled("Connection Portal", Style::default().fg(BRAND_PURPLE).bold())),
512
+ Line::from(""),
513
+ Line::from(Span::styled("Terminal too small", Style::default().fg(ERROR_RED))),
514
+ Line::from(""),
515
+ Line::from(Span::styled(
516
+ format!("Current: {}x{}", area.width, area.height),
517
+ Style::default().fg(TEXT_DIM)
518
+ )),
519
+ Line::from(Span::styled(
520
+ "Required: 60x22",
521
+ Style::default().fg(TEXT_PRIMARY)
522
+ )),
523
+ ];
524
+
525
+ let paragraph = Paragraph::new(msg)
526
+ .alignment(Alignment::Center);
527
+
528
+ f.render_widget(paragraph, Rect {
529
+ x: area.x,
530
+ y: area.y + (area.height / 3),
531
+ width: area.width,
532
+ height: area.height / 3,
533
+ });
534
+ }
@@ -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.38";
147
+ const PACKAGE_VERSION: &str = "2.9.40";
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)),
@@ -20,6 +20,20 @@ const ERROR_RED: Color = Color::Rgb(255, 69, 69);
20
20
  pub fn render(f: &mut Frame, state: &AppState) {
21
21
  let area = f.size();
22
22
 
23
+ // CRITICAL: Validate terminal size before rendering
24
+ // This prevents character corruption when terminal size is wrong on initial render
25
+ if area.width == 0 || area.height == 0 {
26
+ // Terminal not ready - skip render
27
+ return;
28
+ }
29
+
30
+ // Ensure minimum size for portal (85x25)
31
+ if area.width < 85 || area.height < 25 {
32
+ // Render "too small" message
33
+ render_too_small(f, area);
34
+ return;
35
+ }
36
+
23
37
  // CRITICAL: Clear the ENTIRE screen first - this is a standalone screen, not an overlay
24
38
  f.render_widget(Clear, area);
25
39
 
@@ -134,6 +148,9 @@ fn render_content(f: &mut Frame, area: Rect, state: &AppState) {
134
148
  }
135
149
 
136
150
  fn render_options_list(f: &mut Frame, area: Rect, state: &AppState) {
151
+ // CRITICAL: Clear the list area first to prevent duplication
152
+ f.render_widget(Clear, area);
153
+
137
154
  let options = vec![
138
155
  ("Local Bundle", GatewayOption::LocalBundle),
139
156
  ("4Runr Server", GatewayOption::CloudServer),
@@ -186,6 +203,9 @@ fn render_options_list(f: &mut Frame, area: Rect, state: &AppState) {
186
203
  }
187
204
 
188
205
  fn render_option_details(f: &mut Frame, area: Rect, state: &AppState) {
206
+ // CRITICAL: Clear the details area first to prevent content overlap/corruption
207
+ f.render_widget(Clear, area);
208
+
189
209
  let selected_option = &state.setup_portal.selected_option;
190
210
  let detection_result = &state.setup_portal.detection_result;
191
211
 
@@ -278,11 +298,15 @@ fn render_option_details(f: &mut Frame, area: Rect, state: &AppState) {
278
298
  .border_style(Style::default().fg(TEXT_DIM))
279
299
  .style(Style::default().bg(BG_PANEL));
280
300
 
301
+ // CRITICAL: Render block first to clear and establish boundaries
302
+ let inner = block.inner(area);
303
+ f.render_widget(block, area);
304
+
305
+ // Then render paragraph content only within the inner area
281
306
  let paragraph = Paragraph::new(content)
282
- .block(block)
283
307
  .wrap(Wrap { trim: true });
284
308
 
285
- f.render_widget(paragraph, area);
309
+ f.render_widget(paragraph, inner);
286
310
  }
287
311
 
288
312
  fn render_error_box(f: &mut Frame, area: Rect, state: &AppState) {
@@ -345,3 +369,40 @@ fn render_actions(f: &mut Frame, area: Rect, state: &AppState) {
345
369
 
346
370
  f.render_widget(paragraph, inner);
347
371
  }
372
+
373
+ fn render_too_small(f: &mut Frame, area: Rect) {
374
+ // Clear screen
375
+ f.render_widget(Clear, area);
376
+
377
+ // Render background
378
+ let bg = Block::default()
379
+ .style(Style::default().bg(BG_PANEL));
380
+ f.render_widget(bg, area);
381
+
382
+ // Center the message
383
+ let msg = vec![
384
+ Line::from(""),
385
+ Line::from(Span::styled("Setup Portal", Style::default().fg(BRAND_PURPLE).bold())),
386
+ Line::from(""),
387
+ Line::from(Span::styled("Terminal too small", Style::default().fg(ERROR_RED))),
388
+ Line::from(""),
389
+ Line::from(Span::styled(
390
+ format!("Current: {}x{}", area.width, area.height),
391
+ Style::default().fg(TEXT_DIM)
392
+ )),
393
+ Line::from(Span::styled(
394
+ "Required: 85x25",
395
+ Style::default().fg(TEXT_PRIMARY)
396
+ )),
397
+ ];
398
+
399
+ let paragraph = Paragraph::new(msg)
400
+ .alignment(ratatui::layout::Alignment::Center);
401
+
402
+ f.render_widget(paragraph, Rect {
403
+ x: area.x,
404
+ y: area.y + (area.height / 3),
405
+ width: area.width,
406
+ height: area.height / 3,
407
+ });
408
+ }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "4runr-os",
3
- "version": "2.9.38",
3
+ "version": "2.9.40",
4
4
  "type": "module",
5
- "description": "4Runr AI Agent OS - Secure terminal interface for AI agents. v2.9.38: Fixed Setup Portal character corruption - removed redundant Clear widget causing buffer corruption during navigation. ⚠️ Pre-MVP / Development Phase",
5
+ "description": "4Runr AI Agent OS - Secure terminal interface for AI agents. v2.9.40: Fixed Setup Portal rendering - added explicit Clear widgets and fixed Paragraph rendering to prevent content overlap and character corruption. v2.9.39: Fixed Setup Portal initial render bug. ⚠️ Pre-MVP / Development Phase",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
8
8
  "4runr": "dist/index.js",