Skip to content

[Website] Accept ZIP drops across the page - #4121

Merged
adamziel merged 2 commits into
trunkfrom
adamziel/improve-zip-import-ux
Jul 25, 2026
Merged

[Website] Accept ZIP drops across the page#4121
adamziel merged 2 commits into
trunkfrom
adamziel/improve-zip-import-ux

Conversation

@adamziel

@adamziel adamziel commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

The ZIP import tab now has a large click target. While that tab is open, dragging a file anywhere over the page shows a stable full-page drop overlay without changing a word in the panel. A non-ZIP drop stays in the panel and reports the problem inline.

The small-breakpoint screenshots document the responsive layout; touch users keep the click-to-choose path.

Screenshots

Desktop drag target

Before During drag
ZIP import before: a small file picker button ZIP import after: a page-wide drop overlay

Small-breakpoint layout

Before During drag
ZIP import before at the small breakpoint ZIP import with a page-wide drop overlay at the small breakpoint

Testing

  1. Drag a ZIP over different parts of the page and confirm one full-page overlay appears while the panel copy remains unchanged.
  2. Cross child-element boundaries and confirm the overlay does not blink.
  3. Drop the ZIP and confirm the import starts.
  4. Click the target and confirm the file picker still works.
  5. Drop a non-ZIP file and confirm the inline message appears.
@adamziel
adamziel requested review from a team, brandonpayton and Copilot July 20, 2026 16:27

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.

This PR updates Playground ZIP export/import to round-trip complete site state (including user-owned wp-content and wp-config.php) while preserving runtime-managed files, and improves the website ZIP import UX with drag-and-drop plus progress reporting.

Changes:

  • Export now produces a “formatVersion: 2” ZIP containing full wp-content, wp-config.php, and a manifest; import treats that content as authoritative while retaining Playground runtime files and restoring omitted stock files for legacy exports.
  • Website import flow now imports into first-boot MEMFS before the initial OPFS copy, supports click/drop, and reports phase + real persistence progress inline.
  • Replaces the public wpContentFilesExcludedFromExport export with playgroundRuntimeWpContentPaths and updates call sites/tests accordingly.

Reviewed changes

Copilot reviewed 22 out of 27 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
packages/playground/website/src/lib/state/redux/site-management-api-middleware.ts Adds ZIP-to-new-site creation, first-boot initialization integration, and OPFS sync progress reporting.
packages/playground/website/src/lib/state/redux/site-first-boot-initializer.ts Introduces a registry to run initialization work before the first OPFS copy.
packages/playground/website/src/lib/state/redux/boot-site-client.ts Runs first-boot initializers during client boot and reports OPFS sync error on initializer failure.
packages/playground/website/src/lib/state/redux/boot-site-client.spec.ts Adds coverage ensuring the initializer runs before the initial OPFS copy.
packages/playground/website/src/github/import-from-github.ts Switches to new runtime-path list and changes restore semantics for runtime-managed files.
packages/playground/website/src/github/github-export-form/form.tsx Updates export exclusion list to the new runtime-paths constant.
packages/playground/website/src/github/github-export-form/form.spec.tsx Updates mocks to new export symbol name.
packages/playground/website/src/components/toolbar-buttons/download-as-zip.tsx Updates ZIP download to use the new complete export behavior.
packages/playground/website/src/components/saved-playgrounds-panel/style.module.css Adds styles for drag-and-drop ZIP import UI and inline progress/error state.
packages/playground/website/src/components/saved-playgrounds-panel/index.tsx Reworks ZIP import UX: dropzone, inline progress/errors, and new-site-from-ZIP import path.
packages/playground/website/playwright/e2e/opfs.spec.ts Updates e2e flows for new import UI and verifies persistence of customized theme files after reboot.
packages/playground/remote-access/src/remote-access-direct-tunnel.ts Updates remote tunnel ZIP export to use the new complete export behavior.
packages/playground/personal-wp/src/lib/hooks/use-backup.ts Updates personal-wp backup ZIP export to use the new complete export behavior.
packages/playground/blueprints/src/tests/steps/import-wordpress-files.spec.ts Adds tests for complete exports, runtime file retention, and legacy export restoration behavior.
packages/playground/blueprints/src/lib/utils/wp-content-paths.ts Defines runtime-managed wp-content paths and legacy omitted user paths.
packages/playground/blueprints/src/lib/utils/wp-content-files-excluded-from-exports.ts Removes the old excluded-paths list.
packages/playground/blueprints/src/lib/steps/zip-wp-content.ts Makes ZIP export “complete” by default, adds manifest formatVersion: 2, includes wp-config.php.
packages/playground/blueprints/src/lib/steps/index.ts Re-exports the new runtime-paths constant instead of the old excluded list.
packages/playground/blueprints/src/lib/steps/import-wordpress-files.ts Adds format-version handling, runtime-file retention, legacy restoration, and progress tracker captions/percentages.
packages/playground/blueprints/src/index.ts Re-exports the new runtime-paths constant publicly.
packages/playground/blueprints/public/blueprint-v2-schema-validator.js Updates generated schema refs (validator artifact).
packages/playground/blueprints/public/blueprint-schema.json Updates generated schema refs (schema artifact).

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

