update
This commit is contained in:
@@ -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"),
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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))
|
||||
</script>
|
||||
@@ -74,9 +93,9 @@ onUnmounted(() => window.removeEventListener('keydown', taste))
|
||||
<template>
|
||||
<div class="lesemodus" @touchstart.passive="wischStart" @touchend.passive="wischEnde">
|
||||
<header class="lm-kopf">
|
||||
<span>{{ gelesen }} / {{ gesamt }} Abschnitte</span>
|
||||
<span>{{ gelesen }} / {{ gesamt }} Seiten</span>
|
||||
<div class="balken"><i :style="{ width: (gesamt ? 100 * gelesen / gesamt : 0) + '%' }"></i></div>
|
||||
<span v-if="screen.typ === 'abschnitt'">{{ screen.kap.titel }}</span>
|
||||
<span v-if="screen.typ === 'abschnitt'">{{ screen.a.nr }} · {{ screen.i + 1 }} / {{ screen.n }}</span>
|
||||
<button title="Schließen (Esc)" @click="emit('schliessen')">✕</button>
|
||||
</header>
|
||||
<main ref="buehne" class="lm-buehne">
|
||||
@@ -95,13 +114,13 @@ onUnmounted(() => window.removeEventListener('keydown', taste))
|
||||
<div v-else class="stats">{{ screen.kap.abschnitte.length }} Abschnitte · {{ zeit(screen.kap.minuten) }}</div>
|
||||
</section>
|
||||
<section v-else-if="screen.typ === 'abschnitt'" class="lm-screen">
|
||||
<div class="lm-label">{{ screen.kap.titel }}</div>
|
||||
<h2>{{ screen.a.nr }} {{ screen.a.titel }}</h2>
|
||||
<div v-html="html(screen.a)"></div>
|
||||
<div class="lm-label">{{ screen.kap.titel }}<template v-if="screen.i > 0"> · {{ screen.a.nr }} {{ screen.a.titel }}</template></div>
|
||||
<h2 v-if="screen.i === 0">{{ screen.a.nr }} {{ screen.a.titel }}</h2>
|
||||
<div v-html="html(screen)"></div>
|
||||
</section>
|
||||
<section v-else class="lm-screen ende">
|
||||
<h1>Fertig gelesen</h1>
|
||||
<p class="intro">Alle {{ gesamt }} Abschnitte von „{{ daten.titel }}“.</p>
|
||||
<p class="intro">Alle {{ gesamt }} Seiten von „{{ daten.titel }}“.</p>
|
||||
<button class="sekundaer" style="margin-top: 16px" @click="pos = 0">Von vorn</button>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
Reference in New Issue
Block a user