# 🎨 Visual Builder Implementation Plan
## Craft.js + AstroWind + Direct DB Shim
**Objective:** Build a "Squarespace-style" visual editor that saves directly to PostgreSQL via the Shim, without CMS overhead.
---
## 🏗️ ARCHITECTURE
```
┌─────────────────────────────────────────────────────────┐
│ PUBLIC SITE (SSR - Lightning Fast) │
│ https://[site-domain]/[slug] │
│ - Reads sites.config JSONB │
│ - Renders AstroWind components │
│ - Zero editor overhead │
└─────────────────────────────────────────────────────────┘
▲
│ Reads config
│
┌─────────────────────────────────────────────────────────┐
│ POSTGRESQL (sites.config JSONB) │
│ { │
│ "template": "astrowind", │
│ "blocks": [ │
│ { "type": "Hero", "props": {...} }, │
│ { "type": "Features", "props": {...} } │
│ ] │
│ } │
└─────────────────────────────────────────────────────────┘
▲
│ Writes config via Shim
│
┌─────────────────────────────────────────────────────────┐
│ VISUAL EDITOR (React + Craft.js) │
│ https://[admin-domain]/admin/editor/[id] │
│ - Drag-and-drop blocks │
│ - Live preview │
│ - Saves to PostgreSQL via /api/shim/sites/save-config │
└─────────────────────────────────────────────────────────┘
```
---
## 📋 IMPLEMENTATION TASKS
### Phase 1: Editor Infrastructure (60 min)
**Task 1.1: Create Craft.js User Components**
- [ ] `src/components/editor/blocks/HeroBlock.tsx` - Wraps AstroWind Hero
- [ ] `src/components/editor/blocks/FeaturesBlock.tsx` - Wraps Features
- [ ] `src/components/editor/blocks/ContentBlock.tsx` - Wraps Content
- [ ] `src/components/editor/blocks/CTABlock.tsx` - Wraps Call-to-Action
- [ ] `src/components/editor/blocks/index.ts` - Exports all blocks
**Task 1.2: Create Editor Canvas**
- [ ] `src/components/editor/EditorCanvas.tsx` - Main Craft.js editor
- [ ] `src/components/editor/ToolboxPanel.tsx` - Drag-and-drop panel
- [ ] `src/components/editor/SettingsPanel.tsx` - Block properties editor
- [ ] `src/components/editor/TopBar.tsx` - Save/Preview buttons
**Task 1.3: Create Editor Utilities**
- [ ] `src/lib/editor/serializer.ts` - Convert Craft.js → JSONB
- [ ] `src/lib/editor/deserializer.ts` - Convert JSONB → Craft.js
- [ ] `src/lib/editor/templates.ts` - Pre-built template configs
### Phase 2: Template Factory (45 min)
**Task 2.1: AstroWind Component Adapters**
- [ ] `src/components/templates/astrowind/Hero.astro` - Reads from JSONB
- [ ] `src/components/templates/astrowind/Features.astro` - Reads from JSONB
- [ ] `src/components/templates/astrowind/Content.astro` - Reads from JSONB
- [ ] `src/components/templates/astrowind/CTA.astro` - Reads from JSONB
**Task 2.2: Template Registry**
- [ ] `src/lib/templates/registry.ts` - Maps template names to components
- [ ] `src/lib/templates/schemas.ts` - Zod schemas for block configs
- [ ] `src/lib/templates/defaults.ts` - Default block configurations
### Phase 3: API Routes (30 min)
**Task 3.1: Editor API Routes**
- [ ] `POST /api/shim/sites/save-config` - Save JSONB to PostgreSQL
- [ ] `GET /api/shim/sites/[id]/config` - Load JSONB for editor
- [ ] `POST /api/shim/sites/[id]/preview` - Generate preview URL
**Task 3.2: Template API Routes**
- [ ] `GET /api/templates/list` - Available templates
- [ ] `POST /api/templates/apply` - Apply template to site
### Phase 4: Editor Pages (30 min)
**Task 4.1: Editor Route**
- [ ] `src/pages/admin/editor/[id].astro` - Main editor page
- [ ] Token validation wrapper
- [ ] Load site config from Shim
**Task 4.2: Template Selector**
- [ ] `src/pages/admin/templates.astro` - Template gallery
- [ ] Preview thumbnails
- [ ] One-click apply
### Phase 5: Public Rendering (30 min)
**Task 5.1: Dynamic Template Renderer**
- [ ] `src/pages/[...slug].astro` - Reads sites.config and renders blocks
- [ ] Block component resolver
- [ ] SEO metadata injection
**Task 5.2: Site Utilities**
- [ ] `src/lib/templates/renderer.ts` - Render blocks from JSONB
- [ ] `src/lib/templates/seo.ts` - Extract SEO from config
---
## 🔧 CORE COMPONENTS
### 1. Editor Canvas (`EditorCanvas.tsx`)
```typescript
import { Editor, Frame, Element } from '@craftjs/core';
import { HeroBlock, FeaturesBlock, ContentBlock } from './blocks';
export default function EditorCanvas({ initialState, siteId }) {
const handleSave = async (json) => {
await fetch(`/api/shim/sites/save-config`, {
method: 'POST',
body: JSON.stringify({ siteId, config: json })
});
};
return (
{subtitle}
{image &&