Comment thread packages/playground/website/src/github/import-from-github.ts Outdated
Comment thread packages/playground/website/src/github/import-from-github.ts
Comment thread packages/playground/blueprints/src/lib/steps/zip-wp-content.ts
Comment thread packages/playground/website/src/lib/state/redux/site-management-api-middleware.ts Outdated
Comment thread packages/playground/website/src/lib/state/redux/site-management-api-middleware.ts Outdated
Comment thread packages/playground/website/src/lib/state/redux/boot-site-client.ts
@adamziel
adamziel force-pushed the adamziel/improve-zip-import-ux branch 2 times, most recently from 0c06b2c to e3addef Compare July 20, 2026 19:26
@adamziel adamziel changed the title [Website] Export complete site ZIPs with drag-and-drop import progress Jul 20, 2026
@adamziel
adamziel force-pushed the adamziel/improve-zip-import-ux branch 7 times, most recently from 9e03807 to d3313f1 Compare July 21, 2026 00:41
@adamziel adamziel changed the title [Website] Export complete site ZIPs with full-view import loading Jul 21, 2026
@adamziel
adamziel changed the base branch from trunk to adamziel/zip-import-progress July 21, 2026 00:42
@adamziel
adamziel force-pushed the adamziel/improve-zip-import-ux branch from d3313f1 to 2b41603 Compare July 21, 2026 01:35
@adamziel
adamziel force-pushed the adamziel/improve-zip-import-ux branch from 2b41603 to 8dc1886 Compare July 21, 2026 10:33
@adamziel
adamziel force-pushed the adamziel/zip-import-progress branch 2 times, most recently from 4df3e25 to a27aaa9 Compare July 21, 2026 10:51
@adamziel
adamziel force-pushed the adamziel/improve-zip-import-ux branch from 8dc1886 to 206c30c Compare July 21, 2026 10:51
@adamziel
adamziel force-pushed the adamziel/improve-zip-import-ux branch from 206c30c to 6c27c56 Compare July 21, 2026 11:13
@adamziel
adamziel force-pushed the adamziel/zip-import-progress branch from 32ef0d7 to ab958d7 Compare July 21, 2026 11:16
@adamziel
adamziel force-pushed the adamziel/improve-zip-import-ux branch from 6c27c56 to 639dc00 Compare July 21, 2026 11:16
@adamziel
adamziel force-pushed the adamziel/zip-import-progress branch from ab958d7 to 9d68a74 Compare July 21, 2026 12:20
@adamziel
adamziel force-pushed the adamziel/improve-zip-import-ux branch 2 times, most recently from bc593a8 to ccd8a87 Compare July 21, 2026 12:28
@adamziel
adamziel force-pushed the adamziel/zip-import-progress branch from 9d68a74 to c864646 Compare July 21, 2026 12:28
@adamziel
adamziel force-pushed the adamziel/improve-zip-import-ux branch from ccd8a87 to 7337ca0 Compare July 21, 2026 12:40
@adamziel
adamziel force-pushed the adamziel/zip-import-progress branch 2 times, most recently from 63a41a9 to 2b0a33c Compare July 21, 2026 12:50
@adamziel
adamziel force-pushed the adamziel/improve-zip-import-ux branch from 7337ca0 to 24a9426 Compare July 21, 2026 12:50
adamziel added a commit that referenced this pull request Jul 21, 2026
… defaults (#4130)

A version-2 Playground ZIP now defines user-owned `wp-content`: archived
files replace the boot defaults, and a missing user file means it was
deleted. Legacy runtime artifacts remain owned by the importing
Playground through #4141.

Pre-versioned exports remain partial. When one omits a stock plugin,
theme, or database directory, import retains the fresh installation's
copy. An archived copy still wins when present.

This preserves customized default themes such as BrewCommerce's purple
Twenty Twenty-Five background through export, import, and reload without
treating stock themes as runtime files.

## Testing

Browser coverage imports a pre-versioned partial export, then separately
customizes Twenty Twenty-Five, exports it, imports it into a new
Playground, reloads, and checks both the theme file and rendered
background. The full stack passes 488 Blueprint tests, 347 website
tests, both packages' lint and typecheck, and 10 Chromium ZIP-import
scenarios.

## Stack

1. [#4139 — Export complete versioned Playground ZIP
snapshots](#4139)
2. [#4141 — Exclude legacy runtime artifacts from site
snapshots](#4141)
3. [#4130 — Import versioned ZIP user content while retaining legacy
defaults](#4130)
4. [#4131 — Import ZIP contents before initial browser
persistence](#4131)
5. [#4132 — Keep ZIP preparation visible until the imported site is
ready](#4132)
6. [#4121 — Accept ZIP drops across the
page](#4121)
@adamziel
adamziel force-pushed the adamziel/zip-import-progress branch from 2b0a33c to 2bba7ff Compare July 21, 2026 13:45
@adamziel
adamziel force-pushed the adamziel/improve-zip-import-ux branch from 24a9426 to 7c67c5d Compare July 21, 2026 13:46
adamziel added a commit that referenced this pull request Jul 21, 2026
… defaults (#4130)

A version-2 Playground ZIP now defines user-owned `wp-content`: archived
files replace the boot defaults, and a missing user file means it was
deleted. Legacy runtime artifacts remain owned by the importing
Playground through #4141.

Pre-versioned exports remain partial. When one omits a stock plugin,
theme, or database directory, import retains the fresh installation's
copy. An archived copy still wins when present.

This preserves customized default themes such as BrewCommerce's purple
Twenty Twenty-Five background through export, import, and reload without
treating stock themes as runtime files.

## Testing

Browser coverage imports a pre-versioned partial export, then separately
customizes Twenty Twenty-Five, exports it, imports it into a new
Playground, reloads, and checks both the theme file and rendered
background. The full stack passes 488 Blueprint tests, 347 website
tests, both packages' lint and typecheck, and 10 Chromium ZIP-import
scenarios.

## Stack

1. [#4139 — Export complete versioned Playground ZIP
snapshots](#4139)
2. [#4141 — Exclude legacy runtime artifacts from site
snapshots](#4141)
3. [#4130 — Import versioned ZIP user content while retaining legacy
defaults](#4130)
4. [#4131 — Import ZIP contents before initial browser
persistence](#4131)
5. [#4132 — Keep ZIP preparation visible until the imported site is
ready](#4132)
6. [#4121 — Accept ZIP drops across the
page](#4121)
adamziel added a commit that referenced this pull request Jul 21, 2026
…me files (#4141)

Make new ZIP exports preserve the site’s plugins, themes, uploads, and
database exactly. Make imports remove any pre-bundled Playground's
runtime files (such as `0-playground.php` mu-plugin) and lean on their
latest versions shipped in the `/internal` directory.

## Backwards compatibility

Playground's ZIP export format evolved over time. Here's how each
version is handled after this PR:

| ZIP | Site files in `wp-content` | Playground runtime artifacts | If a
theme or plugin is missing |
| --- | --- | --- | --- |
| Before this PR | Custom plugins, themes, and uploads. Default
`themes/twenty*` directories were included only by the self-contained
export. `database/` may be absent. | Some January–May 2024 ZIPs contain
the old SQLite MU plugin. | Backfill well-known paths from the site
loaded right before the import: Akismet, Hello Dolly, WordPress
Importer, default themes, and `database/`. This is a best-effort
heuristic. We can't distinguish a deleted theme from a missing theme. |
| `formatVersion: 2` after this PR | Every site path then on disk:
plugins, themes, uploads, `database/`, and a custom `db.php` if the user
specifically created one. | None. | Leave it missing. |

On every import, archived Playground files are removed. The new
Playground supplies them.

## Playground runtime files

These four old paths are always treated as Playground-provided runtime
files:

- `mu-plugins/sqlite-database-integration`
- `mu-plugins/playground-includes`
- `mu-plugins/0-playground.php`
- `mu-plugins/0-sqlite.php`

From January 29 through May 8, 2024, the self-contained exporter
included `mu-plugins/sqlite-database-integration`. It also included
`mu-plugins/0-sqlite.php` when present. It already omitted
`mu-plugins/0-playground.php` and `mu-plugins/playground-includes`;
those entries handle stale sites, GitHub trees, and hand-built ZIPs.

`db.php` needs a separate rule because it can be a custom WordPress
drop-in. During legacy WordPress boot, `writeLegacyDbPhp()` creates
Playground's copy through `generateDbPhpContent()`. That function writes
`@playground-managed` into the file header. Only a `db.php` with that
marker is treated as a Playground file. An unmarked `db.php` stays with
the site.

Before January 29, 2024, SQLite used
`plugins/sqlite-database-integration`. This PR leaves that path as site
content because it is also a valid user-installed plugin path.

**Breaking change:** `wpContentFilesExcludedFromExport` is no longer
public. `getLegacyPlaygroundRuntimeWpContentPaths()` exposes the smaller
runtime rule.

## Testing

Tests cover a modified Twenty Twenty-Five theme, a deleted v2 theme, a
theme omitted by a pre-v2 ZIP, archived runtime files, marked and custom
`db.php` files, failed runtime staging, and archives without
`wp-content`. The full stack passes 488 Blueprint tests, 347 website
tests, package lint and typecheck, and 10 Chromium ZIP-import scenarios.

## Stack

1. [#4139 — Export complete versioned Playground ZIP
snapshots](#4139)
— merged
2. [#4141 — Exclude legacy runtime artifacts from site
snapshots](#4141)
— includes #4130
3. [#4131 — Import ZIP contents before initial browser
persistence](#4131)
4. [#4132 — Keep ZIP preparation visible until the imported site is
ready](#4132)
5. [#4121 — Accept ZIP drops across the
page](#4121)
adamziel added a commit that referenced this pull request Jul 21, 2026
ZIP imports used to persist a fresh WordPress installation before
applying the
archive. The archive then rewrote that stored filesystem:

1. Boot fresh WordPress in MEMFS.
2. Copy the fresh site to browser storage.
3. Apply the ZIP to the running site.
4. Flush the rewritten site to browser storage again.

This PR applies the ZIP during the new site's first boot, before its
initial
browser-storage copy:

1. Boot fresh WordPress in MEMFS.
2. Apply the ZIP in MEMFS.
3. Copy the finished filesystem to browser storage once.

`createNewSiteFromZip()` registers the import as first-boot work. The
boot path
runs that work before the initial MEMFS-to-OPFS copy, and the call
reports
success only after that copy finishes.

If ZIP initialization or the first OPFS copy fails, the newly created
stored
site is removed and the previously active Playground is selected again.
When
OPFS is unavailable, the import uses a fresh temporary Playground. Its
new slug
forces React to boot a new iframe instead of reusing the previous
runtime.

The ZIP picker now delegates this lifecycle to `createNewSiteFromZip()`
instead
of coordinating site creation, client boot, import, and persistence
through
component effects.

This PR does not change which files a ZIP contains or how versioned and
legacy
ZIP contents are interpreted. Those rules are handled by #4141 and
#4130. This
PR changes when the imported filesystem is written to browser storage.

## Testing

1. Import a ZIP from both temporary and saved Playgrounds and confirm
the new
   site persists after reload.
2. Import a malformed ZIP and confirm no incomplete stored site remains.
3. Confirm the previously active Playground remains selected after a
failed
   stored-site import.

Unit coverage checks that ZIP initialization runs before the initial
OPFS copy
and that failed-import cleanup selects the requested previous site once.
Browser
coverage checks that a failed import leaves the stored-site list
unchanged,
including after reload.

## Stack

1. [#4139 — Export complete versioned Playground ZIP
snapshots](#4139)
— merged
2. [#4141 — Export and import all site files except Playground runtime
files](#4141) —
merged
3. [#4130 — Import versioned ZIP user content while retaining legacy
defaults](#4130) —
merged
4. [#4131 — Import ZIP contents before initial browser
persistence](#4131)
5. [#4132 — Keep ZIP preparation visible until the imported site is
ready](#4132)
6. [#4121 — Accept ZIP drops across the
page](#4121)
@adamziel
adamziel force-pushed the adamziel/zip-import-progress branch from 9c56cbf to fe7b422 Compare July 21, 2026 20:37
@adamziel
adamziel force-pushed the adamziel/improve-zip-import-ux branch 4 times, most recently from 6bbde70 to 2c2fa98 Compare July 21, 2026 22:43
@adamziel
adamziel force-pushed the adamziel/zip-import-progress branch 2 times, most recently from 01522ad to 24e6693 Compare July 24, 2026 17:14
Base automatically changed from adamziel/zip-import-progress to trunk July 24, 2026 22:04
@adamziel
adamziel force-pushed the adamziel/improve-zip-import-ux branch from 2c2fa98 to 4193c51 Compare July 24, 2026 22:09
@adamziel
adamziel merged commit f7ef085 into trunk Jul 25, 2026
52 of 53 checks passed
@adamziel
adamziel deleted the adamziel/improve-zip-import-ux branch July 25, 2026 00:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment