diff --git a/.prettierignore b/.prettierignore index 717b6373e..b94299a92 100644 --- a/.prettierignore +++ b/.prettierignore @@ -19,5 +19,7 @@ tsdoc-metadata.json .git_blame_ignore_revs -# This file is generated by tools/bundle-doc/index.js -src/Documentation/pages.ts \ No newline at end of file +# This file is generated by tools/bundle-doc/index.mjs +src/Documentation/pages.ts +# This file is generated by tools/bundle-doc/generate-math-notation-output.mjs +src/Documentation/data/MathNotationOutput.json diff --git a/package-lock.json b/package-lock.json index c7a843e9a..03e7dc97f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,11 +27,12 @@ "ajv": "^8.17.1", "arg": "^5.0.2", "bcryptjs": "^2.4.3", - "better-react-mathjax": "^2.0.3", "clsx": "^1.2.1", "convert-source-map": "^2.0.0", "date-fns": "^2.30.0", "fast-dice-coefficient": "1.0.3", + "hast-util-from-dom": "^5.0.1", + "hast-util-to-text": "^4.0.2", "jszip": "^3.10.1", "material-ui-color": "^1.2.0", "material-ui-popup-state": "^1.9.3", @@ -45,9 +46,12 @@ "react-markdown": "^8.0.7", "react-resizable": "^3.0.5", "react-syntax-highlighter": "^15.6.6", + "rehype-raw": "^6.1.1", "remark-gfm": "^3.0.1", + "remark-math": "^5.1.1", "sprintf-js": "^1.1.3", - "tss-react": "^4.9.19" + "tss-react": "^4.9.19", + "unist-util-visit-parents": "^5.1.3" }, "devDependencies": { "@babel/core": "^7.28.0", @@ -55,6 +59,7 @@ "@babel/preset-react": "^7.27.1", "@babel/preset-typescript": "^7.27.1", "@electron/packager": "^18.4.4", + "@mathjax/src": "^4.1.0", "@microsoft/api-documenter": "^7.26.34", "@microsoft/api-extractor": "^7.52.13", "@pmmmwh/react-refresh-webpack-plugin": "^0.6.1", @@ -74,7 +79,6 @@ "babel-jest": "^29.7.0", "babel-loader": "^10.0.0", "babel-plugin-transform-barrels": "^1.0.17", - "copy-webpack-plugin": "^13.0.0", "css-loader": "^7.1.2", "csstype": "3.1.3", "electron": "^38.4.0", @@ -87,19 +91,16 @@ "jest-environment-jsdom": "^29.7.0", "jsdom": "^26.1.0", "lodash": "^4.17.21", - "mathjax-full": "^3.2.2", "monaco-editor": "^0.55.1", "monaco-editor-webpack-plugin": "^7.1.1", "prettier": "^2.8.8", "react-refresh": "^0.18.0", - "rehype-mathjax": "^4.0.3", - "rehype-raw": "^6.1.1", - "remark-math": "^5.1.1", "style-loader": "^4.0.0", "typescript": "^5.8.3", "webpack": "^5.100.2", "webpack-cli": "^6.0.1", - "webpack-dev-server": "^5.2.2" + "webpack-dev-server": "^5.2.2", + "xml-formatter": "^3.6.7" }, "engines": { "node": ">=22" @@ -3527,6 +3528,125 @@ "react-dom": "^16.8.0 || ^17.0.0" } }, + "node_modules/@mathjax/mathjax-newcm-font": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@mathjax/mathjax-newcm-font/-/mathjax-newcm-font-4.1.0.tgz", + "integrity": "sha512-n10AwYubUa2hyOzxSRzkwRrgCVns083zkentryXICMPKaWT/watfvK2sUk5D9Bow9mpDfoqb5EWApuUvqnlzaw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@mathjax/src": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@mathjax/src/-/src-4.1.0.tgz", + "integrity": "sha512-xnBL2E5uNHR82iLiUVJXYicKh/Jbq2I1+8yCpcBfJqLEM6CrAR9zBpYYkAFtC/Myvykd5U5uMmRrtr49AKk0TA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@mathjax/mathjax-newcm-font": "4.1.0", + "mhchemparser": "^4.2.1", + "mj-context-menu": "^1.0.0", + "speech-rule-engine": "5.0.0-beta.3" + } + }, + "node_modules/@mathjax/src/node_modules/@xmldom/xmldom": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.8.tgz", + "integrity": "sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.6" + } + }, + "node_modules/@mathjax/src/node_modules/commander": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", + "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/@mathjax/src/node_modules/glob": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", + "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "path-scurry": "^2.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@mathjax/src/node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@mathjax/src/node_modules/mj-context-menu": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mj-context-menu/-/mj-context-menu-1.0.0.tgz", + "integrity": "sha512-OSgBFQRCVhrZwRa9lhqnKcJU92dd/YXgBjup0uyeuj9bOaVsf4myOyrjU4PfhYkdDk6AqVUTov7v5uFMvIbduA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "rimraf": "^6.0.1" + } + }, + "node_modules/@mathjax/src/node_modules/rimraf": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.2.tgz", + "integrity": "sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^13.0.0", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@mathjax/src/node_modules/speech-rule-engine": { + "version": "5.0.0-beta.3", + "resolved": "https://registry.npmjs.org/speech-rule-engine/-/speech-rule-engine-5.0.0-beta.3.tgz", + "integrity": "sha512-wSrjCo8h4SJmGtjicD0OrTuCNH/wEaMsV+ktA1D9C2IPnensiP4pfPv+DnEZDMbauC5SCSE1prP5KA0z/W8bLg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@xmldom/xmldom": "0.9.8", + "commander": "14.0.2", + "wicked-good-xpath": "1.3.0" + }, + "bin": { + "sre": "bin/sre" + } + }, "node_modules/@microsoft/api-documenter": { "version": "7.28.1", "resolved": "https://registry.npmjs.org/@microsoft/api-documenter/-/api-documenter-7.28.1.tgz", @@ -5284,10 +5404,10 @@ "license": "MIT" }, "node_modules/@types/katex": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz", - "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==", - "dev": true + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.8.tgz", + "integrity": "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==", + "license": "MIT" }, "node_modules/@types/keyv": { "version": "3.1.4", @@ -5305,12 +5425,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/mathjax": { - "version": "0.0.37", - "resolved": "https://registry.npmjs.org/@types/mathjax/-/mathjax-0.0.37.tgz", - "integrity": "sha512-y0WSZBtBNQwcYipTU/BhgeFu1EZNlFvUNCmkMXV9kBQZq7/o5z82dNVyH3yy2Xv5zzeNeQoHSL4Xm06+EQiH+g==", - "dev": true - }, "node_modules/@types/mdast": { "version": "3.0.13", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.13.tgz", @@ -5359,7 +5473,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz", "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==", - "dev": true, "license": "MIT" }, "node_modules/@types/prop-types": { @@ -6758,17 +6871,6 @@ "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" }, - "node_modules/better-react-mathjax": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/better-react-mathjax/-/better-react-mathjax-2.0.3.tgz", - "integrity": "sha512-wfifT8GFOKb1TWm2+E50I6DJpLZ5kLbch283Lu043EJtwSv0XvZDjr4YfR4d2MjAhqP6SH4VjjrKgbX8R00oCQ==", - "dependencies": { - "mathjax-full": "^3.2.2" - }, - "peerDependencies": { - "react": ">=16.8" - } - }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -7575,49 +7677,6 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "dev": true }, - "node_modules/copy-webpack-plugin": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-13.0.0.tgz", - "integrity": "sha512-FgR/h5a6hzJqATDGd9YG41SeDViH+0bkHn6WNXCi5zKAZkeESeSxLySSsFLHqLEVCh0E+rITmCf0dusXWYukeQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob-parent": "^6.0.1", - "normalize-path": "^3.0.0", - "schema-utils": "^4.2.0", - "serialize-javascript": "^6.0.2", - "tinyglobby": "^0.2.12" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/core-js-compat": { "version": "3.44.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.44.0.tgz", @@ -9023,14 +9082,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/esm": { - "version": "3.2.25", - "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", - "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", - "engines": { - "node": ">=6" - } - }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -10423,12 +10474,13 @@ } }, "node_modules/hast-util-from-dom": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-4.2.0.tgz", - "integrity": "sha512-t1RJW/OpJbCAJQeKi3Qrj1cAOLA0+av/iPFori112+0X7R3wng+jxLA+kXec8K4szqPRGI8vPxbbpEYvvpwaeQ==", - "dev": true, + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-5.0.1.tgz", + "integrity": "sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==", + "license": "ISC", "dependencies": { - "hastscript": "^7.0.0", + "@types/hast": "^3.0.0", + "hastscript": "^9.0.0", "web-namespaces": "^2.0.0" }, "funding": { @@ -10436,13 +10488,22 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-from-dom/node_modules/hast-util-parse-selector": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz", - "integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==", - "dev": true, + "node_modules/hast-util-from-dom/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", "dependencies": { - "@types/hast": "^2.0.0" + "@types/unist": "*" + } + }, + "node_modules/hast-util-from-dom/node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" }, "funding": { "type": "opencollective", @@ -10450,15 +10511,15 @@ } }, "node_modules/hast-util-from-dom/node_modules/hastscript": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz", - "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==", - "dev": true, + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", "dependencies": { - "@types/hast": "^2.0.0", + "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^3.0.0", - "property-information": "^6.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" }, "funding": { @@ -10466,11 +10527,20 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-from-dom/node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/hast-util-from-parse5": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-7.1.2.tgz", "integrity": "sha512-Nz7FfPBuljzsN3tCQ4kCBKqdNhQE2l0Tn+X1ubgKBPRoiDIu1mL08Cfw4k7q71+Duyaw7DXDN+VTAp4Vh3oCOw==", - "dev": true, "license": "MIT", "dependencies": { "@types/hast": "^2.0.0", @@ -10490,7 +10560,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz", "integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==", - "dev": true, "license": "MIT", "dependencies": { "@types/hast": "^2.0.0" @@ -10504,7 +10573,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz", "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==", - "dev": true, "license": "MIT", "dependencies": { "@types/hast": "^2.0.0", @@ -10519,19 +10587,27 @@ } }, "node_modules/hast-util-is-element": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-2.1.3.tgz", - "integrity": "sha512-O1bKah6mhgEq2WtVMk+Ta5K7pPMqsBBlmzysLdcwKVrqzZQ0CHqUPiIVspNhAG1rvxpvJjtGee17XfauZYKqVA==", - "dev": true, + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "license": "MIT", "dependencies": { - "@types/hast": "^2.0.0", - "@types/unist": "^2.0.0" + "@types/hast": "^3.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-is-element/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/hast-util-parse-selector": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", @@ -10545,7 +10621,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-7.2.3.tgz", "integrity": "sha512-RujVQfVsOrxzPOPSzZFiwofMArbQke6DJjnFfceiEbFh7S05CbPt0cYN+A5YeD3pso0JQk6O1aHBnx9+Pm2uqg==", - "dev": true, "license": "MIT", "dependencies": { "@types/hast": "^2.0.0", @@ -10569,14 +10644,12 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true, "license": "MIT" }, "node_modules/hast-util-to-parse5": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-7.1.0.tgz", "integrity": "sha512-YNRgAJkH2Jky5ySkIqFXTQiaqcAtJyVE+D5lkN6CdtOqrnkLfGYYrEcKuHOJZlp+MwjSwuD3fZuawI+sic/RBw==", - "dev": true, "license": "MIT", "dependencies": { "@types/hast": "^2.0.0", @@ -10592,21 +10665,36 @@ } }, "node_modules/hast-util-to-text": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-3.1.2.tgz", - "integrity": "sha512-tcllLfp23dJJ+ju5wCCZHVpzsQQ43+moJbqVX3jNWPB7z/KFC4FyZD6R7y94cHL6MQ33YtMZL8Z0aIXXI4XFTw==", - "dev": true, + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "license": "MIT", "dependencies": { - "@types/hast": "^2.0.0", - "@types/unist": "^2.0.0", - "hast-util-is-element": "^2.0.0", - "unist-util-find-after": "^4.0.0" + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-to-text/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/hast-util-to-text/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, "node_modules/hast-util-whitespace": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", @@ -10786,7 +10874,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-2.0.1.tgz", "integrity": "sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==", - "dev": true, "license": "MIT", "funding": { "type": "github", @@ -13047,10 +13134,9 @@ } }, "node_modules/katex": { - "version": "0.16.21", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.21.tgz", - "integrity": "sha512-XvqR7FgOHtWupfMiigNzmh+MgUVmDGU2kXZm899ZkPfcuoPuFxyHmXsgATDpFZDAXCI8tvinaVcDo8PIIJSo4A==", - "dev": true, + "version": "0.16.27", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.27.tgz", + "integrity": "sha512-aeQoDkuRWSqQN6nSvVCEFvfXdqo1OQiCmmW1kc9xSdjutPv7BGO7pqY9sQRJpMOGrEdfDgF2TfRXe5eUAD2Waw==", "funding": [ "https://opencollective.com/katex", "https://github.com/sponsors/katex" @@ -13067,7 +13153,7 @@ "version": "8.3.0", "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "dev": true, + "license": "MIT", "engines": { "node": ">= 12" } @@ -13449,17 +13535,6 @@ "node": ">= 0.4" } }, - "node_modules/mathjax-full": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/mathjax-full/-/mathjax-full-3.2.2.tgz", - "integrity": "sha512-+LfG9Fik+OuI8SLwsiR02IVdjcnRCy5MufYLi0C3TdMT56L/pjB0alMVGgoWJF8pN9Rc7FESycZB9BMNWIid5w==", - "dependencies": { - "esm": "^3.2.25", - "mhchemparser": "^4.1.0", - "mj-context-menu": "^0.6.1", - "speech-rule-engine": "^4.0.6" - } - }, "node_modules/mdast-util-definitions": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", @@ -13615,7 +13690,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-2.0.2.tgz", "integrity": "sha512-8gmkKVp9v6+Tgjtq6SYx9kGPpTf6FVYRa53/DLh479aldR9AyP48qeVOgNZ5X7QUK7nOy4yw7vg6mbiGcs9jWQ==", - "dev": true, + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "longest-streak": "^3.0.0", @@ -13751,7 +13826,8 @@ "node_modules/mhchemparser": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/mhchemparser/-/mhchemparser-4.2.1.tgz", - "integrity": "sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==" + "integrity": "sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==", + "dev": true }, "node_modules/micromark": { "version": "3.2.0", @@ -13938,7 +14014,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-2.1.2.tgz", "integrity": "sha512-es0CcOV89VNS9wFmyn+wyFTKweXGW4CEvdaAca6SWRWPyYCbBisnjaHLjWO4Nszuiud84jCpkHsqAJoa768Pvg==", - "dev": true, + "license": "MIT", "dependencies": { "@types/katex": "^0.16.0", "katex": "^0.16.0", @@ -14399,10 +14475,15 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mj-context-menu": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/mj-context-menu/-/mj-context-menu-0.6.1.tgz", - "integrity": "sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA==" + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } }, "node_modules/monaco-editor": { "version": "0.55.1", @@ -14940,6 +15021,13 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -15077,6 +15165,33 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "node_modules/path-scurry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/path-to-regexp": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", @@ -16136,279 +16251,10 @@ "node": ">=6" } }, - "node_modules/rehype-mathjax": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/rehype-mathjax/-/rehype-mathjax-4.0.3.tgz", - "integrity": "sha512-QIwWH9U+r54nMQklVkT1qluxhKyzdPWz9dFwgel3BrseQsWZafRTDTUj8VR8/14nFuRIV2ChuCMz4zpACPoYvg==", - "dev": true, - "dependencies": { - "@types/hast": "^2.0.0", - "@types/mathjax": "^0.0.37", - "hast-util-from-dom": "^4.0.0", - "hast-util-to-text": "^3.1.0", - "jsdom": "^20.0.0", - "mathjax-full": "^3.0.0", - "unified": "^10.0.0", - "unist-util-visit": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-mathjax/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/rehype-mathjax/node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssom": "~0.3.6" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/rehype-mathjax/node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true, - "license": "MIT" - }, - "node_modules/rehype-mathjax/node_modules/data-urls": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", - "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "abab": "^2.0.6", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^11.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/rehype-mathjax/node_modules/html-encoding-sniffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-encoding": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/rehype-mathjax/node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/rehype-mathjax/node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/rehype-mathjax/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/rehype-mathjax/node_modules/jsdom": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", - "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "abab": "^2.0.6", - "acorn": "^8.8.1", - "acorn-globals": "^7.0.0", - "cssom": "^0.5.0", - "cssstyle": "^2.3.0", - "data-urls": "^3.0.2", - "decimal.js": "^10.4.2", - "domexception": "^4.0.0", - "escodegen": "^2.0.0", - "form-data": "^4.0.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.1", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.2", - "parse5": "^7.1.1", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.1.2", - "w3c-xmlserializer": "^4.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^2.0.0", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^11.0.0", - "ws": "^8.11.0", - "xml-name-validator": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "canvas": "^2.5.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/rehype-mathjax/node_modules/tough-cookie": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/rehype-mathjax/node_modules/tr46": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", - "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/rehype-mathjax/node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/rehype-mathjax/node_modules/w3c-xmlserializer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", - "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", - "dev": true, - "license": "MIT", - "dependencies": { - "xml-name-validator": "^4.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/rehype-mathjax/node_modules/whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/rehype-mathjax/node_modules/whatwg-mimetype": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/rehype-mathjax/node_modules/whatwg-url": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", - "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "^3.0.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/rehype-mathjax/node_modules/xml-name-validator": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", - "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12" - } - }, "node_modules/rehype-raw": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-6.1.1.tgz", "integrity": "sha512-d6AKtisSRtDRX4aSPsJGTfnzrX2ZkHQLE5kiUuGOeEoLpbEulFF4hj0mLPbsa+7vmguDKOVVEQdHKDSwoaIDsQ==", - "dev": true, "license": "MIT", "dependencies": { "@types/hast": "^2.0.0", @@ -16448,7 +16294,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-5.1.1.tgz", "integrity": "sha512-cE5T2R/xLVtfFI4cCePtiRn+e6jKMtFDR3P8V3qpv8wpKjwvHoBA4eJzvX+nVrnlNy0911bdGmuspCSwetfYHw==", - "dev": true, + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "mdast-util-math": "^2.0.0", @@ -17424,27 +17270,6 @@ "node": ">= 6" } }, - "node_modules/speech-rule-engine": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/speech-rule-engine/-/speech-rule-engine-4.0.7.tgz", - "integrity": "sha512-sJrL3/wHzNwJRLBdf6CjJWIlxC04iYKkyXvYSVsWVOiC2DSkHmxsqOhEeMsBA9XK+CHuNcsdkbFDnoUfAsmp9g==", - "dependencies": { - "commander": "9.2.0", - "wicked-good-xpath": "1.3.0", - "xmldom-sre": "0.1.31" - }, - "bin": { - "sre": "bin/sre" - } - }, - "node_modules/speech-rule-engine/node_modules/commander": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.2.0.tgz", - "integrity": "sha512-e2i4wANQiSXgnrBlIatyHtP1odfUp0BbV5Y5nEGbxtIrStkEOAAzCUirvLBNXHLr7kwLvJl6V+4V3XV9x7Wd9w==", - "engines": { - "node": "^12.20.0 || >=14" - } - }, "node_modules/sprintf-js": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", @@ -17955,51 +17780,6 @@ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" }, - "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/tldts": { "version": "6.1.86", "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", @@ -18403,13 +18183,32 @@ } }, "node_modules/unist-util-find-after": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-4.0.1.tgz", - "integrity": "sha512-QO/PuPMm2ERxC6vFXEPtmAutOopy5PknD+Oq64gGwxKtk4xwo9Z97t9Av1obPmGU0IyTa6EKYUfTrK2QJS3Ozw==", - "dev": true, + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "license": "MIT", "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0" + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-find-after/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/unist-util-find-after/node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" }, "funding": { "type": "opencollective", @@ -18479,6 +18278,7 @@ "version": "5.1.3", "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "license": "MIT", "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0" @@ -18672,7 +18472,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-4.1.0.tgz", "integrity": "sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw==", - "dev": true, "license": "MIT", "dependencies": { "@types/unist": "^2.0.0", @@ -18744,7 +18543,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", - "dev": true, "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -19241,7 +19039,8 @@ "node_modules/wicked-good-xpath": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/wicked-good-xpath/-/wicked-good-xpath-1.3.0.tgz", - "integrity": "sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw==" + "integrity": "sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw==", + "dev": true }, "node_modules/wildcard": { "version": "2.0.1", @@ -19308,6 +19107,19 @@ } } }, + "node_modules/xml-formatter": { + "version": "3.6.7", + "resolved": "https://registry.npmjs.org/xml-formatter/-/xml-formatter-3.6.7.tgz", + "integrity": "sha512-IsfFYJQuoDqtUlKhm4EzeoBOb+fQwzQVeyxxAQ0sThn/nFnQmyLPTplqq4yRhaOENH/tAyujD2TBfIYzUKB6hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-parser-xo": "^4.1.5" + }, + "engines": { + "node": ">= 16" + } + }, "node_modules/xml-name-validator": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", @@ -19318,6 +19130,16 @@ "node": ">=18" } }, + "node_modules/xml-parser-xo": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/xml-parser-xo/-/xml-parser-xo-4.1.5.tgz", + "integrity": "sha512-TxyRxk9sTOUg3glxSIY6f0nfuqRll2OEF8TspLgh5mZkLuBgheCn3zClcDSGJ58TvNmiwyCCuat4UajPud/5Og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, "node_modules/xmlbuilder": { "version": "15.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", @@ -19334,14 +19156,6 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, - "node_modules/xmldom-sre": { - "version": "0.1.31", - "resolved": "https://registry.npmjs.org/xmldom-sre/-/xmldom-sre-0.1.31.tgz", - "integrity": "sha512-f9s+fUkX04BxQf+7mMWAp5zk61pciie+fFLC9hX9UVvCeJQfNHRHXpeo5MPcR0EUf57PYLdt+ZO4f3Ipk2oZUw==", - "engines": { - "node": ">=0.1" - } - }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 38be271c6..1ce42cbd2 100644 --- a/package.json +++ b/package.json @@ -27,11 +27,12 @@ "ajv": "^8.17.1", "arg": "^5.0.2", "bcryptjs": "^2.4.3", - "better-react-mathjax": "^2.0.3", "clsx": "^1.2.1", "convert-source-map": "^2.0.0", "date-fns": "^2.30.0", "fast-dice-coefficient": "1.0.3", + "hast-util-from-dom": "^5.0.1", + "hast-util-to-text": "^4.0.2", "jszip": "^3.10.1", "material-ui-color": "^1.2.0", "material-ui-popup-state": "^1.9.3", @@ -45,9 +46,12 @@ "react-markdown": "^8.0.7", "react-resizable": "^3.0.5", "react-syntax-highlighter": "^15.6.6", + "rehype-raw": "^6.1.1", "remark-gfm": "^3.0.1", + "remark-math": "^5.1.1", "sprintf-js": "^1.1.3", - "tss-react": "^4.9.19" + "tss-react": "^4.9.19", + "unist-util-visit-parents": "^5.1.3" }, "description": "A cyberpunk-themed incremental game", "devDependencies": { @@ -56,6 +60,7 @@ "@babel/preset-react": "^7.27.1", "@babel/preset-typescript": "^7.27.1", "@electron/packager": "^18.4.4", + "@mathjax/src": "^4.1.0", "@microsoft/api-documenter": "^7.26.34", "@microsoft/api-extractor": "^7.52.13", "@pmmmwh/react-refresh-webpack-plugin": "^0.6.1", @@ -75,7 +80,6 @@ "babel-jest": "^29.7.0", "babel-loader": "^10.0.0", "babel-plugin-transform-barrels": "^1.0.17", - "copy-webpack-plugin": "^13.0.0", "css-loader": "^7.1.2", "csstype": "3.1.3", "electron": "^38.4.0", @@ -88,19 +92,16 @@ "jest-environment-jsdom": "^29.7.0", "jsdom": "^26.1.0", "lodash": "^4.17.21", - "mathjax-full": "^3.2.2", "monaco-editor": "^0.55.1", "monaco-editor-webpack-plugin": "^7.1.1", "prettier": "^2.8.8", "react-refresh": "^0.18.0", - "rehype-mathjax": "^4.0.3", - "rehype-raw": "^6.1.1", - "remark-math": "^5.1.1", "style-loader": "^4.0.0", "typescript": "^5.8.3", "webpack": "^5.100.2", "webpack-cli": "^6.0.1", - "webpack-dev-server": "^5.2.2" + "webpack-dev-server": "^5.2.2", + "xml-formatter": "^3.6.7" }, "overrides": { "react-syntax-highlighter": { @@ -123,8 +124,8 @@ "start:dev": "webpack serve --progress --mode development", "build": "bash ./tools/build.sh production", "build:dev": "bash ./tools/build.sh development", - "lint": "eslint --fix --ext js,jsx,ts,tsx --max-warnings 0 src test", - "lint:report": "eslint --ext js,jsx,ts,tsx --max-warnings 0 src test", + "lint": "eslint --fix --ext js,mjs,jsx,ts,mts,tsx --max-warnings 0 src test", + "lint:report": "eslint --ext js,mjs,jsx,ts,mts,tsx --max-warnings 0 src test", "preinstall": "node ./tools/engines-check/engines-check.js", "test": "jest", "test:watch": "jest --watch", diff --git a/src/Constants.ts b/src/Constants.ts index b1a827567..a18a1c455 100644 --- a/src/Constants.ts +++ b/src/Constants.ts @@ -28,6 +28,7 @@ export const CONSTANTS = { // Faction and Company favor-related things BaseFavorToDonate: 150, + // If we change this constant, we must update the "RepDonation" value in src/Documentation/data/MathNotation.json DonateMoneyToRepDivisor: 1e6, // NeuroFlux Governor Augmentation cost multiplier diff --git a/src/Corporation/ui/DivisionOverview.tsx b/src/Corporation/ui/DivisionOverview.tsx index 414e2d2d5..4f20d8495 100644 --- a/src/Corporation/ui/DivisionOverview.tsx +++ b/src/Corporation/ui/DivisionOverview.tsx @@ -1,7 +1,6 @@ // React Component for displaying an Division's overview information // (top-left panel in the Division UI) import React, { useState } from "react"; -import { MathJax } from "better-react-mathjax"; import { IndustryType } from "@enums"; import { hireAdVert } from "../Actions"; @@ -23,6 +22,8 @@ import Paper from "@mui/material/Paper"; import IconButton from "@mui/material/IconButton"; import HelpIcon from "@mui/icons-material/Help"; import Box from "@mui/material/Box"; +import MathNotation from "../../Documentation/data/MathNotation.json"; +import { MathNotationOutput } from "../../Documentation/ui/MathNotationOutput"; function MakeProductButton(): React.ReactElement { const corp = useCorporation(); @@ -132,8 +133,11 @@ export function DivisionOverview(props: DivisionOverviewProps): React.ReactEleme <> Multiplier for this industry's sales due to its awareness and popularity.
- {`\\(\\text{${division.industry} Industry: }\\alpha = ${division.advertisingFactor}\\)`} - {`\\(\\text{multiplier} = \\left((\\text{awareness}+1)^{\\alpha} \\times (\\text{popularity}+1)^{\\alpha} \\times \\frac{\\text{popularity}+0.001}{\\text{awareness}}\\right)^{0.85}\\)`} + + {division.industry} Industry: 𝞪 = {division.advertisingFactor} + +
+
`1\\;\\textit{${p}}`); + const prod = props.division.producedMaterials.map((materialName) => `1 ${materialName}`); if (props.division.makesProducts) { - prod.push("\\textit{Products}"); + prod.push("Products"); } - return {"\\(" + reqs.join("+") + `\\Rightarrow ` + prod.join("+") + "\\)"}; + return ( + + {reqs.join(" + ")} ⟹ {prod.join(" + ")} + + ); } diff --git a/src/Corporation/ui/Overview.tsx b/src/Corporation/ui/Overview.tsx index 0ac9fffaf..c7d724581 100644 --- a/src/Corporation/ui/Overview.tsx +++ b/src/Corporation/ui/Overview.tsx @@ -1,6 +1,5 @@ // React Component for displaying Corporation Overview info import React, { useState } from "react"; -import { MathJax } from "better-react-mathjax"; import { LevelableUpgrade } from "./LevelableUpgrade"; import { Unlock } from "./Unlock"; import { BribeFactionModal } from "./modals/BribeFactionModal"; @@ -36,6 +35,8 @@ import { ButtonWithTooltip } from "../../ui/Components/ButtonWithTooltip"; import { CreateCorporationModal } from "./modals/CreateCorporationModal"; import InfoIcon from "@mui/icons-material/Info"; import { CorruptibleText } from "../../ui/React/CorruptibleText"; +import MathNotation from "../../Documentation/data/MathNotation.json"; +import { MathNotationOutput } from "../../Documentation/ui/MathNotationOutput"; interface IProps { rerender: () => void; @@ -390,9 +391,9 @@ function DividendsStats({ profit }: IDividendsStatsProps): React.ReactElement { "TributeModifier". Formulas:

