@thewhateverapp/tile-sdk 0.13.0 → 0.13.1

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/dist/index.d.ts CHANGED
@@ -5,8 +5,9 @@ export { useKeyboard } from './react/useKeyboard';
5
5
  export { TileContainer } from './react/TileContainer';
6
6
  export { withTile } from './react/withTile';
7
7
  export { VideoPlayer, useVideoState, useVideo, // Alias for useVideoState
8
- Slideshow, useSlideshowState, OverlaySlot, FullOverlay, GradientOverlay, } from './react/overlay';
9
- export type { VideoState, VideoControls, VideoContextValue, VideoPlayerProps, SlideImage, SlideshowState, SlideshowControls, SlideshowContextValue, SlideshowProps, SlotPosition, OverlaySlotProps, FullOverlayProps, GradientOverlayProps, } from './react/overlay';
8
+ useCuePoint, useCuePoints, useVideoProgress, Slideshow, useSlideshowState, useSlideshow, // Alias for useSlideshowState
9
+ OverlaySlot, FullOverlay, GradientOverlay, } from './react/overlay';
10
+ export type { VideoState, VideoControls, VideoContextValue, VideoPlayerProps, CuePoint, SlideImage, SlideshowState, SlideshowControls, SlideshowContextValue, SlideshowProps, SlotPosition, OverlaySlotProps, FullOverlayProps, GradientOverlayProps, } from './react/overlay';
10
11
  export { getTileBridge, TileBridge } from './bridge/TileBridge';
11
12
  export type { TileMessage, TileConfig, TileTokenData, KeyboardState, VisibilityState } from './bridge/TileBridge';
12
13
  export { StateClient } from './state/StateClient';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,OAAO,EAEL,WAAW,EACX,aAAa,EACb,QAAQ,EAAE,0BAA0B;AAEpC,SAAS,EACT,iBAAiB,EAEjB,WAAW,EACX,WAAW,EACX,eAAe,GAChB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAEV,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,gBAAgB,EAEhB,UAAU,EACV,cAAc,EACd,iBAAiB,EACjB,qBAAqB,EACrB,cAAc,EAEd,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAChE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAGlH,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnE,cAAc,SAAS,CAAC;AAGxB,cAAc,SAAS,CAAC;AAGxB,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,OAAO,EAEL,WAAW,EACX,aAAa,EACb,QAAQ,EAAE,0BAA0B;AACpC,WAAW,EACX,YAAY,EACZ,gBAAgB,EAEhB,SAAS,EACT,iBAAiB,EACjB,YAAY,EAAE,8BAA8B;AAE5C,WAAW,EACX,WAAW,EACX,eAAe,GAChB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAEV,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,gBAAgB,EAChB,QAAQ,EAER,UAAU,EACV,cAAc,EACd,iBAAiB,EACjB,qBAAqB,EACrB,cAAc,EAEd,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAChE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAGlH,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnE,cAAc,SAAS,CAAC;AAGxB,cAAc,SAAS,CAAC;AAGxB,cAAc,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -9,8 +9,9 @@ export { withTile } from './react/withTile';
9
9
  export {
10
10
  // Video player (with route persistence)
11
11
  VideoPlayer, useVideoState, useVideo, // Alias for useVideoState
12
- // Slideshow
13
- Slideshow, useSlideshowState,
12
+ useCuePoint, useCuePoints, useVideoProgress,
13
+ // Slideshow (with route persistence)
14
+ Slideshow, useSlideshowState, useSlideshow, // Alias for useSlideshowState
14
15
  // Positioning components
15
16
  OverlaySlot, FullOverlay, GradientOverlay, } from './react/overlay';
16
17
  // Bridge for secure communication
@@ -1,4 +1,9 @@
1
1
  import React, { type ReactNode } from 'react';
