<style>
.rizzma-live-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 12px;
align-items: start;
}
.rizzma-live-grid .rizzma-live {
margin: 0;
}
.rizzma-live canvas {
border-radius: 3px;
}
.rizzma-live-expand {
display: inline-block;
margin: 2px 0 0;
padding: 0;
background: none;
border: none;
font: inherit;
font-size: 12px;
color: inherit;
opacity: 0.65;
cursor: pointer;
}
.rizzma-live-expand:hover {
opacity: 1;
text-decoration: underline;
}
.rizzma-modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.55);
display: flex;
align-items: center;
justify-content: center;
z-index: 10000;
}
.rizzma-modal-panel {
position: relative;
background: #fff;
border-radius: 8px;
padding: 26px 14px 12px;
box-shadow: 0 12px 48px rgba(0, 0, 0, 0.45);
max-width: 96vw;
max-height: 94vh;
overflow: auto;
}
.rizzma-modal-close {
position: absolute;
top: 2px;
right: 8px;
padding: 2px 6px;
background: none;
border: none;
font: inherit;
font-size: 18px;
line-height: 1;
color: #444;
cursor: pointer;
}
.rizzma-modal-close:hover {
color: #000;
}
</style>
<script type="module">
(() => {
const PKG =
window.RIZZMA_DEMO_PKG ||
"https://orbitalcommons.github.io/rizzma/demo/pkg/rizzma.js";
const DEMOS = new URL("../rizzma-demos.js", new URL(PKG, location.href)).href;
const sessions = [];
let demos = {};
let unanimate = () => {};
let loading = null;
const boot = () =>
(loading ??= Promise.all([import(PKG), import(DEMOS)]).then(
async ([mod, shared]) => {
await mod.default();
({ demos, unanimate } = shared);
return mod;
},
));
const newCanvas = () => {
const canvas = document.createElement("canvas");
canvas.id = "rizzma-live-" + Math.random().toString(36).slice(2);
canvas.style.touchAction = "none";
canvas.style.display = "block";
return canvas;
};
let openOverlay = null;
const closeModal = () => {
if (!openOverlay) return;
const { overlay, panel, session, onKey } = openOverlay;
openOverlay = null;
unanimate(panel);
try {
session.free();
} catch (_) {
}
document.removeEventListener("keydown", onKey);
overlay.remove();
document.body.style.overflow = "";
};
const openModal = (mod, name) => {
closeModal();
const build = demos[name];
if (!build) return;
const k = Math.max(
1,
Math.min(
2.4,
(Math.min(window.innerWidth, 1200) - 90) / 360,
(window.innerHeight - 130) / 270,
),
);
const overlay = document.createElement("div");
overlay.className = "rizzma-modal-overlay";
const panel = document.createElement("div");
panel.className = "rizzma-modal-panel";
const close = document.createElement("button");
close.className = "rizzma-modal-close";
close.textContent = "×";
close.setAttribute("aria-label", "close");
close.addEventListener("click", closeModal);
const canvas = newCanvas();
panel.append(close, canvas);
overlay.append(panel);
overlay.addEventListener("pointerdown", (e) => {
if (e.target === overlay) closeModal();
});
const onKey = (e) => {
if (e.key === "Escape") closeModal();
};
document.addEventListener("keydown", onKey);
document.body.append(overlay);
document.body.style.overflow = "hidden";
try {
const session = build(mod, canvas, panel, k);
openOverlay = { overlay, panel, session, onKey };
} catch (e) {
overlay.remove();
document.body.style.overflow = "";
document.removeEventListener("keydown", onKey);
console.warn("rizzma full-size demo unavailable:", e);
}
};
const upgrade = async (el) => {
if (el.dataset.rizzmaMounted) return;
el.dataset.rizzmaMounted = "1";
try {
const mod = await boot();
const build = demos[el.dataset.demo];
if (!build) return;
const canvas = newCanvas();
el.prepend(canvas);
sessions.push(build(mod, canvas, el));
const expand = document.createElement("button");
expand.className = "rizzma-live-expand";
expand.textContent = "⤢ full size";
expand.addEventListener("click", () => openModal(mod, el.dataset.demo));
canvas.after(expand);
for (const img of el.querySelectorAll("img")) {
img.style.display = "none";
}
} catch (e) {
console.warn("rizzma live demo unavailable:", e);
}
};
const start = () => {
const hosts = document.querySelectorAll(".rizzma-live[data-demo]");
if (!hosts.length) return;
const io = new IntersectionObserver(
(entries) => {
for (const en of entries) {
if (en.isIntersecting) {
io.unobserve(en.target);
upgrade(en.target);
}
}
},
{ rootMargin: "200px" },
);
hosts.forEach((h) => io.observe(h));
const m = /^#rizzma-live=([a-z0-9_]+)$/.exec(location.hash);
if (m) {
boot()
.then((mod) => openModal(mod, m[1]))
.catch(() => {});
}
};
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", start);
} else {
start();
}
})();
</script>