1import { enableTabKeyboard } from "./tab-keyboard.js"; 2 3export function initPageUI() { 4 const appTabs = [...document.querySelectorAll(".app-tab")]; 5 const applicationKicker = document.getElementById("application-kicker"); 6 const applicationTitle = document.getElementById("application-title"); 7 const applicationDescription = document.getElementById("application-description"); 8 const applicationMedia = document.getElementById("application-media"); 9 const workflow = document.getElementById("application-workflow"); 10 const chartTitle = document.getElementById("application-chart-title"); 11 const chartBars = document.getElementById("application-chart-bars"); 12 const workflowResult = document.getElementById("application-workflow-result"); 13 // Paper Tables 9, 11, 12 and 14. 14 const workflows = { 15 data: { 16 chart: "Ï0.5 success rate (%) â", 17 scale: 100, 18 unit: "%", 19 highlights: [1, 3, 5], 20 rows: [ 21 ["10 original · 0 generated · 10 total", 28.5], 22 ["10 original · 100 generated · 110 total", 64.5], 23 ["30 original · 0 generated · 30 total", 57.0], 24 ["30 original · 300 generated · 330 total", 87.0], 25 ["50 original · 0 generated · 50 total", 70.0], 26 ["50 original · 500 generated · 550 total", 93.0], 27 ], 28 result: "+36.0 / +30.0 / +23.0 percentage points at 10 / 30 / 50 original trajectories per task.", 29 }, 30 evaluation: { 31 chart: "Policy-evaluation agreement · Pearson r â", 32 scale: 1, 33 unit: "", 34 decimals: 4, 35 rows: [["0 adaptation rollouts", 0.9020], ["100 rollouts", 0.9580], ["200 rollouts", 0.9760], ["500 rollouts", 0.9890], ["1,000 rollouts", 0.9940]], 36 result: "Perfect ranking agreement at 200 adaptation rollouts · Spearman Ï = 1.0 across five checkpoints.", 37 }, 38 selection: { 39 chart: "RoboTwin success rate (%) â", 40 scale: 100, 41 unit: "%", 42 rows: [["Single sample", 43.2], ["Random-of-10", 43.5], ["VLM-direct", 48.7], ["Ctrl-World", 52.3], ["Pelican-Sim 1.0", 63.8]], 43 result: "+20.6 percentage points over single-sample execution.", 44 }, 45 learning: { 46 chart: "RoboTwin success rate (%) â", 47 scale: 100, 48 unit: "%", 49 rows: [["Initial SFT", 62.7], ["Iter-SFT", 67.0], ["GRPO · terminal only", 68.5], ["Pelican-Sim 1.0 · full GRPO", 75.4]], 50 result: "+12.7 percentage points over the initial policy.", 51 }, 52 }; 53 const setApplicationPanel = (btn) => {
54 appTabs.forEach(tab => { 55 tab.setAttribute("aria-selected", String(tab === btn)); 56 tab.tabIndex = tab === btn ? 0 : -1; 57 }); 58 document.getElementById("application-panel").setAttribute("aria-labelledby", btn.id); 59 applicationKicker.textContent = btn.dataset.kicker; 60 applicationTitle.textContent = btn.dataset.title; 61 applicationDescription.textContent = btn.dataset.description; 62 workflow.classList.remove("hidden"); 63 applicationMedia.classList.remove("hidden"); 64 applicationMedia.classList.add("has-media"); 65 { 66 const content = workflows[btn.dataset.app]; 67 chartTitle.textContent = content.chart; 68 if (btn.dataset.app === "data") { 69 chartBars.innerHTML = renderDataColumns(content.rows); 70 } else { 71 chartBars.replaceChildren(...content.rows.map(([label, value], index) => { 72 const row = document.createElement("div"); 73 row.className = "application-chart-row"; 74 if ((content.highlights ?? [content.rows.length - 1]).includes(index)) row.classList.add("is-highlight"); 75 const heading = document.createElement("div"); 76 heading.className = "application-chart-label"; 77 const name = document.createElement("span"); 78 name.textContent = label; 79 const number = document.createElement("strong"); 80 number.textContent = `${value.toFixed(content.decimals ?? 1)}${content.unit}`; 81 heading.append(name, number); 82 const track = document.createElement("div"); 83 track.className = "application-chart-track"; 84 track.setAttribute("aria-hidden", "true"); 85 const fill = document.createElement("div"); 86 fill.className = "application-chart-fill"; 87 fill.style.width = `${value / content.scale * 100}%`; 88 fill.style.animationDelay = `${index * 60}ms`; 89 track.append(fill); 90 row.append(heading, track); 91 return row; 92 })); 93 } 94 workflowResult.textContent = content.result; 95 } 96 };
97 appTabs.forEach(btn => btn.addEventListener("click", () => setApplicationPanel(btn))); 98 enableTabKeyboard(appTabs); 99 setApplicationPanel(appTabs.find(tab => tab.getAttribute("aria-selected") === "true") ?? appTabs[0]); 100 101 const navLinks = [...document.querySelectorAll(".nav-link")]; 102 const sections = [...document.querySelectorAll("main > section[id]")]; 103 const nav = document.querySelector("body > nav"); 104 let navigationFrame = null; 105 const updateNavigation = () => { 106 const threshold = nav.getBoundingClientRect().height + 130; 107 const active = sections.filter(section => section.getBoundingClientRect().top <= threshold).at(-1); 108 navLinks.forEach(link => { 109 const sectionId = active?.id; 110 if (sectionId && link.hash === `#${sectionId}`) link.setAttribute("aria-current", "true"); 111 else link.removeAttribute("aria-current"); 112 }); 113 navigationFrame = null; 114 }; 115 const scheduleNavigation = () => { 116 if (navigationFrame === null) navigationFrame = requestAnimationFrame(updateNavigation); 117 }; 118 window.addEventListener("scroll", scheduleNavigation, { passive: true }); 119 window.addEventListener("resize", scheduleNavigation); 120 updateNavigation(); 121 122 const copyBtn = document.getElementById("copy"); 123 copyBtn.addEventListener("click", async () => { 124 try { 125 await navigator.clipboard.writeText(document.getElementById("bibtex").innerText); 126 copyBtn.textContent = "Copied"; 127 } catch { 128 copyBtn.textContent = "Select citation to copy"; 129 } 130 setTimeout(() => { copyBtn.textContent = "Copy citation"; }, 1500); 131 }); 132} 133 134// A shared zero baseline makes augmentation gains comparable across budgets. 135function renderDataColumns(rows) { 136 const ticks = [0, 25, 50, 75, 100].map(value => { 137 const y = 256 - value * 2; 138 return `<line x1="42" x2="502" y1="${y}" y2="${y}" stroke="#e3e9f1"/><text x="32" y="${y + 4}" text-anchor="end">${value}</text>`; 139 }).join(""); 140 const groups = [10, 30, 50].map((budget, i) => { 141 const x = 114 + i * 155; 142 const bars = [0, 1].map(j => { 143 const [label, value] = rows[i * 2 + j]; 144 const bx = x - 44 + j * 47; 145 const height = value * 2; 146 return `<g><title>${label}: ${value.toFixed(1)}%</title><rect class="data-column-bar" style="animation-delay: ${(i * 2 + j) * 60}ms" x="${bx}" y="${256 - height}" width="36" height="${height}" rx="3" fill="${j ? "#2563eb" : "#a9b5c5"}"/><text class="column-value" x="${bx + 18}" y="${248 - height}" text-anchor="middle">${value.toFixed(1)}</text></g>`; 147 }).join(""); 148 return `${bars}<text class="column-budget" x="${x}" y="280" text-anchor="middle">${budget} originals</text><text x="${x}" y="301" text-anchor="middle">+${budget * 10} generated</text>`; 149 }).join(""); 150 return `<svg class="data-column-chart" viewBox="0 0 520 330" role="img" aria-label="Table 9: original-only versus Pelican-Sim augmentation success. At 10 originals: 28.5 versus 64.5 percent; at 30: 57.0 versus 87.0; at 50: 70.0 versus 93.0."> 151 <g class="column-legend"><rect x="55" y="8" width="12" height="12" rx="2" fill="#a9b5c5"/><text x="74" y="19">Original only</text><rect x="230" y="8" width="12" height="12" rx="2" fill="#2563eb"/><text x="249" y="19">Original + Pelican-Sim 1.0</text></g> 152 ${ticks}${groups}<text x="272" y="325" text-anchor="middle">Trajectories per task</text> 153 </svg>`; 154}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.