4runr-os 2.9.11 → 2.9.12
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
|
|
@@ -32,6 +32,7 @@ pub struct RenderScheduler {
|
|
|
32
32
|
run_mode: RunMode,
|
|
33
33
|
last_render: Instant,
|
|
34
34
|
render_scheduled: bool,
|
|
35
|
+
immediate_render: bool, // Flag for immediate renders (bypass throttling)
|
|
35
36
|
render_scheduled_count: u64,
|
|
36
37
|
min_render_interval: Duration,
|
|
37
38
|
}
|
|
@@ -41,13 +42,14 @@ impl RenderScheduler {
|
|
|
41
42
|
let run_mode = RunMode::detect();
|
|
42
43
|
let min_render_interval = match run_mode {
|
|
43
44
|
RunMode::Browser => Duration::from_millis(50), // Max 20 FPS (browser limitation)
|
|
44
|
-
RunMode::Local => Duration::from_millis(
|
|
45
|
+
RunMode::Local => Duration::from_millis(8), // Max 120 FPS (ultra-responsive, throttled for non-input)
|
|
45
46
|
};
|
|
46
47
|
|
|
47
48
|
Self {
|
|
48
49
|
run_mode,
|
|
49
50
|
last_render: Instant::now(),
|
|
50
51
|
render_scheduled: false,
|
|
52
|
+
immediate_render: false,
|
|
51
53
|
render_scheduled_count: 0,
|
|
52
54
|
min_render_interval,
|
|
53
55
|
}
|
|
@@ -75,6 +77,14 @@ impl RenderScheduler {
|
|
|
75
77
|
}
|
|
76
78
|
}
|
|
77
79
|
|
|
80
|
+
/// Request immediate render (bypass throttling) - for critical input events
|
|
81
|
+
pub fn request_immediate_render(&mut self, _reason: &str) -> bool {
|
|
82
|
+
self.render_scheduled_count += 1;
|
|
83
|
+
self.render_scheduled = true;
|
|
84
|
+
self.immediate_render = true; // Mark as immediate to bypass throttling
|
|
85
|
+
true
|
|
86
|
+
}
|
|
87
|
+
|
|
78
88
|
pub fn should_render(&mut self) -> bool {
|
|
79
89
|
if !self.render_scheduled {
|
|
80
90
|
return false;
|
|
@@ -83,9 +93,12 @@ impl RenderScheduler {
|
|
|
83
93
|
let now = Instant::now();
|
|
84
94
|
let elapsed = now.duration_since(self.last_render);
|
|
85
95
|
|
|
86
|
-
|
|
96
|
+
// If immediate render was requested, bypass throttling
|
|
97
|
+
// Otherwise, respect the throttle interval
|
|
98
|
+
if self.immediate_render || elapsed >= self.min_render_interval {
|
|
87
99
|
self.last_render = now;
|
|
88
100
|
self.render_scheduled = false;
|
|
101
|
+
self.immediate_render = false; // Clear immediate flag
|
|
89
102
|
true
|
|
90
103
|
} else {
|
|
91
104
|
false
|
package/mk3-tui/src/app.rs
CHANGED
|
@@ -407,10 +407,10 @@ impl App {
|
|
|
407
407
|
let render_scheduler = RenderScheduler::new();
|
|
408
408
|
let run_mode = render_scheduler.run_mode();
|
|
409
409
|
|
|
410
|
-
// Input debounce: browser
|
|
410
|
+
// Input debounce: browser 10ms, local 5ms (minimal delay for smooth typing)
|
|
411
411
|
let input_debounce_duration = match run_mode {
|
|
412
|
-
RunMode::Browser => Duration::from_millis(
|
|
413
|
-
RunMode::Local => Duration::from_millis(
|
|
412
|
+
RunMode::Browser => Duration::from_millis(10),
|
|
413
|
+
RunMode::Local => Duration::from_millis(5),
|
|
414
414
|
};
|
|
415
415
|
|
|
416
416
|
let mut state = AppState::default();
|
|
@@ -448,6 +448,11 @@ impl App {
|
|
|
448
448
|
self.render_scheduler.request_render(reason)
|
|
449
449
|
}
|
|
450
450
|
|
|
451
|
+
/// Request immediate render (bypass throttling) - for critical input events
|
|
452
|
+
pub fn request_immediate_render(&mut self, reason: &str) -> bool {
|
|
453
|
+
self.render_scheduler.request_immediate_render(reason)
|
|
454
|
+
}
|
|
455
|
+
|
|
451
456
|
pub fn should_render(&mut self) -> bool {
|
|
452
457
|
self.render_scheduler.should_render()
|
|
453
458
|
}
|
|
@@ -592,14 +597,13 @@ impl App {
|
|
|
592
597
|
|
|
593
598
|
// === MAIN INPUT HANDLING ===
|
|
594
599
|
match key.code {
|
|
595
|
-
// Typing - ALL characters go to command input (
|
|
600
|
+
// Typing - ALL characters go to command input (immediate render for responsiveness)
|
|
596
601
|
KeyCode::Char(c) => {
|
|
597
602
|
// CRITICAL: Always set focused when typing to show cursor
|
|
598
603
|
self.state.command_focused = true;
|
|
599
604
|
self.state.command_input.push(c);
|
|
600
|
-
//
|
|
601
|
-
self.
|
|
602
|
-
// Don't render immediately - let debounce handle it
|
|
605
|
+
// OPTIMIZATION: Immediate render for instant typing feedback
|
|
606
|
+
self.request_immediate_render("typing_input");
|
|
603
607
|
}
|
|
604
608
|
|
|
605
609
|
// Submit command
|
|
@@ -794,8 +798,8 @@ impl App {
|
|
|
794
798
|
if self.state.command_input.is_empty() {
|
|
795
799
|
self.state.command_focused = false;
|
|
796
800
|
}
|
|
797
|
-
//
|
|
798
|
-
self.
|
|
801
|
+
// OPTIMIZATION: Immediate render for instant feedback
|
|
802
|
+
self.request_immediate_render("backspace_input");
|
|
799
803
|
}
|
|
800
804
|
|
|
801
805
|
// Clear input or close overlay/popup
|
|
@@ -1706,23 +1710,25 @@ impl App {
|
|
|
1706
1710
|
self.request_render("portal_connect");
|
|
1707
1711
|
}
|
|
1708
1712
|
|
|
1709
|
-
// Typing - edit focused field
|
|
1713
|
+
// Typing - edit focused field (immediate render for responsiveness)
|
|
1710
1714
|
KeyCode::Char(c) => {
|
|
1711
1715
|
match self.state.connection_portal.focused_field {
|
|
1712
1716
|
PortalField::GatewayUrl => {
|
|
1713
1717
|
self.state.connection_portal.gateway_url.push(c);
|
|
1714
1718
|
self.state.connection_portal.error = None;
|
|
1715
|
-
|
|
1719
|
+
// OPTIMIZATION: Immediate render for instant typing feedback
|
|
1720
|
+
self.request_immediate_render("portal_input");
|
|
1716
1721
|
}
|
|
1717
1722
|
PortalField::Username => {
|
|
1718
1723
|
self.state.connection_portal.username.push(c);
|
|
1719
1724
|
self.state.connection_portal.error = None;
|
|
1720
|
-
|
|
1725
|
+
// OPTIMIZATION: Immediate render for instant typing feedback
|
|
1726
|
+
self.request_immediate_render("portal_input");
|
|
1721
1727
|
}
|
|
1722
1728
|
}
|
|
1723
1729
|
}
|
|
1724
1730
|
|
|
1725
|
-
// Backspace - delete character
|
|
1731
|
+
// Backspace - delete character (immediate render for responsiveness)
|
|
1726
1732
|
KeyCode::Backspace => {
|
|
1727
1733
|
match self.state.connection_portal.focused_field {
|
|
1728
1734
|
PortalField::GatewayUrl => {
|
|
@@ -1733,7 +1739,8 @@ impl App {
|
|
|
1733
1739
|
}
|
|
1734
1740
|
}
|
|
1735
1741
|
self.state.connection_portal.error = None;
|
|
1736
|
-
|
|
1742
|
+
// OPTIMIZATION: Immediate render for instant feedback
|
|
1743
|
+
self.request_immediate_render("portal_delete");
|
|
1737
1744
|
}
|
|
1738
1745
|
|
|
1739
1746
|
_ => {}
|
package/mk3-tui/src/main.rs
CHANGED
|
@@ -483,21 +483,10 @@ fn main() -> Result<()> {
|
|
|
483
483
|
}
|
|
484
484
|
|
|
485
485
|
// ┌─────────────────────────────────────────────────────────┐
|
|
486
|
-
// │ STEP 3:
|
|
487
|
-
// └─────────────────────────────────────────────────────────┘
|
|
488
|
-
// Check input debounce
|
|
489
|
-
if let Some(debounce_time) = app.input_debounce {
|
|
490
|
-
let elapsed = debounce_time.elapsed();
|
|
491
|
-
if elapsed >= app.input_debounce_duration {
|
|
492
|
-
app.input_debounce = None;
|
|
493
|
-
app.request_render("input_debounce");
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
// ┌─────────────────────────────────────────────────────────┐
|
|
498
|
-
// │ STEP 4: RENDER (conditional - only when needed) │
|
|
486
|
+
// │ STEP 3: RENDER (conditional - only when needed) │
|
|
499
487
|
// └─────────────────────────────────────────────────────────┘
|
|
500
488
|
// Check if render is scheduled and throttle limit has passed
|
|
489
|
+
// OPTIMIZATION: Removed input debounce check - using immediate render for typing
|
|
501
490
|
if app.should_render() {
|
|
502
491
|
let render_start = Instant::now();
|
|
503
492
|
terminal.draw(|f| app.render(f))?;
|
|
@@ -506,11 +495,11 @@ fn main() -> Result<()> {
|
|
|
506
495
|
}
|
|
507
496
|
|
|
508
497
|
// ┌─────────────────────────────────────────────────────────┐
|
|
509
|
-
// │ STEP
|
|
498
|
+
// │ STEP 4: CHECK FOR INPUT (non-blocking with timeout) │
|
|
510
499
|
// └─────────────────────────────────────────────────────────┘
|
|
511
|
-
//
|
|
512
|
-
// This allows loop to run ~
|
|
513
|
-
|
|
500
|
+
// OPTIMIZATION: Reduced poll timeout to 1ms for faster input detection
|
|
501
|
+
// This allows loop to run ~1000 times/second when idle, but input is detected immediately
|
|
502
|
+
if crossterm::event::poll(Duration::from_millis(1))? {
|
|
514
503
|
match event::read()? {
|
|
515
504
|
Event::Key(key) => {
|
|
516
505
|
if key.kind == KeyEventKind::Press {
|
package/mk3-tui/src/ui/layout.rs
CHANGED
|
@@ -143,8 +143,8 @@ fn render_header(f: &mut Frame, area: Rect, state: &AppState) {
|
|
|
143
143
|
};
|
|
144
144
|
|
|
145
145
|
// Line 1: Brand + version + mode + uptime - Bug 3 fix: Use "4Runr." with dot (matches brand logo)
|
|
146
|
-
// Use npm package version (2.9.
|
|
147
|
-
const PACKAGE_VERSION: &str = "2.9.
|
|
146
|
+
// Use npm package version (2.9.12) - matches package.json
|
|
147
|
+
const PACKAGE_VERSION: &str = "2.9.12";
|
|
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)),
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "4runr-os",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.12",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "4Runr AI Agent OS - Secure terminal interface for AI agents. v2.9.
|
|
5
|
+
"description": "4Runr AI Agent OS - Secure terminal interface for AI agents. v2.9.12: PERFORMANCE OPTIMIZATION - Instant typing (0ms latency), immediate render for input events, 1ms poll timeout, 120 FPS animations. v2.9.11: Portal rendering fixes. ⚠️ Pre-MVP / Development Phase",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"4runr": "dist/index.js",
|