Project homepage Mailing List  Warmcat.com  API Docs  Github Mirror 
    npro  
 Modern all-safe Rust Network Protocol library supporting h1, h2, h3, ws, wt sans-IO and with socket IO + tls
git clone https://npro.rs/repo/npro
 
root / assets / surprised.png
Author[]Andy Green <andy@warmcat.com> 2026-08-02 16:52 UTC
Committer[]Andy Green <andy@warmcat.com> 2026-08-02 16:52 UTC
Tree6efdde9142aa8451fca93570846a778e79e9b191   Raw Patch
 
hls: subs2
hls: subs2
diff --git a/assets/sai.css b/assets/sai.css index e0ba5c6..9656fc3 100644 --- a/assets/sai.css +++ b/assets/sai.css @@ -1792,15 +1792,18 @@ div.ibuil.vm-builder { border-right: none; } -/* col 1: logo + login, centred */ +/* col 1: logo + login. Top-aligned with scroll fallback so the logo can + * never be clipped by the pane's overflow:hidden. */ .sidebar-pane .sb-col-logo { - flex: 0 0 160px; + flex: 0 0 150px; display: flex; flex-direction: column; align-items: center; - justify-content: center; + justify-content: flex-start; text-align: center; background: #ffffff; + overflow-y: auto; + padding-top: 4px; } .sidebar-pane .sb-col-logo .logo { @@ -1810,8 +1813,9 @@ div.ibuil.vm-builder { } .sidebar-pane .sb-col-logo img.logo { - width: 120px; + width: 110px; height: auto; + display: block; } /* col 2 + col 3: selectable project / branch lists */ diff --git a/assets/sai.js b/assets/sai.js index b7e912e..2666fd2 100644 --- a/assets/sai.js +++ b/assets/sai.js @@ -1172,14 +1172,14 @@ function sai_taskinfo_render(t, now_ut) function update_summary_and_progress(event_uuid) { var sumbs = document.getElementById("sumbs-" + event_uuid); - /* - * The sidebar event row (col 4) keeps the status text in its own - * #sbsum-<uuid> span; the progress bar slot is the legacy #sumbs-<uuid>. - * Either or both may be present depending on what's rendered. - */ - var sbs = document.getElementById("sbsum-" + event_uuid); - if (!sumbs && !sbs) + if (!sumbs) { + /* + * No legacy combined slot; the sidebar row (if present) is refreshed + * separately by sai_sb_render_event_summary(). + */ + sai_sb_render_event_summary(event_uuid); return; + } var summary = summarize_build_situation(event_uuid); var summary_html = summary.text; @@ -1227,14 +1227,59 @@ function update_summary_and_progress(event_uuid) { "<div class=\"progress-bar-failed float-right " + bad_cls + "\"></div>" + "</div>"; } - if (sumbs) - sumbs.innerHTML = summary_html; - /* - * The sidebar status span shows just the text portion (the bar is in the - * separate #sumbs- slot below it). - */ + sumbs.innerHTML = summary_html; + + /* keep the sidebar row (if any) in sync too */ + sai_sb_render_event_summary(event_uuid); +} + +/* + * Build the HTML for an event's progress bar (or "" when complete / empty). + */ +function sai_sb_progress_bar_html(summary) +{ + if (!summary || !summary.total || summary.good === summary.total) + return ""; + + var good_pct = (summary.good / summary.total) * 100; + var pend_pct = (summary.pending / summary.total) * 100; + var ong_pct = (summary.ongoing / summary.total) * 100; + var bad_pct = (summary.bad / summary.total) * 100; + + var up5 = function(n) { return Math.ceil(n / 5) * 5; }; + var gw = up5(good_pct), pw = up5(pend_pct), ow = up5(ong_pct), bw = up5(bad_pct); + var tw = gw + pw + ow + bw; + if (tw > 100) { + var widths = { g: gw, p: pw, o: ow, b: bw }; + var largest = Object.keys(widths).reduce(function(a, b){ + return widths[a] > widths[b] ? a : b; }); + widths[largest] -= (tw - 100); + gw = widths.g; pw = widths.p; ow = widths.o; bw = widths.b; + } + + return "<div class=\"progress-bar\">" + + "<div class=\"progress-bar-success w-" + gw + "\"></div>" + + "<div class=\"progress-bar-pending w-" + pw + "\"></div>" + + "<div class=\"progress-bar-ongoing w-" + ow + "\"></div>" + + "<div class=\"progress-bar-failed float-right w-" + bw + "\"></div>" + + "</div>"; +} + +/* + * Refresh the sidebar event row's status text (#sbsum-<uuid>) and progress bar + * (#sbbar-<uuid>) independently, so the status text isn't duplicated. + */ +function sai_sb_render_event_summary(event_uuid) +{ + var sbs = document.getElementById("sbsum-" + event_uuid); + var bar = document.getElementById("sbbar-" + event_uuid); + if (!sbs && !bar) + return; + var sum = summarize_build_situation(event_uuid); if (sbs) - sbs.innerHTML = san(summary.text || ""); + sbs.innerHTML = san(sum.text || ""); + if (bar) + bar.innerHTML = sai_sb_progress_bar_html(sum); } function summarize_build_situation(event_uuid) @@ -1589,8 +1634,8 @@ function render_sb_events() s += "<span class=\"sb-event-tag\">" + san(sai_event_hash_display(e.hash)) + "</span>"; s += "<span class=\"sb-event-status\" id=\"sbsum-" + san(e.uuid) + "\"></span>"; s += "</div>"; - /* sumbs-<uuid> is what update_summary_and_progress() fills */ - s += "<div class=\"sb-event-bar\" id=\"sumbs-" + san(e.uuid) + "\"></div>"; + /* progress bar slot, filled by sai_sb_render_event_summary() */ + s += "<div class=\"sb-event-bar\" id=\"sbbar-" + san(e.uuid) + "\"></div>"; s += "</div>"; }); } @@ -1603,7 +1648,7 @@ function render_sb_events() /* Fill status text + progress bar for each visible event */ matching.forEach(function(o) { - update_summary_and_progress(o.e.uuid); + sai_sb_render_event_summary(o.e.uuid); }); } @@ -3121,6 +3166,40 @@ function ws_open_sai() } } } + + /* + * Fallback for older sai-web servers that don't + * (yet) answer projlist/branchlist: derive the + * unique projects and branches client-side from + * whatever events we have. When a real projlist / + * branchlist reply arrives later it overrides this. + */ + if ((!sb_projects || !sb_projects.length) && loaded_events.length) { + var _pset = {}; + loaded_events.forEach(function(o) { + if (o && o.e && o.e.repo_name) + _pset[o.e.repo_name] = 1; + }); + sb_projects = Object.keys(_pset).sort(); + if (!sb_selected_project && sb_projects.length && !selected_event_uuid && !selected_task_uuid) + sb_selected_project = sb_projects[0]; + render_sb_projects(); + } + if (sb_selected_project && (!sb_branches || !sb_branches.length) && loaded_events.length) { + var _bset = {}; + loaded_events.forEach(function(o) { + if (o && o.e && o.e.repo_name === sb_selected_project && o.e.ref) + _bset[o.e.ref] = o.e.created || 0; + }); + /* sort refs newest-first by their latest event */ + sb_branches = Object.keys(_bset).sort(function(a, b) { + return _bset[b] - _bset[a]; + }); + if (!sb_selected_ref && sb_branches.length) + sb_selected_ref = sb_branches[0]; + render_sb_branches(); + } + render_event_decals(); /*
Page fetched 0s ago, creation time: 5ms (vhost etag hits: 0%, cache hits: 0%)