From a370fc90fe7911f808c1fb567ed165a056f833b7 Mon Sep 17 00:00:00 2001 From: Remi <73385395+itsrubberduck@users.noreply.github.com> Date: Sun, 5 Oct 2025 09:43:43 +0200 Subject: [PATCH] Move lesson search to app bar and preserve pitch --- app/pages/classroom-introduction.vue | 22 ++++ app/pages/classroom.vue | 148 ++++++++++++++++++--------- 2 files changed, 121 insertions(+), 49 deletions(-) diff --git a/app/pages/classroom-introduction.vue b/app/pages/classroom-introduction.vue index 4ca3bf3..cbb3b75 100644 --- a/app/pages/classroom-introduction.vue +++ b/app/pages/classroom-introduction.vue @@ -565,6 +565,26 @@ async function ensurePizzicato(ctx: AudioContext | null): Promise void>, sound: RadioSoundInstance | null) { if (activeRadioSound === sound) { activeRadioSound = null @@ -753,6 +773,8 @@ async function speak(text: string, stageId?: string): Promise { } const audio = new Audio(`data:${mime};base64,${base64}`) + audio.preload = 'auto' + setMediaElementPreservesPitch(audio, true) audio.onended = () => { if (speechRequestId === requestId) { diff --git a/app/pages/classroom.vue b/app/pages/classroom.vue index c42359a..a4f7cb3 100644 --- a/app/pages/classroom.vue +++ b/app/pages/classroom.vue @@ -13,6 +13,31 @@ Classroom +
+ +
+
@@ -38,31 +63,7 @@
Start with the ICAO alphabet & numbers, then basics, ground, and more.
- - -
+
{{ lessonSearchResults.length }} {{ lessonSearchResults.length === 1 ? 'match' : 'matches' }} @@ -3705,6 +3706,26 @@ const providerSupportsNativeSpeed = (model?: string | null) => { return normalized.includes('tts') } +const setMediaElementPreservesPitch = (media: HTMLMediaElement | null, preserve: boolean) => { + if (!media) return + const target = !!preserve + try { + ;(media as any).preservesPitch = target + } catch { + // ignore unsupported assignment + } + try { + ;(media as any).mozPreservesPitch = target + } catch { + // ignore unsupported assignment + } + try { + ;(media as any).webkitPreservesPitch = target + } catch { + // ignore unsupported assignment + } +} + async function playAudioSource(source: CachedAudio, targetRate: number) { if (!source?.base64) return @@ -3725,7 +3746,9 @@ async function playAudioSource(source: CachedAudio, targetRate: number) { const playWithoutEffects = async () => { const audio = new Audio(dataUrl) + audio.preload = 'auto' audio.playbackRate = playbackRate + setMediaElementPreservesPitch(audio, true) audioElement.value = audio audio.onended = () => { if (audioElement.value === audio) { @@ -3751,6 +3774,11 @@ async function playAudioSource(source: CachedAudio, targetRate: number) { throw new Error('Audio engine unavailable') } + if (!supportsNativeSpeed && Math.abs(playbackRate - 1) > 0.0001) { + await playWithoutEffects() + return + } + const sound = await pizzicato.createSoundFromBase64(ctx, source.base64) sound.setPlaybackRate(playbackRate) const profile = getReadabilityProfile(readability) @@ -4108,11 +4136,12 @@ onMounted(() => { background: color-mix(in srgb, var(--bg) 85%, transparent) } + .hud-inner { display: flex; align-items: center; - justify-content: space-between; - padding: 10px + gap: 16px; + padding: 10px; } .hud-left { @@ -4121,6 +4150,13 @@ onMounted(() => { gap: 10px } +.hud-center { + flex: 1; + display: flex; + justify-content: center; + padding: 0 16px; +} + .hud-logo { display: inline-flex; align-items: center; @@ -4170,7 +4206,9 @@ onMounted(() => { .hud-right { display: flex; gap: 8px; - align-items: center + align-items: center; + flex-wrap: wrap; + justify-content: flex-end; } .next-objective { @@ -4526,23 +4564,13 @@ onMounted(() => { } -/* HUB search */ -.hub-search { - margin: 18px 0; - display: flex; - flex-direction: column; - gap: 6px; +/* Lesson search */ +.lesson-search { + width: 100%; max-width: 560px; } -.hub-search-label { - font-size: 12px; - letter-spacing: .16em; - text-transform: uppercase; - color: var(--t3); -} - -.hub-search-control { +.lesson-search-control { display: flex; align-items: center; gap: 10px; @@ -4553,7 +4581,7 @@ onMounted(() => { box-shadow: 0 12px 24px rgba(0, 0, 0, .18); } -.hub-search-control input { +.lesson-search-control input { border: none; background: transparent; color: var(--text); @@ -4562,19 +4590,19 @@ onMounted(() => { min-width: 0; } -.hub-search-control input:focus { +.lesson-search-control input:focus { outline: none; } -.hub-search-control input::placeholder { +.lesson-search-control input::placeholder { color: var(--t3); } -.hub-search-icon { +.lesson-search-icon { color: var(--t3); } -.hub-search-clear { +.lesson-search-clear { border: none; background: transparent; color: var(--t3); @@ -4586,18 +4614,40 @@ onMounted(() => { transition: color .2s ease, background .2s ease; } -.hub-search-clear:hover { +.lesson-search-clear:hover { background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--accent); } -.hub-search-results { +.lesson-search-results { display: flex; flex-direction: column; gap: 10px; margin: 10px 0 20px; } +@media (max-width: 900px) { + .hud-inner { + flex-wrap: wrap; + justify-content: flex-start; + } + + .hud-center { + order: 3; + width: 100%; + padding: 10px 0 0; + } + + .lesson-search { + width: 100%; + } + + .hud-right { + width: 100%; + justify-content: flex-start; + } +} + .search-results-meta { display: flex; align-items: center;