ghostscope_ui/components/
app.rs

1use crate::action::{Action, PanelType};
2use crate::components::loading::{LoadingState, LoadingUI};
3use crate::events::EventRegistry;
4use crate::model::ui_state::LayoutMode;
5use crate::model::AppState;
6use anyhow::Result;
7use crossterm::{
8    event::{
9        DisableBracketedPaste, EnableBracketedPaste, Event, EventStream, KeyCode, KeyEventKind,
10    },
11    execute,
12    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
13};
14use futures_util::StreamExt;
15use ratatui::{
16    backend::CrosstermBackend,
17    layout::{Constraint, Direction, Layout, Rect},
18    widgets::{Block, BorderType, Borders},
19    Frame, Terminal,
20};
21use std::io;
22use tracing::debug;
23
24/// Modern TUI application using TEA architecture
25pub struct App {
26    terminal: Terminal<CrosstermBackend<io::Stdout>>,
27    state: AppState,
28    should_quit: bool,
29}
30
31impl App {
32    /// Create a new application instance
33    pub async fn new(event_registry: EventRegistry, layout_mode: LayoutMode) -> Result<Self> {
34        // Setup terminal
35        enable_raw_mode()?;
36        let mut stdout = io::stdout();
37        execute!(stdout, EnterAlternateScreen)?;
38        // Enable bracketed paste to detect paste events (does not affect mouse selection copy)
39        execute!(stdout, EnableBracketedPaste)?;
40        // Mouse capture disabled to allow standard copy/paste functionality
41        let backend = CrosstermBackend::new(stdout);
42        let terminal = Terminal::new(backend)?;
43
44        let mut state = AppState::new(event_registry, layout_mode);
45
46        // Request initial source code on startup if source panel is enabled
47        if state.ui.config.show_source_panel {
48            if let Err(e) = state
49                .event_registry
50                .command_sender
51                .send(crate::events::RuntimeCommand::RequestSourceCode)
52            {
53                tracing::warn!("Failed to send initial source code request: {}", e);
54            } else {
55                // Move to connecting state since we've sent the request
56                state.set_loading_state(
57                    crate::components::loading::LoadingState::ConnectingToRuntime,
58                );
59            }
60        }
61
62        Ok(Self {
63            terminal,
64            state,
65            should_quit: false,
66        })
67    }
68
69    /// Create a new application instance with full UI configuration
70    pub async fn new_with_config(
71        event_registry: EventRegistry,
72        ui_config: crate::model::ui_state::UiConfig,
73    ) -> Result<Self> {
74        // Setup terminal
75        enable_raw_mode()?;
76        let mut stdout = io::stdout();
77        execute!(stdout, EnterAlternateScreen)?;
78        // Enable bracketed paste to detect paste events (does not affect mouse selection copy)
79        execute!(stdout, EnableBracketedPaste)?;
80        // Mouse capture disabled to allow standard copy/paste functionality
81        let backend = CrosstermBackend::new(stdout);
82        let terminal = Terminal::new(backend)?;
83
84        let mut state = AppState::new_with_config(event_registry, ui_config);
85
86        // Request initial source code on startup if source panel is enabled
87        if state.ui.config.show_source_panel {
88            if let Err(e) = state
89                .event_registry
90                .command_sender
91                .send(crate::events::RuntimeCommand::RequestSourceCode)
92            {
93                tracing::warn!("Failed to send initial source code request: {}", e);
94            } else {
95                // Move to connecting state since we've sent the request
96                state.set_loading_state(
97                    crate::components::loading::LoadingState::ConnectingToRuntime,
98                );
99            }
100        }
101
102        Ok(Self {
103            terminal,
104            state,
105            should_quit: false,
106        })
107    }
108
109    /// Main application loop
110    pub async fn run(&mut self) -> Result<()> {
111        debug!("Starting new TEA-based TUI application");
112
113        // Create async event stream (proper crossterm async support)
114        let mut event_stream = EventStream::new();
115        let mut needs_render = true;
116
117        // Create a timeout for loading - if no runtime response, go to ready
118        const LOADING_TIMEOUT_SECS: u64 = 30;
119        let loading_timeout =
120            tokio::time::sleep(tokio::time::Duration::from_secs(LOADING_TIMEOUT_SECS));
121        tokio::pin!(loading_timeout);
122
123        // Create a 1-second interval for loading UI updates (elapsed time, spinner, etc.)
124        let mut loading_ui_ticker = tokio::time::interval(tokio::time::Duration::from_secs(1));
125        loading_ui_ticker.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
126
127        // Initial render
128        self.terminal.draw(|f| Self::draw_ui(f, &mut self.state))?;
129
130        loop {
131            // Handle events using select! to monitor multiple sources
132            tokio::select! {
133                // Handle crossterm events (keyboard, mouse, resize) - proper async
134                Some(event_result) = event_stream.next() => {
135                    match event_result {
136                        Ok(event) => {
137                            if let Event::Key(key) = &event {
138                                tracing::debug!("Raw crossterm event: {:?}", key);
139                            }
140                            if let Err(e) = self.handle_event(event).await {
141                                tracing::error!("Error handling terminal event: {}", e);
142                            }
143                            needs_render = true;
144                        }
145                        Err(e) => {
146                            tracing::error!("Error reading terminal events: {}", e);
147                            break;
148                        }
149                    }
150                }
151
152                // Handle runtime status messages
153                Some(status) = self.state.event_registry.status_receiver.recv() => {
154                    self.handle_runtime_status(status).await;
155                    needs_render = true;
156                }
157
158                // Handle trace events
159                Some(trace_event) = self.state.event_registry.trace_receiver.recv() => {
160                    self.handle_trace_event(trace_event).await;
161                    needs_render = true;
162                }
163
164                // Loading timeout - show error in loading UI
165                () = &mut loading_timeout, if !self.state.loading_state.is_ready() && !self.state.loading_state.is_failed() => {
166                    tracing::info!("No runtime response after {} seconds, connection timeout", LOADING_TIMEOUT_SECS);
167                    self.state.set_loading_state(LoadingState::Failed("Connection timeout - no runtime response".to_string()));
168                    needs_render = true;
169                }
170
171                // Update loading UI periodically (elapsed time, spinner animation)
172                _ = loading_ui_ticker.tick(), if self.state.is_loading() => {
173                    // Just trigger a redraw to update elapsed time and spinner
174                    // No state changes needed - the UI will read fresh elapsed time on render
175                    needs_render = true;
176                }
177
178                // Check for jk escape sequence timeout periodically
179                _ = tokio::time::sleep(std::time::Duration::from_millis(50)) => {
180                    // Check jk timeout
181                    if crate::components::command_panel::input_handler::InputHandler::check_jk_timeout(&mut self.state.command_panel) {
182                        needs_render = true;
183                    }
184
185                    // Check for command response timeout
186                    if let crate::model::panel_state::InputState::WaitingResponse { sent_time, command, .. } = &self.state.command_panel.input_state {
187                        const COMMAND_TIMEOUT_SECS: u64 = 5;
188                        if sent_time.elapsed().as_secs() >= COMMAND_TIMEOUT_SECS {
189                            let timeout_msg = format!("Command timeout: '{command}' - no response after {COMMAND_TIMEOUT_SECS} seconds");
190                            self.clear_waiting_state();
191                            crate::components::command_panel::ResponseFormatter::add_simple_styled_response(
192                                &mut self.state.command_panel,
193                                timeout_msg,
194                                crate::components::command_panel::style_builder::StylePresets::ERROR,
195                                crate::action::ResponseType::Error,
196                            );
197                            needs_render = true;
198                        }
199                    }
200
201                    // Periodic cleanup of file completion cache
202                    self.state.command_panel.cleanup_file_completion_cache();
203                }
204            }
205
206            // Render only when needed (event-driven)
207            if needs_render {
208                self.terminal.draw(|f| Self::draw_ui(f, &mut self.state))?;
209                needs_render = false;
210            }
211
212            // Check for quit condition
213            if self.should_quit || self.state.should_quit {
214                break;
215            }
216        }
217
218        // Send shutdown command to runtime before cleanup
219        if let Err(e) = self
220            .state
221            .event_registry
222            .command_sender
223            .send(crate::events::RuntimeCommand::Shutdown)
224        {
225            tracing::warn!("Failed to send shutdown command to runtime: {}", e);
226        }
227
228        self.cleanup().await
229    }
230
231    /// Handle terminal events and convert to actions
232    async fn handle_event(&mut self, event: Event) -> Result<bool> {
233        let mut actions_to_process = Vec::new();
234
235        match event {
236            Event::Key(key) => {
237                tracing::debug!(
238                    "Event received: key={:?}, is_loading={}",
239                    key,
240                    self.state.is_loading()
241                );
242                if key.kind == KeyEventKind::Press {
243                    // Always handle input - loading state should not block user interaction
244                    // Loading is purely a visual indication
245
246                    // Handle window navigation mode first
247                    if self.state.ui.focus.expecting_window_nav {
248                        match key.code {
249                            KeyCode::Char('h') => {
250                                actions_to_process.push(Action::WindowNavMove(
251                                    crate::action::WindowDirection::Left,
252                                ));
253                                actions_to_process.push(Action::ExitWindowNavMode);
254                            }
255                            KeyCode::Char('j') => {
256                                actions_to_process.push(Action::WindowNavMove(
257                                    crate::action::WindowDirection::Down,
258                                ));
259                                actions_to_process.push(Action::ExitWindowNavMode);
260                            }
261                            KeyCode::Char('k') => {
262                                actions_to_process.push(Action::WindowNavMove(
263                                    crate::action::WindowDirection::Up,
264                                ));
265                                actions_to_process.push(Action::ExitWindowNavMode);
266                            }
267                            KeyCode::Char('l') => {
268                                actions_to_process.push(Action::WindowNavMove(
269                                    crate::action::WindowDirection::Right,
270                                ));
271                                actions_to_process.push(Action::ExitWindowNavMode);
272                            }
273                            KeyCode::Char('v') => {
274                                actions_to_process.push(Action::SwitchLayout);
275                                actions_to_process.push(Action::ExitWindowNavMode);
276                            }
277                            KeyCode::Char('z') => {
278                                actions_to_process.push(Action::ToggleFullscreen);
279                                actions_to_process.push(Action::ExitWindowNavMode);
280                            }
281                            _ => {
282                                // Any other key cancels window navigation
283                                actions_to_process.push(Action::ExitWindowNavMode);
284                            }
285                        }
286                    }
287
288                    // Normal key handling
289
290                    // Clear Ctrl+C flag for any key that's not Ctrl+C
291                    let is_ctrl_c = matches!(key.code, KeyCode::Char('c'))
292                        && key
293                            .modifiers
294                            .contains(crossterm::event::KeyModifiers::CONTROL);
295                    if !is_ctrl_c {
296                        self.state.expecting_second_ctrl_c = false;
297                    }
298
299                    match key.code {
300                        KeyCode::Char('c')
301                            if key
302                                .modifiers
303                                .contains(crossterm::event::KeyModifiers::CONTROL) =>
304                        {
305                            // Use the new centralized Ctrl+C handler
306                            let ctrl_c_actions = self.handle_ctrl_c();
307                            actions_to_process.extend(ctrl_c_actions);
308                        }
309                        KeyCode::Char('w')
310                            if key
311                                .modifiers
312                                .contains(crossterm::event::KeyModifiers::CONTROL) =>
313                        {
314                            // Handle Ctrl+W based on current focus and mode - priority order matters!
315                            if self.state.ui.focus.current_panel == crate::action::PanelType::Source
316                                && self.state.source_panel.mode
317                                    == crate::model::panel_state::SourcePanelMode::FileSearch
318                            {
319                                // HIGHEST PRIORITY: File search delete word
320                                if let Some(ref cache) =
321                                    self.state.command_panel.file_completion_cache
322                                {
323                                    let delete_actions = crate::components::source_panel::SourceSearch::delete_word_file_search(
324                                        &mut self.state.source_panel,
325                                        cache,
326                                    );
327                                    actions_to_process.extend(delete_actions);
328                                }
329                            } else if self.state.ui.focus.current_panel
330                                == crate::action::PanelType::InteractiveCommand
331                            {
332                                match self.state.command_panel.mode {
333                                    crate::model::panel_state::InteractionMode::Input => {
334                                        actions_to_process.push(Action::DeletePreviousWord);
335                                    }
336                                    crate::model::panel_state::InteractionMode::ScriptEditor => {
337                                        actions_to_process.push(Action::DeletePreviousWord);
338                                    }
339                                    _ => {
340                                        // In command mode, use for window navigation
341                                        actions_to_process.push(Action::EnterWindowNavMode);
342                                    }
343                                }
344                            } else {
345                                // In other panels, use for window navigation
346                                actions_to_process.push(Action::EnterWindowNavMode);
347                            }
348                        }
349                        KeyCode::Tab => {
350                            // Handle Tab based on current panel and mode - priority order matters!
351                            if self.state.ui.focus.current_panel == crate::action::PanelType::Source
352                                && self.state.source_panel.mode
353                                    == crate::model::panel_state::SourcePanelMode::FileSearch
354                            {
355                                // HIGHEST PRIORITY: File search navigation
356                                let move_actions = crate::components::source_panel::SourceSearch::move_file_search_down(
357                                        &mut self.state.source_panel,
358                                    );
359                                actions_to_process.extend(move_actions);
360                            } else if self.state.ui.focus.current_panel
361                                == crate::action::PanelType::InteractiveCommand
362                                && self.state.command_panel.mode
363                                    == crate::model::panel_state::InteractionMode::ScriptEditor
364                            {
365                                // Script editor Tab inserts spaces
366                                actions_to_process.push(Action::InsertTab);
367                            } else if self.state.ui.focus.current_panel
368                                == crate::action::PanelType::InteractiveCommand
369                                && self.state.command_panel.mode
370                                    == crate::model::panel_state::InteractionMode::Input
371                            {
372                                // COMMAND INPUT MODE: Let Tab go to focused panel handler for auto-suggestion
373                                let panel_actions = self.handle_focused_panel_input(key)?;
374                                actions_to_process.extend(panel_actions);
375                            } else {
376                                // Normal Tab behavior: cycle focus
377                                actions_to_process.push(Action::FocusNext);
378                            }
379                        }
380                        KeyCode::BackTab => {
381                            // Handle Shift+Tab based on current panel and mode
382                            if self.state.ui.focus.current_panel == crate::action::PanelType::Source
383                                && self.state.source_panel.mode
384                                    == crate::model::panel_state::SourcePanelMode::FileSearch
385                            {
386                                // HIGHEST PRIORITY: File search navigation (up)
387                                let move_actions = crate::components::source_panel::SourceSearch::move_file_search_up(
388                                        &mut self.state.source_panel,
389                                    );
390                                actions_to_process.extend(move_actions);
391                            } else {
392                                // Normal Shift+Tab behavior: cycle focus backward
393                                actions_to_process.push(Action::FocusPrevious);
394                            }
395                        }
396                        KeyCode::F(1) => {
397                            actions_to_process.push(Action::ToggleFullscreen);
398                        }
399                        KeyCode::F(2) => {
400                            actions_to_process.push(Action::SwitchLayout);
401                        }
402                        _ => {
403                            // Forward to focused panel handler
404                            let panel_actions = self.handle_focused_panel_input(key)?;
405                            actions_to_process.extend(panel_actions);
406                        }
407                    }
408                }
409            }
410            Event::Resize(width, height) => {
411                actions_to_process.push(Action::Resize(width, height));
412            }
413            Event::Paste(pasted) => {
414                tracing::debug!("Event received: paste_len={}", pasted.len());
415                // Batch insert pasted text depending on focused panel and mode
416                match self.state.ui.focus.current_panel {
417                    PanelType::InteractiveCommand => {
418                        match self.state.command_panel.mode {
419                            crate::model::panel_state::InteractionMode::Input => {
420                                let actions = self
421                                    .state
422                                    .command_input_handler
423                                    .insert_str(&mut self.state.command_panel, &pasted);
424                                actions_to_process.extend(actions);
425                                self.state.command_renderer.mark_pending_updates();
426                            }
427                            crate::model::panel_state::InteractionMode::ScriptEditor => {
428                                let actions =
429                                    crate::components::command_panel::ScriptEditor::insert_text(
430                                        &mut self.state.command_panel,
431                                        &pasted,
432                                    );
433                                actions_to_process.extend(actions);
434                                self.state.command_renderer.mark_pending_updates();
435                            }
436                            crate::model::panel_state::InteractionMode::Command => {
437                                // Ignore paste in command mode
438                            }
439                        }
440                    }
441                    _ => {
442                        // Ignore paste in other panels
443                    }
444                }
445            }
446            _ => {}
447        }
448
449        // Process all actions
450        for action in actions_to_process {
451            let is_quit = matches!(action, Action::Quit);
452            let additional_actions = self.handle_action(action)?;
453
454            // Process any additional actions returned
455            for additional_action in additional_actions {
456                self.handle_action(additional_action)?;
457            }
458
459            if is_quit || self.state.should_quit {
460                return Ok(true);
461            }
462        }
463
464        Ok(false)
465    }
466
467    /// Handle input for the currently focused panel
468    fn handle_focused_panel_input(
469        &mut self,
470        key: crossterm::event::KeyEvent,
471    ) -> Result<Vec<Action>> {
472        let mut actions = Vec::new();
473
474        match self.state.ui.focus.current_panel {
475            PanelType::InteractiveCommand => {
476                // First, try the new unified key event handler for history and suggestions
477                let unified_actions = self
478                    .state
479                    .command_input_handler
480                    .handle_key_event(&mut self.state.command_panel, key);
481
482                if !unified_actions.is_empty() {
483                    // The unified handler handled the key, mark for updates and return
484                    self.state.command_renderer.mark_pending_updates();
485                    return Ok(unified_actions);
486                }
487
488                // Fall back to existing character-based handling
489                match key.code {
490                    KeyCode::Char(c) => {
491                        tracing::debug!(
492                            "App received char='{}' (code={}), modifiers={:?}, current_panel={:?}",
493                            c,
494                            c as u32,
495                            key.modifiers,
496                            self.state.ui.focus.current_panel
497                        );
498                        // Handle Ctrl+key combinations first
499                        if key
500                            .modifiers
501                            .contains(crossterm::event::KeyModifiers::CONTROL)
502                        {
503                            match c {
504                                's' => {
505                                    // Ctrl+S - only submit script in script mode
506                                    if matches!(
507                                        self.state.command_panel.mode,
508                                        crate::model::panel_state::InteractionMode::ScriptEditor
509                                    ) {
510                                        actions.push(Action::SubmitScript);
511                                    }
512                                }
513                                'a' => {
514                                    match self.state.command_panel.mode {
515                                        crate::model::panel_state::InteractionMode::ScriptEditor => {
516                                            // Ctrl+A - move to beginning of current line in script mode
517                                            let script_actions = crate::components::command_panel::ScriptEditor::move_to_beginning(
518                                                &mut self.state.command_panel,
519                                            );
520                                            actions.extend(script_actions);
521                                        }
522                                        _ => {
523                                            // Ctrl+A - move to beginning of line in input/command mode
524                                            actions.push(Action::MoveCursor(crate::action::CursorDirection::Home));
525                                        }
526                                    }
527                                }
528                                'e' => {
529                                    match self.state.command_panel.mode {
530                                        crate::model::panel_state::InteractionMode::ScriptEditor => {
531                                            // Ctrl+E - move to end of current line in script mode
532                                            let script_actions = crate::components::command_panel::ScriptEditor::move_to_end(
533                                                &mut self.state.command_panel,
534                                            );
535                                            actions.extend(script_actions);
536                                        }
537                                        _ => {
538                                            // Ctrl+E - move to end of line in input/command mode
539                                            actions.push(Action::MoveCursor(crate::action::CursorDirection::End));
540                                        }
541                                    }
542                                }
543                                'f' => {
544                                    match self.state.command_panel.mode {
545                                        crate::model::panel_state::InteractionMode::ScriptEditor => {
546                                            // Ctrl+F - move cursor right (forward one character) in script mode
547                                            let script_actions = crate::components::command_panel::ScriptEditor::move_cursor_right(
548                                                &mut self.state.command_panel,
549                                            );
550                                            actions.extend(script_actions);
551                                        }
552                                        _ => {
553                                            // Ctrl+F - move cursor right in input/command mode
554                                            actions.push(Action::MoveCursor(crate::action::CursorDirection::Right));
555                                        }
556                                    }
557                                }
558                                'b' => {
559                                    match self.state.command_panel.mode {
560                                        crate::model::panel_state::InteractionMode::ScriptEditor => {
561                                            // Ctrl+B - move cursor left (back one character) in script mode
562                                            let script_actions = crate::components::command_panel::ScriptEditor::move_cursor_left(
563                                                &mut self.state.command_panel,
564                                            );
565                                            actions.extend(script_actions);
566                                        }
567                                        _ => {
568                                            // Ctrl+B - move cursor left in input/command mode
569                                            actions.push(Action::MoveCursor(crate::action::CursorDirection::Left));
570                                        }
571                                    }
572                                }
573                                'u' => {
574                                    match self.state.command_panel.mode {
575                                        crate::model::panel_state::InteractionMode::ScriptEditor => {
576                                            // Ctrl+U - delete from cursor to line start in script mode
577                                            let script_actions = crate::components::command_panel::ScriptEditor::delete_to_line_start(
578                                                &mut self.state.command_panel,
579                                            );
580                                            actions.extend(script_actions);
581                                        }
582                                        crate::model::panel_state::InteractionMode::Command => {
583                                            // Ctrl+U - half page up in command mode (fast scroll)
584                                            actions.push(Action::CommandHalfPageUp);
585                                        }
586                                        _ => {
587                                            // Ctrl+U - delete to beginning in input mode
588                                            actions.push(Action::DeleteToBeginning);
589                                        }
590                                    }
591                                }
592                                'd' => {
593                                    match self.state.command_panel.mode {
594                                        crate::model::panel_state::InteractionMode::Command => {
595                                            // Ctrl+D - half page down in command mode (fast scroll)
596                                            actions.push(Action::CommandHalfPageDown);
597                                        }
598                                        _ => {
599                                            // Ctrl+D might be used for other purposes in other modes
600                                        }
601                                    }
602                                }
603                                'k' => {
604                                    match self.state.command_panel.mode {
605                                        crate::model::panel_state::InteractionMode::ScriptEditor => {
606                                            // Ctrl+K - delete from cursor to line end in script mode
607                                            let script_actions = crate::components::command_panel::ScriptEditor::delete_to_end(
608                                                &mut self.state.command_panel,
609                                            );
610                                            actions.extend(script_actions);
611                                        }
612                                        _ => {
613                                            // Ctrl+K - delete to end in input/command mode
614                                            actions.push(Action::DeleteToEnd);
615                                        }
616                                    }
617                                }
618                                'w' => {
619                                    match self.state.command_panel.mode {
620                                        crate::model::panel_state::InteractionMode::ScriptEditor => {
621                                            // Ctrl+W - delete previous word in script mode
622                                            let script_actions = crate::components::command_panel::ScriptEditor::delete_previous_word(
623                                                &mut self.state.command_panel,
624                                            );
625                                            actions.extend(script_actions);
626                                        }
627                                        _ => {
628                                            // Ctrl+W - delete previous word in input/command mode
629                                            actions.push(Action::DeletePreviousWord);
630                                        }
631                                    }
632                                }
633                                'p' => {
634                                    match self.state.command_panel.mode {
635                                        crate::model::panel_state::InteractionMode::Input => {
636                                            // Ctrl+P - go to previous command in input mode
637                                            actions.push(Action::HistoryPrevious);
638                                        }
639                                        crate::model::panel_state::InteractionMode::ScriptEditor => {
640                                            // Ctrl+P - move cursor up (previous line) in script mode
641                                            let script_actions = crate::components::command_panel::ScriptEditor::move_cursor_up(
642                                                &mut self.state.command_panel,
643                                            );
644                                            actions.extend(script_actions);
645                                        }
646                                        _ => {
647                                            // Other modes: use original behavior
648                                            actions.push(Action::HistoryUp);
649                                        }
650                                    }
651                                }
652                                'n' => {
653                                    match self.state.command_panel.mode {
654                                        crate::model::panel_state::InteractionMode::Input => {
655                                            // Ctrl+N - go to next command in input mode
656                                            actions.push(Action::HistoryNext);
657                                        }
658                                        crate::model::panel_state::InteractionMode::ScriptEditor => {
659                                            // Ctrl+N - move cursor down (next line) in script mode
660                                            let script_actions = crate::components::command_panel::ScriptEditor::move_cursor_down(
661                                                &mut self.state.command_panel,
662                                            );
663                                            actions.extend(script_actions);
664                                        }
665                                        _ => {
666                                            // Other modes: use original behavior
667                                            actions.push(Action::HistoryDown);
668                                        }
669                                    }
670                                }
671                                'i' => actions.push(Action::InsertTab),
672                                'h' => {
673                                    match self.state.command_panel.mode {
674                                        crate::model::panel_state::InteractionMode::ScriptEditor => {
675                                            // Ctrl+H - delete character (backspace) in script mode
676                                            let script_actions = crate::components::command_panel::ScriptEditor::delete_char_at_cursor(
677                                                &mut self.state.command_panel,
678                                            );
679                                            actions.extend(script_actions);
680                                        }
681                                        _ => {
682                                            // Ctrl+H - Backspace in input/command mode
683                                            let handler_actions = self
684                                                .state
685                                                .command_input_handler
686                                                .handle_backspace(&mut self.state.command_panel);
687                                            actions.extend(handler_actions);
688                                            self.state.command_renderer.mark_pending_updates();
689                                        }
690                                    }
691                                }
692                                _ => {
693                                    // Use optimized input handler for regular character input
694                                    let handler_actions = self
695                                        .state
696                                        .command_input_handler
697                                        .handle_char_input(&mut self.state.command_panel, c);
698                                    actions.extend(handler_actions);
699                                    self.state.command_renderer.mark_pending_updates();
700                                }
701                            }
702                        } else {
703                            // Handle non-Ctrl character input based on mode
704                            match self.state.command_panel.mode {
705                                crate::model::panel_state::InteractionMode::Command => {
706                                    // In command mode, handle vim-style navigation
707                                    match c {
708                                        'j' => {
709                                            // Move cursor down in unified line view
710                                            actions.push(Action::CommandCursorDown);
711                                        }
712                                        'k' => {
713                                            // Move cursor up in unified line view
714                                            actions.push(Action::CommandCursorUp);
715                                        }
716                                        'h' => {
717                                            // Move cursor left in current line
718                                            actions.push(Action::CommandCursorLeft);
719                                        }
720                                        'l' => {
721                                            // Move cursor right in current line
722                                            actions.push(Action::CommandCursorRight);
723                                        }
724                                        'i' => {
725                                            // Exit command mode and return to previous mode
726                                            actions.push(Action::ExitCommandMode);
727                                        }
728                                        'g' => {
729                                            // Go to top of history (vim style)
730                                            self.state.command_panel.command_cursor_line = 0;
731                                            self.state.command_panel.command_cursor_column = 0;
732                                            self.state.command_renderer.mark_pending_updates();
733                                        }
734                                        'G' => {
735                                            // Go to the last line of the entire content, including current input
736                                            // Use wrapped lines to handle text that exceeds panel width
737                                            let wrapped_lines = self
738                                                .state
739                                                .command_panel
740                                                .get_command_mode_wrapped_lines(
741                                                    self.state.command_panel_width,
742                                                );
743
744                                            if !wrapped_lines.is_empty() {
745                                                let last_line =
746                                                    wrapped_lines.len().saturating_sub(1);
747                                                self.state.command_panel.command_cursor_line =
748                                                    last_line;
749                                                // Set column to end of the last line
750                                                self.state.command_panel.command_cursor_column =
751                                                    wrapped_lines[last_line].chars().count();
752                                            }
753                                            self.state.command_renderer.mark_pending_updates();
754                                        }
755                                        '$' => {
756                                            // Go to end of current line
757                                            if self.state.command_panel.command_cursor_line
758                                                < self.state.command_panel.command_history.len()
759                                            {
760                                                self.state.command_panel.command_cursor_column =
761                                                    self.state.command_panel.command_history[self
762                                                        .state
763                                                        .command_panel
764                                                        .command_cursor_line]
765                                                        .command
766                                                        .chars()
767                                                        .count();
768                                            }
769                                            self.state.command_renderer.mark_pending_updates();
770                                        }
771                                        '0' => {
772                                            // Go to beginning of current line
773                                            self.state.command_panel.command_cursor_column = 0;
774                                            self.state.command_renderer.mark_pending_updates();
775                                        }
776                                        _ => {
777                                            // For other characters in command mode, do nothing or handle as needed
778                                        }
779                                    }
780                                }
781                                _ => {
782                                    // For input and script modes, use normal input handler
783                                    let handler_actions = self
784                                        .state
785                                        .command_input_handler
786                                        .handle_char_input(&mut self.state.command_panel, c);
787                                    actions.extend(handler_actions);
788                                    self.state.command_renderer.mark_pending_updates();
789                                }
790                            }
791                        }
792                    }
793                    KeyCode::Backspace => {
794                        let handler_actions = self
795                            .state
796                            .command_input_handler
797                            .handle_backspace(&mut self.state.command_panel);
798                        actions.extend(handler_actions);
799                        self.state.command_renderer.mark_pending_updates();
800                    }
801                    KeyCode::Enter => {
802                        actions.push(Action::SubmitCommand);
803                    }
804                    KeyCode::Up
805                    | KeyCode::Down
806                    | KeyCode::Left
807                    | KeyCode::Right
808                    | KeyCode::Home
809                    | KeyCode::End => {
810                        let direction = match key.code {
811                            KeyCode::Up => crate::action::CursorDirection::Up,
812                            KeyCode::Down => crate::action::CursorDirection::Down,
813                            KeyCode::Left => crate::action::CursorDirection::Left,
814                            KeyCode::Right => crate::action::CursorDirection::Right,
815                            KeyCode::Home => crate::action::CursorDirection::Home,
816                            KeyCode::End => crate::action::CursorDirection::End,
817                            _ => unreachable!(),
818                        };
819                        let handler_actions = self
820                            .state
821                            .command_input_handler
822                            .handle_movement(&mut self.state.command_panel, direction);
823                        actions.extend(handler_actions);
824                        self.state.command_renderer.mark_pending_updates();
825                    }
826                    KeyCode::Esc => {
827                        // Handle Esc based on current mode
828                        match self.state.command_panel.mode {
829                            crate::model::panel_state::InteractionMode::ScriptEditor => {
830                                // Script mode: Esc exits to input mode (traditional behavior)
831                                actions.push(Action::ExitScriptMode);
832                            }
833                            crate::model::panel_state::InteractionMode::Input => {
834                                // Input mode: Esc enters command mode
835                                actions.push(Action::EnterCommandMode);
836                            }
837                            crate::model::panel_state::InteractionMode::Command => {
838                                // Already in command mode, do nothing
839                            }
840                        }
841                    }
842                    _ => {}
843                }
844            }
845            PanelType::Source => {
846                // Handle source panel input based on current mode
847                match self.state.source_panel.mode {
848                    crate::model::panel_state::SourcePanelMode::Normal => match key.code {
849                        KeyCode::Up => {
850                            actions
851                                .push(Action::NavigateSource(crate::action::SourceNavigation::Up));
852                        }
853                        KeyCode::Down => {
854                            actions.push(Action::NavigateSource(
855                                crate::action::SourceNavigation::Down,
856                            ));
857                        }
858                        KeyCode::Left => {
859                            actions.push(Action::NavigateSource(
860                                crate::action::SourceNavigation::Left,
861                            ));
862                        }
863                        KeyCode::Right => {
864                            actions.push(Action::NavigateSource(
865                                crate::action::SourceNavigation::Right,
866                            ));
867                        }
868                        KeyCode::PageUp => {
869                            actions.push(Action::NavigateSource(
870                                crate::action::SourceNavigation::PageUp,
871                            ));
872                        }
873                        KeyCode::PageDown => {
874                            actions.push(Action::NavigateSource(
875                                crate::action::SourceNavigation::PageDown,
876                            ));
877                        }
878                        KeyCode::Char('/') => {
879                            actions.push(Action::EnterTextSearch);
880                        }
881                        KeyCode::Char('o') => {
882                            actions.push(Action::EnterFileSearch);
883                        }
884                        KeyCode::Char('g') => {
885                            actions.push(Action::SourceGoToLine);
886                        }
887                        KeyCode::Char('G') => {
888                            actions.push(Action::SourceGoToBottom);
889                        }
890                        KeyCode::Char('h') => {
891                            actions.push(Action::NavigateSource(
892                                crate::action::SourceNavigation::Left,
893                            ));
894                        }
895                        KeyCode::Char('j') => {
896                            actions.push(Action::NavigateSource(
897                                crate::action::SourceNavigation::Down,
898                            ));
899                        }
900                        KeyCode::Char('k') => {
901                            actions
902                                .push(Action::NavigateSource(crate::action::SourceNavigation::Up));
903                        }
904                        KeyCode::Char('l') => {
905                            actions.push(Action::NavigateSource(
906                                crate::action::SourceNavigation::Right,
907                            ));
908                        }
909                        KeyCode::Char('n') => {
910                            actions.push(Action::NavigateSource(
911                                crate::action::SourceNavigation::NextMatch,
912                            ));
913                        }
914                        KeyCode::Char('N') => {
915                            actions.push(Action::NavigateSource(
916                                crate::action::SourceNavigation::PrevMatch,
917                            ));
918                        }
919                        KeyCode::Char('w') => {
920                            actions.push(Action::NavigateSource(
921                                crate::action::SourceNavigation::WordForward,
922                            ));
923                        }
924                        KeyCode::Char('b') => {
925                            actions.push(Action::NavigateSource(
926                                crate::action::SourceNavigation::WordBackward,
927                            ));
928                        }
929                        KeyCode::Char('^') => {
930                            actions.push(Action::NavigateSource(
931                                crate::action::SourceNavigation::LineStart,
932                            ));
933                        }
934                        KeyCode::Char('$') => {
935                            actions.push(Action::NavigateSource(
936                                crate::action::SourceNavigation::LineEnd,
937                            ));
938                        }
939                        KeyCode::Char(' ') => {
940                            // Space key - set trace at current line
941                            actions.push(Action::SetTraceFromSourceLine);
942                        }
943                        KeyCode::Char(c) => {
944                            // Handle Ctrl+key combinations in source panel
945                            if key
946                                .modifiers
947                                .contains(crossterm::event::KeyModifiers::CONTROL)
948                            {
949                                match c {
950                                    'd' => {
951                                        // Ctrl+D - half page down (10 lines)
952                                        actions.push(Action::NavigateSource(
953                                            crate::action::SourceNavigation::HalfPageDown,
954                                        ));
955                                    }
956                                    'u' => {
957                                        // Ctrl+U - half page up (10 lines)
958                                        actions.push(Action::NavigateSource(
959                                            crate::action::SourceNavigation::HalfPageUp,
960                                        ));
961                                    }
962                                    _ => {}
963                                }
964                            } else if c.is_ascii_digit() {
965                                actions.push(Action::SourceNumberInput(c));
966                            }
967                        }
968                        KeyCode::Esc => {
969                            // Clear all search highlights and navigation state (like vim)
970                            let clear_actions =
971                                crate::components::source_panel::SourceNavigation::clear_all_state(
972                                    &mut self.state.source_panel,
973                                );
974                            actions.extend(clear_actions);
975                        }
976                        _ => {}
977                    },
978                    crate::model::panel_state::SourcePanelMode::TextSearch => match key.code {
979                        KeyCode::Char(c) => {
980                            actions.push(Action::SourceSearchInput(c));
981                        }
982                        KeyCode::Backspace => {
983                            actions.push(Action::SourceSearchBackspace);
984                        }
985                        KeyCode::Enter => {
986                            actions.push(Action::SourceSearchConfirm);
987                        }
988                        KeyCode::Esc => {
989                            actions.push(Action::ExitTextSearch);
990                        }
991                        _ => {}
992                    },
993                    crate::model::panel_state::SourcePanelMode::FileSearch => match key.code {
994                        KeyCode::Char(c) => {
995                            // Handle Ctrl+key combinations in file search
996                            if key
997                                .modifiers
998                                .contains(crossterm::event::KeyModifiers::CONTROL)
999                            {
1000                                match c {
1001                                    'n' => {
1002                                        // Ctrl+N - move down in file search
1003                                        let move_actions = crate::components::source_panel::SourceSearch::move_file_search_down(
1004                                            &mut self.state.source_panel,
1005                                        );
1006                                        actions.extend(move_actions);
1007                                    }
1008                                    'p' => {
1009                                        // Ctrl+P - move up in file search
1010                                        let move_actions = crate::components::source_panel::SourceSearch::move_file_search_up(
1011                                            &mut self.state.source_panel,
1012                                        );
1013                                        actions.extend(move_actions);
1014                                    }
1015                                    'd' => {
1016                                        // Ctrl+D - page down in file search (move down multiple items)
1017                                        for _ in 0..5 {
1018                                            let move_actions = crate::components::source_panel::SourceSearch::move_file_search_down(
1019                                                &mut self.state.source_panel,
1020                                            );
1021                                            actions.extend(move_actions);
1022                                        }
1023                                    }
1024                                    'u' => {
1025                                        // Ctrl+U - clear entire query
1026                                        if let Some(ref cache) =
1027                                            self.state.command_panel.file_completion_cache
1028                                        {
1029                                            let clear_actions = crate::components::source_panel::SourceSearch::clear_file_search_query(
1030                                                &mut self.state.source_panel,
1031                                                cache,
1032                                            );
1033                                            actions.extend(clear_actions);
1034                                        }
1035                                    }
1036                                    'a' => {
1037                                        // Ctrl+A - move cursor to beginning
1038                                        let move_actions = crate::components::source_panel::SourceSearch::move_cursor_to_start(
1039                                            &mut self.state.source_panel,
1040                                        );
1041                                        actions.extend(move_actions);
1042                                    }
1043                                    'e' => {
1044                                        // Ctrl+E - move cursor to end
1045                                        let move_actions = crate::components::source_panel::SourceSearch::move_cursor_to_end(
1046                                            &mut self.state.source_panel,
1047                                        );
1048                                        actions.extend(move_actions);
1049                                    }
1050                                    'w' => {
1051                                        // Ctrl+W - delete previous word
1052                                        if let Some(ref cache) =
1053                                            self.state.command_panel.file_completion_cache
1054                                        {
1055                                            let delete_actions = crate::components::source_panel::SourceSearch::delete_word_file_search(
1056                                                &mut self.state.source_panel,
1057                                                cache,
1058                                            );
1059                                            actions.extend(delete_actions);
1060                                        }
1061                                    }
1062                                    'b' => {
1063                                        // Ctrl+B - move cursor left
1064                                        let move_actions = crate::components::source_panel::SourceSearch::move_cursor_left(
1065                                            &mut self.state.source_panel,
1066                                        );
1067                                        actions.extend(move_actions);
1068                                    }
1069                                    'f' => {
1070                                        // Ctrl+F - move cursor right
1071                                        let move_actions = crate::components::source_panel::SourceSearch::move_cursor_right(
1072                                            &mut self.state.source_panel,
1073                                        );
1074                                        actions.extend(move_actions);
1075                                    }
1076                                    'h' => {
1077                                        // Ctrl+H - delete previous character (same as backspace)
1078                                        actions.push(Action::SourceFileSearchBackspace);
1079                                    }
1080                                    _ => {
1081                                        // Regular character input
1082                                        actions.push(Action::SourceFileSearchInput(c));
1083                                    }
1084                                }
1085                            } else {
1086                                // Regular character input
1087                                actions.push(Action::SourceFileSearchInput(c));
1088                            }
1089                        }
1090                        KeyCode::Backspace => {
1091                            actions.push(Action::SourceFileSearchBackspace);
1092                        }
1093                        KeyCode::Enter => {
1094                            actions.push(Action::SourceFileSearchConfirm);
1095                        }
1096                        KeyCode::Up => {
1097                            // Arrow Up - move up in file search
1098                            let move_actions =
1099                                crate::components::source_panel::SourceSearch::move_file_search_up(
1100                                    &mut self.state.source_panel,
1101                                );
1102                            actions.extend(move_actions);
1103                        }
1104                        KeyCode::Down => {
1105                            // Arrow Down - move down in file search
1106                            let move_actions = crate::components::source_panel::SourceSearch::move_file_search_down(
1107                                &mut self.state.source_panel,
1108                            );
1109                            actions.extend(move_actions);
1110                        }
1111                        KeyCode::Esc => {
1112                            actions.push(Action::ExitFileSearch);
1113                        }
1114                        _ => {}
1115                    },
1116                }
1117            }
1118            PanelType::EbpfInfo => {
1119                // Handle eBPF panel input using the dedicated handler
1120                let panel_actions = self
1121                    .state
1122                    .ebpf_panel_handler
1123                    .handle_key_event(&mut self.state.ebpf_panel, key);
1124                actions.extend(panel_actions);
1125            }
1126        }
1127        Ok(actions)
1128    }
1129
1130    /// Handle actions (TEA Update)
1131    fn handle_action(&mut self, action: Action) -> Result<Vec<Action>> {
1132        debug!("Handling action: {:?}", action);
1133        let mut additional_actions = Vec::new();
1134
1135        match action {
1136            Action::Quit => {
1137                self.state.should_quit = true;
1138            }
1139            Action::Resize(width, height) => {
1140                // Force refresh of all panel dimensions on terminal resize
1141                tracing::debug!("Terminal resized to {}x{}", width, height);
1142                // The render function will automatically pick up the new dimensions
1143                // and update panel sizes accordingly
1144            }
1145            Action::FocusNext => {
1146                let src_enabled = self.state.ui.config.show_source_panel;
1147                self.state.ui.focus.cycle_next(src_enabled);
1148            }
1149            Action::FocusPrevious => {
1150                let src_enabled = self.state.ui.config.show_source_panel;
1151                self.state.ui.focus.cycle_previous(src_enabled);
1152            }
1153            Action::FocusPanel(panel) => {
1154                if panel == crate::action::PanelType::Source
1155                    && !self.state.ui.config.show_source_panel
1156                {
1157                    // Ignore focusing hidden source panel; fallback to command panel
1158                    self.state
1159                        .ui
1160                        .focus
1161                        .set_panel(crate::action::PanelType::InteractiveCommand);
1162                } else {
1163                    self.state.ui.focus.set_panel(panel);
1164                }
1165            }
1166            Action::ToggleFullscreen => {
1167                self.state.ui.layout.toggle_fullscreen();
1168            }
1169            Action::SwitchLayout => {
1170                self.state.ui.layout.switch_mode();
1171            }
1172            Action::EnterWindowNavMode => {
1173                self.state.ui.focus.expecting_window_nav = true;
1174            }
1175            Action::ExitWindowNavMode => {
1176                self.state.ui.focus.expecting_window_nav = false;
1177            }
1178            Action::WindowNavMove(direction) => {
1179                let src_enabled = self.state.ui.config.show_source_panel;
1180                self.state.ui.focus.move_focus_in_direction(
1181                    direction,
1182                    self.state.ui.layout.mode,
1183                    src_enabled,
1184                );
1185            }
1186            Action::SetSourcePanelVisibility(show) => {
1187                let currently_shown = self.state.ui.config.show_source_panel;
1188                if show == currently_shown {
1189                    return Ok(Vec::new());
1190                }
1191                self.state.ui.config.show_source_panel = show;
1192                if show {
1193                    // If enabling, request source code immediately
1194                    if let Err(e) = self
1195                        .state
1196                        .event_registry
1197                        .command_sender
1198                        .send(crate::events::RuntimeCommand::RequestSourceCode)
1199                    {
1200                        tracing::warn!("Failed to send source request after enabling: {}", e);
1201                    }
1202                    // Inform user
1203                    let plain =
1204                        "āœ… Source panel enabled. Use 'ui source off' to hide it.".to_string();
1205                    let styled = vec![
1206                        crate::components::command_panel::style_builder::StyledLineBuilder::new()
1207                            .styled(
1208                                plain.clone(),
1209                                crate::components::command_panel::style_builder::StylePresets::SUCCESS,
1210                            )
1211                            .build(),
1212                    ];
1213                    additional_actions.push(Action::AddResponseWithStyle {
1214                        content: plain,
1215                        styled_lines: Some(styled),
1216                        response_type: crate::action::ResponseType::Success,
1217                    });
1218                } else {
1219                    // If disabling and focus is Source or fullscreen Source, move focus away
1220                    if self.state.ui.focus.current_panel == crate::action::PanelType::Source {
1221                        self.state
1222                            .ui
1223                            .focus
1224                            .set_panel(crate::action::PanelType::InteractiveCommand);
1225                    }
1226                    if self.state.ui.layout.is_fullscreen
1227                        && matches!(
1228                            self.state.ui.focus.current_panel,
1229                            crate::action::PanelType::Source
1230                        )
1231                    {
1232                        self.state.ui.layout.is_fullscreen = false;
1233                    }
1234                    // Inform user
1235                    let plain = "āœ… Source panel disabled. Panels: eBPF output + command. Use 'ui source on' to enable.".to_string();
1236                    let styled = vec![
1237                        crate::components::command_panel::style_builder::StyledLineBuilder::new()
1238                            .styled(
1239                                plain.clone(),
1240                                crate::components::command_panel::style_builder::StylePresets::SUCCESS,
1241                            )
1242                            .build(),
1243                    ];
1244                    additional_actions.push(Action::AddResponseWithStyle {
1245                        content: plain,
1246                        styled_lines: Some(styled),
1247                        response_type: crate::action::ResponseType::Success,
1248                    });
1249                }
1250            }
1251            Action::InsertChar(c) => {
1252                let actions = crate::components::command_panel::InputHandler::insert_char(
1253                    &mut self.state.command_panel,
1254                    c,
1255                );
1256                additional_actions.extend(actions);
1257            }
1258            Action::DeleteChar => {
1259                let actions = crate::components::command_panel::InputHandler::delete_char(
1260                    &mut self.state.command_panel,
1261                );
1262                additional_actions.extend(actions);
1263            }
1264            Action::MoveCursor(direction) => {
1265                let actions = crate::components::command_panel::InputHandler::move_cursor(
1266                    &mut self.state.command_panel,
1267                    direction,
1268                );
1269                additional_actions.extend(actions);
1270            }
1271            Action::SubmitCommand => {
1272                let actions = self
1273                    .state
1274                    .command_input_handler
1275                    .handle_submit(&mut self.state.command_panel);
1276                additional_actions.extend(actions);
1277                self.state.command_renderer.mark_pending_updates();
1278
1279                // Realtime logging: write command to file if enabled
1280                if self.state.realtime_session_logger.enabled {
1281                    if let Some(command) = self
1282                        .state
1283                        .command_panel
1284                        .command_history
1285                        .last()
1286                        .map(|item| item.command.clone())
1287                    {
1288                        if let Err(e) = self.write_command_to_session_log(&command) {
1289                            tracing::error!("Failed to write command to session log: {}", e);
1290                        }
1291                    }
1292                }
1293            }
1294            Action::SubmitCommandWithText { command } => {
1295                // Handle command submission from history search mode
1296                // Add to history and process the command
1297                self.state.command_panel.add_command_to_history(&command);
1298
1299                // Set the input text and submit it
1300                self.state.command_panel.input_text = command.clone();
1301                let actions = self
1302                    .state
1303                    .command_input_handler
1304                    .handle_submit(&mut self.state.command_panel);
1305                additional_actions.extend(actions);
1306                self.state.command_renderer.mark_pending_updates();
1307
1308                // Realtime logging: write command to file if enabled
1309                if self.state.realtime_session_logger.enabled {
1310                    if let Err(e) = self.write_command_to_session_log(&command) {
1311                        tracing::error!("Failed to write command to session log: {}", e);
1312                    }
1313                }
1314            }
1315            Action::HistoryUp => {
1316                // Handled by input handler
1317            }
1318            Action::HistoryDown => {
1319                // Handled by input handler
1320            }
1321            Action::HistoryPrevious => {
1322                self.state.command_panel.history_previous();
1323                self.state.command_renderer.mark_pending_updates();
1324            }
1325            Action::HistoryNext => {
1326                self.state.command_panel.history_next();
1327                self.state.command_renderer.mark_pending_updates();
1328            }
1329            Action::EnterCommandMode => {
1330                self.state
1331                    .command_panel
1332                    .enter_command_mode(self.state.command_panel_width);
1333            }
1334            Action::ExitCommandMode => {
1335                self.state.command_panel.exit_command_mode();
1336            }
1337            Action::EnterInputMode => {
1338                self.state.command_panel.mode = crate::model::panel_state::InteractionMode::Input;
1339            }
1340            Action::CommandCursorUp => {
1341                self.state.command_panel.move_command_cursor_up();
1342                self.state.command_renderer.mark_pending_updates();
1343            }
1344            Action::CommandCursorDown => {
1345                self.state.command_panel.move_command_cursor_down();
1346                self.state.command_renderer.mark_pending_updates();
1347            }
1348            Action::CommandCursorLeft => {
1349                self.state.command_panel.move_command_cursor_left();
1350                self.state.command_renderer.mark_pending_updates();
1351            }
1352            Action::CommandCursorRight => {
1353                self.state.command_panel.move_command_cursor_right();
1354                self.state.command_renderer.mark_pending_updates();
1355            }
1356            Action::CommandHalfPageUp => {
1357                self.state.command_panel.move_command_half_page_up();
1358                self.state.command_renderer.mark_pending_updates();
1359            }
1360            Action::CommandHalfPageDown => {
1361                self.state.command_panel.move_command_half_page_down();
1362                self.state.command_renderer.mark_pending_updates();
1363            }
1364            Action::EnterScriptMode(command) => {
1365                let actions = crate::components::command_panel::ScriptEditor::enter_script_mode(
1366                    &mut self.state.command_panel,
1367                    &command,
1368                );
1369                additional_actions.extend(actions);
1370                self.state.command_renderer.mark_pending_updates();
1371            }
1372            Action::ExitScriptMode => {
1373                let actions = crate::components::command_panel::ScriptEditor::exit_script_mode(
1374                    &mut self.state.command_panel,
1375                );
1376                additional_actions.extend(actions);
1377                self.state.command_renderer.mark_pending_updates();
1378            }
1379            Action::SubmitScript => {
1380                let actions = crate::components::command_panel::ScriptEditor::submit_script(
1381                    &mut self.state.command_panel,
1382                );
1383                additional_actions.extend(actions);
1384                self.state.command_renderer.mark_pending_updates();
1385            }
1386            Action::CancelScript => {
1387                let actions = crate::components::command_panel::ScriptEditor::exit_script_mode(
1388                    &mut self.state.command_panel,
1389                );
1390                additional_actions.extend(actions);
1391                self.state.command_renderer.mark_pending_updates();
1392            }
1393            Action::AddResponseWithStyle {
1394                content,
1395                styled_lines,
1396                response_type,
1397            } => {
1398                // Realtime logging: write response to file if enabled (before moving content)
1399                if self.state.realtime_session_logger.enabled {
1400                    if let Err(e) = self.write_response_to_session_log(&content) {
1401                        tracing::error!("Failed to write response to session log: {}", e);
1402                    }
1403                }
1404                crate::components::command_panel::ResponseFormatter::add_response_with_style(
1405                    &mut self.state.command_panel,
1406                    content,
1407                    styled_lines,
1408                    response_type,
1409                );
1410                self.state.command_renderer.mark_pending_updates();
1411            }
1412            // Removed old AddWelcomeMessage - now using AddStyledWelcomeMessage
1413            Action::AddStyledWelcomeMessage {
1414                styled_lines,
1415                response_type,
1416            } => {
1417                // New direct styled approach - no complex mapping needed
1418                self.state
1419                    .command_panel
1420                    .add_styled_welcome_lines(styled_lines, response_type);
1421                self.state.command_renderer.mark_pending_updates();
1422            }
1423            Action::SendRuntimeCommand(cmd) => {
1424                // Send command to runtime via event_registry
1425                debug!("Sending runtime command: {:?}", cmd);
1426                if let Err(e) = self.state.event_registry.command_sender.send(cmd) {
1427                    tracing::error!("Failed to send runtime command: {}", e);
1428                    // Add error response to command panel
1429                    let plain = format!("āœ— Failed to send command to runtime: {e}");
1430                    let styled = vec![
1431                        crate::components::command_panel::style_builder::StyledLineBuilder::new()
1432                            .styled(plain.clone(), crate::components::command_panel::style_builder::StylePresets::ERROR)
1433                            .build(),
1434                    ];
1435                    let error_action = Action::AddResponseWithStyle {
1436                        content: plain,
1437                        styled_lines: Some(styled),
1438                        response_type: crate::action::ResponseType::Error,
1439                    };
1440                    additional_actions.push(error_action);
1441                }
1442            }
1443            Action::HandleRuntimeStatus(status) => {
1444                // TODO: Handle runtime status updates
1445                debug!("Would handle runtime status: {:?}", status);
1446            }
1447            Action::DeletePreviousWord => match self.state.command_panel.mode {
1448                crate::model::panel_state::InteractionMode::ScriptEditor => {
1449                    let actions =
1450                        crate::components::command_panel::ScriptEditor::delete_previous_word(
1451                            &mut self.state.command_panel,
1452                        );
1453                    additional_actions.extend(actions);
1454                }
1455                _ => {
1456                    let actions =
1457                        crate::components::command_panel::InputHandler::delete_previous_word(
1458                            &mut self.state.command_panel,
1459                        );
1460                    additional_actions.extend(actions);
1461                }
1462            },
1463            Action::DeleteToEnd => {
1464                let actions = crate::components::command_panel::InputHandler::delete_to_end(
1465                    &mut self.state.command_panel,
1466                );
1467                additional_actions.extend(actions);
1468            }
1469            Action::DeleteToBeginning => {
1470                let actions = crate::components::command_panel::InputHandler::delete_to_beginning(
1471                    &mut self.state.command_panel,
1472                );
1473                additional_actions.extend(actions);
1474            }
1475            Action::InsertTab => {
1476                if self.state.command_panel.mode
1477                    == crate::model::panel_state::InteractionMode::ScriptEditor
1478                {
1479                    let actions = crate::components::command_panel::ScriptEditor::insert_tab(
1480                        &mut self.state.command_panel,
1481                    );
1482                    additional_actions.extend(actions);
1483                }
1484            }
1485            Action::InsertNewline => {
1486                if self.state.command_panel.mode
1487                    == crate::model::panel_state::InteractionMode::ScriptEditor
1488                {
1489                    let actions = crate::components::command_panel::ScriptEditor::insert_newline(
1490                        &mut self.state.command_panel,
1491                    );
1492                    additional_actions.extend(actions);
1493                }
1494            }
1495            // Source panel actions
1496            Action::NavigateSource(direction) => {
1497                let actions = match direction {
1498                    crate::action::SourceNavigation::Up => {
1499                        crate::components::source_panel::SourceNavigation::move_up(
1500                            &mut self.state.source_panel,
1501                        )
1502                    }
1503                    crate::action::SourceNavigation::Down => {
1504                        crate::components::source_panel::SourceNavigation::move_down(
1505                            &mut self.state.source_panel,
1506                        )
1507                    }
1508                    crate::action::SourceNavigation::Left => {
1509                        crate::components::source_panel::SourceNavigation::move_left(
1510                            &mut self.state.source_panel,
1511                        )
1512                    }
1513                    crate::action::SourceNavigation::Right => {
1514                        crate::components::source_panel::SourceNavigation::move_right(
1515                            &mut self.state.source_panel,
1516                        )
1517                    }
1518                    crate::action::SourceNavigation::PageUp => {
1519                        crate::components::source_panel::SourceNavigation::move_up_fast(
1520                            &mut self.state.source_panel,
1521                        )
1522                    }
1523                    crate::action::SourceNavigation::PageDown => {
1524                        crate::components::source_panel::SourceNavigation::move_down_fast(
1525                            &mut self.state.source_panel,
1526                        )
1527                    }
1528                    crate::action::SourceNavigation::HalfPageUp => {
1529                        crate::components::source_panel::SourceNavigation::move_half_page_up(
1530                            &mut self.state.source_panel,
1531                        )
1532                    }
1533                    crate::action::SourceNavigation::HalfPageDown => {
1534                        crate::components::source_panel::SourceNavigation::move_half_page_down(
1535                            &mut self.state.source_panel,
1536                        )
1537                    }
1538                    crate::action::SourceNavigation::GoToLine(line) => {
1539                        crate::components::source_panel::SourceNavigation::go_to_line(
1540                            &mut self.state.source_panel,
1541                            line,
1542                        )
1543                    }
1544                    crate::action::SourceNavigation::NextMatch => {
1545                        crate::components::source_panel::SourceSearch::next_match(
1546                            &mut self.state.source_panel,
1547                        )
1548                    }
1549                    crate::action::SourceNavigation::PrevMatch => {
1550                        crate::components::source_panel::SourceSearch::prev_match(
1551                            &mut self.state.source_panel,
1552                        )
1553                    }
1554                    crate::action::SourceNavigation::WordForward => {
1555                        crate::components::source_panel::SourceNavigation::move_word_forward(
1556                            &mut self.state.source_panel,
1557                        )
1558                    }
1559                    crate::action::SourceNavigation::WordBackward => {
1560                        crate::components::source_panel::SourceNavigation::move_word_backward(
1561                            &mut self.state.source_panel,
1562                        )
1563                    }
1564                    crate::action::SourceNavigation::LineStart => {
1565                        crate::components::source_panel::SourceNavigation::move_to_line_start(
1566                            &mut self.state.source_panel,
1567                        )
1568                    }
1569                    crate::action::SourceNavigation::LineEnd => {
1570                        crate::components::source_panel::SourceNavigation::move_to_line_end(
1571                            &mut self.state.source_panel,
1572                        )
1573                    }
1574                };
1575                additional_actions.extend(actions);
1576            }
1577            Action::LoadSource { path, line } => {
1578                let actions = crate::components::source_panel::SourceNavigation::load_source(
1579                    &mut self.state.source_panel,
1580                    path,
1581                    line,
1582                );
1583                additional_actions.extend(actions);
1584            }
1585            Action::EnterTextSearch => {
1586                let actions = crate::components::source_panel::SourceSearch::enter_search_mode(
1587                    &mut self.state.source_panel,
1588                );
1589                additional_actions.extend(actions);
1590            }
1591            Action::ExitTextSearch => {
1592                let actions = crate::components::source_panel::SourceSearch::exit_search_mode(
1593                    &mut self.state.source_panel,
1594                );
1595                additional_actions.extend(actions);
1596            }
1597            Action::EnterFileSearch => {
1598                let actions = crate::components::source_panel::SourceSearch::enter_file_search_mode(
1599                    &mut self.state.source_panel,
1600                );
1601                additional_actions.extend(actions);
1602
1603                // Use file cache to populate source panel search
1604                if let Some(ref mut cache) = self.state.command_panel.file_completion_cache {
1605                    if !cache.is_empty() {
1606                        // Use existing file cache
1607                        tracing::debug!("Using cached file list for source panel search");
1608                        let files = cache.get_all_files().to_vec();
1609                        let actions =
1610                            crate::components::source_panel::SourceSearch::set_file_search_files(
1611                                &mut self.state.source_panel,
1612                                cache,
1613                                files,
1614                            );
1615                        additional_actions.extend(actions);
1616                    }
1617                } else {
1618                    // Fallback: request file information from runtime
1619                    tracing::debug!("No cached files available, requesting from runtime");
1620                    self.state.route_file_info_to_file_search = true;
1621                    if let Err(e) = self
1622                        .state
1623                        .event_registry
1624                        .command_sender
1625                        .send(crate::events::RuntimeCommand::InfoSource)
1626                    {
1627                        tracing::error!("Failed to send InfoSource command: {}", e);
1628                        // Clear routing flag if send failed
1629                        self.state.route_file_info_to_file_search = false;
1630                        let error_actions =
1631                            crate::components::source_panel::SourceSearch::set_file_search_error(
1632                                &mut self.state.source_panel,
1633                                "Failed to request file list".to_string(),
1634                            );
1635                        additional_actions.extend(error_actions);
1636                    }
1637                }
1638            }
1639            Action::ExitFileSearch => {
1640                let actions = crate::components::source_panel::SourceSearch::exit_file_search_mode(
1641                    &mut self.state.source_panel,
1642                );
1643                additional_actions.extend(actions);
1644            }
1645            Action::SourceSearchInput(ch) => {
1646                let actions = crate::components::source_panel::SourceSearch::push_search_char(
1647                    &mut self.state.source_panel,
1648                    ch,
1649                );
1650                additional_actions.extend(actions);
1651            }
1652            Action::SourceSearchBackspace => {
1653                let actions = crate::components::source_panel::SourceSearch::backspace_search(
1654                    &mut self.state.source_panel,
1655                );
1656                additional_actions.extend(actions);
1657            }
1658            Action::SourceSearchConfirm => {
1659                let actions = crate::components::source_panel::SourceSearch::confirm_search(
1660                    &mut self.state.source_panel,
1661                );
1662                additional_actions.extend(actions);
1663            }
1664            Action::SourceFileSearchInput(ch) => {
1665                if let Some(ref cache) = self.state.command_panel.file_completion_cache {
1666                    let actions =
1667                        crate::components::source_panel::SourceSearch::push_file_search_char(
1668                            &mut self.state.source_panel,
1669                            cache,
1670                            ch,
1671                        );
1672                    additional_actions.extend(actions);
1673                }
1674            }
1675            Action::SourceFileSearchBackspace => {
1676                if let Some(ref cache) = self.state.command_panel.file_completion_cache {
1677                    let actions =
1678                        crate::components::source_panel::SourceSearch::backspace_file_search(
1679                            &mut self.state.source_panel,
1680                            cache,
1681                        );
1682                    additional_actions.extend(actions);
1683                }
1684            }
1685            Action::SourceFileSearchConfirm => {
1686                if let Some(ref cache) = self.state.command_panel.file_completion_cache {
1687                    if let Some(selected_file) =
1688                        crate::components::source_panel::SourceSearch::confirm_file_search(
1689                            &mut self.state.source_panel,
1690                            cache,
1691                        )
1692                    {
1693                        // Load the selected file
1694                        additional_actions.push(Action::LoadSource {
1695                            path: selected_file,
1696                            line: None,
1697                        });
1698                    }
1699                }
1700            }
1701            Action::SourceNumberInput(ch) => {
1702                let actions =
1703                    crate::components::source_panel::SourceNavigation::handle_number_input(
1704                        &mut self.state.source_panel,
1705                        ch,
1706                    );
1707                additional_actions.extend(actions);
1708            }
1709            Action::SourceGoToLine => {
1710                let actions = crate::components::source_panel::SourceNavigation::handle_g_key(
1711                    &mut self.state.source_panel,
1712                );
1713                additional_actions.extend(actions);
1714            }
1715            Action::SourceGoToBottom => {
1716                let actions = crate::components::source_panel::SourceNavigation::handle_shift_g_key(
1717                    &mut self.state.source_panel,
1718                );
1719                additional_actions.extend(actions);
1720            }
1721            Action::SetTraceFromSourceLine => {
1722                // Get current file and line from source panel
1723                if let Some(file_path) = &self.state.source_panel.file_path {
1724                    let line_num = self.state.source_panel.cursor_line + 1; // Convert to 1-based
1725
1726                    // Don't mark line as pending here - wait for trace response
1727
1728                    // Build the trace command
1729                    let trace_command = format!("trace {file_path}:{line_num}");
1730
1731                    // Focus command panel (keep fullscreen state if enabled)
1732                    self.state.ui.focus.current_panel = PanelType::InteractiveCommand;
1733
1734                    // Add command to history (unified method)
1735                    self.state.command_panel.add_command_entry(&trace_command);
1736
1737                    // Clear input
1738                    self.state.command_panel.input_text.clear();
1739                    self.state.command_panel.cursor_position = 0;
1740
1741                    // Enter script mode directly
1742                    additional_actions.push(Action::EnterScriptMode(trace_command));
1743                }
1744            }
1745            Action::SaveEbpfOutput { filename } => {
1746                // Start realtime eBPF output logging
1747                let (content, response_type, style_preset) =
1748                    match self.start_realtime_output_logging(filename) {
1749                        Ok(file_path) => (
1750                            format!(
1751                                "āœ… Realtime eBPF output logging started: {}",
1752                                file_path.display()
1753                            ),
1754                            crate::action::ResponseType::Success,
1755                            crate::components::command_panel::style_builder::StylePresets::SUCCESS,
1756                        ),
1757                        Err(e) => (
1758                            format!("āœ— Failed to start output logging: {e}"),
1759                            crate::action::ResponseType::Error,
1760                            crate::components::command_panel::style_builder::StylePresets::ERROR,
1761                        ),
1762                    };
1763
1764                // Directly add response to command history
1765                crate::components::command_panel::ResponseFormatter::add_simple_styled_response(
1766                    &mut self.state.command_panel,
1767                    content,
1768                    style_preset,
1769                    response_type,
1770                );
1771                self.state.command_renderer.mark_pending_updates();
1772            }
1773            Action::SaveCommandSession { filename } => {
1774                // Start realtime command session logging
1775                let (content, response_type, style_preset) =
1776                    match self.start_realtime_session_logging(filename) {
1777                        Ok(file_path) => (
1778                            format!(
1779                                "āœ… Realtime session logging started: {}",
1780                                file_path.display()
1781                            ),
1782                            crate::action::ResponseType::Success,
1783                            crate::components::command_panel::style_builder::StylePresets::SUCCESS,
1784                        ),
1785                        Err(e) => (
1786                            format!("āœ— Failed to start session logging: {e}"),
1787                            crate::action::ResponseType::Error,
1788                            crate::components::command_panel::style_builder::StylePresets::ERROR,
1789                        ),
1790                    };
1791
1792                // Directly add response to command history
1793                crate::components::command_panel::ResponseFormatter::add_simple_styled_response(
1794                    &mut self.state.command_panel,
1795                    content,
1796                    style_preset,
1797                    response_type,
1798                );
1799                self.state.command_renderer.mark_pending_updates();
1800            }
1801            Action::StopSaveOutput => {
1802                // Stop realtime eBPF output logging
1803                let (content, response_type, style_preset) =
1804                    match self.state.realtime_output_logger.stop() {
1805                        Ok(()) => (
1806                            "āœ… Realtime eBPF output logging stopped".to_string(),
1807                            crate::action::ResponseType::Success,
1808                            crate::components::command_panel::style_builder::StylePresets::SUCCESS,
1809                        ),
1810                        Err(e) => (
1811                            format!("āœ— Failed to stop output logging: {e}"),
1812                            crate::action::ResponseType::Error,
1813                            crate::components::command_panel::style_builder::StylePresets::ERROR,
1814                        ),
1815                    };
1816
1817                // Directly add response to command history
1818                crate::components::command_panel::ResponseFormatter::add_simple_styled_response(
1819                    &mut self.state.command_panel,
1820                    content,
1821                    style_preset,
1822                    response_type,
1823                );
1824                self.state.command_renderer.mark_pending_updates();
1825            }
1826            Action::StopSaveSession => {
1827                // Stop realtime command session logging
1828                let (content, response_type, style_preset) =
1829                    match self.state.realtime_session_logger.stop() {
1830                        Ok(()) => (
1831                            "āœ… Realtime session logging stopped".to_string(),
1832                            crate::action::ResponseType::Success,
1833                            crate::components::command_panel::style_builder::StylePresets::SUCCESS,
1834                        ),
1835                        Err(e) => (
1836                            format!("āœ— Failed to stop session logging: {e}"),
1837                            crate::action::ResponseType::Error,
1838                            crate::components::command_panel::style_builder::StylePresets::ERROR,
1839                        ),
1840                    };
1841
1842                // Directly add response to command history
1843                crate::components::command_panel::ResponseFormatter::add_simple_styled_response(
1844                    &mut self.state.command_panel,
1845                    content,
1846                    style_preset,
1847                    response_type,
1848                );
1849                self.state.command_renderer.mark_pending_updates();
1850            }
1851            Action::NoOp => {
1852                // No operation - does nothing but prevents event fallback
1853            }
1854            _ => {
1855                debug!("Action not yet implemented: {:?}", action);
1856            }
1857        }
1858
1859        Ok(additional_actions)
1860    }
1861
1862    /// Add a module to the loading progress tracking
1863    pub fn add_module_to_loading(&mut self, module_path: String) {
1864        self.state.loading_ui.progress.add_module(module_path);
1865    }
1866
1867    /// Start loading a specific module
1868    pub fn start_module_loading(&mut self, module_path: &str) {
1869        self.state
1870            .loading_ui
1871            .progress
1872            .start_module_loading(module_path);
1873    }
1874
1875    /// Complete loading of a module with stats
1876    pub fn complete_module_loading(
1877        &mut self,
1878        module_path: &str,
1879        functions: usize,
1880        variables: usize,
1881        types: usize,
1882    ) {
1883        use crate::components::loading::ModuleStats;
1884        let stats = ModuleStats {
1885            functions,
1886            variables,
1887            types,
1888        };
1889        self.state
1890            .loading_ui
1891            .progress
1892            .complete_module(module_path, stats);
1893    }
1894
1895    /// Fail loading of a module
1896    pub fn fail_module_loading(&mut self, module_path: &str, error: String) {
1897        self.state
1898            .loading_ui
1899            .progress
1900            .fail_module(module_path, error);
1901    }
1902
1903    /// Set target PID for display
1904    pub fn set_target_pid(&mut self, pid: u32) {
1905        self.state.target_pid = Some(pid);
1906    }
1907
1908    /// Transition to ready state with completion summary (after successful loading)
1909    pub fn transition_to_ready_with_completion(&mut self) {
1910        self.add_loading_completion_summary();
1911        self.state.set_loading_state(LoadingState::Ready);
1912    }
1913
1914    /// Sync file list to command panel for file completion
1915    fn sync_files_to_command_panel(&mut self, files: Vec<String>) {
1916        tracing::debug!(
1917            "Syncing {} files to command panel completion cache",
1918            files.len()
1919        );
1920        if !files.is_empty() {
1921            tracing::debug!(
1922                "First 5 files: {:?}",
1923                files.iter().take(5).collect::<Vec<_>>()
1924            );
1925        }
1926
1927        // Create or update file completion cache
1928        if let Some(cache) = &mut self.state.command_panel.file_completion_cache {
1929            // Update existing cache
1930            let updated = cache.sync_from_source_panel(&files);
1931            tracing::debug!("Updated existing file completion cache: {}", updated);
1932        } else {
1933            // Create new cache only if there are files
1934            if !files.is_empty() {
1935                tracing::debug!(
1936                    "Creating new file completion cache with {} files",
1937                    files.len()
1938                );
1939                self.state.command_panel.file_completion_cache = Some(
1940                    crate::components::command_panel::file_completion::FileCompletionCache::new(
1941                        &files,
1942                    ),
1943                );
1944                tracing::debug!("File completion cache created successfully");
1945            } else {
1946                tracing::debug!("No files to create cache with");
1947            }
1948        }
1949    }
1950
1951    /// Generate and add completion summary to command panel
1952    pub fn add_loading_completion_summary(&mut self) {
1953        let total_time = self.state.loading_ui.progress.elapsed_time();
1954
1955        // Get styled welcome message lines
1956        let mut styled_lines = self.state.loading_ui.create_welcome_message(total_time);
1957
1958        // Add process-specific information if available
1959        if let Some(pid) = self.state.target_pid {
1960            use ratatui::style::{Color, Style};
1961            use ratatui::text::{Line, Span};
1962
1963            // Insert process info after the DWARF statistics line
1964            let mut enhanced_lines = Vec::new();
1965            let mut found_dwarf_stats = false;
1966            for line in styled_lines {
1967                enhanced_lines.push(line.clone());
1968                // Look for the DWARF statistics line (contains "indexed")
1969                let line_text: String = line
1970                    .spans
1971                    .iter()
1972                    .map(|span| span.content.as_ref())
1973                    .collect();
1974                if !found_dwarf_stats && line_text.starts_with("•") && line_text.contains("indexed")
1975                {
1976                    found_dwarf_stats = true;
1977                    enhanced_lines.push(Line::from("")); // Empty line
1978                    enhanced_lines.push(Line::from(Span::styled(
1979                        format!("Attached to process {pid}"),
1980                        Style::default().fg(Color::White),
1981                    )));
1982                    // TODO: Add process name when available
1983                }
1984            }
1985            styled_lines = enhanced_lines;
1986        }
1987
1988        // Removed complex mapping - now using direct styled content approach
1989
1990        // Use new simplified direct styled approach
1991        let action = Action::AddStyledWelcomeMessage {
1992            styled_lines,
1993            response_type: crate::action::ResponseType::Info,
1994        };
1995        if let Err(e) = self.handle_action(action) {
1996            tracing::error!("Failed to add completion summary: {}", e);
1997        }
1998    }
1999
2000    /// Draw the UI (TEA View)
2001    fn draw_ui(f: &mut Frame, state: &mut AppState) {
2002        let size = f.area();
2003
2004        // Show loading screen if still loading
2005        if state.is_loading() {
2006            // Use enhanced DWARF loading UI if we're loading symbols
2007            if matches!(state.loading_state, LoadingState::LoadingSymbols { .. }) {
2008                LoadingUI::render_dwarf_loading(
2009                    f,
2010                    &mut state.loading_ui,
2011                    &state.loading_state,
2012                    state.target_pid,
2013                );
2014            } else {
2015                // Use simple loading UI for other states
2016                LoadingUI::render_simple(
2017                    f,
2018                    &mut state.loading_ui,
2019                    state.loading_state.message(),
2020                    state.loading_state.progress(),
2021                );
2022            }
2023            return;
2024        }
2025
2026        if state.ui.layout.is_fullscreen {
2027            // In fullscreen mode, give the focused panel the entire screen
2028            match state.ui.focus.current_panel {
2029                PanelType::Source => {
2030                    if state.ui.config.show_source_panel {
2031                        Self::draw_source_panel(f, size, state);
2032                    } else {
2033                        // Source hidden: fallback to command panel fullscreen
2034                        Self::draw_command_panel(f, size, state);
2035                    }
2036                }
2037                PanelType::EbpfInfo => {
2038                    Self::draw_ebpf_panel(f, size, state);
2039                }
2040                PanelType::InteractiveCommand => {
2041                    Self::draw_command_panel(f, size, state);
2042                }
2043            }
2044        } else {
2045            // Normal multi-panel layout
2046            if state.ui.config.show_source_panel {
2047                // 3-panel layout
2048                let ratios = &state.ui.config.panel_ratios;
2049                let total_ratio: u32 = ratios.iter().map(|&x| x as u32).sum();
2050
2051                let chunks = match state.ui.layout.mode {
2052                    LayoutMode::Horizontal => {
2053                        Layout::default()
2054                            .direction(Direction::Horizontal)
2055                            .constraints(
2056                                [
2057                                    Constraint::Ratio(ratios[0] as u32, total_ratio), // Source code panel
2058                                    Constraint::Ratio(ratios[1] as u32, total_ratio), // eBPF info panel
2059                                    Constraint::Ratio(ratios[2] as u32, total_ratio), // Command panel
2060                                ]
2061                                .as_ref(),
2062                            )
2063                            .split(size)
2064                    }
2065                    LayoutMode::Vertical => {
2066                        Layout::default()
2067                            .direction(Direction::Vertical)
2068                            .constraints(
2069                                [
2070                                    Constraint::Ratio(ratios[0] as u32, total_ratio), // Source code panel
2071                                    Constraint::Ratio(ratios[1] as u32, total_ratio), // eBPF info panel
2072                                    Constraint::Ratio(ratios[2] as u32, total_ratio), // Command panel
2073                                ]
2074                                .as_ref(),
2075                            )
2076                            .split(size)
2077                    }
2078                };
2079
2080                // Draw panels in proper layout
2081                Self::draw_source_panel(f, chunks[0], state);
2082                Self::draw_ebpf_panel(f, chunks[1], state);
2083                Self::draw_command_panel(f, chunks[2], state);
2084            } else {
2085                // 2-panel layout: [EbpfInfo, InteractiveCommand]
2086                let ratios2 = state.ui.config.two_panel_ratios;
2087                let total2: u32 = (ratios2[0] as u32) + (ratios2[1] as u32);
2088
2089                let chunks = match state.ui.layout.mode {
2090                    LayoutMode::Horizontal => {
2091                        Layout::default()
2092                            .direction(Direction::Horizontal)
2093                            .constraints(
2094                                [
2095                                    Constraint::Ratio(ratios2[0] as u32, total2), // eBPF info panel
2096                                    Constraint::Ratio(ratios2[1] as u32, total2), // Command panel
2097                                ]
2098                                .as_ref(),
2099                            )
2100                            .split(size)
2101                    }
2102                    LayoutMode::Vertical => {
2103                        Layout::default()
2104                            .direction(Direction::Vertical)
2105                            .constraints(
2106                                [
2107                                    Constraint::Ratio(ratios2[0] as u32, total2), // eBPF info panel
2108                                    Constraint::Ratio(ratios2[1] as u32, total2), // Command panel
2109                                ]
2110                                .as_ref(),
2111                            )
2112                            .split(size)
2113                    }
2114                };
2115
2116                Self::draw_ebpf_panel(f, chunks[0], state);
2117                Self::draw_command_panel(f, chunks[1], state);
2118            }
2119        }
2120    }
2121
2122    /// Draw source panel
2123    fn draw_source_panel(f: &mut Frame, area: Rect, state: &AppState) {
2124        let is_focused = state.ui.focus.is_focused(PanelType::Source);
2125        // Create a mutable copy for rendering (area update)
2126        let mut source_state = state.source_panel.clone();
2127
2128        // Get cache reference (create empty cache if None)
2129        let empty_cache = crate::components::command_panel::FileCompletionCache::default();
2130        let cache = state
2131            .command_panel
2132            .file_completion_cache
2133            .as_ref()
2134            .unwrap_or(&empty_cache);
2135
2136        crate::components::source_panel::SourceRenderer::render(
2137            f,
2138            area,
2139            &mut source_state,
2140            cache,
2141            is_focused,
2142        );
2143    }
2144
2145    /// Draw eBPF panel
2146    fn draw_ebpf_panel(f: &mut Frame, area: Rect, state: &mut AppState) {
2147        let is_focused = state.ui.focus.is_focused(PanelType::EbpfInfo);
2148        state
2149            .ebpf_panel_renderer
2150            .render(&mut state.ebpf_panel, f, area, is_focused);
2151    }
2152
2153    /// Draw command panel
2154    fn draw_command_panel(f: &mut Frame, area: Rect, state: &mut AppState) {
2155        // Cache panel width for navigation calculations
2156        let old_width = state.command_panel.cached_panel_width;
2157        state.command_panel_width = area.width.saturating_sub(2); // Subtract borders
2158
2159        // Remap command cursor from old wraps to new wraps (before updating cached width)
2160        state
2161            .command_panel
2162            .remap_command_cursor_on_width_change(old_width, state.command_panel_width);
2163
2164        // Update cached width afterward to keep state consistent
2165        state
2166            .command_panel
2167            .update_panel_width(state.command_panel_width);
2168
2169        let is_focused = state.ui.focus.is_focused(PanelType::InteractiveCommand);
2170        let border_style = if is_focused {
2171            crate::ui::themes::UIThemes::panel_focused()
2172        } else {
2173            crate::ui::themes::UIThemes::panel_unfocused()
2174        };
2175
2176        let block = Block::default()
2177            .title(crate::ui::strings::UIStrings::COMMAND_PANEL_TITLE)
2178            .borders(Borders::ALL)
2179            .border_type(BorderType::Rounded)
2180            .border_style(border_style);
2181
2182        f.render_widget(block, area);
2183
2184        // Use optimized renderer for command panel content
2185        state
2186            .command_renderer
2187            .render(f, area, &state.command_panel, is_focused);
2188    }
2189
2190    /// Handle runtime status messages
2191    async fn handle_runtime_status(&mut self, status: crate::events::RuntimeStatus) {
2192        use crate::components::loading::LoadingState;
2193        use crate::events::RuntimeStatus;
2194
2195        // Update loading state based on runtime status
2196        match &status {
2197            RuntimeStatus::DwarfLoadingStarted => {
2198                self.state.set_loading_state(LoadingState::LoadingSymbols {
2199                    progress: Some(0.0),
2200                });
2201            }
2202            RuntimeStatus::DwarfLoadingCompleted { .. } => {
2203                if self.state.ui.config.show_source_panel {
2204                    self.state
2205                        .set_loading_state(LoadingState::LoadingSourceCode);
2206                } else {
2207                    // If source panel is disabled, we're effectively ready after symbols
2208                    self.transition_to_ready_with_completion();
2209                    // But we still need file info to power command panel completion/search
2210                    tracing::debug!(
2211                        "Source panel hidden on startup; requesting file list for completion cache"
2212                    );
2213                    if let Err(e) = self
2214                        .state
2215                        .event_registry
2216                        .command_sender
2217                        .send(crate::events::RuntimeCommand::InfoSource)
2218                    {
2219                        tracing::warn!("Failed to auto-request file list: {}", e);
2220                    }
2221                }
2222            }
2223            RuntimeStatus::DwarfLoadingFailed(error) => {
2224                self.state
2225                    .set_loading_state(LoadingState::Failed(error.clone()));
2226            }
2227            // Module-level progress handling
2228            RuntimeStatus::DwarfModuleDiscovered {
2229                module_path,
2230                total_modules: _,
2231            } => {
2232                // Add module to progress tracking
2233                self.state
2234                    .loading_ui
2235                    .progress
2236                    .add_module(module_path.clone());
2237            }
2238            RuntimeStatus::DwarfModuleLoadingStarted {
2239                module_path,
2240                current,
2241                total,
2242            } => {
2243                // Start loading specific module
2244                self.state
2245                    .loading_ui
2246                    .progress
2247                    .start_module_loading(module_path);
2248                // Update overall progress based on current/total
2249                let progress = (*current as f64) / (*total as f64);
2250                self.state.set_loading_state(LoadingState::LoadingSymbols {
2251                    progress: Some(progress),
2252                });
2253            }
2254            RuntimeStatus::DwarfModuleLoadingCompleted {
2255                module_path,
2256                stats,
2257                current,
2258                total,
2259            } => {
2260                // Complete module loading with stats
2261                let module_stats = crate::components::loading::ModuleStats {
2262                    functions: stats.functions,
2263                    variables: stats.variables,
2264                    types: stats.types,
2265                };
2266                self.state
2267                    .loading_ui
2268                    .progress
2269                    .complete_module(module_path, module_stats);
2270                // Update overall progress
2271                let progress = (*current as f64) / (*total as f64);
2272                self.state.set_loading_state(LoadingState::LoadingSymbols {
2273                    progress: Some(progress),
2274                });
2275            }
2276            RuntimeStatus::DwarfModuleLoadingFailed {
2277                module_path,
2278                error,
2279                current: _,
2280                total: _,
2281            } => {
2282                // Mark module as failed
2283                self.state
2284                    .loading_ui
2285                    .progress
2286                    .fail_module(module_path, error.clone());
2287            }
2288            RuntimeStatus::SourceCodeLoaded(_) => {
2289                // Transition to ready state with completion summary
2290                self.transition_to_ready_with_completion();
2291            }
2292            RuntimeStatus::SourceCodeLoadFailed(error) => {
2293                self.state
2294                    .set_loading_state(LoadingState::Failed(error.clone()));
2295
2296                // Also display the error in the source panel
2297                crate::components::source_panel::SourceNavigation::show_error_message(
2298                    &mut self.state.source_panel,
2299                    error.clone(),
2300                );
2301            }
2302            _ => {
2303                // For other status messages, if we're still initializing, move to connecting state
2304                if matches!(self.state.loading_state, LoadingState::Initializing) {
2305                    self.state
2306                        .set_loading_state(LoadingState::ConnectingToRuntime);
2307                }
2308            }
2309        }
2310
2311        match status {
2312            RuntimeStatus::SourceCodeLoaded(source_info) => {
2313                // Load source code into source panel
2314                let actions = crate::components::source_panel::SourceNavigation::load_source(
2315                    &mut self.state.source_panel,
2316                    source_info.file_path,
2317                    source_info.current_line,
2318                );
2319                for action in actions {
2320                    let _ = self.handle_action(action);
2321                }
2322
2323                // Auto-request file list for both file completion and source panel search
2324                tracing::debug!("Auto-requesting file list after source code loaded");
2325                if let Err(e) = self
2326                    .state
2327                    .event_registry
2328                    .command_sender
2329                    .send(crate::events::RuntimeCommand::InfoSource)
2330                {
2331                    tracing::warn!("Failed to auto-request file list: {}", e);
2332                }
2333            }
2334            RuntimeStatus::FileInfo { groups } => {
2335                // Convert file groups to flat file list
2336                let mut files = Vec::new();
2337                for group in &groups {
2338                    for file in &group.files {
2339                        // Combine directory and filename for full path
2340                        let full_path = if file.directory.is_empty() {
2341                            file.path.clone()
2342                        } else {
2343                            format!("{}/{}", file.directory, file.path)
2344                        };
2345                        files.push(full_path);
2346                    }
2347                }
2348
2349                // Always sync file list to command panel first
2350                self.sync_files_to_command_panel(files.clone());
2351
2352                if self.state.route_file_info_to_file_search {
2353                    // Route to source panel file search
2354                    if let Some(ref mut cache) = self.state.command_panel.file_completion_cache {
2355                        let actions =
2356                            crate::components::source_panel::SourceSearch::set_file_search_files(
2357                                &mut self.state.source_panel,
2358                                cache,
2359                                files.clone(),
2360                            );
2361                        for action in actions {
2362                            let _ = self.handle_action(action);
2363                        }
2364                    }
2365
2366                    // Reset routing flag
2367                    self.state.route_file_info_to_file_search = false;
2368                } else {
2369                    // Handle as command response (display in command panel)
2370                    self.clear_waiting_state();
2371                    let response =
2372                        crate::components::command_panel::ResponseFormatter::format_file_info(
2373                            &groups, false,
2374                        );
2375                    let styled_lines = crate::components::command_panel::ResponseFormatter::format_file_info_styled(
2376                        &groups, false,
2377                    );
2378                    let action = Action::AddResponseWithStyle {
2379                        content: response,
2380                        styled_lines: Some(styled_lines),
2381                        response_type: crate::action::ResponseType::Info,
2382                    };
2383                    let _ = self.handle_action(action);
2384                }
2385            }
2386            RuntimeStatus::FileInfoFailed { error } => {
2387                if self.state.route_file_info_to_file_search {
2388                    let actions =
2389                        crate::components::source_panel::SourceSearch::set_file_search_error(
2390                            &mut self.state.source_panel,
2391                            error,
2392                        );
2393                    for action in actions {
2394                        let _ = self.handle_action(action);
2395                    }
2396                    self.state.route_file_info_to_file_search = false;
2397                } else {
2398                    self.clear_waiting_state();
2399                    let plain = format!("āœ— Failed to get file information: {error}");
2400                    let styled = vec![
2401                        crate::components::command_panel::style_builder::StyledLineBuilder::new()
2402                            .styled(plain.clone(), crate::components::command_panel::style_builder::StylePresets::ERROR)
2403                            .build(),
2404                    ];
2405                    let action = Action::AddResponseWithStyle {
2406                        content: plain,
2407                        styled_lines: Some(styled),
2408                        response_type: crate::action::ResponseType::Error,
2409                    };
2410                    let _ = self.handle_action(action);
2411                }
2412            }
2413            RuntimeStatus::InfoFunctionResult {
2414                target: _,
2415                info,
2416                verbose,
2417            } => {
2418                // Mark command as completed
2419                self.clear_waiting_state();
2420                // Format and display function debug info
2421                let formatted_info = info.format_for_display(verbose);
2422                let styled_lines = info.format_for_display_styled(verbose);
2423                let action = Action::AddResponseWithStyle {
2424                    content: formatted_info,
2425                    styled_lines: Some(styled_lines),
2426                    response_type: crate::action::ResponseType::Success,
2427                };
2428                let _ = self.handle_action(action);
2429            }
2430            RuntimeStatus::InfoFunctionFailed { target, error } => {
2431                self.clear_waiting_state();
2432                let text = format!("āœ— Failed to get debug info for function '{target}': {error}");
2433                let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
2434                let action = Action::AddResponseWithStyle {
2435                    content: text,
2436                    styled_lines: Some(styled),
2437                    response_type: crate::action::ResponseType::Error,
2438                };
2439                let _ = self.handle_action(action);
2440            }
2441            RuntimeStatus::InfoLineResult {
2442                target: _,
2443                info,
2444                verbose,
2445            } => {
2446                // Mark command as completed
2447                self.clear_waiting_state();
2448                // Format and display line debug info
2449                let formatted_info = info.format_for_display(verbose);
2450                let styled_lines = info.format_for_display_styled(verbose);
2451                let action = Action::AddResponseWithStyle {
2452                    content: formatted_info,
2453                    styled_lines: Some(styled_lines),
2454                    response_type: crate::action::ResponseType::Success,
2455                };
2456                let _ = self.handle_action(action);
2457            }
2458            RuntimeStatus::InfoLineFailed { target, error } => {
2459                self.clear_waiting_state();
2460                let text = format!("āœ— Failed to get debug info for line '{target}': {error}");
2461                let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
2462                let action = Action::AddResponseWithStyle {
2463                    content: text,
2464                    styled_lines: Some(styled),
2465                    response_type: crate::action::ResponseType::Error,
2466                };
2467                let _ = self.handle_action(action);
2468            }
2469            RuntimeStatus::InfoAddressResult {
2470                target: _,
2471                info,
2472                verbose,
2473            } => {
2474                // Mark command as completed
2475                self.clear_waiting_state();
2476                // Format and display address debug info
2477                let formatted_info = info.format_for_display(verbose);
2478                let styled_lines = info.format_for_display_styled(verbose);
2479                let action = Action::AddResponseWithStyle {
2480                    content: formatted_info,
2481                    styled_lines: Some(styled_lines),
2482                    response_type: crate::action::ResponseType::Success,
2483                };
2484                let _ = self.handle_action(action);
2485            }
2486            RuntimeStatus::InfoAddressFailed { target, error } => {
2487                self.clear_waiting_state();
2488                let text = format!("āœ— Failed to get debug info for address '{target}': {error}");
2489                let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
2490                let action = Action::AddResponseWithStyle {
2491                    content: text,
2492                    styled_lines: Some(styled),
2493                    response_type: crate::action::ResponseType::Error,
2494                };
2495                let _ = self.handle_action(action);
2496            }
2497            RuntimeStatus::ShareInfo { libraries } => {
2498                // Determine whether to show all libraries or only those with debug info
2499                let show_all = matches!(
2500                    self.state.command_panel.input_state,
2501                    crate::model::panel_state::InputState::WaitingResponse {
2502                        command_type: crate::model::panel_state::CommandType::InfoShareAll,
2503                        ..
2504                    }
2505                );
2506
2507                self.clear_waiting_state();
2508
2509                let total = libraries.len();
2510                let display_libs: Vec<_> = if show_all {
2511                    libraries
2512                } else {
2513                    libraries
2514                        .into_iter()
2515                        .filter(|l| l.debug_info_available)
2516                        .collect()
2517                };
2518
2519                // If filtering removed all entries, avoid misleading "No shared libraries" message
2520                if !show_all && display_libs.is_empty() && total > 0 {
2521                    let content = format!(
2522                        "šŸ“š Shared Libraries ({total} total)\n\nāš ļø  No libraries with debug info found. Use 'info share all' to view all libraries."
2523                    );
2524                    let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&content);
2525                    let action = Action::AddResponseWithStyle {
2526                        content,
2527                        styled_lines: Some(styled),
2528                        response_type: crate::action::ResponseType::Success,
2529                    };
2530                    let _ = self.handle_action(action);
2531                } else {
2532                    let formatted_info =
2533                        crate::components::command_panel::ResponseFormatter::format_shared_library_info(
2534                            &display_libs, false,
2535                        );
2536                    let styled_lines =
2537                        crate::components::command_panel::ResponseFormatter::format_shared_library_info_styled(
2538                            &display_libs,
2539                            false,
2540                        );
2541                    let action = Action::AddResponseWithStyle {
2542                        content: formatted_info,
2543                        styled_lines: Some(styled_lines),
2544                        response_type: crate::action::ResponseType::Success,
2545                    };
2546                    let _ = self.handle_action(action);
2547                }
2548            }
2549            RuntimeStatus::ShareInfoFailed { error } => {
2550                self.clear_waiting_state();
2551                let text = format!("āœ— Failed to get shared library information: {error}");
2552                let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
2553                let action = Action::AddResponseWithStyle {
2554                    content: text,
2555                    styled_lines: Some(styled),
2556                    response_type: crate::action::ResponseType::Error,
2557                };
2558                let _ = self.handle_action(action);
2559            }
2560            RuntimeStatus::ExecutableFileInfo {
2561                file_path,
2562                file_type,
2563                entry_point,
2564                has_symbols,
2565                has_debug_info,
2566                debug_file_path,
2567                text_section,
2568                data_section,
2569                mode_description,
2570            } => {
2571                self.clear_waiting_state();
2572                let info_display =
2573                    crate::components::command_panel::response_formatter::ExecutableFileInfoDisplay {
2574                        file_path: &file_path,
2575                        file_type: &file_type,
2576                        entry_point,
2577                        has_symbols,
2578                        has_debug_info,
2579                        debug_file_path: &debug_file_path,
2580                        text_section: &text_section,
2581                        data_section: &data_section,
2582                        mode_description: &mode_description,
2583                    };
2584                let formatted_info =
2585                    crate::components::command_panel::ResponseFormatter::format_executable_file_info(
2586                        &info_display,
2587                    );
2588                let styled_lines =
2589                    crate::components::command_panel::ResponseFormatter::format_executable_file_info_styled(
2590                        &info_display,
2591                    );
2592                let action = Action::AddResponseWithStyle {
2593                    content: formatted_info,
2594                    styled_lines: Some(styled_lines),
2595                    response_type: crate::action::ResponseType::Success,
2596                };
2597                let _ = self.handle_action(action);
2598            }
2599            RuntimeStatus::ExecutableFileInfoFailed { error } => {
2600                self.clear_waiting_state();
2601                let text = format!("āœ— Failed to get executable file information: {error}");
2602                let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
2603                let action = Action::AddResponseWithStyle {
2604                    content: text,
2605                    styled_lines: Some(styled),
2606                    response_type: crate::action::ResponseType::Error,
2607                };
2608                let _ = self.handle_action(action);
2609            }
2610            RuntimeStatus::SrcPathInfo { info } => {
2611                self.clear_waiting_state();
2612                let formatted = info.format_for_display();
2613                let styled_lines = info.format_for_display_styled();
2614                let action = Action::AddResponseWithStyle {
2615                    content: formatted,
2616                    styled_lines: Some(styled_lines),
2617                    response_type: crate::action::ResponseType::Info,
2618                };
2619                let _ = self.handle_action(action);
2620            }
2621            RuntimeStatus::SrcPathUpdated { message } => {
2622                self.clear_waiting_state();
2623
2624                // Set flag to route upcoming FileInfo to file search panel
2625                // This ensures file search list is updated with new path mappings
2626                self.state.route_file_info_to_file_search = true;
2627
2628                let plain = format!("āœ… {message}\nšŸ’” Source code and file list reloading...");
2629                let styled = vec![
2630                    crate::components::command_panel::style_builder::StyledLineBuilder::new()
2631                        .styled(
2632                            format!("āœ… {message}"),
2633                            crate::components::command_panel::style_builder::StylePresets::SUCCESS,
2634                        )
2635                        .build(),
2636                    crate::components::command_panel::style_builder::StyledLineBuilder::new()
2637                        .styled(
2638                            "šŸ’” Source code and file list reloading...",
2639                            crate::components::command_panel::style_builder::StylePresets::TIP,
2640                        )
2641                        .build(),
2642                ];
2643                let action = Action::AddResponseWithStyle {
2644                    content: plain,
2645                    styled_lines: Some(styled),
2646                    response_type: crate::action::ResponseType::Success,
2647                };
2648                let _ = self.handle_action(action);
2649            }
2650            RuntimeStatus::SrcPathFailed { error } => {
2651                self.clear_waiting_state();
2652                let text = format!(
2653                    "āœ— {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"
2654                );
2655                let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
2656                let action = Action::AddResponseWithStyle {
2657                    content: text,
2658                    styled_lines: Some(styled),
2659                    response_type: crate::action::ResponseType::Error,
2660                };
2661                let _ = self.handle_action(action);
2662            }
2663            RuntimeStatus::TraceInfo {
2664                trace_id,
2665                target,
2666                status,
2667                pid,
2668                binary,
2669                script_preview,
2670                pc,
2671            } => {
2672                self.clear_waiting_state();
2673
2674                // Extract source location from target (could be "file:line" or "function_name")
2675                // TODO: For function traces, we need source_file and source_line fields in TraceInfo
2676                // Currently only file:line format works for source panel updates
2677                if let Some(colon_pos) = target.rfind(':') {
2678                    let file_part = &target[..colon_pos];
2679                    if let Ok(line_num) = target[colon_pos + 1..].parse::<usize>() {
2680                        // Store the trace location
2681                        self.state
2682                            .source_panel
2683                            .trace_locations
2684                            .insert(trace_id, (file_part.to_string(), line_num));
2685
2686                        // Update line colors if this is the current file
2687                        if self.state.source_panel.file_path.as_ref()
2688                            == Some(&file_part.to_string())
2689                        {
2690                            // Clear pending status if it exists
2691                            if self.state.source_panel.pending_trace_line == Some(line_num) {
2692                                self.state.source_panel.pending_trace_line = None;
2693                            }
2694
2695                            // Update line color based on trace status
2696                            match status {
2697                                crate::events::TraceStatus::Active => {
2698                                    self.state.source_panel.disabled_lines.remove(&line_num);
2699                                    self.state.source_panel.traced_lines.insert(line_num);
2700                                }
2701                                crate::events::TraceStatus::Disabled => {
2702                                    self.state.source_panel.traced_lines.remove(&line_num);
2703                                    self.state.source_panel.disabled_lines.insert(line_num);
2704                                }
2705                                _ => {
2706                                    // For other statuses, don't color the line
2707                                    self.state.source_panel.traced_lines.remove(&line_num);
2708                                    self.state.source_panel.disabled_lines.remove(&line_num);
2709                                }
2710                            }
2711                        }
2712                    }
2713                }
2714
2715                // Format trace info with enhanced display
2716                let mut response = format!("šŸ” Trace {trace_id} Info:\n");
2717                response.push_str(&format!("  Target: {target}\n"));
2718                response.push_str(&format!("  Status: {status}\n"));
2719                response.push_str(&format!("  Binary: {binary}\n"));
2720                response.push_str(&format!("  PC: 0x{pc:x}\n"));
2721                if let Some(p) = pid {
2722                    response.push_str(&format!("  PID: {p}\n"));
2723                }
2724                if let Some(ref preview) = script_preview {
2725                    response.push_str(&format!("  Script:\n{preview}\n"));
2726                }
2727                // Also create styled version
2728                let styled_lines = {
2729                    let temp = crate::events::RuntimeStatus::TraceInfo {
2730                        trace_id,
2731                        target: target.clone(),
2732                        status: status.clone(),
2733                        pid,
2734                        binary: binary.clone(),
2735                        script_preview: None,
2736                        pc,
2737                    };
2738                    if let Some(mut base) = temp.format_trace_info_styled() {
2739                        if let Some(ref preview) = script_preview {
2740                            use crate::components::command_panel::style_builder::StyledLineBuilder;
2741                            use ratatui::text::Line;
2742                            base.push(Line::from(""));
2743                            base.push(StyledLineBuilder::new().key("šŸ“ Script:").build());
2744                            for line in preview.lines() {
2745                                base.push(StyledLineBuilder::new().text("  ").value(line).build());
2746                            }
2747                        }
2748                        Some(base)
2749                    } else {
2750                        None
2751                    }
2752                };
2753
2754                let action = Action::AddResponseWithStyle {
2755                    content: response,
2756                    styled_lines,
2757                    response_type: crate::action::ResponseType::Info,
2758                };
2759                let _ = self.handle_action(action);
2760            }
2761            RuntimeStatus::TraceInfoAll { summary, traces } => {
2762                self.clear_waiting_state();
2763
2764                // Update source panel line colors based on trace status
2765                for trace in &traces {
2766                    // Try to extract file and line from target_display (format: "file:line" or "function_name")
2767                    // TODO: Need source_file and source_line fields for function traces
2768                    if let Some(colon_pos) = trace.target_display.rfind(':') {
2769                        let file_part = &trace.target_display[..colon_pos];
2770                        if let Ok(line_num) = trace.target_display[colon_pos + 1..].parse::<usize>()
2771                        {
2772                            // Store the trace location
2773                            self.state
2774                                .source_panel
2775                                .trace_locations
2776                                .insert(trace.trace_id, (file_part.to_string(), line_num));
2777
2778                            // Update line colors if this is the current file
2779                            if self.state.source_panel.file_path.as_ref()
2780                                == Some(&file_part.to_string())
2781                            {
2782                                match trace.status {
2783                                    crate::events::TraceStatus::Active => {
2784                                        self.state.source_panel.disabled_lines.remove(&line_num);
2785                                        self.state.source_panel.traced_lines.insert(line_num);
2786                                    }
2787                                    crate::events::TraceStatus::Disabled => {
2788                                        self.state.source_panel.traced_lines.remove(&line_num);
2789                                        self.state.source_panel.disabled_lines.insert(line_num);
2790                                    }
2791                                    crate::events::TraceStatus::Failed => {
2792                                        self.state.source_panel.traced_lines.remove(&line_num);
2793                                        self.state.source_panel.disabled_lines.remove(&line_num);
2794                                    }
2795                                }
2796                            }
2797                        }
2798                    }
2799                }
2800
2801                let mut response = format!(
2802                    "šŸ” All Traces ({} total, {} active):\n\n",
2803                    summary.total, summary.active
2804                );
2805                for trace in &traces {
2806                    // Use format_line() to show detailed info including address and module
2807                    response.push_str(&format!("  {}\n", trace.format_line()));
2808                }
2809                // Styled version
2810                let styled_lines = (crate::events::RuntimeStatus::TraceInfoAll {
2811                    summary: summary.clone(),
2812                    traces: traces.clone(),
2813                })
2814                .format_trace_info_styled()
2815                .unwrap_or_default();
2816                let action = Action::AddResponseWithStyle {
2817                    content: response,
2818                    styled_lines: if styled_lines.is_empty() {
2819                        None
2820                    } else {
2821                        Some(styled_lines)
2822                    },
2823                    response_type: crate::action::ResponseType::Info,
2824                };
2825                let _ = self.handle_action(action);
2826            }
2827            RuntimeStatus::TraceInfoFailed { trace_id, error } => {
2828                self.clear_waiting_state();
2829                let text = format!("āœ— Failed to get info for trace {trace_id}: {error}");
2830                let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
2831                let action = Action::AddResponseWithStyle {
2832                    content: text,
2833                    styled_lines: Some(styled),
2834                    response_type: crate::action::ResponseType::Error,
2835                };
2836                let _ = self.handle_action(action);
2837            }
2838            RuntimeStatus::TraceEnabled { trace_id } => {
2839                self.clear_waiting_state();
2840
2841                // Update source panel line color
2842                if let Some((file_path, line_num)) =
2843                    self.state.source_panel.trace_locations.get(&trace_id)
2844                {
2845                    if self.state.source_panel.file_path.as_ref() == Some(file_path) {
2846                        self.state.source_panel.disabled_lines.remove(line_num);
2847                        self.state.source_panel.traced_lines.insert(*line_num);
2848                    }
2849                }
2850
2851                let text = format!("āœ… Trace {trace_id} enabled");
2852                let styled = vec![
2853                    crate::components::command_panel::style_builder::StyledLineBuilder::new()
2854                        .styled(
2855                            text.clone(),
2856                            crate::components::command_panel::style_builder::StylePresets::SUCCESS,
2857                        )
2858                        .build(),
2859                ];
2860                let action = Action::AddResponseWithStyle {
2861                    content: text,
2862                    styled_lines: Some(styled),
2863                    response_type: crate::action::ResponseType::Success,
2864                };
2865                let _ = self.handle_action(action);
2866            }
2867            RuntimeStatus::TraceDisabled { trace_id } => {
2868                self.clear_waiting_state();
2869
2870                // Update source panel line color
2871                if let Some((file_path, line_num)) =
2872                    self.state.source_panel.trace_locations.get(&trace_id)
2873                {
2874                    if self.state.source_panel.file_path.as_ref() == Some(file_path) {
2875                        self.state.source_panel.traced_lines.remove(line_num);
2876                        self.state.source_panel.disabled_lines.insert(*line_num);
2877                    }
2878                }
2879
2880                let text = format!("āœ… Trace {trace_id} disabled");
2881                let styled = vec![
2882                    crate::components::command_panel::style_builder::StyledLineBuilder::new()
2883                        .styled(
2884                            text.clone(),
2885                            crate::components::command_panel::style_builder::StylePresets::SUCCESS,
2886                        )
2887                        .build(),
2888                ];
2889                let action = Action::AddResponseWithStyle {
2890                    content: text,
2891                    styled_lines: Some(styled),
2892                    response_type: crate::action::ResponseType::Success,
2893                };
2894                let _ = self.handle_action(action);
2895            }
2896            RuntimeStatus::AllTracesEnabled { count, error } => {
2897                self.clear_waiting_state();
2898
2899                if error.is_none() {
2900                    // Move all known traces from disabled to enabled
2901                    for (file_path, line_num) in self.state.source_panel.trace_locations.values() {
2902                        if self.state.source_panel.file_path.as_ref() == Some(file_path) {
2903                            self.state.source_panel.disabled_lines.remove(line_num);
2904                            self.state.source_panel.traced_lines.insert(*line_num);
2905                        }
2906                    }
2907                }
2908
2909                let (plain, rtype, style) = if let Some(ref err) = error {
2910                    (
2911                        format!("āœ— Failed to enable traces: {err}"),
2912                        crate::action::ResponseType::Error,
2913                        crate::components::command_panel::style_builder::StylePresets::ERROR,
2914                    )
2915                } else {
2916                    (
2917                        format!("āœ… All traces enabled ({count} traces)"),
2918                        crate::action::ResponseType::Success,
2919                        crate::components::command_panel::style_builder::StylePresets::SUCCESS,
2920                    )
2921                };
2922                let styled = vec![
2923                    crate::components::command_panel::style_builder::StyledLineBuilder::new()
2924                        .styled(plain.clone(), style)
2925                        .build(),
2926                ];
2927                let action = Action::AddResponseWithStyle {
2928                    content: plain,
2929                    styled_lines: Some(styled),
2930                    response_type: rtype,
2931                };
2932                let _ = self.handle_action(action);
2933            }
2934            RuntimeStatus::AllTracesDisabled { count, error } => {
2935                self.clear_waiting_state();
2936
2937                if error.is_none() {
2938                    // Move all known traces from enabled to disabled
2939                    for (file_path, line_num) in self.state.source_panel.trace_locations.values() {
2940                        if self.state.source_panel.file_path.as_ref() == Some(file_path) {
2941                            self.state.source_panel.traced_lines.remove(line_num);
2942                            self.state.source_panel.disabled_lines.insert(*line_num);
2943                        }
2944                    }
2945                }
2946
2947                let (plain, rtype, style) = if let Some(ref err) = error {
2948                    (
2949                        format!("āœ— Failed to disable traces: {err}"),
2950                        crate::action::ResponseType::Error,
2951                        crate::components::command_panel::style_builder::StylePresets::ERROR,
2952                    )
2953                } else {
2954                    (
2955                        format!("āœ… All traces disabled ({count} traces)"),
2956                        crate::action::ResponseType::Success,
2957                        crate::components::command_panel::style_builder::StylePresets::SUCCESS,
2958                    )
2959                };
2960                let styled = vec![
2961                    crate::components::command_panel::style_builder::StyledLineBuilder::new()
2962                        .styled(plain.clone(), style)
2963                        .build(),
2964                ];
2965                let action = Action::AddResponseWithStyle {
2966                    content: plain,
2967                    styled_lines: Some(styled),
2968                    response_type: rtype,
2969                };
2970                let _ = self.handle_action(action);
2971            }
2972            RuntimeStatus::TraceEnableFailed { trace_id, error } => {
2973                self.clear_waiting_state();
2974                let text = format!("āœ— Failed to enable trace {trace_id}: {error}");
2975                let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
2976                let action = Action::AddResponseWithStyle {
2977                    content: text,
2978                    styled_lines: Some(styled),
2979                    response_type: crate::action::ResponseType::Error,
2980                };
2981                let _ = self.handle_action(action);
2982            }
2983            RuntimeStatus::TraceDisableFailed { trace_id, error } => {
2984                self.clear_waiting_state();
2985                let text = format!("āœ— Failed to disable trace {trace_id}: {error}");
2986                let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
2987                let action = Action::AddResponseWithStyle {
2988                    content: text,
2989                    styled_lines: Some(styled),
2990                    response_type: crate::action::ResponseType::Error,
2991                };
2992                let _ = self.handle_action(action);
2993            }
2994            RuntimeStatus::TraceDeleted { trace_id } => {
2995                self.clear_waiting_state();
2996
2997                // Remove from source panel line colors
2998                if let Some((file_path, line_num)) =
2999                    self.state.source_panel.trace_locations.remove(&trace_id)
3000                {
3001                    if self.state.source_panel.file_path.as_ref() == Some(&file_path) {
3002                        self.state.source_panel.traced_lines.remove(&line_num);
3003                        self.state.source_panel.disabled_lines.remove(&line_num);
3004                    }
3005                }
3006
3007                let text = format!("āœ… Trace {trace_id} deleted");
3008                let styled = vec![
3009                    crate::components::command_panel::style_builder::StyledLineBuilder::new()
3010                        .styled(
3011                            text.clone(),
3012                            crate::components::command_panel::style_builder::StylePresets::SUCCESS,
3013                        )
3014                        .build(),
3015                ];
3016                let action = Action::AddResponseWithStyle {
3017                    content: text,
3018                    styled_lines: Some(styled),
3019                    response_type: crate::action::ResponseType::Success,
3020                };
3021                let _ = self.handle_action(action);
3022            }
3023            RuntimeStatus::AllTracesDeleted { count, error } => {
3024                self.clear_waiting_state();
3025
3026                if error.is_none() {
3027                    // Clear all trace locations and colors
3028                    self.state.source_panel.traced_lines.clear();
3029                    self.state.source_panel.disabled_lines.clear();
3030                    self.state.source_panel.trace_locations.clear();
3031                }
3032
3033                let (plain, rtype, style) = if let Some(ref err) = error {
3034                    (
3035                        format!("āœ— Failed to delete traces: {err}"),
3036                        crate::action::ResponseType::Error,
3037                        crate::components::command_panel::style_builder::StylePresets::ERROR,
3038                    )
3039                } else {
3040                    (
3041                        format!("āœ… All traces deleted ({count} traces)"),
3042                        crate::action::ResponseType::Success,
3043                        crate::components::command_panel::style_builder::StylePresets::SUCCESS,
3044                    )
3045                };
3046                let styled = vec![
3047                    crate::components::command_panel::style_builder::StyledLineBuilder::new()
3048                        .styled(plain.clone(), style)
3049                        .build(),
3050                ];
3051                let action = Action::AddResponseWithStyle {
3052                    content: plain,
3053                    styled_lines: Some(styled),
3054                    response_type: rtype,
3055                };
3056                let _ = self.handle_action(action);
3057            }
3058            RuntimeStatus::TraceDeleteFailed { trace_id, error } => {
3059                self.clear_waiting_state();
3060                let text = format!("āœ— Failed to delete trace {trace_id}: {error}");
3061                let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
3062                let action = Action::AddResponseWithStyle {
3063                    content: text,
3064                    styled_lines: Some(styled),
3065                    response_type: crate::action::ResponseType::Error,
3066                };
3067                let _ = self.handle_action(action);
3068            }
3069            RuntimeStatus::TracesSaved {
3070                filename,
3071                saved_count,
3072                total_count,
3073            } => {
3074                self.clear_waiting_state();
3075                let mut text =
3076                    format!("āœ… Saved {saved_count} of {total_count} traces to {filename}\n");
3077                text.push_str("   • Selected indices are preserved in the save file\n");
3078
3079                use crate::components::command_panel::style_builder::{
3080                    StylePresets, StyledLineBuilder,
3081                };
3082                let styled = vec![
3083                    StyledLineBuilder::new()
3084                        .styled(
3085                            format!("āœ… Saved {saved_count} of {total_count} traces to {filename}"),
3086                            StylePresets::SUCCESS,
3087                        )
3088                        .build(),
3089                    StyledLineBuilder::new()
3090                        .text("   • ")
3091                        .styled(
3092                            "Selected indices are preserved in the save file",
3093                            StylePresets::TIP,
3094                        )
3095                        .build(),
3096                ];
3097                let action = Action::AddResponseWithStyle {
3098                    content: text,
3099                    styled_lines: Some(styled),
3100                    response_type: crate::action::ResponseType::Success,
3101                };
3102                let _ = self.handle_action(action);
3103            }
3104            RuntimeStatus::TracesSaveFailed { error } => {
3105                self.clear_waiting_state();
3106                let text = format!("āœ— Failed to save traces: {error}");
3107                let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
3108                let action = Action::AddResponseWithStyle {
3109                    content: text,
3110                    styled_lines: Some(styled),
3111                    response_type: crate::action::ResponseType::Error,
3112                };
3113                let _ = self.handle_action(action);
3114            }
3115            RuntimeStatus::TracesLoaded {
3116                filename,
3117                total_count,
3118                success_count,
3119                failed_count,
3120                disabled_count,
3121                details,
3122            } => {
3123                self.clear_waiting_state();
3124
3125                // Build response message
3126                let mut response = String::new();
3127
3128                if failed_count == 0 {
3129                    // All traces loaded successfully
3130                    response.push_str(&format!(
3131                        "āœ“ Loaded {} traces from {} ({} enabled, {} disabled)",
3132                        total_count,
3133                        filename,
3134                        success_count - disabled_count,
3135                        disabled_count
3136                    ));
3137                    response.push('\n');
3138                    response.push_str("   • Selected indices from the file are restored\n");
3139                } else {
3140                    // Some traces failed
3141                    response.push_str(&format!("āš ļø Partially loaded traces from {filename}\n"));
3142                    response.push_str(&format!(
3143                        "  āœ“ {} traces created ({} enabled, {} disabled)\n",
3144                        success_count,
3145                        success_count - disabled_count,
3146                        disabled_count
3147                    ));
3148                    response
3149                        .push_str("  • Selected indices from the file are restored when present\n");
3150
3151                    // Show failed traces
3152                    for detail in &details {
3153                        if let crate::events::LoadStatus::Failed = detail.status {
3154                            if let Some(ref error) = detail.error {
3155                                response.push_str(&format!("  āœ— {} - {}\n", detail.target, error));
3156                            }
3157                        }
3158                    }
3159                }
3160
3161                // Styled version
3162                let mut styled = Vec::new();
3163                use crate::components::command_panel::style_builder::{
3164                    StylePresets, StyledLineBuilder,
3165                };
3166                if failed_count == 0 {
3167                    styled.push(
3168                        StyledLineBuilder::new()
3169                            .styled(
3170                                format!(
3171                                    "āœ… Loaded {} traces from {} ({} enabled, {} disabled)",
3172                                    total_count,
3173                                    filename,
3174                                    success_count - disabled_count,
3175                                    disabled_count
3176                                ),
3177                                StylePresets::SUCCESS,
3178                            )
3179                            .build(),
3180                    );
3181                    styled.push(
3182                        StyledLineBuilder::new()
3183                            .text("   • ")
3184                            .styled(
3185                                "Selected indices from the file are restored",
3186                                StylePresets::TIP,
3187                            )
3188                            .build(),
3189                    );
3190                } else {
3191                    styled.push(
3192                        StyledLineBuilder::new()
3193                            .styled(
3194                                format!("āš ļø Partially loaded traces from {filename}"),
3195                                StylePresets::WARNING,
3196                            )
3197                            .build(),
3198                    );
3199                    styled.push(
3200                        StyledLineBuilder::new()
3201                            .text("  ")
3202                            .styled(
3203                                format!(
3204                                    "āœ… {} traces created ({} enabled, {} disabled)",
3205                                    success_count,
3206                                    success_count - disabled_count,
3207                                    disabled_count
3208                                ),
3209                                StylePresets::SUCCESS,
3210                            )
3211                            .build(),
3212                    );
3213                    styled.push(
3214                        StyledLineBuilder::new()
3215                            .text("  • ")
3216                            .styled(
3217                                "Selected indices from the file are restored when present",
3218                                StylePresets::TIP,
3219                            )
3220                            .build(),
3221                    );
3222                    for detail in &details {
3223                        if let crate::events::LoadStatus::Failed = detail.status {
3224                            if let Some(ref err) = detail.error {
3225                                styled.push(
3226                                    StyledLineBuilder::new()
3227                                        .text("  ")
3228                                        .styled(
3229                                            format!("āœ— {} - {}", detail.target, err),
3230                                            StylePresets::ERROR,
3231                                        )
3232                                        .build(),
3233                                );
3234                            }
3235                        }
3236                    }
3237                }
3238
3239                let action = Action::AddResponseWithStyle {
3240                    content: response,
3241                    styled_lines: Some(styled),
3242                    response_type: if failed_count == 0 {
3243                        crate::action::ResponseType::Success
3244                    } else {
3245                        crate::action::ResponseType::Warning
3246                    },
3247                };
3248                let _ = self.handle_action(action);
3249            }
3250            RuntimeStatus::TracesLoadFailed { filename, error } => {
3251                self.clear_waiting_state();
3252                let text = format!("āœ— Failed to load {filename}: {error}");
3253                let styled = crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&text);
3254                let action = Action::AddResponseWithStyle {
3255                    content: text,
3256                    styled_lines: Some(styled),
3257                    response_type: crate::action::ResponseType::Error,
3258                };
3259                let _ = self.handle_action(action);
3260            }
3261            _ => {
3262                // Handle other runtime status messages (delegate to command panel or other components)
3263                // For now, pass them to command panel for display
3264
3265                // Check if this is an error status or completed status that should clear waiting state
3266                let should_clear_waiting = matches!(
3267                    status,
3268                    RuntimeStatus::AllTracesEnabled { .. }
3269                        | RuntimeStatus::AllTracesDisabled { .. }
3270                        | RuntimeStatus::AllTracesDeleted { .. }
3271                        | RuntimeStatus::ScriptCompilationCompleted { .. }
3272                        | RuntimeStatus::TraceInfoFailed { .. }
3273                        | RuntimeStatus::FileInfoFailed { .. }
3274                        | RuntimeStatus::ShareInfoFailed { .. }
3275                        | RuntimeStatus::ExecutableFileInfoFailed { .. }
3276                        | RuntimeStatus::SrcPathFailed { .. }
3277                );
3278
3279                if should_clear_waiting {
3280                    self.clear_waiting_state();
3281                }
3282
3283                if let Some(content) = self.format_runtime_status_for_display(&status) {
3284                    // Don't use styled_lines if content contains ANSI color codes
3285                    // Let the renderer handle ANSI parsing instead
3286                    let styled_lines = if content.contains("\x1b[") {
3287                        None
3288                    } else {
3289                        Some(crate::components::command_panel::ResponseFormatter::style_generic_message_lines(&content))
3290                    };
3291                    let action = Action::AddResponseWithStyle {
3292                        content,
3293                        styled_lines,
3294                        response_type: self.get_response_type_for_status(&status),
3295                    };
3296                    let _ = self.handle_action(action);
3297                }
3298            }
3299        }
3300    }
3301
3302    /// Handle trace events
3303    async fn handle_trace_event(&mut self, trace_event: ghostscope_protocol::ParsedTraceEvent) {
3304        tracing::debug!("Trace event: {:?}", trace_event);
3305
3306        // Realtime logging: write eBPF event to file if enabled
3307        if self.state.realtime_output_logger.enabled {
3308            if let Err(e) = self.write_ebpf_event_to_output_log(&trace_event) {
3309                tracing::error!("Failed to write eBPF event to output log: {}", e);
3310            }
3311        }
3312
3313        self.state.ebpf_panel.add_trace_event(trace_event);
3314    }
3315
3316    /// Format runtime status for display in command panel
3317    fn format_runtime_status_for_display(
3318        &mut self,
3319        status: &crate::events::RuntimeStatus,
3320    ) -> Option<String> {
3321        use crate::events::RuntimeStatus;
3322
3323        match status {
3324            RuntimeStatus::ScriptCompilationCompleted { details } => {
3325                // Check if this is part of a batch load operation
3326                if let Some(ref mut batch) = self.state.command_panel.batch_loading {
3327                    // Update batch loading state
3328                    batch.completed_count += 1;
3329                    if details.success_count > 0 {
3330                        batch.success_count += details.success_count;
3331                        // Add successful trace details
3332                        for result in &details.results {
3333                            if matches!(result.status, crate::events::ExecutionStatus::Success) {
3334                                let trace_id = details.trace_ids.first().copied();
3335                                batch.details.push(crate::events::TraceLoadDetail {
3336                                    target: result.target_name.clone(),
3337                                    trace_id,
3338                                    status: crate::events::LoadStatus::Created,
3339                                    error: None,
3340                                });
3341                            }
3342                        }
3343                    } else {
3344                        batch.failed_count += 1;
3345                        // Add failed trace details
3346                        for result in &details.results {
3347                            if let crate::events::ExecutionStatus::Failed(error) = &result.status {
3348                                batch.details.push(crate::events::TraceLoadDetail {
3349                                    target: result.target_name.clone(),
3350                                    trace_id: None,
3351                                    status: crate::events::LoadStatus::Failed,
3352                                    error: Some(error.clone()),
3353                                });
3354                            }
3355                        }
3356                    }
3357
3358                    // Check if all traces have been processed
3359                    if batch.completed_count >= batch.total_count {
3360                        // All traces processed, show summary
3361                        let filename = batch.filename.clone();
3362                        let total_count = batch.total_count;
3363                        let success_count = batch.success_count;
3364                        let failed_count = batch.failed_count;
3365                        let disabled_count = batch.disabled_count;
3366                        let details = batch.details.clone();
3367
3368                        // Clear batch loading state
3369                        self.state.command_panel.batch_loading = None;
3370
3371                        // Clear waiting state
3372                        self.clear_waiting_state();
3373
3374                        // Show summary response
3375                        let mut response = format!("šŸ“‚ Loaded traces from {filename}\n");
3376                        response.push_str(&format!(
3377                            "  Total: {total_count}, Success: {success_count}, Failed: {failed_count}"
3378                        ));
3379                        if disabled_count > 0 {
3380                            response.push_str(&format!(", Disabled: {disabled_count}"));
3381                        }
3382                        response.push('\n');
3383
3384                        // Show details
3385                        if !details.is_empty() {
3386                            response.push_str("\nšŸ“Š Details:\n");
3387                            for detail in &details {
3388                                match detail.status {
3389                                    crate::events::LoadStatus::Created => {
3390                                        if let Some(id) = detail.trace_id {
3391                                            response.push_str(&format!(
3392                                                "  āœ“ {} → trace #{}\n",
3393                                                detail.target, id
3394                                            ));
3395                                        } else {
3396                                            response.push_str(&format!("  āœ“ {}\n", detail.target));
3397                                        }
3398                                    }
3399                                    crate::events::LoadStatus::CreatedDisabled => {
3400                                        if let Some(id) = detail.trace_id {
3401                                            response.push_str(&format!(
3402                                                "  ⊘ {} → trace #{} (disabled)\n",
3403                                                detail.target, id
3404                                            ));
3405                                        } else {
3406                                            response.push_str(&format!(
3407                                                "  ⊘ {} (disabled)\n",
3408                                                detail.target
3409                                            ));
3410                                        }
3411                                    }
3412                                    crate::events::LoadStatus::Failed => {
3413                                        if let Some(ref error) = detail.error {
3414                                            response.push_str(&format!(
3415                                                "  āœ— {}: {}\n",
3416                                                detail.target, error
3417                                            ));
3418                                        } else {
3419                                            response.push_str(&format!("  āœ— {}\n", detail.target));
3420                                        }
3421                                    }
3422                                    _ => {}
3423                                }
3424                            }
3425                        }
3426
3427                        // Create styled version using helper method
3428                        let styled_lines =
3429                            crate::components::command_panel::ResponseFormatter::format_batch_load_summary_styled(
3430                                &filename,
3431                                total_count,
3432                                success_count,
3433                                failed_count,
3434                                disabled_count,
3435                                &details,
3436                            );
3437
3438                        let action = Action::AddResponseWithStyle {
3439                            content: response,
3440                            styled_lines: Some(styled_lines),
3441                            response_type: if failed_count > 0 {
3442                                crate::action::ResponseType::Warning
3443                            } else {
3444                                crate::action::ResponseType::Success
3445                            },
3446                        };
3447                        let _ = self.handle_action(action);
3448
3449                        // Don't return - continue to allow individual trace display to be suppressed
3450                        return None;
3451                    } else {
3452                        // Still waiting for more traces, suppress individual response
3453                        return None;
3454                    }
3455                }
3456
3457                // Not batch loading, handle normally
3458                // Clear waiting state for non-batch trace commands
3459                self.clear_waiting_state();
3460
3461                // Check if compilation actually succeeded
3462                if details.success_count > 0 || details.failed_count > 0 {
3463                    // Get script content from the current cache for better display
3464                    let script_content = self
3465                        .state
3466                        .command_panel
3467                        .script_cache
3468                        .as_ref()
3469                        .map(|cache| cache.lines.join("\n"));
3470
3471                    // Use new format_compilation_results to show all traces
3472                    Some(crate::components::command_panel::script_editor::ScriptEditor::format_compilation_results(
3473                        details,
3474                        script_content.as_deref(),
3475                        &self.state.emoji_config,
3476                    ))
3477                } else {
3478                    // All compilations failed - find the first failed result for error details
3479                    let first_failed = details.results.first();
3480                    if let Some(result) = first_failed {
3481                        if let crate::events::ExecutionStatus::Failed(error) = &result.status {
3482                            let error_details = crate::components::command_panel::script_editor::TraceErrorDetails {
3483                                compilation_errors: None,
3484                                uprobe_error: Some(error.clone()),
3485                                suggestion: Some("Check function name and ensure binary has debug symbols".to_string()),
3486                            };
3487
3488                            // Get script content for error display
3489                            let script_content = self
3490                                .state
3491                                .command_panel
3492                                .script_cache
3493                                .as_ref()
3494                                .map(|cache| cache.lines.join("\n"));
3495
3496                            Some(crate::components::command_panel::script_editor::ScriptEditor::format_trace_error_response_with_script(
3497                                &result.target_name,
3498                                error,
3499                                Some(&error_details),
3500                                script_content.as_deref(),
3501                                &self.state.emoji_config,
3502                            ))
3503                        } else {
3504                            None
3505                        }
3506                    } else {
3507                        None
3508                    }
3509                }
3510            }
3511            RuntimeStatus::AllTracesEnabled { count, error } => {
3512                if let Some(ref err) = error {
3513                    let error_emoji = self
3514                        .state
3515                        .emoji_config
3516                        .get_script_status(crate::ui::emoji::ScriptStatus::Error);
3517                    Some(format!("{error_emoji} {err}"))
3518                } else if *count > 0 {
3519                    let success_emoji = self
3520                        .state
3521                        .emoji_config
3522                        .get_trace_status(crate::ui::emoji::TraceStatusType::Active);
3523                    Some(format!("{success_emoji} Enabled {count} traces"))
3524                } else {
3525                    None
3526                }
3527            }
3528            RuntimeStatus::AllTracesDisabled { count, error } => {
3529                if let Some(ref err) = error {
3530                    let error_emoji = self
3531                        .state
3532                        .emoji_config
3533                        .get_script_status(crate::ui::emoji::ScriptStatus::Error);
3534                    Some(format!("{error_emoji} {err}"))
3535                } else if *count > 0 {
3536                    let disabled_emoji = self
3537                        .state
3538                        .emoji_config
3539                        .get_trace_status(crate::ui::emoji::TraceStatusType::Disabled);
3540                    Some(format!("{disabled_emoji} Disabled {count} traces"))
3541                } else {
3542                    None
3543                }
3544            }
3545            RuntimeStatus::AllTracesDeleted { count, error } => {
3546                if let Some(ref err) = error {
3547                    let error_emoji = self
3548                        .state
3549                        .emoji_config
3550                        .get_script_status(crate::ui::emoji::ScriptStatus::Error);
3551                    Some(format!("{error_emoji} {err}"))
3552                } else if *count > 0 {
3553                    Some(format!("āœ“ Deleted {count} traces"))
3554                } else {
3555                    None
3556                }
3557            }
3558            RuntimeStatus::TraceEnabled { trace_id } => {
3559                let success_emoji = self
3560                    .state
3561                    .emoji_config
3562                    .get_trace_status(crate::ui::emoji::TraceStatusType::Active);
3563                Some(format!("{success_emoji} Trace {trace_id} enabled"))
3564            }
3565            RuntimeStatus::TraceDisabled { trace_id } => {
3566                let disabled_emoji = self
3567                    .state
3568                    .emoji_config
3569                    .get_trace_status(crate::ui::emoji::TraceStatusType::Disabled);
3570                Some(format!("{disabled_emoji} Trace {trace_id} disabled"))
3571            }
3572            _ => None, // Don't display other status types in command panel
3573        }
3574    }
3575
3576    /// Get response type for runtime status
3577    fn get_response_type_for_status(
3578        &self,
3579        status: &crate::events::RuntimeStatus,
3580    ) -> crate::action::ResponseType {
3581        use crate::events::RuntimeStatus;
3582
3583        match status {
3584            RuntimeStatus::ScriptCompilationCompleted { details } => {
3585                // Check if compilation actually succeeded
3586                if details.success_count > 0 {
3587                    crate::action::ResponseType::Success
3588                } else {
3589                    crate::action::ResponseType::Error
3590                }
3591            }
3592            RuntimeStatus::AllTracesEnabled { error, .. }
3593            | RuntimeStatus::AllTracesDisabled { error, .. }
3594            | RuntimeStatus::AllTracesDeleted { error, .. } => {
3595                if error.is_some() {
3596                    crate::action::ResponseType::Error
3597                } else {
3598                    crate::action::ResponseType::Success
3599                }
3600            }
3601            _ => crate::action::ResponseType::Info,
3602        }
3603    }
3604
3605    /// Clear waiting state to return to ready input mode
3606    fn clear_waiting_state(&mut self) {
3607        self.state.command_panel.input_state = crate::model::panel_state::InputState::Ready;
3608    }
3609
3610    /// Validate and resolve file path for saving
3611    /// Returns the absolute path if valid, or an error if the path is unsafe
3612    fn validate_and_resolve_path(filename: &str) -> anyhow::Result<std::path::PathBuf> {
3613        use std::path::{Path, PathBuf};
3614
3615        // Check for path traversal attempts
3616        if filename.contains("..") {
3617            return Err(anyhow::anyhow!(
3618                "Path traversal not allowed (contains '..')"
3619            ));
3620        }
3621
3622        // Resolve to absolute path
3623        let file_path = if Path::new(filename).is_relative() {
3624            let current_dir = std::env::current_dir()?;
3625            current_dir.join(filename)
3626        } else {
3627            PathBuf::from(filename)
3628        };
3629
3630        // Canonicalize and verify the path stays within allowed directory
3631        // For relative paths, ensure they resolve within current directory
3632        if Path::new(filename).is_relative() {
3633            let current_dir = std::env::current_dir()?;
3634            let canonical_current = current_dir
3635                .canonicalize()
3636                .unwrap_or_else(|_| current_dir.clone());
3637
3638            // Check parent directory exists before canonicalizing
3639            if let Some(parent) = file_path.parent() {
3640                if !parent.exists() {
3641                    return Err(anyhow::anyhow!(
3642                        "Directory does not exist: {}",
3643                        parent.display()
3644                    ));
3645                }
3646
3647                // Verify resolved path is within current directory
3648                let canonical_parent = parent
3649                    .canonicalize()
3650                    .unwrap_or_else(|_| parent.to_path_buf());
3651                if !canonical_parent.starts_with(&canonical_current) {
3652                    return Err(anyhow::anyhow!("Cannot save outside current directory"));
3653                }
3654            }
3655        }
3656
3657        Ok(file_path)
3658    }
3659
3660    /// Start realtime eBPF output logging
3661    fn start_realtime_output_logging(
3662        &mut self,
3663        filename: Option<String>,
3664    ) -> anyhow::Result<std::path::PathBuf> {
3665        use chrono::Local;
3666
3667        // Check if already logging
3668        if self.state.realtime_output_logger.enabled {
3669            return Err(anyhow::anyhow!(
3670                "Realtime output logging already active to: {}",
3671                self.state
3672                    .realtime_output_logger
3673                    .file_path
3674                    .as_ref()
3675                    .map(|p| p.display().to_string())
3676                    .unwrap_or_else(|| "unknown".to_string())
3677            ));
3678        }
3679
3680        // Generate filename if not provided
3681        let filename = filename.unwrap_or_else(|| {
3682            let timestamp = Local::now().format("%Y%m%d_%H%M%S");
3683            format!("ebpf_output_{timestamp}.log")
3684        });
3685
3686        // Validate and resolve path
3687        let file_path = Self::validate_and_resolve_path(&filename)?;
3688
3689        // Determine if this is a new file
3690        let is_new_file = !file_path.exists();
3691
3692        // Start the logger
3693        self.state.realtime_output_logger.start(file_path.clone())?;
3694
3695        // Write header if this is a new file
3696        if is_new_file {
3697            let timestamp = Local::now().format("%Y-%m-%d %H:%M:%S");
3698            self.state
3699                .realtime_output_logger
3700                .write_line("# GhostScope eBPF Output Log (Realtime)")?;
3701            self.state
3702                .realtime_output_logger
3703                .write_line(&format!("# Session: {timestamp}"))?;
3704            self.state
3705                .realtime_output_logger
3706                .write_line("# ========================================")?;
3707            self.state.realtime_output_logger.write_line("")?;
3708        } else {
3709            // Add separator for continuation
3710            let timestamp = Local::now().format("%Y-%m-%d %H:%M:%S");
3711            self.state.realtime_output_logger.write_line("")?;
3712            self.state
3713                .realtime_output_logger
3714                .write_line("# ----------------------------------------")?;
3715            self.state
3716                .realtime_output_logger
3717                .write_line(&format!("# Resumed: {timestamp}"))?;
3718            self.state
3719                .realtime_output_logger
3720                .write_line("# ----------------------------------------")?;
3721            self.state.realtime_output_logger.write_line("")?;
3722        }
3723
3724        Ok(file_path)
3725    }
3726
3727    /// Write an eBPF event to the output log (realtime)
3728    fn write_ebpf_event_to_output_log(
3729        &mut self,
3730        event: &ghostscope_protocol::ParsedTraceEvent,
3731    ) -> anyhow::Result<()> {
3732        if self.state.realtime_output_logger.enabled {
3733            // Format timestamp
3734            let secs = event.timestamp / 1_000_000_000;
3735            let nanos = event.timestamp % 1_000_000_000;
3736            let formatted_ts = format!(
3737                "{:02}:{:02}:{:02}.{:06}",
3738                (secs / 3600) % 24,
3739                (secs / 60) % 60,
3740                secs % 60,
3741                nanos / 1000
3742            );
3743
3744            // Format output from instructions
3745            let formatted_output = event.to_formatted_output();
3746            let message = formatted_output.join(" ");
3747
3748            // Write: [timestamp] [PID xxxx/TID yyyy] Trace #id: message
3749            self.state.realtime_output_logger.write_line(&format!(
3750                "[{}] [PID {}/TID {}] Trace #{}: {}",
3751                formatted_ts, event.pid, event.tid, event.trace_id, message
3752            ))?;
3753        }
3754        Ok(())
3755    }
3756
3757    /// Write a command to the session log (realtime)
3758    fn write_command_to_session_log(&mut self, command: &str) -> anyhow::Result<()> {
3759        if self.state.realtime_session_logger.enabled {
3760            self.state.realtime_session_logger.write_line("")?;
3761            self.state
3762                .realtime_session_logger
3763                .write_line(&format!(">>> {command}"))?;
3764        }
3765        Ok(())
3766    }
3767
3768    /// Write a response to the session log (realtime)
3769    fn write_response_to_session_log(&mut self, response: &str) -> anyhow::Result<()> {
3770        if self.state.realtime_session_logger.enabled {
3771            for line in response.lines() {
3772                self.state
3773                    .realtime_session_logger
3774                    .write_line(&format!("    {line}"))?;
3775            }
3776        }
3777        Ok(())
3778    }
3779
3780    /// Start realtime command session logging
3781    fn start_realtime_session_logging(
3782        &mut self,
3783        filename: Option<String>,
3784    ) -> anyhow::Result<std::path::PathBuf> {
3785        use chrono::Local;
3786
3787        // Check if already logging
3788        if self.state.realtime_session_logger.enabled {
3789            return Err(anyhow::anyhow!(
3790                "Realtime session logging already active to: {}",
3791                self.state
3792                    .realtime_session_logger
3793                    .file_path
3794                    .as_ref()
3795                    .map(|p| p.display().to_string())
3796                    .unwrap_or_else(|| "unknown".to_string())
3797            ));
3798        }
3799
3800        // Generate filename if not provided
3801        let filename = filename.unwrap_or_else(|| {
3802            let timestamp = Local::now().format("%Y%m%d_%H%M%S");
3803            format!("command_session_{timestamp}.log")
3804        });
3805
3806        // Validate and resolve path
3807        let file_path = Self::validate_and_resolve_path(&filename)?;
3808
3809        // Determine if this is a new file
3810        let is_new_file = !file_path.exists();
3811
3812        // Start the logger
3813        self.state
3814            .realtime_session_logger
3815            .start(file_path.clone())?;
3816
3817        // Write header if this is a new file
3818        if is_new_file {
3819            let timestamp = Local::now().format("%Y-%m-%d %H:%M:%S");
3820            self.state
3821                .realtime_session_logger
3822                .write_line("# GhostScope Command Session Log (Realtime)")?;
3823            self.state
3824                .realtime_session_logger
3825                .write_line(&format!("# Session: {timestamp}"))?;
3826            self.state
3827                .realtime_session_logger
3828                .write_line("# ========================================")?;
3829            self.state.realtime_session_logger.write_line("")?;
3830
3831            // Write static lines (welcome messages)
3832            for static_line in &self.state.command_panel.static_lines {
3833                self.state
3834                    .realtime_session_logger
3835                    .write_line(&static_line.content)?;
3836            }
3837            self.state.realtime_session_logger.write_line("")?;
3838        } else {
3839            // Add separator for continuation
3840            let timestamp = Local::now().format("%Y-%m-%d %H:%M:%S");
3841            self.state.realtime_session_logger.write_line("")?;
3842            self.state
3843                .realtime_session_logger
3844                .write_line("# ----------------------------------------")?;
3845            self.state
3846                .realtime_session_logger
3847                .write_line(&format!("# Resumed: {timestamp}"))?;
3848            self.state
3849                .realtime_session_logger
3850                .write_line("# ----------------------------------------")?;
3851            self.state.realtime_session_logger.write_line("")?;
3852        }
3853
3854        Ok(file_path)
3855    }
3856
3857    /// Handle Ctrl+C with double-press quit and special mode handling
3858    fn handle_ctrl_c(&mut self) -> Vec<Action> {
3859        // If eBPF panel is in expanded view, close it on single Ctrl+C
3860        if self.state.ui.focus.current_panel == crate::action::PanelType::EbpfInfo
3861            && self.state.ebpf_panel.is_expanded()
3862        {
3863            self.state.ebpf_panel.close_expanded();
3864            // Do not treat as first press for quitting
3865            self.state.expecting_second_ctrl_c = false;
3866            return vec![];
3867        }
3868        // Check if this is a double Ctrl+C press (consecutive, no timeout)
3869        let is_double_press = self.state.expecting_second_ctrl_c;
3870
3871        // Set flag for next Ctrl+C press
3872        self.state.expecting_second_ctrl_c = true;
3873
3874        // Handle double press - always quit
3875        if is_double_press {
3876            tracing::info!("Double Ctrl+C detected, quitting application");
3877            return vec![Action::Quit];
3878        }
3879
3880        // Single Ctrl+C - handle based on current context
3881        match self.state.ui.focus.current_panel {
3882            crate::action::PanelType::InteractiveCommand => {
3883                // Command panel specific handling
3884                if self.state.command_panel.is_in_history_search() {
3885                    // In history search mode - exit search directly
3886                    self.state.command_panel.exit_history_search();
3887                    self.state.command_panel.input_text.clear();
3888                    self.state.command_panel.cursor_position = 0;
3889                    // Don't add empty response - would overwrite previous command's response
3890                    vec![]
3891                } else {
3892                    match self.state.command_panel.mode {
3893                        crate::model::panel_state::InteractionMode::ScriptEditor => {
3894                            // In script mode - exit to input mode
3895                            vec![Action::ExitScriptMode]
3896                        }
3897                        crate::model::panel_state::InteractionMode::Input => {
3898                            // In input mode - clear input and add "quit" command
3899                            self.state.command_panel.input_text.clear();
3900                            self.state.command_panel.cursor_position = 0;
3901                            self.state.command_panel.input_text = "quit".to_string();
3902                            self.state.command_panel.cursor_position = 4;
3903                            // Clear auto-suggestion to prevent suggestions after "quit"
3904                            self.state.command_panel.auto_suggestion.clear();
3905                            // Don't add response here - it would attach to previous command in history
3906                            // User will see "quit" in input box, which is clear enough
3907                            vec![]
3908                        }
3909                        _ => {
3910                            // Other modes - no action needed
3911                            vec![]
3912                        }
3913                    }
3914                }
3915            }
3916            crate::action::PanelType::Source => {
3917                if self.state.source_panel.mode
3918                    == crate::model::panel_state::SourcePanelMode::FileSearch
3919                {
3920                    // In file search mode - exit file search
3921                    vec![Action::ExitFileSearch]
3922                } else {
3923                    // Normal source panel - no action needed
3924                    vec![]
3925                }
3926            }
3927            _ => {
3928                // Other panels - no action needed
3929                vec![]
3930            }
3931        }
3932    }
3933
3934    /// Cleanup terminal
3935    async fn cleanup(&mut self) -> Result<()> {
3936        disable_raw_mode()?;
3937        // Disable bracketed paste before leaving alternate screen
3938        execute!(self.terminal.backend_mut(), DisableBracketedPaste)?;
3939        execute!(self.terminal.backend_mut(), LeaveAlternateScreen)?;
3940        // Mouse capture was not enabled, so no need to disable it
3941        self.terminal.show_cursor()?;
3942        Ok(())
3943    }
3944}