elio 1.3.0

Snappy, batteries-included terminal file manager with rich previews, inline images, bulk actions, and trash support.
Documentation
mod actions;
mod clipboard;
mod constants;
mod create;
mod directory_counts;
mod input;
mod jobs;
mod open_with;
mod overlays;
use crate::preview;
mod search;
mod selection;
mod state;
mod text_edit;
mod types;

#[cfg(test)]
use self::jobs::SchedulerMetricsSnapshot;
use self::jobs::{PreviewLineCountRequest, PreviewPriority, PreviewRequest, SearchRequest};
use self::{constants::*, state::*};
use anyhow::Result;
#[cfg(test)]
use ratatui::layout::Rect;
use ratatui::text::Line;
use std::{
    path::PathBuf,
    time::{Duration, SystemTime},
};

pub use self::state::App;
#[cfg(test)]
pub use self::state::PreviewMetricsSnapshot;
pub(crate) use crate::core::FileClass;
pub(crate) use crate::fs::{
    format_item_count, format_size, format_size_parts, format_time_ago, rect_contains,
    sanitize_terminal_text,
};

pub(crate) use self::types::ClipOp;
pub use self::types::{
    CopyHit, EntryHit, FrameState, GoToHit, OpenWithHit, PathHit, SearchHit, SearchRow,
    SearchScope, ViewMetrics, ViewMode,
};
#[cfg(test)]
pub use crate::core::SidebarItem;
pub use crate::core::{Entry, EntryKind, SidebarItemKind, SidebarRow, SortMode};

impl App {
    pub fn set_frame_state(&mut self, frame_state: FrameState) -> bool {
        let previous_code_line_limit = self.selected_entry().map(|entry| {
            self.preview_code_line_limit_for_entry_with_rows(
                entry,
                self.input.frame_state.preview_rows_visible,
            )
        });
        self.input.frame_state = frame_state;
        let mut dirty = self.sync_scroll() | self.sync_search_scroll() | self.sync_preview_scroll();
        let next_code_line_limit = self.selected_entry().map(|entry| {
            self.preview_code_line_limit_for_entry_with_rows(
                entry,
                self.input.frame_state.preview_rows_visible,
            )
        });
        if previous_code_line_limit != next_code_line_limit {
            self.refresh_preview();
            dirty = true;
        }
        if !self.browser_wheel_burst_active() {
            self.queue_visible_directory_item_counts();
        }
        self.refresh_static_image_preloads_if_needed();
        self.remember_current_directory_view();
        dirty
    }

    pub fn selected_entry(&self) -> Option<&Entry> {
        self.navigation.entries.get(self.navigation.selected)
    }

    pub fn has_pending_auto_reload(&self) -> bool {
        self.navigation
            .directory_runtime
            .pending_reload_at
            .is_some()
    }

    pub fn has_pending_background_work(&self) -> bool {
        self.jobs.scheduler.has_pending_work()
    }

    pub(crate) fn browser_wheel_burst_active(&self) -> bool {
        self.input.wheel_profile == WheelProfile::HighFrequency
            && self.overlays.search.is_none()
            && self.input.last_wheel_target == Some(WheelTarget::Entries)
            && self
                .input
                .wheel_scroll
                .vertical
                .last_input_at
                .is_some_and(|at| at.elapsed() <= WHEEL_SCROLL_BURST_WINDOW)
    }

    pub(crate) fn pending_browser_wheel_timer(&self) -> Option<Duration> {
        if !self.input.browser_wheel_post_burst_pending {
            return None;
        }
        self.input
            .wheel_scroll
            .vertical
            .last_input_at
            .map(|at| WHEEL_SCROLL_BURST_WINDOW.saturating_sub(at.elapsed()))
    }

    pub(crate) fn process_browser_wheel_timers(&mut self) -> bool {
        if self.input.browser_wheel_post_burst_pending && !self.browser_wheel_burst_active() {
            self.input.browser_wheel_post_burst_pending = false;
            return true;
        }
        false
    }

    #[cfg(test)]
    pub fn scheduler_metrics(&self) -> SchedulerMetricsSnapshot {
        self.jobs.scheduler.metrics_snapshot()
    }

    #[cfg(test)]
    pub fn preview_metrics(&self) -> PreviewMetricsSnapshot {
        self.preview.state.metrics.snapshot()
    }

    pub fn report_runtime_error(&mut self, context: &str, error: &anyhow::Error) {
        self.status = format!("{context}: {error}");
    }
}