Skip to content

Commit

Permalink
Starting over with new monarch playground.
Browse files Browse the repository at this point in the history
  • Loading branch information
thesoftwarephilosopher committed Aug 22, 2024
1 parent 80abb57 commit 34e7ccc
Show file tree
Hide file tree
Showing 8 changed files with 429 additions and 69 deletions.
9 changes: 9 additions & 0 deletions site/@imlib/processor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { processSite, SiteProcessor } from "@imlib/core";

export default (files => {
const allFiles = [...files];
const out = processSite(allFiles);
out.set('/token-provider.ts', allFiles.find(f => f.path === '/token-provider.js')!.module!.source);
out.set('/monarch/samplecode.tsx', allFiles.find(f => f.path === '/monarch/samplecode.js')!.module!.source);
return out;
}) as SiteProcessor;
19 changes: 4 additions & 15 deletions site/load-samples.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import monaco from '@imlib/monaco-esm';
import type monacoTypes from 'monaco-editor';
import { setupTheme } from './theme.js';
import { tokenProvider } from './token-provider.js';
import { Mod, modules } from './vanillajsx/compiler.js';

Expand All @@ -8,22 +10,9 @@ monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ESNext,
});

monaco.editor.defineTheme('vsc2', {
base: 'vs-dark',
inherit: true,
rules: [
// { token: "identifier.ts", foreground: "9CDCFE" },
{ token: "property.ts", foreground: "9CDCFE" },
{ token: "function.ts", foreground: "DCDCAA" },
{ token: "method.ts", foreground: "DCDCAA" },
// { token: "delimiter.ts", foreground: "569CD6" },
],
colors: {
"editor.background": '#1b1f25',
},
});
setupTheme();

monaco.languages.setMonarchTokensProvider('typescript', tokenProvider);
monaco.languages.setMonarchTokensProvider('typescript', tokenProvider as monacoTypes.languages.IMonarchLanguage);

for (const sample of document.querySelectorAll<HTMLElement>('.sample')) {
const code = sample.querySelector('.sample-code>pre')!.textContent!.trim();
Expand Down
16 changes: 16 additions & 0 deletions site/monarch/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Monarch test</title>
<script type="module" src="monarch-playground.js"></script>
<link rel="stylesheet" href="style.css">
</head>

<body>
<div id="root"></div>
</body>

</html>
66 changes: 66 additions & 0 deletions site/monarch/monarch-playground.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import monaco from '@imlib/monaco-esm';
import { setupTheme } from '../theme.js';

setupTheme();

// monaco.languages.typescript.typescriptDefaults.addExtraLib(jsxlib(), `ts:filename/jsx.d.ts`);
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
jsx: monaco.languages.typescript.JsxEmit.ReactNative,
target: monaco.languages.typescript.ScriptTarget.ESNext,
});

const root = document.getElementById('root') as HTMLDivElement;

const file1 = await fetch('/token-provider.ts').then(res => res.text());
const file2 = await fetch('/monarch/samplecode.tsx').then(res => res.text());

const editorContainer1 = <div /> as HTMLDivElement;
const editorContainer2 = <div /> as HTMLDivElement;

root.append(editorContainer1);
root.append(editorContainer2);

const editor1 = monaco.editor.create(editorContainer1, {
lineNumbers: 'off',
fontSize: 12,
lineDecorationsWidth: 0,
minimap: { enabled: false },
guides: { indentation: false },
folding: false,
theme: "vsc2",
value: file1,
language: 'javascript',
scrollBeyondLastLine: false,
renderLineHighlightOnlyWhenFocus: true,
tabSize: 2,
});

const editor2 = monaco.editor.create(editorContainer2, {
lineNumbers: 'off',
fontSize: 12,
lineDecorationsWidth: 0,
minimap: { enabled: false },
guides: { indentation: false },
folding: false,
theme: "vsc2",
value: file2,
language: 'typescript',
scrollBeyondLastLine: false,
renderLineHighlightOnlyWhenFocus: true,
tabSize: 2,
});

editor1.layout({ width: 700, height: 900 });
editor2.layout({ width: 700, height: 900 });

updateTokenProvider();
editor1.onDidChangeModelContent(updateTokenProvider);

async function updateTokenProvider() {
const code = editor1.getModel()!.getValue();
const blob = new Blob([code], { type: 'text/javascript' });
const url = URL.createObjectURL(blob);
const mod = await import(url);
URL.revokeObjectURL(url);
monaco.languages.setMonarchTokensProvider('typescript', mod.tokenProvider);
}
229 changes: 229 additions & 0 deletions site/monarch/samplecode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
// sample 0

document.querySelectorAll<HTMLElement>('.sample');
function foo$<K, V, T extends Map<K, V>>(): Required<ReturnType<T['clear']>> {
return null as any;
}

function foo$two<T>(a: any) {
return undefined as T;
}

foo$two(foo$());

// sample 1

export function ClickMe() {
let i = 0;
const el = <button>Click me</button> as HTMLButtonElement;
el.onclick = (e) => {
el.textContent = `Clicked ${++i} times`;
};
return el;
}

// sample 2

export default () => <>
<p><ClickMe /></p>
<p><ClickMe /></p>
<p><ClickMe /></p>
</>;

// sample 3

function TodoInput(attrs: { add: (v: string) => void }) {
const input = <input type='text' /> as HTMLInputElement;
input.placeholder = 'Add todo item...';
input.onkeydown = (e) => {
if (e.key === 'Enter') {
attrs.add(input.value);
input.value = '';
}
};
return input;
}

