Compare commits

..

392 commits

Author SHA1 Message Date
d9289b88b2 use utf-8
All checks were successful
ci/woodpecker/push/deploy Pipeline was successful
2024-12-17 16:50:57 -06:00
6e82102c1b crdt post
All checks were successful
ci/woodpecker/push/deploy Pipeline was successful
2024-12-17 16:38:38 -06:00
9c8c3588c1 update shortBio
All checks were successful
ci/woodpecker/push/deploy Pipeline was successful
2024-12-06 17:40:22 -06:00
1bfabbc5be don't console.log in rss.xml 2024-12-02 05:36:33 -06:00
4dbbd79b06 update left nav style so the title isn't so small on mobile
All checks were successful
ci/woodpecker/push/deploy Pipeline was successful
2024-12-02 05:28:38 -06:00
ac672a54cf aoc language post 2024-12-02 05:26:45 -06:00
9f6665b2b1 about page
All checks were successful
ci/woodpecker/push/deploy Pipeline was successful
2024-11-27 17:38:34 -05:00
2973f40c00 dot color
All checks were successful
ci/woodpecker/push/deploy Pipeline was successful
2024-10-28 22:49:36 -05:00
2745f390d9 main tag
All checks were successful
ci/woodpecker/push/deploy Pipeline was successful
2024-10-22 22:21:13 -05:00
39911006a0 update lockfile
All checks were successful
ci/woodpecker/push/deploy Pipeline was successful
2024-10-22 22:12:54 -05:00
8f8e2bb4bd update rss
Some checks failed
ci/woodpecker/push/deploy Pipeline failed
2024-10-22 22:10:58 -05:00
c2510fea83 move woodpecker file
All checks were successful
ci/woodpecker/push/deploy Pipeline was successful
2024-10-22 21:50:32 -05:00
4e485b29fe add rss feed here 2024-10-22 21:42:10 -05:00
d68bbbaae6 update site title for rss 2024-10-22 21:38:57 -05:00
673d86215b fix link in rss 2024-10-22 21:36:58 -05:00
michael
e1d0bb6d33 nix builder changes
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-10-21 06:42:20 -05:00
3e1745f215 forgot to include git ooops
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-10-21 06:39:05 -05:00
2d0316adee update footer to include hash
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2024-10-21 06:34:52 -05:00
ee867ae783 update for awscli woodpecker
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-10-21 06:14:57 -05:00
f1b8136785 make the tone less doomer 2024-10-21 05:49:15 -05:00
michael
87107d3a29 typo
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-10-21 05:39:50 -05:00
michael
d2a3493aee update docker builder to include aws cli
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2024-10-21 05:37:32 -05:00
2496ba67f6 convert link
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-10-19 17:33:38 -05:00
2dc78d6aea don't index my drafts
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-10-19 17:13:56 -05:00
127a0ca75d upload talk post
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2024-10-19 17:12:39 -05:00
69d0f8a0b1 update github
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-10-15 15:50:17 -05:00
2e2fc8d729 fix headings
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-10-11 17:38:14 -05:00
9356908e0f update about page
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-10-11 17:36:05 -05:00
ea07b359a6 fix the emoji
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-10-08 12:42:34 -05:00
478c26313f reformat easyctf post
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-10-07 23:37:18 -05:00
096a7a1280 old blog posts
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-10-07 23:24:35 -05:00
702ab0e9f6 update footer to include last updated date
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-10-07 16:11:19 -05:00
67e042fc35 projects
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-10-07 15:30:49 -05:00
8dfb20c352 update links
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-10-07 15:28:28 -05:00
8667d33798 undraft
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-20 13:29:12 -05:00
b33d601900 update to use mathsf
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-20 13:18:22 -05:00
55ec917eb8 make stuff line up more
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-20 13:12:58 -05:00
d344924a1d make the title image redirect to about
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-20 12:53:27 -05:00
bc92b2184b grammar
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-19 17:31:38 -05:00
2d92966043 tentative dotted background
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-18 16:37:23 -05:00
4273a1ca0b add the j
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-18 16:27:15 -05:00
c9c6365c87 fix inner links
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-18 16:22:37 -05:00
67e9a31def initial draft done
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-18 16:05:13 -05:00
89abd4ff02 wip
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-18 15:31:29 -05:00
f166392f35 more blog post wip
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-18 14:43:34 -05:00
3e4f2d0095 image :)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-18 06:54:28 -05:00
79e8a2300a it works
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-18 06:38:08 -05:00
2f4c87ea1d shortbio 2024-09-18 06:32:18 -05:00
37d59e6927 more wip
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-18 06:01:37 -05:00
0dd553ad71 add diagrams
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-18 05:21:04 -05:00
71cf0079dc start hcomp post
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-18 04:47:52 -05:00
ce5ef4116d update bio
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-18 01:18:17 -05:00
aeef05a47f faq page wip
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-18 01:14:15 -05:00
198f3727e2 wip
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-16 03:06:17 -05:00
764351ccb1 rip
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-15 19:42:46 -05:00
16c5e3ab81 wip
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2024-09-15 19:38:05 -05:00
67bb9a196c Test rendering this page
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-15 18:31:24 -05:00
66de827e37 updates
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-14 23:57:48 -05:00
509fd14440 change background to be bluer
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-14 23:43:39 -05:00
55da20bde0 bio line height:
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-14 23:41:41 -05:00
9d073a0be6 update saturation
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-14 23:40:35 -05:00
126f3357bb update the warning box color
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-14 23:36:52 -05:00
4a72c2ea7b pijul post
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-10 17:59:54 -05:00
482189f355 lc post
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-10 06:31:00 -05:00
d3d9145610 some fixes
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-09-10 05:28:25 -05:00
772c46f77b update short bio
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-07-26 12:50:35 -05:00
69c17a7a7a new pic who dis
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-07-22 13:31:07 -05:00
3949f19f1f update short bio
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-07-09 16:16:58 -05:00
77b3323fc6 unignore markdown files
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-06-30 20:36:00 -05:00
76ea2d345a undraft
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-06-29 17:39:27 -05:00
06d483b3dd Change the way admonitions work
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-06-29 15:15:48 -05:00
bbfe4f67ca More updates 2024-06-29 15:15:42 -05:00
9318880a9a Remove allow unused metas 2024-06-29 14:53:45 -05:00
83ceb81f8a
a
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-06-29 14:05:06 -05:00
043b3ebe74 new blog post
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-06-28 20:48:21 -05:00
531b33442d boolean equivalences
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2024-06-28 17:46:08 -05:00
85c10d012b fix agda colors
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-06-27 10:27:22 -05:00
e0ff5bf910 agda
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-06-27 00:43:35 -05:00
2b4ca03563 render primitives
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-06-27 00:22:46 -05:00
c1d92f48f9 Fix space
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-06-26 21:04:24 -05:00
726554826a fix deploy
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-06-26 20:44:17 -05:00
6324b12c33 Update builder
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2024-06-26 20:38:45 -05:00
b574a929a7 Update
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2024-06-26 20:29:52 -05:00
68823357ab Add sharp
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2024-06-26 19:50:00 -05:00
4f88615c31 Update builder
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2024-06-26 19:47:02 -05:00
38e2a8cec5 Update dependencies 2024-06-26 19:42:02 -05:00
7375f9c81b agda building!
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2024-06-26 19:17:01 -05:00
a8f1ce9acd agda plugin 2024-06-26 18:18:47 -05:00
b5a5f9cf0a nix 2024-06-26 18:18:25 -05:00
65e5471c3c ci
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-06-21 16:06:43 -05:00
5635d03e08 update bio
All checks were successful
ci/woodpecker/manual/woodpecker Pipeline was successful
2024-06-21 15:57:44 -05:00
4fb464325c update th'bio
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-06-21 01:09:32 -05:00
63d837b264 event: push 2024-06-21 01:00:23 -05:00
0d70a68769 Revert "pudate"
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This reverts commit 435ec21f6e.
2024-06-21 00:58:59 -05:00
5d631561b5 refactoring post
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2024-06-21 00:57:37 -05:00
435ec21f6e pudate
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2024-06-11 13:54:47 -04:00
dfbdf2d4ff update path
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-06-11 10:58:14 -04:00
216b1c35ed show left
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-06-11 10:53:37 -04:00
3fcaeccf48 logical relations
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-06-11 10:52:37 -04:00
d85b7f729f update 2024-05-29 13:33:23 -05:00
22a8c36fff add biome config 2024-05-29 13:32:27 -05:00
6224860b0a add goatcounter
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-05-03 16:08:54 -05:00
4fc2c3e589 Update src/content/posts/2024-05-02-ddr/index.mdx
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-05-03 18:13:56 +00:00
85ce708153 update
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-05-03 06:35:52 -05:00
71481acbf1 pipeline
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-05-03 06:26:42 -05:00
f913eb52e2 ddr 2024-05-03 06:22:47 -05:00
b402ee102f add comments
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-05-03 05:00:04 -05:00
bc8cb94181 tmp ignore
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-05-02 15:51:23 -05:00
1cb392dbae woodpecker
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2024-05-02 15:48:55 -05:00
9bb4c462d8 update 2024-05-02 15:06:19 -05:00
1d48472aa2 start ddr post 2024-05-02 13:00:04 -05:00
1d3a17937d git attributes 2024-04-21 18:24:14 -05:00
cbc83ae11d what the hell 2024-04-20 18:22:15 -05:00
31d4b1fb71 update readme 2024-04-20 01:14:46 -05:00
1f454d6883 lambda calc post 2024-04-20 01:14:46 -05:00
42cbda6ae1 fix the header issue 2024-04-20 01:14:46 -05:00
1f8aac0a5e use alpine/edge 2024-04-20 01:14:46 -05:00
d4fe025437 typst 2024-04-20 01:14:46 -05:00
90f7fa2ee9 home to postlist 2024-04-20 01:14:46 -05:00
ea600b05f7 make the lines more condensed 2024-04-20 01:14:46 -05:00
62b4c105a9 style 2024-04-20 01:14:46 -05:00
565de50eb3 change colors 2024-04-20 01:14:46 -05:00
f048ce45ac remove box shadow 2024-04-20 01:14:46 -05:00
56c95f6051 bio 2024-04-20 01:14:46 -05:00
4768a49fce css 2024-04-20 01:14:46 -05:00
4dafc1b3e8 sourcehut publish 2024-04-20 01:14:46 -05:00
e1cb9b5e0f sudo 2024-04-20 01:14:46 -05:00
226264f124 node > npm 2024-04-20 01:14:46 -05:00
dbbcc3aae7 build 2024-04-20 01:14:46 -05:00
Michael Zhang
32098a3278 Add 'public/. well-known/atproto-did'
Some checks failed
ci/woodpecker/manual/woodpecker Pipeline failed
2024-02-29 09:06:48 -06:00
901fb1c005 a 2024-02-05 15:18:35 -06:00
f8e04d7342 install pnpm
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-24 14:05:49 -05:00
0adaf0c122 try node 20
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-10-24 14:05:09 -05:00
da64309feb undraft path induction post
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-10-24 14:02:45 -05:00
d70327ffb9 tags on home page
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-10-24 14:00:40 -05:00
4895546226 minor: parameter -> index
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-24 11:02:19 -05:00
396e1f5098 not ish
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-23 22:22:36 -05:00
025437ca10 more sane date format
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-23 21:50:45 -05:00
bd63dba9df use the original names
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-23 21:38:28 -05:00
5216167465 path induction post
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-23 21:26:47 -05:00
f50ec75e01 comment out all the other build stuff
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-12 18:34:57 -05:00
0e0249d113 sad
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-10-12 17:22:58 -05:00
92c7982c0c wtf 2023-10-11 18:01:49 -05:00
183c998199 z
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-10-11 17:43:51 -05:00
fe2377b0b4 z
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-10-11 17:43:31 -05:00
0c3659055a z
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-10-11 17:42:41 -05:00
8670e8cf7b a
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-10-11 17:40:21 -05:00
3e07267470 zz
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-10-11 17:38:07 -05:00
9ecc011029 a
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-10-11 17:36:46 -05:00
e22340a98d test
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-10-11 17:34:34 -05:00
948c982418 add packages
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-10-11 17:30:50 -05:00
b33ba945b6 a
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-10-11 17:27:56 -05:00
5319b38560 build
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-10-11 17:19:13 -05:00
1e5bef0b6f use agda
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-10-11 16:17:08 -05:00
efd0b420dd build agda
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-10-11 16:16:32 -05:00
11e1601426 builder
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-10-11 14:46:16 -05:00
1ff3499e81 rip
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-10-11 13:42:22 -05:00
43a56c941a docker shit
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-10-11 13:09:15 -05:00
3935a8934c agda fix
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-10-11 12:47:18 -05:00
3833a310e0 render agda
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-10-11 11:48:34 -05:00
334e6cb1bf add edit history
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-04 09:16:04 -05:00
b36a272815 enable syntax highlighting
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-10-02 17:36:09 -05:00
263ed46253 time to bury this forever 2023-09-21 17:40:43 -05:00
328680c54b equality notes
Some checks failed
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/manual/woodpecker Pipeline failed
2023-09-15 01:42:41 -05:00
f9e388eb6e fix ordering issue 2023-09-15 00:35:19 -05:00
109c2997c3 border radius
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-08 17:24:51 -05:00
2d7abd47d0 dont be so verbose
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-08 05:15:25 -05:00
99e0e5ebab push
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-09-08 05:12:55 -05:00
f497f3536a compiler? 2023-09-08 05:10:52 -05:00
6bb860f61a oops
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-01 22:32:18 -05:00
ab1597a28b add utterances
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-09-01 22:25:11 -05:00
c4b29c5e6f FUCK
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-01 21:38:53 -05:00
60db0faba3 fix width
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-01 21:37:13 -05:00
89ece03307 remove prints 2023-09-01 20:59:39 -05:00
a670600ad7 scroll spy
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-01 20:59:23 -05:00
e3ded78ba3 make toc look better
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-01 19:30:15 -05:00
13d83842aa fix padding again
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-01 19:26:23 -05:00
424c056a09 fix padding
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-01 19:25:56 -05:00
3bf0200e7e wip toc
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-01 12:38:59 -05:00
e94fee5345 latexify
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-01 12:05:29 -05:00
49cfb3ccc4 no extra padding on left on mobile
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-01 10:12:29 -05:00
8e1fac9bd5 admonitions
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-01 10:09:01 -05:00
5ecc5f8eed update
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-01 09:24:12 -05:00
b47c8ffae3 cek post revive
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-01 09:16:36 -05:00
0627d6b0d9 author
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-01 08:02:15 -05:00
2122aca697 keywords
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-01 08:01:19 -05:00
4a03a76348 more opengraph metadata
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-09-01 07:59:21 -05:00
0ef4b692e4 networking?
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 23:15:54 -05:00
88fd13a01a code blocks
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 22:45:24 -05:00
2a1ebcf251 intro
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 22:38:49 -05:00
edcbc68af3 changes
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 22:29:45 -05:00
6468b29dd7 minor edits
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 22:25:25 -05:00
62ad559dbd sidebar styling
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 22:04:16 -05:00
c7c8be95b5 image borders
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 21:59:12 -05:00
b61a54de22 bigger title
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 21:53:16 -05:00
f3d89cfb61 more changes
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 21:52:21 -05:00
83d21cf9b6 remove more technical stuff
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 21:41:52 -05:00
b29d3dda82 mermaid
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 19:10:20 -05:00
8c5c341f44 margin is too big
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 15:17:40 -05:00
35f6d93ca0 about page
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 14:25:04 -05:00
0c6f3ff48a wording
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 14:14:48 -05:00
322441a144 metadata
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 10:37:26 -05:00
b892dfa000 what
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 10:33:26 -05:00
0c2ac5e521 logseq post 2023-08-31 10:33:14 -05:00
10426919e1 include drafts
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 09:05:06 -05:00
86feeefbe3 remove brackets
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 08:53:36 -05:00
2c1b3c5677 blockquote
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 08:47:19 -05:00
e8f1437d95 no padding on links
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 08:46:08 -05:00
e2cc513dd4 a
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 08:39:21 -05:00
c478116f88 fill in site 2023-08-31 03:17:02 -05:00
a3a7d84d1e pragmata
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-08-31 03:09:41 -05:00
4a89b35ba3 ok kinda working
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-08-31 03:07:03 -05:00
4eeec04dad build works 2023-08-30 23:51:10 -05:00
43c5a8f70c progress 2023-08-30 22:47:22 -05:00
4b853c6c86 init astro 2023-08-30 19:30:45 -05:00
98d3dddc41 misc
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-07-04 04:42:47 -05:00
3f6bdacaf6 conclusion
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-07-04 04:35:40 -05:00
abb4f9f8ad fix intro
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-07-04 04:31:46 -05:00
68fb26e7ac fuck meant to be a draft
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-07-04 04:29:24 -05:00
5850483297 new blog post about nginx
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-07-04 04:24:45 -05:00
63c7c43afc
Dotted
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-05-08 23:48:58 -05:00
353f325bac Selection color
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-05-08 11:57:42 -05:00
28025a8c66 Equivalences post
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-05-08 11:51:29 -05:00
7f8137b9f6
add headers
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-05-08 00:39:10 -05:00
2f68aae3dd
Update to true \== false post
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-05-08 00:37:13 -05:00
a08f1206c2
Agda
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-04-21 01:57:08 -05:00
afaec7c0b6
Write some more
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-04-21 01:18:34 -05:00
079c2a8b38
Proof post
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-04-20 15:58:15 -05:00
535c3dca9f
Reword
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-04-20 14:25:51 -05:00
60b97648da
Flake post
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-04-20 14:11:41 -05:00
b781f83415 some slight wording updates on the last post
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-30 03:44:29 -05:00
6d753e5250 let's turn on the toc
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-30 03:32:41 -05:00
a248240a6a more on induction + implement language switcher
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-30 03:29:11 -05:00
07484d5812 small update
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-29 23:38:50 -05:00
db20f9536a Add the docker compose thing
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-29 23:33:59 -05:00
608503637c inductive types wip
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-29 15:03:10 -05:00
e31a8ba367 add rss feed
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-26 14:32:54 -05:00
970a8b6e9f
Add some stuff to about page
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-03-14 03:41:59 -05:00
5e3f3a25c4
Update work status 2023-03-14 03:35:45 -05:00
be60e791da Fix sase link/
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-02-05 17:13:17 -06:00
951689a328 Fix blog source link
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-02-04 01:25:40 -06:00
d6551c4632 Add Forgejo link
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-02-04 01:24:48 -06:00
f8f1987fbf Add buidl status
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-02-04 01:21:21 -06:00
d62683c6bc add linked in 2023-02-04 01:21:21 -06:00
59b4c52eee rust tags 2023-02-04 01:21:21 -06:00
8c44b266ff Add old post about weechat 2023-02-04 01:21:21 -06:00
354a353df9 remove the left margin for tags 2023-02-04 01:21:21 -06:00
49b564a620 change tag icon 2023-02-04 01:21:21 -06:00
806b929d16 Add links to nav 2023-02-04 01:21:21 -06:00
714ab14f0f Add acm involvement 2023-02-04 01:21:21 -06:00
539fa061cd Add wisesplit 2023-02-04 01:21:21 -06:00
78f1049fb7 Update date 2023-02-04 01:21:21 -06:00
df009fccbe path 2023-02-04 01:21:21 -06:00
9046fcc57e decentralized identity blog post, plus some logseq support 2023-02-04 01:21:21 -06:00
90de593a38 update lock 2023-02-04 01:21:21 -06:00
0a6e73df19 layout upd 2023-02-04 01:21:21 -06:00
cc538bc7f3 Update 2023-02-04 01:21:21 -06:00
9396a58f26 Remove matomo 2023-02-04 01:21:21 -06:00
910fa274ba uiuctf 2022 2023-02-04 01:21:21 -06:00
d39c66e866 Change padding 2023-02-04 01:21:21 -06:00
6ae17e62b8 test matomo 2023-02-04 01:21:21 -06:00
63c55873d9 Mastery post 2023-02-04 01:21:21 -06:00
7b065d5192 Change the font in CSS too 2023-02-04 01:21:21 -06:00
cc22f9b9ae Add neofetch :) 2023-02-04 01:21:21 -06:00
10801c1471 Prevent dates from wrapping 2023-02-04 01:21:21 -06:00
4654b8de5f Mastery-based learning draft 2023-02-04 01:21:21 -06:00
aa5936c70b Update font 2023-02-04 01:21:21 -06:00
89c28d0a45 Forgot to account for dark theme 2023-02-04 01:21:21 -06:00
ad3bd9bce1 Relayout home page 2023-02-04 01:21:21 -06:00
a2367d88db Flake init 2023-02-04 01:21:21 -06:00
5090a5a95c Undraft NixOS article 2023-02-04 01:21:21 -06:00
15181bb53d work status 2023-02-04 01:21:21 -06:00
e70343da96 Update work status 2023-02-04 01:21:21 -06:00
417c62988c add unmount and references 2023-02-04 01:21:21 -06:00
d304c41903 Add more indicator to NixOS post 2023-02-04 01:21:21 -06:00
125c39131c Add new NixOS install post 2023-02-04 01:21:21 -06:00
49ecbae990 a 2023-02-04 01:21:21 -06:00
Michael Zhang
1884515c6b a 2023-02-04 01:21:21 -06:00
98e1972a80 start on ecc post 2023-02-04 01:21:21 -06:00
cd872604cd Space not plus 2023-02-04 01:21:21 -06:00
b4caf7a0c2 lol flag format 2023-02-04 01:21:21 -06:00
4e4e3fd3af Explain step 4 2023-02-04 01:21:21 -06:00
abe6172448 Remove background color 2023-02-04 01:21:21 -06:00
Michael Zhang
9af469091d Cybergrabs post finished 2023-02-04 01:21:21 -06:00
Michael Zhang
baa186ea5a Ye 2023-02-04 01:21:21 -06:00
Michael Zhang
fe2c3f2524 Tag on light theme 2023-02-04 01:21:20 -06:00
307ba4e59f Tags 2023-02-04 01:21:20 -06:00
Michael Zhang
895ff81573 no more lowercase title 2023-02-04 01:21:20 -06:00
Michael Zhang
78a86b64e3 i love selection 2023-02-04 01:21:20 -06:00
604e47888b Add real email 2023-02-04 01:21:20 -06:00
dc80e5b44a Add music 2023-02-04 01:21:20 -06:00
afc086cc26 Update readme 2023-02-04 01:21:20 -06:00
26478e2add I don't want to be loweercase anymore... 2023-02-04 01:21:20 -06:00
79159229a5 Katex 2023-02-04 01:21:20 -06:00
Michael Zhang
768a5b1809 Cosmetics 2023-02-04 01:21:20 -06:00
Michael Zhang
44ba9624f7 A bit more 2023-02-04 01:21:20 -06:00
Michael Zhang
34ea75029b Don't include drafts on the home page 2023-02-04 01:21:20 -06:00
Michael Zhang
56bbbc5dee Start drafting CEK 2023-02-04 01:21:20 -06:00
f4d9ae6b14 Nah not having border-radius 2023-02-04 01:21:20 -06:00
e6776d179c No longer centered 2023-02-04 01:21:20 -06:00
ad17aefc9c Change to list so it still reads ok on text-only browsers 2023-02-04 01:21:20 -06:00
9336875427 How do secrets work
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2023-02-04 01:18:38 -06:00
3e90f780aa Wtf
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-02-04 01:15:29 -06:00
b19471d9fd Set SSH_SECRET_KEY to 600
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-02-04 01:12:21 -06:00
bf5a0ee802 Add openssh
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-02-04 01:11:56 -06:00
ac77b241fb Add ssh
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-02-04 01:11:28 -06:00
86a217d5f0 Add rsync
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-02-04 01:10:53 -06:00
cfb914149d mkdir ssh
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-02-04 01:10:28 -06:00
b0188f6c9e Hugo with extensions
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-02-04 01:08:47 -06:00
9623a74793 Add woodpecker file
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-02-04 01:04:29 -06:00
648ed87a91
New home page 2022-01-24 18:35:39 -06:00
184d29012e
Update link 2022-01-04 01:24:08 -06:00
2690a09aa7
Also use monofont for inline code elements 2021-12-12 13:59:56 -06:00
e59fdc101c
Add agda stylesheet + Mono font 2021-12-12 13:56:54 -06:00
efc6916dce
Remove "Posted on" 2021-12-01 13:07:08 -06:00
9caf7b637e
minify 2021-12-01 13:05:52 -06:00
e11c78b3e3
cd to blog 2021-11-23 02:01:58 -06:00
631c1952f8
Wrong host 2021-11-23 02:00:48 -06:00
89f417692d
Update rsync command 2021-11-23 01:59:11 -06:00
2fb170a60d
Update 2021-11-23 01:53:51 -06:00
ad987cb3f6
Upload built blog to website 2021-11-23 01:44:52 -06:00
1838120b01
Add build.yml 2021-11-23 01:41:57 -06:00
10d53c0b2a
z 2021-11-17 21:02:38 -06:00
ee3ff81526
Remove summaries, it looks kinda cluttery 2021-11-01 18:15:23 -05:00
f3a17a3f69
Revert mastodon testing changes 2021-11-01 01:02:23 -05:00
81fa0bbb36 Mastodon testing 2021-11-01 00:52:38 -05:00
50b600b519 Add site title to meta tag 2021-10-31 23:17:12 -05:00
3424164934 Add title and permalink 2021-10-31 23:13:00 -05:00
a2e3f6f6e6 What 2021-10-31 23:08:54 -05:00
5b3c5b0257 remove headers 2021-10-31 22:53:40 -05:00
f757885ac6 wtf was i doing here 2021-10-31 22:53:04 -05:00
4409efed7e Update summary 2021-10-31 22:47:16 -05:00
f17a37183a meta description 2021-10-31 22:45:54 -05:00
09e274a481 wtf every post was in june 2021-10-31 22:36:32 -05:00
114af15e61 Publish e2e article 2021-10-31 22:36:11 -05:00
b04175954b
new post 2021-08-30 02:52:21 -05:00
8bcce8a5c7
mastodon link 2021-08-11 22:41:13 -05:00
7b408c47ac
redo intro 2021-08-03 02:28:40 -05:00
99bd0368b6
d 2021-08-03 02:08:51 -05:00
288e58e650
projects 2021-08-03 02:05:25 -05:00
380c9e0d20
projects 2021-08-03 02:01:30 -05:00
2b6f2c76ef
add enterprise to project list 2021-08-03 01:52:14 -05:00
eb2bfd99f0
project links -> self hosted 2021-07-13 14:35:51 -05:00
ccd16a2648
waht 2021-07-06 18:04:42 -05:00
098d5014a5
d 2021-07-06 18:02:11 -05:00
9b9e46d641
rss 2021-07-06 17:55:57 -05:00
eae8265313
projs 2021-07-06 17:27:33 -05:00
8c1ee0d40e
upd 2021-07-06 16:57:42 -05:00
796cb7eb08
revamp taxonomies 2021-07-06 16:32:34 -05:00
849c0c3bb6
upd 2021-07-06 15:58:39 -05:00
a163172b8b
project list 2021-07-06 14:56:01 -05:00
e2224b842c
i hate crypto 2021-07-06 06:20:56 -05:00
52c5102c5e
i suck at crypto 2021-07-06 05:32:33 -05:00
bddce624f4
upd 2021-07-06 02:19:39 -05:00
99523573f4
draft 2021-07-06 00:52:05 -05:00
63f2a3b7b4
modify 2021-06-30 01:41:11 -05:00
06310d382b
don't be so excited 2021-06-30 01:03:48 -05:00
4375e85777
make the blockquote wider 2021-06-30 01:03:00 -05:00
86a40a6f03
remove todo 2021-06-17 22:38:51 -05:00
f288bab98b
-draft 2021-06-17 22:37:59 -05:00
faec6d1cda
grammar fixes 2021-06-17 22:37:41 -05:00
f880ad26cc
scripts 2021-06-17 22:33:51 -05:00
bf5a9500d9
rage 2021-06-17 15:22:12 -05:00
2c055b0ad3
change some css 2021-05-04 07:02:15 -05:00
6174c737b5
ou 2021-01-07 16:01:08 -06:00
d4b5e83682
Bio 2020-11-03 19:22:52 -06:00
a8d7d2ab3a
Change date 2020-11-03 17:27:16 -06:00
ce0723cd05
Fix broken links 2020-11-03 17:23:50 -06:00
b5682d2166
Add weights to the headers 2020-11-03 17:17:20 -06:00
7d9a04b8b0
push to execute post-receive 2020-11-03 17:02:52 -06:00
f07a3bbf7d
push to execute post-receive 2020-11-03 16:58:19 -06:00
7efd282bf3
push to execute post-receive 2020-11-03 16:56:47 -06:00
dfa7ebfccb
push to execute post-receive 2020-11-03 16:56:21 -06:00
47e52008d2
push to execute post-receive 2020-11-03 16:56:03 -06:00
96ff3c6997
push to execute post-receive 2020-11-03 16:45:42 -06:00
d8ec3896ae
push to execute post-receive 2020-11-03 16:45:28 -06:00
0cb19de91f
push to execute post-receive 2020-11-03 16:45:12 -06:00
acc0fd777a
push to execute post-receive 2020-11-03 16:44:55 -06:00
c2eee664a7
push to execute post-receive 2020-11-03 16:43:51 -06:00
da58f775a6
push to execute post-receive 2020-11-03 16:42:42 -06:00
3cda8d3dd9
push to execute post-receive 2020-11-03 16:42:16 -06:00
90ba02d903
push to execute post-receive 2020-11-03 16:41:55 -06:00
ebe186c792
push to execute post-receive 2020-11-03 16:40:32 -06:00
75e9d77c1e
push to execute post-receive 2020-11-03 16:35:45 -06:00
9d6e454793
push to execute post-receive 2020-11-03 16:34:16 -06:00
e2167780ca
push to execute post-receive 2020-11-03 16:33:57 -06:00
322c8c40ee
push to execute post-receive 2020-11-03 16:33:06 -06:00
179d89177a
push to execute post-receive 2020-11-03 16:32:16 -06:00
4edef45e83
push to execute post-receive 2020-11-03 16:31:40 -06:00
11a088a82d
push to execute post-receive 2020-11-03 16:29:21 -06:00
94dd746d6b
Fix date 2020-11-03 16:26:36 -06:00
552b6b3892
Hugo commit 2020-11-03 16:14:45 -06:00
326 changed files with 71089 additions and 1796 deletions

