"use client";
import PanoramaViewer from "@/components/ui/panorama-viewer";
import { useEffect, useState } from "react";
const BLANK =
"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
/** An equirectangular test scene: sky, sun, hills, a grid every 15° and the compass on the horizon. */
function makePanorama(): string {
const canvas = document.createElement("canvas");
canvas.width = 2048;
canvas.height = 1024;
const context = canvas.getContext("2d");
if (!context) return BLANK;
const w = canvas.width;
const h = canvas.height;
const sky = context.createLinearGradient(0, 0, 0, h / 2);
sky.addColorStop(0, "#1d3b8b");
sky.addColorStop(0.7, "#5b9be6");
sky.addColorStop(1, "#f7d9a0");
context.fillStyle = sky;
context.fillRect(0, 0, w, h / 2);
const ground = context.createLinearGradient(0, h / 2, 0, h);
ground.addColorStop(0, "#5f8f3a");
ground.addColorStop(1, "#23391a");
context.fillStyle = ground;
context.fillRect(0, h / 2, w, h / 2);
context.fillStyle = "#fff4c7";
context.beginPath();
context.arc(w * 0.625, h / 2 - (20 / 180) * h, 30, 0, Math.PI * 2);
context.fill();
context.fillStyle = "#3f6b2f";
context.beginPath();
context.moveTo(0, h / 2);
for (let x = 0; x <= w; x += 8) {
const a = (x / w) * Math.PI * 2;
context.lineTo(
x,
h / 2 -
28 -
22 * Math.sin(a * 3) -
14 * Math.sin(a * 7 + 1) -
8 * Math.sin(a * 13),
);
}
context.lineTo(w, h / 2);
context.fill();
context.strokeStyle = "rgba(255,255,255,0.28)";
context.lineWidth = 2;
for (let i = 0; i <= 24; i += 1) {
context.beginPath();
context.moveTo((i / 24) * w, 0);
context.lineTo((i / 24) * w, h);
context.stroke();
}
for (let i = 1; i < 12; i += 1) {
context.beginPath();
context.moveTo(0, (i / 12) * h);
context.lineTo(w, (i / 12) * h);
context.stroke();
}
context.strokeStyle = "rgba(255,255,255,0.8)";
context.beginPath();
context.moveTo(0, h / 2);
context.lineTo(w, h / 2);
context.stroke();
context.textAlign = "center";
context.textBaseline = "middle";
context.lineJoin = "round";
const label = (text: string, u: number, size: number, y = h / 2 - 70) => {
context.font = `800 ${size}px system-ui, sans-serif`;
context.lineWidth = size / 6;
context.strokeStyle = "rgba(15,23,42,0.75)";
context.fillStyle = "#ffffff";
for (const x of [u * w, u * w - w, u * w + w]) {
context.strokeText(text, x, y);
context.fillText(text, x, y);
}
};
const points = ["S", "SW", "W", "NW", "N", "NE", "E", "SE"];
points.forEach((point, i) =>
label(point, i / 8, point.length === 1 ? 72 : 44),
);
for (let degrees = 0; degrees < 360; degrees += 30)
label(`${degrees}°`, (degrees / 360 + 0.5) % 1, 22, h / 2 + 40);
label("ZENITH", 0.5, 40, 40);
label("NADIR", 0.5, 40, h - 40);
return canvas.toDataURL("image/jpeg", 0.9);
}
/**
* Harvested from the Klyv docs' own PanoramaViewer example (src/site/examples/media-capture.tsx):
* same synthetic equirectangular scene generator and the same copy. The docs' auto-rotate toggle
* used Klyv's own `Switch` component (not part of this draft's dependency set), so it is
* substituted here with a plain native checkbox styled the same way.
*/
export default function PanoramaViewerDemo() {
const [src, setSrc] = useState<string | null>(null);
const [spin, setSpin] = useState(true);
useEffect(() => {
setSrc(makePanorama());
}, []);
return (
<div className="flex w-full flex-col gap-3">
<label className="flex items-center gap-2 self-start text-[12px] font-semibold text-ink-soft">
<input
type="checkbox"
role="switch"
aria-checked={spin}
checked={spin}
onChange={(event) => setSpin(event.target.checked)}
className="size-4 accent-accent"
/>
Start with auto-rotate
</label>
{src && (
<PanoramaViewer
key={String(spin)}
src={src}
alt="Test scene: hills under an evening sky, compass points on the horizon"
autoRotate={spin}
height={380}
/>
)}
</div>
);
}