UNPKG

analytica-frontend-lib

Version:

Repositório público dos componentes utilizados nas plataformas da Analytica Ensino

636 lines (608 loc) 21.5 kB
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var _chunkAKAHADV7js = require('./chunk-AKAHADV7.js'); var _chunk6N4P3WKFjs = require('./chunk-6N4P3WKF.js'); var _chunkTN3AYOMVjs = require('./chunk-TN3AYOMV.js'); // src/components/HtmlMathRenderer/HtmlMathRenderer.tsx var _react = require('react'); require('katex/dist/katex.min.css'); // src/components/MarkdownMathRenderer/MarkdownMathRenderer.tsx var _reactmarkdown = require('react-markdown'); var _reactmarkdown2 = _interopRequireDefault(_reactmarkdown); var _remarkgfm = require('remark-gfm'); var _remarkgfm2 = _interopRequireDefault(_remarkgfm); var _remarkmath = require('remark-math'); var _remarkmath2 = _interopRequireDefault(_remarkmath); var _rehypekatex = require('rehype-katex'); var _rehypekatex2 = _interopRequireDefault(_rehypekatex); // src/components/HtmlMathRenderer/utils.ts var generateSecureRandomId = () => { return crypto.randomUUID(); }; var cleanLatex = (str) => { return str.replaceAll(/[\u200B-\u200D\uFEFF]/g, "").replaceAll(/&amp;lt;|&lt;/gi, String.raw`\lt `).replaceAll(/&amp;gt;|&gt;/gi, String.raw`\gt `).replaceAll(/&amp;amp;|&amp;/gi, "&").trim(); }; var looksLikeLatex = (str) => { if (/[\\^_{}]/.test(str)) return true; const words = str.match(/[a-zA-Z]{3,}/g); if (words && words.length >= 2) return false; return true; }; var HTML_TAG_NAMES = /* @__PURE__ */ new Set([ "p", "div", "span", "br", "b", "strong", "i", "em", "u", "s", "ul", "ol", "li", "h1", "h2", "h3", "h4", "h5", "h6", "a", "img", "table", "thead", "tbody", "tfoot", "tr", "td", "th", "blockquote", "pre", "code", "sub", "sup", "font", "latex" ]); var containsHtmlTag = (content) => { for (const match of content.matchAll(/<\/?([a-z][a-z0-9]*)/gi)) { if (HTML_TAG_NAMES.has(match[1].toLowerCase())) return true; } return false; }; var isLikelyMarkdown = (content) => { if (!content) return false; if (containsHtmlTag(content)) return false; const MARKDOWN_SIGNALS = [ /^#{1,6}[ \t]+\S/m, // ATX heading (#, ##, ... ######) /\*\*[^*\n]+\*\*/, // bold /__[^_\n]+__/, // bold (underscore) /^[ \t]*[-*+][ \t]+\S/m, // bullet list /^[ \t]*\d+\.[ \t]+\S/m, // ordered list /\|[ \t]*:?-+:?[ \t]*\|/, // GFM table delimiter row (|---|---|) /\n\n/ // paragraph break (only reached when no HTML tag is present) ]; return MARKDOWN_SIGNALS.some((pattern) => pattern.test(content)); }; var recoverFromKatexErrorSpans = (htmlContent) => { if (typeof document === "undefined" || !/class="[^"]*katex-error/i.test(htmlContent)) { return htmlContent; } const tempContainer = document.createElement("div"); tempContainer.innerHTML = htmlContent; const sanitizeRecoveredLatex = (raw) => raw.replaceAll(/[\u0300-\u036F]/g, "").replaceAll(/<\/?[a-zA-Z][a-zA-Z0-9]*\s*>?$/g, "").replaceAll(/&amp;lt;|&lt;|</gi, String.raw`\lt `).replaceAll(/&amp;gt;|&gt;|>/gi, String.raw`\gt `).replaceAll(/&amp;amp;|&amp;/gi, "&").trim(); Array.from(tempContainer.querySelectorAll(".katex-error")).forEach( (errorNode) => { if (!tempContainer.contains(errorNode)) return; const title = errorNode.getAttribute("title") || ""; const positionMatch = /at position\s+\d+:(.+)$/.exec(title); let recovered = positionMatch ? positionMatch[1] : ""; if (!recovered) { const parts = []; const walk = (n) => { if (n.nodeType === Node.TEXT_NODE) { parts.push(n.textContent || ""); return; } if (n.nodeType !== Node.ELEMENT_NODE) return; const el = n; if (el.tagName.toLowerCase() === "annotation") { parts.push(el.textContent || ""); return; } if (el.classList.contains("katex-html")) return; Array.from(el.childNodes).forEach(walk); }; Array.from(errorNode.childNodes).forEach(walk); recovered = parts.join(" "); } recovered = sanitizeRecoveredLatex(recovered); if (recovered) { errorNode.replaceWith(document.createTextNode(`$${recovered}$`)); } else { errorNode.remove(); } } ); return tempContainer.innerHTML; }; var decodeDollarEscapes = (text) => text.replaceAll(String.raw`\$`, "$"); var DANGEROUS_ATTRIBUTES = /* @__PURE__ */ new Set([ "contenteditable", "srcdoc", "formaction", "xlink:href" ]); var DANGEROUS_URI_PATTERN = /^\s*(javascript|vbscript|data):/i; var sanitizeHtmlForDisplay = (htmlContent) => { if (!htmlContent) return htmlContent; if (typeof document === "undefined") { let sanitized = htmlContent; sanitized = sanitized.replaceAll( /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "" ); sanitized = sanitized.replaceAll( /<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, "" ); sanitized = sanitized.replaceAll(/ on[a-z]+="[^"]*"/gi, ""); sanitized = sanitized.replaceAll(/ on[a-z]+='[^']*'/gi, ""); sanitized = sanitized.replaceAll(/ on[a-z]+=[^\s>"']+/gi, ""); sanitized = sanitized.replaceAll( / href="(?:javascript|vbscript|data):[^"]*"/gi, "" ); sanitized = sanitized.replaceAll( / href='(?:javascript|vbscript|data):[^']*'/gi, "" ); sanitized = sanitized.replaceAll( / src="(?:javascript|vbscript|data):[^"]*"/gi, "" ); sanitized = sanitized.replaceAll( / src='(?:javascript|vbscript|data):[^']*'/gi, "" ); sanitized = sanitized.replaceAll( / action="(?:javascript|vbscript|data):[^"]*"/gi, "" ); sanitized = sanitized.replaceAll( / action='(?:javascript|vbscript|data):[^']*'/gi, "" ); return sanitized; } const tempDiv = document.createElement("div"); tempDiv.innerHTML = htmlContent; const dangerousTags = tempDiv.querySelectorAll( "script, style, iframe, object, embed" ); dangerousTags.forEach((element) => element.remove()); const allElements = tempDiv.querySelectorAll("*"); allElements.forEach((element) => { const attributeNames = element.getAttributeNames(); attributeNames.forEach((attrName) => { const lowerAttrName = attrName.toLowerCase(); if (lowerAttrName.startsWith("on")) { element.removeAttribute(attrName); return; } if (DANGEROUS_ATTRIBUTES.has(lowerAttrName)) { element.removeAttribute(attrName); return; } if (lowerAttrName === "href" || lowerAttrName === "src" || lowerAttrName === "action") { const value = element.getAttribute(attrName); if (value && DANGEROUS_URI_PATTERN.test(value)) { element.removeAttribute(attrName); } } }); }); return tempDiv.innerHTML; }; var processHtmlWithMath = (htmlContent) => { if (!htmlContent) return []; let processedContent = recoverFromKatexErrorSpans(htmlContent); const parts = []; const sentinel = `__MATH_${generateSecureRandomId()}_`; const mathFormulaPattern = /<span[^>]*class="math-formula"[^>]*data-latex="([^"]*)"[^>]*>[\s\S]*?<\/span>/g; processedContent = processedContent.replaceAll( mathFormulaPattern, (match2, latex) => { const isDisplayMode = match2.includes('data-display-mode="true"'); const placeholder = `${sentinel}${parts.length}__`; parts.push({ type: isDisplayMode ? "block-math" : "math", content: match2, latex: cleanLatex(latex) }); return placeholder; } ); const wrappedMathPattern = /<span[^>]*class="math-expression"[^>]*data-math="([^"]*)"[^>]*>.*?<\/span>/g; processedContent = processedContent.replaceAll( wrappedMathPattern, (match2, latex) => { const placeholder = `${sentinel}${parts.length}__`; parts.push({ type: "math", content: match2, latex: cleanLatex(latex) }); return placeholder; } ); const doubleDollarPattern = /(?<!\\)\$\$([\s\S]+?)\$\$/g; processedContent = processedContent.replaceAll( doubleDollarPattern, (match2, latex) => { const placeholder = `${sentinel}${parts.length}__`; parts.push({ type: "block-math", content: match2, latex: cleanLatex(latex) }); return placeholder; } ); const singleDollarPattern = /(?<!\\)\$([\s\S]+?)\$/g; processedContent = processedContent.replaceAll( singleDollarPattern, (match2, latex) => { if (!looksLikeLatex(latex)) return match2; const placeholder = `${sentinel}${parts.length}__`; parts.push({ type: "math", content: match2, latex: cleanLatex(latex) }); return placeholder; } ); const latexTagPattern = /(?:<latex>|&lt;latex&gt;)([\s\S]*?)(?:<\/latex>|&lt;\/latex&gt;)/g; processedContent = processedContent.replaceAll( latexTagPattern, (match2, latex) => { const placeholder = `${sentinel}${parts.length}__`; parts.push({ type: "math", content: match2, latex: cleanLatex(latex) }); return placeholder; } ); const latexEnvPattern = /\\begin\{([^}]+)\}([\s\S]*?)\\end\{\1\}/g; processedContent = processedContent.replaceAll(latexEnvPattern, (match2) => { const placeholder = `${sentinel}${parts.length}__`; parts.push({ type: "block-math", content: match2, latex: cleanLatex(match2) }); return placeholder; }); const finalParts = []; let currentIndex = 0; const escapedSentinel = sentinel.replaceAll( /[.*+?^${}()|[\]\\]/g, String.raw`\$&` ); const placeholderPattern = new RegExp( String.raw`${escapedSentinel}(\d+)__`, "g" ); let match; while ((match = placeholderPattern.exec(processedContent)) !== null) { if (match.index > currentIndex) { finalParts.push({ type: "text", content: decodeDollarEscapes( processedContent.slice(currentIndex, match.index) ) }); } const mathIndex = Number.parseInt(match[1], 10); if (parts[mathIndex]) { finalParts.push(parts[mathIndex]); } currentIndex = match.index + match[0].length; } if (currentIndex < processedContent.length) { finalParts.push({ type: "text", content: decodeDollarEscapes(processedContent.slice(currentIndex)) }); } return finalParts; }; var containsMath = (content) => { if (!content) return false; const patterns = [ /\$\$[\s\S]+?\$\$/, // Display mode $$...$$ /(?<!\\)\$[\s\S]+?\$/, // Inline mode $...$ /<span[^>]*class="math-formula"/, // Editor spans /<span[^>]*class="math-expression"/, // Legacy spans /<latex>|&lt;latex&gt;/, // LaTeX tags /\\begin\{[^}]+\}/ // LaTeX environments ]; return patterns.some((pattern) => pattern.test(content)); }; var stripHtml = (htmlContent) => { if (!htmlContent) return ""; let content = htmlContent; content = content.replaceAll( /<span[^>]*class="math-formula"[^>]*>[\s\S]*?<\/span>/g, "" ); content = content.replaceAll( /<span[^>]*class="math-expression"[^>]*>[\s\S]*?<\/span>/g, "" ); content = content.replaceAll(/\$\$[\s\S]+?\$\$/g, ""); content = content.replaceAll(/\$[^$]+\$/g, ""); content = content.replaceAll( /(?:<latex>|&lt;latex&gt;)[\s\S]*?(?:<\/latex>|&lt;\/latex&gt;)/g, "" ); content = content.replaceAll( /\\begin\{[a-zA-Z*]+\}[\s\S]*?\\end\{[a-zA-Z*]+\}/g, "" ); if (typeof document === "undefined") { return content.replaceAll(/<[^<>]*>/g, "").trim(); } const tempDiv = document.createElement("div"); tempDiv.innerHTML = content; return (tempDiv.textContent || tempDiv.innerText || "").trim(); }; // src/components/MarkdownMathRenderer/MarkdownMathRenderer.tsx var _jsxruntime = require('react/jsx-runtime'); var stripInvisibleChars = (str) => str.replaceAll(/[\u200B-\u200D\uFEFF]/g, ""); var protectCurrencyInlineMath = (markdown) => markdown.replaceAll( /(?<!\\)\$(?!\$)([^\n$]+?)(?<!\\)\$(?!\$)/g, (match, inner) => looksLikeLatex(inner) ? match : match.replaceAll("$", String.raw`\$`) ); var reflowDisplayMath = (markdown) => markdown.replaceAll( /(?<!\$)\$\$(?!\$)([^\n]+?)\$\$(?!\$)/g, (_match, inner) => ` $$ ${inner.trim()} $$ ` ); var withProtectedCode = (markdown, transform) => { const stash = []; const tokenized = markdown.replaceAll( /```[\s\S]*?```|`[^`\n]*`/g, (segment) => { const token = `__CODE_SEG_${stash.length}__`; stash.push(segment); return token; } ); return transform(tokenized).replaceAll( /__CODE_SEG_(\d+)__/g, (_match, index) => stash[Number(index)] ); }; var preprocessMarkdown = (content) => withProtectedCode( stripInvisibleChars(content), (safe) => reflowDisplayMath(protectCurrencyInlineMath(safe)) ); var MarkdownMathRenderer = _react.forwardRef.call(void 0, ({ content, className, style, testId }, ref) => { const sharedClassName = _chunkTN3AYOMVjs.cn.call(void 0, "leading-relaxed", // Paragraph spacing "[&_p]:my-2 [&_p:first-child]:mt-0 [&_p:last-child]:mb-0", // Headings "[&_h1]:text-xl [&_h1]:font-bold [&_h1]:mt-3 [&_h1]:mb-2", "[&_h2]:text-lg [&_h2]:font-bold [&_h2]:mt-3 [&_h2]:mb-2", "[&_h3]:text-base [&_h3]:font-semibold [&_h3]:mt-2.5 [&_h3]:mb-1.5", "[&_h4]:text-base [&_h4]:font-semibold [&_h4]:mt-2.5 [&_h4]:mb-1.5", "[&_h5]:font-semibold [&_h6]:font-semibold", // Lists "[&_ul]:list-disc [&_ul]:pl-5 [&_ul]:my-2", "[&_ol]:list-decimal [&_ol]:pl-5 [&_ol]:my-2", "[&_li]:my-1 [&_li>ul]:my-1 [&_li>ol]:my-1", // Text formatting "[&_b]:font-bold [&_strong]:font-bold", "[&_i]:italic [&_em]:italic", "[&_u]:underline", "[&_blockquote]:border-l-4 [&_blockquote]:border-border-200 [&_blockquote]:pl-3 [&_blockquote]:text-text-700", "[&_code]:rounded [&_code]:bg-background-100 [&_code]:px-1 [&_code]:py-0.5 [&_code]:text-sm", // Hide the KaTeX MathML accessibility layer visually (still readable to // screen readers). Tailwind preflight overrides KaTeX's default clip // rule, so without this the MathML layer duplicates every formula as // raw text next to the visual render. "[&_.katex-mathml]:sr-only", // Display math spacing "[&_.katex-display]:my-2.5", // Tables (GFM) "[&_table]:border-collapse [&_table]:w-full [&_table]:my-2.5 [&_table]:table-auto", "[&_table_td]:border [&_table_td]:border-border-200 [&_table_td]:p-2 [&_table_td]:min-w-[50px] [&_table_td]:align-top", "[&_table_th]:border [&_table_th]:border-border-200 [&_table_th]:p-2 [&_table_th]:min-w-[50px] [&_table_th]:align-top [&_table_th]:bg-background-50 [&_table_th]:font-semibold", // Images and links "[&_img]:max-w-full [&_img]:h-auto [&_img]:rounded-md [&_img]:my-2", "[&_a]:text-primary-500 [&_a]:underline [&_a:hover]:text-primary-600", className ); return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { ref, className: sharedClassName, style, "data-testid": testId, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _reactmarkdown2.default, { remarkPlugins: [_remarkgfm2.default, _remarkmath2.default], rehypePlugins: [_rehypekatex2.default], children: preprocessMarkdown(content) } ) } ); }); MarkdownMathRenderer.displayName = "MarkdownMathRenderer"; var MarkdownMathRenderer_default = _react.memo.call(void 0, MarkdownMathRenderer); // src/components/HtmlMathRenderer/HtmlMathRenderer.tsx var HtmlMathRenderer = _react.forwardRef.call(void 0, ({ content, className, style, sanitize = true, renderMathError, testId, inline = false }, ref) => { if (!inline && content && isLikelyMarkdown(content)) { return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, MarkdownMathRenderer_default, { ref, content, className, style, testId } ); } const defaultErrorRenderer = (latex) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "text-error-600 text-sm", children: [ "Math Error: ", latex ] }); const errorRenderer = renderMathError || defaultErrorRenderer; const renderContent = () => { if (!content) return null; const normalizedContent = _chunkAKAHADV7js.normalizeLineBreaksInHtml.call(void 0, content, { inline: true }); const processedContent = sanitize ? sanitizeHtmlForDisplay(normalizedContent) : normalizedContent; const parts = processHtmlWithMath(processedContent); if (parts.every((part) => part.type === "text")) { const joinedHtml = parts.map((part) => part.content).join(""); const Element = inline ? "span" : "div"; return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Element, { dangerouslySetInnerHTML: { __html: joinedHtml || processedContent } } ); } const getPartKey = (part, idx) => { const contentHash = (part.latex || part.content).slice(0, 20); return `${part.type}-${idx}-${contentHash}`; }; return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _jsxruntime.Fragment, { children: parts.map((part, index) => { const key = getPartKey(part, index); if (part.type === "math" && part.latex) { return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk6N4P3WKFjs.KatexMath, { math: part.latex, renderError: () => errorRenderer(part.latex) }, key ); } else if (part.type === "block-math" && part.latex) { if (inline) { return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk6N4P3WKFjs.KatexMath, { math: part.latex, renderError: () => errorRenderer(part.latex) }, key ); } return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "my-2.5 text-center", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk6N4P3WKFjs.KatexMath, { math: part.latex, displayMode: true, renderError: () => errorRenderer(part.latex) } ) }, key); } else { return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { dangerouslySetInnerHTML: { __html: part.content } }, key ); } }) }); }; const sharedClassName = _chunkTN3AYOMVjs.cn.call(void 0, // Base styles "leading-relaxed", // Paragraph styles "[&_p]:mb-0", // Hide the KaTeX MathML accessibility layer visually (still readable // to screen readers). Tailwind preflight overrides the KaTeX default // `position: absolute; clip:...` rule, so the MathML layer ends up // duplicating every formula as raw text next to the visual render. "[&_.katex-mathml]:sr-only", // Table styles (only relevant for block mode, but harmless for inline) "[&_table]:border-collapse [&_table]:w-full [&_table]:my-2.5 [&_table]:table-auto", "[&_table_td]:border [&_table_td]:border-border-200 [&_table_td]:p-2 [&_table_td]:min-w-[50px] [&_table_td]:align-top", "[&_table_th]:border [&_table_th]:border-border-200 [&_table_th]:p-2 [&_table_th]:min-w-[50px] [&_table_th]:align-top [&_table_th]:bg-background-50 [&_table_th]:font-semibold", "[&_table_tr:nth-child(even)]:bg-background-50/50", "[&_table_tr:hover]:bg-background-100/50", // Image styles "[&_img]:max-w-full [&_img]:h-auto [&_img]:rounded-md [&_img]:my-2", // Link styles "[&_a]:text-primary-500 [&_a]:underline [&_a:hover]:text-primary-600", // Text formatting styles "[&_b]:font-bold [&_strong]:font-bold", "[&_i]:italic [&_em]:italic", "[&_u]:underline", className ); if (inline) { return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { ref, className: sharedClassName, style, "data-testid": testId, children: renderContent() } ); } return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { ref, className: sharedClassName, style, "data-testid": testId, children: renderContent() } ); } ); HtmlMathRenderer.displayName = "HtmlMathRenderer"; var HtmlMathRenderer_default = _react.memo.call(void 0, HtmlMathRenderer); exports.cleanLatex = cleanLatex; exports.looksLikeLatex = looksLikeLatex; exports.isLikelyMarkdown = isLikelyMarkdown; exports.sanitizeHtmlForDisplay = sanitizeHtmlForDisplay; exports.processHtmlWithMath = processHtmlWithMath; exports.containsMath = containsMath; exports.stripHtml = stripHtml; exports.protectCurrencyInlineMath = protectCurrencyInlineMath; exports.reflowDisplayMath = reflowDisplayMath; exports.MarkdownMathRenderer_default = MarkdownMathRenderer_default; exports.HtmlMathRenderer_default = HtmlMathRenderer_default; //# sourceMappingURL=chunk-HZE6VBF5.js.map