npm package: @jaredef/lovable-export
Description: A React component library for exporting Lovable.dev pages as static HTML and uploading them to a proxy server.
Latest version: 1.0.5

# @jaredef/lovable-export

A React component library for exporting Lovable.dev pages as static HTML and uploading them to a proxy server.

## Installation

```bash
npm install @jaredef/lovable-export
```

or

```bash
pnpm add @jaredef/lovable-export
```

## Quick Start

There are three ways to configure the export functionality:

### Option 1: Direct Props (Simplest)

Pass configuration directly to the ExportButton component:

```tsx
import { Toaster } from "@/components/ui/toaster";
import { Toaster as Sonner } from "@/components/ui/sonner";
import { TooltipProvider } from "@/components/ui/tooltip";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { ExportButton } from '@jaredef/lovable-export'
import '@jaredef/lovable-export/styles.css'
import Index from "./pages/Index";
import NotFound from "./pages/NotFound";

const queryClient = new QueryClient();

const App = () => (
  <QueryClientProvider client={queryClient}>
    <TooltipProvider>
      <Toaster />
      <Sonner />
      <BrowserRouter>
        <Routes>
          <Route path="/" element={<Index />} />
          <Route path="*" element={<NotFound />} />
        </Routes>
      </BrowserRouter>
      <ExportButton
        proxyUrl="https://your-proxy.com"
        authPassword="your-admin-password"
        defaultSlug="my-page"
      />
    </TooltipProvider>
  </QueryClientProvider>
);

export default App;
```

### Option 2: Environment Variables

Configure via Vite environment variables:

```env
# .env or .env.local
VITE_PROXY_URL=https://your-proxy.com
VITE_ADMIN_PASSWORD=your-admin-password
```

```tsx
import { Toaster } from "@/components/ui/toaster";
import { Toaster as Sonner } from "@/components/ui/sonner";
import { TooltipProvider } from "@/components/ui/tooltip";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { ExportButton } from '@jaredef/lovable-export'
import '@jaredef/lovable-export/styles.css'
import Index from "./pages/Index";
import NotFound from "./pages/NotFound";

const queryClient = new QueryClient();

const App = () => (
  <QueryClientProvider client={queryClient}>
    <TooltipProvider>
      <Toaster />
      <Sonner />
      <BrowserRouter>
        <Routes>
          <Route path="/" element={<Index />} />
          <Route path="*" element={<NotFound />} />
        </Routes>
      </BrowserRouter>
      {/* No props needed - reads from env vars */}
      <ExportButton defaultSlug="my-page" />
    </TooltipProvider>
  </QueryClientProvider>
);

export default App;
```

### Option 3: React Context Provider

Wrap your app with ExportProvider for centralized configuration:

```tsx
import { Toaster } from "@/components/ui/toaster";
import { Toaster as Sonner } from "@/components/ui/sonner";
import { TooltipProvider } from "@/components/ui/tooltip";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { ExportProvider, ExportButton } from '@jaredef/lovable-export'
import '@jaredef/lovable-export/styles.css'
import Index from "./pages/Index";
import NotFound from "./pages/NotFound";

const queryClient = new QueryClient();

const App = () => (
  <QueryClientProvider client={queryClient}>
    <ExportProvider
      proxyUrl="https://your-proxy.com"
      authPassword="your-admin-password"
    >
      <TooltipProvider>
        <Toaster />
        <Sonner />
        <BrowserRouter>
          <Routes>
            <Route path="/" element={<Index />} />
            <Route path="*" element={<NotFound />} />
          </Routes>
        </BrowserRouter>
        <ExportButton defaultSlug="my-page" />
      </TooltipProvider>
    </ExportProvider>
  </QueryClientProvider>
);

export default App;
```

## API Reference

### ExportButton

A floating button component that opens an export modal.

#### Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `defaultSlug` | `string` | `''` | Pre-filled slug for the export |
| `defaultTitle` | `string` | `''` | Pre-filled title (falls back to `document.title`) |
| `defaultDescription` | `string` | `''` | Pre-filled description for SEO |
| `position` | `'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left'` | `'bottom-right'` | Button position |
| `proxyUrl` | `string` | From context or env | URL of the proxy server |
| `authPassword` | `string` | From context or env | Admin password for uploads |

