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.
Files changed (51) hide show
  1. package/apps/gateway/dist/apps/gateway/src/index.js +14 -4
  2. package/apps/gateway/dist/apps/gateway/src/index.js.map +1 -1
  3. package/apps/gateway/dist/apps/gateway/src/metrics/monitoring-detail.d.ts +18 -0
  4. package/apps/gateway/dist/apps/gateway/src/metrics/monitoring-detail.d.ts.map +1 -0
  5. package/apps/gateway/dist/apps/gateway/src/metrics/monitoring-detail.js +117 -0
  6. package/apps/gateway/dist/apps/gateway/src/metrics/monitoring-detail.js.map +1 -0
  7. package/apps/gateway/dist/apps/gateway/src/middleware/log-capture.d.ts +2 -0
  8. package/apps/gateway/dist/apps/gateway/src/middleware/log-capture.d.ts.map +1 -0
  9. package/apps/gateway/dist/apps/gateway/src/middleware/log-capture.js +54 -0
  10. package/apps/gateway/dist/apps/gateway/src/middleware/log-capture.js.map +1 -0
  11. package/apps/gateway/dist/apps/gateway/src/routes/monitoring.d.ts +15 -0
  12. package/apps/gateway/dist/apps/gateway/src/routes/monitoring.d.ts.map +1 -0
  13. package/apps/gateway/dist/apps/gateway/src/routes/monitoring.js +164 -0
  14. package/apps/gateway/dist/apps/gateway/src/routes/monitoring.js.map +1 -0
  15. package/apps/gateway/package-lock.json +204 -353
  16. package/apps/gateway/src/index.ts +27 -8
  17. package/apps/gateway/src/metrics/monitoring-detail.ts +162 -0
  18. package/apps/gateway/src/middleware/log-capture.ts +70 -0
  19. package/apps/gateway/src/routes/monitoring.ts +298 -0
  20. package/dist/gateway-client.d.ts +2 -0
  21. package/dist/gateway-client.d.ts.map +1 -1
  22. package/dist/gateway-client.js +22 -0
  23. package/dist/gateway-client.js.map +1 -1
  24. package/dist/tui-handlers.js +498 -0
  25. package/dist/tui-handlers.js.map +1 -1
  26. package/mk3-tui/src/app/render_scheduler.rs +111 -112
  27. package/mk3-tui/src/app.rs +1078 -295
  28. package/mk3-tui/src/debug_log.rs +131 -124
  29. package/mk3-tui/src/io/mod.rs +63 -66
  30. package/mk3-tui/src/io/protocol.rs +14 -15
  31. package/mk3-tui/src/io/stdio.rs +31 -32
  32. package/mk3-tui/src/io/ws.rs +25 -32
  33. package/mk3-tui/src/main.rs +774 -212
  34. package/mk3-tui/src/monitoring/mod.rs +428 -0
  35. package/mk3-tui/src/screens/mod.rs +53 -39
  36. package/mk3-tui/src/storage/cache.rs +221 -224
  37. package/mk3-tui/src/storage/mod.rs +5 -6
  38. package/mk3-tui/src/ui/agent_builder.rs +1148 -922
  39. package/mk3-tui/src/ui/agent_list.rs +344 -295
  40. package/mk3-tui/src/ui/boot.rs +145 -148
  41. package/mk3-tui/src/ui/connection_portal.rs +121 -98
  42. package/mk3-tui/src/ui/help.rs +340 -284
  43. package/mk3-tui/src/ui/layout.rs +966 -803
  44. package/mk3-tui/src/ui/mod.rs +1 -1
  45. package/mk3-tui/src/ui/portal_monitoring.rs +1027 -147
  46. package/mk3-tui/src/ui/run_manager.rs +784 -764
  47. package/mk3-tui/src/ui/safe_viewport.rs +236 -235
  48. package/mk3-tui/src/ui/settings.rs +414 -362
  49. package/mk3-tui/src/ui/setup_portal.rs +158 -101
  50. package/mk3-tui/src/websocket.rs +315 -308
  51. 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, Paragraph, Wrap, Clear, List, ListItem};
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 = matches!(state.operation_mode, OperationMode::Connected) && state.gateway_url.is_some();
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(portal_rect, state.setup_portal.error.is_some(), show_link_banner);
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), // Header
92
+ Constraint::Length(3), // Header
89
93
  Constraint::Length(desc_h), // Description (+ linked banner)
90
- Constraint::Min(11), // Content
91
- Constraint::Length(6), // Error
92
- Constraint::Length(3), // Actions
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), // Header
100
+ Constraint::Length(3), // Header
97
101
  Constraint::Length(desc_h), // Description
98
- Constraint::Min(13), // Content
99
- Constraint::Length(3), // Actions
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("Setup Portal", Style::default().fg(BRAND_PURPLE).bold())),
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("Terminal too small", Style::default().fg(ERROR_RED))),
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
- .alignment(ratatui::layout::Alignment::Center);
377
-
378
- f.render_widget(paragraph, Rect {
379
- x: area.x,
380
- y: area.y + (area.height / 3),
381
- width: area.width,
382
- height: area.height / 3,
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("Local Bundle Gateway", Style::default().fg(NEON_GREEN).bold())),
413
- Line::from(Span::styled("Runs on your machine", Style::default().fg(TEXT_DIM))),
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("Press Enter to connect", Style::default().fg(CYBER_CYAN))),
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(" cd apps/gateway && pnpm start", Style::default().fg(NEON_GREEN))),
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("Then press F2 to reconnect", Style::default().fg(CYBER_CYAN))),
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("Try 4Runr Server or Custom URL", Style::default().fg(TEXT_DIM))),
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("Press Enter to connect", Style::default().fg(CYBER_CYAN))));
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("4Runr Cloud Server", Style::default().fg(NEON_GREEN).bold())),
461
- Line::from(Span::styled("Managed cloud hosting", Style::default().fg(TEXT_DIM))),
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("Service not yet available", Style::default().fg(TEXT_DIM))),
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("Use Local Bundle or Custom URL", Style::default().fg(CYBER_CYAN))),
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("Custom Gateway Server", Style::default().fg(NEON_GREEN).bold())),
480
- Line::from(Span::styled("Your own deployment", Style::default().fg(TEXT_DIM))),
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(" • Running Gateway server", Style::default().fg(TEXT_DIM))),
484
- Line::from(Span::styled(" • Accessible URL (http:// or https://)", Style::default().fg(TEXT_DIM))),
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("Press Enter to continue", Style::default().fg(CYBER_CYAN))),
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
  }