Skip to content

fix(server): preview snapshots preserve blurred backdrops - #24618

Merged
juliusmarminge merged 5 commits into
mainfrom
takeover/pr-20924
Oct 26, 2026
Merged

juliusmarminge merged 5 commits into
mainfrom
takeover/pr-20924

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Oct 25, 2026 •

Copy link
Copy Markdown
Member

Preview snapshots capture at full scale and resize the finished PNG, so blurred backdrops show the page as rendered. Fixes #20899.

Other visible effects: at 125% zoom the PNG is now capped at 1280×711 instead of 1600×889, and the old black padding disappears. Wide plain pages preserve their content and layout with text/edge resampling differences. Narrow controls are byte-identical.

Takeover

Rebuilt from @watashiwaaniket’s #20924, which identified Chromium’s scaled-capture defect and the full-capture/canvas-resize approach.
The resize runs in the tab’s isolated world instead of opening a scratch page and threading a callback through snapshot inputs. HTML-namespaced canvas creation preserves XML-page snapshots.
Replaced the wiring tests with real Chromium tests for blurred, plain, and XML documents. No new dependency or wire change.

Evidence

Supplemental N1 and RM: extended matrix, capture index, and complete reproduction bundle. Supplemental before is merge-base 8bcef34f; after remains b7fddd87.

Linux Xvfb, Electron 44.9.3 / Chromium 152.0.7977.167, genuine Electron webview attached through DesktopBrowserChannel; actual server preview_snapshot outputs. N1 uses 1440×800, scroll 600, zoom 1/DPR 2 and zoom 1.25/DPR 2.5. RM uses real CDP reduced-motion emulation with a running-animation positive control, then stable reduced-motion snapshots. All returned PNGs are 1280×711 and byte-identical to repeats 500 ms later. Independent headed references, two diffs per pair, debugger/attachment/media provenance, ffmpeg X11 recordings and scripts are in the index. Reference resampling leaves edge/text differences; viewer timing is approximate.

Supplemental row Before After Comparison and two diffs per pair
N1 · native attachment · 100% Before N1 at 100% After N1 at 100% Reference and diffs
N1 · native attachment · 125% Before N1 at 125% After N1 at 125% Reference and diffs
RM · reduced-motion animation fixture Before RM After RM Reference and diffs

Independent supplemental proof audit: SUFFICIENT for N1 and RM. All 18 diff images independently recomputed; public fixtures rebuilt byte-for-byte; a fresh actual-server N1 reproduction at 125% passed. Reproduction evidence. Supplemental capture and both proof audits: GPT-6.1 Sol through Codex/T3 delegation.

Fixture matrix, full evidence index, and reproduction bundle contain the headed references, raw images, metrics, fixture/driver scripts, hashes, and nonsecret service traces. All scripts remain external to the PR.

Actual before: c4fa4629. Actual after: b7fddd87, including main by merge commit. Preview source is identical between the captured baseline and current main 8bcef34f.

Chromium 156.0.8078.70: actual headless server snapshot service at DPR 2 versus a matching headed Chromium reference under Xvfb. Height 800 CSS px, root font 16 px; actual dimensions and zoom metrics are in the index. Every static capture matched a second capture 500 ms later. Threshold-0 RGB differences are amplified ×4; threshold-10 masks mark channel differences >10. A7 comparisons normalize the baseline’s 1600×889 image to 1280×711; raw images remain linked in the index.

Fixture Before c4fa4629 After b7fddd87 Diff 0 ×4 Diff >10
Blur · 1440×800 · light before-A1.png after-A1.png diffs-A1-before-after-threshold0-x4.png diffs-A1-before-after-threshold10.png
Blur · 1440×800 · dark before-A2.png after-A2.png diffs-A2-before-after-threshold0-x4.png diffs-A2-before-after-threshold10.png
Blur · 390×800 · light before-A3.png after-A3.png diffs-A3-before-after-threshold0-x4.png diffs-A3-before-after-threshold10.png
Blur · 390×800 · dark before-A4.png after-A4.png diffs-A4-before-after-threshold0-x4.png diffs-A4-before-after-threshold10.png
Blur · 1280×800 · light before-A5.png after-A5.png diffs-A5-before-after-threshold0-x4.png diffs-A5-before-after-threshold10.png
Blur · 640×800 · light before-A6a.png after-A6a.png diffs-A6a-before-after-threshold0-x4.png diffs-A6a-before-after-threshold10.png
Blur · 641×800 · light before-A6b.png after-A6b.png diffs-A6b-before-after-threshold0-x4.png diffs-A6b-before-after-threshold10.png
Plain · 1440×800 · light before-C1.png after-C1.png diffs-C1-before-after-threshold0-x4.png diffs-C1-before-after-threshold10.png
Plain · 390×800 · dark before-C2.png after-C2.png diffs-C2-before-after-threshold0-x4.png diffs-C2-before-after-threshold10.png
XML · 1280×800 · light before-X1.png after-X1.png diffs-X1-before-after-threshold0-x4.png diffs-X1-before-after-threshold10.png
Blur · scroll 600 · 125% · dark before-A7.png after-A7.png diffs-A7-before-after-threshold0-x4.png diffs-A7-before-after-threshold10.png

