Skip to content

Commit 8ab615a

Browse files
Rich-HarrisRich Harris
and
Rich Harris
authored
fix solve button (#258)
* style tweak * fix solve button - closes #256 --------- Co-authored-by: Rich Harris <[email protected]>
1 parent c1e1e58 commit 8ab615a

File tree

2 files changed

+96
-35
lines changed

2 files changed

+96
-35
lines changed

src/routes/tutorial/[slug]/Editor.svelte

Lines changed: 90 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script>
22
import { browser, dev } from '$app/environment';
3-
import { onMount } from 'svelte';
3+
import { onMount, tick } from 'svelte';
44
import { basicSetup } from 'codemirror';
55
import { EditorView, keymap } from '@codemirror/view';
66
import { EditorState } from '@codemirror/state';
@@ -12,7 +12,7 @@
1212
import { tags } from '@lezer/highlight';
1313
import { HighlightStyle } from '@codemirror/language';
1414
import { syntaxHighlighting } from '@codemirror/language';
15-
import { afterNavigate } from '$app/navigation';
15+
import { afterNavigate, beforeNavigate } from '$app/navigation';
1616
import { files, selected_file, selected_name, update_file } from './state.js';
1717
import './codemirror.css';
1818
@@ -30,6 +30,7 @@
3030
let container;
3131
3232
let preserve_editor_focus = false;
33+
let skip_reset = true;
3334
3435
/** @type {any} */
3536
let remove_focus_timeout;
@@ -40,44 +41,79 @@
4041
/** @type {import('@codemirror/view').EditorView} */
4142
let editor_view;
4243
43-
$: if (editor_view && $selected_name) {
44-
select_state($selected_name);
44+
const extensions = [
45+
basicSetup,
46+
EditorState.tabSize.of(2),
47+
keymap.of([indentWithTab]),
48+
indentUnit.of('\t'),
49+
theme
50+
];
51+
52+
$: if (editor_view) select_state($selected_name);
53+
54+
$: reset($files);
55+
56+
/** @param {import('$lib/types').Stub[]} $files */
57+
function reset($files) {
58+
if (skip_reset) return;
59+
60+
for (const file of $files) {
61+
if (file.type !== 'file') continue;
62+
63+
let state = editor_states.get(file.name);
64+
65+
if (state) {
66+
const existing = state.doc.toString();
67+
68+
if (file.contents !== existing) {
69+
const transaction = state.update({
70+
changes: {
71+
from: 0,
72+
to: existing.length,
73+
insert: file.contents
74+
}
75+
});
76+
77+
editor_states.set(file.name, transaction.state);
78+
state = transaction.state;
79+
80+
if ($selected_name === file.name) {
81+
editor_view.setState(state);
82+
}
83+
}
84+
} else {
85+
let lang;
86+
87+
if (file.name.endsWith('.js') || file.name.endsWith('.json')) {
88+
lang = javascript();
89+
} else if (file.name.endsWith('.html')) {
90+
lang = html();
91+
} else if (file.name.endsWith('.svelte')) {
92+
lang = svelte();
93+
}
94+
95+
state = EditorState.create({
96+
doc: file.contents,
97+
extensions: lang ? [...extensions, lang] : extensions
98+
});
99+
100+
editor_states.set(file.name, state);
101+
}
102+
}
45103
}
46104
47-
/** @param {string} $selected_name */
105+
/** @param {string | null} $selected_name */
48106
function select_state($selected_name) {
49-
const file = $files.find((file) => file.name === $selected_name);
50-
if (file?.type !== 'file') return;
51-
52-
let state = editor_states.get(file.name);
53-
if (!state) {
54-
const extensions = [
55-
basicSetup,
56-
EditorState.tabSize.of(2),
57-
keymap.of([indentWithTab]),
58-
indentUnit.of('\t'),
59-
theme
60-
];
61-
62-
if (file.name.endsWith('.js') || file.name.endsWith('.json')) {
63-
extensions.push(javascript());
64-
} else if (file.name.endsWith('.html')) {
65-
extensions.push(html());
66-
} else if (file.name.endsWith('.svelte')) {
67-
extensions.push(svelte());
68-
}
107+
if (skip_reset) return;
69108
70-
state = EditorState.create({
71-
doc: file.contents,
72-
extensions
109+
const state =
110+
($selected_name && editor_states.get($selected_name)) ||
111+
EditorState.create({
112+
doc: '',
113+
extensions: [EditorState.readOnly.of(true)]
73114
});
74115
75-
editor_states.set(file.name, state);
76-
}
77-
78-
if (editor_view) {
79-
editor_view.setState(state);
80-
}
116+
editor_view.setState(state);
81117
}
82118
83119
onMount(() => {
@@ -101,10 +137,12 @@
101137
102138
editor_view = new EditorView({
103139
parent: container,
104-
dispatch(transaction) {
140+
async dispatch(transaction) {
105141
editor_view.update([transaction]);
106142
107143
if (transaction.docChanged && $selected_file) {
144+
skip_reset = true;
145+
108146
// TODO do we even need to update `$files`? maintaining separate editor states is probably sufficient
109147
update_file({
110148
...$selected_file,
@@ -113,6 +151,9 @@
113151
114152
// keep `editor_states` updated so that undo/redo history is preserved for files independently
115153
editor_states.set($selected_file.name, editor_view.state);
154+
155+
await tick();
156+
skip_reset = false;
116157
}
117158
}
118159
});
@@ -126,8 +167,16 @@
126167
};
127168
});
128169
170+
beforeNavigate(() => {
171+
skip_reset = true;
172+
});
173+
129174
afterNavigate(() => {
175+
skip_reset = false;
176+
130177
editor_states.clear();
178+
reset($files);
179+
131180
select_state($selected_name);
132181
});
133182
</script>
@@ -214,4 +263,10 @@
214263
.fake-content {
215264
padding: 0 1rem;
216265
}
266+
267+
@media (prefers-color-scheme: dark) {
268+
.fake * {
269+
color: #666;
270+
}
271+
}
217272
</style>

src/routes/tutorial/[slug]/state.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ export function update_file(file) {
3434

3535
/** @param {import('$lib/types').Stub[]} new_files */
3636
export function reset_files(new_files) {
37+
// if the selected file no longer exists, clear it
38+
selected_name.update(($selected_name) => {
39+
const file = new_files.find((file) => file.name === $selected_name);
40+
return file?.name ?? null;
41+
});
42+
3743
files.set(new_files);
3844
adapter.reset(new_files);
3945
}

0 commit comments

Comments
 (0)