@virtualdisplay.io/client 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +69 -0
- package/LICENSE +21 -0
- package/README.md +540 -0
- package/dist/index.d.ts +503 -0
- package/dist/virtualdisplay.client.cjs.js +8 -0
- package/dist/virtualdisplay.client.cjs.js.map +1 -0
- package/dist/virtualdisplay.client.es.js +7436 -0
- package/dist/virtualdisplay.client.es.js.map +1 -0
- package/dist/virtualdisplay.client.umd.js +8 -0
- package/dist/virtualdisplay.client.umd.js.map +1 -0
- package/node_modules/@virtualdisplay-io/logger/CHANGELOG.md +82 -0
- package/node_modules/@virtualdisplay-io/logger/LICENSE +21 -0
- package/node_modules/@virtualdisplay-io/logger/README.md +314 -0
- package/node_modules/@virtualdisplay-io/logger/dist/index.d.ts +167 -0
- package/node_modules/@virtualdisplay-io/logger/dist/virtualdisplay.logger.cjs.js +4 -0
- package/node_modules/@virtualdisplay-io/logger/dist/virtualdisplay.logger.cjs.js.map +1 -0
- package/node_modules/@virtualdisplay-io/logger/dist/virtualdisplay.logger.es.js +377 -0
- package/node_modules/@virtualdisplay-io/logger/dist/virtualdisplay.logger.es.js.map +1 -0
- package/node_modules/@virtualdisplay-io/logger/package.json +79 -0
- package/package.json +118 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
|
+
|
|
5
|
+
## [3.1.0](https://github.com/virtualdisplay-io/client/compare/v3.0.0...v3.1.0) (2025-06-25)
|
|
6
|
+
|
|
7
|
+
### ⚠ BREAKING CHANGES
|
|
8
|
+
|
|
9
|
+
* Complete API redesign with the following changes:
|
|
10
|
+
- Removed isReady getter - use onReady callback instead
|
|
11
|
+
- Removed getQueueStatus method - internal implementation detail
|
|
12
|
+
- Removed toJSON from ModelNode - not needed for public API
|
|
13
|
+
- Complete restructure from core/ to src/ with domain-driven design
|
|
14
|
+
- New attribute-based API is now the only way to interact with 3D models
|
|
15
|
+
- Fire-and-forget messaging pattern for all operations
|
|
16
|
+
- JSON Schema validation for mapping configuration
|
|
17
|
+
|
|
18
|
+
Features:
|
|
19
|
+
- Simplified color configurator demo (300 lines → 160 lines)
|
|
20
|
+
- Bundled @virtualdisplay-io/logger package
|
|
21
|
+
- Domain-driven directory structure
|
|
22
|
+
- Proper error handling with error codes
|
|
23
|
+
- Event-driven architecture with EventBus
|
|
24
|
+
- onChange callbacks for real-time UI synchronization
|
|
25
|
+
- Comprehensive test coverage
|
|
26
|
+
|
|
27
|
+
Documentation:
|
|
28
|
+
- Complete README rewrite focusing on actual functionality
|
|
29
|
+
- Added terminology standards for consistent communication
|
|
30
|
+
- Removed unimplemented accessibility claims
|
|
31
|
+
- Clear separation between simple and configurable products
|
|
32
|
+
|
|
33
|
+
* fix: update lockfile after removing translator dependency
|
|
34
|
+
|
|
35
|
+
* docs: update README for v4.0 release
|
|
36
|
+
|
|
37
|
+
* fix: add GitHub registry configuration for @virtualdisplay-io scope
|
|
38
|
+
|
|
39
|
+
* fix: add npm authentication to bundle-size workflow and package permissions
|
|
40
|
+
|
|
41
|
+
* fix: use GITHUB_TOKEN for npm auth and add trailing slash to registry URL
|
|
42
|
+
|
|
43
|
+
* chore: update lockfile with fresh install
|
|
44
|
+
|
|
45
|
+
* fix: use GH_PAT instead of GITHUB_TOKEN for package authentication
|
|
46
|
+
|
|
47
|
+
- Replace GITHUB_TOKEN with GH_PAT in all workflows to avoid conflicts with GitHub's reserved token
|
|
48
|
+
- Update CI, release, and bundle-size workflows to use new token variable
|
|
49
|
+
- Add .env.act for local testing with act
|
|
50
|
+
- Required because GitHub's automatic GITHUB_TOKEN cannot access private organization packages
|
|
51
|
+
|
|
52
|
+
* fix: remove push trigger from bundle-size workflow
|
|
53
|
+
|
|
54
|
+
- Bundle size analysis should only run on pull requests
|
|
55
|
+
- Prevents duplicate runs and confusion about which branch is being tested
|
|
56
|
+
- Aligns with CI/CD best practices for PR-based workflows
|
|
57
|
+
|
|
58
|
+
### ✨ Features
|
|
59
|
+
|
|
60
|
+
* v4.0.0 - simplified architecture and attribute-only API ([#41](https://github.com/virtualdisplay-io/client/issues/41)) ([dc649c1](https://github.com/virtualdisplay-io/client/commit/dc649c1b863b2427bf6e0350960c4d2e2a2dacb8))
|
|
61
|
+
|
|
62
|
+
### 🐛 Bug Fixes
|
|
63
|
+
|
|
64
|
+
* add missing conventional-changelog-conventionalcommits dependency ([8a15743](https://github.com/virtualdisplay-io/client/commit/8a15743de3984c742ccb5de0537333f321108cd4))
|
|
65
|
+
* use NODE_AUTH_TOKEN for npm authentication ([#43](https://github.com/virtualdisplay-io/client/issues/43)) ([c148c6c](https://github.com/virtualdisplay-io/client/commit/c148c6cba6b6b4d1fc431ca5618ad2c3c26638c2))
|
|
66
|
+
|
|
67
|
+
### 🔧 Maintenance
|
|
68
|
+
|
|
69
|
+
* trigger release workflow to test GH_PAT secret ([9b07140](https://github.com/virtualdisplay-io/client/commit/9b07140e01d72ccc3774f20733afd197f52c4cde))
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Virtualdisplay
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
# Virtualdisplay client
|
|
2
|
+
|
|
3
|
+
TypeScript library for embedding interactive 3D product models in web
|
|
4
|
+
applications.
|
|
5
|
+
|
|
6
|
+
**Key features:**
|
|
7
|
+
|
|
8
|
+
- Simple attribute-based API for product configurators
|
|
9
|
+
- Fire-and-forget messaging with automatic state synchronization
|
|
10
|
+
- Zero external dependencies (all bundled)
|
|
11
|
+
- Works with any frontend framework or vanilla JavaScript
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @virtualdisplay-io/client
|
|
17
|
+
# or
|
|
18
|
+
pnpm add @virtualdisplay-io/client
|
|
19
|
+
# or
|
|
20
|
+
yarn add @virtualdisplay-io/client
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## What's new in v3.1
|
|
24
|
+
|
|
25
|
+
- Simplified message handling with unified event system
|
|
26
|
+
- JSON Schema validation using AJV
|
|
27
|
+
- Cleaner, more maintainable codebase
|
|
28
|
+
- Improved TypeScript types
|
|
29
|
+
- Better error messages with specific error codes
|
|
30
|
+
|
|
31
|
+
## Quick start
|
|
32
|
+
|
|
33
|
+
### Simple product (no options)
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { VirtualdisplayClient } from '@virtualdisplay-io/client';
|
|
37
|
+
|
|
38
|
+
// That's it! Your 3D model is now displayed
|
|
39
|
+
const client = new VirtualdisplayClient({
|
|
40
|
+
parent: '#product-container',
|
|
41
|
+
license: 'your-license-key',
|
|
42
|
+
model: 'statue-model',
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Configurable product (with options)
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { VirtualdisplayClient } from '@virtualdisplay-io/client';
|
|
50
|
+
|
|
51
|
+
// Create client instance
|
|
52
|
+
const client = new VirtualdisplayClient({
|
|
53
|
+
parent: '#product-container',
|
|
54
|
+
license: 'your-license-key',
|
|
55
|
+
model: 'sneaker',
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Map product options to 3D parts
|
|
59
|
+
client.setMapping({
|
|
60
|
+
attributes: [
|
|
61
|
+
{
|
|
62
|
+
name: 'Color',
|
|
63
|
+
values: [
|
|
64
|
+
{ value: 'Red', nodeIds: ['laces_red', 'sole_red'], isSelected: true },
|
|
65
|
+
{ value: 'Blue', nodeIds: ['laces_blue', 'sole_blue'] },
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Control via product options
|
|
72
|
+
client.getAttribute('Color')?.select('Red');
|
|
73
|
+
|
|
74
|
+
// Or store the attribute for multiple operations
|
|
75
|
+
const sizeAttribute = client.getAttribute('Size');
|
|
76
|
+
if (sizeAttribute) {
|
|
77
|
+
sizeAttribute.select('Large');
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## How it works
|
|
82
|
+
|
|
83
|
+
The 3D server hosts the viewer in an iframe, keeping WebGL complexity isolated
|
|
84
|
+
from your application. The client library handles all communication via
|
|
85
|
+
postMessage and manages state locally using an attribute mapping system.
|
|
86
|
+
|
|
87
|
+
```mermaid
|
|
88
|
+
graph LR
|
|
89
|
+
A[Your application] -->|API calls| B[VirtualdisplayClient]
|
|
90
|
+
B -->|postMessage| C[3D server iframe]
|
|
91
|
+
C -->|Events| B
|
|
92
|
+
B -->|State updates| A
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Key principles:**
|
|
96
|
+
|
|
97
|
+
- **Iframe architecture**: 3D server loads the viewer independently
|
|
98
|
+
- **Fire-and-forget**: Send messages without waiting for confirmation
|
|
99
|
+
- **Attribute mapping**: Connect your product options to 3D model parts
|
|
100
|
+
- **State synchronization**: Client and 3D server stay in sync automatically
|
|
101
|
+
- **Domain-driven design**: Clean separation of concerns with simplified
|
|
102
|
+
architecture
|
|
103
|
+
- **Event-driven**: Loosely coupled components communicate via events
|
|
104
|
+
|
|
105
|
+
## Product options
|
|
106
|
+
|
|
107
|
+
### When do you need mapping
|
|
108
|
+
|
|
109
|
+
Before diving in, it's important to understand when you need attribute mapping:
|
|
110
|
+
|
|
111
|
+
**No mapping needed:**
|
|
112
|
+
|
|
113
|
+
- Static 3D models that always look the same
|
|
114
|
+
- Product showcases without options
|
|
115
|
+
- Architectural visualizations
|
|
116
|
+
- Art pieces or sculptures
|
|
117
|
+
- Single-configuration products
|
|
118
|
+
|
|
119
|
+
**Mapping required:**
|
|
120
|
+
|
|
121
|
+
- Products where users can change colors (different textures/materials per option)
|
|
122
|
+
- Products with size options (different 3D meshes per size)
|
|
123
|
+
- Modular products (parts that can be added/removed)
|
|
124
|
+
- Any product where different options show different 3D elements
|
|
125
|
+
|
|
126
|
+
### Attribute mapping
|
|
127
|
+
|
|
128
|
+
The mapping system bridges your business logic with the 3D visualization. It
|
|
129
|
+
serves multiple purposes:
|
|
130
|
+
|
|
131
|
+
**Why mapping is essential:**
|
|
132
|
+
|
|
133
|
+
- The 3D server is generic - it doesn't know your specific product options
|
|
134
|
+
- Your business rules (stock, pricing, combinations) change independently from
|
|
135
|
+
the 3D model
|
|
136
|
+
- You control exactly which parts of the model are shown for each option
|
|
137
|
+
- You can group multiple 3D nodes (meshes, materials) into logical product
|
|
138
|
+
choices
|
|
139
|
+
|
|
140
|
+
**How it works:** The mapping connects your product options to specific parts of
|
|
141
|
+
the 3D model:
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
client.setMapping({
|
|
145
|
+
attributes: [
|
|
146
|
+
{
|
|
147
|
+
name: 'Color',
|
|
148
|
+
values: [
|
|
149
|
+
{
|
|
150
|
+
value: 'Red',
|
|
151
|
+
nodeIds: ['mat_red_sole', 'mat_red_laces', 'mat_red_logo'],
|
|
152
|
+
isSelected: true,
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
value: 'Blue',
|
|
156
|
+
nodeIds: ['mat_blue_sole', 'mat_blue_laces', 'mat_blue_logo'],
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
name: 'Material',
|
|
162
|
+
values: [
|
|
163
|
+
{ value: 'Leather', nodeIds: ['material_leather_upper'] },
|
|
164
|
+
{
|
|
165
|
+
value: 'Canvas',
|
|
166
|
+
nodeIds: ['material_canvas_upper'],
|
|
167
|
+
isSelected: true,
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Structure explained:**
|
|
176
|
+
|
|
177
|
+
- `attribute` - A product feature like Color, Size, or Material
|
|
178
|
+
- `value` - A specific option like Red, Blue, Small, or Large
|
|
179
|
+
- `nodeIds` - The 3D model parts that represent this option
|
|
180
|
+
- `isSelected` - Whether this option is selected by default
|
|
181
|
+
|
|
182
|
+
**Example benefits:**
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
// Group related 3D parts into one logical choice
|
|
186
|
+
{
|
|
187
|
+
value: 'Red',
|
|
188
|
+
nodeIds: [
|
|
189
|
+
'mesh_sole_red', // Red sole mesh
|
|
190
|
+
'material_laces_red', // Red laces material
|
|
191
|
+
'texture_logo_red', // Red logo texture
|
|
192
|
+
'mesh_stitching_red' // Red stitching details
|
|
193
|
+
]
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// The server automatically handles showing Red parts
|
|
197
|
+
// and hiding Blue/Green parts when Red is selected
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Key advantages:**
|
|
201
|
+
|
|
202
|
+
- Full control over what's visible without modifying the 3D model
|
|
203
|
+
- Business logic stays in your application, not in the 3D server
|
|
204
|
+
- Easy to update when products or availability changes
|
|
205
|
+
- Group complex 3D structures into simple user choices
|
|
206
|
+
|
|
207
|
+
**Need help with mapping?**
|
|
208
|
+
|
|
209
|
+
- We help you connect your product catalog to your 3D models
|
|
210
|
+
- Visual mapping tool coming soon to simplify this process
|
|
211
|
+
- Contact <support@virtualdisplay.io> for mapping assistance
|
|
212
|
+
|
|
213
|
+
### Complete flow
|
|
214
|
+
|
|
215
|
+
Here's how the client, mapping system, and 3D server work together:
|
|
216
|
+
|
|
217
|
+
```mermaid
|
|
218
|
+
sequenceDiagram
|
|
219
|
+
participant UI as Your UI
|
|
220
|
+
participant Client as VirtualdisplayClient
|
|
221
|
+
participant AttrService as AttributeService
|
|
222
|
+
participant EventBus as EventBus
|
|
223
|
+
participant MsgHandler as MessageHandler
|
|
224
|
+
participant Server as 3D server
|
|
225
|
+
|
|
226
|
+
Note over Client,Server: 0. Client initialization
|
|
227
|
+
UI->>Client: new VirtualdisplayClient(...)
|
|
228
|
+
Client->>Client: Create iframe
|
|
229
|
+
Client->>MsgHandler: setIframe()
|
|
230
|
+
Server->>MsgHandler: STATE message (all nodes)
|
|
231
|
+
MsgHandler->>EventBus: emit(STATE_MESSAGE)
|
|
232
|
+
EventBus->>AttrService: Update local state
|
|
233
|
+
|
|
234
|
+
Note over UI,Server: 1. Configure mapping (setMapping)
|
|
235
|
+
UI->>Client: setMapping({attributes: [...]})
|
|
236
|
+
Client->>AttrService: loadMapping(config)
|
|
237
|
+
AttrService->>AttrService: Validate with AJV
|
|
238
|
+
AttrService->>AttrService: Create Attribute objects
|
|
239
|
+
AttrService->>AttrService: Generate default mutations<br/>(from isSelected values)
|
|
240
|
+
AttrService->>EventBus: emit(MUTATION_MESSAGE)
|
|
241
|
+
EventBus->>MsgHandler: Send mutations
|
|
242
|
+
MsgHandler->>Server: postMessage(mutations)
|
|
243
|
+
Server->>Server: Apply changes
|
|
244
|
+
Server->>MsgHandler: STATE message (updated)
|
|
245
|
+
MsgHandler->>EventBus: emit(STATE_MESSAGE)
|
|
246
|
+
EventBus->>AttrService: Update local state
|
|
247
|
+
AttrService->>UI: Trigger onChange callbacks
|
|
248
|
+
|
|
249
|
+
Note over UI,Server: 2. User interaction (getAttribute.select)
|
|
250
|
+
UI->>Client: getAttribute('Color')
|
|
251
|
+
Client->>Client: new AttributeSelector('Color')
|
|
252
|
+
UI->>Client: .select('Blue')
|
|
253
|
+
Client->>AttrService: selectAttributeValue('Color', 'Blue')
|
|
254
|
+
AttrService->>AttrService: Generate mutations<br/>(hide current, show new)
|
|
255
|
+
AttrService->>EventBus: emit(MUTATION_MESSAGE)
|
|
256
|
+
EventBus->>MsgHandler: Send mutations
|
|
257
|
+
MsgHandler->>Server: postMessage(mutations)
|
|
258
|
+
Note right of Client: select() returns<br/>immediately
|
|
259
|
+
Server->>Server: Apply changes
|
|
260
|
+
Server->>MsgHandler: STATE message (updated)
|
|
261
|
+
MsgHandler->>EventBus: emit(STATE_MESSAGE)
|
|
262
|
+
EventBus->>AttrService: Update local state
|
|
263
|
+
AttrService->>UI: Trigger onChange callbacks
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**Phase by phase breakdown:**
|
|
267
|
+
|
|
268
|
+
#### Phase 0: Initialization (always happens)
|
|
269
|
+
|
|
270
|
+
- Client creates iframe and loads 3D server
|
|
271
|
+
- Server initializes the 3D model with all nodes visible
|
|
272
|
+
- Server sends complete node state to client
|
|
273
|
+
- Client now knows about all available nodes in the model
|
|
274
|
+
|
|
275
|
+
**Note:** For simple models without options, this is all you need!
|
|
276
|
+
|
|
277
|
+
#### Phase 1: Configuration (only for configurable products)
|
|
278
|
+
|
|
279
|
+
- You call `setMapping()` with your product structure
|
|
280
|
+
- Client stores the attribute mapping locally
|
|
281
|
+
- Client sends mutations for all `isSelected: true` values
|
|
282
|
+
- Server applies these changes and hides non-selected options
|
|
283
|
+
- Server sends back confirmed state
|
|
284
|
+
- Local state updates based on server response
|
|
285
|
+
- onChange callbacks fire for initial UI synchronization
|
|
286
|
+
|
|
287
|
+
#### Phase 2: User interaction (only for configurable products)
|
|
288
|
+
|
|
289
|
+
- User selects a different option in your UI
|
|
290
|
+
- You call `getAttribute('Color').select('Blue')`
|
|
291
|
+
- Client sends mutation request to server
|
|
292
|
+
- Your code continues immediately (doesn't wait for server response)
|
|
293
|
+
- Server updates the 3D model
|
|
294
|
+
- Server sends back the confirmed state
|
|
295
|
+
- Local state updates based on server response
|
|
296
|
+
- onChange callbacks fire with the actual state
|
|
297
|
+
- UI stays perfectly synchronized with 3D model
|
|
298
|
+
|
|
299
|
+
**Important:** The pattern is identical for both initial mapping and user
|
|
300
|
+
interactions. In both cases:
|
|
301
|
+
|
|
302
|
+
1. Client sends mutations to server
|
|
303
|
+
2. Server applies changes to 3D model
|
|
304
|
+
3. Server sends back confirmed state
|
|
305
|
+
4. Client updates local state based on server response
|
|
306
|
+
5. onChange callbacks fire with the actual state
|
|
307
|
+
|
|
308
|
+
This ensures the server remains the single source of truth for all state
|
|
309
|
+
changes.
|
|
310
|
+
|
|
311
|
+
## Advanced usage
|
|
312
|
+
|
|
313
|
+
### Live state synchronization
|
|
314
|
+
|
|
315
|
+
Keep your UI in sync with the 3D server state using onChange callbacks:
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
const colorAttribute = client.getAttribute('Color');
|
|
319
|
+
|
|
320
|
+
colorAttribute?.getValues().forEach((value) => {
|
|
321
|
+
value.onChange = () => {
|
|
322
|
+
// Update UI when state changes
|
|
323
|
+
updateButton(value.value, value.isSelected);
|
|
324
|
+
};
|
|
325
|
+
});
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Mapping validation
|
|
329
|
+
|
|
330
|
+
Validate your mapping configuration during development:
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
import { mappingSchema } from '@virtualdisplay-io/client';
|
|
334
|
+
import Ajv from 'ajv';
|
|
335
|
+
|
|
336
|
+
const validate = new Ajv().compile(mappingSchema);
|
|
337
|
+
if (!validate(myMapping)) {
|
|
338
|
+
console.error('Invalid mapping:', validate.errors);
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Real-world integration examples
|
|
343
|
+
|
|
344
|
+
### CMS integration
|
|
345
|
+
|
|
346
|
+
The Virtualdisplay client is CMS-agnostic. Store the mapping configuration in
|
|
347
|
+
your CMS alongside your product data:
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
// Fetch product with 3D mapping from your CMS
|
|
351
|
+
const product = await fetch('/api/products/sneaker-pro').then((r) => r.json());
|
|
352
|
+
|
|
353
|
+
// Use the stored mapping directly
|
|
354
|
+
client.setMapping(product.server3dMapping);
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
**Best practices:**
|
|
358
|
+
|
|
359
|
+
- Store the complete mapping configuration with each product
|
|
360
|
+
- Update mappings when product options or availability changes
|
|
361
|
+
- Let your CMS handle filtering of unavailable combinations
|
|
362
|
+
- Version your mappings when 3D models are updated
|
|
363
|
+
|
|
364
|
+
## Example
|
|
365
|
+
|
|
366
|
+
See the [color configurator example](./examples/color-configurator/) for a
|
|
367
|
+
complete working implementation that demonstrates:
|
|
368
|
+
|
|
369
|
+
- Setting up the client
|
|
370
|
+
- Defining attribute mappings
|
|
371
|
+
- Binding UI controls to attributes
|
|
372
|
+
- Real-time state synchronization
|
|
373
|
+
|
|
374
|
+
## API reference
|
|
375
|
+
|
|
376
|
+
### ClientOptions
|
|
377
|
+
|
|
378
|
+
```typescript
|
|
379
|
+
interface ClientOptions {
|
|
380
|
+
parent: string | HTMLElement; // Container element or selector
|
|
381
|
+
license: string; // Your license key
|
|
382
|
+
model: string; // Model ID to load
|
|
383
|
+
debug?: boolean; // Enable debug logging (default: false)
|
|
384
|
+
language?: string; // Language for UI inside the iframe (default: 'nl', supported: 'nl', 'en', 'de')
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Methods
|
|
389
|
+
|
|
390
|
+
#### `setMapping(configuration: MappingConfiguration): void`
|
|
391
|
+
|
|
392
|
+
Configure attribute-to-node mapping for product variants. This is the primary
|
|
393
|
+
method for setting up your product configurator. Call this once with your
|
|
394
|
+
complete configuration - the client efficiently handles all updates.
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
client.setMapping({
|
|
398
|
+
attributes: [
|
|
399
|
+
{
|
|
400
|
+
name: 'Color',
|
|
401
|
+
values: [
|
|
402
|
+
{ value: 'Red', nodeIds: ['node1', 'node2'], isSelected: true },
|
|
403
|
+
{ value: 'Blue', nodeIds: ['node3', 'node4'] },
|
|
404
|
+
],
|
|
405
|
+
},
|
|
406
|
+
],
|
|
407
|
+
});
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
#### `getAttribute(name: string): AttributeSelector | undefined`
|
|
411
|
+
|
|
412
|
+
Get an attribute selector for changing values. Returns undefined if the
|
|
413
|
+
attribute doesn't exist.
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
const colorAttr = client.getAttribute('Color');
|
|
417
|
+
if (colorAttr) {
|
|
418
|
+
colorAttr.select('Blue');
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
#### `destroy(): void`
|
|
423
|
+
|
|
424
|
+
Clean up and remove the client connection. Always call this when unmounting your
|
|
425
|
+
component.
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
// React example
|
|
429
|
+
useEffect(() => {
|
|
430
|
+
const client = new VirtualdisplayClient({ ... });
|
|
431
|
+
return () => client.destroy();
|
|
432
|
+
}, []);
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### Types
|
|
436
|
+
|
|
437
|
+
```typescript
|
|
438
|
+
interface MappingConfiguration {
|
|
439
|
+
attributes: AttributeConfig[];
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
interface AttributeConfig {
|
|
443
|
+
name: string; // Attribute name (e.g., 'Color')
|
|
444
|
+
values: AttributeValueConfig[]; // Possible values
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
interface AttributeValueConfig {
|
|
448
|
+
value: string; // Value name (e.g., 'Red')
|
|
449
|
+
nodeIds: string[]; // 3D node IDs for this value
|
|
450
|
+
isSelected?: boolean; // Default selection state
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
class AttributeSelector {
|
|
454
|
+
// Properties
|
|
455
|
+
name: string; // Attribute name (getter)
|
|
456
|
+
currentValue: string | undefined; // Current selected value (getter)
|
|
457
|
+
availableValues: string[]; // All possible values (getter)
|
|
458
|
+
|
|
459
|
+
// Methods
|
|
460
|
+
select(value: string): AttributeSelector; // Select a value (chainable)
|
|
461
|
+
onChange(callback: () => void): AttributeSelector; // Register change callback (chainable)
|
|
462
|
+
getValues(): AttributeValue[]; // Get all AttributeValue objects
|
|
463
|
+
getValue(value: string): AttributeValue | undefined; // Get specific AttributeValue
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
class AttributeValue {
|
|
467
|
+
// Properties
|
|
468
|
+
value: string; // The value name (e.g., 'Red') - getter
|
|
469
|
+
nodeList: string[]; // Associated 3D node IDs - getter
|
|
470
|
+
isSelected: boolean; // Current selection state - getter
|
|
471
|
+
onChange?: () => void; // Callback when selection changes - property
|
|
472
|
+
}
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
#### NodeSelector
|
|
476
|
+
|
|
477
|
+
The client exports `NodeSelector` for direct node manipulation. This is intended
|
|
478
|
+
for specialized tools like mapping editors and inspectors, not for typical
|
|
479
|
+
product configurators. For product configurators, use the attribute-based API.
|
|
480
|
+
|
|
481
|
+
### Error handling
|
|
482
|
+
|
|
483
|
+
The client throws `VirtualdisplayError` for known error conditions:
|
|
484
|
+
|
|
485
|
+
```typescript
|
|
486
|
+
import {
|
|
487
|
+
VirtualdisplayClient,
|
|
488
|
+
VirtualdisplayError,
|
|
489
|
+
ERROR_CODES,
|
|
490
|
+
} from '@virtualdisplay-io/client';
|
|
491
|
+
|
|
492
|
+
try {
|
|
493
|
+
client.getAttribute('NonExistent').select('Value');
|
|
494
|
+
} catch (error) {
|
|
495
|
+
if (error instanceof VirtualdisplayError) {
|
|
496
|
+
switch (error.code) {
|
|
497
|
+
case ERROR_CODES.ATTRIBUTE_NOT_FOUND:
|
|
498
|
+
console.error('Attribute does not exist');
|
|
499
|
+
break;
|
|
500
|
+
case ERROR_CODES.NO_MAPPING:
|
|
501
|
+
console.error('Call setMapping() first');
|
|
502
|
+
break;
|
|
503
|
+
// Handle other error codes
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
**Error codes:**
|
|
510
|
+
|
|
511
|
+
- `NO_MAPPING` - No mapping configuration set
|
|
512
|
+
- `ATTRIBUTE_NOT_FOUND` - Requested attribute doesn't exist
|
|
513
|
+
- `VALUE_NOT_FOUND` - Requested value doesn't exist for attribute
|
|
514
|
+
- `INVALID_MAPPING` - Mapping configuration is invalid
|
|
515
|
+
- `PARENT_NOT_FOUND` - Parent element for iframe not found
|
|
516
|
+
|
|
517
|
+
## Contributing
|
|
518
|
+
|
|
519
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup and guidelines.
|
|
520
|
+
|
|
521
|
+
## Troubleshooting
|
|
522
|
+
|
|
523
|
+
### Common issues
|
|
524
|
+
|
|
525
|
+
**Iframe not loading:**
|
|
526
|
+
|
|
527
|
+
- Check that the parent element exists in the DOM
|
|
528
|
+
- Verify your license key is valid
|
|
529
|
+
- Ensure the model ID matches your license
|
|
530
|
+
|
|
531
|
+
**Attributes not working:**
|
|
532
|
+
|
|
533
|
+
- Make sure you call `setMapping()` before using `getAttribute()`
|
|
534
|
+
- Verify attribute names match exactly (case-sensitive)
|
|
535
|
+
- Check that node IDs in your mapping exist in the 3D model
|
|
536
|
+
|
|
537
|
+
## Changelog
|
|
538
|
+
|
|
539
|
+
See the [GitHub releases](https://github.com/virtualdisplay-io/client/releases)
|
|
540
|
+
for release notes and version history.
|