rootle 0.8.7

Modal TUI for browsing remote source-control systems (GitHub in-tree, any forge via stdio providers) from the terminal
Documentation
//! Blob cache + the at-commit view (moved from browser.rs,
//! plans/0021 M2 — a pure move).

use super::Browser;
use ratatui::text::Line;

/// One fetched blob, cache entry for `Browser::blobs`.
pub(crate) struct CachedBlob {
    /// File name (syntax detection for restyle).
    pub(crate) name: String,
    /// Language label for the preview footer.
    pub(crate) lang: String,
    /// Sanitized raw text — the restyle source of truth.
    pub(crate) text: String,
    /// Highlighted lines under the current palette.
    pub(crate) lines: Vec<Line<'static>>,
}

impl Browser {
    pub fn show_at_commit(
        &mut self,
        sha: &str,
        name: &str,
        lang: &str,
        text: String,
        lines: Vec<Line<'static>>,
        band: Option<crate::components::preview::BandContext>,
    ) {
        self.note_commit_view();
        self.blobs.insert(
            sha.to_string(),
            CachedBlob {
                name: name.to_string(),
                lang: lang.to_string(),
                text,
                lines: lines.clone(),
            },
        );
        self.preview.set_highlighted(name, lang, lines);
        // The band: the path stays, the commit context dresses it.
        let path = name.split(" @ ").next().unwrap_or(name).to_string();
        self.preview.set_band(Some(path), band);
    }

    /// Esc from a commit view: the present-day blob re-renders from
    /// the in-memory cache (it was fetched moments ago — zero network).
    pub fn commit_view_close(&mut self) {
        let Some((path, sha)) = self.at_commit.take() else {
            return;
        };
        if let Some(c) = self.blobs.get(&sha) {
            let (lang, lines) = (c.lang.clone(), c.lines.clone());
            // The cached name carries the at-commit marker — the
            // present-day view's title is the plain path.
            self.preview.set_highlighted(&path, &lang, lines);
            self.preview.set_band(Some(path), None);
        } else {
            // Cache evicted under us: fall back to the blob path.
            self.preview
                .set_bytes(&path, b"reload the file to restore it");
        }
    }

    /// Blob arrived: store raw text + highlighted lines, refresh if
    /// still selected.
    pub fn blob_loaded(
        &mut self,
        sha: &str,
        name: &str,
        lang: &str,
        text: String,
        lines: Vec<Line<'static>>,
    ) {
        self.blobs.insert(
            sha.to_string(),
            CachedBlob {
                name: name.to_string(),
                lang: lang.to_string(),
                text,
                lines,
            },
        );
        self.pending_blobs.remove(sha);
        self.refresh_preview();
        // The header band (GitHub's file header): the full path rides
        // every file preview; at-commit context only from
        // show_at_commit.
        if let Some((path, s)) = self.selected_file()
            && s == sha
        {
            self.preview.set_band(Some(path), None);
        }
    }

    pub fn blob_failed(&mut self, _sha: &str, message: &str) {
        // Stay marked pending: no auto-retry while the user keeps
        // moving (avoids hammering a failing endpoint per keystroke).
        self.preview.content =
            crate::components::preview::PreviewContent::Text(format!("error: {message}"));
    }
}