cyb/shell/src/worlds/file.rs

//! Spacetime: `cyb://file/<hex>` โ€” particle + spark surface.

use bevy::prelude::*;
use prysm::molecules::file as file_frame;
use prysm::theme;
use spark::Surface;

use super::content;
use super::graph::BrainIndex;
use crate::now::{Now, NowKind, load_file};
use crate::shell::chrome::{CHROME_BOTTOM_H, CHROME_TOP_H, ContentRoot};

pub struct FilePagePlugin;

fn despawn_idle(mut commands: Commands, pages: Query<Entity, With<FilePage>>) {
    for e in &pages {
        commands.entity(e).despawn();
    }
}

#[derive(Component)]
struct FilePage;

impl Plugin for FilePagePlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(
            Update,
            sync_page.run_if(|now: Res<Now>| now.kind == NowKind::File),
        )
        .add_systems(
            Update,
            despawn_idle.run_if(|now: Res<Now>| now.kind != NowKind::File),
        );
    }
}

fn sync_page(
    mut commands: Commands,
    now: Res<Now>,
    index: Option<Res<BrainIndex>>,
    pages: Query<Entity, With<FilePage>>,
) {
    if !now.is_changed() && !pages.is_empty() {
        return;
    }
    for e in &pages {
        commands.entity(e).despawn();
    }
    let Some(p) = now.hash else { return };
    let hash = *p.as_bytes();
    let title = now
        .idx
        .and_then(|i| {
            index
                .as_ref()
                .and_then(|ix| ix.labels.get(i).cloned().flatten())
        })
        .or_else(|| content::lookup(&hash))
        .unwrap_or_else(|| p.short_hex());
    let surface = load_file(hash).and_then(|f| spark::open(&f).ok().flatten());

    let root = commands
        .spawn((
            FilePage,
            ContentRoot,
            Node {
                position_type: PositionType::Absolute,
                top: Val::Px(CHROME_TOP_H),
                bottom: Val::Px(CHROME_BOTTOM_H),
                left: Val::Px(0.0),
                right: Val::Px(0.0),
                flex_direction: FlexDirection::Column,
                align_items: AlignItems::Center,
                overflow: Overflow::clip(),
                ..default()
            },
            BackgroundColor(theme::DARK_BASE),
            GlobalZIndex(8),
        ))
        .id();
    let page = commands
        .spawn((
            crate::worlds::page::scroll_column(),
            ScrollPosition::default(),
            crate::worlds::scroll::PersistScroll("file"),
            ChildOf(root),
        ))
        .id();
    let spark_name = match &surface {
        Some(Surface::Text(_)) => "text",
        Some(Surface::Image { .. }) => "image",
        None => "โ€”",
    };
    let frame = file_frame::spawn(
        &mut commands,
        page,
        &title,
        &p.short_hex(),
        Some(&format!("spark {spark_name}")),
    );
    match surface {
        Some(Surface::Text(body)) => {
            const CAP: usize = 8_000;
            let shown = if body.len() > CAP {
                format!("{}โ€ฆ", &body[..CAP])
            } else {
                body
            };
            commands.spawn((
                Text::new(shown),
                TextFont {
                    font_size: theme::BODY,
                    ..default()
                },
                TextColor(theme::TEXT_PRIMARY),
                ChildOf(frame),
            ));
        }
        Some(Surface::Image { kind, .. }) => {
            commands.spawn((
                Text::new(format!("image spark ({kind:?}) โ€” pixels next")),
                TextFont {
                    font_size: theme::CAPTION,
                    ..default()
                },
                TextColor(theme::TEXT_DIM),
                ChildOf(frame),
            ));
        }
        None => {}
    }
}

Homonyms

soft3/crate/src/genesis/file.rs

Graph