@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.
package/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # @rn-ave/core
2
+
3
+ Core library for `rn-ave` - Agent Visual Edit for React Native.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @rn-ave/core
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ The easiest way to use `rn-ave` is with auto-initialization:
14
+
15
+ ```tsx
16
+ import '@rn-ave/core/auto';
17
+ ```
18
+
19
+ Add this line to the top of your `App.tsx` or `index.js`.
20
+
21
+ ## Features
22
+
23
+ - **Visual Selection**: Tap components to select them.
24
+ - **Context Extraction**: Extracts component name, props, hierarchy, and source location.
25
+ - **AI Agent Integration**: Sends context to Claude Code, OpenCode, or Cursor.
26
+ - **Open in Cursor**: Opens the exact file and line in Cursor.
27
+
28
+ ## Requirements
29
+
30
+ - React Native 0.60+
31
+ - Works best with Cursor editor.
@@ -0,0 +1,11 @@
1
+ /**
2
+ * AppRegistry wrapper to automatically inject Inspector provider
3
+ * around registered React Native components
4
+ */
5
+ /**
6
+ * Wrap AppRegistry.registerComponent to automatically inject RNAVEProvider
7
+ *
8
+ * This allows users to enable the inspector with a single import statement,
9
+ * without manually wrapping their app component.
10
+ */
11
+ export declare function wrapAppRegistry(): void;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ /**
3
+ * AppRegistry wrapper to automatically inject Inspector provider
4
+ * around registered React Native components
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.wrapAppRegistry = wrapAppRegistry;
11
+ const react_native_1 = require("react-native");
12
+ const react_1 = __importDefault(require("react"));
13
+ const provider_1 = require("./provider");
14
+ // Track if we've already wrapped AppRegistry (hot reload protection)
15
+ let isWrapped = false;
16
+ /**
17
+ * Wrap AppRegistry.registerComponent to automatically inject RNAVEProvider
18
+ *
19
+ * This allows users to enable the inspector with a single import statement,
20
+ * without manually wrapping their app component.
21
+ */
22
+ function wrapAppRegistry() {
23
+ // Prevent double-wrapping on hot reload
24
+ if (isWrapped) {
25
+ return;
26
+ }
27
+ isWrapped = true;
28
+ // Store original registerComponent method
29
+ const originalRegister = react_native_1.AppRegistry.registerComponent;
30
+ // Replace with our wrapper
31
+ react_native_1.AppRegistry.registerComponent = function (appKey, componentProvider) {
32
+ // Create a wrapped component provider
33
+ const wrappedProvider = () => {
34
+ const OriginalComponent = componentProvider();
35
+ // Create the wrapped component with Inspector
36
+ const WrappedComponent = (props) => {
37
+ return react_1.default.createElement(provider_1.RNAVEProvider, null, react_1.default.createElement(OriginalComponent, props));
38
+ };
39
+ // Preserve display name for React DevTools debugging
40
+ const originalName = OriginalComponent.displayName || OriginalComponent.name || 'Component';
41
+ WrappedComponent.displayName = `RNAVE(${originalName})`;
42
+ return WrappedComponent;
43
+ };
44
+ // Preserve the original component provider's name
45
+ Object.defineProperty(wrappedProvider, 'name', {
46
+ value: componentProvider.name || 'ComponentProvider',
47
+ writable: false,
48
+ });
49
+ // Call the original registerComponent with our wrapped version
50
+ return originalRegister.call(this, appKey, wrappedProvider);
51
+ };
52
+ }
package/dist/auto.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Auto-initialization module for rn-ave
3
+ *
4
+ * Simply import this module to automatically enable the Inspector:
5
+ * ```
6
+ * import '@rn-ave/core/auto';
7
+ * ```
8
+ *
9
+ * This module:
10
+ * 1. Installs React DevTools hook early
11
+ * 2. Wraps AppRegistry to inject Inspector provider automatically
12
+ *
13
+ * Only Cursor editor is supported.
14
+ */
15
+ export {};
package/dist/auto.js ADDED
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ /**
3
+ * Auto-initialization module for rn-ave
4
+ *
5
+ * Simply import this module to automatically enable the Inspector:
6
+ * ```
7
+ * import '@rn-ave/core/auto';
8
+ * ```
9
+ *
10
+ * This module:
11
+ * 1. Installs React DevTools hook early
12
+ * 2. Wraps AppRegistry to inject Inspector provider automatically
13
+ *
14
+ * Only Cursor editor is supported.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ const hook_1 = require("./fiber/hook");
18
+ const appRegistry_1 = require("./appRegistry");
19
+ // Only run in development mode
20
+ if (typeof __DEV__ !== 'undefined' && __DEV__) {
21
+ // 1. Install React DevTools hook FIRST (critical timing)
22
+ // This must happen before React Native initializes
23
+ try {
24
+ (0, hook_1.installHook)();
25
+ }
26
+ catch (error) {
27
+ // Silent fail
28
+ }
29
+ // 2. Wrap AppRegistry to auto-inject Inspector
30
+ // This must happen before any AppRegistry.registerComponent calls
31
+ (0, appRegistry_1.wrapAppRegistry)();
32
+ }
33
+ else {
34
+ // Production mode - do nothing
35
+ }
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ interface InspectorButtonProps {
3
+ isInspecting: boolean;
4
+ onToggle: () => void;
5
+ }
6
+ export declare function InspectorButton({ isInspecting, onToggle }: InspectorButtonProps): React.JSX.Element;
7
+ export {};
@@ -0,0 +1,125 @@
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.InspectorButton = InspectorButton;
37
+ const react_1 = __importStar(require("react"));
38
+ const react_native_1 = require("react-native");
39
+ const SearchIcon = ({ active }) => (<react_native_1.View style={styles.iconContainer}>
40
+ <react_native_1.View style={[styles.lens, active && styles.lensActive]}/>
41
+ <react_native_1.View style={[styles.handle, active && styles.handleActive]}/>
42
+ </react_native_1.View>);
43
+ function InspectorButton({ isInspecting, onToggle }) {
44
+ const [position] = (0, react_1.useState)(new react_native_1.Animated.ValueXY({ x: react_native_1.Dimensions.get('window').width - 70, y: 100 }));
45
+ const panResponder = react_native_1.PanResponder.create({
46
+ onStartShouldSetPanResponder: () => true,
47
+ onMoveShouldSetPanResponder: () => true,
48
+ onPanResponderGrant: () => {
49
+ position.setOffset({
50
+ x: position.x._value,
51
+ y: position.y._value,
52
+ });
53
+ position.setValue({ x: 0, y: 0 });
54
+ },
55
+ onPanResponderMove: react_native_1.Animated.event([null, { dx: position.x, dy: position.y }], {
56
+ useNativeDriver: false,
57
+ }),
58
+ onPanResponderRelease: () => {
59
+ position.flattenOffset();
60
+ },
61
+ });
62
+ return (<react_native_1.Animated.View style={[styles.container, { transform: position.getTranslateTransform() }]} {...panResponder.panHandlers}>
63
+ <react_native_1.TouchableOpacity style={[styles.button, isInspecting && styles.buttonActive]} onPress={onToggle} activeOpacity={0.8}>
64
+ <SearchIcon active={isInspecting}/>
65
+ </react_native_1.TouchableOpacity>
66
+ </react_native_1.Animated.View>);
67
+ }
68
+ const styles = react_native_1.StyleSheet.create({
69
+ container: {
70
+ position: 'absolute',
71
+ zIndex: 9999,
72
+ },
73
+ button: {
74
+ width: 48,
75
+ height: 48,
76
+ borderRadius: 24,
77
+ backgroundColor: '#1C1C1E',
78
+ justifyContent: 'center',
79
+ alignItems: 'center',
80
+ shadowColor: '#000',
81
+ shadowOffset: { width: 0, height: 4 },
82
+ shadowOpacity: 0.3,
83
+ shadowRadius: 8,
84
+ elevation: 8,
85
+ borderWidth: 1,
86
+ borderColor: '#3A3A3C',
87
+ },
88
+ buttonActive: {
89
+ backgroundColor: '#007AFF',
90
+ borderColor: '#007AFF',
91
+ shadowColor: '#007AFF',
92
+ shadowOpacity: 0.5,
93
+ },
94
+ iconContainer: {
95
+ width: 20,
96
+ height: 20,
97
+ justifyContent: 'center',
98
+ alignItems: 'center',
99
+ },
100
+ lens: {
101
+ width: 14,
102
+ height: 14,
103
+ borderRadius: 7,
104
+ borderWidth: 2,
105
+ borderColor: '#FFFFFF',
106
+ position: 'absolute',
107
+ top: 0,
108
+ left: 0,
109
+ },
110
+ lensActive: {
111
+ borderColor: '#FFFFFF',
112
+ },
113
+ handle: {
114
+ width: 2,
115
+ height: 6,
116
+ backgroundColor: '#FFFFFF',
117
+ position: 'absolute',
118
+ bottom: 1,
119
+ right: 1,
120
+ transform: [{ rotate: '-45deg' }],
121
+ },
122
+ handleActive: {
123
+ backgroundColor: '#FFFFFF',
124
+ },
125
+ });
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ interface InspectorOverlayProps {
3
+ isActive: boolean;
4
+ onTapAtPosition: (x: number, y: number) => void;
5
+ }
6
+ /**
7
+ * InspectorOverlay displays a semi-transparent overlay when inspect mode is active.
8
+ * Tapping anywhere triggers coordinate-based element detection.
9
+ */
10
+ export declare function InspectorOverlay({ isActive, onTapAtPosition }: InspectorOverlayProps): React.JSX.Element | null;
11
+ export {};
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.InspectorOverlay = InspectorOverlay;
7
+ const react_1 = __importDefault(require("react"));
8
+ const react_native_1 = require("react-native");
9
+ /**
10
+ * InspectorOverlay displays a semi-transparent overlay when inspect mode is active.
11
+ * Tapping anywhere triggers coordinate-based element detection.
12
+ */
13
+ function InspectorOverlay({ isActive, onTapAtPosition }) {
14
+ console.log('[rn-ave] InspectorOverlay render, isActive:', isActive);
15
+ if (!isActive)
16
+ return null;
17
+ const handlePress = (event) => {
18
+ const { pageX, pageY } = event.nativeEvent;
19
+ console.log('\n🎯 [rn-ave] InspectorOverlay handlePress');
20
+ console.log(' pageX:', pageX, 'pageY:', pageY);
21
+ onTapAtPosition(pageX, pageY);
22
+ };
23
+ return (<react_native_1.View style={react_native_1.StyleSheet.absoluteFill} pointerEvents="box-only">
24
+ <react_native_1.Pressable style={styles.overlay} onPress={handlePress}>
25
+ <react_native_1.View style={styles.hint}>
26
+ <react_native_1.Text style={styles.hintText}>Tap any element to inspect</react_native_1.Text>
27
+ </react_native_1.View>
28
+ </react_native_1.Pressable>
29
+ </react_native_1.View>);
30
+ }
31
+ const styles = react_native_1.StyleSheet.create({
32
+ overlay: {
33
+ ...react_native_1.StyleSheet.absoluteFillObject,
34
+ backgroundColor: 'rgba(0, 122, 255, 0.08)',
35
+ },
36
+ hint: {
37
+ position: 'absolute',
38
+ top: 60,
39
+ left: 0,
40
+ right: 0,
41
+ alignItems: 'center',
42
+ },
43
+ hintText: {
44
+ backgroundColor: '#1C1C1E',
45
+ color: '#FFFFFF',
46
+ paddingHorizontal: 20,
47
+ paddingVertical: 10,
48
+ borderRadius: 24,
49
+ fontSize: 14,
50
+ fontWeight: '600',
51
+ overflow: 'hidden',
52
+ shadowColor: '#000',
53
+ shadowOffset: { width: 0, height: 4 },
54
+ shadowOpacity: 0.3,
55
+ shadowRadius: 8,
56
+ elevation: 6,
57
+ borderWidth: 1,
58
+ borderColor: '#3A3A3C',
59
+ },
60
+ });
@@ -0,0 +1,7 @@
1
+ import type { Fiber, ExtractedContext } from '../types';
2
+ /**
3
+ * Extracts full context from a fiber node
4
+ * @param fiber - The composite fiber (user's component)
5
+ * @param hostFiber - Optional host fiber (the actual tapped element) to extract source from __rnAve prop
6
+ */
7
+ export declare function extractContext(fiber: Fiber, hostFiber?: Fiber): ExtractedContext;
@@ -0,0 +1,235 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractContext = extractContext;
4
+ const traversal_1 = require("../fiber/traversal");
5
+ const source_1 = require("../fiber/source");
6
+ /**
7
+ * Default box model edges (all zeros)
8
+ */
9
+ function defaultEdges() {
10
+ return { top: 0, right: 0, bottom: 0, left: 0 };
11
+ }
12
+ /**
13
+ * Extracts box model values from a style object
14
+ * Handles both shorthand (margin: 8) and individual values (marginTop: 8)
15
+ */
16
+ function extractBoxModel(style) {
17
+ const boxModel = {
18
+ margin: defaultEdges(),
19
+ padding: defaultEdges(),
20
+ border: defaultEdges(),
21
+ width: 0,
22
+ height: 0,
23
+ };
24
+ if (!style)
25
+ return boxModel;
26
+ // Flatten style arrays (RN allows style={[style1, style2]})
27
+ const flatStyle = Array.isArray(style)
28
+ ? style.reduce((acc, s) => ({ ...acc, ...(s || {}) }), {})
29
+ : style;
30
+ // Extract dimensions
31
+ boxModel.width = flatStyle.width || 0;
32
+ boxModel.height = flatStyle.height || 0;
33
+ // Extract margin
34
+ if (flatStyle.margin !== undefined) {
35
+ const m = flatStyle.margin;
36
+ boxModel.margin = { top: m, right: m, bottom: m, left: m };
37
+ }
38
+ if (flatStyle.marginVertical !== undefined) {
39
+ boxModel.margin.top = flatStyle.marginVertical;
40
+ boxModel.margin.bottom = flatStyle.marginVertical;
41
+ }
42
+ if (flatStyle.marginHorizontal !== undefined) {
43
+ boxModel.margin.left = flatStyle.marginHorizontal;
44
+ boxModel.margin.right = flatStyle.marginHorizontal;
45
+ }
46
+ if (flatStyle.marginTop !== undefined)
47
+ boxModel.margin.top = flatStyle.marginTop;
48
+ if (flatStyle.marginRight !== undefined)
49
+ boxModel.margin.right = flatStyle.marginRight;
50
+ if (flatStyle.marginBottom !== undefined)
51
+ boxModel.margin.bottom = flatStyle.marginBottom;
52
+ if (flatStyle.marginLeft !== undefined)
53
+ boxModel.margin.left = flatStyle.marginLeft;
54
+ // Extract padding
55
+ if (flatStyle.padding !== undefined) {
56
+ const p = flatStyle.padding;
57
+ boxModel.padding = { top: p, right: p, bottom: p, left: p };
58
+ }
59
+ if (flatStyle.paddingVertical !== undefined) {
60
+ boxModel.padding.top = flatStyle.paddingVertical;
61
+ boxModel.padding.bottom = flatStyle.paddingVertical;
62
+ }
63
+ if (flatStyle.paddingHorizontal !== undefined) {
64
+ boxModel.padding.left = flatStyle.paddingHorizontal;
65
+ boxModel.padding.right = flatStyle.paddingHorizontal;
66
+ }
67
+ if (flatStyle.paddingTop !== undefined)
68
+ boxModel.padding.top = flatStyle.paddingTop;
69
+ if (flatStyle.paddingRight !== undefined)
70
+ boxModel.padding.right = flatStyle.paddingRight;
71
+ if (flatStyle.paddingBottom !== undefined)
72
+ boxModel.padding.bottom = flatStyle.paddingBottom;
73
+ if (flatStyle.paddingLeft !== undefined)
74
+ boxModel.padding.left = flatStyle.paddingLeft;
75
+ // Extract border
76
+ if (flatStyle.borderWidth !== undefined) {
77
+ const b = flatStyle.borderWidth;
78
+ boxModel.border = { top: b, right: b, bottom: b, left: b };
79
+ }
80
+ if (flatStyle.borderTopWidth !== undefined)
81
+ boxModel.border.top = flatStyle.borderTopWidth;
82
+ if (flatStyle.borderRightWidth !== undefined)
83
+ boxModel.border.right = flatStyle.borderRightWidth;
84
+ if (flatStyle.borderBottomWidth !== undefined)
85
+ boxModel.border.bottom = flatStyle.borderBottomWidth;
86
+ if (flatStyle.borderLeftWidth !== undefined)
87
+ boxModel.border.left = flatStyle.borderLeftWidth;
88
+ return boxModel;
89
+ }
90
+ /**
91
+ * Sanitizes props to make them serializable
92
+ */
93
+ function sanitizeProps(props) {
94
+ const result = {};
95
+ for (const [key, value] of Object.entries(props)) {
96
+ // Skip internal props
97
+ if (key.startsWith('__'))
98
+ continue;
99
+ if (key === 'children')
100
+ continue;
101
+ // Handle different value types
102
+ if (typeof value === 'function') {
103
+ result[key] = '[Function]';
104
+ }
105
+ else if (typeof value === 'object' && value !== null) {
106
+ try {
107
+ // Test if serializable
108
+ JSON.stringify(value);
109
+ result[key] = value;
110
+ }
111
+ catch {
112
+ result[key] = '[Object]';
113
+ }
114
+ }
115
+ else {
116
+ result[key] = value;
117
+ }
118
+ }
119
+ return result;
120
+ }
121
+ /**
122
+ * Generates a JSX-like preview of the element
123
+ */
124
+ function generateJSXPreview(fiber) {
125
+ const name = (0, traversal_1.getDisplayName)(fiber);
126
+ const props = (0, traversal_1.getCleanProps)(fiber);
127
+ const sanitized = sanitizeProps(props);
128
+ // Build prop string
129
+ const propStrings = [];
130
+ for (const [key, value] of Object.entries(sanitized)) {
131
+ if (typeof value === 'string') {
132
+ propStrings.push(`${key}="${value}"`);
133
+ }
134
+ else if (typeof value === 'number' || typeof value === 'boolean') {
135
+ propStrings.push(`${key}={${value}}`);
136
+ }
137
+ else if (value === '[Function]') {
138
+ propStrings.push(`${key}={[Function]}`);
139
+ }
140
+ else {
141
+ propStrings.push(`${key}={...}`);
142
+ }
143
+ }
144
+ if (propStrings.length === 0) {
145
+ return `<${name} />`;
146
+ }
147
+ if (propStrings.length <= 2) {
148
+ return `<${name} ${propStrings.join(' ')} />`;
149
+ }
150
+ // Multi-line for many props
151
+ return `<${name}\n ${propStrings.join('\n ')}\n/>`;
152
+ }
153
+ /**
154
+ * Extracts source location by traversing UP from a fiber to find any source info.
155
+ * The Babel plugin injects __rnAve on JSX elements, but those become composite fibers
156
+ * which then render host fibers. So we traverse up to find the best source.
157
+ */
158
+ function getSourceFromFiberTree(startFiber) {
159
+ let current = startFiber;
160
+ let firstSource = null;
161
+ console.log(' [source-walk] Starting walk from:', (0, traversal_1.getDisplayName)(startFiber));
162
+ // Traverse up the fiber tree to find a fiber with source info
163
+ while (current) {
164
+ const name = (0, traversal_1.getDisplayName)(current);
165
+ const source = (0, source_1.getSourceLocation)(current);
166
+ // Debug: Log props of every fiber we visit
167
+ const props = current.memoizedProps || {};
168
+ const propKeys = Object.keys(props);
169
+ if (propKeys.some(k => k.startsWith('__rn'))) {
170
+ const metaKey = propKeys.find(k => k.startsWith('__rn'));
171
+ console.log(` [source-walk] Fiber ${name} has metadata prop ${metaKey}:`, JSON.stringify(props[metaKey]));
172
+ }
173
+ if (source && source.file) {
174
+ console.log(` [source-walk] Fiber ${name} has source:`, source.file);
175
+ // Save the first source we find as a fallback
176
+ if (!firstSource) {
177
+ firstSource = source;
178
+ }
179
+ // If it's NOT node_modules, this is definitely what we want
180
+ if (!source.file.includes('node_modules')) {
181
+ console.log(' [source-walk] FOUND USER SOURCE:', source.file);
182
+ return source;
183
+ }
184
+ }
185
+ current = current.return;
186
+ }
187
+ if (firstSource) {
188
+ console.log(' [source-walk] Falling back to node_modules source:', firstSource.file);
189
+ }
190
+ else {
191
+ console.log(' [source-walk] No source info found in fiber tree');
192
+ }
193
+ return firstSource;
194
+ }
195
+ /**
196
+ * Extracts full context from a fiber node
197
+ * @param fiber - The composite fiber (user's component)
198
+ * @param hostFiber - Optional host fiber (the actual tapped element) to extract source from __rnAve prop
199
+ */
200
+ function extractContext(fiber, hostFiber) {
201
+ console.log('\n🔍 [rn-ave] extractContext called');
202
+ console.log(' fiber type:', fiber.type);
203
+ console.log(' fiber tag:', fiber.tag);
204
+ console.log(' hostFiber provided:', !!hostFiber);
205
+ const hierarchy = (0, traversal_1.getComponentHierarchy)(fiber);
206
+ // Sanitize props for all components in the hierarchy
207
+ const sanitizedHierarchy = hierarchy.map(item => ({
208
+ ...item,
209
+ props: sanitizeProps(item.props)
210
+ }));
211
+ // Try to get source from fiber tree's __rnAve prop first (Babel plugin)
212
+ // Then fall back to composite fiber's source (recursive)
213
+ let source = hostFiber ? getSourceFromFiberTree(hostFiber) : null;
214
+ if (!source) {
215
+ source = (0, source_1.getSourceLocationRecursive)(fiber);
216
+ }
217
+ const props = (0, traversal_1.getCleanProps)(fiber);
218
+ const safeProps = sanitizeProps(props);
219
+ const jsxPreview = generateJSXPreview(fiber);
220
+ // Get box model from host fiber's style (the actual rendered element)
221
+ const styleSource = hostFiber || fiber;
222
+ const boxModel = extractBoxModel(styleSource.memoizedProps?.style);
223
+ console.log(' extracted source:', source);
224
+ console.log(' componentName:', (0, traversal_1.getDisplayName)(fiber));
225
+ console.log(' boxModel:', boxModel);
226
+ return {
227
+ componentName: (0, traversal_1.getDisplayName)(fiber),
228
+ componentHierarchy: sanitizedHierarchy,
229
+ props: safeProps,
230
+ source,
231
+ jsxPreview,
232
+ timestamp: Date.now(),
233
+ boxModel,
234
+ };
235
+ }
@@ -0,0 +1,13 @@
1
+ import type { ExtractedContext } from '../types';
2
+ /**
3
+ * Formats extracted context as markdown for AI agents
4
+ */
5
+ export declare function formatForAgent(context: ExtractedContext): string;
6
+ /**
7
+ * Formats context as compact single-line (for clipboard)
8
+ */
9
+ export declare function formatCompact(context: ExtractedContext): string;
10
+ /**
11
+ * Formats context for terminal/console output
12
+ */
13
+ export declare function formatForTerminal(context: ExtractedContext): string;