Skip to content

Commit 65d757d

Browse files
Edwin ChanEdwin Chan
authored andcommitted
Expand LaTeX rendering support
1 parent 1b06500 commit 65d757d

15 files changed

Lines changed: 344 additions & 21 deletions

CENTRAL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Stack: Next.js, NextAuth (Google + dev bypass), Prisma, KaTeX for math, Lucide i
3737
- **Mobile/classes cleanup** — fixed mobile `/problem-sets` hiding task rows by restoring the responsive card table, stabilized the mobile sidebar grid to avoid icon reflow on tap/focus, and made dashboard auth/actions render as a compact mobile account card. `/classes` now has a teacher/admin `Announcements` subtab that lists existing class messages and supports author/admin deletion. Display-name fallbacks now treat literal `"null"`/`"undefined"` strings as empty via `lib/display-name.ts`.
3838
- **Mobile sidebar focus fix** — mobile sidebar sheet geometry now remains stable under `:hover`, `:focus`, and `:focus-within`, preventing the nav grid from jumping upward when a mobile tap focuses one of the upper links (source: `app/globals.css`).
3939
- **Answer previews and LaTeX grading** — Practice and problem-set answer boxes now render a small KaTeX preview above the input using `mathInputToTex(...)`. `lib/grading.ts` normalizes math delimiters and common LaTeX forms through `lib/math-input.ts`, so expression answers and answer keys can use `$...$`, `\sqrt{...}`, coefficients before functions like `5\sqrt{2}-7`, `\frac{...}{...}`, braced powers, and `\pi`.
40+
- **Expanded LaTeX statement support** — KaTeX is upgraded to `0.17.x`, the official `mhchem` extension enables `\ce{...}`, and `lib/latex-compat.ts` converts document wrappers plus `tabular`, `tabular*`, `tabularx`, and `longtable` into safe KaTeX-compatible arrays. Bare display environments render when mixed with prose. Rendering keeps `trust: false`, `maxSize: 50`, and `maxExpand: 1000`; full TeX document compilation and resource-loading commands remain unsupported. See `docs/latex-support.md`.
4041
- **Anime.js animation pass** — the shared `MathCurveLoader` now drives SVG path drawing, rotation, and dot pulses with Anime.js v4 instead of CSS keyframes, `AnimeRouteEffects` adds reduced-motion-aware route reveal animations for key panels/rows/actions, and analytics surfaces use `AnalyticsMotion` plus animated `TrendChart` path/dots for growing bars and drawn charts. `TypewriterGreeting` keeps the existing typed/deleted text state machine and only animates the caret, avoiding per-character text tween flicker. Dependency: `animejs` in `package.json`.
4142
- **Per-set analytics access** — admins can open `/admin/sets/[id]/analytics` directly from the student-facing problem set header and the admin set editor; the editor delete action uses full-size topbar button sizing while the list keeps compact row actions.
4243
- **Problem-set writeups**`/problem-sets/[slug]/writeups` lets signed-in users post LaTeX/HTML solution notes with up to four images, then sort by latest/top and upvote/downvote posts. Authors can delete their own writeups with confirmation; admins can delete any writeup. `/writeups` is the sidebar directory for latest/top writeups and problem-set search. Persistence uses new `Writeup`, `WriteupImage`, and `WriteupVote` Prisma models plus `lib/writeup-images.ts`; image bytes are stored through the existing storage layer and streamed via `/api/files/[id]`.

DBSMO/Projects/dbsmo/Common Tasks.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ Watch for uppercase Prisma enum values versus lowercase `lib/grading.ts` values
3737

3838
Edit `lib/grading.ts` first. Then inspect all callers: full submission, practice submission, admin regrade, FTW solo submit, and FTW room submit (sources: `app/api/submit/route.ts`, `app/api/practice/submit/route.ts`, `app/api/admin/sets/[id]/regrade/route.ts`, `app/api/ftw/matches/[id]/submit/route.ts`, `app/api/ftw/rooms/[code]/submit/route.ts`). Add/update `tests/grading.test.ts`.
3939

