@promakeai/inspector 0.0.1 → 0.0.3
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 +193 -13
- package/dist/hook.d.ts +29 -8
- package/dist/hook.d.ts.map +1 -1
- package/dist/hook.js +42 -8
- package/dist/plugin.d.ts +10 -2
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +177 -91
- package/dist/types.d.ts +18 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -1
- package/package.json +14 -4
package/README.md
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
# @promakeai/inspector
|
|
2
2
|
|
|
3
|
+
[](https://github.com/promakeai/inspector/actions/workflows/build.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@promakeai/inspector)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
3
7
|
Visual element inspector for React apps in iframe with AI prompt support. Perfect for visual editors, page builders, and low-code platforms.
|
|
4
8
|
|
|
5
9
|
## Features
|
|
6
10
|
|
|
7
11
|
- 🎯 **Element Selection** - Click to select any element on the page
|
|
8
12
|
- 🔍 **React Component Detection** - Automatically detects React components via Fiber
|
|
9
|
-
- 📝 **
|
|
10
|
-
- 🤖 **AI Prompt Support** -
|
|
13
|
+
- 📝 **Dynamic Content Input** - Show/hide text editing on-demand with `showContentInput()`
|
|
14
|
+
- 🤖 **AI Prompt Support** - Always-visible prompt input for AI modifications
|
|
11
15
|
- 🎨 **Visual Feedback** - Highlight selected elements with overlay
|
|
12
16
|
- 🔒 **Pause Mode** - Lock selection and disable scroll while editing
|
|
13
17
|
- 🌐 **URL Tracking** - Monitor navigation changes in iframe
|
|
14
18
|
- 🐛 **Error Tracking** - Capture JavaScript errors, promise rejections, and console errors
|
|
15
|
-
- 🎨 **Customizable
|
|
19
|
+
- 🎨 **Customizable Theme** - Full color customization for all UI elements
|
|
20
|
+
- 🌍 **Customizable Labels** - Multi-language support
|
|
21
|
+
- 🚫 **Ignore Elements** - Mark elements as non-inspectable with `data-inspector-ignore`
|
|
22
|
+
- 🔍 **Text Node Detection** - Automatically detect if selected element is a text node
|
|
16
23
|
- 📦 **TypeScript** - Full type safety
|
|
17
24
|
|
|
18
25
|
## Installation
|
|
@@ -48,14 +55,21 @@ import { useInspector } from "@promakeai/inspector/hook";
|
|
|
48
55
|
function App() {
|
|
49
56
|
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
50
57
|
|
|
51
|
-
const { isInspecting, toggleInspector } = useInspector(
|
|
58
|
+
const { isInspecting, toggleInspector, showContentInput } = useInspector(
|
|
52
59
|
iframeRef,
|
|
53
60
|
{
|
|
54
61
|
onElementSelected: (data) => {
|
|
55
62
|
console.log("Element selected:", data);
|
|
63
|
+
console.log("Is text node:", data.isTextNode);
|
|
64
|
+
|
|
56
65
|
// Access component info
|
|
57
66
|
console.log(data.component?.fileName);
|
|
58
67
|
console.log(data.component?.lineNumber);
|
|
68
|
+
|
|
69
|
+
// Show content input if it's a text node
|
|
70
|
+
if (data.isTextNode) {
|
|
71
|
+
showContentInput(true);
|
|
72
|
+
}
|
|
59
73
|
},
|
|
60
74
|
onPromptSubmitted: (data) => {
|
|
61
75
|
console.log("AI Prompt:", data.prompt);
|
|
@@ -87,6 +101,16 @@ function App() {
|
|
|
87
101
|
textPlaceholder: "Enter text...",
|
|
88
102
|
updateText: "Update",
|
|
89
103
|
promptPlaceholder: "Ask AI for changes...",
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
// Optional: Custom theme colors
|
|
107
|
+
backgroundColor: "#ffffff",
|
|
108
|
+
textColor: "#111827",
|
|
109
|
+
buttonColor: "#4417db",
|
|
110
|
+
buttonTextColor: "#ffffff",
|
|
111
|
+
inputBackgroundColor: "#f9fafb",
|
|
112
|
+
inputTextColor: "#111827",
|
|
113
|
+
inputBorderColor: "#d1d5db",
|
|
90
114
|
}
|
|
91
115
|
);
|
|
92
116
|
|
|
@@ -96,6 +120,13 @@ function App() {
|
|
|
96
120
|
{isInspecting ? "Stop Inspector" : "Start Inspector"}
|
|
97
121
|
</button>
|
|
98
122
|
|
|
123
|
+
{/* Optional: Manual content input toggle */}
|
|
124
|
+
{isInspecting && (
|
|
125
|
+
<button onClick={() => showContentInput(true)}>
|
|
126
|
+
Show Content Input
|
|
127
|
+
</button>
|
|
128
|
+
)}
|
|
129
|
+
|
|
99
130
|
<iframe ref={iframeRef} src="http://localhost:5173" />
|
|
100
131
|
</div>
|
|
101
132
|
);
|
|
@@ -104,13 +135,13 @@ function App() {
|
|
|
104
135
|
|
|
105
136
|
## API
|
|
106
137
|
|
|
107
|
-
### `useInspector(iframeRef, callbacks?, labels?)`
|
|
138
|
+
### `useInspector(iframeRef, callbacks?, labels?, theme?)`
|
|
108
139
|
|
|
109
140
|
#### Parameters
|
|
110
141
|
|
|
111
142
|
- **iframeRef**: `RefObject<HTMLIFrameElement>` - Reference to the iframe element
|
|
112
143
|
- **callbacks**: `InspectorCallbacks` (optional)
|
|
113
|
-
- `onElementSelected`: Called when an element is selected
|
|
144
|
+
- `onElementSelected`: Called when an element is selected (receives `isTextNode` property)
|
|
114
145
|
- `onPromptSubmitted`: Called when AI prompt is submitted
|
|
115
146
|
- `onTextUpdated`: Called when text content is updated
|
|
116
147
|
- `onUrlChange`: Called when URL changes in iframe
|
|
@@ -120,6 +151,14 @@ function App() {
|
|
|
120
151
|
- `textPlaceholder`: Placeholder for text input
|
|
121
152
|
- `updateText`: Label for update button
|
|
122
153
|
- `promptPlaceholder`: Placeholder for prompt input
|
|
154
|
+
- **theme**: `InspectorTheme` (optional)
|
|
155
|
+
- `backgroundColor`: Box background color (default: `#ffffff`)
|
|
156
|
+
- `textColor`: Text color (default: `#111827`)
|
|
157
|
+
- `buttonColor`: Button background color (default: `#4417db`)
|
|
158
|
+
- `buttonTextColor`: Button text color (default: `#ffffff`)
|
|
159
|
+
- `inputBackgroundColor`: Input background color (default: `#f9fafb`)
|
|
160
|
+
- `inputTextColor`: Input text color (default: `#111827`)
|
|
161
|
+
- `inputBorderColor`: Input border color (default: `#d1d5db`)
|
|
123
162
|
|
|
124
163
|
#### Returns
|
|
125
164
|
|
|
@@ -127,6 +166,7 @@ function App() {
|
|
|
127
166
|
- **toggleInspector**: `(active?: boolean) => void` - Toggle inspection mode
|
|
128
167
|
- **startInspecting**: `() => void` - Start inspecting
|
|
129
168
|
- **stopInspecting**: `() => void` - Stop inspecting
|
|
169
|
+
- **showContentInput**: `(show: boolean) => void` - Show or hide content input dynamically
|
|
130
170
|
|
|
131
171
|
## Types
|
|
132
172
|
|
|
@@ -139,6 +179,7 @@ interface SelectedElementData {
|
|
|
139
179
|
id: string;
|
|
140
180
|
component: ComponentInfo | null;
|
|
141
181
|
position: ElementPosition;
|
|
182
|
+
isTextNode?: boolean; // Whether the element is a text-only node
|
|
142
183
|
}
|
|
143
184
|
```
|
|
144
185
|
|
|
@@ -186,6 +227,20 @@ interface ErrorData {
|
|
|
186
227
|
}
|
|
187
228
|
```
|
|
188
229
|
|
|
230
|
+
### `InspectorTheme`
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
interface InspectorTheme {
|
|
234
|
+
backgroundColor?: string; // Box background color
|
|
235
|
+
textColor?: string; // Text color
|
|
236
|
+
buttonColor?: string; // Button background color
|
|
237
|
+
buttonTextColor?: string; // Button text color
|
|
238
|
+
inputBackgroundColor?: string; // Input background color
|
|
239
|
+
inputTextColor?: string; // Input text color
|
|
240
|
+
inputBorderColor?: string; // Input border color
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
189
244
|
## Features in Detail
|
|
190
245
|
|
|
191
246
|
### Element Selection
|
|
@@ -193,26 +248,65 @@ interface ErrorData {
|
|
|
193
248
|
Click any element to select it. The inspector will:
|
|
194
249
|
|
|
195
250
|
- Show a blue highlight overlay
|
|
196
|
-
- Display a control box below/above the element
|
|
251
|
+
- Display a control box below/above the element (always shows prompt input)
|
|
197
252
|
- Pause scrolling
|
|
198
253
|
- Extract React component information
|
|
254
|
+
- Detect if element is a text-only node (`isTextNode`)
|
|
255
|
+
|
|
256
|
+
### Dynamic Content Input
|
|
199
257
|
|
|
200
|
-
|
|
258
|
+
The inspector now uses a two-stage approach:
|
|
201
259
|
|
|
202
|
-
|
|
260
|
+
1. **Prompt Input (Always Visible)**: Every element shows a prompt input for AI instructions
|
|
261
|
+
2. **Content Input (On-Demand)**: Text editing appears only when you call `showContentInput(true)`
|
|
203
262
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
-
|
|
263
|
+
This allows you to:
|
|
264
|
+
|
|
265
|
+
- Run AI computations after selection
|
|
266
|
+
- Decide programmatically when to show text editing
|
|
267
|
+
- Keep the UI clean and focused
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
onElementSelected: (data) => {
|
|
271
|
+
if (data.isTextNode) {
|
|
272
|
+
// Show content input above prompt input
|
|
273
|
+
showContentInput(true);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
```
|
|
207
277
|
|
|
208
278
|
### AI Prompts
|
|
209
279
|
|
|
210
280
|
For any element:
|
|
211
281
|
|
|
282
|
+
- Prompt input is always visible
|
|
212
283
|
- Enter a prompt describing desired changes
|
|
213
284
|
- Click the send button (↑)
|
|
214
285
|
- Prompt + element data sent to parent app
|
|
215
286
|
|
|
287
|
+
### Ignoring Elements
|
|
288
|
+
|
|
289
|
+
Mark elements as non-inspectable using the `data-inspector-ignore` attribute:
|
|
290
|
+
|
|
291
|
+
```jsx
|
|
292
|
+
<button data-inspector-ignore onClick={toggleInspect}>
|
|
293
|
+
Inspector Controls
|
|
294
|
+
</button>
|
|
295
|
+
|
|
296
|
+
<div data-inspector-ignore>
|
|
297
|
+
{/* All children will be ignored too */}
|
|
298
|
+
<button>Button 1</button>
|
|
299
|
+
<button>Button 2</button>
|
|
300
|
+
</div>
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
This is perfect for:
|
|
304
|
+
|
|
305
|
+
- Inspector control buttons
|
|
306
|
+
- Floating toolbars
|
|
307
|
+
- Fixed navigation
|
|
308
|
+
- Any UI that shouldn't be inspectable
|
|
309
|
+
|
|
216
310
|
### Component Detection
|
|
217
311
|
|
|
218
312
|
Automatically extracts:
|
|
@@ -243,6 +337,45 @@ Perfect for integrating with error tracking services like Sentry, LogRocket, or
|
|
|
243
337
|
|
|
244
338
|
## Examples
|
|
245
339
|
|
|
340
|
+
### Dynamic Content Input Based on AI Response
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
const { toggleInspector, showContentInput } = useInspector(iframeRef, {
|
|
344
|
+
onElementSelected: async (data) => {
|
|
345
|
+
// Run AI analysis first
|
|
346
|
+
const analysis = await analyzeElement(data);
|
|
347
|
+
|
|
348
|
+
// Show content input only if AI suggests text editing
|
|
349
|
+
if (analysis.shouldShowTextEditor && data.isTextNode) {
|
|
350
|
+
showContentInput(true);
|
|
351
|
+
}
|
|
352
|
+
},
|
|
353
|
+
onPromptSubmitted: async (data) => {
|
|
354
|
+
// Process AI prompt
|
|
355
|
+
const result = await processAIPrompt(data.prompt, data.element);
|
|
356
|
+
|
|
357
|
+
// Optionally show content input after AI processing
|
|
358
|
+
if (result.needsTextInput) {
|
|
359
|
+
showContentInput(true);
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
});
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Dark Theme
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
const { toggleInspector } = useInspector(iframeRef, callbacks, labels, {
|
|
369
|
+
backgroundColor: "#1a1a1a",
|
|
370
|
+
textColor: "#ffffff",
|
|
371
|
+
buttonColor: "#00ff00",
|
|
372
|
+
buttonTextColor: "#000000",
|
|
373
|
+
inputBackgroundColor: "#2a2a2a",
|
|
374
|
+
inputTextColor: "#ffffff",
|
|
375
|
+
inputBorderColor: "#444444",
|
|
376
|
+
});
|
|
377
|
+
```
|
|
378
|
+
|
|
246
379
|
### Multi-language Support
|
|
247
380
|
|
|
248
381
|
```typescript
|
|
@@ -250,10 +383,30 @@ const { toggleInspector } = useInspector(iframeRef, callbacks, {
|
|
|
250
383
|
editText: "Metni Düzenle",
|
|
251
384
|
textPlaceholder: "Metin girin...",
|
|
252
385
|
updateText: "Güncelle",
|
|
253
|
-
promptPlaceholder: "
|
|
386
|
+
promptPlaceholder: "Promake'e değişiklik sor...",
|
|
254
387
|
});
|
|
255
388
|
```
|
|
256
389
|
|
|
390
|
+
### Prevent Inspector Buttons from Being Selected
|
|
391
|
+
|
|
392
|
+
```jsx
|
|
393
|
+
function App() {
|
|
394
|
+
return (
|
|
395
|
+
<div>
|
|
396
|
+
{/* These buttons won't be inspectable */}
|
|
397
|
+
<div className="inspector-controls" data-inspector-ignore>
|
|
398
|
+
<button onClick={toggleInspect}>Toggle Inspector</button>
|
|
399
|
+
<button onClick={() => showContentInput(true)}>
|
|
400
|
+
Show Content Input
|
|
401
|
+
</button>
|
|
402
|
+
</div>
|
|
403
|
+
|
|
404
|
+
<iframe ref={iframeRef} src="http://localhost:5173" />
|
|
405
|
+
</div>
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
257
410
|
### Open File in Editor
|
|
258
411
|
|
|
259
412
|
```typescript
|
|
@@ -303,6 +456,33 @@ const { toggleInspector } = useInspector(iframeRef, {
|
|
|
303
456
|
- React 18.0+
|
|
304
457
|
- TypeScript (recommended)
|
|
305
458
|
|
|
459
|
+
## CI/CD
|
|
460
|
+
|
|
461
|
+
This project includes GitHub Actions workflows:
|
|
462
|
+
|
|
463
|
+
### Build Workflow (`.github/workflows/build.yml`)
|
|
464
|
+
|
|
465
|
+
Automatically runs on every push and pull request:
|
|
466
|
+
|
|
467
|
+
- ✅ Builds the package on Node.js 18.x and 20.x
|
|
468
|
+
- ✅ Validates TypeScript compilation
|
|
469
|
+
- ✅ Checks for build artifacts
|
|
470
|
+
- ✅ Uploads build artifacts for review
|
|
471
|
+
|
|
472
|
+
### Publish Workflow (`.github/workflows/publish.yml`)
|
|
473
|
+
|
|
474
|
+
Automatically publishes to NPM when a release is created:
|
|
475
|
+
|
|
476
|
+
- ✅ Builds the package
|
|
477
|
+
- ✅ Publishes to NPM with `--access public`
|
|
478
|
+
- ✅ Manual trigger support via workflow_dispatch
|
|
479
|
+
|
|
480
|
+
**Setup for NPM publishing:**
|
|
481
|
+
|
|
482
|
+
1. Create an NPM access token at [npmjs.com](https://www.npmjs.com/settings/YOUR_USERNAME/tokens)
|
|
483
|
+
2. Add the token as `NPM_TOKEN` secret in your GitHub repository settings
|
|
484
|
+
3. Create a release on GitHub to trigger automatic publishing
|
|
485
|
+
|
|
306
486
|
## License
|
|
307
487
|
|
|
308
488
|
MIT
|
package/dist/hook.d.ts
CHANGED
|
@@ -12,11 +12,17 @@
|
|
|
12
12
|
* function App() {
|
|
13
13
|
* const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
14
14
|
*
|
|
15
|
-
* const { isInspecting, toggleInspector } = useInspector(
|
|
15
|
+
* const { isInspecting, toggleInspector, showContentInput } = useInspector(
|
|
16
16
|
* iframeRef,
|
|
17
17
|
* {
|
|
18
18
|
* onElementSelected: (data) => {
|
|
19
19
|
* console.log('Element selected:', data);
|
|
20
|
+
* console.log('Is text node:', data.isTextNode);
|
|
21
|
+
*
|
|
22
|
+
* // Dynamically show content input for text nodes
|
|
23
|
+
* if (data.isTextNode) {
|
|
24
|
+
* showContentInput(true);
|
|
25
|
+
* }
|
|
20
26
|
* },
|
|
21
27
|
* onPromptSubmitted: (data) => {
|
|
22
28
|
* console.log('AI Prompt:', data.prompt);
|
|
@@ -40,22 +46,37 @@
|
|
|
40
46
|
* textPlaceholder: 'Enter text...',
|
|
41
47
|
* updateText: 'Update',
|
|
42
48
|
* promptPlaceholder: 'Ask AI for changes...'
|
|
49
|
+
* },
|
|
50
|
+
* {
|
|
51
|
+
* // Optional: Custom theme colors (all optional)
|
|
52
|
+
* backgroundColor: '#ffffff',
|
|
53
|
+
* textColor: '#111827',
|
|
54
|
+
* buttonColor: '#4417db',
|
|
55
|
+
* buttonTextColor: '#ffffff',
|
|
56
|
+
* inputBackgroundColor: '#f9fafb',
|
|
57
|
+
* inputTextColor: '#111827',
|
|
58
|
+
* inputBorderColor: '#d1d5db'
|
|
43
59
|
* }
|
|
44
60
|
* );
|
|
45
61
|
*
|
|
46
62
|
* return (
|
|
47
|
-
*
|
|
48
|
-
* <
|
|
49
|
-
* {
|
|
50
|
-
*
|
|
63
|
+
* <>
|
|
64
|
+
* <div data-inspector-ignore>
|
|
65
|
+
* <button onClick={() => toggleInspector()}>
|
|
66
|
+
* Toggle Inspector
|
|
67
|
+
* </button>
|
|
68
|
+
* <button onClick={() => showContentInput(true)}>
|
|
69
|
+
* Show Content Input
|
|
70
|
+
* </button>
|
|
71
|
+
* </div>
|
|
51
72
|
* <iframe ref={iframeRef} src="http://localhost:5173" />
|
|
52
|
-
*
|
|
73
|
+
* </>
|
|
53
74
|
* );
|
|
54
75
|
* }
|
|
55
76
|
* ```
|
|
56
77
|
*/
|
|
57
78
|
import { RefObject } from "react";
|
|
58
|
-
import type { InspectorCallbacks, InspectorLabels, UseInspectorReturn } from "./types";
|
|
59
|
-
export declare function useInspector(iframeRef: RefObject<HTMLIFrameElement>, callbacks?: InspectorCallbacks, labels?: InspectorLabels): UseInspectorReturn;
|
|
79
|
+
import type { InspectorCallbacks, InspectorLabels, InspectorTheme, UseInspectorReturn } from "./types";
|
|
80
|
+
export declare function useInspector(iframeRef: RefObject<HTMLIFrameElement>, callbacks?: InspectorCallbacks, labels?: InspectorLabels, theme?: InspectorTheme): UseInspectorReturn;
|
|
60
81
|
export type * from "./types";
|
|
61
82
|
//# sourceMappingURL=hook.d.ts.map
|
package/dist/hook.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../src/hook.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../src/hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4EG;AAEH,OAAO,EAAoC,SAAS,EAAE,MAAM,OAAO,CAAC;AACpE,OAAO,KAAK,EACV,kBAAkB,EAClB,eAAe,EACf,cAAc,EACd,kBAAkB,EAMnB,MAAM,SAAS,CAAC;AAoCjB,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,CAyIpB;AAGD,mBAAmB,SAAS,CAAC"}
|
package/dist/hook.js
CHANGED
|
@@ -12,11 +12,17 @@
|
|
|
12
12
|
* function App() {
|
|
13
13
|
* const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
14
14
|
*
|
|
15
|
-
* const { isInspecting, toggleInspector } = useInspector(
|
|
15
|
+
* const { isInspecting, toggleInspector, showContentInput } = useInspector(
|
|
16
16
|
* iframeRef,
|
|
17
17
|
* {
|
|
18
18
|
* onElementSelected: (data) => {
|
|
19
19
|
* console.log('Element selected:', data);
|
|
20
|
+
* console.log('Is text node:', data.isTextNode);
|
|
21
|
+
*
|
|
22
|
+
* // Dynamically show content input for text nodes
|
|
23
|
+
* if (data.isTextNode) {
|
|
24
|
+
* showContentInput(true);
|
|
25
|
+
* }
|
|
20
26
|
* },
|
|
21
27
|
* onPromptSubmitted: (data) => {
|
|
22
28
|
* console.log('AI Prompt:', data.prompt);
|
|
@@ -40,22 +46,37 @@
|
|
|
40
46
|
* textPlaceholder: 'Enter text...',
|
|
41
47
|
* updateText: 'Update',
|
|
42
48
|
* promptPlaceholder: 'Ask AI for changes...'
|
|
49
|
+
* },
|
|
50
|
+
* {
|
|
51
|
+
* // Optional: Custom theme colors (all optional)
|
|
52
|
+
* backgroundColor: '#ffffff',
|
|
53
|
+
* textColor: '#111827',
|
|
54
|
+
* buttonColor: '#4417db',
|
|
55
|
+
* buttonTextColor: '#ffffff',
|
|
56
|
+
* inputBackgroundColor: '#f9fafb',
|
|
57
|
+
* inputTextColor: '#111827',
|
|
58
|
+
* inputBorderColor: '#d1d5db'
|
|
43
59
|
* }
|
|
44
60
|
* );
|
|
45
61
|
*
|
|
46
62
|
* return (
|
|
47
|
-
*
|
|
48
|
-
* <
|
|
49
|
-
* {
|
|
50
|
-
*
|
|
63
|
+
* <>
|
|
64
|
+
* <div data-inspector-ignore>
|
|
65
|
+
* <button onClick={() => toggleInspector()}>
|
|
66
|
+
* Toggle Inspector
|
|
67
|
+
* </button>
|
|
68
|
+
* <button onClick={() => showContentInput(true)}>
|
|
69
|
+
* Show Content Input
|
|
70
|
+
* </button>
|
|
71
|
+
* </div>
|
|
51
72
|
* <iframe ref={iframeRef} src="http://localhost:5173" />
|
|
52
|
-
*
|
|
73
|
+
* </>
|
|
53
74
|
* );
|
|
54
75
|
* }
|
|
55
76
|
* ```
|
|
56
77
|
*/
|
|
57
78
|
import { useEffect, useState, useCallback } from "react";
|
|
58
|
-
export function useInspector(iframeRef, callbacks, labels) {
|
|
79
|
+
export function useInspector(iframeRef, callbacks, labels, theme) {
|
|
59
80
|
const [isInspecting, setIsInspecting] = useState(false);
|
|
60
81
|
/**
|
|
61
82
|
* Send message to iframe
|
|
@@ -83,8 +104,9 @@ export function useInspector(iframeRef, callbacks, labels) {
|
|
|
83
104
|
type: "TOGGLE_INSPECTOR",
|
|
84
105
|
active: newState,
|
|
85
106
|
labels: labels,
|
|
107
|
+
theme: theme,
|
|
86
108
|
});
|
|
87
|
-
}, [isInspecting, sendMessage, labels]);
|
|
109
|
+
}, [isInspecting, sendMessage, labels, theme]);
|
|
88
110
|
/**
|
|
89
111
|
* Start inspecting
|
|
90
112
|
*/
|
|
@@ -97,6 +119,15 @@ export function useInspector(iframeRef, callbacks, labels) {
|
|
|
97
119
|
const stopInspecting = useCallback(() => {
|
|
98
120
|
toggleInspector(false);
|
|
99
121
|
}, [toggleInspector]);
|
|
122
|
+
/**
|
|
123
|
+
* Show or hide content input
|
|
124
|
+
*/
|
|
125
|
+
const showContentInput = useCallback((show) => {
|
|
126
|
+
sendMessage({
|
|
127
|
+
type: "SHOW_CONTENT_INPUT",
|
|
128
|
+
show: show,
|
|
129
|
+
});
|
|
130
|
+
}, [sendMessage]);
|
|
100
131
|
/**
|
|
101
132
|
* Listen for messages from iframe
|
|
102
133
|
*/
|
|
@@ -142,6 +173,8 @@ export function useInspector(iframeRef, callbacks, labels) {
|
|
|
142
173
|
sendMessage({
|
|
143
174
|
type: "TOGGLE_INSPECTOR",
|
|
144
175
|
active: false,
|
|
176
|
+
labels: labels,
|
|
177
|
+
theme: theme,
|
|
145
178
|
});
|
|
146
179
|
}
|
|
147
180
|
};
|
|
@@ -151,5 +184,6 @@ export function useInspector(iframeRef, callbacks, labels) {
|
|
|
151
184
|
toggleInspector,
|
|
152
185
|
startInspecting,
|
|
153
186
|
stopInspecting,
|
|
187
|
+
showContentInput,
|
|
154
188
|
};
|
|
155
189
|
}
|
package/dist/plugin.d.ts
CHANGED
|
@@ -22,12 +22,20 @@
|
|
|
22
22
|
* Plugin özellikler:
|
|
23
23
|
* - Element seçimi (click-to-select)
|
|
24
24
|
* - React Fiber üzerinden component bilgisi çıkarma
|
|
25
|
-
* - Text
|
|
26
|
-
* -
|
|
25
|
+
* - Text node algılama (isTextNode)
|
|
26
|
+
* - Dinamik content input (showContentInput ile kontrol edilir)
|
|
27
|
+
* - AI prompt gönderme (her zaman görünür)
|
|
28
|
+
* - data-inspector-ignore desteği (belirli elementleri ignore etme)
|
|
27
29
|
* - URL değişikliği takibi
|
|
28
30
|
* - JavaScript hata yakalama (error, promise rejection, console.error)
|
|
31
|
+
* - Özelleştirilebilir tema renkleri
|
|
29
32
|
* - Özelleştirilebilir dil desteği
|
|
30
33
|
*
|
|
34
|
+
* Kullanım:
|
|
35
|
+
* - Elementlere data-inspector-ignore attribute'u ekleyerek inspector'dan gizleyebilirsiniz
|
|
36
|
+
* - Control box her zaman prompt input gösterir
|
|
37
|
+
* - Content input sadece showContentInput(true) ile dinamik olarak eklenir
|
|
38
|
+
*
|
|
31
39
|
* Not: Bu plugin otomatik olarak template/iframe'e enjekte edilir.
|
|
32
40
|
* Ana app'te useInspector hook'u ile kontrol edilir.
|
|
33
41
|
*/
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA
|
|
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,CA6pBxC"}
|
package/dist/plugin.js
CHANGED
|
@@ -22,12 +22,20 @@
|
|
|
22
22
|
* Plugin özellikler:
|
|
23
23
|
* - Element seçimi (click-to-select)
|
|
24
24
|
* - React Fiber üzerinden component bilgisi çıkarma
|
|
25
|
-
* - Text
|
|
26
|
-
* -
|
|
25
|
+
* - Text node algılama (isTextNode)
|
|
26
|
+
* - Dinamik content input (showContentInput ile kontrol edilir)
|
|
27
|
+
* - AI prompt gönderme (her zaman görünür)
|
|
28
|
+
* - data-inspector-ignore desteği (belirli elementleri ignore etme)
|
|
27
29
|
* - URL değişikliği takibi
|
|
28
30
|
* - JavaScript hata yakalama (error, promise rejection, console.error)
|
|
31
|
+
* - Özelleştirilebilir tema renkleri
|
|
29
32
|
* - Özelleştirilebilir dil desteği
|
|
30
33
|
*
|
|
34
|
+
* Kullanım:
|
|
35
|
+
* - Elementlere data-inspector-ignore attribute'u ekleyerek inspector'dan gizleyebilirsiniz
|
|
36
|
+
* - Control box her zaman prompt input gösterir
|
|
37
|
+
* - Content input sadece showContentInput(true) ile dinamik olarak eklenir
|
|
38
|
+
*
|
|
31
39
|
* Not: Bu plugin otomatik olarak template/iframe'e enjekte edilir.
|
|
32
40
|
* Ana app'te useInspector hook'u ile kontrol edilir.
|
|
33
41
|
*/
|
|
@@ -62,6 +70,17 @@ export function inspectorPlugin() {
|
|
|
62
70
|
promptPlaceholder: 'Ask Promake for changes...'
|
|
63
71
|
};
|
|
64
72
|
|
|
73
|
+
// Customizable theme colors
|
|
74
|
+
let theme = {
|
|
75
|
+
backgroundColor: '#ffffff',
|
|
76
|
+
textColor: '#111827',
|
|
77
|
+
buttonColor: '#4417db',
|
|
78
|
+
buttonTextColor: '#ffffff',
|
|
79
|
+
inputBackgroundColor: '#f9fafb',
|
|
80
|
+
inputTextColor: '#111827',
|
|
81
|
+
inputBorderColor: '#d1d5db'
|
|
82
|
+
};
|
|
83
|
+
|
|
65
84
|
// Create overlay for highlighting
|
|
66
85
|
function createOverlay() {
|
|
67
86
|
if (overlay) return overlay;
|
|
@@ -95,6 +114,10 @@ export function inspectorPlugin() {
|
|
|
95
114
|
const isTextElement = element.textContent && element.children.length === 0;
|
|
96
115
|
const currentText = isTextElement ? element.textContent.trim() : '';
|
|
97
116
|
|
|
117
|
+
// Store text info in data attribute for later use
|
|
118
|
+
controlBox.setAttribute('data-is-text-element', isTextElement);
|
|
119
|
+
controlBox.setAttribute('data-current-text', currentText);
|
|
120
|
+
|
|
98
121
|
// Calculate position
|
|
99
122
|
const boxWidth = Math.max(320, Math.min(rect.width, 500));
|
|
100
123
|
const centerLeft = rect.left + (rect.width / 2) - (boxWidth / 2);
|
|
@@ -102,8 +125,8 @@ export function inspectorPlugin() {
|
|
|
102
125
|
const spaceBelow = viewportHeight - rect.bottom;
|
|
103
126
|
const spaceAbove = rect.top;
|
|
104
127
|
|
|
105
|
-
// Estimate box height
|
|
106
|
-
const estimatedBoxHeight =
|
|
128
|
+
// Estimate box height (only prompt input now, smaller)
|
|
129
|
+
const estimatedBoxHeight = 100;
|
|
107
130
|
|
|
108
131
|
// Show above if not enough space below
|
|
109
132
|
let topPosition;
|
|
@@ -122,7 +145,7 @@ export function inspectorPlugin() {
|
|
|
122
145
|
top: \${topPosition}px;
|
|
123
146
|
left: \${Math.max(10, centerLeft)}px;
|
|
124
147
|
width: \${boxWidth}px;
|
|
125
|
-
background:
|
|
148
|
+
background: \${theme.backgroundColor};
|
|
126
149
|
border: 1px solid #e5e7eb;
|
|
127
150
|
border-radius: 14px;
|
|
128
151
|
padding: 12px;
|
|
@@ -130,80 +153,33 @@ export function inspectorPlugin() {
|
|
|
130
153
|
z-index: 1000000;
|
|
131
154
|
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
132
155
|
font-size: 14px;
|
|
156
|
+
display: flex;
|
|
157
|
+
flex-direction: column;
|
|
158
|
+
gap: 0;
|
|
133
159
|
\`;
|
|
134
160
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (isTextElement) {
|
|
139
|
-
html += \`
|
|
140
|
-
<div style="margin-bottom: 12px;">
|
|
141
|
-
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px;">
|
|
142
|
-
<label style="font-weight: 500; color: #111827; font-size: 13px;">
|
|
143
|
-
\${labels.editText}
|
|
144
|
-
</label>
|
|
145
|
-
<button
|
|
146
|
-
id="inspector-close"
|
|
147
|
-
style="width: 32px; height: 32px; background: transparent; border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; border-radius: 14px; transition: all 0.2s; padding: 0; flex-shrink: 0;"
|
|
148
|
-
onmouseover="this.style.background='#f3f4f6';"
|
|
149
|
-
onmouseout="this.style.background='transparent';"
|
|
150
|
-
title="Close"
|
|
151
|
-
>
|
|
152
|
-
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
153
|
-
<path d="M12 4L4 12M4 4L12 12" stroke="#6b7280" stroke-width="2" stroke-linecap="round"/>
|
|
154
|
-
</svg>
|
|
155
|
-
</button>
|
|
156
|
-
</div>
|
|
157
|
-
<textarea
|
|
158
|
-
id="inspector-text-input"
|
|
159
|
-
style="width: 100%; padding: 10px 12px; border: 1px solid #d1d5db; border-radius: 14px; font-size: 14px; color: #111827; background: #f9fafb; transition: all 0.2s; resize: vertical; min-height: 60px; max-height: 200px; font-family: inherit; line-height: 1.5;"
|
|
160
|
-
placeholder="\${labels.textPlaceholder}"
|
|
161
|
-
onfocus="this.style.background='#ffffff'; this.style.borderColor='#9ca3af';"
|
|
162
|
-
onblur="this.style.background='#f9fafb'; this.style.borderColor='#d1d5db';"
|
|
163
|
-
>\${currentText}</textarea>
|
|
164
|
-
</div>
|
|
165
|
-
<div style="margin-bottom: 10px;">
|
|
166
|
-
<button
|
|
167
|
-
id="inspector-text-submit"
|
|
168
|
-
style="width: 100%; padding: 10px 16px; background: #4417db; color: white; border: none; border-radius: 14px; cursor: pointer; font-weight: 500; font-size: 14px; transition: all 0.2s;"
|
|
169
|
-
onmouseover="this.style.background='#3712af';"
|
|
170
|
-
onmouseout="this.style.background='#4417db';"
|
|
171
|
-
title="\${labels.updateText}"
|
|
172
|
-
>
|
|
173
|
-
\${labels.updateText}
|
|
174
|
-
</button>
|
|
175
|
-
</div>
|
|
176
|
-
<div style="border-top: 1px solid #e5e7eb; margin: 12px 0;"></div>
|
|
177
|
-
\`;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Prompt input and buttons (side by side)
|
|
181
|
-
html += \`
|
|
182
|
-
<div style="display: flex; gap: 8px; align-items: stretch;">
|
|
161
|
+
// Only prompt input and buttons (always shown)
|
|
162
|
+
let html = \`
|
|
163
|
+
<div id="inspector-prompt-section" style="display: flex; gap: 8px; align-items: stretch;">
|
|
183
164
|
<input
|
|
184
165
|
type="text"
|
|
185
166
|
id="inspector-prompt-input"
|
|
186
|
-
style="flex: 1; padding: 10px 12px; border: 1px solid
|
|
167
|
+
style="flex: 1; padding: 10px 12px; border: 1px solid \${theme.inputBorderColor}; border-radius: 14px; font-size: 14px; color: \${theme.inputTextColor}; background: \${theme.inputBackgroundColor}; transition: all 0.2s;"
|
|
187
168
|
placeholder="\${labels.promptPlaceholder}"
|
|
188
169
|
onfocus="this.style.background='#ffffff'; this.style.borderColor='#9ca3af';"
|
|
189
|
-
onblur="this.style.background='
|
|
170
|
+
onblur="this.style.background='\${theme.inputBackgroundColor}'; this.style.borderColor='\${theme.inputBorderColor}';"
|
|
190
171
|
/>
|
|
191
172
|
<button
|
|
192
173
|
id="inspector-prompt-submit"
|
|
193
|
-
style="width: 44px; height: 44px; padding: 0; background:
|
|
194
|
-
onmouseover="this.style.
|
|
195
|
-
onmouseout="this.style.
|
|
174
|
+
style="width: 44px; height: 44px; padding: 0; background: \${theme.buttonColor}; color: \${theme.buttonTextColor}; border: none; border-radius: 14px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s; flex-shrink: 0;"
|
|
175
|
+
onmouseover="this.style.opacity='0.85';"
|
|
176
|
+
onmouseout="this.style.opacity='1';"
|
|
196
177
|
title="Send"
|
|
197
178
|
>
|
|
198
179
|
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
199
180
|
<path d="M10 15V5M10 5L5 10M10 5L15 10" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
200
181
|
</svg>
|
|
201
182
|
</button>
|
|
202
|
-
\`;
|
|
203
|
-
|
|
204
|
-
// Close button next to send button (only for non-text elements)
|
|
205
|
-
if (!isTextElement) {
|
|
206
|
-
html += \`
|
|
207
183
|
<button
|
|
208
184
|
id="inspector-close"
|
|
209
185
|
style="padding: 10px; background: #f3f4f6; border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; border-radius: 14px; transition: all 0.2s;"
|
|
@@ -215,10 +191,8 @@ export function inspectorPlugin() {
|
|
|
215
191
|
<path d="M12 4L4 12M4 4L12 12" stroke="#6b7280" stroke-width="2" stroke-linecap="round"/>
|
|
216
192
|
</svg>
|
|
217
193
|
</button>
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
html += \`</div>\`;
|
|
194
|
+
</div>
|
|
195
|
+
\`;
|
|
222
196
|
|
|
223
197
|
controlBox.innerHTML = html;
|
|
224
198
|
document.body.appendChild(controlBox);
|
|
@@ -257,10 +231,69 @@ export function inspectorPlugin() {
|
|
|
257
231
|
}
|
|
258
232
|
});
|
|
259
233
|
|
|
260
|
-
//
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
234
|
+
// Prevent prompt input events from bubbling
|
|
235
|
+
promptInput.addEventListener('click', (e) => e.stopPropagation());
|
|
236
|
+
|
|
237
|
+
// Close button
|
|
238
|
+
closeBtn.addEventListener('click', (e) => {
|
|
239
|
+
e.stopPropagation();
|
|
240
|
+
unpauseInspection();
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Focus prompt input
|
|
244
|
+
setTimeout(() => promptInput.focus(), 100);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Show or hide content input section
|
|
248
|
+
function toggleContentInput(show) {
|
|
249
|
+
if (!controlBox) return;
|
|
250
|
+
|
|
251
|
+
const isTextElement = controlBox.getAttribute('data-is-text-element') === 'true';
|
|
252
|
+
const currentText = controlBox.getAttribute('data-current-text') || '';
|
|
253
|
+
|
|
254
|
+
// Check if content input already exists
|
|
255
|
+
let contentSection = controlBox.querySelector('#inspector-content-section');
|
|
256
|
+
|
|
257
|
+
if (show && !contentSection && isTextElement) {
|
|
258
|
+
// Create content input section
|
|
259
|
+
contentSection = document.createElement('div');
|
|
260
|
+
contentSection.id = 'inspector-content-section';
|
|
261
|
+
contentSection.style.cssText = 'margin-bottom: 12px;';
|
|
262
|
+
|
|
263
|
+
contentSection.innerHTML = \`
|
|
264
|
+
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px;">
|
|
265
|
+
<label style="font-weight: 500; color: \${theme.textColor}; font-size: 13px;">
|
|
266
|
+
\${labels.editText}
|
|
267
|
+
</label>
|
|
268
|
+
</div>
|
|
269
|
+
<textarea
|
|
270
|
+
id="inspector-text-input"
|
|
271
|
+
style="width: 100%; padding: 10px 12px; border: 1px solid \${theme.inputBorderColor}; border-radius: 14px; font-size: 14px; color: \${theme.inputTextColor}; background: \${theme.inputBackgroundColor}; transition: all 0.2s; resize: vertical; min-height: 60px; max-height: 200px; font-family: inherit; line-height: 1.5;"
|
|
272
|
+
placeholder="\${labels.textPlaceholder}"
|
|
273
|
+
onfocus="this.style.background='#ffffff'; this.style.borderColor='#9ca3af';"
|
|
274
|
+
onblur="this.style.background='\${theme.inputBackgroundColor}'; this.style.borderColor='\${theme.inputBorderColor}';"
|
|
275
|
+
>\${currentText}</textarea>
|
|
276
|
+
<div style="margin-top: 8px;">
|
|
277
|
+
<button
|
|
278
|
+
id="inspector-text-submit"
|
|
279
|
+
style="width: 100%; padding: 10px 16px; background: \${theme.buttonColor}; color: \${theme.buttonTextColor}; border: none; border-radius: 14px; cursor: pointer; font-weight: 500; font-size: 14px; transition: all 0.2s;"
|
|
280
|
+
onmouseover="this.style.opacity='0.85';"
|
|
281
|
+
onmouseout="this.style.opacity='1';"
|
|
282
|
+
title="\${labels.updateText}"
|
|
283
|
+
>
|
|
284
|
+
\${labels.updateText}
|
|
285
|
+
</button>
|
|
286
|
+
</div>
|
|
287
|
+
<div style="border-top: 1px solid #e5e7eb; margin-top: 12px; margin-bottom: 12px;"></div>
|
|
288
|
+
\`;
|
|
289
|
+
|
|
290
|
+
// Insert at the beginning of controlBox
|
|
291
|
+
const promptSection = controlBox.querySelector('#inspector-prompt-section');
|
|
292
|
+
controlBox.insertBefore(contentSection, promptSection);
|
|
293
|
+
|
|
294
|
+
// Add event listeners
|
|
295
|
+
const textInput = contentSection.querySelector('#inspector-text-input');
|
|
296
|
+
const textSubmit = contentSection.querySelector('#inspector-text-submit');
|
|
264
297
|
|
|
265
298
|
textSubmit.addEventListener('click', (e) => {
|
|
266
299
|
e.stopPropagation();
|
|
@@ -271,7 +304,7 @@ export function inspectorPlugin() {
|
|
|
271
304
|
data: {
|
|
272
305
|
text: newText,
|
|
273
306
|
originalText: currentText,
|
|
274
|
-
element:
|
|
307
|
+
element: selectedElementData
|
|
275
308
|
}
|
|
276
309
|
}, '*');
|
|
277
310
|
}
|
|
@@ -282,7 +315,8 @@ export function inspectorPlugin() {
|
|
|
282
315
|
});
|
|
283
316
|
|
|
284
317
|
textInput.addEventListener('keypress', (e) => {
|
|
285
|
-
if (e.key === 'Enter') {
|
|
318
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
319
|
+
e.preventDefault();
|
|
286
320
|
e.stopPropagation();
|
|
287
321
|
textSubmit.click();
|
|
288
322
|
}
|
|
@@ -290,19 +324,52 @@ export function inspectorPlugin() {
|
|
|
290
324
|
|
|
291
325
|
// Prevent input events from bubbling
|
|
292
326
|
textInput.addEventListener('click', (e) => e.stopPropagation());
|
|
327
|
+
|
|
328
|
+
// Focus text input
|
|
329
|
+
setTimeout(() => textInput.focus(), 100);
|
|
330
|
+
|
|
331
|
+
// Adjust box height
|
|
332
|
+
adjustControlBoxPosition();
|
|
333
|
+
} else if (!show && contentSection) {
|
|
334
|
+
// Remove content input section
|
|
335
|
+
contentSection.remove();
|
|
336
|
+
|
|
337
|
+
// Refocus prompt input
|
|
338
|
+
const promptInput = controlBox.querySelector('#inspector-prompt-input');
|
|
339
|
+
if (promptInput) {
|
|
340
|
+
setTimeout(() => promptInput.focus(), 100);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Adjust box height
|
|
344
|
+
adjustControlBoxPosition();
|
|
293
345
|
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Adjust control box position based on content
|
|
349
|
+
function adjustControlBoxPosition() {
|
|
350
|
+
if (!controlBox || !selectedElement) return;
|
|
294
351
|
|
|
295
|
-
|
|
296
|
-
|
|
352
|
+
const rect = selectedElement.getBoundingClientRect();
|
|
353
|
+
const boxWidth = Math.max(320, Math.min(rect.width, 500));
|
|
354
|
+
const centerLeft = rect.left + (rect.width / 2) - (boxWidth / 2);
|
|
355
|
+
const viewportHeight = window.innerHeight;
|
|
356
|
+
const spaceBelow = viewportHeight - rect.bottom;
|
|
357
|
+
const spaceAbove = rect.top;
|
|
297
358
|
|
|
298
|
-
//
|
|
299
|
-
|
|
300
|
-
e.stopPropagation();
|
|
301
|
-
unpauseInspection();
|
|
302
|
-
});
|
|
359
|
+
// Get actual box height
|
|
360
|
+
const boxHeight = controlBox.offsetHeight;
|
|
303
361
|
|
|
304
|
-
//
|
|
305
|
-
|
|
362
|
+
// Show above if not enough space below
|
|
363
|
+
let topPosition;
|
|
364
|
+
if (spaceBelow < boxHeight + 20 && spaceAbove > spaceBelow) {
|
|
365
|
+
topPosition = rect.top - boxHeight - 10;
|
|
366
|
+
controlBox.setAttribute('data-position', 'above');
|
|
367
|
+
} else {
|
|
368
|
+
topPosition = rect.bottom + 10;
|
|
369
|
+
controlBox.setAttribute('data-position', 'below');
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
controlBox.style.top = topPosition + 'px';
|
|
306
373
|
}
|
|
307
374
|
|
|
308
375
|
// Pause inspection
|
|
@@ -400,8 +467,9 @@ export function inspectorPlugin() {
|
|
|
400
467
|
|
|
401
468
|
const rect = element.getBoundingClientRect();
|
|
402
469
|
overlay.style.display = 'block';
|
|
403
|
-
|
|
404
|
-
overlay.style.
|
|
470
|
+
// position: fixed uses viewport coordinates, no need for scroll offset
|
|
471
|
+
overlay.style.top = rect.top + 'px';
|
|
472
|
+
overlay.style.left = rect.left + 'px';
|
|
405
473
|
overlay.style.width = rect.width + 'px';
|
|
406
474
|
overlay.style.height = rect.height + 'px';
|
|
407
475
|
}
|
|
@@ -417,10 +485,13 @@ export function inspectorPlugin() {
|
|
|
417
485
|
function handleMouseMove(e) {
|
|
418
486
|
if (!inspectMode || isPaused) return;
|
|
419
487
|
|
|
420
|
-
// Ignore control box and
|
|
488
|
+
// Ignore control box, overlay, and elements with data-inspector-ignore
|
|
421
489
|
if (e.target.id === 'inspector-control-box' ||
|
|
422
490
|
e.target.closest('#inspector-control-box') ||
|
|
423
|
-
e.target.id === 'inspector-overlay'
|
|
491
|
+
e.target.id === 'inspector-overlay' ||
|
|
492
|
+
e.target.hasAttribute('data-inspector-ignore') ||
|
|
493
|
+
e.target.closest('[data-inspector-ignore]')) {
|
|
494
|
+
clearHighlight();
|
|
424
495
|
return;
|
|
425
496
|
}
|
|
426
497
|
|
|
@@ -432,16 +503,19 @@ export function inspectorPlugin() {
|
|
|
432
503
|
function handleClick(e) {
|
|
433
504
|
if (!inspectMode) return;
|
|
434
505
|
|
|
435
|
-
// Ignore clicks on control box
|
|
506
|
+
// Ignore clicks on control box and elements with data-inspector-ignore
|
|
436
507
|
if (e.target.id === 'inspector-control-box' ||
|
|
437
|
-
e.target.closest('#inspector-control-box')
|
|
508
|
+
e.target.closest('#inspector-control-box') ||
|
|
509
|
+
e.target.hasAttribute('data-inspector-ignore') ||
|
|
510
|
+
e.target.closest('[data-inspector-ignore]')) {
|
|
438
511
|
return;
|
|
439
512
|
}
|
|
440
513
|
|
|
441
|
-
// If already paused,
|
|
514
|
+
// If already paused, unpause first then ignore this click
|
|
442
515
|
if (isPaused) {
|
|
443
516
|
e.preventDefault();
|
|
444
517
|
e.stopPropagation();
|
|
518
|
+
unpauseInspection();
|
|
445
519
|
return;
|
|
446
520
|
}
|
|
447
521
|
|
|
@@ -452,6 +526,9 @@ export function inspectorPlugin() {
|
|
|
452
526
|
const fiber = getReactFiber(element);
|
|
453
527
|
const componentInfo = fiber ? getComponentInfo(fiber) : null;
|
|
454
528
|
|
|
529
|
+
// Check if element is a text node
|
|
530
|
+
const isTextNode = element.textContent && element.children.length === 0;
|
|
531
|
+
|
|
455
532
|
const elementData = {
|
|
456
533
|
tagName: element.tagName,
|
|
457
534
|
className: element.className,
|
|
@@ -462,7 +539,8 @@ export function inspectorPlugin() {
|
|
|
462
539
|
left: element.getBoundingClientRect().left,
|
|
463
540
|
width: element.getBoundingClientRect().width,
|
|
464
541
|
height: element.getBoundingClientRect().height
|
|
465
|
-
}
|
|
542
|
+
},
|
|
543
|
+
isTextNode: isTextNode
|
|
466
544
|
};
|
|
467
545
|
|
|
468
546
|
// Send info to parent window
|
|
@@ -476,7 +554,8 @@ export function inspectorPlugin() {
|
|
|
476
554
|
// Log for debugging
|
|
477
555
|
console.log('Element selected:', {
|
|
478
556
|
element,
|
|
479
|
-
componentInfo
|
|
557
|
+
componentInfo,
|
|
558
|
+
isTextNode
|
|
480
559
|
});
|
|
481
560
|
|
|
482
561
|
// Pause inspection and show control box
|
|
@@ -511,6 +590,13 @@ export function inspectorPlugin() {
|
|
|
511
590
|
if (event.data.labels) {
|
|
512
591
|
labels = { ...labels, ...event.data.labels };
|
|
513
592
|
}
|
|
593
|
+
|
|
594
|
+
// Update theme if provided
|
|
595
|
+
if (event.data.theme) {
|
|
596
|
+
theme = { ...theme, ...event.data.theme };
|
|
597
|
+
}
|
|
598
|
+
} else if (event.data.type === 'SHOW_CONTENT_INPUT') {
|
|
599
|
+
toggleContentInput(event.data.show);
|
|
514
600
|
}
|
|
515
601
|
});
|
|
516
602
|
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared types for @
|
|
2
|
+
* Shared types for @promakeai/inspector
|
|
3
|
+
*
|
|
4
|
+
* Bu dosya inspector için kullanılan tüm type'ları içerir.
|
|
5
|
+
* Hook ve plugin arasında paylaşılan type'lar burada tanımlanır.
|
|
3
6
|
*/
|
|
4
7
|
export interface ComponentInfo {
|
|
5
8
|
componentName: string;
|
|
@@ -19,6 +22,7 @@ export interface SelectedElementData {
|
|
|
19
22
|
id: string;
|
|
20
23
|
component: ComponentInfo | null;
|
|
21
24
|
position: ElementPosition;
|
|
25
|
+
isTextNode?: boolean;
|
|
22
26
|
}
|
|
23
27
|
export interface UrlChangeData {
|
|
24
28
|
url: string;
|
|
@@ -50,6 +54,18 @@ export interface InspectorLabels {
|
|
|
50
54
|
updateText?: string;
|
|
51
55
|
promptPlaceholder?: string;
|
|
52
56
|
}
|
|
57
|
+
export interface InspectorTheme {
|
|
58
|
+
backgroundColor?: string;
|
|
59
|
+
textColor?: string;
|
|
60
|
+
buttonColor?: string;
|
|
61
|
+
buttonTextColor?: string;
|
|
62
|
+
inputBackgroundColor?: string;
|
|
63
|
+
inputTextColor?: string;
|
|
64
|
+
inputBorderColor?: string;
|
|
65
|
+
}
|
|
66
|
+
export interface ContentInputRequestData {
|
|
67
|
+
show: boolean;
|
|
68
|
+
}
|
|
53
69
|
export interface InspectorCallbacks {
|
|
54
70
|
onElementSelected?: (data: SelectedElementData) => void;
|
|
55
71
|
onUrlChange?: (data: UrlChangeData) => void;
|
|
@@ -62,5 +78,6 @@ export interface UseInspectorReturn {
|
|
|
62
78
|
toggleInspector: (active?: boolean) => void;
|
|
63
79
|
startInspecting: () => void;
|
|
64
80
|
stopInspecting: () => void;
|
|
81
|
+
showContentInput: (show: boolean) => void;
|
|
65
82
|
}
|
|
66
83
|
//# 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":"AAAA
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
CHANGED
package/package.json
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promakeai/inspector",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Visual element inspector for React apps in iframe with AI prompt support",
|
|
5
5
|
"author": "Promake",
|
|
6
|
+
"license": "MIT",
|
|
6
7
|
"type": "module",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/promakeai/inspector.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/promakeai/inspector/issues"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/promakeai/inspector#readme",
|
|
7
16
|
"main": "./dist/hook.js",
|
|
8
17
|
"module": "./dist/hook.js",
|
|
9
18
|
"types": "./dist/hook.d.ts",
|
|
@@ -24,7 +33,8 @@
|
|
|
24
33
|
],
|
|
25
34
|
"scripts": {
|
|
26
35
|
"build": "tsc && tsc -p tsconfig.plugin.json",
|
|
27
|
-
"prepublishOnly": "npm run build"
|
|
36
|
+
"prepublishOnly": "npm run build",
|
|
37
|
+
"publish": "npm run build && npm publish --access public"
|
|
28
38
|
},
|
|
29
39
|
"keywords": [
|
|
30
40
|
"vite",
|
|
@@ -38,8 +48,8 @@
|
|
|
38
48
|
"prompt"
|
|
39
49
|
],
|
|
40
50
|
"peerDependencies": {
|
|
41
|
-
"react": "
|
|
42
|
-
"vite": "
|
|
51
|
+
"react": ">18.0.0",
|
|
52
|
+
"vite": ">5.0.0"
|
|
43
53
|
},
|
|
44
54
|
"devDependencies": {
|
|
45
55
|
"@types/react": "^18.2.0",
|