Compare commits
392 commits
zola-versi
...
master
Author | SHA1 | Date | |
---|---|---|---|
d9289b88b2 | |||
6e82102c1b | |||
9c8c3588c1 | |||
1bfabbc5be | |||
4dbbd79b06 | |||
ac672a54cf | |||
9f6665b2b1 | |||
2973f40c00 | |||
2745f390d9 | |||
39911006a0 | |||
8f8e2bb4bd | |||
c2510fea83 | |||
4e485b29fe | |||
d68bbbaae6 | |||
673d86215b | |||
|
e1d0bb6d33 | ||
3e1745f215 | |||
2d0316adee | |||
ee867ae783 | |||
f1b8136785 | |||
|
87107d3a29 | ||
|
d2a3493aee | ||
2496ba67f6 | |||
2dc78d6aea | |||
127a0ca75d | |||
69d0f8a0b1 | |||
2e2fc8d729 | |||
9356908e0f | |||
ea07b359a6 | |||
478c26313f | |||
096a7a1280 | |||
702ab0e9f6 | |||
67e042fc35 | |||
8dfb20c352 | |||
8667d33798 | |||
b33d601900 | |||
55ec917eb8 | |||
d344924a1d | |||
bc92b2184b | |||
2d92966043 | |||
4273a1ca0b | |||
c9c6365c87 | |||
67e9a31def | |||
89abd4ff02 | |||
f166392f35 | |||
3e4f2d0095 | |||
79e8a2300a | |||
2f4c87ea1d | |||
37d59e6927 | |||
0dd553ad71 | |||
71cf0079dc | |||
ce5ef4116d | |||
aeef05a47f | |||
198f3727e2 | |||
764351ccb1 | |||
16c5e3ab81 | |||
67bb9a196c | |||
66de827e37 | |||
509fd14440 | |||
55da20bde0 | |||
9d073a0be6 | |||
126f3357bb | |||
4a72c2ea7b | |||
482189f355 | |||
d3d9145610 | |||
772c46f77b | |||
69c17a7a7a | |||
3949f19f1f | |||
77b3323fc6 | |||
76ea2d345a | |||
06d483b3dd | |||
bbfe4f67ca | |||
9318880a9a | |||
83ceb81f8a | |||
043b3ebe74 | |||
531b33442d | |||
85c10d012b | |||
e0ff5bf910 | |||
2b4ca03563 | |||
c1d92f48f9 | |||
726554826a | |||
6324b12c33 | |||
b574a929a7 | |||
68823357ab | |||
4f88615c31 | |||
38e2a8cec5 | |||
7375f9c81b | |||
a8f1ce9acd | |||
b5a5f9cf0a | |||
65e5471c3c | |||
5635d03e08 | |||
4fb464325c | |||
63d837b264 | |||
0d70a68769 | |||
5d631561b5 | |||
435ec21f6e | |||
dfbdf2d4ff | |||
216b1c35ed | |||
3fcaeccf48 | |||
d85b7f729f | |||
22a8c36fff | |||
6224860b0a | |||
4fc2c3e589 | |||
85ce708153 | |||
71481acbf1 | |||
f913eb52e2 | |||
b402ee102f | |||
bc8cb94181 | |||
1cb392dbae | |||
9bb4c462d8 | |||
1d48472aa2 | |||
1d3a17937d | |||
cbc83ae11d | |||
31d4b1fb71 | |||
1f454d6883 | |||
42cbda6ae1 | |||
1f8aac0a5e | |||
d4fe025437 | |||
90f7fa2ee9 | |||
ea600b05f7 | |||
62b4c105a9 | |||
565de50eb3 | |||
f048ce45ac | |||
56c95f6051 | |||
4768a49fce | |||
4dafc1b3e8 | |||
e1cb9b5e0f | |||
226264f124 | |||
dbbcc3aae7 | |||
|
32098a3278 | ||
901fb1c005 | |||
f8e04d7342 | |||
0adaf0c122 | |||
da64309feb | |||
d70327ffb9 | |||
4895546226 | |||
396e1f5098 | |||
025437ca10 | |||
bd63dba9df | |||
5216167465 | |||
f50ec75e01 | |||
0e0249d113 | |||
92c7982c0c | |||
183c998199 | |||
fe2377b0b4 | |||
0c3659055a | |||
8670e8cf7b | |||
3e07267470 | |||
9ecc011029 | |||
e22340a98d | |||
948c982418 | |||
b33ba945b6 | |||
5319b38560 | |||
1e5bef0b6f | |||
efd0b420dd | |||
11e1601426 | |||
1ff3499e81 | |||
43a56c941a | |||
3935a8934c | |||
3833a310e0 | |||
334e6cb1bf | |||
b36a272815 | |||
263ed46253 | |||
328680c54b | |||
f9e388eb6e | |||
109c2997c3 | |||
2d7abd47d0 | |||
99e0e5ebab | |||
f497f3536a | |||
6bb860f61a | |||
ab1597a28b | |||
c4b29c5e6f | |||
60db0faba3 | |||
89ece03307 | |||
a670600ad7 | |||
e3ded78ba3 | |||
13d83842aa | |||
424c056a09 | |||
3bf0200e7e | |||
e94fee5345 | |||
49cfb3ccc4 | |||
8e1fac9bd5 | |||
5ecc5f8eed | |||
b47c8ffae3 | |||
0627d6b0d9 | |||
2122aca697 | |||
4a03a76348 | |||
0ef4b692e4 | |||
88fd13a01a | |||
2a1ebcf251 | |||
edcbc68af3 | |||
6468b29dd7 | |||
62ad559dbd | |||
c7c8be95b5 | |||
b61a54de22 | |||
f3d89cfb61 | |||
83d21cf9b6 | |||
b29d3dda82 | |||
8c5c341f44 | |||
35f6d93ca0 | |||
0c6f3ff48a | |||
322441a144 | |||
b892dfa000 | |||
0c2ac5e521 | |||
10426919e1 | |||
86feeefbe3 | |||
2c1b3c5677 | |||
e8f1437d95 | |||
e2cc513dd4 | |||
c478116f88 | |||
a3a7d84d1e | |||
4a89b35ba3 | |||
4eeec04dad | |||
43c5a8f70c | |||
4b853c6c86 | |||
98d3dddc41 | |||
3f6bdacaf6 | |||
abb4f9f8ad | |||
68fb26e7ac | |||
5850483297 | |||
63c7c43afc | |||
353f325bac | |||
28025a8c66 | |||
7f8137b9f6 | |||
2f68aae3dd | |||
a08f1206c2 | |||
afaec7c0b6 | |||
079c2a8b38 | |||
535c3dca9f | |||
60b97648da | |||
b781f83415 | |||
6d753e5250 | |||
a248240a6a | |||
07484d5812 | |||
db20f9536a | |||
608503637c | |||
e31a8ba367 | |||
970a8b6e9f | |||
5e3f3a25c4 | |||
be60e791da | |||
951689a328 | |||
d6551c4632 | |||
f8f1987fbf | |||
d62683c6bc | |||
59b4c52eee | |||
8c44b266ff | |||
354a353df9 | |||
49b564a620 | |||
806b929d16 | |||
714ab14f0f | |||
539fa061cd | |||
78f1049fb7 | |||
df009fccbe | |||
9046fcc57e | |||
90de593a38 | |||
0a6e73df19 | |||
cc538bc7f3 | |||
9396a58f26 | |||
910fa274ba | |||
d39c66e866 | |||
6ae17e62b8 | |||
63c55873d9 | |||
7b065d5192 | |||
cc22f9b9ae | |||
10801c1471 | |||
4654b8de5f | |||
aa5936c70b | |||
89c28d0a45 | |||
ad3bd9bce1 | |||
a2367d88db | |||
5090a5a95c | |||
15181bb53d | |||
e70343da96 | |||
417c62988c | |||
d304c41903 | |||
125c39131c | |||
49ecbae990 | |||
|
1884515c6b | ||
98e1972a80 | |||
cd872604cd | |||
b4caf7a0c2 | |||
4e4e3fd3af | |||
abe6172448 | |||
|
9af469091d | ||
|
baa186ea5a | ||
|
fe2c3f2524 | ||
307ba4e59f | |||
|
895ff81573 | ||
|
78a86b64e3 | ||
604e47888b | |||
dc80e5b44a | |||
afc086cc26 | |||
26478e2add | |||
79159229a5 | |||
|
768a5b1809 | ||
|
44ba9624f7 | ||
|
34ea75029b | ||
|
56bbbc5dee | ||
f4d9ae6b14 | |||
e6776d179c | |||
ad17aefc9c | |||
9336875427 | |||
3e90f780aa | |||
b19471d9fd | |||
bf5a0ee802 | |||
ac77b241fb | |||
86a217d5f0 | |||
cfb914149d | |||
b0188f6c9e | |||
9623a74793 | |||
648ed87a91 | |||
184d29012e | |||
2690a09aa7 | |||
e59fdc101c | |||
efc6916dce | |||
9caf7b637e | |||
e11c78b3e3 | |||
631c1952f8 | |||
89f417692d | |||
2fb170a60d | |||
ad987cb3f6 | |||
1838120b01 | |||
10d53c0b2a | |||
ee3ff81526 | |||
f3a17a3f69 | |||
81fa0bbb36 | |||
50b600b519 | |||
3424164934 | |||
a2e3f6f6e6 | |||
5b3c5b0257 | |||
f757885ac6 | |||
4409efed7e | |||
f17a37183a | |||
09e274a481 | |||
114af15e61 | |||
b04175954b | |||
8bcce8a5c7 | |||
7b408c47ac | |||
99bd0368b6 | |||
288e58e650 | |||
380c9e0d20 | |||
2b6f2c76ef | |||
eb2bfd99f0 | |||
ccd16a2648 | |||
098d5014a5 | |||
9b9e46d641 | |||
eae8265313 | |||
8c1ee0d40e | |||
796cb7eb08 | |||
849c0c3bb6 | |||
a163172b8b | |||
e2224b842c | |||
52c5102c5e | |||
bddce624f4 | |||
99523573f4 | |||
63f2a3b7b4 | |||
06310d382b | |||
4375e85777 | |||
86a40a6f03 | |||
f288bab98b | |||
faec6d1cda | |||
f880ad26cc | |||
bf5a9500d9 | |||
2c055b0ad3 | |||
6174c737b5 | |||
d4b5e83682 | |||
a8d7d2ab3a | |||
ce0723cd05 | |||
b5682d2166 | |||
7d9a04b8b0 | |||
f07a3bbf7d | |||
7efd282bf3 | |||
dfa7ebfccb | |||
47e52008d2 | |||
96ff3c6997 | |||
d8ec3896ae | |||
0cb19de91f | |||
acc0fd777a | |||
c2eee664a7 | |||
da58f775a6 | |||
3cda8d3dd9 | |||
90ba02d903 | |||
ebe186c792 | |||
75e9d77c1e | |||
9d6e454793 | |||
e2167780ca | |||
322c8c40ee | |||
179d89177a | |||
4edef45e83 | |||
11a088a82d | |||
94dd746d6b | |||
552b6b3892 |
326 changed files with 71089 additions and 1796 deletions
28
.build.yml
Normal file
28
.build.yml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
image: alpine/edge
|
||||||
|
oauth: pages.sr.ht/PAGES:RW
|
||||||
|
packages:
|
||||||
|
- hut
|
||||||
|
- npm
|
||||||
|
- rsync
|
||||||
|
- typst
|
||||||
|
environment:
|
||||||
|
site: mzhang.io
|
||||||
|
secrets:
|
||||||
|
- 0b26b413-7901-41c3-a4e2-3c752228ffcb
|
||||||
|
sources:
|
||||||
|
- https://git.sr.ht/~mzhang/blog
|
||||||
|
tasks:
|
||||||
|
- install: |
|
||||||
|
sudo npm install -g pnpm
|
||||||
|
- build: |
|
||||||
|
cd blog
|
||||||
|
pnpm install
|
||||||
|
pnpm run build
|
||||||
|
# hugo --buildDrafts --minify --baseURL https://mzhang.io
|
||||||
|
- upload: |
|
||||||
|
cd blog/dist
|
||||||
|
tar -cvz . > ../site.tar.gz
|
||||||
|
cd ..
|
||||||
|
hut pages publish -d $site site.tar.gz
|
||||||
|
# echo "mzhang.io ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBzBZ+QmM4EO3Fwc1ZcvWV2IY9VF04T0H9brorGj9Udp" >> ~/.ssh/known_hosts
|
||||||
|
# rsync -azvrP dist/ sourcehutBuilds@mzhang.io:/mnt/storage/svcdata/blog-public
|
1
.dockerignore
Normal file
1
.dockerignore
Normal file
|
@ -0,0 +1 @@
|
||||||
|
*
|
|
@ -1,6 +0,0 @@
|
||||||
pipeline:
|
|
||||||
build:
|
|
||||||
image: j1mc/docker-zola
|
|
||||||
commands:
|
|
||||||
- zola build
|
|
||||||
|
|
|
@ -6,4 +6,4 @@ insert_final_newline = true
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 2
|
||||||
|
|
1
.envrc
Normal file
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
use flake
|
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
public/katex/**/* linguist-vendored
|
||||||
|
src/**/*.md linguist-documentation=false
|
35
.gitignore
vendored
35
.gitignore
vendored
|
@ -1 +1,34 @@
|
||||||
public
|
# build output
|
||||||
|
dist/
|
||||||
|
# generated types
|
||||||
|
.astro/
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# logs
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
|
||||||
|
# environment variables
|
||||||
|
.env
|
||||||
|
.env.production
|
||||||
|
|
||||||
|
# macOS-specific files
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
PragmataPro-Mono-Liga-Regular-Nerd-Font-Complete.woff2
|
||||||
|
*.agdai
|
||||||
|
_build
|
||||||
|
.direnv
|
||||||
|
|
||||||
|
|
||||||
|
/result
|
||||||
|
.pnpm-store
|
||||||
|
|
||||||
|
/public/generated
|
||||||
|
|
||||||
|
.frontmatter
|
||||||
|
|
3
.prettierignore
Normal file
3
.prettierignore
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
public/katex
|
||||||
|
pnpm-lock.yaml
|
||||||
|
src/styles/fork-awesome
|
17
.prettierrc.json
Normal file
17
.prettierrc.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"plugins": ["prettier-plugin-astro"],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": "*.astro",
|
||||||
|
"options": {
|
||||||
|
"parser": "astro"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"useTabs": false,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"singleQuote": false,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"printWidth": 100
|
||||||
|
}
|
4
.tokeignore
Normal file
4
.tokeignore
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
public
|
||||||
|
package-lock.json
|
||||||
|
src/styles/fork-awesome
|
||||||
|
pnpm-lock.yaml
|
7
.vscode/extensions.json
vendored
Normal file
7
.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"astro-build.astro-vscode",
|
||||||
|
"eliostruyf.vscode-front-matter"
|
||||||
|
],
|
||||||
|
"unwantedRecommendations": []
|
||||||
|
}
|
11
.vscode/launch.json
vendored
Normal file
11
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"command": "./node_modules/.bin/astro dev",
|
||||||
|
"name": "Development server",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "node-terminal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"npm.packageManager": "pnpm"
|
||||||
|
}
|
27
.woodpecker/deploy.yml
Normal file
27
.woodpecker/deploy.yml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
steps:
|
||||||
|
build:
|
||||||
|
image: git.mzhang.io/michael/blog-docker-builder:6gzd1rhcl41y02yw4z9kpjgrhxifqyfs
|
||||||
|
environment:
|
||||||
|
- ASTRO_TELEMETRY_DISABLED=1
|
||||||
|
commands:
|
||||||
|
- mkdir /tmp
|
||||||
|
- rm -rf node_modules
|
||||||
|
- npm i -g pnpm@9.4.0
|
||||||
|
- npx pnpm install --frozen-lockfile
|
||||||
|
- npx pnpm run build
|
||||||
|
when:
|
||||||
|
- event: push
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
image: git.mzhang.io/michael/blog-docker-builder:6gzd1rhcl41y02yw4z9kpjgrhxifqyfs
|
||||||
|
commands:
|
||||||
|
- mc alias set $AWS_DEFAULT_REGION $AWS_ENDPOINT_URL $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY --api S3v4
|
||||||
|
- mc mirror --overwrite ./dist/ $AWS_DEFAULT_REGION/mzhang-io-website/
|
||||||
|
secrets:
|
||||||
|
- AWS_ACCESS_KEY_ID
|
||||||
|
- AWS_DEFAULT_REGION
|
||||||
|
- AWS_ENDPOINT_URL
|
||||||
|
- AWS_SECRET_ACCESS_KEY
|
||||||
|
when:
|
||||||
|
- branch: master
|
||||||
|
event: push
|
5
README.md
Normal file
5
README.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# Michael's Blog
|
||||||
|
|
||||||
|
https://mzhang.io
|
||||||
|
|
||||||
|
License: GPL-3.0 code / CC-BY-SA-4.0 contents
|
57
astro.config.ts
Normal file
57
astro.config.ts
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import { defineConfig } from "astro/config";
|
||||||
|
import mdx from "@astrojs/mdx";
|
||||||
|
import sitemap from "@astrojs/sitemap";
|
||||||
|
import { rehypeAccessibleEmojis } from "rehype-accessible-emojis";
|
||||||
|
import remarkReadingTime from "./plugin/remark-reading-time";
|
||||||
|
import remarkEmoji from "remark-emoji";
|
||||||
|
import remarkDescription from "astro-remark-description";
|
||||||
|
import remarkAdmonitions, { mkdocsConfig } from "./plugin/remark-admonitions";
|
||||||
|
import remarkMath from "remark-math";
|
||||||
|
import rehypeKatex from "rehype-katex";
|
||||||
|
import remarkTypst from "./plugin/remark-typst";
|
||||||
|
import rehypeLinkHeadings from "@justfork/rehype-autolink-headings";
|
||||||
|
import rehypeSlug from "rehype-slug";
|
||||||
|
|
||||||
|
import markdoc from "@astrojs/markdoc";
|
||||||
|
import remarkAgda from "./plugin/remark-agda";
|
||||||
|
|
||||||
|
const outDir = "dist";
|
||||||
|
const base = process.env.BASE ?? "/";
|
||||||
|
const publicDir = "public";
|
||||||
|
|
||||||
|
// https://astro.build/config
|
||||||
|
export default defineConfig({
|
||||||
|
site: "https://mzhang.io",
|
||||||
|
prefetch: true,
|
||||||
|
integrations: [mdx(), sitemap(), markdoc()],
|
||||||
|
|
||||||
|
outDir,
|
||||||
|
base,
|
||||||
|
trailingSlash: "always",
|
||||||
|
publicDir,
|
||||||
|
|
||||||
|
markdown: {
|
||||||
|
syntaxHighlight: "shiki",
|
||||||
|
shikiConfig: { theme: "css-variables" },
|
||||||
|
remarkPlugins: [
|
||||||
|
() => remarkAgda({ outDir, base, publicDir }),
|
||||||
|
remarkMath,
|
||||||
|
[remarkAdmonitions, mkdocsConfig],
|
||||||
|
remarkReadingTime,
|
||||||
|
remarkTypst,
|
||||||
|
remarkEmoji,
|
||||||
|
[
|
||||||
|
remarkDescription,
|
||||||
|
{
|
||||||
|
name: "excerpt",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
rehypePlugins: [
|
||||||
|
[rehypeKatex, {}],
|
||||||
|
rehypeAccessibleEmojis,
|
||||||
|
rehypeSlug,
|
||||||
|
[rehypeLinkHeadings, { behavior: "wrap" }],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
17
biome.json
Normal file
17
biome.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://biomejs.dev/schemas/1.7.3/schema.json",
|
||||||
|
"organizeImports": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"formatter": {
|
||||||
|
"enabled": true,
|
||||||
|
"indentStyle": "space",
|
||||||
|
"indentWidth": 2
|
||||||
|
},
|
||||||
|
"linter": {
|
||||||
|
"enabled": true,
|
||||||
|
"rules": {
|
||||||
|
"recommended": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
blog.agda-lib
Normal file
2
blog.agda-lib
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
include: src/content/posts src
|
||||||
|
depend: standard-library cubical
|
BIN
bun.lockb
Executable file
BIN
bun.lockb
Executable file
Binary file not shown.
22
config.toml
22
config.toml
|
@ -1,22 +0,0 @@
|
||||||
title = "michael's blog"
|
|
||||||
base_url = "https://iptq.io"
|
|
||||||
|
|
||||||
compile_sass = true
|
|
||||||
highlight_code = true
|
|
||||||
generate_rss = true
|
|
||||||
|
|
||||||
taxonomies = [
|
|
||||||
{ name = "tags", rss = true }
|
|
||||||
]
|
|
||||||
|
|
||||||
[external_renderers]
|
|
||||||
dot = "set -e; echo -n '<p class=\"graphviz\">'; dot -Tsvg | tail -n +4; echo '</p>'"
|
|
||||||
|
|
||||||
[extra]
|
|
||||||
nav_links = [
|
|
||||||
{ url = "/", text = "home" },
|
|
||||||
{ url = "/pages/about", text = "about" },
|
|
||||||
{ url = "/pages/projects", text = "projects" },
|
|
||||||
{ url = "/setup", text = "setup" },
|
|
||||||
{ url = "/pages", text = "all pages" },
|
|
||||||
]
|
|
|
@ -1,8 +0,0 @@
|
||||||
+++
|
|
||||||
title = "drawing bezier curves"
|
|
||||||
date = 2020-04-12
|
|
||||||
draft = true
|
|
||||||
|
|
||||||
[taxonomies]
|
|
||||||
tags = ["osu", "graphics"]
|
|
||||||
+++
|
|
|
@ -1,11 +0,0 @@
|
||||||
+++
|
|
||||||
title = "home"
|
|
||||||
template = "blog.html"
|
|
||||||
page_template = "post.html"
|
|
||||||
|
|
||||||
insert_anchor_links = "left"
|
|
||||||
sort_by = "date"
|
|
||||||
|
|
||||||
[extra]
|
|
||||||
include_posts = true
|
|
||||||
+++
|
|
|
@ -1,639 +0,0 @@
|
||||||
"use strict";
|
|
||||||
|
|
||||||
if( typeof Rust === "undefined" ) {
|
|
||||||
var Rust = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
(function( root, factory ) {
|
|
||||||
if( typeof define === "function" && define.amd ) {
|
|
||||||
define( [], factory );
|
|
||||||
} else if( typeof module === "object" && module.exports ) {
|
|
||||||
module.exports = factory();
|
|
||||||
} else {
|
|
||||||
Rust.helloworld = factory();
|
|
||||||
}
|
|
||||||
}( this, function() {
|
|
||||||
return (function( module_factory ) {
|
|
||||||
var instance = module_factory();
|
|
||||||
|
|
||||||
if( typeof process === "object" && typeof process.versions === "object" && typeof process.versions.node === "string" ) {
|
|
||||||
var fs = require( "fs" );
|
|
||||||
var path = require( "path" );
|
|
||||||
var wasm_path = path.join( __dirname, "helloworld.wasm" );
|
|
||||||
var buffer = fs.readFileSync( wasm_path );
|
|
||||||
var mod = new WebAssembly.Module( buffer );
|
|
||||||
var wasm_instance = new WebAssembly.Instance( mod, instance.imports );
|
|
||||||
return instance.initialize( wasm_instance );
|
|
||||||
} else {
|
|
||||||
var file = fetch( "helloworld.wasm", {credentials: "same-origin"} );
|
|
||||||
|
|
||||||
var wasm_instance = ( typeof WebAssembly.instantiateStreaming === "function"
|
|
||||||
? WebAssembly.instantiateStreaming( file, instance.imports )
|
|
||||||
.then( function( result ) { return result.instance; } )
|
|
||||||
|
|
||||||
: file
|
|
||||||
.then( function( response ) { return response.arrayBuffer(); } )
|
|
||||||
.then( function( bytes ) { return WebAssembly.compile( bytes ); } )
|
|
||||||
.then( function( mod ) { return WebAssembly.instantiate( mod, instance.imports ) } ) );
|
|
||||||
|
|
||||||
return wasm_instance
|
|
||||||
.then( function( wasm_instance ) {
|
|
||||||
var exports = instance.initialize( wasm_instance );
|
|
||||||
console.log( "Finished loading Rust wasm module 'helloworld'" );
|
|
||||||
return exports;
|
|
||||||
})
|
|
||||||
.catch( function( error ) {
|
|
||||||
console.log( "Error loading Rust wasm module 'helloworld':", error );
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}( function() {
|
|
||||||
var Module = {};
|
|
||||||
|
|
||||||
Module.STDWEB_PRIVATE = {};
|
|
||||||
|
|
||||||
// This is based on code from Emscripten's preamble.js.
|
|
||||||
Module.STDWEB_PRIVATE.to_utf8 = function to_utf8( str, addr ) {
|
|
||||||
var HEAPU8 = Module.HEAPU8;
|
|
||||||
for( var i = 0; i < str.length; ++i ) {
|
|
||||||
// Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8.
|
|
||||||
// See http://unicode.org/faq/utf_bom.html#utf16-3
|
|
||||||
// For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description and https://www.ietf.org/rfc/rfc2279.txt and https://tools.ietf.org/html/rfc3629
|
|
||||||
var u = str.charCodeAt( i ); // possibly a lead surrogate
|
|
||||||
if( u >= 0xD800 && u <= 0xDFFF ) {
|
|
||||||
u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt( ++i ) & 0x3FF);
|
|
||||||
}
|
|
||||||
|
|
||||||
if( u <= 0x7F ) {
|
|
||||||
HEAPU8[ addr++ ] = u;
|
|
||||||
} else if( u <= 0x7FF ) {
|
|
||||||
HEAPU8[ addr++ ] = 0xC0 | (u >> 6);
|
|
||||||
HEAPU8[ addr++ ] = 0x80 | (u & 63);
|
|
||||||
} else if( u <= 0xFFFF ) {
|
|
||||||
HEAPU8[ addr++ ] = 0xE0 | (u >> 12);
|
|
||||||
HEAPU8[ addr++ ] = 0x80 | ((u >> 6) & 63);
|
|
||||||
HEAPU8[ addr++ ] = 0x80 | (u & 63);
|
|
||||||
} else if( u <= 0x1FFFFF ) {
|
|
||||||
HEAPU8[ addr++ ] = 0xF0 | (u >> 18);
|
|
||||||
HEAPU8[ addr++ ] = 0x80 | ((u >> 12) & 63);
|
|
||||||
HEAPU8[ addr++ ] = 0x80 | ((u >> 6) & 63);
|
|
||||||
HEAPU8[ addr++ ] = 0x80 | (u & 63);
|
|
||||||
} else if( u <= 0x3FFFFFF ) {
|
|
||||||
HEAPU8[ addr++ ] = 0xF8 | (u >> 24);
|
|
||||||
HEAPU8[ addr++ ] = 0x80 | ((u >> 18) & 63);
|
|
||||||
HEAPU8[ addr++ ] = 0x80 | ((u >> 12) & 63);
|
|
||||||
HEAPU8[ addr++ ] = 0x80 | ((u >> 6) & 63);
|
|
||||||
HEAPU8[ addr++ ] = 0x80 | (u & 63);
|
|
||||||
} else {
|
|
||||||
HEAPU8[ addr++ ] = 0xFC | (u >> 30);
|
|
||||||
HEAPU8[ addr++ ] = 0x80 | ((u >> 24) & 63);
|
|
||||||
HEAPU8[ addr++ ] = 0x80 | ((u >> 18) & 63);
|
|
||||||
HEAPU8[ addr++ ] = 0x80 | ((u >> 12) & 63);
|
|
||||||
HEAPU8[ addr++ ] = 0x80 | ((u >> 6) & 63);
|
|
||||||
HEAPU8[ addr++ ] = 0x80 | (u & 63);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Module.STDWEB_PRIVATE.noop = function() {};
|
|
||||||
Module.STDWEB_PRIVATE.to_js = function to_js( address ) {
|
|
||||||
var kind = Module.HEAPU8[ address + 12 ];
|
|
||||||
if( kind === 0 ) {
|
|
||||||
return undefined;
|
|
||||||
} else if( kind === 1 ) {
|
|
||||||
return null;
|
|
||||||
} else if( kind === 2 ) {
|
|
||||||
return Module.HEAP32[ address / 4 ];
|
|
||||||
} else if( kind === 3 ) {
|
|
||||||
return Module.HEAPF64[ address / 8 ];
|
|
||||||
} else if( kind === 4 ) {
|
|
||||||
var pointer = Module.HEAPU32[ address / 4 ];
|
|
||||||
var length = Module.HEAPU32[ (address + 4) / 4 ];
|
|
||||||
return Module.STDWEB_PRIVATE.to_js_string( pointer, length );
|
|
||||||
} else if( kind === 5 ) {
|
|
||||||
return false;
|
|
||||||
} else if( kind === 6 ) {
|
|
||||||
return true;
|
|
||||||
} else if( kind === 7 ) {
|
|
||||||
var pointer = Module.STDWEB_PRIVATE.arena + Module.HEAPU32[ address / 4 ];
|
|
||||||
var length = Module.HEAPU32[ (address + 4) / 4 ];
|
|
||||||
var output = [];
|
|
||||||
for( var i = 0; i < length; ++i ) {
|
|
||||||
output.push( Module.STDWEB_PRIVATE.to_js( pointer + i * 16 ) );
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
} else if( kind === 8 ) {
|
|
||||||
var arena = Module.STDWEB_PRIVATE.arena;
|
|
||||||
var value_array_pointer = arena + Module.HEAPU32[ address / 4 ];
|
|
||||||
var length = Module.HEAPU32[ (address + 4) / 4 ];
|
|
||||||
var key_array_pointer = arena + Module.HEAPU32[ (address + 8) / 4 ];
|
|
||||||
var output = {};
|
|
||||||
for( var i = 0; i < length; ++i ) {
|
|
||||||
var key_pointer = Module.HEAPU32[ (key_array_pointer + i * 8) / 4 ];
|
|
||||||
var key_length = Module.HEAPU32[ (key_array_pointer + 4 + i * 8) / 4 ];
|
|
||||||
var key = Module.STDWEB_PRIVATE.to_js_string( key_pointer, key_length );
|
|
||||||
var value = Module.STDWEB_PRIVATE.to_js( value_array_pointer + i * 16 );
|
|
||||||
output[ key ] = value;
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
} else if( kind === 9 ) {
|
|
||||||
return Module.STDWEB_PRIVATE.acquire_js_reference( Module.HEAP32[ address / 4 ] );
|
|
||||||
} else if( kind === 10 || kind === 12 || kind === 13 ) {
|
|
||||||
var adapter_pointer = Module.HEAPU32[ address / 4 ];
|
|
||||||
var pointer = Module.HEAPU32[ (address + 4) / 4 ];
|
|
||||||
var deallocator_pointer = Module.HEAPU32[ (address + 8) / 4 ];
|
|
||||||
var num_ongoing_calls = 0;
|
|
||||||
var drop_queued = false;
|
|
||||||
var output = function() {
|
|
||||||
if( pointer === 0 || drop_queued === true ) {
|
|
||||||
if (kind === 10) {
|
|
||||||
throw new ReferenceError( "Already dropped Rust function called!" );
|
|
||||||
} else if (kind === 12) {
|
|
||||||
throw new ReferenceError( "Already dropped FnMut function called!" );
|
|
||||||
} else {
|
|
||||||
throw new ReferenceError( "Already called or dropped FnOnce function called!" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var function_pointer = pointer;
|
|
||||||
if (kind === 13) {
|
|
||||||
output.drop = Module.STDWEB_PRIVATE.noop;
|
|
||||||
pointer = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (num_ongoing_calls !== 0) {
|
|
||||||
if (kind === 12 || kind === 13) {
|
|
||||||
throw new ReferenceError( "FnMut function called multiple times concurrently!" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var args = Module.STDWEB_PRIVATE.alloc( 16 );
|
|
||||||
Module.STDWEB_PRIVATE.serialize_array( args, arguments );
|
|
||||||
|
|
||||||
try {
|
|
||||||
num_ongoing_calls += 1;
|
|
||||||
Module.STDWEB_PRIVATE.dyncall( "vii", adapter_pointer, [function_pointer, args] );
|
|
||||||
var result = Module.STDWEB_PRIVATE.tmp;
|
|
||||||
Module.STDWEB_PRIVATE.tmp = null;
|
|
||||||
} finally {
|
|
||||||
num_ongoing_calls -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( drop_queued === true && num_ongoing_calls === 0 ) {
|
|
||||||
output.drop();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
output.drop = function() {
|
|
||||||
if (num_ongoing_calls !== 0) {
|
|
||||||
drop_queued = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
output.drop = Module.STDWEB_PRIVATE.noop;
|
|
||||||
var function_pointer = pointer;
|
|
||||||
pointer = 0;
|
|
||||||
|
|
||||||
if (function_pointer != 0) {
|
|
||||||
Module.STDWEB_PRIVATE.dyncall( "vi", deallocator_pointer, [function_pointer] );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return output;
|
|
||||||
} else if( kind === 14 ) {
|
|
||||||
var pointer = Module.HEAPU32[ address / 4 ];
|
|
||||||
var length = Module.HEAPU32[ (address + 4) / 4 ];
|
|
||||||
var array_kind = Module.HEAPU32[ (address + 8) / 4 ];
|
|
||||||
var pointer_end = pointer + length;
|
|
||||||
|
|
||||||
switch( array_kind ) {
|
|
||||||
case 0:
|
|
||||||
return Module.HEAPU8.subarray( pointer, pointer_end );
|
|
||||||
case 1:
|
|
||||||
return Module.HEAP8.subarray( pointer, pointer_end );
|
|
||||||
case 2:
|
|
||||||
return Module.HEAPU16.subarray( pointer, pointer_end );
|
|
||||||
case 3:
|
|
||||||
return Module.HEAP16.subarray( pointer, pointer_end );
|
|
||||||
case 4:
|
|
||||||
return Module.HEAPU32.subarray( pointer, pointer_end );
|
|
||||||
case 5:
|
|
||||||
return Module.HEAP32.subarray( pointer, pointer_end );
|
|
||||||
case 6:
|
|
||||||
return Module.HEAPF32.subarray( pointer, pointer_end );
|
|
||||||
case 7:
|
|
||||||
return Module.HEAPF64.subarray( pointer, pointer_end );
|
|
||||||
}
|
|
||||||
} else if( kind === 15 ) {
|
|
||||||
return Module.STDWEB_PRIVATE.get_raw_value( Module.HEAPU32[ address / 4 ] );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Module.STDWEB_PRIVATE.serialize_object = function serialize_object( address, value ) {
|
|
||||||
var keys = Object.keys( value );
|
|
||||||
var length = keys.length;
|
|
||||||
var key_array_pointer = Module.STDWEB_PRIVATE.alloc( length * 8 );
|
|
||||||
var value_array_pointer = Module.STDWEB_PRIVATE.alloc( length * 16 );
|
|
||||||
Module.HEAPU8[ address + 12 ] = 8;
|
|
||||||
Module.HEAPU32[ address / 4 ] = value_array_pointer;
|
|
||||||
Module.HEAPU32[ (address + 4) / 4 ] = length;
|
|
||||||
Module.HEAPU32[ (address + 8) / 4 ] = key_array_pointer;
|
|
||||||
for( var i = 0; i < length; ++i ) {
|
|
||||||
var key = keys[ i ];
|
|
||||||
var key_address = key_array_pointer + i * 8;
|
|
||||||
Module.STDWEB_PRIVATE.to_utf8_string( key_address, key );
|
|
||||||
|
|
||||||
Module.STDWEB_PRIVATE.from_js( value_array_pointer + i * 16, value[ key ] );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Module.STDWEB_PRIVATE.serialize_array = function serialize_array( address, value ) {
|
|
||||||
var length = value.length;
|
|
||||||
var pointer = Module.STDWEB_PRIVATE.alloc( length * 16 );
|
|
||||||
Module.HEAPU8[ address + 12 ] = 7;
|
|
||||||
Module.HEAPU32[ address / 4 ] = pointer;
|
|
||||||
Module.HEAPU32[ (address + 4) / 4 ] = length;
|
|
||||||
for( var i = 0; i < length; ++i ) {
|
|
||||||
Module.STDWEB_PRIVATE.from_js( pointer + i * 16, value[ i ] );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// New browsers and recent Node
|
|
||||||
var cachedEncoder = ( typeof TextEncoder === "function"
|
|
||||||
? new TextEncoder( "utf-8" )
|
|
||||||
// Old Node (before v11)
|
|
||||||
: ( typeof util === "object" && util && typeof util.TextEncoder === "function"
|
|
||||||
? new util.TextEncoder( "utf-8" )
|
|
||||||
// Old browsers
|
|
||||||
: null ) );
|
|
||||||
|
|
||||||
if ( cachedEncoder != null ) {
|
|
||||||
Module.STDWEB_PRIVATE.to_utf8_string = function to_utf8_string( address, value ) {
|
|
||||||
var buffer = cachedEncoder.encode( value );
|
|
||||||
var length = buffer.length;
|
|
||||||
var pointer = 0;
|
|
||||||
|
|
||||||
if ( length > 0 ) {
|
|
||||||
pointer = Module.STDWEB_PRIVATE.alloc( length );
|
|
||||||
Module.HEAPU8.set( buffer, pointer );
|
|
||||||
}
|
|
||||||
|
|
||||||
Module.HEAPU32[ address / 4 ] = pointer;
|
|
||||||
Module.HEAPU32[ (address + 4) / 4 ] = length;
|
|
||||||
};
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Module.STDWEB_PRIVATE.to_utf8_string = function to_utf8_string( address, value ) {
|
|
||||||
var length = Module.STDWEB_PRIVATE.utf8_len( value );
|
|
||||||
var pointer = 0;
|
|
||||||
|
|
||||||
if ( length > 0 ) {
|
|
||||||
pointer = Module.STDWEB_PRIVATE.alloc( length );
|
|
||||||
Module.STDWEB_PRIVATE.to_utf8( value, pointer );
|
|
||||||
}
|
|
||||||
|
|
||||||
Module.HEAPU32[ address / 4 ] = pointer;
|
|
||||||
Module.HEAPU32[ (address + 4) / 4 ] = length;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Module.STDWEB_PRIVATE.from_js = function from_js( address, value ) {
|
|
||||||
var kind = Object.prototype.toString.call( value );
|
|
||||||
if( kind === "[object String]" ) {
|
|
||||||
Module.HEAPU8[ address + 12 ] = 4;
|
|
||||||
Module.STDWEB_PRIVATE.to_utf8_string( address, value );
|
|
||||||
} else if( kind === "[object Number]" ) {
|
|
||||||
if( value === (value|0) ) {
|
|
||||||
Module.HEAPU8[ address + 12 ] = 2;
|
|
||||||
Module.HEAP32[ address / 4 ] = value;
|
|
||||||
} else {
|
|
||||||
Module.HEAPU8[ address + 12 ] = 3;
|
|
||||||
Module.HEAPF64[ address / 8 ] = value;
|
|
||||||
}
|
|
||||||
} else if( value === null ) {
|
|
||||||
Module.HEAPU8[ address + 12 ] = 1;
|
|
||||||
} else if( value === undefined ) {
|
|
||||||
Module.HEAPU8[ address + 12 ] = 0;
|
|
||||||
} else if( value === false ) {
|
|
||||||
Module.HEAPU8[ address + 12 ] = 5;
|
|
||||||
} else if( value === true ) {
|
|
||||||
Module.HEAPU8[ address + 12 ] = 6;
|
|
||||||
} else if( kind === "[object Symbol]" ) {
|
|
||||||
var id = Module.STDWEB_PRIVATE.register_raw_value( value );
|
|
||||||
Module.HEAPU8[ address + 12 ] = 15;
|
|
||||||
Module.HEAP32[ address / 4 ] = id;
|
|
||||||
} else {
|
|
||||||
var refid = Module.STDWEB_PRIVATE.acquire_rust_reference( value );
|
|
||||||
Module.HEAPU8[ address + 12 ] = 9;
|
|
||||||
Module.HEAP32[ address / 4 ] = refid;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// New browsers and recent Node
|
|
||||||
var cachedDecoder = ( typeof TextDecoder === "function"
|
|
||||||
? new TextDecoder( "utf-8" )
|
|
||||||
// Old Node (before v11)
|
|
||||||
: ( typeof util === "object" && util && typeof util.TextDecoder === "function"
|
|
||||||
? new util.TextDecoder( "utf-8" )
|
|
||||||
// Old browsers
|
|
||||||
: null ) );
|
|
||||||
|
|
||||||
if ( cachedDecoder != null ) {
|
|
||||||
Module.STDWEB_PRIVATE.to_js_string = function to_js_string( index, length ) {
|
|
||||||
return cachedDecoder.decode( Module.HEAPU8.subarray( index, index + length ) );
|
|
||||||
};
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// This is ported from Rust's stdlib; it's faster than
|
|
||||||
// the string conversion from Emscripten.
|
|
||||||
Module.STDWEB_PRIVATE.to_js_string = function to_js_string( index, length ) {
|
|
||||||
var HEAPU8 = Module.HEAPU8;
|
|
||||||
index = index|0;
|
|
||||||
length = length|0;
|
|
||||||
var end = (index|0) + (length|0);
|
|
||||||
var output = "";
|
|
||||||
while( index < end ) {
|
|
||||||
var x = HEAPU8[ index++ ];
|
|
||||||
if( x < 128 ) {
|
|
||||||
output += String.fromCharCode( x );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var init = (x & (0x7F >> 2));
|
|
||||||
var y = 0;
|
|
||||||
if( index < end ) {
|
|
||||||
y = HEAPU8[ index++ ];
|
|
||||||
}
|
|
||||||
var ch = (init << 6) | (y & 63);
|
|
||||||
if( x >= 0xE0 ) {
|
|
||||||
var z = 0;
|
|
||||||
if( index < end ) {
|
|
||||||
z = HEAPU8[ index++ ];
|
|
||||||
}
|
|
||||||
var y_z = ((y & 63) << 6) | (z & 63);
|
|
||||||
ch = init << 12 | y_z;
|
|
||||||
if( x >= 0xF0 ) {
|
|
||||||
var w = 0;
|
|
||||||
if( index < end ) {
|
|
||||||
w = HEAPU8[ index++ ];
|
|
||||||
}
|
|
||||||
ch = (init & 7) << 18 | ((y_z << 6) | (w & 63));
|
|
||||||
|
|
||||||
output += String.fromCharCode( 0xD7C0 + (ch >> 10) );
|
|
||||||
ch = 0xDC00 + (ch & 0x3FF);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output += String.fromCharCode( ch );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Module.STDWEB_PRIVATE.id_to_ref_map = {};
|
|
||||||
Module.STDWEB_PRIVATE.id_to_refcount_map = {};
|
|
||||||
Module.STDWEB_PRIVATE.ref_to_id_map = new WeakMap();
|
|
||||||
// Not all types can be stored in a WeakMap
|
|
||||||
Module.STDWEB_PRIVATE.ref_to_id_map_fallback = new Map();
|
|
||||||
Module.STDWEB_PRIVATE.last_refid = 1;
|
|
||||||
|
|
||||||
Module.STDWEB_PRIVATE.id_to_raw_value_map = {};
|
|
||||||
Module.STDWEB_PRIVATE.last_raw_value_id = 1;
|
|
||||||
|
|
||||||
Module.STDWEB_PRIVATE.acquire_rust_reference = function( reference ) {
|
|
||||||
if( reference === undefined || reference === null ) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var id_to_refcount_map = Module.STDWEB_PRIVATE.id_to_refcount_map;
|
|
||||||
var id_to_ref_map = Module.STDWEB_PRIVATE.id_to_ref_map;
|
|
||||||
var ref_to_id_map = Module.STDWEB_PRIVATE.ref_to_id_map;
|
|
||||||
var ref_to_id_map_fallback = Module.STDWEB_PRIVATE.ref_to_id_map_fallback;
|
|
||||||
|
|
||||||
var refid = ref_to_id_map.get( reference );
|
|
||||||
if( refid === undefined ) {
|
|
||||||
refid = ref_to_id_map_fallback.get( reference );
|
|
||||||
}
|
|
||||||
if( refid === undefined ) {
|
|
||||||
refid = Module.STDWEB_PRIVATE.last_refid++;
|
|
||||||
try {
|
|
||||||
ref_to_id_map.set( reference, refid );
|
|
||||||
} catch (e) {
|
|
||||||
ref_to_id_map_fallback.set( reference, refid );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( refid in id_to_ref_map ) {
|
|
||||||
id_to_refcount_map[ refid ]++;
|
|
||||||
} else {
|
|
||||||
id_to_ref_map[ refid ] = reference;
|
|
||||||
id_to_refcount_map[ refid ] = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return refid;
|
|
||||||
};
|
|
||||||
|
|
||||||
Module.STDWEB_PRIVATE.acquire_js_reference = function( refid ) {
|
|
||||||
return Module.STDWEB_PRIVATE.id_to_ref_map[ refid ];
|
|
||||||
};
|
|
||||||
|
|
||||||
Module.STDWEB_PRIVATE.increment_refcount = function( refid ) {
|
|
||||||
Module.STDWEB_PRIVATE.id_to_refcount_map[ refid ]++;
|
|
||||||
};
|
|
||||||
|
|
||||||
Module.STDWEB_PRIVATE.decrement_refcount = function( refid ) {
|
|
||||||
var id_to_refcount_map = Module.STDWEB_PRIVATE.id_to_refcount_map;
|
|
||||||
if( 0 == --id_to_refcount_map[ refid ] ) {
|
|
||||||
var id_to_ref_map = Module.STDWEB_PRIVATE.id_to_ref_map;
|
|
||||||
var ref_to_id_map_fallback = Module.STDWEB_PRIVATE.ref_to_id_map_fallback;
|
|
||||||
var reference = id_to_ref_map[ refid ];
|
|
||||||
delete id_to_ref_map[ refid ];
|
|
||||||
delete id_to_refcount_map[ refid ];
|
|
||||||
ref_to_id_map_fallback.delete(reference);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Module.STDWEB_PRIVATE.register_raw_value = function( value ) {
|
|
||||||
var id = Module.STDWEB_PRIVATE.last_raw_value_id++;
|
|
||||||
Module.STDWEB_PRIVATE.id_to_raw_value_map[ id ] = value;
|
|
||||||
return id;
|
|
||||||
};
|
|
||||||
|
|
||||||
Module.STDWEB_PRIVATE.unregister_raw_value = function( id ) {
|
|
||||||
delete Module.STDWEB_PRIVATE.id_to_raw_value_map[ id ];
|
|
||||||
};
|
|
||||||
|
|
||||||
Module.STDWEB_PRIVATE.get_raw_value = function( id ) {
|
|
||||||
return Module.STDWEB_PRIVATE.id_to_raw_value_map[ id ];
|
|
||||||
};
|
|
||||||
|
|
||||||
Module.STDWEB_PRIVATE.alloc = function alloc( size ) {
|
|
||||||
return Module.web_malloc( size );
|
|
||||||
};
|
|
||||||
|
|
||||||
Module.STDWEB_PRIVATE.dyncall = function( signature, ptr, args ) {
|
|
||||||
return Module.web_table.get( ptr ).apply( null, args );
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is based on code from Emscripten's preamble.js.
|
|
||||||
Module.STDWEB_PRIVATE.utf8_len = function utf8_len( str ) {
|
|
||||||
var len = 0;
|
|
||||||
for( var i = 0; i < str.length; ++i ) {
|
|
||||||
// Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8.
|
|
||||||
// See http://unicode.org/faq/utf_bom.html#utf16-3
|
|
||||||
var u = str.charCodeAt( i ); // possibly a lead surrogate
|
|
||||||
if( u >= 0xD800 && u <= 0xDFFF ) {
|
|
||||||
u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt( ++i ) & 0x3FF);
|
|
||||||
}
|
|
||||||
|
|
||||||
if( u <= 0x7F ) {
|
|
||||||
++len;
|
|
||||||
} else if( u <= 0x7FF ) {
|
|
||||||
len += 2;
|
|
||||||
} else if( u <= 0xFFFF ) {
|
|
||||||
len += 3;
|
|
||||||
} else if( u <= 0x1FFFFF ) {
|
|
||||||
len += 4;
|
|
||||||
} else if( u <= 0x3FFFFFF ) {
|
|
||||||
len += 5;
|
|
||||||
} else {
|
|
||||||
len += 6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return len;
|
|
||||||
};
|
|
||||||
|
|
||||||
Module.STDWEB_PRIVATE.prepare_any_arg = function( value ) {
|
|
||||||
var arg = Module.STDWEB_PRIVATE.alloc( 16 );
|
|
||||||
Module.STDWEB_PRIVATE.from_js( arg, value );
|
|
||||||
return arg;
|
|
||||||
};
|
|
||||||
|
|
||||||
Module.STDWEB_PRIVATE.acquire_tmp = function( dummy ) {
|
|
||||||
var value = Module.STDWEB_PRIVATE.tmp;
|
|
||||||
Module.STDWEB_PRIVATE.tmp = null;
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var HEAP8 = null;
|
|
||||||
var HEAP16 = null;
|
|
||||||
var HEAP32 = null;
|
|
||||||
var HEAPU8 = null;
|
|
||||||
var HEAPU16 = null;
|
|
||||||
var HEAPU32 = null;
|
|
||||||
var HEAPF32 = null;
|
|
||||||
var HEAPF64 = null;
|
|
||||||
|
|
||||||
Object.defineProperty( Module, 'exports', { value: {} } );
|
|
||||||
|
|
||||||
function __web_on_grow() {
|
|
||||||
var buffer = Module.instance.exports.memory.buffer;
|
|
||||||
HEAP8 = new Int8Array( buffer );
|
|
||||||
HEAP16 = new Int16Array( buffer );
|
|
||||||
HEAP32 = new Int32Array( buffer );
|
|
||||||
HEAPU8 = new Uint8Array( buffer );
|
|
||||||
HEAPU16 = new Uint16Array( buffer );
|
|
||||||
HEAPU32 = new Uint32Array( buffer );
|
|
||||||
HEAPF32 = new Float32Array( buffer );
|
|
||||||
HEAPF64 = new Float64Array( buffer );
|
|
||||||
Module.HEAP8 = HEAP8;
|
|
||||||
Module.HEAP16 = HEAP16;
|
|
||||||
Module.HEAP32 = HEAP32;
|
|
||||||
Module.HEAPU8 = HEAPU8;
|
|
||||||
Module.HEAPU16 = HEAPU16;
|
|
||||||
Module.HEAPU32 = HEAPU32;
|
|
||||||
Module.HEAPF32 = HEAPF32;
|
|
||||||
Module.HEAPF64 = HEAPF64;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
imports: {
|
|
||||||
env: {
|
|
||||||
"__cargo_web_snippet_09675c7ed2827e045dc760aeac3d286437cfbe5e": function($0, $1, $2, $3) {
|
|
||||||
$1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);$3 = Module.STDWEB_PRIVATE.to_js($3);Module.STDWEB_PRIVATE.from_js($0, (function(){try{return{value:function(){return($1).setAttribute(($2),($3));}(),success:true};}catch(error){return{error:error,success:false};}})());
|
|
||||||
},
|
|
||||||
"__cargo_web_snippet_0e54fd9c163fcf648ce0a395fde4500fd167a40b": function($0) {
|
|
||||||
var r = Module.STDWEB_PRIVATE.acquire_js_reference( $0 );return (r instanceof DOMException) && (r.name === "InvalidCharacterError");
|
|
||||||
},
|
|
||||||
"__cargo_web_snippet_1edaec034bdcb0a749c6d5de76c29f6371afb5a0": function($0) {
|
|
||||||
var o = Module.STDWEB_PRIVATE.acquire_js_reference( $0 );return (o instanceof Event && o.type === "input");
|
|
||||||
},
|
|
||||||
"__cargo_web_snippet_2908dbb08792df5e699e324eec3e29fd6a57c2c9": function($0) {
|
|
||||||
var o = Module.STDWEB_PRIVATE.acquire_js_reference( $0 );return (o instanceof HTMLInputElement);
|
|
||||||
},
|
|
||||||
"__cargo_web_snippet_3c5e83d16a83fc7147ec91e2506438012952f55a": function($0) {
|
|
||||||
var o = Module.STDWEB_PRIVATE.acquire_js_reference( $0 );return (o instanceof Element);
|
|
||||||
},
|
|
||||||
"__cargo_web_snippet_614a3dd2adb7e9eac4a0ec6e59d37f87e0521c3b": function($0, $1) {
|
|
||||||
$1 = Module.STDWEB_PRIVATE.to_js($1);Module.STDWEB_PRIVATE.from_js($0, (function(){return($1).error;})());
|
|
||||||
},
|
|
||||||
"__cargo_web_snippet_6fcce0aae651e2d748e085ff1f800f87625ff8c8": function($0) {
|
|
||||||
Module.STDWEB_PRIVATE.from_js($0, (function(){return document;})());
|
|
||||||
},
|
|
||||||
"__cargo_web_snippet_72fc447820458c720c68d0d8e078ede631edd723": function($0, $1, $2) {
|
|
||||||
console.error( 'Panic location:', Module.STDWEB_PRIVATE.to_js_string( $0, $1 ) + ':' + $2 );
|
|
||||||
},
|
|
||||||
"__cargo_web_snippet_80d6d56760c65e49b7be8b6b01c1ea861b046bf0": function($0) {
|
|
||||||
Module.STDWEB_PRIVATE.decrement_refcount( $0 );
|
|
||||||
},
|
|
||||||
"__cargo_web_snippet_91749aeb589cd0f9b17cbc01b2872ba709817982": function($0, $1, $2) {
|
|
||||||
$1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);Module.STDWEB_PRIVATE.from_js($0, (function(){try{return{value:function(){return($1).createElement(($2));}(),success:true};}catch(error){return{error:error,success:false};}})());
|
|
||||||
},
|
|
||||||
"__cargo_web_snippet_97495987af1720d8a9a923fa4683a7b683e3acd6": function($0, $1) {
|
|
||||||
console.error( 'Panic error message:', Module.STDWEB_PRIVATE.to_js_string( $0, $1 ) );
|
|
||||||
},
|
|
||||||
"__cargo_web_snippet_99c4eefdc8d4cc724135163b8c8665a1f3de99e4": function($0, $1, $2, $3) {
|
|
||||||
$1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);$3 = Module.STDWEB_PRIVATE.to_js($3);Module.STDWEB_PRIVATE.from_js($0, (function(){var listener=($1);($2).addEventListener(($3),listener);return listener;})());
|
|
||||||
},
|
|
||||||
"__cargo_web_snippet_9f22d4ca7bc938409787341b7db181f8dd41e6df": function($0) {
|
|
||||||
Module.STDWEB_PRIVATE.increment_refcount( $0 );
|
|
||||||
},
|
|
||||||
"__cargo_web_snippet_ab05f53189dacccf2d365ad26daa407d4f7abea9": function($0, $1) {
|
|
||||||
$1 = Module.STDWEB_PRIVATE.to_js($1);Module.STDWEB_PRIVATE.from_js($0, (function(){return($1).value;})());
|
|
||||||
},
|
|
||||||
"__cargo_web_snippet_afafe9a462a05084fec65cacc7d6598e145ff3e3": function($0, $1, $2) {
|
|
||||||
$1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);Module.STDWEB_PRIVATE.from_js($0, (function(){return($1).createTextNode(($2));})());
|
|
||||||
},
|
|
||||||
"__cargo_web_snippet_b06dde4acf09433b5190a4b001259fe5d4abcbc2": function($0, $1) {
|
|
||||||
$1 = Module.STDWEB_PRIVATE.to_js($1);Module.STDWEB_PRIVATE.from_js($0, (function(){return($1).success;})());
|
|
||||||
},
|
|
||||||
"__cargo_web_snippet_d5e30f74cb752784e06bd97a37b1f89b6c3433a7": function($0, $1, $2) {
|
|
||||||
$1 = Module.STDWEB_PRIVATE.to_js($1);$2 = Module.STDWEB_PRIVATE.to_js($2);Module.STDWEB_PRIVATE.from_js($0, (function(){return($1).getElementById(($2));})());
|
|
||||||
},
|
|
||||||
"__cargo_web_snippet_dc2fd915bd92f9e9c6a3bd15174f1414eee3dbaf": function() {
|
|
||||||
console.error( 'Encountered a panic!' );
|
|
||||||
},
|
|
||||||
"__cargo_web_snippet_e741b9d9071097746386b2c2ec044a2bc73e688c": function($0, $1) {
|
|
||||||
$0 = Module.STDWEB_PRIVATE.to_js($0);$1 = Module.STDWEB_PRIVATE.to_js($1);($0).appendChild(($1));
|
|
||||||
},
|
|
||||||
"__cargo_web_snippet_e9638d6405ab65f78daf4a5af9c9de14ecf1e2ec": function($0) {
|
|
||||||
$0 = Module.STDWEB_PRIVATE.to_js($0);Module.STDWEB_PRIVATE.unregister_raw_value(($0));
|
|
||||||
},
|
|
||||||
"__cargo_web_snippet_f765b15a1a1b5cd266e922e6fca98dd570f17edc": function($0, $1) {
|
|
||||||
$0 = Module.STDWEB_PRIVATE.to_js($0);$1 = Module.STDWEB_PRIVATE.to_js($1);($0).textContent=($1);
|
|
||||||
},
|
|
||||||
"__cargo_web_snippet_ff5103e6cc179d13b4c7a785bdce2708fd559fc0": function($0) {
|
|
||||||
Module.STDWEB_PRIVATE.tmp = Module.STDWEB_PRIVATE.to_js( $0 );
|
|
||||||
},
|
|
||||||
"__web_on_grow": __web_on_grow
|
|
||||||
}
|
|
||||||
},
|
|
||||||
initialize: function( instance ) {
|
|
||||||
Object.defineProperty( Module, 'instance', { value: instance } );
|
|
||||||
Object.defineProperty( Module, 'web_malloc', { value: Module.instance.exports.__web_malloc } );
|
|
||||||
Object.defineProperty( Module, 'web_free', { value: Module.instance.exports.__web_free } );
|
|
||||||
Object.defineProperty( Module, 'web_table', { value: Module.instance.exports.__indirect_function_table } );
|
|
||||||
|
|
||||||
|
|
||||||
__web_on_grow();
|
|
||||||
Module.instance.exports.main();
|
|
||||||
|
|
||||||
return Module.exports;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
));
|
|
||||||
}));
|
|
Binary file not shown.
|
@ -1,101 +0,0 @@
|
||||||
+++
|
|
||||||
title = "enterprise: a new ui framework"
|
|
||||||
date = 2020-02-11
|
|
||||||
template = "post.html"
|
|
||||||
|
|
||||||
[taxonomies]
|
|
||||||
tags = ["computers", "web-dev"]
|
|
||||||
+++
|
|
||||||
|
|
||||||
This past weekend, while on my trip to Minneapolis, I completed a very early prototype of "enterprise", a new UI framework I've been kind of envisioning over the past couple of weeks. While the UI framework is mainly targeted at web apps, the hope is that with a bit more effort, native UIs can be produced with almost no changes to existing applications. Before I begin to describe how it works, I'd like to acknowledge [Nathan Ringo][1] for his massively helpful feedback in both the brainstorming and the implementation process.
|
|
||||||
|
|
||||||
<!-- more -->
|
|
||||||
|
|
||||||
## Goals of the project
|
|
||||||
|
|
||||||
This project was born out of many frustrations with existing web frameworks. This is kind of the combination of several projects I wanted to tackle; since it's such a long-term thing I'm going to document a bit of what I want to achieve so I can stay on track. The high-level goals of the project are:
|
|
||||||
|
|
||||||
* **Complete separation of business logic from UI.** Theoretically, one could completely retarget the application to a completely different platform (mobile, web, native, something new that will pop up in 5 years), without changing any of the core logic. It does this by introducing [declarative][4]-style [DSL][2]s that are completely architecture-independent.
|
|
||||||
* **Maximally static component relationships.** Like [Svelte][3], I'm aiming to resolve as many relationships between elements as possible during compile-time, to avoid having to maintain a full virtual DOM at runtime.
|
|
||||||
|
|
||||||
With that, let's dive into the code!
|
|
||||||
|
|
||||||
## Demo: Initial Prototype
|
|
||||||
|
|
||||||
The prototype for experimenting is a simple "Hello, world" application. If you've looked at any web framework before, this is probably one of the simplest examples of bindings: type something into a box and watch as the text magically populates with whatever you wrote in the box. If you're using a WASM-compatible browser with JavaScript enabled, you should be able to try out the demo right here:
|
|
||||||
|
|
||||||
<div id="app"></div>
|
|
||||||
<script type="text/javascript" src="helloworld.js"></script>
|
|
||||||
|
|
||||||
OK, you say, but I could implement this in 3 lines of JavaScript.
|
|
||||||
|
|
||||||
```js
|
|
||||||
inputEl.addEventListener("change", () => {
|
|
||||||
spanEl.innerText = inputEl.value;
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
Sure, this works, but it doesn't scale. If you try to write a page full of these kind of bindings directly using JavaScript, you're either going to start running into bugs or building up a pile of unmaintainable spaghetti code. How does enterprise represent this? Well, the enterprise DSL has no concrete syntax yet, but if it did, it would look something like this:
|
|
||||||
|
|
||||||
```
|
|
||||||
model {
|
|
||||||
name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
view {
|
|
||||||
<TextBox bind:value="name" />
|
|
||||||
Hello, {name}!
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This looks a lot closer to {React, Vue, Svelte, component structure}-ish code. The idea now is that the "compiler", as I've come to call it, reads the entire specification of the code and creates a sort of dependency graph of actions. For clarity, let's assign some IDs first:
|
|
||||||
|
|
||||||
* Let the `TextBox`'s value attribute be `view_value`.
|
|
||||||
* Let the `{name}` code segment in "Hello, name" be `view_name`.
|
|
||||||
* Let the model's name field be `model_name`.
|
|
||||||
|
|
||||||
Now we can model this as:
|
|
||||||
|
|
||||||
```dot
|
|
||||||
digraph "dependency graph" {
|
|
||||||
graph[bgcolor="transparent", class="default-coloring"];
|
|
||||||
rankdir="LR";
|
|
||||||
|
|
||||||
"view_value" -> "model_name"
|
|
||||||
"model_name" -> "view_name"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The arrows in this graph indicate a dependency where changes to `view_value` propagates down the graph until everything else is changed. For the initial prototype, the data structure passed through this graph is simply a string, but with encoding magic and additional specifications, we can add rich text later. What this means the compiler then generates code that looks something like (but not exactly):
|
|
||||||
|
|
||||||
```rs
|
|
||||||
fn create_view_value(model: &mut Model) -> INode {
|
|
||||||
let el = (...);
|
|
||||||
el.add_event_listener(|evt: InputListener| {
|
|
||||||
let new_value = el.get_attribute("value");
|
|
||||||
model.name = new_value;
|
|
||||||
view_name.set_text(new_value);
|
|
||||||
});
|
|
||||||
el
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
There's some complications involving the exact model representation in memory as well as how web attributes are accessed that makes the real code a bit different, but from this example you should be able to see that our "compiler" generated real code that matches our specification above.
|
|
||||||
|
|
||||||
The full code for this can be found [here][5].
|
|
||||||
|
|
||||||
## Future
|
|
||||||
|
|
||||||
Obviously not everyone's application is as simple as a linear dependency graph of simple string values. In fact, I even cheated a bit to get this prototype to function; here's some of the shortcuts I took:
|
|
||||||
|
|
||||||
* I mostly hardcoded web elements in instead of making platform-independent abstractions.
|
|
||||||
* I hardcoded the actual specification for the app myself since the DSL doesn't have a real syntax yet.
|
|
||||||
* All data are string types.
|
|
||||||
|
|
||||||
I'll be working on this some more in the coming weeks, so I'll try to keep updates posted here a bit more frequently. Next time, I'll be looking into implementing the TodoMVC example. Until then, thanks for reading!
|
|
||||||
|
|
||||||
[1]: https://remexre.xyz
|
|
||||||
[2]: https://en.wikipedia.org/wiki/Domain-specific_language
|
|
||||||
[3]: https://svelte.dev
|
|
||||||
[4]: https://en.wikipedia.org/wiki/Declarative_programming
|
|
||||||
[5]: https://git.iptq.io/michael/enterprise
|
|
|
@ -1,100 +0,0 @@
|
||||||
+++
|
|
||||||
title = "enterprise: syntax update"
|
|
||||||
date = 2020-02-17
|
|
||||||
template = "post.html"
|
|
||||||
|
|
||||||
[taxonomies]
|
|
||||||
tags = ["computers", "web-dev"]
|
|
||||||
+++
|
|
||||||
|
|
||||||
[Enterprise][1]'s frontend DSL just got a syntax! Although the major functionality hasn't really changed, I threw out the ugly verbose AST-construction syntax for a hand-rolled recursive-descent-ish parser.
|
|
||||||
|
|
||||||
<!-- more -->
|
|
||||||
|
|
||||||
The rehashed "Hello, world" example looks a bit like this:
|
|
||||||
|
|
||||||
```
|
|
||||||
component HelloWorld {
|
|
||||||
model {
|
|
||||||
name: String = "hello",
|
|
||||||
}
|
|
||||||
|
|
||||||
view {
|
|
||||||
<input bind:value="name" />
|
|
||||||
"Hello, " {name} "!"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This compiles using `cargo-web` into a working version of the last post's prototype. You'll notice that quoted literals are used to represent text rather than just typing it out directly like in XML. This is because I'm actually borrowing Rust syntax and parsing it a bit differently. If I had bare text, then everything you put would have to follow Rust's lexical rules; additionally, data about spacing would be a lot more complicated (and unstable!) to retrieve.
|
|
||||||
|
|
||||||
I could possibly have thrown the whole thing into a parser-generator, using Rust's `proc-macro::TokenTree` as tokens, but TokenTree actually gives you blocks (eg. `()` `{}` `[]`) for free, so I can parse expressions like `{name}` incredibly easily.
|
|
||||||
|
|
||||||
Syntax isn't the only thing that's changed since the last update: I've also revamped how builds work.
|
|
||||||
|
|
||||||
New Build Method
|
|
||||||
----------------
|
|
||||||
|
|
||||||
I'm switching to a build method that's rather unconventional. The original approach looked something like this.
|
|
||||||
|
|
||||||
```dot
|
|
||||||
digraph "dependency graph" {
|
|
||||||
graph[bgcolor="transparent", class="default-coloring"];
|
|
||||||
rankdir="LR";
|
|
||||||
|
|
||||||
"Component DSL" -> "AST" [label = "Parsing"];
|
|
||||||
"AST" -> "Dependency Graph" [label = "Graph traversal"];
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Problem here is, when we want code to be modular, the graph traversal approach is going to need information about _all_ modules that are imported in order to
|
|
||||||
be able to produce a flat set of instructions in the final result. If I make a library for a component (say, `enterprise-router`), what should its crate's contents be?
|
|
||||||
|
|
||||||
> **Tangent**: Here's where I'm going to distract myself a little and put this into a more big-picture perspective. Ultimately, the ideal manifestation of an architecture/business-logic separation would be a DSL that completely hides all implementation of its internals.
|
|
||||||
>
|
|
||||||
> That's a pretty far-out goal, so I'm building enterprise incrementally. Sadly, large parts of the language will still rely on the language in which this framework is implemented, Rust. This means that the underlying implementation of features such as modules and async will be relying on the Rust language having these features. However, note that in the long term, a separate DSL for business logic will be planned.
|
|
||||||
|
|
||||||
So what's the solution here? Instead of visiting your component node by node when your component is defined, all the framework is going to do is parse your definition and store the AST of your component as-is. I chose here to serialize ASTs as JSON data and dump it into a static string that will be bundled into your crate.
|
|
||||||
|
|
||||||
Then, in your `build.rs` file, you'll call something like `enterprise_compiler::build(App)`, where `App` is the name of the static string containing the JSON data of the description of your app. This will actually perform the analysis process, calculating the graph of update dependencies, as well as generating the code that will go into a Rust module that you can include into your code.
|
|
||||||
|
|
||||||
Your `build.rs` file might look something like this:
|
|
||||||
|
|
||||||
```rs
|
|
||||||
#[macro_use]
|
|
||||||
extern crate enterprise_macros;
|
|
||||||
|
|
||||||
component! {
|
|
||||||
component HelloWorld {
|
|
||||||
model {
|
|
||||||
name: String = "hello",
|
|
||||||
}
|
|
||||||
|
|
||||||
view {
|
|
||||||
<input bind:value="name" />
|
|
||||||
"Hello, " {name} "!"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
enterprise_compiler::process("helloworld", HelloWorld);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This will create a string called `HelloWorld` for the HelloWorld component, and then analyze and generate the final application code for it into a file called `helloworld.rs` that you can `mod` into your application. The advantage to this approach is that external modules can just rely on Rust's crate system, since we're just fetching strings out of other crates.
|
|
||||||
|
|
||||||
Source code: [here][3].
|
|
||||||
|
|
||||||
Next Steps
|
|
||||||
----------
|
|
||||||
|
|
||||||
As mentioned in my previous post, I'm still working on implementing [TodoMVC][2], a simple Todo application that should flesh out some more of the reactive functionalities of the framework. This should solidify some more of the questions regarding interactions between data model and DOM.
|
|
||||||
|
|
||||||
I'll also try to abstract more of the system away so it's less dependent on stdweb's implementation. This means adding a notion of "backend", where different backends may have different implementations of a particular component.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[1]: @/enterprise/2020-02-11-prototype/index.md
|
|
||||||
[2]: http://todomvc.com/
|
|
||||||
[3]: https://git.iptq.io/michael/enterprise/src/commit/1453885ed2c3a5159431bb41398b9b8bea4d49f5
|
|
|
@ -1,7 +0,0 @@
|
||||||
+++
|
|
||||||
template = "blog.html"
|
|
||||||
insert_anchor_links = "left"
|
|
||||||
|
|
||||||
[extra]
|
|
||||||
include_posts = true
|
|
||||||
+++
|
|
|
@ -1,9 +0,0 @@
|
||||||
+++
|
|
||||||
title = "all pages"
|
|
||||||
template = "listing.html"
|
|
||||||
page_template = "post.html"
|
|
||||||
insert_anchor_links = "left"
|
|
||||||
|
|
||||||
[extra]
|
|
||||||
include_posts = false
|
|
||||||
+++
|
|
|
@ -1,16 +0,0 @@
|
||||||
+++
|
|
||||||
title = "about me"
|
|
||||||
+++
|
|
||||||
|
|
||||||
Hi there! I'm a software developer at Epic Systems in Verona, Wisconsin, and I recently graduated with a Computer Science degree from the University of Minnesota. I've got a wide variety of interests under this field. I've been doing web development for a long time and now I'm looking into security, programming language development, and software development!
|
|
||||||
|
|
||||||
In an effort to rely on less services, I started doing a lot of self-hosting and rewriting of software. Check out some of the projects I'm doing over on my public [Gitea](https://git.iptq.io)!
|
|
||||||
|
|
||||||
If you want my resume, contact me through one of these means:
|
|
||||||
|
|
||||||
## contact
|
|
||||||
- Discord: **iptq#8440**
|
|
||||||
- Email: (I sign all my Git commits with this email)
|
|
||||||
- PGP Key: [hosted on Keybase][1]
|
|
||||||
|
|
||||||
[1]: https://keybase.io/michaelz/pgp_keys.asc?fingerprint=925ecc02890d5cdae26180d4bda47a31a3c8ee6b
|
|
|
@ -1,11 +0,0 @@
|
||||||
+++
|
|
||||||
title = "projects"
|
|
||||||
+++
|
|
||||||
|
|
||||||
- **[garbage](https://github.com/iptq/garbage)**: A command-line tool for interfacing with the FreeDesktop trashcan spec.
|
|
||||||
- **[leanshot](https://github.com/iptq/leanshot)**: Linux screen capturing tool that freezes the screen before selecting.
|
|
||||||
|
|
||||||
Other projects can be found:
|
|
||||||
|
|
||||||
- [Github](https://github.com/iptq)
|
|
||||||
- [Gitea](https://git.iptq.io/explore)
|
|
Binary file not shown.
Before Width: | Height: | Size: 332 KiB |
|
@ -1,195 +0,0 @@
|
||||||
+++
|
|
||||||
title = "command line email with aerc"
|
|
||||||
date = 2020-05-04
|
|
||||||
insert_anchor_links = "left"
|
|
||||||
|
|
||||||
[taxonomies]
|
|
||||||
tags = ["setup", "email", "protonmail"]
|
|
||||||
|
|
||||||
[extra]
|
|
||||||
include_posts = true
|
|
||||||
toc = true
|
|
||||||
+++
|
|
||||||
|
|
||||||
I just set up command line email with my new work account and my [ProtonMail][1] account today! This article will be covering the full setup.
|
|
||||||
|
|
||||||
![aerc screenshot](aerc-mail.jpg)
|
|
||||||
|
|
||||||
## setting up proton-bridge
|
|
||||||
|
|
||||||
ProtonMail is an email service that end-to-end encrypts its users' emails with PGP. As a result, it doesn't speak SMTP or IMAP directly, since email clients wouldn't know how to undo the encryption anyway. That's why they've provided [proton-bridge][2], a ([now][3]) open-source SMTP/IMAP server that translates their own API calls into SMTP/IMAP for your email clients. For security, it's best to run this locally, so that emails aren't exposed over the network after they're decrypted.
|
|
||||||
|
|
||||||
For my setup, I have proton-bridge running as a systemd service. That means we have to deal with any interactive parts of the service so they're not popping up anymore.
|
|
||||||
|
|
||||||
Firstly, we want to build the bridge without support for the GUI. We won't be using it anyway, so this eliminates the Qt dependency.
|
|
||||||
|
|
||||||
Secondly, proton-bridge stores keys in an encrypted keyring, like [password-store][5]. My regular password-store is encrypted with my passphrase-protected GPG key, so I didn't want to use it since it'll be asking me for the passphrase again every time the timeout expires. We're going to make a separate GPG and password-store setup that will only be used for proton-bridge. Since it's all running locally anyway, we're _not_ to use a passphrase on this GPG key.
|
|
||||||
|
|
||||||
Authenticating only happens once, and the local SMTP/IMAP password doesn't change very often, so we won't really care about that. We'll bundle this up into a couple of nice scripts and then have it configured to start on startup!
|
|
||||||
|
|
||||||
### building proton-bridge
|
|
||||||
|
|
||||||
To build proton-bridge without the GUI, we'll need to grab a copy of the [source][2]. Clone the repo and then change directory to it:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/protonmail/proton-bridge
|
|
||||||
cd proton-bridge
|
|
||||||
```
|
|
||||||
|
|
||||||
As of now, there's no tagged releases, so let's just build straight from master. Peeking into the Makefile, there isn't a release build for the nogui option, so let's add it ourselves. Put this somewhere in the Makefile:
|
|
||||||
|
|
||||||
```make
|
|
||||||
build-nogui:
|
|
||||||
PROTONMAIL_ENV= go build ${BUILD_FLAGS_NOGUI} ./cmd/Desktop-Bridge
|
|
||||||
```
|
|
||||||
|
|
||||||
Then run `make build-nogui` and you should get a binary called `Desktop-Bridge`. Don't authenticate just quite yet; we want to set up the keychain first so it stores it in the right place.
|
|
||||||
|
|
||||||
### isolating the keychain
|
|
||||||
|
|
||||||
proton-bridge needs a keychain to store the keys that it gets from authenticating. The bridge supports [password-store][5] and GNOME keyring, but I'll be setting up password-store here. The goal now is to create a password-store instance that's isolated from the default installation so it doesn't require any interactive prompts.
|
|
||||||
|
|
||||||
For this part, I created two directories: the directory for the new GPG homedir, and the directory for the new password-store. If you're copy-pasting commands out of this post, I'd recommend you add these variables right now:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export PASSWORD_STORE_DIR=/path/to/password/store
|
|
||||||
export GNUPGHOME=/path/to/gpg/home
|
|
||||||
```
|
|
||||||
|
|
||||||
...obviously replacing the paths with paths that you choose. These variables are used by `pass` and `gpg` to overwrite the default directories they use, so it's important to set them up. The next step is to make sure they both exist:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p $PASSWORD_STORE_DIR $GNUPGHOME
|
|
||||||
```
|
|
||||||
|
|
||||||
Now initialize the GPG key first. Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gpg --full-generate-key
|
|
||||||
```
|
|
||||||
|
|
||||||
There should be an interactive prompt. Go through and answer the questions however you see fit. I'd recommend you make the key 4096 bits, never expire, and have no passphrase. This key should never leave your local machine, and you will almost never use it directly, so there should be no problem.
|
|
||||||
|
|
||||||
Then, set up password-store. Run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pass init [gpg-id]
|
|
||||||
```
|
|
||||||
|
|
||||||
where `[gpg-id]` is some identifier for the key you just created (name or email works).
|
|
||||||
|
|
||||||
At this point, you should test your configuration by running the `Desktop-Bridge` program with the `-c` option to open the prompt. Make sure you are able to log in and that rerunning the program should automatically run the bridge using the authenticated user without any interactive prompts.
|
|
||||||
|
|
||||||
```
|
|
||||||
Welcome to ProtonMail Bridge interactive shell
|
|
||||||
___....___
|
|
||||||
^^ __..-:'':__:..:__:'':-..__
|
|
||||||
_.-:__:.-:'': : : :'':-.:__:-._
|
|
||||||
.':.-: : : : : : : : : :._:'.
|
|
||||||
_ :.': : : : : : : : : : : :'.: _
|
|
||||||
[ ]: : : : : : : : : : : : : :[ ]
|
|
||||||
[ ]: : : : : : : : : : : : : :[ ]
|
|
||||||
:::::::::[ ]:__:__:__:__:__:__:__:__:__:__:__:__:__:[ ]:::::::::::
|
|
||||||
!!!!!!!!![ ]!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!![ ]!!!!!!!!!!!
|
|
||||||
^^^^^^^^^[ ]^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^[ ]^^^^^^^^^^^
|
|
||||||
[ ] [ ]
|
|
||||||
[ ] [ ]
|
|
||||||
jgs [ ] [ ]
|
|
||||||
~~^_~^~/ \~^-~^~ _~^-~_^~-^~_^~~-^~_~^~-~_~-^~_^/ \~^ ~~_ ^
|
|
||||||
>>> login
|
|
||||||
Username: iptq
|
|
||||||
Password:
|
|
||||||
Authenticating ...
|
|
||||||
Adding account ...
|
|
||||||
```
|
|
||||||
|
|
||||||
### automating the whole process
|
|
||||||
|
|
||||||
Now that the bridge is able to run independently, let's make a tiny wrapper script called `run-proton-bridge.sh` that calls it using the appropriate environment variables:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# same as before
|
|
||||||
export PASSWORD_STORE_DIR=/path/to/password/store
|
|
||||||
export GNUPGHOME=/path/to/gpg/home
|
|
||||||
|
|
||||||
exec /path/to/Desktop-Bridge $@
|
|
||||||
```
|
|
||||||
|
|
||||||
Save this somewhere, give it executable permissions.
|
|
||||||
|
|
||||||
Finally, we can add a systemd service that runs this whole business whenever the network is available. I'd recommend adding this as a user service rather than a system service. You can put this somewhere like `$HOME/.config/systemd/user/proton-bridge.service`:
|
|
||||||
|
|
||||||
```systemd
|
|
||||||
[Unit]
|
|
||||||
After=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Restart=always
|
|
||||||
ExecStart=/path/to/run-proton-bridge.sh
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=default.target
|
|
||||||
```
|
|
||||||
|
|
||||||
Run this service:
|
|
||||||
|
|
||||||
```
|
|
||||||
systemctl --user start proton-bridge
|
|
||||||
```
|
|
||||||
|
|
||||||
Enable it to have it auto-start:
|
|
||||||
|
|
||||||
```
|
|
||||||
systemctl --user enable proton-bridge
|
|
||||||
```
|
|
||||||
|
|
||||||
Bridge configuration should be complete at this point, so let's move on to configuring our mail client to work with it.
|
|
||||||
|
|
||||||
## setting up aerc
|
|
||||||
|
|
||||||
[aerc][4] is a new (**work-in-progress**) command-line mail client by Drew Devault. It features a familiar set of default keybinds, calls out to your favorite editor for composition, and DWIM for many other things. I just started using it today and had no problem getting used to the interface.
|
|
||||||
|
|
||||||
The setup for basic SMTP/IMAP email accounts is actually pretty trivial. When you run aerc for the first time, or whenever you run the command `:new-account`, an interactive screen is brought up prompting you for the details about your mailbox. If your mail provider doesn't work immediately, jump into `#aerc` on freenode; the folks there are super helpful with different mail providers.
|
|
||||||
|
|
||||||
### setting up aerc.. _with_ protonmail
|
|
||||||
|
|
||||||
If you've been keeping up, then all the pieces should be in place for aerc to work with ProtonMail. There's just a tiny bit of glue we have to add to put it all together.
|
|
||||||
|
|
||||||
**aerc does not allow untrusted certificates**. Since proton-bridge generates a self-signed cert, we'll need to trust this cert before we can do anything. There's not really an easy way to pull the certificate out, so I'd recommend just firing up the bridge and then connecting to it using the openssl client and then copy-pasting the certificate part:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ openssl s_client -starttls imap -connect 127.0.0.1:1143 -showcerts
|
|
||||||
CONNECTED(00000003)
|
|
||||||
Can't use SSL_get_servername
|
|
||||||
depth=0 C = CH, O = Proton Technologies AG, OU = ProtonMail, CN = 127.0.0.1
|
|
||||||
verify return:1
|
|
||||||
---
|
|
||||||
Certificate chain
|
|
||||||
0 s:C = CH, O = Proton Technologies AG, OU = ProtonMail, CN = 127.0.0.1
|
|
||||||
i:C = CH, O = Proton Technologies AG, OU = ProtonMail, CN = 127.0.0.1
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
...
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
---
|
|
||||||
Server certificate
|
|
||||||
subject=C = CH, O = Proton Technologies AG, OU = ProtonMail, CN = 127.0.0.1
|
|
||||||
issuer=C = CH, O = Proton Technologies AG, OU = ProtonMail, CN = 127.0.0.1
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
Take that huge chunk starting with the line `BEGIN CERTIFICATE` and ending with the line `END CERTIFICATE` and stick it into some file (ex. `protonmail.crt`). This is the self-signed cert we need to trust.
|
|
||||||
|
|
||||||
Now we'll need to tell our system to allow this cert. This varies from system to system; I'm on an Arch machine, so I ran:
|
|
||||||
|
|
||||||
```
|
|
||||||
sudo trust anchor --store protonmail.crt
|
|
||||||
```
|
|
||||||
|
|
||||||
After this, fire up `aerc` again, and your emails should start showing up.
|
|
||||||
|
|
||||||
[1]: https://protonmail.com/
|
|
||||||
[2]: https://github.com/ProtonMail/proton-bridge
|
|
||||||
[3]: https://protonmail.com/blog/bridge-open-source/
|
|
||||||
[4]: https://aerc-mail.org/
|
|
|
@ -1,11 +0,0 @@
|
||||||
+++
|
|
||||||
template = "blog.html"
|
|
||||||
insert_anchor_links = "left"
|
|
||||||
|
|
||||||
[extra]
|
|
||||||
include_posts = true
|
|
||||||
+++
|
|
||||||
|
|
||||||
# setup
|
|
||||||
|
|
||||||
These posts are tutorial-style articles for setting things up.
|
|
116
flake.lock
Normal file
116
flake.lock
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"agda": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-parts": "flake-parts",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1729253305,
|
||||||
|
"narHash": "sha256-S7/C8VGMrXeGhFeYXx1cVBjZlNVB5L1o/SkW0hFm0Jc=",
|
||||||
|
"owner": "agda",
|
||||||
|
"repo": "agda",
|
||||||
|
"rev": "3425ed5543d2e6f98094b834fedcdec3a2fb67a6",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "agda",
|
||||||
|
"repo": "agda",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-parts": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1701473968,
|
||||||
|
"narHash": "sha256-YcVE5emp1qQ8ieHUnxt1wCZCC3ZfAS+SRRWZ2TMda7E=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"rev": "34fed993f1674c8d06d58b37ce1e0fe5eebcb9f5",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1726560853,
|
||||||
|
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "flake-utils",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1729265718,
|
||||||
|
"narHash": "sha256-4HQI+6LsO3kpWTYuVGIzhJs1cetFcwT7quWCk/6rqeo=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "ccc0c2126893dd20963580b6478d1a10a4512185",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-lib": {
|
||||||
|
"locked": {
|
||||||
|
"dir": "lib",
|
||||||
|
"lastModified": 1701253981,
|
||||||
|
"narHash": "sha256-ztaDIyZ7HrTAfEEUt9AtTDNoCYxUdSd6NrRHaYOIxtk=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "e92039b55bcd58469325ded85d4f58dd5a4eaf58",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"dir": "lib",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"agda": "agda",
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
31
flake.nix
Normal file
31
flake.nix
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
inputs.agda.url = "github:agda/agda";
|
||||||
|
inputs.agda.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
outputs = { self, nixpkgs, flake-utils, agda, }:
|
||||||
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
pkgs = import nixpkgs { inherit system; overlays = [ agda.overlays.default ]; };
|
||||||
|
agda-pkg = agda.packages.x86_64-linux.default;
|
||||||
|
flakePkgs = rec {
|
||||||
|
agda-bin = pkgs.callPackage ./nix/agda-bin.nix { agda-pkg = pkgs.haskellPackages.Agda.bin; };
|
||||||
|
docker-builder =
|
||||||
|
pkgs.callPackage ./nix/docker-builder.nix { inherit agda-bin; };
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
packages = flake-utils.lib.flattenTree flakePkgs;
|
||||||
|
devShell = pkgs.mkShell {
|
||||||
|
ASTRO_TELEMETRY_DISABLED = 1;
|
||||||
|
|
||||||
|
packages = with pkgs;
|
||||||
|
with flakePkgs; [
|
||||||
|
bun
|
||||||
|
nixfmt-rfc-style
|
||||||
|
nix-tree
|
||||||
|
shellcheck
|
||||||
|
|
||||||
|
nodejs_20
|
||||||
|
corepack
|
||||||
|
];
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
47
frontmatter.json
Normal file
47
frontmatter.json
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://frontmatter.codes/frontmatter.schema.json",
|
||||||
|
"frontMatter.framework.id": "astro",
|
||||||
|
"frontMatter.preview.host": "http://localhost:4321",
|
||||||
|
"frontMatter.content.publicFolder": "public",
|
||||||
|
"frontMatter.content.pageFolders": [
|
||||||
|
{
|
||||||
|
"title": "posts",
|
||||||
|
"path": "[[workspace]]/src/content/posts"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"frontMatter.taxonomy.contentTypes": [
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"pageBundle": false,
|
||||||
|
"previewPath": "",
|
||||||
|
"filePrefix": null,
|
||||||
|
"clearEmpty": true,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"title": "Title",
|
||||||
|
"name": "title",
|
||||||
|
"type": "string",
|
||||||
|
"single": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Description",
|
||||||
|
"name": "description",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Publishing date",
|
||||||
|
"name": "date",
|
||||||
|
"type": "datetime",
|
||||||
|
"default": "{{now}}",
|
||||||
|
"isPublishDate": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Content preview",
|
||||||
|
"name": "heroImage",
|
||||||
|
"type": "image",
|
||||||
|
"isPreviewImage": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
23
nix/agda-bin.nix
Normal file
23
nix/agda-bin.nix
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
{ agda-pkg, runCommand, writeShellScriptBin, writeTextFile, agdaPackages }:
|
||||||
|
|
||||||
|
let
|
||||||
|
libraryFile =
|
||||||
|
with agdaPackages;
|
||||||
|
writeTextFile {
|
||||||
|
name = "agda-libraries";
|
||||||
|
text = ''
|
||||||
|
${agdaPackages.cubical.src}/cubical.agda-lib
|
||||||
|
${agdaPackages.standard-library.src}/standard-library.agda-lib
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# Add an extra layer of indirection here to prevent all of GHC from being pulled in
|
||||||
|
wtf = runCommand "agda-bin" { } ''
|
||||||
|
cp ${agda-pkg}/bin/agda $out
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
|
||||||
|
writeShellScriptBin "agda" ''
|
||||||
|
set -euo pipefail
|
||||||
|
exec ${wtf} --library-file=${libraryFile} $@
|
||||||
|
''
|
47
nix/docker-builder.nix
Normal file
47
nix/docker-builder.nix
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
{ dockerTools
|
||||||
|
, agda-bin
|
||||||
|
, bash
|
||||||
|
, corepack
|
||||||
|
, coreutils
|
||||||
|
, gitMinimal
|
||||||
|
, gnused
|
||||||
|
, minio-client
|
||||||
|
, nodejs_20
|
||||||
|
, openssh
|
||||||
|
, pkgsLinux
|
||||||
|
, rsync
|
||||||
|
}:
|
||||||
|
|
||||||
|
dockerTools.buildLayeredImage {
|
||||||
|
name = "blog-docker-builder";
|
||||||
|
|
||||||
|
contents = with dockerTools; [
|
||||||
|
agda-bin
|
||||||
|
bash
|
||||||
|
caCertificates
|
||||||
|
corepack
|
||||||
|
coreutils
|
||||||
|
fakeNss
|
||||||
|
gitMinimal
|
||||||
|
gnused
|
||||||
|
minio-client
|
||||||
|
nodejs_20
|
||||||
|
openssh
|
||||||
|
rsync
|
||||||
|
usrBinEnv
|
||||||
|
];
|
||||||
|
|
||||||
|
# fakeRootCommands = ''
|
||||||
|
# #!${pkgsLinux.runtimeShell}
|
||||||
|
# ${pkgsLinux.dockerTools.shadowSetup}
|
||||||
|
# groupadd -r builder
|
||||||
|
# useradd -r -g builder builder
|
||||||
|
# '';
|
||||||
|
}
|
||||||
|
|
||||||
|
# copyToRoot = with dockerTools; buildEnv {
|
||||||
|
# name = "blog-docker-builder-image-root";
|
||||||
|
# paths = [
|
||||||
|
# ];
|
||||||
|
# };
|
||||||
|
|
57
package.json
Normal file
57
package.json
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
{
|
||||||
|
"name": "blog",
|
||||||
|
"type": "module",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"packageManager": "pnpm@9.4.0+sha256.b6fd0bfda555e7e584ad7e56b30c68b01d5a04f9ee93989f4b93ca8473c49c74",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "astro dev",
|
||||||
|
"start": "astro dev",
|
||||||
|
"build": "astro build",
|
||||||
|
"preview": "astro preview",
|
||||||
|
"astro": "astro",
|
||||||
|
"format": "prettier -w ."
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@astrojs/markdoc": "^0.11.1",
|
||||||
|
"@astrojs/markdown-remark": "^5.1.1",
|
||||||
|
"@astrojs/mdx": "^1.1.5",
|
||||||
|
"@astrojs/rss": "^3.0.0",
|
||||||
|
"@astrojs/sitemap": "^3.1.6",
|
||||||
|
"@justfork/rehype-autolink-headings": "^5.1.1",
|
||||||
|
"astro": "^3.6.5",
|
||||||
|
"astro-imagetools": "^0.9.0",
|
||||||
|
"astro-remark-description": "^1.1.2",
|
||||||
|
"classnames": "^2.5.1",
|
||||||
|
"fork-awesome": "^1.2.0",
|
||||||
|
"katex": "^0.16.10",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
|
"mdast-util-to-string": "^4.0.0",
|
||||||
|
"nanoid": "^4.0.2",
|
||||||
|
"reading-time": "^1.5.0",
|
||||||
|
"rehype-accessible-emojis": "^0.3.2",
|
||||||
|
"rehype-katex": "^6.0.3",
|
||||||
|
"remark-emoji": "^4.0.1",
|
||||||
|
"remark-github-beta-blockquote-admonitions": "^2.2.1",
|
||||||
|
"remark-math": "^5.1.1",
|
||||||
|
"remark-parse": "^10.0.2",
|
||||||
|
"sanitize-html": "^2.13.1",
|
||||||
|
"sharp": "^0.33.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@biomejs/biome": "^1.8.2",
|
||||||
|
"@types/lodash-es": "^4.17.12",
|
||||||
|
"@types/sanitize-html": "^2.13.0",
|
||||||
|
"date-fns": "^2.30.0",
|
||||||
|
"hast-util-from-html": "^2.0.1",
|
||||||
|
"hast-util-to-html": "^9.0.1",
|
||||||
|
"mdast": "^3.0.0",
|
||||||
|
"mdast-util-from-markdown": "^2.0.1",
|
||||||
|
"prettier": "^3.3.2",
|
||||||
|
"prettier-plugin-astro": "^0.12.3",
|
||||||
|
"rehype-slug": "^6.0.0",
|
||||||
|
"sass": "^1.77.6",
|
||||||
|
"shiki": "^0.14.7",
|
||||||
|
"unified": "^11.0.5",
|
||||||
|
"unist-util-visit": "^5.0.0"
|
||||||
|
}
|
||||||
|
}
|
155
plugin/remark-admonitions.ts
Normal file
155
plugin/remark-admonitions.ts
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
// https://github.com/myl7/remark-github-beta-blockquote-admonitions
|
||||||
|
// License: Apache-2.0
|
||||||
|
|
||||||
|
import { visit } from "unist-util-visit";
|
||||||
|
import type { Data } from "unist";
|
||||||
|
import type { BuildVisitor } from "unist-util-visit";
|
||||||
|
import type { Blockquote, Paragraph, Text } from "mdast";
|
||||||
|
import type { RemarkPlugin } from "@astrojs/markdown-remark";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
|
const remarkAdmonitions: RemarkPlugin =
|
||||||
|
(providedConfig?: Partial<Config>) => (tree) => {
|
||||||
|
visit(tree, handleNode({ ...defaultConfig, ...providedConfig }));
|
||||||
|
};
|
||||||
|
|
||||||
|
export default remarkAdmonitions;
|
||||||
|
|
||||||
|
const handleNode =
|
||||||
|
(config: Config): BuildVisitor =>
|
||||||
|
(node) => {
|
||||||
|
// Filter required elems
|
||||||
|
if (node.type !== "blockquote") return;
|
||||||
|
const blockquote = node as Blockquote;
|
||||||
|
|
||||||
|
if (blockquote.children[0]?.type !== "paragraph") return;
|
||||||
|
|
||||||
|
const paragraph = blockquote.children[0];
|
||||||
|
if (paragraph.children[0]?.type !== "text") return;
|
||||||
|
|
||||||
|
const text = paragraph.children[0];
|
||||||
|
|
||||||
|
// A link break after the title is explicitly required by GitHub
|
||||||
|
const titleEnd = text.value.indexOf("\n");
|
||||||
|
if (titleEnd < 0) return;
|
||||||
|
|
||||||
|
const textBody = text.value.substring(titleEnd + 1);
|
||||||
|
let title = text.value.substring(0, titleEnd);
|
||||||
|
// Handle whitespaces after the title.
|
||||||
|
// Whitespace characters are defined by GFM
|
||||||
|
const m = /[ \t\v\f\r]+$/.exec(title);
|
||||||
|
if (m && !config.titleKeepTrailingWhitespaces) {
|
||||||
|
title = title.substring(0, title.length - m[0].length);
|
||||||
|
}
|
||||||
|
if (!nameFilter(config.titleFilter)(title)) return;
|
||||||
|
const { displayTitle, checkedTitle } = config.titleTextMap(title);
|
||||||
|
|
||||||
|
// Update the text body
|
||||||
|
text.value = textBody;
|
||||||
|
|
||||||
|
// Insert the title element and add classes for the title
|
||||||
|
const paragraphTitleText: Text = { type: "text", value: displayTitle };
|
||||||
|
const paragraphTitle: Paragraph = {
|
||||||
|
type: "paragraph",
|
||||||
|
children: [paragraphTitleText],
|
||||||
|
data: config.dataMaps.title({
|
||||||
|
hProperties: {
|
||||||
|
className: classNameMap(config.classNameMaps.title)(checkedTitle),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
blockquote.children.unshift(paragraphTitle);
|
||||||
|
|
||||||
|
// Add classes for the block
|
||||||
|
blockquote.data = config.dataMaps.block({
|
||||||
|
...blockquote.data,
|
||||||
|
hProperties: {
|
||||||
|
className: classNameMap(config.classNameMaps.block)(checkedTitle),
|
||||||
|
},
|
||||||
|
// The blockquote should be rendered as a div, which is explicitly required by GitHub
|
||||||
|
hName: "div",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const TITLE_PATTERN =
|
||||||
|
/\[\!admonition: (attention|caution|danger|error|hint|important|note|tip|warning)\]/i;
|
||||||
|
|
||||||
|
export const mkdocsConfig: Partial<Config> = {
|
||||||
|
classNameMaps: {
|
||||||
|
block: (title) => [
|
||||||
|
"admonition",
|
||||||
|
...(title.startsWith("admonition: ")
|
||||||
|
? title.substring("admonition: ".length)
|
||||||
|
: title
|
||||||
|
).split(" "),
|
||||||
|
],
|
||||||
|
title: classNames("admonition-title"),
|
||||||
|
},
|
||||||
|
|
||||||
|
titleFilter: (title) => Boolean(title.match(TITLE_PATTERN)),
|
||||||
|
|
||||||
|
titleTextMap: (title: string) => {
|
||||||
|
const match = title.match(TITLE_PATTERN);
|
||||||
|
const displayTitle = match?.[1] ?? "";
|
||||||
|
const checkedTitle = displayTitle;
|
||||||
|
return { displayTitle, checkedTitle };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface Config {
|
||||||
|
classNameMaps: {
|
||||||
|
block: ClassNameMap;
|
||||||
|
title: ClassNameMap;
|
||||||
|
};
|
||||||
|
titleFilter: NameFilter;
|
||||||
|
titleTextMap: (title: string) => {
|
||||||
|
displayTitle: string;
|
||||||
|
checkedTitle: string;
|
||||||
|
};
|
||||||
|
dataMaps: {
|
||||||
|
block: (data: Data) => Data;
|
||||||
|
title: (data: Data) => Data;
|
||||||
|
};
|
||||||
|
titleKeepTrailingWhitespaces: boolean;
|
||||||
|
legacyTitle: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultConfig: Config = {
|
||||||
|
classNameMaps: {
|
||||||
|
block: "admonition",
|
||||||
|
title: "admonition-title",
|
||||||
|
},
|
||||||
|
|
||||||
|
titleFilter: ["[!NOTE]", "[!IMPORTANT]", "[!WARNING]"],
|
||||||
|
|
||||||
|
titleTextMap: (title) => ({
|
||||||
|
displayTitle: title.substring(2, title.length - 1),
|
||||||
|
checkedTitle: title.substring(2, title.length - 1),
|
||||||
|
}),
|
||||||
|
dataMaps: {
|
||||||
|
block: (data) => data,
|
||||||
|
title: (data) => data,
|
||||||
|
},
|
||||||
|
titleKeepTrailingWhitespaces: false,
|
||||||
|
legacyTitle: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
type ClassNames = string | string[];
|
||||||
|
type ClassNameMap = ClassNames | ((title: string) => ClassNames);
|
||||||
|
|
||||||
|
export function classNameMap(gen: ClassNameMap) {
|
||||||
|
return (title: string) => {
|
||||||
|
const classNames = typeof gen === "function" ? gen(title) : gen;
|
||||||
|
return typeof classNames === "object" ? classNames.join(" ") : classNames;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
type NameFilter = ((title: string) => boolean) | string[];
|
||||||
|
|
||||||
|
export function nameFilter(filter: NameFilter) {
|
||||||
|
return (title: string) => {
|
||||||
|
return typeof filter === "function"
|
||||||
|
? filter(title)
|
||||||
|
: filter.includes(title);
|
||||||
|
};
|
||||||
|
}
|
174
plugin/remark-agda.ts
Normal file
174
plugin/remark-agda.ts
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
import type { RootContent } from "hast";
|
||||||
|
import { fromMarkdown } from "mdast-util-from-markdown";
|
||||||
|
import { fromHtml } from "hast-util-from-html";
|
||||||
|
import { toHtml } from "hast-util-to-html";
|
||||||
|
|
||||||
|
import { spawnSync } from "node:child_process";
|
||||||
|
import {
|
||||||
|
mkdirSync,
|
||||||
|
mkdtempSync,
|
||||||
|
readFileSync,
|
||||||
|
existsSync,
|
||||||
|
readdirSync,
|
||||||
|
copyFileSync,
|
||||||
|
writeFileSync,
|
||||||
|
} from "node:fs";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import { join, parse, resolve, basename } from "node:path";
|
||||||
|
import { visit } from "unist-util-visit";
|
||||||
|
import type { RemarkPlugin } from "@astrojs/markdown-remark";
|
||||||
|
|
||||||
|
interface Options {
|
||||||
|
base: string;
|
||||||
|
outDir: string;
|
||||||
|
publicDir: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const remarkAgda: RemarkPlugin = ({ base, publicDir }: Options) => {
|
||||||
|
const destDir = join(publicDir, "generated", "agda");
|
||||||
|
mkdirSync(destDir, { recursive: true });
|
||||||
|
|
||||||
|
return (tree, { history }) => {
|
||||||
|
const path: string = history[history.length - 1]!;
|
||||||
|
if (!(path.endsWith(".lagda.md") || path.endsWith(".agda"))) return;
|
||||||
|
|
||||||
|
// console.log("AGDA:processing path", path);
|
||||||
|
|
||||||
|
const tempDir = mkdtempSync(join(tmpdir(), "agdaRender."));
|
||||||
|
const agdaOutDir = join(tempDir, "output");
|
||||||
|
mkdirSync(agdaOutDir, { recursive: true });
|
||||||
|
|
||||||
|
const childOutput = spawnSync(
|
||||||
|
"agda",
|
||||||
|
[
|
||||||
|
"--html",
|
||||||
|
`--html-dir=${agdaOutDir}`,
|
||||||
|
"--highlight-occurrences",
|
||||||
|
"--html-highlight=code",
|
||||||
|
path,
|
||||||
|
],
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Locate output file
|
||||||
|
const directory = readdirSync(agdaOutDir);
|
||||||
|
const outputFilename = directory.find((name) => name.endsWith(".md"));
|
||||||
|
|
||||||
|
if (childOutput.status !== 0 || outputFilename === undefined) {
|
||||||
|
throw new Error(
|
||||||
|
`Agda output:
|
||||||
|
|
||||||
|
Stdout:
|
||||||
|
${childOutput.stdout}
|
||||||
|
|
||||||
|
Stderr:
|
||||||
|
${childOutput.stderr}
|
||||||
|
`,
|
||||||
|
{
|
||||||
|
cause: childOutput.error,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const outputFile = join(agdaOutDir, outputFilename);
|
||||||
|
|
||||||
|
// // TODO: Handle child output
|
||||||
|
// console.error("--AGDA OUTPUT--");
|
||||||
|
// console.error(childOutput);
|
||||||
|
// console.error(childOutput.stdout?.toString());
|
||||||
|
// console.error(childOutput.stderr?.toString());
|
||||||
|
// console.error("--AGDA OUTPUT--");
|
||||||
|
|
||||||
|
const referencedFiles = new Set();
|
||||||
|
for (const file of readdirSync(agdaOutDir)) {
|
||||||
|
referencedFiles.add(file);
|
||||||
|
|
||||||
|
const fullPath = join(agdaOutDir, file);
|
||||||
|
const fullDestPath = join(destDir, file);
|
||||||
|
|
||||||
|
if (file.endsWith(".html")) {
|
||||||
|
const src = readFileSync(fullPath);
|
||||||
|
writeFileSync(
|
||||||
|
fullDestPath,
|
||||||
|
`
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="stylesheet" href="${base}generated/agda/Agda.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre class="Agda">
|
||||||
|
${src}
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
copyFileSync(fullPath, fullDestPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const htmlname = basename(resolve(outputFile)).replace(/(\.lagda)?\.md/, ".html");
|
||||||
|
|
||||||
|
const doc = readFileSync(outputFile);
|
||||||
|
|
||||||
|
// This is the post-processed markdown with HTML code blocks replacing the Agda code blocks
|
||||||
|
const tree2 = fromMarkdown(doc);
|
||||||
|
|
||||||
|
const collectedCodeBlocks: RootContent[] = [];
|
||||||
|
visit(tree2, "html", (node) => {
|
||||||
|
const html = fromHtml(node.value, { fragment: true });
|
||||||
|
|
||||||
|
const firstChild: RootContent = html.children[0]!;
|
||||||
|
|
||||||
|
visit(html, "element", (node) => {
|
||||||
|
if (node.tagName !== "a") return;
|
||||||
|
|
||||||
|
if (node.properties.href) {
|
||||||
|
// Trim off end
|
||||||
|
const [href, hash, ...rest] = node.properties.href.split("#");
|
||||||
|
if (rest.length > 0) throw new Error("come look at this");
|
||||||
|
|
||||||
|
if (href === htmlname) {
|
||||||
|
node.properties.href = `#${hash}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (referencedFiles.has(href)) {
|
||||||
|
node.properties.href = `${base}generated/agda/${href}${hash ? `#${hash}` : ""}`;
|
||||||
|
node.properties.target = "_blank";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!firstChild?.properties?.className?.includes("Agda")) return;
|
||||||
|
|
||||||
|
const stringContents = toHtml(firstChild);
|
||||||
|
collectedCodeBlocks.push({
|
||||||
|
contents: stringContents,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let idx = 0;
|
||||||
|
try {
|
||||||
|
visit(tree, "code", (node) => {
|
||||||
|
if (!(node.lang === null || node.lang === "agda")) return;
|
||||||
|
|
||||||
|
if (idx > collectedCodeBlocks.length) {
|
||||||
|
throw new Error("failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
node.type = "html";
|
||||||
|
node.value = collectedCodeBlocks[idx].contents;
|
||||||
|
idx += 1;
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
// TODO: Figure out a way to handle this correctly
|
||||||
|
// Possibly by diffing?
|
||||||
|
console.log("Mismatch in number of args. Perhaps there was an empty block?");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default remarkAgda;
|
16
plugin/remark-reading-time.ts
Normal file
16
plugin/remark-reading-time.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import getReadingTime from "reading-time";
|
||||||
|
import { toString as mdastToString } from "mdast-util-to-string";
|
||||||
|
import type { RemarkPlugin } from "@astrojs/markdown-remark";
|
||||||
|
|
||||||
|
const remarkReadingTime: RemarkPlugin = () => {
|
||||||
|
return (tree, { data }) => {
|
||||||
|
const textOnPage = mdastToString(tree);
|
||||||
|
const readingTime = getReadingTime(textOnPage);
|
||||||
|
|
||||||
|
// readingTime.text will give us minutes read as a friendly string,
|
||||||
|
// i.e. "3 min read"
|
||||||
|
data.astro.frontmatter.minutesRead = readingTime.text;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default remarkReadingTime;
|
42
plugin/remark-typst.ts
Normal file
42
plugin/remark-typst.ts
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import type { RemarkPlugin } from "@astrojs/markdown-remark";
|
||||||
|
import { mkdtempSync, readFileSync, writeFileSync } from "node:fs";
|
||||||
|
import { visit } from "unist-util-visit";
|
||||||
|
import { join } from "node:path";
|
||||||
|
import { spawnSync } from "node:child_process";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
|
||||||
|
const remarkTypst: RemarkPlugin = () => {
|
||||||
|
const tmp = mkdtempSync(join(tmpdir(), "typst"));
|
||||||
|
let ctr = 0;
|
||||||
|
|
||||||
|
return (tree) => {
|
||||||
|
visit(
|
||||||
|
tree,
|
||||||
|
(node) => node.type === "code" && node.lang === "typst",
|
||||||
|
(node, index, parent) => {
|
||||||
|
const doc = join(tmp, `${ctr}.typ`);
|
||||||
|
const docOut = join(tmp, `${ctr}.svg`);
|
||||||
|
ctr += 1;
|
||||||
|
|
||||||
|
writeFileSync(doc, node.value);
|
||||||
|
const result = spawnSync(
|
||||||
|
"typst",
|
||||||
|
[
|
||||||
|
"compile",
|
||||||
|
"--format",
|
||||||
|
"svg",
|
||||||
|
doc,
|
||||||
|
],
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
console.log("OUTPUT", result.stderr.toString());
|
||||||
|
|
||||||
|
const svgOut = readFileSync(docOut);
|
||||||
|
node.type = "html";
|
||||||
|
node.value = svgOut.toString();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default remarkTypst;
|
7253
pnpm-lock.yaml
Normal file
7253
pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load diff
1
public/.well-known/atproto-did
Normal file
1
public/.well-known/atproto-did
Normal file
|
@ -0,0 +1 @@
|
||||||
|
did:plc:zbg2asfcpyughqspwjjgyc2d
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 136 KiB |
BIN
public/fonts/Inter-Black.ttf
Normal file
BIN
public/fonts/Inter-Black.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Inter-Bold.ttf
Normal file
BIN
public/fonts/Inter-Bold.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Inter-ExtraBold.ttf
Normal file
BIN
public/fonts/Inter-ExtraBold.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Inter-ExtraLight.ttf
Normal file
BIN
public/fonts/Inter-ExtraLight.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Inter-Light.ttf
Normal file
BIN
public/fonts/Inter-Light.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Inter-Medium.ttf
Normal file
BIN
public/fonts/Inter-Medium.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Inter-Regular.ttf
Normal file
BIN
public/fonts/Inter-Regular.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Inter-SemiBold.ttf
Normal file
BIN
public/fonts/Inter-SemiBold.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Inter-Thin.ttf
Normal file
BIN
public/fonts/Inter-Thin.ttf
Normal file
Binary file not shown.
93
public/fonts/OFL.txt
Normal file
93
public/fonts/OFL.txt
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter)
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
BIN
public/fonts/forkawesome-webfont.eot
Normal file
BIN
public/fonts/forkawesome-webfont.eot
Normal file
Binary file not shown.
3232
public/fonts/forkawesome-webfont.svg
Normal file
3232
public/fonts/forkawesome-webfont.svg
Normal file
File diff suppressed because it is too large
Load diff
After Width: | Height: | Size: 547 KiB |
BIN
public/fonts/forkawesome-webfont.ttf
Normal file
BIN
public/fonts/forkawesome-webfont.ttf
Normal file
Binary file not shown.
BIN
public/fonts/forkawesome-webfont.woff
Normal file
BIN
public/fonts/forkawesome-webfont.woff
Normal file
Binary file not shown.
BIN
public/fonts/forkawesome-webfont.woff2
Normal file
BIN
public/fonts/forkawesome-webfont.woff2
Normal file
Binary file not shown.
119
public/katex/README.md
vendored
Normal file
119
public/katex/README.md
vendored
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
# [<img src="https://katex.org/img/katex-logo-black.svg" width="130" alt="KaTeX">](https://katex.org/)
|
||||||
|
[![npm](https://img.shields.io/npm/v/katex.svg)](https://www.npmjs.com/package/katex)
|
||||||
|
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
|
||||||
|
[![CI](https://github.com/KaTeX/KaTeX/workflows/CI/badge.svg?branch=main&event=push)](https://github.com/KaTeX/KaTeX/actions?query=workflow%3ACI)
|
||||||
|
[![codecov](https://codecov.io/gh/KaTeX/KaTeX/branch/main/graph/badge.svg)](https://codecov.io/gh/KaTeX/KaTeX)
|
||||||
|
[![Discussions](https://img.shields.io/badge/Discussions-join-brightgreen)](https://github.com/KaTeX/KaTeX/discussions)
|
||||||
|
[![jsDelivr](https://data.jsdelivr.com/v1/package/npm/katex/badge?style=rounded)](https://www.jsdelivr.com/package/npm/katex)
|
||||||
|
![katex.min.js size](https://img.badgesize.io/https://unpkg.com/katex/dist/katex.min.js?compression=gzip)
|
||||||
|
[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/KaTeX/KaTeX)
|
||||||
|
[![Financial Contributors on Open Collective](https://opencollective.com/katex/all/badge.svg?label=financial+contributors)](https://opencollective.com/katex)
|
||||||
|
|
||||||
|
KaTeX is a fast, easy-to-use JavaScript library for TeX math rendering on the web.
|
||||||
|
|
||||||
|
* **Fast:** KaTeX renders its math synchronously and doesn't need to reflow the page. See how it compares to a competitor in [this speed test](http://www.intmath.com/cg5/katex-mathjax-comparison.php).
|
||||||
|
* **Print quality:** KaTeX's layout is based on Donald Knuth's TeX, the gold standard for math typesetting.
|
||||||
|
* **Self contained:** KaTeX has no dependencies and can easily be bundled with your website resources.
|
||||||
|
* **Server side rendering:** KaTeX produces the same output regardless of browser or environment, so you can pre-render expressions using Node.js and send them as plain HTML.
|
||||||
|
|
||||||
|
KaTeX is compatible with all major browsers, including Chrome, Safari, Firefox, Opera, Edge, and IE 11.
|
||||||
|
|
||||||
|
KaTeX supports much (but not all) of LaTeX and many LaTeX packages. See the [list of supported functions](https://katex.org/docs/supported.html).
|
||||||
|
|
||||||
|
Try out KaTeX [on the demo page](https://katex.org/#demo)!
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
### Starter template
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<!-- KaTeX requires the use of the HTML5 doctype. Without it, KaTeX may not render properly -->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.15.2/dist/katex.min.css" integrity="sha384-MlJdn/WNKDGXveldHDdyRP1R4CTHr3FeuDNfhsLPYrq2t0UBkUdK2jyTnXPEK1NQ" crossorigin="anonymous">
|
||||||
|
|
||||||
|
<!-- The loading of KaTeX is deferred to speed up page rendering -->
|
||||||
|
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.15.2/dist/katex.min.js" integrity="sha384-VQ8d8WVFw0yHhCk5E8I86oOhv48xLpnDZx5T9GogA/Y84DcCKWXDmSDfn13bzFZY" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
<!-- To automatically render math in text elements, include the auto-render extension: -->
|
||||||
|
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.15.2/dist/contrib/auto-render.min.js" integrity="sha384-+XBljXPPiv+OzfbB3cVmLHf4hdUFHlWNZN5spNQ7rmHTXpd7WvJum6fIACpNNfIR" crossorigin="anonymous"
|
||||||
|
onload="renderMathInElement(document.body);"></script>
|
||||||
|
</head>
|
||||||
|
...
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also [download KaTeX](https://github.com/KaTeX/KaTeX/releases) and host it yourself.
|
||||||
|
|
||||||
|
For details on how to configure auto-render extension, refer to [the documentation](https://katex.org/docs/autorender.html).
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
Call `katex.render` to render a TeX expression directly into a DOM element.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
katex.render("c = \\pm\\sqrt{a^2 + b^2}", element, {
|
||||||
|
throwOnError: false
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Call `katex.renderToString` to generate an HTML string of the rendered math,
|
||||||
|
e.g., for server-side rendering. For example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var html = katex.renderToString("c = \\pm\\sqrt{a^2 + b^2}", {
|
||||||
|
throwOnError: false
|
||||||
|
});
|
||||||
|
// '<span class="katex">...</span>'
|
||||||
|
```
|
||||||
|
|
||||||
|
Make sure to include the CSS and font files in both cases.
|
||||||
|
If you are doing all rendering on the server, there is no need to include the
|
||||||
|
JavaScript on the client.
|
||||||
|
|
||||||
|
The examples above use the `throwOnError: false` option, which renders invalid
|
||||||
|
inputs as the TeX source code in red (by default), with the error message as
|
||||||
|
hover text. For other available options, see the
|
||||||
|
[API documentation](https://katex.org/docs/api.html),
|
||||||
|
[options documentation](https://katex.org/docs/options.html), and
|
||||||
|
[handling errors documentation](https://katex.org/docs/error.html).
|
||||||
|
|
||||||
|
## Demo and Documentation
|
||||||
|
|
||||||
|
Learn more about using KaTeX [on the website](https://katex.org)!
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
### Code Contributors
|
||||||
|
|
||||||
|
This project exists thanks to all the people who contribute code. If you'd like to help, see [our guide to contributing code](CONTRIBUTING.md).
|
||||||
|
<a href="https://github.com/KaTeX/KaTeX/graphs/contributors"><img src="https://contributors-svg.opencollective.com/katex/contributors.svg?width=890&button=false" alt="Code contributors" /></a>
|
||||||
|
|
||||||
|
### Financial Contributors
|
||||||
|
|
||||||
|
Become a financial contributor and help us sustain our community.
|
||||||
|
|
||||||
|
#### Individuals
|
||||||
|
|
||||||
|
<a href="https://opencollective.com/katex"><img src="https://opencollective.com/katex/individuals.svg?width=890" alt="Contribute on Open Collective"></a>
|
||||||
|
|
||||||
|
#### Organizations
|
||||||
|
|
||||||
|
Support this project with your organization. Your logo will show up here with a link to your website.
|
||||||
|
|
||||||
|
<a href="https://opencollective.com/katex/organization/0/website"><img src="https://opencollective.com/katex/organization/0/avatar.svg" alt="Organization 1"></a>
|
||||||
|
<a href="https://opencollective.com/katex/organization/1/website"><img src="https://opencollective.com/katex/organization/1/avatar.svg" alt="Organization 2"></a>
|
||||||
|
<a href="https://opencollective.com/katex/organization/2/website"><img src="https://opencollective.com/katex/organization/2/avatar.svg" alt="Organization 3"></a>
|
||||||
|
<a href="https://opencollective.com/katex/organization/3/website"><img src="https://opencollective.com/katex/organization/3/avatar.svg" alt="Organization 4"></a>
|
||||||
|
<a href="https://opencollective.com/katex/organization/4/website"><img src="https://opencollective.com/katex/organization/4/avatar.svg" alt="Organization 5"></a>
|
||||||
|
<a href="https://opencollective.com/katex/organization/5/website"><img src="https://opencollective.com/katex/organization/5/avatar.svg" alt="Organization 6"></a>
|
||||||
|
<a href="https://opencollective.com/katex/organization/6/website"><img src="https://opencollective.com/katex/organization/6/avatar.svg" alt="Organization 7"></a>
|
||||||
|
<a href="https://opencollective.com/katex/organization/7/website"><img src="https://opencollective.com/katex/organization/7/avatar.svg" alt="Organization 8"></a>
|
||||||
|
<a href="https://opencollective.com/katex/organization/8/website"><img src="https://opencollective.com/katex/organization/8/avatar.svg" alt="Organization 9"></a>
|
||||||
|
<a href="https://opencollective.com/katex/organization/9/website"><img src="https://opencollective.com/katex/organization/9/avatar.svg" alt="Organization 10"></a>
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
KaTeX is licensed under the [MIT License](http://opensource.org/licenses/MIT).
|
327
public/katex/contrib/auto-render.js
vendored
Normal file
327
public/katex/contrib/auto-render.js
vendored
Normal file
|
@ -0,0 +1,327 @@
|
||||||
|
(function webpackUniversalModuleDefinition(root, factory) {
|
||||||
|
if(typeof exports === 'object' && typeof module === 'object')
|
||||||
|
module.exports = factory(require("katex"));
|
||||||
|
else if(typeof define === 'function' && define.amd)
|
||||||
|
define(["katex"], factory);
|
||||||
|
else if(typeof exports === 'object')
|
||||||
|
exports["renderMathInElement"] = factory(require("katex"));
|
||||||
|
else
|
||||||
|
root["renderMathInElement"] = factory(root["katex"]);
|
||||||
|
})((typeof self !== 'undefined' ? self : this), function(__WEBPACK_EXTERNAL_MODULE__771__) {
|
||||||
|
return /******/ (function() { // webpackBootstrap
|
||||||
|
/******/ "use strict";
|
||||||
|
/******/ var __webpack_modules__ = ({
|
||||||
|
|
||||||
|
/***/ 771:
|
||||||
|
/***/ (function(module) {
|
||||||
|
|
||||||
|
module.exports = __WEBPACK_EXTERNAL_MODULE__771__;
|
||||||
|
|
||||||
|
/***/ })
|
||||||
|
|
||||||
|
/******/ });
|
||||||
|
/************************************************************************/
|
||||||
|
/******/ // The module cache
|
||||||
|
/******/ var __webpack_module_cache__ = {};
|
||||||
|
/******/
|
||||||
|
/******/ // The require function
|
||||||
|
/******/ function __webpack_require__(moduleId) {
|
||||||
|
/******/ // Check if module is in cache
|
||||||
|
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
||||||
|
/******/ if (cachedModule !== undefined) {
|
||||||
|
/******/ return cachedModule.exports;
|
||||||
|
/******/ }
|
||||||
|
/******/ // Create a new module (and put it into the cache)
|
||||||
|
/******/ var module = __webpack_module_cache__[moduleId] = {
|
||||||
|
/******/ // no module.id needed
|
||||||
|
/******/ // no module.loaded needed
|
||||||
|
/******/ exports: {}
|
||||||
|
/******/ };
|
||||||
|
/******/
|
||||||
|
/******/ // Execute the module function
|
||||||
|
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
||||||
|
/******/
|
||||||
|
/******/ // Return the exports of the module
|
||||||
|
/******/ return module.exports;
|
||||||
|
/******/ }
|
||||||
|
/******/
|
||||||
|
/************************************************************************/
|
||||||
|
/******/ /* webpack/runtime/compat get default export */
|
||||||
|
/******/ !function() {
|
||||||
|
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||||
|
/******/ __webpack_require__.n = function(module) {
|
||||||
|
/******/ var getter = module && module.__esModule ?
|
||||||
|
/******/ function() { return module['default']; } :
|
||||||
|
/******/ function() { return module; };
|
||||||
|
/******/ __webpack_require__.d(getter, { a: getter });
|
||||||
|
/******/ return getter;
|
||||||
|
/******/ };
|
||||||
|
/******/ }();
|
||||||
|
/******/
|
||||||
|
/******/ /* webpack/runtime/define property getters */
|
||||||
|
/******/ !function() {
|
||||||
|
/******/ // define getter functions for harmony exports
|
||||||
|
/******/ __webpack_require__.d = function(exports, definition) {
|
||||||
|
/******/ for(var key in definition) {
|
||||||
|
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
|
||||||
|
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
||||||
|
/******/ }
|
||||||
|
/******/ }
|
||||||
|
/******/ };
|
||||||
|
/******/ }();
|
||||||
|
/******/
|
||||||
|
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
||||||
|
/******/ !function() {
|
||||||
|
/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
|
||||||
|
/******/ }();
|
||||||
|
/******/
|
||||||
|
/************************************************************************/
|
||||||
|
var __webpack_exports__ = {};
|
||||||
|
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
|
||||||
|
!function() {
|
||||||
|
|
||||||
|
// EXPORTS
|
||||||
|
__webpack_require__.d(__webpack_exports__, {
|
||||||
|
"default": function() { return /* binding */ auto_render; }
|
||||||
|
});
|
||||||
|
|
||||||
|
// EXTERNAL MODULE: external "katex"
|
||||||
|
var external_katex_ = __webpack_require__(771);
|
||||||
|
var external_katex_default = /*#__PURE__*/__webpack_require__.n(external_katex_);
|
||||||
|
;// CONCATENATED MODULE: ./contrib/auto-render/splitAtDelimiters.js
|
||||||
|
/* eslint no-constant-condition:0 */
|
||||||
|
var findEndOfMath = function findEndOfMath(delimiter, text, startIndex) {
|
||||||
|
// Adapted from
|
||||||
|
// https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx
|
||||||
|
var index = startIndex;
|
||||||
|
var braceLevel = 0;
|
||||||
|
var delimLength = delimiter.length;
|
||||||
|
|
||||||
|
while (index < text.length) {
|
||||||
|
var character = text[index];
|
||||||
|
|
||||||
|
if (braceLevel <= 0 && text.slice(index, index + delimLength) === delimiter) {
|
||||||
|
return index;
|
||||||
|
} else if (character === "\\") {
|
||||||
|
index++;
|
||||||
|
} else if (character === "{") {
|
||||||
|
braceLevel++;
|
||||||
|
} else if (character === "}") {
|
||||||
|
braceLevel--;
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
var escapeRegex = function escapeRegex(string) {
|
||||||
|
return string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
||||||
|
};
|
||||||
|
|
||||||
|
var amsRegex = /^\\begin{/;
|
||||||
|
|
||||||
|
var splitAtDelimiters = function splitAtDelimiters(text, delimiters) {
|
||||||
|
var index;
|
||||||
|
var data = [];
|
||||||
|
var regexLeft = new RegExp("(" + delimiters.map(function (x) {
|
||||||
|
return escapeRegex(x.left);
|
||||||
|
}).join("|") + ")");
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
index = text.search(regexLeft);
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index > 0) {
|
||||||
|
data.push({
|
||||||
|
type: "text",
|
||||||
|
data: text.slice(0, index)
|
||||||
|
});
|
||||||
|
text = text.slice(index); // now text starts with delimiter
|
||||||
|
} // ... so this always succeeds:
|
||||||
|
|
||||||
|
|
||||||
|
var i = delimiters.findIndex(function (delim) {
|
||||||
|
return text.startsWith(delim.left);
|
||||||
|
});
|
||||||
|
index = findEndOfMath(delimiters[i].right, text, delimiters[i].left.length);
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rawData = text.slice(0, index + delimiters[i].right.length);
|
||||||
|
var math = amsRegex.test(rawData) ? rawData : text.slice(delimiters[i].left.length, index);
|
||||||
|
data.push({
|
||||||
|
type: "math",
|
||||||
|
data: math,
|
||||||
|
rawData: rawData,
|
||||||
|
display: delimiters[i].display
|
||||||
|
});
|
||||||
|
text = text.slice(index + delimiters[i].right.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text !== "") {
|
||||||
|
data.push({
|
||||||
|
type: "text",
|
||||||
|
data: text
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* harmony default export */ var auto_render_splitAtDelimiters = (splitAtDelimiters);
|
||||||
|
;// CONCATENATED MODULE: ./contrib/auto-render/auto-render.js
|
||||||
|
/* eslint no-console:0 */
|
||||||
|
|
||||||
|
|
||||||
|
/* Note: optionsCopy is mutated by this method. If it is ever exposed in the
|
||||||
|
* API, we should copy it before mutating.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var renderMathInText = function renderMathInText(text, optionsCopy) {
|
||||||
|
var data = auto_render_splitAtDelimiters(text, optionsCopy.delimiters);
|
||||||
|
|
||||||
|
if (data.length === 1 && data[0].type === 'text') {
|
||||||
|
// There is no formula in the text.
|
||||||
|
// Let's return null which means there is no need to replace
|
||||||
|
// the current text node with a new one.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fragment = document.createDocumentFragment();
|
||||||
|
|
||||||
|
for (var i = 0; i < data.length; i++) {
|
||||||
|
if (data[i].type === "text") {
|
||||||
|
fragment.appendChild(document.createTextNode(data[i].data));
|
||||||
|
} else {
|
||||||
|
var span = document.createElement("span");
|
||||||
|
var math = data[i].data; // Override any display mode defined in the settings with that
|
||||||
|
// defined by the text itself
|
||||||
|
|
||||||
|
optionsCopy.displayMode = data[i].display;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (optionsCopy.preProcess) {
|
||||||
|
math = optionsCopy.preProcess(math);
|
||||||
|
}
|
||||||
|
|
||||||
|
external_katex_default().render(math, span, optionsCopy);
|
||||||
|
} catch (e) {
|
||||||
|
if (!(e instanceof (external_katex_default()).ParseError)) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
optionsCopy.errorCallback("KaTeX auto-render: Failed to parse `" + data[i].data + "` with ", e);
|
||||||
|
fragment.appendChild(document.createTextNode(data[i].rawData));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment.appendChild(span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fragment;
|
||||||
|
};
|
||||||
|
|
||||||
|
var renderElem = function renderElem(elem, optionsCopy) {
|
||||||
|
for (var i = 0; i < elem.childNodes.length; i++) {
|
||||||
|
var childNode = elem.childNodes[i];
|
||||||
|
|
||||||
|
if (childNode.nodeType === 3) {
|
||||||
|
// Text node
|
||||||
|
var frag = renderMathInText(childNode.textContent, optionsCopy);
|
||||||
|
|
||||||
|
if (frag) {
|
||||||
|
i += frag.childNodes.length - 1;
|
||||||
|
elem.replaceChild(frag, childNode);
|
||||||
|
}
|
||||||
|
} else if (childNode.nodeType === 1) {
|
||||||
|
(function () {
|
||||||
|
// Element node
|
||||||
|
var className = ' ' + childNode.className + ' ';
|
||||||
|
var shouldRender = optionsCopy.ignoredTags.indexOf(childNode.nodeName.toLowerCase()) === -1 && optionsCopy.ignoredClasses.every(function (x) {
|
||||||
|
return className.indexOf(' ' + x + ' ') === -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (shouldRender) {
|
||||||
|
renderElem(childNode, optionsCopy);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
} // Otherwise, it's something else, and ignore it.
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var renderMathInElement = function renderMathInElement(elem, options) {
|
||||||
|
if (!elem) {
|
||||||
|
throw new Error("No element provided to render");
|
||||||
|
}
|
||||||
|
|
||||||
|
var optionsCopy = {}; // Object.assign(optionsCopy, option)
|
||||||
|
|
||||||
|
for (var option in options) {
|
||||||
|
if (options.hasOwnProperty(option)) {
|
||||||
|
optionsCopy[option] = options[option];
|
||||||
|
}
|
||||||
|
} // default options
|
||||||
|
|
||||||
|
|
||||||
|
optionsCopy.delimiters = optionsCopy.delimiters || [{
|
||||||
|
left: "$$",
|
||||||
|
right: "$$",
|
||||||
|
display: true
|
||||||
|
}, {
|
||||||
|
left: "\\(",
|
||||||
|
right: "\\)",
|
||||||
|
display: false
|
||||||
|
}, // LaTeX uses $…$, but it ruins the display of normal `$` in text:
|
||||||
|
// {left: "$", right: "$", display: false},
|
||||||
|
// $ must come after $$
|
||||||
|
// Render AMS environments even if outside $$…$$ delimiters.
|
||||||
|
{
|
||||||
|
left: "\\begin{equation}",
|
||||||
|
right: "\\end{equation}",
|
||||||
|
display: true
|
||||||
|
}, {
|
||||||
|
left: "\\begin{align}",
|
||||||
|
right: "\\end{align}",
|
||||||
|
display: true
|
||||||
|
}, {
|
||||||
|
left: "\\begin{alignat}",
|
||||||
|
right: "\\end{alignat}",
|
||||||
|
display: true
|
||||||
|
}, {
|
||||||
|
left: "\\begin{gather}",
|
||||||
|
right: "\\end{gather}",
|
||||||
|
display: true
|
||||||
|
}, {
|
||||||
|
left: "\\begin{CD}",
|
||||||
|
right: "\\end{CD}",
|
||||||
|
display: true
|
||||||
|
}, {
|
||||||
|
left: "\\[",
|
||||||
|
right: "\\]",
|
||||||
|
display: true
|
||||||
|
}];
|
||||||
|
optionsCopy.ignoredTags = optionsCopy.ignoredTags || ["script", "noscript", "style", "textarea", "pre", "code", "option"];
|
||||||
|
optionsCopy.ignoredClasses = optionsCopy.ignoredClasses || [];
|
||||||
|
optionsCopy.errorCallback = optionsCopy.errorCallback || console.error; // Enable sharing of global macros defined via `\gdef` between different
|
||||||
|
// math elements within a single call to `renderMathInElement`.
|
||||||
|
|
||||||
|
optionsCopy.macros = optionsCopy.macros || {};
|
||||||
|
renderElem(elem, optionsCopy);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* harmony default export */ var auto_render = (renderMathInElement);
|
||||||
|
}();
|
||||||
|
__webpack_exports__ = __webpack_exports__["default"];
|
||||||
|
/******/ return __webpack_exports__;
|
||||||
|
/******/ })()
|
||||||
|
;
|
||||||
|
});
|
1
public/katex/contrib/auto-render.min.js
vendored
Normal file
1
public/katex/contrib/auto-render.min.js
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("katex")):"function"==typeof define&&define.amd?define(["katex"],t):"object"==typeof exports?exports.renderMathInElement=t(require("katex")):e.renderMathInElement=t(e.katex)}("undefined"!=typeof self?self:this,(function(e){return function(){"use strict";var t={771:function(t){t.exports=e}},r={};function n(e){var a=r[e];if(void 0!==a)return a.exports;var i=r[e]={exports:{}};return t[e](i,i.exports,n),i.exports}n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,{a:t}),t},n.d=function(e,t){for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var a={};return function(){n.d(a,{default:function(){return s}});var e=n(771),t=n.n(e),r=function(e,t,r){for(var n=r,a=0,i=e.length;n<t.length;){var o=t[n];if(a<=0&&t.slice(n,n+i)===e)return n;"\\"===o?n++:"{"===o?a++:"}"===o&&a--,n++}return-1},i=/^\\begin{/,o=function(e,t){for(var n,a=[],o=new RegExp("("+t.map((function(e){return e.left.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&")})).join("|")+")");-1!==(n=e.search(o));){n>0&&(a.push({type:"text",data:e.slice(0,n)}),e=e.slice(n));var l=t.findIndex((function(t){return e.startsWith(t.left)}));if(-1===(n=r(t[l].right,e,t[l].left.length)))break;var d=e.slice(0,n+t[l].right.length),s=i.test(d)?d:e.slice(t[l].left.length,n);a.push({type:"math",data:s,rawData:d,display:t[l].display}),e=e.slice(n+t[l].right.length)}return""!==e&&a.push({type:"text",data:e}),a},l=function(e,r){var n=o(e,r.delimiters);if(1===n.length&&"text"===n[0].type)return null;for(var a=document.createDocumentFragment(),i=0;i<n.length;i++)if("text"===n[i].type)a.appendChild(document.createTextNode(n[i].data));else{var l=document.createElement("span"),d=n[i].data;r.displayMode=n[i].display;try{r.preProcess&&(d=r.preProcess(d)),t().render(d,l,r)}catch(e){if(!(e instanceof t().ParseError))throw e;r.errorCallback("KaTeX auto-render: Failed to parse `"+n[i].data+"` with ",e),a.appendChild(document.createTextNode(n[i].rawData));continue}a.appendChild(l)}return a},d=function e(t,r){for(var n=0;n<t.childNodes.length;n++){var a=t.childNodes[n];if(3===a.nodeType){var i=l(a.textContent,r);i&&(n+=i.childNodes.length-1,t.replaceChild(i,a))}else 1===a.nodeType&&function(){var t=" "+a.className+" ";-1===r.ignoredTags.indexOf(a.nodeName.toLowerCase())&&r.ignoredClasses.every((function(e){return-1===t.indexOf(" "+e+" ")}))&&e(a,r)}()}},s=function(e,t){if(!e)throw new Error("No element provided to render");var r={};for(var n in t)t.hasOwnProperty(n)&&(r[n]=t[n]);r.delimiters=r.delimiters||[{left:"$$",right:"$$",display:!0},{left:"\\(",right:"\\)",display:!1},{left:"\\begin{equation}",right:"\\end{equation}",display:!0},{left:"\\begin{align}",right:"\\end{align}",display:!0},{left:"\\begin{alignat}",right:"\\end{alignat}",display:!0},{left:"\\begin{gather}",right:"\\end{gather}",display:!0},{left:"\\begin{CD}",right:"\\end{CD}",display:!0},{left:"\\[",right:"\\]",display:!0}],r.ignoredTags=r.ignoredTags||["script","noscript","style","textarea","pre","code","option"],r.ignoredClasses=r.ignoredClasses||[],r.errorCallback=r.errorCallback||console.error,r.macros=r.macros||{},d(e,r)}}(),a=a.default}()}));
|
222
public/katex/contrib/auto-render.mjs
vendored
Normal file
222
public/katex/contrib/auto-render.mjs
vendored
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
import katex from '../katex.mjs';
|
||||||
|
|
||||||
|
/* eslint no-constant-condition:0 */
|
||||||
|
var findEndOfMath = function findEndOfMath(delimiter, text, startIndex) {
|
||||||
|
// Adapted from
|
||||||
|
// https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx
|
||||||
|
var index = startIndex;
|
||||||
|
var braceLevel = 0;
|
||||||
|
var delimLength = delimiter.length;
|
||||||
|
|
||||||
|
while (index < text.length) {
|
||||||
|
var character = text[index];
|
||||||
|
|
||||||
|
if (braceLevel <= 0 && text.slice(index, index + delimLength) === delimiter) {
|
||||||
|
return index;
|
||||||
|
} else if (character === "\\") {
|
||||||
|
index++;
|
||||||
|
} else if (character === "{") {
|
||||||
|
braceLevel++;
|
||||||
|
} else if (character === "}") {
|
||||||
|
braceLevel--;
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
var escapeRegex = function escapeRegex(string) {
|
||||||
|
return string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
||||||
|
};
|
||||||
|
|
||||||
|
var amsRegex = /^\\begin{/;
|
||||||
|
|
||||||
|
var splitAtDelimiters = function splitAtDelimiters(text, delimiters) {
|
||||||
|
var index;
|
||||||
|
var data = [];
|
||||||
|
var regexLeft = new RegExp("(" + delimiters.map(x => escapeRegex(x.left)).join("|") + ")");
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
index = text.search(regexLeft);
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index > 0) {
|
||||||
|
data.push({
|
||||||
|
type: "text",
|
||||||
|
data: text.slice(0, index)
|
||||||
|
});
|
||||||
|
text = text.slice(index); // now text starts with delimiter
|
||||||
|
} // ... so this always succeeds:
|
||||||
|
|
||||||
|
|
||||||
|
var i = delimiters.findIndex(delim => text.startsWith(delim.left));
|
||||||
|
index = findEndOfMath(delimiters[i].right, text, delimiters[i].left.length);
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rawData = text.slice(0, index + delimiters[i].right.length);
|
||||||
|
var math = amsRegex.test(rawData) ? rawData : text.slice(delimiters[i].left.length, index);
|
||||||
|
data.push({
|
||||||
|
type: "math",
|
||||||
|
data: math,
|
||||||
|
rawData,
|
||||||
|
display: delimiters[i].display
|
||||||
|
});
|
||||||
|
text = text.slice(index + delimiters[i].right.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text !== "") {
|
||||||
|
data.push({
|
||||||
|
type: "text",
|
||||||
|
data: text
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* eslint no-console:0 */
|
||||||
|
/* Note: optionsCopy is mutated by this method. If it is ever exposed in the
|
||||||
|
* API, we should copy it before mutating.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var renderMathInText = function renderMathInText(text, optionsCopy) {
|
||||||
|
var data = splitAtDelimiters(text, optionsCopy.delimiters);
|
||||||
|
|
||||||
|
if (data.length === 1 && data[0].type === 'text') {
|
||||||
|
// There is no formula in the text.
|
||||||
|
// Let's return null which means there is no need to replace
|
||||||
|
// the current text node with a new one.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fragment = document.createDocumentFragment();
|
||||||
|
|
||||||
|
for (var i = 0; i < data.length; i++) {
|
||||||
|
if (data[i].type === "text") {
|
||||||
|
fragment.appendChild(document.createTextNode(data[i].data));
|
||||||
|
} else {
|
||||||
|
var span = document.createElement("span");
|
||||||
|
var math = data[i].data; // Override any display mode defined in the settings with that
|
||||||
|
// defined by the text itself
|
||||||
|
|
||||||
|
optionsCopy.displayMode = data[i].display;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (optionsCopy.preProcess) {
|
||||||
|
math = optionsCopy.preProcess(math);
|
||||||
|
}
|
||||||
|
|
||||||
|
katex.render(math, span, optionsCopy);
|
||||||
|
} catch (e) {
|
||||||
|
if (!(e instanceof katex.ParseError)) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
optionsCopy.errorCallback("KaTeX auto-render: Failed to parse `" + data[i].data + "` with ", e);
|
||||||
|
fragment.appendChild(document.createTextNode(data[i].rawData));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment.appendChild(span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fragment;
|
||||||
|
};
|
||||||
|
|
||||||
|
var renderElem = function renderElem(elem, optionsCopy) {
|
||||||
|
for (var i = 0; i < elem.childNodes.length; i++) {
|
||||||
|
var childNode = elem.childNodes[i];
|
||||||
|
|
||||||
|
if (childNode.nodeType === 3) {
|
||||||
|
// Text node
|
||||||
|
var frag = renderMathInText(childNode.textContent, optionsCopy);
|
||||||
|
|
||||||
|
if (frag) {
|
||||||
|
i += frag.childNodes.length - 1;
|
||||||
|
elem.replaceChild(frag, childNode);
|
||||||
|
}
|
||||||
|
} else if (childNode.nodeType === 1) {
|
||||||
|
(function () {
|
||||||
|
// Element node
|
||||||
|
var className = ' ' + childNode.className + ' ';
|
||||||
|
var shouldRender = optionsCopy.ignoredTags.indexOf(childNode.nodeName.toLowerCase()) === -1 && optionsCopy.ignoredClasses.every(x => className.indexOf(' ' + x + ' ') === -1);
|
||||||
|
|
||||||
|
if (shouldRender) {
|
||||||
|
renderElem(childNode, optionsCopy);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
} // Otherwise, it's something else, and ignore it.
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var renderMathInElement = function renderMathInElement(elem, options) {
|
||||||
|
if (!elem) {
|
||||||
|
throw new Error("No element provided to render");
|
||||||
|
}
|
||||||
|
|
||||||
|
var optionsCopy = {}; // Object.assign(optionsCopy, option)
|
||||||
|
|
||||||
|
for (var option in options) {
|
||||||
|
if (options.hasOwnProperty(option)) {
|
||||||
|
optionsCopy[option] = options[option];
|
||||||
|
}
|
||||||
|
} // default options
|
||||||
|
|
||||||
|
|
||||||
|
optionsCopy.delimiters = optionsCopy.delimiters || [{
|
||||||
|
left: "$$",
|
||||||
|
right: "$$",
|
||||||
|
display: true
|
||||||
|
}, {
|
||||||
|
left: "\\(",
|
||||||
|
right: "\\)",
|
||||||
|
display: false
|
||||||
|
}, // LaTeX uses $…$, but it ruins the display of normal `$` in text:
|
||||||
|
// {left: "$", right: "$", display: false},
|
||||||
|
// $ must come after $$
|
||||||
|
// Render AMS environments even if outside $$…$$ delimiters.
|
||||||
|
{
|
||||||
|
left: "\\begin{equation}",
|
||||||
|
right: "\\end{equation}",
|
||||||
|
display: true
|
||||||
|
}, {
|
||||||
|
left: "\\begin{align}",
|
||||||
|
right: "\\end{align}",
|
||||||
|
display: true
|
||||||
|
}, {
|
||||||
|
left: "\\begin{alignat}",
|
||||||
|
right: "\\end{alignat}",
|
||||||
|
display: true
|
||||||
|
}, {
|
||||||
|
left: "\\begin{gather}",
|
||||||
|
right: "\\end{gather}",
|
||||||
|
display: true
|
||||||
|
}, {
|
||||||
|
left: "\\begin{CD}",
|
||||||
|
right: "\\end{CD}",
|
||||||
|
display: true
|
||||||
|
}, {
|
||||||
|
left: "\\[",
|
||||||
|
right: "\\]",
|
||||||
|
display: true
|
||||||
|
}];
|
||||||
|
optionsCopy.ignoredTags = optionsCopy.ignoredTags || ["script", "noscript", "style", "textarea", "pre", "code", "option"];
|
||||||
|
optionsCopy.ignoredClasses = optionsCopy.ignoredClasses || [];
|
||||||
|
optionsCopy.errorCallback = optionsCopy.errorCallback || console.error; // Enable sharing of global macros defined via `\gdef` between different
|
||||||
|
// math elements within a single call to `renderMathInElement`.
|
||||||
|
|
||||||
|
optionsCopy.macros = optionsCopy.macros || {};
|
||||||
|
renderElem(elem, optionsCopy);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { renderMathInElement as default };
|
14
public/katex/contrib/copy-tex.css
vendored
Normal file
14
public/katex/contrib/copy-tex.css
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
/* Force selection of entire .katex/.katex-display blocks, so that we can
|
||||||
|
* copy/paste the entire source code. If you omit this CSS, partial
|
||||||
|
* selections of a formula will work, but will copy the ugly HTML
|
||||||
|
* representation instead of the LaTeX source code. (Full selections will
|
||||||
|
* still produce the LaTeX source code.)
|
||||||
|
*/
|
||||||
|
.katex,
|
||||||
|
.katex-display {
|
||||||
|
-webkit-user-select: all;
|
||||||
|
-moz-user-select: all;
|
||||||
|
-ms-user-select: all;
|
||||||
|
user-select: all;
|
||||||
|
}
|
||||||
|
|
116
public/katex/contrib/copy-tex.js
vendored
Normal file
116
public/katex/contrib/copy-tex.js
vendored
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
(function webpackUniversalModuleDefinition(root, factory) {
|
||||||
|
if(typeof exports === 'object' && typeof module === 'object')
|
||||||
|
module.exports = factory();
|
||||||
|
else if(typeof define === 'function' && define.amd)
|
||||||
|
define([], factory);
|
||||||
|
else {
|
||||||
|
var a = factory();
|
||||||
|
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
|
||||||
|
}
|
||||||
|
})((typeof self !== 'undefined' ? self : this), function() {
|
||||||
|
return /******/ (function() { // webpackBootstrap
|
||||||
|
/******/ "use strict";
|
||||||
|
var __webpack_exports__ = {};
|
||||||
|
|
||||||
|
;// CONCATENATED MODULE: ./contrib/copy-tex/katex2tex.js
|
||||||
|
// Set these to how you want inline and display math to be delimited.
|
||||||
|
var defaultCopyDelimiters = {
|
||||||
|
inline: ['$', '$'],
|
||||||
|
// alternative: ['\(', '\)']
|
||||||
|
display: ['$$', '$$'] // alternative: ['\[', '\]']
|
||||||
|
|
||||||
|
}; // Replace .katex elements with their TeX source (<annotation> element).
|
||||||
|
// Modifies fragment in-place. Useful for writing your own 'copy' handler,
|
||||||
|
// as in copy-tex.js.
|
||||||
|
|
||||||
|
var katexReplaceWithTex = function katexReplaceWithTex(fragment, copyDelimiters) {
|
||||||
|
if (copyDelimiters === void 0) {
|
||||||
|
copyDelimiters = defaultCopyDelimiters;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove .katex-html blocks that are preceded by .katex-mathml blocks
|
||||||
|
// (which will get replaced below).
|
||||||
|
var katexHtml = fragment.querySelectorAll('.katex-mathml + .katex-html');
|
||||||
|
|
||||||
|
for (var i = 0; i < katexHtml.length; i++) {
|
||||||
|
var element = katexHtml[i];
|
||||||
|
|
||||||
|
if (element.remove) {
|
||||||
|
element.remove(null);
|
||||||
|
} else {
|
||||||
|
element.parentNode.removeChild(element);
|
||||||
|
}
|
||||||
|
} // Replace .katex-mathml elements with their annotation (TeX source)
|
||||||
|
// descendant, with inline delimiters.
|
||||||
|
|
||||||
|
|
||||||
|
var katexMathml = fragment.querySelectorAll('.katex-mathml');
|
||||||
|
|
||||||
|
for (var _i = 0; _i < katexMathml.length; _i++) {
|
||||||
|
var _element = katexMathml[_i];
|
||||||
|
|
||||||
|
var texSource = _element.querySelector('annotation');
|
||||||
|
|
||||||
|
if (texSource) {
|
||||||
|
if (_element.replaceWith) {
|
||||||
|
_element.replaceWith(texSource);
|
||||||
|
} else {
|
||||||
|
_element.parentNode.replaceChild(texSource, _element);
|
||||||
|
}
|
||||||
|
|
||||||
|
texSource.innerHTML = copyDelimiters.inline[0] + texSource.innerHTML + copyDelimiters.inline[1];
|
||||||
|
}
|
||||||
|
} // Switch display math to display delimiters.
|
||||||
|
|
||||||
|
|
||||||
|
var displays = fragment.querySelectorAll('.katex-display annotation');
|
||||||
|
|
||||||
|
for (var _i2 = 0; _i2 < displays.length; _i2++) {
|
||||||
|
var _element2 = displays[_i2];
|
||||||
|
_element2.innerHTML = copyDelimiters.display[0] + _element2.innerHTML.substr(copyDelimiters.inline[0].length, _element2.innerHTML.length - copyDelimiters.inline[0].length - copyDelimiters.inline[1].length) + copyDelimiters.display[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return fragment;
|
||||||
|
};
|
||||||
|
/* harmony default export */ var katex2tex = (katexReplaceWithTex);
|
||||||
|
;// CONCATENATED MODULE: ./contrib/copy-tex/copy-tex.js
|
||||||
|
// Global copy handler to modify behavior on .katex elements.
|
||||||
|
|
||||||
|
document.addEventListener('copy', function (event) {
|
||||||
|
var selection = window.getSelection();
|
||||||
|
|
||||||
|
if (selection.isCollapsed) {
|
||||||
|
return; // default action OK if selection is empty
|
||||||
|
}
|
||||||
|
|
||||||
|
var fragment = selection.getRangeAt(0).cloneContents();
|
||||||
|
|
||||||
|
if (!fragment.querySelector('.katex-mathml')) {
|
||||||
|
return; // default action OK if no .katex-mathml elements
|
||||||
|
} // Preserve usual HTML copy/paste behavior.
|
||||||
|
|
||||||
|
|
||||||
|
var html = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < fragment.childNodes.length; i++) {
|
||||||
|
html.push(fragment.childNodes[i].outerHTML);
|
||||||
|
}
|
||||||
|
|
||||||
|
event.clipboardData.setData('text/html', html.join('')); // Rewrite plain-text version.
|
||||||
|
|
||||||
|
event.clipboardData.setData('text/plain', katex2tex(fragment).textContent); // Prevent normal copy handling.
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
||||||
|
;// CONCATENATED MODULE: ./contrib/copy-tex/copy-tex.webpack.js
|
||||||
|
/**
|
||||||
|
* This is the webpack entry point for KaTeX. As ECMAScript doesn't support
|
||||||
|
* CSS modules natively, a separate entry point is used.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
__webpack_exports__ = __webpack_exports__["default"];
|
||||||
|
/******/ return __webpack_exports__;
|
||||||
|
/******/ })()
|
||||||
|
;
|
||||||
|
});
|
1
public/katex/contrib/copy-tex.min.css
vendored
Normal file
1
public/katex/contrib/copy-tex.min.css
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
.katex,.katex-display{-webkit-user-select:all;-moz-user-select:all;-ms-user-select:all;user-select:all}
|
1
public/katex/contrib/copy-tex.min.js
vendored
Normal file
1
public/katex/contrib/copy-tex.min.js
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var n=t();for(var l in n)("object"==typeof exports?exports:e)[l]=n[l]}}("undefined"!=typeof self?self:this,(function(){return function(){"use strict";var e={},t={inline:["$","$"],display:["$$","$$"]},n=function(e,n){void 0===n&&(n=t);for(var l=e.querySelectorAll(".katex-mathml + .katex-html"),r=0;r<l.length;r++){var i=l[r];i.remove?i.remove(null):i.parentNode.removeChild(i)}for(var o=e.querySelectorAll(".katex-mathml"),a=0;a<o.length;a++){var d=o[a],f=d.querySelector("annotation");f&&(d.replaceWith?d.replaceWith(f):d.parentNode.replaceChild(f,d),f.innerHTML=n.inline[0]+f.innerHTML+n.inline[1])}for(var c=e.querySelectorAll(".katex-display annotation"),s=0;s<c.length;s++){var p=c[s];p.innerHTML=n.display[0]+p.innerHTML.substr(n.inline[0].length,p.innerHTML.length-n.inline[0].length-n.inline[1].length)+n.display[1]}return e};return document.addEventListener("copy",(function(e){var t=window.getSelection();if(!t.isCollapsed){var l=t.getRangeAt(0).cloneContents();if(l.querySelector(".katex-mathml")){for(var r=[],i=0;i<l.childNodes.length;i++)r.push(l.childNodes[i].outerHTML);e.clipboardData.setData("text/html",r.join("")),e.clipboardData.setData("text/plain",n(l).textContent),e.preventDefault()}}})),e=e.default}()}));
|
86
public/katex/contrib/copy-tex.mjs
vendored
Normal file
86
public/katex/contrib/copy-tex.mjs
vendored
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
// Set these to how you want inline and display math to be delimited.
|
||||||
|
var defaultCopyDelimiters = {
|
||||||
|
inline: ['$', '$'],
|
||||||
|
// alternative: ['\(', '\)']
|
||||||
|
display: ['$$', '$$'] // alternative: ['\[', '\]']
|
||||||
|
|
||||||
|
}; // Replace .katex elements with their TeX source (<annotation> element).
|
||||||
|
// Modifies fragment in-place. Useful for writing your own 'copy' handler,
|
||||||
|
// as in copy-tex.js.
|
||||||
|
|
||||||
|
var katexReplaceWithTex = function katexReplaceWithTex(fragment, copyDelimiters) {
|
||||||
|
if (copyDelimiters === void 0) {
|
||||||
|
copyDelimiters = defaultCopyDelimiters;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove .katex-html blocks that are preceded by .katex-mathml blocks
|
||||||
|
// (which will get replaced below).
|
||||||
|
var katexHtml = fragment.querySelectorAll('.katex-mathml + .katex-html');
|
||||||
|
|
||||||
|
for (var i = 0; i < katexHtml.length; i++) {
|
||||||
|
var element = katexHtml[i];
|
||||||
|
|
||||||
|
if (element.remove) {
|
||||||
|
element.remove(null);
|
||||||
|
} else {
|
||||||
|
element.parentNode.removeChild(element);
|
||||||
|
}
|
||||||
|
} // Replace .katex-mathml elements with their annotation (TeX source)
|
||||||
|
// descendant, with inline delimiters.
|
||||||
|
|
||||||
|
|
||||||
|
var katexMathml = fragment.querySelectorAll('.katex-mathml');
|
||||||
|
|
||||||
|
for (var _i = 0; _i < katexMathml.length; _i++) {
|
||||||
|
var _element = katexMathml[_i];
|
||||||
|
|
||||||
|
var texSource = _element.querySelector('annotation');
|
||||||
|
|
||||||
|
if (texSource) {
|
||||||
|
if (_element.replaceWith) {
|
||||||
|
_element.replaceWith(texSource);
|
||||||
|
} else {
|
||||||
|
_element.parentNode.replaceChild(texSource, _element);
|
||||||
|
}
|
||||||
|
|
||||||
|
texSource.innerHTML = copyDelimiters.inline[0] + texSource.innerHTML + copyDelimiters.inline[1];
|
||||||
|
}
|
||||||
|
} // Switch display math to display delimiters.
|
||||||
|
|
||||||
|
|
||||||
|
var displays = fragment.querySelectorAll('.katex-display annotation');
|
||||||
|
|
||||||
|
for (var _i2 = 0; _i2 < displays.length; _i2++) {
|
||||||
|
var _element2 = displays[_i2];
|
||||||
|
_element2.innerHTML = copyDelimiters.display[0] + _element2.innerHTML.substr(copyDelimiters.inline[0].length, _element2.innerHTML.length - copyDelimiters.inline[0].length - copyDelimiters.inline[1].length) + copyDelimiters.display[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return fragment;
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('copy', function (event) {
|
||||||
|
var selection = window.getSelection();
|
||||||
|
|
||||||
|
if (selection.isCollapsed) {
|
||||||
|
return; // default action OK if selection is empty
|
||||||
|
}
|
||||||
|
|
||||||
|
var fragment = selection.getRangeAt(0).cloneContents();
|
||||||
|
|
||||||
|
if (!fragment.querySelector('.katex-mathml')) {
|
||||||
|
return; // default action OK if no .katex-mathml elements
|
||||||
|
} // Preserve usual HTML copy/paste behavior.
|
||||||
|
|
||||||
|
|
||||||
|
var html = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < fragment.childNodes.length; i++) {
|
||||||
|
html.push(fragment.childNodes[i].outerHTML);
|
||||||
|
}
|
||||||
|
|
||||||
|
event.clipboardData.setData('text/html', html.join('')); // Rewrite plain-text version.
|
||||||
|
|
||||||
|
event.clipboardData.setData('text/plain', katexReplaceWithTex(fragment).textContent); // Prevent normal copy handling.
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
112
public/katex/contrib/mathtex-script-type.js
vendored
Normal file
112
public/katex/contrib/mathtex-script-type.js
vendored
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
(function webpackUniversalModuleDefinition(root, factory) {
|
||||||
|
if(typeof exports === 'object' && typeof module === 'object')
|
||||||
|
module.exports = factory(require("katex"));
|
||||||
|
else if(typeof define === 'function' && define.amd)
|
||||||
|
define(["katex"], factory);
|
||||||
|
else {
|
||||||
|
var a = typeof exports === 'object' ? factory(require("katex")) : factory(root["katex"]);
|
||||||
|
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
|
||||||
|
}
|
||||||
|
})((typeof self !== 'undefined' ? self : this), function(__WEBPACK_EXTERNAL_MODULE__771__) {
|
||||||
|
return /******/ (function() { // webpackBootstrap
|
||||||
|
/******/ "use strict";
|
||||||
|
/******/ var __webpack_modules__ = ({
|
||||||
|
|
||||||
|
/***/ 771:
|
||||||
|
/***/ (function(module) {
|
||||||
|
|
||||||
|
module.exports = __WEBPACK_EXTERNAL_MODULE__771__;
|
||||||
|
|
||||||
|
/***/ })
|
||||||
|
|
||||||
|
/******/ });
|
||||||
|
/************************************************************************/
|
||||||
|
/******/ // The module cache
|
||||||
|
/******/ var __webpack_module_cache__ = {};
|
||||||
|
/******/
|
||||||
|
/******/ // The require function
|
||||||
|
/******/ function __webpack_require__(moduleId) {
|
||||||
|
/******/ // Check if module is in cache
|
||||||
|
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
||||||
|
/******/ if (cachedModule !== undefined) {
|
||||||
|
/******/ return cachedModule.exports;
|
||||||
|
/******/ }
|
||||||
|
/******/ // Create a new module (and put it into the cache)
|
||||||
|
/******/ var module = __webpack_module_cache__[moduleId] = {
|
||||||
|
/******/ // no module.id needed
|
||||||
|
/******/ // no module.loaded needed
|
||||||
|
/******/ exports: {}
|
||||||
|
/******/ };
|
||||||
|
/******/
|
||||||
|
/******/ // Execute the module function
|
||||||
|
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
||||||
|
/******/
|
||||||
|
/******/ // Return the exports of the module
|
||||||
|
/******/ return module.exports;
|
||||||
|
/******/ }
|
||||||
|
/******/
|
||||||
|
/************************************************************************/
|
||||||
|
/******/ /* webpack/runtime/compat get default export */
|
||||||
|
/******/ !function() {
|
||||||
|
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||||
|
/******/ __webpack_require__.n = function(module) {
|
||||||
|
/******/ var getter = module && module.__esModule ?
|
||||||
|
/******/ function() { return module['default']; } :
|
||||||
|
/******/ function() { return module; };
|
||||||
|
/******/ __webpack_require__.d(getter, { a: getter });
|
||||||
|
/******/ return getter;
|
||||||
|
/******/ };
|
||||||
|
/******/ }();
|
||||||
|
/******/
|
||||||
|
/******/ /* webpack/runtime/define property getters */
|
||||||
|
/******/ !function() {
|
||||||
|
/******/ // define getter functions for harmony exports
|
||||||
|
/******/ __webpack_require__.d = function(exports, definition) {
|
||||||
|
/******/ for(var key in definition) {
|
||||||
|
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
|
||||||
|
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
||||||
|
/******/ }
|
||||||
|
/******/ }
|
||||||
|
/******/ };
|
||||||
|
/******/ }();
|
||||||
|
/******/
|
||||||
|
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
||||||
|
/******/ !function() {
|
||||||
|
/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
|
||||||
|
/******/ }();
|
||||||
|
/******/
|
||||||
|
/************************************************************************/
|
||||||
|
var __webpack_exports__ = {};
|
||||||
|
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
|
||||||
|
!function() {
|
||||||
|
/* harmony import */ var katex__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(771);
|
||||||
|
/* harmony import */ var katex__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(katex__WEBPACK_IMPORTED_MODULE_0__);
|
||||||
|
|
||||||
|
var scripts = document.body.getElementsByTagName("script");
|
||||||
|
scripts = Array.prototype.slice.call(scripts);
|
||||||
|
scripts.forEach(function (script) {
|
||||||
|
if (!script.type || !script.type.match(/math\/tex/i)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var display = script.type.match(/mode\s*=\s*display(;|\s|\n|$)/) != null;
|
||||||
|
var katexElement = document.createElement(display ? "div" : "span");
|
||||||
|
katexElement.setAttribute("class", display ? "equation" : "inline-equation");
|
||||||
|
|
||||||
|
try {
|
||||||
|
katex__WEBPACK_IMPORTED_MODULE_0___default().render(script.text, katexElement, {
|
||||||
|
displayMode: display
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
//console.error(err); linter doesn't like this
|
||||||
|
katexElement.textContent = script.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
script.parentNode.replaceChild(katexElement, script);
|
||||||
|
});
|
||||||
|
}();
|
||||||
|
__webpack_exports__ = __webpack_exports__["default"];
|
||||||
|
/******/ return __webpack_exports__;
|
||||||
|
/******/ })()
|
||||||
|
;
|
||||||
|
});
|
1
public/katex/contrib/mathtex-script-type.min.js
vendored
Normal file
1
public/katex/contrib/mathtex-script-type.min.js
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t(require("katex"));else if("function"==typeof define&&define.amd)define(["katex"],t);else{var r="object"==typeof exports?t(require("katex")):t(e.katex);for(var n in r)("object"==typeof exports?exports:e)[n]=r[n]}}("undefined"!=typeof self?self:this,(function(e){return function(){"use strict";var t={771:function(t){t.exports=e}},r={};function n(e){var o=r[e];if(void 0!==o)return o.exports;var i=r[e]={exports:{}};return t[e](i,i.exports,n),i.exports}n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,{a:t}),t},n.d=function(e,t){for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var o,i,a,u={};return o=n(771),i=n.n(o),a=document.body.getElementsByTagName("script"),(a=Array.prototype.slice.call(a)).forEach((function(e){if(!e.type||!e.type.match(/math\/tex/i))return-1;var t=null!=e.type.match(/mode\s*=\s*display(;|\s|\n|$)/),r=document.createElement(t?"div":"span");r.setAttribute("class",t?"equation":"inline-equation");try{i().render(e.text,r,{displayMode:t})}catch(t){r.textContent=e.text}e.parentNode.replaceChild(r,e)})),u=u.default}()}));
|
24
public/katex/contrib/mathtex-script-type.mjs
vendored
Normal file
24
public/katex/contrib/mathtex-script-type.mjs
vendored
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import katex from '../katex.mjs';
|
||||||
|
|
||||||
|
var scripts = document.body.getElementsByTagName("script");
|
||||||
|
scripts = Array.prototype.slice.call(scripts);
|
||||||
|
scripts.forEach(function (script) {
|
||||||
|
if (!script.type || !script.type.match(/math\/tex/i)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var display = script.type.match(/mode\s*=\s*display(;|\s|\n|$)/) != null;
|
||||||
|
var katexElement = document.createElement(display ? "div" : "span");
|
||||||
|
katexElement.setAttribute("class", display ? "equation" : "inline-equation");
|
||||||
|
|
||||||
|
try {
|
||||||
|
katex.render(script.text, katexElement, {
|
||||||
|
displayMode: display
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
//console.error(err); linter doesn't like this
|
||||||
|
katexElement.textContent = script.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
script.parentNode.replaceChild(katexElement, script);
|
||||||
|
});
|
3216
public/katex/contrib/mhchem.js
vendored
Normal file
3216
public/katex/contrib/mhchem.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
1
public/katex/contrib/mhchem.min.js
vendored
Normal file
1
public/katex/contrib/mhchem.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
3109
public/katex/contrib/mhchem.mjs
vendored
Normal file
3109
public/katex/contrib/mhchem.mjs
vendored
Normal file
File diff suppressed because it is too large
Load diff
875
public/katex/contrib/render-a11y-string.js
vendored
Normal file
875
public/katex/contrib/render-a11y-string.js
vendored
Normal file
|
@ -0,0 +1,875 @@
|
||||||
|
(function webpackUniversalModuleDefinition(root, factory) {
|
||||||
|
if(typeof exports === 'object' && typeof module === 'object')
|
||||||
|
module.exports = factory(require("katex"));
|
||||||
|
else if(typeof define === 'function' && define.amd)
|
||||||
|
define(["katex"], factory);
|
||||||
|
else {
|
||||||
|
var a = typeof exports === 'object' ? factory(require("katex")) : factory(root["katex"]);
|
||||||
|
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
|
||||||
|
}
|
||||||
|
})((typeof self !== 'undefined' ? self : this), function(__WEBPACK_EXTERNAL_MODULE__771__) {
|
||||||
|
return /******/ (function() { // webpackBootstrap
|
||||||
|
/******/ "use strict";
|
||||||
|
/******/ var __webpack_modules__ = ({
|
||||||
|
|
||||||
|
/***/ 771:
|
||||||
|
/***/ (function(module) {
|
||||||
|
|
||||||
|
module.exports = __WEBPACK_EXTERNAL_MODULE__771__;
|
||||||
|
|
||||||
|
/***/ })
|
||||||
|
|
||||||
|
/******/ });
|
||||||
|
/************************************************************************/
|
||||||
|
/******/ // The module cache
|
||||||
|
/******/ var __webpack_module_cache__ = {};
|
||||||
|
/******/
|
||||||
|
/******/ // The require function
|
||||||
|
/******/ function __webpack_require__(moduleId) {
|
||||||
|
/******/ // Check if module is in cache
|
||||||
|
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
||||||
|
/******/ if (cachedModule !== undefined) {
|
||||||
|
/******/ return cachedModule.exports;
|
||||||
|
/******/ }
|
||||||
|
/******/ // Create a new module (and put it into the cache)
|
||||||
|
/******/ var module = __webpack_module_cache__[moduleId] = {
|
||||||
|
/******/ // no module.id needed
|
||||||
|
/******/ // no module.loaded needed
|
||||||
|
/******/ exports: {}
|
||||||
|
/******/ };
|
||||||
|
/******/
|
||||||
|
/******/ // Execute the module function
|
||||||
|
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
||||||
|
/******/
|
||||||
|
/******/ // Return the exports of the module
|
||||||
|
/******/ return module.exports;
|
||||||
|
/******/ }
|
||||||
|
/******/
|
||||||
|
/************************************************************************/
|
||||||
|
/******/ /* webpack/runtime/compat get default export */
|
||||||
|
/******/ !function() {
|
||||||
|
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||||
|
/******/ __webpack_require__.n = function(module) {
|
||||||
|
/******/ var getter = module && module.__esModule ?
|
||||||
|
/******/ function() { return module['default']; } :
|
||||||
|
/******/ function() { return module; };
|
||||||
|
/******/ __webpack_require__.d(getter, { a: getter });
|
||||||
|
/******/ return getter;
|
||||||
|
/******/ };
|
||||||
|
/******/ }();
|
||||||
|
/******/
|
||||||
|
/******/ /* webpack/runtime/define property getters */
|
||||||
|
/******/ !function() {
|
||||||
|
/******/ // define getter functions for harmony exports
|
||||||
|
/******/ __webpack_require__.d = function(exports, definition) {
|
||||||
|
/******/ for(var key in definition) {
|
||||||
|
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
|
||||||
|
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
||||||
|
/******/ }
|
||||||
|
/******/ }
|
||||||
|
/******/ };
|
||||||
|
/******/ }();
|
||||||
|
/******/
|
||||||
|
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
||||||
|
/******/ !function() {
|
||||||
|
/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
|
||||||
|
/******/ }();
|
||||||
|
/******/
|
||||||
|
/************************************************************************/
|
||||||
|
var __webpack_exports__ = {};
|
||||||
|
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
|
||||||
|
!function() {
|
||||||
|
/* harmony import */ var katex__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(771);
|
||||||
|
/* harmony import */ var katex__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(katex__WEBPACK_IMPORTED_MODULE_0__);
|
||||||
|
/**
|
||||||
|
* renderA11yString returns a readable string.
|
||||||
|
*
|
||||||
|
* In some cases the string will have the proper semantic math
|
||||||
|
* meaning,:
|
||||||
|
* renderA11yString("\\frac{1}{2}"")
|
||||||
|
* -> "start fraction, 1, divided by, 2, end fraction"
|
||||||
|
*
|
||||||
|
* However, other cases do not:
|
||||||
|
* renderA11yString("f(x) = x^2")
|
||||||
|
* -> "f, left parenthesis, x, right parenthesis, equals, x, squared"
|
||||||
|
*
|
||||||
|
* The commas in the string aim to increase ease of understanding
|
||||||
|
* when read by a screenreader.
|
||||||
|
*/
|
||||||
|
// NOTE: since we're importing types here these files won't actually be
|
||||||
|
// included in the build.
|
||||||
|
// $FlowIgnore: we import the types directly anyways
|
||||||
|
|
||||||
|
var stringMap = {
|
||||||
|
"(": "left parenthesis",
|
||||||
|
")": "right parenthesis",
|
||||||
|
"[": "open bracket",
|
||||||
|
"]": "close bracket",
|
||||||
|
"\\{": "left brace",
|
||||||
|
"\\}": "right brace",
|
||||||
|
"\\lvert": "open vertical bar",
|
||||||
|
"\\rvert": "close vertical bar",
|
||||||
|
"|": "vertical bar",
|
||||||
|
"\\uparrow": "up arrow",
|
||||||
|
"\\Uparrow": "up arrow",
|
||||||
|
"\\downarrow": "down arrow",
|
||||||
|
"\\Downarrow": "down arrow",
|
||||||
|
"\\updownarrow": "up down arrow",
|
||||||
|
"\\leftarrow": "left arrow",
|
||||||
|
"\\Leftarrow": "left arrow",
|
||||||
|
"\\rightarrow": "right arrow",
|
||||||
|
"\\Rightarrow": "right arrow",
|
||||||
|
"\\langle": "open angle",
|
||||||
|
"\\rangle": "close angle",
|
||||||
|
"\\lfloor": "open floor",
|
||||||
|
"\\rfloor": "close floor",
|
||||||
|
"\\int": "integral",
|
||||||
|
"\\intop": "integral",
|
||||||
|
"\\lim": "limit",
|
||||||
|
"\\ln": "natural log",
|
||||||
|
"\\log": "log",
|
||||||
|
"\\sin": "sine",
|
||||||
|
"\\cos": "cosine",
|
||||||
|
"\\tan": "tangent",
|
||||||
|
"\\cot": "cotangent",
|
||||||
|
"\\sum": "sum",
|
||||||
|
"/": "slash",
|
||||||
|
",": "comma",
|
||||||
|
".": "point",
|
||||||
|
"-": "negative",
|
||||||
|
"+": "plus",
|
||||||
|
"~": "tilde",
|
||||||
|
":": "colon",
|
||||||
|
"?": "question mark",
|
||||||
|
"'": "apostrophe",
|
||||||
|
"\\%": "percent",
|
||||||
|
" ": "space",
|
||||||
|
"\\ ": "space",
|
||||||
|
"\\$": "dollar sign",
|
||||||
|
"\\angle": "angle",
|
||||||
|
"\\degree": "degree",
|
||||||
|
"\\circ": "circle",
|
||||||
|
"\\vec": "vector",
|
||||||
|
"\\triangle": "triangle",
|
||||||
|
"\\pi": "pi",
|
||||||
|
"\\prime": "prime",
|
||||||
|
"\\infty": "infinity",
|
||||||
|
"\\alpha": "alpha",
|
||||||
|
"\\beta": "beta",
|
||||||
|
"\\gamma": "gamma",
|
||||||
|
"\\omega": "omega",
|
||||||
|
"\\theta": "theta",
|
||||||
|
"\\sigma": "sigma",
|
||||||
|
"\\lambda": "lambda",
|
||||||
|
"\\tau": "tau",
|
||||||
|
"\\Delta": "delta",
|
||||||
|
"\\delta": "delta",
|
||||||
|
"\\mu": "mu",
|
||||||
|
"\\rho": "rho",
|
||||||
|
"\\nabla": "del",
|
||||||
|
"\\ell": "ell",
|
||||||
|
"\\ldots": "dots",
|
||||||
|
// TODO: add entries for all accents
|
||||||
|
"\\hat": "hat",
|
||||||
|
"\\acute": "acute"
|
||||||
|
};
|
||||||
|
var powerMap = {
|
||||||
|
"prime": "prime",
|
||||||
|
"degree": "degrees",
|
||||||
|
"circle": "degrees",
|
||||||
|
"2": "squared",
|
||||||
|
"3": "cubed"
|
||||||
|
};
|
||||||
|
var openMap = {
|
||||||
|
"|": "open vertical bar",
|
||||||
|
".": ""
|
||||||
|
};
|
||||||
|
var closeMap = {
|
||||||
|
"|": "close vertical bar",
|
||||||
|
".": ""
|
||||||
|
};
|
||||||
|
var binMap = {
|
||||||
|
"+": "plus",
|
||||||
|
"-": "minus",
|
||||||
|
"\\pm": "plus minus",
|
||||||
|
"\\cdot": "dot",
|
||||||
|
"*": "times",
|
||||||
|
"/": "divided by",
|
||||||
|
"\\times": "times",
|
||||||
|
"\\div": "divided by",
|
||||||
|
"\\circ": "circle",
|
||||||
|
"\\bullet": "bullet"
|
||||||
|
};
|
||||||
|
var relMap = {
|
||||||
|
"=": "equals",
|
||||||
|
"\\approx": "approximately equals",
|
||||||
|
"≠": "does not equal",
|
||||||
|
"\\geq": "is greater than or equal to",
|
||||||
|
"\\ge": "is greater than or equal to",
|
||||||
|
"\\leq": "is less than or equal to",
|
||||||
|
"\\le": "is less than or equal to",
|
||||||
|
">": "is greater than",
|
||||||
|
"<": "is less than",
|
||||||
|
"\\leftarrow": "left arrow",
|
||||||
|
"\\Leftarrow": "left arrow",
|
||||||
|
"\\rightarrow": "right arrow",
|
||||||
|
"\\Rightarrow": "right arrow",
|
||||||
|
":": "colon"
|
||||||
|
};
|
||||||
|
var accentUnderMap = {
|
||||||
|
"\\underleftarrow": "left arrow",
|
||||||
|
"\\underrightarrow": "right arrow",
|
||||||
|
"\\underleftrightarrow": "left-right arrow",
|
||||||
|
"\\undergroup": "group",
|
||||||
|
"\\underlinesegment": "line segment",
|
||||||
|
"\\utilde": "tilde"
|
||||||
|
};
|
||||||
|
|
||||||
|
var buildString = function buildString(str, type, a11yStrings) {
|
||||||
|
if (!str) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ret;
|
||||||
|
|
||||||
|
if (type === "open") {
|
||||||
|
ret = str in openMap ? openMap[str] : stringMap[str] || str;
|
||||||
|
} else if (type === "close") {
|
||||||
|
ret = str in closeMap ? closeMap[str] : stringMap[str] || str;
|
||||||
|
} else if (type === "bin") {
|
||||||
|
ret = binMap[str] || str;
|
||||||
|
} else if (type === "rel") {
|
||||||
|
ret = relMap[str] || str;
|
||||||
|
} else {
|
||||||
|
ret = stringMap[str] || str;
|
||||||
|
} // If the text to add is a number and there is already a string
|
||||||
|
// in the list and the last string is a number then we should
|
||||||
|
// combine them into a single number
|
||||||
|
|
||||||
|
|
||||||
|
if (/^\d+$/.test(ret) && a11yStrings.length > 0 && // TODO(kevinb): check that the last item in a11yStrings is a string
|
||||||
|
// I think we might be able to drop the nested arrays, which would make
|
||||||
|
// this easier to type
|
||||||
|
// $FlowFixMe
|
||||||
|
/^\d+$/.test(a11yStrings[a11yStrings.length - 1])) {
|
||||||
|
a11yStrings[a11yStrings.length - 1] += ret;
|
||||||
|
} else if (ret) {
|
||||||
|
a11yStrings.push(ret);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var buildRegion = function buildRegion(a11yStrings, callback) {
|
||||||
|
var regionStrings = [];
|
||||||
|
a11yStrings.push(regionStrings);
|
||||||
|
callback(regionStrings);
|
||||||
|
};
|
||||||
|
|
||||||
|
var handleObject = function handleObject(tree, a11yStrings, atomType) {
|
||||||
|
// Everything else is assumed to be an object...
|
||||||
|
switch (tree.type) {
|
||||||
|
case "accent":
|
||||||
|
{
|
||||||
|
buildRegion(a11yStrings, function (a11yStrings) {
|
||||||
|
buildA11yStrings(tree.base, a11yStrings, atomType);
|
||||||
|
a11yStrings.push("with");
|
||||||
|
buildString(tree.label, "normal", a11yStrings);
|
||||||
|
a11yStrings.push("on top");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "accentUnder":
|
||||||
|
{
|
||||||
|
buildRegion(a11yStrings, function (a11yStrings) {
|
||||||
|
buildA11yStrings(tree.base, a11yStrings, atomType);
|
||||||
|
a11yStrings.push("with");
|
||||||
|
buildString(accentUnderMap[tree.label], "normal", a11yStrings);
|
||||||
|
a11yStrings.push("underneath");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "accent-token":
|
||||||
|
{
|
||||||
|
// Used internally by accent symbols.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "atom":
|
||||||
|
{
|
||||||
|
var text = tree.text;
|
||||||
|
|
||||||
|
switch (tree.family) {
|
||||||
|
case "bin":
|
||||||
|
{
|
||||||
|
buildString(text, "bin", a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "close":
|
||||||
|
{
|
||||||
|
buildString(text, "close", a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// TODO(kevinb): figure out what should be done for inner
|
||||||
|
|
||||||
|
case "inner":
|
||||||
|
{
|
||||||
|
buildString(tree.text, "inner", a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "open":
|
||||||
|
{
|
||||||
|
buildString(text, "open", a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "punct":
|
||||||
|
{
|
||||||
|
buildString(text, "punct", a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "rel":
|
||||||
|
{
|
||||||
|
buildString(text, "rel", a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
tree.family;
|
||||||
|
throw new Error("\"" + tree.family + "\" is not a valid atom type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "color":
|
||||||
|
{
|
||||||
|
var color = tree.color.replace(/katex-/, "");
|
||||||
|
buildRegion(a11yStrings, function (regionStrings) {
|
||||||
|
regionStrings.push("start color " + color);
|
||||||
|
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||||
|
regionStrings.push("end color " + color);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "color-token":
|
||||||
|
{
|
||||||
|
// Used by \color, \colorbox, and \fcolorbox but not directly rendered.
|
||||||
|
// It's a leaf node and has no children so just break.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "delimsizing":
|
||||||
|
{
|
||||||
|
if (tree.delim && tree.delim !== ".") {
|
||||||
|
buildString(tree.delim, "normal", a11yStrings);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "genfrac":
|
||||||
|
{
|
||||||
|
buildRegion(a11yStrings, function (regionStrings) {
|
||||||
|
// genfrac can have unbalanced delimiters
|
||||||
|
var leftDelim = tree.leftDelim,
|
||||||
|
rightDelim = tree.rightDelim; // NOTE: Not sure if this is a safe assumption
|
||||||
|
// hasBarLine true -> fraction, false -> binomial
|
||||||
|
|
||||||
|
if (tree.hasBarLine) {
|
||||||
|
regionStrings.push("start fraction");
|
||||||
|
leftDelim && buildString(leftDelim, "open", regionStrings);
|
||||||
|
buildA11yStrings(tree.numer, regionStrings, atomType);
|
||||||
|
regionStrings.push("divided by");
|
||||||
|
buildA11yStrings(tree.denom, regionStrings, atomType);
|
||||||
|
rightDelim && buildString(rightDelim, "close", regionStrings);
|
||||||
|
regionStrings.push("end fraction");
|
||||||
|
} else {
|
||||||
|
regionStrings.push("start binomial");
|
||||||
|
leftDelim && buildString(leftDelim, "open", regionStrings);
|
||||||
|
buildA11yStrings(tree.numer, regionStrings, atomType);
|
||||||
|
regionStrings.push("over");
|
||||||
|
buildA11yStrings(tree.denom, regionStrings, atomType);
|
||||||
|
rightDelim && buildString(rightDelim, "close", regionStrings);
|
||||||
|
regionStrings.push("end binomial");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "hbox":
|
||||||
|
{
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "kern":
|
||||||
|
{
|
||||||
|
// No op: we don't attempt to present kerning information
|
||||||
|
// to the screen reader.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "leftright":
|
||||||
|
{
|
||||||
|
buildRegion(a11yStrings, function (regionStrings) {
|
||||||
|
buildString(tree.left, "open", regionStrings);
|
||||||
|
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||||
|
buildString(tree.right, "close", regionStrings);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "leftright-right":
|
||||||
|
{
|
||||||
|
// TODO: double check that this is a no-op
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "lap":
|
||||||
|
{
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "mathord":
|
||||||
|
{
|
||||||
|
buildString(tree.text, "normal", a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "op":
|
||||||
|
{
|
||||||
|
var body = tree.body,
|
||||||
|
name = tree.name;
|
||||||
|
|
||||||
|
if (body) {
|
||||||
|
buildA11yStrings(body, a11yStrings, atomType);
|
||||||
|
} else if (name) {
|
||||||
|
buildString(name, "normal", a11yStrings);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "op-token":
|
||||||
|
{
|
||||||
|
// Used internally by operator symbols.
|
||||||
|
buildString(tree.text, atomType, a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "ordgroup":
|
||||||
|
{
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "overline":
|
||||||
|
{
|
||||||
|
buildRegion(a11yStrings, function (a11yStrings) {
|
||||||
|
a11yStrings.push("start overline");
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
a11yStrings.push("end overline");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "phantom":
|
||||||
|
{
|
||||||
|
a11yStrings.push("empty space");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "raisebox":
|
||||||
|
{
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "rule":
|
||||||
|
{
|
||||||
|
a11yStrings.push("rectangle");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "sizing":
|
||||||
|
{
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "spacing":
|
||||||
|
{
|
||||||
|
a11yStrings.push("space");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "styling":
|
||||||
|
{
|
||||||
|
// We ignore the styling and just pass through the contents
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "sqrt":
|
||||||
|
{
|
||||||
|
buildRegion(a11yStrings, function (regionStrings) {
|
||||||
|
var body = tree.body,
|
||||||
|
index = tree.index;
|
||||||
|
|
||||||
|
if (index) {
|
||||||
|
var indexString = flatten(buildA11yStrings(index, [], atomType)).join(",");
|
||||||
|
|
||||||
|
if (indexString === "3") {
|
||||||
|
regionStrings.push("cube root of");
|
||||||
|
buildA11yStrings(body, regionStrings, atomType);
|
||||||
|
regionStrings.push("end cube root");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
regionStrings.push("root");
|
||||||
|
regionStrings.push("start index");
|
||||||
|
buildA11yStrings(index, regionStrings, atomType);
|
||||||
|
regionStrings.push("end index");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
regionStrings.push("square root of");
|
||||||
|
buildA11yStrings(body, regionStrings, atomType);
|
||||||
|
regionStrings.push("end square root");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "supsub":
|
||||||
|
{
|
||||||
|
var base = tree.base,
|
||||||
|
sub = tree.sub,
|
||||||
|
sup = tree.sup;
|
||||||
|
var isLog = false;
|
||||||
|
|
||||||
|
if (base) {
|
||||||
|
buildA11yStrings(base, a11yStrings, atomType);
|
||||||
|
isLog = base.type === "op" && base.name === "\\log";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sub) {
|
||||||
|
var regionName = isLog ? "base" : "subscript";
|
||||||
|
buildRegion(a11yStrings, function (regionStrings) {
|
||||||
|
regionStrings.push("start " + regionName);
|
||||||
|
buildA11yStrings(sub, regionStrings, atomType);
|
||||||
|
regionStrings.push("end " + regionName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sup) {
|
||||||
|
buildRegion(a11yStrings, function (regionStrings) {
|
||||||
|
var supString = flatten(buildA11yStrings(sup, [], atomType)).join(",");
|
||||||
|
|
||||||
|
if (supString in powerMap) {
|
||||||
|
regionStrings.push(powerMap[supString]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
regionStrings.push("start superscript");
|
||||||
|
buildA11yStrings(sup, regionStrings, atomType);
|
||||||
|
regionStrings.push("end superscript");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "text":
|
||||||
|
{
|
||||||
|
// TODO: handle other fonts
|
||||||
|
if (tree.font === "\\textbf") {
|
||||||
|
buildRegion(a11yStrings, function (regionStrings) {
|
||||||
|
regionStrings.push("start bold text");
|
||||||
|
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||||
|
regionStrings.push("end bold text");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
buildRegion(a11yStrings, function (regionStrings) {
|
||||||
|
regionStrings.push("start text");
|
||||||
|
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||||
|
regionStrings.push("end text");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "textord":
|
||||||
|
{
|
||||||
|
buildString(tree.text, atomType, a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "smash":
|
||||||
|
{
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "enclose":
|
||||||
|
{
|
||||||
|
// TODO: create a map for these.
|
||||||
|
// TODO: differentiate between a body with a single atom, e.g.
|
||||||
|
// "cancel a" instead of "start cancel, a, end cancel"
|
||||||
|
if (/cancel/.test(tree.label)) {
|
||||||
|
buildRegion(a11yStrings, function (regionStrings) {
|
||||||
|
regionStrings.push("start cancel");
|
||||||
|
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||||
|
regionStrings.push("end cancel");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
} else if (/box/.test(tree.label)) {
|
||||||
|
buildRegion(a11yStrings, function (regionStrings) {
|
||||||
|
regionStrings.push("start box");
|
||||||
|
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||||
|
regionStrings.push("end box");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
} else if (/sout/.test(tree.label)) {
|
||||||
|
buildRegion(a11yStrings, function (regionStrings) {
|
||||||
|
regionStrings.push("start strikeout");
|
||||||
|
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||||
|
regionStrings.push("end strikeout");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
} else if (/phase/.test(tree.label)) {
|
||||||
|
buildRegion(a11yStrings, function (regionStrings) {
|
||||||
|
regionStrings.push("start phase angle");
|
||||||
|
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||||
|
regionStrings.push("end phase angle");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("KaTeX-a11y: enclose node with " + tree.label + " not supported yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "vcenter":
|
||||||
|
{
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "vphantom":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: vphantom not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "hphantom":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: hphantom not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "operatorname":
|
||||||
|
{
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "array":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: array not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "raw":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: raw not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "size":
|
||||||
|
{
|
||||||
|
// Although there are nodes of type "size" in the parse tree, they have
|
||||||
|
// no semantic meaning and should be ignored.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "url":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: url not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "tag":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: tag not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "verb":
|
||||||
|
{
|
||||||
|
buildString("start verbatim", "normal", a11yStrings);
|
||||||
|
buildString(tree.body, "normal", a11yStrings);
|
||||||
|
buildString("end verbatim", "normal", a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "environment":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: environment not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "horizBrace":
|
||||||
|
{
|
||||||
|
buildString("start " + tree.label.slice(1), "normal", a11yStrings);
|
||||||
|
buildA11yStrings(tree.base, a11yStrings, atomType);
|
||||||
|
buildString("end " + tree.label.slice(1), "normal", a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "infix":
|
||||||
|
{
|
||||||
|
// All infix nodes are replace with other nodes.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "includegraphics":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: includegraphics not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "font":
|
||||||
|
{
|
||||||
|
// TODO: callout the start/end of specific fonts
|
||||||
|
// TODO: map \BBb{N} to "the naturals" or something like that
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "href":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: href not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "cr":
|
||||||
|
{
|
||||||
|
// This is used by environments.
|
||||||
|
throw new Error("KaTeX-a11y: cr not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "underline":
|
||||||
|
{
|
||||||
|
buildRegion(a11yStrings, function (a11yStrings) {
|
||||||
|
a11yStrings.push("start underline");
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
a11yStrings.push("end underline");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "xArrow":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: xArrow not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "cdlabel":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: cdlabel not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "cdlabelparent":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: cdlabelparent not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "mclass":
|
||||||
|
{
|
||||||
|
// \neq and \ne are macros so we let "htmlmathml" render the mathmal
|
||||||
|
// side of things and extract the text from that.
|
||||||
|
var _atomType = tree.mclass.slice(1); // $FlowFixMe: drop the leading "m" from the values in mclass
|
||||||
|
|
||||||
|
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, _atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "mathchoice":
|
||||||
|
{
|
||||||
|
// TODO: track which which style we're using, e.g. dispaly, text, etc.
|
||||||
|
// default to text style if even that may not be the correct style
|
||||||
|
buildA11yStrings(tree.text, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "htmlmathml":
|
||||||
|
{
|
||||||
|
buildA11yStrings(tree.mathml, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "middle":
|
||||||
|
{
|
||||||
|
buildString(tree.delim, atomType, a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "internal":
|
||||||
|
{
|
||||||
|
// internal nodes are never included in the parse tree
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "html":
|
||||||
|
{
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
tree.type;
|
||||||
|
throw new Error("KaTeX a11y un-recognized type: " + tree.type);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var buildA11yStrings = function buildA11yStrings(tree, a11yStrings, atomType) {
|
||||||
|
if (a11yStrings === void 0) {
|
||||||
|
a11yStrings = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tree instanceof Array) {
|
||||||
|
for (var i = 0; i < tree.length; i++) {
|
||||||
|
buildA11yStrings(tree[i], a11yStrings, atomType);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handleObject(tree, a11yStrings, atomType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return a11yStrings;
|
||||||
|
};
|
||||||
|
|
||||||
|
var flatten = function flatten(array) {
|
||||||
|
var result = [];
|
||||||
|
array.forEach(function (item) {
|
||||||
|
if (item instanceof Array) {
|
||||||
|
result = result.concat(flatten(item));
|
||||||
|
} else {
|
||||||
|
result.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
var renderA11yString = function renderA11yString(text, settings) {
|
||||||
|
var tree = katex__WEBPACK_IMPORTED_MODULE_0___default().__parse(text, settings);
|
||||||
|
|
||||||
|
var a11yStrings = buildA11yStrings(tree, [], "normal");
|
||||||
|
return flatten(a11yStrings).join(", ");
|
||||||
|
};
|
||||||
|
|
||||||
|
/* harmony default export */ __webpack_exports__["default"] = (renderA11yString);
|
||||||
|
}();
|
||||||
|
__webpack_exports__ = __webpack_exports__["default"];
|
||||||
|
/******/ return __webpack_exports__;
|
||||||
|
/******/ })()
|
||||||
|
;
|
||||||
|
});
|
1
public/katex/contrib/render-a11y-string.min.js
vendored
Normal file
1
public/katex/contrib/render-a11y-string.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
794
public/katex/contrib/render-a11y-string.mjs
vendored
Normal file
794
public/katex/contrib/render-a11y-string.mjs
vendored
Normal file
|
@ -0,0 +1,794 @@
|
||||||
|
import katex from '../katex.mjs';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* renderA11yString returns a readable string.
|
||||||
|
*
|
||||||
|
* In some cases the string will have the proper semantic math
|
||||||
|
* meaning,:
|
||||||
|
* renderA11yString("\\frac{1}{2}"")
|
||||||
|
* -> "start fraction, 1, divided by, 2, end fraction"
|
||||||
|
*
|
||||||
|
* However, other cases do not:
|
||||||
|
* renderA11yString("f(x) = x^2")
|
||||||
|
* -> "f, left parenthesis, x, right parenthesis, equals, x, squared"
|
||||||
|
*
|
||||||
|
* The commas in the string aim to increase ease of understanding
|
||||||
|
* when read by a screenreader.
|
||||||
|
*/
|
||||||
|
var stringMap = {
|
||||||
|
"(": "left parenthesis",
|
||||||
|
")": "right parenthesis",
|
||||||
|
"[": "open bracket",
|
||||||
|
"]": "close bracket",
|
||||||
|
"\\{": "left brace",
|
||||||
|
"\\}": "right brace",
|
||||||
|
"\\lvert": "open vertical bar",
|
||||||
|
"\\rvert": "close vertical bar",
|
||||||
|
"|": "vertical bar",
|
||||||
|
"\\uparrow": "up arrow",
|
||||||
|
"\\Uparrow": "up arrow",
|
||||||
|
"\\downarrow": "down arrow",
|
||||||
|
"\\Downarrow": "down arrow",
|
||||||
|
"\\updownarrow": "up down arrow",
|
||||||
|
"\\leftarrow": "left arrow",
|
||||||
|
"\\Leftarrow": "left arrow",
|
||||||
|
"\\rightarrow": "right arrow",
|
||||||
|
"\\Rightarrow": "right arrow",
|
||||||
|
"\\langle": "open angle",
|
||||||
|
"\\rangle": "close angle",
|
||||||
|
"\\lfloor": "open floor",
|
||||||
|
"\\rfloor": "close floor",
|
||||||
|
"\\int": "integral",
|
||||||
|
"\\intop": "integral",
|
||||||
|
"\\lim": "limit",
|
||||||
|
"\\ln": "natural log",
|
||||||
|
"\\log": "log",
|
||||||
|
"\\sin": "sine",
|
||||||
|
"\\cos": "cosine",
|
||||||
|
"\\tan": "tangent",
|
||||||
|
"\\cot": "cotangent",
|
||||||
|
"\\sum": "sum",
|
||||||
|
"/": "slash",
|
||||||
|
",": "comma",
|
||||||
|
".": "point",
|
||||||
|
"-": "negative",
|
||||||
|
"+": "plus",
|
||||||
|
"~": "tilde",
|
||||||
|
":": "colon",
|
||||||
|
"?": "question mark",
|
||||||
|
"'": "apostrophe",
|
||||||
|
"\\%": "percent",
|
||||||
|
" ": "space",
|
||||||
|
"\\ ": "space",
|
||||||
|
"\\$": "dollar sign",
|
||||||
|
"\\angle": "angle",
|
||||||
|
"\\degree": "degree",
|
||||||
|
"\\circ": "circle",
|
||||||
|
"\\vec": "vector",
|
||||||
|
"\\triangle": "triangle",
|
||||||
|
"\\pi": "pi",
|
||||||
|
"\\prime": "prime",
|
||||||
|
"\\infty": "infinity",
|
||||||
|
"\\alpha": "alpha",
|
||||||
|
"\\beta": "beta",
|
||||||
|
"\\gamma": "gamma",
|
||||||
|
"\\omega": "omega",
|
||||||
|
"\\theta": "theta",
|
||||||
|
"\\sigma": "sigma",
|
||||||
|
"\\lambda": "lambda",
|
||||||
|
"\\tau": "tau",
|
||||||
|
"\\Delta": "delta",
|
||||||
|
"\\delta": "delta",
|
||||||
|
"\\mu": "mu",
|
||||||
|
"\\rho": "rho",
|
||||||
|
"\\nabla": "del",
|
||||||
|
"\\ell": "ell",
|
||||||
|
"\\ldots": "dots",
|
||||||
|
// TODO: add entries for all accents
|
||||||
|
"\\hat": "hat",
|
||||||
|
"\\acute": "acute"
|
||||||
|
};
|
||||||
|
var powerMap = {
|
||||||
|
"prime": "prime",
|
||||||
|
"degree": "degrees",
|
||||||
|
"circle": "degrees",
|
||||||
|
"2": "squared",
|
||||||
|
"3": "cubed"
|
||||||
|
};
|
||||||
|
var openMap = {
|
||||||
|
"|": "open vertical bar",
|
||||||
|
".": ""
|
||||||
|
};
|
||||||
|
var closeMap = {
|
||||||
|
"|": "close vertical bar",
|
||||||
|
".": ""
|
||||||
|
};
|
||||||
|
var binMap = {
|
||||||
|
"+": "plus",
|
||||||
|
"-": "minus",
|
||||||
|
"\\pm": "plus minus",
|
||||||
|
"\\cdot": "dot",
|
||||||
|
"*": "times",
|
||||||
|
"/": "divided by",
|
||||||
|
"\\times": "times",
|
||||||
|
"\\div": "divided by",
|
||||||
|
"\\circ": "circle",
|
||||||
|
"\\bullet": "bullet"
|
||||||
|
};
|
||||||
|
var relMap = {
|
||||||
|
"=": "equals",
|
||||||
|
"\\approx": "approximately equals",
|
||||||
|
"≠": "does not equal",
|
||||||
|
"\\geq": "is greater than or equal to",
|
||||||
|
"\\ge": "is greater than or equal to",
|
||||||
|
"\\leq": "is less than or equal to",
|
||||||
|
"\\le": "is less than or equal to",
|
||||||
|
">": "is greater than",
|
||||||
|
"<": "is less than",
|
||||||
|
"\\leftarrow": "left arrow",
|
||||||
|
"\\Leftarrow": "left arrow",
|
||||||
|
"\\rightarrow": "right arrow",
|
||||||
|
"\\Rightarrow": "right arrow",
|
||||||
|
":": "colon"
|
||||||
|
};
|
||||||
|
var accentUnderMap = {
|
||||||
|
"\\underleftarrow": "left arrow",
|
||||||
|
"\\underrightarrow": "right arrow",
|
||||||
|
"\\underleftrightarrow": "left-right arrow",
|
||||||
|
"\\undergroup": "group",
|
||||||
|
"\\underlinesegment": "line segment",
|
||||||
|
"\\utilde": "tilde"
|
||||||
|
};
|
||||||
|
|
||||||
|
var buildString = (str, type, a11yStrings) => {
|
||||||
|
if (!str) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ret;
|
||||||
|
|
||||||
|
if (type === "open") {
|
||||||
|
ret = str in openMap ? openMap[str] : stringMap[str] || str;
|
||||||
|
} else if (type === "close") {
|
||||||
|
ret = str in closeMap ? closeMap[str] : stringMap[str] || str;
|
||||||
|
} else if (type === "bin") {
|
||||||
|
ret = binMap[str] || str;
|
||||||
|
} else if (type === "rel") {
|
||||||
|
ret = relMap[str] || str;
|
||||||
|
} else {
|
||||||
|
ret = stringMap[str] || str;
|
||||||
|
} // If the text to add is a number and there is already a string
|
||||||
|
// in the list and the last string is a number then we should
|
||||||
|
// combine them into a single number
|
||||||
|
|
||||||
|
|
||||||
|
if (/^\d+$/.test(ret) && a11yStrings.length > 0 && // TODO(kevinb): check that the last item in a11yStrings is a string
|
||||||
|
// I think we might be able to drop the nested arrays, which would make
|
||||||
|
// this easier to type
|
||||||
|
// $FlowFixMe
|
||||||
|
/^\d+$/.test(a11yStrings[a11yStrings.length - 1])) {
|
||||||
|
a11yStrings[a11yStrings.length - 1] += ret;
|
||||||
|
} else if (ret) {
|
||||||
|
a11yStrings.push(ret);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var buildRegion = (a11yStrings, callback) => {
|
||||||
|
var regionStrings = [];
|
||||||
|
a11yStrings.push(regionStrings);
|
||||||
|
callback(regionStrings);
|
||||||
|
};
|
||||||
|
|
||||||
|
var handleObject = (tree, a11yStrings, atomType) => {
|
||||||
|
// Everything else is assumed to be an object...
|
||||||
|
switch (tree.type) {
|
||||||
|
case "accent":
|
||||||
|
{
|
||||||
|
buildRegion(a11yStrings, a11yStrings => {
|
||||||
|
buildA11yStrings(tree.base, a11yStrings, atomType);
|
||||||
|
a11yStrings.push("with");
|
||||||
|
buildString(tree.label, "normal", a11yStrings);
|
||||||
|
a11yStrings.push("on top");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "accentUnder":
|
||||||
|
{
|
||||||
|
buildRegion(a11yStrings, a11yStrings => {
|
||||||
|
buildA11yStrings(tree.base, a11yStrings, atomType);
|
||||||
|
a11yStrings.push("with");
|
||||||
|
buildString(accentUnderMap[tree.label], "normal", a11yStrings);
|
||||||
|
a11yStrings.push("underneath");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "accent-token":
|
||||||
|
{
|
||||||
|
// Used internally by accent symbols.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "atom":
|
||||||
|
{
|
||||||
|
var {
|
||||||
|
text
|
||||||
|
} = tree;
|
||||||
|
|
||||||
|
switch (tree.family) {
|
||||||
|
case "bin":
|
||||||
|
{
|
||||||
|
buildString(text, "bin", a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "close":
|
||||||
|
{
|
||||||
|
buildString(text, "close", a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// TODO(kevinb): figure out what should be done for inner
|
||||||
|
|
||||||
|
case "inner":
|
||||||
|
{
|
||||||
|
buildString(tree.text, "inner", a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "open":
|
||||||
|
{
|
||||||
|
buildString(text, "open", a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "punct":
|
||||||
|
{
|
||||||
|
buildString(text, "punct", a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "rel":
|
||||||
|
{
|
||||||
|
buildString(text, "rel", a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
tree.family;
|
||||||
|
throw new Error("\"" + tree.family + "\" is not a valid atom type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "color":
|
||||||
|
{
|
||||||
|
var color = tree.color.replace(/katex-/, "");
|
||||||
|
buildRegion(a11yStrings, regionStrings => {
|
||||||
|
regionStrings.push("start color " + color);
|
||||||
|
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||||
|
regionStrings.push("end color " + color);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "color-token":
|
||||||
|
{
|
||||||
|
// Used by \color, \colorbox, and \fcolorbox but not directly rendered.
|
||||||
|
// It's a leaf node and has no children so just break.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "delimsizing":
|
||||||
|
{
|
||||||
|
if (tree.delim && tree.delim !== ".") {
|
||||||
|
buildString(tree.delim, "normal", a11yStrings);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "genfrac":
|
||||||
|
{
|
||||||
|
buildRegion(a11yStrings, regionStrings => {
|
||||||
|
// genfrac can have unbalanced delimiters
|
||||||
|
var {
|
||||||
|
leftDelim,
|
||||||
|
rightDelim
|
||||||
|
} = tree; // NOTE: Not sure if this is a safe assumption
|
||||||
|
// hasBarLine true -> fraction, false -> binomial
|
||||||
|
|
||||||
|
if (tree.hasBarLine) {
|
||||||
|
regionStrings.push("start fraction");
|
||||||
|
leftDelim && buildString(leftDelim, "open", regionStrings);
|
||||||
|
buildA11yStrings(tree.numer, regionStrings, atomType);
|
||||||
|
regionStrings.push("divided by");
|
||||||
|
buildA11yStrings(tree.denom, regionStrings, atomType);
|
||||||
|
rightDelim && buildString(rightDelim, "close", regionStrings);
|
||||||
|
regionStrings.push("end fraction");
|
||||||
|
} else {
|
||||||
|
regionStrings.push("start binomial");
|
||||||
|
leftDelim && buildString(leftDelim, "open", regionStrings);
|
||||||
|
buildA11yStrings(tree.numer, regionStrings, atomType);
|
||||||
|
regionStrings.push("over");
|
||||||
|
buildA11yStrings(tree.denom, regionStrings, atomType);
|
||||||
|
rightDelim && buildString(rightDelim, "close", regionStrings);
|
||||||
|
regionStrings.push("end binomial");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "hbox":
|
||||||
|
{
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "kern":
|
||||||
|
{
|
||||||
|
// No op: we don't attempt to present kerning information
|
||||||
|
// to the screen reader.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "leftright":
|
||||||
|
{
|
||||||
|
buildRegion(a11yStrings, regionStrings => {
|
||||||
|
buildString(tree.left, "open", regionStrings);
|
||||||
|
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||||
|
buildString(tree.right, "close", regionStrings);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "leftright-right":
|
||||||
|
{
|
||||||
|
// TODO: double check that this is a no-op
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "lap":
|
||||||
|
{
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "mathord":
|
||||||
|
{
|
||||||
|
buildString(tree.text, "normal", a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "op":
|
||||||
|
{
|
||||||
|
var {
|
||||||
|
body,
|
||||||
|
name
|
||||||
|
} = tree;
|
||||||
|
|
||||||
|
if (body) {
|
||||||
|
buildA11yStrings(body, a11yStrings, atomType);
|
||||||
|
} else if (name) {
|
||||||
|
buildString(name, "normal", a11yStrings);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "op-token":
|
||||||
|
{
|
||||||
|
// Used internally by operator symbols.
|
||||||
|
buildString(tree.text, atomType, a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "ordgroup":
|
||||||
|
{
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "overline":
|
||||||
|
{
|
||||||
|
buildRegion(a11yStrings, function (a11yStrings) {
|
||||||
|
a11yStrings.push("start overline");
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
a11yStrings.push("end overline");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "phantom":
|
||||||
|
{
|
||||||
|
a11yStrings.push("empty space");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "raisebox":
|
||||||
|
{
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "rule":
|
||||||
|
{
|
||||||
|
a11yStrings.push("rectangle");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "sizing":
|
||||||
|
{
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "spacing":
|
||||||
|
{
|
||||||
|
a11yStrings.push("space");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "styling":
|
||||||
|
{
|
||||||
|
// We ignore the styling and just pass through the contents
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "sqrt":
|
||||||
|
{
|
||||||
|
buildRegion(a11yStrings, regionStrings => {
|
||||||
|
var {
|
||||||
|
body,
|
||||||
|
index
|
||||||
|
} = tree;
|
||||||
|
|
||||||
|
if (index) {
|
||||||
|
var indexString = flatten(buildA11yStrings(index, [], atomType)).join(",");
|
||||||
|
|
||||||
|
if (indexString === "3") {
|
||||||
|
regionStrings.push("cube root of");
|
||||||
|
buildA11yStrings(body, regionStrings, atomType);
|
||||||
|
regionStrings.push("end cube root");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
regionStrings.push("root");
|
||||||
|
regionStrings.push("start index");
|
||||||
|
buildA11yStrings(index, regionStrings, atomType);
|
||||||
|
regionStrings.push("end index");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
regionStrings.push("square root of");
|
||||||
|
buildA11yStrings(body, regionStrings, atomType);
|
||||||
|
regionStrings.push("end square root");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "supsub":
|
||||||
|
{
|
||||||
|
var {
|
||||||
|
base,
|
||||||
|
sub,
|
||||||
|
sup
|
||||||
|
} = tree;
|
||||||
|
var isLog = false;
|
||||||
|
|
||||||
|
if (base) {
|
||||||
|
buildA11yStrings(base, a11yStrings, atomType);
|
||||||
|
isLog = base.type === "op" && base.name === "\\log";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sub) {
|
||||||
|
var regionName = isLog ? "base" : "subscript";
|
||||||
|
buildRegion(a11yStrings, function (regionStrings) {
|
||||||
|
regionStrings.push("start " + regionName);
|
||||||
|
buildA11yStrings(sub, regionStrings, atomType);
|
||||||
|
regionStrings.push("end " + regionName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sup) {
|
||||||
|
buildRegion(a11yStrings, function (regionStrings) {
|
||||||
|
var supString = flatten(buildA11yStrings(sup, [], atomType)).join(",");
|
||||||
|
|
||||||
|
if (supString in powerMap) {
|
||||||
|
regionStrings.push(powerMap[supString]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
regionStrings.push("start superscript");
|
||||||
|
buildA11yStrings(sup, regionStrings, atomType);
|
||||||
|
regionStrings.push("end superscript");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "text":
|
||||||
|
{
|
||||||
|
// TODO: handle other fonts
|
||||||
|
if (tree.font === "\\textbf") {
|
||||||
|
buildRegion(a11yStrings, function (regionStrings) {
|
||||||
|
regionStrings.push("start bold text");
|
||||||
|
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||||
|
regionStrings.push("end bold text");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
buildRegion(a11yStrings, function (regionStrings) {
|
||||||
|
regionStrings.push("start text");
|
||||||
|
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||||
|
regionStrings.push("end text");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "textord":
|
||||||
|
{
|
||||||
|
buildString(tree.text, atomType, a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "smash":
|
||||||
|
{
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "enclose":
|
||||||
|
{
|
||||||
|
// TODO: create a map for these.
|
||||||
|
// TODO: differentiate between a body with a single atom, e.g.
|
||||||
|
// "cancel a" instead of "start cancel, a, end cancel"
|
||||||
|
if (/cancel/.test(tree.label)) {
|
||||||
|
buildRegion(a11yStrings, function (regionStrings) {
|
||||||
|
regionStrings.push("start cancel");
|
||||||
|
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||||
|
regionStrings.push("end cancel");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
} else if (/box/.test(tree.label)) {
|
||||||
|
buildRegion(a11yStrings, function (regionStrings) {
|
||||||
|
regionStrings.push("start box");
|
||||||
|
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||||
|
regionStrings.push("end box");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
} else if (/sout/.test(tree.label)) {
|
||||||
|
buildRegion(a11yStrings, function (regionStrings) {
|
||||||
|
regionStrings.push("start strikeout");
|
||||||
|
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||||
|
regionStrings.push("end strikeout");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
} else if (/phase/.test(tree.label)) {
|
||||||
|
buildRegion(a11yStrings, function (regionStrings) {
|
||||||
|
regionStrings.push("start phase angle");
|
||||||
|
buildA11yStrings(tree.body, regionStrings, atomType);
|
||||||
|
regionStrings.push("end phase angle");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("KaTeX-a11y: enclose node with " + tree.label + " not supported yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "vcenter":
|
||||||
|
{
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "vphantom":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: vphantom not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "hphantom":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: hphantom not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "operatorname":
|
||||||
|
{
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "array":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: array not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "raw":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: raw not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "size":
|
||||||
|
{
|
||||||
|
// Although there are nodes of type "size" in the parse tree, they have
|
||||||
|
// no semantic meaning and should be ignored.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "url":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: url not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "tag":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: tag not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "verb":
|
||||||
|
{
|
||||||
|
buildString("start verbatim", "normal", a11yStrings);
|
||||||
|
buildString(tree.body, "normal", a11yStrings);
|
||||||
|
buildString("end verbatim", "normal", a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "environment":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: environment not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "horizBrace":
|
||||||
|
{
|
||||||
|
buildString("start " + tree.label.slice(1), "normal", a11yStrings);
|
||||||
|
buildA11yStrings(tree.base, a11yStrings, atomType);
|
||||||
|
buildString("end " + tree.label.slice(1), "normal", a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "infix":
|
||||||
|
{
|
||||||
|
// All infix nodes are replace with other nodes.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "includegraphics":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: includegraphics not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "font":
|
||||||
|
{
|
||||||
|
// TODO: callout the start/end of specific fonts
|
||||||
|
// TODO: map \BBb{N} to "the naturals" or something like that
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "href":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: href not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "cr":
|
||||||
|
{
|
||||||
|
// This is used by environments.
|
||||||
|
throw new Error("KaTeX-a11y: cr not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "underline":
|
||||||
|
{
|
||||||
|
buildRegion(a11yStrings, function (a11yStrings) {
|
||||||
|
a11yStrings.push("start underline");
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
a11yStrings.push("end underline");
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "xArrow":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: xArrow not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "cdlabel":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: cdlabel not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "cdlabelparent":
|
||||||
|
{
|
||||||
|
throw new Error("KaTeX-a11y: cdlabelparent not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
case "mclass":
|
||||||
|
{
|
||||||
|
// \neq and \ne are macros so we let "htmlmathml" render the mathmal
|
||||||
|
// side of things and extract the text from that.
|
||||||
|
var _atomType = tree.mclass.slice(1); // $FlowFixMe: drop the leading "m" from the values in mclass
|
||||||
|
|
||||||
|
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, _atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "mathchoice":
|
||||||
|
{
|
||||||
|
// TODO: track which which style we're using, e.g. dispaly, text, etc.
|
||||||
|
// default to text style if even that may not be the correct style
|
||||||
|
buildA11yStrings(tree.text, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "htmlmathml":
|
||||||
|
{
|
||||||
|
buildA11yStrings(tree.mathml, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "middle":
|
||||||
|
{
|
||||||
|
buildString(tree.delim, atomType, a11yStrings);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "internal":
|
||||||
|
{
|
||||||
|
// internal nodes are never included in the parse tree
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "html":
|
||||||
|
{
|
||||||
|
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
tree.type;
|
||||||
|
throw new Error("KaTeX a11y un-recognized type: " + tree.type);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var buildA11yStrings = function buildA11yStrings(tree, a11yStrings, atomType) {
|
||||||
|
if (a11yStrings === void 0) {
|
||||||
|
a11yStrings = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tree instanceof Array) {
|
||||||
|
for (var i = 0; i < tree.length; i++) {
|
||||||
|
buildA11yStrings(tree[i], a11yStrings, atomType);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handleObject(tree, a11yStrings, atomType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return a11yStrings;
|
||||||
|
};
|
||||||
|
|
||||||
|
var flatten = function flatten(array) {
|
||||||
|
var result = [];
|
||||||
|
array.forEach(function (item) {
|
||||||
|
if (item instanceof Array) {
|
||||||
|
result = result.concat(flatten(item));
|
||||||
|
} else {
|
||||||
|
result.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
var renderA11yString = function renderA11yString(text, settings) {
|
||||||
|
var tree = katex.__parse(text, settings);
|
||||||
|
|
||||||
|
var a11yStrings = buildA11yStrings(tree, [], "normal");
|
||||||
|
return flatten(a11yStrings).join(", ");
|
||||||
|
};
|
||||||
|
|
||||||
|
export { renderA11yString as default };
|
BIN
public/katex/fonts/KaTeX_AMS-Regular.ttf
vendored
Normal file
BIN
public/katex/fonts/KaTeX_AMS-Regular.ttf
vendored
Normal file
Binary file not shown.
BIN
public/katex/fonts/KaTeX_AMS-Regular.woff
vendored
Normal file
BIN
public/katex/fonts/KaTeX_AMS-Regular.woff
vendored
Normal file
Binary file not shown.
BIN
public/katex/fonts/KaTeX_AMS-Regular.woff2
vendored
Normal file
BIN
public/katex/fonts/KaTeX_AMS-Regular.woff2
vendored
Normal file
Binary file not shown.
BIN
public/katex/fonts/KaTeX_Caligraphic-Bold.ttf
vendored
Normal file
BIN
public/katex/fonts/KaTeX_Caligraphic-Bold.ttf
vendored
Normal file
Binary file not shown.
BIN
public/katex/fonts/KaTeX_Caligraphic-Bold.woff
vendored
Normal file
BIN
public/katex/fonts/KaTeX_Caligraphic-Bold.woff
vendored
Normal file
Binary file not shown.
BIN
public/katex/fonts/KaTeX_Caligraphic-Bold.woff2
vendored
Normal file
BIN
public/katex/fonts/KaTeX_Caligraphic-Bold.woff2
vendored
Normal file
Binary file not shown.
BIN
public/katex/fonts/KaTeX_Caligraphic-Regular.ttf
vendored
Normal file
BIN
public/katex/fonts/KaTeX_Caligraphic-Regular.ttf
vendored
Normal file
Binary file not shown.
BIN
public/katex/fonts/KaTeX_Caligraphic-Regular.woff
vendored
Normal file
BIN
public/katex/fonts/KaTeX_Caligraphic-Regular.woff
vendored
Normal file
Binary file not shown.
BIN
public/katex/fonts/KaTeX_Caligraphic-Regular.woff2
vendored
Normal file
BIN
public/katex/fonts/KaTeX_Caligraphic-Regular.woff2
vendored
Normal file
Binary file not shown.
BIN
public/katex/fonts/KaTeX_Fraktur-Bold.ttf
vendored
Normal file
BIN
public/katex/fonts/KaTeX_Fraktur-Bold.ttf
vendored
Normal file
Binary file not shown.
BIN
public/katex/fonts/KaTeX_Fraktur-Bold.woff
vendored
Normal file
BIN
public/katex/fonts/KaTeX_Fraktur-Bold.woff
vendored
Normal file
Binary file not shown.
BIN
public/katex/fonts/KaTeX_Fraktur-Bold.woff2
vendored
Normal file
BIN
public/katex/fonts/KaTeX_Fraktur-Bold.woff2
vendored
Normal file
Binary file not shown.
BIN
public/katex/fonts/KaTeX_Fraktur-Regular.ttf
vendored
Normal file
BIN
public/katex/fonts/KaTeX_Fraktur-Regular.ttf
vendored
Normal file
Binary file not shown.
BIN
public/katex/fonts/KaTeX_Fraktur-Regular.woff
vendored
Normal file
BIN
public/katex/fonts/KaTeX_Fraktur-Regular.woff
vendored
Normal file
Binary file not shown.
BIN
public/katex/fonts/KaTeX_Fraktur-Regular.woff2
vendored
Normal file
BIN
public/katex/fonts/KaTeX_Fraktur-Regular.woff2
vendored
Normal file
Binary file not shown.
BIN
public/katex/fonts/KaTeX_Main-Bold.ttf
vendored
Normal file
BIN
public/katex/fonts/KaTeX_Main-Bold.ttf
vendored
Normal file
Binary file not shown.
BIN
public/katex/fonts/KaTeX_Main-Bold.woff
vendored
Normal file
BIN
public/katex/fonts/KaTeX_Main-Bold.woff
vendored
Normal file
Binary file not shown.
BIN
public/katex/fonts/KaTeX_Main-Bold.woff2
vendored
Normal file
BIN
public/katex/fonts/KaTeX_Main-Bold.woff2
vendored
Normal file
Binary file not shown.
BIN
public/katex/fonts/KaTeX_Main-BoldItalic.ttf
vendored
Normal file
BIN
public/katex/fonts/KaTeX_Main-BoldItalic.ttf
vendored
Normal file
Binary file not shown.
BIN
public/katex/fonts/KaTeX_Main-BoldItalic.woff
vendored
Normal file
BIN
public/katex/fonts/KaTeX_Main-BoldItalic.woff
vendored
Normal file
Binary file not shown.
BIN
public/katex/fonts/KaTeX_Main-BoldItalic.woff2
vendored
Normal file
BIN
public/katex/fonts/KaTeX_Main-BoldItalic.woff2
vendored
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue