Compare commits

...
Author SHA1 Message Date
timmy 7fe1901703 fix(demo): make the on-camera caption bar opaque and prove its last line is not clipped
Quality gates / quality (push) Canceled after 0s
The release-delta caption is paragraph length. Rendered at 94% opacity over the running app, the
app's own text bled through it, and the final line sat flush against the bar's bottom edge — the one
caption a reviewer most needs to read was the hardest one to read. The bar is now opaque, long copy
steps down to 10.5/9.5px, and caption() measures the last line box with a Range and fails the run when
the bottom padding is under 3px, so a cramped caption can no longer be recorded as proof.
2026-10-28 12:47:04 +00:00
rockachopa d7026c0ed3 Merge pull request 'Publish the dataset card and labeling guide from the manifest (#27)' (#89) from timmy/27-dataset-card into main
Quality gates / quality (push) Canceled after 0s
Publish the dataset card and labeling guide from the manifest (#27)

Regression tests: npm test 229 pass; dataset-card 14, corpus-manifest 8, corpus-review 21, calibration-harness 19 OK.
2026-10-28 12:02:27 +00:00
+17 -4
View File
@@ -136,9 +136,13 @@ async function caption(text, duration = 1400) {
node.textContent = text;
// Inline styles, not a stylesheet: a stylesheet belongs to one document and the demo reloads the
// page, which silently stripped caption styling and left every later caption clipped body text.
// The bar is opaque, not 94%: a translucent bar let the app's own text show through a long
// release-delta caption and read as run-together garbage. Long copy also steps down a size so a
// paragraph-length caption keeps its bottom padding instead of pressing the last line to the edge.
const size = text.length > 380 ? 9.5 : text.length > 220 ? 10.5 : 12;
node.style.cssText = 'position:fixed;left:16px;right:16px;top:14px;z-index:20000;'
+ 'background:rgba(40,33,29,.94);color:#fff;padding:11px 14px;border-radius:14px;'
+ 'font:800 12px/1.25 system-ui;letter-spacing:.02em;text-align:center;'
+ 'background:#28211d;color:#fff;padding:12px 14px 14px;border-radius:14px;'
+ `font:800 ${size}px/1.32 system-ui;letter-spacing:.02em;text-align:center;`
+ 'box-shadow:0 8px 24px rgba(0,0,0,.2)';
document.body.append(node);
node.animate([{ opacity: 0, transform: 'translateY(-8px)' }, { opacity: 1, transform: 'translateY(0)' }], { duration: 260, fill: 'forwards' });
@@ -152,18 +156,27 @@ async function caption(text, duration = 1400) {
const rect = node.getBoundingClientRect();
const style = getComputedStyle(node);
const topmost = document.elementFromPoint(Math.round(rect.left + rect.width / 2), Math.round(rect.top + rect.height / 2));
// Proving the string was pushed is not proving it was readable. Measure the last line's own box,
// so a paragraph caption pressed flat against the bar's bottom edge fails here instead of shipping.
const range = document.createRange();
range.selectNodeContents(node);
const lines = Array.from(range.getClientRects()).filter(box => box.height > 1);
const lastBottom = lines.length ? Math.max(...lines.map(box => box.bottom)) : rect.bottom;
const padBottom = Math.round(rect.bottom - lastBottom);
return {
ok: style.position === 'fixed' && style.zIndex === '20000' && rect.top <= 40 && rect.height >= 24
&& rect.width > 200 && rect.bottom <= window.innerHeight && (topmost === node || (topmost && node.contains(topmost))),
&& rect.width > 200 && rect.bottom <= window.innerHeight && (topmost === node || (topmost && node.contains(topmost)))
&& lines.length > 0 && padBottom >= 3,
top: Math.round(rect.top), height: Math.round(rect.height), position: style.position,
zIndex: style.zIndex, background: style.backgroundColor, topmost: topmost === node,
padBottom, lines: lines.length,
};
});
const shownBand = await captionBand().catch(() => null);
const bandChanged = Boolean(blankBand && shownBand && !blankBand.equals(shownBand));
if (!painted.ok) throw new Error(`Caption is not an on-screen bar: ${text} ${JSON.stringify(painted)}`);
if (!bandChanged) throw new Error(`Caption painted nothing into the caption band: ${text}`);
captionEvidence.push({ text, visible: true, position: painted.position, top: painted.top, height: painted.height, bandChanged });
captionEvidence.push({ text, visible: true, position: painted.position, top: painted.top, height: painted.height, bandChanged, padBottom: painted.padBottom, lines: painted.lines });
await sleep(duration);
// A caption must not outlive its stated duration. It used to stay on screen until the next one
// replaced it, so a view change left the previous screen's claim hanging over a different screen —