@rork-ai/toolkit-dev-sdk 0.2.6 → 0.2.7

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.
@@ -0,0 +1,345 @@
1
+ /*
2
+ The code here will is moved to our infrastructure (with ctrl+c ctrl+v)
3
+ where we add it in _layout.tsx as it is.
4
+
5
+ It will be added at .rork/BundleInspector.tsx (web vestoin too).
6
+
7
+ That is why:
8
+ 1. This file should never import other files
9
+ 2. This file should never import other modules that will not be on the device.
10
+ We import react-native-safe-area-context and lucide-react-native
11
+ but we are confident it will be there. Still not great.
12
+
13
+ */
14
+
15
+ import { BlurView } from "expo-blur";
16
+ import {
17
+ requireOptionalNativeModule,
18
+ type EventSubscription,
19
+ } from "expo-modules-core";
20
+ import { X } from "lucide-react-native";
21
+ import type { PropsWithChildren } from "react";
22
+ import React, { useCallback, useEffect, useRef, useState } from "react";
23
+ import type { ViewStyle } from "react-native";
24
+ import {
25
+ Dimensions,
26
+ LogBox,
27
+ NativeModules,
28
+ StyleSheet,
29
+ Text,
30
+ TextInput,
31
+ TouchableOpacity,
32
+ View,
33
+ } from "react-native";
34
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
35
+ // @ts-ignore
36
+ import DebuggingOverlay from "react-native/Libraries/Debugging/DebuggingOverlay.js";
37
+ // @ts-ignore
38
+ import useSubscribeToDebuggingOverlayRegistry from "react-native/Libraries/Debugging/useSubscribeToDebuggingOverlayRegistry";
39
+ import type {
40
+ InspectorData,
41
+ TouchedViewDataAtPoint,
42
+ // @ts-ignore
43
+ } from "react-native/Libraries/Renderer/shims/ReactNativeTypes";
44
+ import ErrorUtils from "react-native/Libraries/vendor/core/ErrorUtils";
45
+
46
+ const getInspectorDataForViewAtPoint =
47
+ require("react-native/src/private/devsupport/devmenu/elementinspector/getInspectorDataForViewAtPoint").default;
48
+ const InspectorOverlay =
49
+ require("react-native/src/private/devsupport/devmenu/elementinspector/InspectorOverlay").default;
50
+ // Removed InspectorPanel import - implementing inline
51
+
52
+ type PanelPosition = "top" | "bottom";
53
+
54
+ export type InspectedElementFrame = TouchedViewDataAtPoint["frame"];
55
+
56
+ export type InspectedElement = Readonly<{
57
+ frame: InspectedElementFrame;
58
+ style?: ViewStyle;
59
+ }>;
60
+
61
+ export type ElementsHierarchy = InspectorData["hierarchy"];
62
+
63
+ export type BundleInspectorRef = { enableDebuggingOverlay: () => void };
64
+ export type BundleInspectorProps = PropsWithChildren<{}>;
65
+
66
+ const BridgeModule = requireOptionalNativeModule("Bridge");
67
+
68
+ export function isRorkSandbox() {
69
+ return !!NativeModules.RorkSandbox;
70
+ }
71
+
72
+ if (BridgeModule) {
73
+ LogBox.ignoreAllLogs();
74
+ LogBox.uninstall();
75
+
76
+ // @ts-ignore
77
+ ErrorUtils.setGlobalHandler((error) => {
78
+ sendBridgeMessage("runtime-error", {
79
+ stack: error.stack,
80
+ message: error.message,
81
+ });
82
+ });
83
+ }
84
+
85
+ export function sendBridgeMessage(
86
+ type:
87
+ | "inspector-element"
88
+ | "merge-inspector-state"
89
+ | "runtime-ready"
90
+ | "runtime-error",
91
+ data?: Record<string, any>,
92
+ ) {
93
+ if (data) {
94
+ return BridgeModule?.sendMessage({ type, data: JSON.stringify(data) });
95
+ }
96
+
97
+ return BridgeModule?.sendMessage({ type });
98
+ }
99
+
100
+ export function addBridgeListener(
101
+ listener: (data: Record<string, any>) => void,
102
+ ): EventSubscription {
103
+ return BridgeModule?.addListener("onMessage", (data: any) => {
104
+ if (typeof data !== "object") return;
105
+ if (data.data) {
106
+ listener({ ...data, data: JSON.parse(data.data) });
107
+ } else {
108
+ listener(data);
109
+ }
110
+ });
111
+ }
112
+
113
+ export function useBridge() {
114
+ const [state, setState] = useState({ inspectorEnabled: false });
115
+
116
+ useEffect(() => {
117
+ const subscription = addBridgeListener((data) => {
118
+ if (typeof data === "object" && data.type === "merge-inspector-state") {
119
+ setState((prev) => ({ ...prev, ...data.data }));
120
+ }
121
+ });
122
+
123
+ sendBridgeMessage("runtime-ready");
124
+
125
+ return () => subscription?.remove();
126
+ }, []);
127
+
128
+ const disableInspector = useCallback(() => {
129
+ setState((prev) => ({ ...prev, inspectorEnabled: false }));
130
+ sendBridgeMessage("merge-inspector-state", { inspectorEnabled: false });
131
+ }, []);
132
+
133
+ return { ...state, disableInspector };
134
+ }
135
+
136
+ export function BundleInspector(props: BundleInspectorProps) {
137
+ const innerViewRef = useRef(null);
138
+ const appContainerRootViewRef = useRef(null);
139
+ const debuggingOverlayRef = useRef(null);
140
+ const { inspectorEnabled, disableInspector } = useBridge();
141
+
142
+ const [panelPosition, setPanelPosition] = useState<PanelPosition>("bottom");
143
+ const [inspectedElement, setInspectedElement] =
144
+ useState<InspectedElement | null>(null);
145
+
146
+ const [lastPayload, setLastPayload] = useState<Record<string, any> | null>(
147
+ null,
148
+ );
149
+
150
+ useEffect(() => {
151
+ if (inspectorEnabled) return;
152
+ setInspectedElement(null);
153
+ setPanelPosition("bottom");
154
+ }, [inspectorEnabled]);
155
+
156
+ useSubscribeToDebuggingOverlayRegistry(
157
+ appContainerRootViewRef,
158
+ debuggingOverlayRef,
159
+ );
160
+
161
+ const handleInspectedElementChange = useCallback(
162
+ (viewData: TouchedViewDataAtPoint | null) => {
163
+ if (viewData) {
164
+ const payload = {
165
+ style: viewData.props.style,
166
+ frame: viewData.frame,
167
+ componentStack: viewData.componentStack,
168
+ hierarchy: viewData.hierarchy?.map((h: any) => ({
169
+ name: h.name ?? null,
170
+ })),
171
+ };
172
+
173
+ setLastPayload(payload);
174
+ }
175
+ },
176
+ [],
177
+ );
178
+
179
+ const onTouchPoint = (locationX: number, locationY: number) => {
180
+ getInspectorDataForViewAtPoint(
181
+ innerViewRef.current,
182
+ locationX,
183
+ locationY,
184
+ (viewData: TouchedViewDataAtPoint) => {
185
+ setPanelPosition(
186
+ viewData.pointerY > Dimensions.get("window").height * 0.8
187
+ ? "top"
188
+ : "bottom",
189
+ );
190
+
191
+ setInspectedElement({
192
+ frame: viewData.frame,
193
+ style: viewData.props.style,
194
+ });
195
+
196
+ handleInspectedElementChange(viewData);
197
+
198
+ return false;
199
+ },
200
+ );
201
+ };
202
+
203
+ const { top, bottom } = useSafeAreaInsets();
204
+
205
+ const panelContainerStyle =
206
+ panelPosition === "bottom"
207
+ ? { bottom: 0, marginBottom: bottom }
208
+ : { top: 0, marginTop: top };
209
+
210
+ return (
211
+ <View style={styles.container} ref={appContainerRootViewRef}>
212
+ <View style={styles.container} ref={innerViewRef}>
213
+ {props.children}
214
+ </View>
215
+
216
+ {inspectorEnabled && (
217
+ <DebuggingOverlay ref={debuggingOverlayRef} style={styles.absolute} />
218
+ )}
219
+
220
+ {inspectorEnabled && (
221
+ <View style={styles.inspectorContainer} pointerEvents="box-none">
222
+ <InspectorOverlay
223
+ inspected={inspectedElement}
224
+ onTouchPoint={onTouchPoint}
225
+ />
226
+
227
+ <BlurView
228
+ style={[
229
+ {
230
+ position: "absolute",
231
+ left: 0,
232
+ right: 0,
233
+ backgroundColor: "rgba(0, 0, 0, 0.85)",
234
+ borderRadius: 30,
235
+ marginHorizontal: 12,
236
+ padding: 12,
237
+ paddingLeft: 20,
238
+ overflow: "hidden",
239
+ },
240
+ panelContainerStyle,
241
+ ]}
242
+ >
243
+ {inspectedElement ? (
244
+ <View style={styles.floatingIsland}>
245
+ <Text style={styles.islandText}>Element selected</Text>
246
+ <View style={styles.buttonGroup}>
247
+ <TouchableOpacity
248
+ style={styles.selectButton}
249
+ onPress={() => {
250
+ if (lastPayload) {
251
+ sendBridgeMessage("inspector-element", lastPayload);
252
+ disableInspector();
253
+ }
254
+ }}
255
+ >
256
+ <Text style={styles.buttonText}>Select</Text>
257
+ </TouchableOpacity>
258
+ <TouchableOpacity
259
+ activeOpacity={0.72}
260
+ style={styles.cancelButton}
261
+ onPress={disableInspector}
262
+ >
263
+ <X size={18} color="#808080" strokeWidth={2.5} />
264
+ </TouchableOpacity>
265
+ </View>
266
+ </View>
267
+ ) : (
268
+ <View style={styles.floatingIsland}>
269
+ <Text style={styles.islandText}>
270
+ Touch any element to inspect
271
+ </Text>
272
+ <TouchableOpacity
273
+ activeOpacity={0.72}
274
+ style={styles.cancelTextButton}
275
+ onPress={disableInspector}
276
+ >
277
+ <Text style={styles.buttonText}>Cancel</Text>
278
+ </TouchableOpacity>
279
+ </View>
280
+ )}
281
+ </BlurView>
282
+ </View>
283
+ )}
284
+ </View>
285
+ );
286
+ }
287
+
288
+ const styles = StyleSheet.create({
289
+ container: {
290
+ flex: 1,
291
+ },
292
+ absolute: StyleSheet.absoluteFillObject,
293
+ inspectorContainer: {
294
+ position: "absolute",
295
+ backgroundColor: "transparent",
296
+ top: 0,
297
+ left: 0,
298
+ right: 0,
299
+ bottom: 0,
300
+ },
301
+ floatingIsland: {
302
+ flexDirection: "row",
303
+ justifyContent: "space-between",
304
+ alignItems: "center",
305
+ gap: 16,
306
+ },
307
+ islandText: {
308
+ color: "white",
309
+ fontSize: 16,
310
+ fontWeight: "600",
311
+ flex: 1,
312
+ },
313
+ buttonGroup: {
314
+ flexDirection: "row",
315
+ gap: 8,
316
+ },
317
+ selectButton: {
318
+ backgroundColor: "#007AFF",
319
+ paddingHorizontal: 12,
320
+ paddingVertical: 6,
321
+ borderRadius: 16,
322
+ minWidth: 60,
323
+ },
324
+ cancelButton: {
325
+ backgroundColor: "rgba(255, 255, 255, 0.15)",
326
+ borderRadius: 16,
327
+ aspectRatio: 1,
328
+ height: 28,
329
+ justifyContent: "center",
330
+ alignItems: "center",
331
+ },
332
+ cancelTextButton: {
333
+ backgroundColor: "rgba(255, 255, 255, 0.15)",
334
+ paddingHorizontal: 12,
335
+ paddingVertical: 6,
336
+ borderRadius: 16,
337
+ minWidth: 60,
338
+ },
339
+ buttonText: {
340
+ color: "white",
341
+ fontSize: 14,
342
+ fontWeight: "600",
343
+ textAlign: "center",
344
+ },
345
+ });
@@ -0,0 +1,3 @@
1
+ export function BundleInspector({ children }: { children: React.ReactNode }) {
2
+ return <>{children}</>;
3
+ }