|
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