Add board compilation

This commit is contained in:
dylan 2024-04-24 18:54:55 -07:00
parent e2d1b006fe
commit 4a6482100a
13 changed files with 488 additions and 43 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
node_modules
src/server/public/dist
src/server/public/assets/compiled
.env

331
package-lock.json generated
View File

@ -16,9 +16,12 @@
"@firebox/tsutil": "^0.1.2",
"@sinclair/typebox": "^0.31.5",
"@types/katex": "^0.16.7",
"@types/object-hash": "^3.0.6",
"dotenv": "^16.3.1",
"execa": "^8.0.1",
"fastify": "^4.22.0",
"katex": "^0.16.10",
"object-hash": "^3.0.0",
"tsx": "^4.7.2"
},
"devDependencies": {
@ -1180,6 +1183,11 @@
"dev": true,
"peer": true
},
"node_modules/@types/object-hash": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/object-hash/-/object-hash-3.0.6.tgz",
"integrity": "sha512-fOBV8C1FIu2ELinoILQ+ApxcUKz4ngq+IWUYrxSGjXzzjUALijilampwkMgEtJ+h2njAW3pi853QpzNVCHB73w=="
},
"node_modules/@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
@ -1720,6 +1728,19 @@
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
"which": "^2.0.1"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/csstype": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
@ -1932,6 +1953,64 @@
"node": ">=0.8.x"
}
},
"node_modules/execa": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
"integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
"dependencies": {
"cross-spawn": "^7.0.3",
"get-stream": "^8.0.1",
"human-signals": "^5.0.0",
"is-stream": "^3.0.0",
"merge-stream": "^2.0.0",
"npm-run-path": "^5.1.0",
"onetime": "^6.0.0",
"signal-exit": "^4.1.0",
"strip-final-newline": "^3.0.0"
},
"engines": {
"node": ">=16.17"
},
"funding": {
"url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
"node_modules/execa/node_modules/mimic-fn": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
"integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/execa/node_modules/onetime": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
"integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
"dependencies": {
"mimic-fn": "^4.0.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/execa/node_modules/signal-exit": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/external-editor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
@ -2158,6 +2237,17 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-stream": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
"integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
"engines": {
"node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/get-tsconfig": {
"version": "4.7.3",
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz",
@ -2316,6 +2406,14 @@
"node": ">= 0.8"
}
},
"node_modules/human-signals": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
"integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
"engines": {
"node": ">=16.17.0"
}
},
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@ -2678,6 +2776,17 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
"integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-string": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
@ -2763,6 +2872,11 @@
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
"dev": true
},
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -2959,6 +3073,11 @@
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
},
"node_modules/mime": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
@ -3077,6 +3196,31 @@
"node": ">=0.10.0"
}
},
"node_modules/npm-run-path": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
"integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
"dependencies": {
"path-key": "^4.0.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/npm-run-path/node_modules/path-key": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
"integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@ -3086,6 +3230,14 @@
"node": ">=0.10.0"
}
},
"node_modules/object-hash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
"engines": {
"node": ">= 6"
}
},
"node_modules/object-inspect": {
"version": "1.12.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
@ -3342,6 +3494,14 @@
"node": ">=0.10.0"
}
},
"node_modules/path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"engines": {
"node": ">=8"
}
},
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
@ -3884,6 +4044,25 @@
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dependencies": {
"shebang-regex": "^3.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"engines": {
"node": ">=8"
}
},
"node_modules/side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
@ -3986,6 +4165,17 @@
"node": ">=8"
}
},
"node_modules/strip-final-newline": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
"integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/sucrase": {
"version": "3.34.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz",
@ -4252,6 +4442,20 @@
"defaults": "^1.0.3"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dependencies": {
"isexe": "^2.0.0"
},
"bin": {
"node-which": "bin/node-which"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/which-boxed-primitive": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
@ -5206,6 +5410,11 @@
"dev": true,
"peer": true
},
"@types/object-hash": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/object-hash/-/object-hash-3.0.6.tgz",
"integrity": "sha512-fOBV8C1FIu2ELinoILQ+ApxcUKz4ngq+IWUYrxSGjXzzjUALijilampwkMgEtJ+h2njAW3pi853QpzNVCHB73w=="
},
"@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
@ -5603,6 +5812,16 @@
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true
},
"cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"requires": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
"which": "^2.0.1"
}
},
"csstype": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
@ -5763,6 +5982,42 @@
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
},
"execa": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
"integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
"requires": {
"cross-spawn": "^7.0.3",
"get-stream": "^8.0.1",
"human-signals": "^5.0.0",
"is-stream": "^3.0.0",
"merge-stream": "^2.0.0",
"npm-run-path": "^5.1.0",
"onetime": "^6.0.0",
"signal-exit": "^4.1.0",
"strip-final-newline": "^3.0.0"
},
"dependencies": {
"mimic-fn": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
"integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw=="
},
"onetime": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
"integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
"requires": {
"mimic-fn": "^4.0.0"
}
},
"signal-exit": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="
}
}
},
"external-editor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
@ -5951,6 +6206,11 @@
"has-symbols": "^1.0.3"
}
},
"get-stream": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
"integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA=="
},
"get-tsconfig": {
"version": "4.7.3",
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz",
@ -6061,6 +6321,11 @@
"toidentifier": "1.0.1"
}
},
"human-signals": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
"integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@ -6315,6 +6580,11 @@
"call-bind": "^1.0.2"
}
},
"is-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
"integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="
},
"is-string": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
@ -6370,6 +6640,11 @@
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
"dev": true
},
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -6518,6 +6793,11 @@
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
},
"mime": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
@ -6604,12 +6884,32 @@
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true
},
"npm-run-path": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
"integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
"requires": {
"path-key": "^4.0.0"
},
"dependencies": {
"path-key": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
"integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="
}
}
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"dev": true
},
"object-hash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="
},
"object-inspect": {
"version": "1.12.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
@ -6790,6 +7090,11 @@
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"dev": true
},
"path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
},
"path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
@ -7184,6 +7489,19 @@
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
"shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"requires": {
"shebang-regex": "^3.0.0"
}
},
"shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
},
"side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
@ -7265,6 +7583,11 @@
"ansi-regex": "^5.0.1"
}
},
"strip-final-newline": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
"integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="
},
"sucrase": {
"version": "3.34.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz",
@ -7451,6 +7774,14 @@
"defaults": "^1.0.3"
}
},
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"requires": {
"isexe": "^2.0.0"
}
},
"which-boxed-primitive": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",

