@promakeai/inspector 0.1.2 → 0.2.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 +165 -0
- package/dist/hook.d.ts.map +1 -1
- package/dist/hook.js +24 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +541 -33
- package/dist/types.d.ts +29 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +20 -4
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# @promakeai/inspector
|
|
2
2
|
|
|
3
3
|
[](https://github.com/promakeai/inspector/actions/workflows/build.yml)
|
|
4
|
+
[](https://github.com/promakeai/inspector/actions/workflows/test.yml)
|
|
4
5
|
[](https://www.npmjs.com/package/@promakeai/inspector)
|
|
5
6
|
[](https://opensource.org/licenses/MIT)
|
|
6
7
|
|
|
@@ -24,7 +25,12 @@ Visual element inspector for React apps in iframe with AI prompt support. Perfec
|
|
|
24
25
|
- 🔍 **Text Node Detection** - Automatically detect if selected element is a text node
|
|
25
26
|
- 🖼️ **Image Node Detection** - Automatically detect if selected element is an image
|
|
26
27
|
- 🏷️ **Promake Badge** - Optional "Built by Promake" badge with smooth animations
|
|
28
|
+
- 🔍 **Element Highlighting** - Revisit and highlight previously inspected elements
|
|
29
|
+
- 🆔 **Persistent Element IDs** - Structural signature-based IDs that survive page reloads
|
|
30
|
+
- 🔄 **Reload-Safe Highlighting** - Same element gets same ID even after refresh
|
|
31
|
+
- 🎯 **Smart Fallback** - Position-based matching when DOM structure changes
|
|
27
32
|
- 📦 **TypeScript** - Full type safety
|
|
33
|
+
- ✅ **Well Tested** - 42 unit tests + 35 e2e tests with 90%+ coverage
|
|
28
34
|
|
|
29
35
|
## Installation
|
|
30
36
|
|
|
@@ -211,6 +217,8 @@ function App() {
|
|
|
211
217
|
- **showContentInput**: `(show: boolean, updateImmediately?: boolean) => void` - Show or hide text content input dynamically. If `updateImmediately` is true, DOM is updated immediately on submit (useful for slow backend responses)
|
|
212
218
|
- **showImageInput**: `(show: boolean, updateImmediately?: boolean) => void` - Show or hide image input dynamically. If `updateImmediately` is true, DOM is updated immediately on submit (useful for slow backend responses)
|
|
213
219
|
- **setBadgeVisible**: `(visible: boolean) => void` - Show or hide "Built by Promake" badge in bottom-right corner
|
|
220
|
+
- **highlightElement**: `(identifier: string | ElementIdentifier, options?: HighlightOptions) => void` - Highlight an element in the iframe by ID or selector
|
|
221
|
+
- **getElementByInspectorId**: `(inspectorId: string) => void` - Request element info by unique inspector ID
|
|
214
222
|
|
|
215
223
|
## Types
|
|
216
224
|
|
|
@@ -227,9 +235,115 @@ interface SelectedElementData {
|
|
|
227
235
|
textContent?: string; // Text content of the element (if text node)
|
|
228
236
|
isImageNode?: boolean; // Whether the element is an image node
|
|
229
237
|
imageUrl?: string; // Image URL (if image node)
|
|
238
|
+
inspectorId?: string; // Unique inspector ID for element tracking
|
|
239
|
+
selector?: string; // CSS selector for element identification
|
|
230
240
|
}
|
|
231
241
|
```
|
|
232
242
|
|
|
243
|
+
### `ElementIdentifier`
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
interface ElementIdentifier {
|
|
247
|
+
inspectorId: string; // Unique ID (primary)
|
|
248
|
+
selector: string; // CSS selector (fallback)
|
|
249
|
+
position?: {
|
|
250
|
+
// Position (for disambiguation)
|
|
251
|
+
top: number;
|
|
252
|
+
left: number;
|
|
253
|
+
};
|
|
254
|
+
component?: {
|
|
255
|
+
// Component info (metadata)
|
|
256
|
+
name: string;
|
|
257
|
+
fileName: string;
|
|
258
|
+
lineNumber?: number;
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### `HighlightOptions`
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
interface HighlightOptions {
|
|
267
|
+
duration?: number; // Highlight duration in ms (default: 3000)
|
|
268
|
+
scrollIntoView?: boolean; // Scroll to element (default: true)
|
|
269
|
+
color?: string; // Highlight color (default: '#4417db')
|
|
270
|
+
animation?: "pulse" | "fade" | "none"; // Animation type (default: 'pulse')
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Element Identification & Persistence
|
|
275
|
+
|
|
276
|
+
Inspector uses a **structural signature-based ID system** to ensure the same element gets the same ID across page reloads, making it perfect for features like chat history highlighting.
|
|
277
|
+
|
|
278
|
+
### How Persistent IDs Work
|
|
279
|
+
|
|
280
|
+
#### ID Format
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
pi-{hash}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Example: `pi-1f2a3b4c`
|
|
287
|
+
|
|
288
|
+
#### ID Generation
|
|
289
|
+
|
|
290
|
+
IDs are generated based on element's structural properties:
|
|
291
|
+
|
|
292
|
+
1. **XPath** - DOM tree position (primary identifier)
|
|
293
|
+
2. **Static Identifiers** - `id`, `data-id`, `data-testid`, `data-component`, `data-cy`
|
|
294
|
+
3. **Semantic Properties** - `tag`, `role`, `type`, `name`, `href`, `src`
|
|
295
|
+
4. **Structural Context** - sibling index, parent tag (if no static ID)
|
|
296
|
+
|
|
297
|
+
**Excluded for Stability:**
|
|
298
|
+
|
|
299
|
+
- ❌ Class names (frequently change)
|
|
300
|
+
- ❌ Text content (dynamic)
|
|
301
|
+
- ❌ Style attributes (dynamic)
|
|
302
|
+
|
|
303
|
+
#### Consistency Guarantees
|
|
304
|
+
|
|
305
|
+
✅ **Same element → Same ID** (across page reloads)
|
|
306
|
+
✅ **Class changes → ID unchanged**
|
|
307
|
+
✅ **Text changes → ID unchanged**
|
|
308
|
+
✅ **Style changes → ID unchanged**
|
|
309
|
+
❌ **DOM position changes → Different ID** (new structure = new ID)
|
|
310
|
+
|
|
311
|
+
#### Best Practices for Maximum Consistency
|
|
312
|
+
|
|
313
|
+
To ensure the most reliable element identification:
|
|
314
|
+
|
|
315
|
+
```html
|
|
316
|
+
<!-- ✅ Best: Use data-id for elements you want to reliably track -->
|
|
317
|
+
<div data-id="hero-section">...</div>
|
|
318
|
+
<button data-id="cta-button">...</button>
|
|
319
|
+
|
|
320
|
+
<!-- ✅ Good: Use element.id -->
|
|
321
|
+
<div id="main-content">...</div>
|
|
322
|
+
|
|
323
|
+
<!-- ✅ Good: Use data-testid -->
|
|
324
|
+
<button data-testid="submit-form">...</button>
|
|
325
|
+
|
|
326
|
+
<!-- ⚠️ Okay: Relies on DOM position (changes if DOM structure changes) -->
|
|
327
|
+
<div class="card">...</div>
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
#### Fallback System
|
|
331
|
+
|
|
332
|
+
If an element's ID changes (e.g., DOM restructure), the system uses smart fallbacks:
|
|
333
|
+
|
|
334
|
+
1. **Primary**: Try to find by `inspectorId`
|
|
335
|
+
2. **Fallback 1**: Use CSS selector
|
|
336
|
+
3. **Fallback 2**: Use position matching (if multiple matches)
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
// Full identifier with fallback support
|
|
340
|
+
highlightElement({
|
|
341
|
+
inspectorId: "pi-1f2a3b4c", // Primary
|
|
342
|
+
selector: "button.cta-primary", // Fallback 1
|
|
343
|
+
position: { top: 500, left: 100 }, // Fallback 2
|
|
344
|
+
});
|
|
345
|
+
```
|
|
346
|
+
|
|
233
347
|
### `ComponentInfo`
|
|
234
348
|
|
|
235
349
|
```typescript
|
|
@@ -556,6 +670,57 @@ Perfect for integrating with error tracking services like Sentry, LogRocket, or
|
|
|
556
670
|
|
|
557
671
|
## Examples
|
|
558
672
|
|
|
673
|
+
### Element Highlighting from Chat History
|
|
674
|
+
|
|
675
|
+
```typescript
|
|
676
|
+
const { highlightElement, getElementByInspectorId } = useInspector(iframeRef, {
|
|
677
|
+
onElementSelected: (data) => {
|
|
678
|
+
// Store inspectorId in your chat message
|
|
679
|
+
const message = {
|
|
680
|
+
text: `Selected ${data.tagName} element`,
|
|
681
|
+
inspectorId: data.inspectorId,
|
|
682
|
+
selector: data.selector,
|
|
683
|
+
position: data.position,
|
|
684
|
+
};
|
|
685
|
+
saveToChatHistory(message);
|
|
686
|
+
},
|
|
687
|
+
onElementInfoReceived: (data) => {
|
|
688
|
+
if (data.found) {
|
|
689
|
+
console.log("Element found:", data.element);
|
|
690
|
+
} else {
|
|
691
|
+
console.log("Element not found:", data.error);
|
|
692
|
+
}
|
|
693
|
+
},
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
// Later: Click on a chat message to re-highlight the element
|
|
697
|
+
function handleChatMessageClick(message) {
|
|
698
|
+
// Highlight with full identifier (with fallback support)
|
|
699
|
+
highlightElement(
|
|
700
|
+
{
|
|
701
|
+
inspectorId: message.inspectorId,
|
|
702
|
+
selector: message.selector,
|
|
703
|
+
position: message.position,
|
|
704
|
+
},
|
|
705
|
+
{
|
|
706
|
+
duration: 4000,
|
|
707
|
+
color: "#10b981",
|
|
708
|
+
animation: "pulse",
|
|
709
|
+
}
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// Or simply by ID
|
|
714
|
+
function quickHighlight(inspectorId) {
|
|
715
|
+
highlightElement(inspectorId);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// Request element info
|
|
719
|
+
function checkElementStatus(inspectorId) {
|
|
720
|
+
getElementByInspectorId(inspectorId);
|
|
721
|
+
}
|
|
722
|
+
```
|
|
723
|
+
|
|
559
724
|
### Dynamic Content Input Based on AI Response
|
|
560
725
|
|
|
561
726
|
```typescript
|
package/dist/hook.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../src/hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6GG;AAEH,OAAO,EAAoC,SAAS,EAAE,MAAM,OAAO,CAAC;AACpE,OAAO,KAAK,EACV,kBAAkB,EAClB,eAAe,EACf,cAAc,EACd,kBAAkB,
|
|
1
|
+
{"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../src/hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6GG;AAEH,OAAO,EAAoC,SAAS,EAAE,MAAM,OAAO,CAAC;AACpE,OAAO,KAAK,EACV,kBAAkB,EAClB,eAAe,EACf,cAAc,EACd,kBAAkB,EAUnB,MAAM,SAAS,CAAC;AAmEjB,wBAAgB,YAAY,CAC1B,SAAS,EAAE,SAAS,CAAC,iBAAiB,CAAC,EACvC,SAAS,CAAC,EAAE,kBAAkB,EAC9B,MAAM,CAAC,EAAE,eAAe,EACxB,KAAK,CAAC,EAAE,cAAc,GACrB,kBAAkB,CA6NpB;AAGD,mBAAmB,SAAS,CAAC"}
|
package/dist/hook.js
CHANGED
|
@@ -182,6 +182,25 @@ export function useInspector(iframeRef, callbacks, labels, theme) {
|
|
|
182
182
|
badgeText: labels?.badgeText,
|
|
183
183
|
});
|
|
184
184
|
}, [sendMessage, labels]);
|
|
185
|
+
/**
|
|
186
|
+
* Highlight an element in the iframe
|
|
187
|
+
*/
|
|
188
|
+
const highlightElement = useCallback((identifier, options) => {
|
|
189
|
+
sendMessage({
|
|
190
|
+
type: "HIGHLIGHT_ELEMENT",
|
|
191
|
+
identifier: identifier,
|
|
192
|
+
options: options,
|
|
193
|
+
});
|
|
194
|
+
}, [sendMessage]);
|
|
195
|
+
/**
|
|
196
|
+
* Request element info by inspector ID
|
|
197
|
+
*/
|
|
198
|
+
const getElementByInspectorId = useCallback((inspectorId) => {
|
|
199
|
+
sendMessage({
|
|
200
|
+
type: "GET_ELEMENT_BY_ID",
|
|
201
|
+
inspectorId: inspectorId,
|
|
202
|
+
});
|
|
203
|
+
}, [sendMessage]);
|
|
185
204
|
/**
|
|
186
205
|
* Listen for messages from iframe
|
|
187
206
|
*/
|
|
@@ -227,6 +246,9 @@ export function useInspector(iframeRef, callbacks, labels, theme) {
|
|
|
227
246
|
case "INSPECTOR_ERROR":
|
|
228
247
|
callbacks?.onError?.(messageData.data);
|
|
229
248
|
break;
|
|
249
|
+
case "ELEMENT_INFO_RESPONSE":
|
|
250
|
+
callbacks?.onElementInfoReceived?.(messageData.data);
|
|
251
|
+
break;
|
|
230
252
|
default:
|
|
231
253
|
// Unknown message type - ignore
|
|
232
254
|
break;
|
|
@@ -260,5 +282,7 @@ export function useInspector(iframeRef, callbacks, labels, theme) {
|
|
|
260
282
|
showContentInput,
|
|
261
283
|
showImageInput,
|
|
262
284
|
setBadgeVisible,
|
|
285
|
+
highlightElement,
|
|
286
|
+
getElementByInspectorId,
|
|
263
287
|
};
|
|
264
288
|
}
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAE9B,wBAAgB,eAAe,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAE9B,wBAAgB,eAAe,IAAI,MAAM,CAwvDxC"}
|
package/dist/plugin.js
CHANGED
|
@@ -54,6 +54,347 @@ export function inspectorPlugin() {
|
|
|
54
54
|
},
|
|
55
55
|
children: `
|
|
56
56
|
(function() {
|
|
57
|
+
// ElementTracker class - inline implementation
|
|
58
|
+
class ElementTracker {
|
|
59
|
+
constructor() {
|
|
60
|
+
this.ID_ATTRIBUTE = 'data-inspector-id';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
ensureElementId(element) {
|
|
64
|
+
const existingId = element.getAttribute(this.ID_ATTRIBUTE);
|
|
65
|
+
if (existingId) return existingId;
|
|
66
|
+
|
|
67
|
+
const persistentId = this.generatePersistentId(element);
|
|
68
|
+
element.setAttribute(this.ID_ATTRIBUTE, persistentId);
|
|
69
|
+
return persistentId;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
generatePersistentId(element) {
|
|
73
|
+
const hasUniqueId = element.id || element.getAttribute('data-id');
|
|
74
|
+
|
|
75
|
+
const signatureParts = [];
|
|
76
|
+
|
|
77
|
+
signatureParts.push(\`xpath:\${this.getXPath(element)}\`);
|
|
78
|
+
|
|
79
|
+
if (element.id) signatureParts.push(\`id:\${element.id}\`);
|
|
80
|
+
|
|
81
|
+
const dataId = element.getAttribute('data-id');
|
|
82
|
+
if (dataId) signatureParts.push(\`data-id:\${dataId}\`);
|
|
83
|
+
|
|
84
|
+
const dataTestId = element.getAttribute('data-testid');
|
|
85
|
+
if (dataTestId) signatureParts.push(\`data-testid:\${dataTestId}\`);
|
|
86
|
+
|
|
87
|
+
const dataComponent = element.getAttribute('data-component');
|
|
88
|
+
if (dataComponent) signatureParts.push(\`data-component:\${dataComponent}\`);
|
|
89
|
+
|
|
90
|
+
const dataCy = element.getAttribute('data-cy');
|
|
91
|
+
if (dataCy) signatureParts.push(\`data-cy:\${dataCy}\`);
|
|
92
|
+
|
|
93
|
+
signatureParts.push(\`tag:\${element.tagName.toLowerCase()}\`);
|
|
94
|
+
|
|
95
|
+
const role = element.getAttribute('role');
|
|
96
|
+
if (role) signatureParts.push(\`role:\${role}\`);
|
|
97
|
+
|
|
98
|
+
const type = element.getAttribute('type');
|
|
99
|
+
if (type) signatureParts.push(\`type:\${type}\`);
|
|
100
|
+
|
|
101
|
+
const name = element.getAttribute('name');
|
|
102
|
+
if (name) signatureParts.push(\`name:\${name}\`);
|
|
103
|
+
|
|
104
|
+
const href = element.getAttribute('href');
|
|
105
|
+
if (href) signatureParts.push(\`href:\${href}\`);
|
|
106
|
+
|
|
107
|
+
const src = element.getAttribute('src');
|
|
108
|
+
if (src) signatureParts.push(\`src:\${src}\`);
|
|
109
|
+
|
|
110
|
+
if (!hasUniqueId) {
|
|
111
|
+
signatureParts.push(\`sibling:\${this.getTypedSiblingIndex(element)}\`);
|
|
112
|
+
const parentTag = element.parentElement?.tagName.toLowerCase();
|
|
113
|
+
if (parentTag) signatureParts.push(\`parent:\${parentTag}\`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const signature = signatureParts.join('|');
|
|
117
|
+
const hash = this.createStableHash(signature);
|
|
118
|
+
return \`pi-\${hash}\`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
getXPath(element) {
|
|
122
|
+
if (element.id) {
|
|
123
|
+
return \`//*[@id="\${element.id}"]\`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const dataId = element.getAttribute('data-id');
|
|
127
|
+
if (dataId) {
|
|
128
|
+
return \`//*[@data-id="\${dataId}"]\`;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const parts = [];
|
|
132
|
+
let current = element;
|
|
133
|
+
|
|
134
|
+
while (current && current.nodeType === Node.ELEMENT_NODE) {
|
|
135
|
+
const tagName = current.tagName.toLowerCase();
|
|
136
|
+
const index = this.getTypedSiblingIndex(current);
|
|
137
|
+
parts.unshift(\`\${tagName}[\${index}]\`);
|
|
138
|
+
current = current.parentElement;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return '/' + parts.join('/');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
getTypedSiblingIndex(element) {
|
|
145
|
+
let index = 1;
|
|
146
|
+
let sibling = element.previousElementSibling;
|
|
147
|
+
|
|
148
|
+
while (sibling) {
|
|
149
|
+
if (sibling.tagName === element.tagName) {
|
|
150
|
+
index++;
|
|
151
|
+
}
|
|
152
|
+
sibling = sibling.previousElementSibling;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return index;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
createStableHash(str) {
|
|
159
|
+
let hash = 5381;
|
|
160
|
+
for (let i = 0; i < str.length; i++) {
|
|
161
|
+
const char = str.charCodeAt(i);
|
|
162
|
+
hash = ((hash << 5) + hash) + char;
|
|
163
|
+
hash = hash | 0;
|
|
164
|
+
}
|
|
165
|
+
return (hash >>> 0).toString(36);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
findElementById(doc, id) {
|
|
169
|
+
return doc.querySelector(\`[\${this.ID_ATTRIBUTE}="\${id}"]\`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
findElementWithFallback(doc, identifier) {
|
|
173
|
+
const byId = this.findElementById(doc, identifier.inspectorId);
|
|
174
|
+
if (byId) return byId;
|
|
175
|
+
|
|
176
|
+
if (identifier.selector && identifier.position) {
|
|
177
|
+
return this.findByPositionMatch(doc, identifier.selector, identifier.position);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (identifier.selector) {
|
|
181
|
+
return doc.querySelector(identifier.selector);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
findByPositionMatch(doc, selector, expectedPosition) {
|
|
188
|
+
const elements = Array.from(doc.querySelectorAll(selector));
|
|
189
|
+
|
|
190
|
+
if (elements.length === 0) return null;
|
|
191
|
+
if (elements.length === 1) return elements[0];
|
|
192
|
+
|
|
193
|
+
return elements.reduce((closest, element) => {
|
|
194
|
+
const rect = element.getBoundingClientRect();
|
|
195
|
+
const distance = Math.sqrt(
|
|
196
|
+
Math.pow(rect.top - expectedPosition.top, 2) +
|
|
197
|
+
Math.pow(rect.left - expectedPosition.left, 2)
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
const closestRect = closest.getBoundingClientRect();
|
|
201
|
+
const closestDistance = Math.sqrt(
|
|
202
|
+
Math.pow(closestRect.top - expectedPosition.top, 2) +
|
|
203
|
+
Math.pow(closestRect.left - expectedPosition.left, 2)
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
return distance < closestDistance ? element : closest;
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ElementHighlighter class - inline implementation
|
|
212
|
+
class ElementHighlighter {
|
|
213
|
+
constructor() {
|
|
214
|
+
this.activeOverlay = null;
|
|
215
|
+
this.scrollListener = null;
|
|
216
|
+
this.resizeListener = null;
|
|
217
|
+
this.cleanupTimeout = null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
highlight(element, options = {}) {
|
|
221
|
+
this.clearHighlight();
|
|
222
|
+
|
|
223
|
+
const duration = options.duration || 3000;
|
|
224
|
+
const scrollIntoView = options.scrollIntoView !== false;
|
|
225
|
+
const color = options.color || '#4417db';
|
|
226
|
+
const animation = options.animation || 'pulse';
|
|
227
|
+
|
|
228
|
+
if (scrollIntoView) {
|
|
229
|
+
element.scrollIntoView({
|
|
230
|
+
behavior: 'smooth',
|
|
231
|
+
block: 'center',
|
|
232
|
+
inline: 'center'
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const rect = element.getBoundingClientRect();
|
|
237
|
+
const overlay = document.createElement('div');
|
|
238
|
+
overlay.id = 'inspector-highlight-overlay';
|
|
239
|
+
|
|
240
|
+
overlay.style.cssText = \`
|
|
241
|
+
position: fixed;
|
|
242
|
+
top: \${rect.top}px;
|
|
243
|
+
left: \${rect.left}px;
|
|
244
|
+
width: \${rect.width}px;
|
|
245
|
+
height: \${rect.height}px;
|
|
246
|
+
border: 3px solid \${color};
|
|
247
|
+
background: \${color}22;
|
|
248
|
+
border-radius: 8px;
|
|
249
|
+
pointer-events: none;
|
|
250
|
+
z-index: 999999;
|
|
251
|
+
box-shadow: 0 0 20px \${color}66;
|
|
252
|
+
\`;
|
|
253
|
+
|
|
254
|
+
if (animation === 'pulse') {
|
|
255
|
+
overlay.style.animation = 'inspectorPulse 0.6s ease-in-out infinite';
|
|
256
|
+
} else if (animation === 'fade') {
|
|
257
|
+
overlay.style.animation = 'inspectorFade 0.5s ease-in-out';
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
this.injectStyles();
|
|
261
|
+
|
|
262
|
+
document.body.appendChild(overlay);
|
|
263
|
+
this.activeOverlay = overlay;
|
|
264
|
+
|
|
265
|
+
const updatePosition = () => {
|
|
266
|
+
const newRect = element.getBoundingClientRect();
|
|
267
|
+
overlay.style.top = \`\${newRect.top}px\`;
|
|
268
|
+
overlay.style.left = \`\${newRect.left}px\`;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
this.scrollListener = updatePosition;
|
|
272
|
+
this.resizeListener = updatePosition;
|
|
273
|
+
|
|
274
|
+
window.addEventListener('scroll', this.scrollListener, true);
|
|
275
|
+
window.addEventListener('resize', this.resizeListener);
|
|
276
|
+
|
|
277
|
+
this.cleanupTimeout = setTimeout(() => {
|
|
278
|
+
this.clearHighlight();
|
|
279
|
+
}, duration);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
clearHighlight() {
|
|
283
|
+
if (this.activeOverlay) {
|
|
284
|
+
this.activeOverlay.remove();
|
|
285
|
+
this.activeOverlay = null;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (this.scrollListener) {
|
|
289
|
+
window.removeEventListener('scroll', this.scrollListener, true);
|
|
290
|
+
this.scrollListener = null;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (this.resizeListener) {
|
|
294
|
+
window.removeEventListener('resize', this.resizeListener);
|
|
295
|
+
this.resizeListener = null;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (this.cleanupTimeout !== null) {
|
|
299
|
+
clearTimeout(this.cleanupTimeout);
|
|
300
|
+
this.cleanupTimeout = null;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
injectStyles() {
|
|
305
|
+
const styleId = 'inspector-highlight-styles';
|
|
306
|
+
|
|
307
|
+
if (document.getElementById(styleId)) return;
|
|
308
|
+
|
|
309
|
+
const style = document.createElement('style');
|
|
310
|
+
style.id = styleId;
|
|
311
|
+
style.textContent = \`
|
|
312
|
+
@keyframes inspectorPulse {
|
|
313
|
+
0%, 100% { opacity: 0.7; transform: scale(1); }
|
|
314
|
+
50% { opacity: 1; transform: scale(1.005); }
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
@keyframes inspectorFade {
|
|
318
|
+
0% { opacity: 0; transform: scale(0.98); }
|
|
319
|
+
100% { opacity: 0.7; transform: scale(1); }
|
|
320
|
+
}
|
|
321
|
+
\`;
|
|
322
|
+
|
|
323
|
+
document.head.appendChild(style);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Initialize tracker and highlighter
|
|
328
|
+
const elementTracker = new ElementTracker();
|
|
329
|
+
const elementHighlighter = new ElementHighlighter();
|
|
330
|
+
|
|
331
|
+
// Assign IDs to all elements on page load
|
|
332
|
+
function assignIdsToAllElements() {
|
|
333
|
+
// Wait for DOM to be ready
|
|
334
|
+
if (document.readyState === 'loading') {
|
|
335
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
336
|
+
assignIdsToAllElements();
|
|
337
|
+
});
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Get all elements in the document
|
|
342
|
+
const allElements = document.querySelectorAll('body *');
|
|
343
|
+
allElements.forEach(element => {
|
|
344
|
+
// Skip inspector's own elements
|
|
345
|
+
if (element.hasAttribute('data-inspector-ignore') ||
|
|
346
|
+
element.closest('[data-inspector-ignore]')) {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
elementTracker.ensureElementId(element);
|
|
350
|
+
});
|
|
351
|
+
console.log(\`🆔 Inspector: Assigned IDs to \${allElements.length} elements\`);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Watch for dynamically added elements
|
|
355
|
+
function observeNewElements() {
|
|
356
|
+
if (!document.body) return;
|
|
357
|
+
|
|
358
|
+
const observer = new MutationObserver((mutations) => {
|
|
359
|
+
mutations.forEach(mutation => {
|
|
360
|
+
mutation.addedNodes.forEach(node => {
|
|
361
|
+
// Only process element nodes
|
|
362
|
+
if (node.nodeType === 1) {
|
|
363
|
+
const element = node;
|
|
364
|
+
|
|
365
|
+
// Skip inspector's own elements
|
|
366
|
+
if (element.hasAttribute('data-inspector-ignore') ||
|
|
367
|
+
element.closest('[data-inspector-ignore]')) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Assign ID to the new element
|
|
372
|
+
elementTracker.ensureElementId(element);
|
|
373
|
+
|
|
374
|
+
// Also assign IDs to all children
|
|
375
|
+
const children = element.querySelectorAll('*');
|
|
376
|
+
children.forEach(child => {
|
|
377
|
+
if (!child.hasAttribute('data-inspector-ignore') &&
|
|
378
|
+
!child.closest('[data-inspector-ignore]')) {
|
|
379
|
+
elementTracker.ensureElementId(child);
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
// Start observing
|
|
388
|
+
observer.observe(document.body, {
|
|
389
|
+
childList: true,
|
|
390
|
+
subtree: true
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Initialize on page load
|
|
395
|
+
assignIdsToAllElements();
|
|
396
|
+
observeNewElements();
|
|
397
|
+
|
|
57
398
|
let inspectMode = false;
|
|
58
399
|
let isPaused = false;
|
|
59
400
|
let hoveredElement = null;
|
|
@@ -728,6 +1069,24 @@ export function inspectorPlugin() {
|
|
|
728
1069
|
}
|
|
729
1070
|
}
|
|
730
1071
|
|
|
1072
|
+
// Generate CSS selector for an element
|
|
1073
|
+
function generateSelector(element) {
|
|
1074
|
+
if (element.id) {
|
|
1075
|
+
return '#' + element.id;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
let selector = element.tagName.toLowerCase();
|
|
1079
|
+
|
|
1080
|
+
if (element.className && typeof element.className === 'string') {
|
|
1081
|
+
const classes = element.className.trim().split(/\\s+/).filter(c => c);
|
|
1082
|
+
if (classes.length > 0) {
|
|
1083
|
+
selector += '.' + classes.join('.');
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
return selector;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
731
1090
|
// Get React Fiber node from DOM element
|
|
732
1091
|
function getReactFiber(element) {
|
|
733
1092
|
const key = Object.keys(element).find(k =>
|
|
@@ -865,6 +1224,11 @@ export function inspectorPlugin() {
|
|
|
865
1224
|
e.stopPropagation();
|
|
866
1225
|
|
|
867
1226
|
const element = e.target;
|
|
1227
|
+
|
|
1228
|
+
// Assign unique inspector ID
|
|
1229
|
+
const inspectorId = elementTracker.ensureElementId(element);
|
|
1230
|
+
const selector = generateSelector(element);
|
|
1231
|
+
|
|
868
1232
|
const fiber = getReactFiber(element);
|
|
869
1233
|
const componentInfo = fiber ? getComponentInfo(fiber) : null;
|
|
870
1234
|
|
|
@@ -890,7 +1254,9 @@ export function inspectorPlugin() {
|
|
|
890
1254
|
isTextNode: isTextNode,
|
|
891
1255
|
textContent: textContent,
|
|
892
1256
|
isImageNode: isImageNode,
|
|
893
|
-
imageUrl: imageUrl
|
|
1257
|
+
imageUrl: imageUrl,
|
|
1258
|
+
inspectorId: inspectorId,
|
|
1259
|
+
selector: selector
|
|
894
1260
|
};
|
|
895
1261
|
|
|
896
1262
|
// Send info to parent window
|
|
@@ -969,6 +1335,86 @@ export function inspectorPlugin() {
|
|
|
969
1335
|
labels.badgeText = event.data.badgeText;
|
|
970
1336
|
}
|
|
971
1337
|
setBadgeVisible(event.data.visible);
|
|
1338
|
+
} else if (event.data.type === 'HIGHLIGHT_ELEMENT') {
|
|
1339
|
+
// Handle highlight element request
|
|
1340
|
+
const identifier = event.data.identifier;
|
|
1341
|
+
const options = event.data.options || {};
|
|
1342
|
+
|
|
1343
|
+
let element = null;
|
|
1344
|
+
|
|
1345
|
+
if (typeof identifier === 'string') {
|
|
1346
|
+
// Try as inspector ID first
|
|
1347
|
+
element = elementTracker.findElementById(document, identifier);
|
|
1348
|
+
|
|
1349
|
+
// Fallback to selector
|
|
1350
|
+
if (!element) {
|
|
1351
|
+
element = document.querySelector(identifier);
|
|
1352
|
+
}
|
|
1353
|
+
} else {
|
|
1354
|
+
// Full identifier object with fallback
|
|
1355
|
+
element = elementTracker.findElementWithFallback(document, identifier);
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
if (element) {
|
|
1359
|
+
elementHighlighter.highlight(element, options);
|
|
1360
|
+
} else {
|
|
1361
|
+
console.warn('[Inspector] Element not found for highlighting:', identifier);
|
|
1362
|
+
}
|
|
1363
|
+
} else if (event.data.type === 'GET_ELEMENT_BY_ID') {
|
|
1364
|
+
// Handle get element by ID request
|
|
1365
|
+
const inspectorId = event.data.inspectorId;
|
|
1366
|
+
const element = elementTracker.findElementById(document, inspectorId);
|
|
1367
|
+
|
|
1368
|
+
if (element) {
|
|
1369
|
+
const fiber = getReactFiber(element);
|
|
1370
|
+
const componentInfo = fiber ? getComponentInfo(fiber) : null;
|
|
1371
|
+
const isTextNode = element.textContent && element.children.length === 0;
|
|
1372
|
+
const textContent = isTextNode ? element.textContent.trim() : '';
|
|
1373
|
+
const isImageNode = element.tagName === 'IMG';
|
|
1374
|
+
const imageUrl = isImageNode ? element.src : '';
|
|
1375
|
+
const selector = generateSelector(element);
|
|
1376
|
+
|
|
1377
|
+
const elementData = {
|
|
1378
|
+
tagName: element.tagName,
|
|
1379
|
+
className: element.className,
|
|
1380
|
+
id: element.id,
|
|
1381
|
+
component: componentInfo,
|
|
1382
|
+
position: {
|
|
1383
|
+
top: element.getBoundingClientRect().top,
|
|
1384
|
+
left: element.getBoundingClientRect().left,
|
|
1385
|
+
width: element.getBoundingClientRect().width,
|
|
1386
|
+
height: element.getBoundingClientRect().height
|
|
1387
|
+
},
|
|
1388
|
+
isTextNode: isTextNode,
|
|
1389
|
+
textContent: textContent,
|
|
1390
|
+
isImageNode: isImageNode,
|
|
1391
|
+
imageUrl: imageUrl,
|
|
1392
|
+
inspectorId: inspectorId,
|
|
1393
|
+
selector: selector
|
|
1394
|
+
};
|
|
1395
|
+
|
|
1396
|
+
if (window.parent !== window) {
|
|
1397
|
+
const message = JSON.stringify({
|
|
1398
|
+
type: 'ELEMENT_INFO_RESPONSE',
|
|
1399
|
+
data: {
|
|
1400
|
+
found: true,
|
|
1401
|
+
element: elementData
|
|
1402
|
+
}
|
|
1403
|
+
});
|
|
1404
|
+
window.parent.postMessage(message, '*');
|
|
1405
|
+
}
|
|
1406
|
+
} else {
|
|
1407
|
+
if (window.parent !== window) {
|
|
1408
|
+
const message = JSON.stringify({
|
|
1409
|
+
type: 'ELEMENT_INFO_RESPONSE',
|
|
1410
|
+
data: {
|
|
1411
|
+
found: false,
|
|
1412
|
+
error: 'Element not found'
|
|
1413
|
+
}
|
|
1414
|
+
});
|
|
1415
|
+
window.parent.postMessage(message, '*');
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
972
1418
|
}
|
|
973
1419
|
});
|
|
974
1420
|
|
|
@@ -1245,52 +1691,76 @@ export function inspectorPlugin() {
|
|
|
1245
1691
|
console.log('🏷️ Badge auto-enabled for preview.promake.ai');
|
|
1246
1692
|
}
|
|
1247
1693
|
|
|
1248
|
-
// 🖼️ Auto cache-bust all images on page load
|
|
1694
|
+
// 🖼️ Auto cache-bust all images on page load and for dynamically added images
|
|
1249
1695
|
// This ensures images are always fresh when page is refreshed/reloaded
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1696
|
+
const cacheBustTimestamp = Date.now();
|
|
1697
|
+
|
|
1698
|
+
function cacheBustImage(img) {
|
|
1699
|
+
if (img.src && !img.src.startsWith('data:') && !img.hasAttribute('data-cache-busted')) {
|
|
1700
|
+
try {
|
|
1701
|
+
const url = new URL(img.src);
|
|
1702
|
+
// Remove old cache busting params
|
|
1703
|
+
url.searchParams.delete('_t');
|
|
1704
|
+
url.searchParams.delete('_cache_bust');
|
|
1705
|
+
url.searchParams.delete('v');
|
|
1706
|
+
// Add new timestamp
|
|
1707
|
+
url.searchParams.set('_t', cacheBustTimestamp.toString());
|
|
1708
|
+
|
|
1709
|
+
const newSrc = url.toString();
|
|
1710
|
+
|
|
1711
|
+
// Force reload by clearing and resetting src
|
|
1712
|
+
img.removeAttribute('src');
|
|
1713
|
+
// Use setTimeout to ensure the browser registers the change
|
|
1714
|
+
setTimeout(() => {
|
|
1715
|
+
img.src = newSrc;
|
|
1716
|
+
}, 0);
|
|
1717
|
+
|
|
1718
|
+
img.setAttribute('data-cache-busted', 'true');
|
|
1719
|
+
return true;
|
|
1720
|
+
} catch (e) {
|
|
1721
|
+
// Ignore invalid URLs
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
return false;
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
function cacheBustBackgroundImage(el) {
|
|
1728
|
+
if (el.hasAttribute('data-cache-busted')) return false;
|
|
1253
1729
|
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
if (
|
|
1730
|
+
const bgImage = window.getComputedStyle(el).backgroundImage;
|
|
1731
|
+
if (bgImage && bgImage !== 'none' && bgImage.includes('url(')) {
|
|
1732
|
+
const urlMatch = bgImage.match(/url\(['"]?([^'"]+)['"]?\)/);
|
|
1733
|
+
if (urlMatch && urlMatch[1] && !urlMatch[1].startsWith('data:')) {
|
|
1258
1734
|
try {
|
|
1259
|
-
const url = new URL(
|
|
1260
|
-
// Remove old cache busting params
|
|
1735
|
+
const url = new URL(urlMatch[1], window.location.origin);
|
|
1261
1736
|
url.searchParams.delete('_t');
|
|
1262
1737
|
url.searchParams.delete('_cache_bust');
|
|
1263
1738
|
url.searchParams.delete('v');
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1739
|
+
url.searchParams.set('_t', cacheBustTimestamp.toString());
|
|
1740
|
+
el.style.backgroundImage = 'url("' + url.toString() + '")';
|
|
1741
|
+
el.setAttribute('data-cache-busted', 'true');
|
|
1742
|
+
return true;
|
|
1268
1743
|
} catch (e) {
|
|
1269
1744
|
// Ignore invalid URLs
|
|
1270
1745
|
}
|
|
1271
1746
|
}
|
|
1747
|
+
}
|
|
1748
|
+
return false;
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
function cacheBustAllImages() {
|
|
1752
|
+
let imageCount = 0;
|
|
1753
|
+
|
|
1754
|
+
// Cache-bust all <img> tags
|
|
1755
|
+
const images = document.querySelectorAll('img');
|
|
1756
|
+
images.forEach((img) => {
|
|
1757
|
+
if (cacheBustImage(img)) imageCount++;
|
|
1272
1758
|
});
|
|
1273
1759
|
|
|
1274
1760
|
// Cache-bust background images
|
|
1275
1761
|
const allElements = document.querySelectorAll('*');
|
|
1276
1762
|
allElements.forEach((el) => {
|
|
1277
|
-
|
|
1278
|
-
if (bgImage && bgImage !== 'none' && bgImage.includes('url(')) {
|
|
1279
|
-
const urlMatch = bgImage.match(/url\(['"]?([^'"]+)['"]?\)/);
|
|
1280
|
-
if (urlMatch && urlMatch[1] && !urlMatch[1].startsWith('data:')) {
|
|
1281
|
-
try {
|
|
1282
|
-
const url = new URL(urlMatch[1], window.location.origin);
|
|
1283
|
-
url.searchParams.delete('_t');
|
|
1284
|
-
url.searchParams.delete('_cache_bust');
|
|
1285
|
-
url.searchParams.delete('v');
|
|
1286
|
-
url.searchParams.set('_t', timestamp.toString());
|
|
1287
|
-
el.style.backgroundImage = 'url("' + url.toString() + '")';
|
|
1288
|
-
imageCount++;
|
|
1289
|
-
} catch (e) {
|
|
1290
|
-
// Ignore invalid URLs
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
}
|
|
1763
|
+
if (cacheBustBackgroundImage(el)) imageCount++;
|
|
1294
1764
|
});
|
|
1295
1765
|
|
|
1296
1766
|
if (imageCount > 0) {
|
|
@@ -1298,12 +1768,50 @@ export function inspectorPlugin() {
|
|
|
1298
1768
|
}
|
|
1299
1769
|
}
|
|
1300
1770
|
|
|
1771
|
+
// Watch for dynamically added images
|
|
1772
|
+
function observeNewImages() {
|
|
1773
|
+
if (!document.body) return;
|
|
1774
|
+
|
|
1775
|
+
const imageObserver = new MutationObserver((mutations) => {
|
|
1776
|
+
mutations.forEach(mutation => {
|
|
1777
|
+
mutation.addedNodes.forEach(node => {
|
|
1778
|
+
if (node.nodeType === 1) {
|
|
1779
|
+
const element = node;
|
|
1780
|
+
|
|
1781
|
+
// If it's an img element, cache-bust it
|
|
1782
|
+
if (element.tagName === 'IMG') {
|
|
1783
|
+
cacheBustImage(element);
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
// Check for img children
|
|
1787
|
+
const imgChildren = element.querySelectorAll('img');
|
|
1788
|
+
imgChildren.forEach(img => cacheBustImage(img));
|
|
1789
|
+
|
|
1790
|
+
// Check for background images
|
|
1791
|
+
if (cacheBustBackgroundImage(element)) {
|
|
1792
|
+
// Successfully cache-busted
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
});
|
|
1796
|
+
});
|
|
1797
|
+
});
|
|
1798
|
+
|
|
1799
|
+
imageObserver.observe(document.body, {
|
|
1800
|
+
childList: true,
|
|
1801
|
+
subtree: true
|
|
1802
|
+
});
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1301
1805
|
// Run cache-bust after DOM is fully loaded
|
|
1302
1806
|
if (document.readyState === 'loading') {
|
|
1303
|
-
document.addEventListener('DOMContentLoaded',
|
|
1807
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
1808
|
+
cacheBustAllImages();
|
|
1809
|
+
observeNewImages();
|
|
1810
|
+
});
|
|
1304
1811
|
} else {
|
|
1305
1812
|
// DOM already loaded, run immediately
|
|
1306
1813
|
cacheBustAllImages();
|
|
1814
|
+
observeNewImages();
|
|
1307
1815
|
}
|
|
1308
1816
|
|
|
1309
1817
|
console.log('🔍 Inspector plugin loaded');
|
package/dist/types.d.ts
CHANGED
|
@@ -26,6 +26,8 @@ export interface SelectedElementData {
|
|
|
26
26
|
textContent?: string;
|
|
27
27
|
isImageNode?: boolean;
|
|
28
28
|
imageUrl?: string;
|
|
29
|
+
inspectorId?: string;
|
|
30
|
+
selector?: string;
|
|
29
31
|
}
|
|
30
32
|
export interface UrlChangeData {
|
|
31
33
|
url: string;
|
|
@@ -61,6 +63,30 @@ export interface ErrorData {
|
|
|
61
63
|
columnNumber?: number;
|
|
62
64
|
timestamp: number;
|
|
63
65
|
}
|
|
66
|
+
export interface ElementIdentifier {
|
|
67
|
+
inspectorId: string;
|
|
68
|
+
selector: string;
|
|
69
|
+
position?: {
|
|
70
|
+
top: number;
|
|
71
|
+
left: number;
|
|
72
|
+
};
|
|
73
|
+
component?: {
|
|
74
|
+
name: string;
|
|
75
|
+
fileName: string;
|
|
76
|
+
lineNumber?: number;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export interface HighlightOptions {
|
|
80
|
+
duration?: number;
|
|
81
|
+
scrollIntoView?: boolean;
|
|
82
|
+
color?: string;
|
|
83
|
+
animation?: 'pulse' | 'fade' | 'none';
|
|
84
|
+
}
|
|
85
|
+
export interface ElementInfoData {
|
|
86
|
+
found: boolean;
|
|
87
|
+
element?: SelectedElementData;
|
|
88
|
+
error?: string;
|
|
89
|
+
}
|
|
64
90
|
export interface InspectorLabels {
|
|
65
91
|
editText?: string;
|
|
66
92
|
textPlaceholder?: string;
|
|
@@ -95,6 +121,7 @@ export interface InspectorCallbacks {
|
|
|
95
121
|
onImageUpdated?: (data: ImageUpdatedData) => void;
|
|
96
122
|
onInspectorClosed?: () => void;
|
|
97
123
|
onError?: (data: ErrorData) => void;
|
|
124
|
+
onElementInfoReceived?: (data: ElementInfoData) => void;
|
|
98
125
|
}
|
|
99
126
|
export interface UseInspectorReturn {
|
|
100
127
|
isInspecting: boolean;
|
|
@@ -104,5 +131,7 @@ export interface UseInspectorReturn {
|
|
|
104
131
|
showContentInput: (show: boolean, updateImmediately?: boolean) => void;
|
|
105
132
|
showImageInput: (show: boolean, updateImmediately?: boolean) => void;
|
|
106
133
|
setBadgeVisible: (visible: boolean) => void;
|
|
134
|
+
highlightElement: (identifier: string | ElementIdentifier, options?: HighlightOptions) => void;
|
|
135
|
+
getElementByInspectorId: (inspectorId: string) => void;
|
|
107
136
|
}
|
|
108
137
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promakeai/inspector",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Visual element inspector for React apps in iframe with AI prompt support",
|
|
5
5
|
"author": "Promake",
|
|
6
6
|
"type": "module",
|
|
@@ -33,6 +33,14 @@
|
|
|
33
33
|
"scripts": {
|
|
34
34
|
"build": "tsc && tsc -p tsconfig.plugin.json",
|
|
35
35
|
"dev": "concurrently -n \"HOOK,PLUGIN\" -c \"blue,magenta\" \"tsc --watch\" \"tsc -p tsconfig.plugin.json --watch\"",
|
|
36
|
+
"test": "vitest run",
|
|
37
|
+
"test:watch": "vitest",
|
|
38
|
+
"test:ui": "vitest --ui",
|
|
39
|
+
"test:coverage": "vitest run --coverage",
|
|
40
|
+
"test:e2e": "cd test && npm test",
|
|
41
|
+
"test:e2e:ui": "cd test && npm run test:ui",
|
|
42
|
+
"test:e2e:headed": "cd test && npm run test:headed",
|
|
43
|
+
"test:all": "npm test && npm run test:e2e",
|
|
36
44
|
"prepublishOnly": "npm run build",
|
|
37
45
|
"release": "npm run build && npm publish --access public"
|
|
38
46
|
},
|
|
@@ -48,13 +56,21 @@
|
|
|
48
56
|
"prompt"
|
|
49
57
|
],
|
|
50
58
|
"peerDependencies": {
|
|
51
|
-
"
|
|
52
|
-
"vite": ">5.0.0"
|
|
59
|
+
"vite": ">=5.0.0"
|
|
53
60
|
},
|
|
54
61
|
"devDependencies": {
|
|
62
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
63
|
+
"@testing-library/react": "^16.3.0",
|
|
55
64
|
"@types/react": "^18.2.0",
|
|
65
|
+
"@vitest/coverage-v8": "^4.0.8",
|
|
66
|
+
"@vitest/ui": "^4.0.8",
|
|
56
67
|
"concurrently": "^9.1.0",
|
|
68
|
+
"happy-dom": "^20.0.10",
|
|
69
|
+
"jsdom": "^27.2.0",
|
|
70
|
+
"react": "^18.3.1",
|
|
71
|
+
"react-dom": "^18.3.1",
|
|
57
72
|
"typescript": "^5.3.0",
|
|
58
|
-
"vite": "^5.4.0"
|
|
73
|
+
"vite": "^5.4.0",
|
|
74
|
+
"vitest": "^4.0.8"
|
|
59
75
|
}
|
|
60
76
|
}
|