diff --git a/client/homebrew/brewRenderer/brewRenderer.jsx b/client/homebrew/brewRenderer/brewRenderer.jsx index 21c0608bd..c83a2029b 100644 --- a/client/homebrew/brewRenderer/brewRenderer.jsx +++ b/client/homebrew/brewRenderer/brewRenderer.jsx @@ -186,7 +186,7 @@ const BrewRenderer = (props)=>{ } else { if(pageText.startsWith('\\page')) { const firstLineTokens = Markdown.marked.lexer(pageText.split('\n', 1)[0])[0].tokens; - const injectedTags = firstLineTokens.find((obj)=>obj.injectedTags !== undefined)?.injectedTags; + const injectedTags = firstLineTokens?.find((obj)=>obj.injectedTags !== undefined)?.injectedTags; if(injectedTags) { styles = { ...styles, ...injectedTags.styles }; styles = _.mapKeys(styles, (v, k)=>k.startsWith('--') ? k : _.camelCase(k)); // Convert CSS to camelCase for React diff --git a/package-lock.json b/package-lock.json index 69cc1cade..2e3af673a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,11 +10,11 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.26.9", - "@babel/plugin-transform-runtime": "^7.26.9", + "@babel/core": "^7.26.10", + "@babel/plugin-transform-runtime": "^7.26.10", "@babel/preset-env": "^7.26.9", "@babel/preset-react": "^7.26.3", - "@googleapis/drive": "^8.16.0", + "@googleapis/drive": "^11.0.0", "body-parser": "^1.20.2", "classnames": "^2.5.1", "codemirror": "^5.65.6", @@ -33,7 +33,7 @@ "jwt-simple": "^0.5.6", "less": "^3.13.1", "lodash": "^4.17.21", - "marked": "14.0.0", + "marked": "15.0.0", "marked-emoji": "^2.0.0", "marked-extended-tables": "^2.0.1", "marked-gfm-heading-id": "^4.0.1", @@ -41,21 +41,23 @@ "marked-subsuper-text": "^1.0.3", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", - "mongoose": "^8.12.1", - "nanoid": "5.1.3", + "mongoose": "^8.13.0", + "nanoid": "5.1.5", "nconf": "^0.12.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-frame-component": "^4.1.3", - "react-router": "^7.3.0", + "react-router": "^7.4.0", + "romans": "^3.0.0", "sanitize-filename": "1.6.3", - "superagent": "^10.1.1", - "vitreum": "git+https://git@github.com/calculuschild/vitreum.git" + "superagent": "^10.2.0", + "vitreum": "git+https://git@github.com/calculuschild/vitreum.git", + "written-number": "^0.11.1" }, "devDependencies": { "@stylistic/stylelint-plugin": "^3.1.2", "babel-plugin-transform-import-meta": "^2.3.2", - "eslint": "^9.22.0", + "eslint": "^9.23.0", "eslint-plugin-jest": "^28.11.0", "eslint-plugin-react": "^7.37.4", "globals": "^16.0.0", @@ -63,10 +65,10 @@ "jest-expect-message": "^1.1.3", "jsdom-global": "^3.0.2", "postcss-less": "^6.0.0", - "stylelint": "^16.16.0", + "stylelint": "^16.17.0", "stylelint-config-recess-order": "^6.0.0", "stylelint-config-recommended": "^15.0.0", - "supertest": "^7.0.0" + "supertest": "^7.1.0" }, "engines": { "node": "^20.18.x", @@ -110,21 +112,21 @@ } }, "node_modules/@babel/core": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz", - "integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.9", + "@babel/generator": "^7.26.10", "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.9", - "@babel/parser": "^7.26.9", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", "@babel/template": "^7.26.9", - "@babel/traverse": "^7.26.9", - "@babel/types": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -140,13 +142,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz", - "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", + "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -389,12 +391,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", - "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.9" + "@babel/types": "^7.27.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -1406,15 +1408,15 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.26.9.tgz", - "integrity": "sha512-Jf+8y9wXQbbxvVYTM8gO5oEF2POdNji0NMltEkG7FtmzD9PVz7/lxpqSdTvwsjTMU5HIHuDVNf2SOxLkWi+wPQ==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.26.10.tgz", + "integrity": "sha512-NWaL2qG6HRpONTnj4JvDU6th4jYeZOJgu3QhmFTCihib0ermtOJqktA5BduGm3suhhVe9EMP9c9+mfJ/I9slqw==", "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-plugin-utils": "^7.26.5", "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.6", + "babel-plugin-polyfill-corejs3": "^0.11.0", "babel-plugin-polyfill-regenerator": "^0.6.1", "semver": "^6.3.1" }, @@ -1640,19 +1642,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", - "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.3", - "core-js-compat": "^3.40.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, "node_modules/@babel/preset-modules": { "version": "0.1.6-no-external-plugins", "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", @@ -1700,30 +1689,30 @@ } }, "node_modules/@babel/template": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", - "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz", - "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", + "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.9", - "@babel/parser": "^7.26.9", - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.9", + "@babel/generator": "^7.27.0", + "@babel/parser": "^7.27.0", + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1741,9 +1730,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", - "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -1878,9 +1867,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.1.0.tgz", - "integrity": "sha512-kLrdPDJE1ckPo94kmPPf9Hfd0DU0Jw6oKYrhe+pwSC0iTUInmTa+w6fw8sGgcfkFJGNdWOUeOaDM4quW4a7OkA==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.0.tgz", + "integrity": "sha512-yJLLmLexii32mGrhW29qvU3QBVTu0GUmEf/J4XsBtVhp4JkIUFN/BjWqTF63yRvGApIDpZm5fa97LtYtINmfeQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1901,9 +1890,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz", - "integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1938,9 +1927,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.22.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.22.0.tgz", - "integrity": "sha512-vLFajx9o8d1/oL2ZkpMYbkLv8nDB6yaIwFNt7nI4+I80U/z03SxmfOMsLbvWr3p7C+Wnoh//aOu2pQW8cS0HCQ==", + "version": "9.23.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz", + "integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==", "dev": true, "license": "MIT", "engines": { @@ -1972,9 +1961,9 @@ } }, "node_modules/@googleapis/drive": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-8.16.0.tgz", - "integrity": "sha512-Xi2mMrUTQ+gsfyouRGd0pfnL+jjg4n4sjKsJruM1y4DknuRfdSBTk5E//WrL0YJ/CqpcBgyd7L8DvaPRtxZD3Q==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@googleapis/drive/-/drive-11.0.0.tgz", + "integrity": "sha512-vhkl6/MZ8k5h5XOyenWOD4ys+SNdb8wKYvzU6OBGFx/TzJyHRm4JwgvE8uVDFU6efzNRS0mOiNRfY6nrmHOTtg==", "license": "Apache-2.0", "dependencies": { "googleapis-common": "^7.0.0" @@ -3140,9 +3129,9 @@ } }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "dev": true, "license": "MIT", "bin": { @@ -3704,12 +3693,13 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.10.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", - "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", + "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", + "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2", - "core-js-compat": "^3.38.0" + "@babel/helper-define-polyfill-provider": "^0.6.3", + "core-js-compat": "^3.40.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -5697,19 +5687,19 @@ "license": "MIT" }, "node_modules/eslint": { - "version": "9.22.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.22.0.tgz", - "integrity": "sha512-9V/QURhsRN40xuHXWjV64yvrzMjcz7ZyNoF2jJFmy9j/SLk0u1OLSZgXi28MrXjymnjEGSR80WCdab3RGMDveQ==", + "version": "9.23.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.23.0.tgz", + "integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.2", - "@eslint/config-helpers": "^0.1.0", + "@eslint/config-helpers": "^0.2.0", "@eslint/core": "^0.12.0", - "@eslint/eslintrc": "^3.3.0", - "@eslint/js": "9.22.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.23.0", "@eslint/plugin-kit": "^0.2.7", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -9904,9 +9894,9 @@ } }, "node_modules/marked": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz", - "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==", + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.0.tgz", + "integrity": "sha512-0mouKmBROJv/WSHJBPZZyYofUgawMChnD5je/g+aOBXsHDjb/IsnTQj7mnhQZu+qPJmRQ0ecX3mLGEUm3BgwYA==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -10284,9 +10274,9 @@ } }, "node_modules/mongodb-connection-string-url/node_modules/tr46": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", - "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.0.tgz", + "integrity": "sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw==", "license": "MIT", "dependencies": { "punycode": "^2.3.1" @@ -10305,12 +10295,12 @@ } }, "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.1.tgz", - "integrity": "sha512-mDGf9diDad/giZ/Sm9Xi2YcyzaFpbdLpJPr+E9fSkyQ7KpQD4SdFcugkRQYzhmfI4KeV4Qpnn2sKPdo+kmsgRQ==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", "license": "MIT", "dependencies": { - "tr46": "^5.0.0", + "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" }, "engines": { @@ -10318,14 +10308,14 @@ } }, "node_modules/mongoose": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.12.1.tgz", - "integrity": "sha512-UW22y8QFVYmrb36hm8cGncfn4ARc/XsYWQwRTaj0gxtQk1rDuhzDO1eBantS+hTTatfAIS96LlRCJrcNHvW5+Q==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.13.0.tgz", + "integrity": "sha512-e/iYV1mPeOkg+SWAMHzt3t42/EZyER3OB1H2pjP9C3vQ+Qb5DMeV9Kb+YCUycKgScA3fbwL7dKG4EpinGlg21g==", "license": "MIT", "dependencies": { "bson": "^6.10.3", "kareem": "2.6.3", - "mongodb": "~6.14.0", + "mongodb": "~6.15.0", "mpath": "0.9.0", "mquery": "5.0.0", "ms": "2.1.3", @@ -10401,9 +10391,9 @@ } }, "node_modules/mongoose/node_modules/mongodb": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.14.2.tgz", - "integrity": "sha512-kMEHNo0F3P6QKDq17zcDuPeaywK/YaJVCEQRzPF3TOM/Bl9MFg64YE5Tu7ifj37qZJMhwU1tl2Ioivws5gRG5Q==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.15.0.tgz", + "integrity": "sha512-ifBhQ0rRzHDzqp9jAQP6OwHSH7dbYIQjD3SbJs9YYk9AikKEettW/9s/tbSFDTpXcRbF+u1aLrhHxDFaYtZpFQ==", "license": "Apache-2.0", "dependencies": { "@mongodb-js/saslprep": "^1.1.9", @@ -10480,9 +10470,9 @@ "optional": true }, "node_modules/nanoid": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.3.tgz", - "integrity": "sha512-zAbEOEr7u2CbxwoMRlz/pNSpRP0FdAU4pRaYunCdEezWohXFs+a0Xw7RfkKaezMsmSM1vttcLthJtwRnVtOfHQ==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz", + "integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==", "funding": [ { "type": "github", @@ -11753,9 +11743,9 @@ "license": "MIT" }, "node_modules/react-router": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.3.0.tgz", - "integrity": "sha512-466f2W7HIWaNXTKM5nHTqNxLrHTyXybm7R0eBlVSt0k/u55tTCDO194OIx/NrYD4TS5SXKTNekXfT37kMKUjgw==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.4.0.tgz", + "integrity": "sha512-Y2g5ObjkvX3VFeVt+0CIPuYd9PpgqCslG7ASSIdN73LwA1nNWzcMLaoMRJfP3prZFI92svxFwbn7XkLJ+UPQ6A==", "license": "MIT", "dependencies": { "@types/cookie": "^0.6.0", @@ -12132,6 +12122,12 @@ "inherits": "^2.0.1" } }, + "node_modules/romans": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/romans/-/romans-3.0.0.tgz", + "integrity": "sha512-7DDsAfhtpRr/ZFQXiHDrC3Pe00agcAsFiNt5nNx4ZAQlsc6yJG0mvXA5WAvO8YZyOg349twm2GYhHLw7rCXAzw==", + "license": "MIT" + }, "node_modules/rrweb-cssom": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", @@ -13139,9 +13135,9 @@ "license": "ISC" }, "node_modules/stylelint": { - "version": "16.16.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.16.0.tgz", - "integrity": "sha512-40X5UOb/0CEFnZVEHyN260HlSSUxPES+arrUphOumGWgXERHfwCD0kNBVILgQSij8iliYVwlc0V7M5bcLP9vPg==", + "version": "16.17.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.17.0.tgz", + "integrity": "sha512-I9OwVIWRMqVm2Br5iTbrfSqGRPWQUlvm6oXO1xZuYYu0Gpduy67N8wXOZv15p6E/JdlZiAtQaIoLKZEWk5hrjw==", "dev": true, "funding": [ { @@ -13395,9 +13391,10 @@ } }, "node_modules/superagent": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.1.1.tgz", - "integrity": "sha512-9pIwrHrOj3uAnqg9gDlW7EA2xv+N5au/dSM0kM22HTqmUu8jBxNT+8uA7tA3UoCnmiqzpSbu8rasIUZvbyamMQ==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.0.tgz", + "integrity": "sha512-IKeoGox6oG9zyDeizaezkJ2/aK0wc5la9st7WsAKyrAkfJ56W3whVbVtF68k6wuc87/y9T85NyON5FLz7Mrzzw==", + "license": "MIT", "dependencies": { "component-emitter": "^1.3.0", "cookiejar": "^2.1.4", @@ -13426,9 +13423,9 @@ } }, "node_modules/supertest": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.0.0.tgz", - "integrity": "sha512-qlsr7fIC0lSddmA3tzojvzubYxvlGtzumcdHgPwbFWMISQwL22MhM2Y3LNt+6w9Yyx7559VW5ab70dgphm8qQA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.0.tgz", + "integrity": "sha512-5QeSO8hSrKghtcWEoPiO036fxH0Ii2wVQfFZSP0oqQhmjk8bOLhDFXr4JrvaFmPuEWUoq4znY3uSi8UzLKxGqw==", "dev": true, "license": "MIT", "dependencies": { @@ -14915,6 +14912,12 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/written-number": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/written-number/-/written-number-0.11.1.tgz", + "integrity": "sha512-LhQ68uUnzHH0bwm/QiGA9JwqgadSDOwqB2AIs/LBsrOY6ScqVXKRN2slTCeKAhstDBJ/Of/Yxcjn0pnQmVlmtg==", + "license": "MIT" + }, "node_modules/ws": { "version": "7.5.10", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", diff --git a/package.json b/package.json index 96528b786..8e88a3fd4 100644 --- a/package.json +++ b/package.json @@ -84,11 +84,11 @@ ] }, "dependencies": { - "@babel/core": "^7.26.9", - "@babel/plugin-transform-runtime": "^7.26.9", + "@babel/core": "^7.26.10", + "@babel/plugin-transform-runtime": "^7.26.10", "@babel/preset-env": "^7.26.9", "@babel/preset-react": "^7.26.3", - "@googleapis/drive": "^8.16.0", + "@googleapis/drive": "^11.0.0", "body-parser": "^1.20.2", "classnames": "^2.5.1", "codemirror": "^5.65.6", @@ -107,7 +107,7 @@ "jwt-simple": "^0.5.6", "less": "^3.13.1", "lodash": "^4.17.21", - "marked": "14.0.0", + "marked": "15.0.0", "marked-emoji": "^2.0.0", "marked-extended-tables": "^2.0.1", "marked-gfm-heading-id": "^4.0.1", @@ -115,21 +115,23 @@ "marked-subsuper-text": "^1.0.3", "markedLegacy": "npm:marked@^0.3.19", "moment": "^2.30.1", - "mongoose": "^8.12.1", - "nanoid": "5.1.3", + "mongoose": "^8.13.0", + "nanoid": "5.1.5", "nconf": "^0.12.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-frame-component": "^4.1.3", - "react-router": "^7.3.0", + "react-router": "^7.4.0", + "romans": "^3.0.0", "sanitize-filename": "1.6.3", - "superagent": "^10.1.1", - "vitreum": "git+https://git@github.com/calculuschild/vitreum.git" + "superagent": "^10.2.0", + "vitreum": "git+https://git@github.com/calculuschild/vitreum.git", + "written-number": "^0.11.1" }, "devDependencies": { "@stylistic/stylelint-plugin": "^3.1.2", "babel-plugin-transform-import-meta": "^2.3.2", - "eslint": "^9.22.0", + "eslint": "^9.23.0", "eslint-plugin-jest": "^28.11.0", "eslint-plugin-react": "^7.37.4", "globals": "^16.0.0", @@ -137,9 +139,9 @@ "jest-expect-message": "^1.1.3", "jsdom-global": "^3.0.2", "postcss-less": "^6.0.0", - "stylelint": "^16.16.0", + "stylelint": "^16.17.0", "stylelint-config-recess-order": "^6.0.0", "stylelint-config-recommended": "^15.0.0", - "supertest": "^7.0.0" + "supertest": "^7.1.0" } } diff --git a/server/app.js b/server/app.js index 079f5e03c..6bd0c7c9a 100644 --- a/server/app.js +++ b/server/app.js @@ -69,14 +69,11 @@ const corsOptions = { 'https://homebrewery-stage.herokuapp.com', ]; - if(isLocalEnvironment) { - const localNetworkRegex = /^http:\/\/(localhost|127\.0\.0\.1|10\.\d+\.\d+\.\d+|192\.168\.\d+\.\d+|172\.(1[6-9]|2\d|3[0-1])\.\d+\.\d+):\d+$/; - allowedOrigins.push(localNetworkRegex); - } + const localNetworkRegex = /^http:\/\/(localhost|127\.0\.0\.1|10\.\d+\.\d+\.\d+|192\.168\.\d+\.\d+|172\.(1[6-9]|2\d|3[0-1])\.\d+\.\d+):\d+$/; const herokuRegex = /^https:\/\/(?:homebrewery-pr-\d+\.herokuapp\.com|naturalcrit-pr-\d+\.herokuapp\.com)$/; // Matches any Heroku app - if(!origin || allowedOrigins.includes(origin) || herokuRegex.test(origin)) { + if(!origin || allowedOrigins.includes(origin) || herokuRegex.test(origin) || (isLocalEnvironment && localNetworkRegex.test(origin))) { callback(null, true); } else { console.log(origin, 'not allowed'); diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js index a8b877f4b..6391b511b 100644 --- a/shared/naturalcrit/markdown.js +++ b/shared/naturalcrit/markdown.js @@ -8,6 +8,8 @@ import { markedSmartypantsLite as MarkedSmartypantsLite } import { gfmHeadingId as MarkedGFMHeadingId, resetHeadings as MarkedGFMResetHeadingIDs } from 'marked-gfm-heading-id'; import { markedEmoji as MarkedEmojis } from 'marked-emoji'; import MarkedSubSuperText from 'marked-subsuper-text'; +import { romanize } from 'romans'; +import writtenNumber from 'written-number'; //Icon fonts included so they can appear in emoji autosuggest dropdown import diceFont from '../../themes/fonts/iconFonts/diceFont.js'; @@ -59,6 +61,53 @@ mathParser.functions.signed = function (a) { if(a >= 0) return `+${a}`; return `${a}`; }; +// Add Roman numeral functions +mathParser.functions.toRomans = function (a) { + return romanize(a); +}; +mathParser.functions.toRomansUpper = function (a) { + return romanize(a).toUpperCase(); +}; +mathParser.functions.toRomansLower = function (a) { + return romanize(a).toLowerCase(); +}; +// Add character functions +mathParser.functions.toChar = function (a) { + if(a <= 0) return a; + const genChars = function (i) { + return (i > 26 ? genChars(Math.floor((i - 1) / 26)) : '') + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[(i - 1) % 26]; + }; + return genChars(a); +}; +mathParser.functions.toCharUpper = function (a) { + return mathParser.functions.toChar(a).toUpperCase(); +}; +mathParser.functions.toCharLower = function (a) { + return mathParser.functions.toChar(a).toLowerCase(); +}; +// Add word functions +mathParser.functions.toWords = function (a) { + return writtenNumber(a); +}; +mathParser.functions.toWordsUpper = function (a) { + return mathParser.functions.toWords(a).toUpperCase(); +}; +mathParser.functions.toWordsLower = function (a) { + return mathParser.functions.toWords(a).toLowerCase(); +}; +mathParser.functions.toWordsCaps = function (a) { + const words = mathParser.functions.toWords(a).split(' '); + return words.map((word)=>{ + return word.replace(/(?:^|\b|\s)(\w)/g, function(w, index) { + return index === 0 ? w.toLowerCase() : w.toUpperCase(); + }); + }).join(' '); +}; + +// Normalize variable names; trim edge spaces and shorten blocks of whitespace to 1 space +const normalizeVarNames = (label)=>{ + return label.trim().replace(/\s+/g, ' '); +}; //Processes the markdown within an HTML block if it's just a class-wrapper renderer.html = function (token) { @@ -99,7 +148,7 @@ renderer.link = function (token) { } let out = `-------------------=====v// const mathRegex = /[a-z]+\(|[+\-*/^(),]/g; @@ -664,8 +713,8 @@ function MarkedVariables() { }); } if(match[3]) { // Block Definition - const label = match[4] ? match[4].trim().replace(/\s+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space - const content = match[5] ? match[5].trim().replace(/[ \t]+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space + const label = match[4] ? normalizeVarNames(match[4]) : null; + const content = match[5] ? match[5].trim().replace(/[ \t]+/g, ' ') : null; // Normalize text content (except newlines for block-level content) varsQueue.push( { type : 'varDefBlock', @@ -674,7 +723,7 @@ function MarkedVariables() { }); } if(match[6]) { // Block Call - const label = match[7] ? match[7].trim().replace(/\s+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space + const label = match[7] ? normalizeVarNames(match[7]) : null; varsQueue.push( { type : 'varCallBlock', @@ -683,7 +732,7 @@ function MarkedVariables() { }); } if(match[8]) { // Inline Definition - const label = match[10] ? match[10].trim().replace(/\s+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space + const label = match[10] ? normalizeVarNames(match[10]) : null; let content = match[11] || null; // In case of nested (), find the correct matching end ) @@ -715,7 +764,7 @@ function MarkedVariables() { }); } if(match[12]) { // Inline Call - const label = match[13] ? match[13].trim().replace(/\s+/g, ' ') : null; // Trim edge spaces and shorten blocks of whitespace to 1 space + const label = match[13] ? normalizeVarNames(match[13]) : null; varsQueue.push( { type : 'varCallInline', @@ -902,7 +951,13 @@ let globalPageNumber = 0; const Markdown = { marked : Marked, render : (rawBrewText, pageNumber=0)=>{ - globalVarsList[pageNumber] = {}; //Reset global links for current page, to ensure values are parsed in order + const lastPageNumber = pageNumber > 0 ? globalVarsList[pageNumber - 1].HB_pageNumber.content : 0; + globalVarsList[pageNumber] = { //Reset global links for current page, to ensure values are parsed in order + 'HB_pageNumber' : { //Add document variables for this page + content : !isNaN(Number(lastPageNumber)) ? Number(lastPageNumber) + 1 : lastPageNumber, + resolved : true + } + }; varsQueue = []; //Could move into MarkedVariables() globalPageNumber = pageNumber; if(pageNumber==0) { diff --git a/tests/markdown/non-breaking-spaces.test.js b/tests/markdown/non-breaking-spaces.test.js index c4f36554a..83fb55509 100644 --- a/tests/markdown/non-breaking-spaces.test.js +++ b/tests/markdown/non-breaking-spaces.test.js @@ -54,19 +54,19 @@ describe('Non-Breaking Spaces', ()=>{ test('I am actually a single-line definition list!', function() { const source = 'Term ::> Definition 1\n'; const rendered = Markdown.render(source).trim(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
Term
> Definition 1
\n
`); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
Term
> Definition 1
\n
`); }); test('I am actually a definition list!', function() { const source = 'Term\n::> Definition 1\n'; const rendered = Markdown.render(source).trim(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
Term
\n
> Definition 1
`); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
Term
\n
> Definition 1
`); }); test('I am actually a two-term definition list!', function() { const source = 'Term\n::> Definition 1\n::>> Definition 2'; const rendered = Markdown.render(source).trim(); - expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
Term
\n
> Definition 1
\n
>> Definition 2
`); + expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`
Term
\n
> Definition 1
\n
>> Definition 2
`); }); }); diff --git a/tests/markdown/variables.test.js b/tests/markdown/variables.test.js index 41259da7e..ea98fe56c 100644 --- a/tests/markdown/variables.test.js +++ b/tests/markdown/variables.test.js @@ -370,6 +370,30 @@ describe('Cross-page variables', ()=>{ const rendered = renderAllPages([source0, source1]).join('\n\\page\n').trimReturns(); expect(rendered, `Input:\n${[source0, source1].join('\n\\page\n')}`, { showPrefix: false }).toBe('

two

one

\\page

two

'); }); + + it('Page numbering across pages : default', function() { + const source0 = `$[HB_pageNumber]\n\n`; + const source1 = `$[HB_pageNumber]\n\n`; + renderAllPages([source0, source1]).join('\n\\page\n').trimReturns(); //Requires one full render of document before hoisting is picked up + const rendered = renderAllPages([source0, source1]).join('\n\\page\n').trimReturns(); + expect(rendered, `Input:\n${[source0, source1].join('\n\\page\n')}`, { showPrefix: false }).toBe('

1

\\page

2

'); + }); + + it('Page numbering across pages : custom page number (Number)', function() { + const source0 = `[HB_pageNumber]:100\n\n$[HB_pageNumber]\n\n`; + const source1 = `$[HB_pageNumber]\n\n`; + renderAllPages([source0, source1]).join('\n\\page\n').trimReturns(); //Requires one full render of document before hoisting is picked up + const rendered = renderAllPages([source0, source1]).join('\n\\page\n').trimReturns(); + expect(rendered, `Input:\n${[source0, source1].join('\n\\page\n')}`, { showPrefix: false }).toBe('

100

\\page

101

'); + }); + + it('Page numbering across pages : custom page number (NaN)', function() { + const source0 = `[HB_pageNumber]:a\n\n$[HB_pageNumber]\n\n`; + const source1 = `$[HB_pageNumber]\n\n`; + renderAllPages([source0, source1]).join('\n\\page\n').trimReturns(); //Requires one full render of document before hoisting is picked up + const rendered = renderAllPages([source0, source1]).join('\n\\page\n').trimReturns(); + expect(rendered, `Input:\n${[source0, source1].join('\n\\page\n')}`, { showPrefix: false }).toBe('

a

\\page

a

'); + }); }); describe('Math function parameter handling', ()=>{ @@ -410,4 +434,102 @@ describe('Regression Tests', ()=>{ const rendered = Markdown.render(source).trimReturns(); expect(rendered).toBe('
title 1title 2title 3title 4
fooIpsum))
'); }); + + it('Handle Extra spaces in image alt-text 1', function(){ + const source='![ where is my image??](http://i.imgur.com/hMna6G0.png)'; + const rendered = Markdown.render(source).trimReturns(); + expect(rendered).toBe('

\"where

'); + }); + + it('Handle Extra spaces in image alt-text 2', function(){ + const source='![where is my image??](http://i.imgur.com/hMna6G0.png)'; + const rendered = Markdown.render(source).trimReturns(); + expect(rendered).toBe('

\"where

'); + }); + + it('Handle Extra spaces in image alt-text 3', function(){ + const source='![where is my image?? ](http://i.imgur.com/hMna6G0.png)'; + const rendered = Markdown.render(source).trimReturns(); + expect(rendered).toBe('

\"where

'); + }); + + it('Handle Extra spaces in image alt-text 4', function(){ + const source='![where is my image??](http://i.imgur.com/hMna6G0.png){height=20%,width=20%}'; + const rendered = Markdown.render(source).trimReturns(); + expect(rendered).toBe('

\"where

'); + }); +}); + +describe('Custom Math Function Tests', ()=>{ + it('Sign Test', function() { + const source = `[a]: 13\n\n[b]: -11\n\nPositive: $[sign(a)]\n\nNegative: $[sign(b)]`; + const rendered = Markdown.render(source).trimReturns(); + expect(rendered).toBe('

Positive: +

Negative: -

'); + }); + + it('Signed Test', function() { + const source = `[a]: 13\n\n[b]: -11\n\nPositive: $[signed(a)]\n\nNegative: $[signed(b)]`; + const rendered = Markdown.render(source).trimReturns(); + expect(rendered).toBe('

Positive: +13

Negative: -11

'); + }); + + it('Roman Numerals Test', function() { + const source = `[a]: 18\n\nRoman Numeral: $[toRomans(a)]`; + const rendered = Markdown.render(source).trimReturns(); + expect(rendered).toBe('

Roman Numeral: XVIII

'); + }); + + it('Roman Numerals Test - Uppercase', function() { + const source = `[a]: 18\n\nRoman Numeral: $[toRomansUpper(a)]`; + const rendered = Markdown.render(source).trimReturns(); + expect(rendered).toBe('

Roman Numeral: XVIII

'); + }); + + it('Roman Numerals Test - Lowercase', function() { + const source = `[a]: 18\n\nRoman Numeral: $[toRomansLower(a)]`; + const rendered = Markdown.render(source).trimReturns(); + expect(rendered).toBe('

Roman Numeral: xviii

'); + }); + + it('Number to Characters Test', function() { + const source = `[a]: 18\n\n[b]: 39\n\nCharacters: $[toChar(a)] $[toChar(b)]`; + const rendered = Markdown.render(source).trimReturns(); + expect(rendered).toBe('

Characters: R AM

'); + }); + + it('Number to Characters Test - Uppercase', function() { + const source = `[a]: 18\n\n[b]: 39\n\nCharacters: $[toCharUpper(a)] $[toCharUpper(b)]`; + const rendered = Markdown.render(source).trimReturns(); + expect(rendered).toBe('

Characters: R AM

'); + }); + + it('Number to Characters Test - Lowercase', function() { + const source = `[a]: 18\n\n[b]: 39\n\nCharacters: $[toCharLower(a)] $[toCharLower(b)]`; + const rendered = Markdown.render(source).trimReturns(); + expect(rendered).toBe('

Characters: r am

'); + }); + + it('Number to Words Test', function() { + const source = `[a]: 80085\n\nWords: $[toWords(a)]`; + const rendered = Markdown.render(source).trimReturns(); + expect(rendered).toBe('

Words: eighty thousand and eighty-five

'); + }); + + it('Number to Words Test - Uppercase', function() { + const source = `[a]: 80085\n\nWords: $[toWordsUpper(a)]`; + const rendered = Markdown.render(source).trimReturns(); + expect(rendered).toBe('

Words: EIGHTY THOUSAND AND EIGHTY-FIVE

'); + }); + + it('Number to Words Test - Lowercase', function() { + const source = `[a]: 80085\n\nWords: $[toWordsLower(a)]`; + const rendered = Markdown.render(source).trimReturns(); + expect(rendered).toBe('

Words: eighty thousand and eighty-five

'); + }); + + it('Number to Words Test - Capitalized', function() { + const source = `[a]: 80085\n\nWords: $[toWordsCaps(a)]`; + const rendered = Markdown.render(source).trimReturns(); + expect(rendered).toBe('

Words: Eighty Thousand And Eighty-Five

'); + }); }); \ No newline at end of file diff --git a/themes/Legacy/5ePHB/style.less b/themes/Legacy/5ePHB/style.less index 56d1bed94..41524c74c 100644 --- a/themes/Legacy/5ePHB/style.less +++ b/themes/Legacy/5ePHB/style.less @@ -13,13 +13,13 @@ body { counter-reset : phb-page-numbers; } * { -webkit-print-color-adjust : exact; } .useSansSerif() { - font-family : "ScalySans"; + font-family : 'ScalySans'; em { - font-family : "ScalySans"; + font-family : 'ScalySans'; font-style : italic; } strong { - font-family : "ScalySans"; + font-family : 'ScalySans'; font-weight : 800; letter-spacing : -0.02em; } @@ -46,7 +46,7 @@ body { counter-reset : phb-page-numbers; } padding : 1.0cm 1.7cm; padding-bottom : 1.5cm; overflow : hidden; - font-family : "BookSanity"; + font-family : 'BookSanity'; font-size : 0.317cm; counter-increment : phb-page-numbers; background-color : @background; @@ -106,7 +106,7 @@ body { counter-reset : phb-page-numbers; } h1,h2,h3,h4 { margin-top : 0.2em; margin-bottom : 0.2em; - font-family : "MrJeeves"; + font-family : 'MrJeeves'; font-weight : 800; color : @headerText; } @@ -117,7 +117,7 @@ body { counter-reset : phb-page-numbers; } -moz-column-span : all; & + p::first-letter { float : left; - font-family : "Solberry"; + font-family : 'Solberry'; font-size : 10em; line-height : 0.795em; color : #222222; @@ -134,7 +134,7 @@ body { counter-reset : phb-page-numbers; } } h5 { margin-bottom : 0.2em; - font-family : "ScalySansSmallCaps"; + font-family : 'ScalySansSmallCaps'; font-size : 0.423cm; font-weight : 900; } @@ -200,7 +200,7 @@ body { counter-reset : phb-page-numbers; } & + p { padding-bottom : 0px; } } h3 { - font-family : "ScalySans"; + font-family : 'ScalySans'; font-weight : normal; border-bottom : 1px solid @headerText; } @@ -380,7 +380,7 @@ body { counter-reset : phb-page-numbers; } // ************************************/ .phb .descriptive { margin-bottom : 1em; - font-family : "ScalySans"; + font-family : 'ScalySans'; background-color : #FAF7EA; border-style : solid; border-width : 7px; @@ -394,11 +394,11 @@ body { counter-reset : phb-page-numbers; } } p + p { padding-top : 0.8em; } em { - font-family : "ScalySans"; + font-family : 'ScalySans'; font-style : italic; } strong { - font-family : "ScalySans"; + font-family : 'ScalySans'; font-weight : 800; letter-spacing : -0.02em; } @@ -411,7 +411,7 @@ body { counter-reset : phb-page-numbers; } .phb { .artist { position : absolute; - font-family : "WalterTurncoat"; + font-family : 'WalterTurncoat'; font-size : 0.27cm; color : @captionText; text-align : center; @@ -421,7 +421,7 @@ body { counter-reset : phb-page-numbers; } text-indent : unset; } h5 { - font-family : "WalterTurncoat"; + font-family : 'WalterTurncoat'; font-size : 1.3em; } a { diff --git a/themes/V3/5eDMG/style.less b/themes/V3/5eDMG/style.less index cbc3fa890..d79533c2c 100644 --- a/themes/V3/5eDMG/style.less +++ b/themes/V3/5eDMG/style.less @@ -7,7 +7,7 @@ } .page { - background-image : url("/assets/DMG_background.png"); + background-image : url('/assets/DMG_background.png'); background-size : cover; /* TABLES WITHIN NOTES */ @@ -23,7 +23,7 @@ &::after { height : 58px; - background-image : url("/assets/DMG_footerAccent.png"); + background-image : url('/assets/DMG_footerAccent.png'); } .footnote { bottom : 40px; } diff --git a/themes/V3/5ePHB/snippets.js b/themes/V3/5ePHB/snippets.js index dbcdc6f2a..27ea62bea 100644 --- a/themes/V3/5ePHB/snippets.js +++ b/themes/V3/5ePHB/snippets.js @@ -6,164 +6,12 @@ const MonsterBlockGen = require('./snippets/monsterblock.gen.js'); const scriptGen = require('./snippets/script.gen.js'); const ClassFeatureGen = require('./snippets/classfeature.gen.js'); const CoverPageGen = require('./snippets/coverpage.gen.js'); -const TableOfContentsGen = require('./snippets/tableOfContents.gen.js'); -const indexGen = require('./snippets/index.gen.js'); const QuoteGen = require('./snippets/quote.gen.js'); const dedent = require('dedent-tabs').default; module.exports = [ - - { - groupName : 'Text Editor', - icon : 'fas fa-pencil-alt', - view : 'text', - snippets : [ - { - name : 'Table of Contents', - icon : 'fas fa-book', - gen : TableOfContentsGen, - experimental : true, - subsnippets : [ - { - name : 'Generate Table of Contents', - icon : 'fas fa-book', - gen : TableOfContentsGen, - experimental : true - }, - { - name : 'Table of Contents Individual Inclusion', - icon : 'fas fa-book', - gen : dedent `\n{{tocInclude# CHANGE # to your header level - }}\n`, - subsnippets : [ - { - name : 'Individual Inclusion H1', - icon : 'fas fa-book', - gen : dedent `\n{{tocIncludeH1 \n - }}\n`, - }, - { - name : 'Individual Inclusion H2', - icon : 'fas fa-book', - gen : dedent `\n{{tocIncludeH2 \n - }}\n`, - }, - { - name : 'Individual Inclusion H3', - icon : 'fas fa-book', - gen : dedent `\n{{tocIncludeH3 \n - }}\n`, - }, - { - name : 'Individual Inclusion H4', - icon : 'fas fa-book', - gen : dedent `\n{{tocIncludeH4 \n - }}\n`, - }, - { - name : 'Individual Inclusion H5', - icon : 'fas fa-book', - gen : dedent `\n{{tocIncludeH5 \n - }}\n`, - }, - { - name : 'Individual Inclusion H6', - icon : 'fas fa-book', - gen : dedent `\n{{tocIncludeH6 \n - }}\n`, - } - ] - }, - { - name : 'Table of Contents Range Inclusion', - icon : 'fas fa-book', - gen : dedent `\n{{tocDepthH3 - }}\n`, - subsnippets : [ - { - name : 'Include in ToC up to H3', - icon : 'fas fa-dice-three', - gen : dedent `\n{{tocDepthH3 - }}\n`, - - }, - { - name : 'Include in ToC up to H4', - icon : 'fas fa-dice-four', - gen : dedent `\n{{tocDepthH4 - }}\n`, - }, - { - name : 'Include in ToC up to H5', - icon : 'fas fa-dice-five', - gen : dedent `\n{{tocDepthH5 - }}\n`, - }, - { - name : 'Include in ToC up to H6', - icon : 'fas fa-dice-six', - gen : dedent `\n{{tocDepthH6 - }}\n`, - }, - ] - }, - { - name : 'Table of Contents Individual Exclusion', - icon : 'fas fa-book', - gen : dedent `\n{{tocExcludeH1 \n - }}\n`, - subsnippets : [ - { - name : 'Individual Exclusion H1', - icon : 'fas fa-book', - gen : dedent `\n{{tocExcludeH1 \n - }}\n`, - }, - { - name : 'Individual Exclusion H2', - icon : 'fas fa-book', - gen : dedent `\n{{tocExcludeH2 \n - }}\n`, - }, - { - name : 'Individual Exclusion H3', - icon : 'fas fa-book', - gen : dedent `\n{{tocExcludeH3 \n - }}\n`, - }, - { - name : 'Individual Exclusion H4', - icon : 'fas fa-book', - gen : dedent `\n{{tocExcludeH4 \n - }}\n`, - }, - { - name : 'Individual Exclusion H5', - icon : 'fas fa-book', - gen : dedent `\n{{tocExcludeH5 \n - }}\n`, - }, - { - name : 'Individual Exclusion H6', - icon : 'fas fa-book', - gen : dedent `\n{{tocExcludeH6 \n - }}\n`, - }, - ] - }, - - ] - }, - { - name : 'Index', - icon : 'fas fa-bars', - gen : indexGen, - experimental : true - } - ] - }, { groupName : 'Style Editor', icon : 'fas fa-pencil-alt', @@ -192,70 +40,9 @@ module.exports = [ line-height: 1em; }\n\n` }, - { - name : 'Table of Contents Toggles', - icon : 'fas fa-book', - subsnippets : [ - { - name : 'Enable H1-H4 all pages', - icon : 'fas fa-dice-four', - gen : `.page {\n\th4 {--TOC: include; }\n}\n\n`, - }, - { - name : 'Enable H1-H5 all pages', - icon : 'fas fa-dice-five', - gen : `.page {\n\th4, h5 {--TOC: include; }\n}\n\n`, - }, - { - name : 'Enable H1-H6 all pages', - icon : 'fas fa-dice-six', - gen : `.page {\n\th4, h5, h6 {--TOC: include; }\n}\n\n`, - }, - ] - } ] }, - - /*********************** IMAGES *******************/ - { - groupName : 'Images', - icon : 'fas fa-images', - view : 'text', - snippets : [ - { - name : 'Image', - icon : 'fas fa-image', - gen : dedent` - ![cat warrior](https://s-media-cache-ak0.pinimg.com/736x/4a/81/79/4a8179462cfdf39054a418efd4cb743e.jpg) {width:325px,mix-blend-mode:multiply} - - {{artist,position:relative,top:-230px,left:10px,margin-bottom:-30px - ##### Cat Warrior - [Kyoung Hwan Kim](https://www.artstation.com/tahra) - }}` - }, - { - name : 'Background Image', - icon : 'fas fa-tree', - gen : dedent` - ![homebrew mug](http://i.imgur.com/hMna6G0.png) {position:absolute,top:50px,right:30px,width:280px} - - {{artist,top:80px,right:30px - ##### Homebrew Mug - [naturalcrit](https://homebrew.naturalcrit.com) - }}` - }, - { - name : 'Watermark', - icon : 'fas fa-id-card', - gen : dedent` - {{watermark Homebrewery}}\n` - }, - ] - }, - - /************************* PHB ********************/ - { groupName : 'PHB', icon : 'fas fa-book', @@ -450,9 +237,6 @@ module.exports = [ ] }, - - - /**************** PAGE *************/ { diff --git a/themes/V3/5ePHB/style.less b/themes/V3/5ePHB/style.less index 93cfdf719..555866ba4 100644 --- a/themes/V3/5ePHB/style.less +++ b/themes/V3/5ePHB/style.less @@ -791,47 +791,8 @@ // * TABLE OF CONTENTS // *****************************/ -// Default Exclusions -// Anything not excluded is included, default Headers are H1, H2, and H3. -h4, -h5, -h6, -.page:has(.frontCover), -.page:has(.backCover), -.page:has(.insideCover), -.monster, -.noToC, -.toc { --TOC : exclude; } - - -// Brew level default inclusion changes. -// These add Headers 'back' to inclusion. - -//NOTE: DO NOT USE :HAS WITH .PAGES!!! EXTREMELY SLOW TO RENDER ON LARGE DOCS! - -// Block level inclusion changes -// These include either a single (include) or a range (depth) -.tocIncludeH1 h1 {--TOC : include; } -.tocIncludeH2 h2 {--TOC : include; } -.tocIncludeH3 h3 {--TOC : include; } -.tocIncludeH4 h4 {--TOC : include; } -.tocIncludeH5 h5 {--TOC : include; } -.tocIncludeH6 h6 {--TOC : include; } - -.tocDepthH2 :is(h1, h2) {--TOC : include; } -.tocDepthH3 :is(h1, h2, h3) {--TOC : include; } -.tocDepthH4 :is(h1, h2, h3, h4) {--TOC : include; } -.tocDepthH5 :is(h1, h2, h3, h4, h5) {--TOC : include; } -.tocDepthH6 :is(h1, h2, h3, h4, h5, h6) {--TOC : include; } - -// Block level exclusion changes -// These exclude a single block level -.tocExcludeH1 h1 {--TOC : exclude; } -.tocExcludeH2 h2 {--TOC : exclude; } -.tocExcludeH3 h3 {--TOC : exclude; } -.tocExcludeH4 h4 {--TOC : exclude; } -.tocExcludeH5 h5 {--TOC : exclude; } -.tocExcludeH6 h6 {--TOC : exclude; } +// Additional Default Exclusions +.monster { --TOC : exclude; } .page:has(.partCover) { --TOC : exclude; @@ -964,6 +925,7 @@ h6, } } } + // ***************************** // * INDEX // *****************************/ diff --git a/themes/V3/Blank/snippets.js b/themes/V3/Blank/snippets.js index 380daa37a..c5198fd87 100644 --- a/themes/V3/Blank/snippets.js +++ b/themes/V3/Blank/snippets.js @@ -4,6 +4,8 @@ const WatercolorGen = require('./snippets/watercolor.gen.js'); const ImageMaskGen = require('./snippets/imageMask.gen.js'); const FooterGen = require('./snippets/footer.gen.js'); const dedent = require('dedent-tabs').default; +const TableOfContentsGen = require('./snippets/tableOfContents.gen.js'); +const indexGen = require('./snippets/index.gen.js'); module.exports = [ @@ -36,6 +38,11 @@ module.exports = [ icon : 'fas fa-sort-numeric-down', gen : '{{pageNumber,auto}}\n' }, + { + name : 'Variable Auto Page Number', + icon : 'fas fa-sort-numeric-down', + gen : '{{pageNumber $[HB_pageNumber]}}\n' + }, { name : 'Skip Page Number Increment this Page', icon : 'fas fa-xmark', @@ -141,7 +148,53 @@ module.exports = [ [Homebrewery.Naturalcrit.com](https://homebrewery.naturalcrit.com) }}\n\n`; }, - } + }, + { + name : 'Table of Contents', + icon : 'fas fa-book', + gen : TableOfContentsGen, + experimental : true, + subsnippets : [ + { + name : 'Table of Contents', + icon : 'fas fa-book', + gen : TableOfContentsGen, + experimental : true + }, + { + name : 'Include in ToC up to H3', + icon : 'fas fa-dice-three', + gen : dedent `\n{{tocDepthH3 + }}\n`, + + }, + { + name : 'Include in ToC up to H4', + icon : 'fas fa-dice-four', + gen : dedent `\n{{tocDepthH4 + }}\n`, + }, + { + name : 'Include in ToC up to H5', + icon : 'fas fa-dice-five', + gen : dedent `\n{{tocDepthH5 + }}\n`, + }, + { + name : 'Include in ToC up to H6', + icon : 'fas fa-dice-six', + gen : dedent `\n{{tocDepthH6 + }}\n`, + } + ] + }, + { + name : 'Index', + icon : 'fas fa-bars', + gen : indexGen, + experimental : true + }, + ] }, { @@ -153,7 +206,7 @@ module.exports = [ name : 'Add Comment', icon : 'fas fa-code', gen : '/* This is a comment that will not be rendered into your brew. */' - }, + } ] }, diff --git a/themes/V3/5ePHB/snippets/index.gen.js b/themes/V3/Blank/snippets/index.gen.js similarity index 100% rename from themes/V3/5ePHB/snippets/index.gen.js rename to themes/V3/Blank/snippets/index.gen.js diff --git a/themes/V3/5ePHB/snippets/tableOfContents.gen.js b/themes/V3/Blank/snippets/tableOfContents.gen.js similarity index 100% rename from themes/V3/5ePHB/snippets/tableOfContents.gen.js rename to themes/V3/Blank/snippets/tableOfContents.gen.js diff --git a/themes/V3/Blank/style.less b/themes/V3/Blank/style.less index 9f2bd498e..01b85326f 100644 --- a/themes/V3/Blank/style.less +++ b/themes/V3/Blank/style.less @@ -5,6 +5,7 @@ @import (less) './themes/fonts/iconFonts/diceFont.less'; @import (less) './themes/fonts/iconFonts/gameIcons.less'; @import (less) './themes/fonts/iconFonts/fontAwesome.less'; +@import (less) './themes/fonts/Journal/fonts.less'; :root { //Colors @@ -496,3 +497,117 @@ body { counter-reset : page-numbers 0; } &:not(:has(.skipCounting)) { counter-increment : page-numbers; } } + +// ***************************** +// * INDEX +// *****************************/ +.page { + .index { + + ul ul { margin : 0; } + + ul { + padding-left : 0; + text-indent : 0; + list-style-type : none; + } + + & > ul > li { + padding-left : 1.5em; + text-indent : -1.5em; + } + } +} + +// ***************************** +// * TABLE OF CONTENTS +// *****************************/ + +// Default Exclusions +// Anything not exlcuded is included, default Headers are H1, H2, and H3. +h4, +h5, +h6, +.page:has(.frontCover), +.page:has(.backCover), +.page:has(.insideCover), +.noToC, +.toc { --TOC : exclude; } + +.tocDepthH2 :is(h1, h2) {--TOC : include; } +.tocDepthH3 :is(h1, h2, h3) {--TOC : include; } +.tocDepthH4 :is(h1, h2, h3, h4) {--TOC : include; } +.tocDepthH5 :is(h1, h2, h3, h4, h5) {--TOC : include; } +.tocDepthH6 :is(h1, h2, h3, h4, h5, h6) {--TOC : include; } + +.tocIncludeH1 h1 {--TOC : include; } +.tocIncludeH2 h2 {--TOC : include; } +.tocIncludeH3 h3 {--TOC : include; } +.tocIncludeH4 h4 {--TOC : include; } +.tocIncludeH5 h5 {--TOC : include; } +.tocIncludeH6 h6 {--TOC : include; } + +.page { + &:has(.toc)::after { display : none; } + .toc { + -webkit-column-break-inside : avoid; + page-break-inside : avoid; + break-inside : avoid; + h1 { + margin-bottom : 0.3cm; + text-align : center; + } + a { + display : inline; + color : inherit; + text-decoration : none; + &:hover { text-decoration : underline; } + } + h4 { + margin-top : 0.2cm; + line-height : 0.4cm; + & + ul li { line-height : 1.2em; } + } + ul { + padding-left : 0; + margin-top : 0; + list-style-type : none; + a { + display : flex; + flex-flow : row nowrap; + justify-content : space-between; + width : 100%; + } + li + li h3 { + margin-top : 0.26cm; + line-height : 1em; + } + h3 span:first-child::after { border : none; } + span { + display : contents; + &:first-child::after { + bottom : 0.08cm; + flex : 1; + margin-right : 0.16cm; + margin-bottom : 0.08cm; + margin-left : 0.08cm; /* Spacing before dot leaders */ + content : ''; + border-bottom : 0.05cm dotted #000000; + } + &:last-child { + display : inline-block; + align-self : flex-end; + font-size : 0.34cm; + font-weight : normal; + } + } + ul { /* List indent */ + margin-left : 1em; + } + } + &.wide { + .useColumns(0.96, @fillMode: balance); + } + } + .toc.wide li { break-inside : auto; } +} diff --git a/themes/V3/Journal/style.less b/themes/V3/Journal/style.less index bddefb749..74c976f47 100644 --- a/themes/V3/Journal/style.less +++ b/themes/V3/Journal/style.less @@ -12,7 +12,7 @@ } .useSansSerif() { - font-family : "PermanentMarker"; + font-family : 'PermanentMarker'; font-size : 0.3cm; line-height : 1.2em; color : var(--HB_Color_Text2); @@ -29,7 +29,7 @@ .page { padding : 2.1cm 1.9cm 1.7cm 3.8cm; - background-image : url("/assets/Journal/Background1.webp"); + background-image : url('/assets/Journal/Background1.webp'); background-repeat : no-repeat; background-size : 200% 100%; filter : drop-shadow(1px 4px 14px black); @@ -39,7 +39,7 @@ background-position : right; } &:nth-of-type(2) { - background-image : url("/assets/Journal/Background2.webp"); //Only first page should show ribbon + background-image : url('/assets/Journal/Background2.webp'); //Only first page should show ribbon } & .columnWrapper { @@ -51,7 +51,7 @@ // * BASE // *****************************/ .page { - font-family : "ReenieBeanie"; + font-family : 'ReenieBeanie'; font-size : 0.53cm; line-height : 0.8em; color : var(--HB_Color_Text); @@ -71,7 +71,7 @@ // * HEADERS // *****************************/ h1,h2,h3,h4,h5 { - font-family : "FrederickaTheGreat"; + font-family : 'FrederickaTheGreat'; font-weight : unset; color : var(--HB_Color_HeaderText); } @@ -89,7 +89,7 @@ margin-right : 0.1em; margin-bottom : -20px; margin-left : -40px; - font-family : "FrederickaTheGreat"; + font-family : 'FrederickaTheGreat'; font-size : 1.9em; line-height : 1em; } @@ -114,7 +114,7 @@ &:nth-of-type(3n) { transform : rotate(-1.5deg); } } h5 { - font-family : "PermanentMarker"; + font-family : 'PermanentMarker'; font-size : 0.4cm; font-weight : bold; line-height : 0.951em; //Font is misaligned. Shift up slightly @@ -146,14 +146,14 @@ .note { .useSansSerif(); padding : 0.2cm; - background-image : url("/assets/Journal/HashMarks.png"), + background-image : url('/assets/Journal/HashMarks.png'), linear-gradient(to bottom right, #FF000000, #A36A4E14, #41212100); background-repeat : no-repeat; background-position : center; background-size : 120% 120%; border-style : solid; border-width : 1px; - border-image-source : url("/assets/Journal/Border1.png"); + border-image-source : url('/assets/Journal/Border1.png'); border-image-slice : 18 18 18 18; border-image-width : 6px 6px 6px 6px; border-image-outset : 5px 5px 5px 5px; @@ -173,7 +173,7 @@ .descriptive { .useSansSerif(); padding : 0.2cm; - background-image : url("/assets/Journal/HashMarks.png"), + background-image : url('/assets/Journal/HashMarks.png'), linear-gradient(to bottom right, #FF000000, #41212114, #41212100); background-repeat : no-repeat; background-position : center; @@ -201,7 +201,7 @@ .artist { position : absolute; width : auto; - font-family : "WalterTurncoat"; + font-family : 'WalterTurncoat'; font-size : 0.27cm; color : var(--HB_Color_CaptionText); text-align : center; @@ -211,7 +211,7 @@ text-indent : unset; } h5 { - font-family : "WalterTurncoat"; + font-family : 'WalterTurncoat'; font-size : 1.3em; } a { @@ -309,7 +309,7 @@ .pageNumber { right : 3cm; bottom : 1.25cm; - font-family : "FrederickaTheGreat"; + font-family : 'FrederickaTheGreat'; color : var(--HB_Color_HeaderText); } .footnote { @@ -370,7 +370,7 @@ .page .spellList { .useSansSerif(); - font-family : "PermanentMarker"; + font-family : 'PermanentMarker'; column-count : 2; ul + h5 { margin-top : 15px; } ul { @@ -439,7 +439,7 @@ &:last-child { width : 1%; padding-left : 0.06cm; /* Spacing after dot leaders */ - font-family : "ReenieBeanie"; + font-family : 'ReenieBeanie'; font-size : 0.34cm; font-weight : normal; vertical-align : bottom; /* Keep page number bottom-aligned */