fix(player): keep Next available for the final slide's reveals (#6233)

This commit is contained in:
Akshar Patel
2026-10-25 00:29:08 +00:00
committed by GitHub
parent 31e6ed2aed
commit 680f5d7465
3 changed files with 45 additions and 1 deletions
+3
View File
@@ -63,6 +63,9 @@ Attach the source notes, document, page, or existing deck. Name the audience and
The main story should still make sense when no optional branch is opened.
Use **Next** to step through fragment reveals, including those on the final slide.
The Next button disappears once the final slide's last reveal is shown.
## Review it as a presenter
- Each slide makes one clear point.
@@ -71,7 +71,8 @@ export class SlideshowController {
}
get canNext(): boolean {
// next has a destination: a later slide in this sequence, OR (in a branch) the parent.
const slide = this.currentSlide;
if (slide && this.frame.fragmentIndex + 1 < slide.fragments.length) return true;
const slides = this.slidesOf(this.frame.sequenceId);
return this.frame.slideIndex + 1 < slides.length || this.stack.length > 1;
}
@@ -4,8 +4,10 @@ import { handleRuntimeMessage } from "../runtime-message-handler.js";
import {
childrenMayStillArrive,
dropInvalidSlides,
HyperframesSlideshow,
locateSlideshowParts,
} from "./hyperframes-slideshow.js";
import { SlideshowController } from "./SlideshowController.js";
import { slideshowChannelName } from "./slideshowPresenter.js";
// Dynamic import defers custom-element registration until happy-dom is active.
@@ -131,6 +133,44 @@ describe("<hyperframes-slideshow>", () => {
el.remove();
});
it("keeps Next available until the final slide's fragments are revealed", () => {
const player = {
currentTime: 0,
seek: (time: number) => {
player.currentTime = time;
},
play: vi.fn(),
pause: vi.fn(),
onTimeUpdate: () => () => {},
};
const controller = new SlideshowController(player, {
slides: [
{ sceneId: "intro", start: 0, end: 5, fragments: [], hotspots: [] },
{ sceneId: "closing", start: 5, end: 10, fragments: [6, 8, 9], hotspots: [] },
],
sequences: {},
});
const el = new HyperframesSlideshow();
document.body.appendChild(el);
el.__setControllerForTest(controller);
try {
controller.goToSlide(1);
expect(player.currentTime).toBe(6);
expect(el.querySelector("[data-hf-next]")).not.toBeNull();
el.querySelector<HTMLButtonElement>("[data-hf-next]")?.click();
expect(player.currentTime).toBe(8);
expect(el.querySelector("[data-hf-next]")).not.toBeNull();
el.querySelector<HTMLButtonElement>("[data-hf-next]")?.click();
expect(player.currentTime).toBe(9);
expect(el.querySelector("[data-hf-next]")).toBeNull();
} finally {
el.remove();
}
});
it("renders an icon-only present button inside the nav cluster in normal mode", () => {
const el = makeEl({ index: 1, total: 3 });
const cluster = el.querySelector("[data-hf-nav-cluster]");