View File

@ -6,14 +6,17 @@
"main": "index.js",
"scripts": {
"dev-docker": "docker compose --profile dev up -d",
"dev-server": "echo \"starting server\" && npm run withenv ./src/server/index.ts",
"dev-server": "npm run withenv ./src/server/index.ts",
"dev-build-client": "npm run withenv ./scripts/build.ts",
"dev-watch-client": "npm run withenv ./scripts/watch.ts",
"dev-migrate": "source ./.env && pg-migrations apply --directory ./src/database/migrations",
"prod-migrate": "pg-migrations apply --directory ./src/database/migrations",
"dev-start": "echo \"building frontend\" && npm run dev-build-client && echo \"starting server\" && npm run dev-server",
"prod-build-client": "npm run withenv ./scripts/build.ts",
"prod-docker": "docker compose --profile prod up -d",
"prod-start": "echo \"building frontend\" && npm run prod-build-client && echo \"${DATABASE_URL}\" && echo \"running migrations\" && npm run prod-migrate && echo \"starting server\" && npm run withenv ./src/server/index.ts",
"prod-server": "npm run withenv ./src/server/index.ts",
"prod-start": "echo \"building frontend\" && npm run prod-build-client && echo \"starting server\" && npm run prod-server",
"withenv": "tsx ./scripts/run-with-env.ts",
"make-svgs": "npm run withenv ./scripts/make-svgs.ts",
"compile-content": "npm run withenv ./scripts/compile-content.ts",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
@ -30,9 +33,12 @@
"@firebox/tsutil": "^0.1.2",
"@sinclair/typebox": "^0.31.5",
"@types/katex": "^0.16.7",
"@types/object-hash": "^3.0.6",
"dotenv": "^16.3.1",
"execa": "^8.0.1",
"fastify": "^4.22.0",
"katex": "^0.16.10",
"object-hash": "^3.0.0",
"tsx": "^4.7.2"
},
"devDependencies": {

View File

@ -0,0 +1,80 @@
import fs from "fs";
import { getDirname } from '../util/dirname';
import path from "path";
import { Mathuscript, parseMathuscript } from "../src/client/player/parse";
import { writeFileSync, mkdirSync, rmSync } from 'fs';
import {execa} from 'execa';
import hash from "object-hash";
const __dirname = getDirname(import.meta);
// Function to convert LaTeX to SVG
async function latexToSvg(latex: string, dir: string, tempDir: string | null): Promise<string> {
try {
// const dir = path.join(__dirname, "..", "src", "server", "public", "svgs");
tempDir = tempDir ?? path.join(dir, "temp");
mkdirSync(tempDir, {recursive: true});
const fname = hash(latex);
const tempFileName = path.join(tempDir, fname);
const outFileName = path.join(dir, fname);
writeFileSync(`${tempFileName}.tex`, latex);
// Run LaTeX to generate a DVI file
console.log("generating dvi file");
await execa('latex', ['-halt-on-error', `-output-directory=${tempDir}`, `${tempFileName}.tex`]);
// Convert DVI to SVG
console.log("converting to svg");
await execa('dvisvgm', [`${tempFileName}.dvi`, '-n', '-o', `${outFileName}.svg`]);
console.log("cleaning up temp files");
rmSync(tempDir, {force: true, recursive: true});
return `${fname}.svg`;
} catch (error) {
console.error('Error converting LaTeX to SVG:', error);
throw Error('Error converting LaTeX to SVG');
}
}
const contentDir = path.join(__dirname, "..", "src", "content");
const publicDir = path.join(__dirname, "..", "src", "server", "public");
const assetsDir = path.join(publicDir, "assets");
const compiledAssetsDir = path.join(assetsDir, "compiled");
const compileToDir = path.join(__dirname, "..", "src", "client", "content");
const preamblePath = path.join(contentDir, "preamble.tex");
const sectionsDir = path.join(contentDir, "sections");
const compileContents = async () => {
fs.rmSync(compileToDir, {force: true, recursive: true});
fs.mkdirSync(compileToDir, {recursive: true});
const sectionFiles = fs.readdirSync(sectionsDir);
const sections: {name: string; text: string, parsed: Mathuscript}[] = [];
sectionFiles.forEach(sectionFile => {
const fullName = path.join(sectionsDir, sectionFile);
console.log("Reading", sectionFile);
const fileText = fs.readFileSync(fullName, "utf8");
sections.push({
name: sectionFile,
text: fileText,
parsed: parseMathuscript(fileText),
});
})
const preamble = fs.readFileSync(preamblePath, "utf8");
for (const section of sections) {
console.log("Processing", section.name);
for (const step of section.parsed.filter(step => step.name === "board")) {
console.log('Step');
if (step.text.trim()) {
const svgPath = await latexToSvg(`${preamble}\n\n\\begin{document}\n${step.text}\n\\end{document}\n`, compiledAssetsDir, null);
step.text = svgPath;
}
}
}
fs.writeFileSync(path.join(compileToDir, "content.ts"), `export const content = {sections: ${JSON.stringify(sections)}}`);
}
await compileContents();

View File

@ -42,7 +42,7 @@ const App = (props: { name: string }) => {
margin: auto;
`}>
<h1>MathU</h1>
<MathuscriptPlayer script={script} />
<MathuscriptPlayer scriptName="cantor-schroeder-bernstein" />
</div>
);
};

View File

@ -0,0 +1 @@
export const content = {sections: [{"name":"cantor-schroeder-bernstein.mus","text":"# !use [board, bridget, calvin, kit, theo, axelle]\n\nboard \"Given $\\chalk{yellow}f\\chalk{white}: \\mathbb{Q} \\to \\mathbb{R}$ and $x \\in \\mathbb{Q}$, there is a unique $y \\in \\mathbb{N}$ such that $\\chalk{blue}f\\chalk{white}(x)=y$\"\n\nbridget (happy) \"Hi, friends!\"\n\ncalvin (mathstruck) \"Wow, did you know that $a^2+b^2=c^2$?\"\n\ncalvin (thinking) \"Something else\"\n\nbridget (surprised) \"Me again000000\"\n\nkit (smiling) \"I'm Kit 1!\"\n\nkit (smiling) \"I'm Kit 2!\"\n\nboard \"\"\n\ncalvin (happy) \"I'm back!\"\n\nbridget (aha) \"I'm on the right now!\"\n\ntheo (heh) \"Oh, hey guys. Didn't see you there.\"\n\naxelle \"Oh, hi.\"\n\nboard \"If $ax^2+bx+c=0$, then $x = \\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}$.\"\n\ntheo \"Did you know...\"","parsed":[{"name":"board","text":"a4dc03d7dade1daf859767ba8dd51678d73a3bf7.svg"},{"name":"bridget","emotion":"happy","text":"Hi, friends!"},{"name":"calvin","emotion":"mathstruck","text":"Wow, did you know that $a^2+b^2=c^2$?"},{"name":"calvin","emotion":"thinking","text":"Something else"},{"name":"bridget","emotion":"surprised","text":"Me again000000"},{"name":"kit","emotion":"smiling","text":"I'm Kit 1!"},{"name":"kit","emotion":"smiling","text":"I'm Kit 2!"},{"name":"board","text":""},{"name":"calvin","emotion":"happy","text":"I'm back!"},{"name":"bridget","emotion":"aha","text":"I'm on the right now!"},{"name":"theo","emotion":"heh","text":"Oh, hey guys. Didn't see you there."},{"name":"axelle","text":"Oh, hi."},{"name":"board","text":"fa97c503f244f13f97a181facac0c22c16408700.svg"},{"name":"theo","text":"Did you know..."}]}]}

View File

@ -4,6 +4,7 @@ import { useCallback, useMemo, useRef, useState } from "react";
import { MathText } from "./MathText";
import { characterData } from "./data";
import { DialogText, DialogTextImperatives } from "./DialogText";
import { content } from "../content/content";
type VisualState = {
turn: number,
@ -57,9 +58,9 @@ const getCharData = (name: string | null) => {
const allChars = Object.keys(characterData) as (keyof typeof characterData)[];
export const MathuscriptPlayer = (props: { script: string }) => {
const {script} = props;
const parsedScript = useMemo(() => parseMathuscript(script), [script]);
export const MathuscriptPlayer = (props: { scriptName: string }) => {
const {scriptName} = props;
const parsedScript = content.sections.find(s => s.name.split(".")[0] === scriptName)!.parsed; // useMemo(() => parseMathuscript(script), [script]);
const goAgain = useRef(false);
const [index, setIndex] = useState(0);
const [visualState, setVisualState] = useState<VisualState>({turn: 0, characters: [], board: {text: ""}, dialog: {name: null, emotion: "", text: ""}});
@ -77,7 +78,7 @@ export const MathuscriptPlayer = (props: { script: string }) => {
const step = parsedScript[index];
if (step) {
setIndex(i => i+1);
setVisualState(state => afterStep(state, step));
setVisualState(state => afterStep(state, {emotion: "", ...step}));
if (step.name === "board") {
goAgain.current = true;
} else {
@ -126,7 +127,12 @@ export const MathuscriptPlayer = (props: { script: string }) => {
padding: 1em;
`}>
<div>
<MathText>{visualState.board.text}</MathText>
{
visualState.board.text && <img className={css`
transform: scale(1.25);
`} src={`/assets/compiled/${visualState.board.text}`}/>
}
{/* <MathText>{visualState.board.text}</MathText> */}
</div>
</div>
<div className={css`

View File

@ -2,7 +2,7 @@
const lineRegex = /^\s*(?<name>[a-z-]+)\s+(?:\((?<emotion>[a-z-]+)\))?\s*"(?<text>(?:[^"\\]|\\.)*)"\s*$/;
export const parseMathuscript = (script: string) => {
return script.split("\n").filter(Boolean).map(line => {
return script.split("\n").filter(x => x && !x.startsWith("#")).map(line => {
const match = line.match(lineRegex);
if (!match) {
throw Error(`Bad Line: ${JSON.stringify(line)}`);

17
src/content/preamble.tex Normal file
View File

@ -0,0 +1,17 @@
\documentclass[varwidth=256px]{standalone}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{xcolor}
\definecolor{chalkwhite}{RGB}{240, 250, 245}
\definecolor{chalkred}{RGB}{255, 119, 122}
\definecolor{chalkorange}{RGB}{255, 195, 128}
\definecolor{chalkyellow}{RGB}{244, 244, 120}
\definecolor{chalkgreen}{RGB}{135, 230, 121}
\definecolor{chalkblue}{RGB}{128, 195, 255}
\definecolor{chalkpurple}{RGB}{206, 168, 238}
\definecolor{chalkpink}{RGB}{240, 157, 215}
\newcommand{\chalk}[1]{\color{chalk#1}}
\chalk{white}

View File

@ -0,0 +1,29 @@
# !use [board, bridget, calvin, kit, theo, axelle]
board "Given $\chalk{yellow}f\chalk{white}: \mathbb{Q} \to \mathbb{R}$ and $x \in \mathbb{Q}$, there is a unique $y \in \mathbb{N}$ such that $\chalk{blue}f\chalk{white}(x)=y$"
bridget (happy) "Hi, friends!"
calvin (mathstruck) "Wow, did you know that $a^2+b^2=c^2$?"
calvin (thinking) "Something else"
bridget (surprised) "Me again000000"
kit (smiling) "I'm Kit 1!"
kit (smiling) "I'm Kit 2!"
board ""
calvin (happy) "I'm back!"
bridget (aha) "I'm on the right now!"
theo (heh) "Oh, hey guys. Didn't see you there."
axelle "Oh, hi."
board "If $ax^2+bx+c=0$, then $x = \frac{-b\pm\sqrt{b^2-4ac}}{2a}$."
theo "Did you know..."

View File

@ -1,28 +0,0 @@
import createConnectionPool, {ConnectionPool, ConnectionPoolConfig, sql} from '@databases/pg';
export {sql};
const portString = process.env["DB_PORT"];
const portNumber = portString ? parseInt(portString) : undefined;
const clientConfig: ConnectionPoolConfig = {
host: process.env["DB_HOST"],
user: process.env["DB_USER"],
database: process.env["DB_NAME"],
password: process.env["DB_PASSWORD"],
port: portNumber,
};
// @ts-ignore
const db: ConnectionPool = createConnectionPool({
connectionString: false,
...clientConfig
});
process.once('SIGTERM', () => {
db.dispose().catch((ex) => {
console.error(ex);
});
});
export {db};

View File

@ -1,4 +0,0 @@
CREATE TABLE testtable (
id text,
somecolumn text
)

6
util/dirname.ts Normal file
View File

@ -0,0 +1,6 @@
import { fileURLToPath } from "url";
/**
* Pass in `import.meta` to get the __dirname.
*/
export const getDirname = (importMeta: ImportMeta) => fileURLToPath(new URL('.', importMeta.url));