@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,467 @@
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
+ TouchableOpacity,
31
+ View,
32
+ } from "react-native";
33
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
34
+ // @ts-ignore
35
+ import DebuggingOverlay from "react-native/Libraries/Debugging/DebuggingOverlay.js";
36
+ // @ts-ignore
37
+ import useSubscribeToDebuggingOverlayRegistry from "react-native/Libraries/Debugging/useSubscribeToDebuggingOverlayRegistry";
38
+ import type {
39
+ InspectorData,
40
+ TouchedViewDataAtPoint,
41
+ // @ts-ignore
42
+ } from "react-native/Libraries/Renderer/shims/ReactNativeTypes";
43
+ import ErrorUtils from "react-native/Libraries/vendor/core/ErrorUtils";
44
+
45
+ const getInspectorDataForViewAtPoint =
46
+ require("react-native/src/private/inspector/getInspectorDataForViewAtPoint").default;
47
+ const InspectorOverlay =
48
+ require("react-native/src/private/inspector/InspectorOverlay").default;
49
+ // Removed InspectorPanel import - implementing inline
50
+
51
+ type PanelPosition = "top" | "bottom";
52
+
53
+ export type InspectedElementFrame = TouchedViewDataAtPoint["frame"];
54
+
55
+ export type InspectedElement = Readonly<{
56
+ frame: InspectedElementFrame;
57
+ style?: ViewStyle;
58
+ }>;
59
+
60
+ export type ElementsHierarchy = InspectorData["hierarchy"];
61
+
62
+ export type BundleInspectorRef = { enableDebuggingOverlay: () => void };
63
+ export type BundleInspectorProps = PropsWithChildren<{}>;
64
+
65
+ const BridgeModule = requireOptionalNativeModule("Bridge");
66
+
67
+ export function isRorkSandbox() {
68
+ return !!NativeModules.RorkSandbox;
69
+ }
70
+
71
+ if (BridgeModule) {
72
+ LogBox.ignoreAllLogs();
73
+ LogBox.uninstall();
74
+
75
+ // @ts-ignore
76
+ ErrorUtils.setGlobalHandler((error) => {
77
+ sendBridgeMessage("runtime-error", {
78
+ stack: error.stack,
79
+ message: error.message,
80
+ });
81
+ });
82
+ }
83
+
84
+ export function sendBridgeMessage(
85
+ type:
86
+ | "inspector-element"
87
+ | "merge-inspector-state"
88
+ | "runtime-ready"
89
+ | "runtime-error",
90
+ data?: Record<string, any>,
91
+ ) {
92
+ if (data) {
93
+ return BridgeModule?.sendMessage({ type, data: JSON.stringify(data) });
94
+ }
95
+
96
+ return BridgeModule?.sendMessage({ type });
97
+ }
98
+
99
+ export function addBridgeListener(
100
+ listener: (data: Record<string, any>) => void,
101
+ ): EventSubscription {
102
+ return BridgeModule?.addListener("onMessage", (data: any) => {
103
+ if (typeof data !== "object") return;
104
+ if (data.data) {
105
+ listener({ ...data, data: JSON.parse(data.data) });
106
+ } else {
107
+ listener(data);
108
+ }
109
+ });
110
+ }
111
+
112
+ export function useBridge() {
113
+ const [state, setState] = useState({ inspectorEnabled: false });
114
+
115
+ useEffect(() => {
116
+ const subscription = addBridgeListener((data) => {
117
+ if (typeof data === "object" && data.type === "merge-inspector-state") {
118
+ setState((prev) => ({ ...prev, ...data.data }));
119
+ }
120
+ });
121
+
122
+ sendBridgeMessage("runtime-ready");
123
+
124
+ return () => subscription?.remove();
125
+ }, []);
126
+
127
+ const disableInspector = useCallback(() => {
128
+ setState((prev) => ({ ...prev, inspectorEnabled: false }));
129
+ sendBridgeMessage("merge-inspector-state", { inspectorEnabled: false });
130
+ }, []);
131
+
132
+ return { ...state, disableInspector };
133
+ }
134
+
135
+ export function BundleInspector(props: BundleInspectorProps) {
136
+ const innerViewRef = useRef(null);
137
+ const appContainerRootViewRef = useRef(null);
138
+ const debuggingOverlayRef = useRef(null);
139
+ const { inspectorEnabled, disableInspector } = useBridge();
140
+
141
+ const [panelPosition, setPanelPosition] = useState<PanelPosition>("bottom");
142
+ const [inspectedElement, setInspectedElement] =
143
+ useState<InspectedElement | null>(null);
144
+
145
+ const [lastPayload, setLastPayload] = useState<Record<string, any> | null>(
146
+ null,
147
+ );
148
+
149
+ useEffect(() => {
150
+ if (inspectorEnabled) return;
151
+ setInspectedElement(null);
152
+ setPanelPosition("bottom");
153
+ }, [inspectorEnabled]);
154
+
155
+ useSubscribeToDebuggingOverlayRegistry(
156
+ appContainerRootViewRef,
157
+ debuggingOverlayRef,
158
+ );
159
+
160
+ const handleInspectedElementChange = useCallback(
161
+ (viewData: TouchedViewDataAtPoint | null) => {
162
+ if (viewData) {
163
+ const payload = {
164
+ style: viewData.props.style,
165
+ frame: viewData.frame,
166
+ componentStack: viewData.componentStack,
167
+ hierarchy: viewData.hierarchy?.map((h: any) => ({
168
+ name: h.name ?? null,
169
+ })),
170
+ };
171
+
172
+ setLastPayload(payload);
173
+ }
174
+ },
175
+ [],
176
+ );
177
+
178
+ const onTouchPoint = (locationX: number, locationY: number) => {
179
+ getInspectorDataForViewAtPoint(
180
+ innerViewRef.current,
181
+ locationX,
182
+ locationY,
183
+ (viewData: TouchedViewDataAtPoint) => {
184
+ setPanelPosition(
185
+ viewData.pointerY > Dimensions.get("window").height * 0.8
186
+ ? "top"
187
+ : "bottom",
188
+ );
189
+
190
+ setInspectedElement({
191
+ frame: viewData.frame,
192
+ style: viewData.props.style,
193
+ });
194
+
195
+ handleInspectedElementChange(viewData);
196
+
197
+ return false;
198
+ },
199
+ );
200
+ };
201
+
202
+ const { top, bottom } = useSafeAreaInsets();
203
+
204
+ const panelContainerStyle =
205
+ panelPosition === "bottom"
206
+ ? { bottom: 0, marginBottom: bottom }
207
+ : { top: 0, marginTop: top };
208
+
209
+ return (
210
+ <View style={styles.container} ref={appContainerRootViewRef}>
211
+ <View style={styles.container} ref={innerViewRef}>
212
+ {props.children}
213
+ </View>
214
+
215
+ {inspectorEnabled && (
216
+ <DebuggingOverlay ref={debuggingOverlayRef} style={styles.absolute} />
217
+ )}
218
+
219
+ {inspectorEnabled && (
220
+ <View style={styles.inspectorContainer} pointerEvents="box-none">
221
+ <InspectorOverlay
222
+ inspected={inspectedElement}
223
+ onTouchPoint={onTouchPoint}
224
+ />
225
+
226
+ <BlurView
227
+ style={[
228
+ {
229
+ position: "absolute",
230
+ left: 0,
231
+ right: 0,
232
+ backgroundColor: "rgba(0, 0, 0, 0.85)",
233
+ borderRadius: 30,
234
+ marginHorizontal: 12,
235
+ padding: 12,
236
+ paddingLeft: 20,
237
+ overflow: "hidden",
238
+ },
239
+ panelContainerStyle,
240
+ ]}
241
+ >
242
+ {inspectedElement ? (
243
+ <View style={styles.floatingIsland}>
244
+ <Text style={styles.islandText}>Element selected</Text>
245
+ <View style={styles.buttonGroup}>
246
+ <TouchableOpacity
247
+ style={styles.selectButton}
248
+ onPress={() => {
249
+ if (lastPayload) {
250
+ sendBridgeMessage("inspector-element", lastPayload);
251
+ disableInspector();
252
+ }
253
+ }}
254
+ >
255
+ <Text style={styles.buttonText}>Select</Text>
256
+ </TouchableOpacity>
257
+ <TouchableOpacity
258
+ activeOpacity={0.72}
259
+ style={styles.cancelButton}
260
+ onPress={disableInspector}
261
+ >
262
+ <X size={18} color="#808080" strokeWidth={2.5} />
263
+ </TouchableOpacity>
264
+ </View>
265
+ </View>
266
+ ) : (
267
+ <View style={styles.floatingIsland}>
268
+ <Text style={styles.islandText}>
269
+ Touch any element to inspect
270
+ </Text>
271
+ <TouchableOpacity
272
+ activeOpacity={0.72}
273
+ style={styles.cancelTextButton}
274
+ onPress={disableInspector}
275
+ >
276
+ <Text style={styles.buttonText}>Cancel</Text>
277
+ </TouchableOpacity>
278
+ </View>
279
+ )}
280
+ </BlurView>
281
+ </View>
282
+ )}
283
+ </View>
284
+ );
285
+ }
286
+
287
+ const styles = StyleSheet.create({
288
+ container: {
289
+ flex: 1,
290
+ },
291
+ absolute: StyleSheet.absoluteFillObject,
292
+ inspectorContainer: {
293
+ position: "absolute",
294
+ backgroundColor: "transparent",
295
+ top: 0,
296
+ left: 0,
297
+ right: 0,
298
+ bottom: 0,
299
+ },
300
+ properties: {
301
+ flexShrink: 1,
302
+ },
303
+ elementInfo: {
304
+ padding: 8,
305
+ gap: 3,
306
+ },
307
+ section: {
308
+ marginBottom: 15,
309
+ },
310
+ sectionTitle: {
311
+ color: "white",
312
+ fontSize: 11,
313
+ fontWeight: "600",
314
+ marginBottom: 4,
315
+ textTransform: "uppercase",
316
+ opacity: 0.7,
317
+ },
318
+ boxModel: {
319
+ backgroundColor: "rgba(255, 255, 255, 0.1)",
320
+ padding: 10,
321
+ borderRadius: 4,
322
+ alignItems: "center",
323
+ },
324
+ boxText: {
325
+ color: "white",
326
+ fontSize: 13,
327
+ fontWeight: "500",
328
+ },
329
+ layoutInfo: {
330
+ backgroundColor: "rgba(255, 255, 255, 0.05)",
331
+ padding: 8,
332
+ borderRadius: 4,
333
+ },
334
+ layoutText: {
335
+ color: "white",
336
+ fontSize: 12,
337
+ marginBottom: 2,
338
+ },
339
+ styleInfo: {
340
+ backgroundColor: "rgba(255, 255, 255, 0.05)",
341
+ padding: 8,
342
+ borderRadius: 4,
343
+ },
344
+ styleText: {
345
+ color: "white",
346
+ fontSize: 11,
347
+ marginBottom: 2,
348
+ fontFamily: "monospace",
349
+ },
350
+ waiting: {
351
+ height: 60,
352
+ justifyContent: "center",
353
+ alignItems: "center",
354
+ },
355
+ waitingContent: {
356
+ flex: 1,
357
+ justifyContent: "center",
358
+ alignItems: "center",
359
+ },
360
+ waitingText: {
361
+ fontSize: 14,
362
+ textAlign: "center",
363
+ color: "white",
364
+ opacity: 0.9,
365
+ fontWeight: "500",
366
+ },
367
+ waitingSubText: {
368
+ fontSize: 12,
369
+ textAlign: "center",
370
+ color: "white",
371
+ opacity: 0.6,
372
+ marginTop: 4,
373
+ },
374
+ floatingIsland: {
375
+ flexDirection: "row",
376
+ justifyContent: "space-between",
377
+ alignItems: "center",
378
+ gap: 16,
379
+ },
380
+ islandText: {
381
+ color: "white",
382
+ fontSize: 16,
383
+ fontWeight: "600",
384
+ flex: 1,
385
+ },
386
+ buttonGroup: {
387
+ flexDirection: "row",
388
+ gap: 8,
389
+ },
390
+ selectButton: {
391
+ backgroundColor: "#007AFF",
392
+ paddingHorizontal: 12,
393
+ paddingVertical: 6,
394
+ borderRadius: 16,
395
+ minWidth: 60,
396
+ },
397
+ cancelButton: {
398
+ backgroundColor: "rgba(255, 255, 255, 0.15)",
399
+ borderRadius: 16,
400
+ aspectRatio: 1,
401
+ height: 28,
402
+ justifyContent: "center",
403
+ alignItems: "center",
404
+ },
405
+ cancelTextButton: {
406
+ backgroundColor: "rgba(255, 255, 255, 0.15)",
407
+ paddingHorizontal: 12,
408
+ paddingVertical: 6,
409
+ borderRadius: 16,
410
+ minWidth: 60,
411
+ },
412
+ buttonText: {
413
+ color: "white",
414
+ fontSize: 14,
415
+ fontWeight: "600",
416
+ textAlign: "center",
417
+ },
418
+ panelTitle: {
419
+ color: "white",
420
+ fontSize: 14,
421
+ fontWeight: "600",
422
+ marginBottom: 8,
423
+ textAlign: "center",
424
+ },
425
+ editorSection: {
426
+ marginBottom: 10,
427
+ },
428
+ inputGrid: {
429
+ flexDirection: "row",
430
+ gap: 8,
431
+ },
432
+ inputCell: {
433
+ flex: 1,
434
+ },
435
+ inputLabel: {
436
+ color: "white",
437
+ fontSize: 10,
438
+ fontWeight: "500",
439
+ marginBottom: 2,
440
+ opacity: 0.8,
441
+ },
442
+ compactInput: {
443
+ backgroundColor: "rgba(255, 255, 255, 0.1)",
444
+ paddingHorizontal: 8,
445
+ paddingVertical: 4,
446
+ borderRadius: 4,
447
+ color: "white",
448
+ fontSize: 12,
449
+ borderWidth: 1,
450
+ borderColor: "rgba(255, 255, 255, 0.15)",
451
+ height: 28,
452
+ },
453
+ applyButton: {
454
+ backgroundColor: "#007AFF",
455
+ paddingVertical: 10,
456
+ paddingHorizontal: 20,
457
+ borderRadius: 6,
458
+ alignItems: "center",
459
+ marginTop: 8,
460
+ alignSelf: "flex-end",
461
+ },
462
+ applyButtonText: {
463
+ color: "white",
464
+ fontSize: 14,
465
+ fontWeight: "600",
466
+ },
467
+ });