diff --git a/apps/svelte.dev/src/lib/utils/events.ts b/apps/svelte.dev/src/lib/utils/events.ts deleted file mode 100644 index 6d811a0fa4..0000000000 --- a/apps/svelte.dev/src/lib/utils/events.ts +++ /dev/null @@ -1,20 +0,0 @@ -export function keyEvent(code: number) { - return function (node: HTMLInputElement, callback: (event: KeyboardEvent) => void) { - node.addEventListener('keydown', handleKeydown); - - /** @param {KeyboardEvent} event */ - function handleKeydown(event: KeyboardEvent) { - if (event.keyCode === code) { - callback.call(node, event); - } - } - - return { - destroy() { - node.removeEventListener('keydown', handleKeydown); - } - }; - }; -} - -export const enter = keyEvent(13); diff --git a/apps/svelte.dev/src/routes/(authed)/playground/[id]/+page.svelte b/apps/svelte.dev/src/routes/(authed)/playground/[id]/+page.svelte index 9dd1ad8127..8e7564066f 100644 --- a/apps/svelte.dev/src/routes/(authed)/playground/[id]/+page.svelte +++ b/apps/svelte.dev/src/routes/(authed)/playground/[id]/+page.svelte @@ -13,6 +13,8 @@ let { data } = $props(); + const STORAGE_KEY = 'svelte:playground'; + let repl = $state() as ReturnType; let name = $state(data.gist.name); let modified = $state(false); @@ -60,9 +62,10 @@ } async function set_files() { + const saved = sessionStorage.getItem(STORAGE_KEY); const hash = location.hash.slice(1); - if (!hash) { + if (!hash && !saved) { repl?.set({ // TODO make this munging unnecessary files: structuredClone(data.gist.components).map(munge) @@ -73,16 +76,27 @@ } try { - let files = JSON.parse(await decode_and_decompress_text(hash)).files; + const recovered = JSON.parse(saved ?? (await decode_and_decompress_text(hash))); + let files = recovered.files; if (files[0]?.source) { files = files.map(munge); } + // older hashes may be missing a name + if (recovered.name) { + name = recovered.name; + } + repl.set({ files }); } catch { alert(`Couldn't load the code from the URL. Make sure you copied the link correctly.`); } + + if (saved) { + sessionStorage.removeItem(STORAGE_KEY); + set_hash(saved); + } } function handle_fork({ gist }: { gist: Gist }) { @@ -93,11 +107,19 @@ // Hide hash from URL const hash = location.hash.slice(1); if (hash) { - change_hash(); + set_hash(); } } - async function change_hash(hash?: string) { + async function update_hash() { + // Only change hash when necessary to avoid polluting everyone's browser history + if (modified) { + const json = JSON.stringify({ name, files: repl.toJSON().files }); + await set_hash(json); + } + } + + async function set_hash(hash?: string) { let url = `${location.pathname}${location.search}`; if (hash) { url += `#${await compress_and_encode_text(hash)}`; @@ -143,13 +165,23 @@ { + onhashchange={() => { if (!setting_hash) { set_files(); } }} + onbeforeunload={() => { + if (modified) { + // we can't save to the hash because it's an async operation, so we use + // a short-lived sessionStorage value instead + const json = JSON.stringify({ name, files: repl.toJSON().files }); + sessionStorage.setItem(STORAGE_KEY, json); + } + }} /> + +
{#if browser} -
{ - // Only change hash on editor blur to not pollute everyone's browser history - if (modified) { - const json = JSON.stringify({ files: repl.toJSON().files }); - change_hash(json); - } - }} - > +
(modified = true)} + oninput={() => (modified = true)} onfocus={(e) => e.currentTarget.select()} - use:enter={(e) => (e.currentTarget as HTMLInputElement).blur()} + onkeydown={(e) => e.key === 'Enter' && e.currentTarget.blur()} />