Skip to main content

Migration from FPD-JS

This guide covers breaking changes when migrating from the legacy FPD-JS (Fancy Product Designer) to ChamevoJS.

Package Changes

FPD-JSChamevoJS
fancy-product-designer@chamevo/customizer (or @chamevo/core for headless)
Global FancyProductDesignercreateCustomizer() factory or ChamevoJS class
jQuery dependencyNo jQuery — pure Web Components

Data Format Changes

Product Structure

Legacy (FPD-JS): Products were flat arrays. Product metadata lived on the first view.

[
{
"productTitle": "Hoodie",
"productThumbnail": "hoodie.png",
"product_id": "42",
"title": "Front",
"elements": []
},
{ "title": "Back", "elements": [] }
]

ChamevoJS: Products are CVProduct objects with views as a nested array. Products can carry product-level option overrides via options.

{
"id": "42",
"title": "Hoodie",
"thumbnail": "hoodie.png",
"options": {},
"views": [
{ "id": "front", "title": "Front", "elements": [] },
{ "id": "back", "title": "Back", "elements": [] }
]
}

Backward compatible: loadProduct() accepts both formats. Legacy arrays are auto-normalized at load time.

IDs on Products and Views

Products and views now carry stable id fields. When id is missing, it is auto-generated from title via generateSlugId() (lowercase, spaces to hyphens).

Data Model Changes

CVView new fields:

