diff --git a/packages/runtime/actor/machine.ts b/packages/runtime/actor/machine.ts index b64c95e..32797a7 100644 --- a/packages/runtime/actor/machine.ts +++ b/packages/runtime/actor/machine.ts @@ -26,7 +26,7 @@ export const presentation = machineBase.createMachine({ }, }, independent: { - entry: ["scrollToSlide"], + entry: ["jumpToSlide"], on: { "navigate.next": { actions: [ diff --git a/packages/runtime/actor/setup.ts b/packages/runtime/actor/setup.ts index 7779146..a1967c0 100644 --- a/packages/runtime/actor/setup.ts +++ b/packages/runtime/actor/setup.ts @@ -26,12 +26,28 @@ export const machineBase = setup({ } }), /** - * Scroll the audience-mode viewport to the current slide. No-op in - * presenter mode (no scrolling there) and on empty decks. Used both - * as the entry action for initial state and as a side effect of - * remote-driven navigation events broadcast from the presenter tab. + * Smooth-scroll the audience-mode viewport to the current slide. + * Used as a side effect of navigation events (toolbar buttons, + * keyboard, broadcast from the presenter tab) so the audience + * sees a clean animation between slides. No-op in presenter mode + * (no scrolling there) and on empty decks. */ scrollToSlide({ context }) { + if (context.role !== "audience") return; + if (context.slides.length === 0) return; + const slide = context.slides[context.currentIndex]; + if (!slide) return; + document.querySelector(`#${slide.id}`)?.scrollIntoView({ + behavior: "smooth", + }); + }, + + /** + * Instant-jump variant used as the initial state entry action so the + * page loads positioned at the URL-hash slide without animating + * through every slide above it. + */ + jumpToSlide({ context }) { if (context.role !== "audience") return; if (context.slides.length === 0) return; const slide = context.slides[context.currentIndex];