feat(dossiers-ngi): sticky bar vs-main compact dates tabular wider tree

This commit is contained in:
Hernâni Marques 2026-09-30 23:23:14 +02:00
parent 52e5c1d328
commit da6f384399
No known key found for this signature in database
6 changed files with 231 additions and 192 deletions

View file

@ -58,7 +58,7 @@ Per branch in JSON / sticky bar:
| `tip_time` | committer time of tip (`%cI`) | | `tip_time` | committer time of tip (`%cI`) |
| `built_at` | when this edition freeze was written | | `built_at` | when this edition freeze was written |
| `build_seconds` | wall time of that freeze | | `build_seconds` | wall time of that freeze |
| `vs_parent_plus` / `vs_parent_minus` | `git diff --numstat` tip vs parent tip (sticky bar +grün/−rot; omitted when no parent) | | `vs_main_plus` / `vs_main_minus` | `git diff --numstat` tip vs **main** tip (sticky bar +grün/−rot; omitted on `main` itself) |
Rebuild runs **only when** `.edition-tip` ≠ current tip (timer never forces). Rebuild runs **only when** `.edition-tip` ≠ current tip (timer never forces).
Each run also: `git fetch --prune`, `worktree prune`, drop stale Each run also: `git fetch --prune`, `worktree prune`, drop stale
@ -105,13 +105,13 @@ rsync -avz --exclude .DS_Store "$SITE/branch-status.json" "$WWW/branch-status.js
`bar.js` fetches `/branch-status.json` and draws: `bar.js` fetches `/branch-status.json` and draws:
``` ```
main @ d2fbd79 · tip 2026-09-01 16:52 · build 8.4s · at 2026-09-30 15:23 main sep01 16:52 8.4s sep30 15:23
├── feat/sm-content @ … · tip YYYY-MM-DD HH:MM · … ├── feat/sm-content sep30 16:11 13s sep30 15:23 +N −M
│ └── … │ └── feat/website-features …
└── project_template @ … └── project_template sep28 15:03 2.2s sep30 15:23 +N −M
``` ```
`tip` / `at` always include **date + HH:MM** (ISO already in JSON; clock-only was misleading across days). The bar is **viewport-centered** (max-width 45em, mobile-friendly). Poll every 30s **updates tip/build/at text in place** when the branch structure is unchanged (no full tree remount). Visible cells: name (tree prefix + branch) · tip `monDD HH:MM` · `Ns monDD HH:MM` (build seconds + freeze time) · `+N −M` vs main. No `@sha`, no `default` label (`data-default` stays for flask/cookie). The bar is **viewport-centered**, inner tree `max-width: min(80rem, 100vw − 1.5rem)` (was 45em at 11px ≈ 495px, which clipped long names). Rear cells are right-aligned `tabular-nums`. Poll every 30s **updates when/built/diff in place** when the branch structure is unchanged (no full tree remount).
Each name links to `/_cascade/use/<edition>` (cookie + 302 `/`). Caddy map: Each name links to `/_cascade/use/<edition>` (cookie + 302 `/`). Caddy map:

View file

