Skip to content
Merged

Vim UI #1049

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
4 changes: 1 addition & 3 deletions apps/svelte.dev/src/lib/components/ModalDropdown.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,7 @@
}

// except parents of the current one
const current = details.querySelector(
`[href="${page.url.pathname}"]`
) as HTMLAnchorElement | null;
const current = details.querySelector(`[aria-current="page"]`) as HTMLAnchorElement | null;
if (!current) return;

let node = current as Element;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,20 +161,6 @@

saving = false;
}

// modifying an app should reset the `<select>`, so that
// the example can be reselected
$effect(() => {
if (modified) {
// this is a little tricky, but: we need to wrap this in untrack
// because otherwise we'll read `select.value` and re-run this
// when we navigate, which we don't want
untrack(() => {
// @ts-ignore not sure why this is erroring
select.value = '';
});
}
});
</script>

<svelte:window on:keydown={handleKeydown} />
Expand All @@ -193,7 +179,7 @@
<li>
<a
href="/playground/{example.slug}"
aria-current={page.params.id === example.slug ? 'page' : undefined}
aria-current={page.params.id === example.slug && !modified ? 'page' : undefined}
>
{example.title}
</a>
Expand Down
1 change: 1 addition & 0 deletions apps/svelte.dev/src/routes/tutorial/[...slug]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@
toggle={() => {
workspace.set(Object.values(completed ? a : b));
}}
{workspace}
/>

<div class="top" class:offset={show_editor}>
Expand Down
13 changes: 11 additions & 2 deletions apps/svelte.dev/src/routes/tutorial/[...slug]/Controls.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@
import SecondaryNav from '$lib/components/SecondaryNav.svelte';
import ModalDropdown from '$lib/components/ModalDropdown.svelte';
import type { Exercise, PartStub } from '$lib/tutorial';
import { Icon } from '@sveltejs/site-kit/components';
import { Checkbox, Icon, Toolbox } from '@sveltejs/site-kit/components';
import type { Workspace } from 'editor';

interface Props {
index: PartStub[];
exercise: Exercise;
completed: boolean;
toggle: () => void;
workspace: Workspace;
}

let { index, exercise, completed, toggle }: Props = $props();
let { index, exercise, completed, toggle, workspace }: Props = $props();
</script>

<SecondaryNav>
Expand Down Expand Up @@ -71,6 +73,13 @@
solve
{/if}
</button>

<Toolbox>
<label class="option">
<span>Toggle Vim mode</span>
<Checkbox bind:checked={workspace.vim}></Checkbox>
</label>
</Toolbox>
</SecondaryNav>

<style>
Expand Down
67 changes: 47 additions & 20 deletions packages/editor/src/lib/Workspace.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ export class Workspace {
#readonly = false; // TODO do we need workspaces for readonly stuff?
#files = $state.raw<Item[]>([]);
#current = $state.raw() as File;
#vim = $state(false);

#handlers = {
hover: new Set<(pos: number | null) => void>(),
Expand Down Expand Up @@ -279,23 +280,10 @@ export class Workspace {
if (this.#view) throw new Error('view is already linked');
this.#view = view;

view.setState(this.#get_state(untrack(() => this.#current)));

let should_install_vim = localStorage.getItem('vim') === 'true';

const q = new URLSearchParams(location.search);
if (q.has('vim')) {
should_install_vim = q.get('vim') === 'true';
localStorage.setItem('vim', should_install_vim.toString());
}

if (should_install_vim) {
const { vim } = await import('@replit/codemirror-vim');

this.#view?.dispatch({
effects: vim_mode.reconfigure(vim())
});
}
untrack(() => {
view.setState(this.#get_state(untrack(() => this.#current)));
this.vim = localStorage.getItem('vim') === 'true';
});
}

move(from: Item, to: Item) {
Expand Down Expand Up @@ -476,6 +464,44 @@ export class Workspace {
}
}

get vim() {
return this.#vim;
}

set vim(value) {
this.#toggle_vim(value);
}

async #toggle_vim(value: boolean) {
this.#vim = value;

localStorage.setItem('vim', String(value));

// @ts-expect-error jfc CodeMirror is a struggle
let vim_extension_index = default_extensions.findIndex((ext) => ext.compartment === vim_mode);

let extension: any = [];

if (value) {
const { vim } = await import('@replit/codemirror-vim');
extension = vim();
}

default_extensions[vim_extension_index] = vim_mode.of(extension);

this.#view?.dispatch({
effects: vim_mode.reconfigure(extension)
});

