@rn-ave/core 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.
@@ -0,0 +1,264 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.RNAVEProvider = RNAVEProvider;
37
+ const react_1 = __importStar(require("react"));
38
+ const react_native_1 = require("react-native");
39
+ const hook_1 = require("./fiber/hook");
40
+ const traversal_1 = require("./fiber/traversal");
41
+ const extractor_1 = require("./context/extractor");
42
+ const highlight_1 = require("./overlay/highlight");
43
+ const InspectorButton_1 = require("./components/InspectorButton");
44
+ const InspectorOverlay_1 = require("./components/InspectorOverlay");
45
+ const editor_1 = require("./editor");
46
+ const devMenu_1 = require("./devMenu");
47
+ /**
48
+ * Internal provider component for rn-ave
49
+ * This is used by auto-initialization only - not meant to be used directly
50
+ */
51
+ function RNAVEProvider({ children }) {
52
+ const [isInspecting, setIsInspecting] = (0, react_1.useState)(false);
53
+ const [selectedElement, setSelectedElement] = (0, react_1.useState)(null);
54
+ const [lastContext, setLastContext] = (0, react_1.useState)(null);
55
+ const containerRef = (0, react_1.useRef)(null);
56
+ // Install DevTools hook on mount
57
+ (0, react_1.useEffect)(() => {
58
+ (0, hook_1.installHook)();
59
+ // Debug: log hook state after a short delay
60
+ setTimeout(() => {
61
+ (0, hook_1.debugHookState)();
62
+ }, 1000);
63
+ }, []);
64
+ const enableInspector = (0, react_1.useCallback)(() => {
65
+ setIsInspecting(true);
66
+ (0, hook_1.debugHookState)();
67
+ }, []);
68
+ const disableInspector = (0, react_1.useCallback)(() => {
69
+ setIsInspecting(false);
70
+ setSelectedElement(null);
71
+ }, []);
72
+ const toggleInspecting = (0, react_1.useCallback)(() => {
73
+ if (isInspecting) {
74
+ disableInspector();
75
+ }
76
+ else {
77
+ enableInspector();
78
+ }
79
+ }, [isInspecting, enableInspector, disableInspector]);
80
+ // Register dev menu toggle
81
+ (0, react_1.useEffect)(() => {
82
+ if (typeof __DEV__ !== 'undefined' && !__DEV__)
83
+ return;
84
+ (0, devMenu_1.registerDevMenuToggle)(toggleInspecting);
85
+ }, [toggleInspecting]);
86
+ // Open in editor function - always uses Cursor
87
+ const openInEditor = (0, react_1.useCallback)(async (sourceOverride) => {
88
+ console.log('\n📂 [rn-ave] openInEditor called');
89
+ const source = sourceOverride || lastContext?.source;
90
+ console.log(' source to open:', source);
91
+ if (!source) {
92
+ console.log(' ERROR: No source location available');
93
+ return false;
94
+ }
95
+ return (0, editor_1.launchEditor)({
96
+ source,
97
+ editor: 'cursor',
98
+ });
99
+ }, [lastContext]);
100
+ /**
101
+ * Find the element at tap coordinates using UIManager.measure
102
+ */
103
+ const findElementAtPosition = (0, react_1.useCallback)(async (tapX, tapY) => {
104
+ const fiberRoot = (0, hook_1.getFiberRoot)();
105
+ if (!fiberRoot) {
106
+ (0, hook_1.debugHookState)();
107
+ return null;
108
+ }
109
+ // Collect all host fibers
110
+ const hostFibers = (0, traversal_1.collectAllHostFibers)(fiberRoot);
111
+ if (hostFibers.length === 0) {
112
+ return null;
113
+ }
114
+ // Measure each fiber and find the best match
115
+ const measurePromises = hostFibers.map((fiber) => new Promise((resolve) => {
116
+ if (!fiber.stateNode) {
117
+ resolve(null);
118
+ return;
119
+ }
120
+ // Try to get native tag from different locations
121
+ const nativeTag = fiber.stateNode._nativeTag ||
122
+ fiber.stateNode.canonical?._nativeTag ||
123
+ (0, react_native_1.findNodeHandle)(fiber.stateNode);
124
+ if (!nativeTag) {
125
+ resolve(null);
126
+ return;
127
+ }
128
+ try {
129
+ react_native_1.UIManager.measure(nativeTag, (x, y, width, height, pageX, pageY) => {
130
+ if (width > 0 && height > 0) {
131
+ resolve({
132
+ fiber,
133
+ bounds: { x: pageX, y: pageY, width, height },
134
+ });
135
+ }
136
+ else {
137
+ resolve(null);
138
+ }
139
+ });
140
+ }
141
+ catch (e) {
142
+ resolve(null);
143
+ }
144
+ }));
145
+ const measured = (await Promise.all(measurePromises)).filter((m) => m !== null);
146
+ // Find all fibers that contain the tap point
147
+ const hits = measured.filter(({ bounds }) => {
148
+ return (tapX >= bounds.x &&
149
+ tapX <= bounds.x + bounds.width &&
150
+ tapY >= bounds.y &&
151
+ tapY <= bounds.y + bounds.height);
152
+ });
153
+ if (hits.length === 0) {
154
+ return null;
155
+ }
156
+ // Return the smallest (most specific) element
157
+ hits.sort((a, b) => {
158
+ const areaA = a.bounds.width * a.bounds.height;
159
+ const areaB = b.bounds.width * b.bounds.height;
160
+ return areaA - areaB;
161
+ });
162
+ return hits[0].fiber;
163
+ }, []);
164
+ const handleTapAtPosition = (0, react_1.useCallback)(async (x, y) => {
165
+ console.log('\n👆 [rn-ave] handleTapAtPosition called');
166
+ console.log(' position:', { x, y });
167
+ console.log(' isInspecting:', isInspecting);
168
+ if (!isInspecting) {
169
+ console.log(' Not in inspecting mode, returning');
170
+ return;
171
+ }
172
+ const fiber = await findElementAtPosition(x, y);
173
+ console.log(' fiber found:', !!fiber);
174
+ if (!fiber) {
175
+ console.log(' No fiber found at position');
176
+ return;
177
+ }
178
+ // Find nearest composite component for context (skip node_modules)
179
+ const compositeFiber = (0, traversal_1.findNearestComposite)(fiber, true) || fiber;
180
+ console.log(' compositeFiber:', compositeFiber?.type?.name || compositeFiber?.type);
181
+ // Pass both fibers: composite for name/hierarchy, host for source (__rnAve prop)
182
+ const context = (0, extractor_1.extractContext)(compositeFiber, fiber);
183
+ console.log(' extracted context:', {
184
+ componentName: context.componentName,
185
+ source: context.source,
186
+ });
187
+ setLastContext(context);
188
+ // Get native tag from the host fiber (not composite)
189
+ const nativeTag = fiber.stateNode?._nativeTag ||
190
+ fiber.stateNode?.canonical?._nativeTag ||
191
+ (0, react_native_1.findNodeHandle)(fiber.stateNode) ||
192
+ 0;
193
+ const element = {
194
+ nativeTag,
195
+ componentInfo: context,
196
+ };
197
+ console.log(' setting selectedElement with source:', element.componentInfo.source);
198
+ setSelectedElement(element);
199
+ // Auto-disable inspecting after selection
200
+ setIsInspecting(false);
201
+ }, [isInspecting, findElementAtPosition]);
202
+ const handleDismiss = (0, react_1.useCallback)(() => {
203
+ setSelectedElement(null);
204
+ }, []);
205
+ const handleSendToAgent = (0, react_1.useCallback)(async (agentType, prompt, contextOverride) => {
206
+ if (!selectedElement) {
207
+ return { success: false, error: 'No element selected' };
208
+ }
209
+ if (!prompt?.trim()) {
210
+ return { success: false, error: 'Prompt is required' };
211
+ }
212
+ const baseContext = contextOverride || selectedElement.componentInfo;
213
+ const payload = {
214
+ ...baseContext,
215
+ userPrompt: prompt.trim(),
216
+ };
217
+ // Determine port based on agent type
218
+ const portMap = {
219
+ 'claude-code': 4568,
220
+ 'opencode': 4569,
221
+ 'cursor': 5568,
222
+ };
223
+ const port = portMap[agentType] || 4568;
224
+ console.log(`\n📤 [rn-ave] Sending to ${agentType}...`);
225
+ console.log(`💬 Prompt: "${prompt.trim()}"`);
226
+ console.log(`📦 Component: ${selectedElement.componentInfo.componentName}`);
227
+ if (selectedElement.componentInfo.source) {
228
+ console.log(`📁 File: ${selectedElement.componentInfo.source.file}:${selectedElement.componentInfo.source.line}`);
229
+ }
230
+ try {
231
+ const response = await fetch(`http://localhost:${port}/context`, {
232
+ method: 'POST',
233
+ headers: { 'Content-Type': 'application/json' },
234
+ body: JSON.stringify(payload),
235
+ });
236
+ if (response.ok) {
237
+ const data = await response.json();
238
+ console.log(`✅ [rn-ave] Sent successfully to ${agentType}!`);
239
+ handleDismiss();
240
+ return { success: true };
241
+ }
242
+ else {
243
+ console.log(`⚠️ [rn-ave] Server responded with ${response.status}`);
244
+ return { success: false, error: `Server responded with ${response.status}` };
245
+ }
246
+ }
247
+ catch (e) {
248
+ console.log(`⚠️ [rn-ave] Could not connect to ${agentType} server. Is it running?`);
249
+ return { success: false, error: `Could not connect to ${agentType} server. Is it running?` };
250
+ }
251
+ }, [selectedElement, handleDismiss]);
252
+ return (<react_native_1.View ref={containerRef} style={react_native_1.StyleSheet.absoluteFill} collapsable={false}>
253
+ {children}
254
+
255
+ {/* Inspector overlay - captures taps when active */}
256
+ <InspectorOverlay_1.InspectorOverlay isActive={isInspecting} onTapAtPosition={handleTapAtPosition}/>
257
+
258
+ {/* Highlight overlay - shows selected element */}
259
+ <highlight_1.HighlightOverlay selectedElement={selectedElement} onDismiss={handleDismiss} onSendToAgent={handleSendToAgent} onOpenInEditor={openInEditor}/>
260
+
261
+ {/* Floating inspector button */}
262
+ <InspectorButton_1.InspectorButton isInspecting={isInspecting} onToggle={toggleInspecting}/>
263
+ </react_native_1.View>);
264
+ }
@@ -0,0 +1,94 @@
1
+ export interface Fiber {
2
+ tag: number;
3
+ type: string | Function | null;
4
+ stateNode: any;
5
+ child: Fiber | null;
6
+ sibling: Fiber | null;
7
+ return: Fiber | null;
8
+ memoizedProps: Record<string, any>;
9
+ memoizedState: any;
10
+ _debugSource?: DebugSource;
11
+ _debugOwner?: Fiber;
12
+ }
13
+ export interface DebugSource {
14
+ fileName: string;
15
+ lineNumber: number;
16
+ columnNumber?: number;
17
+ }
18
+ export interface FiberRoot {
19
+ current: Fiber;
20
+ containerInfo: any;
21
+ }
22
+ export interface ReactRenderer {
23
+ findFiberByHostInstance?: (instance: any) => Fiber | null;
24
+ bundleType?: number;
25
+ version?: string;
26
+ rendererPackageName?: string;
27
+ }
28
+ export interface RNDevToolsHook {
29
+ renderers: Map<number, ReactRenderer>;
30
+ inject: (renderer: ReactRenderer) => number;
31
+ onCommitFiberRoot: (rendererID: number, root: FiberRoot) => void;
32
+ onCommitFiberUnmount: (rendererID: number, fiber: Fiber) => void;
33
+ getFiberRoots?: (rendererID: number) => Set<FiberRoot>;
34
+ }
35
+ export interface SourceLocation {
36
+ file: string;
37
+ line: number;
38
+ column?: number;
39
+ }
40
+ export interface RNAveMeta {
41
+ file: string;
42
+ line: number;
43
+ column: number;
44
+ }
45
+ export interface ComponentInfo {
46
+ name: string;
47
+ props: Record<string, any>;
48
+ source: SourceLocation | null;
49
+ }
50
+ export interface ExtractedContext {
51
+ componentName: string;
52
+ componentHierarchy: ComponentInfo[];
53
+ props: Record<string, any>;
54
+ source: SourceLocation | null;
55
+ jsxPreview: string;
56
+ timestamp: number;
57
+ boxModel?: BoxModelValues;
58
+ }
59
+ export interface Position {
60
+ x: number;
61
+ y: number;
62
+ }
63
+ export interface Bounds {
64
+ x: number;
65
+ y: number;
66
+ width: number;
67
+ height: number;
68
+ }
69
+ export interface BoxModelEdges {
70
+ top: number;
71
+ right: number;
72
+ bottom: number;
73
+ left: number;
74
+ }
75
+ export interface BoxModelValues {
76
+ margin: BoxModelEdges;
77
+ padding: BoxModelEdges;
78
+ border: BoxModelEdges;
79
+ width: number;
80
+ height: number;
81
+ }
82
+ export interface SelectedElement {
83
+ nativeTag: number;
84
+ componentInfo: ExtractedContext;
85
+ bounds?: Bounds;
86
+ }
87
+ export type AgentType = 'claude-code' | 'cursor' | 'opencode';
88
+ export interface GestureConfig {
89
+ longPressDuration: number;
90
+ onElementSelected: (nativeTag: number, position: Position) => void;
91
+ }
92
+ declare global {
93
+ var __REACT_DEVTOOLS_GLOBAL_HOOK__: RNDevToolsHook | undefined;
94
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@rn-ave/core",
3
+ "version": "0.1.0",
4
+ "description": "React Native Agent Visual Edit - core library for visual component selection and AI agent integration",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "main": "dist/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.js",
14
+ "types": "./dist/index.d.ts"
15
+ },
16
+ "./auto": {
17
+ "import": "./dist/auto.js",
18
+ "require": "./dist/auto.js",
19
+ "types": "./dist/auto.d.ts"
20
+ }
21
+ },
22
+ "typesVersions": {
23
+ "*": {
24
+ "auto": ["./dist/auto.d.ts"]
25
+ }
26
+ },
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "scripts": {
31
+ "build": "tsc",
32
+ "dev": "tsc --watch"
33
+ },
34
+ "keywords": [
35
+ "react-native",
36
+ "ai-agent",
37
+ "visual-edit",
38
+ "claude-code",
39
+ "opencode",
40
+ "cursor"
41
+ ],
42
+ "license": "MIT",
43
+ "peerDependencies": {
44
+ "react": ">=18.0.0",
45
+ "react-native": ">=0.60.0"
46
+ },
47
+ "peerDependenciesMeta": {
48
+ "react-native-gesture-handler": {
49
+ "optional": true
50
+ }
51
+ },
52
+ "devDependencies": {
53
+ "@types/node": "^20.0.0",
54
+ "@types/react": "^18.0.0",
55
+ "@types/react-native": "^0.72.0",
56
+ "typescript": "^5.0.0"
57
+ }
58
+ }