- {`\\(TotalDividends = DividendRate\\ast(Revenue - Expenses)\\ast 10\\)`} +
- {`\\(Dividend = \\left(OwnedShares\\ast\\frac{TotalDividends}{TotalShares}\\right)^{1 - TributeModifier}\\)`} + } > diff --git a/src/Documentation/data/MathNotation.json b/src/Documentation/data/MathNotation.json new file mode 100644 index 000000000..6f9f68607 --- /dev/null +++ b/src/Documentation/data/MathNotation.json @@ -0,0 +1,10 @@ +{ + "RepDonation": "reputation = \\frac{\\text{donation amount} \\cdot \\text{reputation multiplier}}{10^{6}}", + "CoreCost": "\\large{cost = 10^9 \\cdot 7.5 ^{\\text{cores}}}", + "HomeRAMCost": "\\large{cost = ram \\cdot 3.2 \\cdot 10^4 \\cdot 1.58^{log_2{(ram)}}} \\cdot HomeRamCostMult", + "FavorBonus": "\\huge{\\Delta r = \\Delta r \\times \\frac{100+favor}{100}}", + "RepToFavor": "\\huge{favor=\\log_{1.02}\\left(1+\\frac{r}{25000}\\right)}", + "CorpTotalDividends": "TotalDividends = DividendRate\\ast(Revenue - Expenses)\\ast 10", + "CorpDividend": "Dividend = \\left(OwnedShares\\ast\\frac{TotalDividends}{TotalShares}\\right)^{1 - TributeModifier}", + "CorpAdvertFactor": "\\text{multiplier} = \\left((\\text{awareness}+1)^{\\alpha} \\times (\\text{popularity}+1)^{\\alpha} \\times \\frac{\\text{popularity}+0.001}{\\text{awareness}}\\right)^{0.85}" +} diff --git a/src/Documentation/data/MathNotationOutput.json b/src/Documentation/data/MathNotationOutput.json new file mode 100644 index 000000000..85291f84e --- /dev/null +++ b/src/Documentation/data/MathNotationOutput.json @@ -0,0 +1 @@ +{"0.5\\ \\textit{Water}+0.2\\ \\textit{Chemicals} \\Rightarrow 1\\ \\textit{Plants}+1\\ \\textit{Food}":"0.5 Water+0.2 Chemicals1 Plants+1 Food","{c_{1}}":"c1","{c_{2}}":"c2","{c_{3}}":"c3","{c_{4}}":"c4","{s_{1}}":"s1","{s_{2}}":"s2","{s_{3}}":"s3","{s_{4}}":"s4","F(x,y,z,w) = \\sum_{i = 1}^{6}\\left( (1 + 0.002\\ast x)^{c_{1}}\\ast(1 + 0.002\\ast y)^{c_{2}}{\\ast(1 + 0.002\\ast z)}^{c_{3}}{\\ast(1 + 0.002\\ast w)}^{c_{4}} \\right)^{0.73}":"F(x,y,z,w)=i=16((1+0.002x)c1(1+0.002y)c2(1+0.002z)c3(1+0.002w)c4)0.73","F(x,y,z,w) = (1 + 0.002\\ast x)^{c_{1}}\\ast(1 + 0.002\\ast y)^{c_{2}}{\\ast(1 + 0.002\\ast z)}^{c_{3}}{\\ast(1 + 0.002\\ast w)}^{c_{4}}":"F(x,y,z,w)=(1+0.002x)c1(1+0.002y)c2(1+0.002z)c3(1+0.002w)c4","G(x,y,z,w) = s_{1}\\ast x + s_{2}\\ast y + s_{3}\\ast z + s_{4}\\ast w = S":"G(x,y,z,w)=s1x+s2y+s3z+s4w=S","F(x,y,z,w)":"F(x,y,z,w)","G(x,y,z,w)":"G(x,y,z,w)","\\begin{cases} \\frac{\\partial F}{\\partial x} &= \\lambda\\frac{\\partial G}{\\partial x} \\newline \\frac{\\partial F}{\\partial y} &= \\lambda\\frac{\\partial G}{\\partial y} \\newline \\frac{\\partial F}{\\partial z} &= \\lambda\\frac{\\partial G}{\\partial z} \\newline \\frac{\\partial F}{\\partial w} &= \\lambda\\frac{\\partial G}{\\partial w} \\newline G(x,y,z,w) &= S\\end{cases}":"{Fx=λGxFy=λGyFz=λGzFw=λGwG(x,y,z,w)=S","x\\ast s_{1} = \\frac{S - 500\\ast\\left( \\frac{s_{1}}{c_{1}}\\ast\\left( c_{2} + c_{3} + c_{4} \\right) - \\left( s_{2} + s_{3} + s_{4} \\right) \\right)}{\\frac{c_{1} + c_{2} + c_{3} + c_{4}}{c_{1}}}":"xs1=S500(s1c1(c2+c3+c4)(s2+s3+s4))c1+c2+c3+c4c1","y\\ast s_{2} = \\frac{S - 500\\ast\\left( \\frac{s_{2}}{c_{2}}\\ast\\left( c_{1} + c_{3} + c_{4} \\right) - \\left( s_{1} + s_{3} + s_{4} \\right) \\right)}{\\frac{c_{1} + c_{2} + c_{3} + c_{4}}{c_{2}}}":"ys2=S500(s2c2(c1+c3+c4)(s1+s3+s4))c1+c2+c3+c4c2","z\\ast s_{3} = \\frac{S - 500\\ast\\left( \\frac{s_{3}}{c_{3}}\\ast\\left( c_{1} + c_{2} + c_{4} \\right) - \\left( s_{1} + s_{2} + s_{4} \\right) \\right)}{\\frac{c_{1} + c_{2} + c_{3} + c_{4}}{c_{3}}}":"zs3=S500(s3c3(c1+c2+c4)(s1+s2+s4))c1+c2+c3+c4c3","w\\ast s_{4} = \\frac{S - 500\\ast\\left( \\frac{s_{4}}{c_{4}}\\ast\\left( c_{1} + c_{2} + c_{3} \\right) - \\left( s_{1} + s_{2} + s_{3} \\right) \\right)}{\\frac{c_{1} + c_{2} + c_{3} + c_{4}}{c_{4}}}":"ws4=S500(s4c4(c1+c2+c3)(s1+s2+s3))c1+c2+c3+c4c4","k = 0.002":"k=0.002","\\begin{cases}\\frac{\\partial F}{\\partial x} = \\left( k\\ast c_{1}\\ast(1 + k\\ast x)^{c_{1} - 1} \\right)\\ast(1 + k\\ast y)^{c_{2}}\\ast(1 + k\\ast z)^{c_{3}}\\ast(1 + k\\ast w)^{c_{4}} = \\lambda\\ast s_{1} \\newline \\frac{\\partial F}{\\partial y} = (1 + k\\ast x)^{c_{1}}\\ast\\left( k\\ast c_{2}\\ast(1 + k\\ast y)^{c_{2} - 1} \\right)\\ast(1 + k\\ast z)^{c_{3}}\\ast(1 + k\\ast w)^{c_{4}} = \\lambda\\ast s_{2} \\end{cases}":"{Fx=(kc1(1+kx)c11)(1+ky)c2(1+kz)c3(1+kw)c4=λs1Fy=(1+kx)c1(kc2(1+ky)c21)(1+kz)c3(1+kw)c4=λs2","k\\ast c_{1}\\ast(1 + k\\ast x)^{- 1}\\ast s_{2} = k\\ast c_{2}\\ast(1 + k\\ast y)^{- 1}\\ast s_{1}":"kc1(1+kx)1s2=kc2(1+ky)1s1","c_{1}\\ast s_{2}\\ast(1 + k\\ast y) = c_{2}\\ast s_{1}\\ast(1 + k\\ast x)":"c1s2(1+ky)=c2s1(1+kx)","1 + k\\ast y = \\frac{c_{2}\\ast s_{1}}{c_{1}\\ast s_{2}}\\ast(1 + k\\ast x)":"1+ky=c2s1c1s2(1+kx)","y = \\frac{c_{2}\\ast s_{1} + k\\ast x\\ast c_{2}\\ast s_{1} - c_{1}\\ast s_{2}}{k\\ast c_{1}\\ast s_{2}}":"y=c2s1+kxc2s1c1s2kc1s2","y\\ast s_{2} = \\frac{c_{2}\\ast s_{1}\\ast s_{2} + k\\ast x\\ast c_{2}\\ast s_{1}\\ast s_{2} - c_{1}\\ast s_{2}\\ast s_{2}}{k\\ast c_{1}\\ast s_{2}}":"ys2=c2s1s2+kxc2s1s2c1s2s2kc1s2","y\\ast s_{2} = \\frac{c_{2}\\ast s_{1}}{k\\ast c_{1}} + \\frac{x\\ast c_{2}\\ast s_{1}}{c_{1}} - \\frac{s_{2}}{k}":"ys2=c2s1kc1+xc2s1c1s2k","y\\ast s_{2} = \\frac{c_{2}}{c_{1}}\\ast x\\ast s_{1} + \\frac{1}{k}\\ast\\frac{c_{2}\\ast s_{1} - c_{1}\\ast s_{2}}{c_{1}}":"ys2=c2c1xs1+1kc2s1c1s2c1","y\\ast s_{2} = \\frac{c_{2}}{c_{1}}\\ast x\\ast s_{1} + 500\\ast\\frac{c_{2}\\ast s_{1} - c_{1}\\ast s_{2}}{c_{1}}":"ys2=c2c1xs1+500c2s1c1s2c1","z\\ast s_{3} = \\frac{c_{3}}{c_{1}}\\ast x\\ast s_{1} + 500\\ast\\frac{c_{3}\\ast s_{1} - c_{1}\\ast s_{3}}{c_{1}}":"zs3=c3c1xs1+500c3s1c1s3c1","w\\ast s_{4} = \\frac{c_{4}}{c_{1}}\\ast x\\ast s_{1} + 500\\ast\\frac{c_{4}\\ast s_{1} - c_{1}\\ast s_{4}}{c_{1}}":"ws4=c4c1xs1+500c4s1c1s4c1","x\\ast s_{1} + y\\ast s_{2} + z\\ast s_{3} + w\\ast s_{4} = S":"xs1+ys2+zs3+ws4=S","x\\ast s_{1} + \\frac{c_{2}}{c_{1}}\\ast x\\ast s_{1} + 500\\ast\\frac{c_{2}\\ast s_{1} - c_{1}\\ast s_{2}}{c_{1}} + \\frac{c_{3}}{c_{1}}\\ast x\\ast s_{1} + 500\\ast\\frac{c_{3}\\ast s_{1} - c_{1}\\ast s_{3}}{c_{1}} + \\frac{c_{4}}{c_{1}}\\ast x\\ast s_{1} + 500\\ast\\frac{c_{4}\\ast s_{1} - c_{1}\\ast s_{4}}{c_{1}} = S":"xs1+c2c1xs1+500c2s1c1s2c1+c3c1xs1+500c3s1c1s3c1+c4c1xs1+500c4s1c1s4c1=S","\\frac{x\\ast s_{1}\\ast\\left( c_{1} + c_{2} + c_{3} + c_{4} \\right)}{c_{1}} + \\frac{500}{c_{1}}\\ast\\left( c_{2}\\ast s_{1} - c_{1}\\ast s_{2} + c_{3}\\ast s_{1} - c_{1}\\ast s_{3} + c_{4}\\ast s_{1} - c_{1}\\ast s_{4} \\right) = S":"xs1(c1+c2+c3+c4)c1+500c1(c2s1c1s2+c3s1c1s3+c4s1c1s4)=S","\\frac{x\\ast s_{1}\\ast\\left( c_{1} + c_{2} + c_{3} + c_{4} \\right)}{c_{1}} + \\frac{500}{c_{1}}\\ast\\left( s_{1}\\ast\\left( c_{2} + c_{3} + c_{4}\\ \\right) - c_{1}\\ast\\left( s_{2} + s_{3} + s_{4} \\right) \\right) = S":"xs1(c1+c2+c3+c4)c1+500c1(s1(c2+c3+c4 )c1(s2+s3+s4))=S","x\\ast s_{1}\\ast\\frac{c_{1} + c_{2} + c_{3} + c_{4}}{c_{1}} + \\frac{500}{c_{1}}\\ast\\left( s_{1}\\ast\\left( c_{2} + c_{3} + c_{4}\\ \\right) - c_{1}\\ast\\left( s_{2} + s_{3} + s_{4} \\right) \\right) = S":"xs1c1+c2+c3+c4c1+500c1(s1(c2+c3+c4 )c1(s2+s3+s4))=S","AmountOfChange = Random(0,3)*0.0004":"AmountOfChange=Random(0,3)0.0004","TotalEmployeesProd = OperationsProd + EngineerProd + ManagementProd":"TotalEmployeesProd=OperationsProd+EngineerProd+ManagementProd","ManagementFactor = 1 + \\frac{ManagementProd}{1.2\\ast TotalEmployeesProd}":"ManagementFactor=1+ManagementProd1.2TotalEmployeesProd","EmployeeProductionMultiplier = \\left( (OperationsProd)^{0.4} + (EngineerProd)^{0.3} \\right)\\ast ManagementFactor":"EmployeeProductionMultiplier=((OperationsProd)0.4+(EngineerProd)0.3)ManagementFactor","BalancingMultiplier = 0.05":"BalancingMultiplier=0.05","OfficeMultiplier = BalancingMultiplier\\ast EmployeeProductionMultiplier":"OfficeMultiplier=BalancingMultiplierEmployeeProductionMultiplier","OfficeMultiplier = 0.5\\ast BalancingMultiplier\\ast EmployeeProductionMultiplier":"OfficeMultiplier=0.5BalancingMultiplierEmployeeProductionMultiplier","AssetDelta = \\frac{TotalAssets - PreviousTotalAssets}{10}":"AssetDelta=TotalAssetsPreviousTotalAssets10","Valuation = \\left( 10^{10} + \\frac{Funds}{3} + AssetDelta\\ast 315000 \\right)\\ast\\left( \\sqrt[12]{1.1} \\right)^{NumberOfOfficesAndWarehouses}":"Valuation=(1010+Funds3+AssetDelta315000)(1.112)NumberOfOfficesAndWarehouses","AssetDelta = AssetDelta\\ast(1 - DividendRate)":"AssetDelta=AssetDelta(1DividendRate)","Valuation = (Funds + AssetDelta\\ast 85000)\\ast\\left(\\sqrt[12]{1.1}\\right)^{NumberOfOfficesAndWarehouses}":"Valuation=(Funds+AssetDelta85000)(1.112)NumberOfOfficesAndWarehouses","10^{10}":"1010","FundingRoundShares = [0.1, 0.35, 0.25, 0.2]":"FundingRoundShares=[0.1,0.35,0.25,0.2]","FundingRoundMultiplier = [3, 2, 2, 1.5]":"FundingRoundMultiplier=[3,2,2,1.5]","Offer = CorporationValuation\\ast FundingRoundShares\\ast FundingRoundMultiplier":"Offer=CorporationValuationFundingRoundSharesFundingRoundMultiplier","TributeModifier = 1.15 - CorporationSoftcap":"TributeModifier=1.15CorporationSoftcap","TotalDividends = DividendRate\\ast(Revenue - Expenses)\\ast 10":"TotalDividends=DividendRate(RevenueExpenses)10","Dividend = \\left(OwnedShares\\ast\\frac{TotalDividends}{TotalShares}\\right)^{1 - TributeModifier}":"Dividend=(OwnedSharesTotalDividendsTotalShares)1TributeModifier","RetainedEarning = (1 - DividendRate)\\ast(Revenue - Expenses)\\ast 10":"RetainedEarning=(1DividendRate)(RevenueExpenses)10","OwnershipPercentage = \\frac{OwnedShares}{TotalShares}":"OwnershipPercentage=OwnedSharesTotalShares","TargetSharePrice = \\frac{CorporationValuation*\\left(0.5+\\sqrt{OwnershipPercentage}\\right)}{TotalShares}":"TargetSharePrice=CorporationValuation(0.5+OwnershipPercentage)TotalShares","SharePrice = \\begin{cases} SharePrice\\ast(1 + Math.random()\\ast 0.01), & SharePrice \\leq TargetSharePrice \\newline SharePrice\\ast(1 - Math.random()\\ast 0.01), & SharePrice > TargetSharePrice\\end{cases}":"SharePrice={SharePrice(1+Math.random()0.01),SharePriceTargetSharePriceSharePrice(1Math.random()0.01),SharePrice>TargetSharePrice","NewOwnershipPercentage = \\frac{OwnedShares}{TotalShares+NewShares}":"NewOwnershipPercentage=OwnedSharesTotalShares+NewShares","NewSharePrice = \\frac{CorporationValuation\\ast\\left(0.5+\\sqrt{NewOwnershipPercentage}\\right)}{TotalShares}":"NewSharePrice=CorporationValuation(0.5+NewOwnershipPercentage)TotalShares","Profit = {NewShares\\ast(SharePrice + NewSharePrice)}\\ast{0.5}":"Profit=NewShares(SharePrice+NewSharePrice)0.5","Cooldown = DefaultCooldown\\ast\\frac{TotalShares}{10^{9}}":"Cooldown=DefaultCooldownTotalShares109","MaxPrivateShares = {NewShares}\\ast{0.5}\\ast\\frac{InvestorShares}{TotalShares}":"MaxPrivateShares=NewShares0.5InvestorSharesTotalShares","InvestorShares = InvestorShares + PrivateShares":"InvestorShares=InvestorShares+PrivateShares","IssuedShares = IssuedShares + NewShares - PrivateShares":"IssuedShares=IssuedShares+NewSharesPrivateShares","10^{14}":"1014","10^6":"106","OwnershipPercentage = \\frac{OwnedShares - ProcessedShares}{TotalShares}":"OwnershipPercentage=OwnedSharesProcessedSharesTotalShares","TargetSharePrice = \\frac{CorporationValuation\\ast\\left(0.5 + \\sqrt{OwnershipPercentage}\\right)}{TotalShares}":"TargetSharePrice=CorporationValuation(0.5+OwnershipPercentage)TotalShares","SharePrice = \\begin{cases} SharePrice\\ast 1.005, SharePrice \\leq TargetSharePrice \\newline SharePrice\\ast 0.995, SharePrice > TargetSharePrice\\end{cases}":"SharePrice={SharePrice1.005,SharePriceTargetSharePriceSharePrice0.995,SharePrice>TargetSharePrice","\\left(100-\\frac{700}{10}\\right)=\\left(-100+\\frac{700}{10}\\right)\\ast(-1)=\\left(IPROD+\\frac{IINV}{10}\\right)\\ast(-1)":"(10070010)=(100+70010)(1)=(IPROD+IINV10)(1)","TotalExperienceGain = 0.0015\\ast(TotalEmployees - UnassignedEmployees + InternEmployees\\ast 9)":"TotalExperienceGain=0.0015(TotalEmployeesUnassignedEmployees+InternEmployees9)","Salary = 3\\ast TotalEmployees\\ast\\left(AvgIntelligence+AvgCharisma+AvgCreativity+AvgEfficiency+\\frac{TotalExperience}{TotalEmployees}\\right)":"Salary=3TotalEmployees(AvgIntelligence+AvgCharisma+AvgCreativity+AvgEfficiency+TotalExperienceTotalEmployees)","UpgradeCost = BasePrice\\ast\\left( \\frac{\\sqrt[3]{1.09} - 1}{0.09} \\right)\\ast{1.09}^{\\frac{CurrentSize}{3}}":"UpgradeCost=BasePrice(1.09310.09)1.09CurrentSize3","UpgradeCost_{From\\ 3\\ to\\ n} = \\sum_{k = 3}^{n - 1}{BasePrice\\ast\\left( \\frac{\\sqrt[3]{1.09} - 1}{0.09} \\right)\\ast{1.09}^{\\frac{k}{3}}}":"UpgradeCostFrom 3 to n=k=3n1BasePrice(1.09310.09)1.09k3","UpgradeCost_{From\\ 3\\ to\\ n} = \\sum_{k = 3}^{n - 1}{BasePrice\\ast\\left( \\frac{\\sqrt[3]{1.09} - 1}{0.09} \\right)\\ast\\left( \\sqrt[3]{1.09} \\right)^{k}}":"UpgradeCostFrom 3 to n=k=3n1BasePrice(1.09310.09)(1.093)k","UpgradeCost_{From\\ 3\\ to\\ n} = BasePrice\\ast\\left( \\frac{\\sqrt[3]{1.09} - 1}{0.09} \\right)\\ast\\left( \\frac{\\left( \\sqrt[3]{1.09} \\right)^{n} - 1.09}{\\sqrt[3]{1.09} - 1} \\right)":"UpgradeCostFrom 3 to n=BasePrice(1.09310.09)((1.093)n1.091.0931)","UpgradeCost_{From\\ 3\\ to\\ n} = BasePrice\\ast\\left( \\frac{{1.09}^{\\frac{n}{3}} - 1.09}{0.09} \\right)":"UpgradeCostFrom 3 to n=BasePrice(1.09n31.090.09)","UpgradeCost_{From\\ a\\ to\\ b} = BasePrice\\ast\\left( \\frac{{1.09}^{\\frac{b}{3}} - {1.09}^{\\frac{a}{3}}}{0.09} \\right)":"UpgradeCostFrom a to b=BasePrice(1.09b31.09a30.09)","MaxSize = 3\\ast\\log_{1.09}\\left( MaxCost\\ast\\frac{0.09}{BasePrice} + {1.09}^{\\frac{CurrentSize}{3}} \\right)":"MaxSize=3log1.09(MaxCost0.09BasePrice+1.09CurrentSize3)","InternMultiplier = 0.002\\ast Min\\left(\\frac{1}{9},\\frac{InternEmployees}{TotalEmployees}-\\frac{1}{9}\\right)\\ast 9":"InternMultiplier=0.002Min(19,InternEmployeesTotalEmployees19)9","PenaltyMultiplier = \\begin{cases}0, & (CorpFunds > 0) \\vee (DivisionLastCycleRevenue > DivisionLastCycleExpenses) \\newline 0.001, & (CorpFunds < 0) \\land (DivisionLastCycleRevenue < DivisionLastCycleExpenses)\\end{cases}":"PenaltyMultiplier={0,(CorpFunds>0)(DivisionLastCycleRevenue>DivisionLastCycleExpenses)0.001,(CorpFunds<0)(DivisionLastCycleRevenue<DivisionLastCycleExpenses)","PerfMult = \\begin{cases}1.002, & TotalEmployees < 9 \\newline 1 + InternMultiplier - PenaltyMultiplier, & TotalEmployees \\geq 9\\end{cases}":"PerfMult={1.002,TotalEmployees<91+InternMultiplierPenaltyMultiplier,TotalEmployees9","PartyMult = 1 + \\frac{PartyCostPerEmployee}{10^{7}}":"PartyMult=1+PartyCostPerEmployee107","IncreaseOfMorale = (PartyMult - 1)\\ast 10":"IncreaseOfMorale=(PartyMult1)10","IncreaseOfMorale = \\frac{PartyCostPerEmployee}{10^{6}}":"IncreaseOfMorale=PartyCostPerEmployee106","\\left( CurrentMorale\\ast PerfMult + \\frac{PartyCostPerEmployee}{10^{6}} \\right)\\ast\\left( 1 + \\frac{PartyCostPerEmployee}{10^{7}} \\right) = MaxMorale":"(CurrentMoralePerfMult+PartyCostPerEmployee106)(1+PartyCostPerEmployee107)=MaxMorale","a = CurrentMorale":"a=CurrentMorale","b = MaxMorale":"b=MaxMorale","k = PerfMult":"k=PerfMult","x = PartyCostPerEmployee":"x=PartyCostPerEmployee","\\left( a\\ast k + \\frac{x}{10^{6}} \\right)\\ast\\left( 1 + \\frac{x}{10^{7}} \\right) = b":"(ak+x106)(1+x107)=b","x_{1} = - 500000\\ast\\left( \\sqrt{(a\\ast k - 10)^{2} + 40\\ast b} + a\\ast k + 10 \\right)":"x1=500000((ak10)2+40b+ak+10)","x_{2} = 500000\\ast\\left( \\sqrt{(a\\ast k - 10)^{2} + 40\\ast b} - a\\ast k - 10 \\right)":"x2=500000((ak10)2+40bak10)","x_{1}":"x1","x_{2}":"x2","ProductionBase = AvgMorale\\ast AvgEnergy\\ast 10^{-4}":"ProductionBase=AvgMoraleAvgEnergy104","Exp = \\frac{TotalExperience}{TotalEmployees}":"Exp=TotalExperienceTotalEmployees","ProductionMultiplier = 0.6\\ast IntelligenceMult + 0.1\\ast CharismaMult + Exp + 0.5\\ast CreativityMult + EfficiencyMult":"ProductionMultiplier=0.6IntelligenceMult+0.1CharismaMult+Exp+0.5CreativityMult+EfficiencyMult","ProductionMultiplier = IntelligenceMult + 0.1\\ast CharismaMult + 1.5\\ast Exp + EfficiencyMult":"ProductionMultiplier=IntelligenceMult+0.1CharismaMult+1.5Exp+EfficiencyMult","ProductionMultiplier = 0.4\\ast IntelligenceMult + CharismaMult + 0.5\\ast Exp":"ProductionMultiplier=0.4IntelligenceMult+CharismaMult+0.5Exp","ProductionMultiplier = 2\\ast CharismaMult + Exp + 0.2\\ast CreativityMult + 0.7\\ast EfficiencyMult":"ProductionMultiplier=2CharismaMult+Exp+0.2CreativityMult+0.7EfficiencyMult","ProductionMultiplier = 1.5\\ast IntelligenceMult + 0.8\\ast Exp + CreativityMult + 0.5\\ast EfficiencyMult":"ProductionMultiplier=1.5IntelligenceMult+0.8Exp+CreativityMult+0.5EfficiencyMult","EmployeesJobCount = office.employeeJobs[JobName]":"EmployeesJobCount=office.employeeJobs[JobName]","EmployeeProductionByJob = EmployeesJobCount\\ast ProductionMultiplier\\ast ProductionBase":"EmployeeProductionByJob=EmployeesJobCountProductionMultiplierProductionBase","n = {Number\\ of\\ input\\ materials}":"n=Number of input materials","ProductMarketPriceMult = 5":"ProductMarketPriceMult=5","ProductMarketPrice = ProductMarketPriceMult\\ast\\sum_{i = 1}^{n}{MaterialMarketPrice_i\\ast MaterialCoefficient_i}":"ProductMarketPrice=ProductMarketPriceMulti=1nMaterialMarketPriceiMaterialCoefficienti","MaterialMarkupLimit = \\frac{MaterialQuality}{MaterialMarkup}":"MaterialMarkupLimit=MaterialQualityMaterialMarkup","ProductMarkupLimit = \\frac{Max(ProductEffectiveRating,0.001)}{ProductMarkup}":"ProductMarkupLimit=Max(ProductEffectiveRating,0.001)ProductMarkup","MaxSalesVolume = PotentialSalesVolume\\ast MarkupMultiplier":"MaxSalesVolume=PotentialSalesVolumeMarkupMultiplier","PotentialSalesVolume = \\ ItemMultiplier\\ast BusinessFactor\\ast AdvertFactor\\ast MarketFactor\\ast SaleBotsBonus\\ast ResearchBonus":"PotentialSalesVolume= ItemMultiplierBusinessFactorAdvertFactorMarketFactorSaleBotsBonusResearchBonus","ItemMultiplier = MaterialQuality + 0.001":"ItemMultiplier=MaterialQuality+0.001","ItemMultiplier = 0.5\\ast(ProductEffectiveRating)^{0.65}":"ItemMultiplier=0.5(ProductEffectiveRating)0.65","{BusinessFactor = (BusinessProduction)}^{0.26} + \\left({BusinessProduction}\\ast{0.0001}\\right)":"BusinessFactor=(BusinessProduction)0.26+(BusinessProduction0.0001)","AwarenessFactor = (Awareness + 1)^{IndustryAdvertisingFactor}":"AwarenessFactor=(Awareness+1)IndustryAdvertisingFactor","PopularityFactor = (Popularity + 1)^{IndustryAdvertisingFactor}":"PopularityFactor=(Popularity+1)IndustryAdvertisingFactor","RatioFactor = \\begin{cases}Max(0.01,\\frac{Popularity + 0.001}{Awareness}), & Awareness \\neq 0 \\newline 0.01, & Awareness = 0 \\end{cases}":"RatioFactor={Max(0.01,Popularity+0.001Awareness),Awareness00.01,Awareness=0","AdvertFactor = (AwarenessFactor\\ast PopularityFactor\\ast RatioFactor)^{0.85}":"AdvertFactor=(AwarenessFactorPopularityFactorRatioFactor)0.85","MarketFactor = Max\\left(0.1,{Demand\\ast(100 - Competition)}\\ast{0.01}\\right)":"MarketFactor=Max(0.1,Demand(100Competition)0.01)","MarkupMultiplier = \\begin{cases}10^{12} & SellingPrice \\in (-\\infty, 0] \\newline \\frac{MarketPrice}{SellingPrice} & SellingPrice \\in (0, MarketPrice] \\newline 1 & SellingPrice \\in (MarketPrice, MarketPrice + MarkupLimit] \\newline \\left(\\frac{MarkupLimit}{SellingPrice - MarketPrice}\\right)^{2} & SellingPrice \\in (MarketPrice + MarkupLimit, \\infty) \\end{cases}":"MarkupMultiplier={1012SellingPrice(,0]MarketPriceSellingPriceSellingPrice(0,MarketPrice]1SellingPrice(MarketPrice,MarketPrice+MarkupLimit](MarkupLimitSellingPriceMarketPrice)2SellingPrice(MarketPrice+MarkupLimit,)","ExpectedSalesVolume = \\frac{StoredUnits}{10}":"ExpectedSalesVolume=StoredUnits10","MaxSalesVolume = ExpectedSalesVolume":"MaxSalesVolume=ExpectedSalesVolume","PotentialSalesVolume\\ast MarkupMultiplier = ExpectedSalesVolume":"PotentialSalesVolumeMarkupMultiplier=ExpectedSalesVolume","PotentialSalesVolume\\ast\\left(\\frac{MarkupLimit}{SellingPrice - MarketPrice}\\right)^{2} = ExpectedSalesVolume":"PotentialSalesVolume(MarkupLimitSellingPriceMarketPrice)2=ExpectedSalesVolume","\\frac{MarkupLimit}{SellingPrice - MarketPrice} = \\sqrt{\\frac{ExpectedSalesVolume}{PotentialSalesVolume}}":"MarkupLimitSellingPriceMarketPrice=ExpectedSalesVolumePotentialSalesVolume","SellingPrice = \\frac{MarkupLimit\\ast\\sqrt{PotentialSalesVolume}}{\\sqrt{ExpectedSalesVolume}} + MarketPrice":"SellingPrice=MarkupLimitPotentialSalesVolumeExpectedSalesVolume+MarketPrice","MarkupLimit = (SellingPrice - MarketPrice)\\ast\\sqrt{\\frac{ActualSalesVolume}{M}}":"MarkupLimit=(SellingPriceMarketPrice)ActualSalesVolumeM","TotalEmployeeProd = OperationsProd + EngineerProd + ManagementProd":"TotalEmployeeProd=OperationsProd+EngineerProd+ManagementProd","ManagementFactor = 1 + \\frac{ManagementProd}{1.2\\ast TotalEmployeeProd}":"ManagementFactor=1+ManagementProd1.2TotalEmployeeProd","ProductDevelopmentMultiplier = \\left( (EngineerProd)^{0.34} + (OperationsProd)^{0.2} \\right)\\ast ManagementFactor":"ProductDevelopmentMultiplier=((EngineerProd)0.34+(OperationsProd)0.2)ManagementFactor","Progress = 0.01\\ast ProductDevelopmentMultiplier":"Progress=0.01ProductDevelopmentMultiplier","DevelopmentProgress = DevelopmentProgress + Progress":"DevelopmentProgress=DevelopmentProgress+Progress","CreationJobFactors\\lbrack JobName\\rbrack = CreationJobFactors\\lbrack JobName\\rbrack + {\\lbrace EmployeeJob\\rbrace Prod\\ast Progress}\\ast{0.01}":"CreationJobFactors[JobName]=CreationJobFactors[JobName]+{EmployeeJob}ProdProgress0.01","A = \\ CreationJobFactors\\lbrack Engineer\\rbrack":"A= CreationJobFactors[Engineer]","B = \\ CreationJobFactors\\lbrack Management\\rbrack":"B= CreationJobFactors[Management]","C = \\ CreationJobFactors\\lbrack RnD\\rbrack":"C= CreationJobFactors[RnD]","D = \\ CreationJobFactors\\lbrack Operations\\rbrack":"D= CreationJobFactors[Operations]","E = \\ CreationJobFactors\\lbrack Business\\rbrack":"E= CreationJobFactors[Business]","TotalCreationJobFactors = A + B + C + D + E":"TotalCreationJobFactors=A+B+C+D+E","EngineerRatio = \\frac{A}{TotalCreationJobFactors}":"EngineerRatio=ATotalCreationJobFactors","ManagementRatio = \\frac{B}{TotalCreationJobFactors}":"ManagementRatio=BTotalCreationJobFactors","RnDRatio = \\frac{C}{TotalCreationJobFactors}":"RnDRatio=CTotalCreationJobFactors","OperationsRatio = \\frac{D}{TotalCreationJobFactors}":"OperationsRatio=DTotalCreationJobFactors","BusinessRatio = \\frac{E}{TotalCreationJobFactors}":"BusinessRatio=ETotalCreationJobFactors","DesignInvestMult = 1 + {(DesignInvestment)^{0.1}}\\ast{0.01}":"DesignInvestMult=1+(DesignInvestment)0.10.01","ScienceMult = 1 + {(RP)^{ResearchFactor}}\\ast{0.00125}":"ScienceMult=1+(RP)ResearchFactor0.00125","BalanceMult = 1.2\\ast EngineerRatio + 0.9\\ast ManagementRatio + 1.3\\ast RnDRatio + 1.5\\ast OperationsRatio + BusinessRatio":"BalanceMult=1.2EngineerRatio+0.9ManagementRatio+1.3RnDRatio+1.5OperationsRatio+BusinessRatio","TotalMult = BalanceMult\\ast DesignInvestMult\\ast ScienceMult":"TotalMult=BalanceMultDesignInvestMultScienceMult","TotalMult\\ast (0.1\\ast A + 0.05\\ast B + 0.05\\ast C + 0.02\\ast D + 0.02\\ast E)":"TotalMult(0.1A+0.05B+0.05C+0.02D+0.02E)","TotalMult\\ast (0.15\\ast A + 0.02\\ast B + 0.02\\ast C + 0.02\\ast D + 0.02\\ast E)":"TotalMult(0.15A+0.02B+0.02C+0.02D+0.02E)","TotalMult\\ast (0.05\\ast A + 0.02\\ast B + 0.08\\ast C + 0.05\\ast D + 0.05\\ast E)":"TotalMult(0.05A+0.02B+0.08C+0.05D+0.05E)","TotalMult\\ast (0.02\\ast A + 0.08\\ast B + 0.02\\ast C + 0.05\\ast D + 0.08\\ast E)":"TotalMult(0.02A+0.08B+0.02C+0.05D+0.08E)","TotalMult\\ast (0.08\\ast B + 0.05\\ast C + 0.02\\ast D + 0.1\\ast E)":"TotalMult(0.08B+0.05C+0.02D+0.1E)","TotalMult\\ast (0.08\\ast A + 0.05\\ast B + 0.02\\ast C + 0.05\\ast D + 0.05\\ast E)":"TotalMult(0.08A+0.05B+0.02C+0.05D+0.05E)","ProductRating = \\sum_{i = 1}^{6}{{ProductStat}_i\\ast{StatCoefficient}_i}":"ProductRating=i=16ProductStatiStatCoefficienti","AdvertInvestMult = 1 + {(AdvertisingInvestment)^{0.1}}\\ast{0.01}":"AdvertInvestMult=1+(AdvertisingInvestment)0.10.01","BusinessManagementRatio = Max\\left( BusinessRatio + ManagementRatio,\\ \\left( \\frac{1}{TotalCreationJobFactors} \\right) \\right)":"BusinessManagementRatio=Max(BusinessRatio+ManagementRatio, (1TotalCreationJobFactors))","ProductMarkup = \\frac{100}{AdvertInvestMult\\ast(ProductQuality + 0.001)^{0.65}\\ast BusinessManagementRatio}":"ProductMarkup=100AdvertInvestMult(ProductQuality+0.001)0.65BusinessManagementRatio","Demand = \\begin{cases}Min(100,AdvertInvestMult\\ast(100\\ast(Popularity/Awareness))), & Awareness \\neq 0 \\newline 20, & Awareness = 0 \\end{cases}":"Demand={Min(100,AdvertInvestMult(100(Popularity/Awareness))),Awareness020,Awareness=0","Competition = Random(0,70)":"Competition=Random(0,70)","ProductSize = \\sum_{i = 1}^{NumberOfInputMaterials}{{InputMaterialSize}_i\\ast{InputMaterialCoefficient}_i}":"ProductSize=i=1NumberOfInputMaterialsInputMaterialSizeiInputMaterialCoefficienti","Quality = \\frac{Quality\\ast CurrentQuantity + BuyAmount}{CurrentQuantity + BuyAmount}":"Quality=QualityCurrentQuantity+BuyAmountCurrentQuantity+BuyAmount","Quality = \\frac{Quality\\ast CurrentQuantity + ImportQuality\\ast ImportAmount}{CurrentQuantity + ImportAmount}":"Quality=QualityCurrentQuantity+ImportQualityImportAmountCurrentQuantity+ImportAmount","EngineerSummand = \\frac{EngineerProduction}{90}":"EngineerSummand=EngineerProduction90","ResearchPointSummand = (RP)^{IndustryScienceFactor}":"ResearchPointSummand=(RP)IndustryScienceFactor","AICoresSummand = AICoresQuantity^{IndustryAICoreFactor}\\ast{0.001}":"AICoresSummand=AICoresQuantityIndustryAICoreFactor0.001","OutputQuality = \\sqrt{MaxOutputQuality}\\ast AvgInputQuality":"OutputQuality=MaxOutputQualityAvgInputQuality","OutputRating = \\sqrt{MaxOutputRating}\\ast AvgInputQuality":"OutputRating=MaxOutputRatingAvgInputQuality","UpgradeCost = BasePrice\\ast{PriceMult}^{CurrentLevel}":"UpgradeCost=BasePricePriceMultCurrentLevel","UpgradeCost_{From\\ 0\\ to\\ n} = \\sum_{k = 0}^{n - 1}{BasePrice\\ast {PriceMult}^k}":"UpgradeCostFrom 0 to n=k=0n1BasePricePriceMultk","UpgradeCost_{From\\ 0\\ to\\ n} = BasePrice\\ast\\left( \\frac{1 - {PriceMult}^{n}}{1 - PriceMult} \\right)":"UpgradeCostFrom 0 to n=BasePrice(1PriceMultn1PriceMult)","UpgradeCost_{From\\ 0\\ to\\ n} = BasePrice\\ast\\left( \\frac{{PriceMult}^{n} - 1}{PriceMult - 1} \\right)":"UpgradeCostFrom 0 to n=BasePrice(PriceMultn1PriceMult1)","UpgradeCost_{From\\ a\\ to\\ b} = \\sum_{k = 0}^{b - 1}{BasePrice\\ast {PriceMult}^k} - \\sum_{k = 0}^{a - 1}{BasePrice\\ast {PriceMult}^k}":"UpgradeCostFrom a to b=k=0b1BasePricePriceMultkk=0a1BasePricePriceMultk","UpgradeCost_{From\\ a\\ to\\ b} = BasePrice\\ast\\left( \\frac{{PriceMult}^{b} - 1}{PriceMult - 1} \\right) - BasePrice\\ast\\left( \\frac{{PriceMult}^{a} - 1}{PriceMult - 1} \\right)":"UpgradeCostFrom a to b=BasePrice(PriceMultb1PriceMult1)BasePrice(PriceMulta1PriceMult1)","UpgradeCost_{From\\ a\\ to\\ b} = BasePrice\\ast\\left( \\frac{{PriceMult}^{b} - {PriceMult}^{a}}{PriceMult - 1} \\right)":"UpgradeCostFrom a to b=BasePrice(PriceMultbPriceMultaPriceMult1)","MaxUpgradeLevel = \\log_{PriceMult}\\left( MaxCost\\ast\\frac{PriceMult - 1}{BasePrice} + (PriceMult)^{CurrentLevel} \\right)":"MaxUpgradeLevel=logPriceMult(MaxCostPriceMult1BasePrice+(PriceMult)CurrentLevel)","Benefit = BaseBenefit + Benefit\\ast CurrentLevel":"Benefit=BaseBenefit+BenefitCurrentLevel","RPGain = 0.004\\ast(RnDProduction)^{0.5}\\ast UpgradeMultiplier\\ast ResearchMultiplier":"RPGain=0.004(RnDProduction)0.5UpgradeMultiplierResearchMultiplier","UpgradeCost = BasePrice\\ast{1.07}^{CurrentLevel + 1}":"UpgradeCost=BasePrice1.07CurrentLevel+1","UpgradeCost_{From\\ 1\\ to\\ n} = \\sum_{k = 2}^{n}{BasePrice\\ast {1.07}^k}":"UpgradeCostFrom 1 to n=k=2nBasePrice1.07k","UpgradeCost_{From\\ 1\\ to\\ n} = BasePrice\\ast\\left( \\frac{{1.07}^{n + 1} - {1.07}^{2}}{0.07} \\right)":"UpgradeCostFrom 1 to n=BasePrice(1.07n+11.0720.07)","UpgradeCost_{From\\ a\\ to\\ b} = BasePrice\\ast\\left( \\frac{{1.07}^{b + 1} - {1.07}^{a + 1}}{0.07} \\right)":"UpgradeCostFrom a to b=BasePrice(1.07b+11.07a+10.07)","MaxLevel = (log_{1.07}\\left(MaxCost\\ast\\frac{0.07}{BasePrice} + {1.07}^{CurrentLevel+1} \\right)) - 1":"MaxLevel=(log1.07(MaxCost0.07BasePrice+1.07CurrentLevel+1))1","WarehouseSize = WarehouseLevel\\ast 100\\ast UpgradeMultiplier\\ast ResearchMultiplier":"WarehouseSize=WarehouseLevel100UpgradeMultiplierResearchMultiplier","AdvertMultiplier = WilsonUpgradeBenefit\\ast ResearchAdvertisingMultiplier":"AdvertMultiplier=WilsonUpgradeBenefitResearchAdvertisingMultiplier","Awareness = (Awareness + 3\\ast AdvertMultiplier)\\ast(1.005*AdvertMultiplier)":"Awareness=(Awareness+3AdvertMultiplier)(1.005AdvertMultiplier)","Popularity = (Popularity + AdvertMultiplier)\\ast(1 + {Random(1,3)}\\ast{0.005})\\ast AdvertMultiplier":"Popularity=(Popularity+AdvertMultiplier)(1+Random(1,3)0.005)AdvertMultiplier","reputation = \\frac{\\text{donation amount} \\cdot \\text{reputation multiplier}}{10^{6}}":"reputation=donation amountreputation multiplier106","\\large{cost = 10^9 \\cdot 7.5 ^{\\text{cores}}}":"cost=1097.5cores","\\large{cost = ram \\cdot 3.2 \\cdot 10^4 \\cdot 1.58^{log_2{(ram)}}} \\cdot HomeRamCostMult":"cost=ram3.21041.58log2(ram)HomeRamCostMult","\\huge{\\Delta r = \\Delta r \\times \\frac{100+favor}{100}}":"Δr=Δr×100+favor100","\\huge{favor=\\log_{1.02}\\left(1+\\frac{r}{25000}\\right)}":"favor=log1.02(1+r25000)","\\text{multiplier} = \\left((\\text{awareness}+1)^{\\alpha} \\times (\\text{popularity}+1)^{\\alpha} \\times \\frac{\\text{popularity}+0.001}{\\text{awareness}}\\right)^{0.85}":"multiplier=((awareness+1)α×(popularity+1)α×popularity+0.001awareness)0.85"} \ No newline at end of file diff --git a/src/Documentation/root.ts b/src/Documentation/root.ts index 7d97e1524..59398ad76 100644 --- a/src/Documentation/root.ts +++ b/src/Documentation/root.ts @@ -1,5 +1,6 @@ import { AllPages } from "./pages"; import { EventEmitter } from "../utils/EventEmitter"; +import MathNotationOutput from "./data/MathNotationOutput.json"; export const resolvePage = (title: string): { pageName: string | null; pageContent: string } => { const lang = new Intl.Locale(navigator.language).language; @@ -35,3 +36,13 @@ export const DocumentationPopUpEvents = new EventEmitter<[string | undefined]>() export function openDocumentationPopUp(path: string): void { DocumentationPopUpEvents.emit(path); } + +export function convertMathNotation(value: string): string { + // MathNotationOutput is imported as json data, so we need to typecast here to access the value via string index + // easier without fighting with the TS compiler. + const output = (MathNotationOutput as Record)[value]; + if (output == null) { + throw new Error(`Unknown math notation: ${value}`); + } + return output; +} diff --git a/src/Documentation/ui/MathNotationOutput.tsx b/src/Documentation/ui/MathNotationOutput.tsx new file mode 100644 index 000000000..eb2e601a4 --- /dev/null +++ b/src/Documentation/ui/MathNotationOutput.tsx @@ -0,0 +1,9 @@ +import React from "react"; +import Typography from "@mui/material/Typography"; +import { convertMathNotation } from "../root"; + +export function MathNotationOutput({ notation }: { notation: string }): JSX.Element { + // It's fine to use dangerouslySetInnerHTML here. We control the data in both MathNotation.json and + // MathNotationOutput.json. They are not user-provided data. + return ; +} diff --git a/src/Faction/ui/DonateOption.tsx b/src/Faction/ui/DonateOption.tsx index 6ac24a9f8..a0fac580f 100644 --- a/src/Faction/ui/DonateOption.tsx +++ b/src/Faction/ui/DonateOption.tsx @@ -1,6 +1,5 @@ import React, { useState } from "react"; -import { CONSTANTS } from "../../Constants"; import { Faction } from "../Faction"; import { Player } from "@player"; import { canDonate, donate, repFromDonation } from "../formulas/donation"; @@ -10,12 +9,13 @@ import { Money } from "../../ui/React/Money"; import { Reputation } from "../../ui/React/Reputation"; import { dialogBoxCreate } from "../../ui/React/DialogBox"; -import { MathJax } from "better-react-mathjax"; import Typography from "@mui/material/Typography"; import Paper from "@mui/material/Paper"; import Button from "@mui/material/Button"; import { NumberInput } from "../../ui/React/NumberInput"; +import MathNotation from "../../Documentation/data/MathNotation.json"; +import { MathNotationOutput } from "../../Documentation/ui/MathNotationOutput"; type DonateOptionProps = { faction: Faction; @@ -27,7 +27,6 @@ type DonateOptionProps = { /** React component for a donate option on the Faction UI */ export function DonateOption({ faction, favorToDonate, disabled, rerender }: DonateOptionProps): React.ReactElement { const [donateAmt, setDonateAmt] = useState(NaN); - const digits = (CONSTANTS.DonateMoneyToRepDivisor + "").length - 1; function onDonate(): void { const repGain = donate(donateAmt, faction); @@ -76,9 +75,7 @@ export function DonateOption({ faction, favorToDonate, disabled, rerender }: Don ), }} /> - - {`\\(reputation = \\frac{\\text{donation amount} \\cdot \\text{reputation multiplier}}{10^{${digits}}}\\)`} - + )} diff --git a/src/Locations/ui/CoresButton.tsx b/src/Locations/ui/CoresButton.tsx index 563bcb556..6619efbcd 100644 --- a/src/Locations/ui/CoresButton.tsx +++ b/src/Locations/ui/CoresButton.tsx @@ -6,7 +6,8 @@ import Typography from "@mui/material/Typography"; import { Player } from "@player"; import { Money } from "../../ui/React/Money"; -import { MathJax } from "better-react-mathjax"; +import MathNotation from "../../Documentation/data/MathNotation.json"; +import { MathNotationOutput } from "../../Documentation/ui/MathNotationOutput"; interface IProps { rerender: () => void; @@ -32,7 +33,7 @@ export function CoresButton(props: IProps): React.ReactElement { } return ( - {`\\(\\large{cost = 10^9 \\cdot 7.5 ^{\\text{cores}}}\\)`}}> + }>
diff --git a/src/Locations/ui/RamButton.tsx b/src/Locations/ui/RamButton.tsx index 0a0ba3b22..21354e5f5 100644 --- a/src/Locations/ui/RamButton.tsx +++ b/src/Locations/ui/RamButton.tsx @@ -9,9 +9,10 @@ import { purchaseRamForHomeComputer } from "../../Server/ServerPurchases"; import { Money } from "../../ui/React/Money"; import { formatRam } from "../../ui/formatNumber"; -import { MathJax } from "better-react-mathjax"; import { currentNodeMults } from "../../BitNode/BitNodeMultipliers"; import { ServerConstants } from "../../Server/data/Constants"; +import MathNotation from "../../Documentation/data/MathNotation.json"; +import { MathNotationOutput } from "../../Documentation/ui/MathNotationOutput"; interface IProps { rerender: () => void; @@ -30,12 +31,13 @@ export function RamButton(props: IProps): React.ReactElement { props.rerender(); } - const bnMult = currentNodeMults.HomeComputerRamCost === 1 ? "" : `\\cdot ${currentNodeMults.HomeComputerRamCost}`; - return ( {`\\(\\large{cost = ram \\cdot 3.2 \\cdot 10^4 \\cdot 1.58^{log_2{(ram)}}} ${bnMult}\\)`} + <> + HomeRamCostMult = {currentNodeMults.HomeComputerRamCost} + + } > diff --git a/src/ThirdParty/RehypePlugin.d.mts b/src/ThirdParty/RehypePlugin.d.mts new file mode 100644 index 000000000..ac58baff4 --- /dev/null +++ b/src/ThirdParty/RehypePlugin.d.mts @@ -0,0 +1,6 @@ +// Minimal type information +export declare function createPlugin( + createRenderer: () => { + render: (value: string, { display }: { display: boolean }) => any[]; + }, +): () => (tree: any, file: any) => void; diff --git a/src/ThirdParty/RehypePlugin.mjs b/src/ThirdParty/RehypePlugin.mjs new file mode 100644 index 000000000..04fcdcb7f --- /dev/null +++ b/src/ThirdParty/RehypePlugin.mjs @@ -0,0 +1,343 @@ +/** + * We need to use a custom rehype plugin similar to rehype-mathjax. Specifically, we need to implement a function + * similar to "createPlugin" in that library. That library depends on mathjax, which is what we want to get rid of at + * runtime, so we copy only "create-plugin.js" from that library. + * + * The source code is retrieved from https://raw.githubusercontent.com/remarkjs/remark-math/76c14978ff6805011b8c56727c54104a511b9055/packages/rehype-mathjax/lib/create-plugin.js + * The license is retrieved from https://raw.githubusercontent.com/remarkjs/remark-math/76c14978ff6805011b8c56727c54104a511b9055/license + */ + +/** +(The MIT License) + +Copyright (c) Junyoung Choi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +/** + * @import {ElementContent, Element, Root} from 'hast' + * @import {VFile} from 'vfile' + */ + +/** + * @callback CreateRenderer + * Create a renderer. + * @param {Readonly} options + * Configuration. + * @returns {Renderer} + * Rendeder. + * + * @callback FormatError + * Format an error. + * @param {any} jax + * MathJax object. + * @param {any} error + * Error. + * @returns {string} + * Formatted error. + * + * @typedef InputTexOptions + * Configuration for input tex math. + * + * @property {string | null | undefined} [baseURL] + * URL for use with links to tags, when there is a `` tag in effect + * (optional). + * @property {RegExp | null | undefined} [digits] + * Pattern for recognizing numbers (optional). + * @property {ReadonlyArray | null | undefined} [displayMath] + * Start/end delimiter pairs for display math (optional). + * @property {FormatError | null | undefined} [formatError] + * Function called when TeX syntax errors occur (optional). + * @property {ReadonlyArray | null | undefined} [inlineMath] + * Start/end delimiter pairs for in-line math (optional). + * @property {number | null | undefined} [maxBuffer] + * Max size for the internal TeX string (5K) (optional). + * @property {number | null | undefined} [maxMacros] + * Max number of macro substitutions per expression (optional). + * @property {ReadonlyArray | null | undefined} [packages] + * Extensions to use (optional). + * @property {boolean | null | undefined} [processEnvironments] + * Process `\begin{xxx}...\end{xxx}` outside math mode (optional). + * @property {boolean | null | undefined} [processEscapes] + * Use `\$` to produce a literal dollar sign (optional). + * @property {boolean | null | undefined} [processRefs] + * Process `\ref{...}` outside of math mode (optional). + * @property {string | null | undefined} [tagIndent] + * Amount to indent tags (optional). + * @property {'left' | 'right' | null | undefined} [tagSide] + * Side for `\tag` macros (optional). + * @property {'all' | 'ams' | 'none' | null | undefined} [tags] + * Optional. + * @property {boolean | null | undefined} [useLabelIds] + * Use label name rather than tag for ids (optional). + * + * @typedef {[open: string, close: string]} MathNotation + * Markers to use for math. + * See: + * + * @typedef Options + * Configuration. + * + * ###### Notes + * + * When using `rehype-mathjax/browser`, only `options.tex.displayMath` and + * `options.tex.inlineMath` are used. + * That plugin will use the first delimiter pair in those fields to wrap + * math. + * Then you need to load MathJax yourself on the client and start it with the + * same markers. + * You can pass other options on the client. + * + * When using `rehype-mathjax/chtml`, `options.chtml.fontURL` is required. + * For example: + * + * ```js + * // … + * .use(rehypeMathjaxChtml, { + * chtml: { + * fontURL: 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/output/chtml/fonts/woff-v2' + * } + * }) + * // … + * ``` + * @property {Readonly | null | undefined} [chtml] + * Configuration for the output, when CHTML (optional). + * @property {Readonly | null | undefined} [svg] + * Configuration for the output, when SVG (optional). + * @property {Readonly | null | undefined} [tex] + * Configuration for the input TeX (optional). + * + * @typedef OutputChtmlOptions + * Configuration for output CHTML. + * + * @property {boolean | null | undefined} [adaptiveCSS] + * `true` means only produce CSS that is used in the processed equations (optional). + * @property {'center' | 'left' | 'right' | null | undefined} [displayAlign] + * Default for indentalign when set to `'auto'` (optional). + * @property {string | null | undefined} [displayIndent] + * Default for indentshift when set to `'auto'` (optional). + * @property {number | null | undefined} [exFactor] + * Default size of ex in em units (optional). + * @property {string} fontURL + * The URL where the fonts are found (**required**). + * @property {boolean | null | undefined} [matchFontHeight] + * `true` to match ex-height of surrounding font (optional). + * @property {boolean | null | undefined} [mathmlSpacing] + * `true` for MathML spacing rules, false for TeX rules (optional). + * @property {boolean | null | undefined} [merrorInheritFont] + * `true` to make merror text use surrounding font (optional). + * @property {number | null | undefined} [minScale] + * Smallest scaling factor to use (optional). + * @property {boolean | null | undefined} [mtextInheritFont] + * `true` to make mtext elements use surrounding font (optional). + * @property {number | null | undefined} [scale] + * Global scaling factor for all expressions (optional). + * @property {Readonly> | null | undefined} [skipAttributes] + * RFDa and other attributes NOT to copy to the output (optional). + * + * @typedef OutputSvgOptions + * Configuration for output SVG. + * + * @property {'center' | 'left' | 'right' | null | undefined} [displayAlign] + * Default for indentalign when set to `'auto'` (optional). + * @property {string | null | undefined} [displayIndent] + * Default for indentshift when set to `'auto'` (optional). + * @property {number | null | undefined} [exFactor] + * Default size of ex in em units (optional). + * @property {'global' | 'local' | 'none' | null | undefined} [fontCache] + * Or `'global'` or `'none'` (optional). + * @property {boolean | null | undefined} [internalSpeechTitles] + * Insert `` tags with speech content (optional). + * @property {string | null | undefined} [localID] + * ID to use for local font cache, for single equation processing (optional). + * @property {boolean | null | undefined} [mathmlSpacing] + * `true` for MathML spacing rules, `false` for TeX rules (optional). + * @property {boolean | null | undefined} [merrorInheritFont] + * `true` to make merror text use surrounding font (optional). + * @property {number | null | undefined} [minScale] + * Smallest scaling factor to use (optional). + * @property {boolean | null | undefined} [mtextInheritFont] + * `true` to make mtext elements use surrounding font (optional). + * @property {number | null | undefined} [scale] + * Global scaling factor for all expressions (optional). + * @property {Readonly<Record<string, boolean>> | null | undefined} [skipAttributes] + * RFDa and other attributes *not* to copy to the output (optional). + * @property {number | null | undefined} [titleID] + * Initial ID number to use for `aria-labeledby` titles (optional). + * + * @callback Render + * Render a math node. + * @param {string} value + * Math value. + * @param {Readonly<RenderOptions>} options + * Configuration. + * @returns {Array<ElementContent>} + * Content. + * + * @typedef RenderOptions + * Configuration. + * @property {boolean} display + * Whether to render display math. + * + * @typedef Renderer + * Renderer. + * @property {(() => undefined) | undefined} [register] + * Called before transform. + * @property {(() => undefined) | undefined} [unregister] + * Called after transform. + * @property {Render} render + * Render a math node. + * @property {StyleSheet | undefined} [styleSheet] + * Render a style sheet (optional). + * + * @callback StyleSheet + * Render a style sheet. + * @returns {Element} + * Style sheet. + */ + +import { toText } from "hast-util-to-text"; +import { SKIP, visitParents } from "unist-util-visit-parents"; + +/** @type {Readonly<Options>} */ +const emptyOptions = {}; +/** @type {ReadonlyArray<unknown>} */ +const emptyClasses = []; + +/** + * Create a plugin. + * + * @param {CreateRenderer} createRenderer + * Create a renderer. + * @returns + * Plugin. + */ +export function createPlugin(createRenderer) { + /** + * Plugin. + * + * @param {Readonly<Options> | null | undefined} [options] + * Configuration (optional). + * @returns + * Transform. + */ + return function (options) { + /** + * Transform. + * + * @param {Root} tree + * Tree. + * @param {VFile} file + * File. + * @returns {undefined} + * Nothing. + */ + return function (tree, file) { + const renderer = createRenderer(options || emptyOptions); + let found = false; + /** @type {Element | Root} */ + let context = tree; + + visitParents(tree, "element", function (element, parents) { + const classes = Array.isArray(element.properties.className) ? element.properties.className : emptyClasses; + // This class can be generated from markdown with ` ```math `. + const languageMath = classes.includes("language-math"); + // This class is used by `remark-math` for flow math (block, `$$\nmath\n$$`). + const mathDisplay = classes.includes("math-display"); + // This class is used by `remark-math` for text math (inline, `$math$`). + const mathInline = classes.includes("math-inline"); + let display = mathDisplay; + + // Find `<head>`. + if (element.tagName === "head") { + context = element; + } + + // Any class is fine. + if (!languageMath && !mathDisplay && !mathInline) { + return; + } + + let parent = parents[parents.length - 1]; + let scope = element; + + // If this was generated with ` ```math `, replace the `<pre>` and use + // display. + if ( + element.tagName === "code" && + languageMath && + parent && + parent.type === "element" && + parent.tagName === "pre" + ) { + scope = parent; + parent = parents[parents.length - 2]; + display = true; + } + + /* c8 ignore next -- verbose to test. */ + if (!parent) return; + + if (!found && renderer.register) renderer.register(); + found = true; + + const text = toText(scope, { whitespace: "pre" }); + /** @type {Array<ElementContent> | undefined} */ + let result; + + try { + result = renderer.render(text, { display }); + } catch (error) { + const cause = /** @type {Error} */ (error); + + file.message("Could not render math with mathjax", { + ancestors: [...parents, element], + cause, + place: element.position, + ruleId: "mathjax-error", + source: "rehype-mathjax", + }); + + result = [ + { + type: "element", + tagName: "span", + properties: { + className: ["mathjax-error"], + style: "color:#cc0000", + title: String(cause), + }, + children: [{ type: "text", value: text }], + }, + ]; + } + + const index = parent.children.indexOf(scope); + parent.children.splice(index, 1, ...result); + return SKIP; + }); + + if (found) { + if (renderer.styleSheet) context.children.push(renderer.styleSheet()); + if (renderer.unregister) renderer.unregister(); + } + }; + }; +} diff --git a/src/ui/GameRoot.tsx b/src/ui/GameRoot.tsx index 6575fce60..70d337579 100644 --- a/src/ui/GameRoot.tsx +++ b/src/ui/GameRoot.tsx @@ -69,7 +69,6 @@ import { BypassWrapper } from "./React/BypassWrapper"; import { Apr1 } from "./Apr1"; import { V2Modal } from "../utils/V2Modal"; -import { MathJaxContext } from "better-react-mathjax"; import { useRerender } from "./React/hooks"; import { HistoryProvider } from "./React/Documentation"; import { GoRoot } from "../Go/ui/GoRoot"; @@ -505,7 +504,7 @@ export function GameRoot(): React.ReactElement { }, []); return ( - <MathJaxContext version={3} src={__webpack_public_path__ + "mathjax/tex-chtml.js"}> + <> <ErrorBoundary key={errorBoundaryKey} softReset={softReset}> <BypassWrapper content={bypassGame ? mainPage : null}> <HistoryProvider> @@ -547,6 +546,6 @@ export function GameRoot(): React.ReactElement { </BypassWrapper> </ErrorBoundary> <V2Modal /> - </MathJaxContext> + </> ); } diff --git a/src/ui/MD/MD.tsx b/src/ui/MD/MD.tsx index 592bfdcb0..40ca18772 100644 --- a/src/ui/MD/MD.tsx +++ b/src/ui/MD/MD.tsx @@ -6,11 +6,31 @@ import { h1, h2, h3, h4, h5, h6, li, Td, Th, table, tr, Blockquote, p } from "./ import { code, Pre } from "./code"; import { A } from "./a"; import remarkMath from "remark-math"; -import rehypeMathjax from "rehype-mathjax/svg"; import rehypeRaw from "rehype-raw"; import { FilePath } from "../../Paths/FilePath"; -import { getPage } from "../../Documentation/root"; +import { convertMathNotation, getPage } from "../../Documentation/root"; import { DocImages } from "../../Documentation/pages"; +import { createPlugin } from "../../ThirdParty/RehypePlugin.mjs"; +import { Settings } from "../../Settings/Settings"; +import { fromDom } from "hast-util-from-dom"; + +const rehypePlugin = createPlugin(function () { + return { + render(value: string, { display }: { display: boolean }) { + const mml = convertMathNotation(value); + const element = document.createElement(display ? "div" : "span"); + if (display) { + element.style.textAlign = "center"; + } + element.style.fontFamily = Settings.styles.fontFamily; + // Check the comment in src/Themes/ui/StyleEditorModal.tsx to see why we need to convert the font size. + element.style.fontSize = `${Settings.styles.fontSize * (16 / 14)}px`; + element.style.color = Settings.theme.primary; + element.innerHTML = mml; + return [fromDom(element)]; + }, + }; +}); export function MD({ pageFilePath }: { pageFilePath: FilePath }): React.ReactElement { const pageContent = getPage(pageFilePath); @@ -38,8 +58,9 @@ export function MD({ pageFilePath }: { pageFilePath: FilePath }): React.ReactEle a: A, }} remarkPlugins={[remarkGfm, remarkMath]} - // Use rehypeRaw to support HTML content in NS API docs. - rehypePlugins={[rehypeMathjax, rehypeRaw]} + // Use a custom rehype plugin to render LaTeX notation in md files. Use rehypeRaw to support HTML content in NS + // API docs. + rehypePlugins={[rehypePlugin, rehypeRaw]} transformImageUri={(__src, alt) => { return DocImages[alt]; }} diff --git a/src/ui/React/FavorInfo.tsx b/src/ui/React/FavorInfo.tsx index 44964db4c..faf349b79 100644 --- a/src/ui/React/FavorInfo.tsx +++ b/src/ui/React/FavorInfo.tsx @@ -1,11 +1,11 @@ import React from "react"; -import { MathJax } from "better-react-mathjax"; - import InfoIcon from "@mui/icons-material/Info"; import Tooltip from "@mui/material/Tooltip"; import Typography from "@mui/material/Typography"; import { Favor } from "../../ui/React/Favor"; +import MathNotation from "../../Documentation/data/MathNotation.json"; +import { MathNotationOutput } from "../../Documentation/ui/MathNotationOutput"; export function FavorInfo({ favor, boldLabel }: { favor: number; boldLabel?: boolean }): React.ReactElement { return ( @@ -17,8 +17,8 @@ export function FavorInfo({ favor, boldLabel }: { favor: number; boldLabel?: boo favor is gained whenever you install an Augmentation. The amount of favor you gain depends on the total amount of reputation you earned with this faction across all resets. </Typography> - <MathJax>{"\\(\\huge{r = reputation}\\)"}</MathJax> - <MathJax>{"\\(\\huge{\\Delta r = \\Delta r \\times \\frac{100+favor}{100}}\\)"}</MathJax> + <Typography style={{ fontSize: "2rem" }}>r = Reputation gain</Typography> + <MathNotationOutput notation={MathNotation.FavorBonus} /> </> } > diff --git a/src/ui/React/ReputationInfo.tsx b/src/ui/React/ReputationInfo.tsx index b669b9fea..e9244577c 100644 --- a/src/ui/React/ReputationInfo.tsx +++ b/src/ui/React/ReputationInfo.tsx @@ -1,5 +1,4 @@ import React from "react"; -import { MathJax } from "better-react-mathjax"; import InfoIcon from "@mui/icons-material/Info"; import Tooltip from "@mui/material/Tooltip"; @@ -8,6 +7,8 @@ import Typography from "@mui/material/Typography"; import { addRepToFavor } from "../../Faction/formulas/favor"; import { Favor } from "../../ui/React/Favor"; import { Reputation } from "./Reputation"; +import MathNotation from "../../Documentation/data/MathNotation.json"; +import { MathNotationOutput } from "../../Documentation/ui/MathNotationOutput"; export function ReputationInfo({ favor, @@ -26,8 +27,8 @@ export function ReputationInfo({ You will have <Favor favor={addRepToFavor(favor, playerReputation)} /> faction favor after installing an Augmentation. </Typography> - <MathJax>{"\\(\\huge{r = \\text{total faction reputation}}\\)"}</MathJax> - <MathJax>{"\\(\\huge{favor=\\log_{1.02}\\left(1+\\frac{r}{25000}\\right)}\\)"}</MathJax> + <Typography style={{ fontSize: "2rem" }}>r = Total faction reputation</Typography> + <MathNotationOutput notation={MathNotation.RepToFavor} /> </> } > diff --git a/tools/bundle-doc/generate-math-notation-output.mjs b/tools/bundle-doc/generate-math-notation-output.mjs new file mode 100644 index 000000000..3330f32df --- /dev/null +++ b/tools/bundle-doc/generate-math-notation-output.mjs @@ -0,0 +1,46 @@ +import { readFileSync, writeFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +import remarkMath from "remark-math"; +import remarkParse from "remark-parse"; +import remarkRehype from "remark-rehype"; +import { unified } from "unified"; + +import { createPlugin } from "../../src/ThirdParty/RehypePlugin.mjs"; +import { convertToMML, walkDir } from "./utils.mjs"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const data = {}; +const parser = unified().use(remarkParse).use(remarkMath); +const tool = unified() + .use(remarkRehype) + .use( + createPlugin(function () { + return { + render(value) { + data[value] = convertToMML(value); + return []; + }, + }; + }), + ); + +function processDir(dir) { + walkDir(dir, (filePath) => { + tool.runSync(parser.parse(readFileSync(filePath))); + }); +} + +processDir(resolve(__dirname, "../../src/Documentation/doc/en")); + +const notationInput = JSON.parse(readFileSync(resolve(__dirname, "../../src/Documentation/data/MathNotation.json"))); +for (const value of Object.values(notationInput)) { + data[value] = convertToMML(value); +} +console.log(`Generated MathML data for ${Object.keys(data).length} notation entries`); +writeFileSync(resolve(__dirname, "../../src/Documentation/data/MathNotationOutput.json"), JSON.stringify(data), { + encoding: "utf-8", +}); diff --git a/tools/bundle-doc/index.js b/tools/bundle-doc/index.mjs similarity index 73% rename from tools/bundle-doc/index.js rename to tools/bundle-doc/index.mjs index 926c649da..826f29420 100644 --- a/tools/bundle-doc/index.js +++ b/tools/bundle-doc/index.mjs @@ -1,27 +1,25 @@ -const fs = require("fs"); -const path = require("path"); +import { writeFileSync } from "node:fs"; +import { dirname, relative, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { walkDir } from "./utils.mjs"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); const docFiles = []; const nsDocFiles = []; const docImagesFiles = []; -const docRoot = path.resolve(__dirname, "../../src/Documentation/doc"); -const markdownRoot = path.resolve(__dirname, "../../markdown"); -const docImagesRoot = path.resolve(__dirname, "../../src/Documentation/images"); -const addFileToListOfDocPages = (files, root, filePath) => { +const docRoot = resolve(__dirname, "../../src/Documentation/doc"); +const markdownRoot = resolve(__dirname, "../../markdown"); +const docImagesRoot = resolve(__dirname, "../../src/Documentation/images"); + +function addFileToListOfDocPages(files, root, filePath) { // Windows path uses "\", so we need to replace it with "/". - files.push(path.relative(root, filePath).replace(/\\/g, "/")); -}; -const processDir = (dir) => { - if (!fs.existsSync(dir)) { - return; - } - console.log(dir); - for (const file of fs.readdirSync(dir).sort((a, b) => a.localeCompare(b, "en-US"))) { - const filePath = path.join(dir, file); - if (fs.lstatSync(filePath).isDirectory()) { - processDir(filePath); - continue; - } + files.push(relative(root, filePath).replace(/\\/g, "/")); +} + +function processDir(dir) { + walkDir(dir, (filePath) => { if (filePath.startsWith(docRoot)) { addFileToListOfDocPages(docFiles, docRoot, filePath); } else if (filePath.startsWith(markdownRoot)) { @@ -29,8 +27,8 @@ const processDir = (dir) => { } else { addFileToListOfDocPages(docImagesFiles, docImagesRoot, filePath); } - } -}; + }); +} processDir(docRoot); processDir(markdownRoot); @@ -68,9 +66,4 @@ export const nsApiPages = Object.keys(AllPages) export const DocImages: Record<string, string> = {}; ${docImagesFiles.map((f) => `DocImages["${f}"] = docImages_${f.replaceAll(/\.|-/g, "_")};\n`).join("")}`; -fs.writeFile(path.resolve(__dirname, "../../src/Documentation/pages.ts"), autogeneratedContent, (err) => { - if (err) { - console.error(err); - } - // file written successfully -}); +writeFileSync(resolve(__dirname, "../../src/Documentation/pages.ts"), autogeneratedContent, { encoding: "utf-8" }); diff --git a/tools/bundle-doc/utils.mjs b/tools/bundle-doc/utils.mjs new file mode 100644 index 000000000..420590c94 --- /dev/null +++ b/tools/bundle-doc/utils.mjs @@ -0,0 +1,63 @@ +import { existsSync, lstatSync, readdirSync } from "node:fs"; +import { join } from "node:path"; + +import { mathjax } from "@mathjax/src/js/mathjax.js"; +import { TeX } from "@mathjax/src/js/input/tex.js"; +import { liteAdaptor } from "@mathjax/src/js/adaptors/liteAdaptor.js"; +import { RegisterHTMLHandler } from "@mathjax/src/js/handlers/html.js"; +import "@mathjax/src/js/util/asyncLoad/esm.js"; + +import { SerializedMmlVisitor } from "@mathjax/src/js/core/MmlTree/SerializedMmlVisitor.js"; + +import "@mathjax/src/js/input/tex/base/BaseConfiguration.js"; +import "@mathjax/src/js/input/tex/ams/AmsConfiguration.js"; +import "@mathjax/src/js/input/tex/newcommand/NewcommandConfiguration.js"; +import "@mathjax/src/js/input/tex/noundefined/NoUndefinedConfiguration.js"; + +import xmlFormat from "xml-formatter"; + +export function walkDir(dir, callback) { + if (!existsSync(dir)) { + return; + } + console.log(`Processing ${dir}`); + for (const file of readdirSync(dir).sort((a, b) => a.localeCompare(b, "en-US"))) { + const filePath = join(dir, file); + if (lstatSync(filePath).isDirectory()) { + walkDir(filePath, callback); + continue; + } + callback(filePath); + } +} + +const adaptor = liteAdaptor(); +RegisterHTMLHandler(adaptor); + +const tex = new TeX({ + packages: ["base", "ams", "newcommand", "noundefined"], + formatError(jax, err) { + console.error(jax, err); + process.exit(1); + }, +}); +const visitor = new SerializedMmlVisitor(); +const docMML = mathjax.document("", { InputJax: tex }); + +export function convertToMML(value) { + const node = docMML.convert(value, { + // Do not set "display" css property. The display mode will be decided by the rehype plugin. + display: false, + }); + node.walkTree((node) => { + const attributes = node.attributes; + // Remove unnecessary attributes. + attributes.unset("data-latex"); + attributes.unset("data-latex-item"); + }); + const mathJaxOutput = visitor.visitTree(node, docMML); + return xmlFormat.minify(mathJaxOutput, { + filter: (node) => node.type !== "Comment", + collapseContent: true, + }); +} diff --git a/tools/doc.sh b/tools/doc.sh index fb216b263..ea422a7c6 100644 --- a/tools/doc.sh +++ b/tools/doc.sh @@ -13,12 +13,16 @@ rm input/bitburner.api.json && rm -r input echo "" echo "Bundling ingame documentation..." -node tools/bundle-doc/index.js +node tools/bundle-doc/index.mjs echo "" echo "Add generated docs to git..." # This git add is needed due to documenter using wrong line endings. Console spam discarded. git add markdown/ 2> /dev/null && git add tsdoc-metadata.json 2> /dev/null +echo "" +echo "Generate math notation output..." +node tools/bundle-doc/generate-math-notation-output.mjs + echo "" echo "Documentation build completed." diff --git a/webpack.config.js b/webpack.config.js index 7bc9ac95f..eeb14461d 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -5,7 +5,6 @@ const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin"); const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin"); -const CopyPlugin = require("copy-webpack-plugin"); /** @type {import("webpack-cli").CallableOption} */ module.exports = (env, argv) => { @@ -124,15 +123,6 @@ module.exports = (env, argv) => { module: true, }), enableReactRefresh && new ReactRefreshWebpackPlugin(), - new CopyPlugin({ - patterns: [ - { - from: "{tex-chtml.js,*/**/*}", - to: "mathjax", - context: "node_modules/mathjax-full/es5", - }, - ], - }), ].filter(Boolean), target: "web", entry: entry,