@np-dev/ui-ai-anotation 0.1.0

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.
Files changed (52) hide show
  1. package/README.md +245 -0
  2. package/dist/cjs/index.cjs +1550 -0
  3. package/dist/cjs/index.cjs.map +7 -0
  4. package/dist/cjs/index.native.cjs +1004 -0
  5. package/dist/cjs/index.native.cjs.map +7 -0
  6. package/dist/cjs/index.web.cjs +83 -0
  7. package/dist/cjs/index.web.cjs.map +7 -0
  8. package/dist/esm/index.js +1524 -0
  9. package/dist/esm/index.js.map +7 -0
  10. package/dist/esm/index.native.js +1012 -0
  11. package/dist/esm/index.native.js.map +7 -0
  12. package/dist/esm/index.web.js +62 -0
  13. package/dist/esm/index.web.js.map +7 -0
  14. package/dist/types/components/AnnotationInput.d.ts +8 -0
  15. package/dist/types/components/AnnotationList.d.ts +1 -0
  16. package/dist/types/components/Draggable.d.ts +10 -0
  17. package/dist/types/components/Highlighter.d.ts +1 -0
  18. package/dist/types/components/Toolbar.d.ts +1 -0
  19. package/dist/types/index.d.ts +20 -0
  20. package/dist/types/index.web.d.ts +69 -0
  21. package/dist/types/store.d.ts +66 -0
  22. package/dist/types/utils/fiber.d.ts +51 -0
  23. package/dist/types/utils/platform.d.ts +8 -0
  24. package/dist/types/utils/screenshot.d.ts +28 -0
  25. package/package.json +115 -0
  26. package/src/components/AnnotationInput.tsx +269 -0
  27. package/src/components/AnnotationList.tsx +248 -0
  28. package/src/components/Draggable.tsx +73 -0
  29. package/src/components/Highlighter.tsx +497 -0
  30. package/src/components/Toolbar.tsx +213 -0
  31. package/src/components/native/AnnotationInput.tsx +227 -0
  32. package/src/components/native/AnnotationList.tsx +157 -0
  33. package/src/components/native/Draggable.tsx +65 -0
  34. package/src/components/native/Highlighter.tsx +239 -0
  35. package/src/components/native/Toolbar.tsx +192 -0
  36. package/src/components/native/index.ts +6 -0
  37. package/src/components/web/AnnotationInput.tsx +150 -0
  38. package/src/components/web/AnnotationList.tsx +117 -0
  39. package/src/components/web/Draggable.tsx +74 -0
  40. package/src/components/web/Highlighter.tsx +329 -0
  41. package/src/components/web/Toolbar.tsx +198 -0
  42. package/src/components/web/index.ts +6 -0
  43. package/src/extension.tsx +15 -0
  44. package/src/index.native.tsx +50 -0
  45. package/src/index.tsx +41 -0
  46. package/src/index.web.tsx +124 -0
  47. package/src/store.tsx +120 -0
  48. package/src/utils/fiber.native.ts +90 -0
  49. package/src/utils/fiber.ts +255 -0
  50. package/src/utils/platform.ts +33 -0
  51. package/src/utils/screenshot.native.ts +139 -0
  52. package/src/utils/screenshot.ts +162 -0