class TodoList1 {
ul = <ul class='todolist' /> as HTMLUListElement;
add(v: string) {
const item = <li>{v}</li> as HTMLLIElement;
item.onclick = () => item.remove();
this.ul.append(item);
}
}

export const sample3 = () => {
const list = new TodoList1();
list.add('foo');
list.add('bar');
return <>
<TodoInput add={(v) => list.add(v)} />
{list.ul}
</>;
};

// sample 4

declare const data: Map<string, number>;
// Names of US citizens born in 1882 from ssa.gov

export function FindNames() {
const status = <p class='status' /> as HTMLParagraphElement;
const results = <ul /> as HTMLUListElement;
const input = <input
type='text'
value='.?mary?'
autocomplete='new-password'
oninput={updateMatches}
/> as HTMLInputElement;

updateMatches();
function updateMatches() {
const regex = new RegExp(`(${input.value})`, 'gi');
const matched = ([...data.entries()]
.filter(([k]) => k.match(regex)));

const matches = (matched
.slice(0, 25)
.map(match => <Item1 regex={regex} match={match} />));

results.replaceChildren(...matches);
status.textContent = `${matched.length} / ${data.size}`;
}

return <div>{input}{status}{results}</div>;
}

function Item1(attrs: { match: [string, number], regex: RegExp }) {
const [name, count] = attrs.match;
const total = <small style='color:#fff3'>({count})</small>;
return <li>
<span innerHTML={highlight(name, attrs.regex)} /> {total}
</li>;
}

function highlight(str: string, regex: RegExp) {
return str.replace(regex, '<span class="match">$1</span>');
}

// sample 5

export const sample5 = () => <>
<TodoList />
</>;

function TodoList() {
const list = new List();

list.add('foo');
list.add('bar').toggle();
list.add('qux');

const input = <input type='text' /> as HTMLInputElement;
input.onkeydown = (e) => {
if (e.key === 'Enter' && input.value.trim().length > 0) {
list.add(input.value);
input.value = '';
}
};

return <div id='real-todolist'>
<div>{input}</div>
<div class='actions'>
<Counter list={list} />
<button onclick={() => list.clearDone()}>Clear</button>
<button onclick={() => list.invertAll()}><i>Invert</i></button>
</div>
{list.ul}
</div>;
}

class List extends EventTarget {

ul = <ul class='list' /> as HTMLUListElement;
items: Item[] = [];
itemUnlisteners = new Map<Item, () => void>();

add(text: string) {
const item = new Item(this, text);
this.items.push(item);
this.ul.append(item.li);
this.dispatchEvent(new Event('item-added'));

this.itemUnlisteners.set(item, listen(item, 'toggled', () => {
this.dispatchEvent(new Event('item-toggled'));
}));

return item;
}

rem(item: Item) {
const unlisten = this.itemUnlisteners.get(item)!;
this.itemUnlisteners.delete(item);
unlisten();

this.items = this.items.filter(it => it !== item);
this.dispatchEvent(new Event('item-removed'));
}

clearDone = () => this.doneItems().forEach(it => it.remove());
invertAll = () => this.items.forEach(it => it.toggle());

doneItems = () => this.items.filter(it => it.done);

}

class Item extends EventTarget {

done = false;
#checkbox = <input type='checkbox' /> as HTMLInputElement;
li;

constructor(private list: List, text: string) {
super();
this.li = (
<li class='item'>
{this.#checkbox}
<span onclick={() => this.toggle()}>{text}</span>
<button class='close' onclick={() => this.remove()}></button>
</li> as HTMLLIElement
);
this.#checkbox.onclick = () => this.toggle();
}

remove() {
this.li.remove();
this.list.rem(this);
}

toggle() {
this.done = !this.done;
this.li.classList.toggle('done', this.done);
this.#checkbox.checked = this.done;
this.dispatchEvent(new Event('toggled'));
}

}

function Counter({ list }: { list: List }) {
const span = <span /> as HTMLSpanElement;

const updateText = () => {
const done = list.doneItems().length;
const total = list.items.length;
span.textContent = `Done: ${done}/${total}`;
};

updateText();
list.addEventListener('item-added', updateText);
list.addEventListener('item-removed', updateText);
list.addEventListener('item-toggled', updateText);

return span;
}

function listen(target: EventTarget, event: string, fn: () => void) {
target.addEventListener(event, fn);
return () => target.removeEventListener(event, fn);
}
10 changes: 10 additions & 0 deletions site/monarch/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
body {
background-color: #22272e;
color: #c5d1de;
font-family: system-ui;

#root {
display: flex;
gap: 1em;
}
}
18 changes: 18 additions & 0 deletions site/theme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import monaco from '@imlib/monaco-esm';

export function setupTheme() {
monaco.editor.defineTheme('vsc2', {
base: 'vs-dark',
inherit: true,
rules: [
// { token: "identifier.ts", foreground: "9CDCFE" },
{ token: "property.ts", foreground: "9CDCFE" },
{ token: "function.ts", foreground: "DCDCAA" },
{ token: "method.ts", foreground: "DCDCAA" },
// { token: "delimiter.ts", foreground: "569CD6" },
],
colors: {
"editor.background": '#1b1f25',
},
});
}
Loading

0 comments on commit 34e7ccc

Please sign in to comment.