1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource https://esm.sh/react */
import { ViewUpdate } from "https://esm.sh/@codemirror/view";
import graymatter from "https://esm.sh/gray-matter";
import { useEffect, useMemo, useState } from "https://esm.sh/react";
import { GoogleFonts } from "https://esm.town/v/jxnblk/reactGoogleFonts";
import { render } from "https://esm.town/v/jxnblk/resrvStreaming";
import { EditorView, Prec, useCodemirror } from "https://esm.town/v/jxnblk/useCodemirrorVimMD";
// compression
import { compressToEncodedURIComponent } from "https://esm.sh/lz-string";
// preview
import ReactMarkdown from "https://esm.sh/react-markdown@9";
import rehypeSanitize from "https://esm.sh/rehype-sanitize@6";
// storage
function useLocalStorage<T>(key: string, initialValue: T) {
const [val, setStored] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.log(error);
return initialValue;
}
});
function setVal(value: T): void {
try {
setStored(value);
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.log(error);
}
}
return [val, setVal];
}
// fonts TODO
const iamono = `"iA Writer Mono", serif`;
const colors = [
{ color: "#000", background: "#fff" },
{ color: "#4d4019", background: "#faefd0" },
{ color: "#422c00", background: "#ecb545" },
{ color: "#32221b", background: "#ee976a" },
{ color: "#dab9a8", background: "#422d23" },
{ color: "#eee", background: "#222" },
{ color: "#fff", background: "#000" },
];
const theme = EditorView.theme({
"&": {
height: "100%",
lineHeight: "32px",
},
".cm-content": {
fontFamily: "inherit",
fontSize: "18px",
lineHeight: "32px",
paddingLeft: "32px",
paddingRight: "32px",
paddingTop: "64px",
paddingBottom: "128px",
maxWidth: "768px",
marginRight: "auto",
},
".cm-scroller": {
fontFamily: "inherit",
display: "flex",
},
".cm-gutters": {
textAlign: "right",
color: "inherit",
backgroundColor: "transparent",
borderRight: 0,
paddingRight: "16px",
flexShrink: 0,
marginLeft: "auto",
opacity: 0.3,
},
".cm-gutter.cm-lineNumbers": {
alignItems: "end",
},
".cm-gutterElement": {
display: "flex",
alignItems: "center",
},
".cm-cursor": {
background: "var(--highlight)",
},
".cm-fat-cursor": {
background: "var(--highlight)",
},
"&:not(.cm-focused) .cm-fat-cursor": {
outline: "solid 1px var(--highlight)",
},
"&.cm-focused .cm-selectionBackground": {
background: "var(--highlight) !important",
opacity: "0.2",
},
".tok-heading": {