Make architecture connectors responsive
Browse files- index.html +59 -8
index.html
CHANGED
|
@@ -392,14 +392,14 @@
|
|
| 392 |
</div>
|
| 393 |
|
| 394 |
<div class="tr-architecture" role="img" aria-label="The contextual hidden state enters both a shared SwiGLU branch and two selected residual experts. Token identity controls only the fixed route table.">
|
| 395 |
-
<svg class="tr-lines"
|
| 396 |
-
<path
|
| 397 |
-
<path class="active"
|
| 398 |
-
<path class="active"
|
| 399 |
-
<path class="active"
|
| 400 |
-
<path
|
| 401 |
-
<path class="active"
|
| 402 |
-
<path class="active"
|
| 403 |
</svg>
|
| 404 |
|
| 405 |
<div class="tr-node tr-context">
|
|
@@ -585,13 +585,64 @@
|
|
| 585 |
|
| 586 |
const tabs = [...root.querySelectorAll(".tr-tab")];
|
| 587 |
const panels = [...root.querySelectorAll(".tr-view")];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 588 |
tabs.forEach((tab) => {
|
| 589 |
tab.addEventListener("click", () => {
|
| 590 |
tabs.forEach((item) => item.setAttribute("aria-selected", String(item === tab)));
|
| 591 |
panels.forEach((panel) => panel.classList.toggle("is-active", panel.dataset.panel === tab.dataset.view));
|
|
|
|
| 592 |
});
|
| 593 |
});
|
| 594 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 595 |
const updateArchitecture = (token) => {
|
| 596 |
const route = routes[token];
|
| 597 |
if (!route) return;
|
|
|
|
| 392 |
</div>
|
| 393 |
|
| 394 |
<div class="tr-architecture" role="img" aria-label="The contextual hidden state enters both a shared SwiGLU branch and two selected residual experts. Token identity controls only the fixed route table.">
|
| 395 |
+
<svg class="tr-lines" aria-hidden="true">
|
| 396 |
+
<path data-from=".tr-context" data-from-anchor="bottom" data-to=".tr-shared" data-to-anchor="top" fill="none" stroke="currentColor" stroke-width="1.5"/>
|
| 397 |
+
<path class="active" data-from=".tr-context" data-from-anchor="bottom" data-to=".tr-experts" data-to-anchor="top" fill="none" stroke="currentColor"/>
|
| 398 |
+
<path class="active" data-from=".tr-token" data-from-anchor="bottom" data-to=".tr-table" data-to-anchor="top" fill="none" stroke="currentColor"/>
|
| 399 |
+
<path class="active" data-from=".tr-table" data-from-anchor="left" data-to=".tr-experts" data-to-anchor="right" fill="none" stroke="currentColor" stroke-dasharray="7 7"/>
|
| 400 |
+
<path data-from=".tr-shared" data-from-anchor="bottom" data-to=".tr-sum" data-to-anchor="left" fill="none" stroke="currentColor" stroke-width="1.5"/>
|
| 401 |
+
<path class="active" data-from=".tr-experts" data-from-anchor="bottom" data-to=".tr-sum" data-to-anchor="top" fill="none" stroke="currentColor"/>
|
| 402 |
+
<path class="active" data-from=".tr-sum" data-from-anchor="right" data-to=".tr-output" data-to-anchor="left" fill="none" stroke="currentColor"/>
|
| 403 |
</svg>
|
| 404 |
|
| 405 |
<div class="tr-node tr-context">
|
|
|
|
| 585 |
|
| 586 |
const tabs = [...root.querySelectorAll(".tr-tab")];
|
| 587 |
const panels = [...root.querySelectorAll(".tr-view")];
|
| 588 |
+
const architecture = root.querySelector(".tr-architecture");
|
| 589 |
+
const lineLayer = root.querySelector(".tr-lines");
|
| 590 |
+
|
| 591 |
+
const anchorPoint = (element, anchor, bounds) => {
|
| 592 |
+
const rect = element.getBoundingClientRect();
|
| 593 |
+
const points = {
|
| 594 |
+
top: { x: rect.left + rect.width / 2, y: rect.top },
|
| 595 |
+
right: { x: rect.right, y: rect.top + rect.height / 2 },
|
| 596 |
+
bottom: { x: rect.left + rect.width / 2, y: rect.bottom },
|
| 597 |
+
left: { x: rect.left, y: rect.top + rect.height / 2 }
|
| 598 |
+
};
|
| 599 |
+
const point = points[anchor] || points.bottom;
|
| 600 |
+
return { x: point.x - bounds.left, y: point.y - bounds.top };
|
| 601 |
+
};
|
| 602 |
+
|
| 603 |
+
const updateConnectorPaths = () => {
|
| 604 |
+
if (!architecture || !lineLayer || !architecture.offsetParent) return;
|
| 605 |
+
const bounds = architecture.getBoundingClientRect();
|
| 606 |
+
if (!bounds.width || !bounds.height) return;
|
| 607 |
+
lineLayer.setAttribute("viewBox", `0 0 ${bounds.width} ${bounds.height}`);
|
| 608 |
+
lineLayer.querySelectorAll("[data-from][data-to]").forEach((path) => {
|
| 609 |
+
const from = architecture.querySelector(path.dataset.from);
|
| 610 |
+
const to = architecture.querySelector(path.dataset.to);
|
| 611 |
+
if (!from || !to) return;
|
| 612 |
+
const start = anchorPoint(from, path.dataset.fromAnchor, bounds);
|
| 613 |
+
const end = anchorPoint(to, path.dataset.toAnchor, bounds);
|
| 614 |
+
const dx = end.x - start.x;
|
| 615 |
+
const dy = end.y - start.y;
|
| 616 |
+
let d;
|
| 617 |
+
if (Math.abs(dx) > Math.abs(dy)) {
|
| 618 |
+
const direction = Math.sign(dx) || 1;
|
| 619 |
+
const bend = Math.max(32, Math.abs(dx) * 0.42);
|
| 620 |
+
d = `M ${start.x} ${start.y} C ${start.x + direction * bend} ${start.y}, ${end.x - direction * bend} ${end.y}, ${end.x} ${end.y}`;
|
| 621 |
+
} else {
|
| 622 |
+
const direction = Math.sign(dy) || 1;
|
| 623 |
+
const bend = Math.max(32, Math.abs(dy) * 0.42);
|
| 624 |
+
d = `M ${start.x} ${start.y} C ${start.x} ${start.y + direction * bend}, ${end.x} ${end.y - direction * bend}, ${end.x} ${end.y}`;
|
| 625 |
+
}
|
| 626 |
+
path.setAttribute("d", d);
|
| 627 |
+
});
|
| 628 |
+
};
|
| 629 |
+
|
| 630 |
tabs.forEach((tab) => {
|
| 631 |
tab.addEventListener("click", () => {
|
| 632 |
tabs.forEach((item) => item.setAttribute("aria-selected", String(item === tab)));
|
| 633 |
panels.forEach((panel) => panel.classList.toggle("is-active", panel.dataset.panel === tab.dataset.view));
|
| 634 |
+
if (tab.dataset.view === "overview") requestAnimationFrame(updateConnectorPaths);
|
| 635 |
});
|
| 636 |
});
|
| 637 |
|
| 638 |
+
if (architecture && "ResizeObserver" in window) {
|
| 639 |
+
const connectorResizeObserver = new ResizeObserver(() => requestAnimationFrame(updateConnectorPaths));
|
| 640 |
+
connectorResizeObserver.observe(architecture);
|
| 641 |
+
}
|
| 642 |
+
window.addEventListener("resize", updateConnectorPaths);
|
| 643 |
+
document.fonts?.ready.then(updateConnectorPaths);
|
| 644 |
+
requestAnimationFrame(updateConnectorPaths);
|
| 645 |
+
|
| 646 |
const updateArchitecture = (token) => {
|
| 647 |
const route = routes[token];
|
| 648 |
if (!route) return;
|