|
13 | 13 |
|
14 | 14 | let { data } = $props(); |
15 | 15 |
|
| 16 | + const STORAGE_KEY = 'svelte:playground'; |
| 17 | +
|
16 | 18 | let repl = $state() as ReturnType<typeof Repl>; |
17 | 19 | let name = $state(data.gist.name); |
18 | 20 | let modified = $state(false); |
|
60 | 62 | } |
61 | 63 |
|
62 | 64 | async function set_files() { |
| 65 | + const saved = sessionStorage.getItem(STORAGE_KEY); |
63 | 66 | const hash = location.hash.slice(1); |
64 | 67 |
|
65 | | - if (!hash) { |
| 68 | + if (!hash && !saved) { |
66 | 69 | repl?.set({ |
67 | 70 | // TODO make this munging unnecessary |
68 | 71 | files: structuredClone(data.gist.components).map(munge) |
|
73 | 76 | } |
74 | 77 |
|
75 | 78 | try { |
76 | | - let files = JSON.parse(await decode_and_decompress_text(hash)).files; |
| 79 | + const recovered = JSON.parse(saved ?? (await decode_and_decompress_text(hash))); |
| 80 | + let files = recovered.files; |
77 | 81 |
|
78 | 82 | if (files[0]?.source) { |
79 | 83 | files = files.map(munge); |
80 | 84 | } |
81 | 85 |
|
| 86 | + // older hashes may be missing a name |
| 87 | + if (recovered.name) { |
| 88 | + name = recovered.name; |
| 89 | + } |
| 90 | +
|
82 | 91 | repl.set({ files }); |
83 | 92 | } catch { |
84 | 93 | alert(`Couldn't load the code from the URL. Make sure you copied the link correctly.`); |
85 | 94 | } |
| 95 | +
|
| 96 | + if (saved) { |
| 97 | + sessionStorage.removeItem(STORAGE_KEY); |
| 98 | + set_hash(saved); |
| 99 | + } |
86 | 100 | } |
87 | 101 |
|
88 | 102 | function handle_fork({ gist }: { gist: Gist }) { |
|
93 | 107 | // Hide hash from URL |
94 | 108 | const hash = location.hash.slice(1); |
95 | 109 | if (hash) { |
96 | | - change_hash(); |
| 110 | + set_hash(); |
97 | 111 | } |
98 | 112 | } |
99 | 113 |
|
100 | | - async function change_hash(hash?: string) { |
| 114 | + async function update_hash() { |
| 115 | + // Only change hash when necessary to avoid polluting everyone's browser history |
| 116 | + if (modified) { |
| 117 | + const json = JSON.stringify({ name, files: repl.toJSON().files }); |
| 118 | + await set_hash(json); |
| 119 | + } |
| 120 | + } |
| 121 | +
|
| 122 | + async function set_hash(hash?: string) { |
101 | 123 | let url = `${location.pathname}${location.search}`; |
102 | 124 | if (hash) { |
103 | 125 | url += `#${await compress_and_encode_text(hash)}`; |
|
143 | 165 | </svelte:head> |
144 | 166 |
|
145 | 167 | <svelte:window |
146 | | - on:hashchange={() => { |
| 168 | + onhashchange={() => { |
147 | 169 | if (!setting_hash) { |
148 | 170 | set_files(); |
149 | 171 | } |
150 | 172 | }} |
| 173 | + onbeforeunload={() => { |
| 174 | + if (modified) { |
| 175 | + // we can't save to the hash because it's an async operation, so we use |
| 176 | + // a short-lived sessionStorage value instead |
| 177 | + const json = JSON.stringify({ name, files: repl.toJSON().files }); |
| 178 | + sessionStorage.setItem(STORAGE_KEY, json); |
| 179 | + } |
| 180 | + }} |
151 | 181 | /> |
152 | 182 |
|
| 183 | +<svelte:body onmouseleave={update_hash} /> |
| 184 | + |
153 | 185 | <div class="repl-outer"> |
154 | 186 | <AppControls |
155 | 187 | examples={data.examples} |
|
163 | 195 | /> |
164 | 196 |
|
165 | 197 | {#if browser} |
166 | | - <div |
167 | | - style="display: contents" |
168 | | - onfocusout={() => { |
169 | | - // Only change hash on editor blur to not pollute everyone's browser history |
170 | | - if (modified) { |
171 | | - const json = JSON.stringify({ files: repl.toJSON().files }); |
172 | | - change_hash(json); |
173 | | - } |
174 | | - }} |
175 | | - > |
| 198 | + <div style="display: contents" onfocusout={update_hash}> |
176 | 199 | <Repl |
177 | 200 | bind:this={repl} |
178 | 201 | {svelteUrl} |
|
0 commit comments