Skip to main content

ChamevoJS API (Headless)

Use @chamevo/core to get full canvas orchestration (product loading, view switching, pricing, history) while building your own custom UI.

When to use: You want complete control over the user interface but don't want to manage canvas internals yourself. ChamevoJS handles the data model, view lifecycle, pricing, and serialization.

Installation

npm install @chamevo/core

FabricJS v7 is a peer dependency:

npm install fabric@^7.0.0

Setup

import { ChamevoJS, ChamevoCanvas } from '@chamevo/core';

// 1. Create a <canvas> element
const canvasEl = document.getElementById('my-canvas') as HTMLCanvasElement;

// 2. Create the ChamevoCanvas (FabricJS wrapper)
const canvas = new ChamevoCanvas(canvasEl, {
width: 900,
height: 600,
backgroundColor: '#ffffff',
});

// 3. Create the ChamevoJS orchestrator
const chamevo = new ChamevoJS({
canvas,
options: {
stageWidth: 900,
stageHeight: 600,
fonts: [
{ name: 'Arial' },
{ name: 'Roboto', url: 'google' },
],
},
});

Loading Products

await chamevo.loadProduct({
title: 'T-Shirt',
views: [
{
title: 'Front',
thumbnail: '/images/front-thumb.jpg',
elements: [
{
type: 'image',
source: '/images/tshirt-front.png',
title: 'T-Shirt Front',
parameters: { left: 450, top: 300, draggable: false },
},
],
},
{
title: 'Back',
elements: [
{
type: 'image',
source: '/images/tshirt-back.png',
title: 'T-Shirt Back',
parameters: { left: 450, top: 300, draggable: false },
},
],
},
],
});

Events

ChamevoJS uses a typed event emitter. Subscribe with .on() and unsubscribe with .off():

// Element selection — update your custom UI
chamevo.on('elementSelect', ({ element }) => {
if (element) {
showToolbar(element);
} else {
hideToolbar();
}
});

// Element modifications — sync with your state
chamevo.on('elementModify', ({ element, changes }) => {
console.log(`${element.title} changed:`, changes);
});

// Price updates
chamevo.on('priceChange', ({ price, singleProductPrice, pricingRulesPrice, breakdown }) => {
updatePriceDisplay(price);
// breakdown: PricingLineItem[] — one entry per matched pricing rule
});

// View switches
chamevo.on('viewSelect', ({ view, index }) => {
updateViewTabs(index);
});

// Product loaded
chamevo.on('productCreate', ({ product }) => {
console.log(`Loaded: ${product.title} with ${product.views.length} views`);
});

// Loading state
chamevo.on('loading', ({ isLoading }) => {
toggleSpinner(isLoading);
});

See the Events Guide for the complete event reference.

ChamevoJS Methods

Product Lifecycle

MethodReturnsDescription
loadProduct(product, replaceInitialElements?)Promise<void>Load a CVProduct or legacy CVView[]. Pass replaceInitialElements: true to preserve user-added elements across the load; when omitted it falls back to mainOptions.replaceInitialElements.
getProduct()CVProduct | nullGet the current product with all element states serialized.
getOrder(){ product, usedFonts, usedColors }Get product + font/color metadata for order processing.
reset()voidRemove all user-added elements across all views.
abortProductLoading()voidCancel an in-progress product load.

Views

MethodReturnsDescription
selectView(index)Promise<void>Switch to a view by index. Saves current view state first.
PropertyTypeDescription
viewsChamevoView[]All view instances for the current product.
currentViewChamevoView | nullThe currently active view.
currentViewIndexnumberIndex of the active view.
productCreatedbooleanWhether a product has been fully loaded.

Elements

Elements are added/modified via the canvas instance:

// Add a text element
await canvas.addElement('text', 'Hello World', 'My Text', {
fontSize: 32,
fill: '#333333',
left: 400,
top: 300,
draggable: true,
resizable: true,
});

// Add an image element
await canvas.addElement('image', '/images/logo.png', 'Logo', {
left: 450,
top: 200,
scaleX: 0.5,
scaleY: 0.5,
});

// Modify an element by title
canvas.setElementOptions('My Text', { fill: '#ff0000', fontSize: 48 });

// Remove an element by title
canvas.removeElement('My Text');

To get serialized element data:

// All elements in current view
const elements = chamevo.getElements();

// Only text elements in view 0
const textElements = chamevo.getElements(0, 'text');

// Only image elements across all views
const imageElements = chamevo.getElements(undefined, 'image');

Image Uploads

MethodReturnsDescription
handleImageUpload(source, title, params?)Promise<ICVElement | null>Add an uploaded image with before/after hook events. Returns null if cancelled by a beforeImageUpload listener.
// Add an uploaded image (fires beforeImageUpload / afterImageUpload hooks)
const element = await chamevo.handleImageUpload(
'https://example.com/photo.png',
'User Photo',
{ isCustom: true },
);

if (!element) {
console.log('Upload was cancelled by a hook');
}

Options

PropertyTypeDescription
currentOptionsChamevoOptionsFully resolved options (DEFAULTS -> main -> view -> printArea).
optionsServiceOptionsServiceAccess the options cascade service directly.
// Read resolved options
const maxPrice = chamevo.currentOptions.maxPrice;

// Update global options at runtime
chamevo.optionsService.updateMainOptions({ maxPrice: 100 });

Pricing

PropertyTypeDescription
currentPricenumberTotal price (with quantity multiplier).
singleProductPricenumberPer-unit price from element prices.
pricingRulesPricenumberAdditional price from pricing rules.
orderQuantitynumberQuantity multiplier.
MethodReturnsDescription
calculatePrice(considerQuantity?)numberRecalculate and emit price.
setOrderQuantity(qty)voidSet quantity and recalculate price.
getPricingBreakdown()PricingLineItem[]Returns the current breakdown — one entry per matched pricing rule, with property, target, condition, and amount fields.

Services

ChamevoJS exposes its internal services for advanced use:

chamevo.optionsService   // OptionsService — configuration cascade
chamevo.fontService // FontService — font loading
chamevo.pricingService // PricingService — pricing rules
chamevo.uploadService // UploadService — file validation & upload

Fonts

// Load fonts before using them
await chamevo.fontService.loadFont('Roboto', 'google');
await chamevo.fontService.loadFont('Custom Font', '/fonts/custom.woff2');

History (Undo/Redo)

History is managed by the canvas:

canvas.history.undo();
canvas.history.redo();

console.log(canvas.history.canUndo); // boolean
console.log(canvas.history.canRedo); // boolean

Building a Custom UI

Here's a pattern for wiring ChamevoJS to a custom UI:

// View tabs
function renderViewTabs() {
chamevo.views.forEach((view, i) => {
const tab = document.createElement('button');
tab.textContent = view.title;
tab.classList.toggle('active', i === chamevo.currentViewIndex);
tab.onclick = () => chamevo.selectView(i);
viewBar.appendChild(tab);
});
}

// Element toolbar
chamevo.on('elementSelect', ({ element }) => {
if (!element) {
toolbar.style.display = 'none';
return;
}

toolbar.style.display = 'block';

// Show relevant controls based on element type
if (element.type?.startsWith('text') || element.type?.startsWith('cv-')) {
showTextControls(element);
} else {
showImageControls(element);
}
});

// Undo/redo buttons
undoBtn.onclick = () => canvas.history.undo();
redoBtn.onclick = () => canvas.history.redo();

// Save button
saveBtn.onclick = () => {
const product = chamevo.getProduct();
console.log(JSON.stringify(product));
};

Next Steps