Site builder: security & data-loss hardening + unified asset picker + audit backlog #3
+58
-46
@@ -41,14 +41,12 @@ craft/
|
|||||||
│ │ └── Canvas.tsx # Craft.js <Frame> with device-width switching
|
│ │ └── Canvas.tsx # Craft.js <Frame> with device-width switching
|
||||||
│ │
|
│ │
|
||||||
│ ├── components/
|
│ ├── components/
|
||||||
│ │ ├── resolver.ts # Component map for Craft.js serialization (20 components)
|
│ │ ├── resolver.ts # Component map for Craft.js serialization (39 components)
|
||||||
│ │ ├── layout/
|
│ │ ├── layout/
|
||||||
│ │ │ ├── Container.tsx # Generic container (div/section/article/header/footer/main)
|
│ │ │ ├── Container.tsx # Generic container (div/section/article/header/footer/main)
|
||||||
│ │ │ ├── Section.tsx # Full-width section with centered inner container
|
│ │ │ ├── Section.tsx # Full-width section with centered inner container
|
||||||
│ │ │ ├── ColumnLayout.tsx # Flex columns (1-6, with split ratios)
|
│ │ │ ├── ColumnLayout.tsx # Flex columns (1-6, with split ratios)
|
||||||
│ │ │ ├── BackgroundSection.tsx # Section with background image/gradient overlay
|
│ │ │ └── BackgroundSection.tsx # Section with background image/gradient overlay
|
||||||
│ │ │ ├── HeaderZone.tsx # Page-level header zone wrapper
|
|
||||||
│ │ │ └── FooterZone.tsx # Page-level footer zone wrapper
|
|
||||||
│ │ ├── basic/
|
│ │ ├── basic/
|
||||||
│ │ │ ├── Heading.tsx # Inline-editable heading (h1-h6)
|
│ │ │ ├── Heading.tsx # Inline-editable heading (h1-h6)
|
||||||
│ │ │ ├── TextBlock.tsx # Inline-editable paragraph
|
│ │ │ ├── TextBlock.tsx # Inline-editable paragraph
|
||||||
@@ -73,7 +71,8 @@ craft/
|
|||||||
│ ├── panels/
|
│ ├── panels/
|
||||||
│ │ ├── topbar/
|
│ │ ├── topbar/
|
||||||
│ │ │ ├── TopBar.tsx # Back button, domain badge, device switcher, undo/redo, save, templates
|
│ │ │ ├── TopBar.tsx # Back button, domain badge, device switcher, undo/redo, save, templates
|
||||||
│ │ │ └── TemplateModal.tsx # Template browser with categories and one-click loading
|
│ │ │ ├── TemplateModal.tsx # Template browser with categories and one-click loading
|
||||||
|
│ │ │ └── HeadCodeModal.tsx # Edits SiteDesign.headCode (site-wide, not per-page)
|
||||||
│ │ ├── left/
|
│ │ ├── left/
|
||||||
│ │ │ ├── LeftPanel.tsx # Tabs: Blocks | Pages | Layers | Assets
|
│ │ │ ├── LeftPanel.tsx # Tabs: Blocks | Pages | Layers | Assets
|
||||||
│ │ │ ├── BlocksPanel.tsx # Draggable block toolbox with categories
|
│ │ │ ├── BlocksPanel.tsx # Draggable block toolbox with categories
|
||||||
@@ -81,9 +80,10 @@ craft/
|
|||||||
│ │ │ ├── LayersPanel.tsx # Component hierarchy tree view
|
│ │ │ ├── LayersPanel.tsx # Component hierarchy tree view
|
||||||
│ │ │ └── AssetsPanel.tsx # Asset browser with upload, drag-drop, thumbnails
|
│ │ │ └── AssetsPanel.tsx # Asset browser with upload, drag-drop, thumbnails
|
||||||
│ │ ├── right/
|
│ │ ├── right/
|
||||||
│ │ │ ├── RightPanel.tsx # Tabs: Styles | Settings | Head
|
│ │ │ ├── RightPanel.tsx # Single "Styles" tab -- renders GuidedStyles only
|
||||||
│ │ │ ├── GuidedStyles.tsx # Context-aware style panel (shows selected type)
|
│ │ │ ├── GuidedStyles.tsx # Context-aware dispatcher: picks a StylePanel by selected type
|
||||||
│ │ │ └── SiteDesignPanel.tsx # Site-wide design tokens editor (Basic/Advanced tabs)
|
│ │ │ ├── SiteDesignPanel.tsx # Site-wide design tokens editor (Basic/Advanced tabs)
|
||||||
|
│ │ │ └── styles/ # Per-type StylePanels (Text, Button, Image, Container, Nav, Form, etc.)
|
||||||
│ │ └── context-menu/
|
│ │ └── context-menu/
|
||||||
│ │ └── ContextMenu.tsx # Right-click context menu (duplicate, copy, paste, delete, etc.)
|
│ │ └── ContextMenu.tsx # Right-click context menu (duplicate, copy, paste, delete, etc.)
|
||||||
│ │
|
│ │
|
||||||
@@ -98,7 +98,8 @@ craft/
|
|||||||
│ │ └── definitions.ts # 16 template definitions across 4 categories
|
│ │ └── definitions.ts # 16 template definitions across 4 categories
|
||||||
│ │
|
│ │
|
||||||
│ ├── ui/
|
│ ├── ui/
|
||||||
│ │ └── SettingsTabs.tsx # Reusable General/Style/Advanced tabs for component settings
|
│ │ ├── AssetPicker.tsx # Reusable image/video source picker (upload/browse-uploaded/URL), full+compact variants
|
||||||
|
│ │ └── Modal.tsx # Reusable modal dialog shell
|
||||||
│ │
|
│ │
|
||||||
│ ├── constants/
|
│ ├── constants/
|
||||||
│ │ └── presets.ts # Color, font, spacing, radius, gradient, device width presets
|
│ │ └── presets.ts # Color, font, spacing, radius, gradient, device width presets
|
||||||
@@ -165,7 +166,7 @@ The PHP wrapper (`/docker/whp/web/site-builder/index.php`) injects `WHP_CONFIG`
|
|||||||
|
|
||||||
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.
|
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.
|
6. **Component-based architecture** - Each visual element is a React component that doubles as a Craft.js `UserComponent`. Rendering and HTML export are co-located in one file; style editing is handled by a shared per-type `StylePanel` in `src/panels/right/styles/` rather than a per-component settings panel.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
@@ -188,28 +189,21 @@ export const MyComponent: UserComponent<MyComponentProps> = ({ text, style }) =>
|
|||||||
return <div ref={(r) => { if (r) connect(drag(r)); }} style={style}>{text}</div>;
|
return <div ref={(r) => { if (r) connect(drag(r)); }} style={style}>{text}</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 3. Settings panel (rendered in right panel when selected)
|
// 3. Craft config (displayName, default props, rules)
|
||||||
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 = {
|
MyComponent.craft = {
|
||||||
displayName: 'My Component',
|
displayName: 'My Component',
|
||||||
props: { text: 'Default text', style: {} },
|
props: { text: 'Default text', style: {} },
|
||||||
rules: { canDrag: () => true, canMoveIn: () => false, canMoveOut: () => true },
|
rules: { canDrag: () => true, canMoveIn: () => false, canMoveOut: () => true },
|
||||||
related: { settings: MyComponentSettings },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 5. HTML export (static method for serializing to HTML string)
|
// 4. HTML export (static method for serializing to HTML string)
|
||||||
(MyComponent as any).toHtml = (props: MyComponentProps, childrenHtml: string) => {
|
(MyComponent as any).toHtml = (props: MyComponentProps, childrenHtml: string) => {
|
||||||
return { html: `<div style="...">${childrenHtml}</div>` };
|
return { html: `<div style="...">${childrenHtml}</div>` };
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Style editing for the new type is added separately as a `StylePanel` under `src/panels/right/styles/` (or reuses an existing generic one), and wired into `GuidedStyles.tsx`'s type dispatch -- components no longer carry their own settings UI.
|
||||||
|
|
||||||
### Component Resolver
|
### 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.
|
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.
|
||||||
@@ -270,7 +264,7 @@ The editor auto-saves every 30 seconds when running inside WHP. The save status
|
|||||||
- `whpConfig` - The full config object (or null in standalone mode)
|
- `whpConfig` - The full config object (or null in standalone mode)
|
||||||
- `isWHP` - Boolean shorthand for whether we're running inside WHP
|
- `isWHP` - Boolean shorthand for whether we're running inside WHP
|
||||||
|
|
||||||
## All Components (22)
|
## All Components (39)
|
||||||
|
|
||||||
| # | Component | Type | File | Features |
|
| # | Component | Type | File | Features |
|
||||||
|---|-----------|------|------|----------|
|
|---|-----------|------|------|----------|
|
||||||
@@ -278,24 +272,41 @@ The editor auto-saves every 30 seconds when running inside WHP. The save status
|
|||||||
| 2 | Section | Layout | `layout/Section.tsx` | Full-width with centered inner container, bg color/gradient, vertical padding, inner max-width |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 5 | Heading | Basic | `basic/Heading.tsx` | Inline-editable, h1-h6 level, color, font family/size/weight, text align |
|
||||||
| 6 | FooterZone | Layout | `layout/FooterZone.tsx` | Page-level footer wrapper zone, used by PageContext |
|
| 6 | TextBlock | Basic | `basic/TextBlock.tsx` | Inline-editable paragraph, color, font family/size/weight, text align, line height |
|
||||||
| 7 | Heading | Basic | `basic/Heading.tsx` | Inline-editable, h1-h6 level, color, font family/size/weight, text align |
|
| 7 | ButtonLink | Basic | `basic/ButtonLink.tsx` | Link text/URL/target, 8 color presets (auto text contrast), radius, padding, font size |
|
||||||
| 8 | TextBlock | Basic | `basic/TextBlock.tsx` | Inline-editable paragraph, color, font family/size/weight, text align, line height |
|
| 8 | Logo | Basic | `basic/Logo.tsx` | Text or image logo, link href, font family/size/weight, image width |
|
||||||
| 9 | ButtonLink | Basic | `basic/ButtonLink.tsx` | Link text/URL/target, 8 color presets (auto text contrast), radius, padding, font size |
|
| 9 | Menu | Basic | `basic/Menu.tsx` | Link list with optional CTA styling, horizontal/vertical orientation, alignment, hover colors |
|
||||||
| 10 | Navbar | Basic | `basic/Navbar.tsx` | Text or image logo, page links, external links, CTA buttons, light/dark nav style |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 14 | Icon | Basic | `basic/Icon.tsx` | Font Awesome icon, size/color, background shape, optional link |
|
||||||
| 15 | VideoBlock | Media | `media/VideoBlock.tsx` | YouTube, Vimeo, direct files (.mp4/.webm/.ogg), background mode, autoplay, loop |
|
| 15 | ImageBlock | Media | `media/ImageBlock.tsx` | SVG placeholder, URL input, upload, browse assets, alt text, width/height, object-fit, radius |
|
||||||
| 16 | HeroSimple | Section | `sections/HeroSimple.tsx` | Pre-built hero with heading, subtext, CTA button, gradient/color background |
|
| 16 | VideoBlock | Media | `media/VideoBlock.tsx` | YouTube, Vimeo, direct files (.mp4/.webm/.ogg), background mode, autoplay, loop |
|
||||||
| 17 | FeaturesGrid | Section | `sections/FeaturesGrid.tsx` | 3-column feature cards with icons, titles, descriptions |
|
| 17 | MapEmbed | Media | `media/MapEmbed.tsx` | Embedded map by address, zoom level, height |
|
||||||
| 18 | CTASection | Section | `sections/CTASection.tsx` | Call-to-action banner with heading, text, button |
|
| 18 | HeroSimple | Section | `sections/HeroSimple.tsx` | Pre-built hero with heading, subtext, CTA button, gradient/color background |
|
||||||
| 19 | FormContainer | Form | `forms/FormContainer.tsx` | Form wrapper with action URL and method |
|
| 19 | FeaturesGrid | Section | `sections/FeaturesGrid.tsx` | 3-column feature cards with icons, titles, descriptions |
|
||||||
| 20 | InputField | Form | `forms/InputField.tsx` | Text input with label, placeholder, type (text/email/tel/password/number) |
|
| 20 | CTASection | Section | `sections/CTASection.tsx` | Call-to-action banner with heading, text, button |
|
||||||
| 21 | TextareaField | Form | `forms/TextareaField.tsx` | Textarea with label and placeholder |
|
| 21 | Countdown | Section | `sections/Countdown.tsx` | Countdown timer to a target date, heading, digit/label colors |
|
||||||
| 22 | FormButton | Form | `forms/FormButton.tsx` | Submit button with color and style controls |
|
| 22 | Testimonials | Section | `sections/Testimonials.tsx` | Grid or single-layout testimonial cards, star color, card background |
|
||||||
|
| 23 | FormContainer | Form | `forms/FormContainer.tsx` | Form wrapper with action URL and method |
|
||||||
|
| 24 | InputField | Form | `forms/InputField.tsx` | Text input with label, placeholder, type (text/email/tel/password/number) |
|
||||||
|
| 25 | TextareaField | Form | `forms/TextareaField.tsx` | Textarea with label and placeholder |
|
||||||
|
| 26 | FormButton | Form | `forms/FormButton.tsx` | Submit button with color and style controls |
|
||||||
|
| 27 | ContactForm | Form | `forms/ContactForm.tsx` | Configurable field list, recipient email, success message or thank-you URL redirect |
|
||||||
|
| 28 | StarRating | Basic | `basic/StarRating.tsx` | Star rating display, rating/max stars, filled/empty color |
|
||||||
|
| 29 | SocialLinks | Basic | `basic/SocialLinks.tsx` | Social icon links, size/color/shape, gap, alignment |
|
||||||
|
| 30 | CallToAction | Section | `sections/CallToAction.tsx` | Heading/description with 1-2 buttons, color/gradient/image background with overlay |
|
||||||
|
| 31 | Accordion | Section | `sections/Accordion.tsx` | Expand/collapse item list, header/content colors |
|
||||||
|
| 32 | Tabs | Section | `sections/Tabs.tsx` | Tabbed content panels, active/inactive tab colors |
|
||||||
|
| 33 | PricingTable | Section | `sections/PricingTable.tsx` | Pricing plan cards, featured plan highlight, bullet list |
|
||||||
|
| 34 | Gallery | Section | `sections/Gallery.tsx` | Image grid gallery, configurable columns/gap, optional lightbox |
|
||||||
|
| 35 | ContentSlider | Section | `sections/ContentSlider.tsx` | Auto-playing image/content slider, dots/arrows, configurable interval |
|
||||||
|
| 36 | NumberCounter | Section | `sections/NumberCounter.tsx` | Animated stat counters, columns, number/label colors |
|
||||||
|
| 37 | SubscribeForm | Form | `forms/SubscribeForm.tsx` | Inline/stacked email signup form, heading, button color |
|
||||||
|
| 38 | SearchBar | Basic | `basic/SearchBar.tsx` | Search input with optional button, placeholder text |
|
||||||
|
| 39 | HtmlBlock | Basic | `basic/HtmlBlock.tsx` | Raw/custom HTML embed block, sanitized on export |
|
||||||
|
|
||||||
## Site Design Tokens
|
## Site Design Tokens
|
||||||
|
|
||||||
@@ -351,7 +362,7 @@ Templates are loaded via the Template Modal (opened from TopBar). Loading a temp
|
|||||||
## Multi-Page System
|
## Multi-Page System
|
||||||
|
|
||||||
Pages are managed through `PageContext`:
|
Pages are managed through `PageContext`:
|
||||||
- Each page has: `id`, `name`, `slug`, `craftState`, `headCode`
|
- Each page has: `id`, `name`, `slug`, `craftState` (`headCode` is site-wide only, on `SiteDesignContext`/`SiteDesign`, edited via the TopBar's Head Code modal -- not a per-page field)
|
||||||
- Header and Footer are stored as separate "page" entries with fixed IDs (`__header__`, `__footer__`)
|
- 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
|
- Page switching serializes the current canvas, stores it, then deserializes the target page
|
||||||
- Header/Footer editing puts the canvas in a distinct mode
|
- Header/Footer editing puts the canvas in a distinct mode
|
||||||
@@ -392,21 +403,22 @@ The Assets panel (`AssetsPanel.tsx`) provides:
|
|||||||
- Delete asset
|
- Delete asset
|
||||||
- Integration with WHP API for server-side storage
|
- Integration with WHP API for server-side storage
|
||||||
|
|
||||||
Image and Video components also have inline asset selection (browse button in settings).
|
Image and video fields elsewhere in the editor (ImageStylePanel, MediaStylePanel, HeroStylePanel, NavStylePanel, BackgroundSectionStylePanel, and array-editor cards like FeaturesEditor) use the shared `AssetPicker` (`src/ui/AssetPicker.tsx`) for upload / browse-uploaded / paste-URL, in a `full` or `compact` variant depending on space.
|
||||||
|
|
||||||
## Adding New Components
|
## Adding New Components
|
||||||
|
|
||||||
1. Create `src/components/<category>/<ComponentName>.tsx` following the pattern above
|
1. Create `src/components/<category>/<ComponentName>.tsx` following the pattern above
|
||||||
2. Add the component to `src/components/resolver.ts`
|
2. Add the component to `src/components/resolver.ts`
|
||||||
3. Add a block entry in `src/panels/left/BlocksPanel.tsx` under the appropriate category
|
3. Add a block entry in `src/panels/left/BlocksPanel.tsx` under the appropriate category
|
||||||
4. Implement the `toHtml` static for HTML export
|
4. Add or extend a `StylePanel` in `src/panels/right/styles/` and wire it into `GuidedStyles.tsx`'s type dispatch so the new component is editable when selected
|
||||||
5. Build and test: `npm run dev`, drag the block onto the canvas, verify settings panel, verify HTML export
|
5. Implement the `toHtml` static for HTML export
|
||||||
|
6. Build and test: `npm run dev`, drag the block onto the canvas, verify the style panel, verify HTML export
|
||||||
|
|
||||||
### Checklist for a new component:
|
### Checklist for a new component:
|
||||||
- [ ] Props interface with `style?: CSSProperties`
|
- [ ] Props interface with `style?: CSSProperties`
|
||||||
- [ ] `useNode()` with `connect(drag(ref))` on the root element
|
- [ ] `useNode()` with `connect(drag(ref))` on the root element
|
||||||
- [ ] Settings panel using `useNode()` with `setProp()`
|
- [ ] `StylePanel` entry (new or reused) wired into `GuidedStyles.tsx`
|
||||||
- [ ] `.craft` config with `displayName`, default `props`, `rules`, `related.settings`
|
- [ ] `.craft` config with `displayName`, default `props`, `rules`
|
||||||
- [ ] `.toHtml()` static method using `cssPropsToString()`
|
- [ ] `.toHtml()` static method using `cssPropsToString()`
|
||||||
- [ ] Registered in `resolver.ts`
|
- [ ] Registered in `resolver.ts`
|
||||||
- [ ] Block added to `BlocksPanel.tsx`
|
- [ ] Block added to `BlocksPanel.tsx`
|
||||||
@@ -421,7 +433,7 @@ The editor uses a dark theme defined via CSS custom properties in `src/styles/ed
|
|||||||
- **Border:** `#2d2d3a`
|
- **Border:** `#2d2d3a`
|
||||||
- **Font:** Inter
|
- **Font:** Inter
|
||||||
|
|
||||||
All editor chrome (panels, topbar, settings) is styled via `editor.css`. User content on the canvas uses inline styles exclusively.
|
All editor chrome (panels, topbar, style panels) is styled via `editor.css`. User content on the canvas uses inline styles exclusively.
|
||||||
|
|
||||||
## Presets
|
## Presets
|
||||||
|
|
||||||
@@ -446,7 +458,7 @@ Every component has a static `toHtml(props, childrenHtml)` method. The `html-exp
|
|||||||
|
|
||||||
## Testing Approach
|
## Testing Approach
|
||||||
|
|
||||||
- **Manual testing:** Run `npm run dev`, drag components, edit props, verify settings panels
|
- **Manual testing:** Run `npm run dev`, drag components, edit props, verify style panels
|
||||||
- **Type checking:** `tsc --noEmit` (part of build step)
|
- **Type checking:** `tsc --noEmit` (part of build step)
|
||||||
- **HTML export:** Verify `toHtml()` output matches expected HTML structure
|
- **HTML export:** Verify `toHtml()` output matches expected HTML structure
|
||||||
- **Device preview:** Switch between desktop/tablet/mobile and verify responsive behavior
|
- **Device preview:** Switch between desktop/tablet/mobile and verify responsive behavior
|
||||||
@@ -455,9 +467,9 @@ Every component has a static `toHtml(props, childrenHtml)` method. The `html-exp
|
|||||||
## Development Notes
|
## Development Notes
|
||||||
|
|
||||||
- Path alias `@/` maps to `./src/` (configured in both tsconfig.json and vite.config.ts)
|
- 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
|
- `GuidedStyles` shows the selected component type and dispatches to a shared `StylePanel` in `src/panels/right/styles/` (components no longer carry their own settings UI)
|
||||||
- Text components (Heading, TextBlock) use `contentEditable` for inline editing when selected
|
- Text components (Heading, TextBlock) use `contentEditable` for inline editing when selected
|
||||||
- Button/link navigation is prevented in the editor via `e.preventDefault()`
|
- 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
|
- 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
|
- 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
|
- `AssetPicker` (`src/ui/AssetPicker.tsx`) is the shared upload/browse/URL control reused across StylePanels for every image and video field
|
||||||
|
|||||||
Reference in New Issue
Block a user