This commit is contained in:
team3
2026-06-22 11:36:42 +02:00
parent 4f1f8d18d0
commit 2d1a3d3e37
3 changed files with 17 additions and 21 deletions

View File

@@ -42,7 +42,8 @@ function onKeyUp(e) {
} }
function resetAlt() { altAlone = false } // Fenster-Blur (ALT+Tab) → kein falscher Tipp function resetAlt() { altAlone = false } // Fenster-Blur (ALT+Tab) → kein falscher Tipp
function toggleInput() { function toggleInput() {
const el = rightEl.value?.querySelector('textarea') // Antwortfeld der aktuellen Form: Erklären = textarea, Lückentext-frei = input.
const el = rightEl.value?.querySelector('textarea, input')
if (!el) return if (!el) return
document.activeElement === el ? el.blur() : el.focus() document.activeElement === el ? el.blur() : el.focus()
} }

View File

@@ -592,14 +592,6 @@ const wahlWidget = computed(() => {
if (zeigeForm.value === 'lueckentext' && lueckAktuell.value && !lueckAktuell.value.schwer) return lueckAktuell.value if (zeigeForm.value === 'lueckentext' && lueckAktuell.value && !lueckAktuell.value.schwer) return lueckAktuell.value
return null return null
}) })
// Eingabefeld der aktuellen Form (Lückentext frei / Erklären) — für „Alt fokussiert".
function eingabeFeld() {
if (zeigeForm.value === 'lueckentext' && lueckAktuell.value && lueckAktuell.value.schwer && !lueckAktuell.value.fertig) {
return document.getElementById('bp-lueck-input')
}
if (zeigeForm.value === 'erklaeren' && pruefPhase.value === 'frage_offen') return pruefInputEl.value
return null
}
// Nach einer Antwort: bei Meilenstein-Überschreitung in den neuen Abschnitt, sonst nächste Frage. // Nach einer Antwort: bei Meilenstein-Überschreitung in den neuen Abschnitt, sonst nächste Frage.
function weiterAktion() { function weiterAktion() {
if (abschnittWechsel.value) zumNaechstenAbschnitt() if (abschnittWechsel.value) zumNaechstenAbschnitt()
@@ -621,12 +613,9 @@ function primaerAktion() {
function onPruefKey(e) { function onPruefKey(e) {
if (e.repeat) return if (e.repeat) return
if (props.mode !== 'full' || activeTab.value !== 'pruefung') return if (props.mode !== 'full' || activeTab.value !== 'pruefung') return
// Alt allein → ins Eingabefeld (Lückentext frei / Erklären), wie bei Frage-Antwort. // Alt allein toggelt der Fokus (BausteinFokus lone-tap) → hier NICHT fokussieren,
if (e.key === 'Alt') { // sonst kämpfen keydown-Fokus (hier) und keyup-Toggle (Fokus) → „nur beim Halten".
const el = eingabeFeld() if (e.key === 'Alt') return
if (el) { e.preventDefault(); el.focus() }
return
}
if (!e.altKey) { if (!e.altKey) {
// Nackte 14: Wahl-Widget toggeln — aber nicht, während ein Textfeld den Fokus hat. // Nackte 14: Wahl-Widget toggeln — aber nicht, während ein Textfeld den Fokus hat.
if (['1', '2', '3', '4'].includes(e.key) && wahlWidget.value && !wahlWidget.value.fertig) { if (['1', '2', '3', '4'].includes(e.key) && wahlWidget.value && !wahlWidget.value.fertig) {

View File

@@ -74,7 +74,7 @@ const bausteine = computed(() =>
const fokusBaustein = computed(() => (fokusIndex.value === null ? null : bausteine.value[fokusIndex.value])) const fokusBaustein = computed(() => (fokusIndex.value === null ? null : bausteine.value[fokusIndex.value]))
function openFokus(baustein, tab) { function openFokus(baustein, tab) {
if (!baustein.pruefbar) return // reine Lese-Sections (Rest/Rand) öffnen keinen Prüfungs-Fokus if (!istPruefbar(baustein)) return // reine Lese-Sections (Rest/Rand) öffnen keinen Prüfungs-Fokus
const i = bausteine.value.findIndex((s) => s.title === baustein.title) const i = bausteine.value.findIndex((s) => s.title === baustein.title)
if (i === -1) return if (i === -1) return
fokusIndex.value = i fokusIndex.value = i
@@ -82,10 +82,10 @@ function openFokus(baustein, tab) {
} }
// Nur prüfbare Sections im Fokus anspringen (FullGuide hat dazwischen reine Lese-Sections). // Nur prüfbare Sections im Fokus anspringen (FullGuide hat dazwischen reine Lese-Sections).
function fokusPrev() { function fokusPrev() {
for (let i = fokusIndex.value - 1; i >= 0; i--) if (bausteine.value[i].pruefbar) { fokusIndex.value = i; return } for (let i = fokusIndex.value - 1; i >= 0; i--) if (istPruefbar(bausteine.value[i])) { fokusIndex.value = i; return }
} }
function fokusNext() { function fokusNext() {
for (let i = fokusIndex.value + 1; i < bausteine.value.length; i++) if (bausteine.value[i].pruefbar) { fokusIndex.value = i; return } for (let i = fokusIndex.value + 1; i < bausteine.value.length; i++) if (istPruefbar(bausteine.value[i])) { fokusIndex.value = i; return }
} }
// Erfahrungsleiste: Lernstand über alle Bausteine des Guides (sticky, absolviert ⊇ verstanden ⊇ gemeistert). // Erfahrungsleiste: Lernstand über alle Bausteine des Guides (sticky, absolviert ⊇ verstanden ⊇ gemeistert).
@@ -103,6 +103,12 @@ const fortschritt = computed(() => {
// Prüfung läuft voll 030, sobald Guide ODER FullGuide fertig ist (gleicher Baustein-Score). // Prüfung läuft voll 030, sobald Guide ODER FullGuide fertig ist (gleicher Baustein-Score).
const cap = computed(() => ((props.doneByFormat?.Guide || props.doneByFormat?.FullGuide) ? 30 : 6)) const cap = computed(() => ((props.doneByFormat?.Guide || props.doneByFormat?.FullGuide) ? 30 : 6))
// Section prüfbar? Rest nie. Sonst prüfbar, außer das Feld ist explizit false (FullGuide-Rand).
// Fehlt das Feld (alte Guides ohne `pruefbar`) → prüfbar, damit Bestands-Guides weiter funktionieren.
function istPruefbar(s) {
return props.previewGuide?.format !== 'Rest' && s.pruefbar !== false
}
// --- Chat (Mechanik in useChat; Kontext-Extraktion bleibt hier) --- // --- Chat (Mechanik in useChat; Kontext-Extraktion bleibt hier) ---
const chat = useChat((msgs) => { const chat = useChat((msgs) => {
const { section, outline } = extractContext() const { section, outline } = extractContext()
@@ -208,9 +214,9 @@ function extractContext() {
:key="s.num" :key="s.num"
:class="['section-card', lernstand[s.title]?.gemeistert ? 'gemeistert' : (lernstand[s.title]?.verstanden ? 'verstanden' : (lernstand[s.title]?.absolviert ? 'absolviert' : ''))]" :class="['section-card', lernstand[s.title]?.gemeistert ? 'gemeistert' : (lernstand[s.title]?.verstanden ? 'verstanden' : (lernstand[s.title]?.absolviert ? 'absolviert' : ''))]"
> >
<h3 :class="{ 'baustein-klick': s.pruefbar }" @click="s.pruefbar && openFokus(s, 'pruefung')"> <h3 :class="{ 'baustein-klick': istPruefbar(s) }" @click="istPruefbar(s) && openFokus(s, 'pruefung')">
{{ s.title }} {{ s.title }}
<template v-if="s.pruefbar"> <template v-if="istPruefbar(s)">
<span v-if="lernstand[s.title]?.gemeistert" class="baustein-done gemeistert" title="Gemeistert (25/25)"> Gemeistert</span> <span v-if="lernstand[s.title]?.gemeistert" class="baustein-done gemeistert" title="Gemeistert (25/25)"> Gemeistert</span>
<span v-else-if="lernstand[s.title]?.verstanden" class="baustein-done verstanden" title="Vollständig verstanden (10/10)"> Verstanden</span> <span v-else-if="lernstand[s.title]?.verstanden" class="baustein-done verstanden" title="Vollständig verstanden (10/10)"> Verstanden</span>
<span v-else-if="lernstand[s.title]?.absolviert" class="baustein-done" title="Prüfung bestanden"> Absolviert</span> <span v-else-if="lernstand[s.title]?.absolviert" class="baustein-done" title="Prüfung bestanden"> Absolviert</span>
@@ -219,7 +225,7 @@ function extractContext() {
<GraphView v-if="ansichtModus === 'grafik'" :graph="grafiken[s.title] || null" /> <GraphView v-if="ansichtModus === 'grafik'" :graph="grafiken[s.title] || null" />
<div v-else class="section-body markdown" v-html="renderMarkdown(ansichtModus === 'kompakt' ? (s.kompakt || s.md) : s.md)"></div> <div v-else class="section-body markdown" v-html="renderMarkdown(ansichtModus === 'kompakt' ? (s.kompakt || s.md) : s.md)"></div>
<BausteinPanel <BausteinPanel
v-if="s.pruefbar" v-if="istPruefbar(s)"
mode="trigger" mode="trigger"
:baustein="s.title" :baustein="s.title"
:status="lernstand[s.title]" :status="lernstand[s.title]"