Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

facett-demo/src/about_tab.rs

//! **The `About` tab** — the shared **About-card widget** (`facett-about`) made
//! visible in the gallery.
//!
//! `facett-about` is deliberately NOT a `Facet`: it is the ONE robot-testable
//! identity widget every suite app (korp · holger · dwarves · nornir) mounts as a
//! modal window over a [`nornir_appshell::Identity`](facett_about::Identity). So —
//! exactly like `errcode_tab` wraps the error-code registry — this demo-local
//! wrapper gives it a deck tab: the pane renders the card's field map **inline**
//! (wordmark · version · tagline · subtitle · engines · framework list · the
//! MEASURED GPU rows via the shared [`facett_about::gpu_rows`] — the ONE writer,
//! never a re-derivation) over a seeded nordisk [`Identity`], and a button opens
//! the **real** shared modal ([`facett_about::draw_about_window`]) so the actual
//! widget every app ships is on screen, not a look-alike.
//!
//! `state_json` delegates to [`facett_about::state_json`] — the same oracle a
//! suite app's robot asserts — so the tab's observable surface IS the widget's.

use facett::Facet;
use facett::core::FacetCaps;
use facett_about::{GpuStatus, Identity};

/// The stable tab title (the deck / discovery-contract key + the LEGEND key).
pub const TITLE: &str = "About";

/// The seeded demo [`Identity`] — facett's own card, every field populated so the
/// whole reproduction map paints (korp's `engines` flavour line AND holger's
/// `framework` bullets on one card). No logo bytes: the card's LFS-tolerant path
/// (`logo_png: None` → text identity, never a panic) is the one exercised.
pub fn demo_identity() -> Identity {
    Identity {
        subtitle: "every facett component on one deck, fake-but-realistic data".to_string(),
        repository: "ssh://git@git.gunnar.rs:2222/nordisk/facett.git".to_string(),
        links: vec![(
            "git.gunnar.rs/nordisk/facett".to_string(),
            "https://git.gunnar.rs/nordisk/facett".to_string(),
        )],
        framework: vec![
            "facett-core — the Facet contract + the L0 render kernel".to_string(),
            "facett-app — NavShell · Router · scene".to_string(),
            "facett-about — this very card (one widget, no per-app twin)".to_string(),
        ],
        engines: vec!["the ravens carry the data — hugin ute, munin hemma".to_string()],
        ..Identity::new(
            "facett-demo",
            "facett",
            env!("CARGO_PKG_VERSION"),
            "the component constellation — ONE showcase, every Facet, all palettes",
        )
    }
}

/// The About tab: the shared card's body inline + the real modal on demand.
pub struct AboutTab {
    title: &'static str,
    id: Identity,
    /// The shared modal's toggle (the same bool every suite app keeps).
    window_open: bool,
    /// How many times the modal was dismissed — observable proof the REAL shared
    /// window (not just the inline body) ran its dismiss contract.
    dismissed: u32,
}

impl AboutTab {
    /// The populated gallery tab over [`demo_identity`].
    pub fn new(title: &'static str) -> Self {
        Self { title, id: demo_identity(), window_open: false, dismissed: 0 }
    }

    /// An **empty-identity** twin (everything blank) — the mega-matrix's `Empty`
    /// data-state cell: the card must render its skips (no version line, no
    /// framework list, no links) rather than a panic or placeholder junk.
    pub fn empty(title: &'static str) -> Self {
        Self { title, id: Identity::new("", "", "", ""), window_open: false, dismissed: 0 }
    }
}

impl Facet for AboutTab {
    fn title(&self) -> &str {
        self.title
    }

