4runr-os 2.1.49 → 2.1.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.
Binary file
@@ -511,31 +511,53 @@ fn render_center_column(f: &mut Frame, area: Rect, state: &AppState) {
511
511
  f.render_widget(Paragraph::new(lines), content_area);
512
512
 
513
513
  // === RENDER CLEAN SCROLLBAR (only if scrollable) ===
514
+ // CRITICAL FIX: Only render scrollbar when actually scrollable to prevent flickering
514
515
  if total_logs > visible_height && max_scroll > 0 {
515
516
  // Scrollbar positioned inside the border, 1 char from right edge
516
- let scrollbar_x = panel_area.x + panel_area.width.saturating_sub(3); // Inside border, 1 char from right
517
+ // Use fixed position to prevent jitter
518
+ let scrollbar_x = panel_area.x + panel_area.width.saturating_sub(3);
517
519
  let scrollbar_y = content_area.y;
518
520
  let scrollbar_height = content_area.height;
519
521
 
520
- // Calculate scrollbar thumb size (proportional to visible/total ratio)
521
- let thumb_height = ((visible_height as f32 / total_logs as f32) * scrollbar_height as f32).max(1.0) as u16;
522
- let thumb_height = thumb_height.min(scrollbar_height);
522
+ // CRITICAL: Use integer math to prevent floating point rounding issues
523
+ // Calculate thumb size (minimum 1 char, proportional to visible/total)
524
+ let thumb_height = ((visible_height as u32 * scrollbar_height as u32) / total_logs as u32) as u16;
525
+ let thumb_height = thumb_height.max(1).min(scrollbar_height);
523
526
 
524
- // Calculate thumb position based on scroll offset
525
- let scroll_ratio = if max_scroll > 0 { state.log_scroll as f32 / max_scroll as f32 } else { 0.0 };
526
- let thumb_start = (scroll_ratio * (scrollbar_height - thumb_height) as f32) as u16;
527
+ // Calculate thumb position using integer math for stability
528
+ let thumb_start = if max_scroll > 0 {
529
+ // Use integer division to avoid floating point jitter
530
+ ((state.log_scroll as u32 * (scrollbar_height.saturating_sub(thumb_height) as u32)) / max_scroll as u32) as u16
531
+ } else {
532
+ 0
533
+ };
534
+
535
+ // Clamp thumb_start to valid range
536
+ let thumb_start = thumb_start.min(scrollbar_height.saturating_sub(thumb_height));
537
+ let thumb_end = (thumb_start + thumb_height).min(scrollbar_height);
538
+
539
+ // Render scrollbar track (entire height) as single operation to prevent flicker
540
+ // Build scrollbar string first, then render once
541
+ let mut scrollbar_chars = vec![String::from("│"); scrollbar_height as usize];
542
+
543
+ // Fill thumb portion
544
+ for i in thumb_start..thumb_end {
545
+ if i < scrollbar_height {
546
+ scrollbar_chars[i as usize] = String::from("█");
547
+ }
548
+ }
527
549
 
528
- // Draw clean scrollbar with better visibility
529
- for i in 0..scrollbar_height {
530
- let y = scrollbar_y + i;
550
+ // Render entire scrollbar at once (prevents flickering from multiple renders)
551
+ for (i, c) in scrollbar_chars.iter().enumerate() {
552
+ let y = scrollbar_y + i as u16;
531
553
  if y < scrollbar_y + scrollbar_height {
532
- let (c, color) = if i >= thumb_start && i < thumb_start + thumb_height {
533
- ("█", CYBER_CYAN) // Thumb in cyan (matches panel title)
554
+ let color = if i >= thumb_start as usize && i < thumb_end as usize {
555
+ CYBER_CYAN
534
556
  } else {
535
- ("│", TEXT_MUTED) // Track in muted gray
557
+ TEXT_MUTED
536
558
  };
537
559
  f.render_widget(
538
- Paragraph::new(c).style(Style::default().fg(color)),
560
+ Paragraph::new(c.as_str()).style(Style::default().fg(color)),
539
561
  Rect { x: scrollbar_x, y, width: 1, height: 1 }
540
562
  );
541
563
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "4runr-os",
3
- "version": "2.1.49",
3
+ "version": "2.1.50",
4
4
  "type": "module",
5
5
  "description": "4Runr AI Agent OS - Interactive terminal for managing AI agents",
6
6
  "main": "dist/index.js",