npm package: @supernovaio/prototyping-tooling
Description: Prototyping Tooling — iframe communication bridge for design-to-code workflows
Latest version: 0.9.8

# @supernovaio/prototyping-tooling

Communication bridge for iframe-based design-to-code prototyping workflows. Enables a **host** (parent window) to control and interact with a **client** (child iframe) for element selection, design-mode editing, annotations, and live source-code updates.

## Installation

```bash
npm install @supernovaio/prototyping-tooling
```

## Entry Points

| Import path | Side | Description |
|---|---|---|
| `@supernovaio/prototyping-tooling/host` | Parent | `ForgeHost` — connects to the iframe, sends commands, receives events |
| `@supernovaio/prototyping-tooling/client` | Child | `ForgeClient` — auto-initializes, handles messages, manages overlays |
| `@supernovaio/prototyping-tooling/react` | Parent | React hooks: `useElementSelector`, `useDesignMode` |
| `@supernovaio/prototyping-tooling/build` | Build | Vite plugin that injects `data-forge-id` attributes into JSX |
| `@supernovaio/prototyping-tooling` | Either | Shared types, debug utilities, message constants |

## Quick Start

### Parent (host) app

```typescript
import { ForgeHost } from '@supernovaio/prototyping-tooling/host'

const iframe = document.querySelector('iframe')!
const host = new ForgeHost(iframe)

await host.connect()

host.on('element-selected', (selection) => {
  console.log('Selected:', selection.componentName, selection.forgeId)
})

host.enableDesignMode(true)
```

### Child (client) app

```typescript
// Importing the client auto-initializes it
import '@supernovaio/prototyping-tooling/client'
```

### Vite plugin (child build)

```typescript
// vite.config.ts
import { supernovaDesignPlugin } from '@supernovaio/prototyping-tooling/build'

export default defineConfig({
  plugins: [react(), supernovaDesignPlugin()],
})
```

## API Reference

### `ForgeHost` (host)

| Method | Description |
|---|---|
| `connect()` | Connects to the child iframe. Resolves on `CHILD_READY`. |
| `enableSelectMode(enabled)` | Toggles XPath element-picking mode |
| `enableDesignMode(enabled)` | Toggles design mode (click to select, edit props) |
| `enableAnnotationMode(enabled)` | Toggles annotation mode (click/drag to annotate) |
| `updateElementText(forgeId, text)` | Updates text content of a forge element |
| `updateProperty(forgeId, prop, value)` | Updates any property via Babel AST |
| `deselectElement()` | Clears the design-mode selection |
| `deleteAnnotation(id)` | Removes an annotation dot |
| `keepAnnotation(id, order)` | Confirms and numbers an annotation |
| `updateAnnotationOrders(orders)` | Bulk-updates annotation order numbers |
| `toggleMarkers()` | Toggles visibility of annotation marker badges |
| `restoreAnnotations(annotations)` | Re-sends saved annotations to the child to recreate dots after reconnect |
| `registerComponents(components)` | Sends component definitions to the child |
| `on(event, handler)` | Subscribes to events; returns unsubscribe fn |
| `getStatus()` | Returns `'connecting' \| 'connected' \| 'disconnected'` |
| `disconnect()` | Cleans up listeners and resets state |

#### Events

| Event | Payload |
|---|---|
| `connected` | — |
| `disconnected` | — |
| `xpath-selected` | `xpath: string` |
| `select-mode-disabled` | — |
| `design-mode-disabled` | — |
| `element-selected` | `ElementSelection` |
| `annotation-created` | `AnnotationData` |
| `annotation-dot-clicked` | `annotationId, dotX, dotY` |
| `source-updated` | `forgeId, source, filePath` |

### `ForgeClient` (client)

Singleton that auto-initializes on import. Access via `ForgeClient.getInstance()`.

Accepts an optional `ForgeClientOptions` with an `onError` callback for error handling.

The client also exposes a public `enableAnnotationMode(enabled)` method for console-driven testing (see [Console Access](#console-access)).

### React Hooks

#### `useElementSelector(host)`

Returns `{ selectMode, connectionStatus, selectedXPath, enableSelectMode, clearXPath }`.

#### `useDesignMode(host)`

Returns `{ designMode, selectedElement, enableDesignMode, updateText, updateProperty, deselectElement }`.

### Annotations

Annotation badges are anchored to their associated elements. When the page scrolls or the window resizes, badges automatically reposition to follow their elements.

**Forge ID generation** is deterministic — IDs are derived from `filePath + line + column`, so the same source position always produces the same ID across rebuilds. Elements rendered inside loops (`.map()`) share the same forge-id; the system disambiguates them using a `forgeIdIndex`.

**Restoring annotations** — the parent can call `host.restoreAnnotations(annotations)` after reconnecting to the child (e.g., after HMR reload) to recreate all annotation dots. The child resolves elements by `data-forge-id` first, falling back to XPath.

### Keyboard Shortcuts

The plugin does **not** handle keyboard events. The consuming parent application is responsible for binding its own shortcuts and calling the appropriate `ForgeHost` methods. Example implementation:

```typescript
useEffect(() => {
  const handleKeyDown = (e: KeyboardEvent) => {
    // Cmd/Ctrl + Shift + F → toggle annotation mode
    if (e.shiftKey && (e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'f') {
      e.preventDefault()
      host.enableAnnotationMode(!annotationMode)
    }
    // Escape → close popup
    if (e.key === 'Escape') {
      closePopup()
    }
    // H → toggle annotation markers (skip when typing)
    if (e.key.toLowerCase() === 'h' && !e.metaKey && !e.ctrlKey && !e.shiftKey && !e.altKey) {
      const tag = (e.target as HTMLElement)?.tagName?.toLowerCase()
      if (tag === 'input' || tag === 'textarea') return
      host.toggleMarkers()
    }
  }
  window.addEventListener('keydown', handleKeyDown)
  return () => window.removeEventListener('keydown', handleKeyDown)
}, [host, annotationMode])
```

### Console Access

Both `ForgeHost` and `ForgeClient` expose themselves on the `window` for console-driven testing:

```javascript
// From the parent page's console:
__forgeHost.enableAnnotationMode(true)   // activate annotation mode
__forgeHost.enableAnnotationMode(false)  // deactivate
__forgeHost.toggleMarkers()              // toggle marker visibility
__forgeHost.restoreAnnotations([...])    // restore saved annotations

// From the child iframe's console (switch iframe context in DevTools):
__forgeClient.enableAnnotationMode(true)
__forgeAnnotations.toggleMarkers()
```

### Debug

```typescript
import { enableDebug } from '@supernovaio/prototyping-tooling'

enableDebug(true)  // Turns on timestamped console output
enableDebug(false) // Silences all library logging
```

## Architecture

```
src/
├── shared/          Constants, debug, events, messaging
├── client/
│   ├── general/     ForgeClient orchestrator, SourceCodeUpdater, types
│   ├── annotation/  AnnotationOverlay
│   ├── select-mode/ SelectModeController
│   └── design-mode/ DesignModeController
├── host/            ForgeHost, types
├── react/           useElementSelector, useDesignMode
└── build/           Vite plugin (supernovaDesignPlugin)
```

See [INTEGRATION.md](./INTEGRATION.md) for a detailed integration guide.

## License

MIT