### ExportProvider

React context provider for centralized configuration.

#### Props

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `proxyUrl` | `string` | Yes | URL of the proxy server |
| `authPassword` | `string` | Yes | Admin password for uploads |
| `children` | `ReactNode` | Yes | Child components |

### useExport Hook

For building custom export UIs.

```tsx
import { useExport } from '@jaredef/lovable-export'

function CustomExporter() {
  const {
    status,       // 'idle' | 'exporting' | 'uploading' | 'success' | 'error'
    message,      // Status message string
    handleDownload,  // (slug, title?, description?) => Promise<void>
    handleUpload,    // (slug, title?, description?) => Promise<void>
    createExportZip  // (options?) => Promise<Blob>
  } = useExport({
    proxyUrl: 'https://your-proxy.com',
    authPassword: 'your-password'
  })

  return (
    <button onClick={() => handleDownload('my-slug')}>
      Export to ZIP
    </button>
  )
}
```

### useConfig Hook

Get resolved configuration (props > context > env vars).

```tsx
import { useConfig } from '@jaredef/lovable-export'

function MyComponent() {
  const config = useConfig({
    proxyUrl: 'optional-override',
    authPassword: 'optional-override'
  })

  console.log(config.proxyUrl, config.authPassword)
}
```

### Utility Functions

#### createExportZip

Create a ZIP blob from the current page.

```tsx
import { createExportZip } from '@jaredef/lovable-export'

const zipBlob = await createExportZip({
  title: 'My Page Title',
  description: 'Page description for SEO'
})
```

#### uploadExport

Upload a ZIP to the proxy server.

```tsx
import { uploadExport } from '@jaredef/lovable-export'

const result = await uploadExport({
  zipBlob,
  slug: 'my-page',
  title: 'My Page',
  description: 'Description',
  config: { proxyUrl: '...', authPassword: '...' },
  replace: true
})
```

## Environment Variables

| Variable | Description |
|----------|-------------|
| `VITE_PROXY_URL` | URL of your proxy server (e.g., `https://your-domain.com`) |
| `VITE_ADMIN_PASSWORD` | Admin password for authenticated uploads |

## Configuration Priority

The library resolves configuration in this order (highest priority first):

1. **Props** - Values passed directly to components
2. **Context** - Values from `ExportProvider`
3. **Environment Variables** - `VITE_PROXY_URL` and `VITE_ADMIN_PASSWORD`
4. **Defaults** - `http://localhost:3000` for proxy URL, empty string for password

## Styling

Import the CSS file in your application:

```tsx
import '@jaredef/lovable-export/styles.css'
```

### CSS Custom Properties

Customize the appearance using CSS custom properties:

```css
:root {
  --lovable-export-primary: #2563eb;
  --lovable-export-primary-hover: #1d4ed8;
  --lovable-export-bg: #ffffff;
  --lovable-export-text: #1f2937;
  --lovable-export-border: #e5e7eb;
  /* See input.css for all available properties */
}
```

## TypeScript Types

```tsx
import type {
  ExportButtonProps,
  ExportConfig,
  ExportResult,
  UploadResult,
  ExportStatus
} from '@jaredef/lovable-export'
```

## Troubleshooting

### Export button not appearing

Make sure you've imported the CSS file:
```tsx
import '@jaredef/lovable-export/styles.css'
```

### Upload fails with 401 error

Check that your `authPassword` matches the `ADMIN_PASSWORD` configured on your proxy server.

### Assets not loading in exported page

The export utility automatically converts absolute paths to relative paths. However, external CDN resources are preserved. Ensure all local assets use relative paths (`./assets/...` not `/assets/...`).

### Page validation warnings

The export utility checks for:
- Absolute paths that may not work after export
- Missing `<title>` tag

Address these warnings to ensure the exported page works correctly.

## Peer Dependencies

This package requires:
- `react` >= 18.0.0
- `react-dom` >= 18.0.0

## License

MIT