4runr-os 2.9.46 → 2.9.48
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.48";
|
|
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)),
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/// Wizard for setting up Gateway connection options
|
|
3
3
|
|
|
4
4
|
use ratatui::prelude::*;
|
|
5
|
-
use ratatui::widgets::{Block, Borders, Paragraph, Wrap, Clear, List, ListItem
|
|
5
|
+
use ratatui::widgets::{Block, Borders, Paragraph, Wrap, Clear, List, ListItem};
|
|
6
6
|
use crate::app::{AppState, GatewayOption};
|
|
7
7
|
|
|
8
8
|
// === 4RUNR BRAND COLORS (matching layout.rs) ===
|
|
@@ -18,8 +18,13 @@ 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: &mut AppState) {
|
|
21
|
-
//
|
|
22
|
-
|
|
21
|
+
// Per-frame render ID so we can correlate all logs from the same draw
|
|
22
|
+
static mut RENDER_ID: u64 = 0;
|
|
23
|
+
let render_id = unsafe {
|
|
24
|
+
RENDER_ID += 1;
|
|
25
|
+
RENDER_ID
|
|
26
|
+
};
|
|
27
|
+
eprintln!("[SETUP-PORTAL] R#{} START selected_option={:?}", render_id, state.setup_portal.selected_option);
|
|
23
28
|
|
|
24
29
|
static mut RENDER_COUNT: u64 = 0;
|
|
25
30
|
unsafe {
|
|
@@ -133,7 +138,7 @@ pub fn render(f: &mut Frame, state: &mut AppState) {
|
|
|
133
138
|
|
|
134
139
|
render_header(f, chunks[0], state);
|
|
135
140
|
render_description(f, chunks[1]);
|
|
136
|
-
render_content(f, chunks[2], state);
|
|
141
|
+
render_content(f, chunks[2], state, render_id);
|
|
137
142
|
|
|
138
143
|
if has_error {
|
|
139
144
|
render_error_box(f, chunks[3], state);
|
|
@@ -182,132 +187,79 @@ fn render_description(f: &mut Frame, area: Rect) {
|
|
|
182
187
|
f.render_widget(text, inner);
|
|
183
188
|
}
|
|
184
189
|
|
|
185
|
-
fn render_content(f: &mut Frame, area: Rect, state: &mut AppState) {
|
|
186
|
-
// DEBUG: Log content area
|
|
190
|
+
fn render_content(f: &mut Frame, area: Rect, state: &mut AppState, render_id: u64) {
|
|
187
191
|
static mut LOG_COUNT: u64 = 0;
|
|
188
192
|
unsafe {
|
|
189
193
|
LOG_COUNT += 1;
|
|
190
194
|
if LOG_COUNT % 10 == 1 {
|
|
191
|
-
eprintln!("[SETUP-PORTAL] render_content
|
|
192
|
-
area.x, area.y, area.width, area.height);
|
|
195
|
+
eprintln!("[SETUP-PORTAL] R#{} render_content area: {}x{}", render_id, area.width, area.height);
|
|
193
196
|
}
|
|
194
197
|
}
|
|
195
|
-
|
|
196
|
-
// Split content into two columns: Options (left) and Details (right)
|
|
198
|
+
|
|
197
199
|
let chunks = Layout::default()
|
|
198
200
|
.direction(Direction::Horizontal)
|
|
199
201
|
.constraints([
|
|
200
|
-
Constraint::Percentage(40),
|
|
201
|
-
Constraint::Percentage(60),
|
|
202
|
+
Constraint::Percentage(40),
|
|
203
|
+
Constraint::Percentage(60),
|
|
202
204
|
])
|
|
203
205
|
.split(area);
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
if LOG_COUNT % 10 == 1 {
|
|
208
|
-
eprintln!("[SETUP-PORTAL] Content chunks[0] (options): x={}, y={}, w={}, h={}",
|
|
209
|
-
chunks[0].x, chunks[0].y, chunks[0].width, chunks[0].height);
|
|
210
|
-
eprintln!("[SETUP-PORTAL] Content chunks[1] (details): x={}, y={}, w={}, h={}",
|
|
211
|
-
chunks[1].x, chunks[1].y, chunks[1].width, chunks[1].height);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
render_options_list(f, chunks[0], state);
|
|
216
|
-
render_option_details(f, chunks[1], state);
|
|
206
|
+
|
|
207
|
+
render_options_list(f, chunks[0], state, render_id);
|
|
208
|
+
render_option_details(f, chunks[1], state, render_id);
|
|
217
209
|
}
|
|
218
210
|
|
|
219
|
-
fn render_options_list(f: &mut Frame, area: Rect, state: &mut AppState) {
|
|
220
|
-
// DEBUG: Log options list rendering
|
|
221
|
-
static mut LOG_COUNT: u64 = 0;
|
|
222
|
-
unsafe {
|
|
223
|
-
LOG_COUNT += 1;
|
|
224
|
-
if LOG_COUNT % 10 == 1 {
|
|
225
|
-
eprintln!("[SETUP-PORTAL] render_options_list - area: x={}, y={}, w={}, h={}",
|
|
226
|
-
area.x, area.y, area.width, area.height);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// CRITICAL: Fill entire area with background so every cell is overwritten.
|
|
231
|
-
// Without this, List only paints rows with items; leftover rows show previous frame (duplication).
|
|
211
|
+
fn render_options_list(f: &mut Frame, area: Rect, state: &mut AppState, render_id: u64) {
|
|
232
212
|
let fill = Block::default().style(Style::default().bg(BG_PANEL));
|
|
233
213
|
f.render_widget(fill, area);
|
|
234
|
-
|
|
214
|
+
|
|
235
215
|
let options = vec![
|
|
236
216
|
("Local Bundle", GatewayOption::LocalBundle),
|
|
237
217
|
("4Runr Server", GatewayOption::CloudServer),
|
|
238
218
|
("Custom URL", GatewayOption::CustomUrl),
|
|
239
219
|
];
|
|
240
|
-
|
|
241
|
-
|
|
220
|
+
|
|
221
|
+
let selected_option = &state.setup_portal.selected_option;
|
|
242
222
|
let selected_index = options.iter()
|
|
243
|
-
.position(|(_, opt)| opt ==
|
|
223
|
+
.position(|(_, opt)| opt == selected_option)
|
|
244
224
|
.unwrap_or(0);
|
|
245
225
|
|
|
246
|
-
|
|
247
|
-
eprintln!("[SETUP-PORTAL] LIST selected_index={} selected_option={:?}", selected_index, state.setup_portal.selected_option);
|
|
226
|
+
eprintln!("[SETUP-PORTAL] R#{} LIST selected_index={} selected_option={:?}", render_id, selected_index, state.setup_portal.selected_option);
|
|
248
227
|
|
|
249
|
-
unsafe {
|
|
250
|
-
if LOG_COUNT % 10 == 1 {
|
|
251
|
-
eprintln!("[SETUP-PORTAL] Options count: {}", options.len());
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
228
|
let items: Vec<ListItem> = options
|
|
256
229
|
.iter()
|
|
257
230
|
.map(|(name, option)| {
|
|
258
|
-
let is_selected =
|
|
231
|
+
let is_selected = selected_option == option;
|
|
259
232
|
let style = if is_selected {
|
|
260
233
|
Style::default().fg(CYBER_CYAN).bold()
|
|
261
234
|
} else {
|
|
262
235
|
Style::default().fg(TEXT_MUTED)
|
|
263
236
|
};
|
|
264
|
-
|
|
265
237
|
let icon = match option {
|
|
266
238
|
GatewayOption::LocalBundle => "[■]",
|
|
267
239
|
GatewayOption::CloudServer => "[≡]",
|
|
268
240
|
GatewayOption::CustomUrl => "[◆]",
|
|
269
241
|
};
|
|
270
|
-
|
|
271
242
|
let prefix = if is_selected { "► " } else { " " };
|
|
272
243
|
let text = format!("{}{} {}", prefix, icon, name);
|
|
273
244
|
ListItem::new(text).style(style)
|
|
274
245
|
})
|
|
275
246
|
.collect();
|
|
276
|
-
|
|
277
|
-
// CRITICAL: Use the PERSISTED ListState from app state (not a new one each frame)
|
|
278
|
-
// This is required for Ratatui's stateful widget to maintain selection across frames
|
|
279
|
-
let list_state_selected = state.setup_portal.list_state.selected();
|
|
280
|
-
eprintln!("[SETUP-PORTAL] LIST About to render: list_state.selected()={:?} selected_index={} selected_option={:?}",
|
|
281
|
-
list_state_selected, selected_index, state.setup_portal.selected_option);
|
|
282
|
-
|
|
283
|
-
// Use Block to properly contain and clear the area
|
|
247
|
+
|
|
284
248
|
let block = Block::default()
|
|
285
249
|
.borders(Borders::ALL)
|
|
286
250
|
.title(" Gateway Options ")
|
|
287
251
|
.border_style(Style::default().fg(CYBER_CYAN))
|
|
288
252
|
.style(Style::default().bg(BG_PANEL));
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
// CRITICAL: Pass the mutable reference to the persisted ListState
|
|
295
|
-
f.render_stateful_widget(list, area, &mut state.setup_portal.list_state);
|
|
253
|
+
|
|
254
|
+
// STATELESS: Render list without ListState so only our item styling (► + color) shows selection.
|
|
255
|
+
// This removes any second source of highlight that could drift from selected_option.
|
|
256
|
+
let list = List::new(items).block(block);
|
|
257
|
+
f.render_widget(list, area);
|
|
296
258
|
}
|
|
297
259
|
|
|
298
|
-
fn render_option_details(f: &mut Frame, area: Rect, state: &AppState) {
|
|
299
|
-
|
|
300
|
-
eprintln!("[SETUP-PORTAL] DETAILS selected_option={:?}", state.setup_portal.selected_option);
|
|
260
|
+
fn render_option_details(f: &mut Frame, area: Rect, state: &AppState, render_id: u64) {
|
|
261
|
+
eprintln!("[SETUP-PORTAL] R#{} DETAILS selected_option={:?}", render_id, state.setup_portal.selected_option);
|
|
301
262
|
|
|
302
|
-
static mut LOG_COUNT: u64 = 0;
|
|
303
|
-
unsafe {
|
|
304
|
-
LOG_COUNT += 1;
|
|
305
|
-
if LOG_COUNT % 10 == 1 {
|
|
306
|
-
eprintln!("[SETUP-PORTAL] render_option_details - area: x={}, y={}, w={}, h={}",
|
|
307
|
-
area.x, area.y, area.width, area.height);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
263
|
// CRITICAL: Fill entire area with background so every cell is overwritten.
|
|
312
264
|
// Without this, Paragraph may not paint every cell; leftover shows previous option (corruption).
|
|
313
265
|
let fill = Block::default().style(Style::default().bg(BG_PANEL));
|
|
@@ -397,19 +349,9 @@ fn render_option_details(f: &mut Frame, area: Rect, state: &AppState) {
|
|
|
397
349
|
]
|
|
398
350
|
),
|
|
399
351
|
};
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
if LOG_COUNT % 10 == 1 {
|
|
404
|
-
eprintln!("[SETUP-PORTAL] Title: '{}'", title);
|
|
405
|
-
eprintln!("[SETUP-PORTAL] Content lines: {}", content.len());
|
|
406
|
-
for (i, line) in content.iter().take(3).enumerate() {
|
|
407
|
-
let preview = format!("{:?}", line).chars().take(50).collect::<String>();
|
|
408
|
-
eprintln!("[SETUP-PORTAL] content[{}]: {}", i, preview);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
|
|
352
|
+
|
|
353
|
+
eprintln!("[SETUP-PORTAL] R#{} DETAILS title='{}' lines={}", render_id, title.trim(), content.len());
|
|
354
|
+
|
|
413
355
|
// Use Block to properly contain and clear the area
|
|
414
356
|
let block = Block::default()
|
|
415
357
|
.title(title)
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "4runr-os",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.48",
|
|
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.48: Setup Portal - stateless list (single source of truth), per-frame R# debug. v2.9.47: ListState sync. ⚠️ Pre-MVP / Development Phase",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"4runr": "dist/index.js",
|