update
This commit is contained in:
@@ -169,6 +169,11 @@ def _mathe_auftraege(text: str, wo: str) -> list[str]:
|
||||
if re.search(r"\\n(?![a-zA-Z])", text): # \neq etc. bleiben unberührt
|
||||
auftraege.append(f"{wo}: literales „\\n“ im Text — durch echten"
|
||||
f" Zeilenumbruch bzw. Leerzeile ersetzen.")
|
||||
lange = [m for m in re.findall(r"\$([^$\n]+?)\$", text) if len(m) > 60]
|
||||
if lange: # Inline-Formeln brechen nie um — auf schmalen Screens Überlauf
|
||||
auftraege.append(f"{wo}: {len(lange)} überlange Inline-Formel(n)"
|
||||
f" (>60 Zeichen, z. B. „{lange[0][:40]}…“): abgesetzt"
|
||||
f" als $$…$$ setzen.")
|
||||
formeln = ([{"tex": m, "display": True} for m in re.findall(r"\$\$([\s\S]+?)\$\$", text)]
|
||||
+ [{"tex": m, "display": False} for m in re.findall(r"\$([^$\n]+?)\$", text)])
|
||||
for f in _katex_fehler(formeln):
|
||||
|
||||
@@ -14,7 +14,10 @@ const neuName = ref('')
|
||||
const neuArt = ref('thema')
|
||||
const fehler = ref('')
|
||||
const theme = ref(localStorage.getItem('theme') || 'auto')
|
||||
const navAuf = ref(localStorage.getItem('navAuf') !== '0')
|
||||
// mobil (≤768px) startet die Navigation eingeklappt, sofern nie manuell gesetzt
|
||||
const navAuf = ref(localStorage.getItem('navAuf') !== null
|
||||
? localStorage.getItem('navAuf') !== '0'
|
||||
: window.innerWidth > 768)
|
||||
|
||||
function navToggle() {
|
||||
navAuf.value = !navAuf.value
|
||||
@@ -176,9 +179,11 @@ function runBadge(run) {
|
||||
<button v-for="t in ['board', 'guide', 'üben', 'kennzahlen']" :key="t"
|
||||
:class="{ aktiv: tab === t }" @click="tab = t">{{ t }}</button>
|
||||
</div>
|
||||
<template v-if="tab === 'board'">
|
||||
<button @click="starten" :disabled="!topic || state?.run?.status === 'running'">Start</button>
|
||||
<button class="sekundaer" @click="stoppen" :disabled="state?.run?.status !== 'running'">Stop</button>
|
||||
<template v-if="transferOk">
|
||||
</template>
|
||||
<template v-if="transferOk && tab === 'board'">
|
||||
<button class="sekundaer" title="Thema auf den Server pushen (überschreibt dort)"
|
||||
:disabled="!topic || !!uebertragung || state?.run?.status === 'running'"
|
||||
@click="uebertragen('push')">⇡ Push</button>
|
||||
|
||||
@@ -34,10 +34,18 @@ export const api = {
|
||||
}
|
||||
|
||||
export function wsVerbinden(onEvent) {
|
||||
const url = (import.meta.env.DEV ? 'ws://localhost:8000' : `ws://${location.host}`) + '/ws'
|
||||
// Protokoll an die Seite koppeln: HTTPS erlaubt nur wss:// (Mixed Content)
|
||||
const wsProto = location.protocol === 'https:' ? 'wss' : 'ws'
|
||||
const url = (import.meta.env.DEV ? 'ws://localhost:8000' : `${wsProto}://${location.host}`) + '/ws'
|
||||
let ws
|
||||
const verbinden = () => {
|
||||
try {
|
||||
ws = new WebSocket(url)
|
||||
} catch (e) {
|
||||
// nie den Aufrufer mitreißen (der SecurityError killte sonst den Poll)
|
||||
console.error('WebSocket nicht verfügbar:', e)
|
||||
return
|
||||
}
|
||||
ws.onopen = () => onEvent({ typ: 'verbunden' }) // nach Reconnect: Stand neu laden
|
||||
ws.onmessage = (e) => onEvent(JSON.parse(e.data))
|
||||
ws.onclose = () => setTimeout(verbinden, 2000)
|
||||
|
||||
@@ -186,11 +186,12 @@ input, select {
|
||||
}
|
||||
body.vollbild .sidebar, body.vollbild .topbar { display: none; }
|
||||
body.vollbild .nav-toggle { display: none; }
|
||||
/* Eingeklappt: Themen-Sidebar + Topbar weg — die Kapitelübersicht bleibt */
|
||||
/* Eingeklappt: Themen-Sidebar + Topbar weg — die Kapitelübersicht bleibt.
|
||||
Der Inhalt rückt nach unten, damit das fixe ☰ nichts überlappt. */
|
||||
body.nav-zu .sidebar, body.nav-zu .topbar { display: none; }
|
||||
body.nav-zu .guide-nav { padding-top: 44px; }
|
||||
body.nav-zu .hauptbereich { padding-top: 38px; }
|
||||
.nav-toggle {
|
||||
position: fixed; top: 6px; left: 8px; z-index: 20;
|
||||
position: fixed; top: 6px; left: 8px; z-index: 40; /* über den mobilen Drawern */
|
||||
background: none; color: var(--dim); border: 0; padding: 4px 8px; font-size: 16px;
|
||||
}
|
||||
.nav-toggle:hover { color: var(--text); }
|
||||
@@ -241,3 +242,32 @@ table.kennzahlen th, table.kennzahlen td {
|
||||
}
|
||||
table.kennzahlen th:first-child, table.kennzahlen td:first-child { text-align: left; }
|
||||
table.kennzahlen th { color: var(--dim); }
|
||||
|
||||
/* ── Mobil (≤768px): Navigationen als Drawer, Board mit Spalten-Snap ────────── */
|
||||
.markdown .katex { max-width: 100%; overflow-x: auto; overflow-y: hidden; }
|
||||
@media (max-width: 768px) {
|
||||
.sidebar, .guide-nav {
|
||||
position: fixed; top: 0; bottom: 0; left: 0; z-index: 30;
|
||||
width: min(85vw, 320px); min-width: 0;
|
||||
border-right: 1px solid var(--rand); box-shadow: 4px 0 24px rgba(0, 0, 0, 0.35);
|
||||
background: var(--panel);
|
||||
}
|
||||
/* Guide-Tab: der Drawer zeigt die Kapitelliste, sonst die Themenliste */
|
||||
body:has(.guide-nav) .sidebar { display: none; }
|
||||
/* mobil versteckt ☰ nur die Drawer — die Topbar (Tabs!) bleibt sichtbar */
|
||||
body.nav-zu .topbar { display: flex; }
|
||||
body.nav-zu .hauptbereich { padding-top: 0; }
|
||||
/* mobil heißt ☰-zu: ALLE Drawer weg — auch die Kapitelnav (Desktop behält sie) */
|
||||
body.nav-zu .guide-nav { display: none; }
|
||||
.sidebar .topics { padding-top: 44px; }
|
||||
.guide-nav { padding-top: 44px; }
|
||||
.topbar { padding-left: 40px; gap: 6px; }
|
||||
.topbar .badge { display: none; }
|
||||
.board { scroll-snap-type: x mandatory; padding: 8px; gap: 8px; }
|
||||
.spalte { min-width: 86vw; max-width: 86vw; scroll-snap-align: start; }
|
||||
.seite { padding: 12px; }
|
||||
.seite.guide-text { max-width: 100%; padding: 14px 16px; }
|
||||
.guide-section { padding: 12px 14px; }
|
||||
.guide-text .kapitel-titel { margin-top: 20px; }
|
||||
.flash { max-width: 100%; padding: 18px; margin: 16px auto; }
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@ Regeln (hart):
|
||||
Nur KaTeX-STANDARDBEFEHLE — kein LaTeX-Textsatz wie `\textsc`, `\usepackage`.
|
||||
Ein Parse-Gate prüft jede Formel mit dem echten Renderer.
|
||||
Hervorhebung im Text NUR als `**fett**` — nie einzelne `*…*`.
|
||||
Formeln über ~50 Zeichen ABGESETZT als `$$…$$` — lange Inline-Formeln laufen
|
||||
auf schmalen Bildschirmen aus dem Text.
|
||||
PDF-Artefakte aus den Belegen in saubere Notation übersetzen: „u 6= v" ist
|
||||
$u \neq v$, „V 0" ist $V'$ — du lehrst, du zitierst nicht.
|
||||
5. LÄNGE: „lang" hat {min_woerter}–{max_woerter} Wörter. Die Obergrenze ist ein HARTES
|
||||
|
||||
@@ -203,3 +203,5 @@ def test_katex_gate_und_markdown_hygiene(monkeypatch):
|
||||
assert "Beträge" in auftraege
|
||||
assert "einzelnes „*“" in auftraege
|
||||
assert "literales" in auftraege
|
||||
lang_formel = "Es gilt $" + "x + ".join(["y"] * 30) + "$ hier."
|
||||
assert "überlange Inline-Formel" in " ".join(guide._mathe_auftraege(lang_formel, "T"))
|
||||
|
||||
Reference in New Issue
Block a user