promptslide 0.3.14 → 0.3.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/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.16](https://github.com/prompticeu/promptslide/compare/promptslide-v0.3.15...promptslide-v0.3.16) (2026-08-28)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **cli:** guard deck sync against stale slides ([#103](https://github.com/prompticeu/promptslide/issues/103)) ([f8dddc9](https://github.com/prompticeu/promptslide/commit/f8dddc9fe4177c37dbae729d1efc8971aa46ed92))
9
+
10
+ ## [0.3.15](https://github.com/prompticeu/promptslide/compare/promptslide-v0.3.14...promptslide-v0.3.15) (2026-04-09)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * normalize paths to forward slashes for Windows compatibility ([#99](https://github.com/prompticeu/promptslide/issues/99)) ([4148895](https://github.com/prompticeu/promptslide/commit/414889594e7839172a19c547e1cbb04127bfe55e))
16
+ * remove slide title from navigation controls ([#97](https://github.com/prompticeu/promptslide/issues/97)) ([2e2c011](https://github.com/prompticeu/promptslide/commit/2e2c0115a8488c9bc6c903971998211283148692))
17
+
3
18
  ## [0.3.14](https://github.com/prompticeu/promptslide/compare/promptslide-v0.3.13...promptslide-v0.3.14) (2026-04-01)
4
19
 
5
20
 
package/README.md CHANGED
@@ -7,10 +7,10 @@ PromptSlide is a local-first slide framework built with React, Tailwind CSS, and
7
7
  ## Quick Start
8
8
 
9
9
  ```bash
10
- bun create slides my-deck
10
+ npm create slides my-deck
11
11
  cd my-deck
12
- bun install
13
- bun run dev
12
+ npm install
13
+ npm run dev
14
14
  ```
15
15
 
16
16
  Then open your coding agent and say:
@@ -19,7 +19,7 @@ Then open your coding agent and say:
19
19
 
20
20
  ## CLI Commands
21
21
 
22
- All commands are run via `npx promptslide <command>` (or `bunx promptslide <command>`).
22
+ All commands are run via `npx promptslide <command>`.
23
23
 
24
24
  | Command | Description |
25
25
  | --------- | ---------------------------------------- |
package/dist/index.js CHANGED
@@ -1530,14 +1530,11 @@ function SlideDeck({ slides, transition, directionalTransition, annotations, onA
1530
1530
  children: /* @__PURE__ */ jsx12(ChevronLeft, { className: "h-5 w-5" })
1531
1531
  }
1532
1532
  ),
1533
- /* @__PURE__ */ jsxs6("div", { className: "flex min-w-[4rem] flex-col items-center", children: [
1534
- /* @__PURE__ */ jsxs6("span", { className: "font-mono text-sm text-neutral-500", children: [
1535
- currentSlide + 1,
1536
- " / ",
1537
- slides.length
1538
- ] }),
1539
- slides[currentSlide]?.title && /* @__PURE__ */ jsx12("span", { className: "mt-0.5 text-xs text-neutral-600", children: slides[currentSlide].title })
1540
- ] }),
1533
+ /* @__PURE__ */ jsx12("div", { className: "flex min-w-[4rem] flex-col items-center", children: /* @__PURE__ */ jsxs6("span", { className: "font-mono text-sm text-neutral-500", children: [
1534
+ currentSlide + 1,
1535
+ " / ",
1536
+ slides.length
1537
+ ] }) }),
1541
1538
  /* @__PURE__ */ jsx12(
1542
1539
  "button",
1543
1540
  {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/animation-config.ts","../src/core/animation-context.tsx","../src/core/animated.tsx","../src/core/transitions.ts","../src/core/morph.tsx","../src/core/use-slide-navigation.ts","../src/core/theme-context.tsx","../src/core/slide-renderer.tsx","../src/core/slide-error-boundary.tsx","../src/core/slide-embed.tsx","../src/core/slide-deck.tsx","../src/core/annotations/annotation-overlay.tsx","../src/core/annotations/annotation-form.tsx","../src/core/annotations/annotation-pin.tsx","../src/core/annotations/selectors.ts","../src/core/annotations/annotation-panel.tsx","../src/core/annotations/use-annotations.ts","../src/core/annotations/adapters/http.ts","../src/core/utils.ts"],"sourcesContent":["/**\n * Centralized animation configuration for the slide deck framework.\n * All animation timings, easings, and spring configs are defined here.\n */\n\n// =============================================================================\n// TIMING CONSTANTS\n// =============================================================================\n\n/** Duration for slide-to-slide transitions (seconds) */\nexport const SLIDE_TRANSITION_DURATION = 0.3\n\n/** Duration for morph/layout animations between slides (seconds) */\nexport const MORPH_DURATION = 0.8\n\n/** Duration for within-slide step animations (seconds) */\nexport const STEP_ANIMATION_DURATION = 0.4\n\n/** Default stagger delay for grouped animations (seconds) */\nexport const STAGGER_DELAY = 0.1\n\n// =============================================================================\n// EASING PRESETS\n// =============================================================================\n\nexport const EASE_DEFAULT = \"easeInOut\" as const\nexport const EASE_OUT = \"easeOut\" as const\nexport const EASE_IN = \"easeIn\" as const\n\n/** Smooth ease-in-out curve for morph animations (cubic bezier) */\nexport const EASE_MORPH = [0.4, 0, 0.2, 1] as const\n\n// =============================================================================\n// SPRING CONFIGURATIONS\n// =============================================================================\n\n/** Spring config for snappy, responsive animations */\nexport const SPRING_SNAPPY = {\n type: \"spring\" as const,\n stiffness: 300,\n damping: 30\n}\n\n/** Spring config for smooth, gentle animations */\nexport const SPRING_SMOOTH = {\n type: \"spring\" as const,\n stiffness: 200,\n damping: 25\n}\n\n/** Spring config for bouncy animations */\nexport const SPRING_BOUNCY = {\n type: \"spring\" as const,\n stiffness: 400,\n damping: 20\n}\n\n// =============================================================================\n// TRANSITION PRESETS\n// =============================================================================\n\n/** Standard transition for slide transitions */\nexport const SLIDE_TRANSITION = {\n duration: SLIDE_TRANSITION_DURATION,\n ease: EASE_DEFAULT\n} as const\n\n/** Standard transition for morph animations (smooth ease-in-out) */\nexport const MORPH_TRANSITION = {\n duration: MORPH_DURATION,\n ease: EASE_MORPH\n}\n\n/** Standard transition for step animations (spring-based, duration computed from stiffness/damping) */\nexport const STEP_TRANSITION = {\n ...SPRING_SNAPPY\n}\n\n// =============================================================================\n// DISTANCE CONSTANTS (pixels)\n// =============================================================================\n\n/** Distance for slide animations */\nexport const SLIDE_DISTANCE = 100\n\n/** Distance for within-slide element animations */\nexport const ELEMENT_SLIDE_DISTANCE = 30\n\n// =============================================================================\n// SLIDE DIMENSIONS\n// =============================================================================\n\n/** Standard slide dimensions (16:9 aspect ratio) */\nexport const SLIDE_DIMENSIONS = {\n width: 1280,\n height: 720,\n aspectRatio: 16 / 9\n} as const\n","import { createContext, useContext, useMemo } from \"react\"\n\ninterface AnimationContextValue {\n /** Current animation step (0 = no animations shown yet) */\n currentStep: number\n /** Total animation steps in this slide (declared in slide config) */\n totalSteps: number\n /** When true, all animation steps should be visible (used for backward navigation) */\n showAllAnimations: boolean\n}\n\nconst AnimationContext = createContext<AnimationContextValue>({\n currentStep: 0,\n totalSteps: 0,\n showAllAnimations: false\n})\n\ninterface AnimationProviderProps {\n children: React.ReactNode\n /** Current animation step (0 = no animations shown yet) */\n currentStep: number\n /** Total animation steps declared in slide config */\n totalSteps: number\n /** When true, show all animations regardless of currentStep (for backward navigation) */\n showAllAnimations?: boolean\n}\n\n/**\n * Provides animation context to child components.\n *\n * The totalSteps is now passed from parent (declared in slide config)\n * rather than discovered at runtime, eliminating race conditions.\n */\nexport function AnimationProvider({\n children,\n currentStep,\n totalSteps,\n showAllAnimations = false\n}: AnimationProviderProps) {\n const value = useMemo(\n () => ({\n currentStep,\n totalSteps,\n showAllAnimations\n }),\n [currentStep, totalSteps, showAllAnimations]\n )\n\n return <AnimationContext.Provider value={value}>{children}</AnimationContext.Provider>\n}\n\nexport function useAnimationContext() {\n return useContext(AnimationContext)\n}\n","import { motion, Variants } from \"framer-motion\"\n\nimport {\n ELEMENT_SLIDE_DISTANCE,\n SPRING_SNAPPY,\n STAGGER_DELAY,\n STEP_ANIMATION_DURATION\n} from \"./animation-config\"\nimport { useAnimationContext } from \"./animation-context\"\n\n// =============================================================================\n// ANIMATION TYPES & VARIANTS\n// =============================================================================\n\nexport type AnimationType =\n | \"fade\"\n | \"slide-up\"\n | \"slide-down\"\n | \"slide-left\"\n | \"slide-right\"\n | \"scale\"\n\nconst animationVariants: Record<AnimationType, Variants> = {\n fade: {\n hidden: { opacity: 0 },\n visible: { opacity: 1 }\n },\n \"slide-up\": {\n hidden: { opacity: 0, y: ELEMENT_SLIDE_DISTANCE },\n visible: { opacity: 1, y: 0 }\n },\n \"slide-down\": {\n hidden: { opacity: 0, y: -ELEMENT_SLIDE_DISTANCE },\n visible: { opacity: 1, y: 0 }\n },\n \"slide-left\": {\n hidden: { opacity: 0, x: ELEMENT_SLIDE_DISTANCE },\n visible: { opacity: 1, x: 0 }\n },\n \"slide-right\": {\n hidden: { opacity: 0, x: -ELEMENT_SLIDE_DISTANCE },\n visible: { opacity: 1, x: 0 }\n },\n scale: {\n hidden: { opacity: 0, scale: 0.8 },\n visible: { opacity: 1, scale: 1 }\n }\n}\n\ninterface AnimatedProps {\n /** Which step reveals this content (1-indexed) */\n step: number\n /** Animation type */\n animation?: AnimationType\n /** Animation duration in seconds */\n duration?: number\n /** Delay after trigger in seconds */\n delay?: number\n /** Custom className */\n className?: string\n children: React.ReactNode\n}\n\n/**\n * Animated element that appears at a specific animation step.\n *\n * The step count is now declared in slide config rather than discovered\n * at runtime, so this component simply consumes the context without\n * needing to register itself.\n */\nexport function Animated({\n step,\n animation = \"slide-up\",\n duration = STEP_ANIMATION_DURATION,\n delay = 0,\n className,\n children\n}: AnimatedProps) {\n const { currentStep, showAllAnimations } = useAnimationContext()\n\n // Show all animations when navigating backward, otherwise check step\n const isVisible = showAllAnimations || currentStep >= step\n\n return (\n <motion.div\n initial=\"hidden\"\n animate={isVisible ? \"visible\" : \"hidden\"}\n variants={animationVariants[animation]}\n transition={{\n ...SPRING_SNAPPY,\n duration,\n delay: isVisible ? delay : 0\n }}\n className={className}\n >\n {children}\n </motion.div>\n )\n}\n\n/**\n * Wrapper for staggering multiple children.\n * Each direct child will be animated in sequence.\n */\ninterface AnimatedGroupProps {\n /** Starting step for the first child */\n startStep: number\n /** Animation type for all children */\n animation?: AnimationType\n /** Delay between each child in seconds */\n staggerDelay?: number\n className?: string\n children: React.ReactNode\n}\n\nexport function AnimatedGroup({\n startStep,\n animation = \"slide-up\",\n staggerDelay = STAGGER_DELAY,\n className,\n children\n}: AnimatedGroupProps) {\n const { currentStep, showAllAnimations } = useAnimationContext()\n\n const childArray = Array.isArray(children) ? children : [children]\n\n // Show all animations when navigating backward, otherwise check step\n const isVisible = showAllAnimations || currentStep >= startStep\n\n const containerVariants: Variants = {\n hidden: {},\n visible: {\n transition: {\n staggerChildren: staggerDelay\n }\n }\n }\n\n return (\n <motion.div\n initial=\"hidden\"\n animate={isVisible ? \"visible\" : \"hidden\"}\n variants={containerVariants}\n className={className}\n >\n {childArray.map((child, index) => (\n <motion.div key={index} variants={animationVariants[animation]}>\n {child}\n </motion.div>\n ))}\n </motion.div>\n )\n}\n","/**\n * Slide transition variants for the slide deck framework.\n * These define how slides enter and exit during navigation.\n */\n\nimport type { Variants } from \"framer-motion\"\n\nimport {\n EASE_MORPH,\n MORPH_DURATION,\n SLIDE_DISTANCE,\n SLIDE_TRANSITION,\n SLIDE_TRANSITION_DURATION\n} from \"./animation-config\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport type SlideTransitionType =\n | \"fade\"\n | \"slide-left\"\n | \"slide-right\"\n | \"slide-up\"\n | \"slide-down\"\n | \"zoom\"\n | \"zoom-fade\"\n | \"morph\"\n | \"none\"\n\nexport interface SlideTransitionConfig {\n /** The transition type */\n type: SlideTransitionType\n /** Optional custom duration (overrides default) */\n duration?: number\n /** Whether to use directional transitions based on navigation direction */\n directional?: boolean\n}\n\n// =============================================================================\n// STATIC VARIANTS (non-directional)\n// =============================================================================\n\nconst fadeVariants: Variants = {\n enter: { opacity: 0 },\n center: { opacity: 1 },\n exit: { opacity: 0 }\n}\n\nconst slideLeftVariants: Variants = {\n enter: { x: SLIDE_DISTANCE, opacity: 0 },\n center: { x: 0, opacity: 1 },\n exit: { x: -SLIDE_DISTANCE, opacity: 0 }\n}\n\nconst slideRightVariants: Variants = {\n enter: { x: -SLIDE_DISTANCE, opacity: 0 },\n center: { x: 0, opacity: 1 },\n exit: { x: SLIDE_DISTANCE, opacity: 0 }\n}\n\nconst slideUpVariants: Variants = {\n enter: { y: SLIDE_DISTANCE, opacity: 0 },\n center: { y: 0, opacity: 1 },\n exit: { y: -SLIDE_DISTANCE, opacity: 0 }\n}\n\nconst slideDownVariants: Variants = {\n enter: { y: -SLIDE_DISTANCE, opacity: 0 },\n center: { y: 0, opacity: 1 },\n exit: { y: SLIDE_DISTANCE, opacity: 0 }\n}\n\nconst zoomVariants: Variants = {\n enter: { scale: 0.8, opacity: 0 },\n center: { scale: 1, opacity: 1 },\n exit: { scale: 1.2, opacity: 0 }\n}\n\nconst zoomFadeVariants: Variants = {\n enter: { scale: 0.95, opacity: 0 },\n center: { scale: 1, opacity: 1 },\n exit: { scale: 1.05, opacity: 0 }\n}\n\nconst morphVariants: Variants = {\n enter: {\n opacity: 0,\n zIndex: 1\n },\n center: {\n opacity: 1,\n zIndex: 1,\n transition: {\n opacity: { delay: 0.05, duration: 0.25 }\n }\n },\n exit: {\n opacity: 0,\n zIndex: 0,\n transition: {\n opacity: { duration: MORPH_DURATION, ease: EASE_MORPH }\n }\n }\n}\n\nconst noneVariants: Variants = {\n enter: {},\n center: {},\n exit: {}\n}\n\n// =============================================================================\n// VARIANT REGISTRY\n// =============================================================================\n\nexport const SLIDE_VARIANTS: Record<SlideTransitionType, Variants> = {\n fade: fadeVariants,\n \"slide-left\": slideLeftVariants,\n \"slide-right\": slideRightVariants,\n \"slide-up\": slideUpVariants,\n \"slide-down\": slideDownVariants,\n zoom: zoomVariants,\n \"zoom-fade\": zoomFadeVariants,\n morph: morphVariants,\n none: noneVariants\n}\n\n// =============================================================================\n// DIRECTIONAL VARIANTS (based on navigation direction)\n// =============================================================================\n\nexport function createDirectionalVariants(axis: \"x\" | \"y\" = \"x\"): (direction: number) => Variants {\n return (direction: number) => ({\n enter: {\n [axis]: direction > 0 ? SLIDE_DISTANCE : -SLIDE_DISTANCE,\n opacity: 0\n },\n center: {\n [axis]: 0,\n opacity: 1\n },\n exit: {\n [axis]: direction < 0 ? SLIDE_DISTANCE : -SLIDE_DISTANCE,\n opacity: 0\n }\n })\n}\n\n/** Horizontal directional slide (left/right based on direction) */\nexport const directionalSlideX = createDirectionalVariants(\"x\")\n\n/** Vertical directional slide (up/down based on direction) */\nexport const directionalSlideY = createDirectionalVariants(\"y\")\n\n// =============================================================================\n// HELPER FUNCTIONS\n// =============================================================================\n\nexport function getSlideVariants(\n config: SlideTransitionConfig | SlideTransitionType,\n direction: number = 1\n): Variants {\n const type = typeof config === \"string\" ? config : config.type\n const directional = typeof config === \"object\" ? config.directional : false\n\n if (directional && (type === \"slide-left\" || type === \"slide-right\")) {\n return directionalSlideX(direction)\n }\n\n if (directional && (type === \"slide-up\" || type === \"slide-down\")) {\n return directionalSlideY(direction)\n }\n\n return SLIDE_VARIANTS[type]\n}\n\nexport function getSlideTransition(config?: SlideTransitionConfig | SlideTransitionType): {\n duration: number\n ease: typeof SLIDE_TRANSITION.ease\n} {\n if (!config) return SLIDE_TRANSITION\n\n const type = typeof config === \"string\" ? config : config.type\n\n const defaultDuration = type === \"morph\" ? MORPH_DURATION : SLIDE_TRANSITION_DURATION\n\n const duration = typeof config === \"object\" && config.duration ? config.duration : defaultDuration\n\n return {\n ...SLIDE_TRANSITION,\n duration\n }\n}\n\n// =============================================================================\n// DEFAULT EXPORT\n// =============================================================================\n\nexport const DEFAULT_SLIDE_TRANSITION: SlideTransitionType = \"fade\"\n","import type { Transition } from \"framer-motion\"\n\nimport { motion } from \"framer-motion\"\n\nimport { MORPH_TRANSITION } from \"./animation-config\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\ninterface MorphProps {\n layoutId: string\n transition?: Transition\n className?: string\n children: React.ReactNode\n}\n\ninterface MorphGroupProps {\n groupId: string\n transition?: Transition\n className?: string\n children: React.ReactNode\n}\n\n// =============================================================================\n// MORPH COMPONENT\n// =============================================================================\n\n/**\n * Wrapper component that enables morph/shared-layout animations between slides.\n *\n * Usage:\n * ```tsx\n * // Slide 1 - Large version\n * <Morph layoutId=\"hero-title\">\n * <h1 className=\"text-6xl\">Title</h1>\n * </Morph>\n *\n * // Slide 2 - Small version (same layoutId = morphs between them)\n * <Morph layoutId=\"hero-title\">\n * <h1 className=\"text-2xl\">Title</h1>\n * </Morph>\n * ```\n */\nexport function Morph({\n layoutId,\n transition = MORPH_TRANSITION,\n className,\n children\n}: MorphProps) {\n return (\n <motion.div\n layout\n layoutId={layoutId}\n initial={false}\n transition={transition}\n className={className}\n >\n {children}\n </motion.div>\n )\n}\n\n// =============================================================================\n// MORPH GROUP COMPONENT\n// =============================================================================\n\nexport function MorphGroup({\n groupId,\n transition = MORPH_TRANSITION,\n className,\n children\n}: MorphGroupProps) {\n return (\n <motion.div\n layout\n layoutId={groupId}\n initial={false}\n transition={transition}\n className={className}\n >\n {children}\n </motion.div>\n )\n}\n\n// =============================================================================\n// MORPH ITEM (for use within MorphGroup pattern)\n// =============================================================================\n\ninterface MorphItemProps {\n id: string\n prefix?: string\n transition?: Transition\n className?: string\n children: React.ReactNode\n}\n\nexport function MorphItem({\n id,\n prefix,\n transition = MORPH_TRANSITION,\n className,\n children\n}: MorphItemProps) {\n const layoutId = prefix ? `${prefix}-${id}` : id\n\n return (\n <motion.div\n layout\n layoutId={layoutId}\n initial={false}\n transition={transition}\n className={className}\n >\n {children}\n </motion.div>\n )\n}\n\n// =============================================================================\n// MORPH TEXT (specialized for text that changes size)\n// =============================================================================\n\ninterface MorphTextProps {\n layoutId: string\n as?: \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"p\" | \"span\"\n transition?: Transition\n className?: string\n children: React.ReactNode\n}\n\nexport function MorphText({\n layoutId,\n as: Component = \"span\",\n transition = MORPH_TRANSITION,\n className,\n children\n}: MorphTextProps) {\n const MotionComponent = motion[Component]\n\n return (\n <MotionComponent\n layout\n layoutId={layoutId}\n initial={false}\n transition={transition}\n className={className}\n >\n {children}\n </MotionComponent>\n )\n}\n","import { useCallback, useEffect, useState } from \"react\"\n\nimport type { NavigationDirection, SlideConfig } from \"./types\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\ntype NavigationStatus = \"idle\" | \"transitioning\"\n\ntype QueuedAction = \"advance\" | \"goBack\" | null\n\ninterface NavigationState {\n status: NavigationStatus\n direction: NavigationDirection\n}\n\nexport interface UseSlideNavigationOptions {\n slides: SlideConfig[]\n initialSlide?: number\n onSlideChange?: (slideIndex: number) => void\n}\n\nexport interface UseSlideNavigationReturn {\n currentSlide: number\n animationStep: number\n totalSteps: number\n direction: NavigationDirection\n isTransitioning: boolean\n showAllAnimations: boolean\n advance: () => void\n goBack: () => void\n goToSlide: (index: number) => void\n onTransitionComplete: () => void\n}\n\n// =============================================================================\n// HOOK\n// =============================================================================\n\nexport function useSlideNavigation({\n slides,\n initialSlide = 0,\n onSlideChange\n}: UseSlideNavigationOptions): UseSlideNavigationReturn {\n const [currentSlide, setCurrentSlide] = useState(initialSlide)\n const [animationStep, setAnimationStep] = useState(0)\n\n const [navState, setNavState] = useState<NavigationState>({\n status: \"idle\",\n direction: 0\n })\n\n const [queuedAction, setQueuedAction] = useState<QueuedAction>(null)\n\n const totalSteps = slides[currentSlide]?.steps ?? 0\n\n const onTransitionComplete = useCallback(() => {\n setNavState(prev => {\n if (prev.status === \"transitioning\") {\n return { status: \"idle\", direction: 0 }\n }\n return prev\n })\n }, [])\n\n const advance = useCallback(() => {\n if (navState.status === \"transitioning\") {\n setQueuedAction(\"advance\")\n return\n }\n\n const currentTotalSteps = slides[currentSlide]?.steps ?? 0\n\n if (animationStep >= currentTotalSteps) {\n const nextSlide = (currentSlide + 1) % slides.length\n setNavState({ status: \"transitioning\", direction: 1 })\n setAnimationStep(0)\n setCurrentSlide(nextSlide)\n onSlideChange?.(nextSlide)\n } else {\n setAnimationStep(prev => prev + 1)\n }\n }, [navState.status, animationStep, currentSlide, slides, onSlideChange])\n\n const goBack = useCallback(() => {\n if (navState.status === \"transitioning\") {\n setQueuedAction(\"goBack\")\n return\n }\n\n if (animationStep <= 0) {\n const prevSlide = (currentSlide - 1 + slides.length) % slides.length\n const prevSlideSteps = slides[prevSlide]?.steps ?? 0\n setNavState({ status: \"transitioning\", direction: -1 })\n setAnimationStep(prevSlideSteps)\n setCurrentSlide(prevSlide)\n onSlideChange?.(prevSlide)\n } else {\n setAnimationStep(prev => prev - 1)\n }\n }, [navState.status, animationStep, currentSlide, slides, onSlideChange])\n\n const goToSlide = useCallback(\n (index: number) => {\n if (index < 0 || index >= slides.length || index === currentSlide) {\n return\n }\n\n const direction = index > currentSlide ? 1 : -1\n setNavState({ status: \"transitioning\", direction })\n setAnimationStep(0)\n setCurrentSlide(index)\n onSlideChange?.(index)\n },\n [currentSlide, slides.length, onSlideChange]\n )\n\n useEffect(() => {\n if (navState.status === \"idle\" && queuedAction !== null) {\n setQueuedAction(null)\n if (queuedAction === \"advance\") {\n advance()\n } else if (queuedAction === \"goBack\") {\n goBack()\n }\n }\n }, [navState.status, queuedAction, advance, goBack])\n\n return {\n currentSlide,\n animationStep,\n totalSteps,\n direction: navState.direction,\n isTransitioning: navState.status !== \"idle\",\n showAllAnimations: navState.direction === -1 && navState.status === \"transitioning\",\n advance,\n goBack,\n goToSlide,\n onTransitionComplete\n }\n}\n","import { createContext, useContext, useMemo } from \"react\"\nimport type { ThemeConfig } from \"./types\"\n\nconst ThemeContext = createContext<ThemeConfig | null>(null)\n\n/**\n * Access the full theme config. Returns null outside SlideThemeProvider.\n */\nexport function useTheme(): ThemeConfig | null {\n return useContext(ThemeContext)\n}\n\nfunction buildCssOverrides(theme: ThemeConfig): React.CSSProperties {\n const style: Record<string, string> = {}\n\n if (theme.colors?.primary) style[\"--primary\"] = theme.colors.primary\n if (theme.colors?.primaryForeground) style[\"--primary-foreground\"] = theme.colors.primaryForeground\n if (theme.colors?.secondary) style[\"--secondary\"] = theme.colors.secondary\n if (theme.colors?.secondaryForeground) style[\"--secondary-foreground\"] = theme.colors.secondaryForeground\n if (theme.colors?.accent) style[\"--accent\"] = theme.colors.accent\n if (theme.colors?.accentForeground) style[\"--accent-foreground\"] = theme.colors.accentForeground\n\n if (theme.fonts?.heading) style[\"--font-heading\"] = theme.fonts.heading\n if (theme.fonts?.body) style[\"--font-body\"] = theme.fonts.body\n\n return style as React.CSSProperties\n}\n\ninterface SlideThemeProviderProps {\n theme: ThemeConfig\n children: React.ReactNode\n}\n\n/**\n * Provides the full theme to all descendants.\n * Injects CSS variable overrides via inline style.\n */\nexport function SlideThemeProvider({ theme, children }: SlideThemeProviderProps) {\n const cssOverrides = useMemo(() => buildCssOverrides(theme), [theme])\n\n return (\n <ThemeContext.Provider value={theme}>\n <div style={cssOverrides} className=\"contents\">\n {children}\n </div>\n </ThemeContext.Provider>\n )\n}\n","import { AnimatePresence, motion } from \"framer-motion\"\n\nimport type { SlideTransitionType } from \"./transitions\"\nimport type { NavigationDirection, SlideConfig } from \"./types\"\n\nimport { SLIDE_TRANSITION } from \"./animation-config\"\nimport { AnimationProvider } from \"./animation-context\"\nimport { SlideErrorBoundary } from \"./slide-error-boundary\"\nimport { DEFAULT_SLIDE_TRANSITION, getSlideVariants } from \"./transitions\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport interface SlideRendererProps {\n slides: SlideConfig[]\n currentSlide: number\n animationStep: number\n totalSteps: number\n direction: NavigationDirection\n showAllAnimations: boolean\n transition?: SlideTransitionType\n directionalTransition?: boolean\n onTransitionComplete: () => void\n}\n\n// =============================================================================\n// COMPONENT\n// =============================================================================\n\n/**\n * Renders a single slide with animated transitions.\n * Extracted from SlideDeck so it can be reused in SlideEmbed and other contexts.\n */\nexport function SlideRenderer({\n slides,\n currentSlide,\n animationStep,\n totalSteps,\n direction,\n showAllAnimations,\n transition,\n directionalTransition,\n onTransitionComplete\n}: SlideRendererProps) {\n const currentSlideTransition = slides[currentSlide]?.transition\n const transitionType = currentSlideTransition ?? transition ?? DEFAULT_SLIDE_TRANSITION\n const isDirectional = directionalTransition ?? false\n\n const slideVariants = getSlideVariants(\n { type: transitionType, directional: isDirectional },\n direction\n )\n\n const CurrentSlideComponent = slides[currentSlide]!.component\n\n return (\n <AnimatePresence initial={false}>\n <motion.div\n key={currentSlide}\n variants={slideVariants}\n initial=\"enter\"\n animate=\"center\"\n exit=\"exit\"\n transition={SLIDE_TRANSITION}\n onAnimationComplete={definition => {\n if (definition === \"center\") {\n onTransitionComplete()\n }\n }}\n className=\"absolute inset-0 h-full w-full\"\n >\n <AnimationProvider\n currentStep={animationStep}\n totalSteps={totalSteps}\n showAllAnimations={showAllAnimations}\n >\n <SlideErrorBoundary\n slideIndex={currentSlide}\n slideTitle={slides[currentSlide]?.title}\n >\n <CurrentSlideComponent\n slideNumber={currentSlide + 1}\n totalSlides={slides.length}\n />\n </SlideErrorBoundary>\n </AnimationProvider>\n </motion.div>\n </AnimatePresence>\n )\n}\n","import React from \"react\"\n\ninterface SlideErrorBoundaryProps {\n slideIndex: number\n slideTitle?: string\n children: React.ReactNode\n}\n\ninterface SlideErrorBoundaryState {\n hasError: boolean\n error: Error | null\n}\n\nexport class SlideErrorBoundary extends React.Component<\n SlideErrorBoundaryProps,\n SlideErrorBoundaryState\n> {\n state: SlideErrorBoundaryState = { hasError: false, error: null }\n\n static getDerivedStateFromError(error: Error) {\n return { hasError: true, error }\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n console.error(\n `[PromptSlide] Slide ${this.props.slideIndex + 1}${this.props.slideTitle ? ` (\"${this.props.slideTitle}\")` : \"\"} crashed:`,\n error,\n errorInfo\n )\n }\n\n render() {\n if (this.state.hasError) {\n return (\n <div className=\"flex h-full w-full flex-col items-center justify-center bg-red-950/20 p-12\">\n <div className=\"mb-4 text-lg font-semibold text-red-400\">\n Slide {this.props.slideIndex + 1} Error\n {this.props.slideTitle && (\n <span className=\"ml-2 font-normal text-red-400/70\">({this.props.slideTitle})</span>\n )}\n </div>\n <div className=\"max-w-2xl text-center font-mono text-sm break-words text-red-300/80\">\n {this.state.error?.message ?? \"Unknown error\"}\n </div>\n </div>\n )\n }\n return this.props.children\n }\n}\n","import { useCallback, useEffect, useState } from \"react\"\n\nimport type { SlideTransitionType } from \"./transitions\"\nimport type { SlideConfig } from \"./types\"\n\nimport { SLIDE_DIMENSIONS } from \"./animation-config\"\nimport { SlideRenderer } from \"./slide-renderer\"\nimport { useSlideNavigation } from \"./use-slide-navigation\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\ninterface SlideEmbedProps {\n slides: SlideConfig[]\n transition?: SlideTransitionType\n directionalTransition?: boolean\n}\n\n// =============================================================================\n// COMPONENT\n// =============================================================================\n\n/**\n * Headless slide viewer controlled via window.postMessage.\n * Designed for embedding in an iframe (e.g. the registry editor preview).\n *\n * Inbound messages (parent → embed):\n * { type: \"navigate\", data: { slide: number } }\n * { type: \"advance\" }\n * { type: \"goBack\" }\n *\n * Outbound messages (embed → parent):\n * { type: \"slideReady\" }\n * { type: \"slideState\", data: { currentSlide, totalSlides, animationStep, totalSteps, titles } }\n * { type: \"hmrUpdate\" }\n */\nexport function SlideEmbed({ slides, transition, directionalTransition }: SlideEmbedProps) {\n const [scale, setScale] = useState(1)\n\n const {\n currentSlide,\n animationStep,\n totalSteps,\n direction,\n showAllAnimations,\n advance,\n goBack,\n goToSlide,\n onTransitionComplete\n } = useSlideNavigation({ slides })\n\n // Post slide state to parent whenever it changes\n useEffect(() => {\n const state = {\n currentSlide,\n totalSlides: slides.length,\n animationStep,\n totalSteps,\n titles: slides.map(s => s.title || \"\")\n }\n window.parent.postMessage({ type: \"slideState\", data: state }, \"*\")\n }, [currentSlide, animationStep, totalSteps, slides])\n\n // Signal readiness on mount\n useEffect(() => {\n window.parent.postMessage({ type: \"slideReady\" }, \"*\")\n }, [])\n\n // Listen for Vite HMR updates\n useEffect(() => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const hot = (import.meta as any).hot as { on: (event: string, cb: () => void) => void } | undefined\n if (hot) {\n hot.on(\"vite:afterUpdate\", () => {\n window.parent.postMessage({ type: \"hmrUpdate\" }, \"*\")\n })\n }\n }, [])\n\n // Listen for inbound messages from parent\n const handleMessage = useCallback(\n (event: MessageEvent) => {\n const { type, data } = event.data || {}\n switch (type) {\n case \"navigate\":\n if (typeof data?.slide === \"number\") goToSlide(data.slide)\n break\n case \"advance\":\n advance()\n break\n case \"goBack\":\n goBack()\n break\n }\n },\n [advance, goBack, goToSlide]\n )\n\n useEffect(() => {\n window.addEventListener(\"message\", handleMessage)\n return () => window.removeEventListener(\"message\", handleMessage)\n }, [handleMessage])\n\n // Scale to fit viewport while preserving aspect ratio\n useEffect(() => {\n const calculateScale = () => {\n const scaleX = window.innerWidth / SLIDE_DIMENSIONS.width\n const scaleY = window.innerHeight / SLIDE_DIMENSIONS.height\n setScale(Math.min(scaleX, scaleY))\n }\n\n calculateScale()\n window.addEventListener(\"resize\", calculateScale)\n return () => window.removeEventListener(\"resize\", calculateScale)\n }, [])\n\n return (\n <div className=\"h-screen w-screen overflow-hidden bg-black\">\n <div\n className=\"absolute left-1/2 top-1/2 overflow-hidden\"\n style={{\n width: SLIDE_DIMENSIONS.width,\n height: SLIDE_DIMENSIONS.height,\n transform: `translate(-50%, -50%) scale(${scale})`,\n transformOrigin: \"center center\"\n }}\n >\n <SlideRenderer\n slides={slides}\n currentSlide={currentSlide}\n animationStep={animationStep}\n totalSteps={totalSteps}\n direction={direction}\n showAllAnimations={showAllAnimations}\n transition={transition}\n directionalTransition={directionalTransition}\n onTransitionComplete={onTransitionComplete}\n />\n </div>\n </div>\n )\n}\n","import { LayoutGroup } from \"framer-motion\"\nimport { ChevronLeft, ChevronRight, Download, Grid3X3, List, Maximize, MessageCircle, Monitor } from \"lucide-react\"\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\"\n\nimport type { SlideTransitionType } from \"./transitions\"\nimport type { SlideConfig } from \"./types\"\n\nimport { SLIDE_DIMENSIONS } from \"./animation-config\"\nimport { AnimationProvider } from \"./animation-context\"\nimport { AnnotationOverlay, AnnotationPanel, useAnnotations } from \"./annotations\"\nimport type { Annotation, AnnotationTarget } from \"./annotations\"\nimport { SlideErrorBoundary } from \"./slide-error-boundary\"\nimport { SlideRenderer } from \"./slide-renderer\"\nimport { useSlideNavigation } from \"./use-slide-navigation\"\nimport { cn } from \"./utils\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\ntype ViewMode = \"slide\" | \"list\" | \"grid\"\n\ninterface SlideDeckProps {\n slides: SlideConfig[]\n transition?: SlideTransitionType\n directionalTransition?: boolean\n /** Annotation data to display. When provided (even empty array), annotation UI is enabled. When undefined, annotation UI is hidden. */\n annotations?: Annotation[]\n /** Called when the user creates an annotation */\n onAnnotationAdd?: (slideIndex: number, slideTitle: string, target: AnnotationTarget, body: string) => void\n /** Called when the user deletes an annotation */\n onAnnotationDelete?: (id: string) => void\n}\n\n// =============================================================================\n// EXPORT VIEW (for Playwright screenshot capture)\n// =============================================================================\n\nfunction SlideExportView({ slides, slideIndex }: { slides: SlideConfig[]; slideIndex: number }) {\n const [ready, setReady] = useState(false)\n const clampedIndex = Math.max(0, Math.min(slideIndex, slides.length - 1))\n const slideConfig = slides[clampedIndex]!\n const SlideComponent = slideConfig.component\n\n useEffect(() => {\n setReady(true)\n }, [])\n\n return (\n <div\n data-export-ready={ready ? \"true\" : undefined}\n style={{\n width: SLIDE_DIMENSIONS.width,\n height: SLIDE_DIMENSIONS.height,\n overflow: \"hidden\",\n position: \"relative\",\n background: \"black\"\n }}\n >\n <AnimationProvider\n currentStep={slideConfig.steps}\n totalSteps={slideConfig.steps}\n showAllAnimations={true}\n >\n <SlideErrorBoundary slideIndex={clampedIndex} slideTitle={slideConfig.title}>\n <SlideComponent slideNumber={clampedIndex + 1} totalSlides={slides.length} />\n </SlideErrorBoundary>\n </AnimationProvider>\n </div>\n )\n}\n\n// =============================================================================\n// COMPONENT\n// =============================================================================\n\nexport function SlideDeck({ slides, transition, directionalTransition, annotations, onAnnotationAdd, onAnnotationDelete }: SlideDeckProps) {\n // Check for export mode via URL params\n const [exportParams] = useState(() => {\n if (typeof window === \"undefined\") return null\n const params = new URLSearchParams(window.location.search)\n if (params.get(\"export\") !== \"true\") return null\n return { slideIndex: parseInt(params.get(\"slide\") || \"0\", 10) }\n })\n\n if (exportParams) {\n return <SlideExportView slides={slides} slideIndex={exportParams.slideIndex} />\n }\n\n // Use internal useAnnotations as fallback when no external annotations prop is provided\n const internal = useAnnotations()\n const isExternallyManaged = annotations !== undefined\n const effectiveAnnotations = isExternallyManaged ? annotations : internal.annotations\n const effectiveAdd = isExternallyManaged ? onAnnotationAdd : internal.addAnnotation\n const effectiveDelete = isExternallyManaged ? onAnnotationDelete : internal.deleteAnnotation\n\n const openCount = useMemo(() => effectiveAnnotations.filter(a => a.status === \"open\").length, [effectiveAnnotations])\n const getSlideAnnotations = useCallback(\n (slideIndex: number) => effectiveAnnotations.filter(a => a.slideIndex === slideIndex),\n [effectiveAnnotations]\n )\n\n const [viewMode, setViewMode] = useState<ViewMode>(\"slide\")\n const [isPresentationMode, setIsPresentationMode] = useState(false)\n const [isAnnotationMode, setIsAnnotationMode] = useState(false)\n const [showAnnotationPanel, setShowAnnotationPanel] = useState(false)\n const [selectedAnnotationId, setSelectedAnnotationId] = useState<string | null>(null)\n const [scale, setScale] = useState(1)\n const containerRef = useRef<HTMLDivElement>(null)\n const slideContainerRef = useRef<HTMLDivElement>(null)\n\n const {\n currentSlide,\n animationStep,\n totalSteps,\n direction,\n showAllAnimations,\n advance,\n goBack,\n goToSlide,\n onTransitionComplete\n } = useSlideNavigation({\n slides\n })\n\n const togglePresentationMode = useCallback(async () => {\n if (!document.fullscreenElement) {\n await document.documentElement.requestFullscreen()\n } else {\n await document.exitFullscreen()\n }\n }, [])\n\n // Listen for fullscreen changes\n useEffect(() => {\n const handleFullscreenChange = () => {\n setIsPresentationMode(!!document.fullscreenElement)\n }\n document.addEventListener(\"fullscreenchange\", handleFullscreenChange)\n return () => document.removeEventListener(\"fullscreenchange\", handleFullscreenChange)\n }, [])\n\n // Calculate scale factor for presentation mode\n useEffect(() => {\n const calculateScale = () => {\n if (!isPresentationMode) {\n setScale(1)\n return\n }\n const viewportWidth = window.innerWidth\n const viewportHeight = window.innerHeight\n const scaleX = viewportWidth / SLIDE_DIMENSIONS.width\n const scaleY = viewportHeight / SLIDE_DIMENSIONS.height\n setScale(Math.min(scaleX, scaleY))\n }\n\n calculateScale()\n window.addEventListener(\"resize\", calculateScale)\n return () => window.removeEventListener(\"resize\", calculateScale)\n }, [isPresentationMode])\n\n const handleExportPdf = () => {\n const previousMode = viewMode\n setViewMode(\"list\")\n\n setTimeout(() => {\n const handleAfterPrint = () => {\n setViewMode(previousMode)\n window.removeEventListener(\"afterprint\", handleAfterPrint)\n }\n window.addEventListener(\"afterprint\", handleAfterPrint)\n window.print()\n }, 100)\n }\n\n // Keyboard navigation\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"f\" || e.key === \"F\") {\n togglePresentationMode()\n return\n }\n\n // G for grid view toggle\n if (e.key === \"g\" || e.key === \"G\") {\n setViewMode(prev => (prev === \"grid\" ? \"slide\" : \"grid\"))\n return\n }\n\n if (viewMode !== \"slide\" || (isAnnotationMode && !isPresentationMode)) return\n\n if (e.key === \"ArrowRight\" || e.key === \" \") {\n e.preventDefault()\n advance()\n } else if (e.key === \"ArrowLeft\") {\n e.preventDefault()\n goBack()\n }\n }\n\n window.addEventListener(\"keydown\", handleKeyDown)\n return () => window.removeEventListener(\"keydown\", handleKeyDown)\n }, [advance, goBack, viewMode, togglePresentationMode, isAnnotationMode, isPresentationMode])\n\n return (\n <div className=\"min-h-screen w-full bg-neutral-950 text-foreground\">\n <style>{`\n @media print {\n @page {\n size: 1920px 1080px;\n margin: 0;\n }\n html,\n body {\n width: 100%;\n height: 100%;\n margin: 0 !important;\n padding: 0 !important;\n overflow: visible !important;\n }\n body {\n print-color-adjust: exact;\n -webkit-print-color-adjust: exact;\n background: transparent !important;\n }\n }\n `}</style>\n\n {/* Toolbar */}\n <div\n className={cn(\n \"fixed top-4 z-50 flex gap-1 rounded-lg border border-neutral-800 bg-neutral-950/90 p-1 backdrop-blur-sm transition-[right] print:hidden\",\n isPresentationMode && \"hidden\",\n isAnnotationMode && showAnnotationPanel ? \"right-[19.5rem]\" : \"right-4\"\n )}\n >\n <button\n onClick={() => setViewMode(\"slide\")}\n className={cn(\n \"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\",\n viewMode === \"slide\" && \"bg-neutral-800 text-white\"\n )}\n title=\"Presentation View\"\n >\n <Monitor className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => setViewMode(\"list\")}\n className={cn(\n \"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\",\n viewMode === \"list\" && \"bg-neutral-800 text-white\"\n )}\n title=\"List View\"\n >\n <List className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => setViewMode(\"grid\")}\n className={cn(\n \"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\",\n viewMode === \"grid\" && \"bg-neutral-800 text-white\"\n )}\n title=\"Grid View\"\n >\n <Grid3X3 className=\"h-4 w-4\" />\n </button>\n\n <div className=\"mx-1 w-px bg-neutral-800\" />\n\n <button\n onClick={() => {\n setIsAnnotationMode(prev => {\n const next = !prev\n setShowAnnotationPanel(next)\n if (!next) setSelectedAnnotationId(null)\n return next\n })\n }}\n className={cn(\n \"relative rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\",\n isAnnotationMode && \"bg-[#FF6B35] text-white hover:bg-[#FF7A4A]\"\n )}\n title=\"Annotate slides\"\n >\n <MessageCircle className=\"h-4 w-4\" />\n {openCount > 0 && (\n <span className=\"absolute -top-1 -right-1 flex h-4 w-4 items-center justify-center rounded-full bg-[#FF6B35] text-[10px] font-bold text-white\">\n {openCount}\n </span>\n )}\n </button>\n\n <div className=\"mx-1 w-px bg-neutral-800\" />\n\n <button\n onClick={handleExportPdf}\n className=\"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\"\n title=\"Download PDF\"\n >\n <Download className=\"h-4 w-4\" />\n </button>\n <button\n onClick={togglePresentationMode}\n className=\"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\"\n title=\"Present (F)\"\n >\n <Maximize className=\"h-4 w-4\" />\n </button>\n </div>\n\n {/* Slide View */}\n {viewMode === \"slide\" && (\n <div\n className={cn(\n \"flex h-screen w-full print:hidden\",\n isPresentationMode ? \"bg-black\" : \"\"\n )}\n >\n <div\n ref={containerRef}\n role=\"presentation\"\n tabIndex={isPresentationMode ? 0 : undefined}\n className={cn(\n \"flex flex-1 flex-col items-center justify-center overflow-hidden\",\n isPresentationMode ? \"bg-black p-0\" : \"p-4 md:p-8\"\n )}\n onClick={isPresentationMode ? advance : undefined}\n onKeyDown={\n isPresentationMode\n ? e => {\n if (e.key === \"Enter\" || e.key === \" \") advance()\n }\n : undefined\n }\n >\n <LayoutGroup id=\"slide-deck\">\n {isPresentationMode ? (\n <div\n className=\"pointer-events-none relative overflow-hidden bg-black\"\n style={{\n width: SLIDE_DIMENSIONS.width,\n height: SLIDE_DIMENSIONS.height,\n transform: `scale(${scale})`,\n transformOrigin: \"center center\"\n }}\n >\n <SlideRenderer\n slides={slides}\n currentSlide={currentSlide}\n animationStep={animationStep}\n totalSteps={totalSteps}\n direction={direction}\n showAllAnimations={showAllAnimations}\n transition={transition}\n directionalTransition={directionalTransition}\n onTransitionComplete={onTransitionComplete}\n />\n </div>\n ) : (\n <div ref={slideContainerRef} className=\"relative aspect-video w-full max-w-7xl overflow-hidden rounded-xl border border-neutral-800 bg-black shadow-2xl\">\n <SlideRenderer\n slides={slides}\n currentSlide={currentSlide}\n animationStep={animationStep}\n totalSteps={totalSteps}\n direction={direction}\n showAllAnimations={showAllAnimations}\n transition={transition}\n directionalTransition={directionalTransition}\n onTransitionComplete={onTransitionComplete}\n />\n {isAnnotationMode && (\n <AnnotationOverlay\n slides={slides}\n currentSlide={currentSlide}\n slideContainerRef={slideContainerRef}\n selectedId={selectedAnnotationId}\n onSelectId={setSelectedAnnotationId}\n onShowPanel={() => setShowAnnotationPanel(true)}\n slideAnnotations={getSlideAnnotations(currentSlide)}\n addAnnotation={effectiveAdd ?? (() => {})}\n\n />\n )}\n </div>\n )}\n </LayoutGroup>\n\n {/* Navigation Controls */}\n {!isPresentationMode && (\n <div className=\"mt-6 flex items-center gap-4\">\n <button\n onClick={goBack}\n className=\"rounded-full border border-neutral-800 bg-black/50 p-2 text-neutral-400 backdrop-blur-sm transition-colors hover:bg-neutral-900 hover:text-white\"\n >\n <ChevronLeft className=\"h-5 w-5\" />\n </button>\n <div className=\"flex min-w-[4rem] flex-col items-center\">\n <span className=\"font-mono text-sm text-neutral-500\">\n {currentSlide + 1} / {slides.length}\n </span>\n {slides[currentSlide]?.title && (\n <span className=\"mt-0.5 text-xs text-neutral-600\">\n {slides[currentSlide].title}\n </span>\n )}\n </div>\n <button\n onClick={advance}\n className=\"rounded-full border border-neutral-800 bg-black/50 p-2 text-neutral-400 backdrop-blur-sm transition-colors hover:bg-neutral-900 hover:text-white\"\n >\n <ChevronRight className=\"h-5 w-5\" />\n </button>\n </div>\n )}\n </div>\n\n {/* Annotation Panel — beside the slide */}\n {isAnnotationMode && showAnnotationPanel && !isPresentationMode && (\n <AnnotationPanel\n annotations={getSlideAnnotations(currentSlide)}\n selectedId={selectedAnnotationId}\n onSelect={setSelectedAnnotationId}\n onDelete={effectiveDelete ?? (() => {})}\n onClose={() => {\n setShowAnnotationPanel(false)\n setSelectedAnnotationId(null)\n }}\n />\n )}\n </div>\n )}\n\n {/* Grid View */}\n {viewMode === \"grid\" && (\n <div className=\"mx-auto max-w-7xl p-8 pt-16 print:hidden\">\n <div className=\"grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4\">\n {slides.map((slideConfig, index) => {\n const SlideComponent = slideConfig.component\n const prevSection = index > 0 ? slides[index - 1]?.section : undefined\n const showSectionHeader = slideConfig.section && slideConfig.section !== prevSection\n\n return (\n <div key={index} className={showSectionHeader ? \"col-span-full\" : undefined}>\n {showSectionHeader && (\n <h3 className=\"mt-4 mb-3 text-xs font-bold tracking-[0.2em] text-neutral-500 uppercase first:mt-0\">\n {slideConfig.section}\n </h3>\n )}\n <button\n onClick={() => {\n goToSlide(index)\n setViewMode(\"slide\")\n }}\n className=\"group relative aspect-video w-full overflow-hidden rounded-lg border border-neutral-800 bg-black shadow-sm transition-all hover:border-primary hover:shadow-lg hover:shadow-primary/10\"\n >\n <div\n className=\"h-full w-full origin-top-left scale-[0.25]\"\n style={{ width: \"400%\", height: \"400%\" }}\n >\n <AnimationProvider\n currentStep={slideConfig.steps}\n totalSteps={slideConfig.steps}\n showAllAnimations={true}\n >\n <SlideErrorBoundary slideIndex={index} slideTitle={slideConfig.title}>\n <SlideComponent slideNumber={index + 1} totalSlides={slides.length} />\n </SlideErrorBoundary>\n </AnimationProvider>\n </div>\n <div className=\"absolute inset-0 bg-black/0 transition-colors group-hover:bg-black/20\" />\n <div className=\"absolute bottom-2 left-2 rounded bg-black/70 px-2 py-1 text-xs font-medium text-white\">\n {slideConfig.title ? `${index + 1}. ${slideConfig.title}` : index + 1}\n </div>\n </button>\n </div>\n )\n })}\n </div>\n </div>\n )}\n\n {/* List View */}\n <div\n className={cn(\n \"mx-auto max-w-7xl p-8 pt-16\",\n \"print:m-0 print:block print:max-w-none print:p-0\",\n viewMode === \"list\" ? \"block\" : \"hidden print:block\"\n )}\n >\n <div className=\"grid grid-cols-1 gap-8 print:block\">\n {slides.map((slideConfig, index) => {\n const SlideComponent = slideConfig.component\n return (\n <div\n key={index}\n className=\"aspect-video w-full overflow-hidden rounded-xl border border-neutral-800 bg-black shadow-sm print:relative print:m-0 print:h-[1080px] print:w-[1920px] print:break-after-page print:overflow-hidden print:rounded-none print:border-0 print:shadow-none\"\n >\n <div className=\"h-full w-full print:h-[720px] print:w-[1280px] print:origin-top-left print:scale-[1.5]\">\n <AnimationProvider\n currentStep={slideConfig.steps}\n totalSteps={slideConfig.steps}\n showAllAnimations={true}\n >\n <SlideErrorBoundary slideIndex={index} slideTitle={slideConfig.title}>\n <SlideComponent slideNumber={index + 1} totalSlides={slides.length} />\n </SlideErrorBoundary>\n </AnimationProvider>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n </div>\n )\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\"\nimport type { SlideConfig } from \"../types\"\nimport { AnnotationForm } from \"./annotation-form\"\nimport { AnnotationPin } from \"./annotation-pin\"\nimport { buildElementTarget, resolveTarget } from \"./selectors\"\nimport type { Annotation, AnnotationTarget } from \"./types\"\n\ninterface AnnotationOverlayProps {\n slides: SlideConfig[]\n currentSlide: number\n /** Ref to the slide container element (the div wrapping SlideRenderer) */\n slideContainerRef: React.RefObject<HTMLDivElement | null>\n selectedId: string | null\n onSelectId: (id: string | null) => void\n onShowPanel: () => void\n slideAnnotations: Annotation[]\n addAnnotation: (slideIndex: number, slideTitle: string, target: AnnotationTarget, body: string) => void\n}\n\ninterface PendingAnnotation {\n target: AnnotationTarget\n xPercent: number\n yPercent: number\n}\n\nexport function AnnotationOverlay({ slides, currentSlide, slideContainerRef, selectedId, onSelectId, onShowPanel, slideAnnotations, addAnnotation }: AnnotationOverlayProps) {\n const [pending, setPending] = useState<PendingAnnotation | null>(null)\n const [hoveredElement, setHoveredElement] = useState<DOMRect | null>(null)\n const overlayRef = useRef<HTMLDivElement>(null)\n\n const slideTitle = slides[currentSlide]?.title || `Slide ${currentSlide + 1}`\n\n // Resolve annotation positions — use the element if found, otherwise fallback coordinates\n const resolvedAnnotations = slideAnnotations.map((a, i) => {\n const container = slideContainerRef.current\n if (!container) {\n return { annotation: a, xPercent: a.target.position.xPercent, yPercent: a.target.position.yPercent, number: i + 1 }\n }\n\n const { element } = resolveTarget(a.target, container)\n if (element) {\n const rect = element.getBoundingClientRect()\n const containerRect = container.getBoundingClientRect()\n const xPercent = ((rect.left + rect.width / 2 - containerRect.left) / containerRect.width) * 100\n const yPercent = ((rect.top + rect.height / 2 - containerRect.top) / containerRect.height) * 100\n return { annotation: a, xPercent, yPercent, number: i + 1 }\n }\n\n return { annotation: a, xPercent: a.target.position.xPercent, yPercent: a.target.position.yPercent, number: i + 1 }\n })\n\n const handleOverlayClick = useCallback(\n (e: React.MouseEvent) => {\n const container = slideContainerRef.current\n if (!container) return\n\n // Get the element under the click by temporarily hiding the overlay\n const overlay = overlayRef.current\n if (overlay) overlay.style.pointerEvents = \"none\"\n const elementUnder = document.elementFromPoint(e.clientX, e.clientY) as HTMLElement | null\n if (overlay) overlay.style.pointerEvents = \"\"\n\n if (!elementUnder || !container.contains(elementUnder)) {\n setPending(null)\n onSelectId(null)\n return\n }\n\n // Pick the most specific meaningful element (skip tiny text nodes, pick their parent)\n let target = elementUnder\n if (target.tagName === \"SPAN\" && target.parentElement && container.contains(target.parentElement)) {\n // For inline spans, prefer their parent for a more meaningful target\n const parent = target.parentElement\n if (parent.tagName !== \"DIV\" || parent.children.length <= 3) {\n target = parent\n }\n }\n\n const annotationTarget = buildElementTarget(target, container)\n const containerRect = container.getBoundingClientRect()\n const xPercent = ((e.clientX - containerRect.left) / containerRect.width) * 100\n const yPercent = ((e.clientY - containerRect.top) / containerRect.height) * 100\n\n setPending({ target: annotationTarget, xPercent, yPercent })\n onSelectId(null)\n },\n [slideContainerRef, onSelectId]\n )\n\n const handleSubmit = useCallback(\n (text: string) => {\n if (!pending) return\n addAnnotation(currentSlide, slideTitle, pending.target, text)\n setPending(null)\n onShowPanel()\n },\n [pending, currentSlide, slideTitle, addAnnotation, onShowPanel]\n )\n\n // Track hover for element highlighting\n const handleMouseMove = useCallback(\n (e: React.MouseEvent) => {\n if (pending) {\n setHoveredElement(null)\n return\n }\n\n const container = slideContainerRef.current\n if (!container) return\n\n const overlay = overlayRef.current\n if (overlay) overlay.style.pointerEvents = \"none\"\n const elementUnder = document.elementFromPoint(e.clientX, e.clientY) as HTMLElement | null\n if (overlay) overlay.style.pointerEvents = \"\"\n\n if (elementUnder && container.contains(elementUnder) && elementUnder !== container) {\n const containerRect = container.getBoundingClientRect()\n const elRect = elementUnder.getBoundingClientRect()\n setHoveredElement(\n new DOMRect(\n elRect.left - containerRect.left,\n elRect.top - containerRect.top,\n elRect.width,\n elRect.height\n )\n )\n } else {\n setHoveredElement(null)\n }\n },\n [slideContainerRef, pending]\n )\n\n // Clear pending when slide changes\n useEffect(() => {\n setPending(null)\n onSelectId(null)\n setHoveredElement(null)\n }, [currentSlide, onSelectId])\n\n return (\n <>\n {/* Hover highlight */}\n {hoveredElement && (\n <div\n className=\"pointer-events-none absolute z-20 rounded-lg border-2 border-dashed border-[#FF6B35]/50 bg-[#FF6B35]/5\"\n style={{\n left: hoveredElement.x,\n top: hoveredElement.y,\n width: hoveredElement.width,\n height: hoveredElement.height\n }}\n />\n )}\n\n {/* Click capture overlay */}\n <div\n ref={overlayRef}\n role=\"button\"\n tabIndex={0}\n className=\"absolute inset-0 z-20\"\n style={{ cursor: `url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none'%3E%3Ccircle cx='12' cy='12' r='8' stroke='%23FF6B35' stroke-width='2' opacity='0.8'/%3E%3Ccircle cx='12' cy='12' r='2' fill='%23FF6B35'/%3E%3C/svg%3E\") 12 12, crosshair` }}\n onClick={handleOverlayClick}\n onKeyDown={e => { if (e.key === \"Escape\") { setPending(null); onSelectId(null) } }}\n onMouseMove={handleMouseMove}\n onMouseLeave={() => setHoveredElement(null)}\n />\n\n {/* Annotation pins */}\n {resolvedAnnotations.map(({ annotation, xPercent, yPercent, number }) => (\n <AnnotationPin\n key={annotation.id}\n number={number}\n status={annotation.status}\n xPercent={xPercent}\n yPercent={yPercent}\n isSelected={annotation.id === selectedId}\n onClick={() => {\n onSelectId(annotation.id === selectedId ? null : annotation.id)\n onShowPanel()\n setPending(null)\n }}\n />\n ))}\n\n {/* New annotation form */}\n {pending && (\n <AnnotationForm\n xPercent={pending.xPercent}\n yPercent={pending.yPercent}\n onSubmit={handleSubmit}\n onCancel={() => setPending(null)}\n />\n )}\n\n </>\n )\n}\n","import { useEffect, useRef, useState } from \"react\"\nimport { ArrowUp, X } from \"lucide-react\"\n\ninterface AnnotationFormProps {\n xPercent: number\n yPercent: number\n onSubmit: (text: string) => void\n onCancel: () => void\n}\n\nexport function AnnotationForm({ xPercent, yPercent, onSubmit, onCancel }: AnnotationFormProps) {\n const [text, setText] = useState(\"\")\n const textareaRef = useRef<HTMLTextAreaElement>(null)\n\n useEffect(() => {\n textareaRef.current?.focus()\n }, [])\n\n const handleSubmit = () => {\n const trimmed = text.trim()\n if (!trimmed) return\n onSubmit(trimmed)\n }\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault()\n handleSubmit()\n }\n if (e.key === \"Escape\") {\n onCancel()\n }\n e.stopPropagation()\n }\n\n // Position the form, flipping if near edges\n const left = xPercent > 70 ? undefined : `${xPercent}%`\n const right = xPercent > 70 ? `${100 - xPercent}%` : undefined\n const top = yPercent > 70 ? undefined : `${yPercent}%`\n const bottom = yPercent > 70 ? `${100 - yPercent}%` : undefined\n\n return (\n <div\n role=\"dialog\"\n className=\"absolute z-40 w-72 overflow-hidden rounded-xl border border-white/[0.08] bg-neutral-900/95 shadow-2xl backdrop-blur-2xl\"\n style={{ left, right, top, bottom }}\n onClick={e => e.stopPropagation()}\n onKeyDown={e => e.stopPropagation()}\n >\n <div className=\"flex items-center justify-between border-b border-white/[0.06] px-3.5 py-2.5\">\n <span className=\"text-xs font-medium tracking-wide text-neutral-400\">Add annotation</span>\n <button\n onClick={onCancel}\n className=\"rounded-lg p-1 text-neutral-500 transition-colors hover:bg-white/[0.06] hover:text-neutral-300\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <div className=\"p-3\">\n <textarea\n ref={textareaRef}\n value={text}\n onChange={e => setText(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"Describe the change you want...\"\n className=\"w-full resize-none rounded-lg border border-white/[0.08] bg-white/[0.04] px-3 py-2 text-sm text-white placeholder-neutral-500 outline-none transition-colors focus:border-[#FF6B35]/50 focus:bg-white/[0.06]\"\n rows={3}\n />\n <div className=\"mt-2.5 flex items-center justify-between\">\n <span className=\"text-[11px] text-neutral-600\">Enter to send</span>\n <button\n onClick={handleSubmit}\n disabled={!text.trim()}\n className=\"flex h-7 w-7 items-center justify-center rounded-lg bg-[#FF6B35] text-white shadow-lg shadow-[#FF6B35]/20 transition-all hover:bg-[#FF7A4A] hover:shadow-[#FF6B35]/30 disabled:opacity-30 disabled:shadow-none disabled:hover:bg-[#FF6B35]\"\n >\n <ArrowUp className=\"h-3.5 w-3.5\" strokeWidth={2.5} />\n </button>\n </div>\n </div>\n </div>\n )\n}\n","interface AnnotationPinProps {\n number: number\n status: \"open\" | \"resolved\"\n xPercent: number\n yPercent: number\n isSelected: boolean\n onClick: () => void\n}\n\nexport function AnnotationPin({ number, status, xPercent, yPercent, isSelected, onClick }: AnnotationPinProps) {\n const isResolved = status === \"resolved\"\n\n return (\n <button\n onClick={e => {\n e.stopPropagation()\n onClick()\n }}\n className=\"absolute z-30 -translate-x-1/2 -translate-y-1/2 transition-all duration-200 hover:scale-110\"\n style={{ left: `${xPercent}%`, top: `${yPercent}%` }}\n title={`Annotation #${number}`}\n >\n {/* Glow ring */}\n {isSelected && !isResolved && (\n <div className=\"absolute inset-[-4px] animate-pulse rounded-full bg-[#FF6B35]/25 blur-sm\" />\n )}\n <div\n className={`relative flex h-7 w-7 items-center justify-center rounded-full text-[11px] font-semibold shadow-lg backdrop-blur-sm transition-all duration-200 ${\n isSelected\n ? isResolved\n ? \"bg-neutral-500/90 text-white ring-2 ring-neutral-400/50\"\n : \"bg-[#FF6B35] text-white ring-2 ring-[#FF6B35]/40 ring-offset-1 ring-offset-black/50\"\n : isResolved\n ? \"bg-neutral-700/80 text-neutral-400\"\n : \"bg-[#FF6B35]/90 text-white hover:bg-[#FF6B35]\"\n }`}\n >\n {number}\n </div>\n </button>\n )\n}\n","import type { AnnotationTarget } from \"./types\"\n\n/**\n * Build a composite target descriptor from a clicked DOM element.\n */\nexport function buildElementTarget(\n element: HTMLElement,\n slideRoot: HTMLElement\n): AnnotationTarget {\n const rect = element.getBoundingClientRect()\n const rootRect = slideRoot.getBoundingClientRect()\n\n const xPercent = ((rect.left + rect.width / 2 - rootRect.left) / rootRect.width) * 100\n const yPercent = ((rect.top + rect.height / 2 - rootRect.top) / rootRect.height) * 100\n\n return {\n dataAnnotate: element.getAttribute(\"data-annotate\") || undefined,\n contentNearPin: getTextFingerprint(element),\n position: { xPercent, yPercent }\n }\n}\n\n/**\n * Resolve a stored target back to a DOM element, trying strategies in priority order.\n */\nexport function resolveTarget(\n target: AnnotationTarget,\n slideRoot: HTMLElement\n): { element: HTMLElement | null; method: \"dataAnnotate\" | \"contentNearPin\" | \"position\" } {\n // 1. Try data-annotate attribute\n if (target.dataAnnotate) {\n const el = slideRoot.querySelector<HTMLElement>(`[data-annotate=\"${target.dataAnnotate}\"]`)\n if (el) return { element: el, method: \"dataAnnotate\" }\n }\n\n // 2. Try text content match\n if (target.contentNearPin) {\n const match = findByTextContent(slideRoot, target.contentNearPin)\n if (match) return { element: match, method: \"contentNearPin\" }\n }\n\n // 3. Fallback to coordinates (no element found)\n return { element: null, method: \"position\" }\n}\n\n/**\n * Extract a text fingerprint from an element (first 100 chars, trimmed).\n */\nfunction getTextFingerprint(element: HTMLElement): string | undefined {\n const text = element.textContent?.trim()\n if (!text) return undefined\n return text.slice(0, 100)\n}\n\n/**\n * Find an element by matching its text content.\n */\nfunction findByTextContent(root: HTMLElement, text: string): HTMLElement | null {\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT)\n let best: HTMLElement | null = null\n let bestLength = Infinity\n\n while (walker.nextNode()) {\n const el = walker.currentNode as HTMLElement\n const elText = el.textContent?.trim()\n if (!elText) continue\n\n const elFingerprint = elText.slice(0, 100)\n if (elFingerprint === text && elText.length < bestLength) {\n best = el\n bestLength = elText.length\n }\n }\n\n return best\n}\n","import { MessageCircle, Trash2, X } from \"lucide-react\"\nimport type { Annotation } from \"./types\"\n\ninterface AnnotationPanelProps {\n annotations: Annotation[]\n selectedId: string | null\n onSelect: (id: string) => void\n onDelete: (id: string) => void\n onClose: () => void\n}\n\nexport function AnnotationPanel({ annotations, selectedId, onSelect, onDelete, onClose }: AnnotationPanelProps) {\n const open = annotations.filter(a => a.status === \"open\")\n const resolved = annotations.filter(a => a.status === \"resolved\")\n\n return (\n <div className=\"flex h-full w-80 flex-shrink-0 flex-col border-l border-white/[0.06] bg-neutral-950/95 backdrop-blur-2xl\">\n <div className=\"flex items-center justify-between px-4 py-3.5\">\n <div className=\"flex items-center gap-2.5\">\n <div className=\"flex h-6 w-6 items-center justify-center rounded-lg bg-[#FF6B35]/15\">\n <MessageCircle className=\"h-3.5 w-3.5 text-[#FF6B35]\" />\n </div>\n <span className=\"text-sm font-medium text-white\">Annotations</span>\n {open.length > 0 && (\n <span className=\"flex h-5 min-w-5 items-center justify-center rounded-full bg-[#FF6B35]/15 px-1.5 text-[11px] font-semibold text-[#FF6B35]\">\n {open.length}\n </span>\n )}\n </div>\n <button\n onClick={onClose}\n className=\"rounded-lg p-1.5 text-neutral-500 transition-colors hover:bg-white/[0.06] hover:text-neutral-300\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n\n <div className=\"h-px bg-gradient-to-r from-transparent via-white/[0.06] to-transparent\" />\n\n <div className=\"flex-1 overflow-y-auto p-2\">\n {annotations.length === 0 && (\n <div className=\"flex flex-col gap-4 px-3 py-6\">\n <WorkflowHint />\n </div>\n )}\n\n {open.length > 0 && (\n <div>\n <div className=\"mb-1.5 px-2 pt-1 text-[11px] font-medium tracking-wider text-neutral-500 uppercase\">\n Open\n </div>\n {open.map((a, i) => (\n <AnnotationItem\n key={a.id}\n annotation={a}\n number={i + 1}\n isSelected={a.id === selectedId}\n onSelect={() => onSelect(a.id)}\n onDelete={() => onDelete(a.id)}\n />\n ))}\n </div>\n )}\n\n {resolved.length > 0 && (\n <div className={open.length > 0 ? \"mt-3\" : \"\"}>\n <div className=\"mb-1.5 px-2 pt-1 text-[11px] font-medium tracking-wider text-neutral-500 uppercase\">\n Resolved\n </div>\n {resolved.map((a, i) => (\n <AnnotationItem\n key={a.id}\n annotation={a}\n number={open.length + i + 1}\n isSelected={a.id === selectedId}\n onSelect={() => onSelect(a.id)}\n onDelete={() => onDelete(a.id)}\n />\n ))}\n </div>\n )}\n </div>\n\n {annotations.length > 0 && (\n <>\n <div className=\"h-px bg-gradient-to-r from-transparent via-white/[0.06] to-transparent\" />\n <div className=\"px-3 py-3\">\n <WorkflowHint />\n </div>\n </>\n )}\n </div>\n )\n}\n\nfunction WorkflowHint() {\n return (\n <div className=\"flex flex-col gap-2\">\n <p className=\"text-[11px] font-medium tracking-wider text-neutral-500 uppercase\">Workflow</p>\n <div className=\"flex items-start gap-2.5\">\n <div className=\"mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full bg-[#FF6B35]/15 text-[10px] font-semibold text-[#FF6B35]\">1</div>\n <p className=\"text-[12px] leading-snug text-neutral-400\">Annotate everything you want to fix</p>\n </div>\n <div className=\"flex items-start gap-2.5\">\n <div className=\"mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full bg-[#FF6B35]/15 text-[10px] font-semibold text-[#FF6B35]\">2</div>\n <p className=\"text-[12px] leading-snug text-neutral-400\">Switch to your coding agent</p>\n </div>\n <div className=\"flex items-start gap-2.5\">\n <div className=\"mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full bg-[#FF6B35]/15 text-[10px] font-semibold text-[#FF6B35]\">3</div>\n <p className=\"text-[12px] leading-snug text-neutral-400\">\"Fix open annotations\"</p>\n </div>\n </div>\n )\n}\n\nfunction AnnotationItem({\n annotation,\n number,\n isSelected,\n onSelect,\n onDelete\n}: {\n annotation: Annotation\n number: number\n isSelected: boolean\n onSelect: () => void\n onDelete: () => void\n}) {\n return (\n <div\n role=\"button\"\n tabIndex={0}\n onClick={onSelect}\n onKeyDown={e => { if (e.key === \"Enter\" || e.key === \" \") onSelect() }}\n className={`group relative mb-0.5 cursor-pointer rounded-xl p-2.5 transition-all duration-150 ${\n isSelected\n ? \"bg-[#FF6B35]/10 ring-1 ring-[#FF6B35]/20\"\n : \"hover:bg-white/[0.04]\"\n }`}\n >\n <button\n onClick={e => {\n e.stopPropagation()\n onDelete()\n }}\n className=\"absolute top-2 right-2 rounded-lg p-1 text-neutral-600 opacity-0 transition-all hover:bg-white/[0.08] hover:text-neutral-300 group-hover:opacity-100\"\n >\n <Trash2 className=\"h-3 w-3\" />\n </button>\n <div className=\"flex items-start gap-2.5\">\n <div\n className={`mt-0.5 flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full text-[11px] font-semibold ${\n annotation.status === \"open\"\n ? \"bg-[#FF6B35] text-white\"\n : \"bg-neutral-700 text-neutral-400\"\n }`}\n >\n {number}\n </div>\n <div className=\"min-w-0 pr-4\">\n <p className=\"text-[13px] leading-relaxed text-neutral-200\">{annotation.body}</p>\n {annotation.target.contentNearPin && (\n <p className=\"mt-1 truncate text-[11px] text-neutral-600\">\n {annotation.target.contentNearPin}\n </p>\n )}\n {annotation.resolution && (\n <p className=\"mt-1.5 text-[11px] text-emerald-400/80 italic\">\n {annotation.resolution}\n </p>\n )}\n </div>\n </div>\n </div>\n )\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\"\nimport { createHttpAdapter } from \"./adapters/http\"\nimport type { Annotation, AnnotationStorageAdapter, AnnotationTarget } from \"./types\"\n\nexport function useAnnotations(adapter?: AnnotationStorageAdapter) {\n const adapterRef = useRef(adapter ?? createHttpAdapter())\n const [annotations, setAnnotations] = useState<Annotation[]>([])\n\n // Load on mount and subscribe to external updates\n useEffect(() => {\n adapterRef.current.load().then(setAnnotations)\n return adapterRef.current.subscribe?.(setAnnotations)\n }, [])\n\n const addAnnotation = useCallback(\n (slideIndex: number, slideTitle: string, target: AnnotationTarget, body: string) => {\n const annotation: Annotation = {\n id: crypto.randomUUID(),\n slideIndex,\n slideTitle,\n target,\n body,\n createdAt: new Date().toISOString(),\n status: \"open\"\n }\n setAnnotations(prev => [...prev, annotation])\n adapterRef.current.add(annotation)\n },\n []\n )\n\n const deleteAnnotation = useCallback((id: string) => {\n setAnnotations(prev => prev.filter(a => a.id !== id))\n adapterRef.current.remove(id)\n }, [])\n\n const getSlideAnnotations = useCallback(\n (slideIndex: number) => annotations.filter(a => a.slideIndex === slideIndex),\n [annotations]\n )\n\n const openCount = useMemo(() => annotations.filter(a => a.status === \"open\").length, [annotations])\n\n // Allow external state updates (e.g. from postMessage adapter)\n const updateAnnotations = useCallback((updated: Annotation[]) => {\n setAnnotations(updated)\n }, [])\n\n return { annotations, addAnnotation, deleteAnnotation, getSlideAnnotations, openCount, updateAnnotations }\n}\n","import type { Annotation, AnnotationsFile, AnnotationStorageAdapter } from \"../types\"\n\nconst ENDPOINT = \"/__promptslide_annotations\"\n\nasync function loadAll(): Promise<Annotation[]> {\n try {\n const res = await fetch(ENDPOINT)\n if (!res.ok) return []\n const data: AnnotationsFile = await res.json()\n return data.annotations || []\n } catch {\n return []\n }\n}\n\nasync function saveAll(annotations: Annotation[]): Promise<void> {\n const data: AnnotationsFile = { version: 1, annotations }\n await fetch(ENDPOINT, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(data)\n })\n}\n\n/** Storage adapter for local Vite dev server (reads/writes annotations.json via middleware) */\nexport function createHttpAdapter(): AnnotationStorageAdapter {\n let cache: Annotation[] = []\n\n return {\n async load() {\n cache = await loadAll()\n return cache\n },\n async add(annotation) {\n cache = [...cache, annotation]\n await saveAll(cache)\n },\n async remove(id) {\n cache = cache.filter(a => a.id !== id)\n await saveAll(cache)\n }\n }\n}\n","import type { ClassValue } from \"clsx\"\n\nimport { clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n"],"mappings":"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","names":["jsx","motion","jsx","motion","createContext","useContext","useMemo","jsx","motion","jsx","jsx","motion","useCallback","useEffect","useState","jsx","useState","useEffect","useCallback","MessageCircle","useCallback","useEffect","useMemo","useRef","useState","useCallback","useEffect","useRef","useState","useEffect","useState","jsx","jsxs","jsx","jsxs","jsx","jsxs","useState","useRef","useCallback","useEffect","X","Fragment","jsx","jsxs","useCallback","useEffect","useMemo","useRef","useState","useRef","useState","useEffect","useCallback","useMemo","jsx","jsxs","useState","useEffect","useMemo","useCallback","useRef","MessageCircle"]}
1
+ {"version":3,"sources":["../src/core/animation-config.ts","../src/core/animation-context.tsx","../src/core/animated.tsx","../src/core/transitions.ts","../src/core/morph.tsx","../src/core/use-slide-navigation.ts","../src/core/theme-context.tsx","../src/core/slide-renderer.tsx","../src/core/slide-error-boundary.tsx","../src/core/slide-embed.tsx","../src/core/slide-deck.tsx","../src/core/annotations/annotation-overlay.tsx","../src/core/annotations/annotation-form.tsx","../src/core/annotations/annotation-pin.tsx","../src/core/annotations/selectors.ts","../src/core/annotations/annotation-panel.tsx","../src/core/annotations/use-annotations.ts","../src/core/annotations/adapters/http.ts","../src/core/utils.ts"],"sourcesContent":["/**\n * Centralized animation configuration for the slide deck framework.\n * All animation timings, easings, and spring configs are defined here.\n */\n\n// =============================================================================\n// TIMING CONSTANTS\n// =============================================================================\n\n/** Duration for slide-to-slide transitions (seconds) */\nexport const SLIDE_TRANSITION_DURATION = 0.3\n\n/** Duration for morph/layout animations between slides (seconds) */\nexport const MORPH_DURATION = 0.8\n\n/** Duration for within-slide step animations (seconds) */\nexport const STEP_ANIMATION_DURATION = 0.4\n\n/** Default stagger delay for grouped animations (seconds) */\nexport const STAGGER_DELAY = 0.1\n\n// =============================================================================\n// EASING PRESETS\n// =============================================================================\n\nexport const EASE_DEFAULT = \"easeInOut\" as const\nexport const EASE_OUT = \"easeOut\" as const\nexport const EASE_IN = \"easeIn\" as const\n\n/** Smooth ease-in-out curve for morph animations (cubic bezier) */\nexport const EASE_MORPH = [0.4, 0, 0.2, 1] as const\n\n// =============================================================================\n// SPRING CONFIGURATIONS\n// =============================================================================\n\n/** Spring config for snappy, responsive animations */\nexport const SPRING_SNAPPY = {\n type: \"spring\" as const,\n stiffness: 300,\n damping: 30\n}\n\n/** Spring config for smooth, gentle animations */\nexport const SPRING_SMOOTH = {\n type: \"spring\" as const,\n stiffness: 200,\n damping: 25\n}\n\n/** Spring config for bouncy animations */\nexport const SPRING_BOUNCY = {\n type: \"spring\" as const,\n stiffness: 400,\n damping: 20\n}\n\n// =============================================================================\n// TRANSITION PRESETS\n// =============================================================================\n\n/** Standard transition for slide transitions */\nexport const SLIDE_TRANSITION = {\n duration: SLIDE_TRANSITION_DURATION,\n ease: EASE_DEFAULT\n} as const\n\n/** Standard transition for morph animations (smooth ease-in-out) */\nexport const MORPH_TRANSITION = {\n duration: MORPH_DURATION,\n ease: EASE_MORPH\n}\n\n/** Standard transition for step animations (spring-based, duration computed from stiffness/damping) */\nexport const STEP_TRANSITION = {\n ...SPRING_SNAPPY\n}\n\n// =============================================================================\n// DISTANCE CONSTANTS (pixels)\n// =============================================================================\n\n/** Distance for slide animations */\nexport const SLIDE_DISTANCE = 100\n\n/** Distance for within-slide element animations */\nexport const ELEMENT_SLIDE_DISTANCE = 30\n\n// =============================================================================\n// SLIDE DIMENSIONS\n// =============================================================================\n\n/** Standard slide dimensions (16:9 aspect ratio) */\nexport const SLIDE_DIMENSIONS = {\n width: 1280,\n height: 720,\n aspectRatio: 16 / 9\n} as const\n","import { createContext, useContext, useMemo } from \"react\"\n\ninterface AnimationContextValue {\n /** Current animation step (0 = no animations shown yet) */\n currentStep: number\n /** Total animation steps in this slide (declared in slide config) */\n totalSteps: number\n /** When true, all animation steps should be visible (used for backward navigation) */\n showAllAnimations: boolean\n}\n\nconst AnimationContext = createContext<AnimationContextValue>({\n currentStep: 0,\n totalSteps: 0,\n showAllAnimations: false\n})\n\ninterface AnimationProviderProps {\n children: React.ReactNode\n /** Current animation step (0 = no animations shown yet) */\n currentStep: number\n /** Total animation steps declared in slide config */\n totalSteps: number\n /** When true, show all animations regardless of currentStep (for backward navigation) */\n showAllAnimations?: boolean\n}\n\n/**\n * Provides animation context to child components.\n *\n * The totalSteps is now passed from parent (declared in slide config)\n * rather than discovered at runtime, eliminating race conditions.\n */\nexport function AnimationProvider({\n children,\n currentStep,\n totalSteps,\n showAllAnimations = false\n}: AnimationProviderProps) {\n const value = useMemo(\n () => ({\n currentStep,\n totalSteps,\n showAllAnimations\n }),\n [currentStep, totalSteps, showAllAnimations]\n )\n\n return <AnimationContext.Provider value={value}>{children}</AnimationContext.Provider>\n}\n\nexport function useAnimationContext() {\n return useContext(AnimationContext)\n}\n","import { motion, Variants } from \"framer-motion\"\n\nimport {\n ELEMENT_SLIDE_DISTANCE,\n SPRING_SNAPPY,\n STAGGER_DELAY,\n STEP_ANIMATION_DURATION\n} from \"./animation-config\"\nimport { useAnimationContext } from \"./animation-context\"\n\n// =============================================================================\n// ANIMATION TYPES & VARIANTS\n// =============================================================================\n\nexport type AnimationType =\n | \"fade\"\n | \"slide-up\"\n | \"slide-down\"\n | \"slide-left\"\n | \"slide-right\"\n | \"scale\"\n\nconst animationVariants: Record<AnimationType, Variants> = {\n fade: {\n hidden: { opacity: 0 },\n visible: { opacity: 1 }\n },\n \"slide-up\": {\n hidden: { opacity: 0, y: ELEMENT_SLIDE_DISTANCE },\n visible: { opacity: 1, y: 0 }\n },\n \"slide-down\": {\n hidden: { opacity: 0, y: -ELEMENT_SLIDE_DISTANCE },\n visible: { opacity: 1, y: 0 }\n },\n \"slide-left\": {\n hidden: { opacity: 0, x: ELEMENT_SLIDE_DISTANCE },\n visible: { opacity: 1, x: 0 }\n },\n \"slide-right\": {\n hidden: { opacity: 0, x: -ELEMENT_SLIDE_DISTANCE },\n visible: { opacity: 1, x: 0 }\n },\n scale: {\n hidden: { opacity: 0, scale: 0.8 },\n visible: { opacity: 1, scale: 1 }\n }\n}\n\ninterface AnimatedProps {\n /** Which step reveals this content (1-indexed) */\n step: number\n /** Animation type */\n animation?: AnimationType\n /** Animation duration in seconds */\n duration?: number\n /** Delay after trigger in seconds */\n delay?: number\n /** Custom className */\n className?: string\n children: React.ReactNode\n}\n\n/**\n * Animated element that appears at a specific animation step.\n *\n * The step count is now declared in slide config rather than discovered\n * at runtime, so this component simply consumes the context without\n * needing to register itself.\n */\nexport function Animated({\n step,\n animation = \"slide-up\",\n duration = STEP_ANIMATION_DURATION,\n delay = 0,\n className,\n children\n}: AnimatedProps) {\n const { currentStep, showAllAnimations } = useAnimationContext()\n\n // Show all animations when navigating backward, otherwise check step\n const isVisible = showAllAnimations || currentStep >= step\n\n return (\n <motion.div\n initial=\"hidden\"\n animate={isVisible ? \"visible\" : \"hidden\"}\n variants={animationVariants[animation]}\n transition={{\n ...SPRING_SNAPPY,\n duration,\n delay: isVisible ? delay : 0\n }}\n className={className}\n >\n {children}\n </motion.div>\n )\n}\n\n/**\n * Wrapper for staggering multiple children.\n * Each direct child will be animated in sequence.\n */\ninterface AnimatedGroupProps {\n /** Starting step for the first child */\n startStep: number\n /** Animation type for all children */\n animation?: AnimationType\n /** Delay between each child in seconds */\n staggerDelay?: number\n className?: string\n children: React.ReactNode\n}\n\nexport function AnimatedGroup({\n startStep,\n animation = \"slide-up\",\n staggerDelay = STAGGER_DELAY,\n className,\n children\n}: AnimatedGroupProps) {\n const { currentStep, showAllAnimations } = useAnimationContext()\n\n const childArray = Array.isArray(children) ? children : [children]\n\n // Show all animations when navigating backward, otherwise check step\n const isVisible = showAllAnimations || currentStep >= startStep\n\n const containerVariants: Variants = {\n hidden: {},\n visible: {\n transition: {\n staggerChildren: staggerDelay\n }\n }\n }\n\n return (\n <motion.div\n initial=\"hidden\"\n animate={isVisible ? \"visible\" : \"hidden\"}\n variants={containerVariants}\n className={className}\n >\n {childArray.map((child, index) => (\n <motion.div key={index} variants={animationVariants[animation]}>\n {child}\n </motion.div>\n ))}\n </motion.div>\n )\n}\n","/**\n * Slide transition variants for the slide deck framework.\n * These define how slides enter and exit during navigation.\n */\n\nimport type { Variants } from \"framer-motion\"\n\nimport {\n EASE_MORPH,\n MORPH_DURATION,\n SLIDE_DISTANCE,\n SLIDE_TRANSITION,\n SLIDE_TRANSITION_DURATION\n} from \"./animation-config\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport type SlideTransitionType =\n | \"fade\"\n | \"slide-left\"\n | \"slide-right\"\n | \"slide-up\"\n | \"slide-down\"\n | \"zoom\"\n | \"zoom-fade\"\n | \"morph\"\n | \"none\"\n\nexport interface SlideTransitionConfig {\n /** The transition type */\n type: SlideTransitionType\n /** Optional custom duration (overrides default) */\n duration?: number\n /** Whether to use directional transitions based on navigation direction */\n directional?: boolean\n}\n\n// =============================================================================\n// STATIC VARIANTS (non-directional)\n// =============================================================================\n\nconst fadeVariants: Variants = {\n enter: { opacity: 0 },\n center: { opacity: 1 },\n exit: { opacity: 0 }\n}\n\nconst slideLeftVariants: Variants = {\n enter: { x: SLIDE_DISTANCE, opacity: 0 },\n center: { x: 0, opacity: 1 },\n exit: { x: -SLIDE_DISTANCE, opacity: 0 }\n}\n\nconst slideRightVariants: Variants = {\n enter: { x: -SLIDE_DISTANCE, opacity: 0 },\n center: { x: 0, opacity: 1 },\n exit: { x: SLIDE_DISTANCE, opacity: 0 }\n}\n\nconst slideUpVariants: Variants = {\n enter: { y: SLIDE_DISTANCE, opacity: 0 },\n center: { y: 0, opacity: 1 },\n exit: { y: -SLIDE_DISTANCE, opacity: 0 }\n}\n\nconst slideDownVariants: Variants = {\n enter: { y: -SLIDE_DISTANCE, opacity: 0 },\n center: { y: 0, opacity: 1 },\n exit: { y: SLIDE_DISTANCE, opacity: 0 }\n}\n\nconst zoomVariants: Variants = {\n enter: { scale: 0.8, opacity: 0 },\n center: { scale: 1, opacity: 1 },\n exit: { scale: 1.2, opacity: 0 }\n}\n\nconst zoomFadeVariants: Variants = {\n enter: { scale: 0.95, opacity: 0 },\n center: { scale: 1, opacity: 1 },\n exit: { scale: 1.05, opacity: 0 }\n}\n\nconst morphVariants: Variants = {\n enter: {\n opacity: 0,\n zIndex: 1\n },\n center: {\n opacity: 1,\n zIndex: 1,\n transition: {\n opacity: { delay: 0.05, duration: 0.25 }\n }\n },\n exit: {\n opacity: 0,\n zIndex: 0,\n transition: {\n opacity: { duration: MORPH_DURATION, ease: EASE_MORPH }\n }\n }\n}\n\nconst noneVariants: Variants = {\n enter: {},\n center: {},\n exit: {}\n}\n\n// =============================================================================\n// VARIANT REGISTRY\n// =============================================================================\n\nexport const SLIDE_VARIANTS: Record<SlideTransitionType, Variants> = {\n fade: fadeVariants,\n \"slide-left\": slideLeftVariants,\n \"slide-right\": slideRightVariants,\n \"slide-up\": slideUpVariants,\n \"slide-down\": slideDownVariants,\n zoom: zoomVariants,\n \"zoom-fade\": zoomFadeVariants,\n morph: morphVariants,\n none: noneVariants\n}\n\n// =============================================================================\n// DIRECTIONAL VARIANTS (based on navigation direction)\n// =============================================================================\n\nexport function createDirectionalVariants(axis: \"x\" | \"y\" = \"x\"): (direction: number) => Variants {\n return (direction: number) => ({\n enter: {\n [axis]: direction > 0 ? SLIDE_DISTANCE : -SLIDE_DISTANCE,\n opacity: 0\n },\n center: {\n [axis]: 0,\n opacity: 1\n },\n exit: {\n [axis]: direction < 0 ? SLIDE_DISTANCE : -SLIDE_DISTANCE,\n opacity: 0\n }\n })\n}\n\n/** Horizontal directional slide (left/right based on direction) */\nexport const directionalSlideX = createDirectionalVariants(\"x\")\n\n/** Vertical directional slide (up/down based on direction) */\nexport const directionalSlideY = createDirectionalVariants(\"y\")\n\n// =============================================================================\n// HELPER FUNCTIONS\n// =============================================================================\n\nexport function getSlideVariants(\n config: SlideTransitionConfig | SlideTransitionType,\n direction: number = 1\n): Variants {\n const type = typeof config === \"string\" ? config : config.type\n const directional = typeof config === \"object\" ? config.directional : false\n\n if (directional && (type === \"slide-left\" || type === \"slide-right\")) {\n return directionalSlideX(direction)\n }\n\n if (directional && (type === \"slide-up\" || type === \"slide-down\")) {\n return directionalSlideY(direction)\n }\n\n return SLIDE_VARIANTS[type]\n}\n\nexport function getSlideTransition(config?: SlideTransitionConfig | SlideTransitionType): {\n duration: number\n ease: typeof SLIDE_TRANSITION.ease\n} {\n if (!config) return SLIDE_TRANSITION\n\n const type = typeof config === \"string\" ? config : config.type\n\n const defaultDuration = type === \"morph\" ? MORPH_DURATION : SLIDE_TRANSITION_DURATION\n\n const duration = typeof config === \"object\" && config.duration ? config.duration : defaultDuration\n\n return {\n ...SLIDE_TRANSITION,\n duration\n }\n}\n\n// =============================================================================\n// DEFAULT EXPORT\n// =============================================================================\n\nexport const DEFAULT_SLIDE_TRANSITION: SlideTransitionType = \"fade\"\n","import type { Transition } from \"framer-motion\"\n\nimport { motion } from \"framer-motion\"\n\nimport { MORPH_TRANSITION } from \"./animation-config\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\ninterface MorphProps {\n layoutId: string\n transition?: Transition\n className?: string\n children: React.ReactNode\n}\n\ninterface MorphGroupProps {\n groupId: string\n transition?: Transition\n className?: string\n children: React.ReactNode\n}\n\n// =============================================================================\n// MORPH COMPONENT\n// =============================================================================\n\n/**\n * Wrapper component that enables morph/shared-layout animations between slides.\n *\n * Usage:\n * ```tsx\n * // Slide 1 - Large version\n * <Morph layoutId=\"hero-title\">\n * <h1 className=\"text-6xl\">Title</h1>\n * </Morph>\n *\n * // Slide 2 - Small version (same layoutId = morphs between them)\n * <Morph layoutId=\"hero-title\">\n * <h1 className=\"text-2xl\">Title</h1>\n * </Morph>\n * ```\n */\nexport function Morph({\n layoutId,\n transition = MORPH_TRANSITION,\n className,\n children\n}: MorphProps) {\n return (\n <motion.div\n layout\n layoutId={layoutId}\n initial={false}\n transition={transition}\n className={className}\n >\n {children}\n </motion.div>\n )\n}\n\n// =============================================================================\n// MORPH GROUP COMPONENT\n// =============================================================================\n\nexport function MorphGroup({\n groupId,\n transition = MORPH_TRANSITION,\n className,\n children\n}: MorphGroupProps) {\n return (\n <motion.div\n layout\n layoutId={groupId}\n initial={false}\n transition={transition}\n className={className}\n >\n {children}\n </motion.div>\n )\n}\n\n// =============================================================================\n// MORPH ITEM (for use within MorphGroup pattern)\n// =============================================================================\n\ninterface MorphItemProps {\n id: string\n prefix?: string\n transition?: Transition\n className?: string\n children: React.ReactNode\n}\n\nexport function MorphItem({\n id,\n prefix,\n transition = MORPH_TRANSITION,\n className,\n children\n}: MorphItemProps) {\n const layoutId = prefix ? `${prefix}-${id}` : id\n\n return (\n <motion.div\n layout\n layoutId={layoutId}\n initial={false}\n transition={transition}\n className={className}\n >\n {children}\n </motion.div>\n )\n}\n\n// =============================================================================\n// MORPH TEXT (specialized for text that changes size)\n// =============================================================================\n\ninterface MorphTextProps {\n layoutId: string\n as?: \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"p\" | \"span\"\n transition?: Transition\n className?: string\n children: React.ReactNode\n}\n\nexport function MorphText({\n layoutId,\n as: Component = \"span\",\n transition = MORPH_TRANSITION,\n className,\n children\n}: MorphTextProps) {\n const MotionComponent = motion[Component]\n\n return (\n <MotionComponent\n layout\n layoutId={layoutId}\n initial={false}\n transition={transition}\n className={className}\n >\n {children}\n </MotionComponent>\n )\n}\n","import { useCallback, useEffect, useState } from \"react\"\n\nimport type { NavigationDirection, SlideConfig } from \"./types\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\ntype NavigationStatus = \"idle\" | \"transitioning\"\n\ntype QueuedAction = \"advance\" | \"goBack\" | null\n\ninterface NavigationState {\n status: NavigationStatus\n direction: NavigationDirection\n}\n\nexport interface UseSlideNavigationOptions {\n slides: SlideConfig[]\n initialSlide?: number\n onSlideChange?: (slideIndex: number) => void\n}\n\nexport interface UseSlideNavigationReturn {\n currentSlide: number\n animationStep: number\n totalSteps: number\n direction: NavigationDirection\n isTransitioning: boolean\n showAllAnimations: boolean\n advance: () => void\n goBack: () => void\n goToSlide: (index: number) => void\n onTransitionComplete: () => void\n}\n\n// =============================================================================\n// HOOK\n// =============================================================================\n\nexport function useSlideNavigation({\n slides,\n initialSlide = 0,\n onSlideChange\n}: UseSlideNavigationOptions): UseSlideNavigationReturn {\n const [currentSlide, setCurrentSlide] = useState(initialSlide)\n const [animationStep, setAnimationStep] = useState(0)\n\n const [navState, setNavState] = useState<NavigationState>({\n status: \"idle\",\n direction: 0\n })\n\n const [queuedAction, setQueuedAction] = useState<QueuedAction>(null)\n\n const totalSteps = slides[currentSlide]?.steps ?? 0\n\n const onTransitionComplete = useCallback(() => {\n setNavState(prev => {\n if (prev.status === \"transitioning\") {\n return { status: \"idle\", direction: 0 }\n }\n return prev\n })\n }, [])\n\n const advance = useCallback(() => {\n if (navState.status === \"transitioning\") {\n setQueuedAction(\"advance\")\n return\n }\n\n const currentTotalSteps = slides[currentSlide]?.steps ?? 0\n\n if (animationStep >= currentTotalSteps) {\n const nextSlide = (currentSlide + 1) % slides.length\n setNavState({ status: \"transitioning\", direction: 1 })\n setAnimationStep(0)\n setCurrentSlide(nextSlide)\n onSlideChange?.(nextSlide)\n } else {\n setAnimationStep(prev => prev + 1)\n }\n }, [navState.status, animationStep, currentSlide, slides, onSlideChange])\n\n const goBack = useCallback(() => {\n if (navState.status === \"transitioning\") {\n setQueuedAction(\"goBack\")\n return\n }\n\n if (animationStep <= 0) {\n const prevSlide = (currentSlide - 1 + slides.length) % slides.length\n const prevSlideSteps = slides[prevSlide]?.steps ?? 0\n setNavState({ status: \"transitioning\", direction: -1 })\n setAnimationStep(prevSlideSteps)\n setCurrentSlide(prevSlide)\n onSlideChange?.(prevSlide)\n } else {\n setAnimationStep(prev => prev - 1)\n }\n }, [navState.status, animationStep, currentSlide, slides, onSlideChange])\n\n const goToSlide = useCallback(\n (index: number) => {\n if (index < 0 || index >= slides.length || index === currentSlide) {\n return\n }\n\n const direction = index > currentSlide ? 1 : -1\n setNavState({ status: \"transitioning\", direction })\n setAnimationStep(0)\n setCurrentSlide(index)\n onSlideChange?.(index)\n },\n [currentSlide, slides.length, onSlideChange]\n )\n\n useEffect(() => {\n if (navState.status === \"idle\" && queuedAction !== null) {\n setQueuedAction(null)\n if (queuedAction === \"advance\") {\n advance()\n } else if (queuedAction === \"goBack\") {\n goBack()\n }\n }\n }, [navState.status, queuedAction, advance, goBack])\n\n return {\n currentSlide,\n animationStep,\n totalSteps,\n direction: navState.direction,\n isTransitioning: navState.status !== \"idle\",\n showAllAnimations: navState.direction === -1 && navState.status === \"transitioning\",\n advance,\n goBack,\n goToSlide,\n onTransitionComplete\n }\n}\n","import { createContext, useContext, useMemo } from \"react\"\nimport type { ThemeConfig } from \"./types\"\n\nconst ThemeContext = createContext<ThemeConfig | null>(null)\n\n/**\n * Access the full theme config. Returns null outside SlideThemeProvider.\n */\nexport function useTheme(): ThemeConfig | null {\n return useContext(ThemeContext)\n}\n\nfunction buildCssOverrides(theme: ThemeConfig): React.CSSProperties {\n const style: Record<string, string> = {}\n\n if (theme.colors?.primary) style[\"--primary\"] = theme.colors.primary\n if (theme.colors?.primaryForeground) style[\"--primary-foreground\"] = theme.colors.primaryForeground\n if (theme.colors?.secondary) style[\"--secondary\"] = theme.colors.secondary\n if (theme.colors?.secondaryForeground) style[\"--secondary-foreground\"] = theme.colors.secondaryForeground\n if (theme.colors?.accent) style[\"--accent\"] = theme.colors.accent\n if (theme.colors?.accentForeground) style[\"--accent-foreground\"] = theme.colors.accentForeground\n\n if (theme.fonts?.heading) style[\"--font-heading\"] = theme.fonts.heading\n if (theme.fonts?.body) style[\"--font-body\"] = theme.fonts.body\n\n return style as React.CSSProperties\n}\n\ninterface SlideThemeProviderProps {\n theme: ThemeConfig\n children: React.ReactNode\n}\n\n/**\n * Provides the full theme to all descendants.\n * Injects CSS variable overrides via inline style.\n */\nexport function SlideThemeProvider({ theme, children }: SlideThemeProviderProps) {\n const cssOverrides = useMemo(() => buildCssOverrides(theme), [theme])\n\n return (\n <ThemeContext.Provider value={theme}>\n <div style={cssOverrides} className=\"contents\">\n {children}\n </div>\n </ThemeContext.Provider>\n )\n}\n","import { AnimatePresence, motion } from \"framer-motion\"\n\nimport type { SlideTransitionType } from \"./transitions\"\nimport type { NavigationDirection, SlideConfig } from \"./types\"\n\nimport { SLIDE_TRANSITION } from \"./animation-config\"\nimport { AnimationProvider } from \"./animation-context\"\nimport { SlideErrorBoundary } from \"./slide-error-boundary\"\nimport { DEFAULT_SLIDE_TRANSITION, getSlideVariants } from \"./transitions\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport interface SlideRendererProps {\n slides: SlideConfig[]\n currentSlide: number\n animationStep: number\n totalSteps: number\n direction: NavigationDirection\n showAllAnimations: boolean\n transition?: SlideTransitionType\n directionalTransition?: boolean\n onTransitionComplete: () => void\n}\n\n// =============================================================================\n// COMPONENT\n// =============================================================================\n\n/**\n * Renders a single slide with animated transitions.\n * Extracted from SlideDeck so it can be reused in SlideEmbed and other contexts.\n */\nexport function SlideRenderer({\n slides,\n currentSlide,\n animationStep,\n totalSteps,\n direction,\n showAllAnimations,\n transition,\n directionalTransition,\n onTransitionComplete\n}: SlideRendererProps) {\n const currentSlideTransition = slides[currentSlide]?.transition\n const transitionType = currentSlideTransition ?? transition ?? DEFAULT_SLIDE_TRANSITION\n const isDirectional = directionalTransition ?? false\n\n const slideVariants = getSlideVariants(\n { type: transitionType, directional: isDirectional },\n direction\n )\n\n const CurrentSlideComponent = slides[currentSlide]!.component\n\n return (\n <AnimatePresence initial={false}>\n <motion.div\n key={currentSlide}\n variants={slideVariants}\n initial=\"enter\"\n animate=\"center\"\n exit=\"exit\"\n transition={SLIDE_TRANSITION}\n onAnimationComplete={definition => {\n if (definition === \"center\") {\n onTransitionComplete()\n }\n }}\n className=\"absolute inset-0 h-full w-full\"\n >\n <AnimationProvider\n currentStep={animationStep}\n totalSteps={totalSteps}\n showAllAnimations={showAllAnimations}\n >\n <SlideErrorBoundary\n slideIndex={currentSlide}\n slideTitle={slides[currentSlide]?.title}\n >\n <CurrentSlideComponent\n slideNumber={currentSlide + 1}\n totalSlides={slides.length}\n />\n </SlideErrorBoundary>\n </AnimationProvider>\n </motion.div>\n </AnimatePresence>\n )\n}\n","import React from \"react\"\n\ninterface SlideErrorBoundaryProps {\n slideIndex: number\n slideTitle?: string\n children: React.ReactNode\n}\n\ninterface SlideErrorBoundaryState {\n hasError: boolean\n error: Error | null\n}\n\nexport class SlideErrorBoundary extends React.Component<\n SlideErrorBoundaryProps,\n SlideErrorBoundaryState\n> {\n state: SlideErrorBoundaryState = { hasError: false, error: null }\n\n static getDerivedStateFromError(error: Error) {\n return { hasError: true, error }\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n console.error(\n `[PromptSlide] Slide ${this.props.slideIndex + 1}${this.props.slideTitle ? ` (\"${this.props.slideTitle}\")` : \"\"} crashed:`,\n error,\n errorInfo\n )\n }\n\n render() {\n if (this.state.hasError) {\n return (\n <div className=\"flex h-full w-full flex-col items-center justify-center bg-red-950/20 p-12\">\n <div className=\"mb-4 text-lg font-semibold text-red-400\">\n Slide {this.props.slideIndex + 1} Error\n {this.props.slideTitle && (\n <span className=\"ml-2 font-normal text-red-400/70\">({this.props.slideTitle})</span>\n )}\n </div>\n <div className=\"max-w-2xl text-center font-mono text-sm break-words text-red-300/80\">\n {this.state.error?.message ?? \"Unknown error\"}\n </div>\n </div>\n )\n }\n return this.props.children\n }\n}\n","import { useCallback, useEffect, useState } from \"react\"\n\nimport type { SlideTransitionType } from \"./transitions\"\nimport type { SlideConfig } from \"./types\"\n\nimport { SLIDE_DIMENSIONS } from \"./animation-config\"\nimport { SlideRenderer } from \"./slide-renderer\"\nimport { useSlideNavigation } from \"./use-slide-navigation\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\ninterface SlideEmbedProps {\n slides: SlideConfig[]\n transition?: SlideTransitionType\n directionalTransition?: boolean\n}\n\n// =============================================================================\n// COMPONENT\n// =============================================================================\n\n/**\n * Headless slide viewer controlled via window.postMessage.\n * Designed for embedding in an iframe (e.g. the registry editor preview).\n *\n * Inbound messages (parent → embed):\n * { type: \"navigate\", data: { slide: number } }\n * { type: \"advance\" }\n * { type: \"goBack\" }\n *\n * Outbound messages (embed → parent):\n * { type: \"slideReady\" }\n * { type: \"slideState\", data: { currentSlide, totalSlides, animationStep, totalSteps, titles } }\n * { type: \"hmrUpdate\" }\n */\nexport function SlideEmbed({ slides, transition, directionalTransition }: SlideEmbedProps) {\n const [scale, setScale] = useState(1)\n\n const {\n currentSlide,\n animationStep,\n totalSteps,\n direction,\n showAllAnimations,\n advance,\n goBack,\n goToSlide,\n onTransitionComplete\n } = useSlideNavigation({ slides })\n\n // Post slide state to parent whenever it changes\n useEffect(() => {\n const state = {\n currentSlide,\n totalSlides: slides.length,\n animationStep,\n totalSteps,\n titles: slides.map(s => s.title || \"\")\n }\n window.parent.postMessage({ type: \"slideState\", data: state }, \"*\")\n }, [currentSlide, animationStep, totalSteps, slides])\n\n // Signal readiness on mount\n useEffect(() => {\n window.parent.postMessage({ type: \"slideReady\" }, \"*\")\n }, [])\n\n // Listen for Vite HMR updates\n useEffect(() => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const hot = (import.meta as any).hot as { on: (event: string, cb: () => void) => void } | undefined\n if (hot) {\n hot.on(\"vite:afterUpdate\", () => {\n window.parent.postMessage({ type: \"hmrUpdate\" }, \"*\")\n })\n }\n }, [])\n\n // Listen for inbound messages from parent\n const handleMessage = useCallback(\n (event: MessageEvent) => {\n const { type, data } = event.data || {}\n switch (type) {\n case \"navigate\":\n if (typeof data?.slide === \"number\") goToSlide(data.slide)\n break\n case \"advance\":\n advance()\n break\n case \"goBack\":\n goBack()\n break\n }\n },\n [advance, goBack, goToSlide]\n )\n\n useEffect(() => {\n window.addEventListener(\"message\", handleMessage)\n return () => window.removeEventListener(\"message\", handleMessage)\n }, [handleMessage])\n\n // Scale to fit viewport while preserving aspect ratio\n useEffect(() => {\n const calculateScale = () => {\n const scaleX = window.innerWidth / SLIDE_DIMENSIONS.width\n const scaleY = window.innerHeight / SLIDE_DIMENSIONS.height\n setScale(Math.min(scaleX, scaleY))\n }\n\n calculateScale()\n window.addEventListener(\"resize\", calculateScale)\n return () => window.removeEventListener(\"resize\", calculateScale)\n }, [])\n\n return (\n <div className=\"h-screen w-screen overflow-hidden bg-black\">\n <div\n className=\"absolute left-1/2 top-1/2 overflow-hidden\"\n style={{\n width: SLIDE_DIMENSIONS.width,\n height: SLIDE_DIMENSIONS.height,\n transform: `translate(-50%, -50%) scale(${scale})`,\n transformOrigin: \"center center\"\n }}\n >\n <SlideRenderer\n slides={slides}\n currentSlide={currentSlide}\n animationStep={animationStep}\n totalSteps={totalSteps}\n direction={direction}\n showAllAnimations={showAllAnimations}\n transition={transition}\n directionalTransition={directionalTransition}\n onTransitionComplete={onTransitionComplete}\n />\n </div>\n </div>\n )\n}\n","import { LayoutGroup } from \"framer-motion\"\nimport { ChevronLeft, ChevronRight, Download, Grid3X3, List, Maximize, MessageCircle, Monitor } from \"lucide-react\"\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\"\n\nimport type { SlideTransitionType } from \"./transitions\"\nimport type { SlideConfig } from \"./types\"\n\nimport { SLIDE_DIMENSIONS } from \"./animation-config\"\nimport { AnimationProvider } from \"./animation-context\"\nimport { AnnotationOverlay, AnnotationPanel, useAnnotations } from \"./annotations\"\nimport type { Annotation, AnnotationTarget } from \"./annotations\"\nimport { SlideErrorBoundary } from \"./slide-error-boundary\"\nimport { SlideRenderer } from \"./slide-renderer\"\nimport { useSlideNavigation } from \"./use-slide-navigation\"\nimport { cn } from \"./utils\"\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\ntype ViewMode = \"slide\" | \"list\" | \"grid\"\n\ninterface SlideDeckProps {\n slides: SlideConfig[]\n transition?: SlideTransitionType\n directionalTransition?: boolean\n /** Annotation data to display. When provided (even empty array), annotation UI is enabled. When undefined, annotation UI is hidden. */\n annotations?: Annotation[]\n /** Called when the user creates an annotation */\n onAnnotationAdd?: (slideIndex: number, slideTitle: string, target: AnnotationTarget, body: string) => void\n /** Called when the user deletes an annotation */\n onAnnotationDelete?: (id: string) => void\n}\n\n// =============================================================================\n// EXPORT VIEW (for Playwright screenshot capture)\n// =============================================================================\n\nfunction SlideExportView({ slides, slideIndex }: { slides: SlideConfig[]; slideIndex: number }) {\n const [ready, setReady] = useState(false)\n const clampedIndex = Math.max(0, Math.min(slideIndex, slides.length - 1))\n const slideConfig = slides[clampedIndex]!\n const SlideComponent = slideConfig.component\n\n useEffect(() => {\n setReady(true)\n }, [])\n\n return (\n <div\n data-export-ready={ready ? \"true\" : undefined}\n style={{\n width: SLIDE_DIMENSIONS.width,\n height: SLIDE_DIMENSIONS.height,\n overflow: \"hidden\",\n position: \"relative\",\n background: \"black\"\n }}\n >\n <AnimationProvider\n currentStep={slideConfig.steps}\n totalSteps={slideConfig.steps}\n showAllAnimations={true}\n >\n <SlideErrorBoundary slideIndex={clampedIndex} slideTitle={slideConfig.title}>\n <SlideComponent slideNumber={clampedIndex + 1} totalSlides={slides.length} />\n </SlideErrorBoundary>\n </AnimationProvider>\n </div>\n )\n}\n\n// =============================================================================\n// COMPONENT\n// =============================================================================\n\nexport function SlideDeck({ slides, transition, directionalTransition, annotations, onAnnotationAdd, onAnnotationDelete }: SlideDeckProps) {\n // Check for export mode via URL params\n const [exportParams] = useState(() => {\n if (typeof window === \"undefined\") return null\n const params = new URLSearchParams(window.location.search)\n if (params.get(\"export\") !== \"true\") return null\n return { slideIndex: parseInt(params.get(\"slide\") || \"0\", 10) }\n })\n\n if (exportParams) {\n return <SlideExportView slides={slides} slideIndex={exportParams.slideIndex} />\n }\n\n // Use internal useAnnotations as fallback when no external annotations prop is provided\n const internal = useAnnotations()\n const isExternallyManaged = annotations !== undefined\n const effectiveAnnotations = isExternallyManaged ? annotations : internal.annotations\n const effectiveAdd = isExternallyManaged ? onAnnotationAdd : internal.addAnnotation\n const effectiveDelete = isExternallyManaged ? onAnnotationDelete : internal.deleteAnnotation\n\n const openCount = useMemo(() => effectiveAnnotations.filter(a => a.status === \"open\").length, [effectiveAnnotations])\n const getSlideAnnotations = useCallback(\n (slideIndex: number) => effectiveAnnotations.filter(a => a.slideIndex === slideIndex),\n [effectiveAnnotations]\n )\n\n const [viewMode, setViewMode] = useState<ViewMode>(\"slide\")\n const [isPresentationMode, setIsPresentationMode] = useState(false)\n const [isAnnotationMode, setIsAnnotationMode] = useState(false)\n const [showAnnotationPanel, setShowAnnotationPanel] = useState(false)\n const [selectedAnnotationId, setSelectedAnnotationId] = useState<string | null>(null)\n const [scale, setScale] = useState(1)\n const containerRef = useRef<HTMLDivElement>(null)\n const slideContainerRef = useRef<HTMLDivElement>(null)\n\n const {\n currentSlide,\n animationStep,\n totalSteps,\n direction,\n showAllAnimations,\n advance,\n goBack,\n goToSlide,\n onTransitionComplete\n } = useSlideNavigation({\n slides\n })\n\n const togglePresentationMode = useCallback(async () => {\n if (!document.fullscreenElement) {\n await document.documentElement.requestFullscreen()\n } else {\n await document.exitFullscreen()\n }\n }, [])\n\n // Listen for fullscreen changes\n useEffect(() => {\n const handleFullscreenChange = () => {\n setIsPresentationMode(!!document.fullscreenElement)\n }\n document.addEventListener(\"fullscreenchange\", handleFullscreenChange)\n return () => document.removeEventListener(\"fullscreenchange\", handleFullscreenChange)\n }, [])\n\n // Calculate scale factor for presentation mode\n useEffect(() => {\n const calculateScale = () => {\n if (!isPresentationMode) {\n setScale(1)\n return\n }\n const viewportWidth = window.innerWidth\n const viewportHeight = window.innerHeight\n const scaleX = viewportWidth / SLIDE_DIMENSIONS.width\n const scaleY = viewportHeight / SLIDE_DIMENSIONS.height\n setScale(Math.min(scaleX, scaleY))\n }\n\n calculateScale()\n window.addEventListener(\"resize\", calculateScale)\n return () => window.removeEventListener(\"resize\", calculateScale)\n }, [isPresentationMode])\n\n const handleExportPdf = () => {\n const previousMode = viewMode\n setViewMode(\"list\")\n\n setTimeout(() => {\n const handleAfterPrint = () => {\n setViewMode(previousMode)\n window.removeEventListener(\"afterprint\", handleAfterPrint)\n }\n window.addEventListener(\"afterprint\", handleAfterPrint)\n window.print()\n }, 100)\n }\n\n // Keyboard navigation\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"f\" || e.key === \"F\") {\n togglePresentationMode()\n return\n }\n\n // G for grid view toggle\n if (e.key === \"g\" || e.key === \"G\") {\n setViewMode(prev => (prev === \"grid\" ? \"slide\" : \"grid\"))\n return\n }\n\n if (viewMode !== \"slide\" || (isAnnotationMode && !isPresentationMode)) return\n\n if (e.key === \"ArrowRight\" || e.key === \" \") {\n e.preventDefault()\n advance()\n } else if (e.key === \"ArrowLeft\") {\n e.preventDefault()\n goBack()\n }\n }\n\n window.addEventListener(\"keydown\", handleKeyDown)\n return () => window.removeEventListener(\"keydown\", handleKeyDown)\n }, [advance, goBack, viewMode, togglePresentationMode, isAnnotationMode, isPresentationMode])\n\n return (\n <div className=\"min-h-screen w-full bg-neutral-950 text-foreground\">\n <style>{`\n @media print {\n @page {\n size: 1920px 1080px;\n margin: 0;\n }\n html,\n body {\n width: 100%;\n height: 100%;\n margin: 0 !important;\n padding: 0 !important;\n overflow: visible !important;\n }\n body {\n print-color-adjust: exact;\n -webkit-print-color-adjust: exact;\n background: transparent !important;\n }\n }\n `}</style>\n\n {/* Toolbar */}\n <div\n className={cn(\n \"fixed top-4 z-50 flex gap-1 rounded-lg border border-neutral-800 bg-neutral-950/90 p-1 backdrop-blur-sm transition-[right] print:hidden\",\n isPresentationMode && \"hidden\",\n isAnnotationMode && showAnnotationPanel ? \"right-[19.5rem]\" : \"right-4\"\n )}\n >\n <button\n onClick={() => setViewMode(\"slide\")}\n className={cn(\n \"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\",\n viewMode === \"slide\" && \"bg-neutral-800 text-white\"\n )}\n title=\"Presentation View\"\n >\n <Monitor className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => setViewMode(\"list\")}\n className={cn(\n \"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\",\n viewMode === \"list\" && \"bg-neutral-800 text-white\"\n )}\n title=\"List View\"\n >\n <List className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => setViewMode(\"grid\")}\n className={cn(\n \"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\",\n viewMode === \"grid\" && \"bg-neutral-800 text-white\"\n )}\n title=\"Grid View\"\n >\n <Grid3X3 className=\"h-4 w-4\" />\n </button>\n\n <div className=\"mx-1 w-px bg-neutral-800\" />\n\n <button\n onClick={() => {\n setIsAnnotationMode(prev => {\n const next = !prev\n setShowAnnotationPanel(next)\n if (!next) setSelectedAnnotationId(null)\n return next\n })\n }}\n className={cn(\n \"relative rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\",\n isAnnotationMode && \"bg-[#FF6B35] text-white hover:bg-[#FF7A4A]\"\n )}\n title=\"Annotate slides\"\n >\n <MessageCircle className=\"h-4 w-4\" />\n {openCount > 0 && (\n <span className=\"absolute -top-1 -right-1 flex h-4 w-4 items-center justify-center rounded-full bg-[#FF6B35] text-[10px] font-bold text-white\">\n {openCount}\n </span>\n )}\n </button>\n\n <div className=\"mx-1 w-px bg-neutral-800\" />\n\n <button\n onClick={handleExportPdf}\n className=\"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\"\n title=\"Download PDF\"\n >\n <Download className=\"h-4 w-4\" />\n </button>\n <button\n onClick={togglePresentationMode}\n className=\"rounded-md p-2 text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-white\"\n title=\"Present (F)\"\n >\n <Maximize className=\"h-4 w-4\" />\n </button>\n </div>\n\n {/* Slide View */}\n {viewMode === \"slide\" && (\n <div\n className={cn(\n \"flex h-screen w-full print:hidden\",\n isPresentationMode ? \"bg-black\" : \"\"\n )}\n >\n <div\n ref={containerRef}\n role=\"presentation\"\n tabIndex={isPresentationMode ? 0 : undefined}\n className={cn(\n \"flex flex-1 flex-col items-center justify-center overflow-hidden\",\n isPresentationMode ? \"bg-black p-0\" : \"p-4 md:p-8\"\n )}\n onClick={isPresentationMode ? advance : undefined}\n onKeyDown={\n isPresentationMode\n ? e => {\n if (e.key === \"Enter\" || e.key === \" \") advance()\n }\n : undefined\n }\n >\n <LayoutGroup id=\"slide-deck\">\n {isPresentationMode ? (\n <div\n className=\"pointer-events-none relative overflow-hidden bg-black\"\n style={{\n width: SLIDE_DIMENSIONS.width,\n height: SLIDE_DIMENSIONS.height,\n transform: `scale(${scale})`,\n transformOrigin: \"center center\"\n }}\n >\n <SlideRenderer\n slides={slides}\n currentSlide={currentSlide}\n animationStep={animationStep}\n totalSteps={totalSteps}\n direction={direction}\n showAllAnimations={showAllAnimations}\n transition={transition}\n directionalTransition={directionalTransition}\n onTransitionComplete={onTransitionComplete}\n />\n </div>\n ) : (\n <div ref={slideContainerRef} className=\"relative aspect-video w-full max-w-7xl overflow-hidden rounded-xl border border-neutral-800 bg-black shadow-2xl\">\n <SlideRenderer\n slides={slides}\n currentSlide={currentSlide}\n animationStep={animationStep}\n totalSteps={totalSteps}\n direction={direction}\n showAllAnimations={showAllAnimations}\n transition={transition}\n directionalTransition={directionalTransition}\n onTransitionComplete={onTransitionComplete}\n />\n {isAnnotationMode && (\n <AnnotationOverlay\n slides={slides}\n currentSlide={currentSlide}\n slideContainerRef={slideContainerRef}\n selectedId={selectedAnnotationId}\n onSelectId={setSelectedAnnotationId}\n onShowPanel={() => setShowAnnotationPanel(true)}\n slideAnnotations={getSlideAnnotations(currentSlide)}\n addAnnotation={effectiveAdd ?? (() => {})}\n\n />\n )}\n </div>\n )}\n </LayoutGroup>\n\n {/* Navigation Controls */}\n {!isPresentationMode && (\n <div className=\"mt-6 flex items-center gap-4\">\n <button\n onClick={goBack}\n className=\"rounded-full border border-neutral-800 bg-black/50 p-2 text-neutral-400 backdrop-blur-sm transition-colors hover:bg-neutral-900 hover:text-white\"\n >\n <ChevronLeft className=\"h-5 w-5\" />\n </button>\n <div className=\"flex min-w-[4rem] flex-col items-center\">\n <span className=\"font-mono text-sm text-neutral-500\">\n {currentSlide + 1} / {slides.length}\n </span>\n </div>\n <button\n onClick={advance}\n className=\"rounded-full border border-neutral-800 bg-black/50 p-2 text-neutral-400 backdrop-blur-sm transition-colors hover:bg-neutral-900 hover:text-white\"\n >\n <ChevronRight className=\"h-5 w-5\" />\n </button>\n </div>\n )}\n </div>\n\n {/* Annotation Panel — beside the slide */}\n {isAnnotationMode && showAnnotationPanel && !isPresentationMode && (\n <AnnotationPanel\n annotations={getSlideAnnotations(currentSlide)}\n selectedId={selectedAnnotationId}\n onSelect={setSelectedAnnotationId}\n onDelete={effectiveDelete ?? (() => {})}\n onClose={() => {\n setShowAnnotationPanel(false)\n setSelectedAnnotationId(null)\n }}\n />\n )}\n </div>\n )}\n\n {/* Grid View */}\n {viewMode === \"grid\" && (\n <div className=\"mx-auto max-w-7xl p-8 pt-16 print:hidden\">\n <div className=\"grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4\">\n {slides.map((slideConfig, index) => {\n const SlideComponent = slideConfig.component\n const prevSection = index > 0 ? slides[index - 1]?.section : undefined\n const showSectionHeader = slideConfig.section && slideConfig.section !== prevSection\n\n return (\n <div key={index} className={showSectionHeader ? \"col-span-full\" : undefined}>\n {showSectionHeader && (\n <h3 className=\"mt-4 mb-3 text-xs font-bold tracking-[0.2em] text-neutral-500 uppercase first:mt-0\">\n {slideConfig.section}\n </h3>\n )}\n <button\n onClick={() => {\n goToSlide(index)\n setViewMode(\"slide\")\n }}\n className=\"group relative aspect-video w-full overflow-hidden rounded-lg border border-neutral-800 bg-black shadow-sm transition-all hover:border-primary hover:shadow-lg hover:shadow-primary/10\"\n >\n <div\n className=\"h-full w-full origin-top-left scale-[0.25]\"\n style={{ width: \"400%\", height: \"400%\" }}\n >\n <AnimationProvider\n currentStep={slideConfig.steps}\n totalSteps={slideConfig.steps}\n showAllAnimations={true}\n >\n <SlideErrorBoundary slideIndex={index} slideTitle={slideConfig.title}>\n <SlideComponent slideNumber={index + 1} totalSlides={slides.length} />\n </SlideErrorBoundary>\n </AnimationProvider>\n </div>\n <div className=\"absolute inset-0 bg-black/0 transition-colors group-hover:bg-black/20\" />\n <div className=\"absolute bottom-2 left-2 rounded bg-black/70 px-2 py-1 text-xs font-medium text-white\">\n {slideConfig.title ? `${index + 1}. ${slideConfig.title}` : index + 1}\n </div>\n </button>\n </div>\n )\n })}\n </div>\n </div>\n )}\n\n {/* List View */}\n <div\n className={cn(\n \"mx-auto max-w-7xl p-8 pt-16\",\n \"print:m-0 print:block print:max-w-none print:p-0\",\n viewMode === \"list\" ? \"block\" : \"hidden print:block\"\n )}\n >\n <div className=\"grid grid-cols-1 gap-8 print:block\">\n {slides.map((slideConfig, index) => {\n const SlideComponent = slideConfig.component\n return (\n <div\n key={index}\n className=\"aspect-video w-full overflow-hidden rounded-xl border border-neutral-800 bg-black shadow-sm print:relative print:m-0 print:h-[1080px] print:w-[1920px] print:break-after-page print:overflow-hidden print:rounded-none print:border-0 print:shadow-none\"\n >\n <div className=\"h-full w-full print:h-[720px] print:w-[1280px] print:origin-top-left print:scale-[1.5]\">\n <AnimationProvider\n currentStep={slideConfig.steps}\n totalSteps={slideConfig.steps}\n showAllAnimations={true}\n >\n <SlideErrorBoundary slideIndex={index} slideTitle={slideConfig.title}>\n <SlideComponent slideNumber={index + 1} totalSlides={slides.length} />\n </SlideErrorBoundary>\n </AnimationProvider>\n </div>\n </div>\n )\n })}\n </div>\n </div>\n </div>\n )\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\"\nimport type { SlideConfig } from \"../types\"\nimport { AnnotationForm } from \"./annotation-form\"\nimport { AnnotationPin } from \"./annotation-pin\"\nimport { buildElementTarget, resolveTarget } from \"./selectors\"\nimport type { Annotation, AnnotationTarget } from \"./types\"\n\ninterface AnnotationOverlayProps {\n slides: SlideConfig[]\n currentSlide: number\n /** Ref to the slide container element (the div wrapping SlideRenderer) */\n slideContainerRef: React.RefObject<HTMLDivElement | null>\n selectedId: string | null\n onSelectId: (id: string | null) => void\n onShowPanel: () => void\n slideAnnotations: Annotation[]\n addAnnotation: (slideIndex: number, slideTitle: string, target: AnnotationTarget, body: string) => void\n}\n\ninterface PendingAnnotation {\n target: AnnotationTarget\n xPercent: number\n yPercent: number\n}\n\nexport function AnnotationOverlay({ slides, currentSlide, slideContainerRef, selectedId, onSelectId, onShowPanel, slideAnnotations, addAnnotation }: AnnotationOverlayProps) {\n const [pending, setPending] = useState<PendingAnnotation | null>(null)\n const [hoveredElement, setHoveredElement] = useState<DOMRect | null>(null)\n const overlayRef = useRef<HTMLDivElement>(null)\n\n const slideTitle = slides[currentSlide]?.title || `Slide ${currentSlide + 1}`\n\n // Resolve annotation positions — use the element if found, otherwise fallback coordinates\n const resolvedAnnotations = slideAnnotations.map((a, i) => {\n const container = slideContainerRef.current\n if (!container) {\n return { annotation: a, xPercent: a.target.position.xPercent, yPercent: a.target.position.yPercent, number: i + 1 }\n }\n\n const { element } = resolveTarget(a.target, container)\n if (element) {\n const rect = element.getBoundingClientRect()\n const containerRect = container.getBoundingClientRect()\n const xPercent = ((rect.left + rect.width / 2 - containerRect.left) / containerRect.width) * 100\n const yPercent = ((rect.top + rect.height / 2 - containerRect.top) / containerRect.height) * 100\n return { annotation: a, xPercent, yPercent, number: i + 1 }\n }\n\n return { annotation: a, xPercent: a.target.position.xPercent, yPercent: a.target.position.yPercent, number: i + 1 }\n })\n\n const handleOverlayClick = useCallback(\n (e: React.MouseEvent) => {\n const container = slideContainerRef.current\n if (!container) return\n\n // Get the element under the click by temporarily hiding the overlay\n const overlay = overlayRef.current\n if (overlay) overlay.style.pointerEvents = \"none\"\n const elementUnder = document.elementFromPoint(e.clientX, e.clientY) as HTMLElement | null\n if (overlay) overlay.style.pointerEvents = \"\"\n\n if (!elementUnder || !container.contains(elementUnder)) {\n setPending(null)\n onSelectId(null)\n return\n }\n\n // Pick the most specific meaningful element (skip tiny text nodes, pick their parent)\n let target = elementUnder\n if (target.tagName === \"SPAN\" && target.parentElement && container.contains(target.parentElement)) {\n // For inline spans, prefer their parent for a more meaningful target\n const parent = target.parentElement\n if (parent.tagName !== \"DIV\" || parent.children.length <= 3) {\n target = parent\n }\n }\n\n const annotationTarget = buildElementTarget(target, container)\n const containerRect = container.getBoundingClientRect()\n const xPercent = ((e.clientX - containerRect.left) / containerRect.width) * 100\n const yPercent = ((e.clientY - containerRect.top) / containerRect.height) * 100\n\n setPending({ target: annotationTarget, xPercent, yPercent })\n onSelectId(null)\n },\n [slideContainerRef, onSelectId]\n )\n\n const handleSubmit = useCallback(\n (text: string) => {\n if (!pending) return\n addAnnotation(currentSlide, slideTitle, pending.target, text)\n setPending(null)\n onShowPanel()\n },\n [pending, currentSlide, slideTitle, addAnnotation, onShowPanel]\n )\n\n // Track hover for element highlighting\n const handleMouseMove = useCallback(\n (e: React.MouseEvent) => {\n if (pending) {\n setHoveredElement(null)\n return\n }\n\n const container = slideContainerRef.current\n if (!container) return\n\n const overlay = overlayRef.current\n if (overlay) overlay.style.pointerEvents = \"none\"\n const elementUnder = document.elementFromPoint(e.clientX, e.clientY) as HTMLElement | null\n if (overlay) overlay.style.pointerEvents = \"\"\n\n if (elementUnder && container.contains(elementUnder) && elementUnder !== container) {\n const containerRect = container.getBoundingClientRect()\n const elRect = elementUnder.getBoundingClientRect()\n setHoveredElement(\n new DOMRect(\n elRect.left - containerRect.left,\n elRect.top - containerRect.top,\n elRect.width,\n elRect.height\n )\n )\n } else {\n setHoveredElement(null)\n }\n },\n [slideContainerRef, pending]\n )\n\n // Clear pending when slide changes\n useEffect(() => {\n setPending(null)\n onSelectId(null)\n setHoveredElement(null)\n }, [currentSlide, onSelectId])\n\n return (\n <>\n {/* Hover highlight */}\n {hoveredElement && (\n <div\n className=\"pointer-events-none absolute z-20 rounded-lg border-2 border-dashed border-[#FF6B35]/50 bg-[#FF6B35]/5\"\n style={{\n left: hoveredElement.x,\n top: hoveredElement.y,\n width: hoveredElement.width,\n height: hoveredElement.height\n }}\n />\n )}\n\n {/* Click capture overlay */}\n <div\n ref={overlayRef}\n role=\"button\"\n tabIndex={0}\n className=\"absolute inset-0 z-20\"\n style={{ cursor: `url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none'%3E%3Ccircle cx='12' cy='12' r='8' stroke='%23FF6B35' stroke-width='2' opacity='0.8'/%3E%3Ccircle cx='12' cy='12' r='2' fill='%23FF6B35'/%3E%3C/svg%3E\") 12 12, crosshair` }}\n onClick={handleOverlayClick}\n onKeyDown={e => { if (e.key === \"Escape\") { setPending(null); onSelectId(null) } }}\n onMouseMove={handleMouseMove}\n onMouseLeave={() => setHoveredElement(null)}\n />\n\n {/* Annotation pins */}\n {resolvedAnnotations.map(({ annotation, xPercent, yPercent, number }) => (\n <AnnotationPin\n key={annotation.id}\n number={number}\n status={annotation.status}\n xPercent={xPercent}\n yPercent={yPercent}\n isSelected={annotation.id === selectedId}\n onClick={() => {\n onSelectId(annotation.id === selectedId ? null : annotation.id)\n onShowPanel()\n setPending(null)\n }}\n />\n ))}\n\n {/* New annotation form */}\n {pending && (\n <AnnotationForm\n xPercent={pending.xPercent}\n yPercent={pending.yPercent}\n onSubmit={handleSubmit}\n onCancel={() => setPending(null)}\n />\n )}\n\n </>\n )\n}\n","import { useEffect, useRef, useState } from \"react\"\nimport { ArrowUp, X } from \"lucide-react\"\n\ninterface AnnotationFormProps {\n xPercent: number\n yPercent: number\n onSubmit: (text: string) => void\n onCancel: () => void\n}\n\nexport function AnnotationForm({ xPercent, yPercent, onSubmit, onCancel }: AnnotationFormProps) {\n const [text, setText] = useState(\"\")\n const textareaRef = useRef<HTMLTextAreaElement>(null)\n\n useEffect(() => {\n textareaRef.current?.focus()\n }, [])\n\n const handleSubmit = () => {\n const trimmed = text.trim()\n if (!trimmed) return\n onSubmit(trimmed)\n }\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault()\n handleSubmit()\n }\n if (e.key === \"Escape\") {\n onCancel()\n }\n e.stopPropagation()\n }\n\n // Position the form, flipping if near edges\n const left = xPercent > 70 ? undefined : `${xPercent}%`\n const right = xPercent > 70 ? `${100 - xPercent}%` : undefined\n const top = yPercent > 70 ? undefined : `${yPercent}%`\n const bottom = yPercent > 70 ? `${100 - yPercent}%` : undefined\n\n return (\n <div\n role=\"dialog\"\n className=\"absolute z-40 w-72 overflow-hidden rounded-xl border border-white/[0.08] bg-neutral-900/95 shadow-2xl backdrop-blur-2xl\"\n style={{ left, right, top, bottom }}\n onClick={e => e.stopPropagation()}\n onKeyDown={e => e.stopPropagation()}\n >\n <div className=\"flex items-center justify-between border-b border-white/[0.06] px-3.5 py-2.5\">\n <span className=\"text-xs font-medium tracking-wide text-neutral-400\">Add annotation</span>\n <button\n onClick={onCancel}\n className=\"rounded-lg p-1 text-neutral-500 transition-colors hover:bg-white/[0.06] hover:text-neutral-300\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <div className=\"p-3\">\n <textarea\n ref={textareaRef}\n value={text}\n onChange={e => setText(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"Describe the change you want...\"\n className=\"w-full resize-none rounded-lg border border-white/[0.08] bg-white/[0.04] px-3 py-2 text-sm text-white placeholder-neutral-500 outline-none transition-colors focus:border-[#FF6B35]/50 focus:bg-white/[0.06]\"\n rows={3}\n />\n <div className=\"mt-2.5 flex items-center justify-between\">\n <span className=\"text-[11px] text-neutral-600\">Enter to send</span>\n <button\n onClick={handleSubmit}\n disabled={!text.trim()}\n className=\"flex h-7 w-7 items-center justify-center rounded-lg bg-[#FF6B35] text-white shadow-lg shadow-[#FF6B35]/20 transition-all hover:bg-[#FF7A4A] hover:shadow-[#FF6B35]/30 disabled:opacity-30 disabled:shadow-none disabled:hover:bg-[#FF6B35]\"\n >\n <ArrowUp className=\"h-3.5 w-3.5\" strokeWidth={2.5} />\n </button>\n </div>\n </div>\n </div>\n )\n}\n","interface AnnotationPinProps {\n number: number\n status: \"open\" | \"resolved\"\n xPercent: number\n yPercent: number\n isSelected: boolean\n onClick: () => void\n}\n\nexport function AnnotationPin({ number, status, xPercent, yPercent, isSelected, onClick }: AnnotationPinProps) {\n const isResolved = status === \"resolved\"\n\n return (\n <button\n onClick={e => {\n e.stopPropagation()\n onClick()\n }}\n className=\"absolute z-30 -translate-x-1/2 -translate-y-1/2 transition-all duration-200 hover:scale-110\"\n style={{ left: `${xPercent}%`, top: `${yPercent}%` }}\n title={`Annotation #${number}`}\n >\n {/* Glow ring */}\n {isSelected && !isResolved && (\n <div className=\"absolute inset-[-4px] animate-pulse rounded-full bg-[#FF6B35]/25 blur-sm\" />\n )}\n <div\n className={`relative flex h-7 w-7 items-center justify-center rounded-full text-[11px] font-semibold shadow-lg backdrop-blur-sm transition-all duration-200 ${\n isSelected\n ? isResolved\n ? \"bg-neutral-500/90 text-white ring-2 ring-neutral-400/50\"\n : \"bg-[#FF6B35] text-white ring-2 ring-[#FF6B35]/40 ring-offset-1 ring-offset-black/50\"\n : isResolved\n ? \"bg-neutral-700/80 text-neutral-400\"\n : \"bg-[#FF6B35]/90 text-white hover:bg-[#FF6B35]\"\n }`}\n >\n {number}\n </div>\n </button>\n )\n}\n","import type { AnnotationTarget } from \"./types\"\n\n/**\n * Build a composite target descriptor from a clicked DOM element.\n */\nexport function buildElementTarget(\n element: HTMLElement,\n slideRoot: HTMLElement\n): AnnotationTarget {\n const rect = element.getBoundingClientRect()\n const rootRect = slideRoot.getBoundingClientRect()\n\n const xPercent = ((rect.left + rect.width / 2 - rootRect.left) / rootRect.width) * 100\n const yPercent = ((rect.top + rect.height / 2 - rootRect.top) / rootRect.height) * 100\n\n return {\n dataAnnotate: element.getAttribute(\"data-annotate\") || undefined,\n contentNearPin: getTextFingerprint(element),\n position: { xPercent, yPercent }\n }\n}\n\n/**\n * Resolve a stored target back to a DOM element, trying strategies in priority order.\n */\nexport function resolveTarget(\n target: AnnotationTarget,\n slideRoot: HTMLElement\n): { element: HTMLElement | null; method: \"dataAnnotate\" | \"contentNearPin\" | \"position\" } {\n // 1. Try data-annotate attribute\n if (target.dataAnnotate) {\n const el = slideRoot.querySelector<HTMLElement>(`[data-annotate=\"${target.dataAnnotate}\"]`)\n if (el) return { element: el, method: \"dataAnnotate\" }\n }\n\n // 2. Try text content match\n if (target.contentNearPin) {\n const match = findByTextContent(slideRoot, target.contentNearPin)\n if (match) return { element: match, method: \"contentNearPin\" }\n }\n\n // 3. Fallback to coordinates (no element found)\n return { element: null, method: \"position\" }\n}\n\n/**\n * Extract a text fingerprint from an element (first 100 chars, trimmed).\n */\nfunction getTextFingerprint(element: HTMLElement): string | undefined {\n const text = element.textContent?.trim()\n if (!text) return undefined\n return text.slice(0, 100)\n}\n\n/**\n * Find an element by matching its text content.\n */\nfunction findByTextContent(root: HTMLElement, text: string): HTMLElement | null {\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT)\n let best: HTMLElement | null = null\n let bestLength = Infinity\n\n while (walker.nextNode()) {\n const el = walker.currentNode as HTMLElement\n const elText = el.textContent?.trim()\n if (!elText) continue\n\n const elFingerprint = elText.slice(0, 100)\n if (elFingerprint === text && elText.length < bestLength) {\n best = el\n bestLength = elText.length\n }\n }\n\n return best\n}\n","import { MessageCircle, Trash2, X } from \"lucide-react\"\nimport type { Annotation } from \"./types\"\n\ninterface AnnotationPanelProps {\n annotations: Annotation[]\n selectedId: string | null\n onSelect: (id: string) => void\n onDelete: (id: string) => void\n onClose: () => void\n}\n\nexport function AnnotationPanel({ annotations, selectedId, onSelect, onDelete, onClose }: AnnotationPanelProps) {\n const open = annotations.filter(a => a.status === \"open\")\n const resolved = annotations.filter(a => a.status === \"resolved\")\n\n return (\n <div className=\"flex h-full w-80 flex-shrink-0 flex-col border-l border-white/[0.06] bg-neutral-950/95 backdrop-blur-2xl\">\n <div className=\"flex items-center justify-between px-4 py-3.5\">\n <div className=\"flex items-center gap-2.5\">\n <div className=\"flex h-6 w-6 items-center justify-center rounded-lg bg-[#FF6B35]/15\">\n <MessageCircle className=\"h-3.5 w-3.5 text-[#FF6B35]\" />\n </div>\n <span className=\"text-sm font-medium text-white\">Annotations</span>\n {open.length > 0 && (\n <span className=\"flex h-5 min-w-5 items-center justify-center rounded-full bg-[#FF6B35]/15 px-1.5 text-[11px] font-semibold text-[#FF6B35]\">\n {open.length}\n </span>\n )}\n </div>\n <button\n onClick={onClose}\n className=\"rounded-lg p-1.5 text-neutral-500 transition-colors hover:bg-white/[0.06] hover:text-neutral-300\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n\n <div className=\"h-px bg-gradient-to-r from-transparent via-white/[0.06] to-transparent\" />\n\n <div className=\"flex-1 overflow-y-auto p-2\">\n {annotations.length === 0 && (\n <div className=\"flex flex-col gap-4 px-3 py-6\">\n <WorkflowHint />\n </div>\n )}\n\n {open.length > 0 && (\n <div>\n <div className=\"mb-1.5 px-2 pt-1 text-[11px] font-medium tracking-wider text-neutral-500 uppercase\">\n Open\n </div>\n {open.map((a, i) => (\n <AnnotationItem\n key={a.id}\n annotation={a}\n number={i + 1}\n isSelected={a.id === selectedId}\n onSelect={() => onSelect(a.id)}\n onDelete={() => onDelete(a.id)}\n />\n ))}\n </div>\n )}\n\n {resolved.length > 0 && (\n <div className={open.length > 0 ? \"mt-3\" : \"\"}>\n <div className=\"mb-1.5 px-2 pt-1 text-[11px] font-medium tracking-wider text-neutral-500 uppercase\">\n Resolved\n </div>\n {resolved.map((a, i) => (\n <AnnotationItem\n key={a.id}\n annotation={a}\n number={open.length + i + 1}\n isSelected={a.id === selectedId}\n onSelect={() => onSelect(a.id)}\n onDelete={() => onDelete(a.id)}\n />\n ))}\n </div>\n )}\n </div>\n\n {annotations.length > 0 && (\n <>\n <div className=\"h-px bg-gradient-to-r from-transparent via-white/[0.06] to-transparent\" />\n <div className=\"px-3 py-3\">\n <WorkflowHint />\n </div>\n </>\n )}\n </div>\n )\n}\n\nfunction WorkflowHint() {\n return (\n <div className=\"flex flex-col gap-2\">\n <p className=\"text-[11px] font-medium tracking-wider text-neutral-500 uppercase\">Workflow</p>\n <div className=\"flex items-start gap-2.5\">\n <div className=\"mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full bg-[#FF6B35]/15 text-[10px] font-semibold text-[#FF6B35]\">1</div>\n <p className=\"text-[12px] leading-snug text-neutral-400\">Annotate everything you want to fix</p>\n </div>\n <div className=\"flex items-start gap-2.5\">\n <div className=\"mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full bg-[#FF6B35]/15 text-[10px] font-semibold text-[#FF6B35]\">2</div>\n <p className=\"text-[12px] leading-snug text-neutral-400\">Switch to your coding agent</p>\n </div>\n <div className=\"flex items-start gap-2.5\">\n <div className=\"mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full bg-[#FF6B35]/15 text-[10px] font-semibold text-[#FF6B35]\">3</div>\n <p className=\"text-[12px] leading-snug text-neutral-400\">\"Fix open annotations\"</p>\n </div>\n </div>\n )\n}\n\nfunction AnnotationItem({\n annotation,\n number,\n isSelected,\n onSelect,\n onDelete\n}: {\n annotation: Annotation\n number: number\n isSelected: boolean\n onSelect: () => void\n onDelete: () => void\n}) {\n return (\n <div\n role=\"button\"\n tabIndex={0}\n onClick={onSelect}\n onKeyDown={e => { if (e.key === \"Enter\" || e.key === \" \") onSelect() }}\n className={`group relative mb-0.5 cursor-pointer rounded-xl p-2.5 transition-all duration-150 ${\n isSelected\n ? \"bg-[#FF6B35]/10 ring-1 ring-[#FF6B35]/20\"\n : \"hover:bg-white/[0.04]\"\n }`}\n >\n <button\n onClick={e => {\n e.stopPropagation()\n onDelete()\n }}\n className=\"absolute top-2 right-2 rounded-lg p-1 text-neutral-600 opacity-0 transition-all hover:bg-white/[0.08] hover:text-neutral-300 group-hover:opacity-100\"\n >\n <Trash2 className=\"h-3 w-3\" />\n </button>\n <div className=\"flex items-start gap-2.5\">\n <div\n className={`mt-0.5 flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full text-[11px] font-semibold ${\n annotation.status === \"open\"\n ? \"bg-[#FF6B35] text-white\"\n : \"bg-neutral-700 text-neutral-400\"\n }`}\n >\n {number}\n </div>\n <div className=\"min-w-0 pr-4\">\n <p className=\"text-[13px] leading-relaxed text-neutral-200\">{annotation.body}</p>\n {annotation.target.contentNearPin && (\n <p className=\"mt-1 truncate text-[11px] text-neutral-600\">\n {annotation.target.contentNearPin}\n </p>\n )}\n {annotation.resolution && (\n <p className=\"mt-1.5 text-[11px] text-emerald-400/80 italic\">\n {annotation.resolution}\n </p>\n )}\n </div>\n </div>\n </div>\n )\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\"\nimport { createHttpAdapter } from \"./adapters/http\"\nimport type { Annotation, AnnotationStorageAdapter, AnnotationTarget } from \"./types\"\n\nexport function useAnnotations(adapter?: AnnotationStorageAdapter) {\n const adapterRef = useRef(adapter ?? createHttpAdapter())\n const [annotations, setAnnotations] = useState<Annotation[]>([])\n\n // Load on mount and subscribe to external updates\n useEffect(() => {\n adapterRef.current.load().then(setAnnotations)\n return adapterRef.current.subscribe?.(setAnnotations)\n }, [])\n\n const addAnnotation = useCallback(\n (slideIndex: number, slideTitle: string, target: AnnotationTarget, body: string) => {\n const annotation: Annotation = {\n id: crypto.randomUUID(),\n slideIndex,\n slideTitle,\n target,\n body,\n createdAt: new Date().toISOString(),\n status: \"open\"\n }\n setAnnotations(prev => [...prev, annotation])\n adapterRef.current.add(annotation)\n },\n []\n )\n\n const deleteAnnotation = useCallback((id: string) => {\n setAnnotations(prev => prev.filter(a => a.id !== id))\n adapterRef.current.remove(id)\n }, [])\n\n const getSlideAnnotations = useCallback(\n (slideIndex: number) => annotations.filter(a => a.slideIndex === slideIndex),\n [annotations]\n )\n\n const openCount = useMemo(() => annotations.filter(a => a.status === \"open\").length, [annotations])\n\n // Allow external state updates (e.g. from postMessage adapter)\n const updateAnnotations = useCallback((updated: Annotation[]) => {\n setAnnotations(updated)\n }, [])\n\n return { annotations, addAnnotation, deleteAnnotation, getSlideAnnotations, openCount, updateAnnotations }\n}\n","import type { Annotation, AnnotationsFile, AnnotationStorageAdapter } from \"../types\"\n\nconst ENDPOINT = \"/__promptslide_annotations\"\n\nasync function loadAll(): Promise<Annotation[]> {\n try {\n const res = await fetch(ENDPOINT)\n if (!res.ok) return []\n const data: AnnotationsFile = await res.json()\n return data.annotations || []\n } catch {\n return []\n }\n}\n\nasync function saveAll(annotations: Annotation[]): Promise<void> {\n const data: AnnotationsFile = { version: 1, annotations }\n await fetch(ENDPOINT, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(data)\n })\n}\n\n/** Storage adapter for local Vite dev server (reads/writes annotations.json via middleware) */\nexport function createHttpAdapter(): AnnotationStorageAdapter {\n let cache: Annotation[] = []\n\n return {\n async load() {\n cache = await loadAll()\n return cache\n },\n async add(annotation) {\n cache = [...cache, annotation]\n await saveAll(cache)\n },\n async remove(id) {\n cache = cache.filter(a => a.id !== id)\n await saveAll(cache)\n }\n }\n}\n","import type { ClassValue } from \"clsx\"\n\nimport { clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n"],"mappings":"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","names":["jsx","motion","jsx","motion","createContext","useContext","useMemo","jsx","motion","jsx","jsx","motion","useCallback","useEffect","useState","jsx","useState","useEffect","useCallback","MessageCircle","useCallback","useEffect","useMemo","useRef","useState","useCallback","useEffect","useRef","useState","useEffect","useState","jsx","jsxs","jsx","jsxs","jsx","jsxs","useState","useRef","useCallback","useEffect","X","Fragment","jsx","jsxs","useCallback","useEffect","useMemo","useRef","useState","useRef","useState","useEffect","useCallback","useMemo","jsx","jsxs","useState","useEffect","useMemo","useCallback","useRef","MessageCircle"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promptslide",
3
- "version": "0.3.14",
3
+ "version": "0.3.16",
4
4
  "description": "CLI and slide engine for PromptSlide presentations",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -24,6 +24,7 @@
24
24
  ],
25
25
  "scripts": {
26
26
  "build": "tsup",
27
+ "test": "node --test test/*.test.mjs",
27
28
  "prepublishOnly": "npm run build"
28
29
  },
29
30
  "license": "MIT",
@@ -21,6 +21,7 @@ import {
21
21
  addSlideToDeckConfig,
22
22
  replaceDeckConfig
23
23
  } from "../utils/deck-config.mjs"
24
+ import { collectRegistrySlideSlugs, partitionDeckSlides } from "../utils/deck-sync.mjs"
24
25
  import { confirm, closePrompts } from "../utils/prompts.mjs"
25
26
 
26
27
  const __dirname = dirname(fileURLToPath(import.meta.url))
@@ -164,9 +165,15 @@ export async function add(args) {
164
165
  }
165
166
  }
166
167
  } else if (item.type === "deck" && item.meta?.slides) {
168
+ const registrySlideSlugs = collectRegistrySlideSlugs(resolved.items)
169
+ const { available, missing } = partitionDeckSlides(item.meta.slides, registrySlideSlugs)
170
+ if (missing.length > 0) {
171
+ console.log(` ${yellow("⚠")} Skipped ${missing.length} slide(s) listed in the deck but not included in this version:`)
172
+ console.log(` ${dim(missing.map(s => s.slug).join(", "))}`)
173
+ }
167
174
  const shouldReplace = await confirm(" Replace entire deck-config.ts with this deck?", true)
168
175
  if (shouldReplace) {
169
- const slides = item.meta.slides.map(s => ({
176
+ const slides = available.map(s => ({
170
177
  componentName: s.componentName || toPascalCase(s.slug),
171
178
  importPath: `@/slides/${s.slug}`,
172
179
  steps: s.steps,
@@ -179,7 +186,7 @@ export async function add(args) {
179
186
  console.log(` ${green("✓")} Replaced ${cyan("deck-config.ts")} ${dim(`(${slides.length} slides)`)}`)
180
187
  } else {
181
188
  // Append individual slides
182
- for (const s of item.meta.slides) {
189
+ for (const s of available) {
183
190
  const componentName = s.componentName || toPascalCase(s.slug)
184
191
  const importPath = `@/slides/${s.slug}`
185
192
  const updated = addSlideToDeckConfig(cwd, { componentName, importPath, steps: s.steps })
@@ -22,10 +22,15 @@ function getBuildHtml() {
22
22
  </html>`
23
23
  }
24
24
 
25
+ function toImportPath(p) {
26
+ return p.split("\\").join("/")
27
+ }
28
+
25
29
  function getBuildEntry(cwd) {
26
30
  // Use absolute paths since build root is .promptslide/ temp dir
27
- const globals = join(cwd, "src", "globals.css")
28
- const app = join(cwd, "src", "App")
31
+ // Normalize to forward slashes so imports work on Windows
32
+ const globals = toImportPath(join(cwd, "src", "globals.css"))
33
+ const app = toImportPath(join(cwd, "src", "App"))
29
34
  return `
30
35
  import { StrictMode, createElement } from "react"
31
36
  import { createRoot } from "react-dom/client"
@@ -8,6 +8,7 @@ import { hexToOklch } from "../utils/colors.mjs"
8
8
  import { closePrompts } from "../utils/prompts.mjs"
9
9
  import { fetchRegistryItem, resolveRegistryDependencies, writeLockfile } from "../utils/registry.mjs"
10
10
  import { toPascalCase, replaceDeckConfig } from "../utils/deck-config.mjs"
11
+ import { collectRegistrySlideSlugs, partitionDeckSlides } from "../utils/deck-sync.mjs"
11
12
  import { ensureTsConfig } from "../utils/tsconfig.mjs"
12
13
 
13
14
  const __filename = fileURLToPath(import.meta.url)
@@ -158,7 +159,13 @@ export async function clone(args) {
158
159
 
159
160
  // 6. Generate deck-config.ts
160
161
  if (item.meta?.slides) {
161
- const slides = item.meta.slides.map(s => ({
162
+ const registrySlideSlugs = collectRegistrySlideSlugs(resolved.items)
163
+ const { available, missing } = partitionDeckSlides(item.meta.slides, registrySlideSlugs)
164
+ if (missing.length > 0) {
165
+ console.log(` ${yellow("!")} Skipped ${missing.length} slide(s) listed in the deck but not included in this version:`)
166
+ console.log(` ${dim(missing.map(s => s.slug).join(", "))}`)
167
+ }
168
+ const slides = available.map(s => ({
162
169
  componentName: s.componentName || toPascalCase(s.slug),
163
170
  importPath: `@/slides/${s.slug}`,
164
171
  steps: s.steps,
@@ -223,8 +230,8 @@ export async function clone(args) {
223
230
  console.log(` ${bold("Next steps:")}`)
224
231
  console.log()
225
232
  console.log(` cd ${dirName}`)
226
- console.log(` bun install`)
227
- console.log(` bun run dev`)
233
+ console.log(` npm install`)
234
+ console.log(` npm run dev`)
228
235
  console.log()
229
236
 
230
237
  closePrompts()
@@ -9,6 +9,7 @@ import { hexToOklch, isValidHex } from "../utils/colors.mjs"
9
9
  import { prompt, confirm, closePrompts } from "../utils/prompts.mjs"
10
10
  import { fetchRegistryItem, resolveRegistryDependencies, writeLockfile } from "../utils/registry.mjs"
11
11
  import { toPascalCase, replaceDeckConfig } from "../utils/deck-config.mjs"
12
+ import { collectRegistrySlideSlugs, partitionDeckSlides } from "../utils/deck-sync.mjs"
12
13
  import { ensureTsConfig } from "../utils/tsconfig.mjs"
13
14
 
14
15
  const __filename = fileURLToPath(import.meta.url)
@@ -221,7 +222,13 @@ export async function create(args) {
221
222
 
222
223
  // Reconstruct deck-config.ts from deckConfig metadata
223
224
  if (fromItem.meta?.slides) {
224
- const slides = fromItem.meta.slides.map(s => ({
225
+ const registrySlideSlugs = collectRegistrySlideSlugs(resolved.items)
226
+ const { available, missing } = partitionDeckSlides(fromItem.meta.slides, registrySlideSlugs)
227
+ if (missing.length > 0) {
228
+ console.log(` ${yellow("⚠")} Skipped ${missing.length} slide(s) listed in the deck but not included in this version:`)
229
+ console.log(` ${dim(missing.map(s => s.slug).join(", "))}`)
230
+ }
231
+ const slides = available.map(s => ({
225
232
  componentName: s.componentName || toPascalCase(s.slug),
226
233
  importPath: `@/slides/${s.slug}`,
227
234
  steps: s.steps,
@@ -285,8 +292,8 @@ export async function create(args) {
285
292
  console.log(` ${bold("Next steps:")}`)
286
293
  console.log()
287
294
  console.log(` cd ${dirName}`)
288
- console.log(` bun install`)
289
- console.log(` bun run dev`)
295
+ console.log(` npm install`)
296
+ console.log(` npm run dev`)
290
297
  console.log()
291
298
  console.log(` Then open your coding agent and start building slides!`)
292
299
  console.log()
@@ -5,9 +5,10 @@ import { fileURLToPath } from "node:url"
5
5
  import { bold, green, cyan, red, yellow, dim } from "../utils/ansi.mjs"
6
6
  import { requireAuth } from "../utils/auth.mjs"
7
7
  import { captureSlideAsDataUri, isPlaywrightAvailable, createCaptureSession } from "../utils/export.mjs"
8
- import { publishToRegistry, registryItemExists, fetchRegistryItem, searchRegistry, updateLockfileItem, updateLockfilePublishConfig, readLockfile, writeLockfile, hashContent, detectPackageManager, requestUploadTokens, uploadBinaryToBlob, assetFileToSlug, detectAssetDepsInContent, readDeckMeta, updateDeckMeta, readItemMeta, updateItemMeta } from "../utils/registry.mjs"
8
+ import { publishToRegistry, registryItemExists, fetchRegistryItem, searchRegistry, updateLockfileItem, updateLockfilePublishConfig, readLockfile, writeLockfile, hashContent, detectPackageManager, requestUploadTokens, uploadBinaryToBlob, assetFileToSlug, detectAssetDepsInContent, readDeckMeta, updateDeckMeta, readItemMeta, updateItemMeta, pruneMissingLockfileItems } from "../utils/registry.mjs"
9
9
  import { prompt, confirm, select, closePrompts } from "../utils/prompts.mjs"
10
10
  import { parseDeckConfig } from "../utils/deck-config.mjs"
11
+ import { selectSlidesForPublish } from "../utils/deck-sync.mjs"
11
12
 
12
13
  const __dirname = dirname(fileURLToPath(import.meta.url))
13
14
  const CLI_VERSION = JSON.parse(readFileSync(join(__dirname, "..", "..", "package.json"), "utf-8")).version
@@ -412,7 +413,7 @@ export async function publish(args) {
412
413
  console.log()
413
414
 
414
415
  if (args[0] === "--help" || args[0] === "-h") {
415
- console.log(` ${bold("Usage:")} promptslide publish ${dim("[file] [--type slide|layout|deck|theme]")}`)
416
+ console.log(` ${bold("Usage:")} promptslide publish ${dim("[file] [--type slide|layout|deck|theme] [--all]")}`)
416
417
  console.log()
417
418
  console.log(` Publish a slide, layout, or entire deck to the registry.`)
418
419
  console.log()
@@ -421,6 +422,9 @@ export async function publish(args) {
421
422
  console.log(` promptslide publish --type layout`)
422
423
  console.log(` promptslide publish --type deck`)
423
424
  console.log()
425
+ console.log(` ${bold("Options:")}`)
426
+ console.log(` --all Include slide files not referenced by deck-config.ts in a deck publish`)
427
+ console.log()
424
428
  process.exit(0)
425
429
  }
426
430
 
@@ -433,6 +437,7 @@ export async function publish(args) {
433
437
 
434
438
  // Determine file to publish
435
439
  let typeOverride = null
440
+ const includeUnreferencedSlides = args.includes("--all")
436
441
  const typeIdx = args.indexOf("--type")
437
442
  if (typeIdx !== -1 && args[typeIdx + 1]) {
438
443
  typeOverride = args[typeIdx + 1]
@@ -538,9 +543,23 @@ export async function publish(args) {
538
543
  : []
539
544
 
540
545
  const slidesDir = join(cwd, "src", "slides")
541
- const slideEntries = existsSync(slidesDir)
546
+ const slideFilesOnDisk = existsSync(slidesDir)
542
547
  ? readdirSync(slidesDir).filter(f => f.endsWith(".tsx") || f.endsWith(".ts"))
543
548
  : []
549
+ const slideSelection = selectSlidesForPublish(
550
+ slideFilesOnDisk,
551
+ deckConfig.slides,
552
+ includeUnreferencedSlides
553
+ )
554
+ if (slideSelection.missing.length > 0) {
555
+ console.error(` ${red("Error:")} deck-config.ts references slide files that do not exist:`)
556
+ console.error(` ${slideSelection.missing.join(", ")}`)
557
+ console.error(` ${dim("Restore the files or remove their entries from deck-config.ts before publishing.")}`)
558
+ console.log()
559
+ closePrompts()
560
+ process.exit(1)
561
+ }
562
+ const slideEntries = slideSelection.selected
544
563
 
545
564
  // Discover shared source files (src/components/, src/lib/, etc.)
546
565
  const sharedSources = discoverSharedSources(cwd)
@@ -555,12 +574,14 @@ export async function publish(args) {
555
574
  console.log(` Slides: ${slideEntries.length}`)
556
575
  if (sharedSources.length) console.log(` Shared: ${sharedSources.length} ${dim("(bundled with deck)")}`)
557
576
  console.log(` Total: ${totalItems} items`)
558
- // Info if there are slide files on disk not referenced in deck-config.ts
559
- const deckConfigSlideCount = deckConfig.slides.length
560
- if (deckConfigSlideCount !== slideEntries.length) {
577
+ if (slideSelection.unreferenced.length > 0) {
561
578
  console.log()
562
- console.log(` ${dim("ℹ")} deck-config.ts has ${deckConfigSlideCount} slides, ${slideEntries.length} slide files on disk`)
563
- console.log(` ${dim("All slides are published. Only deck-config slides appear in the deck preview.")}`)
579
+ if (includeUnreferencedSlides) {
580
+ console.log(` ${yellow("!")} Including ${slideSelection.unreferenced.length} slide file(s) not referenced by deck-config.ts because --all was set:`)
581
+ } else {
582
+ console.log(` ${dim("ℹ")} Skipping ${slideSelection.unreferenced.length} slide file(s) not referenced by deck-config.ts:`)
583
+ }
584
+ console.log(` ${dim(slideSelection.unreferenced.join(", "))}`)
564
585
  }
565
586
  if (deckConfig.transition) {
566
587
  console.log(` Transition: ${deckConfig.transition}${deckConfig.directionalTransition ? " (directional)" : ""}`)
@@ -612,6 +633,11 @@ export async function publish(args) {
612
633
 
613
634
  // Read lockfile for skip-if-unchanged
614
635
  const lock = readLockfile(cwd)
636
+ const prunedLockItems = pruneMissingLockfileItems(cwd, lock)
637
+ if (prunedLockItems.length > 0) {
638
+ writeLockfile(cwd, lock)
639
+ console.log(` ${dim("Pruned missing files from lockfile:")} ${prunedLockItems.join(", ")}`)
640
+ }
615
641
  let published = 0
616
642
  let skipped = 0
617
643
  let failed = 0
@@ -20,6 +20,7 @@ import {
20
20
  addSlideToDeckConfig,
21
21
  replaceDeckConfig
22
22
  } from "../utils/deck-config.mjs"
23
+ import { collectRegistrySlideSlugs, partitionDeckSlides } from "../utils/deck-sync.mjs"
23
24
  import { confirm, closePrompts } from "../utils/prompts.mjs"
24
25
 
25
26
  export async function pull(args) {
@@ -174,9 +175,15 @@ export async function pull(args) {
174
175
 
175
176
  // Update deck-config.ts
176
177
  if (deckItem.type === "deck" && deckItem.meta?.slides) {
178
+ const registrySlideSlugs = collectRegistrySlideSlugs(resolved.items)
179
+ const { available, missing } = partitionDeckSlides(deckItem.meta.slides, registrySlideSlugs)
180
+ if (missing.length > 0) {
181
+ console.log(` ${yellow("!")} Skipped ${missing.length} slide(s) listed in the deck but not included in this version:`)
182
+ console.log(` ${dim(missing.map(s => s.slug).join(", "))}`)
183
+ }
177
184
  const shouldReplace = await confirm(" Replace deck-config.ts with pulled deck config?", true)
178
185
  if (shouldReplace) {
179
- const slides = deckItem.meta.slides.map(s => ({
186
+ const slides = available.map(s => ({
180
187
  componentName: s.componentName || toPascalCase(s.slug),
181
188
  importPath: `@/slides/${s.slug}`,
182
189
  steps: s.steps,
@@ -189,7 +196,7 @@ export async function pull(args) {
189
196
  console.log(` ${green("+")} Replaced ${cyan("deck-config.ts")} ${dim(`(${slides.length} slides)`)}`)
190
197
  } else {
191
198
  // Append individual slides
192
- for (const s of deckItem.meta.slides) {
199
+ for (const s of available) {
193
200
  const componentName = s.componentName || toPascalCase(s.slug)
194
201
  const importPath = `@/slides/${s.slug}`
195
202
  const updated = addSlideToDeckConfig(cwd, { componentName, importPath, steps: s.steps, section: s.section })
@@ -399,11 +399,6 @@ export function SlideDeck({ slides, transition, directionalTransition, annotatio
399
399
  <span className="font-mono text-sm text-neutral-500">
400
400
  {currentSlide + 1} / {slides.length}
401
401
  </span>
402
- {slides[currentSlide]?.title && (
403
- <span className="mt-0.5 text-xs text-neutral-600">
404
- {slides[currentSlide].title}
405
- </span>
406
- )}
407
402
  </div>
408
403
  <button
409
404
  onClick={advance}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Return the slide slugs backed by files in a resolved registry dependency set.
3
+ * Only registry-provided files count; scaffold files already present in a target
4
+ * directory must not make stale deck metadata look valid.
5
+ *
6
+ * @param {object[]} items
7
+ * @returns {Set<string>}
8
+ */
9
+ export function collectRegistrySlideSlugs(items) {
10
+ const slugs = new Set()
11
+
12
+ for (const item of items ?? []) {
13
+ for (const file of item.files ?? []) {
14
+ const target = String(file.target ?? "")
15
+ .replaceAll("\\", "/")
16
+ .replace(/^\.\//, "")
17
+ const path = String(file.path ?? "")
18
+ .replaceAll("\\", "/")
19
+ .replace(/^\.\//, "")
20
+ const fullPath = `${target.replace(/\/?$/, "/")}${path}`.replace(/\/+/g, "/")
21
+ const match = fullPath.match(/^src\/slides\/(.+)\.tsx?$/)
22
+ if (match) slugs.add(match[1])
23
+ }
24
+ }
25
+
26
+ return slugs
27
+ }
28
+
29
+ /**
30
+ * Split deck metadata into entries backed by registry files and stale entries.
31
+ * Array order is preserved so generated deck configs match the published order.
32
+ *
33
+ * @param {object[]} slides
34
+ * @param {Set<string>} availableSlugs
35
+ * @returns {{ available: object[], missing: object[] }}
36
+ */
37
+ export function partitionDeckSlides(slides, availableSlugs) {
38
+ const available = []
39
+ const missing = []
40
+
41
+ for (const slide of slides ?? []) {
42
+ if (availableSlugs.has(slide.slug)) available.push(slide)
43
+ else missing.push(slide)
44
+ }
45
+
46
+ return { available, missing }
47
+ }
48
+
49
+ /**
50
+ * Select slide files for deck publishing. By default, only files referenced by
51
+ * deck-config.ts are included. The opt-in flag retains the legacy publish-all
52
+ * behavior for users who intentionally publish scratch or library slides.
53
+ *
54
+ * @param {string[]} slideEntries
55
+ * @param {{ slug: string }[]} configuredSlides
56
+ * @param {boolean} includeUnreferenced
57
+ * @returns {{ selected: string[], unreferenced: string[], missing: string[] }}
58
+ */
59
+ export function selectSlidesForPublish(
60
+ slideEntries,
61
+ configuredSlides,
62
+ includeUnreferenced = false
63
+ ) {
64
+ const filesBySlug = new Map(slideEntries.map(file => [file.replace(/\.tsx?$/, ""), file]))
65
+ const configuredSlugs = new Set(configuredSlides.map(slide => slide.slug))
66
+ const unreferenced = slideEntries.filter(
67
+ file => !configuredSlugs.has(file.replace(/\.tsx?$/, ""))
68
+ )
69
+ const missing = configuredSlides.map(slide => slide.slug).filter(slug => !filesBySlug.has(slug))
70
+ const selected = includeUnreferenced
71
+ ? [...slideEntries]
72
+ : configuredSlides.flatMap(slide => {
73
+ const file = filesBySlug.get(slide.slug)
74
+ return file ? [file] : []
75
+ })
76
+
77
+ return { selected, unreferenced, missing }
78
+ }
@@ -150,6 +150,29 @@ export function removeLockfileItem(cwd, slug) {
150
150
  writeLockfile(cwd, lock)
151
151
  }
152
152
 
153
+ /**
154
+ * Remove lockfile entries whose tracked files have all disappeared locally.
155
+ * Entries with no files (such as the deck manifest) are retained.
156
+ *
157
+ * @param {string} cwd
158
+ * @param {{ items?: Record<string, { files?: Record<string, string> }> }} lock
159
+ * @returns {string[]} Removed item slugs
160
+ */
161
+ export function pruneMissingLockfileItems(cwd, lock) {
162
+ const removed = []
163
+ lock.items ||= {}
164
+
165
+ for (const [slug, item] of Object.entries(lock.items)) {
166
+ const paths = Object.keys(item.files ?? {})
167
+ if (paths.length > 0 && paths.every(path => !existsSync(join(cwd, path)))) {
168
+ delete lock.items[slug]
169
+ removed.push(slug)
170
+ }
171
+ }
172
+
173
+ return removed
174
+ }
175
+
153
176
  /**
154
177
  * Build common auth headers for registry API requests.
155
178
  * @param {{ token: string, organizationId?: string }} auth
@@ -2,6 +2,11 @@ import { readFileSync, writeFileSync, existsSync } from "node:fs"
2
2
  import { join } from "node:path"
3
3
  import { bold, dim } from "../utils/ansi.mjs"
4
4
 
5
+ /** Normalize a path to forward slashes so it can be used in ES module import strings on Windows. */
6
+ function toImportPath(p) {
7
+ return p.split("\\").join("/")
8
+ }
9
+
5
10
  const VIRTUAL_ENTRY_ID = "virtual:promptslide-entry"
6
11
  const RESOLVED_VIRTUAL_ENTRY_ID = "\0" + VIRTUAL_ENTRY_ID
7
12
  const VIRTUAL_EXPORT_ID = "virtual:promptslide-export"
@@ -56,11 +61,12 @@ function getExportHtmlTemplate() {
56
61
  }
57
62
 
58
63
  function getEntryModule(root) {
64
+ const r = toImportPath(root)
59
65
  return `
60
66
  import { StrictMode, createElement } from "react"
61
67
  import { createRoot } from "react-dom/client"
62
- import "${root}/src/globals.css"
63
- import App from "${root}/src/App"
68
+ import "${r}/src/globals.css"
69
+ import App from "${r}/src/App"
64
70
 
65
71
  createRoot(document.getElementById("root")).render(
66
72
  createElement(StrictMode, null, createElement(App))
@@ -69,16 +75,17 @@ createRoot(document.getElementById("root")).render(
69
75
  }
70
76
 
71
77
  function getExportEntryModule(root, slidePath) {
78
+ const r = toImportPath(root)
72
79
  return `
73
80
  import { StrictMode, createElement, useState, useEffect } from "react"
74
81
  import { createRoot } from "react-dom/client"
75
82
  import { AnimationProvider, SlideErrorBoundary, SlideThemeProvider } from "promptslide"
76
- import "${root}/src/globals.css"
77
- import * as slideMod from "${root}/${slidePath}"
83
+ import "${r}/src/globals.css"
84
+ import * as slideMod from "${r}/${slidePath}"
78
85
 
79
86
  let theme = {}
80
87
  try {
81
- const themeMod = await import("${root}/src/theme")
88
+ const themeMod = await import("${r}/src/theme")
82
89
  theme = themeMod.theme || themeMod.default || {}
83
90
  } catch {}
84
91
 
@@ -128,16 +135,17 @@ function getEmbedHtmlTemplate() {
128
135
  }
129
136
 
130
137
  function getEmbedEntryModule(root) {
138
+ const r = toImportPath(root)
131
139
  return `
132
140
  import { StrictMode, createElement } from "react"
133
141
  import { createRoot } from "react-dom/client"
134
142
  import { SlideEmbed, SlideThemeProvider } from "promptslide"
135
- import "${root}/src/globals.css"
136
- import { slides } from "${root}/src/deck-config"
143
+ import "${r}/src/globals.css"
144
+ import { slides } from "${r}/src/deck-config"
137
145
 
138
146
  let theme = {}
139
147
  try {
140
- const themeMod = await import("${root}/src/theme")
148
+ const themeMod = await import("${r}/src/theme")
141
149
  theme = themeMod.theme || themeMod.default || {}
142
150
  } catch {}
143
151
 
@@ -5,8 +5,8 @@ A slide deck built with [PromptSlide](https://github.com/prompticeu/promptslide)
5
5
  ## Getting Started
6
6
 
7
7
  ```bash
8
- bun install
9
- bun run dev
8
+ npm install
9
+ npm run dev
10
10
  ```
11
11
 
12
12
  Then open your coding agent (Claude Code, Cursor, Windsurf, etc.) and say: