hyperframes 0.7.41 → 0.7.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -154,35 +154,61 @@ window.__contrastAudit = async function (imgBase64, time) {
154
154
  var fg = parseColor(cs.color);
155
155
  if (fg[3] <= 0.01) continue;
156
156
 
157
- // Sample 4px ring outside bbox for background color
158
- var rr = [],
159
- gg = [],
160
- bb = [];
161
- var x0 = Math.max(0, Math.floor(rect.x) - 4);
162
- var x1 = Math.min(w - 1, Math.ceil(rect.x + rect.width) + 4);
163
- var y0 = Math.max(0, Math.floor(rect.y) - 4);
164
- var y1 = Math.min(h - 1, Math.ceil(rect.y + rect.height) + 4);
165
- var sample = function (sx, sy) {
166
- if (sx < 0 || sx >= w || sy < 0 || sy >= h) return;
167
- var idx = (sy * w + sx) * 4;
168
- rr.push(px[idx]);
169
- gg.push(px[idx + 1]);
170
- bb.push(px[idx + 2]);
171
- };
172
- for (var x = x0; x <= x1; x++) {
173
- sample(x, y0);
174
- sample(x, y1);
175
- }
176
- for (var y = y0; y <= y1; y++) {
177
- sample(x0, y);
178
- sample(x1, y);
157
+ // Prefer an opaque own/ancestor background-color over the pixel ring. A
158
+ // caption/CTA that paints its OWN solid background (a pill, a button, a
159
+ // card) composites its text over THAT color, not over whatever surrounds
160
+ // the box. Sampling the ring there measured the text against the scene
161
+ // behind the element (often a dark photo) and reported false ~1:1 warnings
162
+ // for perfectly readable CTAs. Walk up until a fully-opaque background-color
163
+ // is found; stop and defer to the ring on the first background-image (text
164
+ // over real pixels). Keep this in sync with commands/contrast-bg.ts.
165
+ var ownBg = null;
166
+ for (var bn = el; bn && bn !== document.body; bn = bn.parentElement) {
167
+ var bcs = getComputedStyle(bn);
168
+ if (bcs.backgroundImage && bcs.backgroundImage !== "none") break;
169
+ var bc = parseColor(bcs.backgroundColor);
170
+ if (bc[3] >= 0.999) {
171
+ ownBg = [bc[0], bc[1], bc[2]];
172
+ break;
173
+ }
179
174
  }
180
175
 
181
- if (rr.length === 0) continue;
176
+ var bgR, bgG, bgB;
177
+ if (ownBg) {
178
+ bgR = ownBg[0];
179
+ bgG = ownBg[1];
180
+ bgB = ownBg[2];
181
+ } else {
182
+ // Sample 4px ring outside bbox for background color
183
+ var rr = [],
184
+ gg = [],
185
+ bb = [];
186
+ var x0 = Math.max(0, Math.floor(rect.x) - 4);
187
+ var x1 = Math.min(w - 1, Math.ceil(rect.x + rect.width) + 4);
188
+ var y0 = Math.max(0, Math.floor(rect.y) - 4);
189
+ var y1 = Math.min(h - 1, Math.ceil(rect.y + rect.height) + 4);
190
+ var sample = function (sx, sy) {
191
+ if (sx < 0 || sx >= w || sy < 0 || sy >= h) return;
192
+ var idx = (sy * w + sx) * 4;
193
+ rr.push(px[idx]);
194
+ gg.push(px[idx + 1]);
195
+ bb.push(px[idx + 2]);
196
+ };
197
+ for (var x = x0; x <= x1; x++) {
198
+ sample(x, y0);
199
+ sample(x, y1);
200
+ }
201
+ for (var y = y0; y <= y1; y++) {
202
+ sample(x0, y);
203
+ sample(x1, y);
204
+ }
182
205
 
183
- var bgR = median(rr),
184
- bgG = median(gg),
206
+ if (rr.length === 0) continue;
207
+
208
+ bgR = median(rr);
209
+ bgG = median(gg);
185
210
  bgB = median(bb);
211
+ }
186
212
 
187
213
  // Composite foreground alpha over measured background
188
214
  var compR = Math.round(fg[0] * fg[3] + bgR * (1 - fg[3]));
@@ -1,10 +1,17 @@
1
1
  FROM node:22-bookworm-slim
2
2
 
3
3
  ARG HYPERFRAMES_VERSION=latest
4
- # Set automatically by `docker build --platform` (BuildKit); we use it to
5
- # decide whether to install chrome-headless-shell, which only ships for
6
- # linux64 (see https://googlechromelabs.github.io/chrome-for-testing/).
4
+ # Set automatically by `docker build --platform` (BuildKit); selects the
5
+ # chrome-headless-shell source below (chrome-for-testing ships linux64 only,
6
+ # see https://googlechromelabs.github.io/chrome-for-testing/).
7
7
  ARG TARGETARCH=amd64
8
+ # Pinned Playwright release used to fetch a known-good, arch-native
9
+ # chrome-headless-shell on arm64. chrome-for-testing publishes no linux-arm64
10
+ # build, so arm64 used to fall back to Debian's *unpinned, rolling* `chromium`
11
+ # package — whose bookworm arm64 build SIGTRAPs at startup (exit 133), breaking
12
+ # every containerized render (issue #2039). Playwright ships a pinned
13
+ # linux-arm64 headless-shell (Google's build, not Debian's). Bump deliberately.
14
+ ARG PLAYWRIGHT_VERSION=1.61.1
8
15
 
9
16
  RUN apt-get update && apt-get install -y --no-install-recommends \
10
17
  ca-certificates curl unzip ffmpeg chromium \
@@ -20,37 +27,36 @@ ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
20
27
  ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
21
28
  ENV CONTAINER=true
22
29
 
23
- # chrome-headless-shell unlocks BeginFrame-based deterministic capture but the
24
- # project only publishes a linux64 binary. On arm64 we skip the install and
25
- # let the engine fall back to system chromium (set via
26
- # PUPPETEER_EXECUTABLE_PATH above). The wrapper script below only sets
27
- # PRODUCER_HEADLESS_SHELL_PATH when the binary is actually present.
30
+ # chrome-headless-shell unlocks BeginFrame-based deterministic capture. Install
31
+ # it on both arches from a pinned source so a build can never silently pick up a
32
+ # broken browser update:
33
+ # amd64 -> chrome-for-testing (linux64) via @puppeteer/browsers
34
+ # arm64 -> Playwright's pinned linux-arm64 headless-shell (chrome-for-testing
35
+ # has no arm64 build; Debian's rolling `chromium` SIGTRAPs — #2039)
36
+ # The wrapper script below wires whichever binary landed into
37
+ # PRODUCER_HEADLESS_SHELL_PATH, or fails the build loudly if neither did.
28
38
  RUN if [ "$TARGETARCH" = "amd64" ]; then \
29
39
  npx --yes @puppeteer/browsers install chrome-headless-shell@stable \
30
40
  --path /root/.cache/puppeteer; \
31
41
  else \
32
- echo "Skipping chrome-headless-shell install on ${TARGETARCH} (linux64-only); using system chromium."; \
42
+ npx --yes playwright-core@${PLAYWRIGHT_VERSION} install chromium-headless-shell; \
33
43
  fi
34
44
 
35
45
  RUN npm install -g hyperframes@${HYPERFRAMES_VERSION}
36
46
 
37
- # Wrapper script: resolves chrome-headless-shell path at build time when
38
- # available so the engine uses BeginFrame rendering. On arm64 (no
39
- # chrome-headless-shell) it leaves PRODUCER_HEADLESS_SHELL_PATH unset, so the
40
- # engine falls back to PUPPETEER_EXECUTABLE_PATH (system chromium).
41
- #
42
- # If TARGETARCH=amd64 and the binary is missing, fail the build loudly — the
43
- # previous (pre-PR) wrapper used an `&&` chain that crashed `docker build` in
44
- # this case, and silently downgrading to system chromium on amd64 would mask
45
- # golden-baseline regressions.
46
- RUN SHELL_PATH=$(find /root/.cache/puppeteer/chrome-headless-shell -name "chrome-headless-shell" -type f 2>/dev/null | head -1); \
47
+ # Wrapper script: resolves the chrome-headless-shell path installed above and
48
+ # exports it as PRODUCER_HEADLESS_SHELL_PATH so the engine uses BeginFrame
49
+ # rendering. @puppeteer/browsers names the binary `chrome-headless-shell`;
50
+ # Playwright names it `headless_shell` match either, in either cache dir.
51
+ # If neither is present the browser install step failed: fail the build loudly
52
+ # rather than silently downgrading to the (arm64-broken) Debian chromium.
53
+ RUN SHELL_PATH=$(find /root/.cache/puppeteer /root/.cache/ms-playwright \
54
+ \( -name "chrome-headless-shell" -o -name "headless_shell" \) -type f 2>/dev/null | head -1); \
47
55
  if [ -n "$SHELL_PATH" ]; then \
48
56
  printf '#!/bin/sh\nexport PRODUCER_HEADLESS_SHELL_PATH=%s\nexec hyperframes render "$@"\n' "$SHELL_PATH" > /usr/local/bin/hf-render; \
49
- elif [ "$TARGETARCH" = "amd64" ]; then \
50
- echo "ERROR: chrome-headless-shell binary not found on amd64 — @puppeteer/browsers install must have failed or moved its cache layout." >&2; \
51
- exit 1; \
52
57
  else \
53
- printf '#!/bin/sh\nexec hyperframes render "$@"\n' > /usr/local/bin/hf-render; \
58
+ echo "ERROR: chrome-headless-shell binary not found for ${TARGETARCH} — the browser install step above must have failed." >&2; \
59
+ exit 1; \
54
60
  fi \
55
61
  && chmod +x /usr/local/bin/hf-render
56
62