From 5a9a9e116b3486283aea828d04eb0e470447877a Mon Sep 17 00:00:00 2001 From: Harsh Vador Date: Wed, 14 Oct 2026 18:24:47 +0530 Subject: [PATCH 1/2] fix(ui): render block-editor math with katex directly, drop react-latex-next react-latex-next 3.0.0 (its latest release) bundles its own copy of katex 0.16.10, so bumping the katex package never changed the code that renders equations: only the stylesheet moved, leaving it out of step with the renderer. Render through katex's own auto-render instead, with the same delimiters react-latex-next used, and move katex to ^0.18.2. auto-render keeps the text outside the delimiters as text nodes, and malformed math still falls back to its source text. --- .../src/main/resources/ui/package.json | 3 +- .../Features/BlockEditorMathEquations.spec.ts | 2 +- .../MathEquationComponent.test.tsx | 31 ++++++++++++---- .../MathEquation/MathEquationComponent.tsx | 35 +++++++++++++++++-- .../src/main/resources/ui/yarn.lock | 27 ++++++-------- 5 files changed, 69 insertions(+), 29 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/package.json b/openmetadata-ui/src/main/resources/ui/package.json index 4ecfe5f3a7ca..2367ca96c7c1 100644 --- a/openmetadata-ui/src/main/resources/ui/package.json +++ b/openmetadata-ui/src/main/resources/ui/package.json @@ -152,7 +152,7 @@ "input-otp": "^1.4.2", "js-yaml": "^5.4.1", "jwt-decode": "^3.1.2", - "katex": "^0.16.21", + "katex": "^0.18.2", "lodash": "4.18.1", "lru-cache": "^6.0.0", "luxon": "^3.2.1", @@ -178,7 +178,6 @@ "react-hook-form": "7.78.0", "react-i18next": "^15.5.3", "react-intersection-observer": "^9.16.0", - "react-latex-next": "^3.0.0", "react-markdown": "^10.1.0", "react-oidc": "^1.0.3", "react-papaparse": "^4.1.0", diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/BlockEditorMathEquations.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/BlockEditorMathEquations.spec.ts index c1b3d1bf3af6..410928b78d4a 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/BlockEditorMathEquations.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/BlockEditorMathEquations.spec.ts @@ -84,7 +84,7 @@ test.describe('BlockEditor math equations', { tag: ['@Discovery'] }, () => { await expect( page .getByTestId('asset-description-container') - .locator('.block-math-equation') + .locator('.block-math-equation .katex') ).toBeVisible(); }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/Extensions/MathEquation/MathEquationComponent.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/Extensions/MathEquation/MathEquationComponent.test.tsx index 9d8822a1499e..b63f2af618c7 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/Extensions/MathEquation/MathEquationComponent.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/Extensions/MathEquation/MathEquationComponent.test.tsx @@ -14,17 +14,13 @@ import { fireEvent, render, screen } from '@testing-library/react'; import { NodeViewProps } from '@tiptap/react'; import { MathEquationComponent } from './MathEquationComponent'; -jest.mock('react-latex-next', () => - jest.fn().mockImplementation(({ children }) => {children}) -); - const updateAttributes = jest.fn(); -const renderComponent = (isEditing: boolean) => +const renderComponent = (isEditing: boolean, equation = 'x^2') => render( { expect(screen.getByRole('textbox')).toHaveValue('x^2'); }); + + it('renders delimited math with KaTeX', () => { + const { container } = renderComponent(false, '$x^2$'); + + expect(container.querySelector('.katex')).toBeInTheDocument(); + }); + + it('renders text outside the delimiters as text, not HTML', () => { + const { container } = renderComponent( + false, + '$x^2$ ' + ); + + expect(container.querySelector('img')).not.toBeInTheDocument(); + expect(container).toHaveTextContent(''); + }); + + it('keeps malformed math as its source text', () => { + const { container } = renderComponent(false, '$\\frac{1}$'); + + expect(container.querySelector('.katex')).not.toBeInTheDocument(); + expect(container).toHaveTextContent('$\\frac{1}$'); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/Extensions/MathEquation/MathEquationComponent.tsx b/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/Extensions/MathEquation/MathEquationComponent.tsx index e40d26f60b27..399b573567da 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/Extensions/MathEquation/MathEquationComponent.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/Extensions/MathEquation/MathEquationComponent.tsx @@ -19,13 +19,42 @@ import { import { Check, XClose } from '@openmetadata/ui-core-components/icons'; import { NodeViewProps, NodeViewWrapper } from '@tiptap/react'; import classNames from 'classnames'; +import renderMathInElement from 'katex/contrib/auto-render'; import 'katex/dist/katex.min.css'; -import { FC, useRef, useState } from 'react'; +import { noop } from 'lodash'; +import { FC, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import Latex from 'react-latex-next'; import { ReactComponent as EditIcon } from '../../../../assets/svg/edit-new.svg'; import './math-equation.less'; +// The delimiters react-latex-next used, so stored `$…$` / `$$…$$` equations +// render as before. +const LATEX_DELIMITERS = [ + { left: '$$', right: '$$', display: true }, + { left: '\\(', right: '\\)', display: false }, + { left: '$', right: '$', display: false }, + { left: '\\[', right: '\\]', display: true }, +]; + +// auto-render keeps everything outside the delimiters as text nodes, so the +// stored attribute is never parsed as HTML. +const RenderedEquation: FC<{ equation: string }> = ({ equation }) => { + const ref = useRef(null); + + useEffect(() => { + if (ref.current) { + ref.current.textContent = equation; + renderMathInElement(ref.current, { + delimiters: LATEX_DELIMITERS, + // Malformed math stays as its source text, without logging per render. + errorCallback: noop, + }); + } + }, [equation]); + + return ; +}; + export const MathEquationComponent: FC = ({ node, updateAttributes, @@ -89,7 +118,7 @@ export const MathEquationComponent: FC = ({ ) : ( - {equation} + )} {/* Show edit button only when the editor is editable */} {!isEditing && editor.isEditable && ( diff --git a/openmetadata-ui/src/main/resources/ui/yarn.lock b/openmetadata-ui/src/main/resources/ui/yarn.lock index 29ae3e531614..916c835e3b18 100644 --- a/openmetadata-ui/src/main/resources/ui/yarn.lock +++ b/openmetadata-ui/src/main/resources/ui/yarn.lock @@ -5951,16 +5951,16 @@ commander@7: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +commander@^15.0.0: + version "15.0.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-15.0.0.tgz#96f3961f12adac1799ef3fbd8bc61d40572d1b11" + integrity sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg== + commander@^2.19.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^8.3.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" - integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== - commander@^9.0.0: version "9.5.0" resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" @@ -9270,12 +9270,12 @@ jwt-decode@^3.1.2: resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== -katex@^0.16.0, katex@^0.16.21: - version "0.16.45" - resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.45.tgz#ba60d39c54746b6b8d39ce0e7f6eace07143149c" - integrity sha512-pQpZbdBu7wCTmQUh7ufPmLr0pFoObnGUoL/yhtwJDgmmQpbkg/0HSVti25Fu4rmd1oCR6NGWe9vqTWuWv3GcNA== +katex@^0.18.2: + version "0.18.11" + resolved "https://registry.yarnpkg.com/katex/-/katex-0.18.11.tgz#ca4e9e6db78aeff485c3a1dbe46a08bf196defe9" + integrity sha512-yvyz/2VMKqoho/35Hat5w37wIEdWJ5ElVMidTAlWrxr5uW4aBGsEpveTmKh18/zw08v/7lGswCPon4OhqmqrTA== dependencies: - commander "^8.3.0" + commander "^15.0.0" keyv@^4.5.4: version "4.5.4" @@ -11757,13 +11757,6 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-latex-next@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/react-latex-next/-/react-latex-next-3.0.0.tgz#3e347a13b1e701439b5fa52f75201bc86166854e" - integrity sha512-x70f1b1G7TronVigsRgKHKYYVUNfZk/3bciFyYX1lYLQH2y3/TXku3+5Vap8MDbJhtopePSYBsYWS6jhzIdz+g== - dependencies: - katex "^0.16.0" - react-markdown@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-10.1.0.tgz#e22bc20faddbc07605c15284255653c0f3bad5ca" From 23f5a1550fee91a212dbff8761bd831e4668793b Mon Sep 17 00:00:00 2001 From: Harsh Vador Date: Thu, 15 Oct 2026 16:28:10 +0530 Subject: [PATCH 2/2] fix(ui): use String.raw for the LaTeX bracket delimiters Sonar S7780: write the \( \) \[ \] delimiters with String.raw instead of escaped backslashes. The values are unchanged. --- .../Extensions/MathEquation/MathEquationComponent.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/Extensions/MathEquation/MathEquationComponent.tsx b/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/Extensions/MathEquation/MathEquationComponent.tsx index 399b573567da..34e60eb7db4b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/Extensions/MathEquation/MathEquationComponent.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/BlockEditor/Extensions/MathEquation/MathEquationComponent.tsx @@ -31,9 +31,9 @@ import './math-equation.less'; // render as before. const LATEX_DELIMITERS = [ { left: '$$', right: '$$', display: true }, - { left: '\\(', right: '\\)', display: false }, + { left: String.raw`\(`, right: String.raw`\)`, display: false }, { left: '$', right: '$', display: false }, - { left: '\\[', right: '\\]', display: true }, + { left: String.raw`\[`, right: String.raw`\]`, display: true }, ]; // auto-render keeps everything outside the delimiters as text nodes, so the