begin migrating to solidjs
This commit is contained in:
parent
3e0ef33bbb
commit
9758ca2a01
15 changed files with 782 additions and 213 deletions
20
web/App.tsx
Normal file
20
web/App.tsx
Normal 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>
|
||||
);
|
||||
}
|
|
@ -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
15
web/index.tsx
Normal 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!);
|
|
@ -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 });
|
|
@ -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
|
@ -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}
|
||||
|
|
1
web/scenes/Document/DocumentEditor.svelte
Normal file
1
web/scenes/Document/DocumentEditor.svelte
Normal file
|
@ -0,0 +1 @@
|
|||
|
|
@ -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 };
|
||||
|
|
|
@ -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, ">")
|
||||
}
|
||||
|
||||
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
107
web/scenes/Document/text.ts
Normal 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;
|
32
web/scenes/Document/types.ts
Normal file
32
web/scenes/Document/types.ts
Normal 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,
|
||||
}>;
|
|
@ -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 {
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue