npm package: @promakeai/inspector-hook
Description: React hook for controlling inspector in parent applications
Author: Promake
Latest version: 1.7.5

# @promakeai/inspector-hook

React hook and utilities for controlling the Promake Inspector from parent applications via iframe communication.

## 📦 Installation

```bash
npm install @promakeai/inspector-hook
# or
yarn add @promakeai/inspector-hook
# or
bun add @promakeai/inspector-hook
```

## 🎯 Overview

This package provides three main exports:

1. **`useInspector`** - React hook for controlling an iframe-embedded Inspector
2. **`updateJSXSource`** - AST-based utility for updating JSX/TSX source code
3. **`inspectorHookPlugin`** - Vite plugin for proper Babel configuration

## 🚀 Quick Start

### Basic Usage

```typescript
import { useInspector } from "@promakeai/inspector-hook";
import { useRef } from "react";

function ParentApp() {
  const iframeRef = useRef<HTMLIFrameElement>(null);

  const inspector = useInspector(iframeRef, {
    onElementSelected: (data) => {
      console.log("Element selected:", data);
    },
    onStyleUpdated: (data) => {
      console.log("Style updated:", data);
    },
  });

  return (
    <div>
      <button onClick={() => inspector.toggleInspector()}>
        {inspector.isInspecting ? "Stop" : "Start"} Inspector
      </button>

      <iframe
        ref={iframeRef}
        src="http://localhost:5173"
        style={{ width: "100%", height: "800px" }}
      />
    </div>
  );
}
```

### With Vite Plugin

```typescript
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { inspectorHookPlugin } from "@promakeai/inspector-hook";

export default defineConfig({
  plugins: [
    inspectorHookPlugin(), // Required for proper Babel configuration
    react(),
  ],
});
```

## 📚 API Reference

### `useInspector(iframeRef, callbacks?, labels?, theme?)`

Main React hook for controlling the Inspector.

#### Parameters

| Parameter   | Type                           | Description                     |
| ----------- | ------------------------------ | ------------------------------- |
| `iframeRef` | `RefObject<HTMLIFrameElement>` | Reference to the iframe element |
| `callbacks` | `InspectorCallbacks`           | Optional event callbacks        |
| `labels`    | `InspectorLabels`              | Optional custom labels for i18n |
| `theme`     | `InspectorTheme`               | Optional custom theme colors    |

#### Returns: `UseInspectorReturn`

```typescript
interface UseInspectorReturn {
  // State
  isInspecting: boolean;

  // Control Methods
  toggleInspector: (active?: boolean) => void;
  startInspecting: () => void;
  stopInspecting: () => void;

  // UI Control Methods
  showContentInput: (show: boolean, updateImmediately?: boolean) => void;
  showImageInput: (show: boolean, updateImmediately?: boolean) => void;
  showStyleEditor: (show: boolean) => void;
  setBadgeVisible: (visible: boolean) => void;
  setShowChildBorders: (show: boolean) => void;

  // Element Methods
  highlightElement: (
    identifier: string | SelectedElementData,
    options?: HighlightOptions
  ) => void;
  getElementByInspectorId: (inspectorId: string) => void;
}
```

### Methods

#### `toggleInspector(active?: boolean)`

Toggles inspector mode on/off.

```typescript
// Toggle
inspector.toggleInspector();

// Set explicitly
inspector.toggleInspector(true); // Start
inspector.toggleInspector(false); // Stop
```

#### `startInspecting()`

Start inspector mode.

```typescript
inspector.startInspecting();
```

#### `stopInspecting()`

Stop inspector mode.

```typescript
inspector.stopInspecting();
```

#### `showContentInput(show: boolean, updateImmediately?: boolean)`

Show or hide the text content editor.

```typescript
// Show content editor
inspector.showContentInput(true);

// Show and apply changes immediately without confirmation
inspector.showContentInput(true, true);

// Hide content editor
inspector.showContentInput(false);
```

**Parameters:**

- `show`: Whether to show or hide the content editor
- `updateImmediately`: If true, applies changes immediately without requiring user confirmation (default: false)

#### `showImageInput(show: boolean, updateImmediately?: boolean)`

Show or hide the image editor.

```typescript
// Show image editor
inspector.showImageInput(true);

// Show and apply changes immediately
inspector.showImageInput(true, true);

// Hide image editor
inspector.showImageInput(false);
```

**Parameters:**