Identical interaction script: start recording, scroll to 600, snapshot, zoom to 125%, snapshot, reset zoom, scroll to 0, snapshot, reload/reconnect, stop. Before timestamps; after timestamps. Both revisions show an enlarged page after Reset zoom and a blank reload interval before viewer recovery; these are existing observations, not claimed fixes.

Recording Before After
Immediate start/stop: usable first frame before-C3.mp4 after-C3.mp4
Scroll/zoom/snapshot during recording before-A7-C4.mp4 after-A7-C4.mp4
Real Browser viewer and reconnect before-C4-viewer.mp4 after-C4-viewer.mp4

12/12 required rows captured, 13 scenarios including the 640/641 boundary. XML X1 extends the original matrix and succeeds before and after.

Verification log
  • Tests: ServerBrowserPage.test.ts 16→19; ServerBrowser.test.ts 27 unchanged. Final merged head: 46/46 pass. Blur regression fails on baseline; XML regression failed on the intermediate implementation and passes after the namespace fix.
  • Scoped lint/format and server typecheck passed. No repository-wide checks run.
  • Review round 1: A found missing evidence; B found the XML canvas regression. Fixed with namespaced canvas and one regression test. Round 2: both GPT-6.1 Sol and Claude Opus 5.5 returned MERGE; no remaining concrete findings..
  • Independent proof audit: SUFFICIENT: independently rebuilt A1 through a fresh app server and thread-bound MCP; 0/910,080 pixels differ. All 78 diff images independently recomputed..
  • CI: Initial publication read: 11 successful, 16 pending, 0 failing; dispatcher owns monitoring and landing.. No merge performed.
  • Unverified rows: none of N1/RM remain. Evidence covers Linux Xvfb native attachment and reduced-motion media set through the attached guest’s existing CDP debugger; no public reduced-motion configuration API or separate headless-server reduced-motion run is claimed. Physical-monitor and other platform behavior were not tested. All changed files remain server paths.
  • Complexity: base64 conversion O(encoded bytes); raster decode/draw O(pixels). A 3,000-token malformed input rejected in 1.1–2.7 ms; invalid PNG bytes rejected in 3.5–5.1 ms (Chromium 148). Resize measured 85–130 ms at 1280×800/DPR2 and 380–520 ms at 3840×2160/DPR2..
  • Takeover friction log; baseline and scoped-test verification.
  • Models/harnesses: GPT-6.1 Sol via Codex and Claude Opus 5.5 through T3 delegation. Integrated evidence transport used a fresh GPT-6 Astra/Codex provider turn; original author credited above.

juliusmarminge and others added 5 commits October 24, 2026 23:42
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ered

Chromium mirrors the top-left of the page into a backdrop-filter when
Page.captureScreenshot shrinks the capture with a clip scale. Capture at
full scale and shrink the PNG on a canvas in an isolated world instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Oct 25, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 25, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at b7fddd8

Macroscope's review found this PR approvable — This is a focused server preview fix that captures full-resolution PNGs and downsamples them in-page to avoid Chromium's backdrop-filter mirroring, with regression coverage for blurred, plain, and XML pages. Recording and live still JPEG paths retain full-scale behavior, with no schema, dependency, or configuration changes.

You can add or adjust custom eligibility rules. Learn more.

@github-actions

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 5.0 KiB 5.0 KiB −7 B (−0.1%) 6.8 KiB ✅
Codex Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Codex Live turn WebSocket wire 1.2 KiB 1.2 KiB −7 B (−0.6%) 2.0 KiB ✅
Codex Live turn WebSocket decoded 20.9 KiB 20.9 KiB 0 B (0.0%) 29.3 KiB ✅
Codex Live turn messages 2 2 0 (0.0%) 8 ✅
Claude Total thread wire 5.0 KiB 5.0 KiB +7 B (+0.1%) 6.8 KiB ✅
Claude Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Claude Live turn WebSocket wire 1.2 KiB 1.2 KiB +7 B (+0.6%) 2.0 KiB ✅
Claude Live turn WebSocket decoded 21.2 KiB 21.2 KiB 0 B (0.0%) 29.3 KiB ✅
Claude Live turn messages 2 2 0 (0.0%) 8 ✅

Baseline: 97548db · PR result: b7fddd8 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@juliusmarminge juliusmarminge added bugfix Fix PR opened by an issue lead; enters the PR loop pr:gate-failed A land gate failed labels Oct 25, 2026
@coderabbitai

