@promakeai/inspector 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,316 @@
1
+ # @promakeai/inspector
2
+
3
+ Visual element inspector for React apps in iframe with AI prompt support. Perfect for visual editors, page builders, and low-code platforms.
4
+
5
+ ## Features
6
+
7
+ - 🎯 **Element Selection** - Click to select any element on the page
8
+ - 🔍 **React Component Detection** - Automatically detects React components via Fiber
9
+ - 📝 **Text Editing** - Edit text content directly
10
+ - 🤖 **AI Prompt Support** - Send prompts to AI for element modifications
11
+ - 🎨 **Visual Feedback** - Highlight selected elements with overlay
12
+ - 🔒 **Pause Mode** - Lock selection and disable scroll while editing
13
+ - 🌐 **URL Tracking** - Monitor navigation changes in iframe
14
+ - 🐛 **Error Tracking** - Capture JavaScript errors, promise rejections, and console errors
15
+ - 🎨 **Customizable Labels** - Multi-language support
16
+ - 📦 **TypeScript** - Full type safety
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install @promakeai/inspector
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ### Template Side (iframe/vite app)
27
+
28
+ Add the plugin to your `vite.config.ts`:
29
+
30
+ ```typescript
31
+ import { defineConfig } from "vite";
32
+ import react from "@vitejs/plugin-react";
33
+ import { inspectorPlugin } from "@promakeai/inspector/plugin";
34
+
35
+ export default defineConfig({
36
+ plugins: [react(), inspectorPlugin()],
37
+ });
38
+ ```
39
+
40
+ ### Parent App Side
41
+
42
+ Use the hook in your React app:
43
+
44
+ ```typescript
45
+ import { useRef } from "react";
46
+ import { useInspector } from "@promakeai/inspector/hook";
47
+
48
+ function App() {
49
+ const iframeRef = useRef<HTMLIFrameElement>(null);
50
+
51
+ const { isInspecting, toggleInspector } = useInspector(
52
+ iframeRef,
53
+ {
54
+ onElementSelected: (data) => {
55
+ console.log("Element selected:", data);
56
+ // Access component info
57
+ console.log(data.component?.fileName);
58
+ console.log(data.component?.lineNumber);
59
+ },
60
+ onPromptSubmitted: (data) => {
61
+ console.log("AI Prompt:", data.prompt);
62
+ // Send to your AI service
63
+ fetch("/api/ai-edit", {
64
+ method: "POST",
65
+ body: JSON.stringify({
66
+ prompt: data.prompt,
67
+ element: data.element,
68
+ }),
69
+ });
70
+ },
71
+ onTextUpdated: (data) => {
72
+ console.log("Text updated:", data.text);
73
+ // Save to backend
74
+ },
75
+ onUrlChange: (data) => {
76
+ console.log("URL changed:", data.pathname);
77
+ },
78
+ onError: (data) => {
79
+ console.error("Error in template:", data);
80
+ // Send to error tracking service (Sentry, etc.)
81
+ // Sentry.captureException(new Error(data.message));
82
+ },
83
+ },
84
+ {
85
+ // Optional: Custom labels
86
+ editText: "Edit Text",
87
+ textPlaceholder: "Enter text...",
88
+ updateText: "Update",
89
+ promptPlaceholder: "Ask AI for changes...",
90
+ }
91
+ );
92
+
93
+ return (
94
+ <div>
95
+ <button onClick={() => toggleInspector()}>
96
+ {isInspecting ? "Stop Inspector" : "Start Inspector"}
97
+ </button>
98
+
99
+ <iframe ref={iframeRef} src="http://localhost:5173" />
100
+ </div>
101
+ );
102
+ }
103
+ ```
104
+
105
+ ## API
106
+
107
+ ### `useInspector(iframeRef, callbacks?, labels?)`
108
+
109
+ #### Parameters
110
+
111
+ - **iframeRef**: `RefObject<HTMLIFrameElement>` - Reference to the iframe element
112
+ - **callbacks**: `InspectorCallbacks` (optional)
113
+ - `onElementSelected`: Called when an element is selected
114
+ - `onPromptSubmitted`: Called when AI prompt is submitted
115
+ - `onTextUpdated`: Called when text content is updated
116
+ - `onUrlChange`: Called when URL changes in iframe
117
+ - `onError`: Called when an error occurs in iframe
118
+ - **labels**: `InspectorLabels` (optional)
119
+ - `editText`: Label for text edit section
120
+ - `textPlaceholder`: Placeholder for text input
121
+ - `updateText`: Label for update button
122
+ - `promptPlaceholder`: Placeholder for prompt input
123
+
124
+ #### Returns
125
+
126
+ - **isInspecting**: `boolean` - Current inspection state
127
+ - **toggleInspector**: `(active?: boolean) => void` - Toggle inspection mode
128
+ - **startInspecting**: `() => void` - Start inspecting
129
+ - **stopInspecting**: `() => void` - Stop inspecting
130
+
131
+ ## Types
132
+
133
+ ### `SelectedElementData`
134
+
135
+ ```typescript
136
+ interface SelectedElementData {
137
+ tagName: string;
138
+ className: string;
139
+ id: string;
140
+ component: ComponentInfo | null;
141
+ position: ElementPosition;
142
+ }
143
+ ```
144
+
145
+ ### `ComponentInfo`
146
+
147
+ ```typescript
148
+ interface ComponentInfo {
149
+ componentName: string;
150
+ fileName: string;
151
+ lineNumber: number;
152
+ columnNumber: number;
153
+ }
154
+ ```
155
+
156
+ ### `PromptSubmittedData`
157
+
158
+ ```typescript
159
+ interface PromptSubmittedData {
160
+ prompt: string;
161
+ element: SelectedElementData;
162
+ }
163
+ ```
164
+
165
+ ### `TextUpdatedData`
166
+
167
+ ```typescript
168
+ interface TextUpdatedData {
169
+ text: string;
170
+ originalText: string;
171
+ element: SelectedElementData;
172
+ }
173
+ ```
174
+
175
+ ### `ErrorData`
176
+
177
+ ```typescript
178
+ interface ErrorData {
179
+ type: "javascript" | "promise" | "console";
180
+ message: string;
181
+ stack?: string;
182
+ fileName?: string;
183
+ lineNumber?: number;
184
+ columnNumber?: number;
185
+ timestamp: number;
186
+ }
187
+ ```
188
+
189
+ ## Features in Detail
190
+
191
+ ### Element Selection
192
+
193
+ Click any element to select it. The inspector will:
194
+
195
+ - Show a blue highlight overlay
196
+ - Display a control box below/above the element
197
+ - Pause scrolling
198
+ - Extract React component information
199
+
200
+ ### Text Editing
201
+
202
+ For text-only elements:
203
+
204
+ - Textarea appears with current text
205
+ - Edit and click "Update"
206
+ - Event is sent to parent app
207
+
208
+ ### AI Prompts
209
+
210
+ For any element:
211
+
212
+ - Enter a prompt describing desired changes
213
+ - Click the send button (↑)
214
+ - Prompt + element data sent to parent app
215
+
216
+ ### Component Detection
217
+
218
+ Automatically extracts:
219
+
220
+ - Component name
221
+ - Source file path
222
+ - Line number
223
+ - Column number
224
+
225
+ **Note**: Works only in development mode with React DevTools enabled.
226
+
227
+ ### Error Tracking
228
+
229
+ Automatically captures and reports:
230
+
231
+ - **JavaScript Errors**: Runtime errors with stack traces
232
+ - **Promise Rejections**: Unhandled promise rejections
233
+ - **Console Errors**: All `console.error()` calls
234
+
235
+ All errors are sent to the parent app with detailed information including:
236
+
237
+ - Error type and message
238
+ - Stack trace
239
+ - File name and line number
240
+ - Timestamp
241
+
242
+ Perfect for integrating with error tracking services like Sentry, LogRocket, or custom logging solutions.
243
+
244
+ ## Examples
245
+
246
+ ### Multi-language Support
247
+
248
+ ```typescript
249
+ const { toggleInspector } = useInspector(iframeRef, callbacks, {
250
+ editText: "Metni Düzenle",
251
+ textPlaceholder: "Metin girin...",
252
+ updateText: "Güncelle",
253
+ promptPlaceholder: "AI için komut girin...",
254
+ });
255
+ ```
256
+
257
+ ### Open File in Editor
258
+
259
+ ```typescript
260
+ onElementSelected: async (data) => {
261
+ if (data.component) {
262
+ await fetch("/api/open-in-editor", {
263
+ method: "POST",
264
+ body: JSON.stringify({
265
+ fileName: data.component.fileName,
266
+ lineNumber: data.component.lineNumber,
267
+ }),
268
+ });
269
+ }
270
+ };
271
+ ```
272
+
273
+ ### Error Tracking with Sentry
274
+
275
+ ```typescript
276
+ import * as Sentry from "@sentry/react";
277
+
278
+ const { toggleInspector } = useInspector(iframeRef, {
279
+ onError: (data) => {
280
+ // Send to Sentry
281
+ Sentry.captureException(new Error(data.message), {
282
+ extra: {
283
+ type: data.type,
284
+ stack: data.stack,
285
+ fileName: data.fileName,
286
+ lineNumber: data.lineNumber,
287
+ timestamp: data.timestamp,
288
+ },
289
+ });
290
+ },
291
+ });
292
+ ```
293
+
294
+ ## Browser Support
295
+
296
+ - Chrome/Edge: ✅
297
+ - Firefox: ✅
298
+ - Safari: ✅
299
+
300
+ ## Requirements
301
+
302
+ - Vite 5.0+
303
+ - React 18.0+
304
+ - TypeScript (recommended)
305
+
306
+ ## License
307
+
308
+ MIT
309
+
310
+ ## Contributing
311
+
312
+ Issues and pull requests are welcome!
313
+
314
+ ## Author
315
+
316
+ Promake
package/dist/hook.d.ts ADDED
@@ -0,0 +1,61 @@
1
+ /**
2
+ * useInspector Hook
3
+ *
4
+ * Ana app'ten template iframe'ine inspector komutları göndermek ve
5
+ * gelen olayları dinlemek için kullanılır.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * import { useRef } from 'react';
10
+ * import { useInspector } from '@promakeai/inspector/hook';
11
+ *
12
+ * function App() {
13
+ * const iframeRef = useRef<HTMLIFrameElement>(null);
14
+ *
15
+ * const { isInspecting, toggleInspector } = useInspector(
16
+ * iframeRef,
17
+ * {
18
+ * onElementSelected: (data) => {
19
+ * console.log('Element selected:', data);
20
+ * },
21
+ * onPromptSubmitted: (data) => {
22
+ * console.log('AI Prompt:', data.prompt);
23
+ * // Send to AI
24
+ * fetch('/api/ai-edit', {
25
+ * method: 'POST',
26
+ * body: JSON.stringify(data)
27
+ * });
28
+ * },
29
+ * onTextUpdated: (data) => {
30
+ * console.log('Text updated:', data.text);
31
+ * },
32
+ * onError: (data) => {
33
+ * console.error('Error:', data);
34
+ * // Send to error tracking service
35
+ * }
36
+ * },
37
+ * {
38
+ * // Optional: Custom labels (defaults to English)
39
+ * editText: 'Edit Text',
40
+ * textPlaceholder: 'Enter text...',
41
+ * updateText: 'Update',
42
+ * promptPlaceholder: 'Ask AI for changes...'
43
+ * }
44
+ * );
45
+ *
46
+ * return (
47
+ * <div>
48
+ * <button onClick={() => toggleInspector()}>
49
+ * {isInspecting ? 'Stop' : 'Start'} Inspector
50
+ * </button>
51
+ * <iframe ref={iframeRef} src="http://localhost:5173" />
52
+ * </div>
53
+ * );
54
+ * }
55
+ * ```
56
+ */
57
+ 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;
60
+ export type * from "./types";
61
+ //# sourceMappingURL=hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../src/hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAEH,OAAO,EAAoC,SAAS,EAAE,MAAM,OAAO,CAAC;AACpE,OAAO,KAAK,EACV,kBAAkB,EAClB,eAAe,EACf,kBAAkB,EAMnB,MAAM,SAAS,CAAC;AA8BjB,wBAAgB,YAAY,CAC1B,SAAS,EAAE,SAAS,CAAC,iBAAiB,CAAC,EACvC,SAAS,CAAC,EAAE,kBAAkB,EAC9B,MAAM,CAAC,EAAE,eAAe,GACvB,kBAAkB,CAwHpB;AAGD,mBAAmB,SAAS,CAAC"}
package/dist/hook.js ADDED
@@ -0,0 +1,155 @@
1
+ /**
2
+ * useInspector Hook
3
+ *
4
+ * Ana app'ten template iframe'ine inspector komutları göndermek ve
5
+ * gelen olayları dinlemek için kullanılır.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * import { useRef } from 'react';
10
+ * import { useInspector } from '@promakeai/inspector/hook';
11
+ *
12
+ * function App() {
13
+ * const iframeRef = useRef<HTMLIFrameElement>(null);
14
+ *
15
+ * const { isInspecting, toggleInspector } = useInspector(
16
+ * iframeRef,
17
+ * {
18
+ * onElementSelected: (data) => {
19
+ * console.log('Element selected:', data);
20
+ * },
21
+ * onPromptSubmitted: (data) => {
22
+ * console.log('AI Prompt:', data.prompt);
23
+ * // Send to AI
24
+ * fetch('/api/ai-edit', {
25
+ * method: 'POST',
26
+ * body: JSON.stringify(data)
27
+ * });
28
+ * },
29
+ * onTextUpdated: (data) => {
30
+ * console.log('Text updated:', data.text);
31
+ * },
32
+ * onError: (data) => {
33
+ * console.error('Error:', data);
34
+ * // Send to error tracking service
35
+ * }
36
+ * },
37
+ * {
38
+ * // Optional: Custom labels (defaults to English)
39
+ * editText: 'Edit Text',
40
+ * textPlaceholder: 'Enter text...',
41
+ * updateText: 'Update',
42
+ * promptPlaceholder: 'Ask AI for changes...'
43
+ * }
44
+ * );
45
+ *
46
+ * return (
47
+ * <div>
48
+ * <button onClick={() => toggleInspector()}>
49
+ * {isInspecting ? 'Stop' : 'Start'} Inspector
50
+ * </button>
51
+ * <iframe ref={iframeRef} src="http://localhost:5173" />
52
+ * </div>
53
+ * );
54
+ * }
55
+ * ```
56
+ */
57
+ import { useEffect, useState, useCallback } from "react";
58
+ export function useInspector(iframeRef, callbacks, labels) {
59
+ const [isInspecting, setIsInspecting] = useState(false);
60
+ /**
61
+ * Send message to iframe
62
+ */
63
+ const sendMessage = useCallback((message) => {
64
+ const iframe = iframeRef.current;
65
+ if (!iframe || !iframe.contentWindow) {
66
+ console.warn("Inspector: iframe not found or not loaded");
67
+ return;
68
+ }
69
+ try {
70
+ iframe.contentWindow.postMessage(message, "*");
71
+ }
72
+ catch (error) {
73
+ console.error("Inspector: Failed to send message:", error);
74
+ }
75
+ }, [iframeRef]);
76
+ /**
77
+ * Toggle inspector mode
78
+ */
79
+ const toggleInspector = useCallback((active) => {
80
+ const newState = active !== undefined ? active : !isInspecting;
81
+ setIsInspecting(newState);
82
+ sendMessage({
83
+ type: "TOGGLE_INSPECTOR",
84
+ active: newState,
85
+ labels: labels,
86
+ });
87
+ }, [isInspecting, sendMessage, labels]);
88
+ /**
89
+ * Start inspecting
90
+ */
91
+ const startInspecting = useCallback(() => {
92
+ toggleInspector(true);
93
+ }, [toggleInspector]);
94
+ /**
95
+ * Stop inspecting
96
+ */
97
+ const stopInspecting = useCallback(() => {
98
+ toggleInspector(false);
99
+ }, [toggleInspector]);
100
+ /**
101
+ * Listen for messages from iframe
102
+ */
103
+ useEffect(() => {
104
+ const handleMessage = (event) => {
105
+ // Security: Only handle expected message types
106
+ if (!event.data || typeof event.data.type !== "string") {
107
+ return;
108
+ }
109
+ const { type, data } = event.data;
110
+ switch (type) {
111
+ case "INSPECTOR_ELEMENT_SELECTED":
112
+ callbacks?.onElementSelected?.(data);
113
+ break;
114
+ case "URL_CHANGED":
115
+ callbacks?.onUrlChange?.(data);
116
+ break;
117
+ case "INSPECTOR_PROMPT_SUBMITTED":
118
+ callbacks?.onPromptSubmitted?.(data);
119
+ break;
120
+ case "INSPECTOR_TEXT_UPDATED":
121
+ callbacks?.onTextUpdated?.(data);
122
+ break;
123
+ case "INSPECTOR_ERROR":
124
+ callbacks?.onError?.(data);
125
+ break;
126
+ default:
127
+ // Unknown message type - ignore
128
+ break;
129
+ }
130
+ };
131
+ window.addEventListener("message", handleMessage);
132
+ return () => {
133
+ window.removeEventListener("message", handleMessage);
134
+ };
135
+ }, [callbacks]);
136
+ /**
137
+ * Cleanup: Turn off inspector on unmount
138
+ */
139
+ useEffect(() => {
140
+ return () => {
141
+ if (isInspecting) {
142
+ sendMessage({
143
+ type: "TOGGLE_INSPECTOR",
144
+ active: false,
145
+ });
146
+ }
147
+ };
148
+ }, [isInspecting, sendMessage]);
149
+ return {
150
+ isInspecting,
151
+ toggleInspector,
152
+ startInspecting,
153
+ stopInspecting,
154
+ };
155
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Vite Inspector Plugin
3
+ *
4
+ * Template/iframe tarafında çalışır. Sayfaya element seçme, React component
5
+ * bilgisi çıkarma, hata yakalama ve AI prompt gönderme özellikleri ekler.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * // vite.config.ts
10
+ * import { defineConfig } from 'vite';
11
+ * import react from '@vitejs/plugin-react';
12
+ * import { inspectorPlugin } from '@promakeai/inspector/plugin';
13
+ *
14
+ * export default defineConfig({
15
+ * plugins: [
16
+ * react(),
17
+ * inspectorPlugin()
18
+ * ]
19
+ * });
20
+ * ```
21
+ *
22
+ * Plugin özellikler:
23
+ * - Element seçimi (click-to-select)
24
+ * - React Fiber üzerinden component bilgisi çıkarma
25
+ * - Text editing desteği
26
+ * - AI prompt gönderme
27
+ * - URL değişikliği takibi
28
+ * - JavaScript hata yakalama (error, promise rejection, console.error)
29
+ * - Özelleştirilebilir dil desteği
30
+ *
31
+ * Not: Bu plugin otomatik olarak template/iframe'e enjekte edilir.
32
+ * Ana app'te useInspector hook'u ile kontrol edilir.
33
+ */
34
+ import { Plugin } from "vite";
35
+ export declare function inspectorPlugin(): Plugin;
36
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAE9B,wBAAgB,eAAe,IAAI,MAAM,CA+kBxC"}
package/dist/plugin.js ADDED
@@ -0,0 +1,625 @@
1
+ /**
2
+ * Vite Inspector Plugin
3
+ *
4
+ * Template/iframe tarafında çalışır. Sayfaya element seçme, React component
5
+ * bilgisi çıkarma, hata yakalama ve AI prompt gönderme özellikleri ekler.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * // vite.config.ts
10
+ * import { defineConfig } from 'vite';
11
+ * import react from '@vitejs/plugin-react';
12
+ * import { inspectorPlugin } from '@promakeai/inspector/plugin';
13
+ *
14
+ * export default defineConfig({
15
+ * plugins: [
16
+ * react(),
17
+ * inspectorPlugin()
18
+ * ]
19
+ * });
20
+ * ```
21
+ *
22
+ * Plugin özellikler:
23
+ * - Element seçimi (click-to-select)
24
+ * - React Fiber üzerinden component bilgisi çıkarma
25
+ * - Text editing desteği
26
+ * - AI prompt gönderme
27
+ * - URL değişikliği takibi
28
+ * - JavaScript hata yakalama (error, promise rejection, console.error)
29
+ * - Özelleştirilebilir dil desteği
30
+ *
31
+ * Not: Bu plugin otomatik olarak template/iframe'e enjekte edilir.
32
+ * Ana app'te useInspector hook'u ile kontrol edilir.
33
+ */
34
+ export function inspectorPlugin() {
35
+ return {
36
+ name: "vite-plugin-inspector",
37
+ transformIndexHtml(html) {
38
+ // Inject inspector client script into HTML
39
+ return {
40
+ html,
41
+ tags: [
42
+ {
43
+ tag: "script",
44
+ attrs: {
45
+ type: "module",
46
+ },
47
+ children: `
48
+ (function() {
49
+ let inspectMode = false;
50
+ let isPaused = false;
51
+ let hoveredElement = null;
52
+ let selectedElement = null;
53
+ let selectedElementData = null;
54
+ let overlay = null;
55
+ let controlBox = null;
56
+
57
+ // Customizable labels
58
+ let labels = {
59
+ editText: 'Edit Text',
60
+ textPlaceholder: 'Enter text...',
61
+ updateText: 'Update',
62
+ promptPlaceholder: 'Ask Promake for changes...'
63
+ };
64
+
65
+ // Create overlay for highlighting
66
+ function createOverlay() {
67
+ if (overlay) return overlay;
68
+
69
+ overlay = document.createElement('div');
70
+ overlay.id = 'inspector-overlay';
71
+ overlay.style.cssText = \`
72
+ position: fixed;
73
+ pointer-events: none;
74
+ border: 2px solid #3b82f6;
75
+ background: rgba(59, 130, 246, 0.1);
76
+ z-index: 999999;
77
+ transition: all 0.1s ease;
78
+ display: none;
79
+ \`;
80
+ document.body.appendChild(overlay);
81
+ return overlay;
82
+ }
83
+
84
+ // Create control box for editing
85
+ function createControlBox(element, elementData) {
86
+ // Remove existing control box
87
+ if (controlBox) {
88
+ controlBox.remove();
89
+ }
90
+
91
+ controlBox = document.createElement('div');
92
+ controlBox.id = 'inspector-control-box';
93
+
94
+ const rect = element.getBoundingClientRect();
95
+ const isTextElement = element.textContent && element.children.length === 0;
96
+ const currentText = isTextElement ? element.textContent.trim() : '';
97
+
98
+ // Calculate position
99
+ const boxWidth = Math.max(320, Math.min(rect.width, 500));
100
+ const centerLeft = rect.left + (rect.width / 2) - (boxWidth / 2);
101
+ const viewportHeight = window.innerHeight;
102
+ const spaceBelow = viewportHeight - rect.bottom;
103
+ const spaceAbove = rect.top;
104
+
105
+ // Estimate box height
106
+ const estimatedBoxHeight = isTextElement ? 280 : 180;
107
+
108
+ // Show above if not enough space below
109
+ let topPosition;
110
+ if (spaceBelow < estimatedBoxHeight && spaceAbove > spaceBelow) {
111
+ // Show above element (positioned from top, showing above element)
112
+ topPosition = rect.top - estimatedBoxHeight - 10;
113
+ controlBox.setAttribute('data-position', 'above');
114
+ } else {
115
+ // Show below element
116
+ topPosition = rect.bottom + 10;
117
+ controlBox.setAttribute('data-position', 'below');
118
+ }
119
+
120
+ controlBox.style.cssText = \`
121
+ position: fixed;
122
+ top: \${topPosition}px;
123
+ left: \${Math.max(10, centerLeft)}px;
124
+ width: \${boxWidth}px;
125
+ background: #ffffff;
126
+ border: 1px solid #e5e7eb;
127
+ border-radius: 14px;
128
+ padding: 12px;
129
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04);
130
+ z-index: 1000000;
131
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
132
+ font-size: 14px;
133
+ \`;
134
+
135
+ let html = '';
136
+
137
+ // Text input (if text element)
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;">
183
+ <input
184
+ type="text"
185
+ id="inspector-prompt-input"
186
+ style="flex: 1; padding: 10px 12px; border: 1px solid #d1d5db; border-radius: 14px; font-size: 14px; color: #111827; background: #f9fafb; transition: all 0.2s;"
187
+ placeholder="\${labels.promptPlaceholder}"
188
+ onfocus="this.style.background='#ffffff'; this.style.borderColor='#9ca3af';"
189
+ onblur="this.style.background='#f9fafb'; this.style.borderColor='#d1d5db';"
190
+ />
191
+ <button
192
+ id="inspector-prompt-submit"
193
+ style="width: 44px; height: 44px; padding: 0; background: #4417db; color: white; border: none; border-radius: 14px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s; flex-shrink: 0;"
194
+ onmouseover="this.style.background='#3712af';"
195
+ onmouseout="this.style.background='#4417db';"
196
+ title="Send"
197
+ >
198
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
199
+ <path d="M10 15V5M10 5L5 10M10 5L15 10" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
200
+ </svg>
201
+ </button>
202
+ \`;
203
+
204
+ // Close button next to send button (only for non-text elements)
205
+ if (!isTextElement) {
206
+ html += \`
207
+ <button
208
+ id="inspector-close"
209
+ style="padding: 10px; background: #f3f4f6; border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; border-radius: 14px; transition: all 0.2s;"
210
+ onmouseover="this.style.background='#e5e7eb';"
211
+ onmouseout="this.style.background='#f3f4f6';"
212
+ title="Close"
213
+ >
214
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
215
+ <path d="M12 4L4 12M4 4L12 12" stroke="#6b7280" stroke-width="2" stroke-linecap="round"/>
216
+ </svg>
217
+ </button>
218
+ \`;
219
+ }
220
+
221
+ html += \`</div>\`;
222
+
223
+ controlBox.innerHTML = html;
224
+ document.body.appendChild(controlBox);
225
+
226
+ // Event listeners
227
+ const promptInput = controlBox.querySelector('#inspector-prompt-input');
228
+ const promptSubmit = controlBox.querySelector('#inspector-prompt-submit');
229
+ const closeBtn = controlBox.querySelector('#inspector-close');
230
+
231
+ // Prompt submit
232
+ promptSubmit.addEventListener('click', (e) => {
233
+ e.stopPropagation();
234
+ const prompt = promptInput.value.trim();
235
+ if (prompt) {
236
+ if (window.parent !== window) {
237
+ window.parent.postMessage({
238
+ type: 'INSPECTOR_PROMPT_SUBMITTED',
239
+ data: {
240
+ prompt: prompt,
241
+ element: elementData
242
+ }
243
+ }, '*');
244
+ }
245
+ console.log('Prompt submitted:', elementData);
246
+
247
+ // Turn off inspect mode after sending
248
+ toggleInspectMode(false);
249
+ }
250
+ });
251
+
252
+ // Enter key on prompt input
253
+ promptInput.addEventListener('keypress', (e) => {
254
+ if (e.key === 'Enter') {
255
+ e.stopPropagation();
256
+ promptSubmit.click();
257
+ }
258
+ });
259
+
260
+ // Text input handlers (if exists)
261
+ if (isTextElement) {
262
+ const textInput = controlBox.querySelector('#inspector-text-input');
263
+ const textSubmit = controlBox.querySelector('#inspector-text-submit');
264
+
265
+ textSubmit.addEventListener('click', (e) => {
266
+ e.stopPropagation();
267
+ const newText = textInput.value;
268
+ if (window.parent !== window) {
269
+ window.parent.postMessage({
270
+ type: 'INSPECTOR_TEXT_UPDATED',
271
+ data: {
272
+ text: newText,
273
+ originalText: currentText,
274
+ element: elementData
275
+ }
276
+ }, '*');
277
+ }
278
+ console.log('Text updated:', newText);
279
+
280
+ // Turn off inspect mode after updating
281
+ toggleInspectMode(false);
282
+ });
283
+
284
+ textInput.addEventListener('keypress', (e) => {
285
+ if (e.key === 'Enter') {
286
+ e.stopPropagation();
287
+ textSubmit.click();
288
+ }
289
+ });
290
+
291
+ // Prevent input events from bubbling
292
+ textInput.addEventListener('click', (e) => e.stopPropagation());
293
+ }
294
+
295
+ // Prevent prompt input events from bubbling
296
+ promptInput.addEventListener('click', (e) => e.stopPropagation());
297
+
298
+ // Close button
299
+ closeBtn.addEventListener('click', (e) => {
300
+ e.stopPropagation();
301
+ unpauseInspection();
302
+ });
303
+
304
+ // Focus prompt input
305
+ setTimeout(() => promptInput.focus(), 100);
306
+ }
307
+
308
+ // Pause inspection
309
+ function pauseInspection(element, elementData) {
310
+ isPaused = true;
311
+ selectedElement = element;
312
+ selectedElementData = elementData;
313
+
314
+ // Disable scrolling
315
+ document.body.style.overflow = 'hidden';
316
+
317
+ // Keep overlay visible
318
+ highlightElement(element);
319
+ overlay.style.borderColor = '#10b981';
320
+ overlay.style.background = 'rgba(16, 185, 129, 0.1)';
321
+
322
+ // Remove hover listeners
323
+ document.removeEventListener('mousemove', handleMouseMove, true);
324
+
325
+ // Create control box
326
+ createControlBox(element, elementData);
327
+ }
328
+
329
+ // Unpause inspection
330
+ function unpauseInspection() {
331
+ isPaused = false;
332
+ selectedElement = null;
333
+ selectedElementData = null;
334
+
335
+ // Re-enable scrolling
336
+ document.body.style.overflow = '';
337
+
338
+ // Remove control box
339
+ if (controlBox) {
340
+ controlBox.remove();
341
+ controlBox = null;
342
+ }
343
+
344
+ // Restore overlay style
345
+ if (overlay) {
346
+ overlay.style.borderColor = '#3b82f6';
347
+ overlay.style.background = 'rgba(59, 130, 246, 0.1)';
348
+ }
349
+
350
+ // Re-enable hover
351
+ if (inspectMode) {
352
+ document.addEventListener('mousemove', handleMouseMove, true);
353
+ }
354
+ }
355
+
356
+ // Get React Fiber node from DOM element
357
+ function getReactFiber(element) {
358
+ const key = Object.keys(element).find(k =>
359
+ k.startsWith('__reactFiber') ||
360
+ k.startsWith('__reactInternalInstance')
361
+ );
362
+ return key ? element[key] : null;
363
+ }
364
+
365
+ // Find component file and line info from fiber
366
+ function getComponentInfo(fiber) {
367
+ let current = fiber;
368
+ while (current) {
369
+ // Check if fiber has _debugSource (development mode)
370
+ if (current._debugSource) {
371
+ const source = current._debugSource;
372
+ return {
373
+ fileName: source.fileName,
374
+ lineNumber: source.lineNumber,
375
+ columnNumber: source.columnNumber,
376
+ componentName: current.type?.name || current.elementType?.name || 'Unknown'
377
+ };
378
+ }
379
+
380
+ // Check if fiber has _debugOwner
381
+ if (current._debugOwner) {
382
+ current = current._debugOwner;
383
+ continue;
384
+ }
385
+
386
+ // Try return fiber (parent component)
387
+ if (current.return) {
388
+ current = current.return;
389
+ continue;
390
+ }
391
+
392
+ break;
393
+ }
394
+ return null;
395
+ }
396
+
397
+ // Highlight element
398
+ function highlightElement(element) {
399
+ if (!overlay || !element) return;
400
+
401
+ const rect = element.getBoundingClientRect();
402
+ overlay.style.display = 'block';
403
+ overlay.style.top = rect.top + window.scrollY + 'px';
404
+ overlay.style.left = rect.left + window.scrollX + 'px';
405
+ overlay.style.width = rect.width + 'px';
406
+ overlay.style.height = rect.height + 'px';
407
+ }
408
+
409
+ // Clear highlight
410
+ function clearHighlight() {
411
+ if (overlay) {
412
+ overlay.style.display = 'none';
413
+ }
414
+ }
415
+
416
+ // Handle mouse move
417
+ function handleMouseMove(e) {
418
+ if (!inspectMode || isPaused) return;
419
+
420
+ // Ignore control box and overlay
421
+ if (e.target.id === 'inspector-control-box' ||
422
+ e.target.closest('#inspector-control-box') ||
423
+ e.target.id === 'inspector-overlay') {
424
+ return;
425
+ }
426
+
427
+ hoveredElement = e.target;
428
+ highlightElement(hoveredElement);
429
+ }
430
+
431
+ // Handle click
432
+ function handleClick(e) {
433
+ if (!inspectMode) return;
434
+
435
+ // Ignore clicks on control box
436
+ if (e.target.id === 'inspector-control-box' ||
437
+ e.target.closest('#inspector-control-box')) {
438
+ return;
439
+ }
440
+
441
+ // If already paused, ignore clicks (don't select new element)
442
+ if (isPaused) {
443
+ e.preventDefault();
444
+ e.stopPropagation();
445
+ return;
446
+ }
447
+
448
+ e.preventDefault();
449
+ e.stopPropagation();
450
+
451
+ const element = e.target;
452
+ const fiber = getReactFiber(element);
453
+ const componentInfo = fiber ? getComponentInfo(fiber) : null;
454
+
455
+ const elementData = {
456
+ tagName: element.tagName,
457
+ className: element.className,
458
+ id: element.id,
459
+ component: componentInfo,
460
+ position: {
461
+ top: element.getBoundingClientRect().top,
462
+ left: element.getBoundingClientRect().left,
463
+ width: element.getBoundingClientRect().width,
464
+ height: element.getBoundingClientRect().height
465
+ }
466
+ };
467
+
468
+ // Send info to parent window
469
+ if (window.parent !== window) {
470
+ window.parent.postMessage({
471
+ type: 'INSPECTOR_ELEMENT_SELECTED',
472
+ data: elementData
473
+ }, '*');
474
+ }
475
+
476
+ // Log for debugging
477
+ console.log('Element selected:', {
478
+ element,
479
+ componentInfo
480
+ });
481
+
482
+ // Pause inspection and show control box
483
+ pauseInspection(element, elementData);
484
+ }
485
+
486
+ // Toggle inspect mode
487
+ function toggleInspectMode(active) {
488
+ inspectMode = active;
489
+
490
+ if (inspectMode) {
491
+ createOverlay();
492
+ document.body.style.cursor = 'crosshair';
493
+ document.addEventListener('mousemove', handleMouseMove, true);
494
+ document.addEventListener('click', handleClick, true);
495
+ } else {
496
+ // Clean up everything
497
+ clearHighlight();
498
+ unpauseInspection();
499
+ document.body.style.cursor = '';
500
+ document.removeEventListener('mousemove', handleMouseMove, true);
501
+ document.removeEventListener('click', handleClick, true);
502
+ }
503
+ }
504
+
505
+ // Listen for messages from parent
506
+ window.addEventListener('message', (event) => {
507
+ if (event.data.type === 'TOGGLE_INSPECTOR') {
508
+ toggleInspectMode(event.data.active);
509
+
510
+ // Update labels if provided
511
+ if (event.data.labels) {
512
+ labels = { ...labels, ...event.data.labels };
513
+ }
514
+ }
515
+ });
516
+
517
+ // Track URL changes
518
+ let lastUrl = location.href;
519
+
520
+ function notifyUrlChange() {
521
+ const newUrl = location.href;
522
+ if (newUrl !== lastUrl) {
523
+ lastUrl = newUrl;
524
+
525
+ if (window.parent !== window) {
526
+ window.parent.postMessage({
527
+ type: 'URL_CHANGED',
528
+ data: {
529
+ url: newUrl,
530
+ pathname: location.pathname,
531
+ search: location.search,
532
+ hash: location.hash
533
+ }
534
+ }, '*');
535
+ }
536
+ }
537
+ }
538
+
539
+ // Monitor URL changes via pushState/replaceState
540
+ const originalPushState = history.pushState;
541
+ const originalReplaceState = history.replaceState;
542
+
543
+ history.pushState = function() {
544
+ originalPushState.apply(this, arguments);
545
+ notifyUrlChange();
546
+ };
547
+
548
+ history.replaceState = function() {
549
+ originalReplaceState.apply(this, arguments);
550
+ notifyUrlChange();
551
+ };
552
+
553
+ // Also listen to popstate
554
+ window.addEventListener('popstate', notifyUrlChange);
555
+
556
+ // Initial URL notification
557
+ setTimeout(() => {
558
+ notifyUrlChange();
559
+ }, 100);
560
+
561
+ // Error tracking
562
+ // Save original console.error FIRST before any functions
563
+ const originalConsoleError = console.error;
564
+
565
+ function sendError(errorData) {
566
+ if (window.parent !== window) {
567
+ window.parent.postMessage({
568
+ type: 'INSPECTOR_ERROR',
569
+ data: errorData
570
+ }, '*');
571
+ }
572
+ // Use original console.error to avoid infinite loop
573
+ originalConsoleError.call(console, '🔍 Inspector Error:', errorData);
574
+ }
575
+
576
+ // Track JavaScript errors
577
+ window.addEventListener('error', (event) => {
578
+ sendError({
579
+ type: 'javascript',
580
+ message: event.message,
581
+ stack: event.error?.stack,
582
+ fileName: event.filename,
583
+ lineNumber: event.lineno,
584
+ columnNumber: event.colno,
585
+ timestamp: Date.now()
586
+ });
587
+ });
588
+
589
+ // Track unhandled promise rejections
590
+ window.addEventListener('unhandledrejection', (event) => {
591
+ sendError({
592
+ type: 'promise',
593
+ message: event.reason?.message || String(event.reason),
594
+ stack: event.reason?.stack,
595
+ timestamp: Date.now()
596
+ });
597
+ });
598
+
599
+ // Track console.error calls
600
+ console.error = function(...args) {
601
+ const message = args.map(arg =>
602
+ typeof arg === 'object' ? JSON.stringify(arg) : String(arg)
603
+ ).join(' ');
604
+
605
+ sendError({
606
+ type: 'console',
607
+ message: message,
608
+ stack: new Error().stack,
609
+ timestamp: Date.now()
610
+ });
611
+
612
+ // Call original console.error
613
+ originalConsoleError.apply(console, args);
614
+ };
615
+
616
+ console.log('🔍 Inspector plugin loaded');
617
+ })();
618
+ `,
619
+ injectTo: "body",
620
+ },
621
+ ],
622
+ };
623
+ },
624
+ };
625
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Shared types for @promake/inspector
3
+ */
4
+ export interface ComponentInfo {
5
+ componentName: string;
6
+ fileName: string;
7
+ lineNumber: number;
8
+ columnNumber: number;
9
+ }
10
+ export interface ElementPosition {
11
+ top: number;
12
+ left: number;
13
+ width: number;
14
+ height: number;
15
+ }
16
+ export interface SelectedElementData {
17
+ tagName: string;
18
+ className: string;
19
+ id: string;
20
+ component: ComponentInfo | null;
21
+ position: ElementPosition;
22
+ }
23
+ export interface UrlChangeData {
24
+ url: string;
25
+ pathname: string;
26
+ search: string;
27
+ hash: string;
28
+ }
29
+ export interface PromptSubmittedData {
30
+ prompt: string;
31
+ element: SelectedElementData;
32
+ }
33
+ export interface TextUpdatedData {
34
+ text: string;
35
+ originalText: string;
36
+ element: SelectedElementData;
37
+ }
38
+ export interface ErrorData {
39
+ type: "javascript" | "promise" | "console";
40
+ message: string;
41
+ stack?: string;
42
+ fileName?: string;
43
+ lineNumber?: number;
44
+ columnNumber?: number;
45
+ timestamp: number;
46
+ }
47
+ export interface InspectorLabels {
48
+ editText?: string;
49
+ textPlaceholder?: string;
50
+ updateText?: string;
51
+ promptPlaceholder?: string;
52
+ }
53
+ export interface InspectorCallbacks {
54
+ onElementSelected?: (data: SelectedElementData) => void;
55
+ onUrlChange?: (data: UrlChangeData) => void;
56
+ onPromptSubmitted?: (data: PromptSubmittedData) => void;
57
+ onTextUpdated?: (data: TextUpdatedData) => void;
58
+ onError?: (data: ErrorData) => void;
59
+ }
60
+ export interface UseInspectorReturn {
61
+ isInspecting: boolean;
62
+ toggleInspector: (active?: boolean) => void;
63
+ startInspecting: () => void;
64
+ stopInspecting: () => void;
65
+ }
66
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAGD,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAGD,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,aAAa,GAAG,IAAI,CAAC;IAChC,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAGD,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAGD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,mBAAmB,CAAC;CAC9B;AAGD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,mBAAmB,CAAC;CAC9B;AAGD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,YAAY,GAAG,SAAS,GAAG,SAAS,CAAC;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAGD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAGD,MAAM,WAAW,kBAAkB;IACjC,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACxD,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IAC5C,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACxD,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;CACrC;AAGD,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,cAAc,EAAE,MAAM,IAAI,CAAC;CAC5B"}
package/dist/types.js ADDED
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Shared types for @promake/inspector
3
+ */
4
+ export {};
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@promakeai/inspector",
3
+ "version": "0.0.1",
4
+ "description": "Visual element inspector for React apps in iframe with AI prompt support",
5
+ "author": "Promake",
6
+ "type": "module",
7
+ "main": "./dist/hook.js",
8
+ "module": "./dist/hook.js",
9
+ "types": "./dist/hook.d.ts",
10
+ "exports": {
11
+ "./plugin": {
12
+ "types": "./dist/plugin.d.ts",
13
+ "import": "./dist/plugin.js"
14
+ },
15
+ "./hook": {
16
+ "types": "./dist/hook.d.ts",
17
+ "import": "./dist/hook.js"
18
+ },
19
+ "./package.json": "./package.json"
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "README.md"
24
+ ],
25
+ "scripts": {
26
+ "build": "tsc && tsc -p tsconfig.plugin.json",
27
+ "prepublishOnly": "npm run build"
28
+ },
29
+ "keywords": [
30
+ "vite",
31
+ "react",
32
+ "inspector",
33
+ "devtools",
34
+ "iframe",
35
+ "visual-editor",
36
+ "element-inspector",
37
+ "ai",
38
+ "prompt"
39
+ ],
40
+ "peerDependencies": {
41
+ "react": "^18.0.0",
42
+ "vite": "^5.0.0"
43
+ },
44
+ "devDependencies": {
45
+ "@types/react": "^18.2.0",
46
+ "typescript": "^5.3.0",
47
+ "vite": "^5.4.0"
48
+ }
49
+ }