- `show`: Whether to show or hide the image editor
- `updateImmediately`: If true, applies changes immediately without requiring user confirmation (default: false)

#### `showStyleEditor(show: boolean)`

Show or hide the style editor.

```typescript
// Show style editor
inspector.showStyleEditor(true);

// Hide style editor
inspector.showStyleEditor(false);
```

#### `setBadgeVisible(visible: boolean)`

Show or hide the "Built with Promake" badge.

```typescript
// Show badge
inspector.setBadgeVisible(true);

// Hide badge
inspector.setBadgeVisible(false);
```

#### `setShowChildBorders(show: boolean)`

Toggle visibility of child element borders during inspection.

```typescript
// Show borders
inspector.setShowChildBorders(true);

// Hide borders
inspector.setShowChildBorders(false);
```

#### `highlightElement(identifier: string | SelectedElementData, options?: HighlightOptions)`

Highlight a specific element in the iframe with visual feedback.

```typescript
// Highlight by inspector ID
inspector.highlightElement("inspector-id-123");

// Highlight with custom options
inspector.highlightElement("inspector-id-123", {
  duration: 5000,
  color: "#ff0000",
  animation: "pulse",
  scrollIntoView: true,
});

// Highlight with navigation to a specific route
inspector.highlightElement("inspector-id-123", {
  targetRoute: "/about",
  duration: 3000,
});

// Highlight using element data
inspector.highlightElement(elementData, {
  animation: "fade",
});
```

**Parameters:**

- `identifier`: Inspector ID (string) or full SelectedElementData object
- `options`: Optional highlight configuration

**HighlightOptions:**

```typescript
interface HighlightOptions {
  duration?: number; // Highlight duration in ms (default: 3000)
  scrollIntoView?: boolean; // Scroll to element (default: true)
  color?: string; // Highlight color (default: '#4417db')
  animation?: "pulse" | "fade" | "none"; // Animation type (default: 'pulse')
  targetRoute?: string; // Navigate to this route before highlighting
}
```

#### `getElementByInspectorId(inspectorId: string)`

Request detailed information about an element by its inspector ID. The response will be sent via the `onElementInfoReceived` callback.

```typescript
// Request element info
inspector.getElementByInspectorId("inspector-id-123");

// Handle response in callbacks
const inspector = useInspector(iframeRef, {
  onElementInfoReceived: (data) => {
    if (data.found) {
      console.log("Element found:", data.element);
    } else {
      console.log("Element not found:", data.error);
    }
  },
});
```

**Response via `onElementInfoReceived`:**

```typescript
interface ElementInfoData {
  found: boolean;
  element?: SelectedElementData;
  error?: string;
}
```

## 📢 Events & Callbacks

### `InspectorCallbacks`

```typescript
interface InspectorCallbacks {
  onElementSelected?: (data: SelectedElementData) => void;
  onUrlChange?: (data: UrlChangeData) => void;
  onPromptSubmitted?: (data: PromptSubmittedData) => void;
  onTextUpdated?: (data: TextUpdatedData) => void;
  onImageUpdated?: (data: ImageUpdatedData) => void;
  onStyleUpdated?: (data: StyleUpdatedData) => void;
  onInspectorClosed?: () => void;
  onError?: (data: ErrorData) => void;
  onElementInfoReceived?: (data: ElementInfoData) => void;
}
```

### Event Examples

#### `onElementSelected`

Called when user selects an element in the iframe.

```typescript
const inspector = useInspector(iframeRef, {
  onElementSelected: (data) => {
    console.log("Selected:", data.tagName);
    console.log("Component:", data.component?.name);
    console.log("Position:", data.position);
    console.log("Parents:", data.parents);
    console.log("Children:", data.children);
  },
});
```

**SelectedElementData:**

```typescript
interface SelectedElementData {
  id: string; // Unique inspector ID
  tagName: string; // HTML tag name
  className: string; // CSS classes
  component: ComponentInfo | null; // React component info
  position: ElementPosition; // Screen position
  isTextNode?: boolean; // Is text element
  textContent?: string; // Text content
  isImageNode?: boolean; // Is image element
  imageUrl?: string; // Image URL
  selector?: string; // CSS selector
  currentRoute?: string; // Current route path
  parents?: ElementReference[]; // Parent elements (3 generations)
  children?: ElementReference[]; // Child elements
}
```

#### `onStyleUpdated`

Called when user updates element styles.

