mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-16 06:18:42 +02:00
CODEBASE: Generate display data for math notation at built time and remove runtime mathjax dependency (#2447)
This commit is contained in:
@@ -19,5 +19,7 @@ tsdoc-metadata.json
|
|||||||
|
|
||||||
.git_blame_ignore_revs
|
.git_blame_ignore_revs
|
||||||
|
|
||||||
# This file is generated by tools/bundle-doc/index.js
|
# This file is generated by tools/bundle-doc/index.mjs
|
||||||
src/Documentation/pages.ts
|
src/Documentation/pages.ts
|
||||||
|
# This file is generated by tools/bundle-doc/generate-math-notation-output.mjs
|
||||||
|
src/Documentation/data/MathNotationOutput.json
|
||||||
|
|||||||
806
package-lock.json
generated
806
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
@@ -27,11 +27,12 @@
|
|||||||
"ajv": "^8.17.1",
|
"ajv": "^8.17.1",
|
||||||
"arg": "^5.0.2",
|
"arg": "^5.0.2",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"better-react-mathjax": "^2.0.3",
|
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
"convert-source-map": "^2.0.0",
|
"convert-source-map": "^2.0.0",
|
||||||
"date-fns": "^2.30.0",
|
"date-fns": "^2.30.0",
|
||||||
"fast-dice-coefficient": "1.0.3",
|
"fast-dice-coefficient": "1.0.3",
|
||||||
|
"hast-util-from-dom": "^5.0.1",
|
||||||
|
"hast-util-to-text": "^4.0.2",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"material-ui-color": "^1.2.0",
|
"material-ui-color": "^1.2.0",
|
||||||
"material-ui-popup-state": "^1.9.3",
|
"material-ui-popup-state": "^1.9.3",
|
||||||
@@ -45,9 +46,12 @@
|
|||||||
"react-markdown": "^8.0.7",
|
"react-markdown": "^8.0.7",
|
||||||
"react-resizable": "^3.0.5",
|
"react-resizable": "^3.0.5",
|
||||||
"react-syntax-highlighter": "^15.6.6",
|
"react-syntax-highlighter": "^15.6.6",
|
||||||
|
"rehype-raw": "^6.1.1",
|
||||||
"remark-gfm": "^3.0.1",
|
"remark-gfm": "^3.0.1",
|
||||||
|
"remark-math": "^5.1.1",
|
||||||
"sprintf-js": "^1.1.3",
|
"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",
|
"description": "A cyberpunk-themed incremental game",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -56,6 +60,7 @@
|
|||||||
"@babel/preset-react": "^7.27.1",
|
"@babel/preset-react": "^7.27.1",
|
||||||
"@babel/preset-typescript": "^7.27.1",
|
"@babel/preset-typescript": "^7.27.1",
|
||||||
"@electron/packager": "^18.4.4",
|
"@electron/packager": "^18.4.4",
|
||||||
|
"@mathjax/src": "^4.1.0",
|
||||||
"@microsoft/api-documenter": "^7.26.34",
|
"@microsoft/api-documenter": "^7.26.34",
|
||||||
"@microsoft/api-extractor": "^7.52.13",
|
"@microsoft/api-extractor": "^7.52.13",
|
||||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.1",
|
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.1",
|
||||||
@@ -75,7 +80,6 @@
|
|||||||
"babel-jest": "^29.7.0",
|
"babel-jest": "^29.7.0",
|
||||||
"babel-loader": "^10.0.0",
|
"babel-loader": "^10.0.0",
|
||||||
"babel-plugin-transform-barrels": "^1.0.17",
|
"babel-plugin-transform-barrels": "^1.0.17",
|
||||||
"copy-webpack-plugin": "^13.0.0",
|
|
||||||
"css-loader": "^7.1.2",
|
"css-loader": "^7.1.2",
|
||||||
"csstype": "3.1.3",
|
"csstype": "3.1.3",
|
||||||
"electron": "^38.4.0",
|
"electron": "^38.4.0",
|
||||||
@@ -88,19 +92,16 @@
|
|||||||
"jest-environment-jsdom": "^29.7.0",
|
"jest-environment-jsdom": "^29.7.0",
|
||||||
"jsdom": "^26.1.0",
|
"jsdom": "^26.1.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mathjax-full": "^3.2.2",
|
|
||||||
"monaco-editor": "^0.55.1",
|
"monaco-editor": "^0.55.1",
|
||||||
"monaco-editor-webpack-plugin": "^7.1.1",
|
"monaco-editor-webpack-plugin": "^7.1.1",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
"react-refresh": "^0.18.0",
|
"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",
|
"style-loader": "^4.0.0",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"webpack": "^5.100.2",
|
"webpack": "^5.100.2",
|
||||||
"webpack-cli": "^6.0.1",
|
"webpack-cli": "^6.0.1",
|
||||||
"webpack-dev-server": "^5.2.2"
|
"webpack-dev-server": "^5.2.2",
|
||||||
|
"xml-formatter": "^3.6.7"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"react-syntax-highlighter": {
|
"react-syntax-highlighter": {
|
||||||
@@ -123,8 +124,8 @@
|
|||||||
"start:dev": "webpack serve --progress --mode development",
|
"start:dev": "webpack serve --progress --mode development",
|
||||||
"build": "bash ./tools/build.sh production",
|
"build": "bash ./tools/build.sh production",
|
||||||
"build:dev": "bash ./tools/build.sh development",
|
"build:dev": "bash ./tools/build.sh development",
|
||||||
"lint": "eslint --fix --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,jsx,ts,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",
|
"preinstall": "node ./tools/engines-check/engines-check.js",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export const CONSTANTS = {
|
|||||||
|
|
||||||
// Faction and Company favor-related things
|
// Faction and Company favor-related things
|
||||||
BaseFavorToDonate: 150,
|
BaseFavorToDonate: 150,
|
||||||
|
// If we change this constant, we must update the "RepDonation" value in src/Documentation/data/MathNotation.json
|
||||||
DonateMoneyToRepDivisor: 1e6,
|
DonateMoneyToRepDivisor: 1e6,
|
||||||
|
|
||||||
// NeuroFlux Governor Augmentation cost multiplier
|
// NeuroFlux Governor Augmentation cost multiplier
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// React Component for displaying an Division's overview information
|
// React Component for displaying an Division's overview information
|
||||||
// (top-left panel in the Division UI)
|
// (top-left panel in the Division UI)
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { MathJax } from "better-react-mathjax";
|
|
||||||
|
|
||||||
import { IndustryType } from "@enums";
|
import { IndustryType } from "@enums";
|
||||||
import { hireAdVert } from "../Actions";
|
import { hireAdVert } from "../Actions";
|
||||||
@@ -23,6 +22,8 @@ import Paper from "@mui/material/Paper";
|
|||||||
import IconButton from "@mui/material/IconButton";
|
import IconButton from "@mui/material/IconButton";
|
||||||
import HelpIcon from "@mui/icons-material/Help";
|
import HelpIcon from "@mui/icons-material/Help";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
|
import MathNotation from "../../Documentation/data/MathNotation.json";
|
||||||
|
import { MathNotationOutput } from "../../Documentation/ui/MathNotationOutput";
|
||||||
|
|
||||||
function MakeProductButton(): React.ReactElement {
|
function MakeProductButton(): React.ReactElement {
|
||||||
const corp = useCorporation();
|
const corp = useCorporation();
|
||||||
@@ -132,8 +133,11 @@ export function DivisionOverview(props: DivisionOverviewProps): React.ReactEleme
|
|||||||
<>
|
<>
|
||||||
<Typography>Multiplier for this industry's sales due to its awareness and popularity.</Typography>
|
<Typography>Multiplier for this industry's sales due to its awareness and popularity.</Typography>
|
||||||
<br />
|
<br />
|
||||||
<MathJax>{`\\(\\text{${division.industry} Industry: }\\alpha = ${division.advertisingFactor}\\)`}</MathJax>
|
<Typography>
|
||||||
<MathJax>{`\\(\\text{multiplier} = \\left((\\text{awareness}+1)^{\\alpha} \\times (\\text{popularity}+1)^{\\alpha} \\times \\frac{\\text{popularity}+0.001}{\\text{awareness}}\\right)^{0.85}\\)`}</MathJax>
|
{division.industry} Industry: 𝞪 = {division.advertisingFactor}
|
||||||
|
</Typography>
|
||||||
|
<br />
|
||||||
|
<MathNotationOutput notation={MathNotation.CorpAdvertFactor} />
|
||||||
<br />
|
<br />
|
||||||
<StatsTable
|
<StatsTable
|
||||||
rows={[
|
rows={[
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Division } from "../Division";
|
import { Division } from "../Division";
|
||||||
import { MathJax } from "better-react-mathjax";
|
|
||||||
import { getRecordEntries } from "../../Types/Record";
|
import { getRecordEntries } from "../../Types/Record";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
division: Division;
|
division: Division;
|
||||||
@@ -10,13 +10,19 @@ interface IProps {
|
|||||||
export function IndustryProductEquation(props: IProps): React.ReactElement {
|
export function IndustryProductEquation(props: IProps): React.ReactElement {
|
||||||
const reqs = [];
|
const reqs = [];
|
||||||
for (const [reqMat, reqAmt] of getRecordEntries(props.division.requiredMaterials)) {
|
for (const [reqMat, reqAmt] of getRecordEntries(props.division.requiredMaterials)) {
|
||||||
if (!reqAmt) continue;
|
if (!reqAmt) {
|
||||||
reqs.push(String.raw`${reqAmt}\;\textit{${reqMat}}`);
|
continue;
|
||||||
}
|
}
|
||||||
const prod = props.division.producedMaterials.map((p) => `1\\;\\textit{${p}}`);
|
reqs.push(`${reqAmt} ${reqMat}`);
|
||||||
|
}
|
||||||
|
const prod = props.division.producedMaterials.map((materialName) => `1 ${materialName}`);
|
||||||
if (props.division.makesProducts) {
|
if (props.division.makesProducts) {
|
||||||
prod.push("\\textit{Products}");
|
prod.push("Products");
|
||||||
}
|
}
|
||||||
|
|
||||||
return <MathJax>{"\\(" + reqs.join("+") + `\\Rightarrow ` + prod.join("+") + "\\)"}</MathJax>;
|
return (
|
||||||
|
<Typography component="span">
|
||||||
|
{reqs.join(" + ")} ⟹ {prod.join(" + ")}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// React Component for displaying Corporation Overview info
|
// React Component for displaying Corporation Overview info
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { MathJax } from "better-react-mathjax";
|
|
||||||
import { LevelableUpgrade } from "./LevelableUpgrade";
|
import { LevelableUpgrade } from "./LevelableUpgrade";
|
||||||
import { Unlock } from "./Unlock";
|
import { Unlock } from "./Unlock";
|
||||||
import { BribeFactionModal } from "./modals/BribeFactionModal";
|
import { BribeFactionModal } from "./modals/BribeFactionModal";
|
||||||
@@ -36,6 +35,8 @@ import { ButtonWithTooltip } from "../../ui/Components/ButtonWithTooltip";
|
|||||||
import { CreateCorporationModal } from "./modals/CreateCorporationModal";
|
import { CreateCorporationModal } from "./modals/CreateCorporationModal";
|
||||||
import InfoIcon from "@mui/icons-material/Info";
|
import InfoIcon from "@mui/icons-material/Info";
|
||||||
import { CorruptibleText } from "../../ui/React/CorruptibleText";
|
import { CorruptibleText } from "../../ui/React/CorruptibleText";
|
||||||
|
import MathNotation from "../../Documentation/data/MathNotation.json";
|
||||||
|
import { MathNotationOutput } from "../../Documentation/ui/MathNotationOutput";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
rerender: () => void;
|
rerender: () => void;
|
||||||
@@ -390,9 +391,9 @@ function DividendsStats({ profit }: IDividendsStatsProps): React.ReactElement {
|
|||||||
"TributeModifier". Formulas:
|
"TributeModifier". Formulas:
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<MathJax>{`\\(TotalDividends = DividendRate\\ast(Revenue - Expenses)\\ast 10\\)`}</MathJax>
|
<MathNotationOutput notation={MathNotation.CorpTotalDividends} />
|
||||||
<br />
|
<br />
|
||||||
<MathJax>{`\\(Dividend = \\left(OwnedShares\\ast\\frac{TotalDividends}{TotalShares}\\right)^{1 - TributeModifier}\\)`}</MathJax>
|
<MathNotationOutput notation={MathNotation.CorpDividend} />
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
10
src/Documentation/data/MathNotation.json
Normal file
10
src/Documentation/data/MathNotation.json
Normal file
@@ -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}"
|
||||||
|
}
|
||||||
1
src/Documentation/data/MathNotationOutput.json
Normal file
1
src/Documentation/data/MathNotationOutput.json
Normal file
File diff suppressed because one or more lines are too long
@@ -1,5 +1,6 @@
|
|||||||
import { AllPages } from "./pages";
|
import { AllPages } from "./pages";
|
||||||
import { EventEmitter } from "../utils/EventEmitter";
|
import { EventEmitter } from "../utils/EventEmitter";
|
||||||
|
import MathNotationOutput from "./data/MathNotationOutput.json";
|
||||||
|
|
||||||
export const resolvePage = (title: string): { pageName: string | null; pageContent: string } => {
|
export const resolvePage = (title: string): { pageName: string | null; pageContent: string } => {
|
||||||
const lang = new Intl.Locale(navigator.language).language;
|
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 {
|
export function openDocumentationPopUp(path: string): void {
|
||||||
DocumentationPopUpEvents.emit(path);
|
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<string, string>)[value];
|
||||||
|
if (output == null) {
|
||||||
|
throw new Error(`Unknown math notation: ${value}`);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|||||||
9
src/Documentation/ui/MathNotationOutput.tsx
Normal file
9
src/Documentation/ui/MathNotationOutput.tsx
Normal file
@@ -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 <Typography dangerouslySetInnerHTML={{ __html: convertMathNotation(notation) }} />;
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
import { CONSTANTS } from "../../Constants";
|
|
||||||
import { Faction } from "../Faction";
|
import { Faction } from "../Faction";
|
||||||
import { Player } from "@player";
|
import { Player } from "@player";
|
||||||
import { canDonate, donate, repFromDonation } from "../formulas/donation";
|
import { canDonate, donate, repFromDonation } from "../formulas/donation";
|
||||||
@@ -10,12 +9,13 @@ import { Money } from "../../ui/React/Money";
|
|||||||
import { Reputation } from "../../ui/React/Reputation";
|
import { Reputation } from "../../ui/React/Reputation";
|
||||||
|
|
||||||
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
import { dialogBoxCreate } from "../../ui/React/DialogBox";
|
||||||
import { MathJax } from "better-react-mathjax";
|
|
||||||
|
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import Paper from "@mui/material/Paper";
|
import Paper from "@mui/material/Paper";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import { NumberInput } from "../../ui/React/NumberInput";
|
import { NumberInput } from "../../ui/React/NumberInput";
|
||||||
|
import MathNotation from "../../Documentation/data/MathNotation.json";
|
||||||
|
import { MathNotationOutput } from "../../Documentation/ui/MathNotationOutput";
|
||||||
|
|
||||||
type DonateOptionProps = {
|
type DonateOptionProps = {
|
||||||
faction: Faction;
|
faction: Faction;
|
||||||
@@ -27,7 +27,6 @@ type DonateOptionProps = {
|
|||||||
/** React component for a donate option on the Faction UI */
|
/** React component for a donate option on the Faction UI */
|
||||||
export function DonateOption({ faction, favorToDonate, disabled, rerender }: DonateOptionProps): React.ReactElement {
|
export function DonateOption({ faction, favorToDonate, disabled, rerender }: DonateOptionProps): React.ReactElement {
|
||||||
const [donateAmt, setDonateAmt] = useState<number>(NaN);
|
const [donateAmt, setDonateAmt] = useState<number>(NaN);
|
||||||
const digits = (CONSTANTS.DonateMoneyToRepDivisor + "").length - 1;
|
|
||||||
|
|
||||||
function onDonate(): void {
|
function onDonate(): void {
|
||||||
const repGain = donate(donateAmt, faction);
|
const repGain = donate(donateAmt, faction);
|
||||||
@@ -76,9 +75,7 @@ export function DonateOption({ faction, favorToDonate, disabled, rerender }: Don
|
|||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Typography>
|
<MathNotationOutput notation={MathNotation.RepDonation} />
|
||||||
<MathJax>{`\\(reputation = \\frac{\\text{donation amount} \\cdot \\text{reputation multiplier}}{10^{${digits}}}\\)`}</MathJax>
|
|
||||||
</Typography>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import Typography from "@mui/material/Typography";
|
|||||||
import { Player } from "@player";
|
import { Player } from "@player";
|
||||||
|
|
||||||
import { Money } from "../../ui/React/Money";
|
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 {
|
interface IProps {
|
||||||
rerender: () => void;
|
rerender: () => void;
|
||||||
@@ -32,7 +33,7 @@ export function CoresButton(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip title={<MathJax>{`\\(\\large{cost = 10^9 \\cdot 7.5 ^{\\text{cores}}}\\)`}</MathJax>}>
|
<Tooltip title={<MathNotationOutput notation={MathNotation.CoreCost} />}>
|
||||||
<span>
|
<span>
|
||||||
<br />
|
<br />
|
||||||
<Typography>
|
<Typography>
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ import { purchaseRamForHomeComputer } from "../../Server/ServerPurchases";
|
|||||||
import { Money } from "../../ui/React/Money";
|
import { Money } from "../../ui/React/Money";
|
||||||
import { formatRam } from "../../ui/formatNumber";
|
import { formatRam } from "../../ui/formatNumber";
|
||||||
|
|
||||||
import { MathJax } from "better-react-mathjax";
|
|
||||||
import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
|
import { currentNodeMults } from "../../BitNode/BitNodeMultipliers";
|
||||||
import { ServerConstants } from "../../Server/data/Constants";
|
import { ServerConstants } from "../../Server/data/Constants";
|
||||||
|
import MathNotation from "../../Documentation/data/MathNotation.json";
|
||||||
|
import { MathNotationOutput } from "../../Documentation/ui/MathNotationOutput";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
rerender: () => void;
|
rerender: () => void;
|
||||||
@@ -30,12 +31,13 @@ export function RamButton(props: IProps): React.ReactElement {
|
|||||||
props.rerender();
|
props.rerender();
|
||||||
}
|
}
|
||||||
|
|
||||||
const bnMult = currentNodeMults.HomeComputerRamCost === 1 ? "" : `\\cdot ${currentNodeMults.HomeComputerRamCost}`;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
<MathJax>{`\\(\\large{cost = ram \\cdot 3.2 \\cdot 10^4 \\cdot 1.58^{log_2{(ram)}}} ${bnMult}\\)`}</MathJax>
|
<>
|
||||||
|
<Typography>HomeRamCostMult = {currentNodeMults.HomeComputerRamCost}</Typography>
|
||||||
|
<MathNotationOutput notation={MathNotation.HomeRAMCost} />
|
||||||
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
|
|||||||
6
src/ThirdParty/RehypePlugin.d.mts
vendored
Normal file
6
src/ThirdParty/RehypePlugin.d.mts
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
// Minimal type information
|
||||||
|
export declare function createPlugin(
|
||||||
|
createRenderer: () => {
|
||||||
|
render: (value: string, { display }: { display: boolean }) => any[];
|
||||||
|
},
|
||||||
|
): () => (tree: any, file: any) => void;
|
||||||
343
src/ThirdParty/RehypePlugin.mjs
vendored
Normal file
343
src/ThirdParty/RehypePlugin.mjs
vendored
Normal file
@@ -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 <fluke8259@gmail.com>
|
||||||
|
|
||||||
|
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>} 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.
|
||||||
|
* <http://docs.mathjax.org/en/latest/options/input/tex.html#the-configuration-block>
|
||||||
|
* @property {string | null | undefined} [baseURL]
|
||||||
|
* URL for use with links to tags, when there is a `<base>` tag in effect
|
||||||
|
* (optional).
|
||||||
|
* @property {RegExp | null | undefined} [digits]
|
||||||
|
* Pattern for recognizing numbers (optional).
|
||||||
|
* @property {ReadonlyArray<MathNotation> | 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<MathNotation> | 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<string> | 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: <http://docs.mathjax.org/en/latest/options/input/tex.html#the-configuration-block>
|
||||||
|
*
|
||||||
|
* @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<OutputChtmlOptions> | null | undefined} [chtml]
|
||||||
|
* Configuration for the output, when CHTML (optional).
|
||||||
|
* @property {Readonly<OutputSvgOptions> | null | undefined} [svg]
|
||||||
|
* Configuration for the output, when SVG (optional).
|
||||||
|
* @property {Readonly<InputTexOptions> | null | undefined} [tex]
|
||||||
|
* Configuration for the input TeX (optional).
|
||||||
|
*
|
||||||
|
* @typedef OutputChtmlOptions
|
||||||
|
* Configuration for output CHTML.
|
||||||
|
* <http://docs.mathjax.org/en/latest/options/output/chtml.html#the-configuration-block>
|
||||||
|
* @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<Record<string, boolean>> | null | undefined} [skipAttributes]
|
||||||
|
* RFDa and other attributes NOT to copy to the output (optional).
|
||||||
|
*
|
||||||
|
* @typedef OutputSvgOptions
|
||||||
|
* Configuration for output SVG.
|
||||||
|
* <http://docs.mathjax.org/en/latest/options/output/svg.html#the-configuration-block>
|
||||||
|
* @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 `<title>` 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();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -69,7 +69,6 @@ import { BypassWrapper } from "./React/BypassWrapper";
|
|||||||
|
|
||||||
import { Apr1 } from "./Apr1";
|
import { Apr1 } from "./Apr1";
|
||||||
import { V2Modal } from "../utils/V2Modal";
|
import { V2Modal } from "../utils/V2Modal";
|
||||||
import { MathJaxContext } from "better-react-mathjax";
|
|
||||||
import { useRerender } from "./React/hooks";
|
import { useRerender } from "./React/hooks";
|
||||||
import { HistoryProvider } from "./React/Documentation";
|
import { HistoryProvider } from "./React/Documentation";
|
||||||
import { GoRoot } from "../Go/ui/GoRoot";
|
import { GoRoot } from "../Go/ui/GoRoot";
|
||||||
@@ -505,7 +504,7 @@ export function GameRoot(): React.ReactElement {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MathJaxContext version={3} src={__webpack_public_path__ + "mathjax/tex-chtml.js"}>
|
<>
|
||||||
<ErrorBoundary key={errorBoundaryKey} softReset={softReset}>
|
<ErrorBoundary key={errorBoundaryKey} softReset={softReset}>
|
||||||
<BypassWrapper content={bypassGame ? mainPage : null}>
|
<BypassWrapper content={bypassGame ? mainPage : null}>
|
||||||
<HistoryProvider>
|
<HistoryProvider>
|
||||||
@@ -547,6 +546,6 @@ export function GameRoot(): React.ReactElement {
|
|||||||
</BypassWrapper>
|
</BypassWrapper>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
<V2Modal />
|
<V2Modal />
|
||||||
</MathJaxContext>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 { code, Pre } from "./code";
|
||||||
import { A } from "./a";
|
import { A } from "./a";
|
||||||
import remarkMath from "remark-math";
|
import remarkMath from "remark-math";
|
||||||
import rehypeMathjax from "rehype-mathjax/svg";
|
|
||||||
import rehypeRaw from "rehype-raw";
|
import rehypeRaw from "rehype-raw";
|
||||||
import { FilePath } from "../../Paths/FilePath";
|
import { FilePath } from "../../Paths/FilePath";
|
||||||
import { getPage } from "../../Documentation/root";
|
import { convertMathNotation, getPage } from "../../Documentation/root";
|
||||||
import { DocImages } from "../../Documentation/pages";
|
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 {
|
export function MD({ pageFilePath }: { pageFilePath: FilePath }): React.ReactElement {
|
||||||
const pageContent = getPage(pageFilePath);
|
const pageContent = getPage(pageFilePath);
|
||||||
@@ -38,8 +58,9 @@ export function MD({ pageFilePath }: { pageFilePath: FilePath }): React.ReactEle
|
|||||||
a: A,
|
a: A,
|
||||||
}}
|
}}
|
||||||
remarkPlugins={[remarkGfm, remarkMath]}
|
remarkPlugins={[remarkGfm, remarkMath]}
|
||||||
// Use rehypeRaw to support HTML content in NS API docs.
|
// Use a custom rehype plugin to render LaTeX notation in md files. Use rehypeRaw to support HTML content in NS
|
||||||
rehypePlugins={[rehypeMathjax, rehypeRaw]}
|
// API docs.
|
||||||
|
rehypePlugins={[rehypePlugin, rehypeRaw]}
|
||||||
transformImageUri={(__src, alt) => {
|
transformImageUri={(__src, alt) => {
|
||||||
return DocImages[alt];
|
return DocImages[alt];
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { MathJax } from "better-react-mathjax";
|
|
||||||
|
|
||||||
import InfoIcon from "@mui/icons-material/Info";
|
import InfoIcon from "@mui/icons-material/Info";
|
||||||
import Tooltip from "@mui/material/Tooltip";
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
|
|
||||||
import { Favor } from "../../ui/React/Favor";
|
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 {
|
export function FavorInfo({ favor, boldLabel }: { favor: number; boldLabel?: boolean }): React.ReactElement {
|
||||||
return (
|
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
|
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.
|
amount of reputation you earned with this faction across all resets.
|
||||||
</Typography>
|
</Typography>
|
||||||
<MathJax>{"\\(\\huge{r = reputation}\\)"}</MathJax>
|
<Typography style={{ fontSize: "2rem" }}>r = Reputation gain</Typography>
|
||||||
<MathJax>{"\\(\\huge{\\Delta r = \\Delta r \\times \\frac{100+favor}{100}}\\)"}</MathJax>
|
<MathNotationOutput notation={MathNotation.FavorBonus} />
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { MathJax } from "better-react-mathjax";
|
|
||||||
|
|
||||||
import InfoIcon from "@mui/icons-material/Info";
|
import InfoIcon from "@mui/icons-material/Info";
|
||||||
import Tooltip from "@mui/material/Tooltip";
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
@@ -8,6 +7,8 @@ import Typography from "@mui/material/Typography";
|
|||||||
import { addRepToFavor } from "../../Faction/formulas/favor";
|
import { addRepToFavor } from "../../Faction/formulas/favor";
|
||||||
import { Favor } from "../../ui/React/Favor";
|
import { Favor } from "../../ui/React/Favor";
|
||||||
import { Reputation } from "./Reputation";
|
import { Reputation } from "./Reputation";
|
||||||
|
import MathNotation from "../../Documentation/data/MathNotation.json";
|
||||||
|
import { MathNotationOutput } from "../../Documentation/ui/MathNotationOutput";
|
||||||
|
|
||||||
export function ReputationInfo({
|
export function ReputationInfo({
|
||||||
favor,
|
favor,
|
||||||
@@ -26,8 +27,8 @@ export function ReputationInfo({
|
|||||||
You will have <Favor favor={addRepToFavor(favor, playerReputation)} /> faction favor after installing an
|
You will have <Favor favor={addRepToFavor(favor, playerReputation)} /> faction favor after installing an
|
||||||
Augmentation.
|
Augmentation.
|
||||||
</Typography>
|
</Typography>
|
||||||
<MathJax>{"\\(\\huge{r = \\text{total faction reputation}}\\)"}</MathJax>
|
<Typography style={{ fontSize: "2rem" }}>r = Total faction reputation</Typography>
|
||||||
<MathJax>{"\\(\\huge{favor=\\log_{1.02}\\left(1+\\frac{r}{25000}\\right)}\\)"}</MathJax>
|
<MathNotationOutput notation={MathNotation.RepToFavor} />
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
46
tools/bundle-doc/generate-math-notation-output.mjs
Normal file
46
tools/bundle-doc/generate-math-notation-output.mjs
Normal file
@@ -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",
|
||||||
|
});
|
||||||
@@ -1,27 +1,25 @@
|
|||||||
const fs = require("fs");
|
import { writeFileSync } from "node:fs";
|
||||||
const path = require("path");
|
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 docFiles = [];
|
||||||
const nsDocFiles = [];
|
const nsDocFiles = [];
|
||||||
const docImagesFiles = [];
|
const docImagesFiles = [];
|
||||||
const docRoot = path.resolve(__dirname, "../../src/Documentation/doc");
|
const docRoot = resolve(__dirname, "../../src/Documentation/doc");
|
||||||
const markdownRoot = path.resolve(__dirname, "../../markdown");
|
const markdownRoot = resolve(__dirname, "../../markdown");
|
||||||
const docImagesRoot = path.resolve(__dirname, "../../src/Documentation/images");
|
const docImagesRoot = resolve(__dirname, "../../src/Documentation/images");
|
||||||
const addFileToListOfDocPages = (files, root, filePath) => {
|
|
||||||
|
function addFileToListOfDocPages(files, root, filePath) {
|
||||||
// Windows path uses "\", so we need to replace it with "/".
|
// Windows path uses "\", so we need to replace it with "/".
|
||||||
files.push(path.relative(root, filePath).replace(/\\/g, "/"));
|
files.push(relative(root, filePath).replace(/\\/g, "/"));
|
||||||
};
|
}
|
||||||
const processDir = (dir) => {
|
|
||||||
if (!fs.existsSync(dir)) {
|
function processDir(dir) {
|
||||||
return;
|
walkDir(dir, (filePath) => {
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
if (filePath.startsWith(docRoot)) {
|
if (filePath.startsWith(docRoot)) {
|
||||||
addFileToListOfDocPages(docFiles, docRoot, filePath);
|
addFileToListOfDocPages(docFiles, docRoot, filePath);
|
||||||
} else if (filePath.startsWith(markdownRoot)) {
|
} else if (filePath.startsWith(markdownRoot)) {
|
||||||
@@ -29,8 +27,8 @@ const processDir = (dir) => {
|
|||||||
} else {
|
} else {
|
||||||
addFileToListOfDocPages(docImagesFiles, docImagesRoot, filePath);
|
addFileToListOfDocPages(docImagesFiles, docImagesRoot, filePath);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
processDir(docRoot);
|
processDir(docRoot);
|
||||||
processDir(markdownRoot);
|
processDir(markdownRoot);
|
||||||
@@ -68,9 +66,4 @@ export const nsApiPages = Object.keys(AllPages)
|
|||||||
export const DocImages: Record<string, string> = {};
|
export const DocImages: Record<string, string> = {};
|
||||||
${docImagesFiles.map((f) => `DocImages["${f}"] = docImages_${f.replaceAll(/\.|-/g, "_")};\n`).join("")}`;
|
${docImagesFiles.map((f) => `DocImages["${f}"] = docImages_${f.replaceAll(/\.|-/g, "_")};\n`).join("")}`;
|
||||||
|
|
||||||
fs.writeFile(path.resolve(__dirname, "../../src/Documentation/pages.ts"), autogeneratedContent, (err) => {
|
writeFileSync(resolve(__dirname, "../../src/Documentation/pages.ts"), autogeneratedContent, { encoding: "utf-8" });
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
// file written successfully
|
|
||||||
});
|
|
||||||
63
tools/bundle-doc/utils.mjs
Normal file
63
tools/bundle-doc/utils.mjs
Normal file
@@ -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,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -13,12 +13,16 @@ rm input/bitburner.api.json && rm -r input
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Bundling ingame documentation..."
|
echo "Bundling ingame documentation..."
|
||||||
node tools/bundle-doc/index.js
|
node tools/bundle-doc/index.mjs
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Add generated docs to git..."
|
echo "Add generated docs to git..."
|
||||||
# This git add is needed due to documenter using wrong line endings. Console spam discarded.
|
# 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
|
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 ""
|
||||||
echo "Documentation build completed."
|
echo "Documentation build completed."
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin");
|
|||||||
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||||
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
|
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
|
||||||
const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
|
const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
|
||||||
const CopyPlugin = require("copy-webpack-plugin");
|
|
||||||
|
|
||||||
/** @type {import("webpack-cli").CallableOption} */
|
/** @type {import("webpack-cli").CallableOption} */
|
||||||
module.exports = (env, argv) => {
|
module.exports = (env, argv) => {
|
||||||
@@ -124,15 +123,6 @@ module.exports = (env, argv) => {
|
|||||||
module: true,
|
module: true,
|
||||||
}),
|
}),
|
||||||
enableReactRefresh && new ReactRefreshWebpackPlugin(),
|
enableReactRefresh && new ReactRefreshWebpackPlugin(),
|
||||||
new CopyPlugin({
|
|
||||||
patterns: [
|
|
||||||
{
|
|
||||||
from: "{tex-chtml.js,*/**/*}",
|
|
||||||
to: "mathjax",
|
|
||||||
context: "node_modules/mathjax-full/es5",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
].filter(Boolean),
|
].filter(Boolean),
|
||||||
target: "web",
|
target: "web",
|
||||||
entry: entry,
|
entry: entry,
|
||||||
|
|||||||
Reference in New Issue
Block a user