diff --git a/CHANGELOG.md b/CHANGELOG.md index cb4a542..62fc5b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. ## [0.1.0] +### UI / UX (breaking cosmetic changes) +- **Options page**: Each option now shows a short description line below when focused, replacing the static header box. +- **Dictionary page**: Removed the Forth description box at the top. The word list now uses the full page height. + ### CLAP Plugin (experimental) - Early CLAP plugin support via nih-plug, baseview, and egui. Feature-gated builds separate CLI from plugin targets. diff --git a/crates/ratatui/src/active_patterns.rs b/crates/ratatui/src/active_patterns.rs deleted file mode 100644 index 94288b1..0000000 --- a/crates/ratatui/src/active_patterns.rs +++ /dev/null @@ -1,104 +0,0 @@ -use crate::theme; -use ratatui::buffer::Buffer; -use ratatui::layout::Rect; -use ratatui::widgets::Widget; - -#[derive(Clone, Copy, PartialEq, Eq)] -pub enum MuteStatus { - Normal, - Muted, - Soloed, - EffectivelyMuted, // Solo active on another pattern -} - -pub struct ActivePatterns<'a> { - patterns: &'a [(usize, usize, usize)], // (bank, pattern, iter) - mute_status: Option<&'a [MuteStatus]>, - current_step: Option<(usize, usize)>, // (current_step, total_steps) -} - -impl<'a> ActivePatterns<'a> { - pub fn new(patterns: &'a [(usize, usize, usize)]) -> Self { - Self { - patterns, - mute_status: None, - current_step: None, - } - } - - pub fn with_step(mut self, current: usize, total: usize) -> Self { - self.current_step = Some((current, total)); - self - } - - pub fn with_mute_status(mut self, status: &'a [MuteStatus]) -> Self { - self.mute_status = Some(status); - self - } -} - -impl Widget for ActivePatterns<'_> { - fn render(self, area: Rect, buf: &mut Buffer) { - if area.width < 10 || area.height == 0 { - return; - } - let theme = theme::get(); - - let max_pattern_rows = if self.current_step.is_some() { - area.height.saturating_sub(1) as usize - } else { - area.height as usize - }; - - for (row, &(bank, pattern, iter)) in self.patterns.iter().enumerate() { - if row >= max_pattern_rows { - break; - } - - let mute_status = self - .mute_status - .and_then(|s| s.get(row)) - .copied() - .unwrap_or(MuteStatus::Normal); - - let (prefix, fg, bg) = match mute_status { - MuteStatus::Soloed => ("S", theme.list.soloed_fg, theme.list.soloed_bg), - MuteStatus::Muted => ("M", theme.list.muted_fg, theme.list.muted_bg), - MuteStatus::EffectivelyMuted => (" ", theme.list.muted_fg, theme.list.muted_bg), - MuteStatus::Normal => { - let bg = if row % 2 == 0 { - theme.table.row_even - } else { - theme.table.row_odd - }; - (" ", theme.ui.text_primary, bg) - } - }; - - let text = format!("{}B{:02}:{:02}({:02})", prefix, bank + 1, pattern + 1, iter.min(99)); - let y = area.y + row as u16; - - let mut chars = text.chars(); - for col in 0..area.width as usize { - let ch = chars.next().unwrap_or(' '); - buf[(area.x + col as u16, y)] - .set_char(ch) - .set_fg(fg) - .set_bg(bg); - } - } - - if let Some((current, total)) = self.current_step { - let text = format!("{:02}/{:02}", current + 1, total); - let y = area.y + area.height.saturating_sub(1); - let mut chars = text.chars(); - for col in 0..area.width as usize { - let ch = chars.next().unwrap_or(' '); - buf[(area.x + col as u16, y)] - .set_char(ch) - .set_fg(theme.ui.text_primary) - .set_bg(theme.table.row_even); - } - } - } -} diff --git a/crates/ratatui/src/lib.rs b/crates/ratatui/src/lib.rs index 6ab2898..4e8cb95 100644 --- a/crates/ratatui/src/lib.rs +++ b/crates/ratatui/src/lib.rs @@ -1,4 +1,3 @@ -mod active_patterns; mod category_list; mod confirm; mod editor; @@ -20,7 +19,6 @@ pub mod theme; mod vu_meter; mod waveform; -pub use active_patterns::{ActivePatterns, MuteStatus}; pub use category_list::{CategoryItem, CategoryList, Selection}; pub use confirm::ConfirmModal; pub use editor::{fuzzy_match, CompletionCandidate, Editor}; diff --git a/src/app/dispatch.rs b/src/app/dispatch.rs index c8255f0..ca40ada 100644 --- a/src/app/dispatch.rs +++ b/src/app/dispatch.rs @@ -388,6 +388,7 @@ impl App { AppCommand::ToggleScope => self.audio.config.show_scope = !self.audio.config.show_scope, AppCommand::ToggleSpectrum => self.audio.config.show_spectrum = !self.audio.config.show_spectrum, AppCommand::TogglePreview => self.audio.config.show_preview = !self.audio.config.show_preview, + AppCommand::TogglePerformanceMode => self.ui.performance_mode = !self.ui.performance_mode, // Metrics AppCommand::ResetPeakVoices => self.metrics.peak_voices = 0, diff --git a/src/app/persistence.rs b/src/app/persistence.rs index 6383e3f..19ba548 100644 --- a/src/app/persistence.rs +++ b/src/app/persistence.rs @@ -26,6 +26,7 @@ impl App { show_spectrum: self.audio.config.show_spectrum, show_preview: self.audio.config.show_preview, show_completion: self.ui.show_completion, + performance_mode: self.ui.performance_mode, color_scheme: self.ui.color_scheme, layout: self.audio.config.layout, hue_rotation: self.ui.hue_rotation, diff --git a/src/commands.rs b/src/commands.rs index 954f4c2..d2932cf 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -251,6 +251,7 @@ pub enum AppCommand { ToggleScope, ToggleSpectrum, TogglePreview, + TogglePerformanceMode, // Metrics ResetPeakVoices, diff --git a/src/init.rs b/src/init.rs index b62ef58..7e4e075 100644 --- a/src/init.rs +++ b/src/init.rs @@ -85,6 +85,7 @@ pub fn init(args: InitArgs) -> Init { app.audio.config.show_spectrum = settings.display.show_spectrum; app.audio.config.show_preview = settings.display.show_preview; app.ui.show_completion = settings.display.show_completion; + app.ui.performance_mode = settings.display.performance_mode; app.ui.color_scheme = settings.display.color_scheme; app.ui.hue_rotation = settings.display.hue_rotation; app.audio.config.layout = settings.display.layout; diff --git a/src/input/mouse.rs b/src/input/mouse.rs index 5e53c36..1920f86 100644 --- a/src/input/mouse.rs +++ b/src/input/mouse.rs @@ -139,10 +139,7 @@ fn handle_scroll(ctx: &mut InputContext, col: u16, row: u16, term: Rect, up: boo } } Page::Dict => { - let (header_area, [cat_area, words_area]) = dict_view::layout(body); - if contains(header_area, col, row) { - return; - } + let [cat_area, words_area] = dict_view::layout(body); if contains(cat_area, col, row) { if up { ctx.dispatch(AppCommand::DictPrevCategory); @@ -300,7 +297,7 @@ fn handle_body_click(ctx: &mut InputContext, col: u16, row: u16, body: Rect) { // --- Main page (grid) --- fn handle_main_click(ctx: &mut InputContext, col: u16, row: u16, area: Rect) { - let [_patterns_area, _, main_area, _, _vu_area] = main_view::layout(area); + let [main_area, _, _vu_area] = main_view::layout(area); if !contains(main_area, col, row) { return; @@ -533,7 +530,7 @@ fn handle_help_click(ctx: &mut InputContext, col: u16, row: u16, area: Rect) { // --- Dict page --- fn handle_dict_click(ctx: &mut InputContext, col: u16, row: u16, area: Rect) { - let (_header_area, [cat_area, words_area]) = dict_view::layout(area); + let [cat_area, words_area] = dict_view::layout(area); if contains(cat_area, col, row) { use crate::model::categories::{self, CatEntry, CATEGORIES}; diff --git a/src/input/options_page.rs b/src/input/options_page.rs index b53e381..91e4e04 100644 --- a/src/input/options_page.rs +++ b/src/input/options_page.rs @@ -26,6 +26,7 @@ pub(crate) fn cycle_option_value(ctx: &mut InputContext, right: bool) { OptionsFocus::ShowSpectrum => ctx.dispatch(AppCommand::ToggleSpectrum), OptionsFocus::ShowCompletion => ctx.dispatch(AppCommand::ToggleCompletion), OptionsFocus::ShowPreview => ctx.dispatch(AppCommand::TogglePreview), + OptionsFocus::PerformanceMode => ctx.dispatch(AppCommand::TogglePerformanceMode), OptionsFocus::Font => { const FONTS: &[&str] = &["6x13", "7x13", "8x13", "9x15", "9x18", "10x20"]; let pos = FONTS.iter().position(|f| *f == ctx.app.ui.font).unwrap_or(2); diff --git a/src/settings.rs b/src/settings.rs index 3ea35ea..0773bc9 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -53,6 +53,8 @@ pub struct DisplaySettings { #[serde(default)] pub layout: MainLayout, #[serde(default)] + pub performance_mode: bool, + #[serde(default)] pub hue_rotation: f32, #[serde(default)] pub onboarding_dismissed: Vec, @@ -98,6 +100,7 @@ impl Default for DisplaySettings { show_completion: true, font: default_font(), zoom_factor: default_zoom(), + performance_mode: false, color_scheme: ColorScheme::default(), layout: MainLayout::default(), hue_rotation: 0.0, diff --git a/src/state/options.rs b/src/state/options.rs index d876f00..72e5268 100644 --- a/src/state/options.rs +++ b/src/state/options.rs @@ -11,6 +11,7 @@ pub enum OptionsFocus { ShowSpectrum, ShowCompletion, ShowPreview, + PerformanceMode, Font, ZoomFactor, WindowSize, @@ -38,6 +39,7 @@ impl CyclicEnum for OptionsFocus { Self::ShowSpectrum, Self::ShowCompletion, Self::ShowPreview, + Self::PerformanceMode, Self::Font, Self::ZoomFactor, Self::WindowSize, @@ -90,26 +92,27 @@ const FULL_LAYOUT: &[(OptionsFocus, usize)] = &[ (OptionsFocus::ShowSpectrum, 7), (OptionsFocus::ShowCompletion, 8), (OptionsFocus::ShowPreview, 9), - (OptionsFocus::Font, 10), - (OptionsFocus::ZoomFactor, 11), - (OptionsFocus::WindowSize, 12), - // blank=13, ABLETON LINK header=14, divider=15 - (OptionsFocus::LinkEnabled, 16), - (OptionsFocus::StartStopSync, 17), - (OptionsFocus::Quantum, 18), - // blank=19, SESSION header=20, divider=21, Tempo=22, Beat=23, Phase=24 - // blank=25, MIDI OUTPUTS header=26, divider=27 - (OptionsFocus::MidiOutput0, 28), - (OptionsFocus::MidiOutput1, 29), - (OptionsFocus::MidiOutput2, 30), - (OptionsFocus::MidiOutput3, 31), - // blank=32, MIDI INPUTS header=33, divider=34 - (OptionsFocus::MidiInput0, 35), - (OptionsFocus::MidiInput1, 36), - (OptionsFocus::MidiInput2, 37), - (OptionsFocus::MidiInput3, 38), - // blank=39, ONBOARDING header=40, divider=41 - (OptionsFocus::ResetOnboarding, 42), + (OptionsFocus::PerformanceMode, 10), + (OptionsFocus::Font, 11), + (OptionsFocus::ZoomFactor, 12), + (OptionsFocus::WindowSize, 13), + // blank=14, ABLETON LINK header=15, divider=16 + (OptionsFocus::LinkEnabled, 17), + (OptionsFocus::StartStopSync, 18), + (OptionsFocus::Quantum, 19), + // blank=20, SESSION header=21, divider=22, Tempo=23, Beat=24, Phase=25 + // blank=26, MIDI OUTPUTS header=27, divider=28 + (OptionsFocus::MidiOutput0, 29), + (OptionsFocus::MidiOutput1, 30), + (OptionsFocus::MidiOutput2, 31), + (OptionsFocus::MidiOutput3, 32), + // blank=33, MIDI INPUTS header=34, divider=35 + (OptionsFocus::MidiInput0, 36), + (OptionsFocus::MidiInput1, 37), + (OptionsFocus::MidiInput2, 38), + (OptionsFocus::MidiInput3, 39), + // blank=40, ONBOARDING header=41, divider=42 + (OptionsFocus::ResetOnboarding, 43), ]; impl OptionsFocus { @@ -165,17 +168,14 @@ fn visible_layout(plugin_mode: bool) -> Vec<(OptionsFocus, usize)> { // based on which sections are hidden. let mut offset: usize = 0; - // Font/Zoom/Window lines (10,11,12) hidden when !plugin_mode + // Font/Zoom/Window lines (11,12,13) hidden when !plugin_mode if !plugin_mode { offset += 3; // 3 lines for Font, ZoomFactor, WindowSize } // Link + Session + MIDI sections hidden when plugin_mode - // These span from blank(13) through MidiInput3(38) = 26 lines + // These span from blank(14) through MidiInput3(39) = 26 lines if plugin_mode { - // blank + LINK header + divider + 3 options + blank + SESSION header + divider + 3 readonlys - // + blank + MIDI OUT header + divider + 4 options + blank + MIDI IN header + divider + 4 options - // = 26 lines (indices 13..=38) let link_section_lines = 26; offset += link_section_lines; } @@ -185,10 +185,10 @@ fn visible_layout(plugin_mode: bool) -> Vec<(OptionsFocus, usize)> { if !focus.is_visible(plugin_mode) { continue; } - // Lines at or below index 9 (ShowPreview) are never shifted - let adjusted = if raw_line <= 9 { + // Lines at or below index 10 (PerformanceMode) are never shifted + let adjusted = if raw_line <= 10 { raw_line - } else if !plugin_mode && raw_line <= 12 { + } else if !plugin_mode && raw_line <= 13 { // Font/Zoom/Window — these are hidden, skip continue; } else { diff --git a/src/state/ui.rs b/src/state/ui.rs index 3c275bf..a18e458 100644 --- a/src/state/ui.rs +++ b/src/state/ui.rs @@ -74,6 +74,7 @@ pub struct UiState { pub prev_page: Page, pub prev_show_title: bool, pub onboarding_dismissed: Vec, + pub performance_mode: bool, pub font: String, pub zoom_factor: f32, pub window_width: u32, @@ -121,6 +122,7 @@ impl Default for UiState { prev_page: Page::default(), prev_show_title: true, onboarding_dismissed: Vec::new(), + performance_mode: false, font: "8x13".to_string(), zoom_factor: 1.5, window_width: 1200, diff --git a/src/views/dict_view.rs b/src/views/dict_view.rs index 3b26dc3..eb9a3d9 100644 --- a/src/views/dict_view.rs +++ b/src/views/dict_view.rs @@ -13,40 +13,18 @@ use crate::widgets::{render_search_bar, CategoryItem, CategoryList, Selection}; use CatEntry::{Category, Section}; -pub fn layout(area: Rect) -> (Rect, [Rect; 2]) { - let [header_area, body_area] = - Layout::vertical([Constraint::Length(5), Constraint::Fill(1)]).areas(area); - let body = Layout::horizontal([Constraint::Length(16), Constraint::Fill(1)]).areas(body_area); - (header_area, body) +pub fn layout(area: Rect) -> [Rect; 2] { + Layout::horizontal([Constraint::Length(16), Constraint::Fill(1)]).areas(area) } pub fn render(frame: &mut Frame, app: &App, area: Rect) { - let (header_area, [cat_area, words_area]) = layout(area); - - render_header(frame, header_area); + let [cat_area, words_area] = layout(area); let is_searching = !app.ui.dict_search_query.is_empty(); render_categories(frame, app, cat_area, is_searching); render_words(frame, app, words_area, is_searching); } -fn render_header(frame: &mut Frame, area: Rect) { - use ratatui::widgets::Wrap; - let theme = theme::get(); - let desc = "Forth uses a stack: values are pushed, functions (called words) consume and \ - produce values. Read left to right: 3 4 + -> push 3, push 4, + pops both, \ - pushes 7. This page lists all words with their signature ( inputs -- outputs )."; - let block = Block::default() - .borders(Borders::ALL) - .border_style(Style::new().fg(theme.dict.border_normal)) - .title("Dictionary"); - let para = Paragraph::new(desc) - .style(Style::new().fg(theme.dict.header_desc)) - .wrap(Wrap { trim: false }) - .block(block); - frame.render_widget(para, area); -} - fn render_categories(frame: &mut Frame, app: &App, area: Rect, dimmed: bool) { let theme = theme::get(); let focused = app.ui.dict_focus == DictFocus::Categories && !dimmed; diff --git a/src/views/main_view.rs b/src/views/main_view.rs index 525d900..5cb35ee 100644 --- a/src/views/main_view.rs +++ b/src/views/main_view.rs @@ -13,12 +13,10 @@ use crate::state::MainLayout; use crate::theme; use crate::views::highlight::highlight_line_with_runtime; use crate::views::render::{adjust_resolved_for_line, adjust_spans_for_line}; -use crate::widgets::{ActivePatterns, Orientation, Scope, Spectrum, VuMeter}; +use crate::widgets::{Orientation, Scope, Spectrum, VuMeter}; -pub fn layout(area: Rect) -> [Rect; 5] { +pub fn layout(area: Rect) -> [Rect; 3] { Layout::horizontal([ - Constraint::Length(13), - Constraint::Length(2), Constraint::Fill(1), Constraint::Length(2), Constraint::Length(10), @@ -27,7 +25,7 @@ pub fn layout(area: Rect) -> [Rect; 5] { } pub fn render(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area: Rect) { - let [patterns_area, _, main_area, _, vu_area] = layout(area); + let [main_area, _, vu_area] = layout(area); let show_scope = app.audio.config.show_scope; let show_spectrum = app.audio.config.show_spectrum; @@ -82,7 +80,6 @@ pub fn render(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area: render_sequencer(frame, app, snapshot, sequencer_area); render_vu_meter(frame, app, vu_area); - render_active_patterns(frame, app, snapshot, patterns_area); } enum VizPanel { @@ -428,45 +425,3 @@ fn render_vu_meter(frame: &mut Frame, app: &App, area: Rect) { frame.render_widget(vu, inner); } -fn render_active_patterns(frame: &mut Frame, app: &App, snapshot: &SequencerSnapshot, area: Rect) { - use crate::widgets::MuteStatus; - - let theme = theme::get(); - let block = Block::default() - .borders(Borders::ALL) - .border_style(Style::new().fg(theme.ui.border)); - let inner = block.inner(area); - frame.render_widget(block, area); - - let patterns: Vec<(usize, usize, usize)> = snapshot - .active_patterns - .iter() - .map(|p| (p.bank, p.pattern, p.iter)) - .collect(); - - let mute_status: Vec = snapshot - .active_patterns - .iter() - .map(|p| { - if app.mute.is_soloed(p.bank, p.pattern) { - MuteStatus::Soloed - } else if app.mute.is_muted(p.bank, p.pattern) { - MuteStatus::Muted - } else if app.mute.is_effectively_muted(p.bank, p.pattern) { - MuteStatus::EffectivelyMuted - } else { - MuteStatus::Normal - } - }) - .collect(); - - let step_info = snapshot - .get_step(app.editor_ctx.bank, app.editor_ctx.pattern) - .map(|step| (step, app.current_edit_pattern().length)); - - let mut widget = ActivePatterns::new(&patterns).with_mute_status(&mute_status); - if let Some((step, total)) = step_info { - widget = widget.with_step(step, total); - } - frame.render_widget(widget, inner); -} diff --git a/src/views/options_view.rs b/src/views/options_view.rs index 9479da7..a5dc27f 100644 --- a/src/views/options_view.rs +++ b/src/views/options_view.rs @@ -8,7 +8,7 @@ use crate::app::App; use crate::engine::LinkState; use crate::midi; use crate::state::OptionsFocus; -use crate::theme; +use crate::theme::{self, ThemeColors}; use crate::widgets::{render_scroll_indicators, IndicatorAlign}; pub fn render(frame: &mut Frame, app: &App, link: &LinkState, area: Rect) { @@ -90,6 +90,12 @@ pub fn render(frame: &mut Frame, app: &App, link: &LinkState, area: Rect) { focus == OptionsFocus::ShowPreview, &theme, ), + render_option_line( + "Performance mode", + if app.ui.performance_mode { "On" } else { "Off" }, + focus == OptionsFocus::PerformanceMode, + &theme, + ), ]; if app.plugin_mode { let zoom_str = format!("{:.0}%", app.ui.zoom_factor * 100.0); @@ -246,6 +252,14 @@ pub fn render(frame: &mut Frame, app: &App, link: &LinkState, area: Rect) { ]); } + // Insert description below focused option + let focus_vec_idx = focus.line_index(app.plugin_mode); + if let Some(desc) = option_description(focus) { + if focus_vec_idx < lines.len() { + lines.insert(focus_vec_idx + 1, render_description_line(desc, &theme)); + } + } + let total_lines = lines.len(); let max_visible = padded.height as usize; @@ -318,6 +332,42 @@ fn render_option_line(label: &str, value: &str, focused: bool, theme: &theme::Th ]) } +fn option_description(focus: OptionsFocus) -> Option<&'static str> { + match focus { + OptionsFocus::ColorScheme => Some("Color scheme for the entire interface"), + OptionsFocus::HueRotation => Some("Shift all theme colors by a hue angle"), + OptionsFocus::RefreshRate => Some("Lower values reduce CPU usage"), + OptionsFocus::RuntimeHighlight => Some("Highlight executed code spans during playback"), + OptionsFocus::ShowScope => Some("Oscilloscope on the engine page"), + OptionsFocus::ShowSpectrum => Some("Spectrum analyzer on the engine page"), + OptionsFocus::ShowCompletion => Some("Word completion popup in the editor"), + OptionsFocus::ShowPreview => Some("Step script preview on the sequencer grid"), + OptionsFocus::PerformanceMode => Some("Hide header and footer bars"), + OptionsFocus::Font => Some("Bitmap font for the plugin window"), + OptionsFocus::ZoomFactor => Some("Scale factor for the plugin window"), + OptionsFocus::WindowSize => Some("Default size for the plugin window"), + OptionsFocus::LinkEnabled => Some("Join an Ableton Link session on the local network"), + OptionsFocus::StartStopSync => Some("Sync transport start/stop with other Link peers"), + OptionsFocus::Quantum => Some("Number of beats per phase cycle"), + OptionsFocus::MidiOutput0 => Some("MIDI output device for channel group 1"), + OptionsFocus::MidiOutput1 => Some("MIDI output device for channel group 2"), + OptionsFocus::MidiOutput2 => Some("MIDI output device for channel group 3"), + OptionsFocus::MidiOutput3 => Some("MIDI output device for channel group 4"), + OptionsFocus::MidiInput0 => Some("MIDI input device for channel group 1"), + OptionsFocus::MidiInput1 => Some("MIDI input device for channel group 2"), + OptionsFocus::MidiInput2 => Some("MIDI input device for channel group 3"), + OptionsFocus::MidiInput3 => Some("MIDI input device for channel group 4"), + OptionsFocus::ResetOnboarding => Some("Re-enable all dismissed guide popups"), + } +} + +fn render_description_line(desc: &str, theme: &ThemeColors) -> Line<'static> { + Line::from(Span::styled( + format!(" {desc}"), + Style::new().fg(theme.ui.text_dim), + )) +} + fn render_readonly_line(label: &str, value: &str, value_style: Style, theme: &theme::ThemeColors) -> Line<'static> { let label_style = Style::new().fg(theme.ui.text_muted); let label_width = 20; diff --git a/src/views/render.rs b/src/views/render.rs index fb6680b..d6290c4 100644 --- a/src/views/render.rs +++ b/src/views/render.rs @@ -97,16 +97,20 @@ pub fn render( height: term.height.saturating_sub(2), }; + let perf = app.ui.performance_mode; + let [header_area, _padding, body_area, _bottom_padding, footer_area] = Layout::vertical([ - Constraint::Length(header_height(padded.width)), - Constraint::Length(1), + Constraint::Length(if perf { 0 } else { header_height(padded.width) }), + Constraint::Length(if perf { 0 } else { 1 }), Constraint::Fill(1), - Constraint::Length(1), - Constraint::Length(3), + Constraint::Length(if perf { 0 } else { 1 }), + Constraint::Length(if perf { 0 } else { 3 }), ]) .areas(padded); - render_header(frame, app, link, snapshot, header_area); + if !perf { + render_header(frame, app, link, snapshot, header_area); + } let (page_area, panel_area) = if app.panel.visible && app.panel.side.is_some() { if body_area.width >= 120 { @@ -139,7 +143,9 @@ pub fn render( render_side_panel(frame, app, side_area); } - render_footer(frame, app, footer_area); + if !perf { + render_footer(frame, app, footer_area); + } let modal_area = render_modal(frame, app, snapshot, term); if app.ui.show_minimap() { diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index f861f2b..062975e 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -1,6 +1,6 @@ pub use cagire_ratatui::{ hint_line, render_props_form, render_scroll_indicators, render_search_bar, - render_section_header, ActivePatterns, CategoryItem, CategoryList, ConfirmModal, - FileBrowserModal, IndicatorAlign, ModalFrame, MuteStatus, NavMinimap, NavTile, Orientation, - SampleBrowser, Scope, Selection, Spectrum, TextInputModal, VuMeter, Waveform, + render_section_header, CategoryItem, CategoryList, ConfirmModal, FileBrowserModal, + IndicatorAlign, ModalFrame, NavMinimap, NavTile, Orientation, SampleBrowser, Scope, Selection, + Spectrum, TextInputModal, VuMeter, Waveform, };