From 78d7f54f0762f787a8cefe5b7d9a7341a026c971 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 20 May 2026 05:55:34 +0000 Subject: [PATCH 1/7] feat(core): add SlideTransition API for page-transition animations Lets slide authors declare per-page enter/exit animations as WAAPI keyframes, with an optional module-level default. The framework owns the transition lifecycle (two-layer stack, direction-aware --osd-dir, interrupt cancellation, prefers-reduced-motion fallback) so authors can focus on the visual. Internal SlideTransitionLayer drives both the Player and the editor viewer. --- .changeset/slide-transition-api.md | 5 + packages/core/src/app/components/player.tsx | 21 ++- .../app/components/slide-transition-layer.tsx | 149 ++++++++++++++++++ packages/core/src/app/lib/sdk.ts | 4 +- packages/core/src/app/lib/transition.ts | 22 +++ .../src/app/lib/use-prefers-reduced-motion.ts | 19 +++ packages/core/src/app/routes/slide.tsx | 16 +- packages/core/src/index.ts | 1 + 8 files changed, 223 insertions(+), 14 deletions(-) create mode 100644 .changeset/slide-transition-api.md create mode 100644 packages/core/src/app/components/slide-transition-layer.tsx create mode 100644 packages/core/src/app/lib/transition.ts create mode 100644 packages/core/src/app/lib/use-prefers-reduced-motion.ts diff --git a/.changeset/slide-transition-api.md b/.changeset/slide-transition-api.md new file mode 100644 index 00000000..255dd5ff --- /dev/null +++ b/.changeset/slide-transition-api.md @@ -0,0 +1,5 @@ +--- +'@open-slide/core': minor +--- + +Add SlideTransition API for declaring per-page page-transition animations. diff --git a/packages/core/src/app/components/player.tsx b/packages/core/src/app/components/player.tsx index 5a894b60..ea70abc8 100644 --- a/packages/core/src/app/components/player.tsx +++ b/packages/core/src/app/components/player.tsx @@ -2,8 +2,9 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useWheelPageNavigation } from '@/lib/use-wheel-page-navigation'; import { cn } from '@/lib/utils'; import type { DesignSystem } from '../lib/design'; -import { SlidePageProvider } from '../lib/page-context'; import type { Page } from '../lib/sdk'; +import type { SlideTransition } from '../lib/transition'; +import { usePrefersReducedMotion } from '../lib/use-prefers-reduced-motion'; import { PresentBlackoutOverlay } from './present/blackout-overlay'; import { PresentControlBar } from './present/control-bar'; import { PresentHelpOverlay } from './present/help-overlay'; @@ -20,6 +21,7 @@ import { } from './present/use-presenter-channel'; import { useTouchSwipe } from './present/use-touch-swipe'; import { SlideCanvas } from './slide-canvas'; +import { SlideTransitionLayer } from './slide-transition-layer'; const IDLE_HIDE_MS = 2000; const BAR_HOTZONE_PX = 160; @@ -27,6 +29,7 @@ const BAR_HOTZONE_PX = 160; type Props = { pages: Page[]; design?: DesignSystem; + transition?: SlideTransition; index: number; onIndexChange: (index: number) => void; onExit: () => void; @@ -44,6 +47,7 @@ type Props = { export function Player({ pages, design, + transition, index, onIndexChange, onExit, @@ -52,6 +56,7 @@ export function Player({ slideId, fullscreen = true, }: Props) { + const prefersReducedMotion = usePrefersReducedMotion(); const rootRef = useRef(null); // Mirrored as state so descendants portaling *into* the player subtree // (tooltips, popovers — the body is outside the fullscreen tree) re-render @@ -284,8 +289,6 @@ export function Player({ const hideCursor = controls && (laser || keyboardDriven || (idle && !overlayActive && !pointerNearBottom)); - const PageComp = pages[index]; - return (
- {PageComp ? ( - - - - ) : null} +