PageSourceSearch

https://zoushilong1024.github.io/Pelican-Sim1.0/assets/js/page-ui.js

js zoushilong1024.github.io collected 2026-10-03 10:15:41 UTC 7,812 bytes, 154 lines download raw bytes

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.