Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7fe1901703 | ||
|
|
d7026c0ed3 |
@@ -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 —
|
||||
|
||||
Reference in new issue
Block a user