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
102 changes: 52 additions & 50 deletions apps/svelte.dev/src/routes/tutorial/[...slug]/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script>
<script lang="ts">
import { afterNavigate, beforeNavigate } from '$app/navigation';
import { SplitPane } from '@rich_harris/svelte-split-pane';
import { reset } from './adapter.svelte';
import { adapter_state, reset } from './adapter.svelte';
import Editor from './Editor.svelte';
import ContextMenu from './filetree/ContextMenu.svelte';
import Filetree from './filetree/Filetree.svelte';
Expand All @@ -10,40 +10,39 @@
import { ScreenToggle } from '@sveltejs/site-kit/components';
import Sidebar from './Sidebar.svelte';
import {
create_directories,
creating,
files,
reset_files,
selected_file,
selected_name,
solution
} from './state.js';
} 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 { Snapshot } from './$types.js';

export let data;
interface Props {
data: any;
}

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

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

/** @type {import('$lib/tutorial').Stub[]} */
let previous_files = [];
let previous_files: Stub[] = [];

/**
* @param {Record<string, string>} map
* @returns {Record<string, import('$lib/tutorial').Stub>}
*/
function create_files(map) {
/** @type {Record<string, import('$lib/tutorial').Stub>} */
const files = {};
function create_files(map: Record<string, string>): Record<string, Stub> {
const files: Record<string, Stub> = {};

/** @type {string[]} */
const to_delete = [];
const to_delete: string[] = [];

for (const key in map) {
const contents = map[key];
Expand All @@ -53,7 +52,7 @@
}

const parts = key.split('/');
const basename = /** @type {string} */ (parts.pop());
const basename = parts.pop()!;
const ext = basename.slice(basename.lastIndexOf('.'));

if (basename === '__delete') {
Expand All @@ -63,7 +62,7 @@

while (parts.length > 0) {
const dir = `/${parts.join('/')}`;
const basename = /** @type {string} */ (parts.pop());
const basename = parts.pop()!;

files[dir] ??= {
type: 'directory',
Expand Down Expand Up @@ -94,14 +93,7 @@
return files;
}

$: a = create_files(data.exercise.a);
$: b = create_files({ ...data.exercise.a, ...data.exercise.b });

$: mobile = w < 800; // for the things we can't do with media queries
$: files.set(Object.values(a));
$: solution.set(b);
$: selected_name.set(data.exercise.focus);
$: completed = is_completed($files, b);
// for the things we can't do with media queries

beforeNavigate(() => {
previous_files = $files;
Expand All @@ -119,11 +111,7 @@
paused = false;
});

/**
* @param {import('$lib/tutorial').Stub[]} files
* @param {Record<string, import('$lib/tutorial').Stub> | null} solution
*/
function is_completed(files, solution) {
function is_completed(files: Stub[], solution: Record<string, Stub> | null) {
if (!solution) return true;

for (const file of files) {
Expand All @@ -143,14 +131,12 @@
return true;
}

/** @param {string} code */
function normalise(code) {
function normalise(code: string) {
// TODO think about more sophisticated normalisation (e.g. truncate multiple newlines)
return code.replace(/\s+/g, ' ').trim();
}

/** @param {string | null} name */
function select_file(name) {
function select_file(name: string | null) {
const file = name && $files.find((file) => file.name === name);

if (!file && name) {
Expand Down Expand Up @@ -178,8 +164,7 @@
show_editor = true;
}

/** @param {string} name */
function navigate_to_file(name) {
function navigate_to_file(name: string) {
if (name === $selected_name) return;

select_file(name);
Expand All @@ -190,11 +175,10 @@
}
}

/** @type {HTMLElement} */
let sidebar;
let sidebar = $state() as HTMLElement;

/** @type {import('./$types').Snapshot<number>} */
export const snapshot = {
// TODO this doesn't seem to work any more?
export const snapshot: Snapshot<number> = {
capture: () => {
const scroll = sidebar.scrollTop;
sidebar.scrollTop = 0;
Expand All @@ -204,6 +188,24 @@
sidebar.scrollTop = scroll;
}
};

let a = $derived(create_files(data.exercise.a));
let b = $derived(create_files({ ...data.exercise.a, ...data.exercise.b }));
let mobile = $derived(w < 800);

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

$effect(() => {
solution.set(b);
});

$effect(() => {
selected_name.set(data.exercise.focus);
});

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

<svelte:head>
Expand All @@ -225,7 +227,7 @@

<svelte:window
bind:innerWidth={w}
on:popstate={(e) => {
onpopstate={(e) => {
const q = new URLSearchParams(location.search);
const file = q.get('file');

Expand Down Expand Up @@ -272,9 +274,9 @@
max="300px"
pos="200px"
>
<section class="navigator" slot="a">
<section slot="a" class="navigator">
{#if mobile}
<button class="file" on:click={() => (show_filetree = !show_filetree)}>
<button class="file" onclick={() => (show_filetree = !show_filetree)}>
{$selected_file?.name.replace(
data.exercise.scope.prefix,
data.exercise.scope.name + '/'
Expand All @@ -290,8 +292,8 @@
{/if}
</section>

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

{#if mobile && show_filetree}
Expand Down
20 changes: 9 additions & 11 deletions apps/svelte.dev/src/routes/tutorial/[...slug]/Editor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@
import { svelteTheme } from '@sveltejs/repl/theme';
import { basicSetup } from 'codemirror';
import { onMount, tick } from 'svelte';
import { adapter_state } from './adapter.svelte';
import './codemirror.css';
import { files, selected_file, selected_name, update_file } from './state.js';
import { toStore } from 'svelte/store';
import { files, selected_file, selected_name, update_file } from './state.svelte';
import { autocomplete_for_svelte } from '@sveltejs/site-kit/codemirror';
import type { Diagnostic } from '@codemirror/lint';
import type { Exercise, Stub } from '$lib/tutorial';
import type { Warning } from 'svelte/compiler';
import './codemirror.css';

interface Props {
exercise: Exercise;
warnings: Record<string, Warning[]>;
}

let { exercise }: Props = $props();
let { exercise, warnings }: Props = $props();

let container = $state() as HTMLDivElement;

Expand All @@ -38,8 +38,6 @@

let editor_view = $state() as EditorView;

const warnings = toStore(() => adapter_state.warnings);

const extensions = [
basicSetup,
EditorState.tabSize.of(2),
Expand Down Expand Up @@ -198,20 +196,20 @@
$effect(() => {
if (editor_view) {
if ($selected_name) {
const current_warnings = $warnings[$selected_name] || [];
const current_warnings = warnings[$selected_name] || [];
const diagnostics = current_warnings.map((warning) => {
/** @type {import('@codemirror/lint').Diagnostic} */
const diagnostic: Diagnostic = {
from: warning.start.character,
to: warning.end.character,
from: warning.start!.character,
to: warning.end!.character,
severity: 'warning',
message: warning.message
};

return diagnostic;
});
const transaction = setDiagnostics(editor_view.state, diagnostics);

const transaction = setDiagnostics(editor_view.state, diagnostics);
editor_view.dispatch(transaction);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { load_webcontainer, reset } from './adapter.svelte';
import { files } from './state.js';
import { files } from './state.svelte';

interface Props {
initial: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import * as context from './context.js';
import Item from './Item.svelte';
import file_icon from '$lib/icons/file.svg';
import { selected_name, solution } from '../state.js';
import { selected_name, solution } from '../state.svelte';

/** @type {import('$lib/tutorial').FileStub} */
export let file;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import Folder from './Folder.svelte';
import * as context from './context.js';
import Modal from '$lib/components/Modal.svelte';
import { files, solution, reset_files, create_directories, selected_name } from '../state.js';
import { files, solution, reset_files, selected_name } from '../state.svelte';
import { create_directories } from '../utils';
import { afterNavigate } from '$app/navigation';

/** @type {import('$lib/tutorial').Exercise} */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import Item from './Item.svelte';
import folder_closed from '$lib/icons/folder.svg';
import folder_open from '$lib/icons/folder-open.svg';
import { files, solution, creating } from '../state.js';
import { files, solution, creating } from '../state.svelte';

/** @type {import('$lib/tutorial').DirectoryStub} */
export let directory;
Expand Down
88 changes: 0 additions & 88 deletions apps/svelte.dev/src/routes/tutorial/[...slug]/state.js

This file was deleted.

Loading
Loading