@@ -0,0 +1,1012 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src/index.native.tsx
9
+ import { View as View6 } from "react-native";
10
+
11
+ // src/store.tsx
12
+ import { createContext, useContext, useReducer } from "react";
13
+ import { jsx } from "react/jsx-runtime";
14
+ var initialState = {
15
+ mode: "disabled",
16
+ annotations: [],
17
+ hoveredElement: null,
18
+ hoveredComponentInfo: null,
19
+ isMinimized: false,
20
+ showList: false
21
+ };
22
+ var AnnotationContext = createContext(void 0);
23
+ function reducer(state, action) {
24
+ switch (action.type) {
25
+ case "SET_MODE":
26
+ return { ...state, mode: action.payload };
27
+ case "ADD_ANNOTATION":
28
+ return { ...state, annotations: [...state.annotations, action.payload] };
29
+ case "REMOVE_ANNOTATION":
30
+ return {
31
+ ...state,
32
+ annotations: state.annotations.filter((a) => a.id !== action.payload)
33
+ };
34
+ case "CLEAR_ALL_ANNOTATIONS":
35
+ return {
36
+ ...state,
37
+ annotations: []
38
+ };
39
+ case "SET_HOVERED":
40
+ if (state.hoveredElement === action.payload.element) return state;
41
+ return {
42
+ ...state,
43
+ hoveredElement: action.payload.element,
44
+ hoveredComponentInfo: action.payload.name ? { name: action.payload.name, details: action.payload.details } : null
45
+ };
46
+ case "RESET_HOVER":
47
+ return { ...state, hoveredElement: null, hoveredComponentInfo: null };
48
+ case "TOGGLE_MINIMIZED":
49
+ return { ...state, isMinimized: !state.isMinimized };
50
+ case "TOGGLE_LIST":
51
+ return { ...state, showList: !state.showList };
52
+ default:
53
+ return state;
54
+ }
55
+ }
56
+ function AiAnnotationProvider({ children }) {
57
+ const [state, dispatch] = useReducer(reducer, initialState);
58
+ return /* @__PURE__ */ jsx(AnnotationContext.Provider, { value: { state, dispatch }, children });
59
+ }
60
+ function useAiAnnotation() {
61
+ const context = useContext(AnnotationContext);
62
+ if (!context) {
63
+ throw new Error("useAiAnnotation must be used within an AiAnnotationProvider");
64
+ }
65
+ return context;
66
+ }
67
+
68
+ // src/components/native/Toolbar.tsx
69
+ import { useState as useState3 } from "react";
70
+ import {
71
+ View as View5,
72
+ Text as Text4,
73
+ TouchableOpacity as TouchableOpacity4,
74
+ StyleSheet as StyleSheet5
75
+ } from "react-native";
76
+
77
+ // src/components/native/Draggable.tsx
78
+ import { useRef } from "react";
79
+ import {
80
+ PanResponder,
81
+ Animated,
82
+ StyleSheet
83
+ } from "react-native";
84
+ import { jsx as jsx2 } from "react/jsx-runtime";
85
+ function Draggable({ children, initialPos = { x: 20, y: 20 }, style }) {
86
+ const pan = useRef(new Animated.ValueXY(initialPos)).current;
87
+ const lastOffset = useRef(initialPos);
88
+ const panResponder = useRef(
89
+ PanResponder.create({
90
+ onStartShouldSetPanResponder: () => true,
91
+ onMoveShouldSetPanResponder: () => true,
92
+ onPanResponderGrant: () => {
93
+ pan.setOffset(lastOffset.current);
94
+ pan.setValue({ x: 0, y: 0 });
95
+ },
96
+ onPanResponderMove: Animated.event(
97
+ [null, { dx: pan.x, dy: pan.y }],
98
+ { useNativeDriver: false }
99
+ ),
100
+ onPanResponderRelease: (_e, gestureState) => {
101
+ lastOffset.current = {
102
+ x: lastOffset.current.x + gestureState.dx,
103
+ y: lastOffset.current.y + gestureState.dy
104
+ };
105
+ pan.flattenOffset();
106
+ }
107
+ })
108
+ ).current;
109
+ return /* @__PURE__ */ jsx2(
110
+ Animated.View,
111
+ {
112
+ ...panResponder.panHandlers,
113
+ style: [
114
+ styles.container,
115
+ style,
116
+ {
117
+ transform: [{ translateX: pan.x }, { translateY: pan.y }]
118
+ }
119
+ ],
120
+ children
121
+ }
122
+ );
123
+ }
124
+ var styles = StyleSheet.create({
125
+ container: {
126
+ position: "absolute",
127
+ zIndex: 9999
128
+ }
129
+ });
130
+
131
+ // src/components/native/Highlighter.tsx
132
+ import { useState as useState2, useCallback } from "react";
133
+ import {
134
+ View as View3,
135
+ Text as Text2,
136
+ TouchableOpacity as TouchableOpacity2,
137
+ StyleSheet as StyleSheet3,
138
+ Dimensions
139
+ } from "react-native";
140
+
141
+ // src/components/native/AnnotationInput.tsx
142
+ import { useState, useEffect } from "react";
143
+ import {
144
+ View as View2,
145
+ Text,
146
+ Modal,
147
+ TextInput,
148
+ TouchableOpacity,
149
+ StyleSheet as StyleSheet2,
150
+ Animated as Animated2,
151
+ KeyboardAvoidingView,
152
+ Platform
153
+ } from "react-native";
154
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
155
+ function AnnotationInput({ onClose, componentName }) {
156
+ const { dispatch } = useAiAnnotation();
157
+ const [note, setNote] = useState("");
158
+ const [fadeAnim] = useState(new Animated2.Value(0));
159
+ const [scaleAnim] = useState(new Animated2.Value(0.95));
160
+ useEffect(() => {
161
+ Animated2.parallel([
162
+ Animated2.timing(fadeAnim, {
163
+ toValue: 1,
164
+ duration: 200,
165
+ useNativeDriver: true
166
+ }),
167
+ Animated2.spring(scaleAnim, {
168
+ toValue: 1,
169
+ friction: 8,
170
+ useNativeDriver: true
171
+ })
172
+ ]).start();
173
+ }, []);
174
+ const handleClose = () => {
175
+ Animated2.parallel([
176
+ Animated2.timing(fadeAnim, {
177
+ toValue: 0,
178
+ duration: 150,
179
+ useNativeDriver: true
180
+ }),
181
+ Animated2.timing(scaleAnim, {
182
+ toValue: 0.95,
183
+ duration: 150,
184
+ useNativeDriver: true
185
+ })
186
+ ]).start(() => {
187
+ onClose();
188
+ });
189
+ };
190
+ const handleSubmit = () => {
191
+ if (!note.trim()) return;
192
+ dispatch({
193
+ type: "ADD_ANNOTATION",
194
+ payload: {
195
+ id: Date.now().toString(),
196
+ componentName,
197
+ note: note.trim(),
198
+ timestamp: Date.now()
199
+ }
200
+ });
201
+ handleClose();
202
+ };
203
+ return /* @__PURE__ */ jsx3(
204
+ Modal,
205
+ {
206
+ visible: true,
207
+ transparent: true,
208
+ animationType: "none",
209
+ onRequestClose: handleClose,
210
+ children: /* @__PURE__ */ jsx3(
211
+ KeyboardAvoidingView,
212
+ {
213
+ behavior: Platform.OS === "ios" ? "padding" : "height",
214
+ style: styles2.keyboardView,
215
+ children: /* @__PURE__ */ jsx3(Animated2.View, { style: [styles2.overlay, { opacity: fadeAnim }], children: /* @__PURE__ */ jsx3(
216
+ TouchableOpacity,
217
+ {
218
+ style: styles2.backdropTouchable,
219
+ activeOpacity: 1,
220
+ onPress: handleClose,
221
+ children: /* @__PURE__ */ jsx3(
222
+ Animated2.View,
223
+ {
224
+ style: [
225
+ styles2.container,
226
+ {
227
+ opacity: fadeAnim,
228
+ transform: [{ scale: scaleAnim }]
229
+ }
230
+ ],
231
+ children: /* @__PURE__ */ jsxs(TouchableOpacity, { activeOpacity: 1, children: [
232
+ /* @__PURE__ */ jsxs(View2, { style: styles2.header, children: [
233
+ /* @__PURE__ */ jsx3(Text, { style: styles2.title, children: "Add Annotation" }),
234
+ /* @__PURE__ */ jsx3(TouchableOpacity, { onPress: handleClose, style: styles2.closeButton, children: /* @__PURE__ */ jsx3(Text, { style: styles2.closeButtonText, children: "\u2715" }) })
235
+ ] }),
236
+ /* @__PURE__ */ jsxs(Text, { style: styles2.componentLabel, children: [
237
+ "Component: ",
238
+ /* @__PURE__ */ jsx3(Text, { style: styles2.componentName, children: componentName })
239
+ ] }),
240
+ /* @__PURE__ */ jsx3(
241
+ TextInput,
242
+ {
243
+ style: styles2.textInput,
244
+ value: note,
245
+ onChangeText: setNote,
246
+ placeholder: "Describe what this component does...",
247
+ placeholderTextColor: "#6b7280",
248
+ multiline: true,
249
+ numberOfLines: 4,
250
+ textAlignVertical: "top",
251
+ autoFocus: true
252
+ }
253
+ ),
254
+ /* @__PURE__ */ jsxs(View2, { style: styles2.buttonContainer, children: [
255
+ /* @__PURE__ */ jsx3(TouchableOpacity, { style: styles2.cancelButton, onPress: handleClose, children: /* @__PURE__ */ jsx3(Text, { style: styles2.cancelButtonText, children: "Cancel" }) }),
256
+ /* @__PURE__ */ jsx3(TouchableOpacity, { style: styles2.submitButton, onPress: handleSubmit, children: /* @__PURE__ */ jsx3(Text, { style: styles2.submitButtonText, children: "Save Annotation" }) })
257
+ ] })
258
+ ] })
259
+ }
260
+ )
261
+ }
262
+ ) })
263
+ }
264
+ )
265
+ }
266
+ );
267
+ }
268
+ var styles2 = StyleSheet2.create({
269
+ keyboardView: {
270
+ flex: 1
271
+ },
272
+ overlay: {
273
+ flex: 1,
274
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
275
+ justifyContent: "center",
276
+ alignItems: "center"
277
+ },
278
+ backdropTouchable: {
279
+ flex: 1,
280
+ width: "100%",
281
+ justifyContent: "center",
282
+ alignItems: "center"
283
+ },
284
+ container: {
285
+ backgroundColor: "#1e1e1e",
286
+ borderRadius: 8,
287
+ padding: 20,
288
+ width: "90%",
289
+ maxWidth: 400
290
+ },
291
+ header: {
292
+ flexDirection: "row",
293
+ justifyContent: "space-between",
294
+ alignItems: "center",
295
+ marginBottom: 16
296
+ },
297
+ title: {
298
+ color: "#e5e7eb",
299
+ fontSize: 18,
300
+ fontWeight: "bold"
301
+ },
302
+ closeButton: {
303
+ padding: 4
304
+ },
305
+ closeButtonText: {
306
+ color: "#e5e7eb",
307
+ fontSize: 20
308
+ },
309
+ componentLabel: {
310
+ color: "#9ca3af",
311
+ fontSize: 14,
312
+ marginBottom: 12
313
+ },
314
+ componentName: {
315
+ color: "#e5e7eb",
316
+ fontWeight: "bold"
317
+ },
318
+ textInput: {
319
+ backgroundColor: "#2d2d2d",
320
+ borderWidth: 1,
321
+ borderColor: "#404040",
322
+ borderRadius: 4,
323
+ padding: 8,
324
+ color: "white",
325
+ height: 100,
326
+ marginBottom: 16,
327
+ fontSize: 14
328
+ },
329
+ buttonContainer: {
330
+ flexDirection: "row",
331
+ justifyContent: "flex-end",
332
+ gap: 8
333
+ },
334
+ cancelButton: {
335
+ paddingHorizontal: 12,
336
+ paddingVertical: 8,
337
+ borderRadius: 4,
338
+ borderWidth: 1,
339
+ borderColor: "#404040"
340
+ },
341
+ cancelButtonText: {
342
+ color: "white",
343
+ fontSize: 14
344
+ },
345
+ submitButton: {
346
+ backgroundColor: "#3b82f6",
347
+ paddingHorizontal: 12,
348
+ paddingVertical: 8,
349
+ borderRadius: 4
350
+ },
351
+ submitButtonText: {
352
+ color: "white",
353
+ fontSize: 14
354
+ }
355
+ });
356
+
357
+ // src/components/native/Highlighter.tsx
358
+ import { Fragment, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
359
+ function Highlighter({ onInspect }) {
360
+ const { state, dispatch } = useAiAnnotation();
361
+ const { mode } = state;
362
+ const [highlightRect, setHighlightRect] = useState2(null);
363
+ const [componentName, setComponentName] = useState2("");
364
+ const [showInput, setShowInput] = useState2(false);
365
+ const [showTooltip, setShowTooltip] = useState2(false);
366
+ const [tooltipPos, setTooltipPos] = useState2({ x: 0, y: 0 });
367
+ const screenDimensions = Dimensions.get("window");
368
+ const handleTouch = useCallback(
369
+ (event) => {
370
+ if (mode !== "inspecting") return;
371
+ const { pageX, pageY } = event.nativeEvent;
372
+ if (onInspect) {
373
+ const result = onInspect(event);
374
+ if (result) {
375
+ setHighlightRect(result.rect);
376
+ setComponentName(result.name);
377
+ setTooltipPos({ x: pageX, y: pageY });
378
+ setShowTooltip(true);
379
+ return;
380
+ }
381
+ }
382
+ setTooltipPos({ x: pageX, y: pageY });
383
+ setShowTooltip(true);
384
+ setComponentName("TouchedComponent");
385
+ setHighlightRect({
386
+ x: pageX - 50,
387
+ y: pageY - 50,
388
+ width: 100,
389
+ height: 100
390
+ });
391
+ },
392
+ [mode, onInspect]
393
+ );
394
+ const handleAddAnnotation = () => {
395
+ setShowInput(true);
396
+ setShowTooltip(false);
397
+ };
398
+ const handleCloseInput = () => {
399
+ setShowInput(false);
400
+ setHighlightRect(null);
401
+ setComponentName("");
402
+ };
403
+ const handleDismiss = () => {
404
+ setShowTooltip(false);
405
+ setHighlightRect(null);
406
+ setComponentName("");
407
+ };
408
+ if (mode !== "inspecting") return null;
409
+ const tooltipWidth = 120;
410
+ const tooltipHeight = 50;
411
+ let adjustedX = tooltipPos.x + 16;
412
+ let adjustedY = tooltipPos.y + 16;
413
+ if (adjustedX + tooltipWidth > screenDimensions.width) {
414
+ adjustedX = tooltipPos.x - tooltipWidth - 16;
415
+ }
416
+ if (adjustedY + tooltipHeight > screenDimensions.height) {
417
+ adjustedY = tooltipPos.y - tooltipHeight - 16;
418
+ }
419
+ return /* @__PURE__ */ jsxs2(View3, { style: StyleSheet3.absoluteFill, pointerEvents: "box-none", children: [
420
+ /* @__PURE__ */ jsx4(
421
+ TouchableOpacity2,
422
+ {
423
+ style: styles3.touchOverlay,
424
+ activeOpacity: 1,
425
+ onPress: handleTouch
426
+ }
427
+ ),
428
+ highlightRect && /* @__PURE__ */ jsxs2(Fragment, { children: [
429
+ /* @__PURE__ */ jsx4(
430
+ View3,
431
+ {
432
+ style: [
433
+ styles3.highlight,
434
+ {
435
+ left: highlightRect.x - 2,
436
+ top: highlightRect.y - 2,
437
+ width: highlightRect.width + 4,
438
+ height: highlightRect.height + 4
439
+ }
440
+ ],
441
+ pointerEvents: "none"
442
+ }
443
+ ),
444
+ componentName && /* @__PURE__ */ jsx4(
445
+ View3,
446
+ {
447
+ style: [
448
+ styles3.nameLabel,
449
+ {
450
+ left: highlightRect.x,
451
+ top: highlightRect.y - 24
452
+ }
453
+ ],
454
+ pointerEvents: "none",
455
+ children: /* @__PURE__ */ jsx4(Text2, { style: styles3.nameLabelText, children: componentName })
456
+ }
457
+ )
458
+ ] }),
459
+ showTooltip && /* @__PURE__ */ jsxs2(
460
+ View3,
461
+ {
462
+ style: [
463
+ styles3.tooltip,
464
+ {
465
+ left: adjustedX,
466
+ top: adjustedY
467
+ }
468
+ ],
469
+ children: [
470
+ /* @__PURE__ */ jsx4(TouchableOpacity2, { style: styles3.tooltipButton, onPress: handleAddAnnotation, children: /* @__PURE__ */ jsx4(Text2, { style: styles3.tooltipButtonText, children: "+" }) }),
471
+ /* @__PURE__ */ jsx4(TouchableOpacity2, { style: styles3.dismissButton, onPress: handleDismiss, children: /* @__PURE__ */ jsx4(Text2, { style: styles3.dismissButtonText, children: "\u2715" }) })
472
+ ]
473
+ }
474
+ ),
475
+ showInput && /* @__PURE__ */ jsx4(AnnotationInput, { onClose: handleCloseInput, componentName: componentName || "Unknown" })
476
+ ] });
477
+ }
478
+ var styles3 = StyleSheet3.create({
479
+ touchOverlay: {
480
+ ...StyleSheet3.absoluteFillObject,
481
+ backgroundColor: "transparent"
482
+ },
483
+ highlight: {
484
+ position: "absolute",
485
+ borderWidth: 2,
486
+ borderColor: "#3b82f6",
487
+ borderRadius: 4,
488
+ backgroundColor: "rgba(59, 130, 246, 0.1)"
489
+ },
490
+ nameLabel: {
491
+ position: "absolute",
492
+ backgroundColor: "#3b82f6",
493
+ paddingHorizontal: 8,
494
+ paddingVertical: 2,
495
+ borderRadius: 4
496
+ },
497
+ nameLabelText: {
498
+ color: "white",
499
+ fontSize: 12,
500
+ fontFamily: "monospace"
501
+ },
502
+ tooltip: {
503
+ position: "absolute",
504
+ flexDirection: "row",
505
+ gap: 6,
506
+ padding: 6,
507
+ backgroundColor: "rgba(0, 0, 0, 0.9)",
508
+ borderRadius: 8,
509
+ shadowColor: "#000",
510
+ shadowOffset: { width: 0, height: 4 },
511
+ shadowOpacity: 0.3,
512
+ shadowRadius: 12,
513
+ elevation: 8
514
+ },
515
+ tooltipButton: {
516
+ width: 32,
517
+ height: 32,
518
+ borderRadius: 6,
519
+ backgroundColor: "#3b82f6",
520
+ justifyContent: "center",
521
+ alignItems: "center"
522
+ },
523
+ tooltipButtonText: {
524
+ color: "white",
525
+ fontSize: 18,
526
+ fontWeight: "bold"
527
+ },
528
+ dismissButton: {
529
+ width: 32,
530
+ height: 32,
531
+ borderRadius: 6,
532
+ backgroundColor: "#6b7280",
533
+ justifyContent: "center",
534
+ alignItems: "center"
535
+ },
536
+ dismissButtonText: {
537
+ color: "white",
538
+ fontSize: 14
539
+ }
540
+ });
541
+
542
+ // src/components/native/AnnotationList.tsx
543
+ import {
544
+ View as View4,
545
+ Text as Text3,
546
+ Modal as Modal2,
547
+ TouchableOpacity as TouchableOpacity3,
548
+ ScrollView,
549
+ StyleSheet as StyleSheet4
550
+ } from "react-native";
551
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
552
+ function AnnotationList() {
553
+ const { state, dispatch } = useAiAnnotation();
554
+ if (!state.showList) return null;
555
+ return /* @__PURE__ */ jsx5(
556
+ Modal2,
557
+ {
558
+ visible: state.showList,
559
+ transparent: true,
560
+ animationType: "fade",
561
+ onRequestClose: () => dispatch({ type: "TOGGLE_LIST" }),
562
+ children: /* @__PURE__ */ jsx5(View4, { style: styles4.overlay, children: /* @__PURE__ */ jsxs3(View4, { style: styles4.container, children: [
563
+ /* @__PURE__ */ jsxs3(View4, { style: styles4.header, children: [
564
+ /* @__PURE__ */ jsxs3(Text3, { style: styles4.title, children: [
565
+ "Annotations (",
566
+ state.annotations.length,
567
+ ")"
568
+ ] }),
569
+ /* @__PURE__ */ jsxs3(View4, { style: styles4.headerActions, children: [
570
+ state.annotations.length > 0 && /* @__PURE__ */ jsx5(
571
+ TouchableOpacity3,
572
+ {
573
+ style: styles4.clearButton,
574
+ onPress: () => dispatch({ type: "CLEAR_ALL_ANNOTATIONS" }),
575
+ children: /* @__PURE__ */ jsx5(Text3, { style: styles4.clearButtonText, children: "Clear All" })
576
+ }
577
+ ),
578
+ /* @__PURE__ */ jsx5(
579
+ TouchableOpacity3,
580
+ {
581
+ style: styles4.closeButton,
582
+ onPress: () => dispatch({ type: "TOGGLE_LIST" }),
583
+ children: /* @__PURE__ */ jsx5(Text3, { style: styles4.closeButtonText, children: "\u2715" })
584
+ }
585
+ )
586
+ ] })
587
+ ] }),
588
+ /* @__PURE__ */ jsx5(ScrollView, { style: styles4.listContainer, children: state.annotations.length === 0 ? /* @__PURE__ */ jsx5(Text3, { style: styles4.emptyText, children: "No annotations yet." }) : state.annotations.map((ann) => /* @__PURE__ */ jsxs3(View4, { style: styles4.annotationItem, children: [
589
+ /* @__PURE__ */ jsxs3(View4, { style: styles4.annotationContent, children: [
590
+ /* @__PURE__ */ jsx5(Text3, { style: styles4.componentName, children: ann.componentName }),
591
+ /* @__PURE__ */ jsx5(Text3, { style: styles4.annotationNote, children: ann.note })
592
+ ] }),
593
+ /* @__PURE__ */ jsx5(
594
+ TouchableOpacity3,
595
+ {
596
+ style: styles4.deleteButton,
597
+ onPress: () => dispatch({ type: "REMOVE_ANNOTATION", payload: ann.id }),
598
+ children: /* @__PURE__ */ jsx5(Text3, { style: styles4.deleteButtonText, children: "\u{1F5D1}" })
599
+ }
600
+ )
601
+ ] }, ann.id)) })
602
+ ] }) })
603
+ }
604
+ );
605
+ }
606
+ var styles4 = StyleSheet4.create({
607
+ overlay: {
608
+ flex: 1,
609
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
610
+ justifyContent: "center",
611
+ alignItems: "center"
612
+ },
613
+ container: {
614
+ backgroundColor: "#1e1e1e",
615
+ borderRadius: 8,
616
+ width: "90%",
617
+ maxWidth: 500,
618
+ maxHeight: "80%",
619
+ padding: 20
620
+ },
621
+ header: {
622
+ flexDirection: "row",
623
+ justifyContent: "space-between",
624
+ alignItems: "center",
625
+ marginBottom: 16
626
+ },
627
+ title: {
628
+ color: "#e5e7eb",
629
+ fontSize: 18,
630
+ fontWeight: "bold"
631
+ },
632
+ headerActions: {
633
+ flexDirection: "row",
634
+ alignItems: "center",
635
+ gap: 8
636
+ },
637
+ clearButton: {
638
+ backgroundColor: "#ef4444",
639
+ paddingHorizontal: 12,
640
+ paddingVertical: 6,
641
+ borderRadius: 4
642
+ },
643
+ clearButtonText: {
644
+ color: "white",
645
+ fontSize: 12
646
+ },
647
+ closeButton: {
648
+ padding: 4
649
+ },
650
+ closeButtonText: {
651
+ color: "#e5e7eb",
652
+ fontSize: 20
653
+ },
654
+ listContainer: {
655
+ flex: 1
656
+ },
657
+ emptyText: {
658
+ color: "#9ca3af",
659
+ textAlign: "center",
660
+ padding: 20
661
+ },
662
+ annotationItem: {
663
+ backgroundColor: "#2d2d2d",
664
+ padding: 12,
665
+ borderRadius: 4,
666
+ marginBottom: 8,
667
+ flexDirection: "row",
668
+ justifyContent: "space-between",
669
+ alignItems: "flex-start"
670
+ },
671
+ annotationContent: {
672
+ flex: 1,
673
+ marginRight: 12
674
+ },
675
+ componentName: {
676
+ color: "#60a5fa",
677
+ fontWeight: "bold",
678
+ fontSize: 14,
679
+ marginBottom: 4
680
+ },
681
+ annotationNote: {
682
+ color: "#e5e7eb",
683
+ fontSize: 14
684
+ },
685
+ deleteButton: {
686
+ padding: 4
687
+ },
688
+ deleteButtonText: {
689
+ fontSize: 16
690
+ }
691
+ });
692
+
693
+ // src/components/native/Toolbar.tsx
694
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
695
+ var copyToClipboard = async (text) => {
696
+ try {
697
+ const ExpoClipboard = await import("expo-clipboard");
698
+ await ExpoClipboard.setStringAsync(text);
699
+ } catch {
700
+ try {
701
+ const RNClipboard = await import("@react-native-clipboard/clipboard");
702
+ RNClipboard.default.setString(text);
703
+ } catch {
704
+ console.warn("No clipboard module available");
705
+ }
706
+ }
707
+ };
708
+ function Toolbar() {
709
+ const { state, dispatch } = useAiAnnotation();
710
+ const [showCopied, setShowCopied] = useState3(false);
711
+ const handleCopy = async () => {
712
+ const data = state.annotations.map((a) => ({
713
+ component: a.componentName,
714
+ instruction: a.note
715
+ }));
716
+ const text = JSON.stringify(data, null, 2);
717
+ await copyToClipboard(text);
718
+ setShowCopied(true);
719
+ setTimeout(() => setShowCopied(false), 2e3);
720
+ };
721
+ const toggleMode = () => {
722
+ dispatch({
723
+ type: "SET_MODE",
724
+ payload: state.mode === "disabled" ? "inspecting" : "disabled"
725
+ });
726
+ };
727
+ const handleToggleMinimized = () => {
728
+ dispatch({ type: "TOGGLE_MINIMIZED" });
729
+ };
730
+ return /* @__PURE__ */ jsxs4(Fragment2, { children: [
731
+ /* @__PURE__ */ jsx6(Highlighter, {}),
732
+ /* @__PURE__ */ jsx6(AnnotationList, {}),
733
+ /* @__PURE__ */ jsx6(Draggable, { children: /* @__PURE__ */ jsxs4(View5, { style: styles5.toolbar, children: [
734
+ /* @__PURE__ */ jsx6(View5, { style: styles5.dragHandle, children: /* @__PURE__ */ jsx6(Text4, { style: styles5.dragHandleText, children: "\u22EE\u22EE" }) }),
735
+ !state.isMinimized && /* @__PURE__ */ jsxs4(Fragment2, { children: [
736
+ /* @__PURE__ */ jsx6(View5, { style: styles5.separator }),
737
+ /* @__PURE__ */ jsx6(
738
+ TouchableOpacity4,
739
+ {
740
+ style: [
741
+ styles5.button,
742
+ state.mode === "inspecting" && styles5.buttonActive
743
+ ],
744
+ onPress: toggleMode,
745
+ children: /* @__PURE__ */ jsx6(Text4, { style: styles5.buttonText, children: state.mode === "inspecting" ? "\u{1F446}" : "\u{1F6AB}" })
746
+ }
747
+ ),
748
+ /* @__PURE__ */ jsxs4(
749
+ TouchableOpacity4,
750
+ {
751
+ style: styles5.button,
752
+ onPress: () => dispatch({ type: "TOGGLE_LIST" }),
753
+ children: [
754
+ /* @__PURE__ */ jsx6(Text4, { style: styles5.buttonText, children: "\u{1F4CB}" }),
755
+ state.annotations.length > 0 && /* @__PURE__ */ jsx6(View5, { style: styles5.badge, children: /* @__PURE__ */ jsx6(Text4, { style: styles5.badgeText, children: state.annotations.length }) })
756
+ ]
757
+ }
758
+ ),
759
+ /* @__PURE__ */ jsxs4(
760
+ TouchableOpacity4,
761
+ {
762
+ style: [styles5.button, showCopied && styles5.buttonSuccess],
763
+ onPress: handleCopy,
764
+ children: [
765
+ /* @__PURE__ */ jsx6(Text4, { style: styles5.buttonText, children: showCopied ? "\u2713" : "\u{1F4CB}" }),
766
+ showCopied && /* @__PURE__ */ jsx6(Text4, { style: styles5.copiedText, children: "Copied!" })
767
+ ]
768
+ }
769
+ )
770
+ ] }),
771
+ /* @__PURE__ */ jsx6(View5, { style: styles5.separator }),
772
+ /* @__PURE__ */ jsx6(TouchableOpacity4, { style: styles5.button, onPress: handleToggleMinimized, children: /* @__PURE__ */ jsx6(Text4, { style: styles5.buttonText, children: state.isMinimized ? "\u2B1C" : "\u2796" }) })
773
+ ] }) })
774
+ ] });
775
+ }
776
+ var styles5 = StyleSheet5.create({
777
+ toolbar: {
778
+ backgroundColor: "#1e1e1e",
779
+ borderWidth: 1,
780
+ borderColor: "#333",
781
+ borderRadius: 8,
782
+ padding: 8,
783
+ flexDirection: "row",
784
+ alignItems: "center",
785
+ gap: 8,
786
+ shadowColor: "#000",
787
+ shadowOffset: { width: 0, height: 4 },
788
+ shadowOpacity: 0.3,
789
+ shadowRadius: 12,
790
+ elevation: 8
791
+ },
792
+ dragHandle: {
793
+ paddingHorizontal: 4
794
+ },
795
+ dragHandleText: {
796
+ color: "#666",
797
+ fontSize: 16
798
+ },
799
+ separator: {
800
+ width: 1,
801
+ height: 24,
802
+ backgroundColor: "#333"
803
+ },
804
+ button: {
805
+ width: 32,
806
+ height: 32,
807
+ borderRadius: 4,
808
+ justifyContent: "center",
809
+ alignItems: "center",
810
+ position: "relative"
811
+ },
812
+ buttonActive: {
813
+ backgroundColor: "#3b82f6"
814
+ },
815
+ buttonSuccess: {
816
+ backgroundColor: "#22c55e",
817
+ flexDirection: "row",
818
+ width: "auto",
819
+ paddingHorizontal: 8,
820
+ gap: 4
821
+ },
822
+ buttonText: {
823
+ fontSize: 16
824
+ },
825
+ copiedText: {
826
+ color: "white",
827
+ fontSize: 12
828
+ },
829
+ badge: {
830
+ position: "absolute",
831
+ top: -4,
832
+ right: -4,
833
+ backgroundColor: "#ef4444",
834
+ borderRadius: 7,
835
+ width: 14,
836
+ height: 14,
837
+ justifyContent: "center",
838
+ alignItems: "center"
839
+ },
840
+ badgeText: {
841
+ color: "white",
842
+ fontSize: 9,
843
+ fontWeight: "bold"
844
+ }
845
+ });
846
+
847
+ // src/utils/screenshot.native.ts
848
+ async function captureScreenshot(viewRef, options = {}) {
849
+ const {
850
+ scale = 2,
851
+ format = "png",
852
+ quality = 0.9,
853
+ copyToClipboard: copyToClipboard2 = false,
854
+ saveToCameraRoll = false
855
+ } = options;
856
+ try {
857
+ let captureRef;
858
+ try {
859
+ const viewShot = await import("react-native-view-shot");
860
+ captureRef = viewShot.captureRef;
861
+ } catch {
862
+ return {
863
+ success: false,
864
+ error: "react-native-view-shot is not installed. Run: npm install react-native-view-shot"
865
+ };
866
+ }
867
+ if (!viewRef.current) {
868
+ return {
869
+ success: false,
870
+ error: "View reference is null"
871
+ };
872
+ }
873
+ const uri = await captureRef(viewRef, {
874
+ format,
875
+ quality,
876
+ result: "tmpfile",
877
+ snapshotContentContainer: false
878
+ });
879
+ let base64;
880
+ if (copyToClipboard2) {
881
+ const base64Result = await captureRef(viewRef, {
882
+ format,
883
+ quality,
884
+ result: "base64",
885
+ snapshotContentContainer: false
886
+ });
887
+ base64 = base64Result;
888
+ }
889
+ if (copyToClipboard2 && base64) {
890
+ try {
891
+ const Clipboard = await import("expo-clipboard");
892
+ await Clipboard.setStringAsync(base64);
893
+ } catch {
894
+ console.warn("expo-clipboard not available for clipboard support");
895
+ }
896
+ }
897
+ if (saveToCameraRoll) {
898
+ try {
899
+ const MediaLibrary = await import("expo-media-library");
900
+ const { status } = await MediaLibrary.requestPermissionsAsync();
901
+ if (status === "granted") {
902
+ await MediaLibrary.saveToLibraryAsync(uri);
903
+ }
904
+ } catch {
905
+ console.warn("expo-media-library not available for saving to camera roll");
906
+ }
907
+ }
908
+ return {
909
+ success: true,
910
+ uri,
911
+ base64
912
+ };
913
+ } catch (error) {
914
+ const message = error instanceof Error ? error.message : "Unknown error";
915
+ console.error("Screenshot capture failed:", message);
916
+ return {
917
+ success: false,
918
+ error: message
919
+ };
920
+ }
921
+ }
922
+
923
+ // src/utils/fiber.native.ts
924
+ function getReactFiber(instance) {
925
+ if (!instance) return null;
926
+ const key = Object.keys(instance).find(
927
+ (k) => k.startsWith("__reactFiber$") || k.startsWith("__reactInternalInstance$") || k.startsWith("_reactInternals")
928
+ );
929
+ return key ? instance[key] : null;
930
+ }
931
+ function getComponentDisplayName(fiber) {
932
+ if (!fiber) return "Unknown";
933
+ let curr = fiber;
934
+ while (curr) {
935
+ const type = curr.type;
936
+ if (type) {
937
+ const name = type.displayName || type.name;
938
+ if (name && typeof name === "string") {
939
+ if (!name.startsWith("RCT") && !name.startsWith("_")) {
940
+ return name;
941
+ }
942
+ }
943
+ }
944
+ curr = curr.return;
945
+ }
946
+ return "Unknown";
947
+ }
948
+ function getElementFromFiber(_fiber) {
949
+ return null;
950
+ }
951
+ function inspectComponent(ref) {
952
+ if (!ref) return null;
953
+ const fiber = getReactFiber(ref);
954
+ if (fiber) {
955
+ return {
956
+ name: getComponentDisplayName(fiber),
957
+ props: fiber.memoizedProps
958
+ };
959
+ }
960
+ if (ref.constructor && ref.constructor.name) {
961
+ return {
962
+ name: ref.constructor.name
963
+ };
964
+ }
965
+ return null;
966
+ }
967
+
968
+ // src/utils/platform.ts
969
+ var isWeb = typeof document !== "undefined" && typeof window !== "undefined";
970
+ var isNative = !isWeb && typeof global !== "undefined";
971
+ function getPlatform() {
972
+ if (isWeb) return "web";
973
+ try {
974
+ const { Platform: Platform3 } = __require("react-native");
975
+ if (Platform3.OS === "ios") return "ios";
976
+ if (Platform3.OS === "android") return "android";
977
+ return "native";
978
+ } catch {
979
+ return "native";
980
+ }
981
+ }
982
+ function isTauriEnv() {
983
+ return isWeb && typeof window !== "undefined" && "__TAURI_INTERNALS__" in window;
984
+ }
985
+
986
+ // src/index.native.tsx
987
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
988
+ function AiAnnotationProvider2({ children }) {
989
+ return /* @__PURE__ */ jsx7(AiAnnotationProvider, { children: /* @__PURE__ */ jsxs5(View6, { style: { flex: 1 }, children: [
990
+ children,
991
+ /* @__PURE__ */ jsx7(Toolbar, {})
992
+ ] }) });
993
+ }
994
+ export {
995
+ AiAnnotationProvider2 as AiAnnotationProvider,
996
+ AnnotationInput,
997
+ AnnotationList,
998
+ Draggable,
999
+ Highlighter,
1000
+ Toolbar,
1001
+ captureScreenshot,
1002
+ getComponentDisplayName,
1003
+ getElementFromFiber,
1004
+ getPlatform,
1005
+ getReactFiber,
1006
+ inspectComponent,
1007
+ isNative,
1008
+ isTauriEnv,
1009
+ isWeb,
1010
+ useAiAnnotation
1011
+ };
1012
+ //# sourceMappingURL=index.native.js.map