react-native-grab 0.0.2 → 0.0.4
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 +6 -5
- package/dist/cjs/metro/withReactNativeGrab.js +31 -31
- package/dist/cjs/metro/withReactNativeGrab.js.map +1 -1
- package/dist/cjs/react-native/__tests__/dom-traversal.test.js +160 -0
- package/dist/cjs/react-native/__tests__/dom-traversal.test.js.map +1 -0
- package/dist/cjs/react-native/__tests__/mock-dom.js +65 -0
- package/dist/cjs/react-native/__tests__/mock-dom.js.map +1 -0
- package/dist/cjs/react-native/__tests__/setup.js +43 -0
- package/dist/cjs/react-native/__tests__/setup.js.map +1 -0
- package/dist/cjs/react-native/__tests__/z-index.test.js +106 -0
- package/dist/cjs/react-native/__tests__/z-index.test.js.map +1 -0
- package/dist/cjs/react-native/copy-payload.js +65 -69
- package/dist/cjs/react-native/copy-payload.js.map +1 -1
- package/dist/cjs/react-native/dom-traversal.js +170 -126
- package/dist/cjs/react-native/dom-traversal.js.map +1 -1
- package/dist/cjs/react-native/fiber.js +16 -16
- package/dist/cjs/react-native/fiber.js.map +1 -1
- package/dist/cjs/react-native/get-dev-server.js +9 -0
- package/dist/cjs/react-native/get-dev-server.js.map +1 -0
- package/dist/cjs/react-native/index.js +346 -260
- package/dist/cjs/react-native/index.js.map +1 -1
- package/dist/cjs/react-native/index.web.js +8 -0
- package/dist/cjs/react-native/index.web.js.map +1 -0
- package/dist/cjs/react-native/pointer-events.js +25 -28
- package/dist/cjs/react-native/pointer-events.js.map +1 -1
- package/dist/cjs/react-native/selection-trigger.js +1 -1
- package/dist/cjs/react-native/settings.js +7 -10
- package/dist/cjs/react-native/settings.js.map +1 -1
- package/dist/cjs/react-native/symbolicate.js.map +1 -1
- package/dist/cjs/react-native/types.js +0 -4
- package/dist/cjs/react-native/types.js.map +1 -1
- package/dist/cjs/react-native/z-index.js +23 -5
- package/dist/cjs/react-native/z-index.js.map +1 -1
- package/dist/esm/metro/index.js +1 -1
- package/dist/esm/metro/withReactNativeGrab.js +33 -33
- package/dist/esm/metro/withReactNativeGrab.js.map +1 -1
- package/dist/esm/react-native/__tests__/dom-traversal.test.js +158 -0
- package/dist/esm/react-native/__tests__/dom-traversal.test.js.map +1 -0
- package/dist/esm/react-native/__tests__/mock-dom.js +58 -0
- package/dist/esm/react-native/__tests__/mock-dom.js.map +1 -0
- package/dist/esm/react-native/__tests__/setup.js +41 -0
- package/dist/esm/react-native/__tests__/setup.js.map +1 -0
- package/dist/esm/react-native/__tests__/z-index.test.js +104 -0
- package/dist/esm/react-native/__tests__/z-index.test.js.map +1 -0
- package/dist/esm/react-native/copy-payload.js +67 -71
- package/dist/esm/react-native/copy-payload.js.map +1 -1
- package/dist/esm/react-native/dom-traversal.js +173 -127
- package/dist/esm/react-native/dom-traversal.js.map +1 -1
- package/dist/esm/react-native/fiber.js +17 -17
- package/dist/esm/react-native/fiber.js.map +1 -1
- package/dist/esm/react-native/get-dev-server.js +3 -0
- package/dist/esm/react-native/get-dev-server.js.map +1 -0
- package/dist/esm/react-native/index.js +351 -262
- package/dist/esm/react-native/index.js.map +1 -1
- package/dist/esm/react-native/index.web.js +4 -0
- package/dist/esm/react-native/index.web.js.map +1 -0
- package/dist/esm/react-native/pointer-events.js +26 -29
- package/dist/esm/react-native/pointer-events.js.map +1 -1
- package/dist/esm/react-native/selection-trigger.js +1 -1
- package/dist/esm/react-native/settings.js +6 -6
- package/dist/esm/react-native/settings.js.map +1 -1
- package/dist/esm/react-native/symbolicate.js +2 -2
- package/dist/esm/react-native/symbolicate.js.map +1 -1
- package/dist/esm/react-native/types.js +0 -4
- package/dist/esm/react-native/types.js.map +1 -1
- package/dist/esm/react-native/z-index.js +24 -6
- package/dist/esm/react-native/z-index.js.map +1 -1
- package/dist/types/metro/index.d.ts +1 -1
- package/dist/types/metro/withReactNativeGrab.d.ts.map +1 -1
- package/dist/types/react-native/__tests__/dom-traversal.test.d.ts +2 -0
- package/dist/types/react-native/__tests__/dom-traversal.test.d.ts.map +1 -0
- package/dist/types/react-native/__tests__/mock-dom.d.ts +24 -0
- package/dist/types/react-native/__tests__/mock-dom.d.ts.map +1 -0
- package/dist/types/react-native/__tests__/setup.d.ts +2 -0
- package/dist/types/react-native/__tests__/setup.d.ts.map +1 -0
- package/dist/types/react-native/__tests__/z-index.test.d.ts +2 -0
- package/dist/types/react-native/__tests__/z-index.test.d.ts.map +1 -0
- package/dist/types/react-native/copy-payload.d.ts +1 -1
- package/dist/types/react-native/copy-payload.d.ts.map +1 -1
- package/dist/types/react-native/dom-traversal.d.ts +19 -6
- package/dist/types/react-native/dom-traversal.d.ts.map +1 -1
- package/dist/types/react-native/fiber.d.ts +1 -1
- package/dist/types/react-native/fiber.d.ts.map +1 -1
- package/dist/types/react-native/get-dev-server.d.ts +3 -0
- package/dist/types/react-native/get-dev-server.d.ts.map +1 -0
- package/dist/types/react-native/index.d.ts +1 -1
- package/dist/types/react-native/index.d.ts.map +1 -1
- package/dist/types/react-native/index.web.d.ts +2 -0
- package/dist/types/react-native/index.web.d.ts.map +1 -0
- package/dist/types/react-native/pointer-events.d.ts +2 -2
- package/dist/types/react-native/pointer-events.d.ts.map +1 -1
- package/dist/types/react-native/settings.d.ts.map +1 -1
- package/dist/types/react-native/symbolicate.d.ts +1 -1
- package/dist/types/react-native/symbolicate.d.ts.map +1 -1
- package/dist/types/react-native/types.d.ts +3 -11
- package/dist/types/react-native/types.d.ts.map +1 -1
- package/dist/types/react-native/z-index.d.ts +1 -1
- package/dist/types/react-native/z-index.d.ts.map +1 -1
- package/package.json +30 -27
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Fragment, useEffect, useRef, useState } from
|
|
3
|
-
import { Animated, DevSettings, Dimensions, PanResponder, Platform, Pressable, StatusBar, StyleSheet, Text, useWindowDimensions, View, } from
|
|
4
|
-
import getDevServer from
|
|
5
|
-
import { FullWindowOverlay } from
|
|
6
|
-
import { buildInspectorCopyPayload } from
|
|
7
|
-
import {
|
|
8
|
-
import { requestInspectorSelection, subscribeInspectorSelectionRequest } from
|
|
9
|
-
import { getInspectorSettings, initializeInspectorSettings, subscribeInspectorSettings, updateInspectorSettings, } from
|
|
2
|
+
import { Fragment, useCallback, useEffect, useRef, useState, } from "react";
|
|
3
|
+
import { Animated, DevSettings, Dimensions, PanResponder, Platform, Pressable, StatusBar, StyleSheet, Text, useWindowDimensions, View, } from "react-native";
|
|
4
|
+
import { getDevServer } from "./get-dev-server";
|
|
5
|
+
import { FullWindowOverlay } from "react-native-screens";
|
|
6
|
+
import { buildInspectorCopyPayload } from "./copy-payload";
|
|
7
|
+
import { startSession as startTraversalSession, } from "./dom-traversal";
|
|
8
|
+
import { requestInspectorSelection, subscribeInspectorSelectionRequest } from "./selection-trigger";
|
|
9
|
+
import { getInspectorSettings, initializeInspectorSettings, subscribeInspectorSettings, updateInspectorSettings, } from "./settings";
|
|
10
10
|
const SETTLE_MS = 80;
|
|
11
11
|
const MOVEMENT_EPSILON_PX = 8;
|
|
12
12
|
const MAX_STACK_LINES = 3;
|
|
13
13
|
const MAX_PREVIEW_TEXT = 120;
|
|
14
|
-
const INSPECTOR_COPY_ENDPOINT =
|
|
14
|
+
const INSPECTOR_COPY_ENDPOINT = "/__react-native-grab/copy";
|
|
15
15
|
const FAB_SIZE = 56;
|
|
16
16
|
const FAB_SCREEN_PADDING = 8;
|
|
17
17
|
const FAB_DEFAULT_RIGHT_OFFSET = 20;
|
|
@@ -20,16 +20,10 @@ const getMetroBaseUrl = () => {
|
|
|
20
20
|
const devServer = getDevServer();
|
|
21
21
|
if (!devServer?.url)
|
|
22
22
|
return null;
|
|
23
|
-
return devServer.url.replace(/\/$/,
|
|
24
|
-
};
|
|
25
|
-
export const ElementInspector = () => {
|
|
26
|
-
if (Platform.OS === 'web') {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
return _jsx(ElementInspectorImpl, {});
|
|
23
|
+
return devServer.url.replace(/\/$/, "");
|
|
30
24
|
};
|
|
31
25
|
const getInitialFabPosition = () => {
|
|
32
|
-
const { width, height } = Dimensions.get(
|
|
26
|
+
const { width, height } = Dimensions.get("window");
|
|
33
27
|
return {
|
|
34
28
|
x: Math.max(FAB_SCREEN_PADDING, width - FAB_DEFAULT_RIGHT_OFFSET - FAB_SIZE),
|
|
35
29
|
y: Math.max(FAB_SCREEN_PADDING, height - FAB_DEFAULT_BOTTOM_OFFSET - FAB_SIZE),
|
|
@@ -43,74 +37,227 @@ const clampFabPosition = (x, y, windowWidth, windowHeight) => {
|
|
|
43
37
|
y: Math.min(Math.max(y, FAB_SCREEN_PADDING), maxY),
|
|
44
38
|
};
|
|
45
39
|
};
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
const settleTimeoutRef = useRef(null);
|
|
61
|
-
const stateRef = useRef(state);
|
|
62
|
-
stateRef.current = state;
|
|
63
|
-
const selectingHintAnim = useRef(new Animated.Value(0)).current;
|
|
64
|
-
const toastAnim = useRef(new Animated.Value(0)).current;
|
|
65
|
-
const fabAppearAnim = useRef(new Animated.Value(1)).current;
|
|
66
|
-
const fabPressAnim = useRef(new Animated.Value(1)).current;
|
|
67
|
-
const fabPosition = useRef(new Animated.ValueXY(getInitialFabPosition())).current;
|
|
68
|
-
const fabPositionRef = useRef(getInitialFabPosition());
|
|
69
|
-
const fabDragStartOffsetRef = useRef({ x: 0, y: 0 });
|
|
70
|
-
const windowSizeRef = useRef({ width: windowWidth, height: windowHeight });
|
|
71
|
-
windowSizeRef.current = { width: windowWidth, height: windowHeight };
|
|
72
|
-
const skipRefs = [inspectorRootRef, overlayRef];
|
|
73
|
-
const idle = state.status === 'idle';
|
|
74
|
-
const selecting = state.status === 'selecting';
|
|
75
|
-
const overlayVisible = state.status !== 'idle';
|
|
76
|
-
const activeHighlightRect = state.status === 'copying' ? state.selectedRect : highlightRect;
|
|
77
|
-
const startSelecting = () => {
|
|
78
|
-
selectedHitTargetRef.current = null;
|
|
79
|
-
setState({ status: 'selecting' });
|
|
80
|
-
};
|
|
81
|
-
const performQuery = (x, y, offset) => {
|
|
82
|
-
const appY = y - statusBarOffset;
|
|
83
|
-
const hitTarget = findHitTargetAtPoint(anchorRef, skipRefs, x, appY, Platform.OS === 'ios'
|
|
40
|
+
const useLatestRef = (value) => {
|
|
41
|
+
const ref = useRef(value);
|
|
42
|
+
ref.current = value;
|
|
43
|
+
return ref;
|
|
44
|
+
};
|
|
45
|
+
const useTraversalSelectionSession = ({ anchorRef, inspectorRootRef, overlayRef, statusBarOffset, setHighlightRect, }) => {
|
|
46
|
+
const traversalSessionRef = useRef(null);
|
|
47
|
+
const skipRefsRef = useRef([inspectorRootRef, overlayRef]);
|
|
48
|
+
const stopSession = useCallback(() => {
|
|
49
|
+
traversalSessionRef.current?.stop();
|
|
50
|
+
traversalSessionRef.current = null;
|
|
51
|
+
}, []);
|
|
52
|
+
const startSession = useCallback(() => {
|
|
53
|
+
const skipPredicate = Platform.OS === "ios"
|
|
84
54
|
? (node) => {
|
|
85
55
|
const inspectorRootNode = inspectorRootRef.current;
|
|
86
56
|
if (!inspectorRootNode)
|
|
87
57
|
return false;
|
|
88
58
|
return node === inspectorRootNode.parentElement;
|
|
89
59
|
}
|
|
90
|
-
: undefined
|
|
60
|
+
: undefined;
|
|
61
|
+
stopSession();
|
|
62
|
+
traversalSessionRef.current = startTraversalSession({
|
|
63
|
+
anchorRef,
|
|
64
|
+
skipRefs: skipRefsRef.current,
|
|
65
|
+
offset: statusBarOffset,
|
|
66
|
+
skipPredicate,
|
|
67
|
+
});
|
|
68
|
+
setHighlightRect(null);
|
|
69
|
+
}, [anchorRef, inspectorRootRef, setHighlightRect, statusBarOffset, stopSession]);
|
|
70
|
+
const queryAtPoint = useCallback((x, y) => {
|
|
71
|
+
const session = traversalSessionRef.current;
|
|
72
|
+
if (!session) {
|
|
73
|
+
setHighlightRect(null);
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const hitTarget = session.move({
|
|
77
|
+
x,
|
|
78
|
+
y: y - statusBarOffset,
|
|
79
|
+
});
|
|
91
80
|
setHighlightRect(hitTarget?.rect ?? null);
|
|
92
81
|
return hitTarget;
|
|
82
|
+
}, [setHighlightRect, statusBarOffset]);
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
return () => {
|
|
85
|
+
stopSession();
|
|
86
|
+
};
|
|
87
|
+
}, [stopSession]);
|
|
88
|
+
return {
|
|
89
|
+
startSession,
|
|
90
|
+
queryAtPoint,
|
|
91
|
+
stopSession,
|
|
93
92
|
};
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const
|
|
93
|
+
};
|
|
94
|
+
const useSelectionGesture = ({ isSelecting, queryAtPoint, onHitTarget, onCancel, }) => {
|
|
95
|
+
const isSelectingRef = useLatestRef(isSelecting);
|
|
96
|
+
const queryAtPointRef = useLatestRef(queryAtPoint);
|
|
97
|
+
const onHitTargetRef = useLatestRef(onHitTarget);
|
|
98
|
+
const onCancelRef = useLatestRef(onCancel);
|
|
99
|
+
const settleTimeoutRef = useRef(null);
|
|
100
|
+
const lastQueryPositionRef = useRef(null);
|
|
101
|
+
const movePositionRef = useRef({ x: 0, y: 0 });
|
|
102
|
+
const clearSettleTimeout = useCallback(() => {
|
|
97
103
|
if (settleTimeoutRef.current != null) {
|
|
98
104
|
clearTimeout(settleTimeoutRef.current);
|
|
99
105
|
settleTimeoutRef.current = null;
|
|
100
106
|
}
|
|
101
|
-
|
|
107
|
+
}, []);
|
|
108
|
+
const resetGestureState = useCallback(() => {
|
|
109
|
+
clearSettleTimeout();
|
|
102
110
|
lastQueryPositionRef.current = null;
|
|
103
|
-
|
|
104
|
-
|
|
111
|
+
}, [clearSettleTimeout]);
|
|
112
|
+
const panResponder = useRef(PanResponder.create({
|
|
113
|
+
onStartShouldSetPanResponder: () => true,
|
|
114
|
+
onMoveShouldSetPanResponder: () => true,
|
|
115
|
+
onPanResponderGrant: (evt) => {
|
|
116
|
+
if (!isSelectingRef.current)
|
|
117
|
+
return;
|
|
118
|
+
const x = evt.nativeEvent.locationX;
|
|
119
|
+
const y = evt.nativeEvent.locationY;
|
|
120
|
+
queryAtPointRef.current(x, y);
|
|
121
|
+
lastQueryPositionRef.current = { x, y };
|
|
122
|
+
},
|
|
123
|
+
onPanResponderMove: (evt) => {
|
|
124
|
+
if (!isSelectingRef.current)
|
|
125
|
+
return;
|
|
126
|
+
movePositionRef.current = {
|
|
127
|
+
x: evt.nativeEvent.locationX,
|
|
128
|
+
y: evt.nativeEvent.locationY,
|
|
129
|
+
};
|
|
130
|
+
clearSettleTimeout();
|
|
131
|
+
settleTimeoutRef.current = setTimeout(() => {
|
|
132
|
+
settleTimeoutRef.current = null;
|
|
133
|
+
if (!isSelectingRef.current)
|
|
134
|
+
return;
|
|
135
|
+
const pos = movePositionRef.current;
|
|
136
|
+
const last = lastQueryPositionRef.current;
|
|
137
|
+
const distance = last ? Math.hypot(pos.x - last.x, pos.y - last.y) : Infinity;
|
|
138
|
+
if (distance < MOVEMENT_EPSILON_PX)
|
|
139
|
+
return;
|
|
140
|
+
lastQueryPositionRef.current = pos;
|
|
141
|
+
queryAtPointRef.current(pos.x, pos.y);
|
|
142
|
+
}, SETTLE_MS);
|
|
143
|
+
},
|
|
144
|
+
onPanResponderRelease: (evt) => {
|
|
145
|
+
if (!isSelectingRef.current)
|
|
146
|
+
return;
|
|
147
|
+
const hitTarget = queryAtPointRef.current(evt.nativeEvent.locationX, evt.nativeEvent.locationY);
|
|
148
|
+
if (!hitTarget) {
|
|
149
|
+
onCancelRef.current();
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
onHitTargetRef.current(hitTarget);
|
|
153
|
+
},
|
|
154
|
+
onPanResponderTerminate: () => {
|
|
155
|
+
if (isSelectingRef.current) {
|
|
156
|
+
onCancelRef.current();
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
})).current;
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
return () => {
|
|
162
|
+
clearSettleTimeout();
|
|
163
|
+
};
|
|
164
|
+
}, [clearSettleTimeout]);
|
|
165
|
+
return {
|
|
166
|
+
panHandlers: panResponder.panHandlers,
|
|
167
|
+
resetGestureState,
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
const useFabController = (windowWidth, windowHeight) => {
|
|
171
|
+
const initialFabPosition = useRef(getInitialFabPosition()).current;
|
|
172
|
+
const fabAppearAnim = useRef(new Animated.Value(1)).current;
|
|
173
|
+
const fabPressAnim = useRef(new Animated.Value(1)).current;
|
|
174
|
+
const fabPosition = useRef(new Animated.ValueXY(initialFabPosition)).current;
|
|
175
|
+
const fabPositionRef = useRef(initialFabPosition);
|
|
176
|
+
const fabDragStartOffsetRef = useRef({ x: 0, y: 0 });
|
|
177
|
+
const windowSizeRef = useRef({ width: windowWidth, height: windowHeight });
|
|
178
|
+
windowSizeRef.current = { width: windowWidth, height: windowHeight };
|
|
179
|
+
const animateFabPress = useCallback((toValue) => {
|
|
180
|
+
Animated.spring(fabPressAnim, {
|
|
181
|
+
toValue,
|
|
182
|
+
useNativeDriver: true,
|
|
183
|
+
tension: 220,
|
|
184
|
+
friction: 16,
|
|
185
|
+
}).start();
|
|
186
|
+
}, [fabPressAnim]);
|
|
187
|
+
const fabPanResponder = useRef(PanResponder.create({
|
|
188
|
+
onStartShouldSetPanResponder: () => false,
|
|
189
|
+
onMoveShouldSetPanResponder: (_evt, gestureState) => Math.abs(gestureState.dx) > 2 || Math.abs(gestureState.dy) > 2,
|
|
190
|
+
onPanResponderGrant: (_evt, gestureState) => {
|
|
191
|
+
fabDragStartOffsetRef.current = {
|
|
192
|
+
x: fabPositionRef.current.x - gestureState.x0,
|
|
193
|
+
y: fabPositionRef.current.y - gestureState.y0,
|
|
194
|
+
};
|
|
195
|
+
},
|
|
196
|
+
onPanResponderMove: (_evt, gestureState) => {
|
|
197
|
+
const { width, height } = windowSizeRef.current;
|
|
198
|
+
const nextPosition = clampFabPosition(gestureState.moveX + fabDragStartOffsetRef.current.x, gestureState.moveY + fabDragStartOffsetRef.current.y, width, height);
|
|
199
|
+
fabPositionRef.current = nextPosition;
|
|
200
|
+
fabPosition.setValue(nextPosition);
|
|
201
|
+
},
|
|
202
|
+
onPanResponderTerminationRequest: () => true,
|
|
203
|
+
onPanResponderTerminate: () => {
|
|
204
|
+
fabPressAnim.setValue(1);
|
|
205
|
+
},
|
|
206
|
+
onPanResponderRelease: () => {
|
|
207
|
+
fabPressAnim.setValue(1);
|
|
208
|
+
},
|
|
209
|
+
})).current;
|
|
210
|
+
useEffect(() => {
|
|
211
|
+
const nextPosition = clampFabPosition(fabPositionRef.current.x, fabPositionRef.current.y, windowWidth, windowHeight);
|
|
212
|
+
fabPositionRef.current = nextPosition;
|
|
213
|
+
fabPosition.setValue(nextPosition);
|
|
214
|
+
}, [fabPosition, windowHeight, windowWidth]);
|
|
215
|
+
return {
|
|
216
|
+
animateFabPress,
|
|
217
|
+
fabAppearAnim,
|
|
218
|
+
fabPanHandlers: fabPanResponder.panHandlers,
|
|
219
|
+
fabPosition,
|
|
220
|
+
fabPressAnim,
|
|
105
221
|
};
|
|
106
|
-
|
|
222
|
+
};
|
|
223
|
+
export const ElementInspector = () => {
|
|
224
|
+
const { width: windowWidth, height: windowHeight } = useWindowDimensions();
|
|
225
|
+
const statusBarOffset = Platform.OS === "android" ? (StatusBar.currentHeight ?? 0) : 0;
|
|
226
|
+
const [state, setState] = useState({ status: "idle" });
|
|
227
|
+
const [highlightRect, setHighlightRect] = useState(null);
|
|
228
|
+
const [toastVisible, setToastVisible] = useState(false);
|
|
229
|
+
const [selectionPillVisible, setSelectionPillVisible] = useState(() => getInspectorSettings().selectionPillVisible);
|
|
230
|
+
const isClosingRef = useRef(false);
|
|
231
|
+
const anchorRef = useRef(null);
|
|
232
|
+
const inspectorRootRef = useRef(null);
|
|
233
|
+
const overlayRef = useRef(null);
|
|
234
|
+
const selectingHintAnim = useRef(new Animated.Value(0)).current;
|
|
235
|
+
const toastAnim = useRef(new Animated.Value(0)).current;
|
|
236
|
+
const idle = state.status === "idle";
|
|
237
|
+
const selecting = state.status === "selecting";
|
|
238
|
+
const overlayVisible = state.status !== "idle";
|
|
239
|
+
const activeHighlightRect = state.status === "copying" ? state.selectedRect : highlightRect;
|
|
240
|
+
const { animateFabPress, fabAppearAnim, fabPanHandlers, fabPosition, fabPressAnim } = useFabController(windowWidth, windowHeight);
|
|
241
|
+
const { queryAtPoint, startSession, stopSession } = useTraversalSelectionSession({
|
|
242
|
+
anchorRef,
|
|
243
|
+
inspectorRootRef,
|
|
244
|
+
overlayRef,
|
|
245
|
+
statusBarOffset,
|
|
246
|
+
setHighlightRect,
|
|
247
|
+
});
|
|
248
|
+
const closeOverlayImmediately = useCallback(() => {
|
|
249
|
+
stopSession();
|
|
250
|
+
setState({ status: "idle" });
|
|
251
|
+
setHighlightRect(null);
|
|
252
|
+
}, [stopSession]);
|
|
253
|
+
const closeOverlay = useCallback(() => {
|
|
107
254
|
if (isClosingRef.current)
|
|
108
255
|
return;
|
|
109
256
|
isClosingRef.current = true;
|
|
110
257
|
closeOverlayImmediately();
|
|
111
258
|
isClosingRef.current = false;
|
|
112
|
-
};
|
|
113
|
-
const showCopiedToast = () => {
|
|
259
|
+
}, [closeOverlayImmediately]);
|
|
260
|
+
const showCopiedToast = useCallback(() => {
|
|
114
261
|
setToastVisible(true);
|
|
115
262
|
toastAnim.setValue(0);
|
|
116
263
|
Animated.sequence([
|
|
@@ -126,15 +273,15 @@ const ElementInspectorImpl = () => {
|
|
|
126
273
|
useNativeDriver: true,
|
|
127
274
|
}),
|
|
128
275
|
]).start(() => setToastVisible(false));
|
|
129
|
-
};
|
|
130
|
-
const copyFeedbackToHostClipboard = async (text) => {
|
|
276
|
+
}, [toastAnim]);
|
|
277
|
+
const copyFeedbackToHostClipboard = useCallback(async (text) => {
|
|
131
278
|
const baseUrl = getMetroBaseUrl();
|
|
132
279
|
if (!baseUrl)
|
|
133
280
|
return false;
|
|
134
281
|
try {
|
|
135
282
|
const response = await fetch(`${baseUrl}${INSPECTOR_COPY_ENDPOINT}`, {
|
|
136
|
-
method:
|
|
137
|
-
headers: {
|
|
283
|
+
method: "POST",
|
|
284
|
+
headers: { "Content-Type": "application/json" },
|
|
138
285
|
body: JSON.stringify({ text }),
|
|
139
286
|
});
|
|
140
287
|
return response.ok;
|
|
@@ -142,7 +289,33 @@ const ElementInspectorImpl = () => {
|
|
|
142
289
|
catch {
|
|
143
290
|
return false;
|
|
144
291
|
}
|
|
145
|
-
};
|
|
292
|
+
}, []);
|
|
293
|
+
const handleSelectionHit = useCallback((hitTarget) => {
|
|
294
|
+
setState({ status: "copying", selectedRect: hitTarget.rect });
|
|
295
|
+
void (async () => {
|
|
296
|
+
const clipboardText = await buildInspectorCopyPayload(hitTarget, {
|
|
297
|
+
maxStackLines: MAX_STACK_LINES,
|
|
298
|
+
maxPreviewText: MAX_PREVIEW_TEXT,
|
|
299
|
+
pathRootHint: null,
|
|
300
|
+
});
|
|
301
|
+
const copied = await copyFeedbackToHostClipboard(clipboardText);
|
|
302
|
+
if (copied) {
|
|
303
|
+
showCopiedToast();
|
|
304
|
+
}
|
|
305
|
+
closeOverlay();
|
|
306
|
+
})();
|
|
307
|
+
}, [closeOverlay, copyFeedbackToHostClipboard, showCopiedToast]);
|
|
308
|
+
const { panHandlers: selectionPanHandlers, resetGestureState } = useSelectionGesture({
|
|
309
|
+
isSelecting: selecting,
|
|
310
|
+
queryAtPoint,
|
|
311
|
+
onHitTarget: handleSelectionHit,
|
|
312
|
+
onCancel: closeOverlay,
|
|
313
|
+
});
|
|
314
|
+
const startSelecting = useCallback(() => {
|
|
315
|
+
resetGestureState();
|
|
316
|
+
startSession();
|
|
317
|
+
setState({ status: "selecting" });
|
|
318
|
+
}, [resetGestureState, startSession]);
|
|
146
319
|
useEffect(() => {
|
|
147
320
|
return subscribeInspectorSettings(() => {
|
|
148
321
|
setSelectionPillVisible(getInspectorSettings().selectionPillVisible);
|
|
@@ -150,15 +323,14 @@ const ElementInspectorImpl = () => {
|
|
|
150
323
|
}, []);
|
|
151
324
|
useEffect(() => {
|
|
152
325
|
return subscribeInspectorSelectionRequest(() => {
|
|
153
|
-
setHighlightRect(null);
|
|
154
326
|
startSelecting();
|
|
155
327
|
});
|
|
156
|
-
}, []);
|
|
328
|
+
}, [startSelecting]);
|
|
157
329
|
useEffect(() => {
|
|
158
330
|
void initializeInspectorSettings();
|
|
159
331
|
}, []);
|
|
160
332
|
useEffect(() => {
|
|
161
|
-
DevSettings.addMenuItem(
|
|
333
|
+
DevSettings.addMenuItem("Toggle React Native Grab", () => {
|
|
162
334
|
void (async () => {
|
|
163
335
|
await initializeInspectorSettings();
|
|
164
336
|
const current = getInspectorSettings().selectionPillVisible;
|
|
@@ -166,104 +338,11 @@ const ElementInspectorImpl = () => {
|
|
|
166
338
|
await updateInspectorSettings({ selectionPillVisible: !current });
|
|
167
339
|
})();
|
|
168
340
|
});
|
|
169
|
-
DevSettings.addMenuItem(
|
|
170
|
-
console.log(
|
|
341
|
+
DevSettings.addMenuItem("Start React Native Grab Selection", () => {
|
|
342
|
+
console.log("[InspectorFeedback] Start selection menu item pressed");
|
|
171
343
|
requestInspectorSelection();
|
|
172
344
|
});
|
|
173
345
|
}, []);
|
|
174
|
-
useEffect(() => {
|
|
175
|
-
const nextPosition = clampFabPosition(fabPositionRef.current.x, fabPositionRef.current.y, windowWidth, windowHeight);
|
|
176
|
-
fabPositionRef.current = nextPosition;
|
|
177
|
-
fabPosition.setValue(nextPosition);
|
|
178
|
-
}, [fabPosition, windowHeight, windowWidth]);
|
|
179
|
-
const panResponder = useRef(PanResponder.create({
|
|
180
|
-
onStartShouldSetPanResponder: () => true,
|
|
181
|
-
onMoveShouldSetPanResponder: () => true,
|
|
182
|
-
onPanResponderGrant: (evt) => {
|
|
183
|
-
if (stateRef.current.status !== 'selecting')
|
|
184
|
-
return;
|
|
185
|
-
const x = evt.nativeEvent.locationX;
|
|
186
|
-
const y = evt.nativeEvent.locationY;
|
|
187
|
-
performQueryRef.current(x, y, statusBarOffset);
|
|
188
|
-
lastQueryPositionRef.current = { x, y };
|
|
189
|
-
},
|
|
190
|
-
onPanResponderMove: (evt) => {
|
|
191
|
-
if (stateRef.current.status !== 'selecting')
|
|
192
|
-
return;
|
|
193
|
-
const x = evt.nativeEvent.locationX;
|
|
194
|
-
const y = evt.nativeEvent.locationY;
|
|
195
|
-
movePositionRef.current = { x, y };
|
|
196
|
-
if (settleTimeoutRef.current != null) {
|
|
197
|
-
clearTimeout(settleTimeoutRef.current);
|
|
198
|
-
settleTimeoutRef.current = null;
|
|
199
|
-
}
|
|
200
|
-
settleTimeoutRef.current = setTimeout(() => {
|
|
201
|
-
settleTimeoutRef.current = null;
|
|
202
|
-
if (stateRef.current.status !== 'selecting')
|
|
203
|
-
return;
|
|
204
|
-
const pos = movePositionRef.current;
|
|
205
|
-
const last = lastQueryPositionRef.current;
|
|
206
|
-
const distance = last
|
|
207
|
-
? Math.hypot(pos.x - last.x, pos.y - last.y)
|
|
208
|
-
: Infinity;
|
|
209
|
-
if (distance < MOVEMENT_EPSILON_PX)
|
|
210
|
-
return;
|
|
211
|
-
lastQueryPositionRef.current = pos;
|
|
212
|
-
performQueryRef.current(pos.x, pos.y, statusBarOffset);
|
|
213
|
-
}, SETTLE_MS);
|
|
214
|
-
},
|
|
215
|
-
onPanResponderRelease: (evt) => {
|
|
216
|
-
if (stateRef.current.status !== 'selecting')
|
|
217
|
-
return;
|
|
218
|
-
const hitTarget = performQueryRef.current(evt.nativeEvent.locationX, evt.nativeEvent.locationY, statusBarOffset);
|
|
219
|
-
if (!hitTarget) {
|
|
220
|
-
closeOverlay();
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
selectedHitTargetRef.current = hitTarget;
|
|
224
|
-
setState({ status: 'copying', selectedRect: hitTarget.rect });
|
|
225
|
-
void (async () => {
|
|
226
|
-
const clipboardText = await buildInspectorCopyPayload(hitTarget, {
|
|
227
|
-
maxStackLines: MAX_STACK_LINES,
|
|
228
|
-
maxPreviewText: MAX_PREVIEW_TEXT,
|
|
229
|
-
pathRootHint: null,
|
|
230
|
-
});
|
|
231
|
-
const copied = await copyFeedbackToHostClipboard(clipboardText);
|
|
232
|
-
if (copied) {
|
|
233
|
-
showCopiedToast();
|
|
234
|
-
}
|
|
235
|
-
closeOverlay();
|
|
236
|
-
})();
|
|
237
|
-
},
|
|
238
|
-
onPanResponderTerminate: () => {
|
|
239
|
-
if (stateRef.current.status === 'selecting') {
|
|
240
|
-
closeOverlay();
|
|
241
|
-
}
|
|
242
|
-
},
|
|
243
|
-
})).current;
|
|
244
|
-
const fabPanResponder = useRef(PanResponder.create({
|
|
245
|
-
onStartShouldSetPanResponder: () => false,
|
|
246
|
-
onMoveShouldSetPanResponder: (_evt, gestureState) => Math.abs(gestureState.dx) > 2 || Math.abs(gestureState.dy) > 2,
|
|
247
|
-
onPanResponderGrant: (_evt, gestureState) => {
|
|
248
|
-
fabDragStartOffsetRef.current = {
|
|
249
|
-
x: fabPositionRef.current.x - gestureState.x0,
|
|
250
|
-
y: fabPositionRef.current.y - gestureState.y0,
|
|
251
|
-
};
|
|
252
|
-
},
|
|
253
|
-
onPanResponderMove: (_evt, gestureState) => {
|
|
254
|
-
const { width, height } = windowSizeRef.current;
|
|
255
|
-
const nextPosition = clampFabPosition(gestureState.moveX + fabDragStartOffsetRef.current.x, gestureState.moveY + fabDragStartOffsetRef.current.y, width, height);
|
|
256
|
-
fabPositionRef.current = nextPosition;
|
|
257
|
-
fabPosition.setValue(nextPosition);
|
|
258
|
-
},
|
|
259
|
-
onPanResponderTerminationRequest: () => true,
|
|
260
|
-
onPanResponderTerminate: () => {
|
|
261
|
-
fabPressAnim.setValue(1);
|
|
262
|
-
},
|
|
263
|
-
onPanResponderRelease: () => {
|
|
264
|
-
fabPressAnim.setValue(1);
|
|
265
|
-
},
|
|
266
|
-
})).current;
|
|
267
346
|
useEffect(() => {
|
|
268
347
|
if (!selecting) {
|
|
269
348
|
selectingHintAnim.stopAnimation();
|
|
@@ -292,76 +371,86 @@ const ElementInspectorImpl = () => {
|
|
|
292
371
|
useNativeDriver: true,
|
|
293
372
|
}).start();
|
|
294
373
|
}, [fabAppearAnim, idle]);
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
},
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
],
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
374
|
+
useEffect(() => {
|
|
375
|
+
return () => {
|
|
376
|
+
resetGestureState();
|
|
377
|
+
stopSession();
|
|
378
|
+
};
|
|
379
|
+
}, [resetGestureState, stopSession]);
|
|
380
|
+
return (_jsx(InspectorRootContainer, { children: _jsxs(View, { ref: inspectorRootRef, style: StyleSheet.absoluteFill, pointerEvents: "box-none", children: [_jsx(View, { ref: anchorRef, style: [StyleSheet.absoluteFill, styles.anchor], pointerEvents: "none" }), _jsx(SelectionFab, { visible: idle && selectionPillVisible, onPress: startSelecting, onPressIn: () => animateFabPress(0.96), onPressOut: () => animateFabPress(1), panHandlers: fabPanHandlers, fabAppearAnim: fabAppearAnim, fabPosition: fabPosition, fabPressAnim: fabPressAnim }), _jsx(SelectionOverlay, { visible: overlayVisible, selecting: selecting, overlayRef: overlayRef, panHandlers: selectionPanHandlers, highlightRect: activeHighlightRect, statusBarOffset: statusBarOffset, selectingHintAnim: selectingHintAnim }), _jsx(CopiedToast, { visible: toastVisible, toastAnim: toastAnim })] }) }));
|
|
381
|
+
};
|
|
382
|
+
const SelectionFab = ({ visible, onPress, onPressIn, onPressOut, panHandlers, fabAppearAnim, fabPosition, fabPressAnim, }) => {
|
|
383
|
+
if (!visible) {
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
return (_jsx(Animated.View, { ...panHandlers, style: [
|
|
387
|
+
styles.fab,
|
|
388
|
+
{
|
|
389
|
+
opacity: fabAppearAnim,
|
|
390
|
+
transform: [
|
|
391
|
+
{ translateX: fabPosition.x },
|
|
392
|
+
{
|
|
393
|
+
translateY: Animated.add(fabPosition.y, fabAppearAnim.interpolate({
|
|
394
|
+
inputRange: [0, 1],
|
|
395
|
+
outputRange: [8, 0],
|
|
396
|
+
})),
|
|
397
|
+
},
|
|
398
|
+
{ scale: fabPressAnim },
|
|
399
|
+
],
|
|
400
|
+
},
|
|
401
|
+
], children: _jsx(Pressable, { style: styles.fabInner, onPress: onPress, onPressIn: onPressIn, onPressOut: onPressOut, children: _jsx(Text, { style: styles.fabIcon, allowFontScaling: false, children: "\u2316" }) }) }));
|
|
402
|
+
};
|
|
403
|
+
const SelectionOverlay = ({ visible, selecting, overlayRef, panHandlers, highlightRect, statusBarOffset, selectingHintAnim, }) => {
|
|
404
|
+
if (!visible) {
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
return (_jsxs(View, { ref: overlayRef, style: styles.overlay, ...(selecting ? panHandlers : {}), children: [highlightRect && (_jsx(View, { pointerEvents: "none", style: [
|
|
408
|
+
styles.highlight,
|
|
409
|
+
{
|
|
410
|
+
left: highlightRect.x,
|
|
411
|
+
top: highlightRect.y + statusBarOffset,
|
|
412
|
+
width: highlightRect.width,
|
|
413
|
+
height: highlightRect.height,
|
|
414
|
+
},
|
|
415
|
+
] })), selecting && (_jsx(Animated.View, { pointerEvents: "none", style: [
|
|
416
|
+
styles.selectionHint,
|
|
417
|
+
{
|
|
418
|
+
opacity: selectingHintAnim.interpolate({
|
|
419
|
+
inputRange: [0, 1],
|
|
420
|
+
outputRange: [0.76, 1],
|
|
421
|
+
}),
|
|
422
|
+
transform: [
|
|
423
|
+
{
|
|
424
|
+
translateY: selectingHintAnim.interpolate({
|
|
425
|
+
inputRange: [0, 1],
|
|
426
|
+
outputRange: [0, -2],
|
|
427
|
+
}),
|
|
428
|
+
},
|
|
429
|
+
],
|
|
430
|
+
},
|
|
431
|
+
], children: _jsx(Text, { style: styles.selectionHintText, children: "Drag or tap to find a source" }) }))] }));
|
|
432
|
+
};
|
|
433
|
+
const CopiedToast = ({ visible, toastAnim }) => {
|
|
434
|
+
if (!visible) {
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
return (_jsx(Animated.View, { pointerEvents: "none", style: [
|
|
438
|
+
styles.toast,
|
|
439
|
+
{
|
|
440
|
+
opacity: toastAnim,
|
|
441
|
+
transform: [
|
|
442
|
+
{
|
|
443
|
+
translateY: toastAnim.interpolate({
|
|
444
|
+
inputRange: [0, 1],
|
|
445
|
+
outputRange: [-8, 0],
|
|
446
|
+
}),
|
|
447
|
+
},
|
|
448
|
+
],
|
|
449
|
+
},
|
|
450
|
+
], children: _jsx(Text, { style: styles.toastText, children: "Source location copied" }) }));
|
|
362
451
|
};
|
|
363
452
|
const InspectorRootContainer = ({ children }) => {
|
|
364
|
-
if (Platform.OS ===
|
|
453
|
+
if (Platform.OS === "ios") {
|
|
365
454
|
return _jsx(FullWindowOverlay, { children: children });
|
|
366
455
|
}
|
|
367
456
|
return _jsx(Fragment, { children: children });
|
|
@@ -371,75 +460,75 @@ const styles = StyleSheet.create({
|
|
|
371
460
|
opacity: 0,
|
|
372
461
|
},
|
|
373
462
|
fab: {
|
|
374
|
-
position:
|
|
463
|
+
position: "absolute",
|
|
375
464
|
left: 0,
|
|
376
465
|
top: 0,
|
|
377
466
|
zIndex: 9999,
|
|
378
467
|
width: FAB_SIZE,
|
|
379
468
|
height: FAB_SIZE,
|
|
380
469
|
borderRadius: 9999,
|
|
381
|
-
backgroundColor:
|
|
470
|
+
backgroundColor: "rgba(14,20,26,0.98)",
|
|
382
471
|
borderWidth: 1,
|
|
383
|
-
borderColor:
|
|
472
|
+
borderColor: "rgba(255,255,255,0.14)",
|
|
384
473
|
elevation: 4,
|
|
385
|
-
shadowColor:
|
|
474
|
+
shadowColor: "#000",
|
|
386
475
|
shadowOffset: { width: 0, height: 2 },
|
|
387
476
|
shadowOpacity: 0.28,
|
|
388
477
|
shadowRadius: 6,
|
|
389
478
|
},
|
|
390
479
|
fabInner: {
|
|
391
480
|
flex: 1,
|
|
392
|
-
flexDirection:
|
|
393
|
-
justifyContent:
|
|
394
|
-
alignItems:
|
|
395
|
-
width:
|
|
396
|
-
height:
|
|
481
|
+
flexDirection: "row",
|
|
482
|
+
justifyContent: "center",
|
|
483
|
+
alignItems: "center",
|
|
484
|
+
width: "100%",
|
|
485
|
+
height: "100%",
|
|
397
486
|
},
|
|
398
487
|
fabIcon: {
|
|
399
|
-
color:
|
|
488
|
+
color: "#F5F7FA",
|
|
400
489
|
fontSize: 28,
|
|
401
|
-
fontWeight:
|
|
490
|
+
fontWeight: "500",
|
|
402
491
|
},
|
|
403
492
|
overlay: {
|
|
404
493
|
...StyleSheet.absoluteFillObject,
|
|
405
|
-
backgroundColor:
|
|
494
|
+
backgroundColor: "rgba(0,0,0,0.015)",
|
|
406
495
|
},
|
|
407
496
|
selectionHint: {
|
|
408
|
-
position:
|
|
497
|
+
position: "absolute",
|
|
409
498
|
top: 56,
|
|
410
|
-
alignSelf:
|
|
411
|
-
backgroundColor:
|
|
499
|
+
alignSelf: "center",
|
|
500
|
+
backgroundColor: "rgba(14,20,26,0.9)",
|
|
412
501
|
borderRadius: 18,
|
|
413
502
|
paddingHorizontal: 14,
|
|
414
503
|
paddingVertical: 8,
|
|
415
504
|
borderWidth: 1,
|
|
416
|
-
borderColor:
|
|
505
|
+
borderColor: "rgba(255,255,255,0.14)",
|
|
417
506
|
},
|
|
418
507
|
selectionHintText: {
|
|
419
|
-
color:
|
|
508
|
+
color: "#F5F7FA",
|
|
420
509
|
fontSize: 13,
|
|
421
|
-
fontWeight:
|
|
510
|
+
fontWeight: "600",
|
|
422
511
|
},
|
|
423
512
|
highlight: {
|
|
424
|
-
position:
|
|
513
|
+
position: "absolute",
|
|
425
514
|
borderWidth: 2,
|
|
426
|
-
borderColor:
|
|
427
|
-
backgroundColor:
|
|
515
|
+
borderColor: "#1264A3",
|
|
516
|
+
backgroundColor: "rgba(18,100,163,0.16)",
|
|
428
517
|
},
|
|
429
518
|
toast: {
|
|
430
|
-
position:
|
|
519
|
+
position: "absolute",
|
|
431
520
|
top: 52,
|
|
432
521
|
left: 16,
|
|
433
522
|
right: 16,
|
|
434
|
-
alignItems:
|
|
523
|
+
alignItems: "center",
|
|
435
524
|
paddingVertical: 10,
|
|
436
525
|
borderRadius: 10,
|
|
437
|
-
backgroundColor:
|
|
526
|
+
backgroundColor: "rgba(18,100,163,0.96)",
|
|
438
527
|
},
|
|
439
528
|
toastText: {
|
|
440
|
-
color:
|
|
529
|
+
color: "#fff",
|
|
441
530
|
fontSize: 14,
|
|
442
|
-
fontWeight:
|
|
531
|
+
fontWeight: "700",
|
|
443
532
|
},
|
|
444
533
|
});
|
|
445
534
|
//# sourceMappingURL=index.js.map
|