Skip to content

fix(frontend): silence noisy KaTeX warning on Unicode symbols in math spans - #1208

Open
bitsandbots wants to merge 2 commits into
lfnovo:mainfrom
bitsandbots:fix/katex-unknown-symbol-warning
Open

fix(frontend): silence noisy KaTeX warning on Unicode symbols in math spans#1208
bitsandbots wants to merge 2 commits into
lfnovo:mainfrom
bitsandbots:fix/katex-unknown-symbol-warning

Conversation

@bitsandbots

@bitsandbots bitsandbots commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Summary

AI-generated content often contains Unicode punctuation (en/em dashes, smart quotes) inside single-dollar math spans — e.g. a price range like $5 – $10. Models legitimately emit inline math as $...$ per this project's own prompts (prompts/*/system.jinja "MATH FORMATTING" sections), so remark-math correctly reads that as inline math. KaTeX's default strict mode then logs a console warning for every character outside its symbol table (the en-dash, in this example).

The warning is harmless — KaTeX still renders a best-effort glyph — but noisy: I saw 66+ occurrences in a few days of normal self-hosted use.

Fix

Pass KaTeX's own strict option as a function that ignores only the unknownSymbol error code, leaving every other strict check (e.g. deprecated commands) at its default. Extracted to one shared KATEX_OPTIONS constant (frontend/src/lib/utils/katex-options.ts) so the three markdown-rendering call sites (chat/insight/source rendering via markdown-renderer.tsx, the note editor live preview via markdown-editor.tsx, and the transformation playground) don't each duplicate the same override.

An earlier version of this fix disabled remark-math's singleDollarTextMath option instead — that was wrong, since it breaks the intentional single-dollar inline math this project's prompts already rely on (caught by the existing KaTeX render test before it went further).

Test plan

  • npm run test -- --run — 140/140 pass, including the existing test asserting single-dollar inline math ($x^2 + y^2 = z^2$) still renders via KaTeX
  • npx tsc --noEmit — clean
  • npm run lint — 0 errors (pre-existing unrelated warnings only)
  • npm run build — clean production build
  • Manually verified in a running instance: a note containing $5 – $10 (the exact failure pattern) renders with zero console warnings, while $x^2+y^2=z^2$ and $$\int_0^1 x^2\,dx=\frac13$$ still render correctly via KaTeX/MathML

🤖 Generated with Claude Code

Review in cubic

AI-generated content often contains Unicode punctuation (en/em dashes,
smart quotes) inside single-dollar math spans, e.g. a price range like
"$5 – $10". Models legitimately emit inline math as $...$ per this
project's own prompts ("MATH FORMATTING" sections), so remark-math
correctly reads it as math - but KaTeX's default strict mode then logs
a console warning for every character outside its symbol table.

The warning is harmless (KaTeX still renders a best-effort glyph) but
noisy: 66+ occurrences in a few days of normal use on one deployment.

Fix: pass KaTeX's own `strict` option as a function that ignores only
the `unknownSymbol` error code, leaving every other strict check (e.g.
deprecated commands) at its default. All three markdown-rendering call
sites (chat/insight/source rendering, the note editor live preview,
and the transformation playground) now share one KATEX_OPTIONS
constant instead of duplicating the same override three times.

Verified: 140/140 frontend tests pass (including the existing test
that asserts single-dollar inline math still renders via KaTeX),
tsc --noEmit clean, eslint clean (0 errors), production build clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 4 files

Confidence score: 4/5

  • In frontend/src/components/ui/markdown-editor.tsx, the Unicode-warning fix is unprotected by a regression test, so a later refactor could reintroduce KaTeX warnings without failing the existing math-rendering test, leading to noisy preview behavior slipping into production — add a focused preview regression test (e.g., $5 – $1...) that asserts warnings stay suppressed.

You’re at about 99% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="frontend/src/components/ui/markdown-editor.tsx">

<violation number="1" location="frontend/src/components/ui/markdown-editor.tsx:51">
P2: The Unicode-warning fix has no regression test for the behavior it changes, so future edits can reintroduce KaTeX warnings while the current math-rendering test still passes. A preview test for a case such as `$5 – $10$` should assert that the expected KaTeX warning is absent while a non-`unknownSymbol` strict warning remains covered.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

