@3dsource/metabox-modular-configurator-api
    Preparing search index...

    @3dsource/metabox-modular-configurator-api

    Metabox Modular Configurator API

    TypeScript/JavaScript API for integrating Metabox Modular Configurator into web applications. Provides programmatic control over modular product configurations, component assembly, materials, environments, and export functionality.

    • Framework-agnostic integration (Angular, React, Vue, vanilla JS)
    • Modular component assembly and configuration
    • Material and environment management per component
    • Real-time configuration state synchronization
    • Screenshot and PDF export capabilities
    • E-commerce CTA workflow support
    • Showcase/animation playback control
    • Full TypeScript type definitions
    • CDN support for rapid prototyping
    • Event-driven architecture for reactive UI

    Runtime Requirements:

    • Modern browser with ES6 module support (Chrome 61+, Firefox 60+, Safari 11+, Edge 79+)
    • HTTPS connection (required by Unreal Engine streaming)
    • Valid Metabox modular configurator ID

    Development Environment:

    • Node.js >= 20 (for NPM installation)
    • npm > 9

    Important Notes:

    • integrateMetabox() validates inputs at runtime
    • Requires non-empty configuratorId and containerId
    • Iframe URL format: https://{domain}/metabox-configurator/modular/{configuratorId}
    • Optional query parameters: introImage, introVideo, loadingImage
    • HTTPS is strictly enforced (HTTP URLs will be rejected)
    • Throws error if container element not found in DOM

    Installation:

    npm install @3dsource/metabox-modular-configurator-api@latest --save
    

    Import:

    import { integrateMetabox, Communicator, ModularConfiguratorEnvelope, SetComponent, SetComponentMaterial, SetEnvironment, GetScreenshot, saveImage } from '@3dsource/metabox-modular-configurator-api';
    

    jsDelivr:

    import { integrateMetabox, SetComponent, SetEnvironment, GetScreenshot, saveImage } from 'https://cdn.jsdelivr.net/npm/@3dsource/metabox-modular-configurator-api@latest/+esm';
    

    Note: For production, pin a specific version instead of using @latest

    Ensure your HTML page has the following structure, where the Metabox Configurator will be integrated. You must provide CSS for the #embed3DSource element to make the configurator responsive and fit your layout needs. Create a container element where the configurator will be embedded:

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Metabox Configurator</title>
    <style>
    #embed3DSource {
    width: 100%;
    height: 600px;
    border: 1px solid #ccc;
    border-radius: 8px;
    }
    </style>
    </head>
    <body>
    <div id="embed3DSource">
    <!-- Configurator iframe will be embedded here -->
    </div>
    </body>
    </html>
    <script type="module">
    import { GetPdf, GetScreenshot, GetCallToActionInformation, integrateMetabox, saveImage, SetComponent, SetComponentMaterial, SetEnvironment, SetEnvironmentMaterial, ShowEmbeddedMenu, ShowOverlayInterface } from 'https://cdn.jsdelivr.net/npm/@3dsource/metabox-modular-configurator-api@latest/+esm';

    // Replace it with your actual configurator ID (not a full URL)
    const configuratorId = 'configurator-id';

    integrateMetabox(configuratorId, 'embed3DSource', (api) => {
    console.log('Configurator API ready!');

    // Listen for configurator data updates
    api.addEventListener('configuratorDataUpdated', (data) => {
    console.log('Configurator data:', data);
    });

    // Listen for screenshot events
    api.addEventListener('screenshotReady', (data) => {
    saveImage(data, 'configurator-screenshot.png');
    });

    // Initial commands example
    api.sendCommandToMetabox(new SetComponent('componentId', 'typeId'));
    api.sendCommandToMetabox(new SetEnvironment('environmentId'));
    api.sendCommandToMetabox(new SetComponentMaterial('componentId', 'slotId', 'materialId'));
    api.sendCommandToMetabox(new SetEnvironmentMaterial('slotId', 'materialId'));
    api.sendCommandToMetabox(new ShowEmbeddedMenu(false));
    api.sendCommandToMetabox(new ShowOverlayInterface(false));
    api.sendCommandToMetabox(new GetPdf());
    api.sendCommandToMetabox(new GetScreenshot('image/png', { x: 1024, y: 1024 }));
    api.sendCommandToMetabox(new GetCallToActionInformation());
    });
    </script>
    import { integrateMetabox, Communicator, ModularConfiguratorEnvelope, SetEnvironment, ShowOverlayInterface, GetScreenshot, saveImage, SetComponent, SetComponentMaterial, type MimeType, ShowEmbeddedMenu } from '@3dsource/metabox-modular-configurator-api';

    class ConfiguratorIntegration {
    private api: Communicator | null = null;
    private readonly configuratorId: string;

    constructor(configuratorId: string) {
    this.configuratorId = configuratorId;
    this.initialize();
    }

    private initialize(): void {
    integrateMetabox(this.configuratorId, 'embed3DSource', (api: Communicator) => {
    this.api = api;
    this.setupEventListeners();
    this.setupInitialState();
    });
    }

    private setupEventListeners(): void {
    if (!this.api) {
    return;
    }

    // Listen for configuration data changes
    this.api.addEventListener('configuratorDataUpdated', (data: ModularConfiguratorEnvelope) => {
    console.log('Configurator data updated:', data);
    this.handleConfiguratorData(data);
    });

    // Handle screenshot events
    this.api.addEventListener('screenshotReady', (imageData: string | null) => {
    saveImage(imageData ?? '', 'configurator-render.png');
    });

    // Handle viewport ready events
    this.api.addEventListener('viewportReady', (isReady: boolean) => {
    console.log('Viewport ready:', isReady);
    });

    // Handle status messages
    this.api.addEventListener('statusMessageChanged', (message: string | null) => {
    console.log('Status message:', message);
    });
    }

    private setupInitialState(): void {
    if (!this.api) return;

    // Configure initial environment and components
    this.api.sendCommandToMetabox(new SetEnvironment('default-environment'));
    this.api.sendCommandToMetabox(new SetComponent('main-product', 'chair'));
    this.api.sendCommandToMetabox(new ShowOverlayInterface(true));
    this.api.sendCommandToMetabox(new ShowEmbeddedMenu(true));
    }

    private handleConfiguratorData(data: ModularConfiguratorEnvelope): void {
    // Access typed data properties according to actual interface
    console.log('Configurator:', data.configurator);
    console.log('Configuration tree:', data.configurationTree);
    console.log('Components by type:', data.componentsByComponentType);
    console.log('Component materials:', data.componentMaterialsIds);
    console.log('Environment ID:', data.environmentId);
    console.log('Environment materials:', data.environmentMaterialsIds);

    // Process configurator data according to your needs
    this.updateUI(data);
    }

    private updateUI(data: ModularConfiguratorEnvelope): void {
    // Update your application UI based on configurator state
    // This method would contain your specific UI update logic
    }

    // Public API methods
    public takeScreenshot(format: MimeType = 'image/png', size?: { x: number; y: number }): void {
    if (this.api) {
    this.api.sendCommandToMetabox(new GetScreenshot(format, size));
    }
    }

    public changeMaterial(componentId: string, slotId: string, materialId: string): void {
    if (this.api) {
    this.api.sendCommandToMetabox(new SetComponentMaterial(componentId, slotId, materialId));
    }
    }

    public changeEnvironment(environmentId: string): void {
    if (this.api) {
    this.api.sendCommandToMetabox(new SetEnvironment(environmentId));
    }
    }

    public destroy(): void {
    // Clean up resources when component is destroyed
    this.api = null;
    }
    }

    // Usage example
    const configurator = new ConfiguratorIntegration('configurator-id');

    // Take a high-resolution screenshot
    configurator.takeScreenshot('image/png', { x: 1920, y: 1080 });

    // Change material
    configurator.changeMaterial('componentId', 'seat-fabric', 'leather-brown');
    Term Description Context
    component Modular product part/assembly (e.g., wheel, bumper, seat) Modular configurator
    componentId Unique component identifier (system-generated UUID) Component management
    componentType Category/classification of component (e.g., "wheel", "bumper") Component organization
    environment 3D scene/room environment for product visualization Visual context
    environmentId Unique environment identifier (system-generated UUID) Environment management
    product Base 3D digital twin referenced by components Component definition
    productId Unique product identifier (system-generated UUID) Product reference
    externalId Custom SKU/identifier for external system integration E-commerce integration
    showcase Camera animation sequence attached to component Component attribute
    slotId Material slot identifier on component/environment Material assignment
    materialId Unique material identifier Material assignment
    configurationTree Hierarchical structure of component assembly State representation

    E-Commerce Configurator (CTA Integration):

    E-Com configurators extend Modular configurators with a call-to-action button.

    Configuration:

    1. CTA Button Label: Custom text (e.g., "Request Quote", "Add to Cart")
    2. Callback URL: HTTP POST endpoint for configuration data

    CTA Workflow:

    User clicks CTAMetabox POSTs config JSONBackend processesReturns { redirectUrl } → User redirected
    

    Backend Response Format:

    {
    "redirectUrl": "https://your.site/checkout-or-thank-you"
    }

    Use Cases:

    • Send lead to CRM with component details
    • Redirect to custom checkout with modular pricing

    Embeds the Metabox Modular Configurator iframe and establishes communication channel.

    Signature:

    function integrateMetabox(configuratorId: string, containerId: string, apiReadyCallback: (api: Communicator) => void, config?: IntegrateMetaboxConfig): void;
    

    Parameters:

    Parameter Type Required Default Description
    configuratorId string - Modular configurator UUID (not full URL)
    containerId string - DOM container element ID
    apiReadyCallback function - Callback invoked when API is ready
    config IntegrateMetaboxConfig {} Additional configuration options

    Config Options (IntegrateMetaboxConfig):

    interface IntegrateMetaboxConfig {
    standalone?: boolean; // Disable built-in Metabox UI
    introImage?: string; // URL for intro image
    introVideo?: string; // URL for intro video
    loadingImage?: string; // URL for loading spinner
    state?: string; // Initial component tree state (rison format)
    domain?: string; // Override domain (HTTPS only)
    }

    Validation & Errors:

    • Throws if configuratorId is empty
    • Throws if a container element not found
    • Throws if URL is not HTTPS
    • Replaces existing iframe with ID embeddedContent

    Example:

    integrateMetabox(
    'modular-config-uuid',
    'my-container',
    (api) => {
    console.log('Modular configurator ready');

    // Work with API here, e.g., listen to events or send commands
    },
    {
    standalone: true,
    loadingImage: 'https://cdn.example.com/loader.gif',
    },
    );

    The API provides comprehensive TypeScript support with the following key interfaces and types:

    The main API interface for sending commands and listening to events.

    class Communicator extends EventDispatcher {
    sendCommandToMetabox<T extends CommandBase>(command: T): void;
    addEventListener<T extends FromMetaBoxApiEvents>(messageType: T, callback: (data: FromMetaboxMessagePayloads[T]) => void): this;
    removeEventListener<T extends FromMetaBoxApiEvents>(messageType: T, callback: (data: FromMetaboxMessagePayloads[T]) => void): this;
    }

    The main data structure contains the complete configurator state.

    interface ModularConfiguratorEnvelope {
    /** Represents the modular configurator api with its components, component types, and environments */
    configurator: InitialModularConfigurator;
    /** The unique tree of components and parent and component type id */
    configurationTree: ComponentWithParent[];
    /** A record mapping all components by current component type */
    componentsByComponentType: Record<string, string>;
    /** A record mapping component material selection */
    componentMaterialsIds: SelectedIds;
    /** The unique environment identifier */
    environmentId: string;
    /** A record mapping environment material selections */
    environmentMaterialsIds: SelectedIds;
    }

    Represents a component in the configurator.

    interface ModularConfiguratorComponent {
    /** The id unique identifier. */
    id: string;
    /** The modular configurator component type identifier. */
    componentType: ModularConfiguratorComponentType;
    /** Display the name for the product. */
    name: string;
    /** Position index of the product. */
    position: number;
    /** The detailed product information. */
    product: Product;
    /** Connectors between connectors and virtual sockets. */
    virtualSocketToConnectorsMap?: ModularConfiguratorVirtualSocketToConnector[];
    /** GraphQL typename for the component. */
    __typename: 'ModularConfiguratorComponent';
    }

    Represents a material that can be applied to components or environments.

    interface Material {
    /** Unique identifier for the material. */
    id: string;
    /** Title or name of the material. */
    title: string;
    /** Unique external id for the material. */
    externalId: string;
    /** List of thumbnails for the material. */
    thumbnailList: Thumbnail[];
    /** GraphQL typename for the material. */
    __typename: 'Material';
    }

    Represents an environment/scene configuration.

    interface Environment {
    /** Unique identifier for the environment. */
    id: string;
    /** Title or name of the environment. */
    title: string;
    /** Flag indicating if UDS is enabled. */
    udsEnabled: boolean;
    /** The north yaw of the sun in the environment. */
    sunNorthYaw: number;
    /** The UDS hour setting for the environment. */
    udsHour: number;
    /** List of thumbnails for the environment (optional). */
    thumbnailList: Thumbnail[];
    /** List of slots for the environment (optional). */
    slots: Slot[];
    /** GraphQL typename for the environment. */
    __typename: 'Environment';
    }

    The full configurator definition containing all available components, environments, and component types.

    interface InitialModularConfigurator {
    /** Unique identifier for the configurator. */
    id: string;
    /** The name of the configurator. */
    name: string;
    /** A list of components for the configurator. */
    components: ModularConfiguratorComponent[];
    /** A list of available environments. */
    environments: ModularConfiguratorEnvironment[];
    /** A list of component types for the configurator. */
    componentTypes: ModularConfiguratorComponentType[];
    /** Indicates if the configurator is active. */
    isActive: boolean;
    /** Indicates if the CTA in ecom configurator is enabled. */
    ctaEnabled: boolean;
    /** A list of connectors showing connections between component types. */
    connectors: ModularConfiguratorConnector[];
    __typename: 'ModularConfigurator';
    }

    Represents a component category/classification (e.g., "wheel", "bumper", "seat").

    interface ModularConfiguratorComponentType {
    /** Display name for the component type. */
    name: string;
    /** Position index of the component type. */
    position: number;
    /** Is root component type. */
    isRoot: boolean;
    /** Is required component type. */
    isRequired: boolean;
    /** Unique identifier. */
    id: string;
    __typename: 'ModularConfiguratorComponentType';
    }

    Base 3D digital twin referenced by each component.

    interface Product {
    /** The title of the product. */
    title: string;
    /** List of thumbnails for the product. */
    thumbnailList: Thumbnail[];
    /** Available material slots for the product. */
    slots: Slot[];
    /** List of meta properties for the product. */
    metaProperties: MetaProperty[];
    /** List of virtual sockets for modular assembly connections. */
    virtualSockets: VirtualSocket[];
    /** Unique identifier for the product. */
    id: string;
    /** External SKU/identifier for ERP/PIM integration. */
    externalId: string;
    /** Showcase animation details (optional). */
    showcase?: Showcase;
    __typename: 'Product';
    }

    A material slot on a component or environment — a customizable surface.

    interface Slot {
    /** Unique identifier for the slot. */
    id: string;
    /** Display label for the slot. */
    label: string;
    /** Available materials for this slot. */
    enabledMaterials: Material[];
    __typename: 'Slot';
    }

    Environment wrapper containing position and label for environment selector UIs.

    interface ModularConfiguratorEnvironment {
    /** Unique identifier for the environment. */
    environmentId: string;
    /** Position index of the environment. */
    position: number;
    /** Display label for the environment. */
    label: string;
    /** Detailed environment configuration. */
    environment: Environment;
    __typename: 'ModularConfiguratorEnvironment';
    }

    Represents a node in the component assembly tree hierarchy.

    interface ComponentWithParent {
    id: string;
    componentTypeId: string;
    parentId: string | null;
    }

    Record type tracking currently selected material/component IDs.

    type SelectedIds = Record<string, Record<string, string>>;
    

    Showcase animation playback status.

    type ShowCaseStatus = 'init' | 'play' | 'pause' | 'stop';
    

    Defines connection rules between component types for modular assembly.

    interface ModularConfiguratorConnector {
    id: string;
    componentTypeId1: string;
    componentTypeId2: string;
    __typename: 'ModularConfiguratorConnector';
    }

    Supported image formats for screenshots.

    type MimeType = 'image/png' | 'image/jpeg' | 'image/webp';
    

    Commands control configurator behavior. Send via api.sendCommandToMetabox(new CommandName(...)).

    Command Parameters Description
    SetComponent componentId: string, typeId: string Add/select component in assembly
    SetComponentMaterial componentId: string, slotId: string, materialId: string Apply material to component slot
    SetEnvironment environmentId: string Change environment/scene
    SetEnvironmentMaterial slotId: string, materialId: string Apply material to environment slot

    Component Assembly Example:

    // Set root component (e.g., vehicle chassis)
    api.sendCommandToMetabox(new SetComponent('chassis-001', 'base'));

    // Add child component (e.g., wheel to chassis)
    api.sendCommandToMetabox(new SetComponent('wheel-fr', 'wheel'));

    // Apply materials
    api.sendCommandToMetabox(new SetComponentMaterial('chassis-001', 'body', 'metallic-blue'));
    api.sendCommandToMetabox(new SetComponentMaterial('wheel-fr', 'rim', 'chrome'));
    • Note: ShowEmbeddedMenu and ShowOverlayInterface commands are ineffective in standalone mode.
    Command Parameters Description
    ShowEmbeddedMenu visible: boolean Show/hide right sidebar menu
    ShowOverlayInterface visible: boolean Show/hide viewport controls
    Command Parameters Description
    SetCamera camera: CameraCommandPayload Set camera position, rotation, fov and restrictions
    GetCamera None Request current camera; returns via getCameraResult event
    ResetCamera None Reset camera to default position
    ApplyZoom zoom: number Apply zoom delta (positive=in, negative=out)

    Example:

    api.sendCommandToMetabox(new ApplyZoom(50)); // Zoom in
    api.sendCommandToMetabox(new ApplyZoom(-25)); // Zoom out
    api.sendCommandToMetabox(new ResetCamera()); // Reset view
    api.sendCommandToMetabox(
    new SetCamera({
    fov: 45,
    mode: 'orbit',
    position: { x: 100, y: 100, z: 100 },
    rotation: { horizontal: 0, vertical: 0 },
    restrictions: {
    maxDistanceToPivot: 0,
    maxFov: 0,
    maxHorizontalRotation: 0,
    maxVerticalRotation: 0,
    minDistanceToPivot: 0,
    minFov: 0,
    minHorizontalRotation: 0,
    minVerticalRotation: 0,
    },
    }),
    );

    // Request current camera state
    api.sendCommandToMetabox(new GetCamera());

    // Listen for the camera data
    api.addEventListener('getCameraResult', (camera) => {
    console.log('Current camera:', camera);
    // camera is of type CameraCommandPayload
    });

    The payload used by the SetCamera command to configure the camera.

    interface CameraCommandPayload {
    fov?: number;
    mode?: 'fps' | 'orbit';
    position?: { x: number; y: number; z: number };
    rotation?: {
    horizontal: number;
    vertical: number;
    };
    restrictions?: {
    maxDistanceToPivot?: number;
    maxFov?: number;
    maxHorizontalRotation?: number;
    maxVerticalRotation?: number;
    minDistanceToPivot?: number;
    minFov?: number;
    minHorizontalRotation?: number;
    minVerticalRotation?: number;
    };
    }
    • fov (number): Field of view in degrees.
    • mode ('fps' | 'orbit'):
      • fps: First‑person style camera, moves freely in space.
      • orbit: Orbiting camera around a pivot (typical product viewer behavior).
    • position ({ x, y, z }): Camera position in world coordinates.
    • rotation ({ horizontal, vertical }): Camera rotation angles in degrees.
      • horizontal: Yaw (left/right).
      • vertical: Pitch (up/down).
    • restrictions (optional): Limits applied by the viewer to constrain user/camera movement.
      • Distance limits (orbit mode): minDistanceToPivot, maxDistanceToPivot.
      • Field‑of‑view limits: minFov, maxFov.
      • Rotation limits: minHorizontalRotation, maxHorizontalRotation, minVerticalRotation, maxVerticalRotation.

    Notes:

    • Provide only the limits you want to enforce; unspecified values are left as currently configured by the viewer.
    • Rotation units are degrees; positive/negative values follow the viewer's right‑handed coordinate system.
    • In orbit mode, distance limits are interpreted relative to the orbit pivot.
    • ResetCamera restores the default position/rotation/FOV defined by the current product or environment template.
    Command Parameters Description
    InitShowcase None Initialize showcase for current component
    PlayShowcase None Start/resume animation playback
    PauseShowcase None Pause animation
    StopShowcase None Stop and reset animation
    Command Parameters Description
    ShowMeasurement None Display component dimensions
    HideMeasurement None Hide dimension overlay
    Command Parameters Description
    ResumeStream None Resume pixel streaming session after pause or disconnection

    Example:

    // Resume the stream after an idle timeout or network interruption
    api.sendCommandToMetabox(new ResumeStream());
    Command Parameters Description Event Triggered
    GetScreenshot format: MimeType, size?: {x: number, y: number} Render screenshot screenshotReady
    GetPdf None Generate PDF export Server-side (no event)
    GetCallToActionInformation None Trigger CTA workflow Backend redirect

    Helper function: triggers a browser download of an image from a data URL or blob URL.

    Signature:

    function saveImage(imageUrl: string, filename: string): void;
    
    Parameter Type Description
    imageUrl string Base64 data URL or blob URL to save
    filename string Suggested filename for the download

    Screenshot Example:

    api.sendCommandToMetabox(new GetScreenshot('image/png', { x: 2048, y: 2048 }));

    api.addEventListener('screenshotReady', (imageData) => {
    saveImage(imageData, 'assembly.png');
    });

    Event-driven architecture for reactive state management. Register listeners via api.addEventListener(eventName, handler).

    Event Payload Type Description Use Case
    configuratorDataUpdated ModularConfiguratorEnvelope Configuration state changed Sync UI with component tree/materials
    ecomConfiguratorDataUpdated EcomConfigurator CTA configuration loaded Display CTA button with label
    viewportReady boolean 3D viewport ready state Hide loading, enable interactions
    showcaseStatusChanged ShowCaseStatus Animation playback status Update play/pause button state
    statusMessageChanged string | null Loading/progress message Display user feedback
    screenshotReady string | null Base64 screenshot data Download or display image
    getCameraResult CameraCommandPayload Current camera data returned Capture/store current camera
    videoResolutionChanged {width: number|null, height: number|null} Stream resolution changed Adjust viewport layout

    Configuration State Sync:

    api.addEventListener('configuratorDataUpdated', (data: ModularConfiguratorEnvelope) => {
    console.log('Environment:', data.environmentId);
    console.log('Component tree:', data.configurationTree);
    console.log('Components by type:', data.componentsByComponentType);
    console.log('Component materials:', data.componentMaterialsIds);

    // Update custom UI
    updateComponentTree(data.configurationTree);
    updateMaterialSelectors(data.componentMaterialsIds);
    });

    Screenshot Handling:

    api.addEventListener('screenshotReady', (imageData: string | null) => {
    if (!imageData) {
    console.error('Screenshot failed');
    return;
    }

    saveImage(imageData, `assembly-${Date.now()}.png`);
    });

    Loading State:

    api.addEventListener('viewportReady', (isReady: boolean) => {
    if (isReady) {
    hideLoadingSpinner();
    enableComponentSelectors();
    }
    });

    api.addEventListener('statusMessageChanged', (message: string | null) => {
    document.getElementById('status-text').textContent = message || '';
    });

    Showcase Control:

    api.addEventListener('showcaseStatusChanged', (status: ShowCaseStatus) => {
    const playButton = document.getElementById('play-btn');

    switch (status) {
    case 'play':
    playButton.textContent = 'Pause';
    break;
    case 'pause':
    case 'stop':
    playButton.textContent = 'Play';
    break;
    }
    });

    Downloads base64-encoded image to user's device.

    Parameters:

    Parameter Type Description
    imageUrl string Base64 data URL (e.g., data:image/png;base64,...)
    filename string Desired filename with extension

    Example:

    api.addEventListener('screenshotReady', (imageData: string | null) => {
    if (imageData) {
    saveImage(imageData, `config-${Date.now()}.png`);
    }
    });

    Implementation Note: Creates a temporary anchor element with download attribute to trigger browser download.

    RxJS wrapper for Communicator events. Works like RxJS fromEvent — creates a typed Observable that emits each time the specified event fires. Alternative to api.addEventListener().

    Parameters:

    Parameter Type Description
    target Communicator The Communicator instance to listen on
    eventName string Event name (e.g., configuratorDataUpdated)

    Returns: Observable<T> — typed Observable matching the event payload.

    Example:

    import { fromCommunicatorEvent } from '@3dsource/metabox-modular-configurator-api';

    integrateMetabox('configurator-id', 'embed3DSource', (api) => {
    fromCommunicatorEvent(api, 'configuratorDataUpdated').subscribe((data) => {
    console.log('Components:', data.componentsByComponentType);
    });

    fromCommunicatorEvent(api, 'screenshotReady').subscribe((imageData) => {
    if (imageData) saveImage(imageData, 'screenshot.png');
    });
    });

    Headless rendering mode for custom UI implementations. Disables all built-in Metabox UI elements. Pass { standalone: true } as the config option to integrateMetabox().

    Use When:

    • Building fully custom modular configurator interface
    • Implementing brand-specific component selection UX
    • Integrating into existing design systems
    • Requiring complete control over component assembly flow

    Don't Use When:

    • Quick prototyping with default UI is sufficient
    • Minimal customization needed
    • Limited development resources
    Feature Default Mode Standalone Mode
    Right sidebar menu ✅ Visible ❌ Hidden
    Viewport overlays ✅ Visible ❌ Hidden
    Template logic ✅ Active ❌ Disabled
    API control ⚠️ Partial ✅ Full
    Event system ✅ Available ✅ Available
    Component tree management ⚠️ Shared ✅ Full control

    TypeScript:

    import { integrateMetabox, Communicator } from '@3dsource/metabox-modular-configurator-api';

    integrateMetabox(
    'modular-config-id',
    'embed3DSource',
    (api: Communicator) => {
    // Work with API here
    },
    { standalone: true }, // <- Enable standalone mode to disable default UI
    );

    JavaScript (CDN):

    <script type="module">
    import { integrateMetabox } from 'https://cdn.jsdelivr.net/npm/@3dsource/metabox-modular-configurator-api@latest/+esm';

    integrateMetabox(
    'config-id',
    'container-id',
    (api) => {
    // Work with API here
    },
    { standalone: true }, // <- Enable standalone mode to disable default UI
    );
    </script>

    To create a custom modular configurator interface, use standalone mode and subscribe to events before sending commands.

    Important: The components menu approach below is used only for building a custom components menu. For building a custom environments menu, iterate configurator.environments directly and use environmentMaterialsIds to show active materials by slots.

    Core Pattern:

    import { integrateMetabox, Communicator, ModularConfiguratorEnvelope } from '@3dsource/metabox-modular-configurator-api';

    integrateMetabox(
    'configurator-uuid',
    'containerId',
    (api: Communicator) => {
    // 1. Subscribe to events BEFORE sending commands
    api.addEventListener('configuratorDataUpdated', (data: ModularConfiguratorEnvelope) => {
    // data.configurator.componentTypes — available component types
    // data.configurator.connectors — available connectors
    // data.configurator.environments — scene list
    // data.configurationTree — ordered tree of active components
    });
    api.addEventListener('viewportReady', (ready) => {
    /* gate UI on this */
    });

    // 2. Send commands via api.sendCommandToMetabox(new CommandClass(...args))
    },
    { standalone: true },
    );

    The menu must follow the configurationTree order — NOT iterate componentTypes or components as separate flat lists.

    Data Sources from ModularConfiguratorEnvelope:

    Field Type Purpose
    configurationTree ComponentWithParent[] Ordered tree of active component nodes (id, componentTypeId, parentId)
    componentsByComponentType Record<string, string> Maps componentTypeId → active componentId
    componentMaterialsIds SelectedIds (Record<string, Record<string, string>>) Maps componentId{ slotId: materialId }
    configurator InitialModularConfigurator Catalog: all componentTypes, components, environments, connectors

    Algorithm:

    Iterate configurationTree nodes. For each node:

    1. Find the component typeconfigurator.componentTypes.find(t => t.id === node.componentTypeId)
    2. Render a component selector — show all components of that type (from configurator.components filtered by componentType.id). The active component is componentsByComponentType[typeId].
    3. Render material slots — for the active component, show its product.slots (filtered to those with enabledMaterials.length > 0). The selected material for each slot comes from componentMaterialsIds[componentId][slotId].

    This produces a single ordered list interleaved as: Type1 selector → Type1 material slots → Type2 selector → Type2 material slots → …

    Example (Angular Signals):

    import { computed, signal } from '@angular/core';
    import type { Communicator, ModularConfiguratorEnvelope, ModularConfiguratorComponent, Slot } from '@3dsource/metabox-modular-configurator-api';
    import { fromCommunicatorEvent, SetComponent, SetComponentMaterial } from '@3dsource/metabox-modular-configurator-api';

    // Store envelope data as a signal (updated on each configuratorDataUpdated event)
    const envelope = signal<ModularConfiguratorEnvelope | null>(null);

    // Subscribe to API events
    function listenApi(api: Communicator) {
    fromCommunicatorEvent(api, 'configuratorDataUpdated').subscribe((data) => {
    envelope.set(data);
    });
    }

    // Build menu tree as a computed signal following configurationTree order
    const menuTree = computed(() => {
    const data = envelope();
    if (!data) return [];

    const { configurator, configurationTree, componentsByComponentType, componentMaterialsIds } = data;
    const menuItems: MenuEntry[] = [];

    for (const node of configurationTree) {
    const ct = configurator.componentTypes.find((t) => t.id === node.componentTypeId);
    if (!ct) continue;

    const components = configurator.components.filter((c) => c.componentType.id === ct.id).sort((a, b) => a.position - b.position);
    const activeComponentId = componentsByComponentType[ct.id];
    const activeComponent = components.find((c) => c.id === activeComponentId);

    // 1. Component type selector
    menuItems.push({
    id: ct.id,
    category: ct.name,
    options: components.map((item) => ({
    label: item.name,
    id: item.id,
    image: item.product.thumbnailList[0]?.urlPath,
    action: () => api.sendCommandToMetabox(new SetComponent(item.id, ct.id)),
    })),
    selectedId: activeComponentId,
    selectedLabel: activeComponent?.name,
    });

    // 2. Material slots for the active component
    if (activeComponent) {
    for (const slot of activeComponent.product.slots) {
    if (slot.enabledMaterials.length === 0) continue;
    const selectedMaterialId = componentMaterialsIds?.[activeComponent.id]?.[slot.id];

    menuItems.push({
    id: `${activeComponent.id}:${slot.id}`,
    category: slot.label,
    options: slot.enabledMaterials.map((mat) => ({
    label: mat.title,
    id: mat.id,
    image: mat.thumbnailList[0]?.urlPath,
    action: () => api.sendCommandToMetabox(new SetComponentMaterial(activeComponent.id, slot.id, mat.id)),
    })),
    selectedId: selectedMaterialId,
    selectedLabel: slot.enabledMaterials.find((m) => m.id === selectedMaterialId)?.title,
    });
    }
    }
    }

    return menuItems;
    });

    Commands for Selection:

    Action Command Args
    Select component SetComponent(id, typeId) id: component ID, typeId: component type ID
    Select material SetComponentMaterial(componentId, slotId, materialId) all three IDs required

    Anti-Patterns:

    • DON'T build two separate lists (component types + material slots) — this loses tree ordering
    • DON'T iterate configurator.componentTypes directly — use configurationTree to determine which types are active and their order
    • DON'T show all component types — only those present as keys in componentsByComponentType (i.e., matching configurationTree nodes)
    api.sendCommandToMetabox(new GetScreenshot('image/png', { x: 1920, y: 1080 }));
    api.sendCommandToMetabox(new GetPdf());
    api.sendCommandToMetabox(new GetCallToActionInformation());

    Symptoms: Blank screen, iframe not appearing, loading indefinitely

    Root Causes & Solutions:

    Issue Diagnostic Solution
    Invalid configurator ID Browser console shows 404 errors Verify modular configurator ID in Metabox admin
    Container not found Error: Container element not found Ensure element exists before calling integrateMetabox()
    HTTP instead of HTTPS Mixed content warnings Use HTTPS or test on localhost
    Container has no dimensions Invisible iframe (0x0) Set explicit width/height on container element
    CORS/iframe blocking X-Frame-Options errors Check domain allowlist in Metabox settings

    Diagnostic Code:

    const containerId = 'embed3DSource';
    const container = document.getElementById(containerId);

    if (!container) {
    throw new Error(`Container #${containerId} not found`);
    }

    // Verify container has dimensions
    const rect = container.getBoundingClientRect();
    if (rect.width === 0 || rect.height === 0) {
    console.warn('⚠️ Container has no dimensions');
    }

    if (location.protocol !== 'https:' && location.hostname !== 'localhost') {
    console.warn('⚠️ HTTPS required for Unreal Engine streaming');
    }

    integrateMetabox('config-id', containerId, (api) => {
    console.log('✅ Modular configurator loaded');
    });

    Symptoms: Commands sent but no visual changes in configurator

    Common Mistakes:

    Mistake Problem Solution
    Sending before API ready Commands ignored Only send in apiReadyCallback
    Wrong component IDs Component not found Verify IDs from configuratorDataUpdated event
    Type mismatch Component rejected Ensure typeId matches available types

    Correct Pattern:

    integrateMetabox('config-id', 'container', (api) => {
    // ✅ Correct: Commands sent after API ready

    // First: Set root component
    api.sendCommandToMetabox(new SetComponent('comp-1', 'chassis'));

    // Then: Add child components
    api.sendCommandToMetabox(new SetComponent('comp-2', 'wheel'));

    // Finally: Apply materials
    api.sendCommandToMetabox(new SetComponentMaterial('comp-1', 'body', 'blue'));
    });

    // ❌ Wrong: Command sent too early
    api.sendCommandToMetabox(new SetComponent('comp-1', 'chassis'));

    If you're still experiencing issues:

    1. Check the browser console for error messages
    2. Verify your configurator ID in a separate browser tab
    3. Test with a minimal example to isolate the issue
    4. Contact support with: browser version, configurator URL, console errors, and steps to reproduce
    • Debounce rapid commands. Avoid sending too many commands in quick succession — debounce material or component changes (e.g., 300 ms).
    • Responsive container. Set the configurator container to responsive dimensions (width: 100%; height: 100%;) and adjust with media queries for mobile.
    • Events work in all modes. configuratorDataUpdated, screenshotReady, and other events fire in both default and standalone mode — use them to keep your UI synchronized.
    • Wait for API ready. Only send commands inside the apiReadyCallback. Commands sent before initialization are silently ignored.
    • Validate IDs from events. Use the configuratorDataUpdated event payload to discover valid component, material, and slot IDs rather than hardcoding them.
    • HTTPS is required. The iframe URL enforces HTTPS. HTTP URLs will be rejected. Use localhost for local development.
    • Pin CDN versions in production. Replace @latest with a specific version to avoid unexpected breaking changes.

    For more examples and advanced usage, visit our documentation site.