chore(repo): initialize planarchy workspace

This commit is contained in:
2026-03-14 14:31:09 +01:00
commit dd55d0e78b
769 changed files with 166461 additions and 0 deletions
@@ -0,0 +1,69 @@
"use client";
import { usePathname, useSearchParams } from "next/navigation";
import { useEffect, useRef, useState } from "react";
/**
* Thin brand-colored progress bar at the top of the page.
* Animates to 100% on route change, then fades out.
* Pure CSS animation — no external dependency.
*/
export function NavProgressBar() {
const pathname = usePathname();
const searchParams = useSearchParams();
const [visible, setVisible] = useState(false);
const [width, setWidth] = useState(0);
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const prevPathRef = useRef<string | null>(null);
// Detect link clicks to start the bar early
useEffect(() => {
function handleClick(e: MouseEvent) {
const target = (e.target as Element).closest("a");
if (!target) return;
const href = target.getAttribute("href");
if (!href || href.startsWith("http") || href.startsWith("#") || href.startsWith("mailto")) return;
// Internal navigation — start bar
setVisible(true);
setWidth(60); // jump to 60% immediately, await route change for completion
}
document.addEventListener("click", handleClick);
return () => document.removeEventListener("click", handleClick);
}, []);
// Complete bar when route actually changes
useEffect(() => {
const current = pathname + searchParams.toString();
if (prevPathRef.current !== null && prevPathRef.current !== current) {
// Route changed — complete the bar
setWidth(100);
if (timerRef.current) clearTimeout(timerRef.current);
timerRef.current = setTimeout(() => {
setVisible(false);
setWidth(0);
}, 350);
}
prevPathRef.current = current;
}, [pathname, searchParams]);
// Cleanup
useEffect(() => () => { if (timerRef.current) clearTimeout(timerRef.current); }, []);
if (!visible && width === 0) return null;
return (
<div
aria-hidden="true"
className="fixed top-0 left-0 right-0 z-[9999] h-0.5 pointer-events-none"
>
<div
className="h-full bg-brand-500 transition-all ease-out"
style={{
width: `${width}%`,
transitionDuration: width === 100 ? "200ms" : "400ms",
opacity: visible ? 1 : 0,
}}
/>
</div>
);
}