    fn ui(&mut self, ui: &mut egui::Ui) {
        egui::ScrollArea::vertical().auto_shrink([false, false]).show(ui, |ui| {
            ui.heading("the shared About card (facett-about)");
            ui.label(
                "ONE widget renders every suite app's About surface from a nornir \
                 Identity — korp · holger · dwarves · nornir mount this exact card. \
                 Below: the card's field map inline; the button opens the REAL modal.",
            );
            ui.separator();

            // ── The card body, inline — the reproduction map in identity order. ──
            ui.vertical_centered(|ui| {
                ui.heading(self.id.display_name.as_str());
                if !self.id.version.is_empty() {
                    ui.label(egui::RichText::new(format!("v{}", self.id.version)).monospace().strong());
                }
                if !self.id.tagline.is_empty() {
                    ui.label(self.id.tagline.as_str());
                }
                if !self.id.subtitle.is_empty() {
                    ui.label(self.id.subtitle.as_str());
                }
                if !self.id.engines.is_empty() {
                    ui.add_space(6.0);
                    for line in &self.id.engines {
                        ui.label(egui::RichText::new(line).italics().weak());
                    }
                }
                if !self.id.framework.is_empty() {
                    ui.add_space(8.0);
                    ui.label(egui::RichText::new("Built on the shared nordisk framework:").weak());
                    for line in &self.id.framework {
                        ui.label(egui::RichText::new(format!("· {line}")).small());
                    }
                }
                // The MEASURED GPU rows — the shared writer (`facett_about::gpu_rows`),
                // so this pane can never disagree with what the suite apps' cards say.
                ui.add_space(8.0);
                ui.separator();
                facett_about::gpu_rows(ui, &GpuStatus::probe());
                if !self.id.links.is_empty() {
                    ui.add_space(6.0);
                    for (label, url) in &self.id.links {
                        ui.hyperlink_to(label.as_str(), url.as_str());
                    }
                }
            });

            ui.separator();
            // ── The REAL shared modal, on demand — the widget the apps actually ship.
            if ui.button("Open the About window").clicked() {
                self.window_open = true;
            }
        });
        // Drawn outside the scroll area: an egui `Window` floats over the pane
        // regardless, and the card anchors itself CENTER_CENTER. No logo texture
        // (`None`) — the LFS-tolerant text-identity path.
        if self.window_open {
            let was_open = self.window_open;
            let dismissed = facett_about::draw_about_window(
                ui.ctx(),
                &mut self.window_open,
                &self.id,
                None,
                360.0,
            );
            if was_open && dismissed {
                self.dismissed += 1;
            }
        }
    }

    fn state_json(&self) -> serde_json::Value {
        // The widget's own oracle (identity + the "gpu" fold), plus the tab's
        // modal-interaction counters.
        let mut v = facett_about::state_json(&self.id, self.window_open);
        if let Some(o) = v.as_object_mut() {
            o.insert("dismissed".to_string(), serde_json::json!(self.dismissed));
        }
        v
    }

    fn update_json(&mut self, msg_json: &str) {
        // The robot seam: {"open": true/false} drives the modal toggle.
        if let Ok(v) = serde_json::from_str::<serde_json::Value>(msg_json)
            && let Some(b) = v.get("open").and_then(|x| x.as_bool())
        {
            self.window_open = b;
        }
    }

    fn caps(&self) -> FacetCaps {
        FacetCaps::NONE.themeable()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn the_seeded_identity_populates_every_card_section() {
        let t = AboutTab::new(TITLE);
        let s = t.state_json();
        assert_eq!(s["wordmark"], "facett");
        assert!(!s["version"].as_str().unwrap().is_empty(), "the version line paints");
        assert!(s["framework"].as_array().unwrap().len() >= 3, "the framework bullets paint");
        assert_eq!(s["links"].as_array().unwrap().len(), 1, "the source link paints");
        assert_eq!(s["open"], false, "the modal starts closed");
        assert_eq!(t.title(), TITLE, "the pane registers under the LEGEND key");
    }

    #[test]
    fn the_empty_identity_renders_its_skips_as_data() {
        let t = AboutTab::empty(TITLE);
        let s = t.state_json();
        assert_eq!(s["framework"].as_array().unwrap().len(), 0);
        assert_eq!(s["links"].as_array().unwrap().len(), 0);
        assert_eq!(s["has_logo"], false);
    }

    #[test]
    fn the_robot_seam_opens_the_real_modal() {
        let mut t = AboutTab::new(TITLE);
        t.update_json(r#"{"open": true}"#);
        assert_eq!(t.state_json()["open"], true, "update_json drives the modal toggle");
    }
}