Files
Jason 29154bd651 Editor Support
Co-authored-by: Copilot <copilot@github.com>
2026-05-05 15:42:30 -05:00

9.0 KiB

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:

{
  "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:

{
  "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:

{
  "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:

{
  "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