export const PREVIEW_OPTIONS = {
remarkPlugins: [remarkMath] as PluggableList,
rehypePlugins: [[rehypeSanitize, SANITIZE_SCHEMA], rehypeKatex] as PluggableList,
rehypePlugins: [[rehypeSanitize, SANITIZE_SCHEMA], [rehypeKatex, KATEX_OPTIONS]] as PluggableList,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The Unicode-warning fix has no regression test for the behavior it changes, so future edits can reintroduce KaTeX warnings while the current math-rendering test still passes. A preview test for a case such as $5 – $10$ should assert that the expected KaTeX warning is absent while a non-unknownSymbol strict warning remains covered.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/src/components/ui/markdown-editor.tsx, line 51:

<comment>The Unicode-warning fix has no regression test for the behavior it changes, so future edits can reintroduce KaTeX warnings while the current math-rendering test still passes. A preview test for a case such as `$5 – $10$` should assert that the expected KaTeX warning is absent while a non-`unknownSymbol` strict warning remains covered.</comment>

<file context>
@@ -46,7 +48,7 @@ const SANITIZE_SCHEMA = {
 export const PREVIEW_OPTIONS = {
   remarkPlugins: [remarkMath] as PluggableList,
-  rehypePlugins: [[rehypeSanitize, SANITIZE_SCHEMA], rehypeKatex] as PluggableList,
+  rehypePlugins: [[rehypeSanitize, SANITIZE_SCHEMA], [rehypeKatex, KATEX_OPTIONS]] as PluggableList,
 }
 
</file context>

Comment thread frontend/src/lib/utils/katex-options.ts Outdated
Per cubic's review on this PR:
- Add a regression test asserting the em/en-dash-in-price-range
  pattern that motivated this fix produces no [unknownSymbol] warning,
  and a second test asserting a different strict violation
  ([commentAtEnd]) is still warned on - proving the ignore is scoped
  to exactly one error code, not silently widened.
- Reword the KATEX_OPTIONS comment: suppressing unknownSymbol isn't
  purely cosmetic. KaTeX's own parser falls back unrecognized
  codepoints to mode: 'text' rather than proper math-mode metrics, so
  an ignored symbol can render with approximated spacing rather than a
  real glyph. Acceptable for stray punctuation, not a blanket license
  to route arbitrary Unicode through math mode.

Verified: 142/142 frontend tests pass (up from 140 - the two new
tests), tsc --noEmit clean, eslint clean (0 errors).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@bitsandbots

Copy link
Copy Markdown
Contributor Author

Addressed both points in 6ce7c00:

  1. Added frontend/src/components/ui/markdown-editor.test.tsx regression tests: one asserting the em/en-dash-in-price-range pattern that motivated this fix produces no [unknownSymbol] warning, and one asserting a different strict violation ([commentAtEnd]) is still warned on — proving the ignore is scoped to exactly one error code, not silently widened by a future edit.
  2. Reworded the KATEX_OPTIONS comment in katex-options.ts to keep the metrics/layout caveat visible: KaTeX's parser falls back unrecognized codepoints to mode: 'text' rather than proper math-mode metrics, so suppressing the warning isn't purely cosmetic — an ignored symbol can render with approximated spacing. Kept scoped to stray punctuation, not a license to route arbitrary Unicode through math mode.

142/142 frontend tests pass (up from 140), tsc --noEmit clean, eslint clean.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files (changes from recent commits).

Confidence score: 4/5

  • In frontend/src/components/ui/markdown-editor.test.tsx, the warning filter appears to expect bracketed codes (e.g., [unknownSymbol]) while KaTeX warns as KaTeX <code>: ..., so the test can pass for the wrong reason and miss real parsing-warning behavior changes; align the matcher with KaTeX’s actual warning format (or assert on both formats) to de-risk false positives.

You’re at about 99% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="frontend/src/components/ui/markdown-editor.test.tsx">

<violation number="1" location="frontend/src/components/ui/markdown-editor.test.tsx:77">
P2: The bracket-wrapped error code filter (`[unknownSymbol]`, `[commentAtEnd]`) may not match KaTeX's actual console.warn format, which uses `KaTeX <code>: <message>` without brackets. If so, the first test passes trivially (no filter match even when warnings exist) and the second fails. Suggest checking the actual format by inspecting KaTeX's Settings.reportError or running the tests once. If format lacks brackets, the test would need updated assertion strings.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
renderPreview('Price range: $5 – $10')
const unknownSymbolWarnings = warn.mock.calls.filter(([msg]) =>
typeof msg === 'string' && msg.includes('[unknownSymbol]')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The bracket-wrapped error code filter ([unknownSymbol], [commentAtEnd]) may not match KaTeX's actual console.warn format, which uses KaTeX <code>: <message> without brackets. If so, the first test passes trivially (no filter match even when warnings exist) and the second fails. Suggest checking the actual format by inspecting KaTeX's Settings.reportError or running the tests once. If format lacks brackets, the test would need updated assertion strings.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/src/components/ui/markdown-editor.test.tsx, line 77:

<comment>The bracket-wrapped error code filter (`[unknownSymbol]`, `[commentAtEnd]`) may not match KaTeX's actual console.warn format, which uses `KaTeX <code>: <message>` without brackets. If so, the first test passes trivially (no filter match even when warnings exist) and the second fails. Suggest checking the actual format by inspecting KaTeX's Settings.reportError or running the tests once. If format lacks brackets, the test would need updated assertion strings.</comment>

<file context>
@@ -58,6 +58,37 @@ describe('MarkdownEditor preview sanitization', () => {
+      const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
+      renderPreview('Price range: $5 – $10')
+      const unknownSymbolWarnings = warn.mock.calls.filter(([msg]) =>
+        typeof msg === 'string' && msg.includes('[unknownSymbol]')
+      )
+      expect(unknownSymbolWarnings).toHaveLength(0)
</file context>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant