@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 +31 -0
- package/dist/appRegistry.d.ts +11 -0
- package/dist/appRegistry.js +52 -0
- package/dist/auto.d.ts +15 -0
- package/dist/auto.js +35 -0
- package/dist/components/InspectorButton.d.ts +7 -0
- package/dist/components/InspectorButton.js +125 -0
- package/dist/components/InspectorOverlay.d.ts +11 -0
- package/dist/components/InspectorOverlay.js +60 -0
- package/dist/context/extractor.d.ts +7 -0
- package/dist/context/extractor.js +235 -0
- package/dist/context/formatter.d.ts +13 -0
- package/dist/context/formatter.js +95 -0
- package/dist/devMenu.d.ts +26 -0
- package/dist/devMenu.js +80 -0
- package/dist/editor.d.ts +20 -0
- package/dist/editor.js +93 -0
- package/dist/fiber/hook.d.ts +18 -0
- package/dist/fiber/hook.js +103 -0
- package/dist/fiber/source.d.ts +21 -0
- package/dist/fiber/source.js +94 -0
- package/dist/fiber/traversal.d.ts +27 -0
- package/dist/fiber/traversal.js +163 -0
- package/dist/gesture/detector.d.ts +17 -0
- package/dist/gesture/detector.js +84 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -0
- package/dist/overlay/highlight.d.ts +14 -0
- package/dist/overlay/highlight.js +177 -0
- package/dist/overlay/tooltip.d.ts +15 -0
- package/dist/overlay/tooltip.js +467 -0
- package/dist/provider.d.ts +10 -0
- package/dist/provider.js +264 -0
- package/dist/types.d.ts +94 -0
- package/dist/types.js +2 -0
- package/package.json +58 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findFiberByNativeTag = findFiberByNativeTag;
|
|
4
|
+
exports.getDisplayName = getDisplayName;
|
|
5
|
+
exports.getComponentHierarchy = getComponentHierarchy;
|
|
6
|
+
exports.getCleanProps = getCleanProps;
|
|
7
|
+
exports.findNearestComposite = findNearestComposite;
|
|
8
|
+
exports.collectAllHostFibers = collectAllHostFibers;
|
|
9
|
+
const source_1 = require("./source");
|
|
10
|
+
// Fiber tag constants (from React)
|
|
11
|
+
const FunctionComponent = 0;
|
|
12
|
+
const ClassComponent = 1;
|
|
13
|
+
const HostRoot = 3;
|
|
14
|
+
const HostComponent = 5;
|
|
15
|
+
const HostText = 6;
|
|
16
|
+
const ForwardRef = 11;
|
|
17
|
+
const MemoComponent = 14;
|
|
18
|
+
const SimpleMemoComponent = 15;
|
|
19
|
+
/**
|
|
20
|
+
* Finds a fiber node by its native tag
|
|
21
|
+
*/
|
|
22
|
+
function findFiberByNativeTag(nativeTag, fiberRoot) {
|
|
23
|
+
let result = null;
|
|
24
|
+
function traverse(fiber) {
|
|
25
|
+
if (!fiber || result)
|
|
26
|
+
return;
|
|
27
|
+
// Check if this fiber's stateNode matches the native tag
|
|
28
|
+
if (fiber.stateNode &&
|
|
29
|
+
typeof fiber.stateNode === 'object' &&
|
|
30
|
+
fiber.stateNode._nativeTag === nativeTag) {
|
|
31
|
+
result = fiber;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
// Traverse children
|
|
35
|
+
traverse(fiber.child);
|
|
36
|
+
// Traverse siblings
|
|
37
|
+
traverse(fiber.sibling);
|
|
38
|
+
}
|
|
39
|
+
traverse(fiberRoot.current);
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Checks if a fiber represents a composite component (function/class)
|
|
44
|
+
*/
|
|
45
|
+
function isCompositeFiber(fiber) {
|
|
46
|
+
return (fiber.tag === FunctionComponent ||
|
|
47
|
+
fiber.tag === ClassComponent ||
|
|
48
|
+
fiber.tag === ForwardRef ||
|
|
49
|
+
fiber.tag === MemoComponent ||
|
|
50
|
+
fiber.tag === SimpleMemoComponent);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Gets the display name of a component from its fiber
|
|
54
|
+
*/
|
|
55
|
+
function getDisplayName(fiber) {
|
|
56
|
+
const { type, tag } = fiber;
|
|
57
|
+
// For host components (View, Text, etc.)
|
|
58
|
+
if (tag === HostComponent) {
|
|
59
|
+
return typeof type === 'string' ? type : 'HostComponent';
|
|
60
|
+
}
|
|
61
|
+
// For text nodes
|
|
62
|
+
if (tag === HostText) {
|
|
63
|
+
return 'Text';
|
|
64
|
+
}
|
|
65
|
+
// For composite components
|
|
66
|
+
if (typeof type === 'function') {
|
|
67
|
+
return type.displayName || type.name || 'Anonymous';
|
|
68
|
+
}
|
|
69
|
+
// For ForwardRef
|
|
70
|
+
if (tag === ForwardRef && type && typeof type === 'object') {
|
|
71
|
+
const wrappedType = type.render;
|
|
72
|
+
if (typeof wrappedType === 'function') {
|
|
73
|
+
return wrappedType.displayName || wrappedType.name || 'ForwardRef';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// For Memo
|
|
77
|
+
if ((tag === MemoComponent || tag === SimpleMemoComponent) && type && typeof type === 'object') {
|
|
78
|
+
const wrappedType = type.type;
|
|
79
|
+
if (typeof wrappedType === 'function') {
|
|
80
|
+
return wrappedType.displayName || wrappedType.name || 'Memo';
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return 'Unknown';
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Gets the component hierarchy from a fiber node up to the root
|
|
87
|
+
*/
|
|
88
|
+
function getComponentHierarchy(fiber) {
|
|
89
|
+
const hierarchy = [];
|
|
90
|
+
let current = fiber;
|
|
91
|
+
while (current) {
|
|
92
|
+
// Only include composite components in hierarchy
|
|
93
|
+
if (isCompositeFiber(current)) {
|
|
94
|
+
hierarchy.push({
|
|
95
|
+
name: getDisplayName(current),
|
|
96
|
+
props: current.memoizedProps || {},
|
|
97
|
+
source: (0, source_1.getSourceLocation)(current),
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
current = current.return;
|
|
101
|
+
}
|
|
102
|
+
return hierarchy;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Gets all props from a fiber, excluding internal ones
|
|
106
|
+
*/
|
|
107
|
+
function getCleanProps(fiber) {
|
|
108
|
+
const props = fiber.memoizedProps || {};
|
|
109
|
+
const cleanProps = {};
|
|
110
|
+
for (const [key, value] of Object.entries(props)) {
|
|
111
|
+
// Skip internal props
|
|
112
|
+
if (key.startsWith('__'))
|
|
113
|
+
continue;
|
|
114
|
+
if (key === 'children')
|
|
115
|
+
continue;
|
|
116
|
+
cleanProps[key] = value;
|
|
117
|
+
}
|
|
118
|
+
return cleanProps;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Finds the nearest composite component ancestor
|
|
122
|
+
* @param fiber - The starting fiber node
|
|
123
|
+
* @param skipNodeModules - If true, skips components from node_modules and returns the nearest user-code component
|
|
124
|
+
*/
|
|
125
|
+
function findNearestComposite(fiber, skipNodeModules = false) {
|
|
126
|
+
let current = fiber;
|
|
127
|
+
let fallback = null; // First composite found (even if from node_modules)
|
|
128
|
+
while (current) {
|
|
129
|
+
if (isCompositeFiber(current)) {
|
|
130
|
+
// Always save the first composite as fallback
|
|
131
|
+
if (!fallback) {
|
|
132
|
+
fallback = current;
|
|
133
|
+
}
|
|
134
|
+
// If not filtering, or if this is user code, return it
|
|
135
|
+
if (!skipNodeModules || !(0, source_1.isFromNodeModules)(current)) {
|
|
136
|
+
return current;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
current = current.return;
|
|
140
|
+
}
|
|
141
|
+
// Fallback to node_modules component if no user code found
|
|
142
|
+
return fallback;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Collects all host component fibers (View, Text, etc.) from the tree
|
|
146
|
+
*/
|
|
147
|
+
function collectAllHostFibers(fiberRoot) {
|
|
148
|
+
const hostFibers = [];
|
|
149
|
+
function traverse(fiber) {
|
|
150
|
+
if (!fiber)
|
|
151
|
+
return;
|
|
152
|
+
// Collect host components (View, Text, Image, etc.)
|
|
153
|
+
if (fiber.tag === HostComponent && fiber.stateNode) {
|
|
154
|
+
hostFibers.push(fiber);
|
|
155
|
+
}
|
|
156
|
+
// Traverse children first (for depth-first order)
|
|
157
|
+
traverse(fiber.child);
|
|
158
|
+
// Then siblings
|
|
159
|
+
traverse(fiber.sibling);
|
|
160
|
+
}
|
|
161
|
+
traverse(fiberRoot.current);
|
|
162
|
+
return hostFibers;
|
|
163
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type PanResponderInstance } from 'react-native';
|
|
2
|
+
import type { GestureConfig } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Creates a PanResponder for long press detection
|
|
5
|
+
* Simpler approach that doesn't require react-native-gesture-handler
|
|
6
|
+
*/
|
|
7
|
+
export declare function createGestureDetector(config: GestureConfig): PanResponderInstance;
|
|
8
|
+
/**
|
|
9
|
+
* Simplified gesture detector that uses callback refs
|
|
10
|
+
* This approach allows us to track which element was pressed
|
|
11
|
+
*/
|
|
12
|
+
export interface SimplePressDetector {
|
|
13
|
+
onPressIn: (nativeTag: number) => void;
|
|
14
|
+
onPressOut: () => void;
|
|
15
|
+
isActive: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare function createSimplePressDetector(onLongPress: (nativeTag: number) => void, duration?: number): SimplePressDetector;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createGestureDetector = createGestureDetector;
|
|
4
|
+
exports.createSimplePressDetector = createSimplePressDetector;
|
|
5
|
+
const react_native_1 = require("react-native");
|
|
6
|
+
/**
|
|
7
|
+
* Creates a PanResponder for long press detection
|
|
8
|
+
* Simpler approach that doesn't require react-native-gesture-handler
|
|
9
|
+
*/
|
|
10
|
+
function createGestureDetector(config) {
|
|
11
|
+
const { longPressDuration = 500, onElementSelected } = config;
|
|
12
|
+
let pressTimer = null;
|
|
13
|
+
let startPosition = { x: 0, y: 0 };
|
|
14
|
+
let hasTriggered = false;
|
|
15
|
+
return react_native_1.PanResponder.create({
|
|
16
|
+
// Intercept all touches
|
|
17
|
+
onStartShouldSetPanResponder: () => true,
|
|
18
|
+
onMoveShouldSetPanResponder: () => false,
|
|
19
|
+
onPanResponderGrant: (evt) => {
|
|
20
|
+
const { pageX, pageY } = evt.nativeEvent;
|
|
21
|
+
startPosition = { x: pageX, y: pageY };
|
|
22
|
+
hasTriggered = false;
|
|
23
|
+
// Start timer for long press
|
|
24
|
+
pressTimer = setTimeout(() => {
|
|
25
|
+
if (!hasTriggered) {
|
|
26
|
+
hasTriggered = true;
|
|
27
|
+
// For now, we'll need to get the native tag from the event
|
|
28
|
+
// This is a limitation - we might need a native module for proper hit testing
|
|
29
|
+
// For MVP, we can use a workaround by adding touch handlers to components
|
|
30
|
+
onElementSelected(0, { x: pageX, y: pageY });
|
|
31
|
+
}
|
|
32
|
+
}, longPressDuration);
|
|
33
|
+
},
|
|
34
|
+
onPanResponderMove: (evt) => {
|
|
35
|
+
const { pageX, pageY } = evt.nativeEvent;
|
|
36
|
+
const distance = Math.sqrt(Math.pow(pageX - startPosition.x, 2) +
|
|
37
|
+
Math.pow(pageY - startPosition.y, 2));
|
|
38
|
+
// Cancel if finger moved too far (more than 10px)
|
|
39
|
+
if (distance > 10 && pressTimer) {
|
|
40
|
+
clearTimeout(pressTimer);
|
|
41
|
+
pressTimer = null;
|
|
42
|
+
hasTriggered = false;
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
onPanResponderRelease: () => {
|
|
46
|
+
if (pressTimer) {
|
|
47
|
+
clearTimeout(pressTimer);
|
|
48
|
+
pressTimer = null;
|
|
49
|
+
}
|
|
50
|
+
hasTriggered = false;
|
|
51
|
+
},
|
|
52
|
+
onPanResponderTerminate: () => {
|
|
53
|
+
if (pressTimer) {
|
|
54
|
+
clearTimeout(pressTimer);
|
|
55
|
+
pressTimer = null;
|
|
56
|
+
}
|
|
57
|
+
hasTriggered = false;
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function createSimplePressDetector(onLongPress, duration = 500) {
|
|
62
|
+
let timer = null;
|
|
63
|
+
let currentTag = null;
|
|
64
|
+
return {
|
|
65
|
+
onPressIn: (nativeTag) => {
|
|
66
|
+
currentTag = nativeTag;
|
|
67
|
+
timer = setTimeout(() => {
|
|
68
|
+
if (currentTag !== null) {
|
|
69
|
+
onLongPress(currentTag);
|
|
70
|
+
}
|
|
71
|
+
}, duration);
|
|
72
|
+
},
|
|
73
|
+
onPressOut: () => {
|
|
74
|
+
if (timer) {
|
|
75
|
+
clearTimeout(timer);
|
|
76
|
+
timer = null;
|
|
77
|
+
}
|
|
78
|
+
currentTag = null;
|
|
79
|
+
},
|
|
80
|
+
get isActive() {
|
|
81
|
+
return timer !== null;
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// rn-ave - React Native Agent Visual Edit
|
|
3
|
+
// Only auto-initialization is supported - import '@rn-ave/core/auto' in your app
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.RNAVEProvider = void 0;
|
|
6
|
+
// Internal provider (used by auto.ts)
|
|
7
|
+
var provider_1 = require("./provider");
|
|
8
|
+
Object.defineProperty(exports, "RNAVEProvider", { enumerable: true, get: function () { return provider_1.RNAVEProvider; } });
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { SelectedElement } from '../types';
|
|
3
|
+
type AgentType = 'claude-code' | 'cursor' | 'opencode';
|
|
4
|
+
interface HighlightOverlayProps {
|
|
5
|
+
selectedElement: SelectedElement | null;
|
|
6
|
+
onDismiss: () => void;
|
|
7
|
+
onSendToAgent?: (agentType: AgentType, prompt?: string) => Promise<{
|
|
8
|
+
success: boolean;
|
|
9
|
+
error?: string;
|
|
10
|
+
}>;
|
|
11
|
+
onOpenInEditor?: () => void;
|
|
12
|
+
}
|
|
13
|
+
export declare function HighlightOverlay({ selectedElement, onDismiss, onSendToAgent, onOpenInEditor, }: HighlightOverlayProps): React.JSX.Element | null;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,177 @@
|
|
|
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.HighlightOverlay = HighlightOverlay;
|
|
37
|
+
const react_1 = __importStar(require("react"));
|
|
38
|
+
const react_native_1 = require("react-native");
|
|
39
|
+
const tooltip_1 = require("./tooltip");
|
|
40
|
+
// Box model colors matching RN inspector
|
|
41
|
+
const COLORS = {
|
|
42
|
+
margin: 'rgba(246, 178, 107, 0.65)', // Coral/orange
|
|
43
|
+
padding: 'rgba(147, 196, 125, 0.65)', // Green
|
|
44
|
+
content: 'rgba(111, 168, 220, 0.65)', // Blue
|
|
45
|
+
};
|
|
46
|
+
// Default edges (all zeros)
|
|
47
|
+
const defaultEdges = () => ({ top: 0, right: 0, bottom: 0, left: 0 });
|
|
48
|
+
/**
|
|
49
|
+
* Box Model Overlay - renders the margin/padding/content visualization
|
|
50
|
+
* directly on the selected element (colors only, no labels)
|
|
51
|
+
*/
|
|
52
|
+
function BoxModelOverlay({ bounds, boxModel }) {
|
|
53
|
+
const margin = boxModel?.margin ?? defaultEdges();
|
|
54
|
+
const padding = boxModel?.padding ?? defaultEdges();
|
|
55
|
+
// Calculate layer dimensions
|
|
56
|
+
// Margin layer extends OUTSIDE the element bounds
|
|
57
|
+
const marginLayer = {
|
|
58
|
+
left: bounds.x - margin.left,
|
|
59
|
+
top: bounds.y - margin.top,
|
|
60
|
+
width: bounds.width + margin.left + margin.right,
|
|
61
|
+
height: bounds.height + margin.top + margin.bottom,
|
|
62
|
+
};
|
|
63
|
+
// Content layer is INSIDE the element, after padding
|
|
64
|
+
const contentLayer = {
|
|
65
|
+
left: bounds.x + padding.left,
|
|
66
|
+
top: bounds.y + padding.top,
|
|
67
|
+
width: Math.max(0, bounds.width - padding.left - padding.right),
|
|
68
|
+
height: Math.max(0, bounds.height - padding.top - padding.bottom),
|
|
69
|
+
};
|
|
70
|
+
// Check if we have any margin or padding to show
|
|
71
|
+
const hasMargin = margin.top > 0 || margin.right > 0 || margin.bottom > 0 || margin.left > 0;
|
|
72
|
+
const hasPadding = padding.top > 0 || padding.right > 0 || padding.bottom > 0 || padding.left > 0;
|
|
73
|
+
return (<>
|
|
74
|
+
{/* MARGIN LAYER - Coral/Orange strips (no labels) */}
|
|
75
|
+
{hasMargin && (<>
|
|
76
|
+
{margin.top > 0 && (<react_native_1.View style={[styles.layer, {
|
|
77
|
+
backgroundColor: COLORS.margin,
|
|
78
|
+
left: marginLayer.left,
|
|
79
|
+
top: marginLayer.top,
|
|
80
|
+
width: marginLayer.width,
|
|
81
|
+
height: margin.top,
|
|
82
|
+
}]} pointerEvents="none"/>)}
|
|
83
|
+
{margin.bottom > 0 && (<react_native_1.View style={[styles.layer, {
|
|
84
|
+
backgroundColor: COLORS.margin,
|
|
85
|
+
left: marginLayer.left,
|
|
86
|
+
top: bounds.y + bounds.height,
|
|
87
|
+
width: marginLayer.width,
|
|
88
|
+
height: margin.bottom,
|
|
89
|
+
}]} pointerEvents="none"/>)}
|
|
90
|
+
{margin.left > 0 && (<react_native_1.View style={[styles.layer, {
|
|
91
|
+
backgroundColor: COLORS.margin,
|
|
92
|
+
left: marginLayer.left,
|
|
93
|
+
top: bounds.y,
|
|
94
|
+
width: margin.left,
|
|
95
|
+
height: bounds.height,
|
|
96
|
+
}]} pointerEvents="none"/>)}
|
|
97
|
+
{margin.right > 0 && (<react_native_1.View style={[styles.layer, {
|
|
98
|
+
backgroundColor: COLORS.margin,
|
|
99
|
+
left: bounds.x + bounds.width,
|
|
100
|
+
top: bounds.y,
|
|
101
|
+
width: margin.right,
|
|
102
|
+
height: bounds.height,
|
|
103
|
+
}]} pointerEvents="none"/>)}
|
|
104
|
+
</>)}
|
|
105
|
+
|
|
106
|
+
{/* PADDING LAYER - Green strips (no labels) */}
|
|
107
|
+
{hasPadding && (<>
|
|
108
|
+
{padding.top > 0 && (<react_native_1.View style={[styles.layer, {
|
|
109
|
+
backgroundColor: COLORS.padding,
|
|
110
|
+
left: bounds.x,
|
|
111
|
+
top: bounds.y,
|
|
112
|
+
width: bounds.width,
|
|
113
|
+
height: padding.top,
|
|
114
|
+
}]} pointerEvents="none"/>)}
|
|
115
|
+
{padding.bottom > 0 && (<react_native_1.View style={[styles.layer, {
|
|
116
|
+
backgroundColor: COLORS.padding,
|
|
117
|
+
left: bounds.x,
|
|
118
|
+
top: bounds.y + bounds.height - padding.bottom,
|
|
119
|
+
width: bounds.width,
|
|
120
|
+
height: padding.bottom,
|
|
121
|
+
}]} pointerEvents="none"/>)}
|
|
122
|
+
{padding.left > 0 && (<react_native_1.View style={[styles.layer, {
|
|
123
|
+
backgroundColor: COLORS.padding,
|
|
124
|
+
left: bounds.x,
|
|
125
|
+
top: bounds.y + padding.top,
|
|
126
|
+
width: padding.left,
|
|
127
|
+
height: Math.max(0, bounds.height - padding.top - padding.bottom),
|
|
128
|
+
}]} pointerEvents="none"/>)}
|
|
129
|
+
{padding.right > 0 && (<react_native_1.View style={[styles.layer, {
|
|
130
|
+
backgroundColor: COLORS.padding,
|
|
131
|
+
left: bounds.x + bounds.width - padding.right,
|
|
132
|
+
top: bounds.y + padding.top,
|
|
133
|
+
width: padding.right,
|
|
134
|
+
height: Math.max(0, bounds.height - padding.top - padding.bottom),
|
|
135
|
+
}]} pointerEvents="none"/>)}
|
|
136
|
+
</>)}
|
|
137
|
+
|
|
138
|
+
{/* CONTENT LAYER - Blue */}
|
|
139
|
+
<react_native_1.View style={[styles.layer, {
|
|
140
|
+
backgroundColor: COLORS.content,
|
|
141
|
+
left: contentLayer.left,
|
|
142
|
+
top: contentLayer.top,
|
|
143
|
+
width: contentLayer.width,
|
|
144
|
+
height: contentLayer.height,
|
|
145
|
+
}]} pointerEvents="none"/>
|
|
146
|
+
</>);
|
|
147
|
+
}
|
|
148
|
+
function HighlightOverlay({ selectedElement, onDismiss, onSendToAgent, onOpenInEditor, }) {
|
|
149
|
+
const [bounds, setBounds] = (0, react_1.useState)(null);
|
|
150
|
+
(0, react_1.useEffect)(() => {
|
|
151
|
+
if (!selectedElement) {
|
|
152
|
+
setBounds(null);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
// Try to measure the element
|
|
156
|
+
const { nativeTag } = selectedElement;
|
|
157
|
+
if (react_native_1.Platform.OS === 'ios' || react_native_1.Platform.OS === 'android') {
|
|
158
|
+
react_native_1.UIManager.measureInWindow(nativeTag, (x, y, width, height) => {
|
|
159
|
+
setBounds({ x, y, width, height });
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}, [selectedElement]);
|
|
163
|
+
if (!selectedElement)
|
|
164
|
+
return null;
|
|
165
|
+
return (<react_native_1.View style={react_native_1.StyleSheet.absoluteFill} pointerEvents="box-none">
|
|
166
|
+
{/* Box Model Visualization - rendered on the element */}
|
|
167
|
+
{bounds && (<BoxModelOverlay bounds={bounds} boxModel={selectedElement.componentInfo.boxModel}/>)}
|
|
168
|
+
|
|
169
|
+
{/* Info tooltip */}
|
|
170
|
+
<tooltip_1.InfoTooltip componentInfo={selectedElement.componentInfo} bounds={bounds} onDismiss={onDismiss} onSendToAgent={onSendToAgent} onOpenInEditor={onOpenInEditor}/>
|
|
171
|
+
</react_native_1.View>);
|
|
172
|
+
}
|
|
173
|
+
const styles = react_native_1.StyleSheet.create({
|
|
174
|
+
layer: {
|
|
175
|
+
position: 'absolute',
|
|
176
|
+
},
|
|
177
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ExtractedContext, Bounds } from '../types';
|
|
3
|
+
type AgentType = 'claude-code' | 'cursor' | 'opencode';
|
|
4
|
+
interface InfoTooltipProps {
|
|
5
|
+
componentInfo: ExtractedContext;
|
|
6
|
+
bounds: Bounds | null;
|
|
7
|
+
onDismiss: () => void;
|
|
8
|
+
onSendToAgent?: (agentType: AgentType, prompt?: string, contextOverride?: any) => Promise<{
|
|
9
|
+
success: boolean;
|
|
10
|
+
error?: string;
|
|
11
|
+
}>;
|
|
12
|
+
onOpenInEditor?: (sourceOverride?: any) => void;
|
|
13
|
+
}
|
|
14
|
+
export declare function InfoTooltip({ componentInfo, bounds, onDismiss, onSendToAgent, onOpenInEditor, }: InfoTooltipProps): React.JSX.Element;
|
|
15
|
+
export {};
|