import { experimentMeta, loadingPanel, loadingLabel, loadingPercent, loadingBar, exportStatus, runtimeSourceUrls, state, } from "./dom.js"; function ensureTrailingSlash(value) { if (!value) { return "./"; } return value.endsWith("/") ? value : `${value}/`; } function buildDatasetRuntimeRoot(repoId, revision = "main", pathPrefix = "") { const safePrefix = pathPrefix.trim().replace(/^\/+|\/+$/g, ""); const suffix = safePrefix ? `${safePrefix}/` : ""; return `https://huggingface.co/datasets/${repoId}/resolve/${revision}/${suffix}`; } function isAbsoluteUrl(path) { return /^(?:[a-z]+:)?\/\//i.test(path); } function resolveAgainstBase(path, baseUrl) { if (!path) { return path; } if (isAbsoluteUrl(path)) { return path; } const normalizedBase = isAbsoluteUrl(baseUrl) ? baseUrl : new URL(baseUrl, window.location.href).toString(); return new URL(path, normalizedBase).toString(); } function shouldResolveFromRuntimeRoot(path) { if (!path || isAbsoluteUrl(path)) { return false; } return path.startsWith("runtime/") || path.startsWith("./runtime/"); } function buildRuntimeRoot(runtimeSource) { if (runtimeSource?.runtime_root_url) { return ensureTrailingSlash(runtimeSource.runtime_root_url); } if (runtimeSource?.hf_dataset_repo) { return buildDatasetRuntimeRoot( runtimeSource.hf_dataset_repo, runtimeSource.hf_dataset_revision || "main", runtimeSource.hf_dataset_path_prefix || "" ); } return ensureTrailingSlash("./"); } function normalizeRuntimeSource(runtimeSource) { const normalized = { manifest_path: "./runtime/viewer_manifest.json", fallback_manifest_urls: ["./data/viewer_manifest.template.json"], ...runtimeSource, }; normalized.runtime_root_url = buildRuntimeRoot(normalized); return normalized; } function buildManifestUrls(runtimeSource) { const urls = []; if (runtimeSource?.manifest_path) { urls.push(resolveAgainstBase(runtimeSource.manifest_path, runtimeSource.runtime_root_url)); } for (const fallback of runtimeSource?.fallback_manifest_urls || []) { urls.push(resolveAgainstBase(fallback, "./")); } return urls; } function resolveRuntimePath(path) { if (!path) { return path; } if (shouldResolveFromRuntimeRoot(path)) { return resolveAgainstBase(path, state.runtimeSource?.runtime_root_url || "./"); } return resolveAgainstBase(path, "./"); } function normalizeManifest(manifest) { return { ...manifest, runs: (manifest.runs || []).map((run) => ({ ...run, path: resolveRuntimePath(run.path), })), }; } function normalizeRunPayload(payload) { const normalized = { ...payload }; if (normalized.assets) { normalized.assets = { ...normalized.assets, circuit: resolveRuntimePath(normalized.assets.circuit), data_overview: resolveRuntimePath(normalized.assets.data_overview), }; } if (normalized.run?.path) { normalized.run = { ...normalized.run, path: resolveRuntimePath(normalized.run.path), }; } if (normalized.timeline_chunks) { normalized.timeline_chunks = normalized.timeline_chunks.map((chunk) => ({ ...chunk, path: resolveRuntimePath(chunk.path), })); } if (normalized.timeline_steps) { normalized.timeline_steps = normalized.timeline_steps.map((step) => { if (!step.chunk_path) { return step; } return { ...step, chunk_path: resolveRuntimePath(step.chunk_path), }; }); } return normalized; } function describePath(path) { if (!path) { return "runtime data"; } try { const url = new URL(path, window.location.href); const parts = url.pathname.split("/").filter(Boolean); return parts.slice(-2).join("/"); } catch { return path.replace("./", ""); } } export function withCacheBust(path) { const separator = path.includes("?") ? "&" : "?"; return `${path}${separator}t=${Date.now()}`; } export function formatMetric(value) { if (value === undefined || value === null || Number.isNaN(Number(value))) { return "—"; } const numeric = Number(value); const absValue = Math.abs(numeric); if (absValue > 0 && absValue < 1e-3) { return numeric.toExponential(2); } return numeric.toFixed(4); } export function formatInteger(value) { if (value === undefined || value === null || Number.isNaN(Number(value))) { return "—"; } return String(Math.round(Number(value))); } export function appendMetaRow(label, value) { const wrapper = document.createElement("div"); const dt = document.createElement("dt"); const dd = document.createElement("dd"); dt.textContent = label; dd.textContent = value; wrapper.append(dt, dd); experimentMeta.appendChild(wrapper); } export function setLoadingState({ visible, label, percent, status }) { if (loadingPanel) { loadingPanel.hidden = !visible; } if (loadingLabel && label) { loadingLabel.textContent = label; } if (typeof percent === "number") { const clamped = Math.max(0, Math.min(100, Math.round(percent))); if (loadingPercent) { loadingPercent.textContent = `${clamped}%`; } if (loadingBar) { loadingBar.style.width = `${clamped}%`; } } if (status) { exportStatus.textContent = status; } } export async function loadRuntimeSource() { for (const url of runtimeSourceUrls) { const response = await fetch(withCacheBust(url), { cache: "no-store" }); if (response.ok) { const runtimeSource = normalizeRuntimeSource(await response.json()); state.runtimeSource = runtimeSource; return runtimeSource; } } const fallback = normalizeRuntimeSource({}); state.runtimeSource = fallback; return fallback; } export async function loadManifest() { const runtimeSource = state.runtimeSource || (await loadRuntimeSource()); for (const url of buildManifestUrls(runtimeSource)) { setLoadingState({ visible: true, label: `Loading manifest from ${describePath(url)}`, percent: 8, status: "loading", }); const response = await fetch(withCacheBust(url), { cache: "no-store" }); if (response.ok) { const data = normalizeManifest(await response.json()); setLoadingState({ visible: true, label: "Manifest ready", percent: 18, status: "loading", }); return data; } } throw new Error("No viewer manifest available."); } export function resolveRuntimeAssetPath(path) { return resolveRuntimePath(path); } export async function loadRunData(path, loadToken) { const response = await fetch(withCacheBust(path), { cache: "no-store" }); if (!response.ok) { throw new Error(`Failed to load run data: ${path}`); } const contentLength = Number(response.headers.get("content-length") || 0); if (!response.body || !contentLength) { setLoadingState({ visible: true, label: `Loading ${describePath(path)}`, percent: 45, status: "loading", }); return normalizeRunPayload(await response.json()); } const reader = response.body.getReader(); const decoder = new TextDecoder(); let received = 0; let text = ""; while (true) { const { done, value } = await reader.read(); if (done) { break; } if (loadToken !== state.activeLoadToken) { throw new Error("Stale run load aborted."); } received += value.byteLength; text += decoder.decode(value, { stream: true }); const progress = 20 + (received / contentLength) * 55; setLoadingState({ visible: true, label: `Downloading ${describePath(path)}`, percent: progress, status: "loading", }); } text += decoder.decode(); return normalizeRunPayload(JSON.parse(text)); } export async function loadRunChunk(path, loadToken) { const response = await fetch(withCacheBust(path), { cache: "no-store" }); if (!response.ok) { throw new Error(`Failed to load run chunk: ${path}`); } if (loadToken !== state.activeLoadToken) { throw new Error("Stale chunk load aborted."); } return normalizeRunPayload(await response.json()); }