Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion web/.prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@ node_modules
out
dist
coverage
public
public

# Validator fixtures contain intentionally malformed markdown; Prettier would
# auto-correct unclosed bold, angle-bracket dangerous URLs, etc.
scripts/__fixtures__/validate-md
42 changes: 42 additions & 0 deletions web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,18 @@
"rehype-katex": "^7.0.1",
"rehype-raw": "^7.0.0",
"rehype-sanitize": "^6.0.0",
"rehype-stringify": "^10.0.1",
"remark-breaks": "^4.0.0",
"remark-directive": "^4.0.0",
"remark-gfm": "^4.0.1",
"remark-math": "^6.0.0",
"remark-parse": "^11.0.0",
"remark-rehype": "^11.1.2",
"resend": "^6.9.1",
"sonner": "^2.0.7",
"svix": "^1.84.1",
"tailwind-merge": "^3.4.0",
"unified": "^11.0.5",
"unist-util-visit": "^5.1.0",
"use-long-press": "^3.3.0",
"zod": "^4.3.6",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Unknown environment name — KaTeX does not know `notathing`.

$$\begin{notathing} x = 1 \end{notathing}$$
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Subscript with no argument — `_` followed immediately by the closing delimiter.

$x_$
6 changes: 6 additions & 0 deletions web/scripts/__fixtures__/validate-md/invalid-katex-in-list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
A KaTeX error inside a bullet-list item must surface (not be discarded with the surrounding markdown).

- First valid item.
- Second valid item with formula $a + b$.
- Third item has a broken formula: $\frac{a}$.
- Fourth item.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
A KaTeX error nested inside a spoiler must propagate, not get swallowed by the directive transformer.

:::spoiler[Hint]
Try the substitution $\foo{u}$.
:::
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
A KaTeX error inside a GFM table cell must surface — confirms cell rendering does not silently drop math errors.

| Operation | Formula |
| --------- | ------------- |
| valid | $a + b$ |
| broken | $\unknowncmd$ |
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Mismatched begin/end — `aligned` opened, `cases` closed.

$$\begin{aligned} x &= 1 \end{cases}$$
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
`\frac` is a two-argument macro; supplying only one is a parse error.

$\frac{a}$
3 changes: 3 additions & 0 deletions web/scripts/__fixtures__/validate-md/invalid-katex-nested.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Outer command (`\sqrt`) is valid, inner command (`\foo`) is not — confirms errors surface from nested expressions.

$\sqrt{\foo{x}}$
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Stray `&` outside an alignment environment — only legal inside `aligned`, `array`, etc.

$x & y$
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Unbalanced braces — opening `{` after `a` is never closed.