```typescript
const inspector = useInspector(iframeRef, {
  onStyleUpdated: (data) => {
    console.log("Element:", data.element);
    console.log("Raw styles:", data.styles);
    console.log("Inline styles:", data.inlineStyles);
    console.log("Tailwind classes:", data.tailwindClasses);

    // Use updateJSXSource to apply changes to source code
    const result = updateJSXSource({
      sourceCode: originalSourceCode,
      lineNumber: data.element.component?.lineNumber!,
      columnNumber: data.element.component?.columnNumber!,
      tagName: data.element.tagName,
      styles: data.inlineStyles,
      className: data.tailwindClasses.join(" "),
    });

    if (result.success) {
      // Save updated code
      saveSourceCode(result.code);
    }
  },
});
```

**StyleUpdatedData:**

```typescript
interface StyleUpdatedData {
  element: SelectedElementData;
  styles: StyleChanges; // Raw style values
  inlineStyles: Record<string, string>; // React inline style object
  tailwindClasses: string[]; // Tailwind class names
  appliedStyles: {
    // Detailed breakdown
    layout?: {
      backgroundColor?: string;
      height?: string;
      width?: string;
      // ... more layout properties
    };
    text?: {
      color?: string;
      fontSize?: string;
      // ... more text properties
    };
    border?: {
      borderRadius?: string;
      // ... more border properties
    };
    spacing?: {
      paddingVertical?: string;
      // ... more spacing properties
    };
  };
}
```

#### `onTextUpdated`

Called when user updates text content.

```typescript
const inspector = useInspector(iframeRef, {
  onTextUpdated: (data) => {
    console.log("New text:", data.text);
    console.log("Original text:", data.originalText);
    console.log("Element:", data.element);
  },
});
```

**TextUpdatedData:**

```typescript
interface TextUpdatedData {
  text: string; // New text content
  originalText: string; // Original text content
  element: SelectedElementData;
}
```

#### `onImageUpdated`

Called when user uploads a new image.

```typescript
const inspector = useInspector(iframeRef, {
  onImageUpdated: (data) => {
    console.log("Image data:", data.imageData); // Base64
    console.log("File info:", data.imageFile);
    console.log("Original URL:", data.originalImageUrl);

    // Upload image to your server
    uploadImage(data.imageData, data.imageFile).then((url) => {
      console.log("New image URL:", url);
    });
  },
});
```

**ImageUpdatedData:**

```typescript
interface ImageUpdatedData {
  imageData: string; // Base64 encoded image
  imageFile: {
    name: string;
    size: number;
    type: string;
  };
  originalImageUrl: string;
  element: SelectedElementData;
}
```

#### `onPromptSubmitted`

Called when user submits an AI prompt.

```typescript
const inspector = useInspector(iframeRef, {
  onPromptSubmitted: (data) => {
    console.log("Prompt:", data.prompt);
    console.log("Element:", data.element);

    // Send to your AI service
    generateAIContent(data.prompt, data.element).then((result) => {
      console.log("AI result:", result);
    });
  },
});
```

**PromptSubmittedData:**

```typescript
interface PromptSubmittedData {
  prompt: string;
  element: SelectedElementData;
}
```

#### `onUrlChange`

Called when iframe navigates to a new URL.

```typescript
const inspector = useInspector(iframeRef, {
  onUrlChange: (data) => {
    console.log("New URL:", data.url);
    console.log("Pathname:", data.pathname);
    console.log("Search:", data.search);
    console.log("Hash:", data.hash);
  },
});
```

**UrlChangeData:**

```typescript
interface UrlChangeData {
  oldUrl: string;
  newUrl: string;
  timestamp: number;
}
```

#### `onError`

Called when an error occurs in the iframe.

```typescript
const inspector = useInspector(iframeRef, {
  onError: (data) => {
    console.error("Error type:", data.type);
    console.error("Message:", data.message);
    console.error("Stack:", data.stack);
    console.error("File:", data.fileName);
    console.error("Line:", data.lineNumber);

    if (data.type === "vite") {
      console.error("Plugin:", data.plugin);
      console.error("Frame:", data.frame);
    }
  },
});
```

**ErrorData:**

```typescript
interface ErrorData {
  type: "javascript" | "promise" | "console" | "vite";
  message: string;
  stack?: string;
  fileName?: string;
  lineNumber?: number;
  columnNumber?: number;
  timestamp: number;
  // Vite-specific fields
  frame?: string; // Code frame
  plugin?: string; // Plugin name
}
```