coderabbitai Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 97548db and b7fddd8.

Files selected for processing: 4
  • apps/server/src/preview/ServerBrowser.test.ts
  • apps/server/src/preview/ServerBrowser.ts
  • apps/server/src/preview/ServerBrowserPage.test.ts
  • apps/server/src/preview/ServerBrowserPage.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review.

Walkthrough

Viewport screenshots now capture the full viewport before resizing the PNG when needed. Recording and viewer still-frame capture use the simplified capture API. New tests cover blurred backdrops, plain pages, and XML documents.

Changes: Screenshot capture

Layer / File(s) Summary
Full-viewport capture and PNG resizing
apps/server/src/preview/ServerBrowserPage.ts, apps/server/src/preview/ServerBrowserPage.test.ts
Capture no longer uses scaled clips or page offsets. When the calculated scale is below one, the captured PNG is resized in an isolated world. Tests check image dimensions and sampled colors.
Recording and viewer capture callers
apps/server/src/preview/ServerBrowser.ts, apps/server/src/preview/ServerBrowser.test.ts
Recording initialization and viewer still-frame capture use the simplified capture API. The test session mock handles the Runtime.callFunctionOn response.

Priority: Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

Possibly related PRs

  • pingdotgg/t3code#24523: Adds locking and deadlines to preview capture paths that call ServerBrowserPage.captureViewport.

Suggested labels: bugfix, size:M

Suggested reviewers: scottn-pv

Merge Risk: Minimal · up to b7fdd

No actionable merge-blocking issue is established for the snapshot change; merge after normal checks.

Pre-merge checks: Passed 3 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Description check Warning The description provides detailed problem context, implementation changes, evidence, verification results, limitations, and agent attribution. However, it omits the required Problem, Change, Scope and… Restructure the description using all required template headings. Under Scope and approval, link the triaged issue or approved discussion and include the specific maintainer approval comment, or explain why this focused fix qualifies for an…
Passed Passed checks: 3 passed
Check name Status Explanation
Title check Passed The title clearly and concisely identifies the main change: preserving blurred backdrops in server preview snapshots.
Linked Issues check Passed Issue #20899 requires correct preview_snapshot rendering for blurred backdrops at desktop width in apps/server. The reviewed change captures the full viewport without a scaled clip, then resizes o…
Out of Scope Changes check Passed The changed files remain in the server preview capture path and its tests. The ServerBrowser call-site updates adapt recording and viewer capture to the new capture API. These changes support the ca…
Full details: Linked Issues check

Explanation

Issue #20899 requires correct preview_snapshot rendering for blurred backdrops at desktop width in apps/server. The reviewed change captures the full viewport without a scaled clip, then resizes oversized PNG output in the tab isolated world. The added Chromium test covers a 1280×800 backdrop-filter page and verifies blue-left/red-right pixels instead of mirrored content. The added plain-page and XML tests cover the related resize paths. The PR evidence reports the required desktop fixture matrix and passing final tests.

Full details: Out of Scope Changes check

Explanation

The changed files remain in the server preview capture path and its tests. The ServerBrowser call-site updates adapt recording and viewer capture to the new capture API. These changes support the capture implementation and do not establish unrelated behavior changes, dependencies, or wire changes.

Full details: Description check

Explanation

The description provides detailed problem context, implementation changes, evidence, verification results, limitations, and agent attribution. However, it omits the required Problem, Change, Scope and approval, and Verification headings. It also does not include explicit maintainer approval for the direction and scope; model review results do not satisfy that requirement.

Resolution

Restructure the description using all required template headings. Under Scope and approval, link the triaged issue or approved discussion and include the specific maintainer approval comment, or explain why this focused fix qualifies for an exemption. Keep the existing verification details under Verification and state any checks that were not run.

Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@juliusmarminge juliusmarminge added pr:in-progress A lead is working on this PR pr:ready Every gate passed; waits for Julius's land label and removed pr:gate-failed A land gate failed pr:in-progress A lead is working on this PR labels Oct 25, 2026
@watashiwaaniket

Copy link
Copy Markdown

Thanks @juliusmarminge for carrying this over the line, and good catch on the XML canvas. If the squash commit can include this, it matches your other takeovers:

Co-authored-by: Aniket Dhakane 55570910+watashiwaaniket@users.noreply.github.com