40+
## Change LaTeX Statement Support
41+
42+
Edit `lib/latex-compat.ts` for source normalization, table conversion, and compatibility macros. Edit `app/problem-sets/[slug]/latex-statement.tsx` for delimiters/environment detection and KaTeX security options. Keep `trust: false`, retain finite `maxSize`/`maxExpand` limits, and add renderer tests in `tests/latex-statement.test.ts`. Do not treat `\usepackage` as permission to load code or files; document the supported fallback in `docs/latex-support.md` (sources: named files).
43+
4044
## Add a Problem Set Field
4145

4246
Edit:

DBSMO/Projects/dbsmo/Components.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ This note maps important [[dbsmo]] UI/components to their source files and usage
3939
- Writeup header link in `app/problem-sets/[slug]/page.tsx`: icon link next to `BookmarkButton` that opens `/problem-sets/[slug]/writeups`.
4040
- `WriteupsPage` and `WriteupsClient` in `app/problem-sets/[slug]/writeups/`: server/client pair for set writeups. The server page handles auth, set visibility, sorting, and initial data; the client component handles the composer, image selection, optimistic voting, confirm-delete controls, and feed cards rendered with `LatexStatement`.
4141
- `WriteupsDirectoryPage` in `app/writeups/page.tsx`: sidebar community writeups page with latest/top tabs and problem-set-focused search suggestions; it reuses `WriteupsClient` without the composer.
42-
- `LatexStatement` in `app/problem-sets/[slug]/latex-statement.tsx`: statement renderer for LaTeX/HTML-style statements and imported image assets.
42+
- `LatexStatement` in `app/problem-sets/[slug]/latex-statement.tsx`: server-side KaTeX renderer for LaTeX/HTML-style statements and imported image assets. It detects delimited and bare display environments, loads the official `mhchem` extension, and applies `lib/latex-compat.ts` before rendering. Compatibility converts document wrappers and common table environments to arrays while preserving `trust: false` and explicit expansion/size limits (sources: named files, `tests/latex-statement.test.ts`, `docs/latex-support.md`).
4343

4444
## Practice and Games
4545

