diff --git a/VERSION b/VERSION index ace4423..42cf067 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.15.1 +1.15.2 diff --git a/VERSIONS.md b/VERSIONS.md index 45ca9b2..470fe10 100644 --- a/VERSIONS.md +++ b/VERSIONS.md @@ -17,6 +17,7 @@ Git tags: `vMAJOR.FEATURE.FIX` (e.g. `v1.8.0`). File `VERSION` omits the `v` pre | Tag | Date (UTC) | Notes | |-----|------------|--------| +| **v1.15.2** | 2026-07-19 | **Bugfix:** warn/error filter — drop blank log lines from HTML; compact console (no black voids above/mid/below); context skips SUMMARY chrome & pure box frames; tighter filter-gap. | | **v1.15.1** | 2026-07-19 | **Bugfix:** mon HTML — phase `section()` boxes no longer painted as SUMMARY (were blue/green `sum-header`); only real SUMMARY rows get solid severity backgrounds (OK/WARN/ERROR/INFO). | | **v1.15.0** | 2026-07-19 | **Feature:** CHF **fake-incoming amount ladder** in `devtesting` (`DEVTESTING_LADDER=1`, default **23** log-spaced rungs **CHF:0.01 → CHF:50000**); synthetic geniban debit funds each rung; baseline plumbing probe kept. | | **v1.14.0** | 2026-07-19 | **Feature:** SUMMARY box rows are **colour-distinct** (per-severity badge + tinted background + bold counts; verdict-coloured header); HTML mon pages style `sum-ok` / `sum-warn` / `sum-err` / … panels the same way. | diff --git a/site-gen/console_to_html.py b/site-gen/console_to_html.py index 43ffcd2..89dd4ee 100755 --- a/site-gen/console_to_html.py +++ b/site-gen/console_to_html.py @@ -549,6 +549,10 @@ def slug_error(i: int, text: str) -> str: def render_line(ln: str, err_slugs: dict[str, str], extra_ids: list[str] | None = None) -> str: + # Drop blank log lines entirely — they only create black voids in the console + # (especially visible under warn/error filter). Anchors still attach to next real line. + if not (ln or "").strip(): + return "" kind = classify(ln) esc = html.escape(ln) for tid, slug in err_slugs.items(): @@ -563,8 +567,11 @@ def render_line(ln: str, err_slugs: dict[str, str], extra_ids: list[str] | None # HTML allows one id — join carefully id_attr = f' id="{html.escape(extra_ids[0])}"' if len(extra_ids) > 1: - # secondary anchors as empty spans prepended - spans = "".join(f'' for x in extra_ids[1:]) + # secondary anchors as empty spans prepended (inline, zero layout impact) + spans = "".join( + f'' + for x in extra_ids[1:] + ) return f'{spans}{esc}\n' return f'{esc}\n' @@ -1175,38 +1182,83 @@ body.filter-warn .filter-chip { border-color: #a68b2d; background: #1a1608; colo padding: 1px 8px; } .filter-clear:hover { border-color: #aaa; background: #222; } -/* Console filter: sticky + overviews stay; match lines + dimmed neighbours (v1.13.11) */ -body.filter-error #mon-console .line:not(.error):not(.blocker):not(.filter-ctx) { display: none !important; } -body.filter-warn #mon-console .line:not(.warn):not(.filter-ctx) { display: none !important; } +/* Console filter: sticky + overviews stay; match lines + dimmed neighbours (v1.13.11+) + v1.15.2: stamp out black voids — hide blanks, zero-size non-matches, compact console */ +body.filter-error #mon-console, +body.filter-warn #mon-console { + padding: 6px 8px; + min-height: 0; + display: flex; + flex-direction: column; + gap: 1px; +} +body.filter-error #mon-console .line:not(.error):not(.blocker):not(.filter-ctx), +body.filter-warn #mon-console .line:not(.warn):not(.filter-ctx) { + display: none !important; + height: 0 !important; + margin: 0 !important; + padding: 0 !important; + border: none !important; + overflow: hidden !important; + line-height: 0 !important; +} +/* empty / whitespace-only lines never take space in filter mode */ +body.filter-error #mon-console .line:empty, +body.filter-warn #mon-console .line:empty, +body.filter-error #mon-console .line.line-blank, +body.filter-warn #mon-console .line.line-blank { + display: none !important; +} /* Context lines: grayed / muted so the warn|error still stands out */ body.filter-error #mon-console .line.filter-ctx, body.filter-warn #mon-console .line.filter-ctx { - opacity: 0.48; - color: #7a7a7a !important; - filter: grayscale(0.9); + opacity: 0.55; + color: #8a8a8a !important; + filter: grayscale(0.85); + margin: 0 !important; + padding: 1px 4px !important; + border: none !important; + background: transparent !important; } body.filter-error #mon-console .line.filter-ctx a.jump, body.filter-warn #mon-console .line.filter-ctx a.jump { color: #6a6a6a !important; } -/* Visual break when filter jumps across a gap in the log */ +/* Match lines stay tight */ +body.filter-error #mon-console .line.error, +body.filter-error #mon-console .line.blocker, +body.filter-warn #mon-console .line.warn { + margin: 1px 0 !important; + padding: 3px 6px !important; +} +/* Compact ellipsis between distant clusters (not a black slab) */ body.filter-error #mon-console .filter-gap, body.filter-warn #mon-console .filter-gap { display: block; - color: #555; + color: #666; text-align: center; - padding: 6px 0; - margin: 4px 0; - letter-spacing: 0.35em; - font-size: 11px; + padding: 0; + margin: 2px 0; + letter-spacing: 0.25em; + font-size: 10px; + line-height: 1.2; user-select: none; - border-top: 1px dashed #333; - border-bottom: 1px dashed #333; + border: none; + background: transparent; + height: auto; } body:not(.filter-error):not(.filter-warn) #mon-console .filter-gap { display: none !important; } body.filter-error .env-context, body.filter-warn .env-context { display: none !important; } body.filter-warn .err-top { display: none; } +/* secondary jump anchors: never affect layout */ +#mon-console .anchor-only { + display: inline; + width: 0; + height: 0; + overflow: hidden; + position: absolute; +} /* v1.12.0: collapsible monitoring env context (default closed) */ .env-context { margin: 0 0 12px; @@ -1436,14 +1488,14 @@ STICKY_JS = """ } // v1.11.0: click error/warn stats → filter log; sticky + overviews stay - // v1.13.11: keep a few neighbour lines (dimmed) so phase/group context stays readable + // v1.13.11: dimmed neighbour context; v1.15.2: stamp out black voids, compact layout var bar = document.getElementById("status-bar"); var chip = document.getElementById("filter-chip"); var chipLabel = document.getElementById("filter-chip-label"); var clearBtn = document.getElementById("filter-clear"); var consoleEl = document.getElementById("mon-console"); - // lines above + below each match (same count either side) - var FILTER_CTX = 3; + // useful neighbours each side (skip blanks / SUMMARY chrome / pure box frames) + var FILTER_CTX = 2; function currentFilter() { if (document.body.classList.contains("filter-error")) return "error"; @@ -1459,11 +1511,13 @@ STICKY_JS = """ consoleEl.querySelectorAll(".filter-gap").forEach(function (el) { el.parentNode && el.parentNode.removeChild(el); }); + document.body.classList.remove("filter-compact"); } function markFilterContext(mode) { clearFilterContext(); if (!consoleEl || !mode) return; + document.body.classList.add("filter-compact"); var lines = Array.prototype.slice.call(consoleEl.querySelectorAll(".line")); if (!lines.length) return; function isMatch(el) { @@ -1477,19 +1531,30 @@ STICKY_JS = """ var t = (el.textContent || "").replace(/\u00a0/g, " ").trim(); return t.length === 0; } - // 0 = hide, 1 = match, 2 = context + function isLowValue(el) { + if (isBlankLine(el)) return true; + if (el.classList.contains("sum-header") || el.classList.contains("sum-ok") + || el.classList.contains("sum-warn") || el.classList.contains("sum-err") + || el.classList.contains("sum-info") || el.classList.contains("sum-block") + || el.classList.contains("sum-totals") || el.classList.contains("sum-verdict-ok") + || el.classList.contains("sum-verdict-warn") || el.classList.contains("sum-verdict-err")) { + return true; + } + var t = (el.textContent || "").replace(/\u00a0/g, " ").trim(); + if (/^[╔╗╚╝║═\-\s│┌┐└┘]+$/.test(t)) return true; + return false; + } var keep = new Array(lines.length); var i, j, k, n, idx; for (i = 0; i < lines.length; i++) keep[i] = 0; for (i = 0; i < lines.length; i++) { if (!isMatch(lines[i])) continue; keep[i] = 1; - // ±FILTER_CTX *non-empty* neighbours (skip blank lines so filter is not full of voids) n = 0; idx = i - 1; while (idx >= 0 && n < FILTER_CTX) { if (keep[idx] === 1) { idx--; continue; } - if (isBlankLine(lines[idx])) { idx--; continue; } + if (isLowValue(lines[idx])) { idx--; continue; } keep[idx] = 2; n++; idx--; @@ -1498,7 +1563,7 @@ STICKY_JS = """ idx = i + 1; while (idx < lines.length && n < FILTER_CTX) { if (keep[idx] === 1) { idx++; continue; } - if (isBlankLine(lines[idx])) { idx++; continue; } + if (isLowValue(lines[idx])) { idx++; continue; } keep[idx] = 2; n++; idx++; @@ -1506,10 +1571,11 @@ STICKY_JS = """ } for (j = 0; j < lines.length; j++) { if (keep[j] === 2) lines[j].classList.add("filter-ctx"); - // never show pure blank lines in filter mode - if (isBlankLine(lines[j]) && keep[j] !== 1) keep[j] = 0; + if (isBlankLine(lines[j])) { + keep[j] = 0; + lines[j].classList.add("line-blank"); + } } - // dashed gap between non-contiguous visible clusters var prevVisible = -2; for (k = 0; k < lines.length; k++) { if (keep[k] === 0) continue;