From 1ca4c52b669be56ad1b6df16ca4e8a11cc6315f8 Mon Sep 17 00:00:00 2001 From: Script Raccoon Date: Thu, 22 Oct 2026 08:15:29 +0200 Subject: [PATCH 1/3] keep formulas on one line if possible --- .cspell.json | 3 +- src/lib/server/formulas.ts | 137 ++++++++++++++++++++++++++++++++++++- src/routes/app.css | 15 +++- 3 files changed, 152 insertions(+), 3 deletions(-) diff --git a/.cspell.json b/.cspell.json index 97c15bb81..690c2cc65 100644 --- a/.cspell.json +++ b/.cspell.json @@ -22,7 +22,8 @@ "tikzcd", "Unif", "Vect", - "Xmark" + "Xmark", + "allowbreak" ], "words": [ "abelian", diff --git a/src/lib/server/formulas.ts b/src/lib/server/formulas.ts index af5d8d057..21bcfdb5d 100644 --- a/src/lib/server/formulas.ts +++ b/src/lib/server/formulas.ts @@ -9,15 +9,27 @@ const MACROS = YAML.parse( export const MATH_REGEX = /\$\$(.*?)\$\$|\$(.*?)\$/gs +const HTML_START = '') { + // a closing tag at depth 0 closes .katex-html itself + if (depth === 0) return { chunks, end: index } + + depth-- + + // the chunk is complete. It is a break point if its last child + // (ignoring spaces) is a relation or if it contains an explicit + // \allowbreak. + if (depth === 0) { + chunks.push({ + markup: html.slice(chunk_start, index + match[0].length), + is_base, + is_break_point: has_allowbreak || last_classes.includes('mrel') + }) + } + } else { + const classes = match[1]?.split(' ') ?? [] + + // a new chunk begins + if (depth === 0) { + chunk_start = index + is_base = classes.includes('katex-base') + last_classes = [] + has_allowbreak = false + } + // a child of the current chunk + else if (depth === 1) { + if (classes.includes('allowbreak')) has_allowbreak = true + if (!classes.includes('mspace')) last_classes = classes + } + + depth++ + } + } + + throw new Error(`Unexpected HTML output of KaTeX: ${html}`) +} diff --git a/src/routes/app.css b/src/routes/app.css index 9029b6344..d136cba15 100644 --- a/src/routes/app.css +++ b/src/routes/app.css @@ -318,12 +318,25 @@ label { } /* cf. https://katex.org/docs/issues */ + +/* allow line breaks in display formulas (KaTeX sets nowrap) */ .katex-display > .katex { white-space: normal; } -.katex-display > .katex > .katex-html > .katex-base { + +/* add space between the lines of a broken display formula */ +.katex-display > .katex > .katex-html > .katex-base, +.katex-display .formula-group > .katex-base { margin: 0.25em 0; } + +/* compensate by reducing the space around display formulas (KaTeX uses 1em) */ .katex-display { margin: 0.5em 0; } + +/* keep formulas and groups of formulas (see formulas.ts) on one line if possible */ +.katex, +.formula-group { + display: inline-block; +} From 67cce79961451d18c7bc20f986347f4ad06f0e5e Mon Sep 17 00:00:00 2001 From: Script Raccoon Date: Thu, 22 Oct 2026 08:25:12 +0200 Subject: [PATCH 2/3] replace a non-breaking space with normal space --- content/Grp_total_explicit_proof.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/Grp_total_explicit_proof.md b/content/Grp_total_explicit_proof.md index 110c8f14f..f8b0633f2 100644 --- a/content/Grp_total_explicit_proof.md +++ b/content/Grp_total_explicit_proof.md @@ -25,7 +25,7 @@ We first need to define a natural transformation $$\eta_T : T \to \Hom({-}, L(T)).$$ For each group $H$ we define the function $\eta_T(H) : T(H) \to \Hom(H, L(T))$ by sending $x \in T(H)$ to the map $$H \to L(T), \quad h \mapsto e(T(\widetilde{h})(x)),$$ -where $\widetilde{h} : \IZ \to H$ denotes the homomorphism mapping $1 \mapsto h$, so that $T(\widetilde{h}) : T(H) \to T(\IZ)$. To see that this defines a group homomorphism from $H$ to $L(T)$, for $h,h' \in H$ we define the homomorphism $[h,h'] : \IZ * \IZ' \to H$ by $[h,h'] \circ \iota_1 = \widetilde{h}$ and $[h,h'] \circ \iota_2 = \widetilde{h'}$. Note that $[h,h'] \circ \mu = \widetilde{hh'}$. Hence, +where $\widetilde{h} : \IZ \to H$ denotes the homomorphism mapping $1 \mapsto h$, so that $T(\widetilde{h}) : T(H) \to T(\IZ)$. To see that this defines a group homomorphism from $H$ to $L(T)$, for $h,h' \in H$ we define the homomorphism $[h,h'] : \IZ * \IZ' \to H$ by $[h,h'] \circ \iota_1 = \widetilde{h}$ and $[h,h'] \circ \iota_2 = \widetilde{h'}$. Note that $[h,h'] \circ \mu = \widetilde{hh'}$. Hence, $$ \begin{align*} From a170ef1d5d6ed6575f42e25c1c0f68bb83025e0f Mon Sep 17 00:00:00 2001 From: Script Raccoon Date: Thu, 22 Oct 2026 08:48:28 +0200 Subject: [PATCH 3/3] prevent line break between formula and punctuation --- src/lib/server/formulas.ts | 78 ++++++++++++++++++++++++++++---------- src/lib/server/markdown.ts | 8 ++-- src/routes/app.css | 14 +++++++ 3 files changed, 76 insertions(+), 24 deletions(-) diff --git a/src/lib/server/formulas.ts b/src/lib/server/formulas.ts index 21bcfdb5d..afb9ab8db 100644 --- a/src/lib/server/formulas.ts +++ b/src/lib/server/formulas.ts @@ -7,7 +7,10 @@ const MACROS = YAML.parse( fs.readFileSync(path.resolve('database', 'data', 'macros.yaml'), 'utf8') ) as Record -export const MATH_REGEX = /\$\$(.*?)\$\$|\$(.*?)\$/gs +// The third group is the punctuation directly after an inline formula +// (see append_suffix). It does not include ], which would break the syntax +// of Markdown links. +export const MATH_REGEX = /\$\$(.*?)\$\$|\$(.*?)\$([.,;:)-]*)/gs const HTML_START = '' + last_chunk.markup = + last_chunk.markup.slice(0, -closing_tag.length) + markup + closing_tag + } else { + chunks.push({ markup, is_base: false, is_break_point: false }) + } } /** diff --git a/src/lib/server/markdown.ts b/src/lib/server/markdown.ts index ba6457ac3..3d85f63ba 100644 --- a/src/lib/server/markdown.ts +++ b/src/lib/server/markdown.ts @@ -130,19 +130,21 @@ md.block.ruler.before( /** * Replaces the math formulas in a markdown text with placeholders and - * returns a dictionary with the rendered formulas. + * returns a dictionary with the rendered formulas. The placeholder of an + * inline formula also replaces the punctuation directly after it, which + * is rendered with the formula. */ function preprocess_math(content: string) { const formulas: Record = {} const with_placeholders = content.replace( MATH_REGEX, - (_, display_formula, inline_formula) => { + (_, display_formula, inline_formula, suffix) => { const placeholder = `@@MATH-${Object.keys(formulas).length}@@` formulas[placeholder] = display_formula === undefined - ? render_formula(inline_formula, { displayMode: false }) + ? render_formula(inline_formula, { displayMode: false }, suffix) : render_formula(display_formula, { displayMode: true }) return placeholder diff --git a/src/routes/app.css b/src/routes/app.css index d136cba15..91b2aac60 100644 --- a/src/routes/app.css +++ b/src/routes/app.css @@ -340,3 +340,17 @@ label { .formula-group { display: inline-block; } + +/* punctuation after an inline formula (see formulas.ts), in the font of the text */ +.formula-suffix { + font-family: var(--text-font); + font-size: calc(1em / 1.21); /* undo the font size of .katex */ +} + +:is(i, em) .formula-suffix { + font-style: italic; +} + +:is(h1, h2, h3) .formula-suffix { + font-family: var(--mono-font); +}