Demo
#5
by
JonnaMat - opened
- app.js +108 -61
- config.json +64 -1
- demo/demo.css +249 -0
- demo/demo.js +448 -0
- index.html +6 -0
- palette.css +74 -0
- style.css +111 -133
app.js
CHANGED
|
@@ -104,12 +104,9 @@ function isExternalModel(model) {
|
|
| 104 |
return !model.startsWith(OPT_ORG + "/");
|
| 105 |
}
|
| 106 |
|
| 107 |
-
// βββ
|
| 108 |
|
| 109 |
let ALL_MODELS = [];
|
| 110 |
-
|
| 111 |
-
// βββ Model Family Keys from config ββββββββββββββββββββββββββββββββββββββββββββ
|
| 112 |
-
|
| 113 |
const ALL_FAMILY_KEYS = Object.keys(config.model_families || {});
|
| 114 |
let MODEL_FAMILIES = {};
|
| 115 |
|
|
@@ -263,14 +260,12 @@ function availableOptions() {
|
|
| 263 |
return opts;
|
| 264 |
}
|
| 265 |
|
| 266 |
-
// Resolve display label for a filter value
|
| 267 |
function valueLabel(filterCfg, val) {
|
| 268 |
if (filterCfg.value_labels && filterCfg.value_labels[val]) return filterCfg.value_labels[val];
|
| 269 |
if (typeof val === "string") return val.charAt(0).toUpperCase() + val.slice(1);
|
| 270 |
return String(val);
|
| 271 |
}
|
| 272 |
|
| 273 |
-
// Sort models: external (original) first, then optimized
|
| 274 |
function sortModels(models) {
|
| 275 |
return [...models].sort((a, b) => {
|
| 276 |
const aExt = isExternalModel(a) ? 0 : 1;
|
|
@@ -279,6 +274,22 @@ function sortModels(models) {
|
|
| 279 |
});
|
| 280 |
}
|
| 281 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 282 |
// βββ Populate page from config ββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 283 |
|
| 284 |
// Hero
|
|
@@ -294,7 +305,7 @@ function renderSidebar() {
|
|
| 294 |
html += `<div class="sidebar-item${isActive ? " active" : ""}" data-base-family="${bf}">${bf}</div>`;
|
| 295 |
if (isActive) {
|
| 296 |
const variants = BASE_FAMILIES[bf].variants;
|
| 297 |
-
const showVariants = variants.length > 1
|
| 298 |
if (showVariants) {
|
| 299 |
variants.forEach(v => {
|
| 300 |
const isVariantActive = v === filters.variant;
|
|
@@ -339,8 +350,8 @@ config.filters.forEach(f => {
|
|
| 339 |
filtersBar.appendChild(createFilterGroup(f.label, "filter-" + f.column));
|
| 340 |
});
|
| 341 |
|
| 342 |
-
// Metric filter
|
| 343 |
-
|
| 344 |
|
| 345 |
function createFilterGroup(label, id) {
|
| 346 |
const div = document.createElement("div");
|
|
@@ -372,13 +383,12 @@ function renderBtnGroup(container, items, activeValue) {
|
|
| 372 |
function populateFilters() {
|
| 373 |
renderSidebar();
|
| 374 |
|
| 375 |
-
// Metric buttons
|
| 376 |
-
const metricEl =
|
| 377 |
renderBtnGroup(metricEl,
|
| 378 |
config.metrics.map(m => ({ value: m.column, label: m.short || m.column })),
|
| 379 |
filters.metric
|
| 380 |
);
|
| 381 |
-
metricEl.closest(".filter-group").style.display = (config.metrics.length <= 1 || filters[GROUP_BY] === "all") ? "none" : "";
|
| 382 |
|
| 383 |
updateDependentFilters();
|
| 384 |
}
|
|
@@ -429,7 +439,7 @@ function updateDependentFilters(resetDefaults) {
|
|
| 429 |
|
| 430 |
// βββ Event binding ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 431 |
|
| 432 |
-
|
| 433 |
const btn = e.target.closest(".btn");
|
| 434 |
if (!btn) return;
|
| 435 |
const group = btn.closest(".btn-group");
|
|
@@ -438,7 +448,10 @@ filtersBar.addEventListener("click", e => {
|
|
| 438 |
const key = group.id.replace("filter-", "");
|
| 439 |
filters[key] = btn.dataset.value;
|
| 440 |
render();
|
| 441 |
-
}
|
|
|
|
|
|
|
|
|
|
| 442 |
|
| 443 |
// βββ Chart ββββββββββββββββββββββββββββοΏ½οΏ½βββββββββββββββββββββββββββββββββββββββ
|
| 444 |
|
|
@@ -474,6 +487,35 @@ function buildChart(filtered) {
|
|
| 474 |
? scenarios
|
| 475 |
: [{ label: "", match: {} }];
|
| 476 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 477 |
scenarioList.forEach(scenario => {
|
| 478 |
// Filter rows matching this scenario
|
| 479 |
const matchRows = gRows.filter(r =>
|
|
@@ -506,16 +548,8 @@ function buildChart(filtered) {
|
|
| 506 |
const chartBlock = document.createElement("div");
|
| 507 |
chartBlock.className = "chart-block";
|
| 508 |
|
| 509 |
-
const heading = document.createElement("h3");
|
| 510 |
-
heading.className = "chart-heading";
|
| 511 |
-
heading.textContent = groupLabel;
|
| 512 |
-
chartBlock.appendChild(heading);
|
| 513 |
-
|
| 514 |
if (scenario.label) {
|
| 515 |
-
|
| 516 |
-
sub.className = "chart-subtitle";
|
| 517 |
-
sub.textContent = scenario.label;
|
| 518 |
-
chartBlock.appendChild(sub);
|
| 519 |
}
|
| 520 |
|
| 521 |
const wrap = document.createElement("div");
|
|
@@ -683,19 +717,13 @@ function buildTables(filtered, chartsShown) {
|
|
| 683 |
html += `<tr class="${rowClass}">`;
|
| 684 |
colDefs.forEach((c, i) => {
|
| 685 |
const val = r[c.key];
|
| 686 |
-
const fmCls = i === firstMetricIdx ? ' class="first-metric metric-cell"' : ' class="metric-cell"';
|
| 687 |
if (c.isModel) {
|
| 688 |
-
|
| 689 |
-
|
| 690 |
-
|
| 691 |
-
} else if (oom) {
|
| 692 |
-
html += `<td${c.isMetric ? fmCls : ''}><span class="oom">OOM</span></td>`;
|
| 693 |
-
} else if (c.isMetric) {
|
| 694 |
const sg = groupRowKey(r);
|
| 695 |
-
const isBest = val !== null && val !== undefined && val === bestByGroup[sg]?.[c.key];
|
| 696 |
-
|
| 697 |
-
const modelColor = MODEL_COLORS[r[MODEL_COL]]?.border || '#888';
|
| 698 |
-
html += `<td${fmCls}>${isBest ? '<strong style="color: white; opacity: 0.7">' + display + '</strong>' : display}</td>`;
|
| 699 |
} else {
|
| 700 |
html += `<td>${val || "β"}</td>`;
|
| 701 |
}
|
|
@@ -737,6 +765,29 @@ function buildExperimentSetup() {
|
|
| 737 |
});
|
| 738 |
}
|
| 739 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 740 |
// βββ Accuracy Table ββββββββββββββββββββββββββββββββββββββββββββββββββββοΏ½οΏ½ββββββ
|
| 741 |
|
| 742 |
async function buildAccuracyTable() {
|
|
@@ -767,37 +818,38 @@ async function buildAccuracyTable() {
|
|
| 767 |
if (vals.length) best[col] = Math.max(...vals);
|
| 768 |
});
|
| 769 |
|
| 770 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 771 |
let fixedHtml = `<table><thead><tr><th>MODEL</th></tr></thead><tbody>`;
|
| 772 |
-
rows.forEach(r => {
|
| 773 |
-
const model = r[modelCol];
|
| 774 |
-
const modelColor = MODEL_COLORS[model]?.border || '#888';
|
| 775 |
-
fixedHtml += `<tr><td class="model-cell"><span class="model-dot" style="background:${modelColor}"></span><a href="${LINK_PREFIX}${model}" target="_blank" rel="noopener" style="color:${modelColor}">${model}</a></td></tr>`;
|
| 776 |
-
});
|
| 777 |
fixedHtml += `</tbody></table>`;
|
| 778 |
|
| 779 |
-
// Scrollable metric columns
|
| 780 |
-
let scrollHtml = `<table><thead><tr>`;
|
| 781 |
-
|
| 782 |
-
scrollHtml += `</tr></thead><tbody>`;
|
| 783 |
-
rows.forEach(r => {
|
| 784 |
-
scrollHtml += `<tr>`;
|
| 785 |
-
metricCols.forEach(col => {
|
| 786 |
-
const val = parseFloat(r[col]);
|
| 787 |
-
const isBest = !isNaN(val) && val === best[col];
|
| 788 |
-
const display = isNaN(val) ? (r[col] || "β") : val.toFixed(2);
|
| 789 |
-
scrollHtml += `<td class="metric-cell">${isBest ? '<strong style="color: white; opacity: 0.7">' + display + '</strong>' : display}</td>`;
|
| 790 |
-
});
|
| 791 |
-
scrollHtml += `</tr>`;
|
| 792 |
-
});
|
| 793 |
scrollHtml += `</tbody></table>`;
|
| 794 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 795 |
const accTitle = familyCfg.accuracy_title || config.accuracy_title || "Accuracy";
|
| 796 |
const accUrl = familyCfg.accuracy_url;
|
| 797 |
const titleHtml = accUrl
|
| 798 |
? `<h3><a href="${accUrl}" target="_blank" rel="noopener" class="acc-title-link">${accTitle}</a></h3>`
|
| 799 |
: `<h3>${accTitle}</h3>`;
|
| 800 |
-
card.innerHTML = `${titleHtml}<div class="table-split"><div class="table-split-fixed">${fixedHtml}</div><div class="table-split-scroll">${scrollHtml}</div></div>`;
|
| 801 |
section.appendChild(card);
|
| 802 |
}
|
| 803 |
|
|
@@ -818,13 +870,8 @@ function render() {
|
|
| 818 |
|
| 819 |
buildChart(filtered);
|
| 820 |
const chartsShown = charts.length > 0;
|
| 821 |
-
// Toggle metric selector visibility
|
| 822 |
-
const metricEl = document.getElementById("filter-metric");
|
| 823 |
-
if (metricEl) {
|
| 824 |
-
metricEl.closest(".filter-group").style.display =
|
| 825 |
-
(config.metrics.length <= 1 || !chartsShown) ? "none" : "";
|
| 826 |
-
}
|
| 827 |
buildTables(filtered, chartsShown);
|
|
|
|
| 828 |
buildAccuracyTable();
|
| 829 |
buildExperimentSetup();
|
| 830 |
}
|
|
|
|
| 104 |
return !model.startsWith(OPT_ORG + "/");
|
| 105 |
}
|
| 106 |
|
| 107 |
+
// βββ Model families & variants βββββββββββββββββββββββββββββββββββββββββββββββ
|
| 108 |
|
| 109 |
let ALL_MODELS = [];
|
|
|
|
|
|
|
|
|
|
| 110 |
const ALL_FAMILY_KEYS = Object.keys(config.model_families || {});
|
| 111 |
let MODEL_FAMILIES = {};
|
| 112 |
|
|
|
|
| 260 |
return opts;
|
| 261 |
}
|
| 262 |
|
|
|
|
| 263 |
function valueLabel(filterCfg, val) {
|
| 264 |
if (filterCfg.value_labels && filterCfg.value_labels[val]) return filterCfg.value_labels[val];
|
| 265 |
if (typeof val === "string") return val.charAt(0).toUpperCase() + val.slice(1);
|
| 266 |
return String(val);
|
| 267 |
}
|
| 268 |
|
|
|
|
| 269 |
function sortModels(models) {
|
| 270 |
return [...models].sort((a, b) => {
|
| 271 |
const aExt = isExternalModel(a) ? 0 : 1;
|
|
|
|
| 274 |
});
|
| 275 |
}
|
| 276 |
|
| 277 |
+
// βββ Shared HTML builders ββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 278 |
+
|
| 279 |
+
function modelCellHtml(model) {
|
| 280 |
+
const color = MODEL_COLORS[model]?.border || '#888';
|
| 281 |
+
const url = LINK_PREFIX + model;
|
| 282 |
+
return `<td class="model-cell"><span class="model-dot" style="background:${color}"></span><a href="${url}" target="_blank" rel="noopener" style="color:${color}">${model}</a></td>`;
|
| 283 |
+
}
|
| 284 |
+
|
| 285 |
+
function metricCellHtml(val, isBest, extraClass) {
|
| 286 |
+
const cls = extraClass ? ` class="${extraClass}"` : ' class="metric-cell"';
|
| 287 |
+
if (val === null || val === undefined) return `<td${cls}><span class="oom">OOM</span></td>`;
|
| 288 |
+
const display = typeof val === "number" ? val.toFixed(2) : (val || "β");
|
| 289 |
+
const content = isBest ? `<strong style="color: white; opacity: 0.7">${display}</strong>` : display;
|
| 290 |
+
return `<td${cls}>${content}</td>`;
|
| 291 |
+
}
|
| 292 |
+
|
| 293 |
// βββ Populate page from config ββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 294 |
|
| 295 |
// Hero
|
|
|
|
| 305 |
html += `<div class="sidebar-item${isActive ? " active" : ""}" data-base-family="${bf}">${bf}</div>`;
|
| 306 |
if (isActive) {
|
| 307 |
const variants = BASE_FAMILIES[bf].variants;
|
| 308 |
+
const showVariants = variants.length > 1;
|
| 309 |
if (showVariants) {
|
| 310 |
variants.forEach(v => {
|
| 311 |
const isVariantActive = v === filters.variant;
|
|
|
|
| 350 |
filtersBar.appendChild(createFilterGroup(f.label, "filter-" + f.column));
|
| 351 |
});
|
| 352 |
|
| 353 |
+
// Metric filter β rendered inside chart section header, not in filters bar
|
| 354 |
+
const metricGroup = createFilterGroup("METRIC", "filter-metric");
|
| 355 |
|
| 356 |
function createFilterGroup(label, id) {
|
| 357 |
const div = document.createElement("div");
|
|
|
|
| 383 |
function populateFilters() {
|
| 384 |
renderSidebar();
|
| 385 |
|
| 386 |
+
// Metric buttons (rendered inside chart header)
|
| 387 |
+
const metricEl = metricGroup.querySelector(".btn-group");
|
| 388 |
renderBtnGroup(metricEl,
|
| 389 |
config.metrics.map(m => ({ value: m.column, label: m.short || m.column })),
|
| 390 |
filters.metric
|
| 391 |
);
|
|
|
|
| 392 |
|
| 393 |
updateDependentFilters();
|
| 394 |
}
|
|
|
|
| 439 |
|
| 440 |
// βββ Event binding ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 441 |
|
| 442 |
+
function handleFilterClick(e) {
|
| 443 |
const btn = e.target.closest(".btn");
|
| 444 |
if (!btn) return;
|
| 445 |
const group = btn.closest(".btn-group");
|
|
|
|
| 448 |
const key = group.id.replace("filter-", "");
|
| 449 |
filters[key] = btn.dataset.value;
|
| 450 |
render();
|
| 451 |
+
}
|
| 452 |
+
|
| 453 |
+
metricGroup.addEventListener("click", handleFilterClick);
|
| 454 |
+
filtersBar.addEventListener("click", handleFilterClick);
|
| 455 |
|
| 456 |
// βββ Chart ββββββββββββββββββββββββββββοΏ½οΏ½βββββββββββββββββββββββββββββββββββββββ
|
| 457 |
|
|
|
|
| 487 |
? scenarios
|
| 488 |
: [{ label: "", match: {} }];
|
| 489 |
|
| 490 |
+
// Insert metric selector into chart section header
|
| 491 |
+
const chartHeader = document.createElement("div");
|
| 492 |
+
chartHeader.className = "chart-header";
|
| 493 |
+
|
| 494 |
+
const headerLeft = document.createElement("div");
|
| 495 |
+
headerLeft.className = "chart-header-left";
|
| 496 |
+
|
| 497 |
+
const headerTitle = document.createElement("h3");
|
| 498 |
+
headerTitle.className = "chart-heading";
|
| 499 |
+
headerTitle.textContent = groupLabel;
|
| 500 |
+
headerLeft.appendChild(headerTitle);
|
| 501 |
+
|
| 502 |
+
const headerSubtitle = document.createElement("p");
|
| 503 |
+
headerSubtitle.className = "chart-subtitle";
|
| 504 |
+
headerLeft.appendChild(headerSubtitle);
|
| 505 |
+
|
| 506 |
+
chartHeader.appendChild(headerLeft);
|
| 507 |
+
|
| 508 |
+
if (config.metrics.length > 1) {
|
| 509 |
+
const metricEl = metricGroup.querySelector(".btn-group");
|
| 510 |
+
renderBtnGroup(metricEl,
|
| 511 |
+
config.metrics.map(m => ({ value: m.column, label: m.short || m.column })),
|
| 512 |
+
filters.metric
|
| 513 |
+
);
|
| 514 |
+
chartHeader.appendChild(metricGroup);
|
| 515 |
+
}
|
| 516 |
+
|
| 517 |
+
section.appendChild(chartHeader);
|
| 518 |
+
|
| 519 |
scenarioList.forEach(scenario => {
|
| 520 |
// Filter rows matching this scenario
|
| 521 |
const matchRows = gRows.filter(r =>
|
|
|
|
| 548 |
const chartBlock = document.createElement("div");
|
| 549 |
chartBlock.className = "chart-block";
|
| 550 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 551 |
if (scenario.label) {
|
| 552 |
+
headerSubtitle.textContent = scenario.label;
|
|
|
|
|
|
|
|
|
|
| 553 |
}
|
| 554 |
|
| 555 |
const wrap = document.createElement("div");
|
|
|
|
| 717 |
html += `<tr class="${rowClass}">`;
|
| 718 |
colDefs.forEach((c, i) => {
|
| 719 |
const val = r[c.key];
|
|
|
|
| 720 |
if (c.isModel) {
|
| 721 |
+
html += modelCellHtml(val);
|
| 722 |
+
} else if (c.isMetric || oom) {
|
| 723 |
+
const cls = i === firstMetricIdx ? "first-metric metric-cell" : "metric-cell";
|
|
|
|
|
|
|
|
|
|
| 724 |
const sg = groupRowKey(r);
|
| 725 |
+
const isBest = !oom && val !== null && val !== undefined && val === bestByGroup[sg]?.[c.key];
|
| 726 |
+
html += metricCellHtml(oom ? null : val, isBest, cls);
|
|
|
|
|
|
|
| 727 |
} else {
|
| 728 |
html += `<td>${val || "β"}</td>`;
|
| 729 |
}
|
|
|
|
| 765 |
});
|
| 766 |
}
|
| 767 |
|
| 768 |
+
// βββ Demo Section βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 769 |
+
|
| 770 |
+
let currentDemoKey = null;
|
| 771 |
+
|
| 772 |
+
async function buildDemo() {
|
| 773 |
+
const section = document.getElementById("demo-section");
|
| 774 |
+
const familyCfg = config.model_families?.[activeFamilyKey()] || {};
|
| 775 |
+
const demoData = familyCfg.demo;
|
| 776 |
+
const key = activeFamilyKey();
|
| 777 |
+
if (!demoData) {
|
| 778 |
+
section.innerHTML = "";
|
| 779 |
+
currentDemoKey = null;
|
| 780 |
+
return;
|
| 781 |
+
}
|
| 782 |
+
// Skip rebuild if demo is already rendered for this family
|
| 783 |
+
if (key === currentDemoKey) return;
|
| 784 |
+
currentDemoKey = key;
|
| 785 |
+
section.innerHTML = "";
|
| 786 |
+
if (typeof renderDemo === "function") {
|
| 787 |
+
await renderDemo(demoData, section, OPT_ORG, familyCfg.data_file, MODEL_COLORS);
|
| 788 |
+
}
|
| 789 |
+
}
|
| 790 |
+
|
| 791 |
// βββ Accuracy Table ββββββββββββββββββββββββββββββββββββββββββββββββββββοΏ½οΏ½ββββββ
|
| 792 |
|
| 793 |
async function buildAccuracyTable() {
|
|
|
|
| 818 |
if (vals.length) best[col] = Math.max(...vals);
|
| 819 |
});
|
| 820 |
|
| 821 |
+
// Build metric cells for a row
|
| 822 |
+
function accMetricCells(r) {
|
| 823 |
+
return metricCols.map(col => {
|
| 824 |
+
const val = parseFloat(r[col]);
|
| 825 |
+
const isBest = !isNaN(val) && val === best[col];
|
| 826 |
+
return metricCellHtml(isNaN(val) ? r[col] : val, isBest);
|
| 827 |
+
}).join("");
|
| 828 |
+
}
|
| 829 |
+
|
| 830 |
+
const metricHeaders = metricCols.map(h => `<th class="metric-cell">${h}</th>`).join("");
|
| 831 |
+
|
| 832 |
+
// Fixed model column (desktop)
|
| 833 |
let fixedHtml = `<table><thead><tr><th>MODEL</th></tr></thead><tbody>`;
|
| 834 |
+
rows.forEach(r => { fixedHtml += `<tr>${modelCellHtml(r[modelCol])}</tr>`; });
|
|
|
|
|
|
|
|
|
|
|
|
|
| 835 |
fixedHtml += `</tbody></table>`;
|
| 836 |
|
| 837 |
+
// Scrollable metric columns (desktop)
|
| 838 |
+
let scrollHtml = `<table><thead><tr>${metricHeaders}</tr></thead><tbody>`;
|
| 839 |
+
rows.forEach(r => { scrollHtml += `<tr>${accMetricCells(r)}</tr>`; });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 840 |
scrollHtml += `</tbody></table>`;
|
| 841 |
|
| 842 |
+
// Combined single table (mobile β fully scrollable)
|
| 843 |
+
let combinedHtml = `<table><thead><tr><th>MODEL</th>${metricHeaders}</tr></thead><tbody>`;
|
| 844 |
+
rows.forEach(r => { combinedHtml += `<tr>${modelCellHtml(r[modelCol])}${accMetricCells(r)}</tr>`; });
|
| 845 |
+
combinedHtml += `</tbody></table>`;
|
| 846 |
+
|
| 847 |
const accTitle = familyCfg.accuracy_title || config.accuracy_title || "Accuracy";
|
| 848 |
const accUrl = familyCfg.accuracy_url;
|
| 849 |
const titleHtml = accUrl
|
| 850 |
? `<h3><a href="${accUrl}" target="_blank" rel="noopener" class="acc-title-link">${accTitle}</a></h3>`
|
| 851 |
: `<h3>${accTitle}</h3>`;
|
| 852 |
+
card.innerHTML = `${titleHtml}<div class="table-split"><div class="table-split-fixed">${fixedHtml}</div><div class="table-split-scroll">${scrollHtml}</div></div><div class="table-scroll table-scroll-mobile">${combinedHtml}</div>`;
|
| 853 |
section.appendChild(card);
|
| 854 |
}
|
| 855 |
|
|
|
|
| 870 |
|
| 871 |
buildChart(filtered);
|
| 872 |
const chartsShown = charts.length > 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 873 |
buildTables(filtered, chartsShown);
|
| 874 |
+
buildDemo();
|
| 875 |
buildAccuracyTable();
|
| 876 |
buildExperimentSetup();
|
| 877 |
}
|
config.json
CHANGED
|
@@ -141,7 +141,70 @@
|
|
| 141 |
"default_device": "orin_nano",
|
| 142 |
"accuracy_file": "data/acc-Cosmos-Reason2.csv",
|
| 143 |
"accuracy_title": "Physical AI Bench (PAI-Bench)",
|
| 144 |
-
"accuracy_url": "https://huggingface.co/spaces/shi-labs/physical-ai-bench-leaderboard"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 145 |
},
|
| 146 |
"Qwen3.5": {
|
| 147 |
"data_file": "data/Qwen3.5.csv",
|
|
|
|
| 141 |
"default_device": "orin_nano",
|
| 142 |
"accuracy_file": "data/acc-Cosmos-Reason2.csv",
|
| 143 |
"accuracy_title": "Physical AI Bench (PAI-Bench)",
|
| 144 |
+
"accuracy_url": "https://huggingface.co/spaces/shi-labs/physical-ai-bench-leaderboard",
|
| 145 |
+
"demo": {
|
| 146 |
+
"title": "Output Examples",
|
| 147 |
+
"benchmark_match": {
|
| 148 |
+
"type": "video",
|
| 149 |
+
"batch": "1",
|
| 150 |
+
"res": "1280x720",
|
| 151 |
+
"fps": "4"
|
| 152 |
+
},
|
| 153 |
+
"clips": [
|
| 154 |
+
{
|
| 155 |
+
"video": "https://nvidia-cosmos.github.io/cosmos-cookbook/gallery/vs_assets/clip_1_short.mp4",
|
| 156 |
+
"label": "Automotive Description",
|
| 157 |
+
"prompt": "Describe this video in detail.",
|
| 158 |
+
"inference_setup": "1280x720 Β· 12 Frames",
|
| 159 |
+
"citation": "Video source: Wagwani, R., Matveiev, N., Bapst, J., & Gu, J. (2025). Autonomous Vehicle Domain Adaptation Gallery. In NVIDIA Cosmos Cookbook.",
|
| 160 |
+
"citation_url": "https://nvidia-cosmos.github.io/cosmos-cookbook/gallery/av_inference.html",
|
| 161 |
+
"outputs": [
|
| 162 |
+
{
|
| 163 |
+
"model": "nvidia/Cosmos-Reason2-2B",
|
| 164 |
+
"text": "The video captures a scenic highway winding through a rural landscape, with multiple lanes of traffic flowing smoothly. Vehicles of various colors and models are seen traveling in both directions along the road. On the left side of the highway, a white pickup truck is followed by a red sedan, while on the right side, a white sedan is seen driving away from the camera. Further down the road, additional cars can be observed, including a blue hatchback and a red SUV, all maintaining a steady pace. The highway curves gently to the right, bordered by patches of dry grass and sparse trees, suggesting it might be late autumn or early spring. The sky above is clear and bright, indicating favorable weather conditions. On the right side of the highway, there is a pedestrian crossing marked by a yellow and black striped barrier, ensuring safety for walkers. In the distance, a lone figure can be seen walking along the sidewalk parallel to the road, adding a sense of scale and tranquility to the scene. The overall atmosphere is calm and orderly, with no signs of congestion or accidents."
|
| 165 |
+
},
|
| 166 |
+
{
|
| 167 |
+
"model": "embedl/Cosmos-Reason2-2B-W4A16-Edge2-FlashHead",
|
| 168 |
+
"text": "The video captures a dynamic scene of a multi-laned highway winding through a wooded area. The road is divided into two lanes in each direction, separated by a median strip. Several vehicles are traveling along the highway, including a white sedan in the rightmost lane, a red hatchback in the leftmost lane, and a white pickup truck further ahead in the same lane as the hatchback. Additional cars can be seen further down the road, moving in both directions. The surrounding landscape features bare trees and patches of grass, indicative of a winter or early spring season. The road markings are clearly visible, with dashed white lines marking the edges of the lanes and solid yellow lines delineating the lanes. On the right side of the road, there is a warning sign with black and yellow stripes, indicating caution for drivers. The sky is clear, suggesting favorable weather conditions for driving."
|
| 169 |
+
}
|
| 170 |
+
]
|
| 171 |
+
},
|
| 172 |
+
{
|
| 173 |
+
"video": "https://nvidia-cosmos.github.io/cosmos-cookbook/gallery/assets/kitchen_oranges_input.mp4",
|
| 174 |
+
"label": "Instruction Understanding",
|
| 175 |
+
"prompt": "The robot in the video was given the instruction - store all fruit in the big bowl. Has it successfully completed the task specified in the instruction? Explain your answer in detail.",
|
| 176 |
+
"inference_setup": "1280x720 Β· 32 Frames",
|
| 177 |
+
"citation": "Video source: Wagwani, R., Matveiev, N., Bapst, J., & Gu, J. (2025). Autonomous Vehicle Domain Adaptation Gallery. In NVIDIA Cosmos Cookbook.",
|
| 178 |
+
"citation_url": "https://nvidia-cosmos.github.io/cosmos-cookbook/gallery/av_inference.html",
|
| 179 |
+
"outputs": [
|
| 180 |
+
{
|
| 181 |
+
"model": "nvidia/Cosmos-Reason2-2B",
|
| 182 |
+
"text": "No, the robot has not successfully completed the task. The instruction was to store all fruit in the big bowl, but the robot only moved one fruit into the bowl. There are still other fruits on the countertop that have not been moved into the bowl. Additionally, the robot's actions appear somewhat clumsy and inefficient."
|
| 183 |
+
},
|
| 184 |
+
{
|
| 185 |
+
"model": "embedl/Cosmos-Reason2-2B-W4A16-Edge2-FlashHead",
|
| 186 |
+
"text": "No, the robot has not successfully completed the task. The instruction was to store all fruit in the big bowl, but the video shows that there are still other fruits on the plate and counter. Additionally, the robot's actions involve moving objects around rather than storing them in the bowl."
|
| 187 |
+
}
|
| 188 |
+
]
|
| 189 |
+
},
|
| 190 |
+
{
|
| 191 |
+
"video": "https://huggingface.co/datasets/embedl/documentation-images/resolve/main/Edge-Inference-Benchmarks/aerial_drone_view.mp4",
|
| 192 |
+
"label": "Search and Rescue",
|
| 193 |
+
"prompt": "Mission: Inspect the drone video for any people or animals that clearly need rescue. Answer format: 1. Rescue needed: Y/N; 2. Description: Describe the scene in detail, explain why rescue is or is not needed.",
|
| 194 |
+
"inference_setup": "1280x720 Β· 24 Frames",
|
| 195 |
+
"outputs": [
|
| 196 |
+
{
|
| 197 |
+
"model": "nvidia/Cosmos-Reason2-2B",
|
| 198 |
+
"text": "1. Rescue needed: N 2. The scene shows a group of people gathered around a table on a grassy field near a road, with a vehicle parked nearby. Individuals appear to be engaged in an activity, possibly a meeting or briefing, with some standing and others seated. There are no visible signs of distress, emergencies, or animals requiring rescue. The environment is calm, and the group seems to be cooperating peacefully."
|
| 199 |
+
},
|
| 200 |
+
{
|
| 201 |
+
"model": "embedl/Cosmos-Reason2-2B-W4A16-Edge2-FlashHead",
|
| 202 |
+
"text": "1. Rescue needed: N; 2. The video shows an aerial view of a serene landscape with a lake, green fields, and a road with a few vehicles and people. There are no visible emergencies, accidents, or animals in distress. The scene appears peaceful and undisturbed."
|
| 203 |
+
}
|
| 204 |
+
]
|
| 205 |
+
}
|
| 206 |
+
]
|
| 207 |
+
}
|
| 208 |
},
|
| 209 |
"Qwen3.5": {
|
| 210 |
"data_file": "data/Qwen3.5.csv",
|
demo/demo.css
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* ββ Output Example Demo ββββββββββββββββββββββββββββββββ */
|
| 2 |
+
.demo-section {
|
| 3 |
+
margin: 0 0 var(--space-xl);
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
/* ββ Toggle button βββββββββββββββββββββββββββββββββββββ */
|
| 7 |
+
.demo-toggle {
|
| 8 |
+
display: inline-flex;
|
| 9 |
+
align-items: center;
|
| 10 |
+
gap: var(--space-sm);
|
| 11 |
+
background: transparent;
|
| 12 |
+
border: 1px solid var(--border);
|
| 13 |
+
border-radius: 6px;
|
| 14 |
+
color: var(--text-muted);
|
| 15 |
+
font-family: inherit;
|
| 16 |
+
font-size: var(--text-md);
|
| 17 |
+
font-weight: 500;
|
| 18 |
+
padding: var(--space-sm) var(--space-md);
|
| 19 |
+
cursor: pointer;
|
| 20 |
+
transition: color 0.15s, border-color 0.15s, background 0.15s;
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
.demo-toggle:hover {
|
| 24 |
+
color: var(--text);
|
| 25 |
+
background: var(--btn-hover-bg);
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
.demo-toggle.active {
|
| 29 |
+
color: var(--teal);
|
| 30 |
+
border-color: var(--btn-active-border);
|
| 31 |
+
background: var(--btn-active-bg);
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
.demo-toggle-arrow {
|
| 35 |
+
display: inline-block;
|
| 36 |
+
transition: transform 0.2s;
|
| 37 |
+
font-size: var(--text-sm);
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
.demo-toggle.active .demo-toggle-arrow {
|
| 41 |
+
transform: rotate(90deg);
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
/* ββ Collapsible content βββββββββββββββββββββββββββββββ */
|
| 45 |
+
.demo-content {
|
| 46 |
+
display: grid;
|
| 47 |
+
grid-template-rows: 0fr;
|
| 48 |
+
transition: grid-template-rows 0.3s ease;
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
.demo-content.open {
|
| 52 |
+
grid-template-rows: 1fr;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
.demo-content-inner {
|
| 56 |
+
overflow: hidden;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
/* ββ Card ββββββββββββββββββββββββββββββββββββββββββββββ */
|
| 60 |
+
.demo-card {
|
| 61 |
+
border-radius: 12px;
|
| 62 |
+
border: 1px solid var(--border);
|
| 63 |
+
padding: var(--space-lg);
|
| 64 |
+
margin-top: var(--space-md);
|
| 65 |
+
position: relative;
|
| 66 |
+
overflow: hidden;
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
/* ββ Title row ββββββββββββββββββββββββββββββββββββββββ */
|
| 70 |
+
.demo-title-row {
|
| 71 |
+
display: flex;
|
| 72 |
+
align-items: center;
|
| 73 |
+
justify-content: space-between;
|
| 74 |
+
gap: var(--space-md);
|
| 75 |
+
margin-bottom: var(--space-sm);
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
.demo-title-row h3 {
|
| 79 |
+
font-size: var(--text-lg);
|
| 80 |
+
font-weight: 600;
|
| 81 |
+
color: var(--text);
|
| 82 |
+
margin-bottom: 0;
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
.demo-title-left {
|
| 86 |
+
display: flex;
|
| 87 |
+
align-items: baseline;
|
| 88 |
+
gap: var(--space-md);
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
.demo-clip-setup {
|
| 92 |
+
font-size: var(--text-md);
|
| 93 |
+
font-weight: 400;
|
| 94 |
+
color: var(--text-dim);
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
/* ββ Device selector ββββββββββββββββββββββββββββββββββ */
|
| 98 |
+
.demo-device-selector {
|
| 99 |
+
display: flex;
|
| 100 |
+
justify-content: center;
|
| 101 |
+
margin-bottom: var(--space-md);
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
/* ββ Video with overlay arrows βββββββββββββββββββββββββ */
|
| 105 |
+
.demo-video-wrap {
|
| 106 |
+
position: relative;
|
| 107 |
+
border-radius: 8px;
|
| 108 |
+
overflow: hidden;
|
| 109 |
+
aspect-ratio: 16 / 9;
|
| 110 |
+
margin-bottom: var(--space-sm);
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
.demo-video {
|
| 114 |
+
position: absolute;
|
| 115 |
+
inset: 0;
|
| 116 |
+
width: 100%;
|
| 117 |
+
height: 100%;
|
| 118 |
+
object-fit: cover;
|
| 119 |
+
display: block;
|
| 120 |
+
transition: opacity 0.3s ease;
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
.demo-arrow {
|
| 124 |
+
position: absolute;
|
| 125 |
+
top: 50%;
|
| 126 |
+
transform: translateY(-50%);
|
| 127 |
+
z-index: 2;
|
| 128 |
+
width: 36px;
|
| 129 |
+
height: 36px;
|
| 130 |
+
display: flex;
|
| 131 |
+
align-items: center;
|
| 132 |
+
justify-content: center;
|
| 133 |
+
background: rgba(0, 0, 0, 0.7);
|
| 134 |
+
border: none;
|
| 135 |
+
border-radius: 50%;
|
| 136 |
+
color: rgba(255, 255, 255, 0.8);
|
| 137 |
+
font-size: var(--text-sm);
|
| 138 |
+
cursor: pointer;
|
| 139 |
+
transition: background 0.15s, color 0.15s;
|
| 140 |
+
font-family: inherit;
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
.demo-arrow-left {
|
| 144 |
+
left: var(--space-md);
|
| 145 |
+
}
|
| 146 |
+
|
| 147 |
+
.demo-arrow-right {
|
| 148 |
+
right: var(--space-md);
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
.demo-arrow:hover:not(:disabled) {
|
| 152 |
+
background: rgba(0, 0, 0, 0.6);
|
| 153 |
+
color: #fff;
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
.demo-arrow:disabled {
|
| 157 |
+
opacity: 0.25;
|
| 158 |
+
cursor: default;
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
/* ββ Card content ββββββββββββββββββββββββββββββββββββββ */
|
| 162 |
+
.demo-prompt {
|
| 163 |
+
font-size: var(--text-md);
|
| 164 |
+
color: var(--text-muted);
|
| 165 |
+
line-height: 1.6;
|
| 166 |
+
margin-bottom: var(--space-md);
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
.demo-prompt strong {
|
| 170 |
+
color: var(--text);
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
.demo-citation {
|
| 174 |
+
font-size: var(--text-sm);
|
| 175 |
+
color: var(--text-dim);
|
| 176 |
+
margin-top: var(--space-md);
|
| 177 |
+
line-height: 1.5;
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
.demo-citation a {
|
| 181 |
+
color: var(--text-dim);
|
| 182 |
+
text-decoration: underline;
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
.demo-citation a:hover {
|
| 186 |
+
color: var(--text-muted);
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
/* ββ Side-by-side outputs ββββββββββββββββββββββββββββββ */
|
| 190 |
+
.demo-outputs {
|
| 191 |
+
display: grid;
|
| 192 |
+
grid-template-columns: 1fr 1fr;
|
| 193 |
+
gap: var(--space-md);
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
.demo-output {
|
| 197 |
+
background: var(--bg);
|
| 198 |
+
border: 1px solid var(--border);
|
| 199 |
+
border-radius: 8px;
|
| 200 |
+
padding: var(--space-md);
|
| 201 |
+
line-height: 1.6;
|
| 202 |
+
font-size: var(--text-md);
|
| 203 |
+
color: var(--text);
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
.demo-output-model {
|
| 207 |
+
display: block;
|
| 208 |
+
font-size: var(--text-md);
|
| 209 |
+
font-weight: 600;
|
| 210 |
+
text-transform: uppercase;
|
| 211 |
+
letter-spacing: 0.06em;
|
| 212 |
+
color: var(--text-dim);
|
| 213 |
+
margin-bottom: var(--space-sm);
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
.demo-output-tps {
|
| 217 |
+
display: block;
|
| 218 |
+
font-size: var(--text-md);
|
| 219 |
+
font-weight: 700;
|
| 220 |
+
color: var(--text-dim);
|
| 221 |
+
margin-bottom: var(--space-sm);
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
.demo-output-tps.oom {
|
| 225 |
+
color: var(--red);
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
.demo-output-text {
|
| 229 |
+
color: var(--text-muted);
|
| 230 |
+
font-size: var(--text-md);
|
| 231 |
+
}
|
| 232 |
+
|
| 233 |
+
/* ββ Responsive ββββββββββββββββββββββββββββββββββββββββ */
|
| 234 |
+
@media (max-width: 768px) {
|
| 235 |
+
.demo-outputs {
|
| 236 |
+
grid-template-columns: 1fr;
|
| 237 |
+
}
|
| 238 |
+
|
| 239 |
+
.demo-title-row {
|
| 240 |
+
flex-direction: column;
|
| 241 |
+
align-items: flex-start;
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
.demo-arrow {
|
| 245 |
+
width: 28px;
|
| 246 |
+
height: 28px;
|
| 247 |
+
font-size: var(--text-sm);
|
| 248 |
+
}
|
| 249 |
+
}
|
demo/demo.js
ADDED
|
@@ -0,0 +1,448 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// ββ Output Example Demo renderer βββββββββββββββββββββββββββββββββββββββββββββ
|
| 2 |
+
// Supports multiple clips with left/right arrow navigation, device selector,
|
| 3 |
+
// and streaming text effect at real TPS speed.
|
| 4 |
+
|
| 5 |
+
const DEMO_DEVICE_LABELS = {
|
| 6 |
+
orin_nano_super: "Jetson Orin Nano Super",
|
| 7 |
+
agx_orin: "Jetson AGX Orin",
|
| 8 |
+
agx_thor: "Jetson AGX Thor",
|
| 9 |
+
};
|
| 10 |
+
|
| 11 |
+
const DEMO_DEFAULT_DEVICE = "agx_orin";
|
| 12 |
+
const DEMO_TOKENS_PER_WORD = 1.33;
|
| 13 |
+
|
| 14 |
+
// ββ CSV parser (minimal, demo-only) βββββββββββββββββββββββββββββββββββββββββ
|
| 15 |
+
|
| 16 |
+
function demoParseCsv(text) {
|
| 17 |
+
const lines = text.replace(/\r/g, "").trim().split("\n");
|
| 18 |
+
const headers = lines[0].split(",");
|
| 19 |
+
return lines.slice(1).map(line => {
|
| 20 |
+
const vals = line.split(",");
|
| 21 |
+
const row = {};
|
| 22 |
+
headers.forEach((h, i) => { row[h] = (vals[i] || "").trim(); });
|
| 23 |
+
return row;
|
| 24 |
+
});
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
// ββ Look up TPS from benchmark data βββββββββββββββββββββββββββββββββββββββββ
|
| 28 |
+
|
| 29 |
+
function demoLookupTps(csvRows, modelName, device, matchCriteria) {
|
| 30 |
+
const row = csvRows.find(r =>
|
| 31 |
+
r.model === modelName &&
|
| 32 |
+
r.device === device &&
|
| 33 |
+
Object.entries(matchCriteria).every(([col, val]) => r[col] === val)
|
| 34 |
+
);
|
| 35 |
+
if (row) {
|
| 36 |
+
if (!row.tps || row.tps.toUpperCase() === "OOM") return { found: true, tps: null };
|
| 37 |
+
return { found: true, tps: parseFloat(row.tps) };
|
| 38 |
+
}
|
| 39 |
+
const oomRow = csvRows.find(r =>
|
| 40 |
+
r.model === modelName &&
|
| 41 |
+
r.device === device &&
|
| 42 |
+
r.tps && r.tps.toUpperCase() === "OOM"
|
| 43 |
+
);
|
| 44 |
+
if (oomRow) return { found: true, tps: null };
|
| 45 |
+
return { found: false, tps: null };
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
// ββ Determine available devices from benchmark data βββββββββββββββββββββββββ
|
| 49 |
+
|
| 50 |
+
function demoAvailableDevices(csvRows, models, matchCriteria) {
|
| 51 |
+
const deviceSet = new Set();
|
| 52 |
+
csvRows.forEach(r => {
|
| 53 |
+
if (!models.includes(r.model)) return;
|
| 54 |
+
if (!Object.entries(matchCriteria).every(([col, val]) => r[col] === val)) return;
|
| 55 |
+
if (r.tps && r.tps.toUpperCase() !== "OOM") deviceSet.add(r.device);
|
| 56 |
+
});
|
| 57 |
+
return Object.keys(DEMO_DEVICE_LABELS).filter(d => deviceSet.has(d));
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
// ββ Streaming text animation ββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 61 |
+
|
| 62 |
+
function demoStreamText(element, fullText, tps) {
|
| 63 |
+
const handle = { _tid: null, cancel() { clearTimeout(this._tid); } };
|
| 64 |
+
element.textContent = "";
|
| 65 |
+
if (!tps || tps <= 0) {
|
| 66 |
+
element.textContent = fullText;
|
| 67 |
+
return handle;
|
| 68 |
+
}
|
| 69 |
+
const words = fullText.split(/(\s+)/);
|
| 70 |
+
const delayMs = 1000 / (tps / DEMO_TOKENS_PER_WORD);
|
| 71 |
+
let idx = 0;
|
| 72 |
+
|
| 73 |
+
function tick() {
|
| 74 |
+
if (idx < words.length) {
|
| 75 |
+
element.textContent += words[idx];
|
| 76 |
+
idx++;
|
| 77 |
+
handle._tid = setTimeout(tick, delayMs);
|
| 78 |
+
}
|
| 79 |
+
}
|
| 80 |
+
tick();
|
| 81 |
+
return handle;
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
// ββ Pre-calculate height for an output box to prevent layout shift ββββββββββ
|
| 85 |
+
|
| 86 |
+
function demoPreCalcHeight(box, textEl, fullText) {
|
| 87 |
+
textEl.textContent = fullText;
|
| 88 |
+
const h = box.offsetHeight;
|
| 89 |
+
box.style.minHeight = h + "px";
|
| 90 |
+
textEl.textContent = "";
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
// ββ Main render function ββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 94 |
+
|
| 95 |
+
async function renderDemo(data, container, optimizedOrg, dataFile, modelColors) {
|
| 96 |
+
if (!data) return;
|
| 97 |
+
|
| 98 |
+
// Normalise: support both old single-clip format and new clips array
|
| 99 |
+
let clips = data.clips;
|
| 100 |
+
if (!clips) {
|
| 101 |
+
clips = [{
|
| 102 |
+
video: data.video,
|
| 103 |
+
label: data.title,
|
| 104 |
+
prompt: data.prompt,
|
| 105 |
+
inference_setup: data.inference_setup,
|
| 106 |
+
citation: data.citation,
|
| 107 |
+
citation_url: data.citation_url,
|
| 108 |
+
outputs: data.outputs,
|
| 109 |
+
}];
|
| 110 |
+
}
|
| 111 |
+
if (!clips.length) return;
|
| 112 |
+
|
| 113 |
+
const matchCriteria = data.benchmark_match || {};
|
| 114 |
+
|
| 115 |
+
// Collect all model names across clips
|
| 116 |
+
const allModelNames = [...new Set(clips.flatMap(c => (c.outputs || []).map(o => o.model)))];
|
| 117 |
+
|
| 118 |
+
// Load benchmark CSV
|
| 119 |
+
let csvRows = [];
|
| 120 |
+
if (dataFile) {
|
| 121 |
+
try {
|
| 122 |
+
const csvResp = await fetch(dataFile);
|
| 123 |
+
if (csvResp.ok) csvRows = demoParseCsv(await csvResp.text());
|
| 124 |
+
} catch { /* proceed without */ }
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
const devices = csvRows.length
|
| 128 |
+
? demoAvailableDevices(csvRows, allModelNames, matchCriteria)
|
| 129 |
+
: [];
|
| 130 |
+
|
| 131 |
+
let selectedDevice = devices.includes(DEMO_DEFAULT_DEVICE)
|
| 132 |
+
? DEMO_DEFAULT_DEVICE
|
| 133 |
+
: (devices[0] || DEMO_DEFAULT_DEVICE);
|
| 134 |
+
|
| 135 |
+
let currentClipIdx = 0;
|
| 136 |
+
let activeStreams = [];
|
| 137 |
+
|
| 138 |
+
// ββ Build DOM skeleton ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 139 |
+
|
| 140 |
+
const section = document.createElement("div");
|
| 141 |
+
section.className = "demo-section";
|
| 142 |
+
|
| 143 |
+
// Toggle button
|
| 144 |
+
const toggle = document.createElement("button");
|
| 145 |
+
toggle.className = "demo-toggle";
|
| 146 |
+
toggle.innerHTML = '<span class="demo-toggle-arrow">▶</span> Show Demo';
|
| 147 |
+
|
| 148 |
+
// Collapsible wrapper
|
| 149 |
+
const content = document.createElement("div");
|
| 150 |
+
content.className = "demo-content";
|
| 151 |
+
const inner = document.createElement("div");
|
| 152 |
+
inner.className = "demo-content-inner";
|
| 153 |
+
|
| 154 |
+
const card = document.createElement("div");
|
| 155 |
+
card.className = "demo-card";
|
| 156 |
+
|
| 157 |
+
// Title row: h3 + device selector
|
| 158 |
+
const titleRow = document.createElement("div");
|
| 159 |
+
titleRow.className = "demo-title-row";
|
| 160 |
+
|
| 161 |
+
const titleLeft = document.createElement("div");
|
| 162 |
+
titleLeft.className = "demo-title-left";
|
| 163 |
+
|
| 164 |
+
const h3 = document.createElement("h3");
|
| 165 |
+
titleLeft.appendChild(h3);
|
| 166 |
+
|
| 167 |
+
const clipSetup = document.createElement("span");
|
| 168 |
+
clipSetup.className = "demo-clip-setup";
|
| 169 |
+
titleLeft.appendChild(clipSetup);
|
| 170 |
+
|
| 171 |
+
titleRow.appendChild(titleLeft);
|
| 172 |
+
|
| 173 |
+
const deviceSelector = document.createElement("div");
|
| 174 |
+
deviceSelector.className = "demo-device-selector";
|
| 175 |
+
|
| 176 |
+
if (devices.length > 1) {
|
| 177 |
+
const deviceGroup = document.createElement("div");
|
| 178 |
+
deviceGroup.className = "btn-group";
|
| 179 |
+
|
| 180 |
+
function renderDeviceButtons() {
|
| 181 |
+
deviceGroup.innerHTML = "";
|
| 182 |
+
devices.forEach(dev => {
|
| 183 |
+
const btn = document.createElement("button");
|
| 184 |
+
btn.className = "btn" + (dev === selectedDevice ? " active" : "");
|
| 185 |
+
btn.dataset.value = dev;
|
| 186 |
+
btn.textContent = DEMO_DEVICE_LABELS[dev] || dev;
|
| 187 |
+
deviceGroup.appendChild(btn);
|
| 188 |
+
});
|
| 189 |
+
}
|
| 190 |
+
renderDeviceButtons();
|
| 191 |
+
|
| 192 |
+
deviceGroup.addEventListener("click", e => {
|
| 193 |
+
const btn = e.target.closest(".btn");
|
| 194 |
+
if (!btn) return;
|
| 195 |
+
const newDevice = btn.dataset.value;
|
| 196 |
+
if (newDevice === selectedDevice) return;
|
| 197 |
+
selectedDevice = newDevice;
|
| 198 |
+
renderDeviceButtons();
|
| 199 |
+
renderOutputs();
|
| 200 |
+
});
|
| 201 |
+
|
| 202 |
+
deviceSelector.appendChild(deviceGroup);
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
card.appendChild(titleRow);
|
| 206 |
+
|
| 207 |
+
// Video with overlay arrows
|
| 208 |
+
const videoWrap = document.createElement("div");
|
| 209 |
+
videoWrap.className = "demo-video-wrap";
|
| 210 |
+
|
| 211 |
+
function makeVideoEl() {
|
| 212 |
+
const v = document.createElement("video");
|
| 213 |
+
v.loop = true;
|
| 214 |
+
v.muted = true;
|
| 215 |
+
v.playsInline = true;
|
| 216 |
+
v.preload = "auto";
|
| 217 |
+
v.className = "demo-video";
|
| 218 |
+
return v;
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
let videoA = makeVideoEl();
|
| 222 |
+
let videoB = makeVideoEl();
|
| 223 |
+
videoB.style.opacity = "0";
|
| 224 |
+
videoWrap.appendChild(videoA);
|
| 225 |
+
videoWrap.appendChild(videoB);
|
| 226 |
+
|
| 227 |
+
const arrowLeft = document.createElement("button");
|
| 228 |
+
arrowLeft.className = "demo-arrow demo-arrow-left";
|
| 229 |
+
arrowLeft.innerHTML = "◀";
|
| 230 |
+
arrowLeft.setAttribute("aria-label", "Previous clip");
|
| 231 |
+
|
| 232 |
+
const arrowRight = document.createElement("button");
|
| 233 |
+
arrowRight.className = "demo-arrow demo-arrow-right";
|
| 234 |
+
arrowRight.innerHTML = "▶";
|
| 235 |
+
arrowRight.setAttribute("aria-label", "Next clip");
|
| 236 |
+
|
| 237 |
+
videoWrap.appendChild(arrowLeft);
|
| 238 |
+
videoWrap.appendChild(arrowRight);
|
| 239 |
+
card.appendChild(videoWrap);
|
| 240 |
+
|
| 241 |
+
let isFirstClip = true;
|
| 242 |
+
|
| 243 |
+
// Device selector (centered under video)
|
| 244 |
+
card.appendChild(deviceSelector);
|
| 245 |
+
|
| 246 |
+
// Prompt (full width)
|
| 247 |
+
const promptEl = document.createElement("div");
|
| 248 |
+
promptEl.className = "demo-prompt";
|
| 249 |
+
card.appendChild(promptEl);
|
| 250 |
+
|
| 251 |
+
// Outputs grid
|
| 252 |
+
const grid = document.createElement("div");
|
| 253 |
+
grid.className = "demo-outputs";
|
| 254 |
+
card.appendChild(grid);
|
| 255 |
+
|
| 256 |
+
// Citation
|
| 257 |
+
const citeEl = document.createElement("p");
|
| 258 |
+
citeEl.className = "demo-citation";
|
| 259 |
+
card.appendChild(citeEl);
|
| 260 |
+
|
| 261 |
+
// ββ Arrow visibility ββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 262 |
+
|
| 263 |
+
function updateArrows() {
|
| 264 |
+
const showArrows = clips.length > 1;
|
| 265 |
+
arrowLeft.style.display = showArrows ? "" : "none";
|
| 266 |
+
arrowRight.style.display = showArrows ? "" : "none";
|
| 267 |
+
arrowLeft.disabled = false;
|
| 268 |
+
arrowRight.disabled = false;
|
| 269 |
+
}
|
| 270 |
+
|
| 271 |
+
arrowLeft.addEventListener("click", () => {
|
| 272 |
+
currentClipIdx = (currentClipIdx - 1 + clips.length) % clips.length;
|
| 273 |
+
renderClip();
|
| 274 |
+
});
|
| 275 |
+
arrowRight.addEventListener("click", () => {
|
| 276 |
+
currentClipIdx = (currentClipIdx + 1) % clips.length;
|
| 277 |
+
renderClip();
|
| 278 |
+
});
|
| 279 |
+
|
| 280 |
+
// ββ Render current clip βββββββββββββββββββββββββββββββββββββββββββοΏ½οΏ½β
|
| 281 |
+
|
| 282 |
+
function cancelStreams() {
|
| 283 |
+
activeStreams.forEach(s => s.cancel());
|
| 284 |
+
activeStreams = [];
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
function renderOutputs() {
|
| 288 |
+
cancelStreams();
|
| 289 |
+
grid.innerHTML = "";
|
| 290 |
+
const clip = clips[currentClipIdx];
|
| 291 |
+
const outputs = clip.outputs || [];
|
| 292 |
+
|
| 293 |
+
outputs.forEach(out => {
|
| 294 |
+
const color = modelColors && modelColors[out.model];
|
| 295 |
+
const borderColor = color ? color.border : null;
|
| 296 |
+
const box = document.createElement("div");
|
| 297 |
+
box.className = "demo-output";
|
| 298 |
+
if (borderColor) box.style.borderColor = borderColor;
|
| 299 |
+
|
| 300 |
+
const modelEl = document.createElement("span");
|
| 301 |
+
modelEl.className = "demo-output-model";
|
| 302 |
+
modelEl.textContent = out.model;
|
| 303 |
+
if (borderColor) modelEl.style.color = borderColor;
|
| 304 |
+
box.appendChild(modelEl);
|
| 305 |
+
|
| 306 |
+
const tpsEl = document.createElement("span");
|
| 307 |
+
tpsEl.className = "demo-output-tps";
|
| 308 |
+
if (borderColor) tpsEl.style.color = borderColor;
|
| 309 |
+
box.appendChild(tpsEl);
|
| 310 |
+
|
| 311 |
+
const textEl = document.createElement("span");
|
| 312 |
+
textEl.className = "demo-output-text";
|
| 313 |
+
box.appendChild(textEl);
|
| 314 |
+
|
| 315 |
+
grid.appendChild(box);
|
| 316 |
+
|
| 317 |
+
// Look up TPS
|
| 318 |
+
let tps = null;
|
| 319 |
+
if (csvRows.length) {
|
| 320 |
+
const lookup = demoLookupTps(csvRows, out.model, selectedDevice, matchCriteria);
|
| 321 |
+
tps = lookup.found ? lookup.tps : (out.tps || null);
|
| 322 |
+
} else {
|
| 323 |
+
tps = out.tps || null;
|
| 324 |
+
}
|
| 325 |
+
|
| 326 |
+
if (tps != null) {
|
| 327 |
+
tpsEl.textContent = tps.toFixed(2) + " Tokens / sec";
|
| 328 |
+
tpsEl.classList.remove("oom");
|
| 329 |
+
textEl.style.display = "";
|
| 330 |
+
demoPreCalcHeight(box, textEl, out.text);
|
| 331 |
+
const handle = demoStreamText(textEl, out.text, tps);
|
| 332 |
+
activeStreams.push(handle);
|
| 333 |
+
} else {
|
| 334 |
+
tpsEl.textContent = "OOM";
|
| 335 |
+
tpsEl.classList.add("oom");
|
| 336 |
+
tpsEl.style.color = "";
|
| 337 |
+
textEl.style.display = "none";
|
| 338 |
+
box.style.minHeight = "";
|
| 339 |
+
}
|
| 340 |
+
});
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
function renderClip() {
|
| 344 |
+
const clip = clips[currentClipIdx];
|
| 345 |
+
|
| 346 |
+
// Title: clip label + setup details
|
| 347 |
+
h3.textContent = clip.label || data.title || "Output Examples";
|
| 348 |
+
clipSetup.textContent = clip.inference_setup || "";
|
| 349 |
+
clipSetup.style.display = clip.inference_setup ? "" : "none";
|
| 350 |
+
|
| 351 |
+
// Video β crossfade: keep old visible, load new behind, fade when frame ready
|
| 352 |
+
if (clip.video) {
|
| 353 |
+
videoWrap.style.display = "";
|
| 354 |
+
if (isFirstClip) {
|
| 355 |
+
// First clip: just load directly into videoA
|
| 356 |
+
videoA.src = clip.video;
|
| 357 |
+
videoA.style.opacity = "1";
|
| 358 |
+
videoA.load();
|
| 359 |
+
videoA.addEventListener("loadeddata", () => videoA.play(), { once: true });
|
| 360 |
+
isFirstClip = false;
|
| 361 |
+
} else {
|
| 362 |
+
// Crossfade: videoA is the old (visible), videoB gets the new source
|
| 363 |
+
videoB.src = clip.video;
|
| 364 |
+
videoB.style.opacity = "0";
|
| 365 |
+
videoB.load();
|
| 366 |
+
|
| 367 |
+
videoB.addEventListener("loadeddata", function onLoaded() {
|
| 368 |
+
videoB.removeEventListener("loadeddata", onLoaded);
|
| 369 |
+
videoB.play();
|
| 370 |
+
|
| 371 |
+
// Wait for actual frame render before fading
|
| 372 |
+
const startFade = () => {
|
| 373 |
+
videoB.style.opacity = "1";
|
| 374 |
+
videoA.style.opacity = "0";
|
| 375 |
+
// After transition, pause old and swap roles
|
| 376 |
+
setTimeout(() => {
|
| 377 |
+
videoA.pause();
|
| 378 |
+
videoA.removeAttribute("src");
|
| 379 |
+
videoA.load();
|
| 380 |
+
// Swap roles
|
| 381 |
+
const tmp = videoA;
|
| 382 |
+
videoA = videoB;
|
| 383 |
+
videoB = tmp;
|
| 384 |
+
}, 300);
|
| 385 |
+
};
|
| 386 |
+
|
| 387 |
+
if (videoB.requestVideoFrameCallback) {
|
| 388 |
+
videoB.requestVideoFrameCallback(startFade);
|
| 389 |
+
} else {
|
| 390 |
+
startFade();
|
| 391 |
+
}
|
| 392 |
+
});
|
| 393 |
+
}
|
| 394 |
+
} else {
|
| 395 |
+
videoWrap.style.display = "none";
|
| 396 |
+
}
|
| 397 |
+
|
| 398 |
+
// Prompt
|
| 399 |
+
if (clip.prompt) {
|
| 400 |
+
promptEl.innerHTML = "<strong>Prompt:</strong> " + clip.prompt.replace(/\n/g, "<br>");
|
| 401 |
+
promptEl.style.display = "";
|
| 402 |
+
} else {
|
| 403 |
+
promptEl.style.display = "none";
|
| 404 |
+
}
|
| 405 |
+
|
| 406 |
+
// Citation
|
| 407 |
+
if (clip.citation) {
|
| 408 |
+
if (clip.citation_url) {
|
| 409 |
+
citeEl.innerHTML = clip.citation.replace(
|
| 410 |
+
/Autonomous Vehicle Domain Adaptation Gallery/,
|
| 411 |
+
`<a href="${clip.citation_url}" target="_blank" rel="noopener">Autonomous Vehicle Domain Adaptation Gallery</a>`
|
| 412 |
+
);
|
| 413 |
+
} else {
|
| 414 |
+
citeEl.textContent = clip.citation;
|
| 415 |
+
}
|
| 416 |
+
citeEl.style.display = "";
|
| 417 |
+
} else {
|
| 418 |
+
citeEl.style.display = "none";
|
| 419 |
+
}
|
| 420 |
+
|
| 421 |
+
// Outputs
|
| 422 |
+
renderOutputs();
|
| 423 |
+
|
| 424 |
+
updateArrows();
|
| 425 |
+
}
|
| 426 |
+
|
| 427 |
+
// ββ Toggle open/close βββββββββββββββββββββββββββββββββββββββββββββββ
|
| 428 |
+
|
| 429 |
+
toggle.addEventListener("click", () => {
|
| 430 |
+
const isOpen = content.classList.toggle("open");
|
| 431 |
+
toggle.classList.toggle("active", isOpen);
|
| 432 |
+
toggle.innerHTML = isOpen
|
| 433 |
+
? '<span class="demo-toggle-arrow">▶</span> Hide Demo'
|
| 434 |
+
: '<span class="demo-toggle-arrow">▶</span> Show Demo';
|
| 435 |
+
|
| 436 |
+
if (isOpen) {
|
| 437 |
+
renderClip();
|
| 438 |
+
} else {
|
| 439 |
+
cancelStreams();
|
| 440 |
+
}
|
| 441 |
+
});
|
| 442 |
+
|
| 443 |
+
inner.appendChild(card);
|
| 444 |
+
content.appendChild(inner);
|
| 445 |
+
section.appendChild(toggle);
|
| 446 |
+
section.appendChild(content);
|
| 447 |
+
container.appendChild(section);
|
| 448 |
+
}
|
index.html
CHANGED
|
@@ -4,7 +4,9 @@
|
|
| 4 |
<meta charset="utf-8"/>
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
| 6 |
<title>Edge Inference Benchmarks β Embedl</title>
|
|
|
|
| 7 |
<link rel="stylesheet" href="style.css"/>
|
|
|
|
| 8 |
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
|
| 9 |
</head>
|
| 10 |
<body>
|
|
@@ -31,6 +33,9 @@
|
|
| 31 |
<!-- Accuracy Table -->
|
| 32 |
<section id="accuracy-section"></section>
|
| 33 |
|
|
|
|
|
|
|
|
|
|
| 34 |
<!-- Filters -->
|
| 35 |
<section class="filters-bar" id="filters-bar"></section>
|
| 36 |
|
|
@@ -88,6 +93,7 @@
|
|
| 88 |
</div>
|
| 89 |
</footer>
|
| 90 |
|
|
|
|
| 91 |
<script src="app.js"></script>
|
| 92 |
</body>
|
| 93 |
</html>
|
|
|
|
| 4 |
<meta charset="utf-8"/>
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
| 6 |
<title>Edge Inference Benchmarks β Embedl</title>
|
| 7 |
+
<link rel="stylesheet" href="palette.css"/>
|
| 8 |
<link rel="stylesheet" href="style.css"/>
|
| 9 |
+
<link rel="stylesheet" href="demo/demo.css"/>
|
| 10 |
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
|
| 11 |
</head>
|
| 12 |
<body>
|
|
|
|
| 33 |
<!-- Accuracy Table -->
|
| 34 |
<section id="accuracy-section"></section>
|
| 35 |
|
| 36 |
+
<!-- Demo Section -->
|
| 37 |
+
<section id="demo-section"></section>
|
| 38 |
+
|
| 39 |
<!-- Filters -->
|
| 40 |
<section class="filters-bar" id="filters-bar"></section>
|
| 41 |
|
|
|
|
| 93 |
</div>
|
| 94 |
</footer>
|
| 95 |
|
| 96 |
+
<script src="demo/demo.js"></script>
|
| 97 |
<script src="app.js"></script>
|
| 98 |
</body>
|
| 99 |
</html>
|
palette.css
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* ββ Palette & Design Tokens ββββββββββββββββββββββββββββ */
|
| 2 |
+
:root {
|
| 3 |
+
/* Colors */
|
| 4 |
+
--bg: #0B1527;
|
| 5 |
+
--bg-opaque: rgba(11, 21, 39, 0.45);
|
| 6 |
+
--teal: #58b1c3;
|
| 7 |
+
--green: #6fcf97;
|
| 8 |
+
--red: #ff4d6d;
|
| 9 |
+
--pink: #ff6ec7;
|
| 10 |
+
--purple: #9b5de5;
|
| 11 |
+
--blue: #007F9E;
|
| 12 |
+
--neutral: #98A6AF;
|
| 13 |
+
--text: #e8e8e8;
|
| 14 |
+
--text-muted: #8899aa;
|
| 15 |
+
--text-dim: #5a6a7a;
|
| 16 |
+
--border: rgba(255, 255, 255, 0.15);
|
| 17 |
+
--btn-hover-bg: rgba(255, 255, 255, 0.03);
|
| 18 |
+
--btn-active-bg: rgba(88, 177, 195, 0.12);
|
| 19 |
+
--btn-active-border: rgba(88, 177, 195, 0.3);
|
| 20 |
+
--row-hover-bg: rgba(255, 255, 255, 0.02);
|
| 21 |
+
--row-border: rgba(255, 255, 255, 0.03);
|
| 22 |
+
--code-bg: rgba(88, 177, 195, 0.1);
|
| 23 |
+
--tooltip-bg: #2c3e50;
|
| 24 |
+
--tooltip-text: #f1f1f1;
|
| 25 |
+
--tooltip-body: #d9d9d9;
|
| 26 |
+
|
| 27 |
+
/* Typography scale */
|
| 28 |
+
--text-sm: 0.75rem; /* secondary text, small UI */
|
| 29 |
+
--text-md: 1rem; /* body text, subtitles, descriptions */
|
| 30 |
+
--text-lg: 1.25rem; /* section headings */
|
| 31 |
+
|
| 32 |
+
/* Spacing scale */
|
| 33 |
+
--space-xs: 0.25rem; /* tiny gaps: legend rows, label margins */
|
| 34 |
+
--space-sm: 0.5rem; /* small: icon gaps, dot gaps, table cell vertical */
|
| 35 |
+
--space-ms: 0.75rem; /* medium-small: table cell horizontal, sidebar padding */
|
| 36 |
+
--space-md: 1rem; /* standard: flex gaps, element margins */
|
| 37 |
+
--space-lg: 1.5rem; /* section internal spacing */
|
| 38 |
+
--space-xl: 2rem; /* major section padding */
|
| 39 |
+
--space-2xl: 3rem; /* large section breaks */
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
/* ββ Reset & Base Typography βββββββββββββββββββββββββββ */
|
| 43 |
+
*, *::before, *::after {
|
| 44 |
+
box-sizing: border-box;
|
| 45 |
+
margin: 0;
|
| 46 |
+
padding: 0;
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
html {
|
| 50 |
+
font-size: 20px;
|
| 51 |
+
background: radial-gradient(
|
| 52 |
+
60rem 40rem at 50% -45px,
|
| 53 |
+
rgba(0, 226, 213, 0.35),
|
| 54 |
+
rgba(18, 208, 254, 0.25) 30%,
|
| 55 |
+
transparent 70%
|
| 56 |
+
), var(--bg);
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
body {
|
| 60 |
+
font-family: "Montserrat", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
| 61 |
+
color: var(--text);
|
| 62 |
+
font-size: var(--text-md);
|
| 63 |
+
line-height: 1.6;
|
| 64 |
+
-webkit-font-smoothing: antialiased;
|
| 65 |
+
position: relative;
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
code {
|
| 69 |
+
background: var(--code-bg);
|
| 70 |
+
color: var(--teal);
|
| 71 |
+
padding: 0.15em 0.45em;
|
| 72 |
+
border-radius: 4px;
|
| 73 |
+
font-size: 0.95em;
|
| 74 |
+
}
|
style.css
CHANGED
|
@@ -1,64 +1,3 @@
|
|
| 1 |
-
/* ββ Palette ββββββββββββββββββββββββββββββββββββββββββββ */
|
| 2 |
-
:root {
|
| 3 |
-
--bg: #0B1527;
|
| 4 |
-
--bg-opaque: rgba(11, 21, 39, 0.45);
|
| 5 |
-
--bg-surface: #1a1a2e;
|
| 6 |
-
--teal: #58b1c3;
|
| 7 |
-
--green: #6fcf97;
|
| 8 |
-
--red: #ff4d6d;
|
| 9 |
-
--pink: #ff6ec7;
|
| 10 |
-
--purple: #9b5de5;
|
| 11 |
-
--blue: #007F9E;
|
| 12 |
-
--neutral: #98A6AF;
|
| 13 |
-
--text: #e8e8e8;
|
| 14 |
-
--text-muted: #8899aa;
|
| 15 |
-
--text-dim: #5a6a7a;
|
| 16 |
-
--border: rgba(255, 255, 255, 0.15);
|
| 17 |
-
--btn-hover-bg: rgba(255, 255, 255, 0.03);
|
| 18 |
-
--btn-active-bg: rgba(88, 177, 195, 0.12);
|
| 19 |
-
--btn-active-border: rgba(88, 177, 195, 0.3);
|
| 20 |
-
--sidebar-active-bg: rgba(88, 177, 195, 0.04);
|
| 21 |
-
--row-hover-bg: rgba(255, 255, 255, 0.02);
|
| 22 |
-
--row-border: rgba(255, 255, 255, 0.03);
|
| 23 |
-
--code-bg: rgba(88, 177, 195, 0.1);
|
| 24 |
-
--tooltip-bg: #2c3e50;
|
| 25 |
-
--tooltip-text: #f1f1f1;
|
| 26 |
-
--tooltip-body: #d9d9d9;
|
| 27 |
-
}
|
| 28 |
-
|
| 29 |
-
/* ββ Reset & Base βββββββββββββββββββββββββββββββββββββββ */
|
| 30 |
-
*, *::before, *::after {
|
| 31 |
-
box-sizing: border-box;
|
| 32 |
-
margin: 0;
|
| 33 |
-
padding: 0;
|
| 34 |
-
}
|
| 35 |
-
|
| 36 |
-
html {
|
| 37 |
-
font-size: 16px;
|
| 38 |
-
background: radial-gradient(
|
| 39 |
-
60rem 40rem at 50% -45px,
|
| 40 |
-
rgba(0, 226, 213, 0.35),
|
| 41 |
-
rgba(18, 208, 254, 0.25) 30%,
|
| 42 |
-
transparent 70%
|
| 43 |
-
), var(--bg);
|
| 44 |
-
}
|
| 45 |
-
|
| 46 |
-
body {
|
| 47 |
-
font-family: "Montserrat", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
| 48 |
-
color: var(--text);
|
| 49 |
-
line-height: 1.6;
|
| 50 |
-
-webkit-font-smoothing: antialiased;
|
| 51 |
-
position: relative;
|
| 52 |
-
}
|
| 53 |
-
|
| 54 |
-
code {
|
| 55 |
-
background: var(--code-bg);
|
| 56 |
-
color: var(--teal);
|
| 57 |
-
padding: 0.15em 0.45em;
|
| 58 |
-
border-radius: 4px;
|
| 59 |
-
font-size: 0.95em;
|
| 60 |
-
}
|
| 61 |
-
|
| 62 |
/* ββ Page Layout βββββββββββββββββββββββββββββββββββββββ */
|
| 63 |
.page {
|
| 64 |
position: relative;
|
|
@@ -66,7 +5,7 @@ code {
|
|
| 66 |
display: flex;
|
| 67 |
flex-direction: row;
|
| 68 |
min-height: 100vh;
|
| 69 |
-
max-width:
|
| 70 |
margin: 0 auto;
|
| 71 |
}
|
| 72 |
|
|
@@ -75,7 +14,7 @@ code {
|
|
| 75 |
width: 280px;
|
| 76 |
max-width: 300px;
|
| 77 |
flex-shrink: 0;
|
| 78 |
-
padding:
|
| 79 |
position: sticky;
|
| 80 |
top: 0;
|
| 81 |
height: 100vh;
|
|
@@ -84,7 +23,7 @@ code {
|
|
| 84 |
|
| 85 |
.sidebar-logo {
|
| 86 |
display: block;
|
| 87 |
-
padding: 0
|
| 88 |
}
|
| 89 |
|
| 90 |
.sidebar-nav {
|
|
@@ -94,8 +33,8 @@ code {
|
|
| 94 |
|
| 95 |
.sidebar-item {
|
| 96 |
display: block;
|
| 97 |
-
padding:
|
| 98 |
-
font-size:
|
| 99 |
font-weight: 400;
|
| 100 |
color: var(--text-dim);
|
| 101 |
text-decoration: none;
|
|
@@ -115,8 +54,8 @@ code {
|
|
| 115 |
|
| 116 |
.sidebar-variant {
|
| 117 |
display: block;
|
| 118 |
-
padding:
|
| 119 |
-
font-size:
|
| 120 |
font-weight: 400;
|
| 121 |
color: var(--text-dim);
|
| 122 |
cursor: pointer;
|
|
@@ -140,15 +79,14 @@ code {
|
|
| 140 |
}
|
| 141 |
|
| 142 |
.main-inner {
|
| 143 |
-
max-width: 1100px;
|
| 144 |
width: 100%;
|
| 145 |
-
padding: 0
|
| 146 |
}
|
| 147 |
|
| 148 |
/* ββ Hero ββββββββββββββββββββββββββββββββββββββββββββββ */
|
| 149 |
.hero {
|
| 150 |
position: relative;
|
| 151 |
-
padding:
|
| 152 |
}
|
| 153 |
|
| 154 |
.hero-badge {
|
|
@@ -156,16 +94,16 @@ code {
|
|
| 156 |
padding: 1px;
|
| 157 |
border-radius: 999px;
|
| 158 |
background: linear-gradient(90deg, var(--teal), var(--blue));
|
| 159 |
-
margin-bottom:
|
| 160 |
}
|
| 161 |
|
| 162 |
.hero-badge span {
|
| 163 |
display: inline-block;
|
| 164 |
-
padding:
|
| 165 |
border-radius: 999px;
|
| 166 |
background: var(--bg-opaque);
|
| 167 |
color: var(--teal);
|
| 168 |
-
font-size:
|
| 169 |
font-weight: 600;
|
| 170 |
letter-spacing: 0.03em;
|
| 171 |
text-transform: uppercase;
|
|
@@ -174,7 +112,7 @@ code {
|
|
| 174 |
.hero h1 {
|
| 175 |
font-size: clamp(1.8rem, 3.5vw, 3.0rem);
|
| 176 |
font-weight: 700;
|
| 177 |
-
margin-bottom:
|
| 178 |
letter-spacing: -0.02em;
|
| 179 |
}
|
| 180 |
|
|
@@ -184,8 +122,7 @@ code {
|
|
| 184 |
|
| 185 |
.hero-sub {
|
| 186 |
color: var(--text-muted);
|
| 187 |
-
font-size:
|
| 188 |
-
max-width: 620px;
|
| 189 |
line-height: 1.6;
|
| 190 |
}
|
| 191 |
|
|
@@ -193,19 +130,19 @@ code {
|
|
| 193 |
.filters-bar {
|
| 194 |
display: flex;
|
| 195 |
flex-wrap: wrap;
|
| 196 |
-
gap:
|
| 197 |
-
padding:
|
| 198 |
top: 0;
|
| 199 |
z-index: 10;
|
| 200 |
}
|
| 201 |
|
| 202 |
.filter-group label {
|
| 203 |
display: block;
|
| 204 |
-
font-size:
|
| 205 |
font-weight: 600;
|
| 206 |
letter-spacing: 0.08em;
|
| 207 |
color: var(--text-dim);
|
| 208 |
-
margin-bottom:
|
| 209 |
}
|
| 210 |
|
| 211 |
.btn-group {
|
|
@@ -216,9 +153,9 @@ code {
|
|
| 216 |
background: transparent;
|
| 217 |
border: 1px solid var(--border);
|
| 218 |
color: var(--text-muted);
|
| 219 |
-
font-size:
|
| 220 |
font-weight: 400;
|
| 221 |
-
padding:
|
| 222 |
cursor: pointer;
|
| 223 |
transition: all 0.15s;
|
| 224 |
}
|
|
@@ -250,21 +187,62 @@ code {
|
|
| 250 |
}
|
| 251 |
|
| 252 |
/* ββ Chart βββββββββββββββββββββββββββββββββββββββββββββ */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 253 |
.chart-block {
|
| 254 |
-
margin:
|
| 255 |
}
|
| 256 |
|
| 257 |
.chart-heading {
|
| 258 |
-
font-size:
|
| 259 |
font-weight: 600;
|
| 260 |
color: var(--text);
|
| 261 |
-
margin-bottom: 0
|
| 262 |
}
|
| 263 |
|
| 264 |
.chart-subtitle {
|
| 265 |
-
font-size:
|
| 266 |
color: var(--text-muted);
|
| 267 |
-
margin-bottom: 0
|
| 268 |
}
|
| 269 |
|
| 270 |
.chart-wrap {
|
|
@@ -273,15 +251,15 @@ code {
|
|
| 273 |
|
| 274 |
/* ββ Tables ββββββββββββββββββββββββββββββββββββββββββββ */
|
| 275 |
.table-card {
|
| 276 |
-
margin:
|
| 277 |
}
|
| 278 |
|
| 279 |
.table-card h3 {
|
| 280 |
-
font-size:
|
| 281 |
font-weight: 600;
|
| 282 |
color: var(--text);
|
| 283 |
-
margin-bottom:
|
| 284 |
-
padding-bottom:
|
| 285 |
border-bottom: 1px solid var(--border);
|
| 286 |
}
|
| 287 |
|
|
@@ -312,22 +290,22 @@ code {
|
|
| 312 |
table {
|
| 313 |
width: 100%;
|
| 314 |
border-collapse: collapse;
|
| 315 |
-
font-size:
|
| 316 |
}
|
| 317 |
|
| 318 |
thead th {
|
| 319 |
text-align: left;
|
| 320 |
font-weight: 600;
|
| 321 |
-
font-size:
|
| 322 |
letter-spacing: 0.05em;
|
| 323 |
color: var(--text-dim);
|
| 324 |
-
padding:
|
| 325 |
border-bottom: 1px solid var(--border);
|
| 326 |
white-space: nowrap;
|
| 327 |
}
|
| 328 |
|
| 329 |
tbody td {
|
| 330 |
-
padding:
|
| 331 |
border-bottom: 1px solid var(--row-border);
|
| 332 |
white-space: nowrap;
|
| 333 |
color: var(--text-muted);
|
|
@@ -349,7 +327,7 @@ tbody tr:hover {
|
|
| 349 |
.model-cell {
|
| 350 |
display: flex;
|
| 351 |
align-items: center;
|
| 352 |
-
gap:
|
| 353 |
font-weight: 500;
|
| 354 |
color: var(--text);
|
| 355 |
}
|
|
@@ -372,7 +350,7 @@ tbody tr:hover {
|
|
| 372 |
}
|
| 373 |
|
| 374 |
th.first-metric, td.first-metric {
|
| 375 |
-
padding-left:
|
| 376 |
}
|
| 377 |
|
| 378 |
.oom {
|
|
@@ -405,11 +383,15 @@ tbody tr.row-group-break td {
|
|
| 405 |
scrollbar-color: var(--text-dim) var(--border);
|
| 406 |
scrollbar-width: thin;
|
| 407 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 408 |
#accuracy-section:empty {
|
| 409 |
margin-bottom: 0;
|
| 410 |
}
|
| 411 |
#accuracy-section {
|
| 412 |
-
margin-bottom:
|
| 413 |
}
|
| 414 |
|
| 415 |
.acc-title-link {
|
|
@@ -422,15 +404,15 @@ tbody tr.row-group-break td {
|
|
| 422 |
}
|
| 423 |
|
| 424 |
.legend-section {
|
| 425 |
-
margin:
|
| 426 |
-
padding-top:
|
| 427 |
}
|
| 428 |
|
| 429 |
.legend-grid {
|
| 430 |
display: grid;
|
| 431 |
grid-template-columns: 1fr;
|
| 432 |
-
gap:
|
| 433 |
-
font-size:
|
| 434 |
color: var(--text-dim);
|
| 435 |
}
|
| 436 |
|
|
@@ -440,37 +422,37 @@ tbody tr.row-group-break td {
|
|
| 440 |
}
|
| 441 |
|
| 442 |
.experiment-setup {
|
| 443 |
-
margin-top:
|
| 444 |
-
font-size:
|
| 445 |
color: var(--text-dim);
|
| 446 |
font-style: italic;
|
| 447 |
line-height: 1.6;
|
| 448 |
}
|
| 449 |
|
| 450 |
.experiment-setup p {
|
| 451 |
-
margin-bottom:
|
| 452 |
}
|
| 453 |
|
| 454 |
/* ββ Footer ββββββββββββββββββββββββββββββββββββββββββββ */
|
| 455 |
.footer {
|
| 456 |
-
padding:
|
| 457 |
-
margin-top:
|
| 458 |
}
|
| 459 |
|
| 460 |
.footer-inner {
|
| 461 |
-
max-width:
|
| 462 |
margin: 0 auto;
|
| 463 |
-
padding: 0
|
| 464 |
}
|
| 465 |
|
| 466 |
.footer-grid {
|
| 467 |
display: flex;
|
| 468 |
-
gap:
|
| 469 |
flex-wrap: wrap;
|
| 470 |
}
|
| 471 |
|
| 472 |
.footer-col {
|
| 473 |
-
font-size:
|
| 474 |
color: var(--text-dim);
|
| 475 |
line-height: 1.7;
|
| 476 |
}
|
|
@@ -480,7 +462,7 @@ tbody tr.row-group-break td {
|
|
| 480 |
}
|
| 481 |
|
| 482 |
.footer-col-title {
|
| 483 |
-
font-size:
|
| 484 |
font-weight: 600;
|
| 485 |
text-transform: uppercase;
|
| 486 |
letter-spacing: 0.08em;
|
|
@@ -489,13 +471,13 @@ tbody tr.row-group-break td {
|
|
| 489 |
|
| 490 |
.footer-logo-img {
|
| 491 |
display: block;
|
| 492 |
-
margin-bottom:
|
| 493 |
}
|
| 494 |
|
| 495 |
.footer-logo {
|
| 496 |
-
font-size:
|
| 497 |
color: var(--text-muted);
|
| 498 |
-
margin-bottom:
|
| 499 |
}
|
| 500 |
|
| 501 |
.footer-logo a {
|
|
@@ -509,7 +491,7 @@ tbody tr.row-group-break td {
|
|
| 509 |
|
| 510 |
.footer-social {
|
| 511 |
display: flex;
|
| 512 |
-
gap:
|
| 513 |
}
|
| 514 |
|
| 515 |
.footer-social a {
|
|
@@ -524,7 +506,7 @@ tbody tr.row-group-break td {
|
|
| 524 |
}
|
| 525 |
|
| 526 |
/* ββ Responsive ββββββββββββββββββββββββββββββββββββββββ */
|
| 527 |
-
@media (max-width:
|
| 528 |
.page {
|
| 529 |
flex-direction: column;
|
| 530 |
}
|
|
@@ -536,20 +518,20 @@ tbody tr.row-group-break td {
|
|
| 536 |
position: static;
|
| 537 |
border-right: none;
|
| 538 |
border-bottom: 1px solid var(--border);
|
| 539 |
-
padding:
|
| 540 |
}
|
| 541 |
|
| 542 |
.sidebar-nav {
|
| 543 |
flex-direction: row;
|
| 544 |
flex-wrap: wrap;
|
| 545 |
-
|
|
|
|
| 546 |
}
|
| 547 |
|
| 548 |
.sidebar-item {
|
| 549 |
border-left: none;
|
| 550 |
border-bottom: 2px solid transparent;
|
| 551 |
-
padding:
|
| 552 |
-
font-size: 0.9rem;
|
| 553 |
}
|
| 554 |
|
| 555 |
.sidebar-item.active {
|
|
@@ -560,8 +542,8 @@ tbody tr.row-group-break td {
|
|
| 560 |
.sidebar-variant {
|
| 561 |
border-left: none;
|
| 562 |
border-bottom: 2px solid transparent;
|
| 563 |
-
padding:
|
| 564 |
-
font-size:
|
| 565 |
}
|
| 566 |
|
| 567 |
.sidebar-variant.active {
|
|
@@ -569,19 +551,15 @@ tbody tr.row-group-break td {
|
|
| 569 |
}
|
| 570 |
|
| 571 |
.main-inner {
|
| 572 |
-
padding: 0
|
| 573 |
}
|
| 574 |
|
| 575 |
-
.
|
| 576 |
-
|
| 577 |
}
|
| 578 |
|
| 579 |
-
.
|
| 580 |
-
|
| 581 |
-
padding: 0.35rem 0.7rem;
|
| 582 |
}
|
| 583 |
|
| 584 |
-
.chart-wrap {
|
| 585 |
-
height: 260px;
|
| 586 |
-
}
|
| 587 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
/* ββ Page Layout βββββββββββββββββββββββββββββββββββββββ */
|
| 2 |
.page {
|
| 3 |
position: relative;
|
|
|
|
| 5 |
display: flex;
|
| 6 |
flex-direction: row;
|
| 7 |
min-height: 100vh;
|
| 8 |
+
max-width: 1640px;
|
| 9 |
margin: 0 auto;
|
| 10 |
}
|
| 11 |
|
|
|
|
| 14 |
width: 280px;
|
| 15 |
max-width: 300px;
|
| 16 |
flex-shrink: 0;
|
| 17 |
+
padding: var(--space-xl) 0;
|
| 18 |
position: sticky;
|
| 19 |
top: 0;
|
| 20 |
height: 100vh;
|
|
|
|
| 23 |
|
| 24 |
.sidebar-logo {
|
| 25 |
display: block;
|
| 26 |
+
padding: 0 var(--space-md) var(--space-2xl);
|
| 27 |
}
|
| 28 |
|
| 29 |
.sidebar-nav {
|
|
|
|
| 33 |
|
| 34 |
.sidebar-item {
|
| 35 |
display: block;
|
| 36 |
+
padding: var(--space-sm) var(--space-md);
|
| 37 |
+
font-size: var(--text-lg);
|
| 38 |
font-weight: 400;
|
| 39 |
color: var(--text-dim);
|
| 40 |
text-decoration: none;
|
|
|
|
| 54 |
|
| 55 |
.sidebar-variant {
|
| 56 |
display: block;
|
| 57 |
+
padding: var(--space-sm) var(--space-md) var(--space-sm) var(--space-xl);
|
| 58 |
+
font-size: var(--text-md);
|
| 59 |
font-weight: 400;
|
| 60 |
color: var(--text-dim);
|
| 61 |
cursor: pointer;
|
|
|
|
| 79 |
}
|
| 80 |
|
| 81 |
.main-inner {
|
|
|
|
| 82 |
width: 100%;
|
| 83 |
+
padding: 0 var(--space-xl) var(--space-xl);
|
| 84 |
}
|
| 85 |
|
| 86 |
/* ββ Hero ββββββββββββββββββββββββββββββββββββββββββββββ */
|
| 87 |
.hero {
|
| 88 |
position: relative;
|
| 89 |
+
padding: var(--space-xl) 0;
|
| 90 |
}
|
| 91 |
|
| 92 |
.hero-badge {
|
|
|
|
| 94 |
padding: 1px;
|
| 95 |
border-radius: 999px;
|
| 96 |
background: linear-gradient(90deg, var(--teal), var(--blue));
|
| 97 |
+
margin-bottom: var(--space-md);
|
| 98 |
}
|
| 99 |
|
| 100 |
.hero-badge span {
|
| 101 |
display: inline-block;
|
| 102 |
+
padding: var(--space-sm) var(--space-md);
|
| 103 |
border-radius: 999px;
|
| 104 |
background: var(--bg-opaque);
|
| 105 |
color: var(--teal);
|
| 106 |
+
font-size: var(--text-md);
|
| 107 |
font-weight: 600;
|
| 108 |
letter-spacing: 0.03em;
|
| 109 |
text-transform: uppercase;
|
|
|
|
| 112 |
.hero h1 {
|
| 113 |
font-size: clamp(1.8rem, 3.5vw, 3.0rem);
|
| 114 |
font-weight: 700;
|
| 115 |
+
margin-bottom: var(--space-sm);
|
| 116 |
letter-spacing: -0.02em;
|
| 117 |
}
|
| 118 |
|
|
|
|
| 122 |
|
| 123 |
.hero-sub {
|
| 124 |
color: var(--text-muted);
|
| 125 |
+
font-size: var(--text-md);
|
|
|
|
| 126 |
line-height: 1.6;
|
| 127 |
}
|
| 128 |
|
|
|
|
| 130 |
.filters-bar {
|
| 131 |
display: flex;
|
| 132 |
flex-wrap: wrap;
|
| 133 |
+
gap: var(--space-md);
|
| 134 |
+
padding: var(--space-md) 0 var(--space-2xl);
|
| 135 |
top: 0;
|
| 136 |
z-index: 10;
|
| 137 |
}
|
| 138 |
|
| 139 |
.filter-group label {
|
| 140 |
display: block;
|
| 141 |
+
font-size: var(--text-sm);
|
| 142 |
font-weight: 600;
|
| 143 |
letter-spacing: 0.08em;
|
| 144 |
color: var(--text-dim);
|
| 145 |
+
margin-bottom: var(--space-xs);
|
| 146 |
}
|
| 147 |
|
| 148 |
.btn-group {
|
|
|
|
| 153 |
background: transparent;
|
| 154 |
border: 1px solid var(--border);
|
| 155 |
color: var(--text-muted);
|
| 156 |
+
font-size: var(--text-md);
|
| 157 |
font-weight: 400;
|
| 158 |
+
padding: var(--space-sm) var(--space-md);
|
| 159 |
cursor: pointer;
|
| 160 |
transition: all 0.15s;
|
| 161 |
}
|
|
|
|
| 187 |
}
|
| 188 |
|
| 189 |
/* ββ Chart βββββββββββββββββββββββββββββββββββββββββββββ */
|
| 190 |
+
.chart-header {
|
| 191 |
+
display: flex;
|
| 192 |
+
flex-wrap: wrap;
|
| 193 |
+
align-items: center;
|
| 194 |
+
justify-content: space-between;
|
| 195 |
+
gap: var(--space-sm) var(--space-md);
|
| 196 |
+
margin-bottom: var(--space-xs);
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
.chart-header-left {
|
| 200 |
+
display: flex;
|
| 201 |
+
align-items: baseline;
|
| 202 |
+
gap: var(--space-md);
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
.chart-header .filter-group {
|
| 206 |
+
margin: 0;
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
.chart-header .filter-group label {
|
| 210 |
+
display: none;
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
.chart-header .btn,
|
| 214 |
+
.demo-device-selector .btn {
|
| 215 |
+
border: 0;
|
| 216 |
+
background: transparent;
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
.chart-header .btn:hover,
|
| 220 |
+
.demo-device-selector .btn:hover {
|
| 221 |
+
color: var(--text);
|
| 222 |
+
background: transparent;
|
| 223 |
+
}
|
| 224 |
+
|
| 225 |
+
.chart-header .btn.active,
|
| 226 |
+
.demo-device-selector .btn.active {
|
| 227 |
+
background: transparent;
|
| 228 |
+
border-color: var(--btn-active-border);
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
.chart-block {
|
| 232 |
+
margin: var(--space-lg) 0;
|
| 233 |
}
|
| 234 |
|
| 235 |
.chart-heading {
|
| 236 |
+
font-size: var(--text-lg);
|
| 237 |
font-weight: 600;
|
| 238 |
color: var(--text);
|
| 239 |
+
margin-bottom: 0;
|
| 240 |
}
|
| 241 |
|
| 242 |
.chart-subtitle {
|
| 243 |
+
font-size: var(--text-md);
|
| 244 |
color: var(--text-muted);
|
| 245 |
+
margin-bottom: 0;
|
| 246 |
}
|
| 247 |
|
| 248 |
.chart-wrap {
|
|
|
|
| 251 |
|
| 252 |
/* ββ Tables ββββββββββββββββββββββββββββββββββββββββββββ */
|
| 253 |
.table-card {
|
| 254 |
+
margin: var(--space-lg) 0;
|
| 255 |
}
|
| 256 |
|
| 257 |
.table-card h3 {
|
| 258 |
+
font-size: var(--text-md);
|
| 259 |
font-weight: 600;
|
| 260 |
color: var(--text);
|
| 261 |
+
margin-bottom: var(--space-sm);
|
| 262 |
+
padding-bottom: var(--space-sm);
|
| 263 |
border-bottom: 1px solid var(--border);
|
| 264 |
}
|
| 265 |
|
|
|
|
| 290 |
table {
|
| 291 |
width: 100%;
|
| 292 |
border-collapse: collapse;
|
| 293 |
+
font-size: var(--text-md);
|
| 294 |
}
|
| 295 |
|
| 296 |
thead th {
|
| 297 |
text-align: left;
|
| 298 |
font-weight: 600;
|
| 299 |
+
font-size: var(--text-sm);
|
| 300 |
letter-spacing: 0.05em;
|
| 301 |
color: var(--text-dim);
|
| 302 |
+
padding: var(--space-sm) var(--space-ms);
|
| 303 |
border-bottom: 1px solid var(--border);
|
| 304 |
white-space: nowrap;
|
| 305 |
}
|
| 306 |
|
| 307 |
tbody td {
|
| 308 |
+
padding: var(--space-sm) var(--space-ms);
|
| 309 |
border-bottom: 1px solid var(--row-border);
|
| 310 |
white-space: nowrap;
|
| 311 |
color: var(--text-muted);
|
|
|
|
| 327 |
.model-cell {
|
| 328 |
display: flex;
|
| 329 |
align-items: center;
|
| 330 |
+
gap: var(--space-sm);
|
| 331 |
font-weight: 500;
|
| 332 |
color: var(--text);
|
| 333 |
}
|
|
|
|
| 350 |
}
|
| 351 |
|
| 352 |
th.first-metric, td.first-metric {
|
| 353 |
+
padding-left: var(--space-xl);
|
| 354 |
}
|
| 355 |
|
| 356 |
.oom {
|
|
|
|
| 383 |
scrollbar-color: var(--text-dim) var(--border);
|
| 384 |
scrollbar-width: thin;
|
| 385 |
}
|
| 386 |
+
|
| 387 |
+
.table-scroll-mobile {
|
| 388 |
+
display: none;
|
| 389 |
+
}
|
| 390 |
#accuracy-section:empty {
|
| 391 |
margin-bottom: 0;
|
| 392 |
}
|
| 393 |
#accuracy-section {
|
| 394 |
+
margin-bottom: var(--space-xl);
|
| 395 |
}
|
| 396 |
|
| 397 |
.acc-title-link {
|
|
|
|
| 404 |
}
|
| 405 |
|
| 406 |
.legend-section {
|
| 407 |
+
margin: var(--space-xl) 0;
|
| 408 |
+
padding-top: var(--space-lg);
|
| 409 |
}
|
| 410 |
|
| 411 |
.legend-grid {
|
| 412 |
display: grid;
|
| 413 |
grid-template-columns: 1fr;
|
| 414 |
+
gap: var(--space-xs);
|
| 415 |
+
font-size: var(--text-sm);
|
| 416 |
color: var(--text-dim);
|
| 417 |
}
|
| 418 |
|
|
|
|
| 422 |
}
|
| 423 |
|
| 424 |
.experiment-setup {
|
| 425 |
+
margin-top: var(--space-md);
|
| 426 |
+
font-size: var(--text-md);
|
| 427 |
color: var(--text-dim);
|
| 428 |
font-style: italic;
|
| 429 |
line-height: 1.6;
|
| 430 |
}
|
| 431 |
|
| 432 |
.experiment-setup p {
|
| 433 |
+
margin-bottom: var(--space-xs);
|
| 434 |
}
|
| 435 |
|
| 436 |
/* ββ Footer ββββββββββββββββββββββββββββββββββββββββββββ */
|
| 437 |
.footer {
|
| 438 |
+
padding: var(--space-xl) 0;
|
| 439 |
+
margin-top: var(--space-xl);
|
| 440 |
}
|
| 441 |
|
| 442 |
.footer-inner {
|
| 443 |
+
max-width: 1640px;
|
| 444 |
margin: 0 auto;
|
| 445 |
+
padding: 0 var(--space-xl);
|
| 446 |
}
|
| 447 |
|
| 448 |
.footer-grid {
|
| 449 |
display: flex;
|
| 450 |
+
gap: var(--space-2xl);
|
| 451 |
flex-wrap: wrap;
|
| 452 |
}
|
| 453 |
|
| 454 |
.footer-col {
|
| 455 |
+
font-size: var(--text-md);
|
| 456 |
color: var(--text-dim);
|
| 457 |
line-height: 1.7;
|
| 458 |
}
|
|
|
|
| 462 |
}
|
| 463 |
|
| 464 |
.footer-col-title {
|
| 465 |
+
font-size: var(--text-sm);
|
| 466 |
font-weight: 600;
|
| 467 |
text-transform: uppercase;
|
| 468 |
letter-spacing: 0.08em;
|
|
|
|
| 471 |
|
| 472 |
.footer-logo-img {
|
| 473 |
display: block;
|
| 474 |
+
margin-bottom: var(--space-sm);
|
| 475 |
}
|
| 476 |
|
| 477 |
.footer-logo {
|
| 478 |
+
font-size: var(--text-md);
|
| 479 |
color: var(--text-muted);
|
| 480 |
+
margin-bottom: var(--space-md);
|
| 481 |
}
|
| 482 |
|
| 483 |
.footer-logo a {
|
|
|
|
| 491 |
|
| 492 |
.footer-social {
|
| 493 |
display: flex;
|
| 494 |
+
gap: var(--space-md);
|
| 495 |
}
|
| 496 |
|
| 497 |
.footer-social a {
|
|
|
|
| 506 |
}
|
| 507 |
|
| 508 |
/* ββ Responsive ββββββββββββββββββββββββββββββββββββββββ */
|
| 509 |
+
@media (max-width: 900px) {
|
| 510 |
.page {
|
| 511 |
flex-direction: column;
|
| 512 |
}
|
|
|
|
| 518 |
position: static;
|
| 519 |
border-right: none;
|
| 520 |
border-bottom: 1px solid var(--border);
|
| 521 |
+
padding: var(--space-sm) 0;
|
| 522 |
}
|
| 523 |
|
| 524 |
.sidebar-nav {
|
| 525 |
flex-direction: row;
|
| 526 |
flex-wrap: wrap;
|
| 527 |
+
align-items: center;
|
| 528 |
+
padding: 0 var(--space-sm);
|
| 529 |
}
|
| 530 |
|
| 531 |
.sidebar-item {
|
| 532 |
border-left: none;
|
| 533 |
border-bottom: 2px solid transparent;
|
| 534 |
+
padding: var(--space-sm) var(--space-md);
|
|
|
|
| 535 |
}
|
| 536 |
|
| 537 |
.sidebar-item.active {
|
|
|
|
| 542 |
.sidebar-variant {
|
| 543 |
border-left: none;
|
| 544 |
border-bottom: 2px solid transparent;
|
| 545 |
+
padding: var(--space-sm) var(--space-md);
|
| 546 |
+
font-size: var(--text-sm);
|
| 547 |
}
|
| 548 |
|
| 549 |
.sidebar-variant.active {
|
|
|
|
| 551 |
}
|
| 552 |
|
| 553 |
.main-inner {
|
| 554 |
+
padding: 0 var(--space-md) var(--space-xl);
|
| 555 |
}
|
| 556 |
|
| 557 |
+
.table-split {
|
| 558 |
+
display: none;
|
| 559 |
}
|
| 560 |
|
| 561 |
+
.table-scroll-mobile {
|
| 562 |
+
display: block;
|
|
|
|
| 563 |
}
|
| 564 |
|
|
|
|
|
|
|
|
|
|
| 565 |
}
|