begin migrating to solidjs

This commit is contained in:
tezlm 2023-08-02 03:15:52 -07:00
parent 3e0ef33bbb
commit 9758ca2a01
Signed by: tezlm
GPG key ID: 649733FCD94AFBBA
15 changed files with 782 additions and 213 deletions

20
web/App.tsx Normal file
View file

@ -0,0 +1,20 @@
import { onMount, onCleanup } from "solid-js";
import SvelteApp from "./App.svelte";
export function App() {
let mydiv;
let svel;
onMount(() => {
svel = new SvelteApp({ target: mydiv });
console.log(svel);
});
onCleanup(() => {
svel?.$destroy();
});
return (
<div ref={mydiv}></div>
);
}

View file

@ -20,6 +20,7 @@
</main>
</div>
</noscript>
<script type="module" src="./main.ts"></script>
<div id="root"></div>
<script type="module" src="./main.tsx"></script>
</body>
</html>

15
web/index.tsx Normal file
View file

@ -0,0 +1,15 @@
/* @refresh reload */
import { render } from 'solid-js/web';
import './index.css';
import { App } from './App';
const root = document.getElementById('root');
if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
throw new Error(
'Root element not found. Did you forget to add it to your index.html? Or maybe the id attribute got misspelled?',
);
}
render(() => <App />, root!);

View file