// update all the other states
for (const file of this.#files) {
if (file.type !== 'file') continue;
if (file === this.#current) continue;

this.states.set(file.name, this.#create_state(file));
}
}

#create_directories(item: Item) {
// create intermediate directories as necessary
const parts = item.name.split('/');
Expand All @@ -497,9 +523,10 @@ export class Workspace {
}

#get_state(file: File) {
let state = this.states.get(file.name);
if (state) return state;
return this.states.get(file.name) ?? this.#create_state(file);
}

#create_state(file: File) {
const extensions = [
...default_extensions,
EditorState.readOnly.of(this.#readonly),
Expand Down Expand Up @@ -573,7 +600,7 @@ export class Workspace {
break;
}

state = EditorState.create({
const state = EditorState.create({
doc: file.contents,
extensions
});
Expand Down
14 changes: 11 additions & 3 deletions packages/repl/src/lib/Input/ComponentSelector.svelte
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
<script lang="ts">
import RunesInfo from './RunesInfo.svelte';
import Migrate from './Migrate.svelte';
import type { Workspace, File } from 'editor';
import { tick } from 'svelte';
import { Checkbox, Toolbox } from '@sveltejs/site-kit/components';

interface Props {
runes: boolean;
onchange: () => void;
workspace: Workspace;
can_migrate: boolean;
migrate: () => void;
}

let { runes, onchange, workspace, can_migrate }: Props = $props();
let { runes, onchange, workspace, can_migrate, migrate }: Props = $props();

let input = $state() as HTMLInputElement;
let input_value = $state(workspace.current.name);
Expand Down Expand Up @@ -162,7 +163,14 @@

<div class="runes">
<RunesInfo {runes} />
<Migrate {can_migrate} />
<Toolbox>
<label class="option">
<span>Toggle Vim mode</span>
<Checkbox bind:checked={workspace.vim}></Checkbox>
</label>

<button disabled={!can_migrate} onclick={migrate}>Migrate to Svelte 5, if possible</button>
</Toolbox>
</div>
</div>

Expand Down
28 changes: 0 additions & 28 deletions packages/repl/src/lib/Input/Migrate.svelte

This file was deleted.

11 changes: 2 additions & 9 deletions packages/repl/src/lib/Input/RunesInfo.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,9 @@

let { runes }: { runes: boolean } = $props();

let open = $state(false);

const { workspace } = get_repl_context();
</script>

<svelte:window
onkeydown={(e) => {
if (e.key === 'Escape') open = false;
}}
/>

<Dropdown align="right">
<div class="target">
<span class="lightning" class:active={runes} role="presentation"></span>
Expand Down Expand Up @@ -106,12 +98,13 @@

.popup {
position: absolute;
right: -6rem;
right: -4rem;
width: 100vw;
max-width: 320px;
z-index: 9999;
background: var(--sk-bg-3);
padding: 1em;
border-radius: var(--sk-border-radius);

p {
font: var(--sk-font-ui-medium);
Expand Down
5 changes: 1 addition & 4 deletions packages/repl/src/lib/Repl.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,6 @@
set_repl_context({
bundle,
toggleable,

migrate,

workspace
});

Expand Down Expand Up @@ -177,7 +174,7 @@
>
{#snippet a()}
<section>
<ComponentSelector {runes} {onchange} {workspace} {can_migrate} />
<ComponentSelector {runes} {onchange} {workspace} {can_migrate} {migrate} />

<Editor {workspace} />
</section>
Expand Down
4 changes: 0 additions & 4 deletions packages/repl/src/lib/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,5 @@ export type ReplState = {
export type ReplContext = {
bundle: Writable<ReplState['bundle']>;
toggleable: Writable<ReplState['toggleable']>;

workspace: Workspace;

// Methods
migrate(): Promise<void>;
};
20 changes: 16 additions & 4 deletions packages/site-kit/src/lib/components/HoverMenu.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
<style>
.hover-menu {
padding: 0.5rem;
white-space: nowrap;

:global {
a,
button {
button,
label {
color: inherit;
padding: 1rem;
display: block;
Expand All @@ -24,11 +26,21 @@
width: 100%;
text-align: left;
border-radius: var(--sk-border-radius-inner);

&:not(:disabled):hover {
background-color: var(--sk-bg-4);
}
}

label {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}

a:hover,
button:hover {
background-color: var(--sk-bg-4);
button:disabled {
color: var(--sk-fg-4);
}
}
}
Expand Down
Loading
Loading