Skip to content

Commit

Permalink
Mount a directory from the browser (#1482)
Browse files Browse the repository at this point in the history
## Motivation for the change, related issues

We would like to enable developers to mount local directories into their
Playground-based projects by prompting users to select a directory.
After the directory is selected the folder can be mounted into
Playground and used by it.

For example, this could be used to load local plugins and themes into
Playground.

## Implementation details

This PR adds a window message listener that takes the provided directory
handle and mounts it to the mountpoint.

```javascript
window.showDirectoryPicker().then(function (directoryHandle) {
	window.parent.postMessage({
		type: 'mount-directory-handle',
		directoryHandle,
		mountpoint: '/wordpress/wp-content/uploads/markdown/',
	});
});
```

## Testing Instructions (or ideally a Blueprint)

- Add this code after line 280 in
`packages/playground/remote/src/lib/worker-thread.ts`
```


	primaryPhp.writeFile(
		'/wordpress/mount.php',
		`<!DOCTYPE html>
		<html>
		<head>
			<title>Directory picker</title>
		</head>
		<body>
			<button id="pick">Pick directory</button>
			<script>
				document.getElementById('pick').addEventListener('click', function() {
					if (!('showDirectoryPicker' in window)) {
						alert('Your browser does not support the Directory Picker API');
						return;
					}
					window.showDirectoryPicker().then(function(directoryHandle) {
						window.parent.postMessage(
							{
								type: 'mount-directory-handle',
								directoryHandle,
								mountpoint: '/wordpress/wp-content/uploads/markdown/',
							}
						);
					});
				});
			</script>
		</body>
		</html>`
	);
```

- open this blueprint
http://localhost:5400/website-server/?url=/mount.php
- Click the button and select a local folder that contains an HTML file
- Change the URL in the Playground header to
`/wp-content/uploads/markdown/YOUR-HTML-FILE.html`
- Confirm that your file was loaded
  • Loading branch information
bgrgicak authored Jun 2, 2024
1 parent ef8830a commit 09c2d17
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 1 deletion.
15 changes: 15 additions & 0 deletions packages/docs/site/docs/10-javascript-api/06-mount-data.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Mount data

## Mount a directory from the browser

You can mount a directory from the browser to Playground using the `window.showDirectoryPicker` API. Check the [Browser compatibility](https://developer.mozilla.org/en-US/docs/Web/API/Window/showDirectoryPicker#browser_compatibility) before using this API.

```javascript
window.showDirectoryPicker().then(function (directoryHandle) {
window.parent.postMessage({
type: 'mount-directory-handle',
directoryHandle,
mountpoint: '/wordpress/wp-content/uploads/markdown/',
});
});
```
24 changes: 23 additions & 1 deletion packages/playground/remote/src/lib/boot-playground-remote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
exposeAPI,
consumeAPI,
setupPostMessageRelay,
SyncProgressCallback
SyncProgressCallback,
} from '@php-wasm/web';

import type { PlaygroundWorkerEndpoint } from './worker-thread';
Expand Down Expand Up @@ -221,6 +221,7 @@ export async function bootPlaygroundRemote() {
wpFrame,
getOrigin((await playground.absoluteUrl)!)
);
setupMountListener(playground);
if (withNetworking) {
await setupFetchNetworkTransport(workerApi);
}
Expand All @@ -242,6 +243,27 @@ function getOrigin(url: string) {
return new URL(url, 'https://example.com').origin;
}

function setupMountListener(playground: WebClientMixin) {
window.addEventListener('message', async (event) => {
if (typeof event.data !== 'object') {
return;
}
if (event.data.type !== 'mount-directory-handle') {
return;
}
if (typeof event.data.directoryHandle !== 'object') {
return;
}
if (!event.data.mountpoint) {
return;
}
await playground.bindOpfs({
opfs: event.data.directoryHandle,
mountpoint: event.data.mountpoint,
});
});
}

function parseVersion<T>(value: string | undefined | null, latest: T) {
if (!value || value === 'latest') {
return latest as string;
Expand Down

0 comments on commit 09c2d17

Please sign in to comment.