diff --git a/package-lock.json b/package-lock.json index 2b90dabfad..a13b01bcf5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22595,6 +22595,12 @@ "resolved": "https://registry.npmjs.org/mathjax/-/mathjax-2.7.9.tgz", "integrity": "sha512-NOGEDTIM9+MrsqnjPEjVGNx4q0GQxqm61yQwSK+/5S59i26wId5IC5gNu9/bu8+CCVl5p9G2IHcAl/wJa+5+BQ==" }, + "node_modules/mathjax-3": { + "name": "mathjax", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/mathjax/-/mathjax-3.2.2.tgz", + "integrity": "sha512-Bt+SSVU8eBG27zChVewOicYs7Xsdt40qm4+UpHyX7k0/O9NliPc+x77k1/FEsPsjKPZGJvtRZM1vO+geW0OhGw==" + }, "node_modules/maxmind": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/maxmind/-/maxmind-4.3.6.tgz", @@ -35778,6 +35784,7 @@ "marked": "^4.1.0", "match-sorter": "^6.2.0", "mathjax": "^2.7.9", + "mathjax-3": "npm:mathjax@^3.2.2", "method-override": "^2.3.3", "minimist": "^1.2.7", "mmmagic": "^0.5.3", @@ -46297,6 +46304,7 @@ "marked": "^4.1.0", "match-sorter": "^6.2.0", "mathjax": "^2.7.9", + "mathjax-3": "npm:mathjax@^3.2.2", "mensch": "^0.3.4", "method-override": "^2.3.3", "mini-css-extract-plugin": "^2.6.0", @@ -60482,6 +60490,11 @@ "resolved": "https://registry.npmjs.org/mathjax/-/mathjax-2.7.9.tgz", "integrity": "sha512-NOGEDTIM9+MrsqnjPEjVGNx4q0GQxqm61yQwSK+/5S59i26wId5IC5gNu9/bu8+CCVl5p9G2IHcAl/wJa+5+BQ==" }, + "mathjax-3": { + "version": "npm:mathjax@3.2.2", + "resolved": "https://registry.npmjs.org/mathjax/-/mathjax-3.2.2.tgz", + "integrity": "sha512-Bt+SSVU8eBG27zChVewOicYs7Xsdt40qm4+UpHyX7k0/O9NliPc+x77k1/FEsPsjKPZGJvtRZM1vO+geW0OhGw==" + }, "maxmind": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/maxmind/-/maxmind-4.3.6.tgz", diff --git a/services/web/app/src/infrastructure/ExpressLocals.js b/services/web/app/src/infrastructure/ExpressLocals.js index e3aac6f390..ebc227a4f0 100644 --- a/services/web/app/src/infrastructure/ExpressLocals.js +++ b/services/web/app/src/infrastructure/ExpressLocals.js @@ -179,6 +179,12 @@ module.exports = function (webRouter, privateApiRouter, publicApiRouter) { } )}` + res.locals.mathJax3Path = `/js/libs/mathjax3/es5/tex-svg-full.js?${querystring.stringify( + { + v: require('mathjax-3/package.json').version, + } + )}` + res.locals.lib = PackageVersions.lib res.locals.moment = moment diff --git a/services/web/app/views/project/editor.pug b/services/web/app/views/project/editor.pug index 6e8013aebd..c6cafa1c33 100644 --- a/services/web/app/views/project/editor.pug +++ b/services/web/app/views/project/editor.pug @@ -120,6 +120,7 @@ block foot-scripts each file in (useOpenTelemetry ? entrypointScripts("tracing") : []) script(type="text/javascript", nonce=scriptNonce, src=file) script(type="text/javascript", nonce=scriptNonce, src=(wsUrl || '/socket.io') + '/socket.io.js') - script(type="text/javascript", nonce=scriptNonce, src=mathJaxPath) + if (richTextVariant !== 'cm6') + script(type="text/javascript", nonce=scriptNonce, src=mathJaxPath) each file in entrypointScripts("ide") script(type="text/javascript", nonce=scriptNonce, src=file) diff --git a/services/web/app/views/project/editor/meta.pug b/services/web/app/views/project/editor/meta.pug index b567c133ab..95cc1c43a4 100644 --- a/services/web/app/views/project/editor/meta.pug +++ b/services/web/app/views/project/editor/meta.pug @@ -37,6 +37,8 @@ meta(name="ol-useOpenTelemetry" data-type="boolean" content=useOpenTelemetry) meta(name="ol-showSupport", data-type="boolean" content=showSupport) meta(name="ol-showCM6SwitchAwaySurvey", data-type="boolean" content=showCM6SwitchAwaySurvey) meta(name="ol-richTextVariant" content=richTextVariant) +if (richTextVariant === 'cm6') + meta(name="ol-mathJax3Path" content=mathJax3Path) - var fileActionI18n = ['edited', 'renamed', 'created', 'deleted'].reduce((acc, i) => {acc[i] = translate('file_action_' + i); return acc}, {}) meta(name="ol-fileActionI18n" data-type="json" content=fileActionI18n) diff --git a/services/web/frontend/js/features/chat/components/message-content.js b/services/web/frontend/js/features/chat/components/message-content.js deleted file mode 100644 index 109dc52f11..0000000000 --- a/services/web/frontend/js/features/chat/components/message-content.js +++ /dev/null @@ -1,56 +0,0 @@ -import { useRef, useEffect } from 'react' -import PropTypes from 'prop-types' -import Linkify from 'react-linkify' - -function MessageContent({ content }) { - const root = useRef(null) - - useEffect(() => { - if (!(window.MathJax && window.MathJax.Hub)) { - return - } - const MJHub = window.MathJax.Hub - const inlineMathConfig = - (MJHub.config && - MJHub.config.tex2jax && - MJHub.config.tex2jax.inlineMath) || - [] - const alreadyConfigured = inlineMathConfig.some( - c => c[0] === '$' && c[1] === '$' - ) - if (!alreadyConfigured) { - MJHub.Config({ - tex2jax: { - inlineMath: inlineMathConfig.concat([['$', '$']]), - }, - }) - } - }, []) - - useEffect(() => { - // adds attributes to all the links generated by , required due to https://github.com/tasti/react-linkify/issues/99 - for (const a of root.current.getElementsByTagName('a')) { - a.setAttribute('target', '_blank') - a.setAttribute('rel', 'noreferrer noopener') - } - - // MathJax typesetting - const MJHub = window.MathJax.Hub - const timeoutHandler = setTimeout(() => { - MJHub.Queue(['Typeset', MJHub, root.current]) - }, 0) - return () => clearTimeout(timeoutHandler) - }, [content]) - - return ( -