#### `onInspectorClosed`

Called when user closes the inspector.

```typescript
const inspector = useInspector(iframeRef, {
  onInspectorClosed: () => {
    console.log("Inspector closed");
    // Clean up or save state
  },
});
```

#### `onElementInfoReceived`

Called in response to `getElementByInspectorId()`.

```typescript
const inspector = useInspector(iframeRef, {
  onElementInfoReceived: (data) => {
    if (data.found) {
      console.log("Element found:", data.element);
    } else {
      console.error("Element not found:", data.error);
    }
  },
});
```

## 🎨 Customization

### Labels (i18n)

Customize all UI text for internationalization:

```typescript
const labels = {
  // Text Editor
  editText: "Edit Text",
  textContentLabel: "Content",
  updateText: "Update",

  // Image Editor
  editImage: "Edit Image",
  imageUploadTitle: "Select Image",
  updateImage: "Update",

  // Style Editor
  styleEditorTitle: "Styles",
  layoutSectionTitle: "Layout",
  textSectionTitle: "Text",

  // Properties
  backgroundColorLabel: "Background",
  colorLabel: "Color",
  fontSizeLabel: "Size",

  // ... see InspectorLabels type for all options
};

const inspector = useInspector(iframeRef, callbacks, labels);
```

See the full `InspectorLabels` interface in the [Types](#types) section for all available label options.

### Theme

Customize colors to match your application:

```typescript
const theme = {
  // Control Box
  backgroundColor: "#ffffff",
  textColor: "#111827",

  // Buttons
  buttonColor: "#4417db",
  buttonTextColor: "#ffffff",
  buttonHoverColor: "#3a13c0",

  // Inputs
  inputBackgroundColor: "#f9fafb",
  inputBorderColor: "#d1d5db",
  inputFocusBorderColor: "#4417db",

  // Status Colors
  warningColor: "#f59e0b",
  successColor: "#10b981",
  errorColor: "#ef4444",

  // Overlay
  overlayColor: "#4417db",
  overlayOpacity: 0.2,

  // ... see InspectorTheme type for all options
};

const inspector = useInspector(iframeRef, callbacks, labels, theme);
```

See the full `InspectorTheme` interface in the [Types](#types) section for all available theme options.

## 🛠️ Utilities

### `updateJSXSource(options): UpdateJSXSourceResult`

AST-based utility for programmatically updating JSX/TSX source code. Intelligently merges styles and classNames without breaking existing code.

#### Parameters

```typescript
interface UpdateJSXSourceOptions {
  sourceCode: string; // Original JSX/TSX source code
  lineNumber: number; // Target element's line number (1-indexed)
  columnNumber: number; // Target element's column number (0-indexed)
  tagName: string; // HTML tag name for validation
  styles?: Record<string, string>; // Inline styles to apply
  className?: string; // Class names to add
}
```

#### Returns

```typescript
interface UpdateJSXSourceResult {
  success: boolean;
  code: string; // Updated source code
  message?: string; // Error or success message
}
```

#### Examples

**Add inline styles:**

```typescript
import { updateJSXSource } from "@promakeai/inspector-hook";

const sourceCode = `
function MyComponent() {
  return <div className="container">Hello</div>;
}
`;

const result = updateJSXSource({
  sourceCode,
  lineNumber: 3,
  columnNumber: 9,
  tagName: "div",
  styles: {
    backgroundColor: "red",
    padding: "20px",
  },
});

if (result.success) {
  console.log(result.code);
  // Output:
  // function MyComponent() {
  //   return <div className="container" style={{ backgroundColor: "red", padding: "20px" }}>Hello</div>;
  // }
}
```

**Add class names:**

```typescript
const result = updateJSXSource({
  sourceCode,
  lineNumber: 3,
  columnNumber: 9,
  tagName: "div",
  className: "bg-red-500 p-4",
});

// Result: <div className="container bg-red-500 p-4">Hello</div>
```

**Merge with existing styles:**

```typescript
const sourceCode = `
<div style={{ color: 'blue', margin: '10px' }}>
  Hello
</div>
`;

const result = updateJSXSource({
  sourceCode,
  lineNumber: 2,
  columnNumber: 0,
  tagName: "div",
  styles: {
    color: "red", // Overrides existing 'blue'
    padding: "20px", // Adds new property
  },
});

// Result: <div style={{ margin: "10px", color: "red", padding: "20px" }}>
```

**Usage with Inspector callbacks:**

```typescript
const inspector = useInspector(iframeRef, {
  onStyleUpdated: async (data) => {
    const { element, inlineStyles, tailwindClasses } = data;
    const component = element.component;

    if (!component?.fileName || !component?.lineNumber) {
      console.error("Missing component info");
      return;
    }

    // Fetch original source code
    const sourceCode = await fetchSourceCode(component.fileName);

    // Update source code
    const result = updateJSXSource({
      sourceCode,
      lineNumber: component.lineNumber,
      columnNumber: component.columnNumber || 0,
      tagName: element.tagName,
      styles: inlineStyles,
      className: tailwindClasses.join(" "),
    });

    if (result.success) {
      // Save updated code back to file
      await saveSourceCode(component.fileName, result.code);
      console.log("✅ Source code updated successfully");
    } else {
      console.error("❌ Failed to update:", result.message);
    }
  },
});
```

**Features:**

- ✅ Preserves existing code structure and formatting
- ✅ Intelligently merges styles without duplicating properties
- ✅ Handles various className formats (string, template literal, expression)
- ✅ Validates tag name to ensure correct element is updated
- ✅ Full TypeScript support with AST-based parsing
- ✅ Supports JSX and TSX files

### `inspectorHookPlugin(): Plugin`

Vite plugin that configures Babel packages for browser compatibility. Required when using `updateJSXSource` in the browser.

#### Usage

```typescript
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { inspectorHookPlugin } from "@promakeai/inspector-hook";

export default defineConfig({
  plugins: [
    inspectorHookPlugin(), // Must be included before react()
    react(),
  ],
});
```

**What it does:**

- Defines `process.env.NODE_ENV` for Babel compatibility
- Sets `process.platform` to "browser"
- Sets `process.version` for version checks

**Note:** This plugin is only needed if you're using `updateJSXSource` in the browser. If you're only using it on the server/backend, you don't need this plugin.

## 📘 Types

All types are re-exported from `@promakeai/inspector-types` for convenience.

### Core Types

```typescript
// Component information from React Fiber or vite-plugin-component-debugger
interface ComponentInfo {
  id?: string;
  name?: string;
  path?: string;
  fileName?: string;
  lineNumber?: number;
  columnNumber?: number;
  component?: string;
}

// Element position on screen
interface ElementPosition {
  top: number;
  left: number;
  width: number;
  height: number;
}

// Element reference for parent/child tracking
interface ElementReference {
  id: string;
  tagName: string;
  className: string;
  selector?: string;
}

// Selected element data
interface SelectedElementData {
  id: string;
  tagName: string;
  className: string;
  component: ComponentInfo | null;
  position: ElementPosition;
  isTextNode?: boolean;
  textContent?: string;
  isImageNode?: boolean;
  imageUrl?: string;
  selector?: string;
  currentRoute?: string;
  parents?: ElementReference[]; // [parent, grandparent, great-grandparent]
  children?: ElementReference[]; // Array of child elements
}
```

### Style Types

```typescript
// Style changes with all supported properties
interface StyleChanges {
  // Layout
  backgroundColor?: string;
  height?: string;
  width?: string;
  display?: string;
  opacity?: string;
  flex?: string;
  flexDirection?: string;
  justifyContent?: string;
  alignItems?: string;

  // Image
  objectFit?: string;

  // Text
  color?: string;
  fontSize?: string;
  fontWeight?: string;
  fontFamily?: string;
  textAlign?: string;
  textDecoration?: string;

  // Border
  borderRadius?: string;
  borderWidth?: string;
  borderColor?: string;
  borderStyle?: string;

  // Spacing - Vertical/Horizontal (combined)
  paddingVertical?: string;
  paddingHorizontal?: string;
  marginVertical?: string;
  marginHorizontal?: string;

  // Spacing - Individual sides
  paddingTop?: string;
  paddingRight?: string;
  paddingBottom?: string;
  paddingLeft?: string;
  marginTop?: string;
  marginRight?: string;
  marginBottom?: string;
  marginLeft?: string;
}
```

### Full InspectorLabels Interface

The `InspectorLabels` interface contains 100+ customizable text labels. Here are the main categories:

```typescript
interface InspectorLabels {
  // Text Editor (4 labels)
  editText?: string;
  textContentLabel?: string;
  textPlaceholder?: string;
  linkUrlLabel?: string;
  updateText?: string;

  // Image Editor (4 labels)
  editImage?: string;
  imageUploadTitle?: string;
  imageUploadHint?: string;
  updateImage?: string;

  // Prompt Input (1 label)
  promptPlaceholder?: string;

  // Style Editor Sections (6 labels)
  styleEditorTitle?: string;
  layoutSectionTitle?: string;
  displaySectionTitle?: string;
  imageSectionTitle?: string;
  textSectionTitle?: string;
  borderSectionTitle?: string;
  spacingSectionTitle?: string;

  // Style Properties (26 labels)
  backgroundColorLabel?: string;
  heightLabel?: string;
  widthLabel?: string;
  displayLabel?: string;
  opacityLabel?: string;
  // ... and 21 more property labels

  // Element Types (10 labels)
  elementContainer?: string;
  elementText?: string;
  elementImage?: string;
  elementButton?: string;
  // ... and 6 more element type labels

  // Dropdown Options (50+ labels)
  // Display options (6)
  displayBlock?: string;
  displayInline?: string;
  // ...

  // Flex direction options (4)
  flexDirectionRow?: string;
  // ...

  // Justify content options (6)
  justifyContentFlexStart?: string;
  // ...

  // Align items options (5)
  alignItemsFlexStart?: string;
  // ...

  // Font size options (9)
  fontSizeXS?: string;
  // ...

  // Font weight options (8)
  fontWeightThin?: string;
  // ...

  // Text decoration options (4)
  textDecorationNone?: string;
  // ...

  // Border style options (9)
  borderStyleSolid?: string;
  // ...

  // Object fit options (5)
  objectFitContain?: string;
  // ...

  // Action Buttons (3 labels)
  saveButton?: string;
  resetButton?: string;
  cancelButton?: string;

  // Status Messages (3 labels)
  unsavedChangesText?: string;
  savingText?: string;
  hintText?: string;

  // Unsaved Changes Dialog (5 labels)
  unsavedDialogTitle?: string;
  unsavedDialogMessage?: string;
  saveChangesButton?: string;
  discardChangesButton?: string;
  continueEditingButton?: string;

  // Tab Names (3 labels)
  textTabLabel?: string;
  imageTabLabel?: string;
  styleTabLabel?: string;

  // Badge (2 labels)
  badgeText?: string;
  badgeUrl?: string;
}
```

### Full InspectorTheme Interface

The `InspectorTheme` interface contains 30+ customizable color properties:

```typescript
interface InspectorTheme {
  // Control Box (3 colors)
  backgroundColor?: string; // Default: #ffffff
  textColor?: string; // Default: #111827
  secondaryTextColor?: string; // Default: #6b7280

  // Buttons (7 colors)
  buttonColor?: string; // Default: #4417db
  buttonTextColor?: string; // Default: #ffffff
  buttonHoverColor?: string; // Default: #3a13c0
  secondaryButtonColor?: string; // Default: #f3f4f6
  secondaryButtonTextColor?: string; // Default: #6b7280
  secondaryButtonHoverColor?: string; // Default: #e5e7eb
  dangerButtonColor?: string; // Default: #ef4444
  dangerButtonTextColor?: string; // Default: #ffffff

  // Inputs (5 colors)
  inputBackgroundColor?: string; // Default: #f9fafb
  inputTextColor?: string; // Default: #111827
  inputBorderColor?: string; // Default: #d1d5db
  inputFocusBorderColor?: string; // Default: #4417db
  inputPlaceholderColor?: string; // Default: #9ca3af

  // Borders & Dividers (1 color)
  borderColor?: string; // Default: #e5e7eb

  // Status Colors (3 colors)
  warningColor?: string; // Default: #f59e0b
  successColor?: string; // Default: #10b981
  errorColor?: string; // Default: #ef4444

  // Tabs (5 colors)
  tabContainerBg?: string; // Default: #f9fafb
  tabActiveBg?: string; // Default: #4417db
  tabInactiveBg?: string; // Default: transparent
  tabActiveColor?: string; // Default: #ffffff
  tabInactiveColor?: string; // Default: #6b7280

  // Badge (3 colors)
  badgeGradientStart?: string; // Default: #411E93
  badgeGradientEnd?: string; // Default: #E87C85
  badgeTextColor?: string; // Default: #ffffff

  // Overlay (2 colors)
  overlayColor?: string; // Default: #4417db
  overlayOpacity?: number; // Default: 0.2

  // Dialog (4 colors)
  dialogBackdropColor?: string; // Default: rgba(0, 0, 0, 0.6)
  dialogBackgroundColor?: string; // Default: #ffffff
  dialogTextColor?: string; // Default: #111827
  dialogSecondaryTextColor?: string; // Default: #6b7280
}
```

## 🔧 Advanced Examples

### Complete Parent Application Example

```typescript
import { useInspector, updateJSXSource } from "@promakeai/inspector-hook";
import { useRef, useState } from "react";

function ParentApp() {
  const iframeRef = useRef<HTMLIFrameElement>(null);
  const [selectedElement, setSelectedElement] = useState(null);

  const inspector = useInspector(
    iframeRef,
    {
      // Element selection
      onElementSelected: (data) => {
        setSelectedElement(data);
        console.log("Selected:", data);
      },

      // Style updates
      onStyleUpdated: async (data) => {
        const component = data.element.component;
        if (!component?.fileName) return;

        // Fetch source code from your backend
        const sourceCode = await fetch(
          `/api/source/${component.fileName}`
        ).then((r) => r.text());

        // Update JSX source
        const result = updateJSXSource({
          sourceCode,
          lineNumber: component.lineNumber!,
          columnNumber: component.columnNumber || 0,
          tagName: data.element.tagName,
          styles: data.inlineStyles,
          className: data.tailwindClasses.join(" "),
        });

        if (result.success) {
          // Save back to your backend
          await fetch(`/api/source/${component.fileName}`, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ code: result.code }),
          });

          alert("✅ Styles saved successfully!");
        }
      },

      // Text updates
      onTextUpdated: async (data) => {
        console.log("Text changed:", data.text);
        // Save to your backend or update state
      },

      // Image uploads
      onImageUpdated: async (data) => {
        // Upload image to your storage
        const formData = new FormData();
        const blob = await fetch(data.imageData).then((r) => r.blob());
        formData.append("image", blob, data.imageFile.name);

        const response = await fetch("/api/upload-image", {
          method: "POST",
          body: formData,
        });

        const { url } = await response.json();
        console.log("Image uploaded:", url);
      },

      // AI prompts
      onPromptSubmitted: async (data) => {
        // Send to your AI service
        const response = await fetch("/api/ai-generate", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            prompt: data.prompt,
            element: data.element,
          }),
        });

        const result = await response.json();
        console.log("AI result:", result);
      },

      // URL navigation
      onUrlChange: (data) => {
        console.log("Navigated to:", data.pathname);
      },

      // Error handling
      onError: (data) => {
        console.error("Error in iframe:", data);
        // Show error notification
      },

      // Inspector closed
      onInspectorClosed: () => {
        console.log("Inspector closed");
        setSelectedElement(null);
      },
    },
    // Custom labels (i18n)
    {
      editText: "Edit Text",
      editImage: "Edit Image",
      styleEditorTitle: "Styles",
      updateText: "Update",
      cancelButton: "Cancel",
    },
    // Custom theme
    {
      buttonColor: "#ff6b6b",
      buttonHoverColor: "#ee5a5a",
      overlayColor: "#ff6b6b",
    }
  );

  return (
    <div style={{ padding: "20px" }}>
      <div style={{ marginBottom: "20px", display: "flex", gap: "10px" }}>
        <button onClick={() => inspector.toggleInspector()}>
          {inspector.isInspecting ? "⏹ Stop" : "▶ Start"} Inspector
        </button>

        <button onClick={() => inspector.showContentInput(true)}>
          📝 Edit Text
        </button>

        <button onClick={() => inspector.showImageInput(true)}>
          🖼️ Edit Image
        </button>

        <button onClick={() => inspector.showStyleEditor(true)}>
          🎨 Edit Styles
        </button>

        <button onClick={() => inspector.setBadgeVisible(true)}>
          🏷️ Show Badge
        </button>

        <button
          onClick={() =>
            inspector.highlightElement("some-id", {
              color: "#ff0000",
              duration: 5000,
            })
          }
        >
          🔦 Highlight Element
        </button>
      </div>

      {selectedElement && (
        <div
          style={{
            marginBottom: "20px",
            padding: "10px",
            background: "#f0f0f0",
          }}
        >
          <strong>Selected:</strong> {selectedElement.tagName}
          {selectedElement.component?.name && (
            <span> ({selectedElement.component.name})</span>
          )}
        </div>
      )}

      <iframe
        ref={iframeRef}
        src="http://localhost:5173"
        style={{
          width: "100%",
          height: "800px",
          border: "1px solid #ccc",
          borderRadius: "8px",
        }}
      />
    </div>
  );
}

export default ParentApp;
```

### Programmatic Element Highlighting

```typescript
// Highlight element by ID
inspector.highlightElement("inspector-id-123", {
  duration: 5000,
  color: "#ff0000",
  animation: "pulse",
});

// Highlight and navigate to a specific route first
inspector.highlightElement("inspector-id-123", {
  targetRoute: "/products",
  scrollIntoView: true,
});

// Highlight using full element data
inspector.highlightElement(selectedElementData, {
  animation: "fade",
  duration: 2000,
});
```

### Getting Element Information

```typescript
// Request element info
inspector.getElementByInspectorId("inspector-id-123");

// Handle response
const inspector = useInspector(iframeRef, {
  onElementInfoReceived: (data) => {
    if (data.found && data.element) {
      console.log("Component:", data.element.component);
      console.log("Position:", data.element.position);
      console.log("Parents:", data.element.parents);
      console.log("Children:", data.element.children);
    } else {
      console.error("Element not found:", data.error);
    }
  },
});
```

## 🤝 Integration with @promakeai/inspector

This package is designed to work seamlessly with `@promakeai/inspector`. Here's the complete setup:

### Child Application (iframe content)

```typescript
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { inspectorDebugger } from "@promakeai/inspector/plugin";

export default defineConfig({
  plugins: [inspectorDebugger({ enabled: true }), react()],
});
```

```typescript
// main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import { Inspector } from "@promakeai/inspector";
import "@promakeai/inspector/inspector.css";
import App from "./App";

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <Inspector />
    <App />
  </React.StrictMode>
);
```

### Parent Application (iframe host)

```typescript
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { inspectorHookPlugin } from "@promakeai/inspector-hook";

export default defineConfig({
  plugins: [inspectorHookPlugin(), react()],
});
```

```typescript
// ParentApp.tsx
import { useInspector } from "@promakeai/inspector-hook";
import { useRef } from "react";

function ParentApp() {
  const iframeRef = useRef<HTMLIFrameElement>(null);

  const inspector = useInspector(iframeRef, {
    onElementSelected: (data) => console.log("Selected:", data),
    onStyleUpdated: (data) => console.log("Style updated:", data),
  });

  return (
    <div>
      <button onClick={() => inspector.toggleInspector()}>
        Toggle Inspector
      </button>
      <iframe ref={iframeRef} src="http://localhost:5173" />
    </div>
  );
}
```

## 📋 Requirements

- **React**: `>=18.0.0`
- **React DOM**: `>=18.0.0`
- **Vite**: `>=5.0.0` (optional, only if using the Vite plugin)

## 📝 License

MIT

## 🐛 Troubleshooting

### Messages not being received

Make sure the iframe is fully loaded before sending messages:

```typescript
<iframe
  ref={iframeRef}
  src="http://localhost:5173"
  onLoad={() => {
    console.log("Iframe loaded, ready to communicate");
  }}
/>
```

### Babel errors when using updateJSXSource

Make sure you've added the Vite plugin:

```typescript
import { inspectorHookPlugin } from "@promakeai/inspector-hook";

export default defineConfig({
  plugins: [
    inspectorHookPlugin(), // Required!
    react(),
  ],
});
```

### Styles not updating correctly

Ensure you're providing the correct line and column numbers from the component info:

```typescript
const result = updateJSXSource({
  sourceCode,
  lineNumber: data.element.component.lineNumber, // ✅ Use from component info
  columnNumber: data.element.component.columnNumber || 0,
  tagName: data.element.tagName,
  styles: data.inlineStyles,
});
```

## 🚀 Changelog

### v1.0.1

- Initial release
- `useInspector` hook for iframe communication
- `updateJSXSource` utility for AST-based code updates
- `inspectorHookPlugin` Vite plugin for Babel configuration
- Full TypeScript support
- Comprehensive callback system
- Theme and label customization

## 📞 Support

For issues, questions, or contributions, please visit:

- GitHub: [github.com/promakeai/inspector](https://github.com/promakeai/inspector)
- Documentation: [promake.ai/docs](https://promake.ai/docs)

---

Made with ❤️ by [Promake](https://promake.ai)