feat(ui): support bracket math delimiters#588
Conversation
LaTeX content commonly uses \(...\) and \[...\], but the existing marked-katex extension only recognizes dollar delimiters. Add parser-native inline and display rules while preserving dollar math, code spans, escaped delimiters, and unmatched text. Cover both delimiter styles and parser boundary cases with focused tests.
There was a problem hiding this comment.
Pull request overview
This PR extends the UI Markdown renderer to support LaTeX bracket-style math delimiters (\(...\) inline and \[...\] display) alongside the existing dollar-delimited math behavior provided by marked-katex-extension.
Changes:
- Add two Marked parser-native extensions to tokenize/render
\(...\)(inline) and\[...\](block) math via KaTeX. - Add regression tests covering bracket delimiters, dollar delimiters, and “don’t hijack code/unmatched/escaped” edge cases.
- Add a short design/spec document describing delimiter rules and edge cases.
File size note (refactoring signal):
packages/ui/src/lib/markdown.tsis ~590 lines (over the ~500-line warning threshold).
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| packages/ui/src/lib/markdown.ts | Registers new Marked extensions for \(...\) / \[...\] and renders them with KaTeX using the existing error policy. |
| packages/ui/src/lib/markdown.test.ts | Adds focused regression tests for bracket delimiter parsing while preserving existing markdown behaviors. |
| docs/superpowers/specs/2026-07-14-bracket-math-delimiters-design.md | Documents the delimiter design decisions, integration plan, and covered edge cases. |
| { | ||
| name: "inlineBracketMath", | ||
| level: "inline", | ||
| // Find unescaped inline bracket math without scanning beyond the next line. |
|
PR builds are available as GitHub Actions artifacts: https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/29299460265 Artifacts expire in 7 days.
|
There was a problem hiding this comment.
Gatekeeper review of a0fb855
Blocking finding
- P1: display bracket math is not recognized when it follows prose without a blank line.
The block extension at packages/ui/src/lib/markdown.ts:431-453 only tokenizes when the current block source begins with \[. It has no block start() hint to interrupt an in-progress paragraph.
Reproduction: a line containing before, followed immediately by a line containing \[x^2\], followed by a line containing after.
Current output is effectively
before
[x^2]
after
This misses a normal LaTeX/LLM formatting pattern and makes the advertised \[...\] support depend on undocumented surrounding whitespace.
Add paragraph-interruption handling, for example a block start() hint that locates a valid unescaped line-start opener, or another parser-native approach that preserves code/fence precedence. Add a regression with prose immediately before and after the display block, including a multiline formula.
Important observations
- packages/ui/src/lib/markdown.ts:21-24 still says all math rendering and delimiter rules are delegated to marked-katex-extension specifically to avoid hand-written tokenizer code. That is now false and should be updated to describe the split ownership.
- The new test assertions all pass, but npx tsx --test src/lib/markdown.test.ts did not terminate locally after reporting 8/8 passed; it remained alive until timeout. Please verify the documented test command exits cleanly in the supported Node environment.
What looks correct
- Inline \(...\) rendering works and stops at line boundaries.
- Standalone and blank-line-separated display formulas render with displayMode.
- Code spans, fences, escaped delimiters, empty/unmatched delimiters, and existing dollar math remain protected by the focused cases.
- KaTeX uses the existing throwOnError: false and strict: ignore policy, with trust left disabled.
Validation
- Focused assertions: 8/8 passed before the runner hang.
- npm run typecheck in packages/ui: passed.
- npm run build in packages/ui: passed.
- git diff --check upstream/dev...HEAD: passed.
- Complete required Electron/Tauri build matrix: green.
Verdict
Changes requested. The inline path is ready, but the display tokenizer must handle the common no-blank-line paragraph boundary before merge.
Interrupt paragraphs before valid bracket display delimiters so prose does not require blank lines around display math. Preserve code, dollar math, escaped, unmatched, and whitespace-only delimiter behavior with regression coverage.
|
PR builds are available as GitHub Actions artifacts: https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/29327837705 Artifacts expire in 7 days.
|
pascalandr
left a comment
There was a problem hiding this comment.
Gatekeeper re-review of 3aa36cd
PASS. The previous blocking case is fixed: single-line and multiline [...] blocks now interrupt adjacent prose without requiring blank lines, while inline code, escaped/empty/unmatched delimiters, dollar math, blockquotes, and lists retain the expected behavior.
Validation:
- Focused Markdown assertions: 11/11 passed.
- Additional blockquote/list/code-span cases: passed.
- UI typecheck: passed.
- UI production build: passed.
- git diff --check: passed.
- Full required Electron/Tauri CI matrix: green.
Non-blocking follow-ups:
- The focused test process remains alive after all assertions under both project-supported Node 20 and local Node 25. The assertions are valid, but the test lifecycle should be fixed before this command is wired into CI.
- The comment above the inline extension's start() says it does not scan beyond the next line, while src.search() can locate an opener on a later line; the tokenizer itself still enforces the intended same-line closing rule.
- packages/ui/src/lib/markdown.ts is approximately 634 lines, above the repository's 500-line refactoring warning threshold.
Verdict: approved.
|
Thanks @jollyxenon |
Why
LaTeX content commonly uses
\(...\)and\[...\], but CodeNomad only recognizes$...$and$$...$$throughmarked-katex-extension.What
\(...\)and display\[...\]formulas.