Plugin API
- Last updated on September 12, 2025
- •
- 7 minutes to read
The Plugin API is a framework built for Handy Editor and related to Customers Canvas products. Unlike the Web-component API, this solution designed to address specific, advanced tasks and is intended as a powerful but complex tool that may require expertise in Design Atoms (DA), Angular, and NGRx. It should be used only when other integration options are insufficient or unavailable for solving your task.
This API is tailored to work with Handy Editor's internal architecture:
- NGRX for state management.
- Design Atoms as the rendering engine.
- Custom tokens (e.g.,
PRICE_TOKEN
,STORE_TOKEN
) to access internal services.
This makes it similar to plugins in tools like Adobe Photoshop or Figma, but unique to Handy Editor.
Getting Started
To use the Plugin API, add the following script to your page before the main CDN script:
if (window.AurigmaWebComponents == null) {
window.AurigmaWebComponents = {};
}
window.AurigmaWebComponents.handyEditor = { autoBootstrapping: false };
// For 3D viewer, use:
// window.AurigmaWebComponents.handyEditor3d = { autoBootstrapping: false };
Important
This script must run before the main CDN file is executed.
Overriding Entities
To override workflow element entities, use token names and create provider constants:
For class entities:
const SOME_CLASS_PROVIDER = { provide: "SOME_TOKEN", useClass: YourImplementedClass };
For string, function, object, etc.:
const SOME_VALUE_PROVIDER = { provide: "SOME_TOKEN", useValue: YOUR_VALUE };
Then, load the module with these providers:
window.addEventListener("DOMContentLoaded", async () => {
await window.AurigmaWebComponents.handyEditor.bootstrap([SOME_CLASS_PROVIDER, SOME_VALUE_PROVIDER]);
// For 3D viewer:
// await window.AurigmaWebComponents.handyEditor3d.bootstrap([...]);
});
External Implementations
External implementations allow you to extend the editor's functionality or integrate with external systems. Implement your JS class based on the provided API:
class CustomService {
constructor() {
const inject = AurigmaWebComponents.handyEditor.inject;
this.store = inject("STORE_TOKEN");
this.store.subscribe(state => console.warn(state));
}
}
Then, load the module with this class:
window.addEventListener("DOMContentLoaded", async () => {
await window.AurigmaWebComponents.handyEditor.bootstrap([], [CustomService]);
});
Note
You can implement as many classes as needed and pass them as an array in the second argument of the bootstrap
function.
To access your class instance, use the injector
property of the web component.
API
NGRX Store
Token: "STORE_TOKEN"
Description: NGRX storage for most application data.
Important
Avoid subscribing directly to the Store! Even though it provides full access to the editor's state, the Store's internal structure may change over time. Instead, use the select
method with the NGRX selectors listed in this article to safely retrieve data.
Example:
class CustomService {
constructor() {
const inject = AurigmaWebComponents.handyEditor.inject;
this.store = inject("STORE_TOKEN");
this.store.subscribe(state => console.warn(state));
}
}
NGRX Utilities
Token: "NGRX_UTILS_TOKEN"
Description: NGRX functions for use in JS.
Available functions:
select
: The RXJS operator to extract data using selectors. For more details, refer to the documentation.ofType
: The RXJS operator to filter events by type. For more details, refer to the documentation.createEffect
: The RXJS function that accepts an observable object returned by NGRX action and create effects from actions. For more details, refer to the documentation.
Example:
class CustomAnalyticsService {
constructor() {
const inject = AurigmaWebComponents.handyEditor.inject;
const { select, ofType, createEffect } = inject("NGRX_UTILS_TOKEN");
const { selectCurrentVariantId } = inject("SELECTORS_TOKEN");
const actions$ = inject("ACTIONS_SERVICE_TOKEN");
this.store = inject("STORE_TOKEN");
this.store.pipe(select(selectCurrentVariantId))
.subscribe(variantId => {
console.log(`Current variant ID: ${variantId}`);
this.sendAnalyticsEvent("variant_changed", { variantId });
});
const addImageEffect = createEffect(() =>
actions$.pipe(
ofType("ADD_IMAGE_ACTION"),
tap(action => {
console.log("Image added:", action.payload);
this.sendAnalyticsEvent("image_added", action.payload);
})
)
);
}
sendAnalyticsEvent(eventName, data) {
console.log(`[Analytics] Event: ${eventName}`, data);
}
}
NGRX Selectors
Token: "SELECTORS_TOKEN"
Description: Functions to extract values from NGRX storage.
Available selectors:
Selector | Description |
---|---|
selectCurrentVariantId |
Extracts the ID of the current variant from storage. |
selectCurrentVariant |
Extracts information about the current variant from storage. |
selectProductId |
Extracts the ID of the current PIM product from storage. |
selectProductVersionId |
Extracts the version ID of the current PIM product from storage. |
selectCurrentSurface |
Extracts the descriptor (SurfaceDescription ) of the current design surface from storage. |
selectSurfaces |
Extracts descriptors (SurfaceDescription ) of all surfaces in the current design from storage. |
selectCountOfSurfaces |
Extracts the number of surfaces (pages) in the current design from storage. |
selectNextStepIsAvailable |
Extracts a value indicating whether the transition to the next step is available. |
Example:
class CustomService {
constructor() {
const inject = AurigmaWebComponents.handyEditor.inject;
const { selectCurrentVariantId } = inject("SELECTORS_TOKEN");
const { select } = inject("NGRX_UTILS_TOKEN");
this.store = inject("STORE_TOKEN");
this.store.pipe(select(selectCurrentVariantId))
.subscribe(id => console.warn(id));
}
}
NGRX Actions Service
Token: "ACTIONS_SERVICE_TOKEN"
Description: Provides an observable Actions
object to track NGRX events.
Example:
class ActionLoggerService {
constructor() {
const inject = AurigmaWebComponents.handyEditor.inject;
const actions$ = inject("ACTIONS_SERVICE_TOKEN");
actions$.subscribe(action => {
console.log(`[Action] ${action.type}`, action.payload);
});
}
}
NGRX Actions
Token: "ACTIONS_TOKEN"
Description: Functions to dispatch actions.
Available actions:
Action | Description |
---|---|
openPanelAction(payload) |
Opens the assets panel. |
openOverlaySpinnerAction(payload) |
Opens the loading spinner overlay. |
closeOverlaySpinnerAction() |
Closes the loading spinner overlay. |
deletePrivateImageAction() |
Deletes an image from private storage. |
deletePrivateImageSuccessAction() |
Notifies about the successful deletion of a file from private storage. |
openFileDialogForUploadAction() |
Opens a file dialog for selecting a file to upload. |
setUploadedImagesAction() |
Notifies that the user has selected files for upload to private storage. |
successUploadImageAction() |
Notifies about the successful upload of a file to private storage. |
setPrivateImagesAction() |
Notifies about receiving private images from the server. |
addToCanvasPrivateImageSuccessAction() |
Notifies about the successful addition of an image from private storage to the canvas. |
addToCanvasPrivateImageAction() |
Adds an image from private storage to the canvas. |
nextStepIsAvailableActions() |
Sets the state of the "Next Step" button (enabled/disabled). |
viewer3dLoadedAction() |
Notifies about the successful initialization of the 3D Viewer (for Handy Editor 3D only). |
Note
Actions can be listened to for side effects or dispatched for processing.
Example:
class CustomWorkflowService {
constructor() {
const inject = AurigmaWebComponents.handyEditor.inject;
const actions = inject("ACTIONS_TOKEN");
const openAssetsPanel = () => {
actions.openPanelAction({ panelType: "ASSETS" });
};
const uploadImageWithSpinner = async (file) => {
actions.openOverlaySpinnerAction({ message: "Uploading image..." });
try {
await this.uploadImage(file);
actions.closeOverlaySpinnerAction();
} catch (error) {
actions.closeOverlaySpinnerAction();
console.error("Upload failed:", error);
}
};
}
async uploadImage(file) {
return new Promise(resolve => setTimeout(resolve, 2000));
}
}
Additional API Tokens
Token | Description |
---|---|
"INJECTOR_TOKEN" |
Angular Injector |
"HOTKEYS_TOKEN" |
Hotkeys service |
"PRODUCT_INDEXER_TOKEN" |
Product indexer for getting real Design Atoms Model entities using descriptor identifiers from the NgRx storage |
"VIEWER_TOKEN" |
Design Atoms Viewer instance of the application |
"EVENT_MANAGER_TOKEN" |
Design Atoms EventManager instance listening to Design Atoms events |
"COMMAND_MANAGER_TOKEN" |
Design Atoms CommandManager instance executing commands on actions |
"COMMAND_MANAGER_COMMANDS_TOKEN" |
Enumerations for CommandManager commands |
"ITEMS_DATA_APPLIER_TOKEN" |
Design Atoms ItemsDataApplier updating the data of the Design Atoms Model elements |
"HISTORY_TOKEN" |
Design Atoms History interacting and managing the editor's history |
"DESIGN_ATOMS_ITEMS_TOKEN" |
Classes for Design Atoms Model items (e.g., RectangleItem , BarcodeItem ) |
"DESIGN_ATOMS_COLORS_TOKEN" |
Classes for Design Atoms Model colors (e.g., CmykColor , RgbColor ) |
"DESIGN_ATOMS_MATH_TOKEN" |
Classes for Design Atoms math utilities (e.g., RectangleF , PointF ) |
"RXJS_OPERATORS_TOKEN" |
RxJS operators (e.g., map , filter , debounceTime ) |
Command Manager Commands
Design Atoms Model Items
- Item
- BarcodeItem
- EllipseItem
- RectangleItem
- ShapeItem
- ClipartItem
- ImageItem
- GroupItem
- PlainTextItem
- BoundedTextItem
Example:
class CustomService {
constructor() {
const inject = AurigmaWebComponents.handyEditor.inject;
this._viewer = inject("VIEWER_TOKEN");
const { RectangleItem, BarcodeItem } = inject("DESIGN_ATOMS_ITEMS_TOKEN");
const item = this._viewer.seletedItems.at(0)
if (item instanceof RectangleItem) {
console.warn("Selected RectangleItem")
}
}
}
Dsign Atoms Model Colors
Example:
class CustomService {
constructor() {
const inject = AurigmaWebComponents.handyEditor.inject;
this._viewer = inject("VIEWER_TOKEN");
const { CmykColor, RgbColor} = inject("DESIGN_ATOMS_COLORS_TOKEN");
const fillColor= this._viewer.seletedItems.at(0)?.fillColor;
if (fillColor instanceof CmykColor) {
console.warn("fillColor is CmykColor")
}
if (fillColor instanceof RgbColor) {
console.warn("fillColor is RgbColor")
}
}
}
Dsign Atoms Model Math
RxJs Operators
- map
- filter
- tap
- debounceTime
- first
- skip
- switchMap
- throttleTime
- merge
- forkJoin
- fromEvent
- from
- iif
- of
- catchError
Example:
class CustomService {
constructor() {
const inject = AurigmaWebComponents.handyEditor.inject;
const { map, fromEvent } = inject("RXJS_OPERATORS_TOKEN");
fromEvent(document, "click")
.pipe(map(e => ({ x: e.pageX, y e.pageY }))
.subsribe((point) => console.warn("Click to point", point));
}
}
Client Use Cases
Custom Analytics Tracking
Task: Track user interactions (e.g., design changes, panel openings).
Solution: Use ACTIONS_SERVICE_TOKEN
to log events.
class AnalyticsService {
constructor() {
const inject = AurigmaWebComponents.handyEditor.inject;
const actions$ = inject("ACTIONS_SERVICE_TOKEN");
actions$.subscribe(action => {
this.sendToAnalytics(action.type, action.payload);
});
}
sendToAnalytics(eventName, data) {
console.log(`[Analytics] ${eventName}`, data);
}
}
Automation: Apply a Template to All Pages
Task: Automatically apply a design template to every surface.
Solution: Use CommandManager
and selectSurfaces
.
class TemplateApplierService {
constructor() {
const inject = AurigmaWebComponents.handyEditor.inject;
const { selectSurfaces } = inject("SELECTORS_TOKEN");
const commandManager = inject("COMMAND_MANAGER_TOKEN");
this.store = inject("STORE_TOKEN");
this.store.pipe(select(selectSurfaces)).subscribe(surfaces => {
surfaces.forEach(surface => {
commandManager.execute({
type: "APPLY_TEMPLATE",
surfaceId: surface.id,
templateId: "your_template_id",
});
});
});
}
}