# Export / import demo with Fabric 2D and Babylon 3D

The root `index.html` is a complete browser demo for the public persistence API.

It creates:

- `front-2d`, rendered by Fabric;
- `preview-3d`, rendered by Babylon;
- two Fabric text layers;
- one GLB product loaded from `examples/assets/demo-product.glb`;
- two material variations applied to the same placed runtime product;
- persisted 3D camera limits and a saved camera preset.

The demo declares a logical `product` part that targets the complete GLB. Both
material variations use `partId: 'product'`. This is intentional: selecting a
variation edits the already placed product instead of calling the add path again.

The toolbar demonstrates:

- `designer.util.state.exportFull()` / `importFull()`;
- `designer.util.state.exportPart('document')` / `importPart('document', ...)`;
- one strict `asuka-state` schema v2 import path;
- `saveToStorage()` / `loadFromStorage()`;
- JSON file import;
- JSON URL import;
- automatic payload detection.

## Recommended integration flow

```js
const full = designer.util.state.exportFull();
await api.saveProduct(full);

const restored = await api.loadProduct();
designer.util.state.importFull(restored, {
  replaceViews: true,
  replaceSnapshots: true,
});
```

For design-only persistence:

```js
const documentState = designer.util.state.exportPart('document');
await api.saveDesign(documentState);

designer.util.state.importPart('document', await api.loadDesign(), {
  replaceViews: true,
  replaceSnapshots: true,
});
```

The demo intentionally detects `format === 'asuka-state'` before looking at `payload.document`. This prevents a complete full-state export from being mistaken for a document-only wrapper.

## Switching between the demo views

View changes use the documented public API namespace:

```js
designer.api.setActiveView('front-2d');
designer.api.setActiveView('preview-3d');
```

`setActiveView()` is not a root-level method on the designer instance. The demo starts in free mode when `demo-license.local.js` is absent or blank. Premium testing is explicit: create a TEST license in the local Symfony administration, copy `demo-license.local.example.js` to `demo-license.local.js`, and place the generated key in that ignored file.


## Runtime restoration guard

Full/document imports protect the imported store while the old Fabric or Babylon runtime unmounts. The demo waits for the replacement engine to mount before reporting success. Multi-mesh Product 3D catalogs are normalized so their logical parts remain visible in Elements after save/import.
## JSON file input handlers

The demo error wrapper forwards the original DOM event arguments. This is required by the file input handler because it reads `event.target.files`. A wrapper must call `action(...args)`, not `action()` without arguments.


## Template and theme controls

The root demo also includes separate `Template` and `Theme` captures. `Appliquer Linear` changes only the live theme. `Restaurer Theme` restores the captured theme without rebuilding the canvases. `Restaurer Template` restores the mixed 2D/3D template while preserving the current theme, pricing, permissions, integrations and history.

## Startup readiness

The demo does not create or restore content immediately after `createAsukaDesigner()` returns. It waits for the application boot flag, the mounted Fabric/Babylon runtime, and the real engine canvas/API. Product 3D creation also waits until the new product is present both in Babylon and in the canonical `document3dScenesByView` state before switching views or capturing JSON. See `FIX_STARTUP_CANVAS_READY_ELEMENT_ADD.md`.