@ -1,6 +1,6 @@
import { api, ed25519 } from "./lib/api";
import App from "./App.svelte";
import Gallery from "./Gallery.svelte";
import { App } from "./App.tsx";
import { render } from "solid-js/web";
let key = localStorage.getItem("key");
if (key) {
@ -17,4 +17,5 @@ api.baseUrl = server || "http://localhost:3210/";
let token = localStorage.getItem("token");
api.token = token || "";
new App({ target: document.body });
render(() => <App />, document.body);
// new App({ target: document.body });

View file

@ -16,8 +16,10 @@
"canonicalize": "^2.0.0",
"carbon-icons-svelte": "^12.1.0",
"events": "^3.3.0",
"solid-js": "^1.7.8",
"typed-emitter": "^2.1.0",
"uint8-to-base64": "^0.2.0"
"uint8-to-base64": "^0.2.0",
"vite-plugin-solid": "^2.7.0"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^2.4.2",

File diff suppressed because it is too large Load diff

View file

@ -5,86 +5,18 @@
import Audio from "../../atoms/Audio.svelte";
import { events } from "../../events";
import { readable } from "svelte/store";
import { sanitize } from "./render";
import { renderInline } from "./render";
import type { Document } from "./types";
export let doc: Document;
export let embeds: Map<string, Event> = new Map();
export let hideNotes = false;
let docEl;
type Document = Array<DocBlock>;
type DocBlock =
{ type: "text", content: DocInline } |
{ type: "code", content: DocInline, lang: string } |
{ type: "quote", content: DocInline } |
{ type: "table", headers?: Array<DocInline>, rows: Array<Array<DocInline>> } |
{ type: "ul", items: Array<DocInline> } |
{ type: "ol", items: Array<DocInline> } | // TODO: find out how to nested lists
{ type: "header", content: DocInline, level: number } |
{ type: "callout", content: DocInline, is: "notice" | "info" | "warning" | "danger" } |
{ type: "embed", ref: string };
type DocInline = DocInlinePart | Array<DocInlinePart>;
type DocInlinePart = string | { text: string } & TextStyle;
// type DocInlinePart = string |
// { text: string } & TextStyle |
// { icon: string, alt: string, href: string } |
// { math: string }; // latex?
type TextStyle = Partial<{
bold: boolean,
italic: boolean,
strike: boolean,
script: "sup" | "sub",
color: string,
bgcolor: string,
href: string,
code: boolean | string,
note: Note,
}>;
type Note = { doc: DocInline, type: "inline" | "aside" };
function renderInline(doc: DocInline, allowNote = true): string {
if (Array.isArray(doc)) {
return doc.map(i => renderInlinePart(i, allowNote)).join("");
} else {
return renderInlinePart(doc, allowNote);
}
}
function renderInlinePart(inline: DocInlinePart, allowNote = true): string {
if (typeof inline === "string") return sanitize(inline);
let html = sanitize(inline.text);
if (inline.bold) html = `<b>${html}</b>`;
if (inline.italic) html = `<i>${html}</i>`;
if (inline.strike) html = `<s>${html}</s>`;
if (inline.code) {
if (typeof inline.code === "string") {
html = `<code class="lang-${sanitize(inline.code)}">${html}</code>`;
} else {
html = `<code>${html}</code>`;
}
}
if (inline.color) html = `<font color="${inline.color}">${html}</font>`;
if (inline.href) html = `<a href="${inline.href}">${html}</a>`;
if (inline.note && allowNote) {
const note = renderInline(inline.note.doc, false);
if (inline.note.type === "inline") {
html = `<span class="noted inline">${html}<span class="note">${note}</span></span>`;
} else {
html = `<span class="noted aside">${html}<span class="note">${note}</span></span>`;
}
}
return html;
}
function open(event: Event) {
const media = [...embeds.values()].filter(i => ["image", "video"].includes(i.derived?.file?.mime?.split("/")[0]));
events.emit("popup", { type: "event", event, paginate: readable(media) });
}
</script>
<div class="document" bind:this={docEl} class:hideNotes>
<div class="document" class:hideNotes>
{#each doc as block}
<!-- svelte doesn't have match or even switch/case aaaaaaaa -->
{#if false}

View file

@ -0,0 +1 @@

View file

@ -2,4 +2,5 @@ import Document from "./Document.svelte";
import EventDocument from "./EventDocument.svelte";
import EventDocScene from "./EventDocScene.svelte";
import ListDocScene from "./ListDocScene.svelte";
export { Document, EventDocument, EventDocScene, ListDocScene };
import { tokenize, parse } from "./text";
export { Document, EventDocument, EventDocScene, ListDocScene, tokenize, parse };

View file

@ -1,35 +1,4 @@
export type Document = Array<DocBlock>;
type DocBlock =
{ type: "text", content: DocInline } |
{ type: "code", content: DocInline, lang: string } |
{ type: "quote", content: DocInline } |
{ type: "table", headers?: Array<DocInline>, rows: Array<Array<DocInline>> } |
{ type: "ul", items: Array<DocInline> } |
{ type: "ol", items: Array<DocInline> } | // TODO: find out how to nested lists
{ type: "header", content: DocInline, level: number } |
{ type: "callout", content: DocInline, is: "notice" | "info" | "warning" | "danger" } |
{ type: "embed", ref: string };
export type DocInline = DocInlinePart | Array<DocInlinePart>;
export type DocInlinePart = string | { text: string } & TextStyle;
// type DocInlinePart = string |
// { text: string } & TextStyle |
// { icon: string, alt: string, href: string } |
// { math: string }; // latex?
type Note = { doc: DocInline, type: "inline" | "aside" };
type TextStyle = Partial<{
bold: boolean,
italic: boolean,
strike: boolean,
script: "sup" | "sub",
color: string,
bgcolor: string,
href: string,
code: boolean | string,
note: Note,
}>;
import type { DocInline, DocInlinePart } from "./types";
export function sanitize(str: string): string {
return str
@ -39,37 +8,36 @@ export function sanitize(str: string): string {
.replace(/>/g, "&gt;")
}
function renderInline(doc: DocInline, allowNote = true): string {
if (Array.isArray(doc)) {
return doc.map(i => renderInlinePart(i, allowNote)).join("");
} else {
return renderInlinePart(doc, allowNote);
}
}
function renderInlinePart(inline: DocInlinePart, allowNote = true): string {
if (typeof inline === "string") return sanitize(inline);
let html = sanitize(inline.text);
if (inline.bold) html = `<b>${html}</b>`;
if (inline.italic) html = `<i>${html}</i>`;
if (inline.strike) html = `<s>${html}</s>`;
if (inline.code) {
if (typeof inline.code === "string") {
html = `<code class="lang-${sanitize(inline.code)}">${html}</code>`;
} else {
html = `<code>${html}</code>`;
}
}
if (inline.color) html = `<font color="${inline.color}">${html}</font>`;
// if (typeof inline.code === "string") html = `<code lang=${inline.code}}>${html}</code>`;
if (inline.note && allowNote) {
const note = renderInline(inline.note.doc, false);
if (inline.note.type === "inline") {
html = `<span class="noted inline">${html}<aside>${note}</aside></span>`;
} else {
html = `<span class="noted aside">${html}<aside>${note}</aside></span>`;
}
}
return html;
export function renderInline(doc: DocInline, allowNote = true): string {
if (Array.isArray(doc)) {
return doc.map(i => renderInlinePart(i, allowNote)).join("");
} else {
return renderInlinePart(doc, allowNote);
}
}
function renderInlinePart(inline: DocInlinePart, allowNote = true): string {
if (typeof inline === "string") return sanitize(inline);
let html = sanitize(inline.text);
if (inline.bold) html = `<b>${html}</b>`;
if (inline.italic) html = `<i>${html}</i>`;
if (inline.strike) html = `<s>${html}</s>`;
if (inline.code) {
if (typeof inline.code === "string") {
html = `<code class="lang-${sanitize(inline.code)}">${html}</code>`;
} else {
html = `<code>${html}</code>`;
}
}
if (inline.color) html = `<font color="${inline.color}">${html}</font>`;
// if (typeof inline.code === "string") html = `<code lang=${inline.code}}>${html}</code>`;
if (inline.note && allowNote) {
const note = renderInline(inline.note.doc, false);
if (inline.note.type === "inline") {
html = `<span class="noted inline">${html}<aside>${note}</aside></span>`;
} else {
html = `<span class="noted aside">${html}<aside>${note}</aside></span>`;
}
}
return html;
}

107
web/scenes/Document/text.ts Normal file
View file

@ -0,0 +1,107 @@
import { DocInline, TextStyle } from "./types";
type Item = { text: string } & TextStyle;
interface Token {
text: string,
type: "text" | "tag" | "start" | "end",
}
export function tokenize(text: string): Array<Token> {
const tokens: Array<Token> = [];
while (text.length) {
const match = text.match(/[{}]|~(\w+)/);
if (!match) {
tokens.push({ text, type: "text" });
break;
}
const chunk = text.slice(0, match.index!);
if (chunk) tokens.push({ text: chunk, type: "text" });
switch (match[0][0]) {
case "~": tokens.push({ text: match[1], type: "tag" }); break;
case "{": tokens.push({ text: "", type: "start" }); break;
case "}": tokens.push({ text: "", type: "end" }); break;
}
text = text.slice(match[0].length + match.index!);
}
return tokens;
}
export function parse(tokens: Array<Token>, strict = true): Array<DocInline> {
const items: Array<Item> = [];
let i = 0
while (i < tokens.length) items.push(...parseNext());
const flat = items.map(i => Object.keys(i).length === 1 ? i.text : i);
return flat.length === 1 ? flat[0] : flat;
function parseNext(): Array<Item> {
if (i >= tokens.length) {
if (strict) {
throw new Error("unexpected eof");
} else {
return [];
}
}
const token = tokens[i++];
if (token.type === "text") {
return [{ text: token.text }];
} else if (token.type === "tag") {
return parseTag(token.text);
} else {
throw new Error("syntax error: unexpected { or }");
}
}
function parseTag(tag: string): Array<Item> {
const args: Array<Array<Item>> = [];
while (tokens[i]?.type === "start") {
i++;
const arg: Array<Item> = [];
if (i >= tokens.length) {
if (strict) {
throw new Error("unexpected eof");
} else {
break;
}
}
while (i < tokens.length && tokens[i]?.type !== "end") {
arg.push(...parseNext());
}
console.log("loop");
i++;
args.push(arg);
}
if (!args[0]) {
if (strict) {
throw new Error("unknown tag " + tag);
} else {
return [{ text: "~" + tag }, ...args.flat()];
}
}
switch (tag) {
case "bold": return args[0].map(i => ({ ...i, bold: true }));
case "italic": return args[0].map(i => ({ ...i, italic: true }));
case "strike": return args[0].map(i => ({ ...i, strike: true }));
case "sub": return args[0].map(i => ({ ...i, script: "sub" }));
case "sup": return args[0].map(i => ({ ...i, script: "sup" }));
case "code": return args[0].map(i => ({ ...i, code: args[1] ? stringify(args[1]) : true }));
case "color": return args[0].map(i => ({ ...i, color: stringify(args[1]) }));
case "bgcolor": return args[0].map(i => ({ ...i, bgcolor: stringify(args[1]) }));
case "link": return args[0].map(i => ({ ...i, href: stringify(args[1]) }));
default: {
if (strict) {
throw new Error("unknown tag " + tag);
} else {
return [{ text: "~" + tag }, ...args.flat()];
}
}
}
}
function stringify(items: Array<Item>): string {
return items.map(i => i.text).join("");
}
}
globalThis.tokenize = tokenize;
globalThis.parse = parse;

View file

@ -0,0 +1,32 @@
export type Document = Array<DocBlock>;
type DocBlock =
{ type: "text", content: DocInline } |
{ type: "code", content: DocInline, lang: string } |
{ type: "quote", content: DocInline } |
{ type: "table", headers?: Array<DocInline>, rows: Array<Array<DocInline>> } |
{ type: "ul", items: Array<DocInline> } |
{ type: "ol", items: Array<DocInline> } | // TODO: find out how to nested lists
{ type: "header", content: DocInline, level: number } |
{ type: "callout", content: DocInline, is: "notice" | "info" | "warning" | "danger" } |
{ type: "embed", ref: string };
export type DocInline = DocInlinePart | Array<DocInlinePart>;
export type DocInlinePart = string | { text: string } & TextStyle;
// type DocInlinePart = string |
// { text: string } & TextStyle |
// { icon: string, alt: string, href: string } |
// { math: string }; // latex?
type Note = { doc: DocInline, type: "inline" | "aside" };
export type TextStyle = Partial<{
bold: boolean,
italic: boolean,
strike: boolean,
script: "sup" | "sub",
color: string,
bgcolor: string,
href: string,
code: boolean | string,
note: Note,
}>;

View file

@ -1,7 +1,14 @@
<script lang="ts">
import { Menu, Item, Submenu, List } from "../atoms/context";
import { events } from "../events";
import { api } from "../lib/api";
import { Document, parse, tokenize } from "../scenes/Document";
let docText = "";
let doc = [];
$: try {
doc = [{ type: "text", content: parse(tokenize(docText), false)}]
} catch (err) {
console.log(err.toString());
}
</script>
<div class="wrapper">
<h1>Hello there</h1>
@ -34,6 +41,8 @@
</Menu>
<hr />
<button on:click={() => events.emit("popup", { type: "create" })}>create bucket</button><br />
<textarea bind:value={docText}></textarea><br />
<Document {doc} />
</div>
<style lang="scss">
.wrapper {

View file

@ -1,16 +1,17 @@
{
"extends": "@tsconfig/svelte/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"composite": true,
"isolatedModules": true,
"module": "ESNext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"target": "ESNext",
"useDefineForClassFields": true
"module": "ESNext",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"jsx": "preserve",
"jsxImportSource": "solid-js",
"types": ["vite/client"],
"noEmit": true,
"isolatedModules": true
},
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"]
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.tsx", "src/**/*.svelte"]
}

View file

@ -1,9 +1,10 @@
import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";
import solidPlugin from "vite-plugin-solid";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [svelte({
plugins: [solidPlugin(), svelte({
experimental: {
sendWarningsToBrowser: true,
}