// Main app
const { useState, useEffect, useRef } = React;
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"accentHue": 195,
"density": "comfortable",
"showTypewriter": true,
"noise": true
}/*EDITMODE-END*/;
function HueSwatches({ value, onChange }) {
const hues = [
{ v: 195, label: "cyan" },
{ v: 168, label: "teal" },
{ v: 145, label: "green" },
{ v: 32, label: "amber" },
{ v: 12, label: "ember" },
{ v: 320, label: "magenta" },
{ v: 285, label: "violet" },
{ v: 220, label: "blue" },
];
return (
{hues.map((h) => {
const active = value === h.v;
return (
);
})}
);
}
function App() {
const t = window.useTweaks ? window.useTweaks(TWEAK_DEFAULTS) : { values: TWEAK_DEFAULTS, setTweak: () => {} };
const tweaks = t.values; const setTweak = t.setTweak;
const [bootState, setBootState] = useState("booting"); // booting → settling → ready
const [openProjectId, setOpenProjectId] = useState(null);
const { PROJECTS, SKILLS, CERTIFICATIONS, TIMELINE } = window.PORTFOLIO_DATA;
const { GridBackground, Nav, Hero, ProjectModal, WorkSection, AboutSection, StackSection, ContactSection, TechMarquee } = window.PortfolioParts;
// CSS var sync for accent hue
useEffect(() => {
document.documentElement.style.setProperty("--accent-hue", tweaks.accentHue);
}, [tweaks.accentHue]);
// Scroll reveal
useEffect(() => {
const io = new IntersectionObserver((entries) => {
entries.forEach((e) => {
if (e.isIntersecting) {
e.target.classList.add("reveal--in");
io.unobserve(e.target);
}
});
}, { threshold: 0.12, rootMargin: "0px 0px -60px 0px" });
document.querySelectorAll(".reveal").forEach((el) => io.observe(el));
return () => io.disconnect();
}, []);
// body class for boot-aware hero entrance
useEffect(() => {
document.body.dataset.boot = bootState;
return () => { delete document.body.dataset.boot; };
}, [bootState]);
const openProject = PROJECTS.find((p) => p.id === openProjectId);
const onNavigate = (id) => {
if (window.scrollToId) window.scrollToId(id);
};
return (
{bootState !== "ready" && window.LoadingScreen && (
setBootState("settling")}
onDone={() => setBootState("ready")}
/>
)}
{openProject && setOpenProjectId(null)} />}
{window.TweaksPanel && (
setTweak("accentHue", v)} />
{PROJECTS.map((p) => (
))}
)}
);
}
ReactDOM.createRoot(document.getElementById("root")).render();