@ -235,11 +235,11 @@ def tip_recorded(editions: Path, slug: str) -> str:
def diff_numstat(repo: Path, parent_tip: str, tip: str) -> tuple[int, int]: def diff_numstat(repo: Path, a: str, b: str) -> tuple[int, int]:
"""Insertions/deletions of tip vs parent tip (git diff --numstat).""" """Insertions/deletions of b vs a (git diff --numstat)."""
if not parent_tip or not tip or parent_tip == tip: if not a or not b or a == b:
return 0, 0 return 0, 0
cp = git(repo, "diff", "--numstat", parent_tip, tip, check=False) cp = git(repo, "diff", "--numstat", a, b, check=False)
if cp.returncode != 0: if cp.returncode != 0:
return 0, 0 return 0, 0
plus = minus = 0 plus = minus = 0
@ -564,6 +564,10 @@ def build_payload(repo: Path, tips: dict[str, str], rebuild: bool) -> dict:
sync_flask_alias(ed_root, tips) sync_flask_alias(ed_root, tips)
tip_times = {name: tip_commit_time(repo, tip) for name, tip in tips.items()} tip_times = {name: tip_commit_time(repo, tip) for name, tip in tips.items()}
main_tip = tips.get("main", "")
vs_main = {
name: diff_numstat(repo, main_tip, tip) for name, tip in tips.items()
}
branches_out = [] branches_out = []
for row in rows: for row in rows:
name = row["branch"] name = row["branch"]
@ -589,12 +593,11 @@ def build_payload(repo: Path, tips: dict[str, str], rebuild: bool) -> dict:
entry["built_at"] = meta["built_at"] entry["built_at"] = meta["built_at"]
if "build_seconds" in meta: if "build_seconds" in meta:
entry["build_seconds"] = meta["build_seconds"] entry["build_seconds"] = meta["build_seconds"]
# sticky bar: +grün / −rot vs parent tip; omit when no parent # sticky bar: +grün / −rot vs main (omit on main itself)
parent = row["parent"] if name != "main" and main_tip:
if parent and parent in tips: plus, minus = vs_main.get(name, (0, 0))
plus, minus = diff_numstat(repo, tips[parent], row["tip"]) entry["vs_main_plus"] = plus
entry["vs_parent_plus"] = plus entry["vs_main_minus"] = minus
entry["vs_parent_minus"] = minus
branches_out.append(entry) branches_out.append(entry)
payload = { payload = {

View file

@ -13,7 +13,7 @@ html {
width: 100%; width: 100%;
max-width: none; max-width: none;
margin: 0; margin: 0;
padding: 0.2rem 0.5rem 0.25rem; padding: 0.2rem 0.75rem 0.25rem;
background: #10141a; background: #10141a;
border-bottom: 1px solid #243040; border-bottom: 1px solid #243040;
color: #c8d0d8; color: #c8d0d8;
@ -23,20 +23,24 @@ html {
} }
.cascade-tree { .cascade-tree {
width: 100%; width: 100%;
max-width: 45em; max-width: min(80rem, calc(100vw - 1.5rem));
margin: 0 auto; margin: 0 auto;
box-sizing: border-box; box-sizing: border-box;
} }
.cascade-tree-lines { .cascade-tree-lines {
white-space: pre; display: flex;
flex-direction: column;
overflow-x: auto; overflow-x: auto;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
} }
a.cascade-tree-row { a.cascade-tree-row {
display: block; display: grid;
grid-template-columns: minmax(12rem, 1fr) 7.5rem 11.5rem 7rem;
column-gap: 0.85rem;
align-items: baseline;
color: #7ec8ff; color: #7ec8ff;
text-decoration: none; text-decoration: none;
padding: 0; padding: 0 0.15rem;
border-radius: 2px; border-radius: 2px;
} }
a.cascade-tree-row:hover { a.cascade-tree-row:hover {
@ -55,12 +59,20 @@ a.cascade-tree-row.is-active:hover {
color: inherit; color: inherit;
font-size: inherit; font-size: inherit;
} }
.cascade-bar .tip { .cascade-name {
color: #8a96a3; white-space: pre;
white-space: nowrap; overflow: hidden;
text-overflow: ellipsis;
min-width: 0;
color: inherit;
} }
.cascade-bar .cascade-default { .cascade-when,
color: #6a9080; .cascade-built,
.cascade-diff {
text-align: right;
font-variant-numeric: tabular-nums;
white-space: nowrap;
color: #8a96a3;
} }
.cascade-timing { .cascade-timing {
display: none; display: none;
@ -74,6 +86,10 @@ a.cascade-tree-row.is-active:hover {
.cascade-tree { .cascade-tree {
max-width: 100%; max-width: 100%;
} }
a.cascade-tree-row {
grid-template-columns: minmax(8rem, 1fr) 6.5rem 10rem 6rem;
column-gap: 0.45rem;
}
} }
.cascade-bar .diff-plus { .cascade-bar .diff-plus {

View file

@ -1,5 +1,19 @@
(function () { (function () {
var POLL_MS = 30000; var POLL_MS = 30000;
var MONTHS = [
"jan",
"feb",
"mar",
"apr",
"may",
"jun",
"jul",
"aug",
"sep",
"oct",
"nov",
"dec",
];
function cookieEdition() { function cookieEdition() {
var m = document.cookie.match(/(?:^|; )tdpd_edition=([^;]*)/); var m = document.cookie.match(/(?:^|; )tdpd_edition=([^;]*)/);
@ -19,13 +33,14 @@
.replace(/"/g, "&quot;"); .replace(/"/g, "&quot;");
} }
/** ISO → "YYYY-MM-DD HH:MM" (date required; clock-only was misleading across days). */ /** ISO → "sep30 16:12" (3-letter English month + DD + HH:MM). */
function shortTime(iso) { function shortTime(iso) {
if (!iso) return ""; if (!iso) return "";
var m = String(iso).match( var m = String(iso).match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}:\d{2})/);
/^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2})(?::\d{2})?/ if (m) {
); var mon = MONTHS[Number(m[2]) - 1] || m[2];
if (m) return m[1] + " " + m[2]; return mon + m[3] + " " + m[4];
}
return String(iso).slice(0, 16).replace("T", " "); return String(iso).slice(0, 16).replace("T", " ");
} }
@ -37,25 +52,21 @@
return Math.round(n) + "s"; return Math.round(n) + "s";
} }
function metaHtml(row) { function builtText(row) {
var tip = row.tip || ""; var sec = fmtBuildSec(row.build_seconds);
var tipTime = row.tip_time || ""; var at = shortTime(row.built_at || "");
var builtAt = row.built_at || ""; if (sec && at) return sec + " " + at;
var buildSec = row.build_seconds; return sec || at;
var parts = []; }
if (tip) parts.push(esc("@" + tip));
if (tipTime) parts.push(esc(shortTime(tipTime))); function diffHtml(row) {
if (buildSec != null && buildSec !== "") var plus = row.vs_main_plus;
parts.push(esc(fmtBuildSec(buildSec))); var minus = row.vs_main_minus;
if (builtAt) parts.push(esc(shortTime(builtAt))); if (plus == null && minus == null) return "";
// vs parent: +grün / −rot (omit when no parent)
var plus = row.vs_parent_plus;
var minus = row.vs_parent_minus;
if (row.parent && (plus != null || minus != null)) {
var p = plus == null ? 0 : Number(plus); var p = plus == null ? 0 : Number(plus);
var m = minus == null ? 0 : Number(minus); var m = minus == null ? 0 : Number(minus);
if (isFinite(p) && isFinite(m)) { if (!isFinite(p) || !isFinite(m)) return "";
parts.push( return (
'<span class="diff-plus">+' + '<span class="diff-plus">+' +
esc(String(p)) + esc(String(p)) +
'</span> <span class="diff-minus">−' + '</span> <span class="diff-minus">−' +
@ -63,33 +74,33 @@
"</span>" "</span>"
); );
} }
}
return parts.join(" · "); function setText(el, text) {
if (!el) return;
var t = text || "";
if (el.textContent !== t) el.textContent = t;
} }
function rowTitle(row, buildSec) { function rowTitle(row) {
var tip = row.tip || ""; var tip = row.tip || "";
var tipTime = row.tip_time || ""; var tipTime = row.tip_time || "";
var plus = row.vs_parent_plus; var plus = row.vs_main_plus;
var minus = row.vs_parent_minus; var minus = row.vs_main_minus;
var vs = ""; var vs = "";
if (row.parent && (plus != null || minus != null)) { if (plus != null || minus != null) {
vs = vs =
" vs " + " vs main +" +
row.parent +
" +" +
String(plus == null ? 0 : plus) + String(plus == null ? 0 : plus) +
"/−" + "/−" +
String(minus == null ? 0 : minus); String(minus == null ? 0 : minus);
} }
var built = builtText(row);
return ( return (
"Switch build to " + "Switch build to " +
(row.branch || "") + (row.branch || "") +
(tip ? " @ " + tip : "") + (tip ? " @ " + tip : "") +
(tipTime ? " tip=" + tipTime : "") + (tipTime ? " tip=" + shortTime(tipTime) : "") +
(buildSec != null && buildSec !== "" (built ? " build=" + built : "") +
? " build=" + fmtBuildSec(buildSec)
: "") +
vs vs
); );
} }
@ -165,28 +176,17 @@
var a = rows[i]; var a = rows[i];
if (a.getAttribute("data-edition") !== (row.edition || "")) return false; if (a.getAttribute("data-edition") !== (row.edition || "")) return false;
if (a.getAttribute("data-branch") !== (row.branch || "")) return false; if (a.getAttribute("data-branch") !== (row.branch || "")) return false;
var tipHtml = metaHtml(row);
var tipEl = a.querySelector(".tip");
var wantTip = tipHtml ? " " + tipHtml : "";
if (tipEl) {
if (tipEl.innerHTML !== wantTip) tipEl.innerHTML = wantTip;
} else if (wantTip) {
tipEl = document.createElement("span");
tipEl.className = "tip";
tipEl.innerHTML = wantTip;
a.appendChild(tipEl);
}
var defEl = a.querySelector(".cascade-default");
var isDefault = (row.branch || "") === defaultBranch; var isDefault = (row.branch || "") === defaultBranch;
if (isDefault && !defEl) { if (isDefault) a.setAttribute("data-default", "1");
defEl = document.createElement("span"); else a.removeAttribute("data-default");
defEl.className = "cascade-default"; setText(a.querySelector(".cascade-when"), shortTime(row.tip_time || ""));
defEl.textContent = " · default"; setText(a.querySelector(".cascade-built"), builtText(row));
a.appendChild(defEl); var diffEl = a.querySelector(".cascade-diff");
} else if (!isDefault && defEl) { if (diffEl) {
defEl.parentNode.removeChild(defEl); var dh = diffHtml(row);
if (diffEl.innerHTML !== dh) diffEl.innerHTML = dh;
} }
a.setAttribute("title", rowTitle(row, row.build_seconds)); a.setAttribute("title", rowTitle(row));
} }
var timingEl = mount.querySelector(".cascade-timing"); var timingEl = mount.querySelector(".cascade-timing");
var t = timingText(timing, list.length); var t = timingText(timing, list.length);
@ -225,20 +225,9 @@
var row = list[i]; var row = list[i];
var edition = row.edition || ""; var edition = row.edition || "";
var branch = row.branch || ""; var branch = row.branch || "";
var tip = row.tip || "";
var prefix = row.prefix || ""; var prefix = row.prefix || "";
var parent = row.parent || ""; var parent = row.parent || "";
var tipTime = row.tip_time || "";
var buildSec = row.build_seconds;
var isDefault = branch === defaultBranch; var isDefault = branch === defaultBranch;
var tipHtml = metaHtml(row);
var label =
esc(prefix) +
"<code>" +
esc(branch) +
"</code>" +
(tipHtml ? '<span class="tip"> ' + tipHtml + "</span>" : "") +
(isDefault ? '<span class="cascade-default"> · default</span>' : "");
html.push( html.push(
'<a class="cascade-tree-row" role="treeitem" href="/_cascade/use/' + '<a class="cascade-tree-row" role="treeitem" href="/_cascade/use/' +
esc(edition) + esc(edition) +
@ -250,9 +239,22 @@
(parent ? ' data-parent="' + esc(parent) + '"' : "") + (parent ? ' data-parent="' + esc(parent) + '"' : "") +
(isDefault ? ' data-default="1"' : "") + (isDefault ? ' data-default="1"' : "") +
' title="' + ' title="' +
esc(rowTitle(row, buildSec)) + esc(rowTitle(row)) +
'">' + '">' +
label + '<span class="cascade-name">' +
esc(prefix) +
"<code>" +
esc(branch) +
"</code></span>" +
'<span class="cascade-when">' +
esc(shortTime(row.tip_time || "")) +
"</span>" +
'<span class="cascade-built">' +
esc(builtText(row)) +
"</span>" +
'<span class="cascade-diff">' +
diffHtml(row) +
"</span>" +
"</a>" "</a>"
); );
} }
@ -280,7 +282,7 @@
.catch(function () { .catch(function () {
if (!soft && mount && !mount.querySelector("a[data-edition]")) { if (!soft && mount && !mount.querySelector("a[data-edition]")) {
mount.innerHTML = mount.innerHTML =
'<a class="cascade-tree-row" href="/_cascade/use/flask" data-edition="flask" data-branch="flask"><code>flask</code></a>' + '<a class="cascade-tree-row" href="/_cascade/use/flask" data-edition="flask" data-branch="flask"><span class="cascade-name"><code>flask</code></span></a>' +
'<span class="tip"> (branch-status unavailable)</span>'; '<span class="tip"> (branch-status unavailable)</span>';
markActive(mount, cookieEdition()); markActive(mount, cookieEdition());
pinBar(); pinBar();

View file

@ -13,7 +13,7 @@ html {
width: 100%; width: 100%;
max-width: none; max-width: none;
margin: 0; margin: 0;
padding: 0.2rem 0.5rem 0.25rem; padding: 0.2rem 0.75rem 0.25rem;
background: #10141a; background: #10141a;
border-bottom: 1px solid #243040; border-bottom: 1px solid #243040;
color: #c8d0d8; color: #c8d0d8;
@ -23,20 +23,24 @@ html {
} }
.cascade-tree { .cascade-tree {
width: 100%; width: 100%;
max-width: 45em; max-width: min(80rem, calc(100vw - 1.5rem));
margin: 0 auto; margin: 0 auto;
box-sizing: border-box; box-sizing: border-box;
} }
.cascade-tree-lines { .cascade-tree-lines {
white-space: pre; display: flex;
flex-direction: column;
overflow-x: auto; overflow-x: auto;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
} }
a.cascade-tree-row { a.cascade-tree-row {
display: block; display: grid;
grid-template-columns: minmax(12rem, 1fr) 7.5rem 11.5rem 7rem;
column-gap: 0.85rem;
align-items: baseline;
color: #7ec8ff; color: #7ec8ff;
text-decoration: none; text-decoration: none;
padding: 0; padding: 0 0.15rem;
border-radius: 2px; border-radius: 2px;
} }
a.cascade-tree-row:hover { a.cascade-tree-row:hover {
@ -55,12 +59,20 @@ a.cascade-tree-row.is-active:hover {
color: inherit; color: inherit;
font-size: inherit; font-size: inherit;
} }
.cascade-bar .tip { .cascade-name {
color: #8a96a3; white-space: pre;
white-space: nowrap; overflow: hidden;
text-overflow: ellipsis;
min-width: 0;
color: inherit;
} }
.cascade-bar .cascade-default { .cascade-when,
color: #6a9080; .cascade-built,
.cascade-diff {
text-align: right;
font-variant-numeric: tabular-nums;
white-space: nowrap;
color: #8a96a3;
} }
.cascade-timing { .cascade-timing {
display: none; display: none;
@ -74,6 +86,10 @@ a.cascade-tree-row.is-active:hover {
.cascade-tree { .cascade-tree {
max-width: 100%; max-width: 100%;
} }
a.cascade-tree-row {
grid-template-columns: minmax(8rem, 1fr) 6.5rem 10rem 6rem;
column-gap: 0.45rem;
}
} }
.cascade-bar .diff-plus { .cascade-bar .diff-plus {

View file

@ -1,5 +1,19 @@
(function () { (function () {
var POLL_MS = 30000; var POLL_MS = 30000;
var MONTHS = [
"jan",
"feb",
"mar",
"apr",
"may",
"jun",
"jul",
"aug",
"sep",
"oct",
"nov",
"dec",
];
function cookieEdition() { function cookieEdition() {
var m = document.cookie.match(/(?:^|; )tdpd_edition=([^;]*)/); var m = document.cookie.match(/(?:^|; )tdpd_edition=([^;]*)/);
@ -19,13 +33,14 @@
.replace(/"/g, "&quot;"); .replace(/"/g, "&quot;");
} }
/** ISO → "YYYY-MM-DD HH:MM" (date required; clock-only was misleading across days). */ /** ISO → "sep30 16:12" (3-letter English month + DD + HH:MM). */
function shortTime(iso) { function shortTime(iso) {
if (!iso) return ""; if (!iso) return "";
var m = String(iso).match( var m = String(iso).match(/^(\d{4})-(\d{2})-(\d{2})T(\d{2}:\d{2})/);
/^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2})(?::\d{2})?/ if (m) {
); var mon = MONTHS[Number(m[2]) - 1] || m[2];
if (m) return m[1] + " " + m[2]; return mon + m[3] + " " + m[4];
}
return String(iso).slice(0, 16).replace("T", " "); return String(iso).slice(0, 16).replace("T", " ");
} }
@ -37,25 +52,21 @@
return Math.round(n) + "s"; return Math.round(n) + "s";
} }
function metaHtml(row) { function builtText(row) {
var tip = row.tip || ""; var sec = fmtBuildSec(row.build_seconds);
var tipTime = row.tip_time || ""; var at = shortTime(row.built_at || "");
var builtAt = row.built_at || ""; if (sec && at) return sec + " " + at;
var buildSec = row.build_seconds; return sec || at;
var parts = []; }
if (tip) parts.push(esc("@" + tip));
if (tipTime) parts.push(esc(shortTime(tipTime))); function diffHtml(row) {
if (buildSec != null && buildSec !== "") var plus = row.vs_main_plus;
parts.push(esc(fmtBuildSec(buildSec))); var minus = row.vs_main_minus;
if (builtAt) parts.push(esc(shortTime(builtAt))); if (plus == null && minus == null) return "";
// vs parent: +grün / −rot (omit when no parent)
var plus = row.vs_parent_plus;
var minus = row.vs_parent_minus;
if (row.parent && (plus != null || minus != null)) {
var p = plus == null ? 0 : Number(plus); var p = plus == null ? 0 : Number(plus);
var m = minus == null ? 0 : Number(minus); var m = minus == null ? 0 : Number(minus);
if (isFinite(p) && isFinite(m)) { if (!isFinite(p) || !isFinite(m)) return "";
parts.push( return (
'<span class="diff-plus">+' + '<span class="diff-plus">+' +
esc(String(p)) + esc(String(p)) +
'</span> <span class="diff-minus">−' + '</span> <span class="diff-minus">−' +
@ -63,33 +74,33 @@
"</span>" "</span>"
); );
} }
}
return parts.join(" · "); function setText(el, text) {
if (!el) return;
var t = text || "";
if (el.textContent !== t) el.textContent = t;
} }
function rowTitle(row, buildSec) { function rowTitle(row) {
var tip = row.tip || ""; var tip = row.tip || "";
var tipTime = row.tip_time || ""; var tipTime = row.tip_time || "";
var plus = row.vs_parent_plus; var plus = row.vs_main_plus;
var minus = row.vs_parent_minus; var minus = row.vs_main_minus;
var vs = ""; var vs = "";
if (row.parent && (plus != null || minus != null)) { if (plus != null || minus != null) {
vs = vs =
" vs " + " vs main +" +
row.parent +
" +" +
String(plus == null ? 0 : plus) + String(plus == null ? 0 : plus) +
"/−" + "/−" +
String(minus == null ? 0 : minus); String(minus == null ? 0 : minus);
} }
var built = builtText(row);
return ( return (
"Switch build to " + "Switch build to " +
(row.branch || "") + (row.branch || "") +
(tip ? " @ " + tip : "") + (tip ? " @ " + tip : "") +
(tipTime ? " tip=" + tipTime : "") + (tipTime ? " tip=" + shortTime(tipTime) : "") +
(buildSec != null && buildSec !== "" (built ? " build=" + built : "") +
? " build=" + fmtBuildSec(buildSec)
: "") +
vs vs
); );
} }
@ -165,28 +176,17 @@
var a = rows[i]; var a = rows[i];
if (a.getAttribute("data-edition") !== (row.edition || "")) return false; if (a.getAttribute("data-edition") !== (row.edition || "")) return false;
if (a.getAttribute("data-branch") !== (row.branch || "")) return false; if (a.getAttribute("data-branch") !== (row.branch || "")) return false;
var tipHtml = metaHtml(row);
var tipEl = a.querySelector(".tip");
var wantTip = tipHtml ? " " + tipHtml : "";
if (tipEl) {
if (tipEl.innerHTML !== wantTip) tipEl.innerHTML = wantTip;
} else if (wantTip) {
tipEl = document.createElement("span");
tipEl.className = "tip";
tipEl.innerHTML = wantTip;
a.appendChild(tipEl);
}
var defEl = a.querySelector(".cascade-default");
var isDefault = (row.branch || "") === defaultBranch; var isDefault = (row.branch || "") === defaultBranch;
if (isDefault && !defEl) { if (isDefault) a.setAttribute("data-default", "1");
defEl = document.createElement("span"); else a.removeAttribute("data-default");
defEl.className = "cascade-default"; setText(a.querySelector(".cascade-when"), shortTime(row.tip_time || ""));
defEl.textContent = " · default"; setText(a.querySelector(".cascade-built"), builtText(row));
a.appendChild(defEl); var diffEl = a.querySelector(".cascade-diff");
} else if (!isDefault && defEl) { if (diffEl) {
defEl.parentNode.removeChild(defEl); var dh = diffHtml(row);
if (diffEl.innerHTML !== dh) diffEl.innerHTML = dh;
} }
a.setAttribute("title", rowTitle(row, row.build_seconds)); a.setAttribute("title", rowTitle(row));
} }
var timingEl = mount.querySelector(".cascade-timing"); var timingEl = mount.querySelector(".cascade-timing");
var t = timingText(timing, list.length); var t = timingText(timing, list.length);
@ -225,20 +225,9 @@
var row = list[i]; var row = list[i];
var edition = row.edition || ""; var edition = row.edition || "";
var branch = row.branch || ""; var branch = row.branch || "";
var tip = row.tip || "";
var prefix = row.prefix || ""; var prefix = row.prefix || "";
var parent = row.parent || ""; var parent = row.parent || "";
var tipTime = row.tip_time || "";
var buildSec = row.build_seconds;
var isDefault = branch === defaultBranch; var isDefault = branch === defaultBranch;
var tipHtml = metaHtml(row);
var label =
esc(prefix) +
"<code>" +
esc(branch) +
"</code>" +
(tipHtml ? '<span class="tip"> ' + tipHtml + "</span>" : "") +
(isDefault ? '<span class="cascade-default"> · default</span>' : "");
html.push( html.push(
'<a class="cascade-tree-row" role="treeitem" href="/_cascade/use/' + '<a class="cascade-tree-row" role="treeitem" href="/_cascade/use/' +
esc(edition) + esc(edition) +
@ -250,9 +239,22 @@
(parent ? ' data-parent="' + esc(parent) + '"' : "") + (parent ? ' data-parent="' + esc(parent) + '"' : "") +
(isDefault ? ' data-default="1"' : "") + (isDefault ? ' data-default="1"' : "") +
' title="' + ' title="' +
esc(rowTitle(row, buildSec)) + esc(rowTitle(row)) +
'">' + '">' +
label + '<span class="cascade-name">' +
esc(prefix) +
"<code>" +
esc(branch) +
"</code></span>" +
'<span class="cascade-when">' +
esc(shortTime(row.tip_time || "")) +
"</span>" +
'<span class="cascade-built">' +
esc(builtText(row)) +
"</span>" +
'<span class="cascade-diff">' +
diffHtml(row) +
"</span>" +
"</a>" "</a>"
); );
} }
@ -280,7 +282,7 @@
.catch(function () { .catch(function () {
if (!soft && mount && !mount.querySelector("a[data-edition]")) { if (!soft && mount && !mount.querySelector("a[data-edition]")) {
mount.innerHTML = mount.innerHTML =
'<a class="cascade-tree-row" href="/_cascade/use/flask" data-edition="flask" data-branch="flask"><code>flask</code></a>' + '<a class="cascade-tree-row" href="/_cascade/use/flask" data-edition="flask" data-branch="flask"><span class="cascade-name"><code>flask</code></span></a>' +
'<span class="tip"> (branch-status unavailable)</span>'; '<span class="tip"> (branch-status unavailable)</span>';
markActive(mount, cookieEdition()); markActive(mount, cookieEdition());
pinBar(); pinBar();