28
.build.yml Normal file
View 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
View file

@ -0,0 +1 @@
*

View file

@ -1,6 +0,0 @@
pipeline:
build:
image: j1mc/docker-zola
commands:
- zola build

View file

@ -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
View file

@ -0,0 +1 @@
use flake

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
public/katex/**/* linguist-vendored
src/**/*.md linguist-documentation=false

35
.gitignore vendored
View file

@ -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
View file

@ -0,0 +1,3 @@
public/katex
pnpm-lock.yaml
src/styles/fork-awesome

17
.prettierrc.json Normal file
View 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
View file

@ -0,0 +1,4 @@
public
package-lock.json
src/styles/fork-awesome
pnpm-lock.yaml

7
.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,7 @@
{
"recommendations": [
"astro-build.astro-vscode",
"eliostruyf.vscode-front-matter"
],
"unwantedRecommendations": []
}

11
.vscode/launch.json vendored Normal file
View 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
View file

@ -0,0 +1,3 @@
{
"npm.packageManager": "pnpm"
}

27
.woodpecker/deploy.yml Normal file
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,2 @@
include: src/content/posts src
depend: standard-library cubical

BIN
bun.lockb Executable file

Binary file not shown.

View file

@ -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" },
]

View file

@ -1,8 +0,0 @@
+++
title = "drawing bezier curves"
date = 2020-04-12
draft = true
[taxonomies]
tags = ["osu", "graphics"]
+++

