Skip to content

Conversation

@akirk
Copy link
Member

@akirk akirk commented Jan 21, 2026

Based on #3152.

Motivation for the change, related issues

Adds a persistent personalwp deployment mode where WordPress sites are stored in OPFS and survive browser sessions.

To avoid conflicts with other (earlier) uses of persistent across the code base.

Implementation details

  • virtual:website-defaults Vite plugin for build-time configuration (defaultStorageType, personalWPSiteSlug, defaultBlueprintUrl)
  • PersonalWPBrowserChrome and PersonalWPOverlay components (lazy-loaded to avoid bundling in playground.wordpress.net)
  • lastUrl tracking in site metadata to restore user position on return
  • Auto-login when returning to a PersonalWP site
  • URL blueprint support: URL params like ?plugin=friends or ?blueprint-url=data:... are applied to existing sites, then URL is cleared
  • Dual dev servers: port 5400 (temporary) and port 5401 (personalwp)
  • Parametrized bootSiteClient() with clearUrlAfterBlueprintApplied and autoLogin options

Screenshot

The new overlay:

Screenshot 2026-01-22 at 03 46 48

Testing Instructions (or ideally a Blueprint)

  1. Run npm run dev (starts both servers)
  2. Visit http://127.0.0.1:5401/website-server/ for PersonalWP mode
  3. Navigate somewhere in wp-admin, close the tab, reopen - you should return to the same page, logged in
  4. Visit with ?plugin=friends - the plugin should be installed on the existing site, URL params cleared after
  5. "Start over" button should delete the site and reload fresh

@akirk akirk requested a review from adamziel January 21, 2026 12:22
@akirk akirk force-pushed the persistent/enable-persistent-deployment branch 4 times, most recently from 2899417 to c278f6e Compare January 21, 2026 16:24
@akirk akirk force-pushed the persistent/overlay-refactor branch from 9863d74 to 02c9df6 Compare January 21, 2026 16:49
Base automatically changed from persistent/overlay-refactor to trunk January 21, 2026 16:57
@akirk akirk changed the title Introduce a Persistent Playground mode [website] Introduce a Persistent Playground mode Jan 21, 2026
@akirk akirk changed the title [website] Introduce a Persistent Playground mode [website] Introduce a Personal Playground mode Jan 21, 2026
@akirk akirk force-pushed the persistent/enable-persistent-deployment branch from 9a28a1f to 90b5665 Compare January 21, 2026 17:59
akirk added 4 commits January 21, 2026 19:10
Add infrastructure for persistent WordPress sites that survive browser
sessions:

- Add virtual:website-defaults Vite plugin for build-time configuration
  (defaultStorageType, defaultSiteSlug, defaultBlueprintUrl)
- Create PersistentBrowserChrome component with separate styling
- Create PersistentPlaygroundOverlay with "More Playgrounds" link and
  "Start over" delete functionality
- Add persistent-playground module for blueprint loading
- Track lastUrl in SiteMetadata to restore user position on return
- Auto-login for persistent sites when returning
- Persist site metadata to OPFS on creation
- Run both dev servers in parallel (port 5400 standard, 5401 persistent)
- Use "default" site slug to keep URLs clean

This is a foundational PR for persistent deployment. Multi-tab
coordination, pending URL blueprints, and other advanced features
will be added in subsequent PRs.
- Use JustViewport directly in PersistentBrowserChrome instead of
  accepting children (no keep-alive needed for OPFS-backed sites)
- Handle seamless display mode for persistent sites
- Simplify site slug/name derivation using nullish coalescing
Enable applying blueprints to existing persistent sites via URL params
like ?plugin=friends or ?blueprint-url=data:...

- Add PendingUrlBlueprint interface and state to slice-sites
- Add resolveUrlParamsForExistingSite to detect actionable URL params
- Store pending blueprint when returning to existing default site
- Merge pending blueprint steps into boot blueprint
- Clear pending blueprint and URL params after successful boot
@akirk akirk force-pushed the persistent/enable-persistent-deployment branch from 90b5665 to eeb8f50 Compare January 21, 2026 18:14

