4runr-os 2.9.39 → 2.9.41
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
|
package/mk3-tui/src/ui/layout.rs
CHANGED
|
@@ -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.
|
|
147
|
+
const PACKAGE_VERSION: &str = "2.9.41";
|
|
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)),
|
|
@@ -18,18 +18,34 @@ const ERROR_RED: Color = Color::Rgb(255, 69, 69);
|
|
|
18
18
|
|
|
19
19
|
/// Render the Setup Portal screen - Full-screen standalone portal
|
|
20
20
|
pub fn render(f: &mut Frame, state: &AppState) {
|
|
21
|
+
// DEBUG: Track render calls
|
|
22
|
+
static mut RENDER_COUNT: u64 = 0;
|
|
23
|
+
unsafe {
|
|
24
|
+
RENDER_COUNT += 1;
|
|
25
|
+
if RENDER_COUNT % 10 == 1 { // Log every 10th render to avoid spam
|
|
26
|
+
eprintln!("[SETUP-PORTAL] Render #{}", RENDER_COUNT);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
21
30
|
let area = f.size();
|
|
22
31
|
|
|
32
|
+
// DEBUG: Log terminal size
|
|
33
|
+
unsafe {
|
|
34
|
+
if RENDER_COUNT % 10 == 1 {
|
|
35
|
+
eprintln!("[SETUP-PORTAL] Terminal size: {}x{}", area.width, area.height);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
23
39
|
// CRITICAL: Validate terminal size before rendering
|
|
24
40
|
// This prevents character corruption when terminal size is wrong on initial render
|
|
25
41
|
if area.width == 0 || area.height == 0 {
|
|
26
|
-
|
|
42
|
+
eprintln!("[SETUP-PORTAL] ⚠️ Terminal size invalid: {}x{} - skipping render", area.width, area.height);
|
|
27
43
|
return;
|
|
28
44
|
}
|
|
29
45
|
|
|
30
46
|
// Ensure minimum size for portal (85x25)
|
|
31
47
|
if area.width < 85 || area.height < 25 {
|
|
32
|
-
|
|
48
|
+
eprintln!("[SETUP-PORTAL] ⚠️ Terminal too small: {}x{} (required: 85x25)", area.width, area.height);
|
|
33
49
|
render_too_small(f, area);
|
|
34
50
|
return;
|
|
35
51
|
}
|
|
@@ -55,6 +71,14 @@ pub fn render(f: &mut Frame, state: &AppState) {
|
|
|
55
71
|
height: portal_height,
|
|
56
72
|
};
|
|
57
73
|
|
|
74
|
+
// DEBUG: Log portal area calculations
|
|
75
|
+
unsafe {
|
|
76
|
+
if RENDER_COUNT % 10 == 1 {
|
|
77
|
+
eprintln!("[SETUP-PORTAL] Portal area: x={}, y={}, w={}, h={}",
|
|
78
|
+
portal_area.x, portal_area.y, portal_area.width, portal_area.height);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
58
82
|
// Split portal into sections
|
|
59
83
|
use ratatui::layout::{Constraint, Direction, Layout};
|
|
60
84
|
|
|
@@ -82,6 +106,30 @@ pub fn render(f: &mut Frame, state: &AppState) {
|
|
|
82
106
|
.constraints(constraints)
|
|
83
107
|
.split(portal_area);
|
|
84
108
|
|
|
109
|
+
// DEBUG: Log layout chunks and selected option
|
|
110
|
+
unsafe {
|
|
111
|
+
if RENDER_COUNT % 10 == 1 {
|
|
112
|
+
eprintln!("[SETUP-PORTAL] Selected option: {:?}", state.setup_portal.selected_option);
|
|
113
|
+
eprintln!("[SETUP-PORTAL] Has error: {}", has_error);
|
|
114
|
+
eprintln!("[SETUP-PORTAL] Chunks count: {}", chunks.len());
|
|
115
|
+
for (i, chunk) in chunks.iter().enumerate() {
|
|
116
|
+
eprintln!("[SETUP-PORTAL] chunks[{}]: x={}, y={}, w={}, h={}",
|
|
117
|
+
i, chunk.x, chunk.y, chunk.width, chunk.height);
|
|
118
|
+
}
|
|
119
|
+
// Check for overlaps
|
|
120
|
+
for i in 0..chunks.len() {
|
|
121
|
+
for j in (i+1)..chunks.len() {
|
|
122
|
+
let a = &chunks[i];
|
|
123
|
+
let b = &chunks[j];
|
|
124
|
+
if a.x < b.x + b.width && a.x + a.width > b.x &&
|
|
125
|
+
a.y < b.y + b.height && a.y + a.height > b.y {
|
|
126
|
+
eprintln!("[SETUP-PORTAL] ⚠️ OVERLAP: chunks[{}] overlaps chunks[{}]!", i, j);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
85
133
|
render_header(f, chunks[0], state);
|
|
86
134
|
render_description(f, chunks[1]);
|
|
87
135
|
render_content(f, chunks[2], state);
|
|
@@ -134,6 +182,16 @@ fn render_description(f: &mut Frame, area: Rect) {
|
|
|
134
182
|
}
|
|
135
183
|
|
|
136
184
|
fn render_content(f: &mut Frame, area: Rect, state: &AppState) {
|
|
185
|
+
// DEBUG: Log content area
|
|
186
|
+
static mut LOG_COUNT: u64 = 0;
|
|
187
|
+
unsafe {
|
|
188
|
+
LOG_COUNT += 1;
|
|
189
|
+
if LOG_COUNT % 10 == 1 {
|
|
190
|
+
eprintln!("[SETUP-PORTAL] render_content - area: x={}, y={}, w={}, h={}",
|
|
191
|
+
area.x, area.y, area.width, area.height);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
137
195
|
// Split content into two columns: Options (left) and Details (right)
|
|
138
196
|
let chunks = Layout::default()
|
|
139
197
|
.direction(Direction::Horizontal)
|
|
@@ -143,11 +201,34 @@ fn render_content(f: &mut Frame, area: Rect, state: &AppState) {
|
|
|
143
201
|
])
|
|
144
202
|
.split(area);
|
|
145
203
|
|
|
204
|
+
// DEBUG: Log content chunks
|
|
205
|
+
unsafe {
|
|
206
|
+
if LOG_COUNT % 10 == 1 {
|
|
207
|
+
eprintln!("[SETUP-PORTAL] Content chunks[0] (options): x={}, y={}, w={}, h={}",
|
|
208
|
+
chunks[0].x, chunks[0].y, chunks[0].width, chunks[0].height);
|
|
209
|
+
eprintln!("[SETUP-PORTAL] Content chunks[1] (details): x={}, y={}, w={}, h={}",
|
|
210
|
+
chunks[1].x, chunks[1].y, chunks[1].width, chunks[1].height);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
146
214
|
render_options_list(f, chunks[0], state);
|
|
147
215
|
render_option_details(f, chunks[1], state);
|
|
148
216
|
}
|
|
149
217
|
|
|
150
218
|
fn render_options_list(f: &mut Frame, area: Rect, state: &AppState) {
|
|
219
|
+
// DEBUG: Log options list rendering
|
|
220
|
+
static mut LOG_COUNT: u64 = 0;
|
|
221
|
+
unsafe {
|
|
222
|
+
LOG_COUNT += 1;
|
|
223
|
+
if LOG_COUNT % 10 == 1 {
|
|
224
|
+
eprintln!("[SETUP-PORTAL] render_options_list - area: x={}, y={}, w={}, h={}",
|
|
225
|
+
area.x, area.y, area.width, area.height);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// CRITICAL: Clear the list area first to prevent duplication
|
|
230
|
+
f.render_widget(Clear, area);
|
|
231
|
+
|
|
151
232
|
let options = vec![
|
|
152
233
|
("Local Bundle", GatewayOption::LocalBundle),
|
|
153
234
|
("4Runr Server", GatewayOption::CloudServer),
|
|
@@ -159,6 +240,15 @@ fn render_options_list(f: &mut Frame, area: Rect, state: &AppState) {
|
|
|
159
240
|
.position(|(_, opt)| opt == &state.setup_portal.selected_option)
|
|
160
241
|
.unwrap_or(0);
|
|
161
242
|
|
|
243
|
+
// DEBUG: Log selected index
|
|
244
|
+
unsafe {
|
|
245
|
+
if LOG_COUNT % 10 == 1 {
|
|
246
|
+
eprintln!("[SETUP-PORTAL] Selected index: {}, option: {:?}",
|
|
247
|
+
selected_index, state.setup_portal.selected_option);
|
|
248
|
+
eprintln!("[SETUP-PORTAL] Options count: {}", options.len());
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
162
252
|
let items: Vec<ListItem> = options
|
|
163
253
|
.iter()
|
|
164
254
|
.map(|(name, option)| {
|
|
@@ -200,6 +290,20 @@ fn render_options_list(f: &mut Frame, area: Rect, state: &AppState) {
|
|
|
200
290
|
}
|
|
201
291
|
|
|
202
292
|
fn render_option_details(f: &mut Frame, area: Rect, state: &AppState) {
|
|
293
|
+
// DEBUG: Log details rendering
|
|
294
|
+
static mut LOG_COUNT: u64 = 0;
|
|
295
|
+
unsafe {
|
|
296
|
+
LOG_COUNT += 1;
|
|
297
|
+
if LOG_COUNT % 10 == 1 {
|
|
298
|
+
eprintln!("[SETUP-PORTAL] render_option_details - area: x={}, y={}, w={}, h={}",
|
|
299
|
+
area.x, area.y, area.width, area.height);
|
|
300
|
+
eprintln!("[SETUP-PORTAL] Selected option: {:?}", state.setup_portal.selected_option);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// CRITICAL: Clear the details area first to prevent content overlap/corruption
|
|
305
|
+
f.render_widget(Clear, area);
|
|
306
|
+
|
|
203
307
|
let selected_option = &state.setup_portal.selected_option;
|
|
204
308
|
let detection_result = &state.setup_portal.detection_result;
|
|
205
309
|
|
|
@@ -285,6 +389,18 @@ fn render_option_details(f: &mut Frame, area: Rect, state: &AppState) {
|
|
|
285
389
|
),
|
|
286
390
|
};
|
|
287
391
|
|
|
392
|
+
// DEBUG: Log what content is being rendered
|
|
393
|
+
unsafe {
|
|
394
|
+
if LOG_COUNT % 10 == 1 {
|
|
395
|
+
eprintln!("[SETUP-PORTAL] Title: '{}'", title);
|
|
396
|
+
eprintln!("[SETUP-PORTAL] Content lines: {}", content.len());
|
|
397
|
+
for (i, line) in content.iter().take(3).enumerate() {
|
|
398
|
+
let preview = format!("{:?}", line).chars().take(50).collect::<String>();
|
|
399
|
+
eprintln!("[SETUP-PORTAL] content[{}]: {}", i, preview);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
288
404
|
// Use Block to properly contain and clear the area
|
|
289
405
|
let block = Block::default()
|
|
290
406
|
.title(title)
|
|
@@ -292,11 +408,58 @@ fn render_option_details(f: &mut Frame, area: Rect, state: &AppState) {
|
|
|
292
408
|
.border_style(Style::default().fg(TEXT_DIM))
|
|
293
409
|
.style(Style::default().bg(BG_PANEL));
|
|
294
410
|
|
|
411
|
+
// CRITICAL: Render block first to clear and establish boundaries
|
|
412
|
+
let inner = block.inner(area);
|
|
413
|
+
|
|
414
|
+
// CRITICAL: Validate inner area to prevent index out of bounds panic
|
|
415
|
+
// Ratatui panics if area coordinates exceed terminal buffer
|
|
416
|
+
let terminal_size = f.size();
|
|
417
|
+
let safe_inner = Rect {
|
|
418
|
+
x: inner.x.min(terminal_size.width.saturating_sub(1)),
|
|
419
|
+
y: inner.y.min(terminal_size.height.saturating_sub(1)),
|
|
420
|
+
width: inner.width.min(terminal_size.width.saturating_sub(inner.x)),
|
|
421
|
+
height: inner.height.min(terminal_size.height.saturating_sub(inner.y)),
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
// DEBUG: Log block inner area
|
|
425
|
+
unsafe {
|
|
426
|
+
if LOG_COUNT % 10 == 1 {
|
|
427
|
+
eprintln!("[SETUP-PORTAL] Block outer: x={}, y={}, w={}, h={}",
|
|
428
|
+
area.x, area.y, area.width, area.height);
|
|
429
|
+
eprintln!("[SETUP-PORTAL] Block inner (original): x={}, y={}, w={}, h={}",
|
|
430
|
+
inner.x, inner.y, inner.width, inner.height);
|
|
431
|
+
eprintln!("[SETUP-PORTAL] Terminal size: {}x{}", terminal_size.width, terminal_size.height);
|
|
432
|
+
eprintln!("[SETUP-PORTAL] Block inner (safe): x={}, y={}, w={}, h={}",
|
|
433
|
+
safe_inner.x, safe_inner.y, safe_inner.width, safe_inner.height);
|
|
434
|
+
|
|
435
|
+
// Warn if we had to clamp
|
|
436
|
+
if inner.x != safe_inner.x || inner.y != safe_inner.y ||
|
|
437
|
+
inner.width != safe_inner.width || inner.height != safe_inner.height {
|
|
438
|
+
eprintln!("[SETUP-PORTAL] ⚠️ WARNING: Inner area was clamped to fit terminal bounds!");
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Validate area before rendering block
|
|
444
|
+
if area.width == 0 || area.height == 0 ||
|
|
445
|
+
area.x >= terminal_size.width || area.y >= terminal_size.height {
|
|
446
|
+
eprintln!("[SETUP-PORTAL] ⚠️ ERROR: Invalid block area, skipping render");
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
f.render_widget(block, area);
|
|
451
|
+
|
|
452
|
+
// Validate safe_inner before rendering paragraph
|
|
453
|
+
if safe_inner.width == 0 || safe_inner.height == 0 {
|
|
454
|
+
eprintln!("[SETUP-PORTAL] ⚠️ ERROR: Invalid inner area (zero size), skipping paragraph render");
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Then render paragraph content only within the safe inner area
|
|
295
459
|
let paragraph = Paragraph::new(content)
|
|
296
|
-
.block(block)
|
|
297
460
|
.wrap(Wrap { trim: true });
|
|
298
461
|
|
|
299
|
-
f.render_widget(paragraph,
|
|
462
|
+
f.render_widget(paragraph, safe_inner);
|
|
300
463
|
}
|
|
301
464
|
|
|
302
465
|
fn render_error_box(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.
|
|
3
|
+
"version": "2.9.41",
|
|
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.41: Fixed Setup Portal index out of bounds panic - added bounds validation and clamping for safe area rendering. Added comprehensive debug logging for Setup Portal rendering diagnostics. v2.9.40: Fixed Setup Portal rendering. ⚠️ Pre-MVP / Development Phase",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"4runr": "dist/index.js",
|