- {content} -

- ) -} - -MessageContent.propTypes = { - content: PropTypes.string.isRequired, -} - -export default MessageContent diff --git a/services/web/frontend/js/features/chat/components/message-content.tsx b/services/web/frontend/js/features/chat/components/message-content.tsx new file mode 100644 index 0000000000..790b39b9e3 --- /dev/null +++ b/services/web/frontend/js/features/chat/components/message-content.tsx @@ -0,0 +1,57 @@ +import { useRef, useEffect, type FC } from 'react' +// @ts-ignore +import Linkify from 'react-linkify' +import { loadMathJax } from '../../../../../modules/source-editor/frontend/js/utils/mathjax' +import useIsMounted from '../../../shared/hooks/use-is-mounted' +import { configureMathJax } from '../../mathjax/configure' + +const MessageContent: FC<{ content: string }> = ({ content }) => { + const root = useRef(null) + const mounted = useIsMounted() + + useEffect(() => { + if (root.current) { + // adds attributes to all the links generated by , required due to https://github.com/tasti/react-linkify/issues/99 + for (const a of root.current.getElementsByTagName('a')) { + a.setAttribute('target', '_blank') + a.setAttribute('rel', 'noreferrer noopener') + } + + // MathJax v2 typesetting + if (window.MathJax?.Hub) { + const timeout = setTimeout(() => { + configureMathJax() + + window.MathJax.Hub.Queue([ + 'Typeset', + window.MathJax.Hub, + root.current, + ]) + }, 0) + + return () => clearTimeout(timeout) + } + + // MathJax v3 typesetting + loadMathJax() + .then(MathJax => { + if (mounted.current) { + MathJax.typesetPromise([root.current]).catch((error: Error) => { + console.error(error) + }) + } + }) + .catch(error => { + console.error(error) + }) + } + }, [content, mounted]) + + return ( +

+ {content} +

+ ) +} + +export default MessageContent diff --git a/services/web/frontend/js/features/mathjax/configure.js b/services/web/frontend/js/features/mathjax/configure.js index a1446cb1d2..1cd476d294 100644 --- a/services/web/frontend/js/features/mathjax/configure.js +++ b/services/web/frontend/js/features/mathjax/configure.js @@ -7,6 +7,7 @@ let configured = false export function configureMathJax() { if (configured) return + if (getMeta('ol-mathJax3Path')) return if (!mathJaxLoaded()) return const inlineMath = [['\\(', '\\)']] diff --git a/services/web/package.json b/services/web/package.json index f34b07a8bc..6fd60bbac9 100644 --- a/services/web/package.json +++ b/services/web/package.json @@ -180,6 +180,7 @@ "marked": "^4.1.0", "match-sorter": "^6.2.0", "mathjax": "^2.7.9", + "mathjax-3": "npm:mathjax@^3.2.2", "method-override": "^2.3.3", "minimist": "^1.2.7", "mmmagic": "^0.5.3", diff --git a/services/web/types/window.ts b/services/web/types/window.ts index 3ca12be73f..9f02651ea4 100644 --- a/services/web/types/window.ts +++ b/services/web/types/window.ts @@ -29,9 +29,7 @@ declare global { isRestrictedTokenMember: boolean _reportCM6Perf: () => void _reportAcePerf: () => void - MathJax: { - Hub: Record - } + MathJax: Record overallThemes: OverallThemeMeta[] } } diff --git a/services/web/webpack.config.js b/services/web/webpack.config.js index 80425818ed..26913b5c43 100644 --- a/services/web/webpack.config.js +++ b/services/web/webpack.config.js @@ -58,6 +58,7 @@ function getModuleDirectory(moduleName) { } const mathjaxDir = getModuleDirectory('mathjax') +const mathjax3Dir = getModuleDirectory('mathjax-3') const aceDir = getModuleDirectory('ace-builds') const pdfjsVersions = ['pdfjs-dist213', 'pdfjs-dist31'] @@ -257,6 +258,22 @@ module.exports = { // Copy the required files for loading MathJax from MathJax NPM package new CopyPlugin({ patterns: [ + // https://www.npmjs.com/package/mathjax#user-content-hosting-your-own-copy-of-the-mathjax-components + { + from: 'es5/tex-svg-full.js', + to: 'js/libs/mathjax3/es5', + context: mathjax3Dir, + }, + { + from: 'es5/input/tex/extensions/**/*.js', + to: 'js/libs/mathjax3', + context: mathjax3Dir, + }, + { + from: 'es5/ui/**/*', + to: 'js/libs/mathjax3', + context: mathjax3Dir, + }, { from: 'MathJax.js', to: 'js/libs/mathjax', context: mathjaxDir }, { from: 'config/**/*', to: 'js/libs/mathjax', context: mathjaxDir }, {