animot-presenter 0.5.14 → 0.5.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AnimotPresenter.svelte +39 -15
- package/dist/cdn/animot-presenter.esm.js +4049 -4043
- package/dist/cdn/animot-presenter.min.js +8 -8
- package/package.json +1 -1
|
@@ -190,6 +190,13 @@
|
|
|
190
190
|
// Single <audio> element for per-slide narration playback. Only used
|
|
191
191
|
// when `project.settings.narrationEnabled` is true. Lazy-allocated so
|
|
192
192
|
// decks without narration don't pay for it.
|
|
193
|
+
//
|
|
194
|
+
// Narration is bound to the deck's PLAY state (`isAutoplay`), not to
|
|
195
|
+
// arbitrary page clicks: the user pressing the play button is what
|
|
196
|
+
// starts narration, the pause button stops it. This keeps multiple
|
|
197
|
+
// decks on a page from playing each other's audio when one is clicked,
|
|
198
|
+
// and keeps autoplay-blocked browsers from silently doing nothing —
|
|
199
|
+
// the user's click on the play control is itself the unlocking gesture.
|
|
193
200
|
let narrationAudio: HTMLAudioElement | null = null;
|
|
194
201
|
function playNarrationForSlide(index: number) {
|
|
195
202
|
if (!project?.settings?.narrationEnabled) return;
|
|
@@ -202,11 +209,11 @@
|
|
|
202
209
|
if (!src) return;
|
|
203
210
|
if (!narrationAudio) narrationAudio = new Audio();
|
|
204
211
|
narrationAudio.src = src;
|
|
205
|
-
// First play() may reject under autoplay policy; the player's first
|
|
206
|
-
// user gesture (pressing play / clicking the deck) unlocks the audio
|
|
207
|
-
// context and subsequent slides will work.
|
|
208
212
|
narrationAudio.play().catch(() => {});
|
|
209
213
|
}
|
|
214
|
+
function pauseNarration() {
|
|
215
|
+
if (narrationAudio) narrationAudio.pause();
|
|
216
|
+
}
|
|
210
217
|
function stopNarration() {
|
|
211
218
|
if (narrationAudio) { narrationAudio.pause(); narrationAudio = null; }
|
|
212
219
|
}
|
|
@@ -571,12 +578,10 @@
|
|
|
571
578
|
const targetSlide = slides[targetIndex];
|
|
572
579
|
clearAllTypewriterAnimations();
|
|
573
580
|
cancelMotionPathLoops();
|
|
574
|
-
// Trigger per-slide narration
|
|
575
|
-
//
|
|
576
|
-
//
|
|
577
|
-
|
|
578
|
-
// (which gets us here) acts as the unlocking gesture.
|
|
579
|
-
playNarrationForSlide(targetIndex);
|
|
581
|
+
// Trigger per-slide narration only while the deck is actively
|
|
582
|
+
// playing. Manual nav while paused stays silent — narration is
|
|
583
|
+
// bound to the play button, not to every interaction.
|
|
584
|
+
if (isAutoplay) playNarrationForSlide(targetIndex);
|
|
580
585
|
const transition = targetSlide.transition;
|
|
581
586
|
const duration = durationOverride ?? transition.duration;
|
|
582
587
|
transitionDurationMs = duration;
|
|
@@ -1136,11 +1141,9 @@
|
|
|
1136
1141
|
await loadCodeHighlights();
|
|
1137
1142
|
loading = false;
|
|
1138
1143
|
if (currentSlide) setTimeout(() => animateMotionPaths(currentSlide!), 300);
|
|
1139
|
-
//
|
|
1140
|
-
//
|
|
1141
|
-
//
|
|
1142
|
-
// click that starts the deck doubles as the audio unlock.
|
|
1143
|
-
playNarrationForSlide(currentSlideIndex);
|
|
1144
|
+
// Narration starts via the play-state effect below — not on
|
|
1145
|
+
// mount. That way the user's click on Play is the gesture
|
|
1146
|
+
// that unlocks audio, and a paused deck stays silent.
|
|
1144
1147
|
if (autoplay) isAutoplay = true;
|
|
1145
1148
|
} catch (e: any) { error = e.message; loading = false; }
|
|
1146
1149
|
}
|
|
@@ -1158,7 +1161,28 @@
|
|
|
1158
1161
|
});
|
|
1159
1162
|
if (containerEl) resizeObserver.observe(containerEl);
|
|
1160
1163
|
resetMouseIdleTimer();
|
|
1161
|
-
|
|
1164
|
+
|
|
1165
|
+
return () => {
|
|
1166
|
+
resizeObserver?.disconnect();
|
|
1167
|
+
clearAutoplayTimer();
|
|
1168
|
+
clearAllTypewriterAnimations();
|
|
1169
|
+
if (mouseIdleTimer) clearTimeout(mouseIdleTimer);
|
|
1170
|
+
stopNarration();
|
|
1171
|
+
};
|
|
1172
|
+
});
|
|
1173
|
+
|
|
1174
|
+
// Narration follows the deck's play/pause state. The play button click
|
|
1175
|
+
// flips `isAutoplay` true → this effect fires → audio starts. The
|
|
1176
|
+
// click itself is the user gesture that unlocks the browser audio
|
|
1177
|
+
// context. Pause/stop turns it back off. Each presenter instance
|
|
1178
|
+
// scopes its own audio, so multiple decks on a page never overlap.
|
|
1179
|
+
$effect(() => {
|
|
1180
|
+
if (!project?.settings?.narrationEnabled) return;
|
|
1181
|
+
if (isAutoplay) {
|
|
1182
|
+
playNarrationForSlide(currentSlideIndex);
|
|
1183
|
+
} else {
|
|
1184
|
+
pauseNarration();
|
|
1185
|
+
}
|
|
1162
1186
|
});
|
|
1163
1187
|
|
|
1164
1188
|
// Watch for prop changes
|