@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,74 @@
1
+ import React, { useState, useEffect, useRef } from 'react';
2
+
3
+ interface DraggableProps {
4
+ children: React.ReactNode;
5
+ initialPos?: { x: number; y: number };
6
+ }
7
+
8
+ export function Draggable({ children, initialPos = { x: 20, y: 20 } }: DraggableProps) {
9
+ const [pos, setPos] = useState(initialPos);
10
+ const [dragging, setDragging] = useState(false);
11
+ const [rel, setRel] = useState({ x: 0, y: 0 });
12
+ const nodeRef = useRef<HTMLDivElement>(null);
13
+
14
+ const onMouseDown = (e: React.MouseEvent) => {
15
+ if (e.button !== 0) return;
16
+ const node = nodeRef.current;
17
+ if (!node) return;
18
+
19
+ // Only drag if clicking the handle or the container itself, but usually we want a specific handle.
20
+ // For now, let's assume the whole container is draggable unless propagation stopped.
21
+
22
+ const rect = node.getBoundingClientRect();
23
+ setDragging(true);
24
+ setRel({
25
+ x: e.pageX - rect.left - window.scrollX,
26
+ y: e.pageY - rect.top - window.scrollY,
27
+ });
28
+ e.preventDefault();
29
+ };
30
+
31
+ const onMouseMove = (e: MouseEvent) => {
32
+ if (!dragging) return;
33
+ setPos({
34
+ x: e.pageX - rel.x,
35
+ y: e.pageY - rel.y,
36
+ });
37
+ e.preventDefault();
38
+ };
39
+
40
+ const onMouseUp = () => {
41
+ setDragging(false);
42
+ };
43
+
44
+ useEffect(() => {
45
+ if (dragging) {
46
+ document.addEventListener('mousemove', onMouseMove);
47
+ document.addEventListener('mouseup', onMouseUp);
48
+ } else {
49
+ document.removeEventListener('mousemove', onMouseMove);
50
+ document.removeEventListener('mouseup', onMouseUp);
51
+ }
52
+ return () => {
53
+ document.removeEventListener('mousemove', onMouseMove);
54
+ document.removeEventListener('mouseup', onMouseUp);
55
+ };
56
+ }, [dragging]);
57
+
58
+ return (
59
+ <div
60
+ ref={nodeRef}
61
+ style={{
62
+ position: 'fixed',
63
+ left: pos.x,
64
+ top: pos.y,
65
+ zIndex: 9999,
66
+ cursor: dragging ? 'grabbing' : 'grab',
67
+ pointerEvents: 'auto',
68
+ }}
69
+ onMouseDown={onMouseDown}
70
+ >
71
+ {children}
72
+ </div>
73
+ );
74
+ }
@@ -0,0 +1,329 @@
1
+ import { useEffect, useState } from "react";
2
+ import { useAiAnnotation } from "../../store";
3
+ import { AnnotationInput } from "./AnnotationInput";
4
+ import { captureScreenshot } from "../../utils/screenshot";
5
+ import { getReactFiber, getComponentDisplayName } from "../../utils/fiber";
6
+
7
+ export function Highlighter() {
8
+ const { state, dispatch } = useAiAnnotation();
9
+ const { hoveredElement, mode, hoveredComponentInfo } = state;
10
+
11
+ const [rect, setRect] = useState<DOMRect | null>(null);
12
+ const [showInput, setShowInput] = useState(false);
13
+ const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
14
+ const [isLocked, setIsLocked] = useState(false);
15
+ const [lockedPos, setLockedPos] = useState({ x: 0, y: 0 });
16
+ const [isCapturing, setIsCapturing] = useState(false);
17
+ const [captureSuccess, setCaptureSuccess] = useState(false);
18
+
19
+ const componentName = hoveredComponentInfo?.name || "";
20
+
21
+ // Detect hovered element when in inspecting mode
22
+ useEffect(() => {
23
+ if (mode !== 'inspecting') return;
24
+
25
+ const handleMouseOver = (e: MouseEvent) => {
26
+ // Don't detect if locked (user is interacting with tooltip)
27
+ if (isLocked) return;
28
+
29
+ const target = e.target as HTMLElement;
30
+
31
+ // Ignore annotation UI elements
32
+ if (target.closest('[data-ai-annotation-ui]') || target.closest('[data-annotation-tooltip]')) {
33
+ return;
34
+ }
35
+
36
+ // Get React component info from fiber
37
+ const fiber = getReactFiber(target);
38
+ const name = fiber ? getComponentDisplayName(fiber) : null;
39
+
40
+ dispatch({
41
+ type: 'SET_HOVERED',
42
+ payload: { element: target, name }
43
+ });
44
+ };
45
+
46
+ document.addEventListener('mouseover', handleMouseOver);
47
+
48
+ return () => {
49
+ document.removeEventListener('mouseover', handleMouseOver);
50
+ };
51
+ }, [mode, isLocked, dispatch]);
52
+
53
+ // Reset all state when mode changes to disabled
54
+ useEffect(() => {
55
+ if (mode === 'disabled') {
56
+ setRect(null);
57
+ setShowInput(false);
58
+ setIsLocked(false);
59
+ setMousePos({ x: 0, y: 0 });
60
+ setLockedPos({ x: 0, y: 0 });
61
+ setIsCapturing(false);
62
+ setCaptureSuccess(false);
63
+
64
+ // Clear hovered element in store
65
+ dispatch({ type: 'RESET_HOVER' });
66
+ }
67
+ }, [mode, dispatch]);
68
+
69
+ useEffect(() => {
70
+ if (!hoveredElement) {
71
+ setRect(null);
72
+ setShowInput(false);
73
+ setIsLocked(false);
74
+ return;
75
+ }
76
+
77
+ const updateRect = () => {
78
+ const newRect = hoveredElement.getBoundingClientRect();
79
+ setRect(newRect);
80
+ };
81
+
82
+ updateRect();
83
+
84
+ const handleMouseMove = (e: MouseEvent) => {
85
+ // Only update mouse position if not locked
86
+ if (!isLocked) {
87
+ setMousePos({ x: e.clientX, y: e.clientY });
88
+ }
89
+ };
90
+
91
+ // Click handler to lock/unlock tooltip
92
+ const handleClick = (e: MouseEvent) => {
93
+ const target = e.target as HTMLElement;
94
+
95
+ // Ignore clicks on the tooltip buttons themselves
96
+ if (target.closest('[data-annotation-tooltip]')) {
97
+ return;
98
+ }
99
+
100
+ // Ignore clicks on annotation input
101
+ if (target.closest('[data-ai-annotation-ui]')) {
102
+ return;
103
+ }
104
+
105
+ if (!isLocked) {
106
+ // Lock the tooltip at current position
107
+ e.preventDefault();
108
+ e.stopPropagation();
109
+ setIsLocked(true);
110
+ setLockedPos({ x: e.clientX, y: e.clientY });
111
+ } else {
112
+ // Unlock and go back to following mode
113
+ setIsLocked(false);
114
+ }
115
+ };
116
+
117
+ // ESC key handler to unlock tooltip
118
+ const handleKeyDown = (e: KeyboardEvent) => {
119
+ if (e.key === 'Escape' && isLocked) {
120
+ e.preventDefault();
121
+ e.stopPropagation();
122
+ setIsLocked(false);
123
+ setShowInput(false);
124
+ }
125
+ };
126
+
127
+ window.addEventListener("scroll", updateRect, true);
128
+ window.addEventListener("resize", updateRect);
129
+ document.addEventListener("mousemove", handleMouseMove);
130
+ document.addEventListener("click", handleClick, true);
131
+ document.addEventListener("keydown", handleKeyDown);
132
+
133
+ return () => {
134
+ window.removeEventListener("scroll", updateRect, true);
135
+ window.removeEventListener("resize", updateRect);
136
+ document.removeEventListener("mousemove", handleMouseMove);
137
+ document.removeEventListener("click", handleClick, true);
138
+ document.removeEventListener("keydown", handleKeyDown);
139
+ };
140
+ }, [hoveredElement, isLocked]);
141
+
142
+ const handleScreenshot = async (e: React.MouseEvent) => {
143
+ e.stopPropagation();
144
+ if (!hoveredElement || isCapturing) return;
145
+
146
+ setIsCapturing(true);
147
+ try {
148
+ const result = await captureScreenshot(hoveredElement, {
149
+ copyToClipboard: true,
150
+ scale: 2,
151
+ });
152
+
153
+ if (result.success) {
154
+ setCaptureSuccess(true);
155
+ setTimeout(() => {
156
+ setCaptureSuccess(false);
157
+ setIsLocked(false); // Unlock after successful capture
158
+ }, 1500);
159
+ }
160
+ } catch (error) {
161
+ console.error("Screenshot failed:", error);
162
+ } finally {
163
+ setIsCapturing(false);
164
+ }
165
+ };
166
+
167
+ const handleAddClick = (e: React.MouseEvent) => {
168
+ e.stopPropagation();
169
+ setShowInput(true);
170
+ setIsLocked(false); // Unlock when showing input
171
+ };
172
+
173
+ const handleCloseInput = () => {
174
+ setShowInput(false);
175
+ };
176
+
177
+ if (mode !== "inspecting" || !rect) return null;
178
+
179
+ // Use locked position or current mouse position
180
+ const tooltipX = isLocked ? lockedPos.x : mousePos.x;
181
+ const tooltipY = isLocked ? lockedPos.y : mousePos.y;
182
+
183
+ return (
184
+ <>
185
+ {/* Highlight overlay */}
186
+ <div
187
+ data-annotation-ui="true"
188
+ style={{
189
+ position: "fixed",
190
+ left: rect.left - 2,
191
+ top: rect.top - 2,
192
+ width: rect.width + 4,
193
+ height: rect.height + 4,
194
+ border: "2px solid #3b82f6",
195
+ borderRadius: 4,
196
+ pointerEvents: "none",
197
+ zIndex: 99998,
198
+ backgroundColor: "rgba(59, 130, 246, 0.1)",
199
+ }}
200
+ />
201
+
202
+ {/* Component name label */}
203
+ {componentName && (
204
+ <div
205
+ data-annotation-ui="true"
206
+ style={{
207
+ position: "fixed",
208
+ left: rect.left,
209
+ top: rect.top - 24,
210
+ backgroundColor: "#3b82f6",
211
+ color: "white",
212
+ padding: "2px 8px",
213
+ borderRadius: 4,
214
+ fontSize: 12,
215
+ fontFamily: "monospace",
216
+ pointerEvents: "none",
217
+ zIndex: 99999,
218
+ }}
219
+ >
220
+ {componentName}
221
+ </div>
222
+ )}
223
+
224
+ {/* Floating tooltip following mouse */}
225
+ {!showInput && (
226
+ <div
227
+ data-annotation-ui="true"
228
+ data-annotation-tooltip="true"
229
+ style={{
230
+ position: "fixed",
231
+ left: tooltipX + 16,
232
+ top: tooltipY + 16,
233
+ display: "flex",
234
+ gap: 6,
235
+ padding: 6,
236
+ backgroundColor: isLocked ? "rgba(0, 0, 0, 0.95)" : "rgba(0, 0, 0, 0.85)",
237
+ borderRadius: 8,
238
+ boxShadow: isLocked
239
+ ? "0 4px 12px rgba(0, 0, 0, 0.4), 0 0 0 2px rgba(59, 130, 246, 0.5)"
240
+ : "0 4px 12px rgba(0, 0, 0, 0.3)",
241
+ zIndex: 100000,
242
+ pointerEvents: isLocked ? "auto" : "none",
243
+ backdropFilter: "blur(8px)",
244
+ transition: isLocked ? "none" : "left 0.05s ease-out, top 0.05s ease-out",
245
+ }}
246
+ >
247
+ {/* Screenshot button */}
248
+ <button
249
+ onClick={handleScreenshot}
250
+ disabled={isCapturing || !isLocked}
251
+ style={{
252
+ width: 32,
253
+ height: 32,
254
+ borderRadius: 6,
255
+ border: "none",
256
+ backgroundColor: captureSuccess
257
+ ? "#22c55e"
258
+ : isCapturing
259
+ ? "#6b7280"
260
+ : "#8b5cf6",
261
+ color: "white",
262
+ cursor: isLocked ? "pointer" : "default",
263
+ display: "flex",
264
+ alignItems: "center",
265
+ justifyContent: "center",
266
+ fontSize: 16,
267
+ transition: "all 0.15s ease",
268
+ opacity: isLocked ? 1 : 0.7,
269
+ }}
270
+ title="Capture screenshot"
271
+ >
272
+ {captureSuccess ? "✓" : isCapturing ? "..." : "📷"}
273
+ </button>
274
+
275
+ {/* Add annotation button */}
276
+ <button
277
+ onClick={handleAddClick}
278
+ disabled={!isLocked}
279
+ style={{
280
+ width: 32,
281
+ height: 32,
282
+ borderRadius: 6,
283
+ border: "none",
284
+ backgroundColor: "#3b82f6",
285
+ color: "white",
286
+ cursor: isLocked ? "pointer" : "default",
287
+ display: "flex",
288
+ alignItems: "center",
289
+ justifyContent: "center",
290
+ fontSize: 18,
291
+ fontWeight: "bold",
292
+ transition: "all 0.15s ease",
293
+ opacity: isLocked ? 1 : 0.7,
294
+ }}
295
+ title="Add annotation"
296
+ >
297
+ +
298
+ </button>
299
+
300
+ {/* Lock hint */}
301
+ {!isLocked && (
302
+ <div
303
+ style={{
304
+ position: "absolute",
305
+ bottom: -20,
306
+ left: "50%",
307
+ transform: "translateX(-50%)",
308
+ fontSize: 10,
309
+ color: "rgba(255, 255, 255, 0.7)",
310
+ whiteSpace: "nowrap",
311
+ pointerEvents: "none",
312
+ }}
313
+ >
314
+ Click to lock
315
+ </div>
316
+ )}
317
+ </div>
318
+ )}
319
+
320
+ {/* Annotation input */}
321
+ {showInput && (
322
+ <AnnotationInput
323
+ onClose={handleCloseInput}
324
+ componentName={componentName || "Unknown"}
325
+ />
326
+ )}
327
+ </>
328
+ );
329
+ }
@@ -0,0 +1,198 @@
1
+ import React, { useState, useEffect, useRef } from 'react';
2
+ import { useAiAnnotation } from '../../store';
3
+ import { Draggable } from './Draggable';
4
+ import { GripVertical, MousePointer2, List, Copy, Minus, Maximize2, Ban, Check } from 'lucide-react';
5
+ import { Highlighter } from './Highlighter';
6
+ import { AnnotationList } from './AnnotationList';
7
+
8
+ export function Toolbar() {
9
+ const { state, dispatch } = useAiAnnotation();
10
+ const [showCopied, setShowCopied] = useState(false);
11
+ const [isAnimating, setIsAnimating] = useState(false);
12
+ const contentRef = useRef<HTMLDivElement>(null);
13
+ const [contentWidth, setContentWidth] = useState<number | null>(null);
14
+
15
+ // Measure content width for smooth animation
16
+ useEffect(() => {
17
+ if (contentRef.current && !state.isMinimized) {
18
+ // Use requestAnimationFrame to ensure DOM is updated before measuring
19
+ requestAnimationFrame(() => {
20
+ if (contentRef.current) {
21
+ setContentWidth(contentRef.current.scrollWidth);
22
+ }
23
+ });
24
+ }
25
+ }, [state.isMinimized, showCopied, state.annotations.length]);
26
+
27
+ // Handle minimize toggle with animation
28
+ const handleToggleMinimized = () => {
29
+ setIsAnimating(true);
30
+ dispatch({ type: 'TOGGLE_MINIMIZED' });
31
+ setTimeout(() => setIsAnimating(false), 300);
32
+ };
33
+
34
+ const handleCopy = () => {
35
+ const data = state.annotations.map((a) => ({
36
+ component: a.componentName,
37
+ instruction: a.note,
38
+ }));
39
+ const text = JSON.stringify(data, null, 2);
40
+ navigator.clipboard.writeText(text).then(() => {
41
+ setShowCopied(true);
42
+ setTimeout(() => setShowCopied(false), 2000);
43
+ });
44
+ };
45
+
46
+ const toggleMode = () => {
47
+ dispatch({
48
+ type: 'SET_MODE',
49
+ payload: state.mode === 'disabled' ? 'inspecting' : 'disabled',
50
+ });
51
+ };
52
+
53
+ return (
54
+ <>
55
+ <Highlighter />
56
+ <AnnotationList />
57
+ <Draggable>
58
+ <div
59
+ style={{
60
+ backgroundColor: '#1e1e1e',
61
+ border: '1px solid #333',
62
+ borderRadius: '8px',
63
+ padding: '8px',
64
+ boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
65
+ display: 'flex',
66
+ alignItems: 'center',
67
+ gap: '8px',
68
+ color: '#e5e7eb',
69
+ transition: 'width 0.2s',
70
+ }}
71
+ data-ai-annotation-ui
72
+ >
73
+ {/* Drag Handle */}
74
+ <div style={{ cursor: 'grab', color: '#666', display: 'flex' }} title="Drag">
75
+ <GripVertical size={20} />
76
+ </div>
77
+
78
+ <div
79
+ ref={contentRef}
80
+ style={{
81
+ display: 'flex',
82
+ alignItems: 'center',
83
+ gap: '8px',
84
+ overflow: 'hidden',
85
+ transition: 'max-width 0.3s ease, opacity 0.2s ease',
86
+ maxWidth: state.isMinimized ? 0 : (contentWidth || 300),
87
+ opacity: state.isMinimized ? 0 : 1,
88
+ paddingTop: '4px',
89
+ paddingBottom: '4px',
90
+ marginTop: '-4px',
91
+ marginBottom: '-4px',
92
+ }}
93
+ >
94
+ <>
95
+ <div style={{ width: '1px', height: '24px', backgroundColor: '#333' }} />
96
+
97
+ <button
98
+ onClick={toggleMode}
99
+ title={state.mode === 'inspecting' ? "Disable Inspection" : "Enable Inspection"}
100
+ style={{
101
+ background: state.mode === 'inspecting' ? '#3b82f6' : 'transparent',
102
+ border: 'none',
103
+ borderRadius: '4px',
104
+ padding: '6px',
105
+ color: state.mode === 'inspecting' ? 'white' : 'inherit',
106
+ cursor: 'pointer',
107
+ display: 'flex'
108
+ }}
109
+ >
110
+ {state.mode === 'inspecting' ? <MousePointer2 size={18} /> : <Ban size={18} />}
111
+ </button>
112
+
113
+ <button
114
+ onClick={() => dispatch({ type: 'TOGGLE_LIST' })}
115
+ title="List Annotations"
116
+ style={{
117
+ background: 'transparent',
118
+ border: 'none',
119
+ borderRadius: '4px',
120
+ padding: '6px',
121
+ color: 'inherit',
122
+ cursor: 'pointer',
123
+ display: 'flex',
124
+ position: 'relative'
125
+ }}
126
+ >
127
+ <List size={18} />
128
+ {state.annotations.length > 0 && (
129
+ <span style={{
130
+ position: 'absolute',
131
+ top: -2,
132
+ right: -2,
133
+ background: '#ef4444',
134
+ color: 'white',
135
+ fontSize: '9px',
136
+ width: '14px',
137
+ height: '14px',
138
+ borderRadius: '50%',
139
+ display: 'flex',
140
+ alignItems: 'center',
141
+ justifyContent: 'center'
142
+ }}>
143
+ {state.annotations.length}
144
+ </span>
145
+ )}
146
+ </button>
147
+
148
+ <button
149
+ onClick={handleCopy}
150
+ title="Copy Annotations"
151
+ style={{
152
+ background: showCopied ? '#22c55e' : 'transparent',
153
+ border: 'none',
154
+ borderRadius: '4px',
155
+ padding: '6px',
156
+ color: showCopied ? 'white' : 'inherit',
157
+ cursor: 'pointer',
158
+ display: 'flex',
159
+ alignItems: 'center',
160
+ gap: '4px',
161
+ transition: 'background-color 0.2s, color 0.2s',
162
+ minWidth: showCopied ? '75px' : 'auto',
163
+ }}
164
+ >
165
+ {showCopied ? (
166
+ <>
167
+ <Check size={18} />
168
+ <span style={{ fontSize: '12px', whiteSpace: 'nowrap' }}>Copied!</span>
169
+ </>
170
+ ) : (
171
+ <Copy size={18} />
172
+ )}
173
+ </button>
174
+ </>
175
+ </div>
176
+
177
+ <div style={{ width: '1px', height: '24px', backgroundColor: '#333', marginLeft: state.isMinimized ? 0 : 'auto' }} />
178
+
179
+ <button
180
+ onClick={handleToggleMinimized}
181
+ title={state.isMinimized ? "Expand" : "Minimize"}
182
+ style={{
183
+ background: 'transparent',
184
+ border: 'none',
185
+ borderRadius: '4px',
186
+ padding: '6px',
187
+ color: 'inherit',
188
+ cursor: 'pointer',
189
+ display: 'flex'
190
+ }}
191
+ >
192
+ {state.isMinimized ? <Maximize2 size={18} /> : <Minus size={18} />}
193
+ </button>
194
+ </div>
195
+ </Draggable>
196
+ </>
197
+ );
198
+ }
@@ -0,0 +1,6 @@
1
+ // Web components export (re-export from parent for backwards compatibility)
2
+ export { Toolbar } from '../Toolbar';
3
+ export { Highlighter } from '../Highlighter';
4
+ export { AnnotationInput } from '../AnnotationInput';
5
+ export { AnnotationList } from '../AnnotationList';
6
+ export { Draggable } from '../Draggable';
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Extension entry point
3
+ * Self-contained - no cross-imports with react-native code
4
+ */
5
+
6
+ // Re-export web components directly
7
+ export { AiAnnotationProvider, useAiAnnotation } from './index.web';
8
+ export type { Annotation, Mode, HoveredElement, HoveredComponentInfo } from './index.web';
9
+
10
+ // Export web components
11
+ export { Toolbar } from './components/web/Toolbar';
12
+ export { Highlighter } from './components/web/Highlighter';
13
+ export { AnnotationInput } from './components/web/AnnotationInput';
14
+ export { AnnotationList } from './components/web/AnnotationList';
15
+ export { Draggable } from './components/web/Draggable';
@@ -0,0 +1,50 @@
1
+ import React from 'react';
2
+ import { View } from 'react-native';
3
+ import { AiAnnotationProvider as Provider } from './store';
4
+ import { Toolbar } from './components/native/Toolbar';
5
+
6
+ export interface AiAnnotationProviderProps {
7
+ children: React.ReactNode;
8
+ }
9
+
10
+ /**
11
+ * React Native AI Annotation Provider
12
+ * Wraps your app to provide annotation functionality
13
+ */
14
+ export function AiAnnotationProvider({ children }: AiAnnotationProviderProps) {
15
+ return (
16
+ <Provider>
17
+ <View style={{ flex: 1 }}>
18
+ {children}
19
+ <Toolbar />
20
+ </View>
21
+ </Provider>
22
+ );
23
+ }
24
+
25
+ // Re-export store
26
+ export * from './store';
27
+
28
+ // Export native components
29
+ export { Toolbar } from './components/native/Toolbar';
30
+ export { Highlighter } from './components/native/Highlighter';
31
+ export { AnnotationInput } from './components/native/AnnotationInput';
32
+ export { AnnotationList } from './components/native/AnnotationList';
33
+ export { Draggable } from './components/native/Draggable';
34
+
35
+ // Export native screenshot utility
36
+ export { captureScreenshot } from './utils/screenshot.native';
37
+ export type { ScreenshotOptions, ScreenshotResult } from './utils/screenshot.native';
38
+
39
+ // Export native fiber utilities
40
+ export {
41
+ getReactFiber,
42
+ getComponentDisplayName,
43
+ getElementFromFiber,
44
+ inspectComponent,
45
+ } from './utils/fiber.native';
46
+ export type { ComponentInfo } from './utils/fiber.native';
47
+
48
+ // Export platform utilities
49
+ export { isWeb, isNative, getPlatform, isTauriEnv } from './utils/platform';
50
+ export type { PlatformType } from './utils/platform';