4runr-os 2.10.39 → 2.10.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.
- package/apps/gateway/dist/apps/gateway/src/index.js +14 -4
- package/apps/gateway/dist/apps/gateway/src/index.js.map +1 -1
- package/apps/gateway/dist/apps/gateway/src/metrics/monitoring-detail.d.ts +18 -0
- package/apps/gateway/dist/apps/gateway/src/metrics/monitoring-detail.d.ts.map +1 -0
- package/apps/gateway/dist/apps/gateway/src/metrics/monitoring-detail.js +117 -0
- package/apps/gateway/dist/apps/gateway/src/metrics/monitoring-detail.js.map +1 -0
- package/apps/gateway/dist/apps/gateway/src/middleware/log-capture.d.ts +2 -0
- package/apps/gateway/dist/apps/gateway/src/middleware/log-capture.d.ts.map +1 -0
- package/apps/gateway/dist/apps/gateway/src/middleware/log-capture.js +54 -0
- package/apps/gateway/dist/apps/gateway/src/middleware/log-capture.js.map +1 -0
- package/apps/gateway/dist/apps/gateway/src/routes/monitoring.d.ts +15 -0
- package/apps/gateway/dist/apps/gateway/src/routes/monitoring.d.ts.map +1 -0
- package/apps/gateway/dist/apps/gateway/src/routes/monitoring.js +164 -0
- package/apps/gateway/dist/apps/gateway/src/routes/monitoring.js.map +1 -0
- package/apps/gateway/package-lock.json +204 -353
- package/apps/gateway/src/index.ts +27 -8
- package/apps/gateway/src/metrics/monitoring-detail.ts +162 -0
- package/apps/gateway/src/middleware/log-capture.ts +70 -0
- package/apps/gateway/src/routes/monitoring.ts +298 -0
- package/dist/gateway-client.d.ts +2 -0
- package/dist/gateway-client.d.ts.map +1 -1
- package/dist/gateway-client.js +22 -0
- package/dist/gateway-client.js.map +1 -1
- package/dist/tui-handlers.js +498 -0
- package/dist/tui-handlers.js.map +1 -1
- package/mk3-tui/src/app/render_scheduler.rs +111 -112
- package/mk3-tui/src/app.rs +1078 -295
- package/mk3-tui/src/debug_log.rs +131 -124
- package/mk3-tui/src/io/mod.rs +63 -66
- package/mk3-tui/src/io/protocol.rs +14 -15
- package/mk3-tui/src/io/stdio.rs +31 -32
- package/mk3-tui/src/io/ws.rs +25 -32
- package/mk3-tui/src/main.rs +774 -212
- package/mk3-tui/src/monitoring/mod.rs +428 -0
- package/mk3-tui/src/screens/mod.rs +53 -39
- package/mk3-tui/src/storage/cache.rs +221 -224
- package/mk3-tui/src/storage/mod.rs +5 -6
- package/mk3-tui/src/ui/agent_builder.rs +1148 -922
- package/mk3-tui/src/ui/agent_list.rs +344 -295
- package/mk3-tui/src/ui/boot.rs +145 -148
- package/mk3-tui/src/ui/connection_portal.rs +121 -98
- package/mk3-tui/src/ui/help.rs +340 -284
- package/mk3-tui/src/ui/layout.rs +966 -803
- package/mk3-tui/src/ui/mod.rs +1 -1
- package/mk3-tui/src/ui/portal_monitoring.rs +1027 -147
- package/mk3-tui/src/ui/run_manager.rs +784 -764
- package/mk3-tui/src/ui/safe_viewport.rs +236 -235
- package/mk3-tui/src/ui/settings.rs +414 -362
- package/mk3-tui/src/ui/setup_portal.rs +158 -101
- package/mk3-tui/src/websocket.rs +315 -308
- package/package.json +2 -2
|
@@ -1,12 +1,11 @@
|
|
|
1
|
+
use crate::app::{AppState, GatewayOption, OperationMode};
|
|
1
2
|
/// Setup Portal Screen - Clean Architecture
|
|
2
3
|
/// Gateway connection setup wizard with three options:
|
|
3
4
|
/// - Local Bundle (localhost:3001)
|
|
4
5
|
/// - 4Runr Server (cloud, coming soon)
|
|
5
6
|
/// - Custom URL (user's own gateway)
|
|
6
|
-
|
|
7
7
|
use ratatui::prelude::*;
|
|
8
|
-
use ratatui::widgets::{Block, Borders,
|
|
9
|
-
use crate::app::{AppState, GatewayOption, OperationMode};
|
|
8
|
+
use ratatui::widgets::{Block, Borders, Clear, List, ListItem, Paragraph, Wrap};
|
|
10
9
|
|
|
11
10
|
// === BRAND COLORS ===
|
|
12
11
|
const BRAND_PURPLE: Color = Color::Rgb(138, 43, 226);
|
|
@@ -23,49 +22,54 @@ const ERROR_RED: Color = Color::Rgb(255, 69, 69);
|
|
|
23
22
|
/// This is called from main.rs after terminal.clear() and autoresize()
|
|
24
23
|
pub fn render(f: &mut Frame, state: &mut AppState) {
|
|
25
24
|
let area = f.size();
|
|
26
|
-
|
|
25
|
+
|
|
27
26
|
// Validate terminal size
|
|
28
27
|
if area.width == 0 || area.height == 0 {
|
|
29
28
|
return; // Invalid terminal state, skip frame
|
|
30
29
|
}
|
|
31
|
-
|
|
30
|
+
|
|
32
31
|
if area.width < 85 || area.height < 25 {
|
|
33
32
|
render_too_small(f, area);
|
|
34
33
|
return;
|
|
35
34
|
}
|
|
36
|
-
|
|
35
|
+
|
|
37
36
|
// Full-screen clear (already done in main.rs, but ensure it's clean)
|
|
38
37
|
f.render_widget(Clear, area);
|
|
39
|
-
|
|
38
|
+
|
|
40
39
|
// Background
|
|
41
40
|
let bg = Block::default().style(Style::default().bg(BG_PANEL));
|
|
42
41
|
f.render_widget(bg, area);
|
|
43
|
-
|
|
42
|
+
|
|
44
43
|
// Center portal dialog
|
|
45
44
|
let portal_width = area.width.min(110).max(85);
|
|
46
45
|
let portal_height = area.height.min(38).max(25);
|
|
47
46
|
let portal_x = area.width.saturating_sub(portal_width) / 2;
|
|
48
47
|
let portal_y = area.height.saturating_sub(portal_height) / 2;
|
|
49
|
-
|
|
48
|
+
|
|
50
49
|
let portal_rect = Rect {
|
|
51
50
|
x: portal_x,
|
|
52
51
|
y: portal_y,
|
|
53
52
|
width: portal_width,
|
|
54
53
|
height: portal_height,
|
|
55
54
|
};
|
|
56
|
-
|
|
57
|
-
let show_link_banner =
|
|
55
|
+
|
|
56
|
+
let show_link_banner =
|
|
57
|
+
matches!(state.operation_mode, OperationMode::Connected) && state.gateway_url.is_some();
|
|
58
58
|
// Layout: Header | Description | Content | Error (optional) | Actions
|
|
59
|
-
let sections = build_layout(
|
|
60
|
-
|
|
59
|
+
let sections = build_layout(
|
|
60
|
+
portal_rect,
|
|
61
|
+
state.setup_portal.error.is_some(),
|
|
62
|
+
show_link_banner,
|
|
63
|
+
);
|
|
64
|
+
|
|
61
65
|
render_header(f, sections.header, state);
|
|
62
66
|
render_description(f, sections.description, state);
|
|
63
67
|
render_content(f, sections.content, state);
|
|
64
|
-
|
|
68
|
+
|
|
65
69
|
if let Some(error_rect) = sections.error {
|
|
66
70
|
render_error_box(f, error_rect, state);
|
|
67
71
|
}
|
|
68
|
-
|
|
72
|
+
|
|
69
73
|
render_actions(f, sections.actions, state);
|
|
70
74
|
}
|
|
71
75
|
|
|
@@ -81,30 +85,30 @@ struct PortalLayout {
|
|
|
81
85
|
/// Build vertical layout with dynamic error section
|
|
82
86
|
fn build_layout(area: Rect, has_error: bool, show_link_banner: bool) -> PortalLayout {
|
|
83
87
|
use ratatui::layout::{Constraint, Direction, Layout};
|
|
84
|
-
|
|
88
|
+
|
|
85
89
|
let desc_h = if show_link_banner { 5u16 } else { 3u16 };
|
|
86
90
|
let constraints = if has_error {
|
|
87
91
|
vec![
|
|
88
|
-
Constraint::Length(3),
|
|
92
|
+
Constraint::Length(3), // Header
|
|
89
93
|
Constraint::Length(desc_h), // Description (+ linked banner)
|
|
90
|
-
Constraint::Min(11),
|
|
91
|
-
Constraint::Length(6),
|
|
92
|
-
Constraint::Length(3),
|
|
94
|
+
Constraint::Min(11), // Content
|
|
95
|
+
Constraint::Length(6), // Error
|
|
96
|
+
Constraint::Length(3), // Actions
|
|
93
97
|
]
|
|
94
98
|
} else {
|
|
95
99
|
vec![
|
|
96
|
-
Constraint::Length(3),
|
|
100
|
+
Constraint::Length(3), // Header
|
|
97
101
|
Constraint::Length(desc_h), // Description
|
|
98
|
-
Constraint::Min(13),
|
|
99
|
-
Constraint::Length(3),
|
|
102
|
+
Constraint::Min(13), // Content
|
|
103
|
+
Constraint::Length(3), // Actions
|
|
100
104
|
]
|
|
101
105
|
};
|
|
102
|
-
|
|
106
|
+
|
|
103
107
|
let chunks = Layout::default()
|
|
104
108
|
.direction(Direction::Vertical)
|
|
105
109
|
.constraints(constraints)
|
|
106
110
|
.split(area);
|
|
107
|
-
|
|
111
|
+
|
|
108
112
|
if has_error {
|
|
109
113
|
PortalLayout {
|
|
110
114
|
header: chunks[0],
|
|
@@ -140,19 +144,19 @@ fn render_header(f: &mut Frame, area: Rect, state: &AppState) {
|
|
|
140
144
|
} else {
|
|
141
145
|
" ⚙ Gateway Setup "
|
|
142
146
|
};
|
|
143
|
-
|
|
147
|
+
|
|
144
148
|
let border_color = if state.setup_portal.error.is_some() {
|
|
145
149
|
ERROR_RED
|
|
146
150
|
} else {
|
|
147
151
|
BRAND_PURPLE
|
|
148
152
|
};
|
|
149
|
-
|
|
153
|
+
|
|
150
154
|
let block = Block::default()
|
|
151
155
|
.title(title)
|
|
152
156
|
.borders(Borders::ALL)
|
|
153
157
|
.border_style(Style::default().fg(border_color).bold())
|
|
154
158
|
.style(Style::default().bg(BG_PANEL));
|
|
155
|
-
|
|
159
|
+
|
|
156
160
|
f.render_widget(block, area);
|
|
157
161
|
}
|
|
158
162
|
|
|
@@ -160,10 +164,10 @@ fn render_description(f: &mut Frame, area: Rect, state: &AppState) {
|
|
|
160
164
|
let block = Block::default()
|
|
161
165
|
.borders(Borders::LEFT | Borders::RIGHT)
|
|
162
166
|
.style(Style::default().bg(BG_PANEL));
|
|
163
|
-
|
|
167
|
+
|
|
164
168
|
let inner = block.inner(area);
|
|
165
169
|
f.render_widget(block, area);
|
|
166
|
-
|
|
170
|
+
|
|
167
171
|
let mut s = String::from("Choose how you want to connect to a Gateway server.");
|
|
168
172
|
if matches!(state.operation_mode, OperationMode::Connected) {
|
|
169
173
|
if let Some(ref u) = state.gateway_url {
|
|
@@ -177,22 +181,19 @@ fn render_description(f: &mut Frame, area: Rect, state: &AppState) {
|
|
|
177
181
|
.style(Style::default().fg(TEXT_DIM).bg(BG_PANEL))
|
|
178
182
|
.wrap(Wrap { trim: true })
|
|
179
183
|
.alignment(ratatui::layout::Alignment::Center);
|
|
180
|
-
|
|
184
|
+
|
|
181
185
|
f.render_widget(text, inner);
|
|
182
186
|
}
|
|
183
187
|
|
|
184
188
|
fn render_content(f: &mut Frame, area: Rect, state: &mut AppState) {
|
|
185
189
|
use ratatui::layout::{Constraint, Direction, Layout};
|
|
186
|
-
|
|
190
|
+
|
|
187
191
|
let left_w = (area.width.saturating_mul(38) / 100).clamp(28, 44);
|
|
188
192
|
let chunks = Layout::default()
|
|
189
193
|
.direction(Direction::Horizontal)
|
|
190
|
-
.constraints([
|
|
191
|
-
Constraint::Length(left_w),
|
|
192
|
-
Constraint::Min(22),
|
|
193
|
-
])
|
|
194
|
+
.constraints([Constraint::Length(left_w), Constraint::Min(22)])
|
|
194
195
|
.split(area);
|
|
195
|
-
|
|
196
|
+
|
|
196
197
|
render_options_list(f, chunks[0], state);
|
|
197
198
|
render_details_panel(f, chunks[1], state);
|
|
198
199
|
}
|
|
@@ -200,15 +201,15 @@ fn render_content(f: &mut Frame, area: Rect, state: &mut AppState) {
|
|
|
200
201
|
fn render_options_list(f: &mut Frame, area: Rect, state: &mut AppState) {
|
|
201
202
|
// Clear to prevent artifacts
|
|
202
203
|
f.render_widget(Clear, area);
|
|
203
|
-
|
|
204
|
+
|
|
204
205
|
let options = [
|
|
205
206
|
("Local Bundle", GatewayOption::LocalBundle),
|
|
206
207
|
("4Runr Server", GatewayOption::CloudServer),
|
|
207
208
|
("Custom URL", GatewayOption::CustomUrl),
|
|
208
209
|
];
|
|
209
|
-
|
|
210
|
+
|
|
210
211
|
let selected = &state.setup_portal.selected_option;
|
|
211
|
-
|
|
212
|
+
|
|
212
213
|
let items: Vec<ListItem> = options
|
|
213
214
|
.iter()
|
|
214
215
|
.map(|(name, option)| {
|
|
@@ -218,28 +219,28 @@ fn render_options_list(f: &mut Frame, area: Rect, state: &mut AppState) {
|
|
|
218
219
|
} else {
|
|
219
220
|
Style::default().fg(TEXT_MUTED)
|
|
220
221
|
};
|
|
221
|
-
|
|
222
|
+
|
|
222
223
|
let icon = get_option_icon(option);
|
|
223
224
|
let prefix = if is_selected { "► " } else { " " };
|
|
224
225
|
let text = format!("{}{} {}", prefix, icon, name);
|
|
225
|
-
|
|
226
|
+
|
|
226
227
|
ListItem::new(text).style(style)
|
|
227
228
|
})
|
|
228
229
|
.collect();
|
|
229
|
-
|
|
230
|
+
|
|
230
231
|
let block = Block::default()
|
|
231
232
|
.borders(Borders::ALL)
|
|
232
233
|
.title(" Gateway Options ")
|
|
233
234
|
.border_style(Style::default().fg(CYBER_CYAN))
|
|
234
235
|
.style(Style::default().bg(BG_PANEL));
|
|
235
|
-
|
|
236
|
+
|
|
236
237
|
let inner = block.inner(area);
|
|
237
238
|
f.render_widget(block, area);
|
|
238
|
-
|
|
239
|
+
|
|
239
240
|
// Background fill
|
|
240
241
|
let fill = Block::default().style(Style::default().bg(BG_PANEL));
|
|
241
242
|
f.render_widget(fill, inner);
|
|
242
|
-
|
|
243
|
+
|
|
243
244
|
// Stateless list (selection shown via prefix and color)
|
|
244
245
|
let list = List::new(items).style(Style::default().bg(BG_PANEL));
|
|
245
246
|
f.render_widget(list, inner);
|
|
@@ -248,40 +249,40 @@ fn render_options_list(f: &mut Frame, area: Rect, state: &mut AppState) {
|
|
|
248
249
|
fn render_details_panel(f: &mut Frame, area: Rect, state: &AppState) {
|
|
249
250
|
// Clear to prevent ghost text
|
|
250
251
|
f.render_widget(Clear, area);
|
|
251
|
-
|
|
252
|
+
|
|
252
253
|
let selected = &state.setup_portal.selected_option;
|
|
253
254
|
let detection = &state.setup_portal.detection_result;
|
|
254
|
-
|
|
255
|
+
|
|
255
256
|
let (title, lines) = build_details_content(selected, detection);
|
|
256
|
-
|
|
257
|
+
|
|
257
258
|
let block = Block::default()
|
|
258
259
|
.title(title)
|
|
259
260
|
.borders(Borders::ALL)
|
|
260
261
|
.border_style(Style::default().fg(TEXT_DIM))
|
|
261
262
|
.style(Style::default().bg(BG_PANEL));
|
|
262
|
-
|
|
263
|
+
|
|
263
264
|
let inner = block.inner(area);
|
|
264
265
|
f.render_widget(block, area);
|
|
265
|
-
|
|
266
|
+
|
|
266
267
|
// Validate inner area
|
|
267
268
|
if inner.width == 0 || inner.height == 0 {
|
|
268
269
|
return;
|
|
269
270
|
}
|
|
270
|
-
|
|
271
|
+
|
|
271
272
|
// Pad content to fill height (prevents ghost text from previous selection)
|
|
272
273
|
let mut padded_lines = lines;
|
|
273
274
|
while padded_lines.len() < inner.height as usize {
|
|
274
275
|
padded_lines.push(Line::from(""));
|
|
275
276
|
}
|
|
276
|
-
|
|
277
|
+
|
|
277
278
|
// Background fill + content
|
|
278
279
|
let fill = Block::default().style(Style::default().bg(BG_PANEL));
|
|
279
280
|
f.render_widget(fill, inner);
|
|
280
|
-
|
|
281
|
+
|
|
281
282
|
let paragraph = Paragraph::new(padded_lines)
|
|
282
283
|
.wrap(Wrap { trim: true })
|
|
283
284
|
.style(Style::default().bg(BG_PANEL));
|
|
284
|
-
|
|
285
|
+
|
|
285
286
|
f.render_widget(paragraph, inner);
|
|
286
287
|
}
|
|
287
288
|
|
|
@@ -292,19 +293,19 @@ fn render_error_box(f: &mut Frame, area: Rect, state: &AppState) {
|
|
|
292
293
|
.borders(Borders::ALL)
|
|
293
294
|
.border_style(Style::default().fg(ERROR_RED).bold())
|
|
294
295
|
.style(Style::default().bg(BG_PANEL));
|
|
295
|
-
|
|
296
|
+
|
|
296
297
|
let inner = block.inner(area);
|
|
297
298
|
f.render_widget(block, area);
|
|
298
|
-
|
|
299
|
+
|
|
299
300
|
let text = vec![
|
|
300
301
|
Line::from(""),
|
|
301
302
|
Line::from(Span::styled(
|
|
302
303
|
format!(" {}", error),
|
|
303
|
-
Style::default().fg(TEXT_PRIMARY)
|
|
304
|
+
Style::default().fg(TEXT_PRIMARY),
|
|
304
305
|
)),
|
|
305
306
|
Line::from(""),
|
|
306
307
|
];
|
|
307
|
-
|
|
308
|
+
|
|
308
309
|
let paragraph = Paragraph::new(text).wrap(Wrap { trim: true });
|
|
309
310
|
f.render_widget(paragraph, inner);
|
|
310
311
|
}
|
|
@@ -315,10 +316,10 @@ fn render_actions(f: &mut Frame, area: Rect, state: &AppState) {
|
|
|
315
316
|
.borders(Borders::ALL)
|
|
316
317
|
.border_style(Style::default().fg(TEXT_DIM))
|
|
317
318
|
.style(Style::default().bg(BG_PANEL));
|
|
318
|
-
|
|
319
|
+
|
|
319
320
|
let inner = block.inner(area);
|
|
320
321
|
f.render_widget(block, area);
|
|
321
|
-
|
|
322
|
+
|
|
322
323
|
let text = if state.setup_portal.detecting && state.setup_portal.detection_result.is_none() {
|
|
323
324
|
Line::from(vec![Span::styled(
|
|
324
325
|
"Scanning Gateway options… (first load)",
|
|
@@ -342,45 +343,53 @@ fn render_actions(f: &mut Frame, area: Rect, state: &AppState) {
|
|
|
342
343
|
Span::styled(" Cancel", Style::default().fg(TEXT_DIM)),
|
|
343
344
|
])
|
|
344
345
|
};
|
|
345
|
-
|
|
346
|
+
|
|
346
347
|
let paragraph = Paragraph::new(text)
|
|
347
348
|
.alignment(ratatui::layout::Alignment::Center)
|
|
348
349
|
.style(Style::default().bg(BG_PANEL));
|
|
349
|
-
|
|
350
|
+
|
|
350
351
|
f.render_widget(paragraph, inner);
|
|
351
352
|
}
|
|
352
353
|
|
|
353
354
|
fn render_too_small(f: &mut Frame, area: Rect) {
|
|
354
355
|
f.render_widget(Clear, area);
|
|
355
|
-
|
|
356
|
+
|
|
356
357
|
let bg = Block::default().style(Style::default().bg(BG_PANEL));
|
|
357
358
|
f.render_widget(bg, area);
|
|
358
|
-
|
|
359
|
+
|
|
359
360
|
let msg = vec![
|
|
360
361
|
Line::from(""),
|
|
361
|
-
Line::from(Span::styled(
|
|
362
|
+
Line::from(Span::styled(
|
|
363
|
+
"Setup Portal",
|
|
364
|
+
Style::default().fg(BRAND_PURPLE).bold(),
|
|
365
|
+
)),
|
|
362
366
|
Line::from(""),
|
|
363
|
-
Line::from(Span::styled(
|
|
367
|
+
Line::from(Span::styled(
|
|
368
|
+
"Terminal too small",
|
|
369
|
+
Style::default().fg(ERROR_RED),
|
|
370
|
+
)),
|
|
364
371
|
Line::from(""),
|
|
365
372
|
Line::from(Span::styled(
|
|
366
373
|
format!("Current: {}x{}", area.width, area.height),
|
|
367
|
-
Style::default().fg(TEXT_DIM)
|
|
374
|
+
Style::default().fg(TEXT_DIM),
|
|
368
375
|
)),
|
|
369
376
|
Line::from(Span::styled(
|
|
370
377
|
"Required: 85x25",
|
|
371
|
-
Style::default().fg(TEXT_PRIMARY)
|
|
378
|
+
Style::default().fg(TEXT_PRIMARY),
|
|
372
379
|
)),
|
|
373
380
|
];
|
|
374
|
-
|
|
375
|
-
let paragraph = Paragraph::new(msg)
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
381
|
+
|
|
382
|
+
let paragraph = Paragraph::new(msg).alignment(ratatui::layout::Alignment::Center);
|
|
383
|
+
|
|
384
|
+
f.render_widget(
|
|
385
|
+
paragraph,
|
|
386
|
+
Rect {
|
|
387
|
+
x: area.x,
|
|
388
|
+
y: area.y + (area.height / 3),
|
|
389
|
+
width: area.width,
|
|
390
|
+
height: area.height / 3,
|
|
391
|
+
},
|
|
392
|
+
);
|
|
384
393
|
}
|
|
385
394
|
|
|
386
395
|
// === HELPER FUNCTIONS ===
|
|
@@ -395,7 +404,7 @@ fn get_option_icon(option: &GatewayOption) -> &'static str {
|
|
|
395
404
|
|
|
396
405
|
fn build_details_content(
|
|
397
406
|
option: &GatewayOption,
|
|
398
|
-
detection: &Option<crate::app::DetectionResult
|
|
407
|
+
detection: &Option<crate::app::DetectionResult>,
|
|
399
408
|
) -> (&'static str, Vec<Line<'static>>) {
|
|
400
409
|
match option {
|
|
401
410
|
GatewayOption::LocalBundle => build_local_bundle_details(detection),
|
|
@@ -405,15 +414,21 @@ fn build_details_content(
|
|
|
405
414
|
}
|
|
406
415
|
|
|
407
416
|
fn build_local_bundle_details(
|
|
408
|
-
detection: &Option<crate::app::DetectionResult
|
|
417
|
+
detection: &Option<crate::app::DetectionResult>,
|
|
409
418
|
) -> (&'static str, Vec<Line<'static>>) {
|
|
410
419
|
let mut lines = vec![
|
|
411
420
|
Line::from(""),
|
|
412
|
-
Line::from(Span::styled(
|
|
413
|
-
|
|
421
|
+
Line::from(Span::styled(
|
|
422
|
+
"Local Bundle Gateway",
|
|
423
|
+
Style::default().fg(NEON_GREEN).bold(),
|
|
424
|
+
)),
|
|
425
|
+
Line::from(Span::styled(
|
|
426
|
+
"Runs on your machine",
|
|
427
|
+
Style::default().fg(TEXT_DIM),
|
|
428
|
+
)),
|
|
414
429
|
Line::from(""),
|
|
415
430
|
];
|
|
416
|
-
|
|
431
|
+
|
|
417
432
|
if let Some(result) = detection {
|
|
418
433
|
if result.local_bundle.running {
|
|
419
434
|
lines.extend(vec![
|
|
@@ -423,7 +438,10 @@ fn build_local_bundle_details(
|
|
|
423
438
|
Span::styled("localhost:3001", Style::default().fg(CYBER_CYAN)),
|
|
424
439
|
]),
|
|
425
440
|
Line::from(""),
|
|
426
|
-
Line::from(Span::styled(
|
|
441
|
+
Line::from(Span::styled(
|
|
442
|
+
"Press Enter to connect",
|
|
443
|
+
Style::default().fg(CYBER_CYAN),
|
|
444
|
+
)),
|
|
427
445
|
]);
|
|
428
446
|
} else if result.local_bundle.available {
|
|
429
447
|
lines.extend(vec![
|
|
@@ -433,9 +451,15 @@ fn build_local_bundle_details(
|
|
|
433
451
|
]),
|
|
434
452
|
Line::from(""),
|
|
435
453
|
Line::from(Span::styled("Start with:", Style::default().fg(TEXT_DIM))),
|
|
436
|
-
Line::from(Span::styled(
|
|
454
|
+
Line::from(Span::styled(
|
|
455
|
+
" cd apps/gateway && pnpm start",
|
|
456
|
+
Style::default().fg(NEON_GREEN),
|
|
457
|
+
)),
|
|
437
458
|
Line::from(""),
|
|
438
|
-
Line::from(Span::styled(
|
|
459
|
+
Line::from(Span::styled(
|
|
460
|
+
"Then press F2 to reconnect",
|
|
461
|
+
Style::default().fg(CYBER_CYAN),
|
|
462
|
+
)),
|
|
439
463
|
]);
|
|
440
464
|
} else {
|
|
441
465
|
lines.extend(vec![
|
|
@@ -444,47 +468,80 @@ fn build_local_bundle_details(
|
|
|
444
468
|
Span::styled("Bundle not found", Style::default().fg(ERROR_RED)),
|
|
445
469
|
]),
|
|
446
470
|
Line::from(""),
|
|
447
|
-
Line::from(Span::styled(
|
|
471
|
+
Line::from(Span::styled(
|
|
472
|
+
"Try 4Runr Server or Custom URL",
|
|
473
|
+
Style::default().fg(TEXT_DIM),
|
|
474
|
+
)),
|
|
448
475
|
]);
|
|
449
476
|
}
|
|
450
477
|
} else {
|
|
451
|
-
lines.push(Line::from(Span::styled(
|
|
478
|
+
lines.push(Line::from(Span::styled(
|
|
479
|
+
"Press Enter to connect",
|
|
480
|
+
Style::default().fg(CYBER_CYAN),
|
|
481
|
+
)));
|
|
452
482
|
}
|
|
453
|
-
|
|
483
|
+
|
|
454
484
|
(" [■] Local Bundle ", lines)
|
|
455
485
|
}
|
|
456
486
|
|
|
457
487
|
fn build_cloud_server_details() -> (&'static str, Vec<Line<'static>>) {
|
|
458
488
|
let lines = vec![
|
|
459
489
|
Line::from(""),
|
|
460
|
-
Line::from(Span::styled(
|
|
461
|
-
|
|
490
|
+
Line::from(Span::styled(
|
|
491
|
+
"4Runr Cloud Server",
|
|
492
|
+
Style::default().fg(NEON_GREEN).bold(),
|
|
493
|
+
)),
|
|
494
|
+
Line::from(Span::styled(
|
|
495
|
+
"Managed cloud hosting",
|
|
496
|
+
Style::default().fg(TEXT_DIM),
|
|
497
|
+
)),
|
|
462
498
|
Line::from(""),
|
|
463
499
|
Line::from(vec![
|
|
464
500
|
Span::styled("[!] ", Style::default().fg(AMBER_WARN)),
|
|
465
501
|
Span::styled("Coming Soon", Style::default().fg(AMBER_WARN).bold()),
|
|
466
502
|
]),
|
|
467
503
|
Line::from(""),
|
|
468
|
-
Line::from(Span::styled(
|
|
504
|
+
Line::from(Span::styled(
|
|
505
|
+
"Service not yet available",
|
|
506
|
+
Style::default().fg(TEXT_DIM),
|
|
507
|
+
)),
|
|
469
508
|
Line::from(""),
|
|
470
|
-
Line::from(Span::styled(
|
|
509
|
+
Line::from(Span::styled(
|
|
510
|
+
"Use Local Bundle or Custom URL",
|
|
511
|
+
Style::default().fg(CYBER_CYAN),
|
|
512
|
+
)),
|
|
471
513
|
];
|
|
472
|
-
|
|
514
|
+
|
|
473
515
|
(" [≡] 4Runr Server ", lines)
|
|
474
516
|
}
|
|
475
517
|
|
|
476
518
|
fn build_custom_url_details() -> (&'static str, Vec<Line<'static>>) {
|
|
477
519
|
let lines = vec![
|
|
478
520
|
Line::from(""),
|
|
479
|
-
Line::from(Span::styled(
|
|
480
|
-
|
|
521
|
+
Line::from(Span::styled(
|
|
522
|
+
"Custom Gateway Server",
|
|
523
|
+
Style::default().fg(NEON_GREEN).bold(),
|
|
524
|
+
)),
|
|
525
|
+
Line::from(Span::styled(
|
|
526
|
+
"Your own deployment",
|
|
527
|
+
Style::default().fg(TEXT_DIM),
|
|
528
|
+
)),
|
|
481
529
|
Line::from(""),
|
|
482
530
|
Line::from(Span::styled("Requirements:", Style::default().fg(TEXT_DIM))),
|
|
483
|
-
Line::from(Span::styled(
|
|
484
|
-
|
|
531
|
+
Line::from(Span::styled(
|
|
532
|
+
" • Running Gateway server",
|
|
533
|
+
Style::default().fg(TEXT_DIM),
|
|
534
|
+
)),
|
|
535
|
+
Line::from(Span::styled(
|
|
536
|
+
" • Accessible URL (http:// or https://)",
|
|
537
|
+
Style::default().fg(TEXT_DIM),
|
|
538
|
+
)),
|
|
485
539
|
Line::from(""),
|
|
486
|
-
Line::from(Span::styled(
|
|
540
|
+
Line::from(Span::styled(
|
|
541
|
+
"Press Enter to continue",
|
|
542
|
+
Style::default().fg(CYBER_CYAN),
|
|
543
|
+
)),
|
|
487
544
|
];
|
|
488
|
-
|
|
545
|
+
|
|
489
546
|
(" [◆] Custom URL ", lines)
|
|
490
547
|
}
|