Skip to content

[Website] Add front-page thumbnails to saved Playgrounds - #4133

Merged
adamziel merged 6 commits into
WordPress:trunkfrom
articles-adamziel-com:website-saved-playground-thumbnails
Jul 21, 2026
Merged

[Website] Add front-page thumbnails to saved Playgrounds#4133
adamziel merged 6 commits into
WordPress:trunkfrom
articles-adamziel-com:website-saved-playground-thumbnails

Conversation

@adamziel

@adamziel adamziel commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Saved Playgrounds now show a 128×96 image of their front page instead of all sharing the same WordPress mark. When no image is available, the WordPress mark uses the same subtle 4:3 frame.

The website calls captureSiteThumbnail() from #4142 after saving and stores the result in the existing site metadata. A capture failure does not block saving or booting. Recent autosaves also keep their WordPress and PHP versions.

On phones, names and details align with the top of the preview and names may use three lines.

Desktop before Desktop after
Saved Playgrounds list using the WordPress mark Saved Playgrounds list using a front-page thumbnail
Mobile before Mobile after
Mobile saved Playgrounds list using the WordPress mark Mobile saved Playgrounds list with top-aligned details and a framed fallback

Testing

Save a Playground, open Your Playgrounds, and check that its front-page thumbnail appears. Start another Playground and check that the previous entry keeps its WordPress and PHP versions under Recent autosaves. Reload the saved Playground and check that its thumbnail remains.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds front-page thumbnails to saved Playgrounds to make the saved list easier to scan by capturing a compact screenshot and persisting it alongside existing site metadata.

Changes:

  • Persist a captured front-page thumbnail into saved site metadata and render it in the “Your Playgrounds” list with a logo fallback.
  • Add remote-side capture pipeline using a disposable iframe + MU plugin message bridge (Document-Isolation-Policy compatible).
  • Add storage + UI tests (unit + Playwright) to verify thumbnail persistence and display.

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/playground/website/src/lib/state/redux/slice-sites.ts Extends persisted site metadata to include an optional thumbnail image.
packages/playground/website/src/lib/state/redux/persist-temporary-site.ts Triggers thumbnail capture after a successful save.
packages/playground/website/src/lib/state/redux/capture-site-thumbnail.ts Adds a safe wrapper that captures and persists thumbnails without breaking save/boot flows.
packages/playground/website/src/lib/state/redux/boot-site-client.ts Triggers thumbnail capture after initial OPFS sync / on boot for stored sites.
packages/playground/website/src/lib/state/redux/boot-site-client.spec.ts Updates logger mock to include warn for new thumbnail failure logging.
packages/playground/website/src/lib/state/opfs/opfs-site-storage.spec.ts Adds unit test asserting thumbnail is stored in OPFS metadata.
packages/playground/website/src/components/saved-playgrounds-panel/style.module.css Adds styles for a thumbnail preview area in saved rows.
packages/playground/website/src/components/saved-playgrounds-panel/index.tsx Renders thumbnail when present, otherwise falls back to logo/WordPress mark.
packages/playground/website/playwright/e2e/website-ui.spec.ts Adds E2E test verifying thumbnail is captured, persisted, and reused.
packages/playground/remote/src/lib/playground-mu-plugin/0-playground.php Adds message handler to load renderer module and return captured thumbnail to parent.
packages/playground/remote/src/lib/playground-client.ts Extends client interface with captureSiteThumbnail() and exports SiteThumbnail type.
packages/playground/remote/src/lib/capture-site-thumbnail.ts Implements DOM->canvas capture and encoding to WebP/JPEG inside the WP iframe.
packages/playground/remote/src/lib/boot-playground-remote.ts Implements cross-frame capture by booting a disposable iframe and requesting capture via postMessage.
packages/playground/remote/service-worker.ts Ensures the capture module is served as an app asset from within scoped WP documents.
packages/playground/client/src/index.ts Re-exports SiteThumbnail from @wp-playground/remote.
package.json Adds modern-screenshot dependency used by the renderer.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/playground/remote/src/lib/playground-mu-plugin/0-playground.php Outdated
Comment thread packages/playground/remote/src/lib/boot-playground-remote.ts Outdated
adamziel added a commit that referenced this pull request Jul 21, 2026
#4142)

Adds `captureSiteThumbnail()` to `PlaygroundClient`:

```ts
const thumbnail = await client.captureSiteThumbnail();
const src = `data:${thumbnail.mime};base64,${thumbnail.data}`;
```

The method loads `/` in a disposable 1024×768 iframe and returns a
320×240 WebP image. It falls back to JPEG when WebP encoding is
unavailable. The caller owns the result; this PR does not save it or
change the Saved Playgrounds UI.

The renderer uses `modern-screenshot`'s `domToCanvas()`. It clones
`document.documentElement`, embeds its styles and resources in an SVG
`<foreignObject>`, draws that SVG onto a scaled canvas, and encodes the
canvas with `toBlob()`.

## Why capture runs in WordPress

The remote frame cannot always read the WordPress DOM. For example, a
site may send `Cross-Origin-Embedder-Policy: require-corp` and
`Cross-Origin-Opener-Policy: same-origin`. Playground then uses
`Document-Isolation-Policy`, which blocks synchronous DOM access from
the remote frame even though both frames share an origin.

The MU plugin therefore receives the request from the trusted parent and
lazy-loads the renderer inside the front page. It checks the source
window, origin, and renderer URL. The remote frame accepts only the
response with the matching request ID. Normal page loads do not download
`modern-screenshot`.

The disposable page has a 20-second load watchdog. Once it loads,
rendering has a 3-second deadline. DOM cloning and canvas rendering stay
in the document, with periodic task yields. A resource worker handles
supported fetches. Every result or failure removes the iframe and its
listeners.

The service worker serves the marked renderer and resource worker as app
assets. Without that exception, its scoped-referrer handling would
redirect both requests into WordPress's virtual URL namespace, which
returns a 404.

This is the first slice split from #4133. Nothing calls the API
automatically yet, and this PR does not write OPFS metadata.

## Testing

Open a Playground and run `const thumbnail = await
playground.captureSiteThumbnail()` in the browser console. Turn the
result into a data URL, open it, and confirm it shows the front page as
a 320×240 image. Repeat in Chrome, Firefox, and Safari.
@adamziel
adamziel force-pushed the website-saved-playground-thumbnails branch from cd32bda to b614906 Compare July 21, 2026 21:07
@adamziel
adamziel merged commit ebdba11 into WordPress:trunk Jul 21, 2026
53 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment