// ─── Embed SVG Generator ───────────────────────────────────────────────────── // // Extracted module. Call initEmbed(deps) from the main app once globals are // ready. Returns { showEmbedModal }. // eslint-disable-next-line no-unused-vars function initEmbed(deps) { const { config, filters, activeFamilyKey, getActiveModelSet, getData, MODEL_COL, FAMILY_COL, GROUP_BY, CHART_CFG, MODEL_COLORS, MODEL_SHORT, isOOMRow, isExternalModel, sortModels, parseModelSize, } = deps; let embedModal = null; let cachedCommitHash = null; const HF_DOCS_REPO = "embedl/documentation-images"; const HF_SVG_FOLDER = "Edge-Inference-Benchmarks"; const HF_SPACES_REPO = "embedl/Edge-Inference-Benchmarks"; const HF_SPACES_URL = "https://huggingface.co/spaces/" + HF_SPACES_REPO; async function loadCommitHash() { if (cachedCommitHash) return cachedCommitHash; try { const resp = await fetch("https://huggingface.co/api/spaces/" + HF_SPACES_REPO); if (resp.ok) { const data = await resp.json(); cachedCommitHash = data.sha ? data.sha.substring(0, 7) : null; } } catch {} return cachedCommitHash; } function embedFamilyKey() { return filters.variant || activeFamilyKey(); } function embedFileName() { return embedFamilyKey() + "__" + filters[GROUP_BY] + ".svg"; } function getEmbedChartData() { const familyCfg = config.model_families?.[activeFamilyKey()] || {}; const chartCfg = familyCfg.chart || CHART_CFG; const scenarios = chartCfg.scenarios || []; const metricCol = filters.metric; const metricCfg = config.metrics.find(m => m.column === metricCol) || {}; const groupFilterCfg = config.filters.find(f => f.column === GROUP_BY); const groupVal = filters[GROUP_BY]; if (groupVal === "all") return null; const groupLabel = groupFilterCfg?.value_labels?.[groupVal] || String(groupVal); const familyModels = getActiveModelSet(); const filtered = getData().filter(r => { if (!familyModels.has(r[MODEL_COL])) return false; for (const f of config.filters) { const fv = filters[f.column]; if (fv === "all" || fv === "" || fv === undefined) continue; if (String(r[f.column]) !== String(fv)) return false; } return true; }); const gRows = filtered.filter(r => String(r[GROUP_BY]) === String(groupVal)); if (!gRows.length) return null; const uniqueModels = new Set(gRows.map(r => r[MODEL_COL])); if (uniqueModels.size <= 1) return null; const scenarioList = scenarios.length ? scenarios : [{ label: "", match: {} }]; // Find first scenario that produces data (mirrors buildChart logic) let scenario = null; let picked = []; for (const sc of scenarioList) { const matchRows = gRows.filter(r => Object.entries(sc.match || {}).every(([col, val]) => String(r[col]) === String(val) ) ); const matchedModels = new Set(matchRows.map(r => r[MODEL_COL])); const oomRows = gRows.filter(r => !matchedModels.has(r[MODEL_COL]) && isOOMRow(r) && Object.entries(sc.match || {}).every(([col, val]) => r[col] === null || r[col] === "" || r[col] === "OOM" || String(r[col]) === String(val) ) ); const allRows = matchRows.concat(oomRows); const models = sortModels([...new Set(allRows.map(r => r[MODEL_COL]))]); const candidates = models.map(m => allRows.find(r => r[MODEL_COL] === m)).filter(Boolean); if (candidates.length) { scenario = sc; picked = candidates; break; } } if (!picked.length) return null; const hib = metricCfg.higher_is_better !== false; picked.sort((a, b) => { const sizeA = parseModelSize(a[FAMILY_COL] || a[MODEL_COL]); const sizeB = parseModelSize(b[FAMILY_COL] || b[MODEL_COL]); if (sizeA !== sizeB) return sizeA - sizeB; const extA = isExternalModel(a[MODEL_COL]) ? 0 : 1; const extB = isExternalModel(b[MODEL_COL]) ? 0 : 1; if (extA !== extB) return extA - extB; const va = a[metricCol] ?? 0; const vb = b[metricCol] ?? 0; return hib ? va - vb : vb - va; }); const rawLabels = picked.map(r => MODEL_SHORT[r[MODEL_COL]]); const families = new Set(picked.map(r => r[FAMILY_COL])); const needPrefix = families.size > 1; const labels = rawLabels.map((lbl, i) => { if (needPrefix) { const fk = picked[i][FAMILY_COL] || ""; return lbl ? `${fk} ${lbl}` : fk; } return lbl; }); return { familyKey: embedFamilyKey(), groupLabel, scenarioLabel: scenario.label || "", metricCol, metricLabel: metricCfg.short || metricCol, higherIsBetter: hib, picked, labels, }; } function generateEmbedSVG(version) { const data = getEmbedChartData(); if (!data) return null; const { familyKey, groupLabel, scenarioLabel, metricCol, metricLabel, higherIsBetter, picked, labels } = data; const width = 800; const barHeight = 32; const barGap = 10; const topPad = 76; const bottomPad = 44; const leftPad = 260; const rightPad = 90; const barAreaWidth = width - leftPad - rightPad; const contentHeight = picked.length * (barHeight + barGap) - barGap; const height = topPad + contentHeight + bottomPad; const maxVal = Math.max(...picked.map(r => r[metricCol] ?? 0), 1); function esc(s) { return s.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """); } let bars = ""; picked.forEach((r, i) => { const val = r[metricCol] ?? 0; const barW = maxVal > 0 ? Math.max((val / maxVal) * barAreaWidth, 0) : 0; const y = topPad + i * (barHeight + barGap); const cy = y + barHeight / 2 + 5; const color = MODEL_COLORS[r[MODEL_COL]]?.border || "#58b1c3"; const label = labels[i]; const oom = val === 0 && isOOMRow(r); const valText = oom ? "OOM" : val.toFixed(1); const valColor = oom ? "#ff4d6d" : "#e8e8e8"; bars += ` ${esc(label)}\n`; if (!oom && barW > 0) { bars += ` \n`; } bars += ` ${valText}\n`; }); const hint = higherIsBetter ? "(higher is better)" : "(lower is better)"; const subtitle = [familyKey, groupLabel, scenarioLabel, metricLabel + " " + hint].filter(Boolean).join(" \u00b7 "); const commitAttr = version ? ` data-commit="${esc(version)}"` : ""; return ` Edge Inference Benchmarks ${esc(subtitle)} ${bars} embedl.com View Benchmarks \u2192 `; } function createEmbedModal() { const overlay = document.createElement("div"); overlay.className = "embed-overlay"; overlay.innerHTML = `

