@movementinfra/expo-twostep-video 0.1.13 → 0.1.14
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/README.md +69 -0
- package/build/VideoScrubber.d.ts.map +1 -1
- package/build/VideoScrubber.js +4 -2
- package/build/VideoScrubber.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -224,6 +224,72 @@ TwoStepVideo.releaseAll(); // Release everything
|
|
|
224
224
|
TwoStepVideo.cleanupFile(result.uri); // Delete temp file
|
|
225
225
|
```
|
|
226
226
|
|
|
227
|
+
## Video Scrubber Component
|
|
228
|
+
|
|
229
|
+
### VideoScrubber
|
|
230
|
+
|
|
231
|
+
A native-style video scrubber/trimmer with thumbnail strip, draggable trim handles, and a draggable playhead for frame-accurate seeking:
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
import { VideoScrubber, VideoScrubberRef } from 'expo-twostep-video';
|
|
235
|
+
|
|
236
|
+
function VideoEditor({ asset }: { asset: VideoAsset }) {
|
|
237
|
+
const playerRef = useRef<TwoStepVideoViewRef>(null);
|
|
238
|
+
const [trimStart, setTrimStart] = useState(0);
|
|
239
|
+
const [trimEnd, setTrimEnd] = useState(asset.duration);
|
|
240
|
+
const [currentTime, setCurrentTime] = useState(0);
|
|
241
|
+
|
|
242
|
+
return (
|
|
243
|
+
<View>
|
|
244
|
+
<TwoStepVideoView
|
|
245
|
+
ref={playerRef}
|
|
246
|
+
assetId={asset.id}
|
|
247
|
+
onProgress={(e) => setCurrentTime(e.nativeEvent.currentTime)}
|
|
248
|
+
/>
|
|
249
|
+
|
|
250
|
+
<VideoScrubber
|
|
251
|
+
assetId={asset.id}
|
|
252
|
+
duration={asset.duration}
|
|
253
|
+
currentTime={currentTime}
|
|
254
|
+
startTime={trimStart}
|
|
255
|
+
endTime={trimEnd}
|
|
256
|
+
onStartTimeChange={setTrimStart}
|
|
257
|
+
onEndTimeChange={setTrimEnd}
|
|
258
|
+
onScrub={(time) => playerRef.current?.seek(time)}
|
|
259
|
+
onScrubbing={(time) => playerRef.current?.seek(time)}
|
|
260
|
+
/>
|
|
261
|
+
</View>
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**Props:**
|
|
267
|
+
|
|
268
|
+
| Prop | Type | Description |
|
|
269
|
+
|------|------|-------------|
|
|
270
|
+
| `assetId` | `string` | Asset ID for thumbnail generation |
|
|
271
|
+
| `duration` | `number` | Total video duration in seconds |
|
|
272
|
+
| `currentTime` | `number` | Current playback position (for playhead) |
|
|
273
|
+
| `startTime` | `number` | Selected start time in seconds |
|
|
274
|
+
| `endTime` | `number` | Selected end time in seconds |
|
|
275
|
+
| `thumbnailCount` | `number` | Number of thumbnails (default: 10) |
|
|
276
|
+
| `thumbnailHeight` | `number` | Height of thumbnail strip (default: 50) |
|
|
277
|
+
| `onStartTimeChange` | `function` | Called when start handle moves |
|
|
278
|
+
| `onEndTimeChange` | `function` | Called when end handle moves |
|
|
279
|
+
| `onScrub` | `function` | Called when playhead is tapped or dragged |
|
|
280
|
+
| `onScrubbing` | `function` | Called continuously during any drag |
|
|
281
|
+
| `onScrubEnd` | `function` | Called when drag ends |
|
|
282
|
+
| `minDuration` | `number` | Minimum selection duration (default: 0.5) |
|
|
283
|
+
| `showPlayhead` | `boolean` | Show playhead indicator (default: true) |
|
|
284
|
+
| `handleWidth` | `number` | Handle width in pixels (default: 20) |
|
|
285
|
+
| `theme` | `object` | Custom colors for handles, playhead, etc. |
|
|
286
|
+
|
|
287
|
+
**Features:**
|
|
288
|
+
- Drag the **yellow handles** to adjust trim start/end times
|
|
289
|
+
- Drag the **white playhead** to scrub through the video
|
|
290
|
+
- Tap anywhere in the selection to seek to that position
|
|
291
|
+
- All drag operations call `onScrubbing` for smooth video preview
|
|
292
|
+
|
|
227
293
|
## Video Player Component
|
|
228
294
|
|
|
229
295
|
### TwoStepVideoView
|
|
@@ -331,6 +397,9 @@ import type {
|
|
|
331
397
|
PanZoomState,
|
|
332
398
|
MirrorAxis,
|
|
333
399
|
VideoQuality,
|
|
400
|
+
VideoScrubberProps,
|
|
401
|
+
VideoScrubberRef,
|
|
402
|
+
VideoScrubberTheme,
|
|
334
403
|
} from 'expo-twostep-video';
|
|
335
404
|
```
|
|
336
405
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoScrubber.d.ts","sourceRoot":"","sources":["../src/VideoScrubber.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAqB/B,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAsB,MAAM,uBAAuB,CAAC;AAYjG;;;;;;;;;;;;;;;;GAgBG;AACH,QAAA,MAAM,aAAa,
|
|
1
|
+
{"version":3,"file":"VideoScrubber.d.ts","sourceRoot":"","sources":["../src/VideoScrubber.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAqB/B,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAsB,MAAM,uBAAuB,CAAC;AAYjG;;;;;;;;;;;;;;;;GAgBG;AACH,QAAA,MAAM,aAAa,6FAqdjB,CAAC;AAuGH,eAAe,aAAa,CAAC"}
|
package/build/VideoScrubber.js
CHANGED
|
@@ -368,10 +368,11 @@ const VideoScrubber = forwardRef((props, ref) => {
|
|
|
368
368
|
left: startPosition,
|
|
369
369
|
width: Math.max(0, endPosition - startPosition),
|
|
370
370
|
height: thumbnailHeight,
|
|
371
|
+
zIndex: 1,
|
|
371
372
|
},
|
|
372
373
|
]} onTouchEnd={handleTap}/>
|
|
373
374
|
|
|
374
|
-
{/* Playhead */}
|
|
375
|
+
{/* Playhead - rendered last for highest z-index */}
|
|
375
376
|
{showPlayhead && playheadPosition >= startPosition && playheadPosition <= endPosition && (<Animated.View style={[
|
|
376
377
|
styles.playhead,
|
|
377
378
|
{
|
|
@@ -380,8 +381,9 @@ const VideoScrubber = forwardRef((props, ref) => {
|
|
|
380
381
|
top: -4,
|
|
381
382
|
backgroundColor: theme.playheadColor,
|
|
382
383
|
opacity: dragging === 'playhead' ? 0.8 : 1,
|
|
384
|
+
zIndex: 10,
|
|
383
385
|
},
|
|
384
|
-
]} {...playheadPanResponder.panHandlers}>
|
|
386
|
+
]} hitSlop={{ top: 20, bottom: 20, left: 20, right: 20 }} {...playheadPanResponder.panHandlers}>
|
|
385
387
|
<View style={[styles.playheadKnob, { backgroundColor: theme.playheadColor }]}/>
|
|
386
388
|
</Animated.View>)}
|
|
387
389
|
</View>);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoScrubber.js","sourceRoot":"","sources":["../src/VideoScrubber.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react';\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport {\n View,\n Image,\n StyleSheet,\n LayoutChangeEvent,\n PanResponder,\n PanResponderGestureState,\n GestureResponderEvent,\n Animated,\n} from 'react-native';\n\nimport { VideoScrubberProps, VideoScrubberRef, VideoScrubberTheme } from './VideoScrubber.types';\nimport ExpoTwoStepVideoModule from './ExpoTwoStepVideoModule';\n\nconst DEFAULT_THEME: Required<VideoScrubberTheme> = {\n handleColor: '#FFD700',\n selectedRegionColor: 'rgba(255,215,0,0.2)',\n playheadColor: '#FFFFFF',\n backgroundColor: '#1c1c1e',\n dimmedColor: 'rgba(0,0,0,0.6)',\n borderColor: '#FFD700',\n};\n\n/**\n * Video scrubber/trimmer component with thumbnail strip and draggable handles\n *\n * @example\n * ```tsx\n * <VideoScrubber\n * assetId={asset.id}\n * duration={asset.duration}\n * currentTime={currentTime}\n * startTime={trimStart}\n * endTime={trimEnd}\n * onStartTimeChange={setTrimStart}\n * onEndTimeChange={setTrimEnd}\n * onScrubbing={(time) => playerRef.current?.seek(time)}\n * />\n * ```\n */\nconst VideoScrubber = forwardRef<VideoScrubberRef, VideoScrubberProps>((props, ref) => {\n const {\n assetId,\n duration,\n currentTime,\n startTime,\n endTime,\n thumbnailCount = 10,\n thumbnailHeight = 50,\n onStartTimeChange,\n onEndTimeChange,\n onScrub,\n onScrubbing,\n onScrubEnd,\n minDuration = 0.5,\n style,\n disabled = false,\n theme: userTheme,\n showPlayhead = true,\n handleWidth = 20,\n } = props;\n\n const theme = useMemo(\n () => ({ ...DEFAULT_THEME, ...userTheme }),\n [userTheme]\n );\n\n const [thumbnails, setThumbnails] = useState<string[]>([]);\n const [containerWidth, setContainerWidth] = useState(0);\n const [isLoading, setIsLoading] = useState(true);\n const [dragging, setDragging] = useState<'start' | 'end' | 'playhead' | null>(null);\n\n // Track current values during drag - synced with props when not dragging\n const dragStartTimeRef = useRef(startTime);\n const dragEndTimeRef = useRef(endTime);\n const currentTimeRef = useRef(currentTime);\n const durationRef = useRef(duration);\n const containerWidthRef = useRef(containerWidth);\n const handleWidthRef = useRef(handleWidth);\n\n // Sync refs with props when they change from outside (e.g., new video loaded)\n useEffect(() => {\n if (!dragging) {\n dragStartTimeRef.current = startTime;\n dragEndTimeRef.current = endTime;\n }\n }, [startTime, endTime, dragging]);\n\n // Keep other refs in sync\n useEffect(() => {\n currentTimeRef.current = currentTime;\n }, [currentTime]);\n\n useEffect(() => {\n durationRef.current = duration;\n }, [duration]);\n\n useEffect(() => {\n containerWidthRef.current = containerWidth;\n }, [containerWidth]);\n\n useEffect(() => {\n handleWidthRef.current = handleWidth;\n }, [handleWidth]);\n\n // Calculate thumbnail times\n const thumbnailTimes = useMemo(() => {\n if (duration <= 0) return [];\n const count = Math.min(thumbnailCount, Math.max(1, Math.ceil(duration)));\n const interval = duration / count;\n return Array.from({ length: count }, (_, i) => i * interval + interval / 2);\n }, [duration, thumbnailCount]);\n\n // Load thumbnails\n useEffect(() => {\n if (!assetId || thumbnailTimes.length === 0 || duration <= 0) {\n setThumbnails([]);\n setIsLoading(false);\n return;\n }\n\n let cancelled = false;\n setIsLoading(true);\n\n ExpoTwoStepVideoModule.generateThumbnails(\n assetId,\n thumbnailTimes,\n { width: thumbnailHeight * 1.5, height: thumbnailHeight }\n )\n .then((thumbs: string[]) => {\n if (!cancelled) {\n setThumbnails(thumbs);\n setIsLoading(false);\n }\n })\n .catch((err: Error) => {\n if (!cancelled) {\n console.error('Failed to generate thumbnails:', err);\n setIsLoading(false);\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [assetId, thumbnailTimes, thumbnailHeight, duration]);\n\n // The content area is between the handles\n const contentWidth = containerWidth > 0 ? containerWidth - handleWidth * 2 : 0;\n\n // Convert time to position within the content area (between handles)\n const timeToPosition = useCallback(\n (time: number) => {\n if (duration <= 0 || contentWidth <= 0) return handleWidth;\n return handleWidth + (time / duration) * contentWidth;\n },\n [duration, contentWidth, handleWidth]\n );\n\n // Calculate positions\n const startPosition = timeToPosition(startTime);\n const endPosition = timeToPosition(endTime);\n const playheadPosition = timeToPosition(currentTime);\n\n // Handle layout change\n const handleLayout = useCallback((e: LayoutChangeEvent) => {\n setContainerWidth(e.nativeEvent.layout.width);\n }, []);\n\n // Store callbacks in refs to avoid recreating PanResponder on every render\n const onStartTimeChangeRef = useRef(onStartTimeChange);\n const onEndTimeChangeRef = useRef(onEndTimeChange);\n const onScrubRef = useRef(onScrub);\n const onScrubbingRef = useRef(onScrubbing);\n const onScrubEndRef = useRef(onScrubEnd);\n\n\n useEffect(() => {\n onStartTimeChangeRef.current = onStartTimeChange;\n onEndTimeChangeRef.current = onEndTimeChange;\n onScrubRef.current = onScrub;\n onScrubbingRef.current = onScrubbing;\n onScrubEndRef.current = onScrubEnd;\n }, [onStartTimeChange, onEndTimeChange, onScrub, onScrubbing, onScrubEnd]);\n\n // Create pan responders for handles and playhead\n // Using refs to avoid stale closures - PanResponder is only created once per type\n const createPanResponder = useCallback(\n (type: 'start' | 'end' | 'playhead') => {\n let initialTime = 0;\n\n return PanResponder.create({\n onStartShouldSetPanResponder: () => !disabled,\n onMoveShouldSetPanResponder: (_, gestureState) => {\n // Claim the gesture if there's any horizontal movement\n // This prevents ScrollView from capturing horizontal drags\n return !disabled && Math.abs(gestureState.dx) > 2;\n },\n // Capture phase handlers - these fire before parent views\n onStartShouldSetPanResponderCapture: () => false, // Don't capture on start, let it bubble\n onMoveShouldSetPanResponderCapture: (_, gestureState) => {\n // Only capture if horizontal movement is dominant\n return !disabled && Math.abs(gestureState.dx) > 5 && Math.abs(gestureState.dx) > Math.abs(gestureState.dy);\n },\n onPanResponderGrant: () => {\n // Read current values from refs at the moment the gesture starts\n if (type === 'start') {\n initialTime = dragStartTimeRef.current;\n } else if (type === 'end') {\n initialTime = dragEndTimeRef.current;\n } else {\n initialTime = currentTimeRef.current;\n }\n setDragging(type);\n },\n onPanResponderMove: (_e: GestureResponderEvent, gestureState: PanResponderGestureState) => {\n const containerW = containerWidthRef.current;\n const hw = handleWidthRef.current;\n const dur = durationRef.current;\n // Content width is where time is mapped (between handles)\n const cw = containerW - hw * 2;\n if (cw <= 0 || dur <= 0) return;\n\n const deltaX = gestureState.dx;\n const deltaTime = (deltaX / cw) * dur;\n let newTime = initialTime + deltaTime;\n\n if (type === 'start') {\n // Constrain start handle\n newTime = Math.max(0, Math.min(newTime, dragEndTimeRef.current - minDuration));\n dragStartTimeRef.current = newTime;\n onStartTimeChangeRef.current?.(newTime);\n onScrubbingRef.current?.(newTime);\n } else if (type === 'end') {\n // Constrain end handle\n newTime = Math.max(dragStartTimeRef.current + minDuration, Math.min(newTime, dur));\n dragEndTimeRef.current = newTime;\n onEndTimeChangeRef.current?.(newTime);\n onScrubbingRef.current?.(newTime);\n } else {\n // Playhead - constrain to selection\n newTime = Math.max(dragStartTimeRef.current, Math.min(newTime, dragEndTimeRef.current));\n onScrubRef.current?.(newTime);\n onScrubbingRef.current?.(newTime);\n }\n },\n onPanResponderRelease: (_e: GestureResponderEvent, gestureState: PanResponderGestureState) => {\n setDragging(null);\n if (type === 'start') {\n onScrubEndRef.current?.(dragStartTimeRef.current);\n } else if (type === 'end') {\n onScrubEndRef.current?.(dragEndTimeRef.current);\n } else {\n const containerW = containerWidthRef.current;\n const hw = handleWidthRef.current;\n const dur = durationRef.current;\n const cw = containerW - hw * 2;\n if (cw > 0 && dur > 0) {\n const deltaTime = (gestureState.dx / cw) * dur;\n const newTime = Math.max(\n dragStartTimeRef.current,\n Math.min(initialTime + deltaTime, dragEndTimeRef.current)\n );\n onScrubEndRef.current?.(newTime);\n }\n }\n },\n onPanResponderTerminate: () => {\n setDragging(null);\n },\n });\n },\n [disabled, minDuration] // Only recreate if disabled or minDuration changes\n );\n\n const startPanResponder = useMemo(\n () => createPanResponder('start'),\n [createPanResponder]\n );\n const endPanResponder = useMemo(\n () => createPanResponder('end'),\n [createPanResponder]\n );\n const playheadPanResponder = useMemo(\n () => createPanResponder('playhead'),\n [createPanResponder]\n );\n\n // Tap to seek - locationX is relative to the tap area which spans from startPosition to endPosition\n const handleTap = useCallback(\n (e: GestureResponderEvent) => {\n if (disabled) return;\n const { locationX } = e.nativeEvent;\n const startT = dragStartTimeRef.current;\n const endT = dragEndTimeRef.current;\n const selectionDuration = endT - startT;\n if (selectionDuration <= 0) return;\n\n // The tap area spans the selection, so locationX=0 is startTime and locationX=width is endTime\n // Calculate time proportionally within the selection\n const width = containerWidthRef.current;\n const dur = durationRef.current;\n if (width <= 0 || dur <= 0) return;\n\n // Calculate the width of the tap area (endPosition - startPosition in content coordinates)\n const cw = width - handleWidth * 2; // contentWidth\n const tapAreaWidth = ((endT - startT) / dur) * cw;\n if (tapAreaWidth <= 0) return;\n\n const proportion = Math.max(0, Math.min(1, locationX / tapAreaWidth));\n const time = startT + proportion * selectionDuration;\n onScrubRef.current?.(time);\n },\n [disabled, handleWidth]\n );\n\n // Expose ref methods\n useImperativeHandle(ref, () => ({\n regenerateThumbnails: async () => {\n if (!assetId || thumbnailTimes.length === 0) return;\n setIsLoading(true);\n try {\n const thumbs = await ExpoTwoStepVideoModule.generateThumbnails(\n assetId,\n thumbnailTimes,\n { width: thumbnailHeight * 1.5, height: thumbnailHeight }\n );\n setThumbnails(thumbs);\n } catch (err) {\n console.error('Failed to regenerate thumbnails:', err);\n } finally {\n setIsLoading(false);\n }\n },\n setPlayheadPosition: (time: number) => {\n onScrub?.(Math.max(startTime, Math.min(time, endTime)));\n },\n getSelection: () => ({ startTime, endTime }),\n }));\n\n const thumbnailWidth = contentWidth > 0 && thumbnails.length > 0\n ? contentWidth / thumbnails.length\n : 0;\n\n return (\n <View\n style={[\n styles.container,\n { backgroundColor: theme.backgroundColor },\n style,\n ]}\n onLayout={handleLayout}\n >\n {/* Thumbnail Strip - positioned between handles */}\n <View style={[styles.thumbnailStrip, { height: thumbnailHeight, marginLeft: handleWidth, width: contentWidth }]}>\n {thumbnails.map((thumb, index) => (\n <Image\n key={index}\n source={{ uri: `data:image/png;base64,${thumb}` }}\n style={[\n styles.thumbnail,\n {\n width: thumbnailWidth,\n height: thumbnailHeight,\n },\n ]}\n resizeMode=\"cover\"\n />\n ))}\n {isLoading && thumbnails.length === 0 && (\n <View style={[styles.loadingPlaceholder, { height: thumbnailHeight }]} />\n )}\n </View>\n\n {/* Dimmed region - left (covers trimmed content from left handle to start handle position) */}\n <View\n style={[\n styles.dimmedRegion,\n {\n left: handleWidth,\n width: Math.max(0, startPosition - handleWidth),\n height: thumbnailHeight,\n backgroundColor: theme.dimmedColor,\n },\n ]}\n pointerEvents=\"none\"\n />\n\n {/* Dimmed region - right (covers trimmed content from end handle position to right handle) */}\n <View\n style={[\n styles.dimmedRegion,\n {\n left: endPosition,\n width: Math.max(0, containerWidth - handleWidth - endPosition),\n height: thumbnailHeight,\n backgroundColor: theme.dimmedColor,\n },\n ]}\n pointerEvents=\"none\"\n />\n\n {/* Selection frame - top and bottom borders (span between handles) */}\n <View\n style={[\n styles.selectionBorder,\n styles.selectionBorderTop,\n {\n left: startPosition,\n width: Math.max(0, endPosition - startPosition),\n backgroundColor: theme.borderColor,\n },\n ]}\n pointerEvents=\"none\"\n />\n <View\n style={[\n styles.selectionBorder,\n styles.selectionBorderBottom,\n {\n left: startPosition,\n width: Math.max(0, endPosition - startPosition),\n top: thumbnailHeight - 2,\n backgroundColor: theme.borderColor,\n },\n ]}\n pointerEvents=\"none\"\n />\n\n {/* Start Handle - positioned to the LEFT of the trim point (outside content when startTime=0) */}\n <Animated.View\n style={[\n styles.handle,\n styles.startHandle,\n {\n left: startPosition - handleWidth,\n width: handleWidth,\n height: thumbnailHeight,\n backgroundColor: theme.handleColor,\n opacity: dragging === 'start' ? 0.8 : 1,\n },\n ]}\n hitSlop={{ top: 15, bottom: 15, left: 15, right: 10 }}\n {...startPanResponder.panHandlers}\n >\n <View style={styles.handleChevron}>\n <View style={[styles.chevronLine, styles.chevronLineTopLeft]} />\n <View style={[styles.chevronLine, styles.chevronLineBottomLeft]} />\n </View>\n </Animated.View>\n\n {/* End Handle - positioned to the RIGHT of the trim point (outside content when endTime=duration) */}\n <Animated.View\n style={[\n styles.handle,\n styles.endHandle,\n {\n left: endPosition,\n width: handleWidth,\n height: thumbnailHeight,\n backgroundColor: theme.handleColor,\n opacity: dragging === 'end' ? 0.8 : 1,\n },\n ]}\n hitSlop={{ top: 15, bottom: 15, left: 10, right: 15 }}\n {...endPanResponder.panHandlers}\n >\n <View style={styles.handleChevron}>\n <View style={[styles.chevronLine, styles.chevronLineTopRight]} />\n <View style={[styles.chevronLine, styles.chevronLineBottomRight]} />\n </View>\n </Animated.View>\n\n {/* Tap area for seeking - covers the selection between start and end trim points */}\n <View\n style={[\n styles.tapArea,\n {\n left: startPosition,\n width: Math.max(0, endPosition - startPosition),\n height: thumbnailHeight,\n },\n ]}\n onTouchEnd={handleTap}\n />\n\n {/* Playhead */}\n {showPlayhead && playheadPosition >= startPosition && playheadPosition <= endPosition && (\n <Animated.View\n style={[\n styles.playhead,\n {\n left: playheadPosition - 1,\n height: thumbnailHeight + 8,\n top: -4,\n backgroundColor: theme.playheadColor,\n opacity: dragging === 'playhead' ? 0.8 : 1,\n },\n ]}\n {...playheadPanResponder.panHandlers}\n >\n <View style={[styles.playheadKnob, { backgroundColor: theme.playheadColor }]} />\n </Animated.View>\n )}\n </View>\n );\n});\n\nVideoScrubber.displayName = 'VideoScrubber';\n\nconst styles = StyleSheet.create({\n container: {\n width: '100%',\n borderRadius: 8,\n overflow: 'hidden',\n position: 'relative',\n },\n thumbnailStrip: {\n flexDirection: 'row',\n width: '100%',\n },\n thumbnail: {\n backgroundColor: '#2c2c2e',\n },\n loadingPlaceholder: {\n flex: 1,\n backgroundColor: '#2c2c2e',\n },\n dimmedRegion: {\n position: 'absolute',\n top: 0,\n },\n selectionBorder: {\n position: 'absolute',\n height: 2,\n },\n selectionBorderTop: {\n top: 0,\n },\n selectionBorderBottom: {\n bottom: 0,\n },\n handle: {\n position: 'absolute',\n top: 0,\n justifyContent: 'center',\n alignItems: 'center',\n borderRadius: 4,\n },\n startHandle: {\n borderTopLeftRadius: 6,\n borderBottomLeftRadius: 6,\n borderTopRightRadius: 0,\n borderBottomRightRadius: 0,\n },\n endHandle: {\n borderTopLeftRadius: 0,\n borderBottomLeftRadius: 0,\n borderTopRightRadius: 6,\n borderBottomRightRadius: 6,\n },\n handleChevron: {\n width: 8,\n height: 16,\n justifyContent: 'center',\n alignItems: 'center',\n },\n chevronLine: {\n width: 2,\n height: 8,\n backgroundColor: '#000',\n borderRadius: 1,\n position: 'absolute',\n },\n chevronLineTopLeft: {\n transform: [{ rotate: '20deg' }],\n top: 0,\n },\n chevronLineBottomLeft: {\n transform: [{ rotate: '-20deg' }],\n bottom: 0,\n },\n chevronLineTopRight: {\n transform: [{ rotate: '-20deg' }],\n top: 0,\n },\n chevronLineBottomRight: {\n transform: [{ rotate: '20deg' }],\n bottom: 0,\n },\n tapArea: {\n position: 'absolute',\n top: 0,\n },\n playhead: {\n position: 'absolute',\n width: 2,\n borderRadius: 1,\n },\n playheadKnob: {\n position: 'absolute',\n top: -4,\n left: -4,\n width: 10,\n height: 10,\n borderRadius: 5,\n },\n});\n\nexport default VideoScrubber;\n"]}
|
|
1
|
+
{"version":3,"file":"VideoScrubber.js","sourceRoot":"","sources":["../src/VideoScrubber.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EACL,UAAU,EACV,WAAW,EACX,SAAS,EACT,mBAAmB,EACnB,OAAO,EACP,MAAM,EACN,QAAQ,GACT,MAAM,OAAO,CAAC;AACf,OAAO,EACL,IAAI,EACJ,KAAK,EACL,UAAU,EAEV,YAAY,EAGZ,QAAQ,GACT,MAAM,cAAc,CAAC;AAGtB,OAAO,sBAAsB,MAAM,0BAA0B,CAAC;AAE9D,MAAM,aAAa,GAAiC;IAClD,WAAW,EAAE,SAAS;IACtB,mBAAmB,EAAE,qBAAqB;IAC1C,aAAa,EAAE,SAAS;IACxB,eAAe,EAAE,SAAS;IAC1B,WAAW,EAAE,iBAAiB;IAC9B,WAAW,EAAE,SAAS;CACvB,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,aAAa,GAAG,UAAU,CAAuC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;IACpF,MAAM,EACJ,OAAO,EACP,QAAQ,EACR,WAAW,EACX,SAAS,EACT,OAAO,EACP,cAAc,GAAG,EAAE,EACnB,eAAe,GAAG,EAAE,EACpB,iBAAiB,EACjB,eAAe,EACf,OAAO,EACP,WAAW,EACX,UAAU,EACV,WAAW,GAAG,GAAG,EACjB,KAAK,EACL,QAAQ,GAAG,KAAK,EAChB,KAAK,EAAE,SAAS,EAChB,YAAY,GAAG,IAAI,EACnB,WAAW,GAAG,EAAE,GACjB,GAAG,KAAK,CAAC;IAEV,MAAM,KAAK,GAAG,OAAO,CACnB,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,aAAa,EAAE,GAAG,SAAS,EAAE,CAAC,EAC1C,CAAC,SAAS,CAAC,CACZ,CAAC;IAEF,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAW,EAAE,CAAC,CAAC;IAC3D,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACxD,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjD,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAsC,IAAI,CAAC,CAAC;IAEpF,yEAAyE;IACzE,MAAM,gBAAgB,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IAC3C,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,cAAc,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;IAC3C,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,iBAAiB,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;IACjD,MAAM,cAAc,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;IAE3C,8EAA8E;IAC9E,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,gBAAgB,CAAC,OAAO,GAAG,SAAS,CAAC;YACrC,cAAc,CAAC,OAAO,GAAG,OAAO,CAAC;QACnC,CAAC;IACH,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;IAEnC,0BAA0B;IAC1B,SAAS,CAAC,GAAG,EAAE;QACb,cAAc,CAAC,OAAO,GAAG,WAAW,CAAC;IACvC,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;IAElB,SAAS,CAAC,GAAG,EAAE;QACb,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAC;IACjC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEf,SAAS,CAAC,GAAG,EAAE;QACb,iBAAiB,CAAC,OAAO,GAAG,cAAc,CAAC;IAC7C,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;IAErB,SAAS,CAAC,GAAG,EAAE;QACb,cAAc,CAAC,OAAO,GAAG,WAAW,CAAC;IACvC,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;IAElB,4BAA4B;IAC5B,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,EAAE;QAClC,IAAI,QAAQ,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACzE,MAAM,QAAQ,GAAG,QAAQ,GAAG,KAAK,CAAC;QAClC,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,QAAQ,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC;IAC9E,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC;IAE/B,kBAAkB;IAClB,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,OAAO,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;YAC7D,aAAa,CAAC,EAAE,CAAC,CAAC;YAClB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO;QACT,CAAC;QAED,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,YAAY,CAAC,IAAI,CAAC,CAAC;QAEnB,sBAAsB,CAAC,kBAAkB,CACvC,OAAO,EACP,cAAc,EACd,EAAE,KAAK,EAAE,eAAe,GAAG,GAAG,EAAE,MAAM,EAAE,eAAe,EAAE,CAC1D;aACE,IAAI,CAAC,CAAC,MAAgB,EAAE,EAAE;YACzB,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,aAAa,CAAC,MAAM,CAAC,CAAC;gBACtB,YAAY,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;QACH,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,GAAU,EAAE,EAAE;YACpB,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,GAAG,CAAC,CAAC;gBACrD,YAAY,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;QACH,CAAC,CAAC,CAAC;QAEL,OAAO,GAAG,EAAE;YACV,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,QAAQ,CAAC,CAAC,CAAC;IAEzD,0CAA0C;IAC1C,MAAM,YAAY,GAAG,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAE/E,qEAAqE;IACrE,MAAM,cAAc,GAAG,WAAW,CAChC,CAAC,IAAY,EAAE,EAAE;QACf,IAAI,QAAQ,IAAI,CAAC,IAAI,YAAY,IAAI,CAAC;YAAE,OAAO,WAAW,CAAC;QAC3D,OAAO,WAAW,GAAG,CAAC,IAAI,GAAG,QAAQ,CAAC,GAAG,YAAY,CAAC;IACxD,CAAC,EACD,CAAC,QAAQ,EAAE,YAAY,EAAE,WAAW,CAAC,CACtC,CAAC;IAEF,sBAAsB;IACtB,MAAM,aAAa,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;IAChD,MAAM,WAAW,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,gBAAgB,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC;IAErD,uBAAuB;IACvB,MAAM,YAAY,GAAG,WAAW,CAAC,CAAC,CAAoB,EAAE,EAAE;QACxD,iBAAiB,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAChD,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,2EAA2E;IAC3E,MAAM,oBAAoB,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;IACvD,MAAM,kBAAkB,GAAG,MAAM,CAAC,eAAe,CAAC,CAAC;IACnD,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,MAAM,cAAc,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;IAC3C,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;IAGzC,SAAS,CAAC,GAAG,EAAE;QACb,oBAAoB,CAAC,OAAO,GAAG,iBAAiB,CAAC;QACjD,kBAAkB,CAAC,OAAO,GAAG,eAAe,CAAC;QAC7C,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;QAC7B,cAAc,CAAC,OAAO,GAAG,WAAW,CAAC;QACrC,aAAa,CAAC,OAAO,GAAG,UAAU,CAAC;IACrC,CAAC,EAAE,CAAC,iBAAiB,EAAE,eAAe,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC;IAE3E,iDAAiD;IACjD,kFAAkF;IAClF,MAAM,kBAAkB,GAAG,WAAW,CACpC,CAAC,IAAkC,EAAE,EAAE;QACrC,IAAI,WAAW,GAAG,CAAC,CAAC;QAEpB,OAAO,YAAY,CAAC,MAAM,CAAC;YACzB,4BAA4B,EAAE,GAAG,EAAE,CAAC,CAAC,QAAQ;YAC7C,2BAA2B,EAAE,CAAC,CAAC,EAAE,YAAY,EAAE,EAAE;gBAC/C,uDAAuD;gBACvD,2DAA2D;gBAC3D,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;YACpD,CAAC;YACD,0DAA0D;YAC1D,mCAAmC,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,wCAAwC;YAC1F,kCAAkC,EAAE,CAAC,CAAC,EAAE,YAAY,EAAE,EAAE;gBACtD,kDAAkD;gBAClD,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;YAC7G,CAAC;YACD,mBAAmB,EAAE,GAAG,EAAE;gBACxB,iEAAiE;gBACjE,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;oBACrB,WAAW,GAAG,gBAAgB,CAAC,OAAO,CAAC;gBACzC,CAAC;qBAAM,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;oBAC1B,WAAW,GAAG,cAAc,CAAC,OAAO,CAAC;gBACvC,CAAC;qBAAM,CAAC;oBACN,WAAW,GAAG,cAAc,CAAC,OAAO,CAAC;gBACvC,CAAC;gBACD,WAAW,CAAC,IAAI,CAAC,CAAC;YACpB,CAAC;YACD,kBAAkB,EAAE,CAAC,EAAyB,EAAE,YAAsC,EAAE,EAAE;gBACxF,MAAM,UAAU,GAAG,iBAAiB,CAAC,OAAO,CAAC;gBAC7C,MAAM,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC;gBAClC,MAAM,GAAG,GAAG,WAAW,CAAC,OAAO,CAAC;gBAChC,0DAA0D;gBAC1D,MAAM,EAAE,GAAG,UAAU,GAAG,EAAE,GAAG,CAAC,CAAC;gBAC/B,IAAI,EAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;oBAAE,OAAO;gBAEhC,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,CAAC;gBAC/B,MAAM,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,GAAG,GAAG,CAAC;gBACtC,IAAI,OAAO,GAAG,WAAW,GAAG,SAAS,CAAC;gBAEtC,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;oBACrB,yBAAyB;oBACzB,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,cAAc,CAAC,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC;oBAC/E,gBAAgB,CAAC,OAAO,GAAG,OAAO,CAAC;oBACnC,oBAAoB,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC;oBACxC,cAAc,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC;gBACpC,CAAC;qBAAM,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;oBAC1B,uBAAuB;oBACvB,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,OAAO,GAAG,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;oBACnF,cAAc,CAAC,OAAO,GAAG,OAAO,CAAC;oBACjC,kBAAkB,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC;oBACtC,cAAc,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC;gBACpC,CAAC;qBAAM,CAAC;oBACN,oCAAoC;oBACpC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;oBACxF,UAAU,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC;oBAC9B,cAAc,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC;gBACpC,CAAC;YACH,CAAC;YACD,qBAAqB,EAAE,CAAC,EAAyB,EAAE,YAAsC,EAAE,EAAE;gBAC3F,WAAW,CAAC,IAAI,CAAC,CAAC;gBAClB,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;oBACrB,aAAa,CAAC,OAAO,EAAE,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;gBACpD,CAAC;qBAAM,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;oBAC1B,aAAa,CAAC,OAAO,EAAE,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;gBAClD,CAAC;qBAAM,CAAC;oBACN,MAAM,UAAU,GAAG,iBAAiB,CAAC,OAAO,CAAC;oBAC7C,MAAM,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC;oBAClC,MAAM,GAAG,GAAG,WAAW,CAAC,OAAO,CAAC;oBAChC,MAAM,EAAE,GAAG,UAAU,GAAG,EAAE,GAAG,CAAC,CAAC;oBAC/B,IAAI,EAAE,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;wBACtB,MAAM,SAAS,GAAG,CAAC,YAAY,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,GAAG,CAAC;wBAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CACtB,gBAAgB,CAAC,OAAO,EACxB,IAAI,CAAC,GAAG,CAAC,WAAW,GAAG,SAAS,EAAE,cAAc,CAAC,OAAO,CAAC,CAC1D,CAAC;wBACF,aAAa,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC;oBACnC,CAAC;gBACH,CAAC;YACH,CAAC;YACD,uBAAuB,EAAE,GAAG,EAAE;gBAC5B,WAAW,CAAC,IAAI,CAAC,CAAC;YACpB,CAAC;SACF,CAAC,CAAC;IACL,CAAC,EACD,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,mDAAmD;KAC5E,CAAC;IAEF,MAAM,iBAAiB,GAAG,OAAO,CAC/B,GAAG,EAAE,CAAC,kBAAkB,CAAC,OAAO,CAAC,EACjC,CAAC,kBAAkB,CAAC,CACrB,CAAC;IACF,MAAM,eAAe,GAAG,OAAO,CAC7B,GAAG,EAAE,CAAC,kBAAkB,CAAC,KAAK,CAAC,EAC/B,CAAC,kBAAkB,CAAC,CACrB,CAAC;IACF,MAAM,oBAAoB,GAAG,OAAO,CAClC,GAAG,EAAE,CAAC,kBAAkB,CAAC,UAAU,CAAC,EACpC,CAAC,kBAAkB,CAAC,CACrB,CAAC;IAEF,oGAAoG;IACpG,MAAM,SAAS,GAAG,WAAW,CAC3B,CAAC,CAAwB,EAAE,EAAE;QAC3B,IAAI,QAAQ;YAAE,OAAO;QACrB,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC,WAAW,CAAC;QACpC,MAAM,MAAM,GAAG,gBAAgB,CAAC,OAAO,CAAC;QACxC,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,CAAC;QACpC,MAAM,iBAAiB,GAAG,IAAI,GAAG,MAAM,CAAC;QACxC,IAAI,iBAAiB,IAAI,CAAC;YAAE,OAAO;QAEnC,+FAA+F;QAC/F,qDAAqD;QACrD,MAAM,KAAK,GAAG,iBAAiB,CAAC,OAAO,CAAC;QACxC,MAAM,GAAG,GAAG,WAAW,CAAC,OAAO,CAAC;QAChC,IAAI,KAAK,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;YAAE,OAAO;QAEnC,2FAA2F;QAC3F,MAAM,EAAE,GAAG,KAAK,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC,eAAe;QACnD,MAAM,YAAY,GAAG,CAAC,CAAC,IAAI,GAAG,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,EAAE,CAAC;QAClD,IAAI,YAAY,IAAI,CAAC;YAAE,OAAO;QAE9B,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC,CAAC,CAAC;QACtE,MAAM,IAAI,GAAG,MAAM,GAAG,UAAU,GAAG,iBAAiB,CAAC;QACrD,UAAU,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC,EACD,CAAC,QAAQ,EAAE,WAAW,CAAC,CACxB,CAAC;IAEF,qBAAqB;IACrB,mBAAmB,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;QAC9B,oBAAoB,EAAE,KAAK,IAAI,EAAE;YAC/B,IAAI,CAAC,OAAO,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO;YACpD,YAAY,CAAC,IAAI,CAAC,CAAC;YACnB,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,sBAAsB,CAAC,kBAAkB,CAC5D,OAAO,EACP,cAAc,EACd,EAAE,KAAK,EAAE,eAAe,GAAG,GAAG,EAAE,MAAM,EAAE,eAAe,EAAE,CAC1D,CAAC;gBACF,aAAa,CAAC,MAAM,CAAC,CAAC;YACxB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,GAAG,CAAC,CAAC;YACzD,CAAC;oBAAS,CAAC;gBACT,YAAY,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;QACD,mBAAmB,EAAE,CAAC,IAAY,EAAE,EAAE;YACpC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;QAC1D,CAAC;QACD,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;KAC7C,CAAC,CAAC,CAAC;IAEJ,MAAM,cAAc,GAAG,YAAY,GAAG,CAAC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;QAC9D,CAAC,CAAC,YAAY,GAAG,UAAU,CAAC,MAAM;QAClC,CAAC,CAAC,CAAC,CAAC;IAEN,OAAO,CACL,CAAC,IAAI,CACH,KAAK,CAAC,CAAC;YACL,MAAM,CAAC,SAAS;YAChB,EAAE,eAAe,EAAE,KAAK,CAAC,eAAe,EAAE;YAC1C,KAAK;SACN,CAAC,CACF,QAAQ,CAAC,CAAC,YAAY,CAAC,CAEvB;MAAA,CAAC,kDAAkD,CACnD;MAAA,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC,CAC9G;QAAA,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAChC,CAAC,KAAK,CACJ,GAAG,CAAC,CAAC,KAAK,CAAC,CACX,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,yBAAyB,KAAK,EAAE,EAAE,CAAC,CAClD,KAAK,CAAC,CAAC;gBACL,MAAM,CAAC,SAAS;gBAChB;oBACE,KAAK,EAAE,cAAc;oBACrB,MAAM,EAAE,eAAe;iBACxB;aACF,CAAC,CACF,UAAU,CAAC,OAAO,EAClB,CACH,CAAC,CACF;QAAA,CAAC,SAAS,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,CACvC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,kBAAkB,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,CAAC,EAAG,CAC1E,CACH;MAAA,EAAE,IAAI,CAEN;;MAAA,CAAC,6FAA6F,CAC9F;MAAA,CAAC,IAAI,CACH,KAAK,CAAC,CAAC;YACL,MAAM,CAAC,YAAY;YACnB;gBACE,IAAI,EAAE,WAAW;gBACjB,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,aAAa,GAAG,WAAW,CAAC;gBAC/C,MAAM,EAAE,eAAe;gBACvB,eAAe,EAAE,KAAK,CAAC,WAAW;aACnC;SACF,CAAC,CACF,aAAa,CAAC,MAAM,EAGtB;;MAAA,CAAC,6FAA6F,CAC9F;MAAA,CAAC,IAAI,CACH,KAAK,CAAC,CAAC;YACL,MAAM,CAAC,YAAY;YACnB;gBACE,IAAI,EAAE,WAAW;gBACjB,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,cAAc,GAAG,WAAW,GAAG,WAAW,CAAC;gBAC9D,MAAM,EAAE,eAAe;gBACvB,eAAe,EAAE,KAAK,CAAC,WAAW;aACnC;SACF,CAAC,CACF,aAAa,CAAC,MAAM,EAGtB;;MAAA,CAAC,qEAAqE,CACtE;MAAA,CAAC,IAAI,CACH,KAAK,CAAC,CAAC;YACL,MAAM,CAAC,eAAe;YACtB,MAAM,CAAC,kBAAkB;YACzB;gBACE,IAAI,EAAE,aAAa;gBACnB,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,GAAG,aAAa,CAAC;gBAC/C,eAAe,EAAE,KAAK,CAAC,WAAW;aACnC;SACF,CAAC,CACF,aAAa,CAAC,MAAM,EAEtB;MAAA,CAAC,IAAI,CACH,KAAK,CAAC,CAAC;YACL,MAAM,CAAC,eAAe;YACtB,MAAM,CAAC,qBAAqB;YAC5B;gBACE,IAAI,EAAE,aAAa;gBACnB,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,GAAG,aAAa,CAAC;gBAC/C,GAAG,EAAE,eAAe,GAAG,CAAC;gBACxB,eAAe,EAAE,KAAK,CAAC,WAAW;aACnC;SACF,CAAC,CACF,aAAa,CAAC,MAAM,EAGtB;;MAAA,CAAC,gGAAgG,CACjG;MAAA,CAAC,QAAQ,CAAC,IAAI,CACZ,KAAK,CAAC,CAAC;YACL,MAAM,CAAC,MAAM;YACb,MAAM,CAAC,WAAW;YAClB;gBACE,IAAI,EAAE,aAAa,GAAG,WAAW;gBACjC,KAAK,EAAE,WAAW;gBAClB,MAAM,EAAE,eAAe;gBACvB,eAAe,EAAE,KAAK,CAAC,WAAW;gBAClC,OAAO,EAAE,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;aACxC;SACF,CAAC,CACF,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CACtD,IAAI,iBAAiB,CAAC,WAAW,CAAC,CAElC;QAAA,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAChC;UAAA,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,kBAAkB,CAAC,CAAC,EAC7D;UAAA,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,qBAAqB,CAAC,CAAC,EAClE;QAAA,EAAE,IAAI,CACR;MAAA,EAAE,QAAQ,CAAC,IAAI,CAEf;;MAAA,CAAC,oGAAoG,CACrG;MAAA,CAAC,QAAQ,CAAC,IAAI,CACZ,KAAK,CAAC,CAAC;YACL,MAAM,CAAC,MAAM;YACb,MAAM,CAAC,SAAS;YAChB;gBACE,IAAI,EAAE,WAAW;gBACjB,KAAK,EAAE,WAAW;gBAClB,MAAM,EAAE,eAAe;gBACvB,eAAe,EAAE,KAAK,CAAC,WAAW;gBAClC,OAAO,EAAE,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;aACtC;SACF,CAAC,CACF,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CACtD,IAAI,eAAe,CAAC,WAAW,CAAC,CAEhC;QAAA,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAChC;UAAA,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,mBAAmB,CAAC,CAAC,EAC9D;UAAA,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,sBAAsB,CAAC,CAAC,EACnE;QAAA,EAAE,IAAI,CACR;MAAA,EAAE,QAAQ,CAAC,IAAI,CAEf;;MAAA,CAAC,mFAAmF,CACpF;MAAA,CAAC,IAAI,CACH,KAAK,CAAC,CAAC;YACL,MAAM,CAAC,OAAO;YACd;gBACE,IAAI,EAAE,aAAa;gBACnB,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,GAAG,aAAa,CAAC;gBAC/C,MAAM,EAAE,eAAe;gBACvB,MAAM,EAAE,CAAC;aACV;SACF,CAAC,CACF,UAAU,CAAC,CAAC,SAAS,CAAC,EAGxB;;MAAA,CAAC,kDAAkD,CACnD;MAAA,CAAC,YAAY,IAAI,gBAAgB,IAAI,aAAa,IAAI,gBAAgB,IAAI,WAAW,IAAI,CACvF,CAAC,QAAQ,CAAC,IAAI,CACZ,KAAK,CAAC,CAAC;gBACL,MAAM,CAAC,QAAQ;gBACf;oBACE,IAAI,EAAE,gBAAgB,GAAG,CAAC;oBAC1B,MAAM,EAAE,eAAe,GAAG,CAAC;oBAC3B,GAAG,EAAE,CAAC,CAAC;oBACP,eAAe,EAAE,KAAK,CAAC,aAAa;oBACpC,OAAO,EAAE,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;oBAC1C,MAAM,EAAE,EAAE;iBACX;aACF,CAAC,CACF,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CACtD,IAAI,oBAAoB,CAAC,WAAW,CAAC,CAErC;UAAA,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,eAAe,EAAE,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC,EAC/E;QAAA,EAAE,QAAQ,CAAC,IAAI,CAAC,CACjB,CACH;IAAA,EAAE,IAAI,CAAC,CACR,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,aAAa,CAAC,WAAW,GAAG,eAAe,CAAC;AAE5C,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;IAC/B,SAAS,EAAE;QACT,KAAK,EAAE,MAAM;QACb,YAAY,EAAE,CAAC;QACf,QAAQ,EAAE,QAAQ;QAClB,QAAQ,EAAE,UAAU;KACrB;IACD,cAAc,EAAE;QACd,aAAa,EAAE,KAAK;QACpB,KAAK,EAAE,MAAM;KACd;IACD,SAAS,EAAE;QACT,eAAe,EAAE,SAAS;KAC3B;IACD,kBAAkB,EAAE;QAClB,IAAI,EAAE,CAAC;QACP,eAAe,EAAE,SAAS;KAC3B;IACD,YAAY,EAAE;QACZ,QAAQ,EAAE,UAAU;QACpB,GAAG,EAAE,CAAC;KACP;IACD,eAAe,EAAE;QACf,QAAQ,EAAE,UAAU;QACpB,MAAM,EAAE,CAAC;KACV;IACD,kBAAkB,EAAE;QAClB,GAAG,EAAE,CAAC;KACP;IACD,qBAAqB,EAAE;QACrB,MAAM,EAAE,CAAC;KACV;IACD,MAAM,EAAE;QACN,QAAQ,EAAE,UAAU;QACpB,GAAG,EAAE,CAAC;QACN,cAAc,EAAE,QAAQ;QACxB,UAAU,EAAE,QAAQ;QACpB,YAAY,EAAE,CAAC;KAChB;IACD,WAAW,EAAE;QACX,mBAAmB,EAAE,CAAC;QACtB,sBAAsB,EAAE,CAAC;QACzB,oBAAoB,EAAE,CAAC;QACvB,uBAAuB,EAAE,CAAC;KAC3B;IACD,SAAS,EAAE;QACT,mBAAmB,EAAE,CAAC;QACtB,sBAAsB,EAAE,CAAC;QACzB,oBAAoB,EAAE,CAAC;QACvB,uBAAuB,EAAE,CAAC;KAC3B;IACD,aAAa,EAAE;QACb,KAAK,EAAE,CAAC;QACR,MAAM,EAAE,EAAE;QACV,cAAc,EAAE,QAAQ;QACxB,UAAU,EAAE,QAAQ;KACrB;IACD,WAAW,EAAE;QACX,KAAK,EAAE,CAAC;QACR,MAAM,EAAE,CAAC;QACT,eAAe,EAAE,MAAM;QACvB,YAAY,EAAE,CAAC;QACf,QAAQ,EAAE,UAAU;KACrB;IACD,kBAAkB,EAAE;QAClB,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QAChC,GAAG,EAAE,CAAC;KACP;IACD,qBAAqB,EAAE;QACrB,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;QACjC,MAAM,EAAE,CAAC;KACV;IACD,mBAAmB,EAAE;QACnB,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;QACjC,GAAG,EAAE,CAAC;KACP;IACD,sBAAsB,EAAE;QACtB,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QAChC,MAAM,EAAE,CAAC;KACV;IACD,OAAO,EAAE;QACP,QAAQ,EAAE,UAAU;QACpB,GAAG,EAAE,CAAC;KACP;IACD,QAAQ,EAAE;QACR,QAAQ,EAAE,UAAU;QACpB,KAAK,EAAE,CAAC;QACR,YAAY,EAAE,CAAC;KAChB;IACD,YAAY,EAAE;QACZ,QAAQ,EAAE,UAAU;QACpB,GAAG,EAAE,CAAC,CAAC;QACP,IAAI,EAAE,CAAC,CAAC;QACR,KAAK,EAAE,EAAE;QACT,MAAM,EAAE,EAAE;QACV,YAAY,EAAE,CAAC;KAChB;CACF,CAAC,CAAC;AAEH,eAAe,aAAa,CAAC","sourcesContent":["import * as React from 'react';\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport {\n View,\n Image,\n StyleSheet,\n LayoutChangeEvent,\n PanResponder,\n PanResponderGestureState,\n GestureResponderEvent,\n Animated,\n} from 'react-native';\n\nimport { VideoScrubberProps, VideoScrubberRef, VideoScrubberTheme } from './VideoScrubber.types';\nimport ExpoTwoStepVideoModule from './ExpoTwoStepVideoModule';\n\nconst DEFAULT_THEME: Required<VideoScrubberTheme> = {\n handleColor: '#FFD700',\n selectedRegionColor: 'rgba(255,215,0,0.2)',\n playheadColor: '#FFFFFF',\n backgroundColor: '#1c1c1e',\n dimmedColor: 'rgba(0,0,0,0.6)',\n borderColor: '#FFD700',\n};\n\n/**\n * Video scrubber/trimmer component with thumbnail strip and draggable handles\n *\n * @example\n * ```tsx\n * <VideoScrubber\n * assetId={asset.id}\n * duration={asset.duration}\n * currentTime={currentTime}\n * startTime={trimStart}\n * endTime={trimEnd}\n * onStartTimeChange={setTrimStart}\n * onEndTimeChange={setTrimEnd}\n * onScrubbing={(time) => playerRef.current?.seek(time)}\n * />\n * ```\n */\nconst VideoScrubber = forwardRef<VideoScrubberRef, VideoScrubberProps>((props, ref) => {\n const {\n assetId,\n duration,\n currentTime,\n startTime,\n endTime,\n thumbnailCount = 10,\n thumbnailHeight = 50,\n onStartTimeChange,\n onEndTimeChange,\n onScrub,\n onScrubbing,\n onScrubEnd,\n minDuration = 0.5,\n style,\n disabled = false,\n theme: userTheme,\n showPlayhead = true,\n handleWidth = 20,\n } = props;\n\n const theme = useMemo(\n () => ({ ...DEFAULT_THEME, ...userTheme }),\n [userTheme]\n );\n\n const [thumbnails, setThumbnails] = useState<string[]>([]);\n const [containerWidth, setContainerWidth] = useState(0);\n const [isLoading, setIsLoading] = useState(true);\n const [dragging, setDragging] = useState<'start' | 'end' | 'playhead' | null>(null);\n\n // Track current values during drag - synced with props when not dragging\n const dragStartTimeRef = useRef(startTime);\n const dragEndTimeRef = useRef(endTime);\n const currentTimeRef = useRef(currentTime);\n const durationRef = useRef(duration);\n const containerWidthRef = useRef(containerWidth);\n const handleWidthRef = useRef(handleWidth);\n\n // Sync refs with props when they change from outside (e.g., new video loaded)\n useEffect(() => {\n if (!dragging) {\n dragStartTimeRef.current = startTime;\n dragEndTimeRef.current = endTime;\n }\n }, [startTime, endTime, dragging]);\n\n // Keep other refs in sync\n useEffect(() => {\n currentTimeRef.current = currentTime;\n }, [currentTime]);\n\n useEffect(() => {\n durationRef.current = duration;\n }, [duration]);\n\n useEffect(() => {\n containerWidthRef.current = containerWidth;\n }, [containerWidth]);\n\n useEffect(() => {\n handleWidthRef.current = handleWidth;\n }, [handleWidth]);\n\n // Calculate thumbnail times\n const thumbnailTimes = useMemo(() => {\n if (duration <= 0) return [];\n const count = Math.min(thumbnailCount, Math.max(1, Math.ceil(duration)));\n const interval = duration / count;\n return Array.from({ length: count }, (_, i) => i * interval + interval / 2);\n }, [duration, thumbnailCount]);\n\n // Load thumbnails\n useEffect(() => {\n if (!assetId || thumbnailTimes.length === 0 || duration <= 0) {\n setThumbnails([]);\n setIsLoading(false);\n return;\n }\n\n let cancelled = false;\n setIsLoading(true);\n\n ExpoTwoStepVideoModule.generateThumbnails(\n assetId,\n thumbnailTimes,\n { width: thumbnailHeight * 1.5, height: thumbnailHeight }\n )\n .then((thumbs: string[]) => {\n if (!cancelled) {\n setThumbnails(thumbs);\n setIsLoading(false);\n }\n })\n .catch((err: Error) => {\n if (!cancelled) {\n console.error('Failed to generate thumbnails:', err);\n setIsLoading(false);\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [assetId, thumbnailTimes, thumbnailHeight, duration]);\n\n // The content area is between the handles\n const contentWidth = containerWidth > 0 ? containerWidth - handleWidth * 2 : 0;\n\n // Convert time to position within the content area (between handles)\n const timeToPosition = useCallback(\n (time: number) => {\n if (duration <= 0 || contentWidth <= 0) return handleWidth;\n return handleWidth + (time / duration) * contentWidth;\n },\n [duration, contentWidth, handleWidth]\n );\n\n // Calculate positions\n const startPosition = timeToPosition(startTime);\n const endPosition = timeToPosition(endTime);\n const playheadPosition = timeToPosition(currentTime);\n\n // Handle layout change\n const handleLayout = useCallback((e: LayoutChangeEvent) => {\n setContainerWidth(e.nativeEvent.layout.width);\n }, []);\n\n // Store callbacks in refs to avoid recreating PanResponder on every render\n const onStartTimeChangeRef = useRef(onStartTimeChange);\n const onEndTimeChangeRef = useRef(onEndTimeChange);\n const onScrubRef = useRef(onScrub);\n const onScrubbingRef = useRef(onScrubbing);\n const onScrubEndRef = useRef(onScrubEnd);\n\n\n useEffect(() => {\n onStartTimeChangeRef.current = onStartTimeChange;\n onEndTimeChangeRef.current = onEndTimeChange;\n onScrubRef.current = onScrub;\n onScrubbingRef.current = onScrubbing;\n onScrubEndRef.current = onScrubEnd;\n }, [onStartTimeChange, onEndTimeChange, onScrub, onScrubbing, onScrubEnd]);\n\n // Create pan responders for handles and playhead\n // Using refs to avoid stale closures - PanResponder is only created once per type\n const createPanResponder = useCallback(\n (type: 'start' | 'end' | 'playhead') => {\n let initialTime = 0;\n\n return PanResponder.create({\n onStartShouldSetPanResponder: () => !disabled,\n onMoveShouldSetPanResponder: (_, gestureState) => {\n // Claim the gesture if there's any horizontal movement\n // This prevents ScrollView from capturing horizontal drags\n return !disabled && Math.abs(gestureState.dx) > 2;\n },\n // Capture phase handlers - these fire before parent views\n onStartShouldSetPanResponderCapture: () => false, // Don't capture on start, let it bubble\n onMoveShouldSetPanResponderCapture: (_, gestureState) => {\n // Only capture if horizontal movement is dominant\n return !disabled && Math.abs(gestureState.dx) > 5 && Math.abs(gestureState.dx) > Math.abs(gestureState.dy);\n },\n onPanResponderGrant: () => {\n // Read current values from refs at the moment the gesture starts\n if (type === 'start') {\n initialTime = dragStartTimeRef.current;\n } else if (type === 'end') {\n initialTime = dragEndTimeRef.current;\n } else {\n initialTime = currentTimeRef.current;\n }\n setDragging(type);\n },\n onPanResponderMove: (_e: GestureResponderEvent, gestureState: PanResponderGestureState) => {\n const containerW = containerWidthRef.current;\n const hw = handleWidthRef.current;\n const dur = durationRef.current;\n // Content width is where time is mapped (between handles)\n const cw = containerW - hw * 2;\n if (cw <= 0 || dur <= 0) return;\n\n const deltaX = gestureState.dx;\n const deltaTime = (deltaX / cw) * dur;\n let newTime = initialTime + deltaTime;\n\n if (type === 'start') {\n // Constrain start handle\n newTime = Math.max(0, Math.min(newTime, dragEndTimeRef.current - minDuration));\n dragStartTimeRef.current = newTime;\n onStartTimeChangeRef.current?.(newTime);\n onScrubbingRef.current?.(newTime);\n } else if (type === 'end') {\n // Constrain end handle\n newTime = Math.max(dragStartTimeRef.current + minDuration, Math.min(newTime, dur));\n dragEndTimeRef.current = newTime;\n onEndTimeChangeRef.current?.(newTime);\n onScrubbingRef.current?.(newTime);\n } else {\n // Playhead - constrain to selection\n newTime = Math.max(dragStartTimeRef.current, Math.min(newTime, dragEndTimeRef.current));\n onScrubRef.current?.(newTime);\n onScrubbingRef.current?.(newTime);\n }\n },\n onPanResponderRelease: (_e: GestureResponderEvent, gestureState: PanResponderGestureState) => {\n setDragging(null);\n if (type === 'start') {\n onScrubEndRef.current?.(dragStartTimeRef.current);\n } else if (type === 'end') {\n onScrubEndRef.current?.(dragEndTimeRef.current);\n } else {\n const containerW = containerWidthRef.current;\n const hw = handleWidthRef.current;\n const dur = durationRef.current;\n const cw = containerW - hw * 2;\n if (cw > 0 && dur > 0) {\n const deltaTime = (gestureState.dx / cw) * dur;\n const newTime = Math.max(\n dragStartTimeRef.current,\n Math.min(initialTime + deltaTime, dragEndTimeRef.current)\n );\n onScrubEndRef.current?.(newTime);\n }\n }\n },\n onPanResponderTerminate: () => {\n setDragging(null);\n },\n });\n },\n [disabled, minDuration] // Only recreate if disabled or minDuration changes\n );\n\n const startPanResponder = useMemo(\n () => createPanResponder('start'),\n [createPanResponder]\n );\n const endPanResponder = useMemo(\n () => createPanResponder('end'),\n [createPanResponder]\n );\n const playheadPanResponder = useMemo(\n () => createPanResponder('playhead'),\n [createPanResponder]\n );\n\n // Tap to seek - locationX is relative to the tap area which spans from startPosition to endPosition\n const handleTap = useCallback(\n (e: GestureResponderEvent) => {\n if (disabled) return;\n const { locationX } = e.nativeEvent;\n const startT = dragStartTimeRef.current;\n const endT = dragEndTimeRef.current;\n const selectionDuration = endT - startT;\n if (selectionDuration <= 0) return;\n\n // The tap area spans the selection, so locationX=0 is startTime and locationX=width is endTime\n // Calculate time proportionally within the selection\n const width = containerWidthRef.current;\n const dur = durationRef.current;\n if (width <= 0 || dur <= 0) return;\n\n // Calculate the width of the tap area (endPosition - startPosition in content coordinates)\n const cw = width - handleWidth * 2; // contentWidth\n const tapAreaWidth = ((endT - startT) / dur) * cw;\n if (tapAreaWidth <= 0) return;\n\n const proportion = Math.max(0, Math.min(1, locationX / tapAreaWidth));\n const time = startT + proportion * selectionDuration;\n onScrubRef.current?.(time);\n },\n [disabled, handleWidth]\n );\n\n // Expose ref methods\n useImperativeHandle(ref, () => ({\n regenerateThumbnails: async () => {\n if (!assetId || thumbnailTimes.length === 0) return;\n setIsLoading(true);\n try {\n const thumbs = await ExpoTwoStepVideoModule.generateThumbnails(\n assetId,\n thumbnailTimes,\n { width: thumbnailHeight * 1.5, height: thumbnailHeight }\n );\n setThumbnails(thumbs);\n } catch (err) {\n console.error('Failed to regenerate thumbnails:', err);\n } finally {\n setIsLoading(false);\n }\n },\n setPlayheadPosition: (time: number) => {\n onScrub?.(Math.max(startTime, Math.min(time, endTime)));\n },\n getSelection: () => ({ startTime, endTime }),\n }));\n\n const thumbnailWidth = contentWidth > 0 && thumbnails.length > 0\n ? contentWidth / thumbnails.length\n : 0;\n\n return (\n <View\n style={[\n styles.container,\n { backgroundColor: theme.backgroundColor },\n style,\n ]}\n onLayout={handleLayout}\n >\n {/* Thumbnail Strip - positioned between handles */}\n <View style={[styles.thumbnailStrip, { height: thumbnailHeight, marginLeft: handleWidth, width: contentWidth }]}>\n {thumbnails.map((thumb, index) => (\n <Image\n key={index}\n source={{ uri: `data:image/png;base64,${thumb}` }}\n style={[\n styles.thumbnail,\n {\n width: thumbnailWidth,\n height: thumbnailHeight,\n },\n ]}\n resizeMode=\"cover\"\n />\n ))}\n {isLoading && thumbnails.length === 0 && (\n <View style={[styles.loadingPlaceholder, { height: thumbnailHeight }]} />\n )}\n </View>\n\n {/* Dimmed region - left (covers trimmed content from left handle to start handle position) */}\n <View\n style={[\n styles.dimmedRegion,\n {\n left: handleWidth,\n width: Math.max(0, startPosition - handleWidth),\n height: thumbnailHeight,\n backgroundColor: theme.dimmedColor,\n },\n ]}\n pointerEvents=\"none\"\n />\n\n {/* Dimmed region - right (covers trimmed content from end handle position to right handle) */}\n <View\n style={[\n styles.dimmedRegion,\n {\n left: endPosition,\n width: Math.max(0, containerWidth - handleWidth - endPosition),\n height: thumbnailHeight,\n backgroundColor: theme.dimmedColor,\n },\n ]}\n pointerEvents=\"none\"\n />\n\n {/* Selection frame - top and bottom borders (span between handles) */}\n <View\n style={[\n styles.selectionBorder,\n styles.selectionBorderTop,\n {\n left: startPosition,\n width: Math.max(0, endPosition - startPosition),\n backgroundColor: theme.borderColor,\n },\n ]}\n pointerEvents=\"none\"\n />\n <View\n style={[\n styles.selectionBorder,\n styles.selectionBorderBottom,\n {\n left: startPosition,\n width: Math.max(0, endPosition - startPosition),\n top: thumbnailHeight - 2,\n backgroundColor: theme.borderColor,\n },\n ]}\n pointerEvents=\"none\"\n />\n\n {/* Start Handle - positioned to the LEFT of the trim point (outside content when startTime=0) */}\n <Animated.View\n style={[\n styles.handle,\n styles.startHandle,\n {\n left: startPosition - handleWidth,\n width: handleWidth,\n height: thumbnailHeight,\n backgroundColor: theme.handleColor,\n opacity: dragging === 'start' ? 0.8 : 1,\n },\n ]}\n hitSlop={{ top: 15, bottom: 15, left: 15, right: 10 }}\n {...startPanResponder.panHandlers}\n >\n <View style={styles.handleChevron}>\n <View style={[styles.chevronLine, styles.chevronLineTopLeft]} />\n <View style={[styles.chevronLine, styles.chevronLineBottomLeft]} />\n </View>\n </Animated.View>\n\n {/* End Handle - positioned to the RIGHT of the trim point (outside content when endTime=duration) */}\n <Animated.View\n style={[\n styles.handle,\n styles.endHandle,\n {\n left: endPosition,\n width: handleWidth,\n height: thumbnailHeight,\n backgroundColor: theme.handleColor,\n opacity: dragging === 'end' ? 0.8 : 1,\n },\n ]}\n hitSlop={{ top: 15, bottom: 15, left: 10, right: 15 }}\n {...endPanResponder.panHandlers}\n >\n <View style={styles.handleChevron}>\n <View style={[styles.chevronLine, styles.chevronLineTopRight]} />\n <View style={[styles.chevronLine, styles.chevronLineBottomRight]} />\n </View>\n </Animated.View>\n\n {/* Tap area for seeking - covers the selection between start and end trim points */}\n <View\n style={[\n styles.tapArea,\n {\n left: startPosition,\n width: Math.max(0, endPosition - startPosition),\n height: thumbnailHeight,\n zIndex: 1,\n },\n ]}\n onTouchEnd={handleTap}\n />\n\n {/* Playhead - rendered last for highest z-index */}\n {showPlayhead && playheadPosition >= startPosition && playheadPosition <= endPosition && (\n <Animated.View\n style={[\n styles.playhead,\n {\n left: playheadPosition - 1,\n height: thumbnailHeight + 8,\n top: -4,\n backgroundColor: theme.playheadColor,\n opacity: dragging === 'playhead' ? 0.8 : 1,\n zIndex: 10,\n },\n ]}\n hitSlop={{ top: 20, bottom: 20, left: 20, right: 20 }}\n {...playheadPanResponder.panHandlers}\n >\n <View style={[styles.playheadKnob, { backgroundColor: theme.playheadColor }]} />\n </Animated.View>\n )}\n </View>\n );\n});\n\nVideoScrubber.displayName = 'VideoScrubber';\n\nconst styles = StyleSheet.create({\n container: {\n width: '100%',\n borderRadius: 8,\n overflow: 'hidden',\n position: 'relative',\n },\n thumbnailStrip: {\n flexDirection: 'row',\n width: '100%',\n },\n thumbnail: {\n backgroundColor: '#2c2c2e',\n },\n loadingPlaceholder: {\n flex: 1,\n backgroundColor: '#2c2c2e',\n },\n dimmedRegion: {\n position: 'absolute',\n top: 0,\n },\n selectionBorder: {\n position: 'absolute',\n height: 2,\n },\n selectionBorderTop: {\n top: 0,\n },\n selectionBorderBottom: {\n bottom: 0,\n },\n handle: {\n position: 'absolute',\n top: 0,\n justifyContent: 'center',\n alignItems: 'center',\n borderRadius: 4,\n },\n startHandle: {\n borderTopLeftRadius: 6,\n borderBottomLeftRadius: 6,\n borderTopRightRadius: 0,\n borderBottomRightRadius: 0,\n },\n endHandle: {\n borderTopLeftRadius: 0,\n borderBottomLeftRadius: 0,\n borderTopRightRadius: 6,\n borderBottomRightRadius: 6,\n },\n handleChevron: {\n width: 8,\n height: 16,\n justifyContent: 'center',\n alignItems: 'center',\n },\n chevronLine: {\n width: 2,\n height: 8,\n backgroundColor: '#000',\n borderRadius: 1,\n position: 'absolute',\n },\n chevronLineTopLeft: {\n transform: [{ rotate: '20deg' }],\n top: 0,\n },\n chevronLineBottomLeft: {\n transform: [{ rotate: '-20deg' }],\n bottom: 0,\n },\n chevronLineTopRight: {\n transform: [{ rotate: '-20deg' }],\n top: 0,\n },\n chevronLineBottomRight: {\n transform: [{ rotate: '20deg' }],\n bottom: 0,\n },\n tapArea: {\n position: 'absolute',\n top: 0,\n },\n playhead: {\n position: 'absolute',\n width: 2,\n borderRadius: 1,\n },\n playheadKnob: {\n position: 'absolute',\n top: -4,\n left: -4,\n width: 10,\n height: 10,\n borderRadius: 5,\n },\n});\n\nexport default VideoScrubber;\n"]}
|