diff --git a/static/playground.html b/static/playground.html
new file mode 100644
index 0000000..55d703e
--- /dev/null
+++ b/static/playground.html
@@ -0,0 +1,62 @@
+
+
+
+
+
+ Scramjet
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/static/playground.js b/static/playground.js
new file mode 100644
index 0000000..bdf8803
--- /dev/null
+++ b/static/playground.js
@@ -0,0 +1,217 @@
+const scramjet = new ScramjetController({
+ files: {
+ wasm: "/scram/scramjet.wasm.js",
+ worker: "/scram/scramjet.worker.js",
+ client: "/scram/scramjet.client.js",
+ shared: "/scram/scramjet.shared.js",
+ sync: "/scram/scramjet.sync.js",
+ },
+ siteFlags: {
+ "https://worker-playground.glitch.me/.*": {
+ serviceworkers: true,
+ },
+ },
+});
+
+scramjet.init("./sw.js");
+
+const connection = new BareMux.BareMuxConnection("/baremux/worker.js");
+const flex = css`
+ display: flex;
+`;
+const col = css`
+ flex-direction: column;
+`;
+
+const store = $store(
+ {
+ url: "https://google.com",
+ wispurl:
+ _CONFIG?.wispurl ||
+ (location.protocol === "https:" ? "wss" : "ws") +
+ "://" +
+ location.host +
+ "/wisp/",
+ bareurl:
+ _CONFIG?.bareurl ||
+ (location.protocol === "https:" ? "https" : "http") +
+ "://" +
+ location.host +
+ "/bare/",
+ proxy: "",
+ },
+ { ident: "settings", backing: "localstorage", autosave: "auto" }
+);
+connection.setTransport("/epoxy/index.mjs", [{ wisp: store.wispurl }]);
+
+function PlaygroundApp() {
+ this.css = `
+ width: 100%;
+ height: 100%;
+ color: #f0fef4;
+ display: flex;
+ padding: 0.5em;
+ box-sizing: border-box;
+ gap: 0.5em;
+
+
+ .codesplit {
+ width: 50%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+
+ gap: 0.5em;
+ }
+
+ .mcontainer {
+ background: #1e1e1e;
+ h2 {
+ margin: 0.1em;
+ }
+
+ border: 1px solid #313131;
+ flex-basis: 100%;
+ display: flex;
+ flex-direction: column;
+ }
+ .monaco {
+ flex: 1;
+ }
+
+ .frame {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ gap: 0.5em;
+ iframe {
+ width: 100%;
+
+ border: 1px solid #313131;
+ }
+ }
+ .config {
+ border: 1px solid #313131;
+ background: #1e1e1e;
+ padding: 0.5em;
+ }
+ `;
+
+ this.fakeorigin = "https://sandboxedorigin.com";
+ this.mount = async () => {
+ const monaco = await import(
+ "https://cdn.jsdelivr.net/npm/monaco-editor/+esm"
+ );
+
+ monaco.editor.setTheme("vs-dark");
+ const html = monaco.editor.create(this.htmlbox, {
+ value: `
+
+
+
+
+
+
+
+
+
+ Scramjet Sandbox Playground
+
+ Scramjet allows any webpage to be run on the same origin in an isolated manner
+
+
+
+
+
+
+`,
+ language: "html",
+ });
+ const js = monaco.editor.create(this.jsbox, {
+ value: `function checkOrigin() {
+ alert("origin: " + window.origin);
+}
+
+// external resources fetched will be re-
+// directed to the WISP server
+function loadResource(url) {
+ fetch(url).then(r => {
+ console.log("loaded", r);
+ })
+}`,
+ language: "javascript",
+ });
+ const css = monaco.editor.create(this.cssbox, {
+ value: `body, html {
+ background: #1e1e1e;
+ color: white;
+ width: 100%;
+ height: 100%;
+}`,
+ language: "css",
+ });
+ let oldjs;
+ let oldhtml;
+ let oldcss;
+
+ setInterval(async () => {
+ if (
+ oldjs !== js.getValue() ||
+ oldhtml !== html.getValue() ||
+ oldcss !== css.getValue()
+ ) {
+ oldjs = js.getValue();
+ oldhtml = html.getValue();
+ oldcss = css.getValue();
+
+ (await navigator.serviceWorker.ready).active.postMessage({
+ type: "playgroundData",
+ html: html.getValue(),
+ css: css.getValue(),
+ js: js.getValue(),
+ origin: this.fakeorigin,
+ });
+
+ this.frame.src = scramjet.encodeUrl(this.fakeorigin);
+ }
+ }, 1000);
+ };
+
+ return html`
+
+ `;
+}
+
+window.addEventListener("load", async () => {
+ document.body.appendChild(h(PlaygroundApp));
+});
diff --git a/static/sw.js b/static/sw.js
index 613e50c..ad960d3 100644
--- a/static/sw.js
+++ b/static/sw.js
@@ -18,3 +18,35 @@ async function handleRequest(event) {
self.addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event));
});
+
+let playgroundData;
+self.addEventListener("message", ({ data }) => {
+ if (data.type === "playgroundData") {
+ playgroundData = data;
+ }
+});
+
+scramjet.addEventListener("request", (e) => {
+ let headers = {};
+ if (e.url.href === playgroundData.origin + "/") {
+ headers["content-type"] = "text/html";
+ e.response = new Response(playgroundData.html, {
+ headers,
+ });
+ } else if (e.url.href === playgroundData.origin + "/style.css") {
+ headers["content-type"] = "text/css";
+ e.response = new Response(playgroundData.css, {
+ headers,
+ });
+ } else if (e.url.href === playgroundData.origin + "/script.js") {
+ headers["content-type"] = "application/javascript";
+ e.response = new Response(playgroundData.js, {
+ headers,
+ });
+ } else {
+ return;
+ }
+
+ e.response.rawHeaders = headers;
+ e.response.finalURL = e.url;
+});
diff --git a/static/ui.js b/static/ui.js
index dd3b10e..a4da0d8 100644
--- a/static/ui.js
+++ b/static/ui.js
@@ -122,7 +122,7 @@ function Config() {
`;
}
-function App() {
+function BrowserApp() {
this.urlencoded = "";
this.css = `
width: 100%;
@@ -253,9 +253,8 @@ function App() {
`;
}
-
window.addEventListener("load", async () => {
- document.body.appendChild(h(App));
+ document.body.appendChild(h(BrowserApp));
function b64(buffer) {
let binary = "";
const bytes = new Uint8Array(buffer);