Embed in Model Card

`; overlay.querySelector(".embed-close").addEventListener("click", () => overlay.classList.remove("visible")); overlay.addEventListener("click", e => { if (e.target === overlay) overlay.classList.remove("visible"); }); overlay.querySelector("#embed-upload-manual").addEventListener("click", () => { const svg = generateEmbedSVG(cachedCommitHash); if (!svg) return; const fileName = embedFileName(); const blob = new Blob([svg], { type: "image/svg+xml" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = fileName; a.click(); URL.revokeObjectURL(url); const commitMsg = "Update " + fileName + " (" + (cachedCommitHash || "latest") + ")"; window.open("https://huggingface.co/datasets/" + HF_DOCS_REPO + "/upload/main/" + HF_SVG_FOLDER + "?commit_message=" + encodeURIComponent(commitMsg), "_blank"); }); overlay.querySelector("#embed-copy").addEventListener("click", () => { const textarea = overlay.querySelector("#embed-code"); navigator.clipboard.writeText(textarea.value); const btn = overlay.querySelector("#embed-copy"); const orig = btn.textContent; btn.textContent = "Copied!"; setTimeout(() => { btn.textContent = orig; }, 2000); }); document.body.appendChild(overlay); return overlay; } function setEmbedStatus(cls, html) { const el = embedModal.querySelector("#embed-status"); el.className = "embed-status " + cls; el.innerHTML = html; } function showUploadUI(show) { embedModal.querySelector("#embed-upload-manual").style.display = show ? "" : "none"; } async function runEmbedUpload() { const version = cachedCommitHash; const fileName = embedFileName(); const svgPath = HF_SVG_FOLDER + "/" + fileName; const svgUrl = "https://huggingface.co/datasets/" + HF_DOCS_REPO + "/resolve/main/" + svgPath; setEmbedStatus("checking", "Checking\u2026"); showUploadUI(false); let remoteCommit = null; let exists = false; try { const resp = await fetch(svgUrl, { cache: "no-store" }); if (resp.ok) { exists = true; const text = await resp.text(); const m = text.match(/data-commit="([^"]+)"/); if (m) remoteCommit = m[1]; } } catch {} if (exists && version && remoteCommit === version) { setEmbedStatus("up-to-date", ""); return; } if (exists) { setEmbedStatus("needs-token", "SVG outdated \u2014 use Download SVG & Open HF below to update"); } else { setEmbedStatus("needs-token", "SVG not yet uploaded \u2014 use Download SVG & Open HF below to upload manually"); } showUploadUI(true); } async function showEmbedModal() { if (!embedModal) embedModal = createEmbedModal(); const version = await loadCommitHash(); const svg = generateEmbedSVG(version); if (!svg) return; embedModal.querySelector("#embed-preview").innerHTML = svg; const famKey = embedFamilyKey(); const fileName = embedFileName(); const svgUrl = "https://huggingface.co/datasets/" + HF_DOCS_REPO + "/resolve/main/" + HF_SVG_FOLDER + "/" + fileName; const embedCode = '\n \n'; embedModal.querySelector("#embed-code").value = embedCode; embedModal.classList.add("visible"); runEmbedUpload(); } return { showEmbedModal }; } // end initEmbed