From 26da589d9054b925a87829f3e519d99e1dff5fa0 Mon Sep 17 00:00:00 2001 From: team3 Date: Wed, 30 Sep 2026 01:16:32 +0200 Subject: [PATCH] update --- backend/config.py | 2 +- backend/main.py | 2 +- frontend/src/components/Lesemodus.vue | 53 ++++++++++++++++++--------- 3 files changed, 38 insertions(+), 19 deletions(-) diff --git a/backend/config.py b/backend/config.py index 33675d9..c94cb6b 100644 --- a/backend/config.py +++ b/backend/config.py @@ -11,7 +11,7 @@ EFFORT = "high" PARALLEL = 5 # gleichzeitige Kapitel-Agenten MAX_TURNS = 800 KONSISTENZ_GRUPPE = 5 # Kapitel je Querverweis-Agent -LESE_WPM = 180 +LESE_WPM = 120 STUFEN = [ ("recherche", "Thema deuten und Quellen beschaffen"), diff --git a/backend/main.py b/backend/main.py index bd4b2c4..2b66a02 100644 --- a/backend/main.py +++ b/backend/main.py @@ -30,7 +30,7 @@ class ThemaNeu(BaseModel): class Position(BaseModel): abschnitt: str | None = None - lesemodus: int | None = None + lesemodus: str | int | None = None # neu: key des Bildschirms, alt: Index def _pruefen(name: str) -> dict: diff --git a/frontend/src/components/Lesemodus.vue b/frontend/src/components/Lesemodus.vue index 40b8819..f2c5499 100644 --- a/frontend/src/components/Lesemodus.vue +++ b/frontend/src/components/Lesemodus.vue @@ -8,25 +8,41 @@ const emit = defineEmits(['schliessen']) const buehne = ref(null) const pos = ref(0) -// Bildschirme: Vorwort, je Kapitel ein Intro, je Abschnitt ein Bildschirm, Ende. +// Ein Abschnitt wird an seinen Zwischenüberschriften (###) in Seiten geteilt; Codeblöcke bleiben ganz. +function teile(text) { + const out = [] + let akt = [], zaun = false + for (const zeile of (text || '').split('\n')) { + if (/^(```|~~~)/.test(zeile)) zaun = !zaun + if (!zaun && /^### /.test(zeile) && akt.some((z) => z.trim())) { out.push(akt.join('\n')); akt = [] } + akt.push(zeile) + } + if (akt.some((z) => z.trim())) out.push(akt.join('\n')) + return out.length ? out : [''] +} +// Bildschirme: Vorwort, je Kapitel ein Intro, je Seite eines Abschnitts ein Bildschirm, Ende. +// key ist die gespeicherte Leseposition und bleibt stabil, wenn Kapitel dazukommen. const screens = computed(() => { const out = [] const kaps = props.daten?.kapitel || [] - if (props.daten?.vorwort) out.push({ typ: 'vorwort' }) + if (props.daten?.vorwort) out.push({ typ: 'vorwort', key: 'vorwort' }) kaps.forEach((kap, ki) => { - out.push({ typ: 'kapitel', kap, nr: ki + 1, von: kaps.length }) - for (const a of kap.abschnitte) out.push({ typ: 'abschnitt', a, kap }) + out.push({ typ: 'kapitel', kap, nr: ki + 1, von: kaps.length, key: 'k' + (ki + 1) }) + for (const a of kap.abschnitte) { + const t = teile(a.text) + t.forEach((text, i) => out.push({ typ: 'abschnitt', a, kap, text, i, n: t.length, key: a.id + ':' + i })) + } }) - out.push({ typ: 'ende' }) + out.push({ typ: 'ende', key: 'ende' }) return out }) const screen = computed(() => screens.value[pos.value] || { typ: 'ende' }) const gesamt = computed(() => screens.value.filter((s) => s.typ === 'abschnitt').length) const gelesen = computed(() => screens.value.slice(0, pos.value + 1).filter((s) => s.typ === 'abschnitt').length) const cache = new Map() -function html(a) { - const key = a.id + ':' + (a.text || '').length - if (!cache.has(key)) cache.set(key, render(a.text)) +function html(s) { + const key = s.key + ':' + s.text.length + if (!cache.has(key)) cache.set(key, render(s.text)) return cache.get(key) } @@ -36,7 +52,8 @@ let merkTimer = null watch(pos, (p) => { buehne.value?.scrollTo({ top: 0 }) clearTimeout(merkTimer) - merkTimer = setTimeout(() => api.positionSetzen(props.topic, { lesemodus: p }).catch(() => {}), 1500) + const key = screens.value[p]?.key + if (key) merkTimer = setTimeout(() => api.positionSetzen(props.topic, { lesemodus: key }).catch(() => {}), 1500) }) function taste(e) { @@ -65,8 +82,10 @@ function wischEnde(e) { } onMounted(async () => { window.addEventListener('keydown', taste) - const merk = Number((await api.position(props.topic).catch(() => ({}))).lesemodus || 0) - pos.value = Math.min(merk, screens.value.length - 1) + const merk = (await api.position(props.topic).catch(() => ({}))).lesemodus + // Alte Positionen waren ein Index, neue sind ein key. + const i = typeof merk === 'string' ? screens.value.findIndex((s) => s.key === merk) : Number(merk || 0) + pos.value = Math.min(Math.max(i, 0), screens.value.length - 1) }) onUnmounted(() => window.removeEventListener('keydown', taste)) @@ -74,9 +93,9 @@ onUnmounted(() => window.removeEventListener('keydown', taste))