FieldTypeDescription
lockedbooleanWhether the view is locked (optional view that user hasn't unlocked yet).

CVProduct new fields:

FieldTypeDescription
namesNumbersNamesNumbersEntry[]Names & Numbers roster shared across all views. Stored at product level and serialized in getProduct() output.

Note: In earlier builds, namesNumbers was stored per view in view.namesNumbers. It is now stored at the product level in product.namesNumbers. Legacy view-level data is auto-migrated to the product level on loadProduct().

CVProduct.options: Products can carry product-level option overrides (options?: Partial<ChamevoOptions>) that are merged at load time.

getProduct() / getOrder() Return Types

LegacyChamevoJS
getProduct() returnCVView[] | nullCVProduct | null
Includes product metadataNo (on first view)Yes (id, title, thumbnail, options)
View IDsNoYes (auto-generated if missing)

getOrder() returns a richer object:

{
product: CVProduct | null;
usedFonts: FontInfo[]; // { name, url?, type? }
usedColors: ColorInfo[]; // { hex, name? } — name derived from hexNames option
}

Catalog Data Types

Product catalog (setProducts()):

CVProductCatalogInput supports flat product lists, categorized products, and both new and legacy formats:

// Flat (no categories)
customizer.setProducts([
{ title: 'T-Shirt', views: [...] },
{ title: 'Hoodie', views: [...] },
]);

// Categorized
customizer.setProducts([
{ category: 'Shirts', products: [{ title: 'T-Shirt', views: [...] }] },
]);

// Legacy format (auto-normalized)
customizer.setProducts([
[{ productTitle: 'T-Shirt', title: 'Front', elements: [...] }, { title: 'Back', ... }],
]);

Graphics catalog (setGraphics()):

CVGraphicsCatalogInput supports flat lists and recursive categories. The designs field name on categories is kept for legacy FPD-JS JSON compatibility:

customizer.setGraphics([
{
title: 'Nature',
category: [{ title: 'Animals', designs: [{ source: 'cat.svg', title: 'Cat', parameters: {} }] }],
},
]);

Event Changes

FPD-JS EventChamevoJS Core EventWeb Component Event
productCreateproductCreatecvProductCreate
viewSelectviewSelectcvViewSelect
elementAddelementAdd
elementRemoveelementRemove
elementSelectelementSelectcvElementSelect
priceChangepriceChangecvPriceChange

Event Listening

// FPD-JS (jQuery)
$('#fpd').on('productCreate', function(event, product) { ... });

// ChamevoJS (native events)
chamevo.on('productCreate', ({ product }) => { ... });

// Or on the web component
customizer.addEventListener('cvProductCreate', (e) => {
const { product } = e.detail;
});

Event Payload Changes

EventLegacy PayloadChamevoJS Payload
productCreate{ product: CVView[] }{ product: CVProduct }
productChange{ product: CVView[] }{ product: CVProduct }

Web component events (cv-customizer) forward core events with a cv prefix. See the Events guide for the complete event reference.

Configuration Changes

Options Cascade

Legacy: Options passed as a flat object, no scope awareness.

ChamevoJS: Options cascade with scope annotations (global, view, printArea):

OPTION_DEFAULTS -> mainOptions -> viewOptions -> printAreaProfile

Use chamevo.currentOptions to read the fully cascaded options for the active view/print area. See Options Cascade for details.

disabledModules vs mainBarModules:

  • mainBarModules — global only, controls which modules exist in the main bar
  • disabledModules — participates in full cascade (global → view → print area), temporarily hides modules for specific contexts. Uses the same module IDs as mainBarModules.

Removed Options

OptionLegacy PurposeReason Removed
facebookAppIdFacebook login for image importsFacebook API deprecated
instagramClientIdInstagram API integrationInstagram Basic Display API sunset
instagramRedirectUriInstagram OAuth redirectRemoved with Instagram integration
instagramTokenUriInstagram OAuth token endpointRemoved with Instagram integration
designsJSONURL to load designs JSON at initProducts/designs loaded via loadProduct() API
productsJSONURL to load products JSON at initProducts loaded via loadProduct() API
loadFirstProductInStageAuto-load first product on initExplicit loadProduct() call required
editorBoxParametersProperties shown in editor helper boxReplaced by cv-element-toolbar component
boundingBoxPropsProperty keys synced to bounding boxBounding box logic redesigned in core
boundingBoxModeBounding box display modeOnly clipping mode was ever used; now always clipping. Legacy values (false, 0, '0') silently normalized to null.
customAddsControl which media types users can addUse mainBarModules / disabledModules instead
uploadZonesToppedKeep upload zones on top of all elementsRemoved; upload zone z-ordering simplified
canvasHeightAlias for stageHeightRedundant; use stageHeight
maxCanvasHeightMaximum canvas height limitRemoved; canvas size controlled by stageHeight
deselectActiveOnOutsideDeselect element on outside clickAlways deselects (standard canvas behavior)
outOfBoundaryColorColor tint for out-of-bounds elementsReplaced by clipping/bounding box visual feedback
autoFillUploadZonesAuto-fill upload zones with first uploadUpload zone behavior simplified
dragDropImagesToUploadZonesDrag-drop files into upload zonesUpload zones handle drops natively
rulerFixedLock ruler to fixed positionRuler addon redesigned; always follows viewport
rulerPositionRuler placement (top/bottom)Ruler addon uses standard top+left placement
imageLoadTimestampAppend timestamp to image URLsNo longer needed — use HTTP cache headers instead
fabricCanvasOptionsFabricJS canvas optionsManaged internally
mobileGesturesBehaviourMobile gesture handlingBuilt-in gesture handling
responsiveBreakpointsLayout breakpoint widthsAutomatic based on container width

New Options

OptionTypeDefaultScopeDescription
labelsPartial<ChamevoLabels>{}globalUI label overrides for i18n. Replaces langJSON.
maxColorableSVGPathsnumber10viewMax paths in multi-path SVG for per-path color editing.
disabledModulesstring[][]viewHide specific modules per view/print area.
uploadStorageScope'global' | 'scoped''global'global'scoped' creates separate upload buckets per view/print area.
aiServiceAIServiceConfigglobalAI service config for text-to-image.
enableDynamicViewsbooleanfalseglobalAllow user to add/remove/duplicate views.
dynamicViewsOptionsDynamicViewsOptionsviewDynamic views config.
optionalViewbooleanfalseviewMake view optional (user must unlock).
industry{ type, opts }viewIndustry-specific behavior (e.g. 'engraving').
actionsActionsConfigglobalAction bar layout: { left, center, right }.
toolbarCVToolbarConfigglobalToolbar config: { placement, dynamicContext, openTextInputOnSelect, filters }.
unitOfMeasurementRulerUnit'mm'globalUnit for ruler and dynamic views.
customizationRequiredRule'any' | 'all''any'globalWhether any or all views need customization.
unsavedProductAlertbooleanfalseglobalAlert when leaving with unsaved changes.
downloadFilenamestring'Product'globalFilename for product downloads.
replaceInitialElementsbooleanfalseglobalOnly replace initial elements on product change.
guidedTourRecord<string, string> | nullnullglobalGuided tour steps. Key = target selector, value = text.
modalModestring | falsefalseglobalCSS selector for trigger element. Customizer opens as modal overlay on trigger click.
layoutsCVLayoutItem[] | string[]viewLayout templates. Accepts array or URL to JSON.

See Options Reference for the complete list.

Deprecated Options (Moved to Module Config)

These options still work in ChamevoOptions for backward compatibility but are deprecated in favor of the modulesConfig property:

Legacy OptionModule Config Replacement
langJSONlabels option (inline object, not URL)
allowedImageTypesmodulesConfig.uploads.allowedTypes
uploadAgreementModalmodulesConfig.uploads.agreementModal
fileServerURLmodulesConfig.uploads.fileServerURL
imageQualityRatingsmodulesConfig.uploads.qualityRatings
designCategoriesmodulesConfig.graphics.designCategories
textTemplatesmodulesConfig.text.templates
disableTextEmojismodulesConfig.text.disableEmojis
swapProductConfirmationmodulesConfig.products.confirmSwap
namesNumbersEntryPricemodulesConfig.namesNumbers.entryPrice
namesNumbersDropdownmodulesConfig.namesNumbers.dropdown
bulkVariationsmodulesConfig.bulkVariations.variations
bulkVariationsPlacementmodulesConfig.bulkVariations.placement
pixabayApiKeymodulesConfig.pixabay.apiKey
pixabayHighResImagesmodulesConfig.pixabay.highRes
pixabayLangmodulesConfig.pixabay.lang
saveActionBrowserStoragemodulesConfig.myDesigns.maxDesigns
layersOnlyEditablemodulesConfig.layers.onlyEditable

Removed Pricing Properties

Removed PropertyNotes
canvasSizePricing rules based on canvas dimensions are no longer supported. Use coverage to price based on how much of the print area is filled.
imageSizePricing based on raw image dimensions is no longer supported. Use coverage instead.
imageSizeScaledPricing based on scaled image dimensions is no longer supported. Use coverage instead.

Component Changes

jQuery → Web Components

FPD-JSChamevoJS
jQuery UIStencilJS Web Components (shadow DOM)
#fpd container<cv-customizer> element
CSS classesCSS custom properties (--cv-*)
$('#fpd').on('event')customizer.addEventListener('cvEvent')

CSS Theming

Legacy: Hardcoded styles, difficult to customize.

ChamevoJS: CSS custom properties (--cv-*) for all visual tokens. Override on cv-customizer or any ancestor:

cv-customizer {
--cv-primary: #e11d48;
--cv-radius: 0.5rem;
}

Dark mode: <cv-customizer color-scheme="dark">. UI style presets: <cv-customizer ui-style="rounded">.

See the Theming guide for all available tokens.

Module System

Legacy: Modules registered on global FancyProductDesigner.additionalModules. UI controllers tightly coupled.

ChamevoJS: Independent cv-module-* web components. Canvas addons via canvas.use(). Per-module typed config props with resolution order: config prop → modulesConfig → default. Action buttons can be placed externally via <cv-action-button>.

Designs → Graphics rename: The legacy "designs" module is now cv-module-graphics with CVGraphicsModuleConfig. The type alias CVDesignsModuleConfig is kept as deprecated.

Saved Designs

Legacy: SaveLoad module stored CVView[] in localStorage.

ChamevoJS: cv-module-my-designs stores CVProduct objects. Pluggable storage via CVDesignStorage interface (default: localStorage, 50-design limit). Configure via CVMyDesignsModuleConfig.

i18n

Legacy: Translator class loading external JSON language files via langJSON URL.

ChamevoJS: labels option in ChamevoOptions with Partial<ChamevoLabels>:

customizer.setOptions({
labels: { addText: 'Texto', save: 'Guardar' },
});

See the i18n guide for the complete label key reference.

FabricJS Version

LegacyChamevoJS
VersionFabricJS 5.xFabricJS 7.x
Object patternfabric.util.createClass()ES6 classes with CV prefix
CanvasOne per viewSingle shared canvas

Legacy: One FabricJS canvas per view (caused browser crashes with many views due to GPU/WebGL limits).

ChamevoJS: Single canvas shared across all views. Only the active view is rendered. View switching saves state, clears canvas, and renders the new view.

Removed Dependencies

Legacy DependencyChamevoJS Replacement
jQueryNative DOM / StencilJS
vanilla-picker + tinycolor2cv-color-picker (zero-dep HSV picker)
webfontloaderFontService (CSS Font Loading API)
AreaSortablesortable.ts (Pointer Events utility)

Upload Zone → Print Area Conversion

FPD-JS used uploadZone: true image elements as drop targets for user uploads. ChamevoJS replaces this concept with structured print areas (CVPrintArea).

No migration work needed — ChamevoJS automatically converts legacy upload zone elements into print areas during loadProduct(). You can keep your existing product data as-is.

What Happens Automatically

  1. Elements with uploadZone: true are detected during product loading
  2. The image is preloaded to get its natural dimensions
  3. A print area is created with calculated bounding box and output dimensions
  4. The original image becomes the print area's placeholder (shown until the user adds content)
  5. The upload zone element is removed from the canvas element list

Property Mapping

FPD-JS Upload ZoneChamevoJS Print AreaNotes
sourceplaceholderOriginal image shown as placeholder
left, topprintingBox.left, printingBox.topDirect mapping
naturalWidth × scaleXprintingBox.widthCalculated from image dimensions
naturalHeight × scaleYprintingBox.heightCalculated from image dimensions
titleidSlugified: "uz_" + title
output.width, output.heightAuto-calculated in mm at 72 DPI
showIndicatorAlways enabled

Example

Legacy product data with an upload zone:

{
"type": "image",
"title": "Front Print Area",
"source": "/images/upload-zone.png",
"parameters": {
"uploadZone": true,
"left": 100,
"top": 150,
"scaleX": 0.5,
"scaleY": 0.5
}
}

ChamevoJS automatically converts this to a print area during loading — no code changes required. The placeholder image is displayed until the user adds their own content.

Serialization

Once converted, getProduct() returns the print area in the modern format:

{
"printAreas": [{
"id": "uz_front_print_area",
"printingBox": { "left": 100, "top": 150, "width": 200, "height": 150 },
"output": { "width": 70.56, "height": 52.92 },
"placeholder": "/images/upload-zone.png"
}]
}

Fallback

If the upload zone image fails to preload (e.g. broken URL), the element is kept as a regular image element instead of being converted.

Next Steps