npm package: @mark_stagepanda/email-studio-dropin
Description: Drop-in React TypeScript email studio builder
Latest version: 0.1.5

# @mark_stagepanda/email-studio-dropin

A drop-in, TypeScript-ready React email studio that can be embedded in Stagepanda or any React app.

Current package version: `0.1.2`

## What it provides
- Drag/drop email block editor UI
- Editable HTML view (paste/edit HTML and apply it to canvas)
- Image upload + on-canvas resize/rotate handles
- Right-side block settings panel
- Mobile/desktop preview modes
- Undo/redo history (20 activities) with version dropdown restore
- File-based starter templates from `template-library/` folders
- Typed `onStateChange` callback for persistence

## Install

### Option A: local package (recommended during development)
From this repo root:

```bash
cd packages/email-studio-dropin
npm install
npm run build
npm pack
```

Then in your target React app (for example, Stagepanda):

```bash
npm install /absolute/path/to/packages/email-studio-dropin/<generated-tarball>.tgz
```

### Option B: publish to npm (for third-party users)

```bash
cd packages/email-studio-dropin
npm publish --access public
```

Then consumers install with:

```bash
npm install @mark_stagepanda/email-studio-dropin
# or pin this release explicitly:
npm install @mark_stagepanda/email-studio-dropin@0.1.2
```

## Usage

```tsx
import React, { useState } from "react";
import {
  EmailStudioDropin,
  EmailStudioOutput,
  EmailStudioState,
  exportEmailHtmlFromState,
} from "@mark_stagepanda/email-studio-dropin";

export default function EmailBuilderPage() {
  const [editorState, setEditorState] = useState<EmailStudioState | null>(null);
  const [currentHtml, setCurrentHtml] = useState("");
  const [output, setOutput] = useState<EmailStudioOutput | null>(null);

  return (
    <div style={{ height: "calc(100vh - 64px)" }}>
      <EmailStudioDropin
        height="100%"
        initialHtml="<!doctype html><html><body>...</body></html>"
        defaultTemplateId="library:invitations/event-invitation"
        onStateChange={(state) => setEditorState(state)}
        onHtmlChange={(html) => setCurrentHtml(html)}
        onOutputChange={(next) => setOutput(next)}
        bootstrap={{
          companyName: "Stagepanda",
          socialLinks: {
            facebook: "https://facebook.com/stagepanda",
            linkedin: "https://linkedin.com/company/stagepanda",
            instagram: "https://instagram.com/stagepanda",
            youtube: "https://youtube.com/@stagepanda",
            stagepanda: "https://stagepanda.com",
          },
        }}
      />
    </div>
  );
}
```

## Exporting for email clients
The editor top bar includes built-in export controls with presets for `Universal`, `Gmail`, and `Outlook`.

You can also export programmatically:

```tsx
const html = exportEmailHtmlFromState(editorState, { client: "outlook" });
```

Programmatic export helper:
- `exportEmailHtmlFromState(state, { client?: "universal" | "gmail" | "outlook", width?: number })`
- `downloadEmailHtml(state, { client?: "universal" | "gmail" | "outlook", filename?: string, width?: number })`
- `importHtmlToBlocks(html)` to map pasted HTML into editable design blocks

## Props
- `initialState?: Partial<EmailStudioState>`
  Seed initial editor data (name, blocks, preview, etc).
- `initialHtml?: string`
  Optional email HTML string to import as editable blocks on load. If `initialState.blocks` is provided, it takes precedence over `initialHtml`.
- `defaultTemplateId?: string`
  Sets the starter template when `initialState.blocks` is empty. Use a generated ID from `template-library` (for example `library:invitations/event-invitation`).
- `onStateChange?: (state: EmailStudioState) => void`
  Called whenever editor state changes.
- `onHtmlChange?: (html: string) => void`
  Called whenever editor state changes with the latest generated email HTML.