DBSMO/Projects/dbsmo/File Map.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ This is a folder/file guide for [[dbsmo]], verified with CodeGraph and `rg --fil
6060
- `lib/auth.ts`, `lib/auth-server.ts` - NextAuth configuration and server helpers (sources: named files).
6161
- `lib/permissions.ts` - role-to-permission mapping and `hasPermission` helper (source: `lib/permissions.ts`).
6262
- `lib/grading.ts`, `lib/math-input.ts` - deterministic answer normalization and grading engine plus math-input helpers for stripping delimiters, converting common LaTeX forms, and rendering practice answer previews (sources: named files).
63+
- `lib/latex-compat.ts` - safe statement-rendering compatibility for document wrappers, common table environments, booktabs-style rules, shorthand macros, and bounded column expansion before KaTeX rendering (sources: named file, `app/problem-sets/[slug]/latex-statement.tsx`, `tests/latex-statement.test.ts`).
6364
- `lib/visibility.ts` - student visibility/status helpers for problem sets (source: `lib/visibility.ts`).
6465
- `lib/problem-tags.ts`, `lib/problem-content-format.ts`, `lib/problem-set-order.ts`, `lib/problem-set-authoring.ts` - problem metadata normalization, ordering, and authoring validation (sources: named files).
6566
- `lib/import/` - JSON/ZIP import validation, manifest and answer schemas, image asset handling, optional image ZIP parsing, ZIP path safety, JSON draft storage, image asset persistence, and JSON export conversion (sources: `lib/import/**`).

DBSMO/Projects/dbsmo/Risks and Pitfalls.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ Practice mode records `PracticeSolve` only for correct answers and has a unique
5757

5858
PDF upload limit is 25 MB (`lib/uploaded-pdf.ts`), legacy problem-set ZIP import limit is 50 MB (`lib/import/zip-dry-run.ts`), JSON import limit is 5 MB per docs/source path, image ZIP limit is 100 MB (`lib/import/image-zip.ts`), image assets are 4 MB each/50 max (`lib/import/image-assets.ts`), writeup images are 5 MB each and max 4 per post (`lib/writeup-images.ts`, `app/api/problem-sets/[id]/writeups/route.ts`), and profile avatar URL/data URL max is 700,000 characters while UI says under 512 KB (sources: named files, `docs/import-format.md`, `app/api/settings/route.ts`, `app/settings/page.tsx`).
5959

60+
## KaTeX Is Not a TeX Compiler
61+
62+
`LatexStatement` renders math with KaTeX and a bounded compatibility pass; it does not execute arbitrary `\usepackage` declarations or compile full documents. `lib/latex-compat.ts` converts supported table/document forms, but true `\multicolumn` spanning and arbitrary package features remain unavailable. Keep `trust: false`, `maxSize`, and `maxExpand` protections when expanding support, because statements and writeups are user-controlled (sources: `app/problem-sets/[slug]/latex-statement.tsx`, `lib/latex-compat.ts`, `docs/latex-support.md`).
63+
6064
## Route Handlers Often Duplicate Auth Checks
6165

6266
Middleware is not the only authorization layer. Many route handlers independently load session/current user and check role/permissions/visibility. When changing auth semantics, search callers and route handlers rather than editing only `proxy.ts` or `lib/auth.ts` (sources: `app/api/submit/route.ts`, `app/api/admin/sets/[id]/route.ts`, `app/api/files/[id]/route.ts`, `app/api/admin/classes/[id]/route.ts`).

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,6 @@ Session updates from the CodeGraph/Second Brain indexing pass onward:
5454
- Audited the signed-in production problem-set page in the real Chrome session and corrected the redesign: removed percentage shape paths from tall panels, normalized compact panel headers, removed the light-blue problem statement band, and switched functional surfaces to softer hand-drawn squircles.
5555
- Removed the global yellow wavy eyebrow/title underlines and moved the handwritten character into stable two-pass ink borders for cards, panels, controls, and form fields.
5656
- Restored the desktop sidebar as a compact icon rail that expands on hover/focus, preserved the stable mobile sheet, and introduced a shared simplified Sigma favicon/landing mark.
57+
- Upgraded KaTeX to v0.17, enabled the official `mhchem` extension, and added secure compatibility rendering for document wrappers and table environments such as `tabular`, `tabular*`, `tabularx`, and `longtable`.
5758

5859
© 2026 Cosmic Crusader

app/globals.css

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,13 +1327,15 @@ td {
13271327

13281328
.statement-math-block {
13291329
display: block;
1330+
max-width: 100%;
13301331
margin: 0.75rem 0;
1332+
overflow-x: auto;
1333+
overflow-y: hidden;
13311334
}
13321335

1333-
.statement-text .katex-display {
1336+
.statement-text .katex-display,
1337+
.statement-math-block .katex-display {
13341338
margin: 0;
1335-
overflow-x: auto;
1336-
overflow-y: hidden;
13371339
}
13381340

13391341
.problem-image {

app/problem-sets/[slug]/latex-statement.tsx

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
import katex from "katex";
2+
import "katex/contrib/mhchem";
23
import {
34
normalizeProblemContentFormat,
45
type ProblemContentFormat,
56
} from "@/lib/problem-content-format";
6-
7-
const MATH_SEGMENT_PATTERN = String.raw`(\\\[[\s\S]+?\\\]|\\\([\s\S]+?\\\)|\$\$[\s\S]+?\$\$|\$[^$\n]+\$|\[\[img:[a-z0-9][a-z0-9_-]{0,63}\]\])`;
7+
import {
8+
KATEX_COMPAT_MACROS,
9+
normalizeLatexForKatex,
10+
normalizeLatexStatementSource,
11+
} from "@/lib/latex-compat";
12+
13+
const DISPLAY_ENVIRONMENTS = String.raw`(?:tabular\*?|tabularx|longtable|array|darray|matrix\*?|pmatrix\*?|bmatrix\*?|Bmatrix\*?|vmatrix\*?|Vmatrix\*?|align\*?|alignat\*?|aligned|alignedat|gather\*?|gathered|equation\*?|split|cases|dcases|rcases|CD)`;
14+
const BARE_DISPLAY_ENVIRONMENT_PATTERN = String.raw`\\begin\{${DISPLAY_ENVIRONMENTS}\}[\s\S]+?\\end\{${DISPLAY_ENVIRONMENTS}\}`;
15+
const BARE_DISPLAY_ENVIRONMENT_REGEX = new RegExp(`^${BARE_DISPLAY_ENVIRONMENT_PATTERN}$`);
16+
const MATH_SEGMENT_PATTERN = String.raw`(\\\[[\s\S]+?\\\]|\\\([\s\S]+?\\\)|\$\$[\s\S]+?\$\$|\$[^$\n]+\$|${BARE_DISPLAY_ENVIRONMENT_PATTERN}|\[\[img:[a-z0-9][a-z0-9_-]{0,63}\]\])`;
817
const HAS_MATH_SEGMENT_REGEX = new RegExp(MATH_SEGMENT_PATTERN);
918
const MATH_SEGMENT_REGEX = new RegExp(MATH_SEGMENT_PATTERN, "g");
1019
const IMG_TOKEN_REGEX = /^\[\[img:([a-z0-9][a-z0-9_-]{0,63})\]\]$/;
@@ -16,11 +25,14 @@ type LatexStatementProps = {
1625
};
1726

1827
function renderMath(tex: string, displayMode: boolean): string {
19-
return katex.renderToString(tex, {
28+
return katex.renderToString(normalizeLatexForKatex(tex), {
2029
throwOnError: false,
2130
strict: "ignore",
2231
displayMode,
2332
trust: false,
33+
maxSize: 50,
34+
maxExpand: 1000,
35+
macros: { ...KATEX_COMPAT_MACROS },
2436
});
2537
}
2638

@@ -41,6 +53,10 @@ function parseMathSegment(part: string): { displayMode: boolean; tex: string } |
4153
return { displayMode: false, tex: part.slice(1, -1).trim() };
4254
}
4355

56+
if (BARE_DISPLAY_ENVIRONMENT_REGEX.test(part)) {
57+
return { displayMode: true, tex: part.trim() };
58+
}
59+
4460
return null;
4561
}
4662

@@ -102,7 +118,7 @@ function normalizeStatementInput(statement: string, format: ProblemContentFormat
102118
if (format === "HTML") {
103119
return normalizeHtmlStatement(value);
104120
}
105-
return value;
121+
return normalizeLatexStatementSource(value);
106122
}
107123

108124
export function LatexStatement({ statement, format, assets }: LatexStatementProps) {

docs/admin-guide.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- **Problem Number:** A positive integer (e.g. 1, 2, 3). If you leave it as-is, new problems default to sequential numbers.
1717
4. For each problem, use the **LaTeX / HTML** toggle next to the statement field.
1818
- Use **LaTeX** for `$...$`/`$$...$$` style input.
19+
- Table environments (`tabular`, `tabular*`, `tabularx`, and `longtable`) and chemistry with `\ce{...}` are supported through the compatibility renderer; see [LaTeX rendering support](./latex-support.md).
1920
- Use **HTML** for content that includes tags like `<math>...</math>`.
2021
5. You can preview the statement live before saving.
2122
- If you upload per-problem images, **Toggle Previews** renders them below the statement using the same image tokens used on the student problem page.

docs/import-format.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ Each entry in `problems` defines one question.
104104
- `LATEX` (default): the statement is interpreted as normal text with LaTeX delimiters (`$...$`, `$$...$$`, `\(...\)`, `\[...\]`).
105105
- `HTML`: the renderer accepts HTML-like source and converts `<math>...</math>`, `<imath>...</imath>`, and `<cmath>...</cmath>` math tags to KaTeX output.
106106

107+
LaTeX statements also accept bare display environments and compatibility forms for `tabular`, `tabular*`, `tabularx`, and `longtable`. Chemistry commands from `mhchem`, such as `\ce{...}`, are enabled. See [LaTeX rendering support](./latex-support.md) for conversions, limitations, and security rules.
108+
107109
## Practice Tag Behavior
108110

109111
Per-problem `topicTags` drive Practice mode.

0 commit comments

Comments
 (0)