diff --git a/app/pages/classroom.vue b/app/pages/classroom.vue index e9de6d3..dad4463 100644 --- a/app/pages/classroom.vue +++ b/app/pages/classroom.vue @@ -1058,38 +1058,40 @@
Your readback
- +
+ +
@@ -1423,7 +1425,7 @@ import { altitudeToWords, minutesToWords } from '~~/shared/learn/scenario' -import type {BlankWidth, Frequency, Lesson, LessonField, ModuleDef, Scenario} from '~~/shared/learn/types' +import type {BlankWidth, Frequency, Lesson, LessonField, ModuleDef, ReadbackSegment, Scenario} from '~~/shared/learn/types' import {loadPizzicatoLite} from '~~/shared/utils/pizzicatoLite' import type {PizzicatoLite} from '~~/shared/utils/pizzicatoLite' import {createNoiseGenerators, getReadabilityProfile} from '~~/shared/utils/radioEffects' @@ -3376,6 +3378,33 @@ const correctReadbackText = computed(() => { }).join('').trim() }) +// Pair each input field with its preceding text label so they wrap together +// as a single visual unit — otherwise the label (e.g. "runway") can land on +// one line while the input lands on the next, forcing the user to look up to +// remember what they're typing. Reported by Detlef (FSC e.V.). +type ClozeGroup = { id: string; segments: ReadbackSegment[] } +const clozeGroups = computed(() => { + if (!activeLesson.value) return [] + const segments = activeLesson.value.readback + const groups: ClozeGroup[] = [] + let i = 0 + while (i < segments.length) { + const seg = segments[i]! + const next = segments[i + 1] + if (seg.type === 'text' && next && next.type === 'field') { + groups.push({ id: `g-${next.key}`, segments: [seg, next] }) + i += 2 + } else if (seg.type === 'field') { + groups.push({ id: `g-${seg.key}`, segments: [seg] }) + i += 1 + } else { + groups.push({ id: `g-t-${i}`, segments: [seg] }) + i += 1 + } + } + return groups +}) + async function speakCorrectReadback() { const text = correctReadbackText.value if (!text || ttsLoading.value) return @@ -6605,12 +6634,28 @@ onMounted(() => { display: flex; flex-wrap: wrap; gap: 10px; - align-items: stretch; + align-items: flex-start; line-height: 1.4; text-transform: uppercase; letter-spacing: .08em; } +/* Each label/text + its input field travel together as one wrap-unit so the + user always sees the prompt next to the blank they're filling. */ +.cloze-group { + display: inline-flex; + flex-wrap: nowrap; + gap: 10px; + align-items: stretch; + max-width: 100%; +} + +@media (max-width: 640px) { + .cloze-group { + flex-wrap: wrap; + } +} + .cloze-chunk { display: inline-flex; align-items: center;