Repository navigation
fix(server): preview snapshots preserve blurred backdrops - #24618
Conversation
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>
ApprovabilityVerdict: Approved at 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. |
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 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.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
|
No actionable comments were generated in the recent review.
|
| 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:
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: 
3 · 
1
-
Failed checks: 1 warning Passed checks: 3 passed-
Full details: Linked Issues check
-
Explanation
Issue
#20899requires correctpreview_snapshotrendering for blurred backdrops at desktop width inapps/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×800backdrop-filterpage 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
ServerBrowsercall-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.
|
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 |
## 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
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_snapshotoutputs. 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.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.
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.
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
ServerBrowserPage.test.ts16→19;ServerBrowser.test.ts27 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.