feat: final graph

This commit is contained in:
2026-04-04 23:24:44 +02:00
parent 3132d40391
commit 3eec7f316e
9 changed files with 896 additions and 503 deletions

View File

@@ -3,7 +3,9 @@ import { useNavigate, useLocation } from "react-router-dom";
import { TooltipProvider } from "@/components/ui/tooltip";
import { Toaster } from "@/components/ui/sonner";
import frameSvg from "@/assets/frame.themed.svg";
import browserSvg from "@/assets/browser.min.svg";
import NavMenu from "./NavMenu";
import LazyEyes from "./LazyEyes";
const TITLE = `
@@ -80,11 +82,47 @@ function RomanClock() {
);
}
// ---------------------------------------------------------------------------
// Per-frame layout configs
// ---------------------------------------------------------------------------
interface FrameLayout {
svg: string;
frameHeight: number;
containerMaxW: string;
containerMinW: string;
title: { paddingTop: number; height: number; paddingLeft: number; paddingRight: number; paddingBottom: number };
content: { marginLeft: number; marginTop: number; marginRight: number; width: number; height: number; paddingTop: number; paddingBottom: number };
nav: { top: number; left: number };
}
const defaultLayout: FrameLayout = {
svg: frameSvg,
frameHeight: 1315,
containerMaxW: "max-w-5xl",
containerMinW: "min-w-5xl",
title: { paddingTop: 65, height: 185, paddingLeft: 60, paddingRight: 500, paddingBottom: 25 },
content: { marginLeft: 12, marginRight: 68, width: 843, height: 1030, paddingTop: 8, paddingBottom: 20 },
nav: { top: 150, left: -210 },
};
const browseLayout: FrameLayout = {
svg: browserSvg,
frameHeight: 884,
containerMaxW: "max-w-5xl",
containerMinW: "min-w-5xl",
title: { paddingTop: 65, height: 185, paddingLeft: 60, paddingRight: 500, paddingBottom: 25 },
content: { marginLeft: 268, marginTop: 63, width: 476, height: 377, paddingTop: 0, paddingBottom: 0 },
nav: { top: 150, left: -210 },
};
export default function AppShell({ children }: { children: ReactNode }) {
const navigate = useNavigate();
const location = useLocation();
const [theme, setTheme] = useState<Theme>(getStoredTheme);
const isEditor = location.pathname.startsWith("/editor");
const isBrowse = location.pathname === "/browse";
const layout = isBrowse ? browseLayout : defaultLayout;
useEffect(() => {
const root = document.documentElement;
@@ -104,17 +142,17 @@ export default function AppShell({ children }: { children: ReactNode }) {
<TooltipProvider>
<div className="flex flex-col h-screen bg-background text-foreground">
<main className="flex-1 overflow-auto" data-scroll-root>
<div className="relative max-w-5xl min-w-5xl mx-auto min-h-full">
<div className={`relative ${layout.containerMaxW} ${layout.containerMinW} mx-auto min-h-full`}>
{/* Frame SVG — background */}
<div
aria-hidden
className="absolute inset-0 w-full min-w-full pointer-events-none select-none"
style={{
zIndex: 0,
height: "1315px",
height: `${layout.frameHeight}px`,
background: "var(--primary)",
WebkitMaskImage: `url(${frameSvg})`,
maskImage: `url(${frameSvg})`,
WebkitMaskImage: `url(${layout.svg})`,
maskImage: `url(${layout.svg})`,
WebkitMaskSize: "cover",
maskSize: "cover",
WebkitMaskRepeat: "no-repeat",
@@ -123,15 +161,26 @@ export default function AppShell({ children }: { children: ReactNode }) {
}}
/>
{/* Lazy eyes on the browse frame figure */}
{isBrowse && (
<LazyEyes
eyes={[
{ top: 81, left: 554, size: 5, irisSize: 2 },
{ top: 85, left: 562, size: 5, irisSize: 2 },
]}
maxShift={2}
/>
)}
{/* Top hole — ASCII title */}
<div
className="relative z-10 flex flex-col cursor-pointer overflow-hidden"
style={{ paddingTop: 65, height: 185, paddingLeft: 60, paddingRight: 500, paddingBottom: 25 }}
onClick={() => navigate("/")}
style={layout.title}
>
<pre
className="select-none text-primary/70 hover:text-primary transition-colors whitespace-pre origin-center"
className="select-none text-primary/70 hover:text-primary transition-colors whitespace-pre origin-center bg-background"
style={{ height: 60, fontSize: 6, lineHeight: 1.05, transform: "scale(0.50)", transformOrigin: "left center", alignContent: "center" }}
onClick={() => navigate("/")}
>
{TITLE}
</pre>
@@ -141,7 +190,7 @@ export default function AppShell({ children }: { children: ReactNode }) {
{/* Bottom hole — main content */}
<div
className="relative z-10 overflow-auto"
style={{ marginLeft: 12, marginRight: 68, width: 843, height: 1030, paddingTop: 8, paddingBottom: 20 }}
style={layout.content}
>
{children}
</div>
@@ -150,7 +199,7 @@ export default function AppShell({ children }: { children: ReactNode }) {
{!isEditor && (
<div
className="absolute z-20"
style={{ top: 150, left: -210 }}
style={layout.nav}
>
<NavMenu theme={theme} onToggleTheme={toggleTheme} />
</div>
@@ -159,7 +208,7 @@ export default function AppShell({ children }: { children: ReactNode }) {
</div>
</main>
<footer className="flex items-center justify-center gap-3 text-[10px] text-muted-foreground py-4 shrink-0 tracking-widest uppercase">
<span>Becoming with hubris · MMXXVI</span>
<span>Created with hubris · MMXXVI</span>
</footer>
</div>
<Toaster />