- `onOutputChange?: (output: { json: EmailStudioState; html: string; subject: string; preheader: string }) => void`
  Called whenever editor state changes with JSON, HTML, plus email subject and preheader.
- `htmlClient?: "universal" | "gmail" | "outlook"`
  Chooses the HTML generation preset used by `onHtmlChange`.
- `className?: string`
  Class name on root container.
- `style?: React.CSSProperties`
  Additional inline root styles.
- `hideTopBar?: boolean`
  Hide the top action bar if embedding into an existing shell.
- `height?: number | string`
  Root editor height (`"100vh"` by default).
- `fontHref?: string`
  Override the loaded webfont URL.
- `bootstrap?: { companyName?: string; socialLinks?: Partial<Record<"facebook" | "twitter" | "x" | "linkedin" | "instagram" | "youtube" | "stagepanda", string>> }`
  Optional initializer. Company name is auto-applied to footer block content; social links are auto-applied to matching social icons.

## Templates
- Template selection is opened from `Create campaign +` and shown in a bottom-sheet selector.
- The selector shows categories on the left and template thumbnails on the right.
- Selecting a template replaces the current canvas blocks.
- Folder-generated templates are exported from package:
  - `TEMPLATE_PRESETS`
  - `DEFAULT_TEMPLATE_ID`
  - `createTemplateBlocks(templateId, bootstrap?)`

## File-Based Template Categories (Primary Folder)
You can manage templates from files using:

`packages/email-studio-dropin/template-library/`

Rules:
- Every subfolder is a **category**.
- Every `.html` file in that subfolder is a template.
- Template name = filename without `.html`.

Example:

```text
template-library/
  event-invitation/
    Launch-Invite.html
  reminders/
    Last-Call.html
```

After adding files, run:

```bash
npm run build --prefix packages/email-studio-dropin
```

The build auto-generates `src/emailStudio/generatedTemplateLibrary.ts` and those templates appear in the Create Campaign template selector by category.

Development hot reload:

```bash
npm start
```

This now runs watchers for `template-library` and drop-in TypeScript build, so edits/new HTML files are regenerated automatically during development.

## Integration notes for any email project
- Persist `onStateChange` output in your backend (JSON).
- Restore saved drafts via `initialState`.
- Wrap the component in a fixed-height container (`height: 100%` or viewport-calculated) to avoid layout collapse.
- Keep `react` and `react-dom` as host app dependencies (peer deps).

## Stagepanda-specific integration checklist
1. Create a route/page for the editor and mount `EmailStudioDropin` in a full-height container.
2. Connect `onStateChange` to your draft autosave endpoint.
3. Optionally pass `hideTopBar` if Stagepanda already has global actions.
4. Persist and rehydrate `initialState` for editing existing templates.

## Block Extension Workflow
When adding a new content block type, update these extension points:

1. `src/emailStudio/constants.ts`
- Add the block to `BLOCKS` (left panel + insert menu).
- Add default config in `DEFAULTS`.

2. `src/emailStudio/components/icons.tsx`
- Add `BlockIcon` rendering for the new `type`.

3. `src/EmailStudioDropin.tsx`
- Ensure `actions.addBlock` maps/normalizes the block type if needed.
- Add any bootstrap-driven defaults.

4. `src/emailStudio/components/Canvas.tsx`
- Add render branch in `renderBlockContent` for canvas preview/editing.

5. `src/emailStudio/components/RightPanel.tsx`
- Add settings panel UI branch for the block.
- Keep settings updates isolated where possible (small subcomponents per block).

6. `src/emailStudio/exportHtml.ts`
- Add HTML export branch for the new block.

7. `src/emailStudio/importHtml.ts` (optional)
- Add reverse mapping if HTML import should produce this block.

8. Regression coverage
- Add/update tests for:
  - insert behavior
  - settings behavior
  - export output
  - any bootstrap/default interactions

## Distribution Verification
Before publishing, run:

```bash
npm run verify:dropin
```

This runs clean build and package dry-run checks for the drop-in.