From 4a6482100aaf2756c4c8d9d118ba5b39ffac3bc8 Mon Sep 17 00:00:00 2001 From: dylan <> Date: Wed, 24 Apr 2024 18:54:55 -0700 Subject: [PATCH] Add board compilation --- .gitignore | 1 + package-lock.json | 331 ++++++++++++++++++ package.json | 14 +- scripts/compile-content.ts | 80 +++++ src/client/app.tsx | 2 +- src/client/content/content.ts | 1 + src/client/player/Player.tsx | 16 +- src/client/player/parse.ts | 2 +- src/content/preamble.tex | 17 + .../sections/cantor-schroeder-bernstein.mus | 29 ++ src/database/db.ts | 28 -- src/database/migrations/1-first-migration.sql | 4 - util/dirname.ts | 6 + 13 files changed, 488 insertions(+), 43 deletions(-) create mode 100644 scripts/compile-content.ts create mode 100644 src/client/content/content.ts create mode 100644 src/content/preamble.tex create mode 100644 src/content/sections/cantor-schroeder-bernstein.mus delete mode 100644 src/database/db.ts delete mode 100644 src/database/migrations/1-first-migration.sql create mode 100644 util/dirname.ts diff --git a/.gitignore b/.gitignore index 10a9f33..b8215de 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules src/server/public/dist +src/server/public/assets/compiled .env \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4e24956..e997eeb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 01f211c..f41f6c2 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/scripts/compile-content.ts b/scripts/compile-content.ts new file mode 100644 index 0000000..69b5243 --- /dev/null +++ b/scripts/compile-content.ts @@ -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 { + 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(); diff --git a/src/client/app.tsx b/src/client/app.tsx index 6d4086a..0fbbca4 100644 --- a/src/client/app.tsx +++ b/src/client/app.tsx @@ -42,7 +42,7 @@ const App = (props: { name: string }) => { margin: auto; `}>

MathU

- + ); }; diff --git a/src/client/content/content.ts b/src/client/content/content.ts new file mode 100644 index 0000000..cb2de11 --- /dev/null +++ b/src/client/content/content.ts @@ -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..."}]}]} \ No newline at end of file diff --git a/src/client/player/Player.tsx b/src/client/player/Player.tsx index ae9c110..3da33c0 100644 --- a/src/client/player/Player.tsx +++ b/src/client/player/Player.tsx @@ -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({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; `}>
- {visualState.board.text} + { + visualState.board.text && + } + {/* {visualState.board.text} */}
[a-z-]+)\s+(?:\((?[a-z-]+)\))?\s*"(?(?:[^"\\]|\\.)*)"\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)}`); diff --git a/src/content/preamble.tex b/src/content/preamble.tex new file mode 100644 index 0000000..dc3977b --- /dev/null +++ b/src/content/preamble.tex @@ -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} \ No newline at end of file diff --git a/src/content/sections/cantor-schroeder-bernstein.mus b/src/content/sections/cantor-schroeder-bernstein.mus new file mode 100644 index 0000000..73472d2 --- /dev/null +++ b/src/content/sections/cantor-schroeder-bernstein.mus @@ -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..." \ No newline at end of file diff --git a/src/database/db.ts b/src/database/db.ts deleted file mode 100644 index f418120..0000000 --- a/src/database/db.ts +++ /dev/null @@ -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}; \ No newline at end of file diff --git a/src/database/migrations/1-first-migration.sql b/src/database/migrations/1-first-migration.sql deleted file mode 100644 index d7e816b..0000000 --- a/src/database/migrations/1-first-migration.sql +++ /dev/null @@ -1,4 +0,0 @@ -CREATE TABLE testtable ( - id text, - somecolumn text -) \ No newline at end of file diff --git a/util/dirname.ts b/util/dirname.ts new file mode 100644 index 0000000..99f4134 --- /dev/null +++ b/util/dirname.ts @@ -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)); \ No newline at end of file