Skip to main content

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