Sfoglia il codice sorgente

web: 8-port switch layout + toml viewer for no-run recipes

- topology: the 200G fabric switch drew every port on the top edge at
  80px pitch — with 8 nodes (8x cluster) ports 5-8 landed at x 709-949,
  off the box (and off the cables). Now 4+4 across the top/bottom edges
  (the box fits 4 per edge), 10g row-1 cables and 200g row-1 cables
  terminate on the switch bottom edge (346). Probed: 8 fab + 8 lan
  ports in-box, all cable endpoints on switch edges, 4-port case
  unchanged; playwright gate green.
- report/recipes view: the 'toml' viewer link only existed when the
  recipe had run reports ('no reports' rows had no way to read the
  recipe). The link now always renders; no-run rows show 'toml + no
  reports'. /r/<recipe>/recipe.toml already resolved from the recipe
  dir root (no traversal), verified 200 for a no-reports recipe.
Alexander Korolev 1 mese fa
parent
commit
6813ba085c
2 ha cambiato i file con 15 aggiunte e 8 eliminazioni
  1. 1 1
      src/report/page.html
  2. 14 7
      src/web/ui.html

+ 1 - 1
src/report/page.html

@@ -493,7 +493,7 @@ function render() {
           `<a class="open" href="/r/${encodeURIComponent(r.name)}/${encodeURIComponent(f)}" target="_blank" title="open ${esc(f)}">${esc(f.split('.').pop())}</a>`).join(" ")}</span> ` +
         `${esc(e.date)}${e.time ? "·" + esc(e.time) : ""} ${esc(e.label)} — ${e.pct != null ? esc(e.pct) + "/100" : '<span class="dim">no score</span>'}${e.points ? " (" + esc(e.points) + ")" : ""}${ts}${chip}`;
       }).join("<br>");
-      out.push(`<tr class="hist"><td colspan="${vis.length}">${r.reports.length ? toml + " " + lines : "no reports"}</td></tr>`);
+      out.push(`<tr class="hist"><td colspan="${vis.length}">${toml}${r.reports.length ? " " + lines : ' <span class="dim">no reports</span>'}</td></tr>`);
     }
   }
   document.querySelector("tbody").innerHTML = out.join("");

+ 14 - 7
src/web/ui.html

@@ -986,11 +986,16 @@ function tgCablePath(l, t) {
     }
     return `M ${b.dcx} ${b.y} C ${b.dcx} ${b.y - 50} ${px} ${b.y - 80} ${px} 266`;
   }
-  if (l.kind === "200g") { // row0: device bottom → fabric switch top
-    const ex = b.dcx - 40, ey = b.y + b.h;
+  if (l.kind === "200g") {
     const fi = t.links.filter((x) => x.kind === "200g").findIndex((x) => x.b === l.b);
-    const px = 396 + fi * 80;
-    return `M ${ex} ${ey} C ${ex} ${ey + 76} ${px} ${ey + 66} ${px} 290`;
+    const px = 396 + (fi % 4) * 80;
+    if (bi < 4) { // row0: device bottom → fabric switch top
+      const ex = b.dcx - 40, ey = b.y + b.h;
+      return `M ${ex} ${ey} C ${ex} ${ey + 76} ${px} ${ey + 66} ${px} 290`;
+    }
+    // row1: device top → fabric switch bottom
+    const ex = b.dcx - 40, ey = b.y;
+    return `M ${ex} ${ey} C ${ex} ${ey - 76} ${px} ${ey - 66} ${px} 346`;
   }
   // dac: arc between adjacent device tops
   const ai = tgIdx(t, l.a);
@@ -1025,8 +1030,10 @@ function swSvg(sw, entries) {
   const y = ib ? 290 : 210;
   const sub = ib ? "200G fabric · HA" : "passive · no HA";
   const step = ib ? 80 : 38;
-  const rows = entries.map((p) => `<rect class="port" data-${ib ? "fab" : "lan"}port="${esc(p.name)}"
-    x="${396 + p.i * step - 7}" y="${p.top ? y + 2 : y + 49}" width="14" height="7" rx="2"/>`).join("");
+  // ib ports sit 4+4 across two edges (the box fits 4 per edge at step 80);
+  // lan ports keep the continuous index (step 38 fits 8 across 2 rows)
+  const rows = entries.map((p) => { const col = ib ? p.i % 4 : p.i; return `<rect class="port" data-${ib ? "fab" : "lan"}port="${esc(p.name)}"
+    x="${396 + col * step - 7}" y="${p.top ? y + 2 : y + 49}" width="14" height="7" rx="2"/>`; }).join("");
   return `<g class="sw ${sw.kind}" data-sw="${esc(sw.entity)}" transform="translate(0,0)">
     <title>${esc(sw.entity)}</title>
     <rect class="swbox" x="370" y="${y}" width="320" height="56" rx="8"/>
@@ -1162,7 +1169,7 @@ function cablesSvg(t) {
 function switchesSvg(t) {
   const fi = {};
   const fabEntries = t.links.filter((l) => l.kind === "200g").map((l) => ({
-    name: l.b, i: (fi[l.b] = fi[l.b] ?? Object.keys(fi).length), top: true,
+    name: l.b, i: (fi[l.b] = fi[l.b] ?? Object.keys(fi).length), top: fi[l.b] < 4,
   }));
   const lanEntries = t.nodes.map((n, i) => ({ name: n, i, top: i < 4 }));
   const glyphs = t.switches.map((s) => s.kind === "aux" ? fanSvg(s) : swSvg(s, fabEntries)).join("");