Rebuilt the visual site builder from scratch using Craft.js, React 18, and TypeScript. The new editor renders directly in the DOM (no iframe), supports 40+ components, multi-page with shared header/footer, 16 templates, full-spectrum color/gradient controls, custom head code injection, save/publish workflow, and auto-save. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
464 lines
22 KiB
Markdown
464 lines
22 KiB
Markdown
# WHP Site Builder v2 (Craft.js) - Project Documentation
|
|
|
|
## Overview
|
|
|
|
A visual drag-and-drop website builder rebuilt from the ground up using Craft.js, React 18, and TypeScript. Replaces the legacy GrapesJS-based editor (`/workspace/site-builder/`). Users create multi-page websites without writing code, with server-side storage through WHP's PHP API layer.
|
|
|
|
**Stack:** Vite 6 + React 18 + TypeScript 5 + @craftjs/core 0.2.x
|
|
**Bundle:** ~460KB JS + ~15KB CSS
|
|
**Version:** 2.0.0
|
|
|
|
## File Structure
|
|
|
|
```
|
|
craft/
|
|
├── index.html # HTML shell (loads fonts, FA icons, mounts React)
|
|
├── package.json # Dependencies and scripts (v2.0.0)
|
|
├── tsconfig.json # TypeScript config (ES2020, strict, path aliases)
|
|
├── vite.config.ts # Vite config (builds to dist/js/editor.js + dist/css/editor.css)
|
|
├── CLAUDE.md # This file
|
|
├── README.md # Brief project readme
|
|
├── FEATURES.md # User-facing features list
|
|
├── dist/ # Build output (not committed)
|
|
│ ├── index.html
|
|
│ ├── js/editor.js
|
|
│ ├── css/editor.css
|
|
│ └── assets/
|
|
├── src/
|
|
│ ├── main.tsx # Entry point: reads WHP_CONFIG, mounts <App>
|
|
│ ├── App.tsx # Wraps <Editor> with providers, passes resolver
|
|
│ │
|
|
│ ├── types/
|
|
│ │ └── index.ts # WhpConfig, PageData, AssetData, StyleProps, DeviceMode
|
|
│ │
|
|
│ ├── state/
|
|
│ │ ├── EditorConfigContext.tsx # React context for WHP_CONFIG (useEditorConfig hook)
|
|
│ │ ├── PageContext.tsx # Multi-page state (pages, header, footer, CRUD, switching)
|
|
│ │ └── SiteDesignContext.tsx # Site-wide design tokens (17 properties, Basic/Advanced)
|
|
│ │
|
|
│ ├── editor/
|
|
│ │ ├── EditorShell.tsx # 3-panel layout: TopBar + LeftPanel + Canvas + RightPanel + ContextMenu
|
|
│ │ └── Canvas.tsx # Craft.js <Frame> with device-width switching
|
|
│ │
|
|
│ ├── components/
|
|
│ │ ├── resolver.ts # Component map for Craft.js serialization (20 components)
|
|
│ │ ├── layout/
|
|
│ │ │ ├── Container.tsx # Generic container (div/section/article/header/footer/main)
|
|
│ │ │ ├── Section.tsx # Full-width section with centered inner container
|
|
│ │ │ ├── ColumnLayout.tsx # Flex columns (1-6, with split ratios)
|
|
│ │ │ ├── BackgroundSection.tsx # Section with background image/gradient overlay
|
|
│ │ │ ├── HeaderZone.tsx # Page-level header zone wrapper
|
|
│ │ │ └── FooterZone.tsx # Page-level footer zone wrapper
|
|
│ │ ├── basic/
|
|
│ │ │ ├── Heading.tsx # Inline-editable heading (h1-h6)
|
|
│ │ │ ├── TextBlock.tsx # Inline-editable paragraph
|
|
│ │ │ ├── ButtonLink.tsx # Styled <a> with color presets
|
|
│ │ │ ├── Navbar.tsx # Navigation bar (text/image logo, page links, external links, CTA)
|
|
│ │ │ ├── Footer.tsx # Footer component (links, copyright, social)
|
|
│ │ │ ├── Divider.tsx # Horizontal rule (color, thickness)
|
|
│ │ │ └── Spacer.tsx # Vertical spacing element
|
|
│ │ ├── media/
|
|
│ │ │ ├── ImageBlock.tsx # Image with placeholder, upload, browse, sizing
|
|
│ │ │ └── VideoBlock.tsx # Video embed (YouTube, Vimeo, direct files, background mode)
|
|
│ │ ├── sections/
|
|
│ │ │ ├── HeroSimple.tsx # Pre-built hero section with heading, subtext, CTA
|
|
│ │ │ ├── FeaturesGrid.tsx # 3-column feature cards grid
|
|
│ │ │ └── CTASection.tsx # Call-to-action banner section
|
|
│ │ └── forms/
|
|
│ │ ├── FormContainer.tsx # Form wrapper with action/method
|
|
│ │ ├── InputField.tsx # Input field with label and placeholder
|
|
│ │ ├── TextareaField.tsx # Textarea field with label
|
|
│ │ └── FormButton.tsx # Submit button with styling
|
|
│ │
|
|
│ ├── panels/
|
|
│ │ ├── topbar/
|
|
│ │ │ ├── TopBar.tsx # Back button, domain badge, device switcher, undo/redo, save, templates
|
|
│ │ │ └── TemplateModal.tsx # Template browser with categories and one-click loading
|
|
│ │ ├── left/
|
|
│ │ │ ├── LeftPanel.tsx # Tabs: Blocks | Pages | Layers | Assets
|
|
│ │ │ ├── BlocksPanel.tsx # Draggable block toolbox with categories
|
|
│ │ │ ├── PagesPanel.tsx # Multi-page CRUD, header/footer editing
|
|
│ │ │ ├── LayersPanel.tsx # Component hierarchy tree view
|
|
│ │ │ └── AssetsPanel.tsx # Asset browser with upload, drag-drop, thumbnails
|
|
│ │ ├── right/
|
|
│ │ │ ├── RightPanel.tsx # Tabs: Styles | Settings | Head
|
|
│ │ │ ├── GuidedStyles.tsx # Context-aware style panel (shows selected type)
|
|
│ │ │ └── SiteDesignPanel.tsx # Site-wide design tokens editor (Basic/Advanced tabs)
|
|
│ │ └── context-menu/
|
|
│ │ └── ContextMenu.tsx # Right-click context menu (duplicate, copy, paste, delete, etc.)
|
|
│ │
|
|
│ ├── hooks/
|
|
│ │ ├── useWhpApi.ts # Save/load/deploy via WHP API (with auto-save)
|
|
│ │ ├── useAssets.ts # Asset upload, browse, delete via WHP API
|
|
│ │ ├── useContextMenu.ts # Right-click menu state management
|
|
│ │ └── useKeyboardShortcuts.ts # Keyboard shortcut handler (undo, redo, delete, etc.)
|
|
│ │
|
|
│ ├── templates/
|
|
│ │ ├── index.ts # Template exports
|
|
│ │ └── definitions.ts # 16 template definitions across 4 categories
|
|
│ │
|
|
│ ├── ui/
|
|
│ │ └── SettingsTabs.tsx # Reusable General/Style/Advanced tabs for component settings
|
|
│ │
|
|
│ ├── constants/
|
|
│ │ └── presets.ts # Color, font, spacing, radius, gradient, device width presets
|
|
│ │
|
|
│ ├── utils/
|
|
│ │ ├── style-helpers.ts # cssPropsToString(), mergeStyles()
|
|
│ │ └── html-export.ts # Recursive node-to-HTML renderer, full page export
|
|
│ │
|
|
│ └── styles/
|
|
│ └── editor.css # Dark theme, CSS variables
|
|
```
|
|
|
|
## Running Locally
|
|
|
|
```bash
|
|
cd /workspace/site-builder/craft
|
|
npm install
|
|
npm run dev
|
|
```
|
|
|
|
Opens at `http://localhost:5173`. The Vite dev server proxies `/api` requests to `http://192.168.1.105:8080` (the WHP staging server) for save/load during development.
|
|
|
|
In standalone mode (no WHP_CONFIG on `window`), the editor runs fully client-side without save/load functionality.
|
|
|
|
## Building
|
|
|
|
```bash
|
|
npm run build
|
|
```
|
|
|
|
Runs `tsc && vite build`. Output goes to `dist/`:
|
|
- `dist/index.html` - HTML shell
|
|
- `dist/js/editor.js` - Single JS bundle
|
|
- `dist/css/editor.css` - All styles
|
|
- `dist/assets/` - Static assets (if any)
|
|
|
|
## Deploying to WHP
|
|
|
|
Copy the built `dist/` contents into the WHP site-builder web directory:
|
|
|
|
```bash
|
|
# Build
|
|
cd /workspace/site-builder/craft && npm run build
|
|
|
|
# Deploy to WHP Docker container
|
|
cp dist/index.html /docker/whp/web/site-builder/editor.html
|
|
cp -r dist/js/ /docker/whp/web/site-builder/js/
|
|
cp -r dist/css/ /docker/whp/web/site-builder/css/
|
|
```
|
|
|
|
The PHP wrapper (`/docker/whp/web/site-builder/index.php`) injects `WHP_CONFIG` into the HTML before serving it, so the editor gets the current user's session, CSRF token, site ID, etc.
|
|
|
|
## Architecture
|
|
|
|
### Key Decisions
|
|
|
|
1. **No iframe** - The canvas renders directly in the DOM (unlike GrapesJS which uses an iframe). This simplifies drag-and-drop and avoids cross-origin issues but means editor CSS must not leak into user content.
|
|
|
|
2. **Inline styles** - All component styling uses React `CSSProperties` (inline styles). No class-based CSS for user content. This makes HTML export trivial and avoids stylesheet management.
|
|
|
|
3. **Single Frame, multi-page** - Craft.js `<Frame>` holds one page at a time. Page switching serializes the current state, stores it, and deserializes the new page's state.
|
|
|
|
4. **Header/Footer as separate pages** - Header and Footer are stored as independent Craft.js states (like pages) that render above and below every page. Editing them uses the same canvas but with a distinct editing mode. This provides site-wide shared navigation and footer.
|
|
|
|
5. **API compatibility** - The save endpoint sends data in the same format as the GrapesJS version (`{ site_id, name, html, css, grapesjs: serializedJson }`), so the PHP backend doesn't need changes.
|
|
|
|
6. **Component-based architecture** - Each visual element is a React component that doubles as a Craft.js `UserComponent`. All rendering, settings UI, and HTML export are co-located in one file.
|
|
|
|
7. **Site Design Tokens** - A `SiteDesignContext` provides 17 design properties (colors, fonts, radii, nav style) that components can reference. Templates import their own design tokens when loaded.
|
|
|
|
### Component Architecture
|
|
|
|
Every component in `src/components/` follows this pattern:
|
|
|
|
```typescript
|
|
import { UserComponent, useNode } from '@craftjs/core';
|
|
|
|
// 1. Props interface
|
|
interface MyComponentProps {
|
|
text?: string;
|
|
style?: CSSProperties;
|
|
}
|
|
|
|
// 2. The component itself (renders in editor canvas)
|
|
export const MyComponent: UserComponent<MyComponentProps> = ({ text, style }) => {
|
|
const { connectors: { connect, drag } } = useNode();
|
|
return <div ref={(r) => { if (r) connect(drag(r)); }} style={style}>{text}</div>;
|
|
};
|
|
|
|
// 3. Settings panel (rendered in right panel when selected)
|
|
const MyComponentSettings: React.FC = () => {
|
|
const { actions: { setProp }, props } = useNode((node) => ({
|
|
props: node.data.props as MyComponentProps,
|
|
}));
|
|
return <div>/* preset buttons, inputs, etc. */</div>;
|
|
};
|
|
|
|
// 4. Craft config (displayName, default props, rules, related settings)
|
|
MyComponent.craft = {
|
|
displayName: 'My Component',
|
|
props: { text: 'Default text', style: {} },
|
|
rules: { canDrag: () => true, canMoveIn: () => false, canMoveOut: () => true },
|
|
related: { settings: MyComponentSettings },
|
|
};
|
|
|
|
// 5. HTML export (static method for serializing to HTML string)
|
|
(MyComponent as any).toHtml = (props: MyComponentProps, childrenHtml: string) => {
|
|
return { html: `<div style="...">${childrenHtml}</div>` };
|
|
};
|
|
```
|
|
|
|
### Component Resolver
|
|
|
|
All components must be registered in `src/components/resolver.ts`. This map is passed to `<Editor resolver={componentResolver}>` so Craft.js can serialize/deserialize the node tree.
|
|
|
|
```typescript
|
|
export const componentResolver = {
|
|
Container, Section, ColumnLayout, BackgroundSection,
|
|
Heading, TextBlock, ButtonLink, Navbar, Footer, Divider, Spacer,
|
|
ImageBlock, VideoBlock,
|
|
HeroSimple, FeaturesGrid, CTASection,
|
|
FormContainer, InputField, TextareaField, FormButton,
|
|
};
|
|
```
|
|
|
|
## WHP Integration
|
|
|
|
### PHP Wrapper
|
|
|
|
The WHP control panel serves the editor through `index.php`, which:
|
|
1. Verifies user authentication
|
|
2. Validates the `site_id` parameter
|
|
3. Generates a CSRF token
|
|
4. Injects a `WHP_CONFIG` object into the HTML as a `<script>` tag before the app bundle
|
|
|
|
```javascript
|
|
window.WHP_CONFIG = {
|
|
user: "username",
|
|
apiUrl: "/panel/api/site-builder",
|
|
csrfToken: "abc123...",
|
|
siteId: 42,
|
|
siteDomain: "example.com",
|
|
siteName: "My Site",
|
|
backUrl: "/panel/sites",
|
|
isRoot: false
|
|
};
|
|
```
|
|
|
|
### API Endpoints
|
|
|
|
The editor communicates with WHP through these endpoints (all require CSRF token):
|
|
|
|
| Method | Endpoint | Description |
|
|
|--------|----------|-------------|
|
|
| POST | `/panel/api/site-builder?action=save` | Save project (JSON body with site_id, html, css, craft state) |
|
|
| GET | `/panel/api/site-builder?action=load&site_id=N` | Load project for a site |
|
|
| POST | `/panel/api/site-builder?action=upload` | Upload asset (multipart) |
|
|
| GET | `/panel/api/site-builder?action=assets&site_id=N` | List assets for a site |
|
|
| DELETE | `/panel/api/site-builder?action=delete_asset` | Delete an asset |
|
|
| POST | `/panel/api/site-builder?action=deploy&site_id=N` | Deploy/publish site to document root |
|
|
|
|
### Auto-Save
|
|
|
|
The editor auto-saves every 30 seconds when running inside WHP. The save status is displayed in the top bar.
|
|
|
|
### EditorConfigContext
|
|
|
|
`useEditorConfig()` provides access to `WHP_CONFIG` throughout the React tree:
|
|
- `whpConfig` - The full config object (or null in standalone mode)
|
|
- `isWHP` - Boolean shorthand for whether we're running inside WHP
|
|
|
|
## All Components (22)
|
|
|
|
| # | Component | Type | File | Features |
|
|
|---|-----------|------|------|----------|
|
|
| 1 | Container | Layout | `layout/Container.tsx` | Generic wrapper, tag selector (div/section/article/header/footer/main), bg color, padding, radius |
|
|
| 2 | Section | Layout | `layout/Section.tsx` | Full-width with centered inner container, bg color/gradient, vertical padding, inner max-width |
|
|
| 3 | ColumnLayout | Layout | `layout/ColumnLayout.tsx` | 1-6 columns, split ratios (50-50, 30-70, 70-30, 33-33-33, 25-25-25-25, etc.), gap control |
|
|
| 4 | BackgroundSection | Layout | `layout/BackgroundSection.tsx` | Section with background image, gradient overlay, parallax-ready |
|
|
| 5 | HeaderZone | Layout | `layout/HeaderZone.tsx` | Page-level header wrapper zone, used by PageContext |
|
|
| 6 | FooterZone | Layout | `layout/FooterZone.tsx` | Page-level footer wrapper zone, used by PageContext |
|
|
| 7 | Heading | Basic | `basic/Heading.tsx` | Inline-editable, h1-h6 level, color, font family/size/weight, text align |
|
|
| 8 | TextBlock | Basic | `basic/TextBlock.tsx` | Inline-editable paragraph, color, font family/size/weight, text align, line height |
|
|
| 9 | ButtonLink | Basic | `basic/ButtonLink.tsx` | Link text/URL/target, 8 color presets (auto text contrast), radius, padding, font size |
|
|
| 10 | Navbar | Basic | `basic/Navbar.tsx` | Text or image logo, page links, external links, CTA buttons, light/dark nav style |
|
|
| 11 | Footer | Basic | `basic/Footer.tsx` | Footer with links, copyright, social links |
|
|
| 12 | Divider | Basic | `basic/Divider.tsx` | Horizontal rule with color and thickness controls |
|
|
| 13 | Spacer | Basic | `basic/Spacer.tsx` | Vertical spacing element with height control |
|
|
| 14 | ImageBlock | Media | `media/ImageBlock.tsx` | SVG placeholder, URL input, upload, browse assets, alt text, width/height, object-fit, radius |
|
|
| 15 | VideoBlock | Media | `media/VideoBlock.tsx` | YouTube, Vimeo, direct files (.mp4/.webm/.ogg), background mode, autoplay, loop |
|
|
| 16 | HeroSimple | Section | `sections/HeroSimple.tsx` | Pre-built hero with heading, subtext, CTA button, gradient/color background |
|
|
| 17 | FeaturesGrid | Section | `sections/FeaturesGrid.tsx` | 3-column feature cards with icons, titles, descriptions |
|
|
| 18 | CTASection | Section | `sections/CTASection.tsx` | Call-to-action banner with heading, text, button |
|
|
| 19 | FormContainer | Form | `forms/FormContainer.tsx` | Form wrapper with action URL and method |
|
|
| 20 | InputField | Form | `forms/InputField.tsx` | Text input with label, placeholder, type (text/email/tel/password/number) |
|
|
| 21 | TextareaField | Form | `forms/TextareaField.tsx` | Textarea with label and placeholder |
|
|
| 22 | FormButton | Form | `forms/FormButton.tsx` | Submit button with color and style controls |
|
|
|
|
## Site Design Tokens
|
|
|
|
The `SiteDesignContext` provides 17 design properties organized into Basic and Advanced tabs:
|
|
|
|
### Basic Tab (6 properties)
|
|
| Property | Default | Description |
|
|
|----------|---------|-------------|
|
|
| primaryColor | `#3b82f6` | Primary brand color |
|
|
| secondaryColor | `#8b5cf6` | Secondary brand color |
|
|
| accentColor | `#10b981` | Accent/highlight color |
|
|
| headingFont | `Inter, sans-serif` | Font for headings |
|
|
| bodyFont | `Inter, sans-serif` | Font for body text |
|
|
| linkColor | `#3b82f6` | Default link color |
|
|
|
|
### Advanced Tab (11 properties)
|
|
| Property | Default | Description |
|
|
|----------|---------|-------------|
|
|
| successColor | `#10b981` | Success state color |
|
|
| warningColor | `#f59e0b` | Warning state color |
|
|
| errorColor | `#ef4444` | Error state color |
|
|
| backgroundColor | `#ffffff` | Page background |
|
|
| textColor | `#1f2937` | Default text color |
|
|
| mutedTextColor | `#6b7280` | Muted/secondary text |
|
|
| borderColor | `#e5e7eb` | Default border color |
|
|
| borderRadius | `8px` | Global border radius |
|
|
| buttonFont | `Inter, sans-serif` | Button font family |
|
|
| buttonRadius | `8px` | Button border radius |
|
|
| navStyle | `light` | Navbar style (light/dark) |
|
|
|
|
Design tokens are imported from templates and can be edited via the Site Design panel. Components can read these tokens via `useSiteDesign()`.
|
|
|
|
## Templates
|
|
|
|
16 pre-built templates organized into 4 categories:
|
|
|
|
| Category | Templates |
|
|
|----------|-----------|
|
|
| Business | Restaurant, Small Business, SaaS Landing, Agency, Medical |
|
|
| Creative | Portfolio, Photography, Content Creator, Event/Conference |
|
|
| Personal | Resume/CV, Blog, Wedding, Coming Soon |
|
|
| Community | Church, Non-Profit, Fitness/Gym |
|
|
|
|
Each template includes:
|
|
- Site design tokens (color palette, fonts)
|
|
- Header content (Navbar)
|
|
- Footer content
|
|
- One or more pages with pre-built content
|
|
- SVG thumbnail preview
|
|
|
|
Templates are loaded via the Template Modal (opened from TopBar). Loading a template replaces all pages, header, footer, and optionally imports the design tokens.
|
|
|
|
## Multi-Page System
|
|
|
|
Pages are managed through `PageContext`:
|
|
- Each page has: `id`, `name`, `slug`, `craftState`, `headCode`
|
|
- Header and Footer are stored as separate "page" entries with fixed IDs (`__header__`, `__footer__`)
|
|
- Page switching serializes the current canvas, stores it, then deserializes the target page
|
|
- Header/Footer editing puts the canvas in a distinct mode
|
|
- The PagesPanel provides add, rename, delete, reorder, and header/footer edit buttons
|
|
|
|
## Keyboard Shortcuts
|
|
|
|
| Shortcut | Action |
|
|
|----------|--------|
|
|
| Ctrl/Cmd + Z | Undo |
|
|
| Ctrl/Cmd + Shift + Z | Redo |
|
|
| Ctrl/Cmd + Y | Redo (alternative) |
|
|
| Delete / Backspace | Delete selected element |
|
|
|
|
Shortcuts are disabled when focus is on input, textarea, select, or contentEditable elements.
|
|
|
|
## Context Menu (Right-Click)
|
|
|
|
The context menu appears on right-click within the canvas and provides:
|
|
- Duplicate element
|
|
- Copy / Paste
|
|
- Move Up / Move Down
|
|
- Select Parent
|
|
- Delete (with danger styling)
|
|
|
|
Items are disabled contextually (e.g., cannot delete ROOT, cannot move if already first/last).
|
|
|
|
## Layers Panel
|
|
|
|
The Layers panel shows a hierarchical tree of all components on the current page. Clicking a layer selects the corresponding component. The tree displays component display names and nests children with indentation.
|
|
|
|
## Asset Management
|
|
|
|
The Assets panel (`AssetsPanel.tsx`) provides:
|
|
- Upload button and drag-and-drop zone
|
|
- Thumbnail grid of uploaded assets
|
|
- Copy URL to clipboard
|
|
- Delete asset
|
|
- Integration with WHP API for server-side storage
|
|
|
|
Image and Video components also have inline asset selection (browse button in settings).
|
|
|
|
## Adding New Components
|
|
|
|
1. Create `src/components/<category>/<ComponentName>.tsx` following the pattern above
|
|
2. Add the component to `src/components/resolver.ts`
|
|
3. Add a block entry in `src/panels/left/BlocksPanel.tsx` under the appropriate category
|
|
4. Implement the `toHtml` static for HTML export
|
|
5. Build and test: `npm run dev`, drag the block onto the canvas, verify settings panel, verify HTML export
|
|
|
|
### Checklist for a new component:
|
|
- [ ] Props interface with `style?: CSSProperties`
|
|
- [ ] `useNode()` with `connect(drag(ref))` on the root element
|
|
- [ ] Settings panel using `useNode()` with `setProp()`
|
|
- [ ] `.craft` config with `displayName`, default `props`, `rules`, `related.settings`
|
|
- [ ] `.toHtml()` static method using `cssPropsToString()`
|
|
- [ ] Registered in `resolver.ts`
|
|
- [ ] Block added to `BlocksPanel.tsx`
|
|
|
|
## CSS / Theme
|
|
|
|
The editor uses a dark theme defined via CSS custom properties in `src/styles/editor.css`:
|
|
- **Base:** `#16161a`
|
|
- **Surface:** `#1c1c24`
|
|
- **Accent:** `#3b82f6` (blue)
|
|
- **Text:** `#e4e4e7`
|
|
- **Border:** `#2d2d3a`
|
|
- **Font:** Inter
|
|
|
|
All editor chrome (panels, topbar, settings) is styled via `editor.css`. User content on the canvas uses inline styles exclusively.
|
|
|
|
## Presets
|
|
|
|
Style presets are defined in `src/constants/presets.ts`:
|
|
- `TEXT_COLORS` - 8 text color swatches
|
|
- `BG_COLORS` - 8 background color swatches
|
|
- `FONT_FAMILIES` - 8 Google Fonts
|
|
- `TEXT_SIZES` - XS through 2XL
|
|
- `FONT_WEIGHTS` - Light through Bold
|
|
- `SPACING_PRESETS` - None through XL
|
|
- `RADIUS_PRESETS` - None through Full (9999px)
|
|
- `GRADIENTS` - 12 gradient presets
|
|
- `DEVICE_WIDTHS` - Desktop (100%), Tablet (768px), Mobile (375px)
|
|
|
|
## HTML Export
|
|
|
|
Every component has a static `toHtml(props, childrenHtml)` method. The `html-export.ts` utility recursively walks the Craft.js node tree and calls each component's `toHtml` to produce a complete HTML document. Export includes:
|
|
- Full `<!DOCTYPE html>` document structure
|
|
- Google Fonts preload links (optional)
|
|
- Inline styles throughout
|
|
- Header and footer wrapping each page
|
|
|
|
## Testing Approach
|
|
|
|
- **Manual testing:** Run `npm run dev`, drag components, edit props, verify settings panels
|
|
- **Type checking:** `tsc --noEmit` (part of build step)
|
|
- **HTML export:** Verify `toHtml()` output matches expected HTML structure
|
|
- **Device preview:** Switch between desktop/tablet/mobile and verify responsive behavior
|
|
- **WHP integration:** Deploy to staging, verify save/load, verify PHP wrapper injection
|
|
|
|
## Development Notes
|
|
|
|
- Path alias `@/` maps to `./src/` (configured in both tsconfig.json and vite.config.ts)
|
|
- `GuidedStyles` shows selected component type and delegates to per-component settings panels
|
|
- Text components (Heading, TextBlock) use `contentEditable` for inline editing when selected
|
|
- Button/link navigation is prevented in the editor via `e.preventDefault()`
|
|
- Image upload integrates with WHP API; in standalone mode falls back to local `blob:` URLs
|
|
- Auto-save runs every 30 seconds when connected to WHP API
|
|
- The SettingsTabs UI component provides a reusable General/Style/Advanced tab layout for component settings
|