@juliusmarminge
juliusmarminge merged commit 2763bbe into main Oct 26, 2026
61 checks passed
@juliusmarminge
juliusmarminge deleted the takeover/pr-20924 branch October 26, 2026 06:50
github-actions Bot added a commit to omarcresp/t3code-flake that referenced this pull request Oct 26, 2026
## What's Changed
* chore(lint): enforce react/set-state-in-effect by @juliusmarminge in pingdotgg/t3code#25119
* fix(web): short branch names no longer get clipped in thread details by @t3dotgg in pingdotgg/t3code#25328
* refactor(mobile): stream and MCP webviews tie their work to the view's lifetime by @juliusmarminge in pingdotgg/t3code#25226
* fix(web): stacked notices above the composer no longer cast a dark band by @t3dotgg in pingdotgg/t3code#25329
* fix(web): thread undo notice sits above the bottom shelves instead of pushing the list by @maria-rcks in pingdotgg/t3code#25270
* feat(web): "could not load" screen tells users how to debug it by @t3dotgg in pingdotgg/t3code#25317
* fix(web): usage tabs no longer jump when switching to Limits by @t3dotgg in pingdotgg/t3code#25336
* fix(web): composer bottom toolbar keeps its background under update notices by @t3dotgg in pingdotgg/t3code#25335
* chore(lint): enforce catchConditionalRefailToCatchIf by @juliusmarminge in pingdotgg/t3code#25081
* chore(lint): enforce flatMapConditionalToFilterOrFail by @juliusmarminge in pingdotgg/t3code#25082
* chore(lint): enforce runEffectInsideEffect by @juliusmarminge in pingdotgg/t3code#25096
* chore(lint): enforce abortControllerInEffect by @juliusmarminge in pingdotgg/t3code#25098
* chore(lint): enforce t3code/no-inline-schema-compile by @juliusmarminge in pingdotgg/t3code#25113
* chore(lint): enforce oxc/no-accumulating-spread by @juliusmarminge in pingdotgg/t3code#25114
* chore(lint): enforce t3code/no-polling-loop-in-tests by @juliusmarminge in pingdotgg/t3code#25120
* docs: read GitHub Actions logs through cafe by @juliusmarminge in pingdotgg/t3code#25343
* feat(web): "failed to start" screen helps users recover and report the crash by @t3dotgg in pingdotgg/t3code#25345
* fix(web): keep large thread counts from shrinking labels by @Yash-Singh1 in pingdotgg/t3code#25355
* fix(task-cache): avoid cross-request admission waiters by @juliusmarminge in pingdotgg/t3code#25353
* fix(server): select the active iPhone Duo display for screenshots by @shivamhwp in pingdotgg/t3code#24912
* fix(server): remove one-child-per-model instruction by @Yash-Singh1 in pingdotgg/t3code#25359
* fix(dev): skip auto-seeding large databases by @Yash-Singh1 in pingdotgg/t3code#25365
* fix(server): preview snapshots preserve blurred backdrops by @juliusmarminge in pingdotgg/t3code#24618
* fix(web): model picker no longer jumps when switching providers by @t3dotgg in pingdotgg/t3code#25358
* test(mobile): run React hooks with the release compiler by @juliusmarminge in pingdotgg/t3code#24274
* fix(mobile): pause videos outside visible thread and PR content by @juliusmarminge in pingdotgg/t3code#24268
* fix(server): retry stalled MCP registration for resumed Claude turns by @Yash-Singh1 in pingdotgg/t3code#25372
* fix(mobile): avoid crashes opening wrapped source links by @juliusmarminge in pingdotgg/t3code#25156
* refactor(web): disconnect and reconnect delays read a shared deadline instead of effect timers by @juliusmarminge in pingdotgg/t3code#25363
* chore(lint): enforce effectFnOpportunity by @juliusmarminge in pingdotgg/t3code#25099
* fix(web): composer body ref stops re-attaching on every render by @juliusmarminge in pingdotgg/t3code#25409
* fix(web): switching threads no longer delays the next thread's visit by up to 10s by @juliusmarminge in pingdotgg/t3code#25368
* fix(mobile): mobile:install builds on a cache miss for pooled devices, iOS on EAS by @juliusmarminge in pingdotgg/t3code#25390
* revert(web): composer body ref goes back to the #25079 ref callback by @juliusmarminge in pingdotgg/t3code#25412
* feat(mobile): thread rows drop the branch and lead with the title by @juliusmarminge in pingdotgg/t3code#25415
* refactor(web): file breadcrumb menus refetch on a new workspace change, not on every open by @juliusmarminge in pingdotgg/t3code#25377
* fix(web): citations to older responses load their history instead of failing by @juliusmarminge in pingdotgg/t3code#25371


**Full Changelog**: pingdotgg/t3code@v0.1.3-nightly.20261026.3710...v0.1.3-nightly.20261026.3717

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.1.3-nightly.20261026.3717
Pipyakas pushed a commit to Pipyakas/t3code that referenced this pull request Oct 26, 2026
…24618)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 2763bbe)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix Fix PR opened by an issue lead; enters the PR loop pr:ready Every gate passed; waits for Julius's land label size:M 30-99 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: preview_snapshot renders backdrop-filter as mirrored tiles at viewports wider than 640px

2 participants