View file

@ -1,11 +0,0 @@
+++
title = "home"
template = "blog.html"
page_template = "post.html"
insert_anchor_links = "left"
sort_by = "date"
[extra]
include_posts = true
+++

View file

@ -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;
}
};
}
));
}));

View file

@ -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

View file

@ -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

View file

@ -1,7 +0,0 @@
+++
template = "blog.html"
insert_anchor_links = "left"
[extra]
include_posts = true
+++

View file

@ -1,9 +0,0 @@
+++
title = "all pages"
template = "listing.html"
page_template = "post.html"
insert_anchor_links = "left"
[extra]
include_posts = false
+++

View file

@ -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

View file

@ -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

View file

@ -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/

View file

@ -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
View 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
View 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
View 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
View 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
View 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
View 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"
}
}

View 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
View 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;

View 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
View 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

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
did:plc:zbg2asfcpyughqspwjjgyc2d

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

BIN
public/fonts/Inter-Bold.ttf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/fonts/Inter-Thin.ttf Normal file

Binary file not shown.

93
public/fonts/OFL.txt Normal file
View 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.

Binary file not shown.

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 547 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

119
public/katex/README.md vendored Normal file
View 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
View 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__;
/******/ })()
;
});

View 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
View 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
View 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
View 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
View 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
View 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
View 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();
});

View 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__;
/******/ })()
;
});

View 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}()}));

View 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

File diff suppressed because it is too large Load diff

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

File diff suppressed because it is too large Load diff

View 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__;
/******/ })()
;
});

File diff suppressed because one or more lines are too long

View 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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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

Binary file not shown.

BIN
public/katex/fonts/KaTeX_Main-Bold.woff2 vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show more