import css from './style.module.css';
import BrowserChrome from '../browser-chrome';
import PersonalBrowserChrome from '../personal-browser-chrome';
Copy link
Collaborator

@adamziel adamziel Jan 21, 2026

Choose a reason for hiding this comment

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

How could we do code splitting here to avoid adding these components into playground.wordpress.net build.js bundle?

Copy link
Member Author

@akirk akirk Jan 22, 2026

Choose a reason for hiding this comment

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

I added a React.lazy and verified that the PersonalWP components are in a separate lazy-loaded chunk:

# Fresh production build, checking for PersonalWP-specific content ("Start over" button text)

$ grep -l "Start over" dist/packages/playground/website/assets/*.js
index-CxgH81pX.js   # Lazy chunk only

$ grep -c "Start over" dist/packages/playground/website/assets/main-*.js
0   #  Not in main bundle

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks! We also need to consider the impact on packages/vite-extensions/vite-list-assets-required-for-offline-mode.ts. The offline mode won't work until all those files are cached so we don't want to put the PersonalWP assets on the core Playground offline assets list.

We roughly have two possible solutions here:

  1. Output a separate list of required assets for Playground and PersonalWP. This is ideal but might not be easy and maybe shouldn't block this work.
  2. Break the offline mode support in PersonalWP for the time being, and exclude PersonalWP bundle files from the list of required assets. We could do that by storing them in, say, dist/packages/playground/website/assets/personalwp/ and adding that as a regexp to packages/vite-extensions/vite-list-assets-required-for-offline-mode.ts.

A third option, that would take care of 1 and 2 but would also make the maintenance more annoying, would be to just copy the entire website package as personal-wp and duplicate everything. It has its own upsides and downsides, but would solve for this particular problem.

akirk added 4 commits January 21, 2026 20:54
- Rename personal-* folders and components to personalwp-*
- Rename variables: defaultSiteSlug -> personalWPSiteSlug,
  isPersonalMode -> isPersonalWPMode,
  shouldUsePersonalBlueprint -> shouldUsePersonalWPBlueprint
- Rename PendingUrlBlueprint -> BlueprintResolvedFromUrl with
  siteSlug -> targetSiteSlug for clarity
- Apply lastUrl persistence to all sites (not just storage !== none)
- Restrict URL param clearing to PersonalWP only and also clear hash
This ensures the PersonalWP components are not included in the
playground.wordpress.net bundle, only loaded when needed.
Add BootSiteClientOptions interface with:
- clearUrlAfterBlueprintApplied: clears URL params and hash after boot
- autoLogin: enables auto-login when WordPress is already installed

This removes the implicit dependency on personalWPSiteSlug inside the
function and makes the behavior explicit at the call site.
- Mode: personal -> personalwp, personal-development -> personalwp-development
- Blueprint: personal-boot.json -> personalwp-boot.json
- Cache dir: packages-playground-website-personal -> packages-playground-website-personalwp
- Config: personalWebsiteDevServerPort -> personalwpWebsiteDevServerPort
@akirk akirk changed the title [website] Introduce a Personal Playground mode [website] Introduce a PersonalWP Playground mode Jan 22, 2026
@akirk akirk changed the title [website] Introduce a PersonalWP Playground mode [website] Introduce a Personal Playground mode Jan 22, 2026
const activeSite = useActiveSite();

// Personal mode uses JustViewport directly (no keep-alive needed since data is in OPFS)
if (defaultStorageType === 'opfs') {
Copy link
Collaborator

@adamziel adamziel Jan 22, 2026

Choose a reason for hiding this comment

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

This if is pretty generic. It looks a bit as if Playground had a concept of defaultStorageType that it does not have. When it does, the implementation may differ. Let's disambiguate the naming here and just say if (insidePersonalWp) { or something to that effect that tells us directly "ah, this is another app and it does things a bit differently".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants