promptslide 0.2.0

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.
Files changed (39) hide show
  1. package/dist/index.d.ts +377 -0
  2. package/dist/index.js +963 -0
  3. package/dist/index.js.map +1 -0
  4. package/package.json +65 -0
  5. package/src/commands/build.mjs +73 -0
  6. package/src/commands/create.mjs +197 -0
  7. package/src/commands/preview.mjs +22 -0
  8. package/src/commands/studio.mjs +27 -0
  9. package/src/core/animated.tsx +153 -0
  10. package/src/core/animation-config.ts +98 -0
  11. package/src/core/animation-context.tsx +54 -0
  12. package/src/core/index.ts +73 -0
  13. package/src/core/layouts/shared-footer.tsx +43 -0
  14. package/src/core/morph.tsx +153 -0
  15. package/src/core/slide-deck.tsx +430 -0
  16. package/src/core/slide-error-boundary.tsx +50 -0
  17. package/src/core/theme-context.tsx +48 -0
  18. package/src/core/transitions.ts +200 -0
  19. package/src/core/types.ts +136 -0
  20. package/src/core/use-slide-navigation.ts +142 -0
  21. package/src/core/utils.ts +8 -0
  22. package/src/index.mjs +70 -0
  23. package/src/utils/ansi.mjs +5 -0
  24. package/src/utils/colors.mjs +44 -0
  25. package/src/utils/prompts.mjs +50 -0
  26. package/src/utils/tsconfig.mjs +35 -0
  27. package/src/vite/config.mjs +40 -0
  28. package/src/vite/plugin.mjs +66 -0
  29. package/templates/default/AGENTS.md +453 -0
  30. package/templates/default/README.md +35 -0
  31. package/templates/default/package.json +26 -0
  32. package/templates/default/public/logo.svg +7 -0
  33. package/templates/default/src/App.tsx +11 -0
  34. package/templates/default/src/deck-config.ts +8 -0
  35. package/templates/default/src/globals.css +157 -0
  36. package/templates/default/src/layouts/slide-layout-centered.tsx +59 -0
  37. package/templates/default/src/slides/slide-example.tsx +53 -0
  38. package/templates/default/src/slides/slide-title.tsx +27 -0
  39. package/templates/default/src/theme.ts +8 -0
@@ -0,0 +1,153 @@
1
+ import { motion, Variants } from "framer-motion"
2
+
3
+ import {
4
+ ELEMENT_SLIDE_DISTANCE,
5
+ SPRING_SNAPPY,
6
+ STAGGER_DELAY,
7
+ STEP_ANIMATION_DURATION
8
+ } from "./animation-config"
9
+ import { useAnimationContext } from "./animation-context"
10
+
11
+ // =============================================================================
12
+ // ANIMATION TYPES & VARIANTS
13
+ // =============================================================================
14
+
15
+ export type AnimationType =
16
+ | "fade"
17
+ | "slide-up"
18
+ | "slide-down"
19
+ | "slide-left"
20
+ | "slide-right"
21
+ | "scale"
22
+
23
+ const animationVariants: Record<AnimationType, Variants> = {
24
+ fade: {
25
+ hidden: { opacity: 0 },
26
+ visible: { opacity: 1 }
27
+ },
28
+ "slide-up": {
29
+ hidden: { opacity: 0, y: ELEMENT_SLIDE_DISTANCE },
30
+ visible: { opacity: 1, y: 0 }
31
+ },
32
+ "slide-down": {
33
+ hidden: { opacity: 0, y: -ELEMENT_SLIDE_DISTANCE },
34
+ visible: { opacity: 1, y: 0 }
35
+ },
36
+ "slide-left": {
37
+ hidden: { opacity: 0, x: ELEMENT_SLIDE_DISTANCE },
38
+ visible: { opacity: 1, x: 0 }
39
+ },
40
+ "slide-right": {
41
+ hidden: { opacity: 0, x: -ELEMENT_SLIDE_DISTANCE },
42
+ visible: { opacity: 1, x: 0 }
43
+ },
44
+ scale: {
45
+ hidden: { opacity: 0, scale: 0.8 },
46
+ visible: { opacity: 1, scale: 1 }
47
+ }
48
+ }
49
+
50
+ interface AnimatedProps {
51
+ /** Which step reveals this content (1-indexed) */
52
+ step: number
53
+ /** Animation type */
54
+ animation?: AnimationType
55
+ /** Animation duration in seconds */
56
+ duration?: number
57
+ /** Delay after trigger in seconds */
58
+ delay?: number
59
+ /** Custom className */
60
+ className?: string
61
+ children: React.ReactNode
62
+ }
63
+
64
+ /**
65
+ * Animated element that appears at a specific animation step.
66
+ *
67
+ * The step count is now declared in slide config rather than discovered
68
+ * at runtime, so this component simply consumes the context without
69
+ * needing to register itself.
70
+ */
71
+ export function Animated({
72
+ step,
73
+ animation = "slide-up",
74
+ duration = STEP_ANIMATION_DURATION,
75
+ delay = 0,
76
+ className,
77
+ children
78
+ }: AnimatedProps) {
79
+ const { currentStep, showAllAnimations } = useAnimationContext()
80
+
81
+ // Show all animations when navigating backward, otherwise check step
82
+ const isVisible = showAllAnimations || currentStep >= step
83
+
84
+ return (
85
+ <motion.div
86
+ initial="hidden"
87
+ animate={isVisible ? "visible" : "hidden"}
88
+ variants={animationVariants[animation]}
89
+ transition={{
90
+ ...SPRING_SNAPPY,
91
+ duration,
92
+ delay: isVisible ? delay : 0
93
+ }}
94
+ className={className}
95
+ >
96
+ {children}
97
+ </motion.div>
98
+ )
99
+ }
100
+
101
+ /**
102
+ * Wrapper for staggering multiple children.
103
+ * Each direct child will be animated in sequence.
104
+ */
105
+ interface AnimatedGroupProps {
106
+ /** Starting step for the first child */
107
+ startStep: number
108
+ /** Animation type for all children */
109
+ animation?: AnimationType
110
+ /** Delay between each child in seconds */
111
+ staggerDelay?: number
112
+ className?: string
113
+ children: React.ReactNode
114
+ }
115
+
116
+ export function AnimatedGroup({
117
+ startStep,
118
+ animation = "slide-up",
119
+ staggerDelay = STAGGER_DELAY,
120
+ className,
121
+ children
122
+ }: AnimatedGroupProps) {
123
+ const { currentStep, showAllAnimations } = useAnimationContext()
124
+
125
+ const childArray = Array.isArray(children) ? children : [children]
126
+
127
+ // Show all animations when navigating backward, otherwise check step
128
+ const isVisible = showAllAnimations || currentStep >= startStep
129
+
130
+ const containerVariants: Variants = {
131
+ hidden: {},
132
+ visible: {
133
+ transition: {
134
+ staggerChildren: staggerDelay
135
+ }
136
+ }
137
+ }
138
+
139
+ return (
140
+ <motion.div
141
+ initial="hidden"
142
+ animate={isVisible ? "visible" : "hidden"}
143
+ variants={containerVariants}
144
+ className={className}
145
+ >
146
+ {childArray.map((child, index) => (
147
+ <motion.div key={index} variants={animationVariants[animation]}>
148
+ {child}
149
+ </motion.div>
150
+ ))}
151
+ </motion.div>
152
+ )
153
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Centralized animation configuration for the slide deck framework.
3
+ * All animation timings, easings, and spring configs are defined here.
4
+ */
5
+
6
+ // =============================================================================
7
+ // TIMING CONSTANTS
8
+ // =============================================================================
9
+
10
+ /** Duration for slide-to-slide transitions (seconds) */
11
+ export const SLIDE_TRANSITION_DURATION = 0.3
12
+
13
+ /** Duration for morph/layout animations between slides (seconds) */
14
+ export const MORPH_DURATION = 0.8
15
+
16
+ /** Duration for within-slide step animations (seconds) */
17
+ export const STEP_ANIMATION_DURATION = 0.4
18
+
19
+ /** Default stagger delay for grouped animations (seconds) */
20
+ export const STAGGER_DELAY = 0.1
21
+
22
+ // =============================================================================
23
+ // EASING PRESETS
24
+ // =============================================================================
25
+
26
+ export const EASE_DEFAULT = "easeInOut" as const
27
+ export const EASE_OUT = "easeOut" as const
28
+ export const EASE_IN = "easeIn" as const
29
+
30
+ /** Smooth ease-in-out curve for morph animations (cubic bezier) */
31
+ export const EASE_MORPH = [0.4, 0, 0.2, 1] as const
32
+
33
+ // =============================================================================
34
+ // SPRING CONFIGURATIONS
35
+ // =============================================================================
36
+
37
+ /** Spring config for snappy, responsive animations */
38
+ export const SPRING_SNAPPY = {
39
+ type: "spring" as const,
40
+ stiffness: 300,
41
+ damping: 30
42
+ }
43
+
44
+ /** Spring config for smooth, gentle animations */
45
+ export const SPRING_SMOOTH = {
46
+ type: "spring" as const,
47
+ stiffness: 200,
48
+ damping: 25
49
+ }
50
+
51
+ /** Spring config for bouncy animations */
52
+ export const SPRING_BOUNCY = {
53
+ type: "spring" as const,
54
+ stiffness: 400,
55
+ damping: 20
56
+ }
57
+
58
+ // =============================================================================
59
+ // TRANSITION PRESETS
60
+ // =============================================================================
61
+
62
+ /** Standard transition for slide transitions */
63
+ export const SLIDE_TRANSITION = {
64
+ duration: SLIDE_TRANSITION_DURATION,
65
+ ease: EASE_DEFAULT
66
+ } as const
67
+
68
+ /** Standard transition for morph animations (smooth ease-in-out) */
69
+ export const MORPH_TRANSITION = {
70
+ duration: MORPH_DURATION,
71
+ ease: EASE_MORPH
72
+ }
73
+
74
+ /** Standard transition for step animations (spring-based, duration computed from stiffness/damping) */
75
+ export const STEP_TRANSITION = {
76
+ ...SPRING_SNAPPY
77
+ }
78
+
79
+ // =============================================================================
80
+ // DISTANCE CONSTANTS (pixels)
81
+ // =============================================================================
82
+
83
+ /** Distance for slide animations */
84
+ export const SLIDE_DISTANCE = 100
85
+
86
+ /** Distance for within-slide element animations */
87
+ export const ELEMENT_SLIDE_DISTANCE = 30
88
+
89
+ // =============================================================================
90
+ // SLIDE DIMENSIONS
91
+ // =============================================================================
92
+
93
+ /** Standard slide dimensions (16:9 aspect ratio) */
94
+ export const SLIDE_DIMENSIONS = {
95
+ width: 1280,
96
+ height: 720,
97
+ aspectRatio: 16 / 9
98
+ } as const
@@ -0,0 +1,54 @@
1
+ import { createContext, useContext, useMemo } from "react"
2
+
3
+ interface AnimationContextValue {
4
+ /** Current animation step (0 = no animations shown yet) */
5
+ currentStep: number
6
+ /** Total animation steps in this slide (declared in slide config) */
7
+ totalSteps: number
8
+ /** When true, all animation steps should be visible (used for backward navigation) */
9
+ showAllAnimations: boolean
10
+ }
11
+
12
+ const AnimationContext = createContext<AnimationContextValue>({
13
+ currentStep: 0,
14
+ totalSteps: 0,
15
+ showAllAnimations: false
16
+ })
17
+
18
+ interface AnimationProviderProps {
19
+ children: React.ReactNode
20
+ /** Current animation step (0 = no animations shown yet) */
21
+ currentStep: number
22
+ /** Total animation steps declared in slide config */
23
+ totalSteps: number
24
+ /** When true, show all animations regardless of currentStep (for backward navigation) */
25
+ showAllAnimations?: boolean
26
+ }
27
+
28
+ /**
29
+ * Provides animation context to child components.
30
+ *
31
+ * The totalSteps is now passed from parent (declared in slide config)
32
+ * rather than discovered at runtime, eliminating race conditions.
33
+ */
34
+ export function AnimationProvider({
35
+ children,
36
+ currentStep,
37
+ totalSteps,
38
+ showAllAnimations = false
39
+ }: AnimationProviderProps) {
40
+ const value = useMemo(
41
+ () => ({
42
+ currentStep,
43
+ totalSteps,
44
+ showAllAnimations
45
+ }),
46
+ [currentStep, totalSteps, showAllAnimations]
47
+ )
48
+
49
+ return <AnimationContext.Provider value={value}>{children}</AnimationContext.Provider>
50
+ }
51
+
52
+ export function useAnimationContext() {
53
+ return useContext(AnimationContext)
54
+ }
@@ -0,0 +1,73 @@
1
+ // Types
2
+ export type {
3
+ SlideProps,
4
+ SlideComponent,
5
+ SlideConfig,
6
+ NavigationDirection
7
+ } from "./types"
8
+
9
+ // Animation Config
10
+ export {
11
+ SLIDE_TRANSITION_DURATION,
12
+ MORPH_DURATION,
13
+ STEP_ANIMATION_DURATION,
14
+ STAGGER_DELAY,
15
+ EASE_DEFAULT,
16
+ EASE_OUT,
17
+ EASE_IN,
18
+ EASE_MORPH,
19
+ SPRING_SNAPPY,
20
+ SPRING_SMOOTH,
21
+ SPRING_BOUNCY,
22
+ SLIDE_TRANSITION,
23
+ MORPH_TRANSITION,
24
+ STEP_TRANSITION,
25
+ SLIDE_DISTANCE,
26
+ ELEMENT_SLIDE_DISTANCE,
27
+ SLIDE_DIMENSIONS
28
+ } from "./animation-config"
29
+
30
+ // Animation Context
31
+ export { AnimationProvider, useAnimationContext } from "./animation-context"
32
+
33
+ // Animated Components
34
+ export { Animated, AnimatedGroup } from "./animated"
35
+ export type { AnimationType } from "./animated"
36
+
37
+ // Transitions
38
+ export {
39
+ SLIDE_VARIANTS,
40
+ createDirectionalVariants,
41
+ directionalSlideX,
42
+ directionalSlideY,
43
+ getSlideVariants,
44
+ getSlideTransition,
45
+ DEFAULT_SLIDE_TRANSITION
46
+ } from "./transitions"
47
+ export type { SlideTransitionType, SlideTransitionConfig } from "./transitions"
48
+
49
+ // Morph
50
+ export { Morph, MorphGroup, MorphItem, MorphText } from "./morph"
51
+
52
+ // Navigation Hook
53
+ export { useSlideNavigation } from "./use-slide-navigation"
54
+ export type {
55
+ UseSlideNavigationOptions,
56
+ UseSlideNavigationReturn
57
+ } from "./use-slide-navigation"
58
+
59
+ // Theme
60
+ export { SlideThemeProvider, useTheme } from "./theme-context"
61
+ export type { ThemeConfig } from "./types"
62
+
63
+ // Shared Footer
64
+ export { SlideFooter } from "./layouts/shared-footer"
65
+
66
+ // SlideDeck
67
+ export { SlideDeck } from "./slide-deck"
68
+
69
+ // Error Boundary
70
+ export { SlideErrorBoundary } from "./slide-error-boundary"
71
+
72
+ // Utils
73
+ export { cn } from "./utils"
@@ -0,0 +1,43 @@
1
+ import { useTheme } from "../theme-context"
2
+
3
+ interface SlideFooterProps {
4
+ slideNumber: number
5
+ totalSlides: number
6
+ /** Use light text/logo for dark slide backgrounds */
7
+ variant?: "default" | "light"
8
+ }
9
+
10
+ export function SlideFooter({ slideNumber, totalSlides, variant = "default" }: SlideFooterProps) {
11
+ const theme = useTheme()
12
+ if (!theme) return null
13
+
14
+ const logoUrl = variant === "light"
15
+ ? (theme.logo?.fullLight ?? theme.logo?.full)
16
+ : theme.logo?.full
17
+
18
+ const textClass = variant === "light"
19
+ ? "text-white/70"
20
+ : "text-muted-foreground"
21
+
22
+ const nameClass = variant === "light"
23
+ ? "text-white font-semibold"
24
+ : "text-foreground font-semibold"
25
+
26
+ return (
27
+ <div className={`mt-4 flex shrink-0 items-center justify-between text-sm ${textClass}`}>
28
+ <div className={`flex items-center gap-3 tracking-tight ${nameClass}`}>
29
+ {logoUrl && (
30
+ <img
31
+ src={logoUrl}
32
+ alt={`${theme.name} Logo`}
33
+ className="h-8 w-auto"
34
+ />
35
+ )}
36
+ <span className="text-lg">{theme.name}</span>
37
+ </div>
38
+ <div className="font-mono">
39
+ {slideNumber} / {totalSlides}
40
+ </div>
41
+ </div>
42
+ )
43
+ }
@@ -0,0 +1,153 @@
1
+ import type { Transition } from "framer-motion"
2
+
3
+ import { motion } from "framer-motion"
4
+
5
+ import { MORPH_TRANSITION } from "./animation-config"
6
+
7
+ // =============================================================================
8
+ // TYPES
9
+ // =============================================================================
10
+
11
+ interface MorphProps {
12
+ layoutId: string
13
+ transition?: Transition
14
+ className?: string
15
+ children: React.ReactNode
16
+ }
17
+
18
+ interface MorphGroupProps {
19
+ groupId: string
20
+ transition?: Transition
21
+ className?: string
22
+ children: React.ReactNode
23
+ }
24
+
25
+ // =============================================================================
26
+ // MORPH COMPONENT
27
+ // =============================================================================
28
+
29
+ /**
30
+ * Wrapper component that enables morph/shared-layout animations between slides.
31
+ *
32
+ * Usage:
33
+ * ```tsx
34
+ * // Slide 1 - Large version
35
+ * <Morph layoutId="hero-title">
36
+ * <h1 className="text-6xl">Title</h1>
37
+ * </Morph>
38
+ *
39
+ * // Slide 2 - Small version (same layoutId = morphs between them)
40
+ * <Morph layoutId="hero-title">
41
+ * <h1 className="text-2xl">Title</h1>
42
+ * </Morph>
43
+ * ```
44
+ */
45
+ export function Morph({
46
+ layoutId,
47
+ transition = MORPH_TRANSITION,
48
+ className,
49
+ children
50
+ }: MorphProps) {
51
+ return (
52
+ <motion.div
53
+ layout
54
+ layoutId={layoutId}
55
+ initial={false}
56
+ transition={transition}
57
+ className={className}
58
+ >
59
+ {children}
60
+ </motion.div>
61
+ )
62
+ }
63
+
64
+ // =============================================================================
65
+ // MORPH GROUP COMPONENT
66
+ // =============================================================================
67
+
68
+ export function MorphGroup({
69
+ groupId,
70
+ transition = MORPH_TRANSITION,
71
+ className,
72
+ children
73
+ }: MorphGroupProps) {
74
+ return (
75
+ <motion.div
76
+ layout
77
+ layoutId={groupId}
78
+ initial={false}
79
+ transition={transition}
80
+ className={className}
81
+ >
82
+ {children}
83
+ </motion.div>
84
+ )
85
+ }
86
+
87
+ // =============================================================================
88
+ // MORPH ITEM (for use within MorphGroup pattern)
89
+ // =============================================================================
90
+
91
+ interface MorphItemProps {
92
+ id: string
93
+ prefix?: string
94
+ transition?: Transition
95
+ className?: string
96
+ children: React.ReactNode
97
+ }
98
+
99
+ export function MorphItem({
100
+ id,
101
+ prefix,
102
+ transition = MORPH_TRANSITION,
103
+ className,
104
+ children
105
+ }: MorphItemProps) {
106
+ const layoutId = prefix ? `${prefix}-${id}` : id
107
+
108
+ return (
109
+ <motion.div
110
+ layout
111
+ layoutId={layoutId}
112
+ initial={false}
113
+ transition={transition}
114
+ className={className}
115
+ >
116
+ {children}
117
+ </motion.div>
118
+ )
119
+ }
120
+
121
+ // =============================================================================
122
+ // MORPH TEXT (specialized for text that changes size)
123
+ // =============================================================================
124
+
125
+ interface MorphTextProps {
126
+ layoutId: string
127
+ as?: "h1" | "h2" | "h3" | "h4" | "p" | "span"
128
+ transition?: Transition
129
+ className?: string
130
+ children: React.ReactNode
131
+ }
132
+
133
+ export function MorphText({
134
+ layoutId,
135
+ as: Component = "span",
136
+ transition = MORPH_TRANSITION,
137
+ className,
138
+ children
139
+ }: MorphTextProps) {
140
+ const MotionComponent = motion[Component]
141
+
142
+ return (
143
+ <MotionComponent
144
+ layout
145
+ layoutId={layoutId}
146
+ initial={false}
147
+ transition={transition}
148
+ className={className}
149
+ >
150
+ {children}
151
+ </MotionComponent>
152
+ )
153
+ }