From b414f117526ff2315ea40e86c1a915884633a989 Mon Sep 17 00:00:00 2001 From: tomoam <29677552+tomoam@users.noreply.github.com> Date: Tue, 16 May 2023 22:42:59 +0900 Subject: [PATCH 1/4] chore: improve detection of iOS devices --- src/lib/client/adapters/webcontainer/index.js | 9 +++++--- src/lib/client/adapters/webcontainer/utils.js | 9 +++++++- src/routes/tutorial/[slug]/Loading.svelte | 23 +++++++++++++++++-- src/routes/tutorial/[slug]/Output.svelte | 21 +++++++++++++++-- src/routes/tutorial/[slug]/adapter.js | 8 +++++-- 5 files changed, 60 insertions(+), 10 deletions(-) diff --git a/src/lib/client/adapters/webcontainer/index.js b/src/lib/client/adapters/webcontainer/index.js index 3b98cd5ca..e2cd4eee7 100644 --- a/src/lib/client/adapters/webcontainer/index.js +++ b/src/lib/client/adapters/webcontainer/index.js @@ -4,7 +4,7 @@ import AnsiToHtml from 'ansi-to-html'; import * as yootils from 'yootils'; import { escape_html, get_depth } from '../../../utils.js'; import { ready } from '../common/index.js'; -import { isWebContainerSupported } from './utils.js'; +import { is_webcontainer_supported, is_ios_device } from './utils.js'; /** * @typedef {import("../../../../routes/tutorial/[slug]/state.js").CompilerWarning} CompilerWarning @@ -23,11 +23,14 @@ let vm; * @param {import('svelte/store').Writable<{ value: number, text: string }>} progress * @param {import('svelte/store').Writable} logs * @param {import('svelte/store').Writable>} warnings + * @param {boolean} force * @returns {Promise} */ -export async function create(base, error, progress, logs, warnings) { - if (!isWebContainerSupported()) { +export async function create(base, error, progress, logs, warnings, force) { + if (!is_webcontainer_supported()) { throw new Error('WebContainers are not supported by Safari 16.3 or earlier'); + } else if (is_ios_device() && !force) { + throw new Error('maybe cause out of memory'); } progress.set({ value: 0, text: 'loading files' }); diff --git a/src/lib/client/adapters/webcontainer/utils.js b/src/lib/client/adapters/webcontainer/utils.js index 1d6c6a569..c9941faa8 100644 --- a/src/lib/client/adapters/webcontainer/utils.js +++ b/src/lib/client/adapters/webcontainer/utils.js @@ -2,7 +2,7 @@ * Checks if WebContainer is supported on the current browser. * This function is borrowed from [stackblitz/webcontainer-docs](https://github.com/stackblitz/webcontainer-docs/blob/369dd58b2749b085ed7642f22108a9bcbcd68fc4/docs/.vitepress/theme/components/Examples/WCEmbed/utils.ts#L4-L29) */ -export function isWebContainerSupported() { +export function is_webcontainer_supported() { const hasSharedArrayBuffer = 'SharedArrayBuffer' in window; const looksLikeChrome = navigator.userAgent.toLowerCase().includes('chrome'); const looksLikeFirefox = navigator.userAgent.includes('Firefox'); @@ -28,3 +28,10 @@ export function isWebContainerSupported() { return false; } } + +export function is_ios_device() { + return ( + /iPad|iPhone/.test(window.navigator.userAgent) || + (window.navigator.userAgent.includes('Macintosh') && window.navigator.maxTouchPoints > 1) + ); +} diff --git a/src/routes/tutorial/[slug]/Loading.svelte b/src/routes/tutorial/[slug]/Loading.svelte index 7563a6514..6a88eb098 100644 --- a/src/routes/tutorial/[slug]/Loading.svelte +++ b/src/routes/tutorial/[slug]/Loading.svelte @@ -1,5 +1,9 @@
{#if error} - {#if !isWebContainerSupported()} + {#if !is_webcontainer_supported()}

This app requires modern web platform features. Please use a browser other than Safari.

+ {:else if is_ios_device() && !forced_booted} +

On iOS devices, your browser may run out of memory.

+ {:else} {error.message}

Yikes!

@@ -145,6 +157,13 @@ height: 10rem; } + button { + background: var(--sk-theme-1); + color: white; + padding: 0.5rem; + height: 4rem; + } + @media (prefers-color-scheme: dark) { .loading { --faded: #444; diff --git a/src/routes/tutorial/[slug]/Output.svelte b/src/routes/tutorial/[slug]/Output.svelte index a633b8d84..c8013e99e 100644 --- a/src/routes/tutorial/[slug]/Output.svelte +++ b/src/routes/tutorial/[slug]/Output.svelte @@ -4,7 +4,8 @@ import { browser, dev } from '$app/environment'; import Chrome from './Chrome.svelte'; import Loading from './Loading.svelte'; - import { base, error, logs, progress, subscribe } from './adapter'; + import { files } from './state.js'; + import { base, create_adapter, error, logs, progress, reset, subscribe } from './adapter.js'; /** @type {import('$lib/types').Exercise} */ export let exercise; @@ -89,6 +90,15 @@ iframe.style.visibility = 'visible'; } } + + let forced_booted = false; + + function force_boot_adapter() { + $error = null; + forced_booted = true; + create_adapter(true); + reset($files); + } @@ -117,7 +127,14 @@ {/if} {#if paused || loading || $error} - + force_boot_adapter()} + /> {/if}
diff --git a/src/routes/tutorial/[slug]/adapter.js b/src/routes/tutorial/[slug]/adapter.js index cca55d3ad..e7d48bcba 100644 --- a/src/routes/tutorial/[slug]/adapter.js +++ b/src/routes/tutorial/[slug]/adapter.js @@ -21,11 +21,11 @@ export const warnings = writable({}); /** @type {Promise} */ let ready = new Promise(() => {}); -if (browser) { +export function create_adapter(force = false) { ready = new Promise(async (fulfil, reject) => { try { const module = await import('$lib/client/adapters/webcontainer/index.js'); - const adapter = await module.create(base, error, progress, logs, warnings); + const adapter = await module.create(base, error, progress, logs, warnings, force); fulfil(adapter); } catch (error) { @@ -34,6 +34,10 @@ if (browser) { }); } +if (browser) { + create_adapter(); +} + /** @typedef {'reload'} EventName */ /** @type {Map void>>} */ From 76f98a6db45a8dff1d88377cc84879ecdde5fcc1 Mon Sep 17 00:00:00 2001 From: tomoam <29677552+tomoam@users.noreply.github.com> Date: Wed, 17 May 2023 08:28:32 +0900 Subject: [PATCH 2/4] fix a message --- src/lib/client/adapters/webcontainer/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/client/adapters/webcontainer/index.js b/src/lib/client/adapters/webcontainer/index.js index e2cd4eee7..f7653cfa4 100644 --- a/src/lib/client/adapters/webcontainer/index.js +++ b/src/lib/client/adapters/webcontainer/index.js @@ -30,7 +30,7 @@ export async function create(base, error, progress, logs, warnings, force) { if (!is_webcontainer_supported()) { throw new Error('WebContainers are not supported by Safari 16.3 or earlier'); } else if (is_ios_device() && !force) { - throw new Error('maybe cause out of memory'); + throw new Error('On iOS devices, your browser may run out of memory'); } progress.set({ value: 0, text: 'loading files' }); From 8c22e3a2f4c09317a34f222f112eba07ebfe1d0b Mon Sep 17 00:00:00 2001 From: tomoam <29677552+tomoam@users.noreply.github.com> Date: Wed, 17 May 2023 12:08:23 +0900 Subject: [PATCH 3/4] add a comment --- src/lib/client/adapters/webcontainer/utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/client/adapters/webcontainer/utils.js b/src/lib/client/adapters/webcontainer/utils.js index c9941faa8..08cecaedd 100644 --- a/src/lib/client/adapters/webcontainer/utils.js +++ b/src/lib/client/adapters/webcontainer/utils.js @@ -32,6 +32,7 @@ export function is_webcontainer_supported() { export function is_ios_device() { return ( /iPad|iPhone/.test(window.navigator.userAgent) || + // on iPadOS 13 or later, UserAgent is the same as Safari on MacOS, so maxTouchPoints is used to detect it (window.navigator.userAgent.includes('Macintosh') && window.navigator.maxTouchPoints > 1) ); } From a9c36850959333f993e51d9de423f949198ddbb6 Mon Sep 17 00:00:00 2001 From: tomoam <29677552+tomoam@users.noreply.github.com> Date: Wed, 17 May 2023 12:09:57 +0900 Subject: [PATCH 4/4] simplify the button text --- src/routes/tutorial/[slug]/Loading.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/tutorial/[slug]/Loading.svelte b/src/routes/tutorial/[slug]/Loading.svelte index 6a88eb098..cd0876d0c 100644 --- a/src/routes/tutorial/[slug]/Loading.svelte +++ b/src/routes/tutorial/[slug]/Loading.svelte @@ -29,7 +29,7 @@

This app requires modern web platform features. Please use a browser other than Safari.

{:else if is_ios_device() && !forced_booted}

On iOS devices, your browser may run out of memory.

- + {:else} {error.message}

Yikes!