Terminal Markdown previewer — GUI-like experience.
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

chore: split app theme picker

RivoLink 20893047 e3149e23

+165 -152
+6 -152
src/app/mod.rs
··· 5 5 toc::{should_hide_single_h1, should_promote_h2_when_no_h1, toc_display_level, TocEntry}, 6 6 }, 7 7 render::{build_status_bar, build_toc_line_with_index, toc_header_line}, 8 - theme::{ 9 - current_syntect_theme, current_theme_preset, set_theme_preset, theme_preset_index, 10 - ThemePreset, THEME_PRESETS, 11 - }, 8 + theme::{current_syntect_theme, current_theme_preset, theme_preset_index}, 12 9 }; 13 10 use ratatui::text::Line; 14 11 use std::{ ··· 23 20 pub(crate) mod file_picker; 24 21 pub(crate) use file_picker::{FilePickerMode, FilePickerState, PickerIndexTruncation}; 25 22 use file_picker::{PendingPicker, PickerLoadState}; 23 + 24 + pub(super) mod theme_picker; 25 + pub(crate) use theme_picker::ThemePickerState; 26 26 27 27 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 28 28 pub(crate) struct FileState { ··· 50 50 flash_active: bool, 51 51 } 52 52 53 - #[derive(Clone)] 54 - pub(crate) struct ThemePreviewCacheEntry { 55 - lines: Vec<Line<'static>>, 56 - toc: Vec<TocEntry>, 57 - } 58 - 59 - pub(crate) struct ThemePickerState { 60 - open: bool, 61 - index: usize, 62 - original: Option<ThemePreset>, 63 - preview_cache: Vec<Option<ThemePreviewCacheEntry>>, 64 - } 65 - 66 53 pub(crate) struct AppConfig { 67 54 pub(crate) filename: String, 68 55 pub(crate) source: String, ··· 73 60 } 74 61 75 62 pub(crate) struct App { 76 - lines: Vec<Line<'static>>, 63 + pub(super) lines: Vec<Line<'static>>, 77 64 pub(super) plain_lines: Vec<String>, 78 65 pub(super) folded_plain_lines: Option<Vec<String>>, 79 66 pub(super) scroll: usize, 80 - toc: Vec<TocEntry>, 67 + pub(super) toc: Vec<TocEntry>, 81 68 toc_visible: bool, 82 69 pub(super) search: SearchState, 83 70 pub(super) debug_input: bool, ··· 378 365 self.status_cache_key = None; 379 366 } 380 367 381 - pub(crate) fn invalidate_theme_preview_cache(&mut self) { 382 - self.theme_picker.preview_cache.fill(None); 383 - } 384 - 385 - fn store_theme_preview( 386 - &mut self, 387 - preset: ThemePreset, 388 - lines: &[Line<'static>], 389 - toc: &[TocEntry], 390 - ) { 391 - let idx = theme_preset_index(preset); 392 - if let Some(slot) = self.theme_picker.preview_cache.get_mut(idx) { 393 - *slot = Some(ThemePreviewCacheEntry { 394 - lines: lines.to_vec(), 395 - toc: toc.to_vec(), 396 - }); 397 - } 398 - } 399 - 400 - fn store_current_theme_preview(&mut self) { 401 - let preset = current_theme_preset(); 402 - let lines = self.lines.clone(); 403 - let toc = self.toc.clone(); 404 - self.store_theme_preview(preset, &lines, &toc); 405 - } 406 - 407 - pub(crate) fn open_theme_picker(&mut self) { 408 - self.theme_picker.open = true; 409 - let current = current_theme_preset(); 410 - self.theme_picker.index = theme_preset_index(current); 411 - self.theme_picker.original = Some(current); 412 - self.store_current_theme_preview(); 413 - } 414 - 415 - pub(crate) fn close_theme_picker(&mut self) { 416 - self.theme_picker.open = false; 417 - self.theme_picker.original = None; 418 - } 419 - 420 - pub(crate) fn is_theme_picker_open(&self) -> bool { 421 - self.theme_picker.open 422 - } 423 - 424 368 pub(crate) fn open_help(&mut self) { 425 369 self.help_open = true; 426 370 } ··· 433 377 self.help_open 434 378 } 435 379 436 - pub(crate) fn theme_picker_index(&self) -> usize { 437 - self.theme_picker.index 438 - } 439 - 440 - #[cfg(test)] 441 - pub(crate) fn theme_picker_original(&self) -> Option<ThemePreset> { 442 - self.theme_picker.original 443 - } 444 - 445 380 pub(crate) fn clear_reload_flash(&mut self) { 446 381 self.reload_flash = None; 447 382 } ··· 452 387 453 388 pub(crate) fn set_last_file_state(&mut self, state: FileState) { 454 389 self.last_file_state = Some(state); 455 - } 456 - 457 - pub(crate) fn theme_picker_reference_preset(&self) -> ThemePreset { 458 - self.theme_picker.original.unwrap_or(current_theme_preset()) 459 - } 460 - 461 - pub(crate) fn move_theme_picker_up(&mut self) { 462 - let total = THEME_PRESETS.len(); 463 - if total == 0 { 464 - return; 465 - } 466 - if self.theme_picker.index == 0 { 467 - self.theme_picker.index = total - 1; 468 - } else { 469 - self.theme_picker.index -= 1; 470 - } 471 - } 472 - 473 - pub(crate) fn move_theme_picker_down(&mut self) { 474 - let total = THEME_PRESETS.len(); 475 - if total == 0 { 476 - return; 477 - } 478 - self.theme_picker.index = (self.theme_picker.index + 1) % total; 479 - } 480 - 481 - pub(crate) fn set_theme_picker_index(&mut self, idx: usize) -> bool { 482 - if idx < THEME_PRESETS.len() { 483 - self.theme_picker.index = idx; 484 - true 485 - } else { 486 - false 487 - } 488 - } 489 - 490 - pub(crate) fn selected_theme_preset(&self) -> Option<ThemePreset> { 491 - THEME_PRESETS.get(self.theme_picker.index).copied() 492 - } 493 - 494 - #[cfg(test)] 495 - pub(crate) fn has_cached_theme_preview(&self, preset: ThemePreset) -> bool { 496 - self.theme_picker 497 - .preview_cache 498 - .get(theme_preset_index(preset)) 499 - .and_then(|entry| entry.as_ref()) 500 - .is_some() 501 - } 502 - 503 - pub(crate) fn preview_theme_preset( 504 - &mut self, 505 - preset: ThemePreset, 506 - ss: &SyntaxSet, 507 - themes: &ThemeSet, 508 - ) { 509 - if current_theme_preset() == preset { 510 - return; 511 - } 512 - set_theme_preset(preset); 513 - let cached = self 514 - .theme_picker 515 - .preview_cache 516 - .get(theme_preset_index(preset)) 517 - .and_then(|entry| entry.as_ref()) 518 - .cloned(); 519 - if let Some(entry) = cached { 520 - self.replace_content(entry.lines, entry.toc); 521 - return; 522 - } 523 - 524 - let theme = current_syntect_theme(themes); 525 - let (new_lines, new_toc) = 526 - parse_markdown_with_width(&self.source, ss, theme, self.render_width); 527 - self.store_theme_preview(preset, &new_lines, &new_toc); 528 - self.replace_content(new_lines, new_toc); 529 - } 530 - 531 - pub(crate) fn restore_theme_picker_preview(&mut self, ss: &SyntaxSet, themes: &ThemeSet) { 532 - if let Some(original) = self.theme_picker.original { 533 - self.preview_theme_preset(original, ss, themes); 534 - } 535 - self.close_theme_picker(); 536 390 } 537 391 538 392 pub(crate) fn scroll_down(&mut self, n: usize) {
+159
src/app/theme_picker.rs
··· 1 + use crate::{ 2 + markdown::{parse_markdown_with_width, toc::TocEntry}, 3 + theme::{ 4 + current_syntect_theme, current_theme_preset, set_theme_preset, theme_preset_index, 5 + ThemePreset, THEME_PRESETS, 6 + }, 7 + }; 8 + use ratatui::text::Line; 9 + use syntect::{highlighting::ThemeSet, parsing::SyntaxSet}; 10 + 11 + use super::App; 12 + 13 + #[derive(Clone)] 14 + pub(crate) struct ThemePreviewCacheEntry { 15 + pub(super) lines: Vec<Line<'static>>, 16 + pub(super) toc: Vec<TocEntry>, 17 + } 18 + 19 + pub(crate) struct ThemePickerState { 20 + pub(super) open: bool, 21 + pub(super) index: usize, 22 + pub(super) original: Option<ThemePreset>, 23 + pub(super) preview_cache: Vec<Option<ThemePreviewCacheEntry>>, 24 + } 25 + 26 + impl App { 27 + pub(crate) fn open_theme_picker(&mut self) { 28 + self.theme_picker.open = true; 29 + let current = current_theme_preset(); 30 + self.theme_picker.index = theme_preset_index(current); 31 + self.theme_picker.original = Some(current); 32 + self.store_current_theme_preview(); 33 + } 34 + 35 + pub(crate) fn close_theme_picker(&mut self) { 36 + self.theme_picker.open = false; 37 + self.theme_picker.original = None; 38 + } 39 + 40 + pub(crate) fn is_theme_picker_open(&self) -> bool { 41 + self.theme_picker.open 42 + } 43 + 44 + pub(crate) fn theme_picker_index(&self) -> usize { 45 + self.theme_picker.index 46 + } 47 + 48 + #[cfg(test)] 49 + pub(crate) fn theme_picker_original(&self) -> Option<ThemePreset> { 50 + self.theme_picker.original 51 + } 52 + 53 + pub(crate) fn theme_picker_reference_preset(&self) -> ThemePreset { 54 + self.theme_picker.original.unwrap_or(current_theme_preset()) 55 + } 56 + 57 + pub(crate) fn move_theme_picker_up(&mut self) { 58 + let total = THEME_PRESETS.len(); 59 + if total == 0 { 60 + return; 61 + } 62 + if self.theme_picker.index == 0 { 63 + self.theme_picker.index = total - 1; 64 + } else { 65 + self.theme_picker.index -= 1; 66 + } 67 + } 68 + 69 + pub(crate) fn move_theme_picker_down(&mut self) { 70 + let total = THEME_PRESETS.len(); 71 + if total == 0 { 72 + return; 73 + } 74 + self.theme_picker.index = (self.theme_picker.index + 1) % total; 75 + } 76 + 77 + pub(crate) fn set_theme_picker_index(&mut self, idx: usize) -> bool { 78 + if idx < THEME_PRESETS.len() { 79 + self.theme_picker.index = idx; 80 + true 81 + } else { 82 + false 83 + } 84 + } 85 + 86 + pub(crate) fn selected_theme_preset(&self) -> Option<ThemePreset> { 87 + THEME_PRESETS.get(self.theme_picker.index).copied() 88 + } 89 + 90 + pub(crate) fn preview_theme_preset( 91 + &mut self, 92 + preset: ThemePreset, 93 + ss: &SyntaxSet, 94 + themes: &ThemeSet, 95 + ) { 96 + if current_theme_preset() == preset { 97 + return; 98 + } 99 + set_theme_preset(preset); 100 + let cached = self 101 + .theme_picker 102 + .preview_cache 103 + .get(theme_preset_index(preset)) 104 + .and_then(|entry| entry.as_ref()) 105 + .cloned(); 106 + if let Some(entry) = cached { 107 + self.replace_content(entry.lines, entry.toc); 108 + return; 109 + } 110 + 111 + let theme = current_syntect_theme(themes); 112 + let (new_lines, new_toc) = 113 + parse_markdown_with_width(&self.source, ss, theme, self.render_width); 114 + self.store_theme_preview(preset, &new_lines, &new_toc); 115 + self.replace_content(new_lines, new_toc); 116 + } 117 + 118 + pub(crate) fn restore_theme_picker_preview(&mut self, ss: &SyntaxSet, themes: &ThemeSet) { 119 + if let Some(original) = self.theme_picker.original { 120 + self.preview_theme_preset(original, ss, themes); 121 + } 122 + self.close_theme_picker(); 123 + } 124 + 125 + pub(crate) fn store_theme_preview( 126 + &mut self, 127 + preset: ThemePreset, 128 + lines: &[Line<'static>], 129 + toc: &[TocEntry], 130 + ) { 131 + let idx = theme_preset_index(preset); 132 + if let Some(slot) = self.theme_picker.preview_cache.get_mut(idx) { 133 + *slot = Some(ThemePreviewCacheEntry { 134 + lines: lines.to_vec(), 135 + toc: toc.to_vec(), 136 + }); 137 + } 138 + } 139 + 140 + pub(crate) fn store_current_theme_preview(&mut self) { 141 + let preset = current_theme_preset(); 142 + let lines = self.lines.clone(); 143 + let toc = self.toc.clone(); 144 + self.store_theme_preview(preset, &lines, &toc); 145 + } 146 + 147 + pub(crate) fn invalidate_theme_preview_cache(&mut self) { 148 + self.theme_picker.preview_cache.fill(None); 149 + } 150 + 151 + #[cfg(test)] 152 + pub(crate) fn has_cached_theme_preview(&self, preset: ThemePreset) -> bool { 153 + self.theme_picker 154 + .preview_cache 155 + .get(theme_preset_index(preset)) 156 + .and_then(|entry| entry.as_ref()) 157 + .is_some() 158 + } 159 + }