Use modern Gesture API v2 + reanimated for pinch zoom and scrubber
Browse files
keystone-app/components/CustomVideoPlayer.tsx
CHANGED
|
@@ -1,14 +1,15 @@
|
|
| 1 |
import React, { useEffect, useState, useRef } from 'react';
|
| 2 |
-
import {
|
| 3 |
-
View,
|
| 4 |
-
StyleSheet,
|
| 5 |
-
TouchableOpacity,
|
| 6 |
-
Text,
|
| 7 |
TouchableWithoutFeedback,
|
| 8 |
} from 'react-native';
|
| 9 |
import { useVideoPlayer, VideoView } from 'expo-video';
|
| 10 |
import { MaterialIcons } from '@expo/vector-icons';
|
| 11 |
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
|
|
| 12 |
|
| 13 |
interface CustomVideoPlayerProps {
|
| 14 |
uri: string;
|
|
@@ -17,8 +18,8 @@ interface CustomVideoPlayerProps {
|
|
| 17 |
onScrubEnd?: () => void;
|
| 18 |
}
|
| 19 |
|
| 20 |
-
export default function CustomVideoPlayer({
|
| 21 |
-
uri, shouldPlay, onScrubStart, onScrubEnd
|
| 22 |
}: CustomVideoPlayerProps) {
|
| 23 |
const insets = useSafeAreaInsets();
|
| 24 |
const [isPlaying, setIsPlaying] = useState(shouldPlay);
|
|
@@ -27,7 +28,6 @@ export default function CustomVideoPlayer({
|
|
| 27 |
const [showControls, setShowControls] = useState(true);
|
| 28 |
const [scrubberWidth, setScrubberWidth] = useState(0);
|
| 29 |
|
| 30 |
-
const scrubberViewRef = useRef<View>(null);
|
| 31 |
const scrubberPageXRef = useRef(0);
|
| 32 |
const controlsTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
| 33 |
|
|
@@ -36,7 +36,6 @@ export default function CustomVideoPlayer({
|
|
| 36 |
if (shouldPlay) p.play();
|
| 37 |
});
|
| 38 |
|
| 39 |
-
// Sync external shouldPlay
|
| 40 |
useEffect(() => {
|
| 41 |
if (shouldPlay) {
|
| 42 |
player.play();
|
|
@@ -47,7 +46,6 @@ export default function CustomVideoPlayer({
|
|
| 47 |
}
|
| 48 |
}, [shouldPlay, player]);
|
| 49 |
|
| 50 |
-
// Poll for time updates
|
| 51 |
useEffect(() => {
|
| 52 |
const interval = setInterval(() => {
|
| 53 |
if (player) {
|
|
@@ -58,7 +56,6 @@ export default function CustomVideoPlayer({
|
|
| 58 |
return () => clearInterval(interval);
|
| 59 |
}, [player]);
|
| 60 |
|
| 61 |
-
// Auto-hide controls
|
| 62 |
const resetControlsTimeout = () => {
|
| 63 |
setShowControls(true);
|
| 64 |
if (controlsTimeoutRef.current) clearTimeout(controlsTimeoutRef.current);
|
|
@@ -98,17 +95,15 @@ export default function CustomVideoPlayer({
|
|
| 98 |
}
|
| 99 |
};
|
| 100 |
|
| 101 |
-
|
| 102 |
-
const seekFromPageX = (pageX: number) => {
|
| 103 |
if (duration <= 0 || scrubberWidth <= 0) return;
|
| 104 |
-
const relX =
|
| 105 |
const pct = Math.max(0, Math.min(1, relX / scrubberWidth));
|
| 106 |
const seekTime = pct * duration;
|
| 107 |
player.currentTime = seekTime;
|
| 108 |
setCurrentTime(seekTime);
|
| 109 |
};
|
| 110 |
|
| 111 |
-
// Measure scrubber's absolute screen position
|
| 112 |
const handleScrubberLayout = () => {
|
| 113 |
scrubberViewRef.current?.measureInWindow((x, _y, w, _h) => {
|
| 114 |
scrubberPageXRef.current = x;
|
|
@@ -116,6 +111,29 @@ export default function CustomVideoPlayer({
|
|
| 116 |
});
|
| 117 |
};
|
| 118 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
const formatTime = (secs: number) => {
|
| 120 |
const m = Math.floor(secs / 60);
|
| 121 |
const s = Math.floor(secs % 60);
|
|
@@ -127,7 +145,7 @@ export default function CustomVideoPlayer({
|
|
| 127 |
return (
|
| 128 |
<TouchableWithoutFeedback onPress={handleScreenTap}>
|
| 129 |
<View style={styles.container}>
|
| 130 |
-
<VideoView
|
| 131 |
style={styles.video}
|
| 132 |
player={player}
|
| 133 |
allowsFullscreen={false}
|
|
@@ -135,62 +153,37 @@ export default function CustomVideoPlayer({
|
|
| 135 |
nativeControls={false}
|
| 136 |
/>
|
| 137 |
|
| 138 |
-
{/* Custom Controls Overlay */}
|
| 139 |
{showControls && (
|
| 140 |
<View style={styles.overlay} pointerEvents="box-none">
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
<TouchableOpacity
|
| 144 |
-
style={styles.playPauseBtn}
|
| 145 |
onPress={togglePlayPause}
|
| 146 |
activeOpacity={0.7}
|
| 147 |
>
|
| 148 |
-
<MaterialIcons
|
| 149 |
-
name={isPlaying ?
|
| 150 |
-
size={48}
|
| 151 |
-
color="#fff"
|
| 152 |
/>
|
| 153 |
</TouchableOpacity>
|
| 154 |
|
| 155 |
-
{/* Bottom Scrubber Bar */}
|
| 156 |
<View style={[styles.bottomBar, { paddingBottom: insets.bottom + 120 }]}>
|
| 157 |
<Text style={styles.timeText}>{formatTime(currentTime)}</Text>
|
| 158 |
-
|
| 159 |
-
{/*
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
onMoveShouldSetResponder={() => true}
|
| 172 |
-
onMoveShouldSetResponderCapture={() => true}
|
| 173 |
-
onResponderTerminationRequest={() => false}
|
| 174 |
-
onResponderGrant={(e) => {
|
| 175 |
-
onScrubStart?.();
|
| 176 |
-
seekFromPageX(e.nativeEvent.pageX);
|
| 177 |
-
}}
|
| 178 |
-
onResponderMove={(e) => {
|
| 179 |
-
seekFromPageX(e.nativeEvent.pageX);
|
| 180 |
-
}}
|
| 181 |
-
onResponderRelease={() => {
|
| 182 |
-
onScrubEnd?.();
|
| 183 |
-
resetControlsTimeout();
|
| 184 |
-
}}
|
| 185 |
-
onResponderTerminate={() => {
|
| 186 |
-
onScrubEnd?.();
|
| 187 |
-
}}
|
| 188 |
-
>
|
| 189 |
-
<View style={styles.scrubberTrack}>
|
| 190 |
-
<View style={[styles.scrubberFill, { width: `${progressPercent}%` }]} />
|
| 191 |
-
<View style={[styles.scrubberThumb, { left: `${progressPercent}%` }]} />
|
| 192 |
</View>
|
| 193 |
-
</
|
| 194 |
|
| 195 |
<Text style={styles.timeText}>{formatTime(duration)}</Text>
|
| 196 |
</View>
|
|
|
|
| 1 |
import React, { useEffect, useState, useRef } from 'react';
|
| 2 |
+
import {
|
| 3 |
+
View,
|
| 4 |
+
StyleSheet,
|
| 5 |
+
TouchableOpacity,
|
| 6 |
+
Text,
|
| 7 |
TouchableWithoutFeedback,
|
| 8 |
} from 'react-native';
|
| 9 |
import { useVideoPlayer, VideoView } from 'expo-video';
|
| 10 |
import { MaterialIcons } from '@expo/vector-icons';
|
| 11 |
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
| 12 |
+
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
| 13 |
|
| 14 |
interface CustomVideoPlayerProps {
|
| 15 |
uri: string;
|
|
|
|
| 18 |
onScrubEnd?: () => void;
|
| 19 |
}
|
| 20 |
|
| 21 |
+
export default function CustomVideoPlayer({
|
| 22 |
+
uri, shouldPlay, onScrubStart, onScrubEnd,
|
| 23 |
}: CustomVideoPlayerProps) {
|
| 24 |
const insets = useSafeAreaInsets();
|
| 25 |
const [isPlaying, setIsPlaying] = useState(shouldPlay);
|
|
|
|
| 28 |
const [showControls, setShowControls] = useState(true);
|
| 29 |
const [scrubberWidth, setScrubberWidth] = useState(0);
|
| 30 |
|
|
|
|
| 31 |
const scrubberPageXRef = useRef(0);
|
| 32 |
const controlsTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
| 33 |
|
|
|
|
| 36 |
if (shouldPlay) p.play();
|
| 37 |
});
|
| 38 |
|
|
|
|
| 39 |
useEffect(() => {
|
| 40 |
if (shouldPlay) {
|
| 41 |
player.play();
|
|
|
|
| 46 |
}
|
| 47 |
}, [shouldPlay, player]);
|
| 48 |
|
|
|
|
| 49 |
useEffect(() => {
|
| 50 |
const interval = setInterval(() => {
|
| 51 |
if (player) {
|
|
|
|
| 56 |
return () => clearInterval(interval);
|
| 57 |
}, [player]);
|
| 58 |
|
|
|
|
| 59 |
const resetControlsTimeout = () => {
|
| 60 |
setShowControls(true);
|
| 61 |
if (controlsTimeoutRef.current) clearTimeout(controlsTimeoutRef.current);
|
|
|
|
| 95 |
}
|
| 96 |
};
|
| 97 |
|
| 98 |
+
const seekFromAbsoluteX = (absX: number) => {
|
|
|
|
| 99 |
if (duration <= 0 || scrubberWidth <= 0) return;
|
| 100 |
+
const relX = absX - scrubberPageXRef.current;
|
| 101 |
const pct = Math.max(0, Math.min(1, relX / scrubberWidth));
|
| 102 |
const seekTime = pct * duration;
|
| 103 |
player.currentTime = seekTime;
|
| 104 |
setCurrentTime(seekTime);
|
| 105 |
};
|
| 106 |
|
|
|
|
| 107 |
const handleScrubberLayout = () => {
|
| 108 |
scrubberViewRef.current?.measureInWindow((x, _y, w, _h) => {
|
| 109 |
scrubberPageXRef.current = x;
|
|
|
|
| 111 |
});
|
| 112 |
};
|
| 113 |
|
| 114 |
+
const scrubberViewRef = useRef<View>(null);
|
| 115 |
+
|
| 116 |
+
/* ── Scrubber gesture ──
|
| 117 |
+
.runOnJS(true) → callbacks run on JS thread (no worklet needed)
|
| 118 |
+
.minDistance(0) → activates immediately on touch (tap-to-seek works)
|
| 119 |
+
This gesture is inside the FlatList item, and RNGH's FlatList
|
| 120 |
+
properly yields to nested gesture handlers.
|
| 121 |
+
*/
|
| 122 |
+
const scrubGesture = Gesture.Pan()
|
| 123 |
+
.runOnJS(true)
|
| 124 |
+
.minDistance(0)
|
| 125 |
+
.onStart((e) => {
|
| 126 |
+
onScrubStart?.();
|
| 127 |
+
seekFromAbsoluteX(e.absoluteX);
|
| 128 |
+
})
|
| 129 |
+
.onUpdate((e) => {
|
| 130 |
+
seekFromAbsoluteX(e.absoluteX);
|
| 131 |
+
})
|
| 132 |
+
.onEnd(() => {
|
| 133 |
+
onScrubEnd?.();
|
| 134 |
+
resetControlsTimeout();
|
| 135 |
+
});
|
| 136 |
+
|
| 137 |
const formatTime = (secs: number) => {
|
| 138 |
const m = Math.floor(secs / 60);
|
| 139 |
const s = Math.floor(secs % 60);
|
|
|
|
| 145 |
return (
|
| 146 |
<TouchableWithoutFeedback onPress={handleScreenTap}>
|
| 147 |
<View style={styles.container}>
|
| 148 |
+
<VideoView
|
| 149 |
style={styles.video}
|
| 150 |
player={player}
|
| 151 |
allowsFullscreen={false}
|
|
|
|
| 153 |
nativeControls={false}
|
| 154 |
/>
|
| 155 |
|
|
|
|
| 156 |
{showControls && (
|
| 157 |
<View style={styles.overlay} pointerEvents="box-none">
|
| 158 |
+
<TouchableOpacity
|
| 159 |
+
style={styles.playPauseBtn}
|
|
|
|
|
|
|
| 160 |
onPress={togglePlayPause}
|
| 161 |
activeOpacity={0.7}
|
| 162 |
>
|
| 163 |
+
<MaterialIcons
|
| 164 |
+
name={isPlaying ? 'pause' : 'play-arrow'}
|
| 165 |
+
size={48}
|
| 166 |
+
color="#fff"
|
| 167 |
/>
|
| 168 |
</TouchableOpacity>
|
| 169 |
|
|
|
|
| 170 |
<View style={[styles.bottomBar, { paddingBottom: insets.bottom + 120 }]}>
|
| 171 |
<Text style={styles.timeText}>{formatTime(currentTime)}</Text>
|
| 172 |
+
|
| 173 |
+
{/* Scrubber — wrapped in GestureDetector so RNGH handles
|
| 174 |
+
touch negotiation with the parent FlatList */}
|
| 175 |
+
<GestureDetector gesture={scrubGesture}>
|
| 176 |
+
<View
|
| 177 |
+
ref={scrubberViewRef}
|
| 178 |
+
style={styles.scrubberContainer}
|
| 179 |
+
onLayout={handleScrubberLayout}
|
| 180 |
+
>
|
| 181 |
+
<View style={styles.scrubberTrack}>
|
| 182 |
+
<View style={[styles.scrubberFill, { width: `${progressPercent}%` }]} />
|
| 183 |
+
<View style={[styles.scrubberThumb, { left: `${progressPercent}%` }]} />
|
| 184 |
+
</View>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 185 |
</View>
|
| 186 |
+
</GestureDetector>
|
| 187 |
|
| 188 |
<Text style={styles.timeText}>{formatTime(duration)}</Text>
|
| 189 |
</View>
|
keystone-app/components/FullScreenSwiper.tsx
CHANGED
|
@@ -1,14 +1,14 @@
|
|
| 1 |
import React, { useRef, useState, useEffect } from 'react';
|
| 2 |
-
import {
|
| 3 |
-
View,
|
| 4 |
-
StyleSheet,
|
| 5 |
-
FlatList,
|
| 6 |
-
Platform,
|
| 7 |
-
Animated,
|
| 8 |
-
useWindowDimensions,
|
| 9 |
-
} from 'react-native';
|
| 10 |
import { Image } from 'expo-image';
|
| 11 |
-
import {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
import CustomVideoPlayer from './CustomVideoPlayer';
|
| 13 |
|
| 14 |
interface FullScreenSwiperProps {
|
|
@@ -20,138 +20,102 @@ interface FullScreenSwiperProps {
|
|
| 20 |
|
| 21 |
/* ─────────────────────────────────────────────────────────────
|
| 22 |
ZoomableImage
|
| 23 |
-
• Pinch
|
| 24 |
-
•
|
| 25 |
-
• Both run on the
|
| 26 |
───────────────────────────────────────────────────────────── */
|
| 27 |
-
function ZoomableImage({
|
| 28 |
-
uri, thumbUri, width, height, onSwipeDown, onZoomChange
|
| 29 |
-
}: {
|
| 30 |
-
uri: string; thumbUri?: string; width: number; height: number;
|
| 31 |
onSwipeDown: () => void; onZoomChange: (zooming: boolean) => void;
|
| 32 |
}) {
|
| 33 |
-
|
| 34 |
-
const
|
| 35 |
-
const
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
/* ── Pinch handlers ── */
|
| 54 |
-
const onPinchEvent = Animated.event(
|
| 55 |
-
[{ nativeEvent: { scale: pinchScaleAnim } }],
|
| 56 |
-
{ useNativeDriver: true }
|
| 57 |
-
);
|
| 58 |
-
|
| 59 |
-
const onPinchStateChange = (e: any) => {
|
| 60 |
-
if (e.nativeEvent.state === State.ACTIVE) {
|
| 61 |
-
onZoomChangeRef.current(true); // disable FlatList scroll
|
| 62 |
-
}
|
| 63 |
-
if (e.nativeEvent.oldState === State.ACTIVE) {
|
| 64 |
-
onZoomChangeRef.current(false); // re-enable FlatList scroll
|
| 65 |
-
const newBase = baseScaleRef.current * e.nativeEvent.scale;
|
| 66 |
-
if (newBase < 1.15) {
|
| 67 |
-
// Snap back to 1×
|
| 68 |
-
baseScaleRef.current = 1;
|
| 69 |
-
baseScaleAnim.setValue(1);
|
| 70 |
-
Animated.spring(pinchScaleAnim, {
|
| 71 |
-
toValue: 1, useNativeDriver: true, bounciness: 0,
|
| 72 |
-
}).start();
|
| 73 |
} else {
|
| 74 |
-
|
| 75 |
-
const clamped = Math.min(5, newBase);
|
| 76 |
-
baseScaleRef.current = clamped;
|
| 77 |
-
baseScaleAnim.setValue(clamped);
|
| 78 |
-
pinchScaleAnim.setValue(1);
|
| 79 |
}
|
| 80 |
-
}
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
|
|
|
|
|
|
|
|
|
| 97 |
} else {
|
| 98 |
-
|
| 99 |
-
toValue: 0, useNativeDriver: true, bounciness: 0,
|
| 100 |
-
}).start();
|
| 101 |
}
|
| 102 |
-
}
|
| 103 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
|
| 105 |
return (
|
| 106 |
-
<
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
>
|
| 123 |
-
<Animated.View
|
| 124 |
-
style={{
|
| 125 |
-
width,
|
| 126 |
-
height,
|
| 127 |
-
justifyContent: 'center',
|
| 128 |
-
alignItems: 'center',
|
| 129 |
-
transform: [{ scale: displayScale }, { translateY }],
|
| 130 |
-
}}
|
| 131 |
-
>
|
| 132 |
-
<Image
|
| 133 |
-
source={{ uri }}
|
| 134 |
-
placeholder={thumbUri ? { uri: thumbUri } : undefined}
|
| 135 |
-
style={{ width: '100%', height: '100%' }}
|
| 136 |
-
contentFit="contain"
|
| 137 |
-
cachePolicy="memory-disk"
|
| 138 |
-
transition={200}
|
| 139 |
-
backgroundColor="transparent"
|
| 140 |
-
allowDownscaling={false}
|
| 141 |
-
/>
|
| 142 |
-
</Animated.View>
|
| 143 |
-
</PinchGestureHandler>
|
| 144 |
</Animated.View>
|
| 145 |
-
</
|
| 146 |
);
|
| 147 |
}
|
| 148 |
|
| 149 |
-
|
| 150 |
/* ─────────────────────────────────────────────────────────────
|
| 151 |
FullScreenSwiper
|
|
|
|
|
|
|
| 152 |
───────────────────────────────────────────────────────────── */
|
| 153 |
-
export default function FullScreenSwiper({
|
| 154 |
-
assets, initialIndex, onClose, onIndexChange
|
| 155 |
}: FullScreenSwiperProps) {
|
| 156 |
const { width, height } = useWindowDimensions();
|
| 157 |
const [activeIndex, setActiveIndex] = useState(initialIndex);
|
|
|
|
| 1 |
import React, { useRef, useState, useEffect } from 'react';
|
| 2 |
+
import { View, StyleSheet, Platform, useWindowDimensions } from 'react-native';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
import { Image } from 'expo-image';
|
| 4 |
+
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
| 5 |
+
import { FlatList } from 'react-native-gesture-handler';
|
| 6 |
+
import Animated, {
|
| 7 |
+
useSharedValue,
|
| 8 |
+
useAnimatedStyle,
|
| 9 |
+
withSpring,
|
| 10 |
+
runOnJS,
|
| 11 |
+
} from 'react-native-reanimated';
|
| 12 |
import CustomVideoPlayer from './CustomVideoPlayer';
|
| 13 |
|
| 14 |
interface FullScreenSwiperProps {
|
|
|
|
| 20 |
|
| 21 |
/* ─────────────────────────────────────────────────────────────
|
| 22 |
ZoomableImage
|
| 23 |
+
• Gesture.Pinch() — native multi-touch pinch-to-zoom
|
| 24 |
+
• Gesture.Pan() — single-finger swipe-down-to-close
|
| 25 |
+
• Both run on the UI thread via reanimated worklets
|
| 26 |
───────────────────────────────────────────────────────────── */
|
| 27 |
+
function ZoomableImage({
|
| 28 |
+
uri, thumbUri, width, height, onSwipeDown, onZoomChange,
|
| 29 |
+
}: {
|
| 30 |
+
uri: string; thumbUri?: string; width: number; height: number;
|
| 31 |
onSwipeDown: () => void; onZoomChange: (zooming: boolean) => void;
|
| 32 |
}) {
|
| 33 |
+
const scale = useSharedValue(1);
|
| 34 |
+
const savedScale = useSharedValue(1);
|
| 35 |
+
const translateY = useSharedValue(0);
|
| 36 |
+
|
| 37 |
+
const pinchGesture = Gesture.Pinch()
|
| 38 |
+
.onStart(() => {
|
| 39 |
+
'worklet';
|
| 40 |
+
runOnJS(onZoomChange)(true);
|
| 41 |
+
})
|
| 42 |
+
.onUpdate((e) => {
|
| 43 |
+
'worklet';
|
| 44 |
+
scale.value = Math.max(0.5, Math.min(5, savedScale.value * e.scale));
|
| 45 |
+
})
|
| 46 |
+
.onEnd(() => {
|
| 47 |
+
'worklet';
|
| 48 |
+
runOnJS(onZoomChange)(false);
|
| 49 |
+
if (scale.value < 1.15) {
|
| 50 |
+
scale.value = withSpring(1, { damping: 15, stiffness: 150 });
|
| 51 |
+
savedScale.value = 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
} else {
|
| 53 |
+
savedScale.value = Math.min(5, scale.value);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
}
|
| 55 |
+
});
|
| 56 |
+
|
| 57 |
+
const panGesture = Gesture.Pan()
|
| 58 |
+
.minPointers(1)
|
| 59 |
+
.maxPointers(1)
|
| 60 |
+
.activeOffsetY(20)
|
| 61 |
+
.failOffsetX([-15, 15])
|
| 62 |
+
.onStart(() => {
|
| 63 |
+
'worklet';
|
| 64 |
+
runOnJS(onZoomChange)(true);
|
| 65 |
+
})
|
| 66 |
+
.onUpdate((e) => {
|
| 67 |
+
'worklet';
|
| 68 |
+
translateY.value = e.translationY;
|
| 69 |
+
})
|
| 70 |
+
.onEnd((e) => {
|
| 71 |
+
'worklet';
|
| 72 |
+
runOnJS(onZoomChange)(false);
|
| 73 |
+
if (e.translationY > 100) {
|
| 74 |
+
runOnJS(onSwipeDown)();
|
| 75 |
} else {
|
| 76 |
+
translateY.value = withSpring(0, { damping: 15, stiffness: 150 });
|
|
|
|
|
|
|
| 77 |
}
|
| 78 |
+
});
|
| 79 |
+
|
| 80 |
+
const composedGesture = Gesture.Simultaneous(pinchGesture, panGesture);
|
| 81 |
+
|
| 82 |
+
const animatedStyle = useAnimatedStyle(() => ({
|
| 83 |
+
transform: [
|
| 84 |
+
{ scale: scale.value },
|
| 85 |
+
{ translateY: translateY.value },
|
| 86 |
+
],
|
| 87 |
+
}));
|
| 88 |
|
| 89 |
return (
|
| 90 |
+
<GestureDetector gesture={composedGesture}>
|
| 91 |
+
<Animated.View
|
| 92 |
+
style={[
|
| 93 |
+
{ width, height, justifyContent: 'center', alignItems: 'center' },
|
| 94 |
+
animatedStyle,
|
| 95 |
+
]}
|
| 96 |
+
>
|
| 97 |
+
<Image
|
| 98 |
+
source={{ uri }}
|
| 99 |
+
placeholder={thumbUri ? { uri: thumbUri } : undefined}
|
| 100 |
+
style={{ width: '100%', height: '100%' }}
|
| 101 |
+
contentFit="contain"
|
| 102 |
+
cachePolicy="memory-disk"
|
| 103 |
+
transition={200}
|
| 104 |
+
backgroundColor="transparent"
|
| 105 |
+
allowDownscaling={false}
|
| 106 |
+
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
</Animated.View>
|
| 108 |
+
</GestureDetector>
|
| 109 |
);
|
| 110 |
}
|
| 111 |
|
|
|
|
| 112 |
/* ─────────────────────────────────────────────────────────────
|
| 113 |
FullScreenSwiper
|
| 114 |
+
Uses RNGH's FlatList so gesture handlers inside items
|
| 115 |
+
properly negotiate with the scroll gesture on Android.
|
| 116 |
───────────────────────────────────────────────────────────── */
|
| 117 |
+
export default function FullScreenSwiper({
|
| 118 |
+
assets, initialIndex, onClose, onIndexChange,
|
| 119 |
}: FullScreenSwiperProps) {
|
| 120 |
const { width, height } = useWindowDimensions();
|
| 121 |
const [activeIndex, setActiveIndex] = useState(initialIndex);
|
keystone-app/package-lock.json
CHANGED
|
@@ -31,6 +31,7 @@
|
|
| 31 |
"react-dom": "19.1.0",
|
| 32 |
"react-native": "0.81.5",
|
| 33 |
"react-native-gesture-handler": "~2.28.0",
|
|
|
|
| 34 |
"react-native-safe-area-context": "~5.6.0",
|
| 35 |
"react-native-screens": "~4.16.0",
|
| 36 |
"react-native-web": "^0.21.2"
|
|
@@ -1171,6 +1172,22 @@
|
|
| 1171 |
"@babel/core": "^7.0.0-0"
|
| 1172 |
}
|
| 1173 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1174 |
"node_modules/@babel/plugin-transform-typescript": {
|
| 1175 |
"version": "7.29.7",
|
| 1176 |
"license": "MIT",
|
|
@@ -2999,6 +3016,556 @@
|
|
| 2999 |
"node": ">= 20.19.4"
|
| 3000 |
}
|
| 3001 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3002 |
"node_modules/@react-native/normalize-colors": {
|
| 3003 |
"version": "0.81.5",
|
| 3004 |
"license": "MIT"
|
|
@@ -7888,6 +8455,21 @@
|
|
| 7888 |
"react-native": "*"
|
| 7889 |
}
|
| 7890 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7891 |
"node_modules/react-native-safe-area-context": {
|
| 7892 |
"version": "5.6.2",
|
| 7893 |
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.6.2.tgz",
|
|
@@ -7939,6 +8521,32 @@
|
|
| 7939 |
"version": "6.0.0",
|
| 7940 |
"license": "MIT"
|
| 7941 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7942 |
"node_modules/react-native/node_modules/@react-native/virtualized-lists": {
|
| 7943 |
"version": "0.81.5",
|
| 7944 |
"license": "MIT",
|
|
|
|
| 31 |
"react-dom": "19.1.0",
|
| 32 |
"react-native": "0.81.5",
|
| 33 |
"react-native-gesture-handler": "~2.28.0",
|
| 34 |
+
"react-native-reanimated": "~4.1.1",
|
| 35 |
"react-native-safe-area-context": "~5.6.0",
|
| 36 |
"react-native-screens": "~4.16.0",
|
| 37 |
"react-native-web": "^0.21.2"
|
|
|
|
| 1172 |
"@babel/core": "^7.0.0-0"
|
| 1173 |
}
|
| 1174 |
},
|
| 1175 |
+
"node_modules/@babel/plugin-transform-template-literals": {
|
| 1176 |
+
"version": "7.29.7",
|
| 1177 |
+
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.29.7.tgz",
|
| 1178 |
+
"integrity": "sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==",
|
| 1179 |
+
"license": "MIT",
|
| 1180 |
+
"peer": true,
|
| 1181 |
+
"dependencies": {
|
| 1182 |
+
"@babel/helper-plugin-utils": "^7.29.7"
|
| 1183 |
+
},
|
| 1184 |
+
"engines": {
|
| 1185 |
+
"node": ">=6.9.0"
|
| 1186 |
+
},
|
| 1187 |
+
"peerDependencies": {
|
| 1188 |
+
"@babel/core": "^7.0.0-0"
|
| 1189 |
+
}
|
| 1190 |
+
},
|
| 1191 |
"node_modules/@babel/plugin-transform-typescript": {
|
| 1192 |
"version": "7.29.7",
|
| 1193 |
"license": "MIT",
|
|
|
|
| 3016 |
"node": ">= 20.19.4"
|
| 3017 |
}
|
| 3018 |
},
|
| 3019 |
+
"node_modules/@react-native/metro-babel-transformer": {
|
| 3020 |
+
"version": "0.86.0",
|
| 3021 |
+
"resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.86.0.tgz",
|
| 3022 |
+
"integrity": "sha512-SjKej3E5qIahqo/G+rSOrmJUQM44RyKtWtO+VfmKAAMoJWkBFomM22hTLKCIS5cdbIAJ9COAmU+KAi2wVSO0wQ==",
|
| 3023 |
+
"license": "MIT",
|
| 3024 |
+
"peer": true,
|
| 3025 |
+
"dependencies": {
|
| 3026 |
+
"@babel/core": "^7.25.2",
|
| 3027 |
+
"@react-native/babel-preset": "0.86.0",
|
| 3028 |
+
"hermes-parser": "0.36.0",
|
| 3029 |
+
"nullthrows": "^1.1.1"
|
| 3030 |
+
},
|
| 3031 |
+
"engines": {
|
| 3032 |
+
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
| 3033 |
+
},
|
| 3034 |
+
"peerDependencies": {
|
| 3035 |
+
"@babel/core": "*"
|
| 3036 |
+
}
|
| 3037 |
+
},
|
| 3038 |
+
"node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/babel-plugin-codegen": {
|
| 3039 |
+
"version": "0.86.0",
|
| 3040 |
+
"resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.86.0.tgz",
|
| 3041 |
+
"integrity": "sha512-qdsABWNW7uTll90l4Vh03gjeyu3WVDi2CyiiyvYGMRDcoYbjbQi6df3BMAm9lQI2yslZ1T14LlDDAsgTwNxplA==",
|
| 3042 |
+
"license": "MIT",
|
| 3043 |
+
"peer": true,
|
| 3044 |
+
"dependencies": {
|
| 3045 |
+
"@babel/traverse": "^7.29.0",
|
| 3046 |
+
"@react-native/codegen": "0.86.0"
|
| 3047 |
+
},
|
| 3048 |
+
"engines": {
|
| 3049 |
+
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
| 3050 |
+
}
|
| 3051 |
+
},
|
| 3052 |
+
"node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/babel-preset": {
|
| 3053 |
+
"version": "0.86.0",
|
| 3054 |
+
"resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.86.0.tgz",
|
| 3055 |
+
"integrity": "sha512-bYQcWiPySNvF4dns9Ls9gMmwgq66ohvM9Fwc/Kn8r85t66UNHxch3p1QwPiSorDelFauZwJbgo9+ReibTgvpbA==",
|
| 3056 |
+
"license": "MIT",
|
| 3057 |
+
"peer": true,
|
| 3058 |
+
"dependencies": {
|
| 3059 |
+
"@babel/core": "^7.25.2",
|
| 3060 |
+
"@babel/plugin-proposal-export-default-from": "^7.24.7",
|
| 3061 |
+
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
| 3062 |
+
"@babel/plugin-syntax-export-default-from": "^7.24.7",
|
| 3063 |
+
"@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
|
| 3064 |
+
"@babel/plugin-syntax-optional-chaining": "^7.8.3",
|
| 3065 |
+
"@babel/plugin-transform-async-generator-functions": "^7.25.4",
|
| 3066 |
+
"@babel/plugin-transform-async-to-generator": "^7.24.7",
|
| 3067 |
+
"@babel/plugin-transform-block-scoping": "^7.25.0",
|
| 3068 |
+
"@babel/plugin-transform-class-properties": "^7.25.4",
|
| 3069 |
+
"@babel/plugin-transform-classes": "^7.25.4",
|
| 3070 |
+
"@babel/plugin-transform-destructuring": "^7.24.8",
|
| 3071 |
+
"@babel/plugin-transform-flow-strip-types": "^7.25.2",
|
| 3072 |
+
"@babel/plugin-transform-for-of": "^7.24.7",
|
| 3073 |
+
"@babel/plugin-transform-modules-commonjs": "^7.24.8",
|
| 3074 |
+
"@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7",
|
| 3075 |
+
"@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7",
|
| 3076 |
+
"@babel/plugin-transform-optional-catch-binding": "^7.24.7",
|
| 3077 |
+
"@babel/plugin-transform-optional-chaining": "^7.24.8",
|
| 3078 |
+
"@babel/plugin-transform-private-methods": "^7.24.7",
|
| 3079 |
+
"@babel/plugin-transform-private-property-in-object": "^7.24.7",
|
| 3080 |
+
"@babel/plugin-transform-react-display-name": "^7.24.7",
|
| 3081 |
+
"@babel/plugin-transform-react-jsx": "^7.25.2",
|
| 3082 |
+
"@babel/plugin-transform-react-jsx-self": "^7.24.7",
|
| 3083 |
+
"@babel/plugin-transform-react-jsx-source": "^7.24.7",
|
| 3084 |
+
"@babel/plugin-transform-regenerator": "^7.24.7",
|
| 3085 |
+
"@babel/plugin-transform-runtime": "^7.24.7",
|
| 3086 |
+
"@babel/plugin-transform-typescript": "^7.25.2",
|
| 3087 |
+
"@babel/plugin-transform-unicode-regex": "^7.24.7",
|
| 3088 |
+
"@react-native/babel-plugin-codegen": "0.86.0",
|
| 3089 |
+
"babel-plugin-syntax-hermes-parser": "0.36.0",
|
| 3090 |
+
"babel-plugin-transform-flow-enums": "^0.0.2",
|
| 3091 |
+
"react-refresh": "^0.14.0"
|
| 3092 |
+
},
|
| 3093 |
+
"engines": {
|
| 3094 |
+
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
| 3095 |
+
},
|
| 3096 |
+
"peerDependencies": {
|
| 3097 |
+
"@babel/core": "*"
|
| 3098 |
+
}
|
| 3099 |
+
},
|
| 3100 |
+
"node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/codegen": {
|
| 3101 |
+
"version": "0.86.0",
|
| 3102 |
+
"resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.86.0.tgz",
|
| 3103 |
+
"integrity": "sha512-uTs9DBo3+/lUqinsGZK0FKJRBVClrwMXoZToaDxE1Q2SL2e55vs2GwyZfIKzPl5uJnbu4PfFMIp0/mLXLWUMuA==",
|
| 3104 |
+
"license": "MIT",
|
| 3105 |
+
"peer": true,
|
| 3106 |
+
"dependencies": {
|
| 3107 |
+
"@babel/core": "^7.25.2",
|
| 3108 |
+
"@babel/parser": "^7.29.0",
|
| 3109 |
+
"hermes-parser": "0.36.0",
|
| 3110 |
+
"invariant": "^2.2.4",
|
| 3111 |
+
"nullthrows": "^1.1.1",
|
| 3112 |
+
"tinyglobby": "^0.2.15",
|
| 3113 |
+
"yargs": "^17.6.2"
|
| 3114 |
+
},
|
| 3115 |
+
"engines": {
|
| 3116 |
+
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
| 3117 |
+
},
|
| 3118 |
+
"peerDependencies": {
|
| 3119 |
+
"@babel/core": "*"
|
| 3120 |
+
}
|
| 3121 |
+
},
|
| 3122 |
+
"node_modules/@react-native/metro-babel-transformer/node_modules/babel-plugin-syntax-hermes-parser": {
|
| 3123 |
+
"version": "0.36.0",
|
| 3124 |
+
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.36.0.tgz",
|
| 3125 |
+
"integrity": "sha512-LhD0xdoedDw7ansQgXbB2DADLZIK/LRXuWNBPuVzMc5S2WK5GyT89tCM+cQzxFGO0mGyLK6D5TrVOJJzAoDy8Q==",
|
| 3126 |
+
"license": "MIT",
|
| 3127 |
+
"peer": true,
|
| 3128 |
+
"dependencies": {
|
| 3129 |
+
"hermes-parser": "0.36.0"
|
| 3130 |
+
}
|
| 3131 |
+
},
|
| 3132 |
+
"node_modules/@react-native/metro-babel-transformer/node_modules/hermes-estree": {
|
| 3133 |
+
"version": "0.36.0",
|
| 3134 |
+
"resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.36.0.tgz",
|
| 3135 |
+
"integrity": "sha512-A1+8zn5oss2CFP7pKsOaxorQG6FNIz1WU1VDqruLPPZl3LVgeE2C5xfFg8Ow6/Ow4mSslLLtYP1J3n38eKyW9w==",
|
| 3136 |
+
"license": "MIT",
|
| 3137 |
+
"peer": true
|
| 3138 |
+
},
|
| 3139 |
+
"node_modules/@react-native/metro-babel-transformer/node_modules/hermes-parser": {
|
| 3140 |
+
"version": "0.36.0",
|
| 3141 |
+
"resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.36.0.tgz",
|
| 3142 |
+
"integrity": "sha512-GdpwMmH5x6IpC1cijvcvYnlPB60Mh6kTSF/NFdYV/j56gYdi+0RIakYs+eqOV+bbO0SW7mgVVGSsTJxyPQfo3w==",
|
| 3143 |
+
"license": "MIT",
|
| 3144 |
+
"peer": true,
|
| 3145 |
+
"dependencies": {
|
| 3146 |
+
"hermes-estree": "0.36.0"
|
| 3147 |
+
}
|
| 3148 |
+
},
|
| 3149 |
+
"node_modules/@react-native/metro-config": {
|
| 3150 |
+
"version": "0.86.0",
|
| 3151 |
+
"resolved": "https://registry.npmjs.org/@react-native/metro-config/-/metro-config-0.86.0.tgz",
|
| 3152 |
+
"integrity": "sha512-7v+xbTeEci9ZcQ/Z1OqI4RXcqN69wSMDYL5BAMvOReZ7U04+aDQ0/SQhClYPn6x2/RxM4WzMKSAuNyLKqvYVtw==",
|
| 3153 |
+
"license": "MIT",
|
| 3154 |
+
"peer": true,
|
| 3155 |
+
"dependencies": {
|
| 3156 |
+
"@react-native/js-polyfills": "0.86.0",
|
| 3157 |
+
"@react-native/metro-babel-transformer": "0.86.0",
|
| 3158 |
+
"metro-config": "^0.84.3",
|
| 3159 |
+
"metro-runtime": "^0.84.3"
|
| 3160 |
+
},
|
| 3161 |
+
"engines": {
|
| 3162 |
+
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
| 3163 |
+
}
|
| 3164 |
+
},
|
| 3165 |
+
"node_modules/@react-native/metro-config/node_modules/@babel/code-frame": {
|
| 3166 |
+
"version": "7.29.7",
|
| 3167 |
+
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
|
| 3168 |
+
"integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
|
| 3169 |
+
"license": "MIT",
|
| 3170 |
+
"peer": true,
|
| 3171 |
+
"dependencies": {
|
| 3172 |
+
"@babel/helper-validator-identifier": "^7.29.7",
|
| 3173 |
+
"js-tokens": "^4.0.0",
|
| 3174 |
+
"picocolors": "^1.1.1"
|
| 3175 |
+
},
|
| 3176 |
+
"engines": {
|
| 3177 |
+
"node": ">=6.9.0"
|
| 3178 |
+
}
|
| 3179 |
+
},
|
| 3180 |
+
"node_modules/@react-native/metro-config/node_modules/@react-native/js-polyfills": {
|
| 3181 |
+
"version": "0.86.0",
|
| 3182 |
+
"resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.86.0.tgz",
|
| 3183 |
+
"integrity": "sha512-zYy/Cjd1VTnZ2iCNaG9bDF9C3l2ntESiPRscjIlI5FKugu6aeTwsDSv1aI8Bc4Kp3vEdoVg+UQhLAhE4svREaQ==",
|
| 3184 |
+
"license": "MIT",
|
| 3185 |
+
"peer": true,
|
| 3186 |
+
"engines": {
|
| 3187 |
+
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
| 3188 |
+
}
|
| 3189 |
+
},
|
| 3190 |
+
"node_modules/@react-native/metro-config/node_modules/accepts": {
|
| 3191 |
+
"version": "2.0.0",
|
| 3192 |
+
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
| 3193 |
+
"integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
|
| 3194 |
+
"license": "MIT",
|
| 3195 |
+
"peer": true,
|
| 3196 |
+
"dependencies": {
|
| 3197 |
+
"mime-types": "^3.0.0",
|
| 3198 |
+
"negotiator": "^1.0.0"
|
| 3199 |
+
},
|
| 3200 |
+
"engines": {
|
| 3201 |
+
"node": ">= 0.6"
|
| 3202 |
+
}
|
| 3203 |
+
},
|
| 3204 |
+
"node_modules/@react-native/metro-config/node_modules/hermes-estree": {
|
| 3205 |
+
"version": "0.35.0",
|
| 3206 |
+
"resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.35.0.tgz",
|
| 3207 |
+
"integrity": "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==",
|
| 3208 |
+
"license": "MIT",
|
| 3209 |
+
"peer": true
|
| 3210 |
+
},
|
| 3211 |
+
"node_modules/@react-native/metro-config/node_modules/hermes-parser": {
|
| 3212 |
+
"version": "0.35.0",
|
| 3213 |
+
"resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.35.0.tgz",
|
| 3214 |
+
"integrity": "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==",
|
| 3215 |
+
"license": "MIT",
|
| 3216 |
+
"peer": true,
|
| 3217 |
+
"dependencies": {
|
| 3218 |
+
"hermes-estree": "0.35.0"
|
| 3219 |
+
}
|
| 3220 |
+
},
|
| 3221 |
+
"node_modules/@react-native/metro-config/node_modules/metro": {
|
| 3222 |
+
"version": "0.84.4",
|
| 3223 |
+
"resolved": "https://registry.npmjs.org/metro/-/metro-0.84.4.tgz",
|
| 3224 |
+
"integrity": "sha512-8ETTubqfD6ornDy2zYDvRcKnVDOXdFJsjetYDBsY4oAsb6NJkiwFR+FaMESyGppFmQUyBQA4H4sFGxzcQSGtFA==",
|
| 3225 |
+
"license": "MIT",
|
| 3226 |
+
"peer": true,
|
| 3227 |
+
"dependencies": {
|
| 3228 |
+
"@babel/code-frame": "^7.29.0",
|
| 3229 |
+
"@babel/core": "^7.25.2",
|
| 3230 |
+
"@babel/generator": "^7.29.1",
|
| 3231 |
+
"@babel/parser": "^7.29.0",
|
| 3232 |
+
"@babel/template": "^7.28.6",
|
| 3233 |
+
"@babel/traverse": "^7.29.0",
|
| 3234 |
+
"@babel/types": "^7.29.0",
|
| 3235 |
+
"accepts": "^2.0.0",
|
| 3236 |
+
"ci-info": "^2.0.0",
|
| 3237 |
+
"connect": "^3.6.5",
|
| 3238 |
+
"debug": "^4.4.0",
|
| 3239 |
+
"error-stack-parser": "^2.0.6",
|
| 3240 |
+
"flow-enums-runtime": "^0.0.6",
|
| 3241 |
+
"graceful-fs": "^4.2.4",
|
| 3242 |
+
"hermes-parser": "0.35.0",
|
| 3243 |
+
"image-size": "^1.0.2",
|
| 3244 |
+
"invariant": "^2.2.4",
|
| 3245 |
+
"jest-worker": "^29.7.0",
|
| 3246 |
+
"jsc-safe-url": "^0.2.2",
|
| 3247 |
+
"lodash.throttle": "^4.1.1",
|
| 3248 |
+
"metro-babel-transformer": "0.84.4",
|
| 3249 |
+
"metro-cache": "0.84.4",
|
| 3250 |
+
"metro-cache-key": "0.84.4",
|
| 3251 |
+
"metro-config": "0.84.4",
|
| 3252 |
+
"metro-core": "0.84.4",
|
| 3253 |
+
"metro-file-map": "0.84.4",
|
| 3254 |
+
"metro-resolver": "0.84.4",
|
| 3255 |
+
"metro-runtime": "0.84.4",
|
| 3256 |
+
"metro-source-map": "0.84.4",
|
| 3257 |
+
"metro-symbolicate": "0.84.4",
|
| 3258 |
+
"metro-transform-plugins": "0.84.4",
|
| 3259 |
+
"metro-transform-worker": "0.84.4",
|
| 3260 |
+
"mime-types": "^3.0.1",
|
| 3261 |
+
"nullthrows": "^1.1.1",
|
| 3262 |
+
"serialize-error": "^2.1.0",
|
| 3263 |
+
"source-map": "^0.5.6",
|
| 3264 |
+
"throat": "^5.0.0",
|
| 3265 |
+
"ws": "^7.5.10",
|
| 3266 |
+
"yargs": "^17.6.2"
|
| 3267 |
+
},
|
| 3268 |
+
"bin": {
|
| 3269 |
+
"metro": "src/cli.js"
|
| 3270 |
+
},
|
| 3271 |
+
"engines": {
|
| 3272 |
+
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
| 3273 |
+
}
|
| 3274 |
+
},
|
| 3275 |
+
"node_modules/@react-native/metro-config/node_modules/metro-babel-transformer": {
|
| 3276 |
+
"version": "0.84.4",
|
| 3277 |
+
"resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.84.4.tgz",
|
| 3278 |
+
"integrity": "sha512-rvCfz8snl9h20VcvpOHxZuHP1SlAkv4HXbzw7nyyVwu6Eqo5PRerbakQ9XmUCOsRy70spJ37O+G1TK8oMzo48g==",
|
| 3279 |
+
"license": "MIT",
|
| 3280 |
+
"peer": true,
|
| 3281 |
+
"dependencies": {
|
| 3282 |
+
"@babel/core": "^7.25.2",
|
| 3283 |
+
"flow-enums-runtime": "^0.0.6",
|
| 3284 |
+
"hermes-parser": "0.35.0",
|
| 3285 |
+
"metro-cache-key": "0.84.4",
|
| 3286 |
+
"nullthrows": "^1.1.1"
|
| 3287 |
+
},
|
| 3288 |
+
"engines": {
|
| 3289 |
+
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
| 3290 |
+
}
|
| 3291 |
+
},
|
| 3292 |
+
"node_modules/@react-native/metro-config/node_modules/metro-cache": {
|
| 3293 |
+
"version": "0.84.4",
|
| 3294 |
+
"resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.84.4.tgz",
|
| 3295 |
+
"integrity": "sha512-gpcFQdSLUwUCk71saKoE64jLFbx2nwTfVCcPSULMNT8QYq0p1eZZE29Jvd0HtT/UlhC3ZOutLxJME5xqD2JUZg==",
|
| 3296 |
+
"license": "MIT",
|
| 3297 |
+
"peer": true,
|
| 3298 |
+
"dependencies": {
|
| 3299 |
+
"exponential-backoff": "^3.1.1",
|
| 3300 |
+
"flow-enums-runtime": "^0.0.6",
|
| 3301 |
+
"https-proxy-agent": "^7.0.5",
|
| 3302 |
+
"metro-core": "0.84.4"
|
| 3303 |
+
},
|
| 3304 |
+
"engines": {
|
| 3305 |
+
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
| 3306 |
+
}
|
| 3307 |
+
},
|
| 3308 |
+
"node_modules/@react-native/metro-config/node_modules/metro-cache-key": {
|
| 3309 |
+
"version": "0.84.4",
|
| 3310 |
+
"resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.84.4.tgz",
|
| 3311 |
+
"integrity": "sha512-wVO79aGrkYImpnaVS4+d5RrRBRPX31QtvKB3wKGBuiNSznduZTQHzsrJZRroFJSwnygrzdsGUtDQPuqqFjFdvw==",
|
| 3312 |
+
"license": "MIT",
|
| 3313 |
+
"peer": true,
|
| 3314 |
+
"dependencies": {
|
| 3315 |
+
"flow-enums-runtime": "^0.0.6"
|
| 3316 |
+
},
|
| 3317 |
+
"engines": {
|
| 3318 |
+
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
| 3319 |
+
}
|
| 3320 |
+
},
|
| 3321 |
+
"node_modules/@react-native/metro-config/node_modules/metro-config": {
|
| 3322 |
+
"version": "0.84.4",
|
| 3323 |
+
"resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.84.4.tgz",
|
| 3324 |
+
"integrity": "sha512-PMotGDjXcXLWo2TMRH+VR99phFNgYTwqh4OoieIKK3yTJa1Jmkl+fZJxDO0jfBvNF+WESHciHvpNuBtXaF3B0Q==",
|
| 3325 |
+
"license": "MIT",
|
| 3326 |
+
"peer": true,
|
| 3327 |
+
"dependencies": {
|
| 3328 |
+
"connect": "^3.6.5",
|
| 3329 |
+
"flow-enums-runtime": "^0.0.6",
|
| 3330 |
+
"jest-validate": "^29.7.0",
|
| 3331 |
+
"metro": "0.84.4",
|
| 3332 |
+
"metro-cache": "0.84.4",
|
| 3333 |
+
"metro-core": "0.84.4",
|
| 3334 |
+
"metro-runtime": "0.84.4",
|
| 3335 |
+
"yaml": "^2.6.1"
|
| 3336 |
+
},
|
| 3337 |
+
"engines": {
|
| 3338 |
+
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
| 3339 |
+
}
|
| 3340 |
+
},
|
| 3341 |
+
"node_modules/@react-native/metro-config/node_modules/metro-core": {
|
| 3342 |
+
"version": "0.84.4",
|
| 3343 |
+
"resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.84.4.tgz",
|
| 3344 |
+
"integrity": "sha512-HONpWC5LGXZn3ffkd4Hu6AIrfE7j4Z0g0wMo/goV24WOB3lhuFZ40KgvaDiSw8iyQHloMYay5N/wPX+z8oN/PQ==",
|
| 3345 |
+
"license": "MIT",
|
| 3346 |
+
"peer": true,
|
| 3347 |
+
"dependencies": {
|
| 3348 |
+
"flow-enums-runtime": "^0.0.6",
|
| 3349 |
+
"lodash.throttle": "^4.1.1",
|
| 3350 |
+
"metro-resolver": "0.84.4"
|
| 3351 |
+
},
|
| 3352 |
+
"engines": {
|
| 3353 |
+
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
| 3354 |
+
}
|
| 3355 |
+
},
|
| 3356 |
+
"node_modules/@react-native/metro-config/node_modules/metro-file-map": {
|
| 3357 |
+
"version": "0.84.4",
|
| 3358 |
+
"resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.84.4.tgz",
|
| 3359 |
+
"integrity": "sha512-KSVDi/u60hKPx++NLu3MTIvyjzNoJnFAF8PQFxaj1jiSka/wjw+Ua6sNuJ0TDHQv+7AAoFQxeMgaRAe8Yic5wQ==",
|
| 3360 |
+
"license": "MIT",
|
| 3361 |
+
"peer": true,
|
| 3362 |
+
"dependencies": {
|
| 3363 |
+
"debug": "^4.4.0",
|
| 3364 |
+
"fb-watchman": "^2.0.0",
|
| 3365 |
+
"flow-enums-runtime": "^0.0.6",
|
| 3366 |
+
"graceful-fs": "^4.2.4",
|
| 3367 |
+
"invariant": "^2.2.4",
|
| 3368 |
+
"jest-worker": "^29.7.0",
|
| 3369 |
+
"micromatch": "^4.0.4",
|
| 3370 |
+
"nullthrows": "^1.1.1",
|
| 3371 |
+
"walker": "^1.0.7"
|
| 3372 |
+
},
|
| 3373 |
+
"engines": {
|
| 3374 |
+
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
| 3375 |
+
}
|
| 3376 |
+
},
|
| 3377 |
+
"node_modules/@react-native/metro-config/node_modules/metro-minify-terser": {
|
| 3378 |
+
"version": "0.84.4",
|
| 3379 |
+
"resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.84.4.tgz",
|
| 3380 |
+
"integrity": "sha512-5qpbaVOMC7CPitIpuewzVeGw7E+C3ykbv2mqTjQLl85Z3annSVGlSCTcsZjqXZzjupfK4Ztj3dDc4kc44NZwtQ==",
|
| 3381 |
+
"license": "MIT",
|
| 3382 |
+
"peer": true,
|
| 3383 |
+
"dependencies": {
|
| 3384 |
+
"flow-enums-runtime": "^0.0.6",
|
| 3385 |
+
"terser": "^5.15.0"
|
| 3386 |
+
},
|
| 3387 |
+
"engines": {
|
| 3388 |
+
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
| 3389 |
+
}
|
| 3390 |
+
},
|
| 3391 |
+
"node_modules/@react-native/metro-config/node_modules/metro-resolver": {
|
| 3392 |
+
"version": "0.84.4",
|
| 3393 |
+
"resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.84.4.tgz",
|
| 3394 |
+
"integrity": "sha512-1qLgbxQ5ZGhhutuPot1Yp348ofDsATL2WkrHF65TobqTT9K3P9qJXw38bomk7ncp5B7OYMfWwtyBZo1lCV792A==",
|
| 3395 |
+
"license": "MIT",
|
| 3396 |
+
"peer": true,
|
| 3397 |
+
"dependencies": {
|
| 3398 |
+
"flow-enums-runtime": "^0.0.6"
|
| 3399 |
+
},
|
| 3400 |
+
"engines": {
|
| 3401 |
+
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
| 3402 |
+
}
|
| 3403 |
+
},
|
| 3404 |
+
"node_modules/@react-native/metro-config/node_modules/metro-runtime": {
|
| 3405 |
+
"version": "0.84.4",
|
| 3406 |
+
"resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.84.4.tgz",
|
| 3407 |
+
"integrity": "sha512-Jibypds4g7AhzdRKY+kDoj51s5EXMwgyp5ddtlreDAsWefMdOx+agWqgm0H2XSZ/ueanHHVM89fnf5OJnlxa8Q==",
|
| 3408 |
+
"license": "MIT",
|
| 3409 |
+
"peer": true,
|
| 3410 |
+
"dependencies": {
|
| 3411 |
+
"@babel/runtime": "^7.25.0",
|
| 3412 |
+
"flow-enums-runtime": "^0.0.6"
|
| 3413 |
+
},
|
| 3414 |
+
"engines": {
|
| 3415 |
+
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
| 3416 |
+
}
|
| 3417 |
+
},
|
| 3418 |
+
"node_modules/@react-native/metro-config/node_modules/metro-source-map": {
|
| 3419 |
+
"version": "0.84.4",
|
| 3420 |
+
"resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.84.4.tgz",
|
| 3421 |
+
"integrity": "sha512-jbWkPxIesVuo1IWkvezmMJld6iu8nD62GsrZiV6jP37AOdbo4OBq1FJ+qkOg8sV05wAHB//jAbziuW0SlJfW4g==",
|
| 3422 |
+
"license": "MIT",
|
| 3423 |
+
"peer": true,
|
| 3424 |
+
"dependencies": {
|
| 3425 |
+
"@babel/traverse": "^7.29.0",
|
| 3426 |
+
"@babel/types": "^7.29.0",
|
| 3427 |
+
"flow-enums-runtime": "^0.0.6",
|
| 3428 |
+
"invariant": "^2.2.4",
|
| 3429 |
+
"metro-symbolicate": "0.84.4",
|
| 3430 |
+
"nullthrows": "^1.1.1",
|
| 3431 |
+
"ob1": "0.84.4",
|
| 3432 |
+
"source-map": "^0.5.6",
|
| 3433 |
+
"vlq": "^1.0.0"
|
| 3434 |
+
},
|
| 3435 |
+
"engines": {
|
| 3436 |
+
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
| 3437 |
+
}
|
| 3438 |
+
},
|
| 3439 |
+
"node_modules/@react-native/metro-config/node_modules/metro-symbolicate": {
|
| 3440 |
+
"version": "0.84.4",
|
| 3441 |
+
"resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.84.4.tgz",
|
| 3442 |
+
"integrity": "sha512-OnfpacxUqGPZQ27t8qK9mFa7uqHIlVWeqRqkCbvMvreEBiamEeOn8krKtcwgP5M4cYDPwuSmCTopHMVthqG4zA==",
|
| 3443 |
+
"license": "MIT",
|
| 3444 |
+
"peer": true,
|
| 3445 |
+
"dependencies": {
|
| 3446 |
+
"flow-enums-runtime": "^0.0.6",
|
| 3447 |
+
"invariant": "^2.2.4",
|
| 3448 |
+
"metro-source-map": "0.84.4",
|
| 3449 |
+
"nullthrows": "^1.1.1",
|
| 3450 |
+
"source-map": "^0.5.6",
|
| 3451 |
+
"vlq": "^1.0.0"
|
| 3452 |
+
},
|
| 3453 |
+
"bin": {
|
| 3454 |
+
"metro-symbolicate": "src/index.js"
|
| 3455 |
+
},
|
| 3456 |
+
"engines": {
|
| 3457 |
+
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
| 3458 |
+
}
|
| 3459 |
+
},
|
| 3460 |
+
"node_modules/@react-native/metro-config/node_modules/metro-transform-plugins": {
|
| 3461 |
+
"version": "0.84.4",
|
| 3462 |
+
"resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.84.4.tgz",
|
| 3463 |
+
"integrity": "sha512-kehr6HbAecqD0/a3xLXobELdPaAmRAl8bel0qagPF4vhZtux93nS8S4eq2kgKt6J2GnQpVjSoW1PXdst04mwow==",
|
| 3464 |
+
"license": "MIT",
|
| 3465 |
+
"peer": true,
|
| 3466 |
+
"dependencies": {
|
| 3467 |
+
"@babel/core": "^7.25.2",
|
| 3468 |
+
"@babel/generator": "^7.29.1",
|
| 3469 |
+
"@babel/template": "^7.28.6",
|
| 3470 |
+
"@babel/traverse": "^7.29.0",
|
| 3471 |
+
"flow-enums-runtime": "^0.0.6",
|
| 3472 |
+
"nullthrows": "^1.1.1"
|
| 3473 |
+
},
|
| 3474 |
+
"engines": {
|
| 3475 |
+
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
| 3476 |
+
}
|
| 3477 |
+
},
|
| 3478 |
+
"node_modules/@react-native/metro-config/node_modules/metro-transform-worker": {
|
| 3479 |
+
"version": "0.84.4",
|
| 3480 |
+
"resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.84.4.tgz",
|
| 3481 |
+
"integrity": "sha512-W1IYMvvXTu4MxYr7d9h7CeG2vpIr3bmLLIavkPY4O1ilzDrvS8z/NEe6y+pC44Ff7raMXQgYSfdqDUwN/i39gg==",
|
| 3482 |
+
"license": "MIT",
|
| 3483 |
+
"peer": true,
|
| 3484 |
+
"dependencies": {
|
| 3485 |
+
"@babel/core": "^7.25.2",
|
| 3486 |
+
"@babel/generator": "^7.29.1",
|
| 3487 |
+
"@babel/parser": "^7.29.0",
|
| 3488 |
+
"@babel/types": "^7.29.0",
|
| 3489 |
+
"flow-enums-runtime": "^0.0.6",
|
| 3490 |
+
"metro": "0.84.4",
|
| 3491 |
+
"metro-babel-transformer": "0.84.4",
|
| 3492 |
+
"metro-cache": "0.84.4",
|
| 3493 |
+
"metro-cache-key": "0.84.4",
|
| 3494 |
+
"metro-minify-terser": "0.84.4",
|
| 3495 |
+
"metro-source-map": "0.84.4",
|
| 3496 |
+
"metro-transform-plugins": "0.84.4",
|
| 3497 |
+
"nullthrows": "^1.1.1"
|
| 3498 |
+
},
|
| 3499 |
+
"engines": {
|
| 3500 |
+
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
| 3501 |
+
}
|
| 3502 |
+
},
|
| 3503 |
+
"node_modules/@react-native/metro-config/node_modules/mime-db": {
|
| 3504 |
+
"version": "1.54.0",
|
| 3505 |
+
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
| 3506 |
+
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
| 3507 |
+
"license": "MIT",
|
| 3508 |
+
"peer": true,
|
| 3509 |
+
"engines": {
|
| 3510 |
+
"node": ">= 0.6"
|
| 3511 |
+
}
|
| 3512 |
+
},
|
| 3513 |
+
"node_modules/@react-native/metro-config/node_modules/mime-types": {
|
| 3514 |
+
"version": "3.0.2",
|
| 3515 |
+
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
|
| 3516 |
+
"integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
|
| 3517 |
+
"license": "MIT",
|
| 3518 |
+
"peer": true,
|
| 3519 |
+
"dependencies": {
|
| 3520 |
+
"mime-db": "^1.54.0"
|
| 3521 |
+
},
|
| 3522 |
+
"engines": {
|
| 3523 |
+
"node": ">=18"
|
| 3524 |
+
},
|
| 3525 |
+
"funding": {
|
| 3526 |
+
"type": "opencollective",
|
| 3527 |
+
"url": "https://opencollective.com/express"
|
| 3528 |
+
}
|
| 3529 |
+
},
|
| 3530 |
+
"node_modules/@react-native/metro-config/node_modules/negotiator": {
|
| 3531 |
+
"version": "1.0.0",
|
| 3532 |
+
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
| 3533 |
+
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
|
| 3534 |
+
"license": "MIT",
|
| 3535 |
+
"peer": true,
|
| 3536 |
+
"engines": {
|
| 3537 |
+
"node": ">= 0.6"
|
| 3538 |
+
}
|
| 3539 |
+
},
|
| 3540 |
+
"node_modules/@react-native/metro-config/node_modules/ob1": {
|
| 3541 |
+
"version": "0.84.4",
|
| 3542 |
+
"resolved": "https://registry.npmjs.org/ob1/-/ob1-0.84.4.tgz",
|
| 3543 |
+
"integrity": "sha512-eJXMpz4aQHXF/YBB9ddqZDIS+ooO91hObo9FoW/xBkr54/zCwYYCDqT/O54vNo8kOkWs5Ou/y28NgdrV0edQNA==",
|
| 3544 |
+
"license": "MIT",
|
| 3545 |
+
"peer": true,
|
| 3546 |
+
"dependencies": {
|
| 3547 |
+
"flow-enums-runtime": "^0.0.6"
|
| 3548 |
+
},
|
| 3549 |
+
"engines": {
|
| 3550 |
+
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
|
| 3551 |
+
}
|
| 3552 |
+
},
|
| 3553 |
+
"node_modules/@react-native/metro-config/node_modules/yaml": {
|
| 3554 |
+
"version": "2.9.0",
|
| 3555 |
+
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
|
| 3556 |
+
"integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
|
| 3557 |
+
"license": "ISC",
|
| 3558 |
+
"peer": true,
|
| 3559 |
+
"bin": {
|
| 3560 |
+
"yaml": "bin.mjs"
|
| 3561 |
+
},
|
| 3562 |
+
"engines": {
|
| 3563 |
+
"node": ">= 14.6"
|
| 3564 |
+
},
|
| 3565 |
+
"funding": {
|
| 3566 |
+
"url": "https://github.com/sponsors/eemeli"
|
| 3567 |
+
}
|
| 3568 |
+
},
|
| 3569 |
"node_modules/@react-native/normalize-colors": {
|
| 3570 |
"version": "0.81.5",
|
| 3571 |
"license": "MIT"
|
|
|
|
| 8455 |
"react-native": "*"
|
| 8456 |
}
|
| 8457 |
},
|
| 8458 |
+
"node_modules/react-native-reanimated": {
|
| 8459 |
+
"version": "4.1.7",
|
| 8460 |
+
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-4.1.7.tgz",
|
| 8461 |
+
"integrity": "sha512-Q4H6xA3Tn7QL0/E/KjI86I1KK4tcf+ErRE04LH34Etka2oVQhW6oXQ+Q8ZcDCVxiWp5vgbBH6XcH8BOo4w/Rhg==",
|
| 8462 |
+
"license": "MIT",
|
| 8463 |
+
"dependencies": {
|
| 8464 |
+
"react-native-is-edge-to-edge": "^1.2.1",
|
| 8465 |
+
"semver": "^7.7.2"
|
| 8466 |
+
},
|
| 8467 |
+
"peerDependencies": {
|
| 8468 |
+
"react": "*",
|
| 8469 |
+
"react-native": "0.78 - 0.82",
|
| 8470 |
+
"react-native-worklets": "0.5 - 0.8"
|
| 8471 |
+
}
|
| 8472 |
+
},
|
| 8473 |
"node_modules/react-native-safe-area-context": {
|
| 8474 |
"version": "5.6.2",
|
| 8475 |
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.6.2.tgz",
|
|
|
|
| 8521 |
"version": "6.0.0",
|
| 8522 |
"license": "MIT"
|
| 8523 |
},
|
| 8524 |
+
"node_modules/react-native-worklets": {
|
| 8525 |
+
"version": "0.8.3",
|
| 8526 |
+
"resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.8.3.tgz",
|
| 8527 |
+
"integrity": "sha512-oCBJROyLU7yG/1R8s0INMflygTH71bx+5XcYkH0CM938TlhSoVbiunE1WVW5FZa51vwYqfLie/IXMX2s1Kh3eg==",
|
| 8528 |
+
"license": "MIT",
|
| 8529 |
+
"peer": true,
|
| 8530 |
+
"dependencies": {
|
| 8531 |
+
"@babel/plugin-transform-arrow-functions": "^7.27.1",
|
| 8532 |
+
"@babel/plugin-transform-class-properties": "^7.27.1",
|
| 8533 |
+
"@babel/plugin-transform-classes": "^7.28.4",
|
| 8534 |
+
"@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1",
|
| 8535 |
+
"@babel/plugin-transform-optional-chaining": "^7.27.1",
|
| 8536 |
+
"@babel/plugin-transform-shorthand-properties": "^7.27.1",
|
| 8537 |
+
"@babel/plugin-transform-template-literals": "^7.27.1",
|
| 8538 |
+
"@babel/plugin-transform-unicode-regex": "^7.27.1",
|
| 8539 |
+
"@babel/preset-typescript": "^7.27.1",
|
| 8540 |
+
"convert-source-map": "^2.0.0",
|
| 8541 |
+
"semver": "^7.7.3"
|
| 8542 |
+
},
|
| 8543 |
+
"peerDependencies": {
|
| 8544 |
+
"@babel/core": "*",
|
| 8545 |
+
"@react-native/metro-config": "*",
|
| 8546 |
+
"react": "*",
|
| 8547 |
+
"react-native": "0.81 - 0.85"
|
| 8548 |
+
}
|
| 8549 |
+
},
|
| 8550 |
"node_modules/react-native/node_modules/@react-native/virtualized-lists": {
|
| 8551 |
"version": "0.81.5",
|
| 8552 |
"license": "MIT",
|
keystone-app/package.json
CHANGED
|
@@ -26,6 +26,7 @@
|
|
| 26 |
"react-dom": "19.1.0",
|
| 27 |
"react-native": "0.81.5",
|
| 28 |
"react-native-gesture-handler": "~2.28.0",
|
|
|
|
| 29 |
"react-native-safe-area-context": "~5.6.0",
|
| 30 |
"react-native-screens": "~4.16.0",
|
| 31 |
"react-native-web": "^0.21.2"
|
|
|
|
| 26 |
"react-dom": "19.1.0",
|
| 27 |
"react-native": "0.81.5",
|
| 28 |
"react-native-gesture-handler": "~2.28.0",
|
| 29 |
+
"react-native-reanimated": "~4.1.1",
|
| 30 |
"react-native-safe-area-context": "~5.6.0",
|
| 31 |
"react-native-screens": "~4.16.0",
|
| 32 |
"react-native-web": "^0.21.2"
|