Editor Support

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
Jason
2026-05-05 15:42:30 -05:00
parent 9f7c078ccc
commit 29154bd651
32 changed files with 3698 additions and 17 deletions
+311
View File
@@ -0,0 +1,311 @@
# Canvas API - Hierarchical Image Fallback System
## Overview
The Canvas API provides intelligent image serving with automatic fallback across multiple directory levels. This eliminates image duplication and makes it easy to share common elements (like foreground plants or backgrounds) across multiple products.
## Architecture
### URL Pattern
```
GET /api/canvas/<product_code>/<layer>?color=<color>&material=<material>
```
### Directory Structure
```
app/static/images/
├── doors/
│ ├── storm/
│ │ ├── 404/
│ │ │ ├── base.jpg # Product-specific base
│ │ │ ├── door-white.png # Product-specific door color
│ │ │ └── door-black.png
│ │ ├── 505/
│ │ │ ├── base.jpg
│ │ │ └── door-white.png
│ │ └── layers/
│ │ ├── base.jpg # Fallback for ALL storm doors
│ │ ├── hardware.png # Shared hardware
│ │ └── foreground.png # Shared foreground
│ ├── entry/
│ │ ├── 600/
│ │ │ ├── base.jpg
│ │ │ └── door-bronze.png
│ │ └── layers/
│ │ └── foreground-plants.png # Shared for entry doors
│ └── layers/
│ ├── hardware-generic.png # Fallback for ALL doors
│ └── foreground-default.png
├── windows/
│ ├── storm/
│ │ └── layers/
│ │ └── foreground-minimal.png
│ └── layers/
│ └── base-generic.jpg
└── layers/
├── foreground-default.png # Global fallback
├── base-generic.jpg # Global fallback
└── hardware-generic.png # Global fallback
```
### Fallback Priority
When requesting an image, the system searches in this order (most specific to least specific):
1. **Product-specific with variant**: `/images/<type>/<subtype>/<code>/<layer>-<color>.png`
2. **Product-specific**: `/images/<type>/<subtype>/<code>/<layer>.png`
3. **Subtype fallback with variant**: `/images/<type>/<subtype>/layers/<layer>-<color>.png`
4. **Subtype fallback**: `/images/<type>/<subtype>/layers/<layer>.png`
5. **Type fallback**: `/images/<type>/layers/<layer>.png`
6. **Global fallback**: `/images/layers/<layer>.png`
7. **404**: Image not found
## API Endpoints
### Get Layer Image
```
GET /api/canvas/<product_code>/<layer>?color=<color>&material=<material>
```
**Parameters:**
- `product_code` (path, required): Product code (e.g., "404", "505")
- `layer` (path, required): Layer name - must be one of: `base`, `door`, `hardware`, `overlay`, `foreground`
- `color` (query, optional): Color variant (e.g., "white", "black", "bronze")
- `material` (query, optional): Material variant (for future use)
**Response:**
- Success: Image file (PNG, JPG, JPEG, or WebP)
- Error 400: Invalid layer name
- Error 404: Image not found
**Examples:**
```
GET /api/canvas/404/base
GET /api/canvas/404/door?color=white
GET /api/canvas/505/hardware
GET /api/canvas/600/foreground
```
### Get Canvas Info
```
GET /api/canvas/<product_code>/info
```
Returns metadata about available layers for a product.
**Response:**
```json
{
"product": "404",
"type": "window",
"subtype": "storm-window",
"availableLayers": {
"base": true,
"door": true,
"door_colors": ["white", "black", "bronze", "sandstone"],
"hardware": true,
"foreground": true
},
"colors": ["White", "Black", "Bronze", "Sandstone"],
"materials": ["Aluminum"]
}
```
### Test Endpoint
```
GET /api/canvas/test
```
Verifies the Canvas API is running.
## Product Configuration
### Enable Canvas API
Add `"useCanvasAPI": true` to the product's `imageConfig`:
```json
{
"productCode": "404",
"description": "#404 FALCON STORM WINDOWS",
"colors": ["White", "Black", "Bronze", "Sandstone"],
"materials": ["Aluminum"],
"imageConfig": {
"useCanvasAPI": true
}
}
```
That's it! No need to specify paths or layers - the Canvas API will automatically find them using the fallback system.
### Optional: Legacy Layered Configuration
If you're migrating from the old static layered system, you can keep both configurations during transition:
```json
{
"productCode": "404",
"imageConfig": {
"useCanvasAPI": true,
"layered": true,
"basePath": "images/products/404/",
"layers": {
"base": "base.jpg",
"door": {
"white": "door-white.png",
"black": "door-black.png"
}
}
}
}
```
## File Organization Strategy
### Product-Specific Files
Place in `/images/<type>/<subtype>/<code>/`:
- Unique base images with specific backgrounds
- Color variants that are product-specific
- Custom hardware or features
### Subtype Shared Files
Place in `/images/<type>/<subtype>/layers/`:
- Common hardware for that subtype
- Shared foreground elements (plants, railings)
- Default base images for that subtype
### Type Shared Files
Place in `/images/<type>/layers/`:
- Generic hardware for all products of that type
- Common decorative elements
### Global Shared Files
Place in `/images/layers/`:
- Universal fallback images
- Default placeholder elements
## Migration Guide
### From Static Layered System
**Before:**
```
app/static/images/
└── products/
├── 404/
│ ├── base.jpg
│ ├── door-white.png
│ ├── door-black.png
│ ├── hardware.png
│ └── plants.png
└── 505/
├── base.jpg
├── door-white.png
├── hardware.png # Duplicate!
└── plants.png # Duplicate!
```
**After:**
```
app/static/images/
└── doors/
└── storm/
├── 404/
│ ├── base.jpg
│ ├── door-white.png
│ └── door-black.png
├── 505/
│ ├── base.jpg
│ └── door-white.png
└── layers/
├── hardware.png # Shared by both!
└── plants.png # Shared by both!
```
**Update JSON:**
```json
{
"productCode": "404",
"imageConfig": {
"useCanvasAPI": true // Enable Canvas API
}
}
```
### Migration Steps
1. **Identify shared elements**: Look for duplicate files across products
2. **Reorganize directory**: Move files to appropriate fallback levels
3. **Update product JSON**: Add `"useCanvasAPI": true`
4. **Test**: Verify images load correctly
5. **Cleanup**: Remove old duplicate files
## Development Tips
### Testing Fallback Behavior
1. Start with global fallback layer
2. Test product without specific file - should show global fallback
3. Add subtype-specific layer - should override global
4. Add product-specific layer - should override subtype
### Debugging
Check Flask logs to see which image path was found:
```
[INFO] Found image: /path/to/images/doors/storm/404/base.jpg
```
If image not found, logs show what was searched:
```
[WARNING] No image found for 404/door (color: white)
```
### Browser Testing
Open browser console to see Canvas API requests:
```
GET /api/canvas/404/base → 200 OK
GET /api/canvas/404/door?color=white → 200 OK
GET /api/canvas/404/foreground → 200 OK (from fallback)
```
## Performance Considerations
### Advantages
- **Reduced file duplication**: Share common elements across products
- **Smaller total file size**: No duplicate hardware/foreground images
- **Easier maintenance**: Update one shared file affects all products
- **Smart caching**: Browser caches shared layers for faster loading
### Best Practices
- Optimize images before uploading (TinyPNG, ImageOptim)
- Use appropriate formats:
- JPG for base layers (no transparency needed)
- PNG for layers with transparency
- WebP for modern browsers (optional)
- Keep file sizes reasonable:
- Base: 200-500KB
- Layers: 50-200KB each
- Foreground: 50-150KB
## Troubleshooting
### Image Not Showing
1. Check product's `baseType` and `subType` in products.json
2. Verify file exists in correct directory structure
3. Check Flask logs for the search path
4. Ensure product has `"useCanvasAPI": true`
### Wrong Image Displayed
- Check fallback priority - more specific path should override generic
- Verify file naming matches expected pattern
- Check that color parameter matches filename (lowercase)
### 404 Errors
- Open `/api/canvas/<product>/info` to see what's available
- Check file permissions
- Verify directory structure matches expected pattern
## Future Enhancements
Potential additions to the Canvas API system:
- Dynamic color tinting (apply color to grayscale base)
- Image composition/overlays
- Real-time layer opacity/blend modes
- A/B testing different foreground elements
- Seasonal foreground rotation
- Material-based texture overlays
+285
View File
@@ -0,0 +1,285 @@
# Canvas API Implementation Summary
## What Was Implemented
Your product finder now has a **hierarchical image fallback system** (Canvas API) that automatically serves images with smart fallbacks across multiple directory levels. This eliminates duplicate files and makes it easy to share common elements (like plants, hardware, backgrounds) across products.
## System Architecture
### Three-Layer Approach
1. **Backend (Python/Flask)** - Smart image resolution with fallback logic
2. **Frontend (JavaScript)** - Dynamic image loading using Canvas API endpoints
3. **File Structure** - Hierarchical organization with automatic fallbacks
```
Product Specific → Subtype Shared → Type Shared → Global Shared
```
## What Changed
### New Files Created
1. **`app/blueprints/canvas.py`** - Canvas API Flask blueprint with fallback logic
- `/api/canvas/<product_code>/<layer>?color=<color>` - Get image with fallback
- `/api/canvas/<product_code>/info` - Get available layers info
- `/api/canvas/test` - Test endpoint
2. **`information/CANVAS_API_GUIDE.md`** - Complete technical documentation
3. **`information/CANVAS_API_QUICKSTART.md`** - 5-minute setup guide
4. **`information/CANVAS_DIRECTORY_SETUP.md`** - Directory setup scripts and helpers
5. **`information/FOREGROUND_LAYER_EXAMPLE.md`** - Foreground layer guide (from earlier)
6. **`information/FOREGROUND_QUICKSTART.md`** - Quick foreground guide (from earlier)
### Modified Files
1. **`app/app.py`** - Registered canvas blueprint
2. **`app/static/js/script.js`** - Added Canvas API support
- `buildCanvasAPIUrl()` - Build API URLs
- `updateCanvasAPIPreview()` - Update layers using API
- Updated rendering logic to support Canvas API
3. **`app/static/css/styles.css`** - Added `.layer-foreground` with z-index 5 (from earlier)
4. **`information/LAYERED_IMAGES.md`** - Updated with foreground layer info
5. **`information/products-layered-example.json`** - Added Canvas API example (product #700)
## How It Works
### 1. Directory Structure
```
app/static/images/
├── doors/
│ └── storm/
│ ├── 404/
│ │ └── door-white.png # Product-specific
│ └── layers/
│ └── hardware.png # Shared by all storm doors
└── layers/
└── foreground.png # Global fallback
```
### 2. Product Configuration
```json
{
"productCode": "404",
"imageConfig": {
"useCanvasAPI": true
}
}
```
### 3. Automatic Fallback
Request: `/api/canvas/404/hardware`
Searches in order:
1. `/images/doors/storm/404/hardware.png` ← Product-specific
2. `/images/doors/storm/layers/hardware.png` ← Subtype shared ✓ **Found!**
3. `/images/doors/layers/hardware.png` ← Type shared
4. `/images/layers/hardware.png` ← Global
### 4. Frontend Rendering
```javascript
// JavaScript automatically calls Canvas API
const url = buildCanvasAPIUrl('404', 'door', 'white');
// Result: /api/canvas/404/door?color=white
doorLayer.src = url; // Image loads with automatic fallback!
```
## Layering System (Updated)
All 5 layers are now supported:
1. **Base** (z-index: 1) - Background/house/frame
2. **Door** (z-index: 2) - Door/window panel (color variants)
3. **Hardware** (z-index: 3) - Handles, locks, hinges
4. **Overlay** (z-index: 4) - Glass views, decorative overlays
5. **Foreground** (z-index: 5) - Plants, decorations ⭐ **NEW from earlier request**
## Benefits
### ✅ Eliminates Duplication
Before: Each product has own copy of hardware.png
After: One shared hardware.png for all products of that type
### ✅ Smart Fallbacks
Product can have custom image OR inherit from parent levels automatically
### ✅ Easier Maintenance
Update one shared file → affects all products using it
### ✅ Flexible Organization
- Product-specific overrides: `/images/<type>/<subtype>/<code>/`
- Subtype shared: `/images/<type>/<subtype>/layers/`
- Type shared: `/images/<type>/layers/`
- Global fallback: `/images/layers/`
### ✅ Reduced File Size
Example with 10 products sharing hardware + foreground:
- Before: 10 × (100KB + 150KB) = 2.5MB
- After: 1 × (100KB + 150KB) = 250KB
- **Savings: 2.25MB (90% reduction for shared elements)**
## Usage Examples
### Example 1: All Storm Doors Share Hardware
```
/images/doors/storm/layers/hardware.png ← One file
```
Products 404, 505, 450 all use this automatically!
### Example 2: Product 404 Has Custom Foreground
```
/images/doors/storm/404/foreground.png ← Product 404
/images/doors/storm/layers/foreground.png ← Products 505, 450
```
Product 404 gets custom, others get shared.
### Example 3: Global Plant Layer
```
/images/layers/foreground-plants.png ← Used by ALL products
```
Unless a more specific version exists.
## Migration Path
### Phase 1: Keep Existing System (Low Risk)
- Leave current products unchanged
- New products use Canvas API: `"useCanvasAPI": true`
- Both systems work simultaneously
### Phase 2: Identify Duplicates
- Run duplicate file detection scripts
- Find common hardware, foregrounds, bases
- Plan shared directory structure
### Phase 3: Reorganize Files
- Create type/subtype structure
- Move shared files to appropriate `/layers/` directories
- Update product JSON: Add `"useCanvasAPI": true`
### Phase 4: Cleanup
- Remove old duplicate files
- Verify all products load correctly
- Measure storage savings
## API Endpoints Reference
```
GET /api/canvas/<product_code>/<layer>?color=<color>&material=<material>
→ Returns image file with automatic fallback
GET /api/canvas/<product_code>/info
→ Returns metadata about available layers
GET /api/canvas/test
→ Test endpoint to verify API is working
```
## Configuration Options
### Enable Canvas API
```json
{
"productCode": "404",
"imageConfig": {
"useCanvasAPI": true
}
}
```
### Keep Backward Compatibility (During Migration)
```json
{
"productCode": "404",
"imageConfig": {
"useCanvasAPI": true,
"layered": true,
"basePath": "images/products/404/",
"layers": { /* ... */ }
}
}
```
If Canvas API fails, falls back to static layered system.
## Testing
### 1. Test API is Working
```
Visit: http://localhost:8080/api/canvas/test
Expected: {"status": "ok"}
```
### 2. Test Specific Image
```
Visit: http://localhost:8080/api/canvas/404/base
Expected: Image file loads
```
### 3. Test Fallback
```
Visit: http://localhost:8080/api/canvas/404/hardware
Check Flask logs to see which path was used
```
### 4. Test Product Info
```
Visit: http://localhost:8080/api/canvas/404/info
Expected: JSON with available layers
```
### 5. Test Product Page
```
Visit product page
Open browser console
Check for Canvas API requests: GET /api/canvas/404/door?color=white
```
## Troubleshooting
### Images not loading?
1. Check Flask logs for which path was searched
2. Visit `/api/canvas/<product_code>/info` to see available layers
3. Verify `"useCanvasAPI": true` in products.json
4. Check directory structure matches type/subtype
### 404 errors?
- File doesn't exist at any fallback level
- Check file naming (lowercase for colors)
- Verify baseType and subType in products.json
### Wrong image showing?
- More specific path overrides generic
- Check fallback priority order
- Verify file exists where you expect
## Documentation Files
- **[CANVAS_API_GUIDE.md](CANVAS_API_GUIDE.md)** - Complete technical guide
- **[CANVAS_API_QUICKSTART.md](CANVAS_API_QUICKSTART.md)** - 5-minute setup
- **[CANVAS_DIRECTORY_SETUP.md](CANVAS_DIRECTORY_SETUP.md)** - Setup scripts
- **[FOREGROUND_LAYER_EXAMPLE.md](FOREGROUND_LAYER_EXAMPLE.md)** - Foreground guide
- **[FOREGROUND_QUICKSTART.md](FOREGROUND_QUICKSTART.md)** - Quick foreground setup
- **[LAYERED_IMAGES.md](LAYERED_IMAGES.md)** - Full layering system
## Next Steps
1.**Test the API**: Visit `/api/canvas/test`
2.**Create directory structure**: Use scripts in CANVAS_DIRECTORY_SETUP.md
3.**Move/organize images**: Place in appropriate fallback levels
4.**Update products.json**: Add `"useCanvasAPI": true`
5.**Test products**: Load product pages and verify images
6.**Optimize**: Run duplicate detection and consolidate files
## Summary
You now have a sophisticated, production-ready image serving system with:
**Smart hierarchical fallbacks**
**Automatic image resolution**
**5-layer compositing** (including foreground from earlier)
**Massive storage savings**
**Easy maintenance**
**Flexible organization**
**Backward compatible**
The system is fully implemented and ready to use. Start with a few test products, verify it works, then gradually migrate your entire catalog!
+197
View File
@@ -0,0 +1,197 @@
# Quick Start: Canvas API Hierarchical Image System
## What Is This?
The Canvas API lets you share images across multiple products with automatic fallback. Instead of duplicating the same plants.png file in every product folder, you can have ONE shared file that all products use.
## 5-Minute Setup
### Step 1: Organize Your Images
**Create this structure:**
```
app/static/images/
└── doors/
└── storm/
├── 404/
│ └── door-white.png # Product-specific
├── 505/
│ └── door-black.png # Product-specific
└── layers/
├── base.jpg # Shared by all storm doors
├── hardware.png # Shared by all storm doors
└── foreground.png # Shared by all storm doors
```
### Step 2: Enable Canvas API
**Edit products.json:**
```json
{
"productCode": "404",
"description": "#404 FALCON STORM WINDOWS",
"colors": ["White", "Black"],
"imageConfig": {
"useCanvasAPI": true
}
}
```
### Step 3: Test
1. Start your Flask server
2. Visit: `http://localhost:8080/api/canvas/test`
3. Should see: `{"status": "ok"}`
4. Test specific image: `http://localhost:8080/api/canvas/404/base`
5. Open product page - images should load automatically!
## How It Works
When you request `/api/canvas/404/door?color=white`, the system searches:
1. `/images/doors/storm/404/door-white.png`**Checks here first**
2. `/images/doors/storm/404/door.png`
3. `/images/doors/storm/layers/door-white.png`
4. `/images/doors/storm/layers/door.png`**Uses this if 404-specific doesn't exist**
5. `/images/doors/layers/door.png`
6. `/images/layers/door.png`
## Directory Level Guide
| Level | Path | Use For | Example |
|-------|------|---------|---------|
| **Product** | `/images/doors/storm/404/` | Unique to this product | Custom door colors, unique base |
| **Subtype** | `/images/doors/storm/layers/` | Shared across storm doors | Common hardware, shared plants |
| **Type** | `/images/doors/layers/` | Shared across all doors | Generic handles, railings |
| **Global** | `/images/layers/` | Shared across everything | Default fallback images |
## Common Scenarios
### Scenario 1: All Products Share Plants
**Put foreground.png here:**
```
/images/doors/storm/layers/foreground.png
```
Both product 404 and 505 will use this same file automatically!
### Scenario 2: Product 404 Needs Custom Plants
**Add product-specific version:**
```
/images/doors/storm/404/foreground.png ← Product 404 uses this
/images/doors/storm/layers/foreground.png ← Product 505 uses this
```
### Scenario 3: Same Hardware for All Doors
**Put at type level:**
```
/images/doors/layers/hardware.png
```
Every door product (storm, entry, patio) uses the same hardware!
## Real-World Example
### Before (Static Layered System)
```
/images/products/404/
base.jpg (800KB)
door-white.png (200KB)
hardware.png (100KB) ← Duplicate
plants.png (150KB) ← Duplicate
/images/products/505/
base.jpg (800KB)
door-black.png (200KB)
hardware.png (100KB) ← Duplicate!
plants.png (150KB) ← Duplicate!
Total: 2.5MB
```
### After (Canvas API)
```
/images/doors/storm/404/
base.jpg (800KB)
door-white.png (200KB)
/images/doors/storm/505/
base.jpg (800KB)
door-black.png (200KB)
/images/doors/storm/layers/
hardware.png (100KB) ← Shared!
plants.png (150KB) ← Shared!
Total: 2.25MB (saved 250KB)
```
With 10 products sharing the same hardware/plants, you'd save **~2MB**!
## Quick Reference
### Enable for a Product
```json
{
"productCode": "404",
"imageConfig": {
"useCanvasAPI": true
}
}
```
### API Endpoints
```
/api/canvas/<product_code>/<layer>?color=<color>
/api/canvas/<product_code>/info
/api/canvas/test
```
### Valid Layers
- `base` - Background/house
- `door` - Door/window panel
- `hardware` - Handles/locks
- `overlay` - Glass views
- `foreground` - Plants/decorations
### Directory Pattern
```
/images/<type>/<subtype>/<product_code>/<layer>-<color>.<ext>
/images/<type>/<subtype>/layers/<layer>.<ext>
/images/<type>/layers/<layer>.<ext>
/images/layers/<layer>.<ext>
```
## Troubleshooting
**Images not loading?**
1. Check Flask logs: `[INFO] Found image: /path/to/file`
2. Visit: `/api/canvas/YOUR-PRODUCT/info`
3. Verify `"useCanvasAPI": true` in products.json
4. Check baseType/subType match directory structure
**Wrong image showing?**
- More specific paths override generic ones
- Product-specific overrides subtype
- Subtype overrides type
- Type overrides global
**404 error?**
- File doesn't exist at any fallback level
- Check file naming (lowercase for colors)
- Verify directory structure matches type/subtype
## Next Steps
1. ✅ Read the [full Canvas API Guide](CANVAS_API_GUIDE.md) for advanced features
2. ✅ Review [example JSON configurations](products-layered-example.json)
3. ✅ Check the [fallback system documentation](CANVAS_API_GUIDE.md#fallback-priority)
## Benefits
**Eliminate duplication** - Share common files across products
**Easier updates** - Change one file, affects all products
**Smaller total size** - Less storage and bandwidth
**Smart fallbacks** - Products automatically inherit shared elements
**Flexible organization** - Add product-specific overrides anytime
That's it! Start organizing your images by type/subtype and watch the magic happen. 🎨
+336
View File
@@ -0,0 +1,336 @@
# Directory Structure Helper - Canvas API
## Quick Directory Setup Scripts
### Windows PowerShell Script
Save as `create-canvas-structure.ps1`:
```powershell
# Create Canvas API directory structure
# Run from: app/static/images/
param(
[string]$BaseType = "doors",
[string]$SubType = "storm",
[string]$ProductCode = ""
)
# Create base structure
$basePath = "."
# Create type/subtype/layers directories
New-Item -ItemType Directory -Force -Path "$basePath/$BaseType/$SubType/layers" | Out-Null
Write-Host "✓ Created: $BaseType/$SubType/layers/"
# Create product-specific directory if provided
if ($ProductCode) {
New-Item -ItemType Directory -Force -Path "$basePath/$BaseType/$SubType/$ProductCode" | Out-Null
Write-Host "✓ Created: $BaseType/$SubType/$ProductCode/"
}
# Create type-level layers directory
New-Item -ItemType Directory -Force -Path "$basePath/$BaseType/layers" | Out-Null
Write-Host "✓ Created: $BaseType/layers/"
# Create global layers directory
New-Item -ItemType Directory -Force -Path "$basePath/layers" | Out-Null
Write-Host "✓ Created: layers/"
Write-Host "`nDirectory structure created successfully!"
Write-Host "Now add your images to the appropriate directories."
```
**Usage:**
```powershell
# From app/static/images/ directory
.\create-canvas-structure.ps1 -BaseType "doors" -SubType "storm" -ProductCode "404"
.\create-canvas-structure.ps1 -BaseType "windows" -SubType "double-hung" -ProductCode "505"
```
### Linux/Mac Bash Script
Save as `create-canvas-structure.sh`:
```bash
#!/bin/bash
# Create Canvas API directory structure
# Run from: app/static/images/
BASE_TYPE=${1:-"doors"}
SUB_TYPE=${2:-"storm"}
PRODUCT_CODE=$3
# Create type/subtype/layers directories
mkdir -p "$BASE_TYPE/$SUB_TYPE/layers"
echo "✓ Created: $BASE_TYPE/$SUB_TYPE/layers/"
# Create product-specific directory if provided
if [ -n "$PRODUCT_CODE" ]; then
mkdir -p "$BASE_TYPE/$SUB_TYPE/$PRODUCT_CODE"
echo "✓ Created: $BASE_TYPE/$SUB_TYPE/$PRODUCT_CODE/"
fi
# Create type-level layers directory
mkdir -p "$BASE_TYPE/layers"
echo "✓ Created: $BASE_TYPE/layers/"
# Create global layers directory
mkdir -p "layers"
echo "✓ Created: layers/"
echo ""
echo "Directory structure created successfully!"
echo "Now add your images to the appropriate directories."
```
**Usage:**
```bash
# From app/static/images/ directory
chmod +x create-canvas-structure.sh
./create-canvas-structure.sh doors storm 404
./create-canvas-structure.sh windows double-hung 505
```
## Python Script for Bulk Setup
Save as `setup_canvas_structure.py` in `app/`:
```python
"""
Setup Canvas API directory structure for multiple products
"""
import os
import json
def load_products():
"""Load products from products.json"""
with open('data/products.json', 'r', encoding='utf-8') as f:
return json.load(f)
def create_canvas_directories():
"""Create directory structure based on product data"""
products = load_products()
base_path = 'static/images'
created_dirs = set()
for product in products:
# Get product type info
base_type = product.get('baseType', '').lower()
subtype_obj = product.get('subType', {})
product_code = product.get('productCode') or product.get('id')
if not base_type or not product_code:
continue
# Extract subtype
sub_type = None
if isinstance(subtype_obj, dict):
if subtype_obj.get('door'):
sub_type = subtype_obj['door'].lower().replace(' ', '-')
elif subtype_obj.get('window'):
sub_type = subtype_obj['window'].lower().replace(' ', '-')
if not sub_type:
continue
# Create directories
dirs_to_create = [
f"{base_path}/{base_type}/{sub_type}/layers",
f"{base_path}/{base_type}/{sub_type}/{product_code}",
f"{base_path}/{base_type}/layers",
f"{base_path}/layers"
]
for dir_path in dirs_to_create:
if dir_path not in created_dirs:
os.makedirs(dir_path, exist_ok=True)
created_dirs.add(dir_path)
print(f"✓ Created: {dir_path}")
print(f"\n✅ Created {len(created_dirs)} directories")
print("\nNext steps:")
print("1. Move your product images to the appropriate directories")
print("2. Add 'useCanvasAPI': true to product imageConfig")
print("3. Test with: /api/canvas/<product_code>/info")
if __name__ == '__main__':
create_canvas_directories()
```
**Usage:**
```bash
cd app/
python setup_canvas_structure.py
```
## Manual Directory Creation Reference
### Complete Structure Example
```
app/static/images/
├── doors/
│ ├── storm/
│ │ ├── 404/
│ │ │ ├── base.jpg
│ │ │ ├── door-white.png
│ │ │ └── door-black.png
│ │ ├── 505/
│ │ │ ├── base.jpg
│ │ │ └── door-white.png
│ │ └── layers/
│ │ ├── base.jpg
│ │ ├── hardware.png
│ │ └── foreground.png
│ ├── entry/
│ │ ├── 600/
│ │ │ ├── base.jpg
│ │ │ └── door-bronze.png
│ │ └── layers/
│ │ └── foreground-plants.png
│ └── layers/
│ └── hardware-generic.png
├── windows/
│ ├── storm/
│ │ ├── 450/
│ │ │ └── base.jpg
│ │ └── layers/
│ │ └── foreground-minimal.png
│ └── layers/
│ └── base-generic.jpg
└── layers/
├── foreground-default.png
└── base-generic.jpg
```
## File Naming Conventions
### Layer Files
- **Base**: `base.jpg` or `base.png`
- **Door (no color)**: `door.png`
- **Door (with color)**: `door-white.png`, `door-black.png`, `door-bronze.png`
- **Hardware**: `hardware.png`
- **Foreground**: `foreground.png` or descriptive like `foreground-plants.png`
- **Overlay**: `overlay.png` or `view-inside.png`, `view-outside.png`
### Tips
- Use lowercase for color names in filenames
- Use hyphens to separate words
- Be descriptive for shared files: `foreground-spring-plants.png`
- Keep product-specific files simple: `base.jpg`, `door-white.png`
## Migration Helper
### Identify Duplicate Files
```powershell
# Windows PowerShell
Get-ChildItem -Path "app/static/images/products" -Recurse -File |
Group-Object -Property Length, Name |
Where-Object { $_.Count -gt 1 } |
Select-Object Name, Count
```
```bash
# Linux/Mac
find app/static/images/products -type f -exec md5sum {} + |
sort |
awk 'BEGIN{cmd="md5sum"}{if($1==prev){print $2}else{prev=$1}}'
```
This will show you which files are duplicated and can be shared.
## Validation Script
Save as `validate_canvas_structure.py`:
```python
"""
Validate Canvas API directory structure
"""
import os
import json
def validate_structure():
"""Check if directory structure matches product data"""
with open('app/data/products.json', 'r', encoding='utf-8') as f:
products = json.load(f)
issues = []
base_path = 'app/static/images'
for product in products:
if not product.get('imageConfig', {}).get('useCanvasAPI'):
continue
product_code = product.get('productCode') or product.get('id')
base_type = product.get('baseType', '').lower()
subtype_obj = product.get('subType', {})
# Extract subtype
sub_type = None
if isinstance(subtype_obj, dict):
if subtype_obj.get('door'):
sub_type = subtype_obj['door'].lower().replace(' ', '-')
elif subtype_obj.get('window'):
sub_type = subtype_obj['window'].lower().replace(' ', '-')
if not all([product_code, base_type, sub_type]):
issues.append(f"⚠️ {product_code}: Missing type/subtype info")
continue
# Check if product directory exists
product_dir = f"{base_path}/{base_type}/{sub_type}/{product_code}"
if not os.path.exists(product_dir):
issues.append(f"{product_code}: Directory not found: {product_dir}")
else:
# Check for at least one image file
files = os.listdir(product_dir)
image_files = [f for f in files if f.endswith(('.png', '.jpg', '.jpeg', '.webp'))]
if not image_files:
issues.append(f"⚠️ {product_code}: No images in {product_dir}")
else:
print(f"{product_code}: {len(image_files)} images found")
if issues:
print("\n❌ Issues found:")
for issue in issues:
print(f" {issue}")
else:
print("\n✅ All Canvas API products validated successfully!")
if __name__ == '__main__':
validate_structure()
```
**Usage:**
```bash
python validate_canvas_structure.py
```
## Quick Commands
### Create structure for all common types
```bash
# Doors
mkdir -p app/static/images/doors/{storm,entry,patio,screen}/layers
mkdir -p app/static/images/doors/layers
# Windows
mkdir -p app/static/images/windows/{storm,double-hung,casement,sliding}/layers
mkdir -p app/static/images/windows/layers
# Global
mkdir -p app/static/images/layers
```
### Move images from old structure
```bash
# Example: Move shared hardware from products to shared layers
mv app/static/images/products/*/hardware.png app/static/images/doors/storm/layers/
```
## Conclusion
These scripts help you quickly set up and validate the Canvas API directory structure. Choose the approach that works best for your workflow:
- **Manual**: Create directories as needed
- **Scripts**: Automate creation for multiple products
- **Migration**: Use helper scripts to identify duplicates and reorganize
+142
View File
@@ -0,0 +1,142 @@
# Foreground Layer Example
## Overview
The foreground layer adds depth and realism to product images by placing decorative elements (like plants, porch furniture, or architectural details) in front of the product.
## Layer Stacking Order
From back to front:
1. **Base Layer** (z-index: 1) - Background/house/frame
2. **Door Layer** (z-index: 2) - Door or window panel (changes with color)
3. **Hardware Layer** (z-index: 3) - Handles, locks, hinges
4. **Overlay Layer** (z-index: 4) - Glass views or decorative overlays
5. **Foreground Layer** (z-index: 5) - Plants, decorative items that appear in front
## Creating a Foreground Layer
### Step 1: Prepare Your Image
1. Take or create an image of decorative elements (plants, porch items, etc.)
2. Remove the background completely (use Photoshop, GIMP, or Photopea)
3. Save as PNG with transparency
4. Ensure dimensions match your base layer image
### Step 2: Position Elements
When creating your foreground layer, position elements to:
- Frame the product naturally
- Not obscure critical product details
- Add depth by having some elements extend beyond product edges
- Consider perspective and lighting consistency
### Step 3: Export Settings
- Format: PNG-24
- Transparency: Enabled
- Color Profile: sRGB
- Resolution: Match your base layer (typically 800-1200px width)
## JSON Configuration Example
```json
{
"productCode": "600",
"description": "#600 PREMIUM ENTRY DOOR",
"image": "images/600.jpg",
"colors": ["White", "Black", "Bronze", "Sandstone"],
"materials": ["Aluminum", "Vinyl"],
"imageConfig": {
"layered": true,
"basePath": "images/products/600/",
"layers": {
"base": "house-background.jpg",
"door": {
"white": "door-white.png",
"black": "door-black.png",
"bronze": "door-bronze.png",
"sandstone": "door-sandstone.png"
},
"hardware": "handle-silver.png",
"foreground": "front-plants.png"
}
}
}
```
## File Structure
```
app/images/products/600/
├── house-background.jpg # Background layer with house/frame
├── door-white.png # White door (transparent background)
├── door-black.png # Black door (transparent background)
├── door-bronze.png # Bronze door (transparent background)
├── door-sandstone.png # Sandstone door (transparent background)
├── handle-silver.png # Hardware layer (transparent background)
└── front-plants.png # Foreground layer with plants (transparent background)
```
## Tips for Best Results
### Foreground Elements to Consider
- **Potted plants**: Add life and color to the scene
- **Hanging baskets**: Create visual interest at different heights
- **Porch railings**: Partial railings in foreground add depth
- **Architectural details**: Columns, trim, or decorative elements
- **Seasonal decorations**: Pumpkins, wreaths, holiday items (create variants)
### Technical Tips
- Keep file sizes reasonable (compress PNGs without losing quality)
- Use soft shadows on foreground elements for realism
- Match lighting direction across all layers
- Consider blur/depth of field on very close foreground elements
- Test on different screen sizes to ensure elements don't obscure product
### Common Mistakes to Avoid
- ❌ Foreground elements too large, obscuring product
- ❌ Inconsistent lighting between layers
- ❌ Mismatched image dimensions
- ❌ Harsh edges on transparent elements
- ❌ Too much foreground detail competing with product
## Advanced: Multiple Foreground Variants
You can create different foreground options for variety:
```json
{
"productCode": "600",
"description": "#600 PREMIUM ENTRY DOOR",
"imageConfig": {
"layered": true,
"basePath": "images/products/600/",
"layers": {
"base": "house-background.jpg",
"door": {
"white": "door-white.png",
"black": "door-black.png"
},
"hardware": "handle-silver.png",
"foreground": "plants-spring.png"
// Could add logic to swap between plants-spring.png, plants-summer.png, etc.
}
}
}
```
## Testing Your Foreground Layer
1. Load your product page
2. Change door colors - foreground should stay visible on top
3. Verify no important product details are obscured
4. Check on mobile/tablet to ensure proper scaling
5. Test with different browsers for compatibility
## Performance Considerations
- PNG files can be large - optimize them using tools like:
- TinyPNG (https://tinypng.com)
- ImageOptim (https://imageoptim.com)
- Photoshop "Export for Web"
- Target file size: 50-200KB for foreground layer
- Use appropriate resolution (no need for 4K if displaying at 800px)
## Accessibility Note
The foreground layer has `pointer-events: none` CSS property, meaning clicks pass through to the controls below. This ensures the decorative layer doesn't interfere with user interaction.
+113
View File
@@ -0,0 +1,113 @@
# Quick Start: Adding Foreground Layers to Your Products
## Overview
Your product finder now supports foreground layers - perfect for adding plants, decorative elements, or any items that should appear in front of the product.
## Layer Order (back to front)
1. **Base** (z-index: 1) - House/background
2. **Door** (z-index: 2) - Door/window (changes with color)
3. **Hardware** (z-index: 3) - Handles/locks
4. **Overlay** (z-index: 4) - Glass views
5. **Foreground** (z-index: 5) - Plants/decorative items ⭐ NEW!
## Quick Implementation Steps
### Step 1: Prepare Your Foreground Image
1. Create/photograph decorative elements (plants, porch items, etc.)
2. Remove background (make transparent)
3. Save as PNG
4. Match dimensions with your base layer image
### Step 2: Save File to Correct Location
```
app/images/products/YOUR-PRODUCT-CODE/foreground.png
```
Example:
```
app/images/products/600/
├── house-background.jpg
├── door-white.png
├── door-black.png
├── handle-silver.png
└── plants.png ← Your new foreground layer
```
### Step 3: Update Product JSON
Edit your product in `app/data/products.json`:
```json
{
"productCode": "600",
"description": "Your Product Name",
"colors": ["White", "Black", "Bronze"],
"imageConfig": {
"layered": true,
"basePath": "images/products/600/",
"layers": {
"base": "house-background.jpg",
"door": {
"white": "door-white.png",
"black": "door-black.png",
"bronze": "door-bronze.png"
},
"hardware": "handle-silver.png",
"foreground": "plants.png" Add this line
}
}
}
```
### Step 4: Test
1. Save changes
2. Refresh your browser
3. Navigate to the product
4. The foreground layer should now appear on top of everything
## Tips for Best Results
### Good Foreground Elements
✅ Potted plants at corners of frame
✅ Hanging baskets to the side
✅ Partial porch railings
✅ Seasonal decorations (wreaths, pumpkins)
### Avoid
❌ Obscuring important product details
❌ Elements too large or too busy
❌ Mismatched lighting/shadows
❌ Low-quality or pixelated images
## Example Products
See these files for complete examples:
- [information/FOREGROUND_LAYER_EXAMPLE.md](FOREGROUND_LAYER_EXAMPLE.md) - Detailed guide
- [information/products-layered-example.json](products-layered-example.json) - Product #600 example
- [information/LAYERED_IMAGES.md](LAYERED_IMAGES.md) - Full layered system guide
## Troubleshooting
**Foreground not showing?**
- Check file path in JSON matches actual file location
- Ensure `"layered": true` is set
- Verify PNG has transparency
- Check browser console for 404 errors
**Foreground blocking clicks?**
- This shouldn't happen - the layer has `pointer-events: none`
- If it does, check CSS for `.layer-foreground`
**Image quality issues?**
- Use PNG-24 format
- Match base layer dimensions
- Optimize file size with TinyPNG or similar
## File Sizes & Performance
- Target: 50-200KB for foreground PNG
- Optimize images before uploading
- Browser caches layers after first load
## Need Help?
Check the detailed documentation:
- `information/FOREGROUND_LAYER_EXAMPLE.md` - Complete guide with tips
- `information/LAYERED_IMAGES.md` - Full layered system documentation
+34
View File
@@ -11,6 +11,7 @@ Images are stacked in layers (like Photoshop layers):
2. **Door Layer** - The door panel (PNG with transparency) - changes with color selection
3. **Hardware Layer** - Handle/lock set (PNG with transparency) - can flip for left/right hinge
4. **Overlay Layer** - Glass view/decorative elements (PNG with transparency) - optional
5. **Foreground Layer** - Plants, decorative items that appear in front (PNG with transparency) - optional
### Fallback Behavior
- **No layered config**: Shows the standard flat `image` field
@@ -83,6 +84,32 @@ Images are stacked in layers (like Photoshop layers):
}
```
### Product with Foreground Layer (Plants/Decorative Elements)
```json
{
"productCode": "600",
"description": "#600 PREMIUM ENTRY DOOR",
"image": "images/600.jpg",
"colors": ["White", "Black", "Bronze"],
"materials": ["Aluminum"],
"imageConfig": {
"layered": true,
"basePath": "images/products/600/",
"layers": {
"base": "base.jpg",
"door": {
"white": "door-white.png",
"black": "door-black.png",
"bronze": "door-bronze.png"
},
"hardware": "handle.png",
"foreground": "plants.png"
}
}
}
```
**Note:** The foreground layer appears on top of all other layers and is perfect for adding plants, decorative elements, or other items that should appear in front of the product.
## File Structure
### Recommended Directory Layout
@@ -97,6 +124,13 @@ app/images/products/
│ ├── handle.png # Hardware (transparent BG)
│ ├── view-inside.png # Optional inside view overlay
│ └── view-outside.png # Optional outside view overlay
├── 600/
│ ├── base.jpg # Frame/house background
│ ├── door-white.png # White door panel (transparent BG)
│ ├── door-black.png # Black door panel (transparent BG)
│ ├── door-bronze.png # Bronze door panel (transparent BG)
│ ├── handle.png # Hardware (transparent BG)
│ └── plants.png # Foreground layer - plants/decorative (transparent BG)
├── 450/
│ ├── base.jpg
│ └── door-white.png
+210
View File
@@ -0,0 +1,210 @@
# Layer Transform System
## Overview
The Canvas API layer system supports CSS transforms for individual layers, allowing you to flip or rotate specific layers without modifying the image files. This is particularly useful for:
- **Door Hinge Direction**: Flip the door layer horizontally to show left-hinge vs right-hinge
- **Mirror Effects**: Create symmetric variations of products
- **Multi-Configuration Support**: Use the same image assets for multiple product variations
## Configuration
Add transform configuration to your product's `imageConfig` object:
```json
{
"productCode": "404",
"imageConfig": {
"useCanvasAPI": true,
"layerTransforms": {
"door": "flip-horizontal",
"hardware": "flip-horizontal"
}
}
}
```
## Available Transforms
### `flip-horizontal`
Flips the layer horizontally (left-right mirror).
**CSS Applied**: `transform: scaleX(-1)`
**Use Case**: Door hinge direction (left-hinge vs right-hinge)
```json
"layerTransforms": {
"door": "flip-horizontal"
}
```
### `flip-vertical`
Flips the layer vertically (top-bottom mirror).
**CSS Applied**: `transform: scaleY(-1)`
**Use Case**: Ceiling-mounted vs floor-mounted products
```json
"layerTransforms": {
"base": "flip-vertical"
}
```
### `flip-both`
Flips the layer both horizontally and vertically (180° rotation).
**CSS Applied**: `transform: scale(-1, -1)`
**Use Case**: Complete inversion of a layer
```json
"layerTransforms": {
"overlay": "flip-both"
}
```
## Layer Names
Available layers you can transform:
- `base` - Background/house layer
- `door` - Door/window layer (with color variations)
- `hardware` - Hardware layer (handles, locks, etc.)
- `overlay` - Additional overlay layer
- `foreground` - Foreground layer (plants, decorations)
## Complete Example
### Left-Hinge Door (Default)
```json
{
"id": "404",
"productCode": "404",
"imageConfig": {
"useCanvasAPI": true
}
}
```
### Right-Hinge Door (Flipped)
Create a separate product entry with a different code:
```json
{
"id": "404R",
"productCode": "404R",
"description": "#404 FALCON STORM WINDOWS (RIGHT-HINGE)",
"imageConfig": {
"useCanvasAPI": true,
"layerTransforms": {
"door": "flip-horizontal",
"hardware": "flip-horizontal"
}
}
}
```
**Important**: Both products can share the same image files! The transform is applied in CSS at render time.
## Image Preparation Tips
When creating images that will be flipped:
1. **Design for the default orientation first** (e.g., left-hinge door)
2. **Keep text/logos off flippable layers** - they will be reversed
3. **Test the flipped version** to ensure it looks natural
4. **Consider asymmetric details** - handles, hinges, decorative elements
## Implementation Details
### CSS Classes
The system applies these CSS classes automatically:
- `.layer-flip-horizontal` - Horizontal flip
- `.layer-flip-vertical` - Vertical flip
- `.layer-flip-both` - Both axes flip
### JavaScript
Transforms are applied in `updateCanvasAPIPreview()` function:
```javascript
const layerTransforms = product.imageConfig?.layerTransforms || {};
function applyTransform(element, layerName) {
element.classList.remove('layer-flip-horizontal', 'layer-flip-vertical', 'layer-flip-both');
const transform = layerTransforms[layerName];
if (transform) {
element.classList.add(`layer-${transform}`);
}
}
```
## Advanced: Multiple Products, Same Images
You can create an entire product family from a single set of images:
```
/static/images/window/storm-window/404/
├── door-white.png (left-hinge design)
├── door-black.png (left-hinge design)
└── ...
Products using these images:
- 404 - Left-hinge (no transform)
- 404R - Right-hinge (door flipped)
- 404T - Top-mount variant (base flipped)
- 404RT - Right-hinge top-mount (both flipped)
```
## Browser Compatibility
CSS transforms are supported in all modern browsers:
- Chrome/Edge: ✅
- Firefox: ✅
- Safari: ✅
- Opera: ✅
## Performance
Layer transforms are GPU-accelerated CSS operations with no performance impact. Flipping layers is instant and doesn't require:
- Additional HTTP requests
- Image processing
- Additional storage
- Server-side rendering
## Troubleshooting
### Transform not applying
1. Check that `useCanvasAPI: true` is set
2. Verify layer name matches exactly (case-sensitive)
3. Check browser console for JavaScript errors
4. Ensure CSS is loaded properly
### Image looks distorted
Transforms maintain aspect ratio. If the image looks wrong:
1. Verify the original image has correct proportions
2. Check that all layers use the same canvas dimensions
3. Test without transforms first to isolate the issue
### Text is backwards
This is expected! Don't place text or logos on layers that will be flipped. Instead:
1. Keep text on non-flipped layers (usually `base` or `overlay`)
2. Create separate images for left/right variants if text is essential
3. Use the overlay layer for directional text
## Future Enhancements
Possible additions:
- Rotation angles (90°, 180°, 270°)
- Scale adjustments (zoom in/out specific layers)
- Position offsets (shift layers left/right/up/down)
- Animation/transition effects
+69
View File
@@ -38,6 +38,46 @@
}
}
},
{
"id": "600",
"productCode": "600",
"category": "DOORS",
"description": "#600 PREMIUM ENTRY DOOR WITH PLANTS",
"discontinued": false,
"location": "Iola",
"baseType": "Door",
"subType": {
"door": "Entry Door",
"window": null
},
"materials": [
"Aluminum"
],
"colors": [
"White",
"Black",
"Bronze",
"Sandstone"
],
"isAccessory": false,
"compatibleAccessories": [],
"image": "images/600.jpg",
"imageConfig": {
"layered": true,
"basePath": "images/products/600/",
"layers": {
"base": "house-background.jpg",
"door": {
"white": "door-white.png",
"black": "door-black.png",
"bronze": "door-bronze.png",
"sandstone": "door-sandstone.png"
},
"hardware": "handle-silver.png",
"foreground": "plants.png"
}
}
},
{
"id": "450",
"productCode": "450",
@@ -102,5 +142,34 @@
}
}
}
},
{
"id": "700",
"productCode": "700",
"category": "DOORS",
"description": "#700 MODERN ENTRY DOOR (Canvas API Example)",
"discontinued": false,
"location": "Iola",
"baseType": "Door",
"subType": {
"door": "Entry Door",
"window": null
},
"materials": [
"Aluminum",
"Vinyl"
],
"colors": [
"White",
"Black",
"Bronze"
],
"isAccessory": false,
"compatibleAccessories": [],
"image": "images/700.jpg",
"imageConfig": {
"useCanvasAPI": true
},
"_comment": "Canvas API Example - Images will be automatically loaded from hierarchical fallback system. Product-specific images go in /images/doors/entry/700/, shared images go in /images/doors/entry/layers/, /images/doors/layers/, or /images/layers/"
}
]