OverviewThis WebsiteHome LabTrading BotEcosimProcedural Generation
Source Code

src/pages/projects/mod.rs

pub mod data;
pub mod ecosim;
pub mod gpu_shaders;
pub mod home_lab;
pub mod keybind_plugin;
pub mod orbit_camera;
pub mod proc_gen;
pub mod this_website;
pub mod trading_bot;

use leptos::*;
use leptos_meta::{Meta, Title};
use leptos_router::A;

use data::{aggregated_skills, SHOW_SKILL_COUNT};

#[component]
pub fn Projects() -> impl IntoView {
    let skills_view = aggregated_skills()
        .into_iter()
        .map(|(skill, projects)| {
            let count = projects.len();
            let popup_items = projects
                .iter()
                .map(|p| {
                    let name: &'static str = p.name;
                    let tagline: &'static str = p.tagline;
                    let href = format!("/projects/{}", p.slug);
                    view! {
                        <A href=href class="skill-popup-project">
                            <span class="skill-popup-name">{name}</span>
                            <span class="skill-popup-sep">" - "</span>
                            <span class="skill-popup-desc">{tagline}</span>
                        </A>
                    }
                })
                .collect::<Vec<_>>();

            view! {
                <li class="skill-tag-wrapper">
                    {skill}
                    {SHOW_SKILL_COUNT.then(|| view! { <span class="skill-count">"("{count}")"</span> })}
                    <div class="skill-popup">
                        <div class="skill-popup-box">{popup_items}</div>
                    </div>
                </li>
            }
        })
        .collect::<Vec<_>>();

    view! {
        <Title text="Projects – Peter Pinto"/>
        <Meta name="description" content="A collection of personal projects and open work by Peter Pinto."/>
        <div class="page">
            <span class="eyebrow">"Projects"</span>
            <h1>"Things I've " <em style="font-style:italic; color: var(--accent)">"Built"</em></h1>
            <p class="lead">
                "A selection of personal projects — tools, systems, and open work."
            </p>

            <ul class="skills-list" style="margin-top: 1.5rem; margin-bottom: 0.5rem;">
                {skills_view}
            </ul>

            <hr class="divider"/>

            <div class="project-grid">
                // ── This Website ──────────────────────────────────
                <div class="project-card">
                    <span class="eyebrow">"Full-stack Rust"</span>
                    <h3>"This Website"</h3>
                    <p>
                        "A full-stack Rust web application with server-side rendering,
                        WASM hydration, and a live view-source mode. Built with Leptos,
                        Axum, and cargo-leptos."
                    </p>
                    <A href="/projects/this-website" class="btn btn-outline">"Details →"</A>
                </div>

                // ── Home Lab ───────────────────────────────────────
                <div class="project-card">
                    <span class="eyebrow">"Infrastructure"</span>
                    <h3>"Home Lab"</h3>
                    <p>
                        "A three-node bare-metal Kubernetes cluster on repurposed hardware,
                        running RKE2, Istio, and Longhorn with real workloads — Gitea runner,
                        RabbitMQ, Matrix, InfluxDB, and more."
                    </p>
                    <A href="/projects/home-lab" class="btn btn-outline">"Details →"</A>
                </div>

                // ── Trading Bot ────────────────────────────────────
                <div class="project-card">
                    <span class="eyebrow">"Rust · Kubernetes Operator"</span>
                    <h3>"Trading Bot"</h3>
                    <p>
                        "A Kubernetes-native operator that manages the full lifecycle of automated
                        trading bot deployments via Custom Resource Definitions — configuration,
                        scheduling, and declarative risk limits all in one spec."
                    </p>
                    <A href="/projects/trading-bot" class="btn btn-outline">"Details →"</A>
                </div>

                // ── Ecosim ─────────────────────────────────────────
                <div class="project-card">
                    <span class="eyebrow">"Game · Bevy"</span>
                    <h3>"Ecosim"</h3>
                    <p>
                        "A planet-scale ecosystem simulator built on Bevy. Players seed a world,
                        guide species populations, and intervene in emergent food webs — from
                        global climate patterns down to individual organisms."
                    </p>
                    <A href="/projects/ecosim" class="btn btn-outline">"Details →"</A>
                </div>

                // ── Procedural Generation ──────────────────────────
                <div class="project-card">
                    <span class="eyebrow">"Rust · Bevy · H3"</span>
                    <h3>"Procedural Generation"</h3>
                    <p>
                        "A planet-scale hexagonal grid engine using Uber's H3 library.
                        Cells refine and coarsen dynamically based on camera distance,
                        with Perlin noise terrain and a trait-based simulation system."
                    </p>
                    <A href="/projects/proc-gen" class="btn btn-outline">"Details →"</A>
                </div>

                // ── Orbit Camera ───────────────────────────────────
                <div class="project-card">
                    <span class="eyebrow">"Bevy Plugin"</span>
                    <h3>"Orbit Camera"</h3>
                    <p>
                        "A reusable Bevy orbital camera plugin. Input is fully decoupled
                        via a trait — swap keyboard, mouse, or gamepad without touching
                        the camera code. Quaternion rotation, two modes, speed falloff."
                    </p>
                    <A href="/projects/orbit-camera" class="btn btn-outline">"Details →"</A>
                </div>

                // ── Keybind Plugin ─────────────────────────────────
                <div class="project-card">
                    <span class="eyebrow">"Bevy Plugin"</span>
                    <h3>"Keybind Plugin"</h3>
                    <p>
                        "A Bevy input binding plugin that maps keyboard, mouse, gamepad,
                        and touch inputs to named actions. Game logic responds to actions,
                        not hardware — rebinding is a runtime change."
                    </p>
                    <A href="/projects/keybind-plugin" class="btn btn-outline">"Details →"</A>
                </div>

                // ── GPU Shaders ────────────────────────────────────
                <div class="project-card">
                    <span class="eyebrow">"Rust-GPU · SPIR-V"</span>
                    <h3>"GPU Shaders"</h3>
                    <p>
                        "SPIR-V shaders written in Rust via Rust-GPU. The vertex shader
                        generates H3 hexagonal cell boundaries entirely on the GPU from
                        a list of cell indices — no mesh data from the CPU."
                    </p>
                    <A href="/projects/gpu-shaders" class="btn btn-outline">"Details →"</A>
                </div>
            </div>
        </div>
    }
}