From 03ff9c4b2723678bb469f86b8a380b333b3bbec8 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 11 Apr 2026 14:23:07 +0300 Subject: [PATCH] fix(sidebar): highlights not rendering math in read-only text --- .../widgets/sidebar/HighlightsList.spec.ts | 32 +++++++++++++++++++ .../src/widgets/sidebar/HighlightsList.tsx | 30 ++++++++++++++--- 2 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 apps/client/src/widgets/sidebar/HighlightsList.spec.ts diff --git a/apps/client/src/widgets/sidebar/HighlightsList.spec.ts b/apps/client/src/widgets/sidebar/HighlightsList.spec.ts new file mode 100644 index 0000000000..8cfa4ee0c5 --- /dev/null +++ b/apps/client/src/widgets/sidebar/HighlightsList.spec.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from "vitest"; +import { extractHighlightsFromStaticHtml } from "./HighlightsList.js"; + +describe("extractHighlightsFromStaticHtml", () => { + it("extracts highlighted text with math equations", () => { + const container = document.createElement("div"); + container.innerHTML = `

+ + Highlighted  + + \\(e=mc^2\\) + +  math + +

`; + document.body.appendChild(container); + + const highlights = extractHighlightsFromStaticHtml(container); + + // Should extract 3 highlights: "Highlighted ", the math element, and " math" + expect(highlights.length).toBe(3); + + // The math highlight should preserve the .math-tex wrapper + const mathHighlight = highlights.find(h => h.text.includes("math-tex")); + expect(mathHighlight).toBeDefined(); + expect(mathHighlight?.text).toContain('class="math-tex"'); + expect(mathHighlight?.text).toContain("e=mc^2"); + expect(mathHighlight?.attrs.background).toBeTruthy(); + + document.body.removeChild(container); + }); +}); diff --git a/apps/client/src/widgets/sidebar/HighlightsList.tsx b/apps/client/src/widgets/sidebar/HighlightsList.tsx index 1cd874a457..4720d0cd14 100644 --- a/apps/client/src/widgets/sidebar/HighlightsList.tsx +++ b/apps/client/src/widgets/sidebar/HighlightsList.tsx @@ -267,7 +267,7 @@ function ReadOnlyTextHighlightsList() { />; } -function extractHighlightsFromStaticHtml(el: HTMLElement | null) { +export function extractHighlightsFromStaticHtml(el: HTMLElement | null) { if (!el) return []; const { color: defaultColor, backgroundColor: defaultBackgroundColor } = getComputedStyle(el); @@ -279,6 +279,7 @@ function extractHighlightsFromStaticHtml(el: HTMLElement | null) { ); const highlights: DomHighlight[] = []; + const processedMathElements = new Set(); let node: Node | null; while ((node = walker.nextNode())) { @@ -287,23 +288,42 @@ function extractHighlightsFromStaticHtml(el: HTMLElement | null) { const style = getComputedStyle(el); + // For elements inside math-tex, get styles from the styled ancestor + const mathEl = el.closest(".math-tex"); + + // Skip if we've already processed this math element + if (mathEl && processedMathElements.has(mathEl)) continue; + + const styledEl = mathEl?.parentElement ?? el; + const styledElStyle = styledEl !== el ? getComputedStyle(styledEl) : style; + if ( el.closest('strong, em, u') || style.color !== defaultColor || - style.backgroundColor !== defaultBackgroundColor + style.backgroundColor !== defaultBackgroundColor || + styledElStyle.color !== defaultColor || + styledElStyle.backgroundColor !== defaultBackgroundColor ) { + const attrs: RawHighlight["attrs"] = { bold: !!el.closest("strong"), italic: !!el.closest("em"), underline: !!el.closest("u"), - background: el.style.backgroundColor, - color: el.style.color + background: styledEl.style.backgroundColor, + color: styledEl.style.color }; if (Object.values(attrs).some(Boolean)) { + const text = mathEl ? mathEl.outerHTML : node.textContent; + + // Track processed math elements to avoid duplicates + if (mathEl) { + processedMathElements.add(mathEl); + } + highlights.push({ id: randomString(), - text: node.textContent, + text, element: el, attrs });