Skip to content

Commit 75edc88

Browse files
committed
moar TUI
1 parent 2bdbdf6 commit 75edc88

File tree

13 files changed

+2979
-292
lines changed

13 files changed

+2979
-292
lines changed

hack/libvirt/libvirt-kvm.xml

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
<libosinfo:os id="http://libosinfo.org/linux/2022"/>
66
</libosinfo:libosinfo>
77
</metadata>
8-
<memory unit='MiB'>8048</memory>
8+
<memory unit='MiB'>4096</memory>
99
<vcpu>4</vcpu>
1010
<os firmware="efi">
1111
<type arch="x86_64" machine="q35">hvm</type>
1212
<kernel>__PWD__/target/uki.efi</kernel>
13-
<loader readonly="yes" type="pflash">/usr/share/OVMF/OVMF_CODE_4M.fd</loader>
13+
<loader readonly="yes" type="pflash">/usr/share/edk2/x64/OVMF_CODE.4m.fd</loader>
1414
<boot dev="hd"/>
1515
</os>
1616
<features>
@@ -25,15 +25,20 @@
2525
</clock>
2626
<devices>
2727
<emulator>/usr/bin/qemu-system-x86_64</emulator>
28-
29-
<console type="pty">
30-
<target type="virtio" port="0"/>
31-
</console>
28+
29+
<input type="keyboard" bus="ps2">
30+
<alias name="input1"/>
31+
</input>
32+
3233
<serial type="pty">
3334
<target type="isa-serial" port="0">
3435
<model name="isa-serial"/>
3536
</target>
3637
</serial>
38+
39+
<console type="pty">
40+
<target type="virtio" port="0"/>
41+
</console>
3742

3843
<interface type="bridge">
3944
<source bridge="vm-br0"/>

src/bin/feos-tui/app.rs

Lines changed: 192 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,22 @@ pub enum CurrentView {
66
Dashboard,
77
VMs,
88
Containers,
9+
IsolatedPods,
910
Logs,
10-
System,
1111
}
1212

1313
impl CurrentView {
1414
pub fn titles() -> Vec<&'static str> {
15-
vec!["Dashboard", "VMs", "Containers", "Logs", "System"]
15+
vec!["Dashboard", "VMs", "Containers", "Isolated Pods", "Logs"]
1616
}
1717

