1use super::App;
2use crate::action::Action;
3use crate::components::loading::LoadingState;
4
5impl App {
6 pub fn add_module_to_loading(&mut self, module_path: String) {
7 self.state.loading_ui.progress.add_module(module_path);
8 }
9
10 pub fn start_module_loading(&mut self, module_path: &str) {
12 self.state
13 .loading_ui
14 .progress
15 .start_module_loading(module_path);
16 }
17
18 pub fn complete_module_loading(
20 &mut self,
21 module_path: &str,
22 functions: usize,
23 variables: usize,
24 types: usize,
25 ) {
26 use crate::components::loading::ModuleStats;
27 let stats = ModuleStats {
28 functions,
29 variables,
30 types,
31 debug_source: "unknown".to_string(),
32 debug_source_path: None,
33 };
34 self.state
35 .loading_ui
36 .progress
37 .complete_module(module_path, stats);
38 }
39
40 pub fn fail_module_loading(&mut self, module_path: &str, error: String) {
42 self.state
43 .loading_ui
44 .progress
45 .fail_module(module_path, error);
46 }
47
48 pub fn set_target_pid(&mut self, pid: u32) {
50 self.state.target_pid = Some(pid);
51 }
52
53 pub fn transition_to_ready_with_completion(&mut self) {
55 self.add_loading_completion_summary();
56 self.state.set_loading_state(LoadingState::Ready);
57 }
58
59 fn sync_files_to_command_panel(&mut self, files: Vec<String>) {
61 tracing::debug!(
62 "Syncing {} files to command panel completion cache",
63 files.len()
64 );
65 if !files.is_empty() {
66 tracing::debug!(
67 "First 5 files: {:?}",
68 files.iter().take(5).collect::<Vec<_>>()
69 );
70 }
71
72 if let Some(cache) = &mut self.state.command_panel.file_completion_cache {
74 let updated = cache.sync_from_source_panel(&files);
76 tracing::debug!("Updated existing file completion cache: {}", updated);
77 } else {
78 if !files.is_empty() {
80 tracing::debug!(
81 "Creating new file completion cache with {} files",
82 files.len()
83 );
84 self.state.command_panel.file_completion_cache = Some(
85 crate::components::command_panel::file_completion::FileCompletionCache::new(
86 &files,
87 ),
88 );
89 tracing::debug!("File completion cache created successfully");
90 } else {
91 tracing::debug!("No files to create cache with");
92 }
93 }
94 }
95
96 pub fn add_loading_completion_summary(&mut self) {
98 let total_time = self.state.loading_ui.progress.elapsed_time();
99
100 let mut styled_lines = self.state.loading_ui.create_welcome_message(total_time);
102
103 if let Some(pid) = self.state.target_pid {
105 use ratatui::style::{Color, Style};
106 use ratatui::text::{Line, Span};
107
108 let mut enhanced_lines = Vec::new();
110 let mut found_dwarf_stats = false;
111 for line in styled_lines {
112 enhanced_lines.push(line.clone());
113 let line_text: String = line
115 .spans
116 .iter()
117 .map(|span| span.content.as_ref())
118 .collect();
119 if !found_dwarf_stats && line_text.starts_with("•") && line_text.contains("indexed")
120 {
121 found_dwarf_stats = true;
122 enhanced_lines.push(Line::from("")); enhanced_lines.push(Line::from(Span::styled(
124 format!("Attached to process {pid}"),
125 Style::default().fg(Color::White),
126 )));
127 }
129 }
130 styled_lines = enhanced_lines;
131 }
132
133 let action = Action::AddStyledWelcomeMessage {
137 styled_lines,
138 response_type: crate::action::ResponseType::Info,
139 };
140 if let Err(e) = self.handle_action(action) {
141 tracing::error!("Failed to add completion summary: {}", e);
142 }
143 }
144
145 pub(super) async fn handle_runtime_status(&mut self, status: crate::events::RuntimeStatus) {
146 use crate::components::loading::LoadingState;
147 use crate::events::RuntimeStatus;
148
149 match &status {
151 RuntimeStatus::DwarfLoadingStarted => {
152 self.state.set_loading_state(LoadingState::LoadingSymbols {
153 progress: Some(0.0),
154 });
155 }
156 RuntimeStatus::DwarfLoadingCompleted { .. } => {
157 if self.state.ui.config.show_source_panel {
158 self.state
159 .set_loading_state(LoadingState::LoadingSourceCode);
160 } else {
161 self.transition_to_ready_with_completion();
163 tracing::debug!(
165 "Source panel hidden on startup; requesting file list for completion cache"
166 );
167 if let Err(e) = self
168 .state
169 .event_registry
170 .command_sender
171 .send(crate::events::RuntimeCommand::InfoSource)
172 {
173 tracing::warn!("Failed to auto-request file list: {}", e);
174 }
175 }
176 }
177 RuntimeStatus::DwarfLoadingFailed(error) => {
178 self.state
179 .set_loading_state(LoadingState::Failed(error.clone()));
180 }
181 RuntimeStatus::DwarfModuleDiscovered {
183 module_path,
184 total_modules: _,
185 } => {
186 self.state
188 .loading_ui
189 .progress
190 .add_module(module_path.clone());
191 }
192 RuntimeStatus::DwarfModuleLoadingStarted {
193 module_path,
194 current,
195 total,
196 } => {
197 self.state
199 .loading_ui
200 .progress
201 .start_module_loading(module_path);
202 let progress = (*current as f64) / (*total as f64);
204 self.state.set_loading_state(LoadingState::LoadingSymbols {
205 progress: Some(progress),
206 });
207 }
208 RuntimeStatus::DwarfModuleLoadingCompleted {
209 module_path,
210 stats,
211 current,
212 total,
213 } => {
214 let module_stats = crate::components::loading::ModuleStats {
216 functions: stats.functions,
217 variables: stats.variables,
218 types: stats.types,
219 debug_source: stats.debug_source.clone(),
220 debug_source_path: stats.debug_source_path.clone(),
221 };
222 self.state
223 .loading_ui
224 .progress
225 .complete_module(module_path, module_stats);
226 let progress = (*current as f64) / (*total as f64);
228 self.state.set_loading_state(LoadingState::LoadingSymbols {
229 progress: Some(progress),
230 });
231 }
232 RuntimeStatus::DwarfModuleLoadingFailed {
233 module_path,
234 error,
235 current: _,
236 total: _,
237 } => {
238 self.state
240 .loading_ui
241 .progress
242 .fail_module(module_path, error.clone());
243 }
244 RuntimeStatus::SourceCodeLoaded(_) => {
245 self.transition_to_ready_with_completion();
247 }
248 RuntimeStatus::SourceCodeLoadFailed(error) => {
249 self.state
250 .set_loading_state(LoadingState::Failed(error.clone()));
251
252 crate::components::source_panel::SourceNavigation::show_error_message(
254 &mut self.state.source_panel,
255 error.clone(),
256 );
257 }
258 _ => {
259 if matches!(self.state.loading_state, LoadingState::Initializing) {
261 self.state
262 .set_loading_state(LoadingState::ConnectingToRuntime);
263 }
264 }
265 }
266
267 match status {
268 RuntimeStatus::SourceCodeLoaded(source_info) => {
269 let actions = crate::components::source_panel::SourceNavigation::load_source(
271 &mut self.state.source_panel,
272 source_info.file_path,
273 source_info.current_line,
274 );
275 for action in actions {
276 let _ = self.handle_action(action);
277 }
278
279 tracing::debug!("Auto-requesting file list after source code loaded");
281 if let Err(e) = self
282 .state
283 .event_registry
284 .command_sender
285 .send(crate::events::RuntimeCommand::InfoSource)
286 {
287 tracing::warn!("Failed to auto-request file list: {}", e);
288 }
289 }
290 RuntimeStatus::FileInfo { groups } => {
291 let mut files = Vec::new();
293 for group in &groups {
294 for file in &group.files {
295 let full_path = if file.directory.is_empty() {
297 file.path.clone()
298 } else {
299 format!("{}/{}", file.directory, file.path)
300 };
301 files.push(full_path);
302 }
303 }
304
305 self.sync_files_to_command_panel(files.clone());
307
308 if self.state.route_file_info_to_file_search {
309 if let Some(ref mut cache) = self.state.command_panel.file_completion_cache {
311 let actions =
312 crate::components::source_panel::SourceSearch::set_file_search_files(
313 &mut self.state.source_panel,
314 cache,
315 files.clone(),
316 );
317 for action in actions {
318 let _ = self.handle_action(action);
319 }
320 }
321
322 self.state.route_file_info_to_file_search = false;
324 } else {
325 self.clear_waiting_state();
327 let response =
328 crate::components::command_panel::ResponseFormatter::format_file_info(
329 &groups, false,
330 );
331 let styled_lines = crate::components::command_panel::ResponseFormatter::format_file_info_styled(
332 &groups, false,
333 );
334 let action = Action::AddResponseWithStyle {
335 content: response,
336 styled_lines: Some(styled_lines),
337 response_type: crate::action::ResponseType::Info,
338 };
339 let _ = self.handle_action(action);
340 }
341 }
342 RuntimeStatus::FileInfoFailed { error } => {
343 if self.state.route_file_info_to_file_search {
344 let actions =
345 crate::components::source_panel::SourceSearch::set_file_search_error(
346 &mut self.state.source_panel,
347 error,
348 );
349 for action in actions {
350 let _ = self.handle_action(action);
351 }
352 self.state.route_file_info_to_file_search = false;
353 } else {
354 self.clear_waiting_state();
355 let plain = format!("✗ Failed to get file information: {error}");
356 let styled = vec![
357 crate::components::command_panel::style_builder::StyledLineBuilder::new()
358 .styled(plain.clone(), crate::components::command_panel::style_builder::StylePresets::ERROR)
359 .build(),
360 ];
361 let action = Action::AddResponseWithStyle {
362 content: plain,
363 styled_lines: Some(styled),
364 response_type: crate::action::ResponseType::Error,
365 };
366 let _ = self.handle_action(action);
367 }
368 }
369 RuntimeStatus::InfoFunctionResult {
370 target: _,
371 info,
372 verbose,
373 } => {
374 self.clear_waiting_state();
376 let formatted_info = info.format_for_display(verbose);
378 let styled_lines = info.format_for_display_styled(verbose);
379 let action = Action::AddResponseWithStyle {
380 content: formatted_info,
381 styled_lines: Some(styled_lines),
382 response_type: crate::action::ResponseType::Success,
383 };
384 let _ = self.handle_action(action);
385 }
386 RuntimeStatus::InfoFunctionFailed { target, error } => {
387 self.clear_waiting_state();
388 let text = format!("✗ Failed to get debug info for function '{target}': {error}");
389 let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
390 let action = Action::AddResponseWithStyle {
391 content: text,
392 styled_lines: Some(styled),
393 response_type: crate::action::ResponseType::Error,
394 };
395 let _ = self.handle_action(action);
396 }
397 RuntimeStatus::InfoLineResult {
398 target: _,
399 info,
400 verbose,
401 } => {
402 self.clear_waiting_state();
404 let formatted_info = info.format_for_display(verbose);
406 let styled_lines = info.format_for_display_styled(verbose);
407 let action = Action::AddResponseWithStyle {
408 content: formatted_info,
409 styled_lines: Some(styled_lines),
410 response_type: crate::action::ResponseType::Success,
411 };
412 let _ = self.handle_action(action);
413 }
414 RuntimeStatus::InfoLineFailed { target, error } => {
415 self.clear_waiting_state();
416 let text = format!("✗ Failed to get debug info for line '{target}': {error}");
417 let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
418 let action = Action::AddResponseWithStyle {
419 content: text,
420 styled_lines: Some(styled),
421 response_type: crate::action::ResponseType::Error,
422 };
423 let _ = self.handle_action(action);
424 }
425 RuntimeStatus::InfoAddressResult {
426 target: _,
427 info,
428 verbose,
429 } => {
430 self.clear_waiting_state();
432 let formatted_info = info.format_for_display(verbose);
434 let styled_lines = info.format_for_display_styled(verbose);
435 let action = Action::AddResponseWithStyle {
436 content: formatted_info,
437 styled_lines: Some(styled_lines),
438 response_type: crate::action::ResponseType::Success,
439 };
440 let _ = self.handle_action(action);
441 }
442 RuntimeStatus::InfoAddressFailed { target, error } => {
443 self.clear_waiting_state();
444 let text = format!("✗ Failed to get debug info for address '{target}': {error}");
445 let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
446 let action = Action::AddResponseWithStyle {
447 content: text,
448 styled_lines: Some(styled),
449 response_type: crate::action::ResponseType::Error,
450 };
451 let _ = self.handle_action(action);
452 }
453 RuntimeStatus::ShareInfo { libraries } => {
454 let show_all = matches!(
456 self.state.command_panel.input_state,
457 crate::model::panel_state::InputState::WaitingResponse {
458 command_type: crate::model::panel_state::CommandType::InfoShareAll,
459 ..
460 }
461 );
462
463 self.clear_waiting_state();
464
465 let total = libraries.len();
466 let display_libs: Vec<_> = if show_all {
467 libraries
468 } else {
469 libraries
470 .into_iter()
471 .filter(|l| l.debug_info_available)
472 .collect()
473 };
474
475 if !show_all && display_libs.is_empty() && total > 0 {
477 let content = format!(
478 "📚 Shared Libraries ({total} total)\n\n⚠️ No libraries with debug info found. Use 'info share all' to view all libraries."
479 );
480 let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&content);
481 let action = Action::AddResponseWithStyle {
482 content,
483 styled_lines: Some(styled),
484 response_type: crate::action::ResponseType::Success,
485 };
486 let _ = self.handle_action(action);
487 } else {
488 let formatted_info =
489 crate::components::command_panel::ResponseFormatter::format_shared_library_info(
490 &display_libs, false,
491 );
492 let styled_lines =
493 crate::components::command_panel::ResponseFormatter::format_shared_library_info_styled(
494 &display_libs,
495 false,
496 );
497 let action = Action::AddResponseWithStyle {
498 content: formatted_info,
499 styled_lines: Some(styled_lines),
500 response_type: crate::action::ResponseType::Success,
501 };
502 let _ = self.handle_action(action);
503 }
504 }
505 RuntimeStatus::ShareInfoFailed { error } => {
506 self.clear_waiting_state();
507 let text = format!("✗ Failed to get shared library information: {error}");
508 let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
509 let action = Action::AddResponseWithStyle {
510 content: text,
511 styled_lines: Some(styled),
512 response_type: crate::action::ResponseType::Error,
513 };
514 let _ = self.handle_action(action);
515 }
516 RuntimeStatus::ExecutableFileInfo {
517 file_path,
518 file_type,
519 entry_point,
520 has_symbols,
521 has_debug_info,
522 debug_file_path,
523 text_section,
524 data_section,
525 mode_description,
526 } => {
527 self.clear_waiting_state();
528 let info_display =
529 crate::components::command_panel::response_formatter::ExecutableFileInfoDisplay {
530 file_path: &file_path,
531 file_type: &file_type,
532 entry_point,
533 has_symbols,
534 has_debug_info,
535 debug_file_path: &debug_file_path,
536 text_section: &text_section,
537 data_section: &data_section,
538 mode_description: &mode_description,
539 };
540 let formatted_info =
541 crate::components::command_panel::ResponseFormatter::format_executable_file_info(
542 &info_display,
543 );
544 let styled_lines =
545 crate::components::command_panel::ResponseFormatter::format_executable_file_info_styled(
546 &info_display,
547 );
548 let action = Action::AddResponseWithStyle {
549 content: formatted_info,
550 styled_lines: Some(styled_lines),
551 response_type: crate::action::ResponseType::Success,
552 };
553 let _ = self.handle_action(action);
554 }
555 RuntimeStatus::ExecutableFileInfoFailed { error } => {
556 self.clear_waiting_state();
557 let text = format!("✗ Failed to get executable file information: {error}");
558 let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
559 let action = Action::AddResponseWithStyle {
560 content: text,
561 styled_lines: Some(styled),
562 response_type: crate::action::ResponseType::Error,
563 };
564 let _ = self.handle_action(action);
565 }
566 RuntimeStatus::SrcPathInfo { info } => {
567 self.clear_waiting_state();
568 let formatted = info.format_for_display();
569 let styled_lines = info.format_for_display_styled();
570 let action = Action::AddResponseWithStyle {
571 content: formatted,
572 styled_lines: Some(styled_lines),
573 response_type: crate::action::ResponseType::Info,
574 };
575 let _ = self.handle_action(action);
576 }
577 RuntimeStatus::SrcPathUpdated { message } => {
578 self.clear_waiting_state();
579
580 self.state.route_file_info_to_file_search = true;
583
584 let plain = format!("✅ {message}\n💡 Source code and file list reloading...");
585 let styled = vec![
586 crate::components::command_panel::style_builder::StyledLineBuilder::new()
587 .styled(
588 format!("✅ {message}"),
589 crate::components::command_panel::style_builder::StylePresets::SUCCESS,
590 )
591 .build(),
592 crate::components::command_panel::style_builder::StyledLineBuilder::new()
593 .styled(
594 "💡 Source code and file list reloading...",
595 crate::components::command_panel::style_builder::StylePresets::TIP,
596 )
597 .build(),
598 ];
599 let action = Action::AddResponseWithStyle {
600 content: plain,
601 styled_lines: Some(styled),
602 response_type: crate::action::ResponseType::Success,
603 };
604 let _ = self.handle_action(action);
605 }
606 RuntimeStatus::SrcPathFailed { error } => {
607 self.clear_waiting_state();
608 let text = format!(
609 "✗ {error}\n\n📘 No source available? You can hide the Source panel:\n ui source off # in UI command mode\n --no-source-panel # CLI flag\n [ui].show_source_panel=false # in config.toml"
610 );
611 let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
612 let action = Action::AddResponseWithStyle {
613 content: text,
614 styled_lines: Some(styled),
615 response_type: crate::action::ResponseType::Error,
616 };
617 let _ = self.handle_action(action);
618 }
619 RuntimeStatus::TraceInfo {
620 trace_id,
621 target,
622 status,
623 pid,
624 host_pid,
625 binary,
626 script_preview,
627 pc,
628 } => {
629 self.clear_waiting_state();
630
631 if let Some(colon_pos) = target.rfind(':') {
635 let file_part = &target[..colon_pos];
636 if let Ok(line_num) = target[colon_pos + 1..].parse::<usize>() {
637 self.state
639 .source_panel
640 .trace_locations
641 .insert(trace_id, (file_part.to_string(), line_num));
642
643 if self.state.source_panel.file_path.as_ref()
645 == Some(&file_part.to_string())
646 {
647 if self.state.source_panel.pending_trace_line == Some(line_num) {
649 self.state.source_panel.pending_trace_line = None;
650 }
651
652 match status {
654 crate::events::TraceStatus::Active => {
655 self.state.source_panel.disabled_lines.remove(&line_num);
656 self.state.source_panel.traced_lines.insert(line_num);
657 }
658 crate::events::TraceStatus::Disabled => {
659 self.state.source_panel.traced_lines.remove(&line_num);
660 self.state.source_panel.disabled_lines.insert(line_num);
661 }
662 _ => {
663 self.state.source_panel.traced_lines.remove(&line_num);
665 self.state.source_panel.disabled_lines.remove(&line_num);
666 }
667 }
668 }
669 }
670 }
671
672 let mut response = format!("🔍 Trace {trace_id} Info:\n");
674 response.push_str(&format!(" Target: {target}\n"));
675 response.push_str(&format!(" Status: {status}\n"));
676 response.push_str(&format!(" Binary: {binary}\n"));
677 response.push_str(&format!(" PC: 0x{pc:x}\n"));
678 match (pid, host_pid) {
679 (Some(proc_pid), Some(host_pid_val)) if proc_pid != host_pid_val => {
680 response.push_str(&format!(" PID(proc): {proc_pid}\n"));
681 response.push_str(&format!(" PID(host): {host_pid_val}\n"));
682 }
683 (Some(proc_pid), _) => {
684 response.push_str(&format!(" PID: {proc_pid}\n"));
685 }
686 (None, Some(host_pid_val)) => {
687 response.push_str(&format!(" PID(host): {host_pid_val}\n"));
688 }
689 (None, None) => {}
690 }
691 if let Some(ref preview) = script_preview {
692 response.push_str(&format!(" Script:\n{preview}\n"));
693 }
694 let styled_lines = {
696 let temp = crate::events::RuntimeStatus::TraceInfo {
697 trace_id,
698 target: target.clone(),
699 status: status.clone(),
700 pid,
701 host_pid,
702 binary: binary.clone(),
703 script_preview: None,
704 pc,
705 };
706 if let Some(mut base) = temp.format_trace_info_styled() {
707 if let Some(ref preview) = script_preview {
708 use crate::components::command_panel::style_builder::StyledLineBuilder;
709 use ratatui::text::Line;
710 base.push(Line::from(""));
711 base.push(StyledLineBuilder::new().key("📝 Script:").build());
712 for line in preview.lines() {
713 base.push(StyledLineBuilder::new().text(" ").value(line).build());
714 }
715 }
716 Some(base)
717 } else {
718 None
719 }
720 };
721
722 let action = Action::AddResponseWithStyle {
723 content: response,
724 styled_lines,
725 response_type: crate::action::ResponseType::Info,
726 };
727 let _ = self.handle_action(action);
728 }
729 RuntimeStatus::TraceInfoAll { summary, traces } => {
730 self.clear_waiting_state();
731
732 for trace in &traces {
734 if let Some(colon_pos) = trace.target_display.rfind(':') {
737 let file_part = &trace.target_display[..colon_pos];
738 if let Ok(line_num) = trace.target_display[colon_pos + 1..].parse::<usize>()
739 {
740 self.state
742 .source_panel
743 .trace_locations
744 .insert(trace.trace_id, (file_part.to_string(), line_num));
745
746 if self.state.source_panel.file_path.as_ref()
748 == Some(&file_part.to_string())
749 {
750 match trace.status {
751 crate::events::TraceStatus::Active => {
752 self.state.source_panel.disabled_lines.remove(&line_num);
753 self.state.source_panel.traced_lines.insert(line_num);
754 }
755 crate::events::TraceStatus::Disabled => {
756 self.state.source_panel.traced_lines.remove(&line_num);
757 self.state.source_panel.disabled_lines.insert(line_num);
758 }
759 crate::events::TraceStatus::Failed => {
760 self.state.source_panel.traced_lines.remove(&line_num);
761 self.state.source_panel.disabled_lines.remove(&line_num);
762 }
763 }
764 }
765 }
766 }
767 }
768
769 let mut response = format!(
770 "🔍 All Traces ({} total, {} active):\n\n",
771 summary.total, summary.active
772 );
773 for trace in &traces {
774 response.push_str(&format!(" {}\n", trace.format_line()));
776 }
777 let styled_lines = (crate::events::RuntimeStatus::TraceInfoAll {
779 summary: summary.clone(),
780 traces: traces.clone(),
781 })
782 .format_trace_info_styled()
783 .unwrap_or_default();
784 let action = Action::AddResponseWithStyle {
785 content: response,
786 styled_lines: if styled_lines.is_empty() {
787 None
788 } else {
789 Some(styled_lines)
790 },
791 response_type: crate::action::ResponseType::Info,
792 };
793 let _ = self.handle_action(action);
794 }
795 RuntimeStatus::TraceInfoFailed { trace_id, error } => {
796 self.clear_waiting_state();
797 let text = format!("✗ Failed to get info for trace {trace_id}: {error}");
798 let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
799 let action = Action::AddResponseWithStyle {
800 content: text,
801 styled_lines: Some(styled),
802 response_type: crate::action::ResponseType::Error,
803 };
804 let _ = self.handle_action(action);
805 }
806 RuntimeStatus::TraceEnabled { trace_id } => {
807 self.clear_waiting_state();
808
809 if let Some((file_path, line_num)) =
811 self.state.source_panel.trace_locations.get(&trace_id)
812 {
813 if self.state.source_panel.file_path.as_ref() == Some(file_path) {
814 self.state.source_panel.disabled_lines.remove(line_num);
815 self.state.source_panel.traced_lines.insert(*line_num);
816 }
817 }
818
819 let text = format!("✅ Trace {trace_id} enabled");
820 let styled = vec![
821 crate::components::command_panel::style_builder::StyledLineBuilder::new()
822 .styled(
823 text.clone(),
824 crate::components::command_panel::style_builder::StylePresets::SUCCESS,
825 )
826 .build(),
827 ];
828 let action = Action::AddResponseWithStyle {
829 content: text,
830 styled_lines: Some(styled),
831 response_type: crate::action::ResponseType::Success,
832 };
833 let _ = self.handle_action(action);
834 }
835 RuntimeStatus::TraceDisabled { trace_id } => {
836 self.clear_waiting_state();
837
838 if let Some((file_path, line_num)) =
840 self.state.source_panel.trace_locations.get(&trace_id)
841 {
842 if self.state.source_panel.file_path.as_ref() == Some(file_path) {
843 self.state.source_panel.traced_lines.remove(line_num);
844 self.state.source_panel.disabled_lines.insert(*line_num);
845 }
846 }
847
848 let text = format!("✅ Trace {trace_id} disabled");
849 let styled = vec![
850 crate::components::command_panel::style_builder::StyledLineBuilder::new()
851 .styled(
852 text.clone(),
853 crate::components::command_panel::style_builder::StylePresets::SUCCESS,
854 )
855 .build(),
856 ];
857 let action = Action::AddResponseWithStyle {
858 content: text,
859 styled_lines: Some(styled),
860 response_type: crate::action::ResponseType::Success,
861 };
862 let _ = self.handle_action(action);
863 }
864 RuntimeStatus::AllTracesEnabled { count, error } => {
865 self.clear_waiting_state();
866
867 if error.is_none() {
868 for (file_path, line_num) in self.state.source_panel.trace_locations.values() {
870 if self.state.source_panel.file_path.as_ref() == Some(file_path) {
871 self.state.source_panel.disabled_lines.remove(line_num);
872 self.state.source_panel.traced_lines.insert(*line_num);
873 }
874 }
875 }
876
877 let (plain, rtype, style) = if let Some(ref err) = error {
878 (
879 format!("✗ Failed to enable traces: {err}"),
880 crate::action::ResponseType::Error,
881 crate::components::command_panel::style_builder::StylePresets::ERROR,
882 )
883 } else {
884 (
885 format!("✅ All traces enabled ({count} traces)"),
886 crate::action::ResponseType::Success,
887 crate::components::command_panel::style_builder::StylePresets::SUCCESS,
888 )
889 };
890 let styled = vec![
891 crate::components::command_panel::style_builder::StyledLineBuilder::new()
892 .styled(plain.clone(), style)
893 .build(),
894 ];
895 let action = Action::AddResponseWithStyle {
896 content: plain,
897 styled_lines: Some(styled),
898 response_type: rtype,
899 };
900 let _ = self.handle_action(action);
901 }
902 RuntimeStatus::AllTracesDisabled { count, error } => {
903 self.clear_waiting_state();
904
905 if error.is_none() {
906 for (file_path, line_num) in self.state.source_panel.trace_locations.values() {
908 if self.state.source_panel.file_path.as_ref() == Some(file_path) {
909 self.state.source_panel.traced_lines.remove(line_num);
910 self.state.source_panel.disabled_lines.insert(*line_num);
911 }
912 }
913 }
914
915 let (plain, rtype, style) = if let Some(ref err) = error {
916 (
917 format!("✗ Failed to disable traces: {err}"),
918 crate::action::ResponseType::Error,
919 crate::components::command_panel::style_builder::StylePresets::ERROR,
920 )
921 } else {
922 (
923 format!("✅ All traces disabled ({count} traces)"),
924 crate::action::ResponseType::Success,
925 crate::components::command_panel::style_builder::StylePresets::SUCCESS,
926 )
927 };
928 let styled = vec![
929 crate::components::command_panel::style_builder::StyledLineBuilder::new()
930 .styled(plain.clone(), style)
931 .build(),
932 ];
933 let action = Action::AddResponseWithStyle {
934 content: plain,
935 styled_lines: Some(styled),
936 response_type: rtype,
937 };
938 let _ = self.handle_action(action);
939 }
940 RuntimeStatus::TraceEnableFailed { trace_id, error } => {
941 self.clear_waiting_state();
942 let text = format!("✗ Failed to enable trace {trace_id}: {error}");
943 let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
944 let action = Action::AddResponseWithStyle {
945 content: text,
946 styled_lines: Some(styled),
947 response_type: crate::action::ResponseType::Error,
948 };
949 let _ = self.handle_action(action);
950 }
951 RuntimeStatus::TraceDisableFailed { trace_id, error } => {
952 self.clear_waiting_state();
953 let text = format!("✗ Failed to disable trace {trace_id}: {error}");
954 let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
955 let action = Action::AddResponseWithStyle {
956 content: text,
957 styled_lines: Some(styled),
958 response_type: crate::action::ResponseType::Error,
959 };
960 let _ = self.handle_action(action);
961 }
962 RuntimeStatus::TraceDeleted { trace_id } => {
963 self.clear_waiting_state();
964
965 if let Some((file_path, line_num)) =
967 self.state.source_panel.trace_locations.remove(&trace_id)
968 {
969 if self.state.source_panel.file_path.as_ref() == Some(&file_path) {
970 self.state.source_panel.traced_lines.remove(&line_num);
971 self.state.source_panel.disabled_lines.remove(&line_num);
972 }
973 }
974
975 let text = format!("✅ Trace {trace_id} deleted");
976 let styled = vec![
977 crate::components::command_panel::style_builder::StyledLineBuilder::new()
978 .styled(
979 text.clone(),
980 crate::components::command_panel::style_builder::StylePresets::SUCCESS,
981 )
982 .build(),
983 ];
984 let action = Action::AddResponseWithStyle {
985 content: text,
986 styled_lines: Some(styled),
987 response_type: crate::action::ResponseType::Success,
988 };
989 let _ = self.handle_action(action);
990 }
991 RuntimeStatus::AllTracesDeleted { count, error } => {
992 self.clear_waiting_state();
993
994 if error.is_none() {
995 self.state.source_panel.traced_lines.clear();
997 self.state.source_panel.disabled_lines.clear();
998 self.state.source_panel.trace_locations.clear();
999 }
1000
1001 let (plain, rtype, style) = if let Some(ref err) = error {
1002 (
1003 format!("✗ Failed to delete traces: {err}"),
1004 crate::action::ResponseType::Error,
1005 crate::components::command_panel::style_builder::StylePresets::ERROR,
1006 )
1007 } else {
1008 (
1009 format!("✅ All traces deleted ({count} traces)"),
1010 crate::action::ResponseType::Success,
1011 crate::components::command_panel::style_builder::StylePresets::SUCCESS,
1012 )
1013 };
1014 let styled = vec![
1015 crate::components::command_panel::style_builder::StyledLineBuilder::new()
1016 .styled(plain.clone(), style)
1017 .build(),
1018 ];
1019 let action = Action::AddResponseWithStyle {
1020 content: plain,
1021 styled_lines: Some(styled),
1022 response_type: rtype,
1023 };
1024 let _ = self.handle_action(action);
1025 }
1026 RuntimeStatus::TraceDeleteFailed { trace_id, error } => {
1027 self.clear_waiting_state();
1028 let text = format!("✗ Failed to delete trace {trace_id}: {error}");
1029 let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
1030 let action = Action::AddResponseWithStyle {
1031 content: text,
1032 styled_lines: Some(styled),
1033 response_type: crate::action::ResponseType::Error,
1034 };
1035 let _ = self.handle_action(action);
1036 }
1037 RuntimeStatus::TracesSaved {
1038 filename,
1039 saved_count,
1040 total_count,
1041 } => {
1042 self.clear_waiting_state();
1043 let mut text =
1044 format!("✅ Saved {saved_count} of {total_count} traces to {filename}\n");
1045 text.push_str(" • Selected indices are preserved in the save file\n");
1046
1047 use crate::components::command_panel::style_builder::{
1048 StylePresets, StyledLineBuilder,
1049 };
1050 let styled = vec![
1051 StyledLineBuilder::new()
1052 .styled(
1053 format!("✅ Saved {saved_count} of {total_count} traces to {filename}"),
1054 StylePresets::SUCCESS,
1055 )
1056 .build(),
1057 StyledLineBuilder::new()
1058 .text(" • ")
1059 .styled(
1060 "Selected indices are preserved in the save file",
1061 StylePresets::TIP,
1062 )
1063 .build(),
1064 ];
1065 let action = Action::AddResponseWithStyle {
1066 content: text,
1067 styled_lines: Some(styled),
1068 response_type: crate::action::ResponseType::Success,
1069 };
1070 let _ = self.handle_action(action);
1071 }
1072 RuntimeStatus::TracesSaveFailed { error } => {
1073 self.clear_waiting_state();
1074 let text = format!("✗ Failed to save traces: {error}");
1075 let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
1076 let action = Action::AddResponseWithStyle {
1077 content: text,
1078 styled_lines: Some(styled),
1079 response_type: crate::action::ResponseType::Error,
1080 };
1081 let _ = self.handle_action(action);
1082 }
1083 RuntimeStatus::TracesLoaded {
1084 filename,
1085 total_count,
1086 success_count,
1087 failed_count,
1088 disabled_count,
1089 details,
1090 } => {
1091 self.clear_waiting_state();
1092
1093 let mut response = String::new();
1095
1096 if failed_count == 0 {
1097 response.push_str(&format!(
1099 "✓ Loaded {} traces from {} ({} enabled, {} disabled)",
1100 total_count,
1101 filename,
1102 success_count - disabled_count,
1103 disabled_count
1104 ));
1105 response.push('\n');
1106 response.push_str(" • Selected indices from the file are restored\n");
1107 } else {
1108 response.push_str(&format!("⚠️ Partially loaded traces from {filename}\n"));
1110 response.push_str(&format!(
1111 " ✓ {} traces created ({} enabled, {} disabled)\n",
1112 success_count,
1113 success_count - disabled_count,
1114 disabled_count
1115 ));
1116 response
1117 .push_str(" • Selected indices from the file are restored when present\n");
1118
1119 for detail in &details {
1121 if let crate::events::LoadStatus::Failed = detail.status {
1122 if let Some(ref error) = detail.error {
1123 response.push_str(&format!(" ✗ {} - {}\n", detail.target, error));
1124 }
1125 }
1126 }
1127 }
1128
1129 let mut styled = Vec::new();
1131 use crate::components::command_panel::style_builder::{
1132 StylePresets, StyledLineBuilder,
1133 };
1134 if failed_count == 0 {
1135 styled.push(
1136 StyledLineBuilder::new()
1137 .styled(
1138 format!(
1139 "✅ Loaded {} traces from {} ({} enabled, {} disabled)",
1140 total_count,
1141 filename,
1142 success_count - disabled_count,
1143 disabled_count
1144 ),
1145 StylePresets::SUCCESS,
1146 )
1147 .build(),
1148 );
1149 styled.push(
1150 StyledLineBuilder::new()
1151 .text(" • ")
1152 .styled(
1153 "Selected indices from the file are restored",
1154 StylePresets::TIP,
1155 )
1156 .build(),
1157 );
1158 } else {
1159 styled.push(
1160 StyledLineBuilder::new()
1161 .styled(
1162 format!("⚠️ Partially loaded traces from {filename}"),
1163 StylePresets::WARNING,
1164 )
1165 .build(),
1166 );
1167 styled.push(
1168 StyledLineBuilder::new()
1169 .text(" ")
1170 .styled(
1171 format!(
1172 "✅ {} traces created ({} enabled, {} disabled)",
1173 success_count,
1174 success_count - disabled_count,
1175 disabled_count
1176 ),
1177 StylePresets::SUCCESS,
1178 )
1179 .build(),
1180 );
1181 styled.push(
1182 StyledLineBuilder::new()
1183 .text(" • ")
1184 .styled(
1185 "Selected indices from the file are restored when present",
1186 StylePresets::TIP,
1187 )
1188 .build(),
1189 );
1190 for detail in &details {
1191 if let crate::events::LoadStatus::Failed = detail.status {
1192 if let Some(ref err) = detail.error {
1193 styled.push(
1194 StyledLineBuilder::new()
1195 .text(" ")
1196 .styled(
1197 format!("✗ {} - {}", detail.target, err),
1198 StylePresets::ERROR,
1199 )
1200 .build(),
1201 );
1202 }
1203 }
1204 }
1205 }
1206
1207 let action = Action::AddResponseWithStyle {
1208 content: response,
1209 styled_lines: Some(styled),
1210 response_type: if failed_count == 0 {
1211 crate::action::ResponseType::Success
1212 } else {
1213 crate::action::ResponseType::Warning
1214 },
1215 };
1216 let _ = self.handle_action(action);
1217 }
1218 RuntimeStatus::TracesLoadFailed { filename, error } => {
1219 self.clear_waiting_state();
1220 let text = format!("✗ Failed to load {filename}: {error}");
1221 let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
1222 let action = Action::AddResponseWithStyle {
1223 content: text,
1224 styled_lines: Some(styled),
1225 response_type: crate::action::ResponseType::Error,
1226 };
1227 let _ = self.handle_action(action);
1228 }
1229 RuntimeStatus::TraceBackpressure {
1230 dropped_since_last,
1231 dropped_total,
1232 queue_capacity,
1233 } => {
1234 self.show_trace_backpressure_alert(
1235 dropped_since_last,
1236 dropped_total,
1237 queue_capacity,
1238 );
1239 }
1240 RuntimeStatus::EbpfOutputLoss {
1241 trace_id,
1242 target_display,
1243 lost_since_last,
1244 lost_total,
1245 } => {
1246 self.show_ebpf_output_loss_alert(
1247 trace_id,
1248 target_display,
1249 lost_since_last,
1250 lost_total,
1251 );
1252 }
1253 _ => {
1254 let should_clear_waiting = matches!(
1259 status,
1260 RuntimeStatus::AllTracesEnabled { .. }
1261 | RuntimeStatus::AllTracesDisabled { .. }
1262 | RuntimeStatus::AllTracesDeleted { .. }
1263 | RuntimeStatus::ScriptCompilationCompleted { .. }
1264 | RuntimeStatus::TraceInfoFailed { .. }
1265 | RuntimeStatus::FileInfoFailed { .. }
1266 | RuntimeStatus::ShareInfoFailed { .. }
1267 | RuntimeStatus::ExecutableFileInfoFailed { .. }
1268 | RuntimeStatus::SrcPathFailed { .. }
1269 );
1270
1271 if should_clear_waiting {
1272 self.clear_waiting_state();
1273 }
1274
1275 if let Some(content) = self.format_runtime_status_for_display(&status) {
1276 let styled_lines = if content.contains("\x1b[") {
1279 None
1280 } else {
1281 Some(crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&content))
1282 };
1283 let action = Action::AddResponseWithStyle {
1284 content,
1285 styled_lines,
1286 response_type: self.get_response_type_for_status(&status),
1287 };
1288 let _ = self.handle_action(action);
1289 }
1290 }
1291 }
1292 }
1293
1294 pub(super) async fn handle_trace_event(&mut self, trace_event: crate::events::UiTraceEvent) {
1296 tracing::debug!("Trace event: {:?}", trace_event);
1297
1298 if self.state.realtime_output_logger.enabled {
1300 if let Err(e) = self.write_ebpf_event_to_output_log(&trace_event) {
1301 tracing::error!("Failed to write eBPF event to output log: {}", e);
1302 }
1303 }
1304
1305 self.state.ebpf_panel.add_trace_event(trace_event);
1306 }
1307
1308 fn add_ebpf_runtime_warning(&mut self, content: String) {
1309 let event = crate::model::panel_state::EbpfPanelState::runtime_warning_event(
1310 content,
1311 current_boot_timestamp_ns(),
1312 );
1313
1314 if self.state.realtime_output_logger.enabled {
1315 if let Err(e) = self.write_ebpf_event_to_output_log(&event) {
1316 tracing::error!("Failed to write eBPF runtime warning to output log: {}", e);
1317 }
1318 }
1319
1320 self.state.ebpf_panel.add_trace_event(event);
1321 }
1322
1323 fn show_trace_backpressure_alert(
1324 &mut self,
1325 dropped_since_last: u64,
1326 dropped_total: u64,
1327 queue_capacity: usize,
1328 ) {
1329 let content = format!(
1330 "⚠ Trace queue saturated: dropped {dropped_since_last} events in last 1s (total {dropped_total}, capacity {queue_capacity})"
1331 );
1332 let styled_lines =
1333 crate::components::command_panel::ResponseFormatter::style_generic_message_lines(
1334 &content,
1335 );
1336 crate::components::command_panel::ResponseFormatter::upsert_runtime_alert_with_style(
1337 &mut self.state.command_panel,
1338 content,
1339 Some(styled_lines),
1340 crate::action::ResponseType::Warning,
1341 );
1342 self.state.command_renderer.mark_pending_updates();
1343
1344 self.add_ebpf_runtime_warning(format!(
1345 "Warning: TUI trace queue saturated; dropped {dropped_since_last} events before display in last 1s (total {dropped_total}, capacity {queue_capacity})"
1346 ));
1347 }
1348
1349 fn show_ebpf_output_loss_alert(
1350 &mut self,
1351 trace_id: u32,
1352 target_display: String,
1353 lost_since_last: u64,
1354 lost_total: u64,
1355 ) {
1356 let content = format!(
1357 "⚠ eBPF output helper failed: trace #{trace_id} ({target_display}) lost {lost_since_last} events in kernel before userspace delivery (total {lost_total})"
1358 );
1359 let styled_lines =
1360 crate::components::command_panel::ResponseFormatter::style_generic_message_lines(
1361 &content,
1362 );
1363 crate::components::command_panel::ResponseFormatter::upsert_runtime_alert_with_style(
1364 &mut self.state.command_panel,
1365 content,
1366 Some(styled_lines),
1367 crate::action::ResponseType::Warning,
1368 );
1369 self.state.command_renderer.mark_pending_updates();
1370
1371 self.add_ebpf_runtime_warning(format!(
1372 "Warning: eBPF output helper failed; trace #{trace_id} ({target_display}) lost {lost_since_last} events in kernel before userspace delivery (total {lost_total})"
1373 ));
1374 }
1375
1376 fn format_runtime_status_for_display(
1378 &mut self,
1379 status: &crate::events::RuntimeStatus,
1380 ) -> Option<String> {
1381 use crate::events::RuntimeStatus;
1382
1383 match status {
1384 RuntimeStatus::ScriptCompilationCompleted { details } => {
1385 if let Some(ref mut batch) = self.state.command_panel.batch_loading {
1387 batch.record_script_compilation(details);
1388
1389 if batch.completed_count >= batch.total_count {
1391 let filename = batch.filename.clone();
1393 let total_count = batch.total_count;
1394 let success_count = batch.success_count;
1395 let failed_count = batch.failed_count;
1396 let disabled_count = batch.disabled_count;
1397 let details = batch.details.clone();
1398
1399 self.state.command_panel.batch_loading = None;
1401
1402 self.clear_waiting_state();
1404
1405 let mut response = format!("📂 Loaded traces from {filename}\n");
1407 response.push_str(&format!(
1408 " Total: {total_count}, Success: {success_count}, Failed: {failed_count}"
1409 ));
1410 if disabled_count > 0 {
1411 response.push_str(&format!(", Disabled: {disabled_count}"));
1412 }
1413 response.push('\n');
1414
1415 if !details.is_empty() {
1417 response.push_str("\n📊 Details:\n");
1418 for detail in &details {
1419 match detail.status {
1420 crate::events::LoadStatus::Created => {
1421 if let Some(id) = detail.trace_id {
1422 response.push_str(&format!(
1423 " ✓ {} → trace #{}\n",
1424 detail.target, id
1425 ));
1426 } else {
1427 response.push_str(&format!(" ✓ {}\n", detail.target));
1428 }
1429 }
1430 crate::events::LoadStatus::CreatedDisabled => {
1431 if let Some(id) = detail.trace_id {
1432 response.push_str(&format!(
1433 " ⊘ {} → trace #{} (disabled)\n",
1434 detail.target, id
1435 ));
1436 } else {
1437 response.push_str(&format!(
1438 " ⊘ {} (disabled)\n",
1439 detail.target
1440 ));
1441 }
1442 }
1443 crate::events::LoadStatus::Failed => {
1444 if let Some(ref error) = detail.error {
1445 response.push_str(&format!(
1446 " ✗ {}: {}\n",
1447 detail.target, error
1448 ));
1449 } else {
1450 response.push_str(&format!(" ✗ {}\n", detail.target));
1451 }
1452 }
1453 _ => {}
1454 }
1455 }
1456 }
1457
1458 let styled_lines =
1460 crate::components::command_panel::ResponseFormatter::format_batch_load_summary_styled(
1461 &filename,
1462 total_count,
1463 success_count,
1464 failed_count,
1465 disabled_count,
1466 &details,
1467 );
1468
1469 let action = Action::AddResponseWithStyle {
1470 content: response,
1471 styled_lines: Some(styled_lines),
1472 response_type: if failed_count > 0 {
1473 crate::action::ResponseType::Warning
1474 } else {
1475 crate::action::ResponseType::Success
1476 },
1477 };
1478 let _ = self.handle_action(action);
1479
1480 return None;
1482 } else {
1483 return None;
1485 }
1486 }
1487
1488 self.clear_waiting_state();
1491
1492 if details.success_count > 0 || details.failed_count > 0 {
1494 let script_content = self
1496 .state
1497 .command_panel
1498 .script_cache
1499 .as_ref()
1500 .map(|cache| cache.lines.join("\n"));
1501
1502 Some(crate::components::command_panel::script_editor::ScriptEditor::format_compilation_results(
1504 details,
1505 script_content.as_deref(),
1506 &self.state.emoji_config,
1507 ))
1508 } else {
1509 let first_failed = details.results.first();
1511 if let Some(result) = first_failed {
1512 if let crate::events::ExecutionStatus::Failed(error) = &result.status {
1513 let error_details = crate::components::command_panel::script_editor::TraceErrorDetails {
1514 compilation_errors: None,
1515 uprobe_error: Some(error.clone()),
1516 suggestion: Some("Check function name and ensure binary has debug symbols".to_string()),
1517 };
1518
1519 let script_content = self
1521 .state
1522 .command_panel
1523 .script_cache
1524 .as_ref()
1525 .map(|cache| cache.lines.join("\n"));
1526
1527 Some(crate::components::command_panel::script_editor::ScriptEditor::format_trace_error_response_with_script(
1528 &result.target_name,
1529 error,
1530 Some(&error_details),
1531 script_content.as_deref(),
1532 &self.state.emoji_config,
1533 ))
1534 } else {
1535 None
1536 }
1537 } else {
1538 None
1539 }
1540 }
1541 }
1542 RuntimeStatus::AllTracesEnabled { count, error } => {
1543 if let Some(ref err) = error {
1544 let error_emoji = self
1545 .state
1546 .emoji_config
1547 .get_script_status(crate::ui::emoji::ScriptStatus::Error);
1548 Some(format!("{error_emoji} {err}"))
1549 } else if *count > 0 {
1550 let success_emoji = self
1551 .state
1552 .emoji_config
1553 .get_trace_status(crate::ui::emoji::TraceStatusType::Active);
1554 Some(format!("{success_emoji} Enabled {count} traces"))
1555 } else {
1556 None
1557 }
1558 }
1559 RuntimeStatus::AllTracesDisabled { count, error } => {
1560 if let Some(ref err) = error {
1561 let error_emoji = self
1562 .state
1563 .emoji_config
1564 .get_script_status(crate::ui::emoji::ScriptStatus::Error);
1565 Some(format!("{error_emoji} {err}"))
1566 } else if *count > 0 {
1567 let disabled_emoji = self
1568 .state
1569 .emoji_config
1570 .get_trace_status(crate::ui::emoji::TraceStatusType::Disabled);
1571 Some(format!("{disabled_emoji} Disabled {count} traces"))
1572 } else {
1573 None
1574 }
1575 }
1576 RuntimeStatus::AllTracesDeleted { count, error } => {
1577 if let Some(ref err) = error {
1578 let error_emoji = self
1579 .state
1580 .emoji_config
1581 .get_script_status(crate::ui::emoji::ScriptStatus::Error);
1582 Some(format!("{error_emoji} {err}"))
1583 } else if *count > 0 {
1584 Some(format!("✓ Deleted {count} traces"))
1585 } else {
1586 None
1587 }
1588 }
1589 RuntimeStatus::TraceEnabled { trace_id } => {
1590 let success_emoji = self
1591 .state
1592 .emoji_config
1593 .get_trace_status(crate::ui::emoji::TraceStatusType::Active);
1594 Some(format!("{success_emoji} Trace {trace_id} enabled"))
1595 }
1596 RuntimeStatus::TraceDisabled { trace_id } => {
1597 let disabled_emoji = self
1598 .state
1599 .emoji_config
1600 .get_trace_status(crate::ui::emoji::TraceStatusType::Disabled);
1601 Some(format!("{disabled_emoji} Trace {trace_id} disabled"))
1602 }
1603 _ => None, }
1605 }
1606
1607 fn get_response_type_for_status(
1609 &self,
1610 status: &crate::events::RuntimeStatus,
1611 ) -> crate::action::ResponseType {
1612 use crate::events::RuntimeStatus;
1613
1614 match status {
1615 RuntimeStatus::ScriptCompilationCompleted { details } => {
1616 if details.success_count > 0 {
1618 crate::action::ResponseType::Success
1619 } else {
1620 crate::action::ResponseType::Error
1621 }
1622 }
1623 RuntimeStatus::AllTracesEnabled { error, .. }
1624 | RuntimeStatus::AllTracesDisabled { error, .. }
1625 | RuntimeStatus::AllTracesDeleted { error, .. } => {
1626 if error.is_some() {
1627 crate::action::ResponseType::Error
1628 } else {
1629 crate::action::ResponseType::Success
1630 }
1631 }
1632 _ => crate::action::ResponseType::Info,
1633 }
1634 }
1635
1636 pub(super) fn clear_waiting_state(&mut self) {
1638 self.state.command_panel.input_state = crate::model::panel_state::InputState::Ready;
1639 }
1640}
1641
1642fn current_boot_timestamp_ns() -> u64 {
1643 std::fs::read_to_string("/proc/uptime")
1644 .ok()
1645 .and_then(|contents| {
1646 contents
1647 .split_whitespace()
1648 .next()
1649 .and_then(|secs| secs.parse::<f64>().ok())
1650 })
1651 .map(|secs| (secs * 1_000_000_000.0) as u64)
1652 .unwrap_or(0)
1653}