From 0d89da119ea399f12b3267f8cc4b4b38237824b0 Mon Sep 17 00:00:00 2001 From: Matthias Jung Date: Mon, 9 Feb 2026 20:31:23 +0100 Subject: [PATCH 01/17] feature: thousand separators and exponent to support siunitx --- assets/latex.js | 208 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 143 insertions(+), 65 deletions(-) diff --git a/assets/latex.js b/assets/latex.js index bdf64c5..536b679 100644 --- a/assets/latex.js +++ b/assets/latex.js @@ -1,69 +1,147 @@ document.addEventListener("DOMContentLoaded", function() { - var macros = { - ",": (context) => context.future()?.text === " " ? "{\\char`,}" : "\\char`,", - "\\qty": "{#1\\,\\mathrm{#2}}", - "\\qtyrange": "{#1\\text{--}#2\\,\\mathrm{#3}}", - "\\unit": "{\\mathrm{#1}}", - "\\num": "{#1}", - "\\squared": "{^{2}}", - "\\cubed": "{^{3}}", - "\\per": "/", - "\\percent": "\\%", - "\\tera": "\\text{T}", - "\\giga": "\\text{G}", - "\\mega": "\\text{M}", - "\\kilo": "\\text{k}", - "\\dezi": "\\text{d}", - "\\centi": "\\text{c}", - "\\milli": "\\text{m}", - "\\micro": "\\text{μ}", - "\\nano": "\\text{n}", - "\\pico": "\\text{p}", - "\\kilogram": "\\text{kg}", - "\\gram": "\\text{g}", - "\\meter": "\\text{m}", - "\\second": "\\text{s}", - "\\hour": "\\text{h}", - "\\ampere": "\\text{A}", - "\\kelvin": "\\text{K}", - "\\mol": "\\text{mol}", - "\\bel": "\\text{B}", - "\\dezibel": "\\text{dB}", - "\\dB": "\\text{dB}", - "\\dBm": "\\text{dBm}", - "\\dBu": "\\text{dBu}", - "\\dBW": "\\text{dBW}", - "\\dBi": "\\text{dBi}", - "\\dBd": "\\text{dBd}", - "\\candela": "\\text{cd}", - "\\newton": "\\text{N}", - "\\hertz": "\\text{Hz}", - "\\pascal": "\\text{Pa}", - "\\volt": "\\text{V}", - "\\watt": "\\text{W}", - "\\joule": "\\text{J}", - "\\henry": "\\text{H}", - "\\farad": "\\text{F}", - "\\coulomb": "\\text{C}", - "\\ohm": "\\Omega", - "\\Ohm": "\\Omega", - "\\weber": "\\text{Wb}", - "\\tesla": "\\text{T}", - "\\bit": "\\text{b}", - "\\baud": "\\text{Bd}", - "\\degree": "^\\circ", - "\\degreeCelsius": "^\\circ\\text{C}", - }; + function formatThousands(input) { + + let s = String(input).trim(); + + // Extract exponent (e or E) and convert to \cdot 10^{...} + let exponent = ""; + const expMatch = s.match(/([eE])([+-]?\d+)$/); + if (expMatch) { + let expValue = expMatch[2]; + + // Remove leading "+" + if (expValue[0] === "+") { + expValue = expValue.slice(1); + } + + // Exponent numerisch prüfen + const expNum = parseInt(expValue, 10); + + // e0 → komplett unterdrücken + if (expNum !== 0) { + exponent = "\\cdot 10^{" + expNum + "}"; + } + + s = s.slice(0, -expMatch[0].length); + } + + // Sign + let sign = ""; + if (s[0] === "+" || s[0] === "-") { + sign = s[0]; + s = s.slice(1); + } + + // Find decimal point (last . or ,) + let intPart = s; + let fracSep = ""; + let fracDigits = ""; + const decMatch = s.match(/([.,])(\d+)$/); + if (decMatch) { + intPart = s.slice(0, decMatch.index); + fracSep = decMatch[1]; + fracDigits = decMatch[2]; + } + + // Thousand grouping in integer part + if (intPart.length >= 5) { + intPart = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, "\\,"); + } + + // Thousand grouping in fractional part (from left in 3-digit blocks) + if (fracDigits.length >= 4) { + fracDigits = fracDigits.replace(/(\d{3})(?=\d)/g, "$1\\,"); + } + + // Reassemble + return res = sign + intPart + (fracDigits ? (fracSep + fracDigits) : "") + exponent; + } + + + var macros = { + ",": (context) => context.future()?.text === " " ? "{\\char`,}" : "\\char`,", + "\\num": (context) => { + const [arg] = context.consumeArgs(1); + const raw = arg.reverse().map(t => t.text).join(""); + return formatThousands(raw); + }, + "\\qty": (context) => { + const [numTok, unitTok] = context.consumeArgs(2); + const rawNum = numTok.reverse().map(t => t.text).join(""); + const unit = unitTok.reverse().map(t => t.text).join(""); + const formattedNum = formatThousands(rawNum); + return formattedNum + "\\,\\mathrm{" + unit + "}"; + }, + "\\qtyrange": (context) => { + const [aTok, bTok, unitTok] = context.consumeArgs(3); + const aRaw = aTok.reverse().map(t => t.text).join(""); + const bRaw = bTok.reverse().map(t => t.text).join(""); + const unit = unitTok.reverse().map(t => t.text).join(""); + const aFmt = formatThousands(aRaw); + const bFmt = formatThousands(bRaw); + return aFmt + "\\,\\text{--}\\," + bFmt + "\\,\\mathrm{" + unit + "}"; + }, + "\\unit": "{\\mathrm{#1}}", + "\\squared": "{^{2}}", + "\\cubed": "{^{3}}", + "\\per": "/", + "\\percent": "\\%", + "\\tera": "\\text{T}", + "\\giga": "\\text{G}", + "\\mega": "\\text{M}", + "\\kilo": "\\text{k}", + "\\dezi": "\\text{d}", + "\\centi": "\\text{c}", + "\\milli": "\\text{m}", + "\\micro": "\\text{μ}", + "\\nano": "\\text{n}", + "\\pico": "\\text{p}", + "\\kilogram": "\\text{kg}", + "\\gram": "\\text{g}", + "\\meter": "\\text{m}", + "\\second": "\\text{s}", + "\\hour": "\\text{h}", + "\\ampere": "\\text{A}", + "\\kelvin": "\\text{K}", + "\\mol": "\\text{mol}", + "\\bel": "\\text{B}", + "\\dezibel": "\\text{dB}", + "\\dB": "\\text{dB}", + "\\dBm": "\\text{dBm}", + "\\dBu": "\\text{dBu}", + "\\dBW": "\\text{dBW}", + "\\dBi": "\\text{dBi}", + "\\dBd": "\\text{dBd}", + "\\candela": "\\text{cd}", + "\\newton": "\\text{N}", + "\\hertz": "\\text{Hz}", + "\\pascal": "\\text{Pa}", + "\\volt": "\\text{V}", + "\\watt": "\\text{W}", + "\\joule": "\\text{J}", + "\\henry": "\\text{H}", + "\\farad": "\\text{F}", + "\\coulomb": "\\text{C}", + "\\ohm": "\\Omega", + "\\Ohm": "\\Omega", + "\\weber": "\\text{Wb}", + "\\tesla": "\\text{T}", + "\\bit": "\\text{b}", + "\\baud": "\\text{Bd}", + "\\degree": "^\\circ", + "\\degreeCelsius": "^\\circ\\text{C}", + }; + + renderMathInElement(document.body, { + delimiters: [ + { left: '$$', right: '$$', display: true }, // Note: $$ has to come before $ + { left: '$', right: '$', display: false }, + { left: '\\[', right: '\\]', display: true }, + { left: "\\(", right: "\\)", display: false }, + ], + throwOnError: false, + macros: macros + }); - renderMathInElement(document.body, { - delimiters: [ - {left: '$$', right: '$$', display: true}, // Note: $$ has to come before $ - {left: '$', right: '$', display: false}, - {left: '\\[', right: '\\]', display: true}, - {left: "\\(", right: "\\)", display: false}, - ], - throwOnError : false, - macros: macros - }); }); \ No newline at end of file From 314de951cfce0a8089044de28eb42302aa8b4a5b Mon Sep 17 00:00:00 2001 From: Matthias Jung Date: Mon, 9 Feb 2026 20:44:01 +0100 Subject: [PATCH 02/17] fix: short milli solves #67 --- assets/latex.js | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/latex.js b/assets/latex.js index 536b679..25c8999 100644 --- a/assets/latex.js +++ b/assets/latex.js @@ -94,6 +94,7 @@ document.addEventListener("DOMContentLoaded", function() { "\\dezi": "\\text{d}", "\\centi": "\\text{c}", "\\milli": "\\text{m}", + "\\m": "\\text{m}", "\\micro": "\\text{μ}", "\\nano": "\\text{n}", "\\pico": "\\text{p}", From 6cc779af89720beadf5944578ee8e659480d3f48 Mon Sep 17 00:00:00 2001 From: Matthias Jung Date: Mon, 9 Feb 2026 20:45:35 +0100 Subject: [PATCH 03/17] fix: undo was not the problem for #67 --- assets/latex.js | 1 - 1 file changed, 1 deletion(-) diff --git a/assets/latex.js b/assets/latex.js index 25c8999..536b679 100644 --- a/assets/latex.js +++ b/assets/latex.js @@ -94,7 +94,6 @@ document.addEventListener("DOMContentLoaded", function() { "\\dezi": "\\text{d}", "\\centi": "\\text{c}", "\\milli": "\\text{m}", - "\\m": "\\text{m}", "\\micro": "\\text{μ}", "\\nano": "\\text{n}", "\\pico": "\\text{p}", From 0af159852c63bd42615b1abdd31a2382a1d1b902 Mon Sep 17 00:00:00 2001 From: Matthias Jung Date: Wed, 11 Feb 2026 13:16:04 +0100 Subject: [PATCH 04/17] Update assets/latex.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- assets/latex.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/latex.js b/assets/latex.js index 25c8999..a737e1c 100644 --- a/assets/latex.js +++ b/assets/latex.js @@ -55,7 +55,7 @@ document.addEventListener("DOMContentLoaded", function() { } // Reassemble - return res = sign + intPart + (fracDigits ? (fracSep + fracDigits) : "") + exponent; + return sign + intPart + (fracDigits ? (fracSep + fracDigits) : "") + exponent; } From 1419fad3788f8b1e89bdddc6b2751ec858225cac Mon Sep 17 00:00:00 2001 From: Matthias Jung Date: Wed, 11 Feb 2026 13:18:28 +0100 Subject: [PATCH 05/17] Update assets/latex.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- assets/latex.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/latex.js b/assets/latex.js index a737e1c..52512c8 100644 --- a/assets/latex.js +++ b/assets/latex.js @@ -15,10 +15,10 @@ document.addEventListener("DOMContentLoaded", function() { expValue = expValue.slice(1); } - // Exponent numerisch prüfen + // Parse exponent as a numeric value const expNum = parseInt(expValue, 10); - // e0 → komplett unterdrücken + // Suppress exponent if it is 0 (no \cdot 10^0 shown) if (expNum !== 0) { exponent = "\\cdot 10^{" + expNum + "}"; } From e9643b8944c48f1d5d02ddb86289922a50ba70ce Mon Sep 17 00:00:00 2001 From: Matthias Jung Date: Sat, 14 Feb 2026 00:29:24 +0100 Subject: [PATCH 06/17] feature: add brightspot config --- src/config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config.py b/src/config.py index a87869c..edac069 100644 --- a/src/config.py +++ b/src/config.py @@ -26,6 +26,7 @@ class Config: self.p_data_slides = self.p_data_contents / "slides" self.p_data_snippets = self.p_data_contents / "snippets" self.p_data_static = self.p_data_contents / "static" + self.p_data_brightspots = self.p_data_contents / "brightspots" self.p_data_fragenkatalog = self.p_data_questions / "fragenkatalog3b.json" self.p_data_metadata = self.p_data_questions / "metadata3b.json" From 0e9a4dceac02bc43b625094d676bc3509d4c2aef Mon Sep 17 00:00:00 2001 From: Matthias Jung Date: Sat, 14 Feb 2026 00:29:52 +0100 Subject: [PATCH 07/17] feature: add brightspot rendering in question --- assets/style-course.css | 25 +++++++++++++++++++++++++ src/build.py | 3 +++ templates/html/question.html | 9 ++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/assets/style-course.css b/assets/style-course.css index 846f5e4..59a0bd0 100644 --- a/assets/style-course.css +++ b/assets/style-course.css @@ -24,6 +24,7 @@ background: #ddd; margin-top: 2em; margin-bottom: 2em; + position: relative; } .question p { padding: 0; @@ -34,6 +35,30 @@ font-weight: 600; margin: 1ex 1em 1.5ex 1em; } + +.question.has-brightspot .question-text { + margin-right: 3em; +} + +.question .question-brightspot { + position: absolute; + top: 1ex; + right: 1ex; + line-height: 0; + text-decoration: none; + color: inherit; +} + +.question .question-brightspot-icon { + width: 2em; + height: 2em; + display: block; +} + +.question .question-brightspot:focus-visible { + outline: 2px solid currentColor; + outline-offset: 2px; +} .question .question-picture { max-width: 100%; margin-bottom: 1em; diff --git a/src/build.py b/src/build.py index 1e70dee..2dc6332 100644 --- a/src/build.py +++ b/src/build.py @@ -103,6 +103,8 @@ class Build: picture_question = "" alt_text_question = "" + brightspot_file = self.config.p_data_brightspots / f"{number}.md" + return question_template.render( question=question["question"], number=number, @@ -112,6 +114,7 @@ class Build: answer_pictures=answer_pictures, alt_text_answers=alt_text_answers, alt_text_question=alt_text_question, + brightspot=brightspot_file.exists(), ) def __build_question_slide(self, input): diff --git a/templates/html/question.html b/templates/html/question.html index a95851c..b8e7a92 100644 --- a/templates/html/question.html +++ b/templates/html/question.html @@ -1,4 +1,11 @@ -
+
+ {% if brightspot %} + + + + {% endif %}
{{ number }}: {{ question }}
{% if picture_question %}
{{ alt_text_question }}
From c79020e23970729c20486834c3202ee99e62f6aa Mon Sep 17 00:00:00 2001 From: Matthias Jung Date: Sat, 14 Feb 2026 01:38:37 +0100 Subject: [PATCH 08/17] feature: add brightspot rendering --- build.py | 1 + src/build.py | 21 +++++++++++++++++++++ templates/html/brightspot.html | 10 ++++++++++ templates/html/brightspot_question.html | 17 +++++++++++++++++ 4 files changed, 49 insertions(+) create mode 100644 templates/html/brightspot.html create mode 100644 templates/html/brightspot_question.html diff --git a/build.py b/build.py index b913c57..ae82066 100644 --- a/build.py +++ b/build.py @@ -13,3 +13,4 @@ bd.build_edition("NE") bd.build_edition("EA") bd.build_edition("NEA") bd.build_assets() +bd.build_brightspots() diff --git a/src/build.py b/src/build.py index 2dc6332..cbe2c9d 100644 --- a/src/build.py +++ b/src/build.py @@ -1,3 +1,4 @@ +import functools import json import random import shutil @@ -453,3 +454,23 @@ class Build: self.__build_course_page(snippets, "patenkarte", "patenkarte") self.__build_html_page(contents, "pruefung") self.__build_html_page(contents, "infos") + + def build_brightspots(self): + for brightspot_file in self.config.p_data_brightspots.glob("*.md"): + with brightspot_file.open() as file: + content = file.read() + with (self.config.p_build / f"{brightspot_file.stem}.html").open("w") as file: + with FiftyOhmHtmlRenderer( + question_renderer=functools.partial( + self.__build_question, + template_file="html/brightspot_question.html", + ), + picture_handler=self.__picture_handler, + photo_handler=self.__photo_handler, + include_handler=self.__include_handler, + ) as renderer: + brightspot_template = self.env.get_template("html/brightspot.html") + result = renderer.render(Document(content)) + result = brightspot_template.render(content=result, number=brightspot_file.stem) + result = self.__build_page(result, course_wrapper=False) + file.write(result) diff --git a/templates/html/brightspot.html b/templates/html/brightspot.html new file mode 100644 index 0000000..328a52d --- /dev/null +++ b/templates/html/brightspot.html @@ -0,0 +1,10 @@ + diff --git a/templates/html/brightspot_question.html b/templates/html/brightspot_question.html new file mode 100644 index 0000000..72b8038 --- /dev/null +++ b/templates/html/brightspot_question.html @@ -0,0 +1,17 @@ +
+
{{ number }}: {{ question }}
+ {% if picture_question %} +
{{ alt_text_question }}
+ {% endif %} +
+ Richtige Lösung:
+ {% if answers[0] %} + {{ answers[0] }} + {% endif %} + {% if answer_pictures[0] %} +
{{ alt_text_answers[0] }}
+ {% endif %} +
+
+ + \ No newline at end of file From d105d9cb5a0b7b59f6555a9782e9bb6c9d8d7037 Mon Sep 17 00:00:00 2001 From: Matthias Jung Date: Sat, 14 Feb 2026 11:58:20 +0100 Subject: [PATCH 09/17] feature: renamed and fixed style --- assets/style-course.css | 26 ++++++++++---------------- templates/html/brightspot.html | 2 +- templates/html/question.html | 19 +++++++++++-------- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/assets/style-course.css b/assets/style-course.css index 59a0bd0..deaf658 100644 --- a/assets/style-course.css +++ b/assets/style-course.css @@ -36,28 +36,22 @@ margin: 1ex 1em 1.5ex 1em; } -.question.has-brightspot .question-text { - margin-right: 3em; +.question .question-brightspot { + float: right; + margin: 0 0 0.5ex 1em; } -.question .question-brightspot { - position: absolute; - top: 1ex; - right: 1ex; - line-height: 0; - text-decoration: none; - color: inherit; +.question .question-text::after { + content: ""; + display: block; + clear: right; } .question .question-brightspot-icon { - width: 2em; - height: 2em; + width: 1em; + height: 1em; display: block; -} - -.question .question-brightspot:focus-visible { - outline: 2px solid currentColor; - outline-offset: 2px; + margin-right: 0.35em; } .question .question-picture { max-width: 100%; diff --git a/templates/html/brightspot.html b/templates/html/brightspot.html index 328a52d..6667820 100644 --- a/templates/html/brightspot.html +++ b/templates/html/brightspot.html @@ -3,7 +3,7 @@ - Lichtblick: {{number}} + Lösungsweg: {{number}} {{content}} diff --git a/templates/html/question.html b/templates/html/question.html index b8e7a92..6871281 100644 --- a/templates/html/question.html +++ b/templates/html/question.html @@ -1,12 +1,15 @@
- {% if brightspot %} - - - - {% endif %} -
{{ number }}: {{ question }}
+
+ {% if brightspot %} + + + Lösungsweg + + {% endif %} + {{ number }}: {{ question }} +
{% if picture_question %}
{{ alt_text_question }}
{% endif %} From 6b5189d2c468e14c48030996b51f8c5adf6cfd4d Mon Sep 17 00:00:00 2001 From: Matthias Jung Date: Sat, 14 Feb 2026 17:04:39 +0100 Subject: [PATCH 10/17] refactor: question layout --- templates/html/brightspot_question.html | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/templates/html/brightspot_question.html b/templates/html/brightspot_question.html index 72b8038..cdcad9c 100644 --- a/templates/html/brightspot_question.html +++ b/templates/html/brightspot_question.html @@ -3,13 +3,18 @@ {% if picture_question %}
{{ alt_text_question }}
{% endif %} -
- Richtige Lösung:
+
{% if answers[0] %} - {{ answers[0] }} + Richtig: {{ answers[0] }}
+ Falsch: {{ answers[1] }}
+ Falsch: {{ answers[2] }}
+ Falsch: {{ answers[3] }}
{% endif %} {% if answer_pictures[0] %} -
{{ alt_text_answers[0] }}
+ Richtig:
{{ alt_text_answers[0] }}

+ Falsch:
{{ alt_text_answers[1] }}

+ Falsch:
{{ alt_text_answers[2] }}

+ Falsch:
{{ alt_text_answers[3] }}

{% endif %}
From 0b30a7a41d83b649da148a0b0317d3d8a0cd0989 Mon Sep 17 00:00:00 2001 From: Tim Grelka Date: Sat, 14 Feb 2026 23:08:48 +0100 Subject: [PATCH 11/17] feat: improve styling for solutions --- assets/style-course.css | 12 ++++++------ src/build.py | 19 ++++++++++--------- templates/html/brightspot.html | 12 +++++++++--- templates/html/brightspot_question.html | 4 ++-- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/assets/style-course.css b/assets/style-course.css index deaf658..f89075a 100644 --- a/assets/style-course.css +++ b/assets/style-course.css @@ -19,7 +19,7 @@ font-style: italic; } -.question { +.question:not(.for-solution) { padding: 1ex; background: #ddd; margin-top: 2em; @@ -190,7 +190,7 @@ td { } ul { - list-style-type: square; + list-style-type: square; } ul li::marker { @@ -438,15 +438,15 @@ span.formula { /* Enable Line Breaks in Equations ... */ .course .katex-display > .katex { - white-space: normal + white-space: normal; } /* Add space between broken lines: */ .course .katex-display .base { - margin: 0.5em 0em + margin: 0.5em 0em; } /* Compensate by reducing space around display math */ .course .katex-display { - margin: 1em 0em; -} \ No newline at end of file + margin: 1em 0em; +} diff --git a/src/build.py b/src/build.py index cbe2c9d..8a88c84 100644 --- a/src/build.py +++ b/src/build.py @@ -1,4 +1,3 @@ -import functools import json import random import shutil @@ -461,16 +460,18 @@ class Build: content = file.read() with (self.config.p_build / f"{brightspot_file.stem}.html").open("w") as file: with FiftyOhmHtmlRenderer( - question_renderer=functools.partial( - self.__build_question, - template_file="html/brightspot_question.html", - ), + question_renderer=self.__build_question, picture_handler=self.__picture_handler, photo_handler=self.__photo_handler, include_handler=self.__include_handler, ) as renderer: + question = self.__build_question( + brightspot_file.stem, template_file="html/brightspot_question.html" + ) brightspot_template = self.env.get_template("html/brightspot.html") - result = renderer.render(Document(content)) - result = brightspot_template.render(content=result, number=brightspot_file.stem) - result = self.__build_page(result, course_wrapper=False) - file.write(result) + solution = renderer.render(Document(content)) + page = brightspot_template.render( + question=question, solution=solution, number=brightspot_file.stem + ) + page = self.__build_page(page, course_wrapper=False) + file.write(page) diff --git a/templates/html/brightspot.html b/templates/html/brightspot.html index 6667820..aecae60 100644 --- a/templates/html/brightspot.html +++ b/templates/html/brightspot.html @@ -1,10 +1,16 @@ - \ No newline at end of file From 4a4f6aaa78dfe763b112498f8ed1a7b8c881b382 Mon Sep 17 00:00:00 2001 From: Matthias Jung Date: Sat, 14 Feb 2026 23:21:35 +0100 Subject: [PATCH 12/17] feature: degree special handling --- assets/latex.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/assets/latex.js b/assets/latex.js index 702ffba..85fbb1c 100644 --- a/assets/latex.js +++ b/assets/latex.js @@ -71,7 +71,11 @@ document.addEventListener("DOMContentLoaded", function() { const rawNum = numTok.reverse().map(t => t.text).join(""); const unit = unitTok.reverse().map(t => t.text).join(""); const formattedNum = formatThousands(rawNum); - return formattedNum + "\\,\\mathrm{" + unit + "}"; + if (unit.startsWith("\\degree")) { + return formattedNum + "\\mathrm{" + unit + "}"; + } else { + return formattedNum + "\\,\\mathrm{" + unit + "}"; + } }, "\\qtyrange": (context) => { const [aTok, bTok, unitTok] = context.consumeArgs(3); @@ -80,7 +84,11 @@ document.addEventListener("DOMContentLoaded", function() { const unit = unitTok.reverse().map(t => t.text).join(""); const aFmt = formatThousands(aRaw); const bFmt = formatThousands(bRaw); - return aFmt + "\\,\\text{--}\\," + bFmt + "\\,\\mathrm{" + unit + "}"; + if (unit.startsWith("\\degree")) { + return aFmt + "\\text{--}\\," + bFmt + "\\mathrm{" + unit + "}"; + } else { + return aFmt + "\\,\\text{--}\\," + bFmt + "\\,\\mathrm{" + unit + "}"; + } }, "\\unit": "{\\mathrm{#1}}", "\\squared": "{^{2}}", @@ -129,8 +137,7 @@ document.addEventListener("DOMContentLoaded", function() { "\\tesla": "\\text{T}", "\\bit": "\\text{b}", "\\baud": "\\text{Bd}", - "\\degree": "^\\circ", - "\\degreeCelsius": "^\\circ\\text{C}", + "\\degreeCelsius": "\\,\\degree\\text{C}", }; renderMathInElement(document.body, { From 236bfff1f4f0c7af72f4e6f11c2629c9bd1de4b8 Mon Sep 17 00:00:00 2001 From: Matthias Jung Date: Sun, 15 Feb 2026 00:03:54 +0100 Subject: [PATCH 13/17] feature: include PM --- assets/latex.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/assets/latex.js b/assets/latex.js index 85fbb1c..1181000 100644 --- a/assets/latex.js +++ b/assets/latex.js @@ -26,9 +26,13 @@ document.addEventListener("DOMContentLoaded", function() { s = s.slice(0, -expMatch[0].length); } - // Sign + // Sign (+, -, \pm). Also accept "\\pm" (double-escaped backslash) and optional whitespace. let sign = ""; - if (s[0] === "+" || s[0] === "-") { + const pmMatch = s.match(/^(?:\\\\pm|\\pm)\s*/); + if (pmMatch) { + sign = "\\pm"; + s = s.slice(pmMatch[0].length); + } else if (s[0] === "+" || s[0] === "-") { sign = s[0]; s = s.slice(1); } From 77371fb3641d183547ad88c985861c7fc2841a91 Mon Sep 17 00:00:00 2001 From: Tim Grelka Date: Sun, 15 Feb 2026 13:17:27 +0100 Subject: [PATCH 14/17] fix: move button outside of question --- assets/style-course.css | 16 ++++----- templates/html/question.html | 68 +++++++++++++++++++----------------- 2 files changed, 41 insertions(+), 43 deletions(-) diff --git a/assets/style-course.css b/assets/style-course.css index f89075a..03b1830 100644 --- a/assets/style-course.css +++ b/assets/style-course.css @@ -36,23 +36,19 @@ margin: 1ex 1em 1.5ex 1em; } -.question .question-brightspot { - float: right; - margin: 0 0 0.5ex 1em; +.solution { + margin-top: -1.5em; + margin-bottom: 1em; + text-align: end; } -.question .question-text::after { - content: ""; - display: block; - clear: right; -} - -.question .question-brightspot-icon { +.solution .solution-icon { width: 1em; height: 1em; display: block; margin-right: 0.35em; } + .question .question-picture { max-width: 100%; margin-bottom: 1em; diff --git a/templates/html/question.html b/templates/html/question.html index 6871281..d92af5a 100644 --- a/templates/html/question.html +++ b/templates/html/question.html @@ -1,35 +1,37 @@ -
-
- {% if brightspot %} - - - Lösungsweg - - {% endif %} - {{ number }}: {{ question }} -
- {% if picture_question %} -
{{ alt_text_question }}
- {% endif %} -
- {% for i in range(4) %} - - {% endfor %} -
+
+
+ {{ number }}: {{ question }}
- + {% if picture_question %} +
{{ alt_text_question }}
+ {% endif %} +
+ {% for i in range(4) %} + + {% endfor %} +
+
+
+ {% if solution %} + + + Lösungsweg anzeigen + + {% endif %} +
+ \ No newline at end of file From 3302eed760aad0213b1f90eb4717da08185b4e81 Mon Sep 17 00:00:00 2001 From: Tim Grelka Date: Sun, 15 Feb 2026 13:17:37 +0100 Subject: [PATCH 15/17] refactor: rename brightspots to solution --- build.py | 14 ++++++------ src/build.py | 20 ++++++++--------- src/config.py | 2 +- templates/html/brightspot_question.html | 22 ------------------- .../html/{brightspot.html => solution.html} | 0 templates/html/solution_question.html | 21 ++++++++++++++++++ 6 files changed, 38 insertions(+), 41 deletions(-) delete mode 100644 templates/html/brightspot_question.html rename templates/html/{brightspot.html => solution.html} (100%) create mode 100644 templates/html/solution_question.html diff --git a/build.py b/build.py index ae82066..e364d8b 100644 --- a/build.py +++ b/build.py @@ -6,11 +6,11 @@ conf = config.Config() # Build Everything: bd = build.Build(conf) bd.build_website() -bd.build_edition("N") -bd.build_edition("E") -bd.build_edition("A") -bd.build_edition("NE") -bd.build_edition("EA") -bd.build_edition("NEA") +# bd.build_edition("N") +# bd.build_edition("E") +# bd.build_edition("A") +# bd.build_edition("NE") +# bd.build_edition("EA") +# bd.build_edition("NEA") bd.build_assets() -bd.build_brightspots() +bd.build_solutions() diff --git a/src/build.py b/src/build.py index 8a88c84..ad1f8a4 100644 --- a/src/build.py +++ b/src/build.py @@ -103,7 +103,7 @@ class Build: picture_question = "" alt_text_question = "" - brightspot_file = self.config.p_data_brightspots / f"{number}.md" + solution_file = self.config.p_data_solutions / f"{number}.md" return question_template.render( question=question["question"], @@ -114,7 +114,7 @@ class Build: answer_pictures=answer_pictures, alt_text_answers=alt_text_answers, alt_text_question=alt_text_question, - brightspot=brightspot_file.exists(), + has_solution=solution_file.exists(), ) def __build_question_slide(self, input): @@ -454,11 +454,11 @@ class Build: self.__build_html_page(contents, "pruefung") self.__build_html_page(contents, "infos") - def build_brightspots(self): - for brightspot_file in self.config.p_data_brightspots.glob("*.md"): - with brightspot_file.open() as file: + def build_solutions(self): + for solution_file in self.config.p_data_solutions.glob("*.md"): + with solution_file.open() as file: content = file.read() - with (self.config.p_build / f"{brightspot_file.stem}.html").open("w") as file: + with (self.config.p_build / f"{solution_file.stem}.html").open("w") as file: with FiftyOhmHtmlRenderer( question_renderer=self.__build_question, picture_handler=self.__picture_handler, @@ -466,12 +466,10 @@ class Build: include_handler=self.__include_handler, ) as renderer: question = self.__build_question( - brightspot_file.stem, template_file="html/brightspot_question.html" + solution_file.stem, template_file="html/solution_question.html" ) - brightspot_template = self.env.get_template("html/brightspot.html") + solution_template = self.env.get_template("html/solution.html") solution = renderer.render(Document(content)) - page = brightspot_template.render( - question=question, solution=solution, number=brightspot_file.stem - ) + page = solution_template.render(question=question, solution=solution, number=solution_file.stem) page = self.__build_page(page, course_wrapper=False) file.write(page) diff --git a/src/config.py b/src/config.py index edac069..81433f3 100644 --- a/src/config.py +++ b/src/config.py @@ -26,7 +26,7 @@ class Config: self.p_data_slides = self.p_data_contents / "slides" self.p_data_snippets = self.p_data_contents / "snippets" self.p_data_static = self.p_data_contents / "static" - self.p_data_brightspots = self.p_data_contents / "brightspots" + self.p_data_solutions = self.p_data_contents / "solutions" self.p_data_fragenkatalog = self.p_data_questions / "fragenkatalog3b.json" self.p_data_metadata = self.p_data_questions / "metadata3b.json" diff --git a/templates/html/brightspot_question.html b/templates/html/brightspot_question.html deleted file mode 100644 index 931eaee..0000000 --- a/templates/html/brightspot_question.html +++ /dev/null @@ -1,22 +0,0 @@ -
-
{{ number }}: {{ question }}
- {% if picture_question %} -
{{ alt_text_question }}
- {% endif %} -
- {% if answers[0] %} - Richtig: {{ answers[0] }}
- Falsch: {{ answers[1] }}
- Falsch: {{ answers[2] }}
- Falsch: {{ answers[3] }}
- {% endif %} - {% if answer_pictures[0] %} - Richtig:
{{ alt_text_answers[0] }}

- Falsch:
{{ alt_text_answers[1] }}

- Falsch:
{{ alt_text_answers[2] }}

- Falsch:
{{ alt_text_answers[3] }}

- {% endif %} -
-
- - \ No newline at end of file diff --git a/templates/html/brightspot.html b/templates/html/solution.html similarity index 100% rename from templates/html/brightspot.html rename to templates/html/solution.html diff --git a/templates/html/solution_question.html b/templates/html/solution_question.html new file mode 100644 index 0000000..409d67c --- /dev/null +++ b/templates/html/solution_question.html @@ -0,0 +1,21 @@ +
+
Frage: {{ question }}
+ {% if picture_question %} +
{{ alt_text_question }}
+ {% endif %} +
+ {% for i in range(1) %} +
+ {{ 'Lösung:' if i == 0 else 'Falsch:' }} + {% if answers[i] %} + {{ answers[i] }} + {% endif %} + {% if answer_pictures[i] %} +
{{ alt_text_answers[i] }}
+ {% endif %} +
+ {% endfor %} +
+
+ + \ No newline at end of file From 16208eee22fe08d613b456ff6a386d73a35bf21c Mon Sep 17 00:00:00 2001 From: Tim Grelka Date: Sun, 15 Feb 2026 13:20:17 +0100 Subject: [PATCH 16/17] fix: use has_solution parameter --- templates/html/question.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/html/question.html b/templates/html/question.html index d92af5a..bb613b8 100644 --- a/templates/html/question.html +++ b/templates/html/question.html @@ -24,7 +24,7 @@