4runr-os 2.6.4 → 2.6.6
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/mk3-tui/src/ui/agent_list.rs +293 -341
- package/mk3-tui/src/ui/help.rs +284 -211
- package/mk3-tui/src/ui/layout.rs +1 -1
- package/package.json +2 -2
|
@@ -1,341 +1,293 @@
|
|
|
1
|
-
/// Agent List Screen
|
|
2
|
-
/// View and manage AI agents
|
|
3
|
-
|
|
4
|
-
use ratatui::prelude::*;
|
|
5
|
-
use ratatui::widgets::{Block, Borders, Paragraph, Wrap, Table, Row, Cell};
|
|
6
|
-
use crate::app::{AppState, AgentInfo};
|
|
7
|
-
|
|
8
|
-
// === 4RUNR BRAND COLORS ===
|
|
9
|
-
const BRAND_PURPLE: Color = Color::Rgb(138, 43, 226);
|
|
10
|
-
const CYBER_CYAN: Color = Color::Rgb(0, 255, 255);
|
|
11
|
-
const NEON_GREEN: Color = Color::Rgb(57, 255, 20);
|
|
12
|
-
const TEXT_PRIMARY: Color = Color::Rgb(230, 230, 240);
|
|
13
|
-
const TEXT_DIM: Color = Color::Rgb(100, 100, 120);
|
|
14
|
-
const TEXT_MUTED: Color = Color::Rgb(60, 60, 80);
|
|
15
|
-
const BG_PANEL: Color = Color::Rgb(18, 18, 25);
|
|
16
|
-
|
|
17
|
-
/// Render agent list screen
|
|
18
|
-
pub fn render(f: &mut Frame, state: &AppState) {
|
|
19
|
-
let area = f.size();
|
|
20
|
-
|
|
21
|
-
if state.agent_list.detail_view.is_some() {
|
|
22
|
-
// Render detail popup
|
|
23
|
-
render_detail_popup(f, area, state);
|
|
24
|
-
} else {
|
|
25
|
-
// Render list view
|
|
26
|
-
render_list_view(f, area, state);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
fn render_list_view(f: &mut Frame, area: Rect, state: &AppState) {
|
|
31
|
-
use ratatui::layout::{Constraint, Direction, Layout};
|
|
32
|
-
|
|
33
|
-
// Split into header, content, footer
|
|
34
|
-
let chunks = Layout::default()
|
|
35
|
-
.direction(Direction::Vertical)
|
|
36
|
-
.constraints([
|
|
37
|
-
Constraint::Length(3), // Header
|
|
38
|
-
Constraint::Min(0), // Content
|
|
39
|
-
Constraint::Length(3), // Footer
|
|
40
|
-
])
|
|
41
|
-
.split(area);
|
|
42
|
-
|
|
43
|
-
// === HEADER ===
|
|
44
|
-
let agent_count = state.agent_list.agents.len();
|
|
45
|
-
let header_block = Block::default()
|
|
46
|
-
.title(format!(" 🤖 Agent List ", ))
|
|
47
|
-
.title_style(Style::default().fg(BRAND_PURPLE).add_modifier(Modifier::BOLD))
|
|
48
|
-
.borders(Borders::ALL)
|
|
49
|
-
.border_style(Style::default().fg(CYBER_CYAN))
|
|
50
|
-
.style(Style::default().bg(BG_PANEL));
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
"
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
.
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
let
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
// Render
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
let
|
|
196
|
-
.
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
.
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
let
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
detail_lines.push(Line::from(vec![
|
|
295
|
-
Span::styled(current_line.trim_end().to_string(), Style::default().fg(TEXT_DIM)),
|
|
296
|
-
]));
|
|
297
|
-
current_line = format!(" {}", word);
|
|
298
|
-
} else {
|
|
299
|
-
if current_line.len() > 2 {
|
|
300
|
-
current_line.push(' ');
|
|
301
|
-
}
|
|
302
|
-
current_line.push_str(word);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
if current_line.trim().len() > 0 {
|
|
306
|
-
detail_lines.push(Line::from(vec![
|
|
307
|
-
Span::styled(current_line.trim_end().to_string(), Style::default().fg(TEXT_DIM)),
|
|
308
|
-
]));
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// Render content
|
|
314
|
-
f.render_widget(
|
|
315
|
-
Paragraph::new(detail_lines)
|
|
316
|
-
.wrap(Wrap { trim: false }),
|
|
317
|
-
content_inner
|
|
318
|
-
);
|
|
319
|
-
|
|
320
|
-
// === FOOTER ===
|
|
321
|
-
let footer_text = Line::from(vec![
|
|
322
|
-
Span::styled("Press ", Style::default().fg(TEXT_DIM)),
|
|
323
|
-
Span::styled("ESC", Style::default().fg(NEON_GREEN).add_modifier(Modifier::BOLD)),
|
|
324
|
-
Span::styled(" to close", Style::default().fg(TEXT_DIM)),
|
|
325
|
-
]);
|
|
326
|
-
|
|
327
|
-
f.render_widget(
|
|
328
|
-
Paragraph::new(footer_text)
|
|
329
|
-
.alignment(Alignment::Center)
|
|
330
|
-
.style(Style::default().bg(BG_PANEL)),
|
|
331
|
-
chunks[2]
|
|
332
|
-
);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/// Helper function to add a field line
|
|
336
|
-
fn add_field(lines: &mut Vec<Line>, label: &str, value: String) {
|
|
337
|
-
lines.push(Line::from(vec![
|
|
338
|
-
Span::styled(format!(" {:<15}", format!("{}:", label)), Style::default().fg(CYBER_CYAN).add_modifier(Modifier::BOLD)),
|
|
339
|
-
Span::styled(value, Style::default().fg(TEXT_PRIMARY)),
|
|
340
|
-
]));
|
|
341
|
-
}
|
|
1
|
+
/// Agent List Screen
|
|
2
|
+
/// View and manage AI agents
|
|
3
|
+
|
|
4
|
+
use ratatui::prelude::*;
|
|
5
|
+
use ratatui::widgets::{Block, Borders, Paragraph, Wrap, Table, Row, Cell};
|
|
6
|
+
use crate::app::{AppState, AgentInfo};
|
|
7
|
+
|
|
8
|
+
// === 4RUNR BRAND COLORS ===
|
|
9
|
+
const BRAND_PURPLE: Color = Color::Rgb(138, 43, 226);
|
|
10
|
+
const CYBER_CYAN: Color = Color::Rgb(0, 255, 255);
|
|
11
|
+
const NEON_GREEN: Color = Color::Rgb(57, 255, 20);
|
|
12
|
+
const TEXT_PRIMARY: Color = Color::Rgb(230, 230, 240);
|
|
13
|
+
const TEXT_DIM: Color = Color::Rgb(100, 100, 120);
|
|
14
|
+
const TEXT_MUTED: Color = Color::Rgb(60, 60, 80);
|
|
15
|
+
const BG_PANEL: Color = Color::Rgb(18, 18, 25);
|
|
16
|
+
|
|
17
|
+
/// Render agent list screen
|
|
18
|
+
pub fn render(f: &mut Frame, state: &AppState) {
|
|
19
|
+
let area = f.size();
|
|
20
|
+
|
|
21
|
+
if state.agent_list.detail_view.is_some() {
|
|
22
|
+
// Render detail popup
|
|
23
|
+
render_detail_popup(f, area, state);
|
|
24
|
+
} else {
|
|
25
|
+
// Render list view
|
|
26
|
+
render_list_view(f, area, state);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
fn render_list_view(f: &mut Frame, area: Rect, state: &AppState) {
|
|
31
|
+
use ratatui::layout::{Constraint, Direction, Layout};
|
|
32
|
+
|
|
33
|
+
// Split into header, content, footer
|
|
34
|
+
let chunks = Layout::default()
|
|
35
|
+
.direction(Direction::Vertical)
|
|
36
|
+
.constraints([
|
|
37
|
+
Constraint::Length(3), // Header
|
|
38
|
+
Constraint::Min(0), // Content
|
|
39
|
+
Constraint::Length(3), // Footer
|
|
40
|
+
])
|
|
41
|
+
.split(area);
|
|
42
|
+
|
|
43
|
+
// === HEADER ===
|
|
44
|
+
let agent_count = state.agent_list.agents.len();
|
|
45
|
+
let header_block = Block::default()
|
|
46
|
+
.title(format!(" 🤖 Agent List ({} agents) ", agent_count))
|
|
47
|
+
.title_style(Style::default().fg(BRAND_PURPLE).add_modifier(Modifier::BOLD))
|
|
48
|
+
.borders(Borders::ALL)
|
|
49
|
+
.border_style(Style::default().fg(CYBER_CYAN))
|
|
50
|
+
.style(Style::default().bg(BG_PANEL));
|
|
51
|
+
|
|
52
|
+
f.render_widget(header_block, chunks[0]);
|
|
53
|
+
|
|
54
|
+
// === CONTENT: Agent Table ===
|
|
55
|
+
let content_block = Block::default()
|
|
56
|
+
.borders(Borders::ALL)
|
|
57
|
+
.border_style(Style::default().fg(TEXT_MUTED))
|
|
58
|
+
.style(Style::default().bg(BG_PANEL));
|
|
59
|
+
|
|
60
|
+
let table_area = content_block.inner(chunks[1]);
|
|
61
|
+
f.render_widget(content_block, chunks[1]);
|
|
62
|
+
|
|
63
|
+
if state.agent_list.agents.is_empty() {
|
|
64
|
+
// Show "No agents" message
|
|
65
|
+
f.render_widget(
|
|
66
|
+
Paragraph::new("No agents available.\n\nUse Agent Builder to create new agents.")
|
|
67
|
+
.style(Style::default().fg(TEXT_DIM))
|
|
68
|
+
.alignment(Alignment::Center),
|
|
69
|
+
table_area
|
|
70
|
+
);
|
|
71
|
+
} else {
|
|
72
|
+
// Create table rows
|
|
73
|
+
let rows: Vec<Row> = state.agent_list.agents.iter().enumerate().map(|(i, agent)| {
|
|
74
|
+
let is_selected = i == state.agent_list.selected_index;
|
|
75
|
+
let style = if is_selected {
|
|
76
|
+
Style::default().fg(NEON_GREEN).add_modifier(Modifier::BOLD)
|
|
77
|
+
} else {
|
|
78
|
+
Style::default().fg(TEXT_PRIMARY)
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
let name = if is_selected {
|
|
82
|
+
format!("▶ {}", agent.name)
|
|
83
|
+
} else {
|
|
84
|
+
format!(" {}", agent.name)
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
let desc = agent.description.as_deref().unwrap_or("No description").chars().take(30).collect::<String>();
|
|
88
|
+
let model = agent.model.chars().take(15).collect::<String>();
|
|
89
|
+
let provider = agent.provider.chars().take(10).collect::<String>();
|
|
90
|
+
|
|
91
|
+
Row::new(vec![
|
|
92
|
+
Cell::from(name).style(style),
|
|
93
|
+
Cell::from(desc).style(style),
|
|
94
|
+
Cell::from(model).style(style),
|
|
95
|
+
Cell::from(provider).style(style),
|
|
96
|
+
])
|
|
97
|
+
}).collect();
|
|
98
|
+
|
|
99
|
+
let table = Table::new(rows, [
|
|
100
|
+
Constraint::Percentage(25),
|
|
101
|
+
Constraint::Percentage(35),
|
|
102
|
+
Constraint::Percentage(20),
|
|
103
|
+
Constraint::Percentage(20),
|
|
104
|
+
])
|
|
105
|
+
.header(Row::new(vec![
|
|
106
|
+
Cell::from("Name").style(Style::default().fg(CYBER_CYAN).add_modifier(Modifier::BOLD)),
|
|
107
|
+
Cell::from("Description").style(Style::default().fg(CYBER_CYAN).add_modifier(Modifier::BOLD)),
|
|
108
|
+
Cell::from("Model").style(Style::default().fg(CYBER_CYAN).add_modifier(Modifier::BOLD)),
|
|
109
|
+
Cell::from("Provider").style(Style::default().fg(CYBER_CYAN).add_modifier(Modifier::BOLD)),
|
|
110
|
+
]))
|
|
111
|
+
.column_spacing(1)
|
|
112
|
+
.style(Style::default().fg(TEXT_PRIMARY));
|
|
113
|
+
|
|
114
|
+
f.render_widget(table, table_area);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// === FOOTER ===
|
|
118
|
+
let footer_text = Line::from(vec![
|
|
119
|
+
Span::styled("↑/↓", Style::default().fg(CYBER_CYAN)),
|
|
120
|
+
Span::styled(" Navigate | ", Style::default().fg(TEXT_DIM)),
|
|
121
|
+
Span::styled("Enter", Style::default().fg(NEON_GREEN)),
|
|
122
|
+
Span::styled(" View Details | ", Style::default().fg(TEXT_DIM)),
|
|
123
|
+
Span::styled("ESC", Style::default().fg(BRAND_PURPLE)),
|
|
124
|
+
Span::styled(" Close", Style::default().fg(TEXT_DIM)),
|
|
125
|
+
]);
|
|
126
|
+
|
|
127
|
+
f.render_widget(
|
|
128
|
+
Paragraph::new(footer_text)
|
|
129
|
+
.alignment(Alignment::Center)
|
|
130
|
+
.style(Style::default().bg(BG_PANEL))
|
|
131
|
+
.block(Block::default().borders(Borders::ALL).border_style(Style::default().fg(TEXT_MUTED))),
|
|
132
|
+
chunks[2]
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
fn render_detail_popup(f: &mut Frame, area: Rect, state: &AppState) {
|
|
137
|
+
// First render a full-screen dim overlay
|
|
138
|
+
let dim_overlay = Block::default()
|
|
139
|
+
.style(Style::default().bg(Color::Black));
|
|
140
|
+
f.render_widget(dim_overlay, area);
|
|
141
|
+
|
|
142
|
+
// Then render detail popup on top
|
|
143
|
+
let detail_index = state.agent_list.detail_view.unwrap();
|
|
144
|
+
if let Some(agent) = state.agent_list.agents.get(detail_index) {
|
|
145
|
+
render_agent_detail(f, area, agent);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
fn render_agent_detail(f: &mut Frame, area: Rect, agent: &AgentInfo) {
|
|
150
|
+
use ratatui::layout::{Constraint, Direction, Layout};
|
|
151
|
+
|
|
152
|
+
// Calculate popup size (75% width, 85% height, centered)
|
|
153
|
+
let popup_width = (area.width * 75 / 100).max(60);
|
|
154
|
+
let popup_height = (area.height * 85 / 100).max(20);
|
|
155
|
+
let popup_x = (area.width.saturating_sub(popup_width)) / 2;
|
|
156
|
+
let popup_y = (area.height.saturating_sub(popup_height)) / 2;
|
|
157
|
+
|
|
158
|
+
let popup_area = Rect {
|
|
159
|
+
x: popup_x,
|
|
160
|
+
y: popup_y,
|
|
161
|
+
width: popup_width,
|
|
162
|
+
height: popup_height,
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// Render detail popup block
|
|
166
|
+
let detail_block = Block::default()
|
|
167
|
+
.title(format!(" 📋 Agent Details: {} ", agent.name))
|
|
168
|
+
.title_style(Style::default().fg(BRAND_PURPLE).add_modifier(Modifier::BOLD))
|
|
169
|
+
.borders(Borders::ALL)
|
|
170
|
+
.border_style(Style::default().fg(CYBER_CYAN))
|
|
171
|
+
.style(Style::default().bg(BG_PANEL));
|
|
172
|
+
|
|
173
|
+
let inner = detail_block.inner(popup_area);
|
|
174
|
+
f.render_widget(detail_block, popup_area);
|
|
175
|
+
|
|
176
|
+
// Split inner area for better layout
|
|
177
|
+
let content_chunks = Layout::default()
|
|
178
|
+
.direction(Direction::Vertical)
|
|
179
|
+
.constraints([
|
|
180
|
+
Constraint::Min(0), // Main content
|
|
181
|
+
Constraint::Length(2), // Footer
|
|
182
|
+
])
|
|
183
|
+
.split(inner);
|
|
184
|
+
|
|
185
|
+
// Create detail text with better spacing
|
|
186
|
+
let mut detail_lines = vec![
|
|
187
|
+
Line::from(""), // Top padding
|
|
188
|
+
Line::from(vec![
|
|
189
|
+
Span::styled("Name: ", Style::default().fg(CYBER_CYAN).add_modifier(Modifier::BOLD)),
|
|
190
|
+
Span::styled(&agent.name, Style::default().fg(NEON_GREEN)),
|
|
191
|
+
]),
|
|
192
|
+
Line::from(""),
|
|
193
|
+
];
|
|
194
|
+
|
|
195
|
+
if let Some(desc) = &agent.description {
|
|
196
|
+
detail_lines.push(Line::from(vec![
|
|
197
|
+
Span::styled("Description: ", Style::default().fg(CYBER_CYAN).add_modifier(Modifier::BOLD)),
|
|
198
|
+
Span::styled(desc, Style::default().fg(TEXT_PRIMARY)),
|
|
199
|
+
]));
|
|
200
|
+
detail_lines.push(Line::from(""));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
detail_lines.push(Line::from(vec![
|
|
204
|
+
Span::styled("Model: ", Style::default().fg(CYBER_CYAN).add_modifier(Modifier::BOLD)),
|
|
205
|
+
Span::styled(&agent.model, Style::default().fg(TEXT_PRIMARY)),
|
|
206
|
+
]));
|
|
207
|
+
detail_lines.push(Line::from(""));
|
|
208
|
+
|
|
209
|
+
detail_lines.push(Line::from(vec![
|
|
210
|
+
Span::styled("Provider: ", Style::default().fg(CYBER_CYAN).add_modifier(Modifier::BOLD)),
|
|
211
|
+
Span::styled(&agent.provider, Style::default().fg(TEXT_PRIMARY)),
|
|
212
|
+
]));
|
|
213
|
+
detail_lines.push(Line::from(""));
|
|
214
|
+
|
|
215
|
+
if let Some(temp) = agent.temperature {
|
|
216
|
+
detail_lines.push(Line::from(vec![
|
|
217
|
+
Span::styled("Temperature: ", Style::default().fg(CYBER_CYAN).add_modifier(Modifier::BOLD)),
|
|
218
|
+
Span::styled(format!("{:.2}", temp), Style::default().fg(TEXT_PRIMARY)),
|
|
219
|
+
]));
|
|
220
|
+
detail_lines.push(Line::from(""));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if let Some(max_tokens) = agent.max_tokens {
|
|
224
|
+
detail_lines.push(Line::from(vec![
|
|
225
|
+
Span::styled("Max Tokens: ", Style::default().fg(CYBER_CYAN).add_modifier(Modifier::BOLD)),
|
|
226
|
+
Span::styled(format!("{}", max_tokens), Style::default().fg(TEXT_PRIMARY)),
|
|
227
|
+
]));
|
|
228
|
+
detail_lines.push(Line::from(""));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if !agent.tools.is_empty() {
|
|
232
|
+
detail_lines.push(Line::from(""));
|
|
233
|
+
detail_lines.push(Line::from(vec![
|
|
234
|
+
Span::styled("Tools:", Style::default().fg(CYBER_CYAN).add_modifier(Modifier::BOLD)),
|
|
235
|
+
]));
|
|
236
|
+
for tool in &agent.tools {
|
|
237
|
+
detail_lines.push(Line::from(vec![
|
|
238
|
+
Span::raw(" • "),
|
|
239
|
+
Span::styled(tool, Style::default().fg(NEON_GREEN)),
|
|
240
|
+
]));
|
|
241
|
+
}
|
|
242
|
+
detail_lines.push(Line::from(""));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if let Some(prompt) = &agent.system_prompt {
|
|
246
|
+
detail_lines.push(Line::from(""));
|
|
247
|
+
detail_lines.push(Line::from(vec![
|
|
248
|
+
Span::styled("System Prompt:", Style::default().fg(CYBER_CYAN).add_modifier(Modifier::BOLD)),
|
|
249
|
+
]));
|
|
250
|
+
detail_lines.push(Line::from(""));
|
|
251
|
+
// Show first 300 chars of prompt
|
|
252
|
+
let prompt_preview = prompt.chars().take(300).collect::<String>();
|
|
253
|
+
// Simple word wrapping
|
|
254
|
+
let wrap_width = (popup_width.saturating_sub(6)) as usize;
|
|
255
|
+
let words: Vec<&str> = prompt_preview.split_whitespace().collect();
|
|
256
|
+
let mut current_line = String::from(" ");
|
|
257
|
+
|
|
258
|
+
for word in words {
|
|
259
|
+
if current_line.len() + word.len() + 1 > wrap_width {
|
|
260
|
+
detail_lines.push(Line::from(current_line.clone()).style(Style::default().fg(TEXT_DIM)));
|
|
261
|
+
current_line = format!(" {}", word);
|
|
262
|
+
} else {
|
|
263
|
+
if current_line.len() > 2 {
|
|
264
|
+
current_line.push(' ');
|
|
265
|
+
}
|
|
266
|
+
current_line.push_str(word);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if current_line.len() > 2 {
|
|
270
|
+
detail_lines.push(Line::from(current_line).style(Style::default().fg(TEXT_DIM)));
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Render main content
|
|
275
|
+
f.render_widget(
|
|
276
|
+
Paragraph::new(detail_lines)
|
|
277
|
+
.wrap(Wrap { trim: false }),
|
|
278
|
+
content_chunks[0]
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
// Render footer
|
|
282
|
+
let footer_text = Line::from(vec![
|
|
283
|
+
Span::styled("Press ", Style::default().fg(TEXT_DIM)),
|
|
284
|
+
Span::styled("ESC", Style::default().fg(NEON_GREEN).add_modifier(Modifier::BOLD)),
|
|
285
|
+
Span::styled(" to close", Style::default().fg(TEXT_DIM)),
|
|
286
|
+
]);
|
|
287
|
+
|
|
288
|
+
f.render_widget(
|
|
289
|
+
Paragraph::new(footer_text)
|
|
290
|
+
.alignment(Alignment::Center),
|
|
291
|
+
content_chunks[1]
|
|
292
|
+
);
|
|
293
|
+
}
|
package/mk3-tui/src/ui/help.rs
CHANGED
|
@@ -1,211 +1,284 @@
|
|
|
1
|
-
/// Help popup screen
|
|
2
|
-
///
|
|
3
|
-
|
|
4
|
-
use ratatui::prelude::*;
|
|
5
|
-
use ratatui::widgets::{Block, Borders, Paragraph, Wrap};
|
|
6
|
-
use crate::app::AppState;
|
|
7
|
-
|
|
8
|
-
// === 4RUNR BRAND COLORS ===
|
|
9
|
-
const BRAND_PURPLE: Color = Color::Rgb(138, 43, 226);
|
|
10
|
-
const CYBER_CYAN: Color = Color::Rgb(0, 255, 255);
|
|
11
|
-
const NEON_GREEN: Color = Color::Rgb(57, 255, 20);
|
|
12
|
-
const AMBER_WARN: Color = Color::Rgb(255, 191, 0);
|
|
13
|
-
const TEXT_PRIMARY: Color = Color::Rgb(230, 230, 240);
|
|
14
|
-
const TEXT_DIM: Color = Color::Rgb(100, 100, 120);
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
let
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
Constraint::Length(3), //
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
.
|
|
59
|
-
.
|
|
60
|
-
.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
]);
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
1
|
+
/// Help popup screen
|
|
2
|
+
/// Professional, organized command reference - REDESIGNED
|
|
3
|
+
|
|
4
|
+
use ratatui::prelude::*;
|
|
5
|
+
use ratatui::widgets::{Block, Borders, Paragraph, Wrap};
|
|
6
|
+
use crate::app::AppState;
|
|
7
|
+
|
|
8
|
+
// === 4RUNR BRAND COLORS ===
|
|
9
|
+
const BRAND_PURPLE: Color = Color::Rgb(138, 43, 226);
|
|
10
|
+
const CYBER_CYAN: Color = Color::Rgb(0, 255, 255);
|
|
11
|
+
const NEON_GREEN: Color = Color::Rgb(57, 255, 20);
|
|
12
|
+
const AMBER_WARN: Color = Color::Rgb(255, 191, 0);
|
|
13
|
+
const TEXT_PRIMARY: Color = Color::Rgb(230, 230, 240);
|
|
14
|
+
const TEXT_DIM: Color = Color::Rgb(100, 100, 120);
|
|
15
|
+
const BG_PANEL: Color = Color::Rgb(18, 18, 25);
|
|
16
|
+
|
|
17
|
+
/// Render help popup overlay
|
|
18
|
+
pub fn render_help(f: &mut Frame, area: Rect, _state: &AppState) {
|
|
19
|
+
// Render background overlay (dimmed)
|
|
20
|
+
let overlay = Block::default()
|
|
21
|
+
.style(Style::default().bg(Color::Black));
|
|
22
|
+
f.render_widget(overlay, area);
|
|
23
|
+
|
|
24
|
+
// Calculate popup size (85% width, 90% height, centered)
|
|
25
|
+
let popup_width = (area.width * 85 / 100).max(70);
|
|
26
|
+
let popup_height = (area.height * 90 / 100).max(25);
|
|
27
|
+
let popup_x = (area.width.saturating_sub(popup_width)) / 2;
|
|
28
|
+
let popup_y = (area.height.saturating_sub(popup_height)) / 2;
|
|
29
|
+
|
|
30
|
+
let popup_area = Rect {
|
|
31
|
+
x: popup_x,
|
|
32
|
+
y: popup_y,
|
|
33
|
+
width: popup_width,
|
|
34
|
+
height: popup_height,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Render help popup
|
|
38
|
+
render_help_content(f, popup_area);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
fn render_help_content(f: &mut Frame, area: Rect) {
|
|
42
|
+
use ratatui::layout::{Constraint, Direction, Layout};
|
|
43
|
+
|
|
44
|
+
// Split into header, content, footer
|
|
45
|
+
let chunks = Layout::default()
|
|
46
|
+
.direction(Direction::Vertical)
|
|
47
|
+
.constraints([
|
|
48
|
+
Constraint::Length(3), // Header
|
|
49
|
+
Constraint::Min(0), // Content
|
|
50
|
+
Constraint::Length(3), // Footer
|
|
51
|
+
])
|
|
52
|
+
.split(area);
|
|
53
|
+
|
|
54
|
+
// === HEADER ===
|
|
55
|
+
let header_block = Block::default()
|
|
56
|
+
.title(" 📖 4Runr AI Agent OS - Command Reference ")
|
|
57
|
+
.title_style(Style::default().fg(BRAND_PURPLE).add_modifier(Modifier::BOLD))
|
|
58
|
+
.borders(Borders::ALL)
|
|
59
|
+
.border_style(Style::default().fg(CYBER_CYAN))
|
|
60
|
+
.style(Style::default().bg(BG_PANEL));
|
|
61
|
+
|
|
62
|
+
f.render_widget(header_block, chunks[0]);
|
|
63
|
+
|
|
64
|
+
// === CONTENT ===
|
|
65
|
+
let content_block = Block::default()
|
|
66
|
+
.borders(Borders::ALL)
|
|
67
|
+
.border_style(Style::default().fg(CYBER_CYAN))
|
|
68
|
+
.style(Style::default().bg(BG_PANEL));
|
|
69
|
+
|
|
70
|
+
let inner = content_block.inner(chunks[1]);
|
|
71
|
+
f.render_widget(content_block, chunks[1]);
|
|
72
|
+
|
|
73
|
+
// Build help text - single column, vertical layout
|
|
74
|
+
let mut help_lines = vec![
|
|
75
|
+
Line::from(""),
|
|
76
|
+
Line::from(vec![
|
|
77
|
+
Span::styled("═══════════════════════════════════════════════════════════════", Style::default().fg(CYBER_CYAN)),
|
|
78
|
+
]),
|
|
79
|
+
Line::from(vec![
|
|
80
|
+
Span::styled(" 🧭 NAVIGATION COMMANDS", Style::default().fg(CYBER_CYAN).add_modifier(Modifier::BOLD)),
|
|
81
|
+
]),
|
|
82
|
+
Line::from(vec![
|
|
83
|
+
Span::styled("═══════════════════════════════════════════════════════════════", Style::default().fg(CYBER_CYAN)),
|
|
84
|
+
]),
|
|
85
|
+
Line::from(""),
|
|
86
|
+
Line::from(vec![
|
|
87
|
+
Span::styled(" build", Style::default().fg(NEON_GREEN).add_modifier(Modifier::BOLD)),
|
|
88
|
+
]),
|
|
89
|
+
Line::from(vec![
|
|
90
|
+
Span::styled(" Open Agent Builder (6-step wizard)", Style::default().fg(TEXT_PRIMARY)),
|
|
91
|
+
]),
|
|
92
|
+
Line::from(""),
|
|
93
|
+
Line::from(vec![
|
|
94
|
+
Span::styled(" runs", Style::default().fg(NEON_GREEN).add_modifier(Modifier::BOLD)),
|
|
95
|
+
]),
|
|
96
|
+
Line::from(vec![
|
|
97
|
+
Span::styled(" Open Run Manager (list, filter, sort)", Style::default().fg(TEXT_PRIMARY)),
|
|
98
|
+
]),
|
|
99
|
+
Line::from(""),
|
|
100
|
+
Line::from(vec![
|
|
101
|
+
Span::styled(" config, settings", Style::default().fg(NEON_GREEN).add_modifier(Modifier::BOLD)),
|
|
102
|
+
]),
|
|
103
|
+
Line::from(vec![
|
|
104
|
+
Span::styled(" Open Settings (mode, AI provider)", Style::default().fg(TEXT_PRIMARY)),
|
|
105
|
+
]),
|
|
106
|
+
Line::from(""),
|
|
107
|
+
Line::from(""),
|
|
108
|
+
Line::from(vec![
|
|
109
|
+
Span::styled("═══════════════════════════════════════════════════════════════", Style::default().fg(BRAND_PURPLE)),
|
|
110
|
+
]),
|
|
111
|
+
Line::from(vec![
|
|
112
|
+
Span::styled(" 💻 LOCAL COMMANDS", Style::default().fg(BRAND_PURPLE).add_modifier(Modifier::BOLD)),
|
|
113
|
+
]),
|
|
114
|
+
Line::from(vec![
|
|
115
|
+
Span::styled("═══════════════════════════════════════════════════════════════", Style::default().fg(BRAND_PURPLE)),
|
|
116
|
+
]),
|
|
117
|
+
Line::from(""),
|
|
118
|
+
Line::from(vec![
|
|
119
|
+
Span::styled(" quit, exit", Style::default().fg(NEON_GREEN).add_modifier(Modifier::BOLD)),
|
|
120
|
+
]),
|
|
121
|
+
Line::from(vec![
|
|
122
|
+
Span::styled(" Exit application", Style::default().fg(TEXT_PRIMARY)),
|
|
123
|
+
]),
|
|
124
|
+
Line::from(""),
|
|
125
|
+
Line::from(vec![
|
|
126
|
+
Span::styled(" clear", Style::default().fg(NEON_GREEN).add_modifier(Modifier::BOLD)),
|
|
127
|
+
]),
|
|
128
|
+
Line::from(vec![
|
|
129
|
+
Span::styled(" Clear operations log", Style::default().fg(TEXT_PRIMARY)),
|
|
130
|
+
]),
|
|
131
|
+
Line::from(""),
|
|
132
|
+
Line::from(vec![
|
|
133
|
+
Span::styled(" help", Style::default().fg(NEON_GREEN).add_modifier(Modifier::BOLD)),
|
|
134
|
+
]),
|
|
135
|
+
Line::from(vec![
|
|
136
|
+
Span::styled(" Show this help", Style::default().fg(TEXT_PRIMARY)),
|
|
137
|
+
]),
|
|
138
|
+
Line::from(""),
|
|
139
|
+
Line::from(vec![
|
|
140
|
+
Span::styled(" :perf", Style::default().fg(NEON_GREEN).add_modifier(Modifier::BOLD)),
|
|
141
|
+
]),
|
|
142
|
+
Line::from(vec![
|
|
143
|
+
Span::styled(" Show performance stats", Style::default().fg(TEXT_PRIMARY)),
|
|
144
|
+
]),
|
|
145
|
+
Line::from(""),
|
|
146
|
+
Line::from(""),
|
|
147
|
+
Line::from(vec![
|
|
148
|
+
Span::styled("═══════════════════════════════════════════════════════════════", Style::default().fg(AMBER_WARN)),
|
|
149
|
+
]),
|
|
150
|
+
Line::from(vec![
|
|
151
|
+
Span::styled(" 🌐 WEBSOCKET COMMANDS (requires connection)", Style::default().fg(AMBER_WARN).add_modifier(Modifier::BOLD)),
|
|
152
|
+
]),
|
|
153
|
+
Line::from(vec![
|
|
154
|
+
Span::styled("═══════════════════════════════════════════════════════════════", Style::default().fg(AMBER_WARN)),
|
|
155
|
+
]),
|
|
156
|
+
Line::from(""),
|
|
157
|
+
Line::from(vec![
|
|
158
|
+
Span::styled(" agent.list", Style::default().fg(NEON_GREEN).add_modifier(Modifier::BOLD)),
|
|
159
|
+
]),
|
|
160
|
+
Line::from(vec![
|
|
161
|
+
Span::styled(" List all agents", Style::default().fg(TEXT_PRIMARY)),
|
|
162
|
+
]),
|
|
163
|
+
Line::from(""),
|
|
164
|
+
Line::from(vec![
|
|
165
|
+
Span::styled(" agent.get", Style::default().fg(NEON_GREEN).add_modifier(Modifier::BOLD)),
|
|
166
|
+
]),
|
|
167
|
+
Line::from(vec![
|
|
168
|
+
Span::styled(" Get agent details (data: {name})", Style::default().fg(TEXT_PRIMARY)),
|
|
169
|
+
]),
|
|
170
|
+
Line::from(""),
|
|
171
|
+
Line::from(vec![
|
|
172
|
+
Span::styled(" agent.create", Style::default().fg(NEON_GREEN).add_modifier(Modifier::BOLD)),
|
|
173
|
+
]),
|
|
174
|
+
Line::from(vec![
|
|
175
|
+
Span::styled(" Create agent (use Agent Builder)", Style::default().fg(TEXT_PRIMARY)),
|
|
176
|
+
]),
|
|
177
|
+
Line::from(""),
|
|
178
|
+
Line::from(vec![
|
|
179
|
+
Span::styled(" agent.delete", Style::default().fg(NEON_GREEN).add_modifier(Modifier::BOLD)),
|
|
180
|
+
]),
|
|
181
|
+
Line::from(vec![
|
|
182
|
+
Span::styled(" Delete agent (data: {name})", Style::default().fg(TEXT_PRIMARY)),
|
|
183
|
+
]),
|
|
184
|
+
Line::from(""),
|
|
185
|
+
Line::from(vec![
|
|
186
|
+
Span::styled(" system.status", Style::default().fg(NEON_GREEN).add_modifier(Modifier::BOLD)),
|
|
187
|
+
]),
|
|
188
|
+
Line::from(vec![
|
|
189
|
+
Span::styled(" Get system status", Style::default().fg(TEXT_PRIMARY)),
|
|
190
|
+
]),
|
|
191
|
+
Line::from(""),
|
|
192
|
+
Line::from(vec![
|
|
193
|
+
Span::styled(" run.list", Style::default().fg(NEON_GREEN).add_modifier(Modifier::BOLD)),
|
|
194
|
+
]),
|
|
195
|
+
Line::from(vec![
|
|
196
|
+
Span::styled(" List runs (requires gateway)", Style::default().fg(TEXT_PRIMARY)),
|
|
197
|
+
]),
|
|
198
|
+
Line::from(""),
|
|
199
|
+
Line::from(vec![
|
|
200
|
+
Span::styled(" tool.list", Style::default().fg(NEON_GREEN).add_modifier(Modifier::BOLD)),
|
|
201
|
+
]),
|
|
202
|
+
Line::from(vec![
|
|
203
|
+
Span::styled(" List available tools", Style::default().fg(TEXT_PRIMARY)),
|
|
204
|
+
]),
|
|
205
|
+
Line::from(""),
|
|
206
|
+
Line::from(""),
|
|
207
|
+
Line::from(vec![
|
|
208
|
+
Span::styled("═══════════════════════════════════════════════════════════════", Style::default().fg(CYBER_CYAN)),
|
|
209
|
+
]),
|
|
210
|
+
Line::from(vec![
|
|
211
|
+
Span::styled(" ⌨️ SCREEN CONTROLS", Style::default().fg(CYBER_CYAN).add_modifier(Modifier::BOLD)),
|
|
212
|
+
]),
|
|
213
|
+
Line::from(vec![
|
|
214
|
+
Span::styled("═══════════════════════════════════════════════════════════════", Style::default().fg(CYBER_CYAN)),
|
|
215
|
+
]),
|
|
216
|
+
Line::from(""),
|
|
217
|
+
Line::from(vec![
|
|
218
|
+
Span::styled(" Agent Builder:", Style::default().fg(BRAND_PURPLE).add_modifier(Modifier::BOLD)),
|
|
219
|
+
]),
|
|
220
|
+
Line::from(vec![
|
|
221
|
+
Span::styled(" Enter", Style::default().fg(NEON_GREEN)),
|
|
222
|
+
Span::styled(" = Next step | ", Style::default().fg(TEXT_DIM)),
|
|
223
|
+
Span::styled("Backspace", Style::default().fg(NEON_GREEN)),
|
|
224
|
+
Span::styled(" = Previous | ", Style::default().fg(TEXT_DIM)),
|
|
225
|
+
Span::styled("ESC", Style::default().fg(NEON_GREEN)),
|
|
226
|
+
Span::styled(" = Cancel", Style::default().fg(TEXT_DIM)),
|
|
227
|
+
]),
|
|
228
|
+
Line::from(""),
|
|
229
|
+
Line::from(vec![
|
|
230
|
+
Span::styled(" Run Manager:", Style::default().fg(BRAND_PURPLE).add_modifier(Modifier::BOLD)),
|
|
231
|
+
]),
|
|
232
|
+
Line::from(vec![
|
|
233
|
+
Span::styled(" ↑/↓", Style::default().fg(NEON_GREEN)),
|
|
234
|
+
Span::styled(" = Navigate | ", Style::default().fg(TEXT_DIM)),
|
|
235
|
+
Span::styled("F", Style::default().fg(NEON_GREEN)),
|
|
236
|
+
Span::styled(" = Filter | ", Style::default().fg(TEXT_DIM)),
|
|
237
|
+
Span::styled("S", Style::default().fg(NEON_GREEN)),
|
|
238
|
+
Span::styled(" = Sort | ", Style::default().fg(TEXT_DIM)),
|
|
239
|
+
Span::styled("R", Style::default().fg(NEON_GREEN)),
|
|
240
|
+
Span::styled(" = Refresh", Style::default().fg(TEXT_DIM)),
|
|
241
|
+
]),
|
|
242
|
+
Line::from(""),
|
|
243
|
+
Line::from(vec![
|
|
244
|
+
Span::styled(" Settings:", Style::default().fg(BRAND_PURPLE).add_modifier(Modifier::BOLD)),
|
|
245
|
+
]),
|
|
246
|
+
Line::from(vec![
|
|
247
|
+
Span::styled(" ↑/↓", Style::default().fg(NEON_GREEN)),
|
|
248
|
+
Span::styled(" = Navigate | ", Style::default().fg(TEXT_DIM)),
|
|
249
|
+
Span::styled("Space", Style::default().fg(NEON_GREEN)),
|
|
250
|
+
Span::styled(" = Toggle | ", Style::default().fg(TEXT_DIM)),
|
|
251
|
+
Span::styled("Enter", Style::default().fg(NEON_GREEN)),
|
|
252
|
+
Span::styled(" = Save", Style::default().fg(TEXT_DIM)),
|
|
253
|
+
]),
|
|
254
|
+
Line::from(""),
|
|
255
|
+
];
|
|
256
|
+
|
|
257
|
+
f.render_widget(
|
|
258
|
+
Paragraph::new(help_lines)
|
|
259
|
+
.style(Style::default().bg(BG_PANEL))
|
|
260
|
+
.wrap(Wrap { trim: false }),
|
|
261
|
+
inner
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
// === FOOTER ===
|
|
265
|
+
let footer_text = Line::from(vec![
|
|
266
|
+
Span::styled("Press ", Style::default().fg(TEXT_DIM)),
|
|
267
|
+
Span::styled("ESC", Style::default().fg(NEON_GREEN).add_modifier(Modifier::BOLD)),
|
|
268
|
+
Span::styled(" to close", Style::default().fg(TEXT_DIM)),
|
|
269
|
+
]);
|
|
270
|
+
|
|
271
|
+
let footer_block = Block::default()
|
|
272
|
+
.borders(Borders::ALL)
|
|
273
|
+
.border_style(Style::default().fg(CYBER_CYAN))
|
|
274
|
+
.style(Style::default().bg(BG_PANEL));
|
|
275
|
+
|
|
276
|
+
let footer_inner = footer_block.inner(chunks[2]);
|
|
277
|
+
f.render_widget(footer_block, chunks[2]);
|
|
278
|
+
f.render_widget(
|
|
279
|
+
Paragraph::new(footer_text)
|
|
280
|
+
.style(Style::default().bg(BG_PANEL))
|
|
281
|
+
.alignment(Alignment::Center),
|
|
282
|
+
footer_inner
|
|
283
|
+
);
|
|
284
|
+
}
|
package/mk3-tui/src/ui/layout.rs
CHANGED
|
@@ -120,7 +120,7 @@ fn render_header(f: &mut Frame, area: Rect, state: &AppState) {
|
|
|
120
120
|
|
|
121
121
|
// Line 1: Brand + version + uptime - Bug 3 fix: Use "4Runr." with dot (matches brand logo)
|
|
122
122
|
// Use npm package version (2.3.5) - matches package.json
|
|
123
|
-
const PACKAGE_VERSION: &str = "2.6.
|
|
123
|
+
const PACKAGE_VERSION: &str = "2.6.6";
|
|
124
124
|
let brand_line = Line::from(vec![
|
|
125
125
|
Span::styled("4Runr.", Style::default().fg(BRAND_PURPLE).add_modifier(Modifier::BOLD)),
|
|
126
126
|
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.6.
|
|
3
|
+
"version": "2.6.6",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "4Runr AI Agent OS - Secure terminal interface for AI agents. v2.6.
|
|
5
|
+
"description": "4Runr AI Agent OS - Secure terminal interface for AI agents. v2.6.6: CRITICAL FIX - Help popup transparency fixed (solid background). Completely redesigned help popup (clean, readable, single-column layout). Fixed agent detail popup rendering bugs (proper dimming, no overlap). Enhanced visual hierarchy with better spacing. Built with Rust + Ratatui. ⚠️ Pre-MVP / Development Phase",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"4runr": "dist/index.js",
|