1818
pub fn from_index(index: usize) -> Self {
1919
match index {
2020
0 => CurrentView::Dashboard,
2121
1 => CurrentView::VMs,
2222
2 => CurrentView::Containers,
23-
3 => CurrentView::Logs,
24-
4 => CurrentView::System,
23+
3 => CurrentView::IsolatedPods,
24+
4 => CurrentView::Logs,
2525
_ => CurrentView::Dashboard,
2626
}
2727
}
@@ -31,8 +31,8 @@ impl CurrentView {
3131
CurrentView::Dashboard => 0,
3232
CurrentView::VMs => 1,
3333
CurrentView::Containers => 2,
34-
CurrentView::Logs => 3,
35-
CurrentView::System => 4,
34+
CurrentView::IsolatedPods => 3,
35+
CurrentView::Logs => 4,
3636
}
3737
}
3838
}
@@ -41,13 +41,15 @@ impl CurrentView {
4141
pub enum SystemAction {
4242
Reboot,
4343
Shutdown,
44+
Cancel,
4445
}
4546

4647
impl SystemAction {
4748
pub fn as_str(&self) -> &'static str {
4849
match self {
4950
SystemAction::Reboot => "Reboot System",
5051
SystemAction::Shutdown => "Shutdown System",
52+
SystemAction::Cancel => "Cancel",
5153
}
5254
}
5355
}
@@ -58,13 +60,26 @@ pub struct App {
5860
pub host_info: HostInfo,
5961
pub vms: Vec<VmInfo>,
6062
pub containers: Vec<ContainerInfo>,
63+
pub isolated_pods: Vec<IsolatedPodInfo>,
6164
pub cpu_history: Vec<u64>,
6265
pub memory_history: Vec<u64>,
6366
pub feos_logs: Vec<LogEntry>,
6467
pub kernel_logs: Vec<LogEntry>,
6568
pub selected_vm_index: usize,
6669
pub selected_container_index: usize,
70+
pub container_logs_expanded: bool, // Whether container logs are in expanded view
71+
pub selected_isolated_pod_index: usize,
72+
pub selected_pod_container_index: usize,
73+
pub selected_pod_log_tab: usize, // 0 = kernel logs, 1 = container logs
74+
pub logs_expanded: bool, // Whether logs are in expanded view
75+
pub selected_global_log_tab: usize, // 0 = feos logs, 1 = kernel logs
76+
pub global_logs_expanded: bool, // Whether global logs are in expanded view
77+
pub log_scroll_offset: usize, // For scrolling logs
78+
pub log_line_wrap: bool, // Whether to wrap long log lines
79+
pub help_modal_open: bool, // Whether help modal is open
80+
pub help_scroll_offset: usize, // For scrolling help content
6781
pub selected_system_action: usize,
82+
pub system_modal_open: bool,
6883
pub system_confirmation: Option<SystemAction>,
6984
last_update: Instant,
7085
last_log_update: Instant,
@@ -78,13 +93,26 @@ impl Default for App {
7893
host_info: get_mock_host_info(),
7994
vms: get_mock_vms(),
8095
containers: get_mock_containers(),
96+
isolated_pods: get_mock_isolated_pods(),
8197
cpu_history: get_mock_cpu_history(),
8298
memory_history: get_mock_memory_history(),
8399
feos_logs: get_mock_feos_logs(),
84100
kernel_logs: get_mock_kernel_logs(),
85101
selected_vm_index: 0,
86102
selected_container_index: 0,
103+
container_logs_expanded: false,
104+
selected_isolated_pod_index: 0,
105+
selected_pod_container_index: 0,
106+
selected_pod_log_tab: 0,
107+
logs_expanded: false,
108+
selected_global_log_tab: 0,
109+
global_logs_expanded: false,
110+
log_scroll_offset: 0,
111+
log_line_wrap: true,
112+
help_modal_open: false,
113+
help_scroll_offset: 0,
87114
selected_system_action: 0,
115+
system_modal_open: false,
88116
system_confirmation: None,
89117
last_update: Instant::now(),
90118
last_log_update: Instant::now(),
@@ -102,6 +130,7 @@ impl App {
102130
self.host_info = get_mock_host_info();
103131
self.vms = get_mock_vms();
104132
self.containers = get_mock_containers();
133+
self.isolated_pods = get_mock_isolated_pods();
105134
self.cpu_history = get_mock_cpu_history();
106135
self.memory_history = get_mock_memory_history();
107136

@@ -115,6 +144,18 @@ impl App {
115144
self.selected_container_index = self.containers.len() - 1;
116145
}
117146

147+
// Ensure selected isolated pod index is still valid after update
148+
if self.selected_isolated_pod_index >= self.isolated_pods.len() && !self.isolated_pods.is_empty() {
149+
self.selected_isolated_pod_index = self.isolated_pods.len() - 1;
150+
}
151+
152+
// Ensure selected pod container index is still valid after update
153+
if let Some(pod) = self.isolated_pods.get(self.selected_isolated_pod_index) {
154+
if self.selected_pod_container_index >= pod.containers.len() && !pod.containers.is_empty() {
155+
self.selected_pod_container_index = pod.containers.len() - 1;
156+
}
157+
}
158+
118159
self.last_update = now;
119160
}
120161

@@ -179,26 +220,162 @@ impl App {
179220
self.containers.get(self.selected_container_index)
180221
}
181222

223+
pub fn select_next_isolated_pod(&mut self) {
224+
if !self.isolated_pods.is_empty() {
225+
self.selected_isolated_pod_index = (self.selected_isolated_pod_index + 1) % self.isolated_pods.len();
226+
// Reset container selection when switching pods
227+
self.selected_pod_container_index = 0;
228+
}
229+
}
230+
231+
pub fn select_previous_isolated_pod(&mut self) {
232+
if !self.isolated_pods.is_empty() {
233+
if self.selected_isolated_pod_index == 0 {
234+
self.selected_isolated_pod_index = self.isolated_pods.len() - 1;
235+
} else {
236+
self.selected_isolated_pod_index -= 1;
237+
}
238+
// Reset container selection when switching pods
239+
self.selected_pod_container_index = 0;
240+
}
241+
}
242+
243+
pub fn get_selected_isolated_pod(&self) -> Option<&IsolatedPodInfo> {
244+
self.isolated_pods.get(self.selected_isolated_pod_index)
245+
}
246+
247+
pub fn select_next_pod_container(&mut self) {
248+
if let Some(pod) = self.get_selected_isolated_pod() {
249+
if !pod.containers.is_empty() {
250+
self.selected_pod_container_index = (self.selected_pod_container_index + 1) % pod.containers.len();
251+
}
252+
}
253+
}
254+
255+
pub fn select_previous_pod_container(&mut self) {
256+
if let Some(pod) = self.get_selected_isolated_pod() {
257+
if !pod.containers.is_empty() {
258+
if self.selected_pod_container_index == 0 {
259+
self.selected_pod_container_index = pod.containers.len() - 1;
260+
} else {
261+
self.selected_pod_container_index -= 1;
262+
}
263+
}
264+
}
265+
}
266+
267+
pub fn get_selected_pod_container(&self) -> Option<&IsolatedPodContainer> {
268+
self.get_selected_isolated_pod()
269+
.and_then(|pod| pod.containers.get(self.selected_pod_container_index))
270+
}
271+
272+
pub fn switch_pod_log_tab(&mut self) {
273+
self.selected_pod_log_tab = (self.selected_pod_log_tab + 1) % 2; // Toggle between 0 and 1
274+
}
275+
276+
pub fn toggle_logs_expanded(&mut self) {
277+
self.logs_expanded = !self.logs_expanded;
278+
// Reset scroll when toggling expanded mode
279+
self.log_scroll_offset = 0;
280+
}
281+
282+
pub fn switch_global_log_tab(&mut self) {
283+
self.selected_global_log_tab = (self.selected_global_log_tab + 1) % 2; // Toggle between 0 and 1
284+
}
285+
286+
pub fn toggle_global_logs_expanded(&mut self) {
287+
self.global_logs_expanded = !self.global_logs_expanded;
288+
// Reset scroll when toggling expanded mode
289+
self.log_scroll_offset = 0;
290+
}
291+
292+
pub fn toggle_container_logs_expanded(&mut self) {
293+
self.container_logs_expanded = !self.container_logs_expanded;
294+
// Reset scroll when toggling expanded mode
295+
self.log_scroll_offset = 0;
296+
}
297+
298+
pub fn scroll_logs_up(&mut self) {
299+
if self.log_scroll_offset > 0 {
300+
self.log_scroll_offset -= 1;
301+
}
302+
}
303+
304+
pub fn scroll_logs_down(&mut self) {
305+
self.log_scroll_offset += 1;
306+
}
307+
308+
pub fn scroll_logs_page_up(&mut self) {
309+
self.log_scroll_offset = self.log_scroll_offset.saturating_sub(10);
310+
}
311+
312+
pub fn scroll_logs_page_down(&mut self) {
313+
self.log_scroll_offset += 10;
314+
}
315+
316+
pub fn toggle_log_line_wrap(&mut self) {
317+
self.log_line_wrap = !self.log_line_wrap;
318+
}
319+
320+
pub fn open_help_modal(&mut self) {
321+
self.help_modal_open = true;
322+
self.help_scroll_offset = 0; // Reset scroll when opening
323+
}
324+
325+
pub fn close_help_modal(&mut self) {
326+
self.help_modal_open = false;
327+
self.help_scroll_offset = 0;
328+
}
329+
330+
pub fn scroll_help_up(&mut self) {
331+
if self.help_scroll_offset > 0 {
332+
self.help_scroll_offset -= 1;
333+
}
334+
}
335+
336+
pub fn scroll_help_down(&mut self) {
337+
self.help_scroll_offset += 1;
338+
}
339+
182340
pub fn select_next_system_action(&mut self) {
183-
self.selected_system_action = (self.selected_system_action + 1) % 2; // 2 actions: reboot, shutdown
341+
self.selected_system_action = (self.selected_system_action + 1) % 3; // 3 actions: reboot, shutdown, cancel
184342
}
185343

186344
pub fn select_previous_system_action(&mut self) {
187345
if self.selected_system_action == 0 {
188-
self.selected_system_action = 1;
346+
self.selected_system_action = 2;
189347
} else {
190348
self.selected_system_action -= 1;
191349
}
192350
}
193351

194352
pub fn get_system_actions() -> Vec<SystemAction> {
195-
vec![SystemAction::Reboot, SystemAction::Shutdown]
353+
vec![SystemAction::Reboot, SystemAction::Shutdown, SystemAction::Cancel]
354+
}
355+
356+
pub fn open_system_modal(&mut self) {
357+
self.system_modal_open = true;
358+
self.selected_system_action = 0; // Reset to first option
359+
}
360+
361+
pub fn close_system_modal(&mut self) {
362+
self.system_modal_open = false;
363+
self.system_confirmation = None;
196364
}
197365

198366
pub fn trigger_system_confirmation(&mut self) {
199367
let actions = Self::get_system_actions();
200368
if let Some(action) = actions.get(self.selected_system_action) {
201-
self.system_confirmation = Some(*action);
369+
match action {
370+
SystemAction::Cancel => {
371+
// Close modal immediately for Cancel
372+
self.close_system_modal();
373+
}
374+
_ => {
375+
// Show confirmation dialog for Reboot/Shutdown
376+
self.system_confirmation = Some(*action);
377+
}
378+
}
202379
}
203380
}
204381

@@ -212,12 +389,15 @@ impl App {
212389
SystemAction::Shutdown => {
213390
// Placeholder for shutdown logic
214391
}
392+
SystemAction::Cancel => {
393+
// Just cancel, no action needed
394+
}
215395
}
216396
}
217-
self.system_confirmation = None;
397+
self.close_system_modal();
218398
}
219399

220400
pub fn cancel_system_action(&mut self) {
221-
self.system_confirmation = None;
401+
self.close_system_modal();
222402
}
223403
}

0 commit comments

Comments
 (0)