$\frac{a{b}$
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Unclosed brace inside inline math — KaTeX parser hits end-of-input expecting `}`.

$x{$
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Unclosed environment — `aligned` is opened but never closed.

$$\begin{aligned} x &= 1$$
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Unknown control sequence — KaTeX should reject `\foo`.

$\foo{bar}$
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
`\left(` opens a dynamic delimiter group with no matching `\right`.

$\left( x + y$
5 changes: 5 additions & 0 deletions web/scripts/__fixtures__/validate-md/valid-blockquote.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
A blockquote with embedded inline math, multi-line continuation:

> The Cauchy-Schwarz inequality states that for any real numbers $a_i$ and $b_i$,
> $\left(\sum a_i b_i\right)^2 \le \left(\sum a_i^2\right) \left(\sum b_i^2\right)$,
> with equality if and only if the sequences are proportional.
8 changes: 8 additions & 0 deletions web/scripts/__fixtures__/validate-md/valid-code.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Inline `code` span and a fenced code block — supported by the schema even if rare in problem statements.

```python
def gcd(a, b):
while b:
a, b = b, a % b
return a
```
7 changes: 7 additions & 0 deletions web/scripts/__fixtures__/validate-md/valid-display-math.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Single-line display math:

$$\int_0^1 x \, dx = \frac{1}{2}$$

The canonical archive example combining several means:

$$a=\frac{x+y}{2},\ g=\sqrt{xy},\ h=\frac{2xy}{x+y},\ k=\sqrt{\frac{x^2+y^2}{2}}.$$
11 changes: 11 additions & 0 deletions web/scripts/__fixtures__/validate-md/valid-image.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Plain relative image reference (the bulk-import authoring shape):

![Triangle ABC](images/fig1.png)

Same image with a scale URL parameter (already supported in the renderer):

![Scaled diagram](images/fig2.png?scale=75)

Media-protocol URL pointing at an R2-hosted asset:

![](media:abc123?scale=50)
13 changes: 13 additions & 0 deletions web/scripts/__fixtures__/validate-md/valid-inline-math.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Battery of inline math drawn from the SKMO archive.

Plain inequality: $x \ne y$.

Fraction: $\frac{a}{b}$.

Degrees and radicals: $60^\circ$, $\sqrt{xy}$.

Interval notation: $\langle a, b\rangle$ and $\langle b, \infty)$.

Subscripted point names: $k_1(S_1, 3\,{\text{cm}})$.

Powers of trig functions: $\tg^2\gamma \cdot \tg\frac{\gamma}{2}$.
11 changes: 11 additions & 0 deletions web/scripts/__fixtures__/validate-md/valid-link.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Link variants the renderer's url-allow-list accepts:

External HTTPS: [Wikipedia](https://en.wikipedia.org/wiki/Mathematical_olympiad).

Mailto: [Send a question](mailto:olympiada@example.com).

Anchor in the same page: [Jump to the proof](#proof).

Media protocol (R2-hosted asset): [PDF version](media:abc123).

Domain-only link without protocol: [example.com](example.com).
6 changes: 6 additions & 0 deletions web/scripts/__fixtures__/validate-md/valid-list-bullet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Bullet list with mixed plain and math items:

- First item in the list.
- Second item with an embedded formula $a^2 + b^2 = c^2$.
- Third item with several math symbols: $\alpha$, $\beta$, $\gamma$.
- Final item.
6 changes: 6 additions & 0 deletions web/scripts/__fixtures__/validate-md/valid-list-nested.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Nested list — outer bullet, inner numbered. Mirrors case-analysis structure in real solutions.

- We split into two cases by the sign of $a$.
1. If $a > 0$, then $\sqrt{a^2} = a$.
2. If $a < 0$, then $\sqrt{a^2} = -a$.
- In both cases $\sqrt{a^2} = |a|$.
6 changes: 6 additions & 0 deletions web/scripts/__fixtures__/validate-md/valid-list-numbered.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Numbered list, common in problem solutions:

1. First we show that $a > 0$.
2. Therefore $\sqrt{a}$ is well-defined.
3. We apply the AM-GM inequality.
4. Conclusion: equality holds if and only if $a = b$.
9 changes: 9 additions & 0 deletions web/scripts/__fixtures__/validate-md/valid-math-align.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Aligned multi-line equation common in solutions:

$$
\begin{aligned}
(a + b)^2 &= a^2 + 2ab + b^2 \\
(a - b)^2 &= a^2 - 2ab + b^2 \\
(a + b)(a - b) &= a^2 - b^2
\end{aligned}
$$
5 changes: 5 additions & 0 deletions web/scripts/__fixtures__/validate-md/valid-math-mixed-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
For given positive numbers $x \ne y$ consider the means

$$a=\frac{x+y}{2},\ g=\sqrt{xy},\ h=\frac{2xy}{x+y},\ k=\sqrt{\frac{x^2+y^2}{2}}.$$

(These are the arithmetic, geometric, harmonic, and quadratic means of $x$ and $y$.)
3 changes: 3 additions & 0 deletions web/scripts/__fixtures__/validate-md/valid-prose.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
A simple paragraph with **bold**, _italic_, and ~~strikethrough~~ text.

A second paragraph follows with **_bold italic_** and a `code` span.
19 changes: 19 additions & 0 deletions web/scripts/__fixtures__/validate-md/valid-realistic-problem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
For given positive numbers $x \ne y$ consider the means

$$a=\frac{x+y}{2},\ g=\sqrt{xy},\ h=\frac{2xy}{x+y},\ k=\sqrt{\frac{x^2+y^2}{2}}.$$

(These are the arithmetic, geometric, harmonic, and quadratic means of $x$ and $y$.)

Prove that the following chain of inequalities holds:

1. $h < g$,
2. $g < a$,
3. $a < k$.

For intuition, the four means can be visualised on the figure below:

![Four means of two numbers](images/means.png?scale=80)

:::spoiler[Hint]
Start with $h < g$: it suffices to show $\frac{2xy}{x+y} < \sqrt{xy}$, which after clearing denominators reduces to $(\sqrt{x} - \sqrt{y})^2 > 0$.
:::
11 changes: 11 additions & 0 deletions web/scripts/__fixtures__/validate-md/valid-spoiler.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Problem statement asking the reader to find a closed form.

:::spoiler[Hint]
Try the substitution $u = x^2$. After substituting you get

$$\int u \, du = \frac{u^2}{2} + C.$$

Convert back to the original variable to obtain the result.
:::

After solving, compare your work with the model solution.
7 changes: 7 additions & 0 deletions web/scripts/__fixtures__/validate-md/valid-table.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
GFM table — needed for solutions per §3 format-coverage check.

| Operation | Formula | Example |
| -------------- | ----------- | --------------- |
| addition | $a + b$ | $2 + 3 = 5$ |
| multiplication | $a \cdot b$ | $2 \cdot 3 = 6$ |
| exponentiation | $a^b$ | $2^3 = 8$ |
Loading
Loading