Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
75d8224
use update_file
Rich-Harris Oct 18, 2024
26c5109
notes to self
Rich-Harris Oct 18, 2024
c50bac1
enforce that workspace always has a selected file
Rich-Harris Oct 18, 2024
4ac10d0
selected_name always exists
Rich-Harris Oct 18, 2024
c3a2a14
make selected_name readonly
Rich-Harris Oct 18, 2024
dde2a56
rename
Rich-Harris Oct 18, 2024
487b1c1
tidy up
Rich-Harris Oct 18, 2024
1c41d32
tidy up
Rich-Harris Oct 18, 2024
07de85d
simplify
Rich-Harris Oct 18, 2024
f87061b
tweak signature
Rich-Harris Oct 18, 2024
47d2ba6
fix
Rich-Harris Oct 18, 2024
120272d
fix
Rich-Harris Oct 18, 2024
13251ec
add remove method
Rich-Harris Oct 18, 2024
bdc5fc8
simplify drag and drop
Rich-Harris Oct 18, 2024
74501be
remove all mutations of workspace.files
Rich-Harris Oct 18, 2024
bbfbb46
make files readonly
Rich-Harris Oct 18, 2024
61c847a
reorder stuff
Rich-Harris Oct 18, 2024
c073a67
Merge branch 'main' into robustify-workspace
Rich-Harris Oct 18, 2024
c1095fb
tidy up
Rich-Harris Oct 18, 2024
96b4fa4
use fewer effects
Rich-Harris Oct 18, 2024
f7e7cdd
remove an effect
Rich-Harris Oct 18, 2024
c66c128
unused
Rich-Harris Oct 18, 2024
c7c71d4
more
Rich-Harris Oct 18, 2024
8587887
make workspace responsible for editor states
Rich-Harris Oct 18, 2024
669a5f9
move more stuff into workspace
Rich-Harris Oct 18, 2024
02ed97e
more
Rich-Harris Oct 18, 2024
694170b
more
Rich-Harris Oct 18, 2024
8fdfb02
avoid a reset_files
Rich-Harris Oct 18, 2024
ea27f5f
remove some indirection
Rich-Harris Oct 18, 2024
5554ce3
simplify
Rich-Harris Oct 19, 2024
72bd64a
simplify
Rich-Harris Oct 19, 2024
c498ea0
simplify
Rich-Harris Oct 19, 2024
93e4959
tweak
Rich-Harris Oct 19, 2024
b7c70d5
simplify
Rich-Harris Oct 19, 2024
dca82d7
tidy up
Rich-Harris Oct 19, 2024
4766dd0
lint
Rich-Harris Oct 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 5 additions & 14 deletions apps/svelte.dev/src/lib/tutorial/adapters/rollup/index.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ export async function create(): Promise<Adapter> {
state.progress = { value: 0.5, text: 'loading svelte compiler' };

/** Paths and contents of the currently loaded file stubs */
let current_stubs = stubs_to_map([]);
let current_files: Item[] = [];

async function compile() {
state.bundle = await bundler.bundle(
[...current_stubs.values()]
current_files
// TODO we can probably remove all the SvelteKit specific stuff from the tutorial content once this settles down
.filter((f): f is File => f.name.startsWith('/src/lib/') && f.type === 'file')
.map((f) => ({ ...f, name: f.name.slice(9) })),
Expand All @@ -54,30 +54,21 @@ export async function create(): Promise<Adapter> {
const q = yootils.queue(1);

return {
reset: (stubs) => {
reset: (files) => {
return q.add(async () => {
current_stubs = stubs_to_map(stubs, current_stubs);
current_files = files;

await compile();

return false;
});
},
update: (file) => {
return q.add(async () => {
current_stubs.set(file.name, file);
current_files = current_files.map((old) => (old.name === file.name ? file : old));

await compile();

return false;
});
}
};
}

function stubs_to_map(files: Item[], map = new Map<string, Item>()) {
for (const file of files) {
map.set(file.name, file);
}
return map;
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
}, 500);
}

function handle_change() {
function onchange() {
const was_modified = modified;
modified = true;

Expand Down Expand Up @@ -170,9 +170,7 @@
{relaxed}
{can_escape}
injectedJS={mapbox_setup}
change={handle_change}
add={handle_change}
remove={handle_change}
{onchange}
previewTheme={$theme.current}
/>
</div>
Expand Down
91 changes: 26 additions & 65 deletions apps/svelte.dev/src/routes/tutorial/[...slug]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@
import { ScreenToggle } from '@sveltejs/site-kit/components';
import Sidebar from './Sidebar.svelte';
import { solution } from './state.svelte';
import { create_directories } from './utils';
import { needs_webcontainers, text_files } from './shared';
import OutputRollup from './OutputRollup.svelte';
import { page } from '$app/stores';
import Controls from './Controls.svelte';
import type { Item } from 'editor';
import type { Snapshot } from './$types.js';
import { tick } from 'svelte';

interface Props {
data: any;
Expand All @@ -31,9 +29,6 @@
let paused = $state(false);
let w = $state(1000);

let editor: any; // TODO
let skip_set_files = true;

let previous_files: Item[] = [];

function create_files(map: Record<string, string>): Record<string, Item> {
Expand Down Expand Up @@ -119,15 +114,18 @@
const file = name && workspace.files.find((file) => file.name === name);

if (!file && name) {
// trigger file creation input. first, create any intermediate directories
const new_directories = create_directories(name, workspace.files);
// create intermediate directories if necessary
const parent = name.split('/').slice(0, -1).join('/');

if (new_directories.length > 0) {
workspace.reset_files([...workspace.files, ...new_directories]);
}
if (!workspace.files.some((item) => item.name === parent)) {
const basename = parent.split('/').pop()!;

// find the parent directory
const parent = name.split('/').slice(0, -1).join('/');
workspace.add({
type: 'directory',
name: parent,
basename
});
}

workspace.creating = {
parent,
Expand All @@ -137,19 +135,22 @@
show_filetree = true;
} else {
show_filetree = false;
workspace.selected_name = name;

if (name) {
workspace.select(name);
}
}

show_editor = true;
}

function navigate_to_file(name: string) {
if (name === workspace.selected_name) return;
if (name === workspace.current.name) return;

select_file(name);

if (mobile) {
const q = new URLSearchParams({ file: workspace.selected_name || '' });
const q = new URLSearchParams({ file: workspace.current.name || '' });
history.pushState({}, '', `?${q}`);
}
}
Expand All @@ -171,9 +172,8 @@
let a = $derived(create_files(data.exercise.a));
let b = $derived(create_files({ ...data.exercise.a, ...data.exercise.b }));

const workspace = new Workspace({
files: Object.values(a),
selected_name: data.exercise.focus,
const workspace = new Workspace(Object.values(a), {
initial: data.exercise.focus,
onupdate(file) {
adapter.update(file);
},
Expand All @@ -188,28 +188,16 @@
let mobile = $derived(w < 800);

$effect(() => {
workspace.files = Object.values(a);
});

$effect(() => {
// TODO get rid of this store/effect
solution.set(b);
});

$effect(() => {
workspace.selected_name = data.exercise.focus; // TODO this probably belongs in afterNavigate
});

beforeNavigate(() => {
skip_set_files = true;
previous_files = workspace.files;
});

afterNavigate(async () => {
skip_set_files = false;

editor.reset();

w = window.innerWidth;
workspace.reset(Object.values(a), data.exercise.focus);

const will_delete = previous_files.some((file) => !(file.name in a));

Expand All @@ -220,11 +208,6 @@
paused = false;
});

$effect(() => {
const files = workspace.files; // capture the dependency. TODO don't use an effect here
if (!skip_set_files) editor.update_files(files);
});

let completed = $derived(is_completed(workspace.files, b));
</script>

Expand Down Expand Up @@ -268,7 +251,7 @@
exercise={data.exercise}
{completed}
toggle={() => {
workspace.reset_files(Object.values(completed ? a : b));
workspace.set(Object.values(completed ? a : b));
}}
/>

Expand Down Expand Up @@ -297,34 +280,19 @@
<section slot="a" class="navigator">
{#if mobile}
<button class="file" onclick={() => (show_filetree = !show_filetree)}>
{workspace.selected_name?.replace(
{workspace.current.name.replace(
data.exercise.scope.prefix,
data.exercise.scope.name + '/'
) ?? 'Files'}
</button>
{:else}
<Filetree
exercise={data.exercise}
{workspace}
on:select={(e) => {
select_file(e.detail.name);
}}
/>
<Filetree exercise={data.exercise} {workspace} />
{/if}
</section>

<section slot="b" class="editor-container">
<Editor
bind:this={editor}
{workspace}
onchange={async (file, contents) => {
skip_set_files = true;

workspace.update_file({ ...file, contents });

await tick();
skip_set_files = false;
}}
autocomplete_filter={(file) => {
return (
file.name.startsWith('/src') &&
Expand All @@ -334,18 +302,11 @@
);
}}
/>
<ImageViewer selected={workspace.selected_file} />
<ImageViewer selected={workspace.current} />

{#if mobile && show_filetree}
<div class="mobile-filetree">
<Filetree
mobile
exercise={data.exercise}
{workspace}
on:select={(e) => {
navigate_to_file(e.detail.name);
}}
/>
<Filetree mobile exercise={data.exercise} {workspace} />
</div>
{/if}
</section>
Expand Down Expand Up @@ -374,7 +335,7 @@
const url = new URL(location.origin + location.pathname);

if (show_editor) {
url.searchParams.set('file', workspace.selected_name ?? '');
url.searchParams.set('file', workspace.current.name ?? '');
}

history.pushState({}, '', url); // TODO use SvelteKit pushState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
{renaming}
basename={file.basename}
icon={file_icon}
selected={file.name === workspace.selected_name}
selected={file.name === workspace.current.name}
{actions}
onclick={() => select(file.name)}
onedit={() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import { writable } from 'svelte/store';
import Folder from './Folder.svelte';
import * as context from './context.js';
import Modal from '$lib/components/Modal.svelte';
import { solution } from '../state.svelte';
import { create_directories } from '../utils';
import { afterNavigate } from '$app/navigation';
import type { Exercise } from '$lib/tutorial';
import type { Workspace, Item } from 'editor';
Expand All @@ -18,8 +16,6 @@

let { exercise, mobile = false, workspace }: Props = $props();

const dispatch = createEventDispatcher();

const hidden = new Set(['__client.js', 'node_modules', '__delete']);

let modal_text = $state('');
Expand Down Expand Up @@ -61,15 +57,7 @@
? { type, name, basename, text: true, contents: '' }
: { type, name, basename };

workspace.reset_files([
...workspace.files,
...create_directories(name, workspace.files),
file
]);

if (type === 'file') {
dispatch('select', { name });
}
workspace.add(file);
},

rename: async (to_rename, new_name) => {
Expand All @@ -94,27 +82,8 @@
return;
}

if (to_rename.type === 'directory') {
for (const file of workspace.files) {
if (file.name.startsWith(to_rename.name + '/')) {
file.name = new_full_name + file.name.slice(to_rename.name.length);
}
}
}

const was_selected = workspace.selected_name === to_rename.name;

to_rename.basename = new_full_name.split('/').pop()!;
to_rename.name = new_full_name;

workspace.reset_files([
...workspace.files,
...create_directories(new_full_name, workspace.files)
]);

if (was_selected) {
dispatch('select', { name: new_full_name });
}
workspace.rename(to_rename, new_full_name);
workspace.focus();
},

remove: async (file) => {
Expand All @@ -125,19 +94,11 @@
return;
}

dispatch('select', { name: null });

workspace.reset_files(
workspace.files.filter((f) => {
if (f === file) return false;
if (f.name.startsWith(file.name + '/')) return false;
return true;
})
);
workspace.remove(file);
},

select: (name) => {
dispatch('select', { name });
workspace.select(name);
},

workspace
Expand Down
Loading
Loading