diff --git a/content/tutorial/common/src/__client.js b/content/tutorial/common/src/__client.js index 1a419ed0b..a6741586f 100644 --- a/content/tutorial/common/src/__client.js +++ b/content/tutorial/common/src/__client.js @@ -1,69 +1,36 @@ -window.addEventListener('message', async (e) => { - if (e.data.type === 'fetch') { - const names = e.data.names; - - const transformed = await Promise.all( - names.map(async (name) => { - const res = await fetch(name); - return { - name, - code: await res.text() - }; - }) - ); - - const css_files = []; - - for (const { name, code } of transformed) { - if ( - name.endsWith('.svelte') && - code.includes('svelte&type=style&lang.css') - ) { - css_files.push(name + '?svelte&type=style&lang.css'); - } - } - - if (css_files.length > 0) { - const css_transformed = await Promise.all( - css_files.map(async (name) => { - const res = await fetch(name); - return { - name, - code: await res.text() - }; - }) - ); - - transformed.push(...css_transformed); - } +function post(data) { + parent.postMessage(data, '*'); +} - parent.postMessage( - { - type: 'fetch-result', - data: transformed - }, - '*' - ); - } -}); +function ping() { + post({ + type: 'ping', + path: location.pathname + location.search + location.hash + }); +} -let can_focus = false; +function pause() { + post({ type: 'ping-pause' }); +} -window.addEventListener('pointerdown', (e) => { - can_focus = true; -}); +// Hack into the alert that's used in some tutorials and send a message prior to the alert, +// else the parent thinks we lost contact and wrongfully reloads the page. +// The drawback is that alert is no longer blocking, but no tutorial relies on this. +const alert = window.alert; +window.alert = (message) => { + pause(); -window.addEventListener('pointerup', (e) => { - can_focus = false; -}); + setTimeout(() => { + alert(message); + }); +}; -window.addEventListener('keydown', (e) => { - can_focus = true; -}); +let can_focus = false; -window.addEventListener('keyup', (e) => { - can_focus = false; -}); +window.addEventListener('pointerdown', (e) => can_focus = true); +window.addEventListener('pointerup', (e) => can_focus = false); +window.addEventListener('keydown', (e) => can_focus = true); +window.addEventListener('keyup', (e) => can_focus = false); /** * The iframe sometimes takes focus control in ways we can't prevent @@ -78,12 +45,7 @@ window.addEventListener('focusin', (e) => { if (e.target.tagName === 'BODY' && e.relatedTarget) return; // otherwise, broadcast an event that causes the editor to reclaim focus - parent.postMessage( - { - type: 'iframe_took_focus' - }, - '*' - ); + post({ type: 'iframe_took_focus' }); }); window.addEventListener('click', (e) => { @@ -102,47 +64,38 @@ window.addEventListener('click', (e) => { } }); -function ping() { - parent.postMessage( - { - type: 'ping', - data: { - path: location.pathname + location.search + location.hash - } - }, - '*' - ); -} +window.addEventListener('visibilitychange', () => { + if (document.visibilityState === 'visible') { + ping(); + } else { + pause(); + } +}); + +let previous_href = location.href; -let pre_url = location.href; const url_observer = new MutationObserver(() => { - if (location.href !== pre_url) { - pre_url = location.href; + if (location.href !== previous_href) { + previous_href = location.href; ping(); } }); -url_observer.observe(document, { subtree: true, childList: true, attributes: true }); +url_observer.observe(document, { + subtree: true, + childList: true, + attributes: true +}); + +setInterval(ping, 100); ping(); if (import.meta.hot) { import.meta.hot.on('vite:beforeUpdate', (event) => { - parent.postMessage( - { - type: 'hmr', - data: event.updates - }, - '*' - ); + post({ type: 'hmr', data: event.updates }); }); import.meta.hot.on('svelte:warnings', (data) => { - parent.postMessage( - { - type: 'warnings', - data - }, - '*' - ); + post({ type: 'warnings', data }); }); } diff --git a/src/routes/tutorial/[slug]/Output.svelte b/src/routes/tutorial/[slug]/Output.svelte index 59b94af4e..013661cb2 100644 --- a/src/routes/tutorial/[slug]/Output.svelte +++ b/src/routes/tutorial/[slug]/Output.svelte @@ -34,6 +34,13 @@ }; }); + afterNavigate(() => { + clearTimeout(timeout); + }); + + /** @type {any} */ + let timeout; + /** @param {MessageEvent} e */ async function handle_message(e) { if (e.origin !== $base) return; @@ -41,8 +48,20 @@ if (paused) return; if (e.data.type === 'ping') { - path = e.data.data.path ?? path; + path = e.data.path; loading = false; + + clearTimeout(timeout); + timeout = setTimeout(() => { + if (dev && !iframe) return; + + // we lost contact, refresh the page + loading = true; + set_iframe_src($base + path); + loading = false; + }, 1000); + } else if (e.data.type === 'ping-pause') { + clearTimeout(timeout); } else if (e.data.type === 'warnings') { warnings.update(($warnings) => ({ ...$warnings,