Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 1 addition & 13 deletions apps/svelte.dev/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,11 @@
"sync-docs": "tsx scripts/sync-docs/index.ts"
},
"dependencies": {
"@codemirror/autocomplete": "^6.9.0",
"@codemirror/commands": "^6.2.5",
"@codemirror/lang-css": "^6.2.1",
"@codemirror/lang-html": "^6.4.6",
"@codemirror/lang-javascript": "^6.2.1",
"@codemirror/language": "^6.9.0",
"@codemirror/lint": "^6.4.1",
"@codemirror/search": "^6.5.2",
"@codemirror/state": "^6.2.1",
"@codemirror/view": "^6.17.1",
"@jridgewell/sourcemap-codec": "^1.4.15",
"@lezer/common": "^1.0.4",
"@lezer/highlight": "^1.1.6",
"@lezer/javascript": "^1.4.7",
"@lezer/lr": "^1.3.10",
"@replit/codemirror-lang-svelte": "^6.0.0",
"@replit/codemirror-vim": "^6.0.14",
"@rich_harris/svelte-split-pane": "^1.1.3",
"@shikijs/twoslash": "^1.22.0",
"@sveltejs/amp": "^1.1.3",
Expand All @@ -46,10 +34,10 @@
"adm-zip": "^0.5.10",
"ansi-to-html": "^0.7.2",
"base64-js": "^1.5.1",
"codemirror": "^6.0.1",
"cookie": "^0.7.0",
"d3-geo": "^3.1.0",
"d3-geo-projection": "^4.0.0",
"editor": "workspace:*",
"flexsearch": "^0.7.43",
"flru": "^1.0.2",
"port-authority": "^2.0.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Adapter, FileStub, Stub, Warning } from '$lib/tutorial';
import type { Adapter, Warning } from '$lib/tutorial';
import type { File, Item } from 'editor';
import Bundler from '@sveltejs/repl/bundler';
// @ts-ignore package exports don't have types
import * as yootils from 'yootils';
Expand Down Expand Up @@ -43,7 +44,7 @@ export async function create(): Promise<Adapter> {
const result = await bundler.bundle(
[...current_stubs.values()]
// TODO we can probably remove all the SvelteKit specific stuff from the tutorial content once this settles down
.filter((f): f is FileStub => f.name.startsWith('/src/lib/') && f.type === 'file')
.filter((f): f is File => f.name.startsWith('/src/lib/') && f.type === 'file')
.map((f) => ({
name: f.name.slice(9).split('.').slice(0, -1).join('.'),
source: f.contents,
Expand Down Expand Up @@ -85,7 +86,7 @@ export async function create(): Promise<Adapter> {
};
}

function stubs_to_map(files: Stub[], map = new Map<string, Stub>()) {
function stubs_to_map(files: Item[], map = new Map<string, Item>()) {
for (const file of files) {
map.set(file.name, file);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import * as yootils from 'yootils';
import { get_depth } from '../../../utils/path.js';
import { escape_html } from '../../../utils/escape.js';
import { ready } from '../common/index.js';
import type { Adapter, FileStub, Stub, Warning } from '$lib/tutorial';
import type { Adapter, Warning } from '$lib/tutorial';
import type { Item, File } from 'editor';

const converter = new AnsiToHtml({
fg: 'var(--sk-text-3)'
Expand All @@ -27,7 +28,7 @@ export async function create(): Promise<Adapter> {
state.progress = { value: 0, text: 'loading files' };

const q = yootils.queue(1);
const q_per_file = new Map<string, Array<FileStub>>();
const q_per_file = new Map<string, Array<File>>();

/** Paths and contents of the currently loaded file stubs */
let current_stubs = stubs_to_map([]);
Expand Down Expand Up @@ -138,7 +139,7 @@ export async function create(): Promise<Adapter> {
return {
reset: (stubs) => {
return q.add(async () => {
const to_write: Stub[] = [];
const to_write: Item[] = [];

const force_delete = [];

Expand All @@ -151,7 +152,7 @@ export async function create(): Promise<Adapter> {
continue;
}

const current = current_stubs.get(stub.name) as FileStub;
const current = current_stubs.get(stub.name) as File;

if (current?.contents !== stub.contents) {
to_write.push(stub);
Expand Down Expand Up @@ -272,7 +273,7 @@ export async function create(): Promise<Adapter> {
};
}

function is_config(file: Stub) {
function is_config(file: Item) {
return file.type === 'file' && is_config_path(file.name);
}

Expand All @@ -299,7 +300,7 @@ function wait_for_restart_vite() {
});
}

function convert_stubs_to_tree(stubs: Stub[], depth = 1) {
function convert_stubs_to_tree(stubs: Item[], depth = 1) {
const tree: FileSystemTree = {};

for (const stub of stubs) {
Expand All @@ -319,7 +320,7 @@ function convert_stubs_to_tree(stubs: Stub[], depth = 1) {
return tree;
}

function to_file(file: FileStub) {
function to_file(file: File) {
// special case
if (file.name === '/src/app.html' || file.name === '/src/error.html') {
const contents = file.contents + '<script type="module" src="/src/__client.js"></script>';
Expand All @@ -336,7 +337,7 @@ function to_file(file: FileStub) {
};
}

function stubs_to_map(files: Stub[], map = new Map<string, Stub>()) {
function stubs_to_map(files: Item[], map = new Map<string, Item>()) {
for (const file of files) {
map.set(file.name, file);
}
Expand Down
21 changes: 3 additions & 18 deletions apps/svelte.dev/src/lib/tutorial/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,10 @@
import type { Writable } from 'svelte/store';

export interface FileStub {
type: 'file';
name: string;
basename: string;
contents: string;
text: boolean;
}

export interface DirectoryStub {
type: 'directory';
name: string;
basename: string;
}

export type Stub = FileStub | DirectoryStub;
import type { File, Directory, Item } from 'editor';

export interface Adapter {
/** Returns `false` if the reset was in such a way that a reload of the iframe isn't needed */
reset(files: Array<Stub>): Promise<boolean>;
update(file: FileStub): Promise<boolean>;
reset(files: Array<Item>): Promise<boolean>;
update(file: File): Promise<boolean>;
}

export interface Scope {
Expand Down
41 changes: 28 additions & 13 deletions apps/svelte.dev/src/routes/tutorial/[...slug]/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
<script lang="ts">
import { afterNavigate, beforeNavigate } from '$app/navigation';
import { SplitPane } from '@rich_harris/svelte-split-pane';
import { adapter_state, reset } from './adapter.svelte';
import Editor from './Editor.svelte';
import * as adapter from './adapter.svelte';
import { Editor, Workspace } from 'editor';
import ContextMenu from './filetree/ContextMenu.svelte';
import Filetree from './filetree/Filetree.svelte';
import ImageViewer from './ImageViewer.svelte';
import Output from './Output.svelte';
import { ScreenToggle } from '@sveltejs/site-kit/components';
import Sidebar from './Sidebar.svelte';
import { solution, Workspace } from './state.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 { Stub } from '$lib/tutorial';
import type { Item } from 'editor';
import type { Snapshot } from './$types.js';

interface Props {
Expand All @@ -24,18 +24,16 @@

let { data }: Props = $props();

const workspace = new Workspace();

let path = data.exercise.path;
let show_editor = $state(false);
let show_filetree = $state(false);
let paused = $state(false);
let w = $state(1000);

let previous_files: Stub[] = [];
let previous_files: Item[] = [];

function create_files(map: Record<string, string>): Record<string, Stub> {
const files: Record<string, Stub> = {};
function create_files(map: Record<string, string>): Record<string, Item> {
const files: Record<string, Item> = {};

const to_delete: string[] = [];

Expand Down Expand Up @@ -98,13 +96,13 @@
const will_delete = previous_files.some((file) => !(file.name in a));

if (data.exercise.path !== path || will_delete) paused = true;
await reset(workspace.files);
await adapter.reset(workspace.files);

path = data.exercise.path;
paused = false;
});

function is_completed(files: Stub[], solution: Record<string, Stub> | null) {
function is_completed(files: Item[], solution: Record<string, Item> | null) {
if (!solution) return true;

for (const file of files) {
Expand Down Expand Up @@ -185,6 +183,19 @@
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,
onupdate(file) {
adapter.update(file);
},
onreset(items) {
adapter.reset(items);
}
});

solution.set(b);

// for the things we can't do with media queries
let mobile = $derived(w < 800);

Expand All @@ -197,7 +208,7 @@
});

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

let completed = $derived(is_completed(workspace.files, b));
Expand Down Expand Up @@ -289,7 +300,11 @@
</section>

<section slot="b" class="editor-container">
<Editor exercise={data.exercise} warnings={adapter_state.warnings} {workspace} />
<Editor
exercise={data.exercise}
warnings={adapter.adapter_state.warnings}
{workspace}
/>
<ImageViewer selected={workspace.selected_file} />

{#if mobile && show_filetree}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script>
/** @type {import('$lib/tutorial').FileStub | null} */
/** @type {import('editor').File | null} */
export let selected;

const image_types = new Map([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import Loading from './Loading.svelte';
import { adapter_state, subscribe, reset } from './adapter.svelte';
import type { Exercise } from '$lib/tutorial';
import type { Workspace } from './state.svelte';
import type { Workspace } from 'editor';

interface Props {
exercise: Exercise;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { browser } from '$app/environment';
import { page } from '$app/stores';
import type { state as WCState } from '$lib/tutorial/adapters/webcontainer/index.svelte';
import type { state as RollupState } from '$lib/tutorial/adapters/rollup/index.svelte';
import type { Adapter, FileStub, Stub } from '$lib/tutorial';
import type { Adapter } from '$lib/tutorial';
import type { File, Item } from 'editor';
import { needs_webcontainers } from './shared';

let initial_load = true;
Expand Down Expand Up @@ -111,7 +112,7 @@ function publish(event: EventName) {
subscriptions.get(event)?.forEach((fn) => fn());
}

export async function reset(files: Stub[]) {
export async function reset(files: Item[]) {
try {
const adapter = await get_adapter();
const should_reload = await adapter.reset(files);
Expand All @@ -126,7 +127,7 @@ export async function reset(files: Stub[]) {
}
}

export async function update(file: FileStub) {
export async function update(file: File) {
const adapter = await get_adapter();
const should_reload = await adapter.update(file);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
import Item from './Item.svelte';
import file_icon from '$lib/icons/file.svg';
import { solution } from '../state.svelte';
import type { FileStub, MenuItem } from '$lib/tutorial';
import type { MenuItem } from '$lib/tutorial';
import type { File } from 'editor';
interface Props {
file: FileStub;
file: File;
depth: number;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
import Folder from './Folder.svelte';
import * as context from './context.js';
import Modal from '$lib/components/Modal.svelte';
import { solution, Workspace } from '../state.svelte';
import { solution } from '../state.svelte';
import { create_directories } from '../utils';
import { afterNavigate } from '$app/navigation';
import type { Exercise, Stub } from '$lib/tutorial';
import type { Exercise } from '$lib/tutorial';
import type { Workspace, Item } from 'editor';
interface Props {
exercise: Exercise;
Expand Down Expand Up @@ -55,7 +56,7 @@
const basename = name.split('/').pop()!;
const file: Stub =
const file: Item =
type === 'file'
? { type, name, basename, text: true, contents: '' }
: { type, name, basename };
Expand Down Expand Up @@ -142,7 +143,7 @@
workspace
});
function is_deleted(file: Stub) {
function is_deleted(file: Item) {
if (file.type === 'directory') return `${file.name}/__delete` in exercise.a;
if (file.text) return file.contents.startsWith('__delete');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
import folder_closed from '$lib/icons/folder.svg';
import folder_open from '$lib/icons/folder-open.svg';
import { solution } from '../state.svelte';
import type { DirectoryStub, FileStub, MenuItem, Stub } from '$lib/tutorial';
import type { MenuItem } from '$lib/tutorial';
import type { Directory, File as IFile, Item as IItem } from 'editor';

interface Props {
directory: DirectoryStub;
directory: Directory;
prefix: string;
depth: number;
contents: Stub[];
contents: IItem[];
}

let { directory, prefix, depth, contents }: Props = $props();
Expand All @@ -36,7 +37,7 @@

let child_files = $derived(
children.filter((child) => get_depth(child.name) === segments && child.type === 'file')
) as FileStub[];
) as IFile[];

const can_create = $derived.by(() => {
const result = {
Expand Down Expand Up @@ -156,7 +157,7 @@

{#each child_directories as directory}
<Folder
directory={directory as DirectoryStub}
directory={directory as Directory}
prefix={directory.name + '/'}
depth={depth + 1}
contents={children}
Expand Down
Loading
Loading