Skip to content

Commit

Permalink
Vim UI (#1049)
Browse files Browse the repository at this point in the history
* add UI for vim mode

* oops

* fix

* man codemirror is confusing

* fix

* refactor

* move stuff

* add UI to tutorial as well

* drive-by fixes

* fix
  • Loading branch information
Rich-Harris authored Dec 20, 2024
1 parent 1a008bd commit 1e037e2
Show file tree
Hide file tree
Showing 15 changed files with 167 additions and 92 deletions.
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

0 comments on commit 1e037e2

Please sign in to comment.