2
+ declare global {
3
+ interface Window {
4
+ __slideshowSingleton?: SlideshowSingletonClass;
5
+ }
6
+ }
2
7
  export interface SlideImage {
3
8
  url: string;
4
9
  alt?: string;
@@ -23,6 +28,49 @@ export interface SlideshowContextValue {
23
28
  state: SlideshowState;
24
29
  controls: SlideshowControls;
25
30
  }
31
+ type StateCallback = (state: SlideshowState) => void;
32
+ /**
33
+ * SlideshowSingleton - Manages slideshow state across route changes.
34
+ *
35
+ * The current slide index and pause state persist across Next.js soft navigations.
36
+ * When navigating from /tile to /page, the slideshow maintains its position.
37
+ */
38
+ declare class SlideshowSingletonClass {
39
+ private stateCallbacks;
40
+ private currentState;
41
+ private intervalRef;
42
+ private intervalMs;
43
+ private transitionDuration;
44
+ /**
45
+ * Initialize slideshow with images
46
+ */
47
+ initialize(images: SlideImage[], options?: {
48
+ intervalMs?: number;
49
+ autoAdvance?: boolean;
50
+ transitionDuration?: number;
51
+ }): void;
52
+ /**
53
+ * Start auto-advance timer
54
+ */
55
+ private startAutoAdvance;
56
+ /**
57
+ * Stop auto-advance timer
58
+ */
59
+ private stopAutoAdvance;
60
+ next(): void;
61
+ prev(): void;
62
+ goTo(index: number): void;
63
+ pause(): void;
64
+ resume(): void;
65
+ toggle(): void;
66
+ getState(): SlideshowState;
67
+ onStateChange(callback: StateCallback): () => void;
68
+ private notifyListeners;
69
+ /**
70
+ * Clean up (call when slideshow is removed)
71
+ */
72
+ destroy(): void;
73
+ }
26
74
  export interface SlideshowProps {
27
75
  /** Array of images to display */
28
76
  images: SlideImage[];
@@ -46,12 +94,24 @@ export interface SlideshowProps {
46
94
  imageClassName?: string;
47
95
  }
48
96
  /**
49
- * Slideshow component with auto-advancing image carousel.
50
- * Provides slideshow state and controls to child overlays via context.
97
+ * Slideshow component with auto-advancing image carousel and route persistence.
98
+ *
99
+ * The current slide index persists across Next.js route changes (tile ↔ page).
100
+ * When navigating between routes, the slideshow maintains its position.
101
+ *
102
+ * Usage:
103
+ * ```tsx
104
+ * <Slideshow images={images} className="w-full h-full">
105
+ * <YourOverlay />
106
+ * </Slideshow>
107
+ * ```
51
108
  */
52
109
  export declare function Slideshow({ images, intervalMs, autoAdvance, transition, transitionDuration, showDots, showArrows, children, className, imageClassName, }: SlideshowProps): React.JSX.Element;
53
110
  /**
54
111
  * Hook to access slideshow state and controls from within Slideshow children.
112
+ * Also aliased as useSlideshow for consistency with VideoPlayer.
55
113
  */
56
114
  export declare function useSlideshowState(): SlideshowContextValue;
115
+ export declare const useSlideshow: typeof useSlideshowState;
116
+ export {};
57
117
  //# sourceMappingURL=Slideshow.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Slideshow.d.ts","sourceRoot":"","sources":["../../../src/react/overlay/Slideshow.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,EAOZ,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,cAAc,CAAC;IACtB,QAAQ,EAAE,iBAAiB,CAAC;CAC7B;AAID,MAAM,WAAW,cAAc;IAC7B,iCAAiC;IACjC,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,wCAAwC;IACxC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IACvC,+CAA+C;IAC/C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,mCAAmC;IACnC,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,EACxB,MAAM,EACN,UAAiB,EACjB,WAAkB,EAClB,UAAmB,EACnB,kBAAwB,EACxB,QAAe,EACf,UAAiB,EACjB,QAAQ,EACR,SAAc,EACd,cAAmB,GACpB,EAAE,cAAc,qBAsLhB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,qBAAqB,CAMzD"}
1
+ {"version":3,"file":"Slideshow.d.ts","sourceRoot":"","sources":["../../../src/react/overlay/Slideshow.tsx"],"names":[],"mappings":"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"}
@@ -1,67 +1,209 @@
1
1
  'use client';
2
- import React, { createContext, useContext, useEffect, useRef, useState, useCallback, } from 'react';
3
- const SlideshowContext = createContext(null);
2
+ import React, { createContext, useContext, useEffect, useState, useCallback, } from 'react';
4
3
  /**
5
- * Slideshow component with auto-advancing image carousel.
6
- * Provides slideshow state and controls to child overlays via context.
4
+ * SlideshowSingleton - Manages slideshow state across route changes.
5
+ *
6
+ * The current slide index and pause state persist across Next.js soft navigations.
7
+ * When navigating from /tile to /page, the slideshow maintains its position.
7
8
  */
8
- export function Slideshow({ images, intervalMs = 5000, autoAdvance = true, transition = 'fade', transitionDuration = 500, showDots = true, showArrows = true, children, className = '', imageClassName = '', }) {
9
- const [currentIndex, setCurrentIndex] = useState(0);
10
- const [isTransitioning, setIsTransitioning] = useState(false);
11
- const [isPaused, setIsPaused] = useState(!autoAdvance);
12
- const intervalRef = useRef(null);
13
- const totalSlides = images.length;
14
- // Control functions
15
- const next = useCallback(() => {
16
- if (isTransitioning || totalSlides <= 1)
9
+ class SlideshowSingletonClass {
10
+ constructor() {
11
+ this.stateCallbacks = new Set();
12
+ this.currentState = {
13
+ currentIndex: 0,
14
+ totalSlides: 0,
15
+ isTransitioning: false,
16
+ isPaused: false,
17
+ images: [],
18
+ };
19
+ this.intervalRef = null;
20
+ this.intervalMs = 5000;
21
+ this.transitionDuration = 500;
22
+ }
23
+ /**
24
+ * Initialize slideshow with images
25
+ */
26
+ initialize(images, options) {
27
+ const imagesChanged = JSON.stringify(images) !== JSON.stringify(this.currentState.images);
28
+ // Only reset if images changed
29
+ if (imagesChanged) {
30
+ this.currentState = {
31
+ ...this.currentState,
32
+ currentIndex: 0,
33
+ totalSlides: images.length,
34
+ images,
35
+ isPaused: options?.autoAdvance === false,
36
+ };
37
+ }
38
+ else {
39
+ // Just update totalSlides in case it changed
40
+ this.currentState = {
41
+ ...this.currentState,
42
+ totalSlides: images.length,
43
+ images,
44
+ };
45
+ }
46
+ if (options?.intervalMs) {
47
+ this.intervalMs = options.intervalMs;
48
+ }
49
+ if (options?.transitionDuration) {
50
+ this.transitionDuration = options.transitionDuration;
51
+ }
52
+ this.notifyListeners();
53
+ this.startAutoAdvance();
54
+ }
55
+ /**
56
+ * Start auto-advance timer
57
+ */
58
+ startAutoAdvance() {
59
+ this.stopAutoAdvance();
60
+ if (this.currentState.isPaused || this.currentState.totalSlides <= 1) {
17
61
  return;
18
- setIsTransitioning(true);
19
- setCurrentIndex(prev => (prev + 1) % totalSlides);
20
- setTimeout(() => setIsTransitioning(false), transitionDuration);
21
- }, [isTransitioning, totalSlides, transitionDuration]);
22
- const prev = useCallback(() => {
23
- if (isTransitioning || totalSlides <= 1)
62
+ }
63
+ this.intervalRef = setInterval(() => {
64
+ this.next();
65
+ }, this.intervalMs);
66
+ }
67
+ /**
68
+ * Stop auto-advance timer
69
+ */
70
+ stopAutoAdvance() {
71
+ if (this.intervalRef) {
72
+ clearInterval(this.intervalRef);
73
+ this.intervalRef = null;
74
+ }
75
+ }
76
+ // Navigation controls
77
+ next() {
78
+ if (this.currentState.isTransitioning || this.currentState.totalSlides <= 1)
24
79
  return;
25
- setIsTransitioning(true);
26
- setCurrentIndex(prev => (prev - 1 + totalSlides) % totalSlides);
27
- setTimeout(() => setIsTransitioning(false), transitionDuration);
28
- }, [isTransitioning, totalSlides, transitionDuration]);
29
- const goTo = useCallback((index) => {
30
- if (isTransitioning || index === currentIndex || index < 0 || index >= totalSlides)
80
+ this.currentState = {
81
+ ...this.currentState,
82
+ isTransitioning: true,
83
+ currentIndex: (this.currentState.currentIndex + 1) % this.currentState.totalSlides,
84
+ };
85
+ this.notifyListeners();
86
+ setTimeout(() => {
87
+ this.currentState = { ...this.currentState, isTransitioning: false };
88
+ this.notifyListeners();
89
+ }, this.transitionDuration);
90
+ }
91
+ prev() {
92
+ if (this.currentState.isTransitioning || this.currentState.totalSlides <= 1)
31
93
  return;
32
- setIsTransitioning(true);
33
- setCurrentIndex(index);
34
- setTimeout(() => setIsTransitioning(false), transitionDuration);
35
- }, [isTransitioning, currentIndex, totalSlides, transitionDuration]);
36
- const pause = useCallback(() => {
37
- setIsPaused(true);
38
- }, []);
39
- const resume = useCallback(() => {
40
- setIsPaused(false);
41
- }, []);
42
- const toggle = useCallback(() => {
43
- setIsPaused(prev => !prev);
44
- }, []);
45
- // Auto-advance timer
46
- useEffect(() => {
47
- if (isPaused || totalSlides <= 1) {
48
- if (intervalRef.current) {
49
- clearInterval(intervalRef.current);
50
- intervalRef.current = null;
51
- }
94
+ this.currentState = {
95
+ ...this.currentState,
96
+ isTransitioning: true,
97
+ currentIndex: (this.currentState.currentIndex - 1 + this.currentState.totalSlides) % this.currentState.totalSlides,
98
+ };
99
+ this.notifyListeners();
100
+ setTimeout(() => {
101
+ this.currentState = { ...this.currentState, isTransitioning: false };
102
+ this.notifyListeners();
103
+ }, this.transitionDuration);
104
+ }
105
+ goTo(index) {
106
+ if (this.currentState.isTransitioning ||
107
+ index === this.currentState.currentIndex ||
108
+ index < 0 ||
109
+ index >= this.currentState.totalSlides)
52
110
  return;
53
- }
54
- intervalRef.current = setInterval(next, intervalMs);
55
- return () => {
56
- if (intervalRef.current) {
57
- clearInterval(intervalRef.current);
58
- intervalRef.current = null;
59
- }
111
+ this.currentState = {
112
+ ...this.currentState,
113
+ isTransitioning: true,
114
+ currentIndex: index,
60
115
  };
61
- }, [isPaused, intervalMs, next, totalSlides]);
116
+ this.notifyListeners();
117
+ setTimeout(() => {
118
+ this.currentState = { ...this.currentState, isTransitioning: false };
119
+ this.notifyListeners();
120
+ }, this.transitionDuration);
121
+ }
122
+ pause() {
123
+ this.currentState = { ...this.currentState, isPaused: true };
124
+ this.stopAutoAdvance();
125
+ this.notifyListeners();
126
+ }
127
+ resume() {
128
+ this.currentState = { ...this.currentState, isPaused: false };
129
+ this.startAutoAdvance();
130
+ this.notifyListeners();
131
+ }
132
+ toggle() {
133
+ if (this.currentState.isPaused) {
134
+ this.resume();
135
+ }
136
+ else {
137
+ this.pause();
138
+ }
139
+ }
140
+ // State management
141
+ getState() {
142
+ return { ...this.currentState };
143
+ }
144
+ onStateChange(callback) {
145
+ this.stateCallbacks.add(callback);
146
+ callback(this.currentState);
147
+ return () => this.stateCallbacks.delete(callback);
148
+ }
149
+ notifyListeners() {
150
+ this.stateCallbacks.forEach(cb => cb(this.currentState));
151
+ }
152
+ /**
153
+ * Clean up (call when slideshow is removed)
154
+ */
155
+ destroy() {
156
+ this.stopAutoAdvance();
157
+ this.stateCallbacks.clear();
158
+ }
159
+ }
160
+ /**
161
+ * Get the SlideshowSingleton instance (creates if needed)
162
+ */
163
+ function getSlideshowSingleton() {
164
+ if (typeof window === 'undefined') {
165
+ return new SlideshowSingletonClass();
166
+ }
167
+ if (!window.__slideshowSingleton) {
168
+ window.__slideshowSingleton = new SlideshowSingletonClass();
169
+ }
170
+ return window.__slideshowSingleton;
171
+ }
172
+ // =============================================================================
173
+ // React Context and Component
174
+ // =============================================================================
175
+ const SlideshowContext = createContext(null);
176
+ /**
177
+ * Slideshow component with auto-advancing image carousel and route persistence.
178
+ *
179
+ * The current slide index persists across Next.js route changes (tile ↔ page).
180
+ * When navigating between routes, the slideshow maintains its position.
181
+ *
182
+ * Usage:
183
+ * ```tsx
184
+ * <Slideshow images={images} className="w-full h-full">
185
+ * <YourOverlay />
186
+ * </Slideshow>
187
+ * ```
188
+ */
189
+ export function Slideshow({ images, intervalMs = 5000, autoAdvance = true, transition = 'fade', transitionDuration = 500, showDots = true, showArrows = true, children, className = '', imageClassName = '', }) {
190
+ const [state, setState] = useState(() => getSlideshowSingleton().getState());
191
+ // Initialize and subscribe to singleton
192
+ useEffect(() => {
193
+ const singleton = getSlideshowSingleton();
194
+ // Initialize with images and options
195
+ singleton.initialize(images, {
196
+ intervalMs,
197
+ autoAdvance,
198
+ transitionDuration,
199
+ });
200
+ // Subscribe to state changes
201
+ const unsubscribe = singleton.onStateChange(setState);
202
+ return unsubscribe;
203
+ }, [images, intervalMs, autoAdvance, transitionDuration]);
62
204
  // Get transition styles
63
205
  const getTransitionStyles = (index) => {
64
- const isActive = index === currentIndex;
206
+ const isActive = index === state.currentIndex;
65
207
  switch (transition) {
66
208
  case 'fade':
67
209
  return {
@@ -71,7 +213,7 @@ export function Slideshow({ images, intervalMs = 5000, autoAdvance = true, trans
71
213
  inset: 0,
72
214
  };
73
215
  case 'slide':
74
- const offset = (index - currentIndex) * 100;
216
+ const offset = (index - state.currentIndex) * 100;
75
217
  return {
76
218
  transform: `translateX(${offset}%)`,
77
219
  transition: `transform ${transitionDuration}ms ease-in-out`,
@@ -87,15 +229,19 @@ export function Slideshow({ images, intervalMs = 5000, autoAdvance = true, trans
87
229
  };
88
230
  }
89
231
  };
232
+ // Control functions bound to singleton
233
+ const singleton = getSlideshowSingleton();
234
+ const controls = {
235
+ next: useCallback(() => singleton.next(), []),
236
+ prev: useCallback(() => singleton.prev(), []),
237
+ goTo: useCallback((index) => singleton.goTo(index), []),
238
+ pause: useCallback(() => singleton.pause(), []),
239
+ resume: useCallback(() => singleton.resume(), []),
240
+ toggle: useCallback(() => singleton.toggle(), []),
241
+ };
90
242
  const contextValue = {
91
- state: {
92
- currentIndex,
93
- totalSlides,
94
- isTransitioning,
95
- isPaused,
96
- images,
97
- },
98
- controls: { next, prev, goTo, pause, resume, toggle },
243
+ state,
244
+ controls,
99
245
  };
100
246
  if (images.length === 0) {
101
247
  return (React.createElement("div", { className: `relative w-full h-full bg-black flex items-center justify-center ${className}` },
@@ -105,20 +251,22 @@ export function Slideshow({ images, intervalMs = 5000, autoAdvance = true, trans
105
251
  React.createElement("div", { className: `relative w-full h-full bg-black overflow-hidden ${className}` },
106
252
  React.createElement("div", { className: "relative w-full h-full" }, images.map((image, index) => (React.createElement("div", { key: `${image.url}-${index}`, style: getTransitionStyles(index), className: imageClassName },
107
253
  React.createElement("img", { src: image.url, alt: image.alt || `Slide ${index + 1}`, className: "w-full h-full object-contain", loading: index === 0 ? 'eager' : 'lazy' }))))),
108
- showArrows && totalSlides > 1 && (React.createElement(React.Fragment, null,
109
- React.createElement("button", { onClick: prev, disabled: isTransitioning, className: "absolute left-2 top-1/2 -translate-y-1/2 p-2 bg-black/40 hover:bg-black/60 rounded-full text-white transition-colors disabled:opacity-50", "aria-label": "Previous slide" },
254
+ showArrows && state.totalSlides > 1 && (React.createElement(React.Fragment, null,
255
+ React.createElement("button", { onClick: controls.prev, disabled: state.isTransitioning, className: "absolute left-2 top-1/2 -translate-y-1/2 p-2 bg-black/40 hover:bg-black/60 rounded-full text-white transition-colors disabled:opacity-50", "aria-label": "Previous slide" },
110
256
  React.createElement("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24" },
111
257
  React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }))),
112
- React.createElement("button", { onClick: next, disabled: isTransitioning, className: "absolute right-2 top-1/2 -translate-y-1/2 p-2 bg-black/40 hover:bg-black/60 rounded-full text-white transition-colors disabled:opacity-50", "aria-label": "Next slide" },
258
+ React.createElement("button", { onClick: controls.next, disabled: state.isTransitioning, className: "absolute right-2 top-1/2 -translate-y-1/2 p-2 bg-black/40 hover:bg-black/60 rounded-full text-white transition-colors disabled:opacity-50", "aria-label": "Next slide" },
113
259
  React.createElement("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24" },
114
260
  React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }))))),
115
- showDots && totalSlides > 1 && (React.createElement("div", { className: "absolute bottom-3 left-1/2 -translate-x-1/2 flex gap-1.5" }, images.map((_, index) => (React.createElement("button", { key: index, onClick: () => goTo(index), disabled: isTransitioning, className: `w-2 h-2 rounded-full transition-colors ${index === currentIndex
261
+ showDots && state.totalSlides > 1 && (React.createElement("div", { className: "absolute bottom-3 left-1/2 -translate-x-1/2 flex gap-1.5" }, images.map((_, index) => (React.createElement("button", { key: index, onClick: () => controls.goTo(index), disabled: state.isTransitioning, className: `w-2 h-2 rounded-full transition-colors ${index === state.currentIndex
116
262
  ? 'bg-white'
117
263
  : 'bg-white/40 hover:bg-white/60'}`, "aria-label": `Go to slide ${index + 1}` }))))),
118
- children)));
264
+ React.createElement("div", { className: "absolute inset-0 z-10 pointer-events-none" },
265
+ React.createElement("div", { className: "pointer-events-auto" }, children)))));
119
266
  }
120
267
  /**
121
268
  * Hook to access slideshow state and controls from within Slideshow children.
269
+ * Also aliased as useSlideshow for consistency with VideoPlayer.
122
270
  */
123
271
  export function useSlideshowState() {
124
272
  const context = useContext(SlideshowContext);
@@ -127,3 +275,5 @@ export function useSlideshowState() {
127
275
  }
128
276
  return context;
129
277
  }
278
+ // Alias for consistency with useVideo/useVideoState
279
+ export const useSlideshow = useSlideshowState;
@@ -1,7 +1,7 @@
1
1
  export { VideoPlayer, useVideoState, useVideo, // Alias for useVideoState (compatibility)
2
2
  useCuePoint, useCuePoints, useVideoProgress, } from './VideoPlayer';
3
3
  export type { VideoState, VideoControls, VideoContextValue, VideoPlayerProps, CuePoint, } from './VideoPlayer';
4
- export { Slideshow, useSlideshowState, } from './Slideshow';
4
+ export { Slideshow, useSlideshowState, useSlideshow, } from './Slideshow';
5
5
  export type { SlideImage, SlideshowState, SlideshowControls, SlideshowContextValue, SlideshowProps, } from './Slideshow';
6
6
  export { OverlaySlot, FullOverlay, GradientOverlay, } from './OverlaySlot';
7
7
  export type { SlotPosition, OverlaySlotProps, FullOverlayProps, GradientOverlayProps, } from './OverlaySlot';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/overlay/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,WAAW,EACX,aAAa,EACb,QAAQ,EAAE,0CAA0C;AACpD,WAAW,EACX,YAAY,EACZ,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,gBAAgB,EAChB,QAAQ,GACT,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,SAAS,EACT,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,UAAU,EACV,cAAc,EACd,iBAAiB,EACjB,qBAAqB,EACrB,cAAc,GACf,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,WAAW,EACX,WAAW,EACX,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/overlay/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,WAAW,EACX,aAAa,EACb,QAAQ,EAAE,0CAA0C;AACpD,WAAW,EACX,YAAY,EACZ,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,gBAAgB,EAChB,QAAQ,GACT,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,SAAS,EACT,iBAAiB,EACjB,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,UAAU,EACV,cAAc,EACd,iBAAiB,EACjB,qBAAqB,EACrB,cAAc,GACf,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,WAAW,EACX,WAAW,EACX,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,eAAe,CAAC"}
@@ -1,7 +1,8 @@
1
1
  // Video player with HLS streaming support and route persistence
2
2
  export { VideoPlayer, useVideoState, useVideo, // Alias for useVideoState (compatibility)
3
3
  useCuePoint, useCuePoints, useVideoProgress, } from './VideoPlayer';
4
- // Image slideshow component
5
- export { Slideshow, useSlideshowState, } from './Slideshow';
4
+ // Image slideshow component with route persistence
5
+ export { Slideshow, useSlideshowState, useSlideshow, // Alias for useSlideshowState (consistency)
6
+ } from './Slideshow';
6
7
  // Overlay positioning components
7
8
  export { OverlaySlot, FullOverlay, GradientOverlay, } from './OverlaySlot';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thewhateverapp/tile-sdk",
3
- "version": "0.13.0",
3
+ "version": "0.13.1",
4
4
  "description": "SDK for building interactive tiles on The Whatever App platform",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",