/* global window, document, fetch, navigator */ function el(id) { const node = document.getElementById(id); if (!node) throw new Error(`Missing element: ${id}`); return node; } function getSearchParams() { return new URLSearchParams(window.location.search); } function getQueryValue(name) { return getSearchParams().get(name); } function titleCase(text) { return String(text || "") .split(/[-_\s]+/) .filter(Boolean) .map((part) => part[0].toUpperCase() + part.slice(1)) .join(" "); } function clampRate(value) { const numeric = Number(value); if (!Number.isFinite(numeric) || numeric <= 0) return 1; return numeric; } function buildAssetUrl(mode, manifest, type, path) { if (mode === "hub") { const repo = type === "gt" ? manifest.repos.gt_dataset : manifest.repos.generated_dataset; return `https://huggingface.co/datasets/${repo}/resolve/main/${path}`; } if (type === "gt") { return `/data/${path}`; } return `/data/sub_generated_videos/${path}`; } function isLocalPreviewHost() { const host = window.location.hostname || ""; return host === "localhost" || host === "127.0.0.1"; } function detectSourceMode() { const explicit = getQueryValue("source"); if (explicit === "hub") return "hub"; if (explicit === "local" && isLocalPreviewHost()) return "local"; if (isLocalPreviewHost()) return "local"; return "hub"; } function sameValue(a, b, epsilon) { return Math.abs(a - b) <= epsilon; } function setVideoSource(video, src) { if (!src) { video.removeAttribute("src"); video.load(); return; } if (video.dataset.currentSrc === src) return; video.dataset.currentSrc = src; video.src = src; video.load(); } function createButton(label, active, onClick, className) { const button = document.createElement("button"); button.type = "button"; button.className = className; if (active) button.classList.add("active"); button.textContent = label; button.addEventListener("click", onClick); return button; } function createResultCard(result) { const article = document.createElement("article"); article.className = "video-card result-card"; article.dataset.engine = result.engine; article.dataset.model = result.model; const header = document.createElement("div"); header.className = "card-header"; const badges = document.createElement("div"); badges.className = "badge-stack"; const engineBadge = document.createElement("span"); engineBadge.className = "badge"; engineBadge.textContent = result.engine_label; badges.appendChild(engineBadge); const modelBadge = document.createElement("span"); modelBadge.className = "badge badge-secondary"; modelBadge.textContent = result.model_label; badges.appendChild(modelBadge); const title = document.createElement("div"); title.className = "card-title"; title.textContent = result.model_label; const subtitle = document.createElement("div"); subtitle.className = "card-subtitle"; subtitle.textContent = `${result.engine_label} renderer`; const video = document.createElement("video"); video.controls = true; video.preload = "metadata"; video.playsInline = true; video.dataset.role = "result-video"; header.appendChild(badges); header.appendChild(title); header.appendChild(subtitle); article.appendChild(header); article.appendChild(video); return { article, video }; } function parseSetParam(name, validValues) { const raw = getQueryValue(name); if (!raw) return null; const out = new Set(); for (const part of raw.split(",")) { const item = decodeURIComponent(part).trim(); if (item && validValues.has(item)) out.add(item); } return out.size > 0 ? out : null; } function setsEqual(left, right) { if (left.size !== right.size) return false; for (const item of left) { if (!right.has(item)) return false; } return true; } async function loadManifest() { const response = await fetch("./static/manifest.json", { cache: "no-store" }); if (!response.ok) throw new Error(`Failed to load manifest: ${response.status}`); return response.json(); } async function copyToClipboard(text) { if (navigator.clipboard && navigator.clipboard.writeText) { await navigator.clipboard.writeText(text); return true; } return false; } async function main() { const manifest = await loadManifest(); const samples = Array.isArray(manifest.samples) ? manifest.samples : []; if (samples.length === 0) throw new Error("Manifest has no samples"); const allModels = []; const seenModels = new Set(); for (const engine of manifest.engine_order) { for (const model of manifest.model_order[engine] || []) { if (seenModels.has(model)) continue; seenModels.add(model); allModels.push(model); } } const engineSet = new Set(manifest.engine_order); const modelSet = new Set(allModels); const state = { samples, sourceMode: detectSourceMode(), currentIndex: 0, syncEnabled: getQueryValue("sync") !== "0", muted: getQueryValue("muted") !== "0", propagating: false, selectedEngines: parseSetParam("engines", engineSet) || new Set(manifest.engine_order), selectedModels: parseSetParam("models", modelSet) || new Set(allModels), playbackRate: clampRate(getQueryValue("rate") || "1"), }; const sampleSelect = el("sample-select"); const sampleSearch = el("sample-search"); const sampleCounter = el("sample-counter"); const sourceLocal = el("source-local"); const sourceHub = el("source-hub"); const sourceNote = el("source-note"); const metaTask = el("meta-task"); const metaSplit = el("meta-split"); const metaResults = el("meta-results"); const metaDetection = el("meta-detection"); const sampleMeta = el("sample-meta"); const gtVideo = el("gt-video"); const gtTitle = el("gt-title"); const comparisonTitle = el("comparison-title"); const resultsSummary = el("results-summary"); const activeFilterSummary = el("active-filter-summary"); const resultsGrid = el("results-grid"); const gtCard = resultsGrid.querySelector(".gt-card"); const emptyState = el("empty-state"); const engineFilters = el("engine-filters"); const modelFilters = el("model-filters"); const presetBar = el("preset-bar"); const syncToggle = el("sync-toggle"); const muteToggle = el("mute-toggle"); const playbackRate = el("playback-rate"); const copyLinkButton = el("copy-link"); playbackRate.value = String(state.playbackRate); const presetDefinitions = [ { key: "all", label: "All", engines: manifest.engine_order, models: allModels, }, { key: "code", label: "Code", engines: ["threejs", "p5js"], models: allModels.filter((model) => model !== "svd-img2vid" && model !== "sora-2" && model !== "veo31"), }, { key: "threejs", label: "Three.js", engines: ["threejs"], models: manifest.model_order.threejs || [], }, { key: "p5js", label: "p5.js", engines: ["p5js"], models: manifest.model_order.p5js || [], }, { key: "video", label: "Video", engines: ["video"], models: manifest.model_order.video || [], }, ]; function currentSample() { return state.samples[state.currentIndex]; } function allVisibleVideos() { return Array.from(resultsGrid.querySelectorAll("video")); } function updateUrlState() { const params = new URLSearchParams(); params.set("sample", currentSample().id); params.set("source", state.sourceMode); if (!setsEqual(state.selectedEngines, new Set(manifest.engine_order))) { params.set("engines", Array.from(state.selectedEngines).join(",")); } if (!setsEqual(state.selectedModels, new Set(allModels))) { params.set("models", Array.from(state.selectedModels).join(",")); } if (!state.syncEnabled) params.set("sync", "0"); if (!state.muted) params.set("muted", "0"); if (!sameValue(state.playbackRate, 1, 0.001)) { params.set("rate", String(state.playbackRate)); } const nextUrl = `${window.location.pathname}?${params.toString()}`; window.history.replaceState(null, "", nextUrl); } function updateSourceButtons() { const localPreview = isLocalPreviewHost(); sourceLocal.disabled = !localPreview; sourceLocal.title = localPreview ? "Read local files under /data" : "Local mode is only available in local preview"; sourceLocal.classList.toggle("active", state.sourceMode === "local"); sourceHub.classList.toggle("active", state.sourceMode === "hub"); sourceNote.textContent = state.sourceMode === "local" ? "Reading videos from this repo under /data. This is the best mode for local inspection." : localPreview ? "Reading videos from the public Hugging Face datasets. This matches the deployed Space." : "Reading videos from the public Hugging Face datasets used by this Space."; } function updateSyncButtons() { syncToggle.textContent = `Sync: ${state.syncEnabled ? "On" : "Off"}`; muteToggle.textContent = `Muted: ${state.muted ? "On" : "Off"}`; syncToggle.classList.toggle("active", state.syncEnabled); muteToggle.classList.toggle("active", state.muted); } function updateCounter() { sampleCounter.textContent = `${state.currentIndex + 1}/${state.samples.length}`; } function applyPlaybackSettings() { state.playbackRate = clampRate(playbackRate.value || "1"); for (const video of allVisibleVideos()) { video.playbackRate = state.playbackRate; video.defaultPlaybackRate = state.playbackRate; video.muted = state.muted; } } function propagate(origin, action) { if (!state.syncEnabled || state.propagating) return; state.propagating = true; const videos = allVisibleVideos(); try { if (action === "play") { for (const video of videos) { if (video === origin) continue; if (!sameValue(video.currentTime, origin.currentTime, 0.08)) { video.currentTime = origin.currentTime; } video.playbackRate = origin.playbackRate; video.play().catch(() => { /* ignore autoplay restrictions */ }); } } else if (action === "pause") { for (const video of videos) { if (video === origin) continue; if (!sameValue(video.currentTime, origin.currentTime, 0.08)) { video.currentTime = origin.currentTime; } video.pause(); } } else if (action === "seek") { for (const video of videos) { if (video === origin) continue; if (!sameValue(video.currentTime, origin.currentTime, 0.1)) { video.currentTime = origin.currentTime; } } } else if (action === "rate") { for (const video of videos) { if (video === origin) continue; video.playbackRate = origin.playbackRate; video.defaultPlaybackRate = origin.playbackRate; } } } finally { window.setTimeout(() => { state.propagating = false; }, 0); } } function bindSync(video) { video.addEventListener("play", () => propagate(video, "play")); video.addEventListener("pause", () => propagate(video, "pause")); video.addEventListener("seeked", () => propagate(video, "seek")); video.addEventListener("ratechange", () => propagate(video, "rate")); } function modelsForEngines(engines) { const out = new Set(); for (const engine of engines) { for (const model of manifest.model_order[engine] || []) { out.add(model); } } return out; } function activePresetKey() { for (const preset of presetDefinitions) { if ( setsEqual(state.selectedEngines, new Set(preset.engines)) && setsEqual(state.selectedModels, new Set(preset.models)) ) { return preset.key; } } return null; } function describeFilters() { const engineSummary = state.selectedEngines.size === manifest.engine_order.length ? "All engines" : `${state.selectedEngines.size} engine${state.selectedEngines.size === 1 ? "" : "s"}`; const modelSummary = state.selectedModels.size === allModels.length ? "all models" : `${state.selectedModels.size} model${state.selectedModels.size === 1 ? "" : "s"}`; return `${engineSummary} · ${modelSummary}`; } function setFilterState(engines, models) { state.selectedEngines = new Set(engines); state.selectedModels = new Set(models); renderFilterButtons(); renderSample(); } function renderFilterButtons() { const activePreset = activePresetKey(); presetBar.innerHTML = ""; for (const preset of presetDefinitions) { const button = createButton( preset.label, activePreset === preset.key, () => setFilterState(preset.engines, preset.models), "preset-btn" ); presetBar.appendChild(button); } engineFilters.innerHTML = ""; for (const engine of manifest.engine_order) { const button = createButton( manifest.engine_labels[engine] || titleCase(engine), state.selectedEngines.has(engine), () => { if (state.selectedEngines.has(engine)) state.selectedEngines.delete(engine); else state.selectedEngines.add(engine); if (state.selectedEngines.size === 0) state.selectedEngines.add(engine); renderFilterButtons(); renderSample(); }, "pill-btn" ); engineFilters.appendChild(button); } modelFilters.innerHTML = ""; for (const model of allModels) { const button = createButton( manifest.model_labels[model] || titleCase(model), state.selectedModels.has(model), () => { if (state.selectedModels.has(model)) state.selectedModels.delete(model); else state.selectedModels.add(model); if (state.selectedModels.size === 0) state.selectedModels.add(model); renderFilterButtons(); renderSample(); }, "pill-btn" ); modelFilters.appendChild(button); } } function renderSampleOptions(filterText) { const normalized = String(filterText || "").trim().toLowerCase(); sampleSelect.innerHTML = ""; let nextSelectedValue = null; for (const sample of state.samples) { const haystack = `${sample.id} ${sample.label} ${sample.difficulty} ${ sample.is_3d ? "3d" : "2d" }`.toLowerCase(); if (normalized && !haystack.includes(normalized)) continue; const option = document.createElement("option"); option.value = sample.id; option.textContent = sample.label; sampleSelect.appendChild(option); if (sample.id === currentSample().id) nextSelectedValue = sample.id; } if (nextSelectedValue) { sampleSelect.value = nextSelectedValue; } else if (sampleSelect.options.length > 0) { sampleSelect.selectedIndex = 0; } } function setSampleByIndex(index) { state.currentIndex = Math.max(0, Math.min(state.samples.length - 1, index)); sampleSelect.value = currentSample().id; updateCounter(); renderSample(); } function renderSampleMeta(sample) { sampleMeta.innerHTML = ""; const chips = [ { label: sample.is_3d ? "3D" : "2D", tone: "dimension" }, { label: sample.difficulty || "unknown", tone: sample.difficulty || "default" }, { label: `${sample.available_result_count} results`, tone: "count" }, ]; for (const chip of chips) { const span = document.createElement("span"); span.className = `meta-chip ${chip.tone}`; span.textContent = chip.label; sampleMeta.appendChild(span); } metaTask.textContent = sample.id; metaSplit.textContent = sample.split || "sub"; metaResults.textContent = `${sample.available_result_count}`; metaDetection.textContent = sample.detection_json_path ? "available" : "none"; } function filteredResults(sample) { return (sample.results || []).filter( (result) => state.selectedEngines.has(result.engine) && state.selectedModels.has(result.model) ); } function renderSample() { const sample = currentSample(); const visibleResults = filteredResults(sample); sampleSelect.value = sample.id; comparisonTitle.textContent = `${sample.id} · ${sample.is_3d ? "3D" : "2D"} · ${titleCase(sample.difficulty)}`; gtTitle.textContent = `${sample.id} · Ground Truth`; activeFilterSummary.textContent = describeFilters(); renderSampleMeta(sample); setVideoSource(gtVideo, buildAssetUrl(state.sourceMode, manifest, "gt", sample.gt.path)); for (const card of Array.from(resultsGrid.querySelectorAll(".result-card"))) { card.remove(); } for (const result of visibleResults) { const card = createResultCard(result); setVideoSource(card.video, buildAssetUrl(state.sourceMode, manifest, "generated", result.path)); card.video.muted = state.muted; card.video.playbackRate = state.playbackRate; bindSync(card.video); resultsGrid.appendChild(card.article); } if (gtCard && resultsGrid.firstElementChild !== gtCard) { resultsGrid.insertBefore(gtCard, resultsGrid.firstElementChild); } resultsSummary.textContent = `${visibleResults.length} generated · ${sample.available_result_count} total`; emptyState.hidden = visibleResults.length > 0; applyPlaybackSettings(); updateUrlState(); } bindSync(gtVideo); for (const sample of state.samples) { const option = document.createElement("option"); option.value = sample.id; option.textContent = sample.label; sampleSelect.appendChild(option); } const requestedSample = getQueryValue("sample"); if (requestedSample) { const requestedIndex = state.samples.findIndex((sample) => sample.id === requestedSample); if (requestedIndex >= 0) { state.currentIndex = requestedIndex; } } sampleSelect.addEventListener("change", () => { const nextId = sampleSelect.value; const nextIndex = state.samples.findIndex((sample) => sample.id === nextId); if (nextIndex >= 0) setSampleByIndex(nextIndex); }); sampleSearch.addEventListener("input", () => { renderSampleOptions(sampleSearch.value); }); sampleSearch.addEventListener("keydown", (event) => { if (event.key !== "Enter") return; const nextId = sampleSelect.value; const nextIndex = state.samples.findIndex((sample) => sample.id === nextId); if (nextIndex >= 0) setSampleByIndex(nextIndex); }); el("prev-sample").addEventListener("click", () => setSampleByIndex(state.currentIndex - 1)); el("next-sample").addEventListener("click", () => setSampleByIndex(state.currentIndex + 1)); sourceLocal.addEventListener("click", () => { if (!isLocalPreviewHost()) return; state.sourceMode = "local"; updateSourceButtons(); renderSample(); }); sourceHub.addEventListener("click", () => { state.sourceMode = "hub"; updateSourceButtons(); renderSample(); }); syncToggle.addEventListener("click", () => { state.syncEnabled = !state.syncEnabled; updateSyncButtons(); updateUrlState(); }); muteToggle.addEventListener("click", () => { state.muted = !state.muted; updateSyncButtons(); applyPlaybackSettings(); updateUrlState(); }); playbackRate.addEventListener("change", () => { applyPlaybackSettings(); updateUrlState(); }); el("play-all").addEventListener("click", () => { for (const video of allVisibleVideos()) { video.play().catch(() => { /* ignore autoplay restrictions */ }); } }); el("pause-all").addEventListener("click", () => { for (const video of allVisibleVideos()) { video.pause(); } }); el("restart-all").addEventListener("click", () => { for (const video of allVisibleVideos()) { video.currentTime = 0; video.pause(); } }); el("reset-filters").addEventListener("click", () => { setFilterState(manifest.engine_order, allModels); }); el("random-sample").addEventListener("click", () => { const nextIndex = Math.floor(Math.random() * state.samples.length); setSampleByIndex(nextIndex); }); copyLinkButton.addEventListener("click", async () => { const copied = await copyToClipboard(window.location.href).catch(() => false); const original = "Copy link"; copyLinkButton.textContent = copied ? "Copied" : "Copy failed"; window.setTimeout(() => { copyLinkButton.textContent = original; }, 1200); }); document.addEventListener("keydown", (event) => { if (event.key === "ArrowLeft") setSampleByIndex(state.currentIndex - 1); if (event.key === "ArrowRight") setSampleByIndex(state.currentIndex + 1); }); renderSampleOptions(""); renderFilterButtons(); updateSourceButtons(); updateSyncButtons(); updateCounter(); renderSample(); } window.addEventListener("DOMContentLoaded", () => { main().catch((error) => { console.error(error); document.body.innerHTML = `
${String(
error && error.stack ? error.stack : error
)}`;
});
});