Skip to main content

soma_core/cli/
advanced_preview.rs

1// src/cli/advanced_preview.rs
2// Issue #18: AdvancedPreviewSystem - Split-pane Live Editor with Real-time Preview
3
4use crate::edit_control::{EditModificationInterface, EditModification};
5use crate::cli::preview::{EditPreviewBuilder, PreviewResult};
6use crossterm::{
7    cursor,
8    event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
9    execute,
10    style::{Color, ResetColor, SetForegroundColor},
11    terminal::{self, ClearType, size},
12};
13use std::io::{self, stdout, Write};
14use std::time::{Duration, Instant};
15
16/// AdvancedPreviewSystem for Issue #18
17/// Provides split-pane live editor with real-time preview capabilities
18pub struct AdvancedPreviewSystem {
19    interface: EditModificationInterface,
20    editor_pane: EditorPane,
21    preview_pane: PreviewPane,
22    layout: SplitLayout,
23    active_pane: ActivePane,
24    auto_preview: bool,
25    preview_delay: Duration,
26    last_edit: Instant,
27}
28
29#[derive(Debug, Clone)]
30pub struct EditorPane {
31    content: Vec<String>,
32    cursor_row: usize,
33    cursor_col: usize,
34    scroll_offset: usize,
35    syntax_highlighting: bool,
36    show_line_numbers: bool,
37    width: u16,
38    height: u16,
39}
40
41impl EditorPane {
42    pub fn content(&self) -> &Vec<String> {
43        &self.content
44    }
45
46    pub fn syntax_highlighting(&self) -> bool {
47        self.syntax_highlighting
48    }
49
50    pub fn show_line_numbers(&self) -> bool {
51        self.show_line_numbers
52    }
53
54    pub fn width(&self) -> u16 {
55        self.width
56    }
57
58    pub fn height(&self) -> u16 {
59        self.height
60    }
61}
62
63#[derive(Debug)]
64pub struct PreviewPane {
65    preview_result: Option<PreviewResult>,
66    scroll_offset: usize,
67    auto_refresh: bool,
68    width: u16,
69    height: u16,
70    last_update: Instant,
71}
72
73impl PreviewPane {
74    pub fn auto_refresh(&self) -> bool {
75        self.auto_refresh
76    }
77
78    pub fn width(&self) -> u16 {
79        self.width
80    }
81
82    pub fn height(&self) -> u16 {
83        self.height
84    }
85}
86
87#[derive(Debug, Clone, PartialEq)]
88pub enum SplitLayout {
89    Horizontal,    // Left-Right split
90    Vertical,      // Top-Bottom split
91    EditorOnly,    // Editor takes full screen
92    PreviewOnly,   // Preview takes full screen
93}
94
95#[derive(Debug, Clone, PartialEq)]
96pub enum ActivePane {
97    Editor,
98    Preview,
99}
100
101impl AdvancedPreviewSystem {
102    /// Create new AdvancedPreviewSystem from EditModificationInterface
103    pub fn new(interface: EditModificationInterface) -> Self {
104        let (terminal_width, terminal_height) = size().unwrap_or((80, 24));
105        
106        let editor_content: Vec<String> = interface
107            .modifiable_edit
108            .compute_final_code()
109            .lines()
110            .map(|s| s.to_string())
111            .collect();
112
113        let editor_pane = EditorPane {
114            content: editor_content,
115            cursor_row: 0,
116            cursor_col: 0,
117            scroll_offset: 0,
118            syntax_highlighting: true,
119            show_line_numbers: true,
120            width: terminal_width / 2,
121            height: terminal_height.saturating_sub(6), // Account for header/footer
122        };
123
124        let preview_pane = PreviewPane {
125            preview_result: None,
126            scroll_offset: 0,
127            auto_refresh: true,
128            width: terminal_width / 2,
129            height: terminal_height.saturating_sub(6),
130            last_update: Instant::now(),
131        };
132
133        Self {
134            interface,
135            editor_pane,
136            preview_pane,
137            layout: SplitLayout::Horizontal,
138            active_pane: ActivePane::Editor,
139            auto_preview: true,
140            preview_delay: Duration::from_millis(500), // 0.5s delay for auto-refresh
141            last_edit: Instant::now(),
142        }
143    }
144
145    /// Getter methods for demo access
146    pub fn layout(&self) -> &SplitLayout {
147        &self.layout
148    }
149
150    pub fn active_pane(&self) -> &ActivePane {
151        &self.active_pane
152    }
153
154    pub fn auto_preview(&self) -> bool {
155        self.auto_preview
156    }
157
158    pub fn preview_delay(&self) -> Duration {
159        self.preview_delay
160    }
161
162    pub fn editor_pane(&self) -> &EditorPane {
163        &self.editor_pane
164    }
165
166    pub fn preview_pane(&self) -> &PreviewPane {
167        &self.preview_pane
168    }
169
170    pub fn interface(&self) -> &EditModificationInterface {
171        &self.interface
172    }
173
174    pub fn interface_mut(&mut self) -> &mut EditModificationInterface {
175        &mut self.interface
176    }
177
178    /// Main interactive editing loop with live preview
179    pub fn edit_with_live_preview(&mut self) -> io::Result<Option<EditModificationInterface>> {
180        terminal::enable_raw_mode()?;
181
182        // Initial preview generation
183        self.update_preview();
184
185        let result = loop {
186            self.render()?;
187
188            // Auto-refresh preview if needed
189            if self.auto_preview && self.should_refresh_preview() {
190                self.update_preview();
191            }
192
193            if let Event::Key(key_event) = event::read()? {
194                match self.handle_key(key_event)? {
195                    PreviewAction::Exit => {
196                        break Ok(None);
197                    }
198                    PreviewAction::Save => {
199                        break Ok(Some(self.interface.clone()));
200                    }
201                    PreviewAction::Continue => {}
202                }
203            }
204        };
205
206        terminal::disable_raw_mode()?;
207        result
208    }
209
210    /// Render the split-pane interface
211    fn render(&self) -> io::Result<()> {
212        execute!(
213            stdout(),
214            terminal::Clear(ClearType::All),
215            cursor::MoveTo(0, 0)
216        )?;
217
218        // Header
219        self.render_header()?;
220
221        match self.layout {
222            SplitLayout::Horizontal => self.render_horizontal_split()?,
223            SplitLayout::Vertical => self.render_vertical_split()?,
224            SplitLayout::EditorOnly => self.render_editor_only()?,
225            SplitLayout::PreviewOnly => self.render_preview_only()?,
226        }
227
228        // Footer
229        self.render_footer()?;
230
231        // Position cursor
232        self.position_cursor()?;
233
234        stdout().flush()
235    }
236
237    fn render_header(&self) -> io::Result<()> {
238        execute!(stdout(), SetForegroundColor(Color::Cyan))?;
239        println!("🔄 SOMA AdvancedPreviewSystem v1.0 - Issue #18 - Split-Pane Live Editor");
240        execute!(stdout(), ResetColor)?;
241        
242        println!(
243            "📁 {} | Layout: {:?} | Active: {:?} | Auto-preview: {} | Changes: {}",
244            self.interface.modifiable_edit.base_edit.file,
245            self.layout,
246            self.active_pane,
247            if self.auto_preview { "ON" } else { "OFF" },
248            self.interface.modification_count()
249        );
250
251        println!(); // Separator
252        Ok(())
253    }
254
255    fn render_horizontal_split(&self) -> io::Result<()> {
256        let (terminal_width, _) = size().unwrap_or((80, 24));
257        let split_pos = terminal_width / 2;
258
259        // Split header
260        execute!(stdout(), SetForegroundColor(Color::Yellow))?;
261        print!("┌");
262        for _ in 0..split_pos.saturating_sub(2) {
263            print!("─");
264        }
265        print!("┬");
266        for _ in split_pos..terminal_width.saturating_sub(1) {
267            print!("─");
268        }
269        println!("┐");
270
271        print!("│");
272        execute!(stdout(), SetForegroundColor(Color::Green))?;
273        let editor_title = if self.active_pane == ActivePane::Editor { "► Editor" } else { "  Editor" };
274        print!("{:^width$}", editor_title, width = split_pos.saturating_sub(2) as usize);
275        execute!(stdout(), SetForegroundColor(Color::Yellow))?;
276        print!("│");
277        execute!(stdout(), SetForegroundColor(Color::Blue))?;
278        let preview_title = if self.active_pane == ActivePane::Preview { "► Live Preview" } else { "  Live Preview" };
279        print!("{:^width$}", preview_title, width = (terminal_width - split_pos - 1) as usize);
280        execute!(stdout(), SetForegroundColor(Color::Yellow))?;
281        println!("│");
282
283        print!("├");
284        for _ in 0..split_pos.saturating_sub(2) {
285            print!("─");
286        }
287        print!("┼");
288        for _ in split_pos..terminal_width.saturating_sub(1) {
289            print!("─");
290        }
291        println!("┤");
292        execute!(stdout(), ResetColor)?;
293
294        // Content area
295        let content_height = self.editor_pane.height.min(self.preview_pane.height) as usize;
296        for row in 0..content_height {
297            // Editor side
298            print!("│");
299            self.render_editor_line(row, split_pos.saturating_sub(2) as usize)?;
300            print!("│");
301            
302            // Preview side
303            self.render_preview_line(row, (terminal_width - split_pos - 1) as usize)?;
304            println!("│");
305        }
306
307        // Bottom border
308        execute!(stdout(), SetForegroundColor(Color::Yellow))?;
309        print!("└");
310        for _ in 0..split_pos.saturating_sub(2) {
311            print!("─");
312        }
313        print!("┴");
314        for _ in split_pos..terminal_width.saturating_sub(1) {
315            print!("─");
316        }
317        println!("┘");
318        execute!(stdout(), ResetColor)?;
319
320        Ok(())
321    }
322
323    fn render_vertical_split(&self) -> io::Result<()> {
324        let (terminal_width, terminal_height) = size().unwrap_or((80, 24));
325        let split_pos = terminal_height / 2;
326
327        // Editor section (top)
328        execute!(stdout(), SetForegroundColor(Color::Green))?;
329        let editor_title = if self.active_pane == ActivePane::Editor { "► Editor" } else { "  Editor" };
330        println!("┌─ {} {}", editor_title, "─".repeat(terminal_width.saturating_sub(12) as usize));
331        execute!(stdout(), ResetColor)?;
332
333        for row in 0..split_pos.saturating_sub(4) as usize {
334            self.render_editor_line(row, terminal_width as usize)?;
335            println!();
336        }
337
338        // Separator
339        execute!(stdout(), SetForegroundColor(Color::Yellow))?;
340        println!("{}", "─".repeat(terminal_width as usize));
341        execute!(stdout(), ResetColor)?;
342
343        // Preview section (bottom)
344        execute!(stdout(), SetForegroundColor(Color::Blue))?;
345        let preview_title = if self.active_pane == ActivePane::Preview { "► Live Preview" } else { "  Live Preview" };
346        println!("└─ {} {}", preview_title, "─".repeat(terminal_width.saturating_sub(17) as usize));
347        execute!(stdout(), ResetColor)?;
348
349        for row in 0..(terminal_height - split_pos - 2) as usize {
350            self.render_preview_line(row, terminal_width as usize)?;
351            println!();
352        }
353
354        Ok(())
355    }
356
357    fn render_editor_only(&self) -> io::Result<()> {
358        let (terminal_width, terminal_height) = size().unwrap_or((80, 24));
359        
360        execute!(stdout(), SetForegroundColor(Color::Green))?;
361        println!("┌─ ► Full Editor Mode {}", "─".repeat(terminal_width.saturating_sub(21) as usize));
362        execute!(stdout(), ResetColor)?;
363
364        for row in 0..terminal_height.saturating_sub(6) as usize {
365            self.render_editor_line(row, terminal_width as usize)?;
366            println!();
367        }
368
369        Ok(())
370    }
371
372    fn render_preview_only(&self) -> io::Result<()> {
373        let (terminal_width, terminal_height) = size().unwrap_or((80, 24));
374        
375        execute!(stdout(), SetForegroundColor(Color::Blue))?;
376        println!("┌─ ► Full Preview Mode {}", "─".repeat(terminal_width.saturating_sub(22) as usize));
377        execute!(stdout(), ResetColor)?;
378
379        for row in 0..terminal_height.saturating_sub(6) as usize {
380            self.render_preview_line(row, terminal_width as usize)?;
381            println!();
382        }
383
384        Ok(())
385    }
386
387    fn render_editor_line(&self, row: usize, width: usize) -> io::Result<()> {
388        let content_row = row + self.editor_pane.scroll_offset;
389        
390        if content_row < self.editor_pane.content.len() {
391            let line = &self.editor_pane.content[content_row];
392            let cursor_marker = if content_row == self.editor_pane.cursor_row { ">" } else { " " };
393            
394            let display_text = if self.editor_pane.show_line_numbers {
395                format!("{:3}:{} {}", content_row + 1, cursor_marker, line)
396            } else {
397                format!("{} {}", cursor_marker, line)
398            };
399
400            if self.editor_pane.syntax_highlighting && self.active_pane == ActivePane::Editor {
401                self.render_syntax_highlighted(&display_text, width)?;
402            } else {
403                print!("{:width$}", display_text.chars().take(width).collect::<String>(), width = width);
404            }
405        } else {
406            print!("{:width$}", "~", width = width);
407        }
408
409        Ok(())
410    }
411
412    fn render_preview_line(&self, row: usize, width: usize) -> io::Result<()> {
413        if let Some(ref preview) = self.preview_pane.preview_result {
414            let preview_text = self.get_preview_line(preview, row);
415            print!("{:width$}", preview_text.chars().take(width).collect::<String>(), width = width);
416        } else {
417            execute!(stdout(), SetForegroundColor(Color::DarkGrey))?;
418            print!("{:width$}", "Preview updating...", width = width);
419            execute!(stdout(), ResetColor)?;
420        }
421
422        Ok(())
423    }
424
425    fn get_preview_line(&self, preview: &PreviewResult, row: usize) -> String {
426        let content_row = row + self.preview_pane.scroll_offset;
427        let mut line_index = 0;
428
429        for section in &preview.sections {
430            let section_lines: Vec<&str> = section.content.lines().collect();
431            
432            if content_row == line_index {
433                return format!("╔═ {} ═╗", section.title);
434            }
435            line_index += 1;
436
437            for (i, line) in section_lines.iter().enumerate() {
438                if content_row == line_index {
439                    return if i == section_lines.len() - 1 {
440                        format!("╚ {} ╝", line)
441                    } else {
442                        format!("║ {} ║", line)
443                    };
444                }
445                line_index += 1;
446            }
447
448            if content_row == line_index {
449                return "║".to_string();
450            }
451            line_index += 1;
452        }
453
454        "".to_string()
455    }
456
457    fn render_syntax_highlighted(&self, text: &str, width: usize) -> io::Result<()> {
458        // Simple syntax highlighting for Rust-like code
459        let mut in_string = false;
460        let mut in_comment = false;
461        
462        for (i, ch) in text.chars().enumerate() {
463            if i >= width { break; }
464            
465            if in_comment {
466                execute!(stdout(), SetForegroundColor(Color::DarkGreen))?;
467                print!("{}", ch);
468            } else if in_string {
469                execute!(stdout(), SetForegroundColor(Color::Yellow))?;
470                print!("{}", ch);
471                if ch == '"' {
472                    in_string = false;
473                    execute!(stdout(), ResetColor)?;
474                }
475            } else {
476                match ch {
477                    '/' if text.chars().nth(i + 1) == Some('/') => {
478                        in_comment = true;
479                        execute!(stdout(), SetForegroundColor(Color::DarkGreen))?;
480                        print!("{}", ch);
481                    }
482                    '"' => {
483                        in_string = true;
484                        execute!(stdout(), SetForegroundColor(Color::Yellow))?;
485                        print!("{}", ch);
486                    }
487                    _ => {
488                        if text[..i].ends_with("fn ") || text[..i].ends_with("let ") || text[..i].ends_with("pub ") {
489                            execute!(stdout(), SetForegroundColor(Color::Blue))?;
490                        } else {
491                            execute!(stdout(), ResetColor)?;
492                        }
493                        print!("{}", ch);
494                    }
495                }
496            }
497        }
498        
499        // Pad remaining width
500        for _ in text.chars().count()..width {
501            print!(" ");
502        }
503        
504        execute!(stdout(), ResetColor)?;
505        Ok(())
506    }
507
508    fn render_footer(&self) -> io::Result<()> {
509        execute!(stdout(), SetForegroundColor(Color::Cyan))?;
510        println!("─────────────────────────────────────────────────────────────────────────");
511        execute!(stdout(), ResetColor)?;
512        
513        println!(
514            "Cursor: {}:{} | Undo: {} | Redo: {} | Last Update: {:.1}s ago | Tab: Switch Pane",
515            self.editor_pane.cursor_row + 1,
516            self.editor_pane.cursor_col + 1,
517            if self.interface.can_undo() { "✓" } else { "✗" },
518            if self.interface.can_redo() { "✓" } else { "✗" },
519            self.preview_pane.last_update.elapsed().as_secs_f32()
520        );
521
522        println!("Ctrl+S: Save | Ctrl+Q: Quit | Ctrl+L: Layout | Ctrl+A: Auto-preview | Ctrl+R: Refresh");
523
524        Ok(())
525    }
526
527    fn position_cursor(&self) -> io::Result<()> {
528        if self.active_pane == ActivePane::Editor {
529            let header_offset = 6; // Account for header and split decorations
530            let display_row = header_offset + self.editor_pane.cursor_row - self.editor_pane.scroll_offset;
531            let display_col = match self.layout {
532                SplitLayout::Horizontal => {
533                    let line_num_offset = if self.editor_pane.show_line_numbers { 6 } else { 2 };
534                    1 + line_num_offset + self.editor_pane.cursor_col
535                }
536                _ => {
537                    let line_num_offset = if self.editor_pane.show_line_numbers { 6 } else { 2 };
538                    line_num_offset + self.editor_pane.cursor_col
539                }
540            };
541            
542            execute!(
543                stdout(),
544                cursor::MoveTo(display_col as u16, display_row as u16)
545            )?;
546        }
547        
548        Ok(())
549    }
550
551    /// Enhanced key handling for split-pane interface
552    fn handle_key(&mut self, key: KeyEvent) -> io::Result<PreviewAction> {
553        match (key.code, key.modifiers) {
554            // Core actions
555            (KeyCode::Char('q'), KeyModifiers::CONTROL) => Ok(PreviewAction::Exit),
556            (KeyCode::Char('s'), KeyModifiers::CONTROL) => {
557                self.save_editor_changes();
558                Ok(PreviewAction::Save)
559            },
560            
561            // Layout controls
562            (KeyCode::Char('l'), KeyModifiers::CONTROL) => {
563                self.cycle_layout();
564                Ok(PreviewAction::Continue)
565            },
566            (KeyCode::Tab, _) => {
567                self.switch_active_pane();
568                Ok(PreviewAction::Continue)
569            },
570            
571            // Preview controls
572            (KeyCode::Char('a'), KeyModifiers::CONTROL) => {
573                self.auto_preview = !self.auto_preview;
574                Ok(PreviewAction::Continue)
575            },
576            (KeyCode::Char('r'), KeyModifiers::CONTROL) => {
577                self.update_preview();
578                Ok(PreviewAction::Continue)
579            },
580            
581            // Undo/Redo
582            (KeyCode::Char('z'), KeyModifiers::CONTROL) => {
583                if self.interface.undo() {
584                    self.update_editor_from_interface();
585                    if self.auto_preview {
586                        self.update_preview();
587                    }
588                }
589                Ok(PreviewAction::Continue)
590            },
591            (KeyCode::Char('y'), KeyModifiers::CONTROL) => {
592                if self.interface.redo() {
593                    self.update_editor_from_interface();
594                    if self.auto_preview {
595                        self.update_preview();
596                    }
597                }
598                Ok(PreviewAction::Continue)
599            },
600            
601            // Editor navigation (when editor is active)
602            _ if self.active_pane == ActivePane::Editor => {
603                self.handle_editor_key(key)
604            },
605            
606            // Preview navigation (when preview is active)
607            _ if self.active_pane == ActivePane::Preview => {
608                self.handle_preview_key(key)
609            },
610            
611            _ => Ok(PreviewAction::Continue),
612        }
613    }
614
615    fn handle_editor_key(&mut self, key: KeyEvent) -> io::Result<PreviewAction> {
616        match key.code {
617            KeyCode::Up => {
618                if self.editor_pane.cursor_row > 0 {
619                    self.editor_pane.cursor_row -= 1;
620                    self.adjust_editor_scroll();
621                    self.adjust_cursor_bounds();
622                }
623            }
624            KeyCode::Down => {
625                if self.editor_pane.cursor_row < self.editor_pane.content.len().saturating_sub(1) {
626                    self.editor_pane.cursor_row += 1;
627                    self.adjust_editor_scroll();
628                    self.adjust_cursor_bounds();
629                }
630            }
631            KeyCode::Left => {
632                if self.editor_pane.cursor_col > 0 {
633                    self.editor_pane.cursor_col -= 1;
634                } else if self.editor_pane.cursor_row > 0 {
635                    self.editor_pane.cursor_row -= 1;
636                    self.editor_pane.cursor_col = self.editor_pane.content
637                        .get(self.editor_pane.cursor_row)
638                        .map(|s| s.len())
639                        .unwrap_or(0);
640                    self.adjust_editor_scroll();
641                }
642            }
643            KeyCode::Right => {
644                let line_len = self.editor_pane.content
645                    .get(self.editor_pane.cursor_row)
646                    .map(|s| s.len())
647                    .unwrap_or(0);
648                
649                if self.editor_pane.cursor_col < line_len {
650                    self.editor_pane.cursor_col += 1;
651                } else if self.editor_pane.cursor_row < self.editor_pane.content.len().saturating_sub(1) {
652                    self.editor_pane.cursor_row += 1;
653                    self.editor_pane.cursor_col = 0;
654                    self.adjust_editor_scroll();
655                }
656            }
657            KeyCode::Home => {
658                self.editor_pane.cursor_col = 0;
659            }
660            KeyCode::End => {
661                self.editor_pane.cursor_col = self.editor_pane.content
662                    .get(self.editor_pane.cursor_row)
663                    .map(|s| s.len())
664                    .unwrap_or(0);
665            }
666            KeyCode::Enter => {
667                self.insert_newline();
668                self.mark_editor_changed();
669            }
670            KeyCode::Char(c) => {
671                self.insert_char(c);
672                self.mark_editor_changed();
673            }
674            KeyCode::Backspace => {
675                self.delete_char();
676                self.mark_editor_changed();
677            }
678            KeyCode::Delete => {
679                self.delete_char_forward();
680                self.mark_editor_changed();
681            }
682            _ => {}
683        }
684
685        Ok(PreviewAction::Continue)
686    }
687
688    fn handle_preview_key(&mut self, key: KeyEvent) -> io::Result<PreviewAction> {
689        match key.code {
690            KeyCode::Up => {
691                if self.preview_pane.scroll_offset > 0 {
692                    self.preview_pane.scroll_offset -= 1;
693                }
694            }
695            KeyCode::Down => {
696                self.preview_pane.scroll_offset += 1;
697            }
698            KeyCode::PageUp => {
699                self.preview_pane.scroll_offset = self.preview_pane.scroll_offset.saturating_sub(10);
700            }
701            KeyCode::PageDown => {
702                self.preview_pane.scroll_offset += 10;
703            }
704            KeyCode::Home => {
705                self.preview_pane.scroll_offset = 0;
706            }
707            _ => {}
708        }
709
710        Ok(PreviewAction::Continue)
711    }
712
713    fn cycle_layout(&mut self) {
714        self.layout = match self.layout {
715            SplitLayout::Horizontal => SplitLayout::Vertical,
716            SplitLayout::Vertical => SplitLayout::EditorOnly,
717            SplitLayout::EditorOnly => SplitLayout::PreviewOnly,
718            SplitLayout::PreviewOnly => SplitLayout::Horizontal,
719        };
720        self.update_pane_dimensions();
721    }
722
723    fn switch_active_pane(&mut self) {
724        self.active_pane = match self.active_pane {
725            ActivePane::Editor => ActivePane::Preview,
726            ActivePane::Preview => ActivePane::Editor,
727        };
728    }
729
730    fn update_pane_dimensions(&mut self) {
731        let (terminal_width, terminal_height) = size().unwrap_or((80, 24));
732        let content_height = terminal_height.saturating_sub(6);
733
734        match self.layout {
735            SplitLayout::Horizontal => {
736                self.editor_pane.width = terminal_width / 2;
737                self.editor_pane.height = content_height;
738                self.preview_pane.width = terminal_width / 2;
739                self.preview_pane.height = content_height;
740            }
741            SplitLayout::Vertical => {
742                self.editor_pane.width = terminal_width;
743                self.editor_pane.height = content_height / 2;
744                self.preview_pane.width = terminal_width;
745                self.preview_pane.height = content_height / 2;
746            }
747            SplitLayout::EditorOnly => {
748                self.editor_pane.width = terminal_width;
749                self.editor_pane.height = content_height;
750            }
751            SplitLayout::PreviewOnly => {
752                self.preview_pane.width = terminal_width;
753                self.preview_pane.height = content_height;
754            }
755        }
756    }
757
758    fn should_refresh_preview(&self) -> bool {
759        self.last_edit.elapsed() >= self.preview_delay
760    }
761
762    pub fn update_preview(&mut self) {
763        let preview_builder = EditPreviewBuilder::new()
764            .with_diff(true)
765            .with_risk_assessment(true)
766            .with_syntax_highlighting(true);
767        
768        let preview = preview_builder.build();
769        self.preview_pane.preview_result = Some(preview.generate_preview(&self.interface.modifiable_edit));
770        self.preview_pane.last_update = Instant::now();
771    }
772
773    fn mark_editor_changed(&mut self) {
774        self.last_edit = Instant::now();
775    }
776
777    fn save_editor_changes(&mut self) {
778        let new_content = self.editor_pane.content.join("\n");
779        let old_content = self.interface.modifiable_edit.compute_final_code();
780        
781        if new_content != old_content {
782            self.interface.add_modification(
783                EditModification::CodeChange {
784                    line: 0,
785                    old: old_content,
786                    new: new_content,
787                },
788                "Live editor changes".to_string(),
789            );
790            self.update_preview();
791        }
792    }
793
794    fn update_editor_from_interface(&mut self) {
795        let new_content = self.interface.modifiable_edit.compute_final_code();
796        self.editor_pane.content = new_content.lines().map(|s| s.to_string()).collect();
797    }
798
799    fn adjust_cursor_bounds(&mut self) {
800        if let Some(line) = self.editor_pane.content.get(self.editor_pane.cursor_row) {
801            if self.editor_pane.cursor_col > line.len() {
802                self.editor_pane.cursor_col = line.len();
803            }
804        }
805    }
806
807    fn adjust_editor_scroll(&mut self) {
808        let visible_lines = self.editor_pane.height as usize;
809        
810        if self.editor_pane.cursor_row < self.editor_pane.scroll_offset {
811            self.editor_pane.scroll_offset = self.editor_pane.cursor_row;
812        } else if self.editor_pane.cursor_row >= self.editor_pane.scroll_offset + visible_lines {
813            self.editor_pane.scroll_offset = self.editor_pane.cursor_row - visible_lines + 1;
814        }
815    }
816
817    fn insert_char(&mut self, c: char) {
818        if self.editor_pane.cursor_row < self.editor_pane.content.len() {
819            self.editor_pane.content[self.editor_pane.cursor_row].insert(self.editor_pane.cursor_col, c);
820            self.editor_pane.cursor_col += 1;
821        }
822    }
823
824    fn insert_newline(&mut self) {
825        if self.editor_pane.cursor_row < self.editor_pane.content.len() {
826            let current_line = self.editor_pane.content[self.editor_pane.cursor_row].clone();
827            let (left, right) = current_line.split_at(self.editor_pane.cursor_col);
828
829            self.editor_pane.content[self.editor_pane.cursor_row] = left.to_string();
830            self.editor_pane.content.insert(self.editor_pane.cursor_row + 1, right.to_string());
831
832            self.editor_pane.cursor_row += 1;
833            self.editor_pane.cursor_col = 0;
834            self.adjust_editor_scroll();
835        }
836    }
837
838    fn delete_char(&mut self) {
839        if self.editor_pane.cursor_col > 0 && self.editor_pane.cursor_row < self.editor_pane.content.len() {
840            self.editor_pane.content[self.editor_pane.cursor_row].remove(self.editor_pane.cursor_col - 1);
841            self.editor_pane.cursor_col -= 1;
842        } else if self.editor_pane.cursor_row > 0 {
843            // Merge with previous line
844            let current_line = self.editor_pane.content.remove(self.editor_pane.cursor_row);
845            self.editor_pane.cursor_row -= 1;
846            self.editor_pane.cursor_col = self.editor_pane.content[self.editor_pane.cursor_row].len();
847            self.editor_pane.content[self.editor_pane.cursor_row].push_str(&current_line);
848            self.adjust_editor_scroll();
849        }
850    }
851
852    fn delete_char_forward(&mut self) {
853        if self.editor_pane.cursor_row < self.editor_pane.content.len() {
854            let line_len = self.editor_pane.content[self.editor_pane.cursor_row].len();
855            if self.editor_pane.cursor_col < line_len {
856                self.editor_pane.content[self.editor_pane.cursor_row].remove(self.editor_pane.cursor_col);
857            } else if self.editor_pane.cursor_row < self.editor_pane.content.len() - 1 {
858                // Merge with next line
859                let next_line = self.editor_pane.content.remove(self.editor_pane.cursor_row + 1);
860                self.editor_pane.content[self.editor_pane.cursor_row].push_str(&next_line);
861            }
862        }
863    }
864}
865
866#[derive(Debug)]
867enum PreviewAction {
868    Continue,
869    Save,
870    Exit,
871}
872
873#[cfg(test)]
874mod tests {
875    use super::*;
876    use crate::agents::gpt4_agent::ProposedEdit;
877    use crate::edit_control::ModifiableEdit;
878
879    fn create_test_interface() -> EditModificationInterface {
880        let proposed = ProposedEdit {
881            file: "test.rs".to_string(),
882            line_range: (10, 15),
883            new_code: "fn test() {\n    println!(\"Hello, World!\");\n    return 42;\n}".to_string(),
884            reason: "Test function for AdvancedPreviewSystem".to_string(),
885            confidence: 0.9,
886        };
887        let modifiable = ModifiableEdit::from_proposed_edit(proposed);
888        EditModificationInterface::new(modifiable)
889    }
890
891    #[test]
892    fn test_advanced_preview_system_creation() {
893        let interface = create_test_interface();
894        let preview_system = AdvancedPreviewSystem::new(interface);
895
896        assert_eq!(preview_system.layout, SplitLayout::Horizontal);
897        assert_eq!(preview_system.active_pane, ActivePane::Editor);
898        assert!(preview_system.auto_preview);
899        assert_eq!(preview_system.editor_pane.content.len(), 4); // 4 lines in test code
900        assert_eq!(preview_system.editor_pane.cursor_row, 0);
901        assert_eq!(preview_system.editor_pane.cursor_col, 0);
902    }
903
904    #[test]
905    fn test_layout_cycling() {
906        let interface = create_test_interface();
907        let mut preview_system = AdvancedPreviewSystem::new(interface);
908
909        assert_eq!(preview_system.layout, SplitLayout::Horizontal);
910        
911        preview_system.cycle_layout();
912        assert_eq!(preview_system.layout, SplitLayout::Vertical);
913        
914        preview_system.cycle_layout();
915        assert_eq!(preview_system.layout, SplitLayout::EditorOnly);
916        
917        preview_system.cycle_layout();
918        assert_eq!(preview_system.layout, SplitLayout::PreviewOnly);
919        
920        preview_system.cycle_layout();
921        assert_eq!(preview_system.layout, SplitLayout::Horizontal);
922    }
923
924    #[test]
925    fn test_pane_switching() {
926        let interface = create_test_interface();
927        let mut preview_system = AdvancedPreviewSystem::new(interface);
928
929        assert_eq!(preview_system.active_pane, ActivePane::Editor);
930        
931        preview_system.switch_active_pane();
932        assert_eq!(preview_system.active_pane, ActivePane::Preview);
933        
934        preview_system.switch_active_pane();
935        assert_eq!(preview_system.active_pane, ActivePane::Editor);
936    }
937
938    #[test]
939    fn test_editor_text_insertion() {
940        let interface = create_test_interface();
941        let mut preview_system = AdvancedPreviewSystem::new(interface);
942
943        let initial_line = preview_system.editor_pane.content[0].clone();
944        
945        preview_system.insert_char('X');
946        assert_eq!(preview_system.editor_pane.cursor_col, 1);
947        assert!(preview_system.editor_pane.content[0].starts_with('X'));
948        assert_ne!(preview_system.editor_pane.content[0], initial_line);
949    }
950
951    #[test]
952    fn test_editor_newline_insertion() {
953        let interface = create_test_interface();
954        let mut preview_system = AdvancedPreviewSystem::new(interface);
955
956        let initial_lines = preview_system.editor_pane.content.len();
957        
958        preview_system.insert_newline();
959        assert_eq!(preview_system.editor_pane.content.len(), initial_lines + 1);
960        assert_eq!(preview_system.editor_pane.cursor_row, 1);
961        assert_eq!(preview_system.editor_pane.cursor_col, 0);
962    }
963
964    #[test]
965    fn test_cursor_movement() {
966        let interface = create_test_interface();
967        let mut preview_system = AdvancedPreviewSystem::new(interface);
968
969        // Test right movement
970        let initial_col = preview_system.editor_pane.cursor_col;
971        preview_system.editor_pane.cursor_col = 5;
972        preview_system.adjust_cursor_bounds();
973        assert!(preview_system.editor_pane.cursor_col >= initial_col);
974
975        // Test down movement
976        preview_system.editor_pane.cursor_row = 1;
977        preview_system.adjust_cursor_bounds();
978        assert!(preview_system.editor_pane.cursor_row <= preview_system.editor_pane.content.len());
979    }
980
981    #[test]
982    fn test_scroll_adjustment() {
983        let interface = create_test_interface();
984        let mut preview_system = AdvancedPreviewSystem::new(interface);
985
986        // Simulate moving cursor beyond visible area
987        preview_system.editor_pane.cursor_row = 50;
988        preview_system.editor_pane.height = 20; // Visible height
989        preview_system.adjust_editor_scroll();
990
991        // Scroll offset should adjust to keep cursor visible
992        assert!(preview_system.editor_pane.scroll_offset > 0);
993        assert!(preview_system.editor_pane.cursor_row >= preview_system.editor_pane.scroll_offset);
994    }
995
996    #[test]
997    fn test_auto_preview_timing() {
998        let interface = create_test_interface();
999        let mut preview_system = AdvancedPreviewSystem::new(interface);
1000
1001        // Initially should not need refresh
1002        assert!(!preview_system.should_refresh_preview());
1003
1004        // Simulate passage of time by setting a very short delay
1005        preview_system.preview_delay = Duration::from_nanos(1);
1006        std::thread::sleep(Duration::from_millis(1));
1007
1008        // Now should need refresh
1009        assert!(preview_system.should_refresh_preview());
1010    }
1011
1012    #[test]
1013    fn test_preview_line_extraction() {
1014        let interface = create_test_interface();
1015        let mut preview_system = AdvancedPreviewSystem::new(interface);
1016
1017        preview_system.update_preview();
1018        
1019        if let Some(ref preview) = preview_system.preview_pane.preview_result {
1020            let line = preview_system.get_preview_line(preview, 0);
1021            assert!(!line.is_empty());
1022            // Should contain section header format
1023            assert!(line.contains("╔═") || line.contains("║") || line.is_empty());
1024        }
1025    }
1026}