@zargaryanvh/react-component-inspector 1.0.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/LICENSE +21 -0
- package/README.md +274 -0
- package/dist/InspectionContext.d.ts +35 -0
- package/dist/InspectionContext.js +122 -0
- package/dist/InspectionHighlight.d.ts +6 -0
- package/dist/InspectionHighlight.js +65 -0
- package/dist/InspectionTooltip.d.ts +6 -0
- package/dist/InspectionTooltip.js +232 -0
- package/dist/InspectionWrapper.d.ts +28 -0
- package/dist/InspectionWrapper.js +68 -0
- package/dist/autoInspection.d.ts +14 -0
- package/dist/autoInspection.js +258 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +9 -0
- package/dist/inspection.d.ts +29 -0
- package/dist/inspection.js +140 -0
- package/dist/inspectionInterceptors.d.ts +27 -0
- package/dist/inspectionInterceptors.js +64 -0
- package/dist/useInspectionMetadata.d.ts +32 -0
- package/dist/useInspectionMetadata.js +52 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# React Component Inspector
|
|
2
|
+
|
|
3
|
+
> **A powerful development tool for inspecting React components with AI-friendly metadata extraction. Fully designed by Cursor AI.**
|
|
4
|
+
|
|
5
|
+
## 🎯 What is This?
|
|
6
|
+
|
|
7
|
+
React Component Inspector is a development-only tool that helps you identify, inspect, and extract detailed metadata from React components in your application. It's designed to work seamlessly with AI coding assistants (like Cursor) by providing structured, copyable metadata about any component in your UI.
|
|
8
|
+
|
|
9
|
+
## 🔍 What Problem Does It Solve?
|
|
10
|
+
|
|
11
|
+
### The Challenge
|
|
12
|
+
When working with AI assistants to fix or modify frontend code, you often need to:
|
|
13
|
+
- Identify which component is responsible for a specific UI element
|
|
14
|
+
- Understand the component's props, variants, and usage context
|
|
15
|
+
- Get the exact file path and component structure
|
|
16
|
+
- Extract CSS selectors and element identifiers for precise targeting
|
|
17
|
+
|
|
18
|
+
**Without this tool**, you'd need to:
|
|
19
|
+
- Manually inspect the DOM
|
|
20
|
+
- Search through codebases
|
|
21
|
+
- Guess component names and file locations
|
|
22
|
+
- Manually extract element information
|
|
23
|
+
|
|
24
|
+
### The Solution
|
|
25
|
+
React Component Inspector provides:
|
|
26
|
+
- **One-click component identification** - Just hold CTRL and hover
|
|
27
|
+
- **Rich metadata extraction** - Component name, props, file path, usage context
|
|
28
|
+
- **AI-optimized format** - Copy-paste ready metadata for AI assistants
|
|
29
|
+
- **Zero production overhead** - Completely disabled in production builds
|
|
30
|
+
|
|
31
|
+
## 📊 What Data Does It Provide?
|
|
32
|
+
|
|
33
|
+
When you inspect a component, you get:
|
|
34
|
+
|
|
35
|
+
### Element Identification
|
|
36
|
+
- Element type (HTML tag)
|
|
37
|
+
- Element text/label content
|
|
38
|
+
- Element ID
|
|
39
|
+
- CSS classes
|
|
40
|
+
- CSS selector
|
|
41
|
+
- Position and size
|
|
42
|
+
- Role and accessibility attributes
|
|
43
|
+
|
|
44
|
+
### Component Metadata
|
|
45
|
+
- Component name
|
|
46
|
+
- Component ID (unique instance identifier)
|
|
47
|
+
- Variant (if applicable)
|
|
48
|
+
- Usage path (component hierarchy)
|
|
49
|
+
- Instance index
|
|
50
|
+
- Props signature (key props affecting behavior)
|
|
51
|
+
- Source file path
|
|
52
|
+
|
|
53
|
+
### Example Output
|
|
54
|
+
```
|
|
55
|
+
=== ELEMENT IDENTIFICATION ===
|
|
56
|
+
Element Type: button
|
|
57
|
+
Element Text/Label: "Save Transaction"
|
|
58
|
+
Element ID: save-button
|
|
59
|
+
Element Classes: MuiButton-root, primary-button
|
|
60
|
+
CSS Selector: button#save-button
|
|
61
|
+
Position: (450, 320)
|
|
62
|
+
Size: 120x36px
|
|
63
|
+
|
|
64
|
+
=== COMPONENT METADATA ===
|
|
65
|
+
Component Name: SaveButton
|
|
66
|
+
Component ID: save-button-0
|
|
67
|
+
Variant: primary
|
|
68
|
+
Usage Path: ActivityPage > EditTransactionModal > TransactionForm
|
|
69
|
+
Instance: 0
|
|
70
|
+
Props: variant=primary, disabled=false
|
|
71
|
+
Source File: src/components/buttons/SaveButton.tsx
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## 🚀 How to Use This Data for AI-Powered Frontend Optimization
|
|
75
|
+
|
|
76
|
+
### 1. **Precise Component Targeting**
|
|
77
|
+
Copy the metadata and ask your AI assistant:
|
|
78
|
+
```
|
|
79
|
+
"I need to modify the SaveButton component. Here's the metadata:
|
|
80
|
+
[paste metadata]
|
|
81
|
+
|
|
82
|
+
Change the button color to green and add an icon."
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 2. **Context-Aware Refactoring**
|
|
86
|
+
The usage path tells you exactly where the component is used:
|
|
87
|
+
```
|
|
88
|
+
"Refactor the TransactionCard component used in:
|
|
89
|
+
ActivityPage > TransactionList > TransactionCard
|
|
90
|
+
|
|
91
|
+
Make it accept a new 'priority' prop."
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 3. **CSS Selector Generation**
|
|
95
|
+
Use the CSS selector for automated testing or styling:
|
|
96
|
+
```javascript
|
|
97
|
+
// The metadata provides: button#save-button
|
|
98
|
+
const saveButton = document.querySelector('button#save-button');
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 4. **Component Discovery**
|
|
102
|
+
Find all instances of a component:
|
|
103
|
+
```
|
|
104
|
+
"Find all instances of TransactionCard in the codebase.
|
|
105
|
+
The component is defined in: src/components/transactions/TransactionCard.tsx"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 5. **AI-Powered Debugging**
|
|
109
|
+
Share component metadata with AI to debug issues:
|
|
110
|
+
```
|
|
111
|
+
"This button isn't working. Component metadata:
|
|
112
|
+
[paste metadata]
|
|
113
|
+
|
|
114
|
+
The onClick handler should be in: src/components/buttons/SaveButton.tsx"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## 📦 Installation
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
npm install react-component-inspector
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## 🔧 Setup
|
|
124
|
+
|
|
125
|
+
### 1. Wrap Your App
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
import { InspectionProvider } from 'react-component-inspector';
|
|
129
|
+
import { InspectionTooltip } from 'react-component-inspector';
|
|
130
|
+
import { InspectionHighlight } from 'react-component-inspector';
|
|
131
|
+
import { setupInterceptors } from 'react-component-inspector';
|
|
132
|
+
|
|
133
|
+
function App() {
|
|
134
|
+
// Setup request interceptors (optional - blocks API calls when CTRL is held)
|
|
135
|
+
useEffect(() => {
|
|
136
|
+
setupInterceptors();
|
|
137
|
+
}, []);
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<InspectionProvider>
|
|
141
|
+
<YourApp />
|
|
142
|
+
<InspectionTooltip />
|
|
143
|
+
<InspectionHighlight />
|
|
144
|
+
</InspectionProvider>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### 2. Add Metadata to Components
|
|
150
|
+
|
|
151
|
+
#### Option A: Using the Hook (Recommended)
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
import { useInspectionMetadata } from 'react-component-inspector';
|
|
155
|
+
|
|
156
|
+
function MyButton({ variant, disabled, onClick }) {
|
|
157
|
+
const inspectionProps = useInspectionMetadata({
|
|
158
|
+
componentName: "MyButton",
|
|
159
|
+
variant: variant,
|
|
160
|
+
usagePath: "HomePage > ActionBar",
|
|
161
|
+
props: { variant, disabled },
|
|
162
|
+
sourceFile: "src/components/MyButton.tsx",
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<button {...inspectionProps} onClick={onClick}>
|
|
167
|
+
Click me
|
|
168
|
+
</button>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
#### Option B: Using the Wrapper Component
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
import { InspectionWrapper } from 'react-component-inspector';
|
|
177
|
+
|
|
178
|
+
function MyComponent({ variant, children }) {
|
|
179
|
+
return (
|
|
180
|
+
<InspectionWrapper
|
|
181
|
+
componentName="MyComponent"
|
|
182
|
+
variant={variant}
|
|
183
|
+
usagePath="HomePage > ContentArea"
|
|
184
|
+
props={{ variant }}
|
|
185
|
+
sourceFile="src/components/MyComponent.tsx"
|
|
186
|
+
>
|
|
187
|
+
<div>{children}</div>
|
|
188
|
+
</InspectionWrapper>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
#### Option C: Using Data Attributes (Manual)
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
<div
|
|
197
|
+
data-inspection-name="MyComponent"
|
|
198
|
+
data-inspection-id="my-component-0"
|
|
199
|
+
data-inspection-variant="primary"
|
|
200
|
+
data-inspection-usage-path="HomePage > ContentArea"
|
|
201
|
+
data-inspection-instance="0"
|
|
202
|
+
data-inspection-props="variant=primary"
|
|
203
|
+
data-inspection-file="src/components/MyComponent.tsx"
|
|
204
|
+
>
|
|
205
|
+
Content
|
|
206
|
+
</div>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## 🎮 Usage
|
|
210
|
+
|
|
211
|
+
1. **Activate**: Hold the `CTRL` key (or `Cmd` on Mac)
|
|
212
|
+
2. **Inspect**: Hover over any component with inspection metadata
|
|
213
|
+
3. **View**: A tooltip appears showing component metadata
|
|
214
|
+
4. **Lock**: Press `CTRL+H` to lock the tooltip position
|
|
215
|
+
5. **Copy**: Click the copy icon to copy metadata to clipboard
|
|
216
|
+
6. **Deactivate**: Release `CTRL` to exit inspection mode
|
|
217
|
+
|
|
218
|
+
## 🛡️ Safety Features
|
|
219
|
+
|
|
220
|
+
- **Development Only**: Completely disabled in production (`NODE_ENV !== "development"`)
|
|
221
|
+
- **Request Blocking**: When CTRL is held, all API/Firebase requests are blocked to prevent accidental mutations
|
|
222
|
+
- **Zero Overhead**: No code included in production builds
|
|
223
|
+
- **Non-Intrusive**: Doesn't modify your components or affect their behavior
|
|
224
|
+
|
|
225
|
+
## 📚 Documentation
|
|
226
|
+
|
|
227
|
+
- [Quick Start Guide](./docs/QUICK_START.md)
|
|
228
|
+
- [API Reference](./docs/API.md)
|
|
229
|
+
- [Advanced Usage](./docs/ADVANCED.md)
|
|
230
|
+
- [AI Integration Guide](./docs/AI_INTEGRATION.md)
|
|
231
|
+
|
|
232
|
+
## 🎨 Features
|
|
233
|
+
|
|
234
|
+
- ✅ Visual component highlighting
|
|
235
|
+
- ✅ Rich metadata extraction
|
|
236
|
+
- ✅ Copy-to-clipboard functionality
|
|
237
|
+
- ✅ Automatic component detection
|
|
238
|
+
- ✅ CSS selector generation
|
|
239
|
+
- ✅ Usage path tracking
|
|
240
|
+
- ✅ Instance indexing
|
|
241
|
+
- ✅ Props signature extraction
|
|
242
|
+
- ✅ Request blocking during inspection
|
|
243
|
+
- ✅ Production-safe (zero overhead)
|
|
244
|
+
|
|
245
|
+
## 🤖 Designed by Cursor AI
|
|
246
|
+
|
|
247
|
+
This tool was fully designed and developed using [Cursor](https://cursor.sh), an AI-powered code editor. The entire codebase, architecture, and documentation were created through AI-assisted development, demonstrating the power of AI in building developer tools.
|
|
248
|
+
|
|
249
|
+
## 📄 License
|
|
250
|
+
|
|
251
|
+
MIT
|
|
252
|
+
|
|
253
|
+
## 🤝 Contributing
|
|
254
|
+
|
|
255
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
256
|
+
|
|
257
|
+
## ⚠️ Important Notes
|
|
258
|
+
|
|
259
|
+
- This tool is **development-only** and will not work in production
|
|
260
|
+
- Requires Material-UI (MUI) for the tooltip UI components
|
|
261
|
+
- Works best with TypeScript but supports JavaScript
|
|
262
|
+
- Request interceptors are optional but recommended for safety
|
|
263
|
+
|
|
264
|
+
## 💡 Tips for AI Integration
|
|
265
|
+
|
|
266
|
+
1. **Always copy the full metadata** - It contains all context needed
|
|
267
|
+
2. **Include the usage path** - Helps AI understand component hierarchy
|
|
268
|
+
3. **Share the source file** - Directs AI to the exact location
|
|
269
|
+
4. **Use CSS selectors** - For precise element targeting in AI prompts
|
|
270
|
+
5. **Copy element text** - Helps AI understand component purpose
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
**Made with ❤️ using Cursor AI**
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React, { ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Component inspection metadata
|
|
4
|
+
*/
|
|
5
|
+
export interface ComponentMetadata {
|
|
6
|
+
componentName: string;
|
|
7
|
+
componentId: string;
|
|
8
|
+
variant?: string;
|
|
9
|
+
role?: string;
|
|
10
|
+
usagePath: string;
|
|
11
|
+
instanceIndex: number;
|
|
12
|
+
propsSignature: string;
|
|
13
|
+
sourceFile: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Inspection context state
|
|
17
|
+
*/
|
|
18
|
+
interface InspectionState {
|
|
19
|
+
isInspectionActive: boolean;
|
|
20
|
+
isLocked: boolean;
|
|
21
|
+
hoveredComponent: ComponentMetadata | null;
|
|
22
|
+
hoveredElement: HTMLElement | null;
|
|
23
|
+
setHoveredComponent: (component: ComponentMetadata | null, element: HTMLElement | null) => void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Inspection Provider - Only active in development
|
|
27
|
+
*/
|
|
28
|
+
export declare const InspectionProvider: React.FC<{
|
|
29
|
+
children: ReactNode;
|
|
30
|
+
}>;
|
|
31
|
+
/**
|
|
32
|
+
* Hook to access inspection context
|
|
33
|
+
*/
|
|
34
|
+
export declare const useInspection: () => InspectionState;
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React, { createContext, useContext, useState, useCallback, useEffect } from "react";
|
|
3
|
+
import { setInspectionActive } from "./inspectionInterceptors";
|
|
4
|
+
import { setupAutoInspection } from "./autoInspection";
|
|
5
|
+
const InspectionContext = createContext(undefined);
|
|
6
|
+
/**
|
|
7
|
+
* Inspection Provider - Only active in development
|
|
8
|
+
*/
|
|
9
|
+
export const InspectionProvider = ({ children }) => {
|
|
10
|
+
const [isInspectionActive, setIsInspectionActive] = useState(false);
|
|
11
|
+
const [isLocked, setIsLocked] = useState(false);
|
|
12
|
+
const [hoveredComponent, setHoveredComponentState] = useState(null);
|
|
13
|
+
const [hoveredElement, setHoveredElement] = useState(null);
|
|
14
|
+
// Track CTRL key state and CTRL+H for locking
|
|
15
|
+
React.useEffect(() => {
|
|
16
|
+
if (process.env.NODE_ENV !== "development") {
|
|
17
|
+
return; // Only in development
|
|
18
|
+
}
|
|
19
|
+
const handleKeyDown = (e) => {
|
|
20
|
+
// H key pressed (with or without CTRL) to lock tooltip position while hovering
|
|
21
|
+
if (e.key.toLowerCase() === "h") {
|
|
22
|
+
// Only lock if inspection is active, we have a hovered component, and it's not already locked
|
|
23
|
+
if (isInspectionActive && hoveredComponent && !isLocked) {
|
|
24
|
+
e.preventDefault();
|
|
25
|
+
setIsLocked(true);
|
|
26
|
+
if (process.env.NODE_ENV === "development") {
|
|
27
|
+
console.log("[Inspection] Tooltip locked - position fixed. Press H again or release CTRL to unlock.");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// If already locked and H is pressed again, unlock (toggle behavior)
|
|
31
|
+
else if (isLocked && isInspectionActive) {
|
|
32
|
+
e.preventDefault();
|
|
33
|
+
setIsLocked(false);
|
|
34
|
+
if (process.env.NODE_ENV === "development") {
|
|
35
|
+
console.log("[Inspection] Tooltip unlocked - inspection continues while CTRL is held.");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// CTRL key pressed
|
|
41
|
+
if (e.key === "Control" || e.ctrlKey) {
|
|
42
|
+
setIsInspectionActive(true);
|
|
43
|
+
setInspectionActive(true);
|
|
44
|
+
if (process.env.NODE_ENV === "development") {
|
|
45
|
+
console.log("[Inspection] Activated - Hold CTRL and hover over components. Press H to lock tooltip position.");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const handleKeyUp = (e) => {
|
|
50
|
+
// CTRL key released - unlock and clear
|
|
51
|
+
if (e.key === "Control" || (!e.ctrlKey && e.key !== "h")) {
|
|
52
|
+
setIsInspectionActive(false);
|
|
53
|
+
setIsLocked(false);
|
|
54
|
+
setInspectionActive(false);
|
|
55
|
+
setHoveredComponentState(null);
|
|
56
|
+
setHoveredElement(null);
|
|
57
|
+
if (process.env.NODE_ENV === "development") {
|
|
58
|
+
console.log("[Inspection] Deactivated");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Note: H key release doesn't unlock anymore - use H key press to toggle lock state
|
|
62
|
+
};
|
|
63
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
64
|
+
window.addEventListener("keyup", handleKeyUp);
|
|
65
|
+
return () => {
|
|
66
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
67
|
+
window.removeEventListener("keyup", handleKeyUp);
|
|
68
|
+
};
|
|
69
|
+
}, [isInspectionActive, hoveredComponent, isLocked]);
|
|
70
|
+
const setHoveredComponent = useCallback((component, element) => {
|
|
71
|
+
if (process.env.NODE_ENV !== "development") {
|
|
72
|
+
return; // Only in development
|
|
73
|
+
}
|
|
74
|
+
// Validate element is still in DOM before setting
|
|
75
|
+
if (element && !document.body.contains(element)) {
|
|
76
|
+
setHoveredComponentState(null);
|
|
77
|
+
setHoveredElement(null);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
setHoveredComponentState(component);
|
|
81
|
+
setHoveredElement(element);
|
|
82
|
+
}, []);
|
|
83
|
+
// Setup automatic inspection detection via data attributes
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
if (process.env.NODE_ENV !== "development") {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const cleanup = setupAutoInspection(setHoveredComponent, isInspectionActive, isLocked);
|
|
89
|
+
return cleanup;
|
|
90
|
+
}, [isInspectionActive, isLocked, setHoveredComponent]);
|
|
91
|
+
// Don't render provider in production
|
|
92
|
+
if (process.env.NODE_ENV !== "development") {
|
|
93
|
+
return _jsx(_Fragment, { children: children });
|
|
94
|
+
}
|
|
95
|
+
return (_jsx(InspectionContext.Provider, { value: {
|
|
96
|
+
isInspectionActive,
|
|
97
|
+
isLocked,
|
|
98
|
+
hoveredComponent,
|
|
99
|
+
hoveredElement,
|
|
100
|
+
setHoveredComponent,
|
|
101
|
+
}, children: children }));
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Hook to access inspection context
|
|
105
|
+
*/
|
|
106
|
+
export const useInspection = () => {
|
|
107
|
+
const context = useContext(InspectionContext);
|
|
108
|
+
if (process.env.NODE_ENV !== "development") {
|
|
109
|
+
// Return dummy state in production
|
|
110
|
+
return {
|
|
111
|
+
isInspectionActive: false,
|
|
112
|
+
isLocked: false,
|
|
113
|
+
hoveredComponent: null,
|
|
114
|
+
hoveredElement: null,
|
|
115
|
+
setHoveredComponent: () => { },
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
if (!context) {
|
|
119
|
+
throw new Error("useInspection must be used within InspectionProvider");
|
|
120
|
+
}
|
|
121
|
+
return context;
|
|
122
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { Box } from "@mui/material";
|
|
4
|
+
import { useInspection } from "./InspectionContext";
|
|
5
|
+
/**
|
|
6
|
+
* Highlight overlay that shows the boundary of the hovered component
|
|
7
|
+
* Only visible when CTRL is held and a component is hovered
|
|
8
|
+
*/
|
|
9
|
+
export const InspectionHighlight = () => {
|
|
10
|
+
const { isInspectionActive, hoveredElement } = useInspection();
|
|
11
|
+
const [highlightStyle, setHighlightStyle] = useState(null);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
// Show highlight for any element when CTRL is held, even without metadata
|
|
14
|
+
if (!isInspectionActive) {
|
|
15
|
+
setHighlightStyle(null);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
// If no hoveredElement but inspection is active, don't show highlight
|
|
19
|
+
if (!hoveredElement) {
|
|
20
|
+
setHighlightStyle(null);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const updateHighlight = () => {
|
|
24
|
+
// Check if element is still in the DOM
|
|
25
|
+
if (!document.body.contains(hoveredElement)) {
|
|
26
|
+
setHighlightStyle(null);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const rect = hoveredElement.getBoundingClientRect();
|
|
31
|
+
setHighlightStyle({
|
|
32
|
+
position: "fixed",
|
|
33
|
+
left: `${rect.left + window.scrollX}px`,
|
|
34
|
+
top: `${rect.top + window.scrollY}px`,
|
|
35
|
+
width: `${rect.width}px`,
|
|
36
|
+
height: `${rect.height}px`,
|
|
37
|
+
pointerEvents: "none",
|
|
38
|
+
zIndex: 999998,
|
|
39
|
+
border: "2px solid #2196f3",
|
|
40
|
+
backgroundColor: "rgba(33, 150, 243, 0.1)",
|
|
41
|
+
boxShadow: "0 0 0 1px rgba(33, 150, 243, 0.3), 0 0 8px rgba(33, 150, 243, 0.2)",
|
|
42
|
+
borderRadius: "2px",
|
|
43
|
+
transition: "all 0.1s ease-out",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
// Element might have been removed, clear highlight
|
|
48
|
+
setHighlightStyle(null);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
updateHighlight();
|
|
52
|
+
// Update on scroll/resize
|
|
53
|
+
const handleUpdate = () => updateHighlight();
|
|
54
|
+
window.addEventListener("scroll", handleUpdate, true);
|
|
55
|
+
window.addEventListener("resize", handleUpdate);
|
|
56
|
+
return () => {
|
|
57
|
+
window.removeEventListener("scroll", handleUpdate, true);
|
|
58
|
+
window.removeEventListener("resize", handleUpdate);
|
|
59
|
+
};
|
|
60
|
+
}, [isInspectionActive, hoveredElement]);
|
|
61
|
+
if (!isInspectionActive || !highlightStyle) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return _jsx(Box, { sx: highlightStyle });
|
|
65
|
+
};
|