@cabin3d/viewer
v1.0.0
Published
3D Product Viewer with modular features for materials, mesh toggle, and more
Downloads
47
Maintainers
Readme
@cabin3d/viewer
A modular 3D product viewer for React with lazy-loadable features.
Installation
npm install @cabin3d/viewer
# or
pnpm add @cabin3d/viewerPeer Dependencies
npm install react react-dom three @react-three/fiber @react-three/dreiQuick Start
import { CabinViewer } from '@cabin3d/viewer';
function App() {
return (
<CabinViewer
projectId="your-project-id"
apiUrl="https://your-api.com/api"
/>
);
}Features
Materials Feature
Apply materials and switch between color/texture variants.
<CabinViewer
projectId="xxx"
features={{
materials: { showVariants: true }
}}
/>Mesh Toggle Feature
Show/hide individual mesh parts.
<CabinViewer
projectId="xxx"
features={{
meshToggle: { showInUI: true }
}}
/>Transform Feature (New)
Enable interactive mesh transformations with gizmo controls.
<CabinViewer
projectId="xxx"
features={{
transform: {
showGizmo: true,
meshConfigs: {
'mesh_name': {
enabled: true,
allowedProperties: {
position: { x: true, y: true, z: false },
rotation: { x: true, y: true, z: true },
scale: { x: true, y: true, z: true, uniform: true }
},
limits: { scale: { min: 0.5, max: 2.0 } }
}
}
}
}}
/>Mesh States Feature (New)
Manage multiple states per mesh with smooth transitions.
<CabinViewer
projectId="xxx"
features={{
meshStates: {
meshConfigs: {
'door_mesh': {
enabled: true,
states: [
{ id: 'closed', name: 'Closed', isDefault: true, transform: {...} },
{ id: 'open', name: 'Open', transform: {...}, transition: { duration: 0.5, easing: 'easeOut' } }
]
}
}
}
}}
/>Mesh Events Feature (New)
Configure cursor interactions, collision detection, and event actions.
<CabinViewer
projectId="xxx"
features={{
meshEvents: {
meshConfigs: {
'interactive_mesh': {
enabled: true,
cursor: {
onHover: { cursor: 'pointer', highlight: true, highlightColor: '#ffffff' },
onClick: [{ type: 'changeState', params: { targetStateId: 'open' } }]
}
}
}
}
}}
/>API Reference
Mesh Configuration API
All mesh configurations are stored per-project and fetched from the dashboard API.
GET /api/projects/{projectId}/mesh-configs
Fetch all mesh configurations for a project.
Query Parameters: | Param | Type | Required | Description | |-------|------|----------|-------------| | modelId | UUID | No | Filter by specific model |
Response:
{
"meshConfigs": [
{
"id": "uuid",
"projectId": "uuid",
"modelId": "uuid",
"meshName": "string",
"displayName": "string | null",
"thumbnailUrl": "string | null",
"displayOrder": "number | null",
"transformConfig": {
"enabled": true,
"allowedProperties": { ... },
"limits": { ... },
"allowFlip": { "x": false, "y": false, "z": false }
},
"statesConfig": {
"enabled": true,
"states": [{ "id": "uuid", "name": "string", ... }],
"currentStateId": "uuid | null"
},
"eventsConfig": {
"enabled": true,
"draggable": { ... },
"collision": { ... },
"cursor": { ... },
"events": []
}
}
]
}PATCH /api/projects/{projectId}/mesh-configs
Upsert a mesh configuration.
Request Body:
{
"modelId": "uuid (required)",
"meshName": "string (required)",
"displayName": "optional string",
"thumbnailUrl": "optional string",
"displayOrder": "optional number",
"transformConfig": { /* MeshTransformConfig */ },
"statesConfig": { /* MeshStatesConfig */ },
"eventsConfig": { /* MeshEventsConfig */ }
}DELETE /api/projects/{projectId}/mesh-configs
Delete a mesh configuration.
Query Parameters: | Param | Type | Required | Description | |-------|------|----------|-------------| | modelId | UUID | Yes | Model ID | | meshName | string | Yes | Mesh name |
Event System
The viewer uses an event bus for communication between features and parent application.
Using Events
import { EventBus, useEventBus } from '@cabin3d/viewer';
// Subscribe to events
useEventBus('mesh:click', (payload) => {
console.log('Mesh clicked:', payload.meshName);
});
// Emit events
EventBus.emit('mesh:changeState', { meshName: 'door', stateId: 'open' });Available Events
| Event | Payload | Description |
|-------|---------|-------------|
| mesh:click | { meshName, position } | When mesh is clicked |
| mesh:transformed | { meshName, position, rotation, scale } | After transform change |
| mesh:changeState | { meshName, stateId } | Request/notify state change |
| mesh:visibility | { meshName, visible } | Toggle mesh visibility |
| mesh:collision | { meshName, targetMesh } | Collision detected |
| mesh:showInfo | { meshName, title, description } | Show info popup |
| mesh:select | { meshName } | Select mesh for transform |
| model:loaded | { scene } | When model finishes loading |
| materials:update | [{ meshName, material, activeVariantId }] | Update material assignments |
Event Actions
Actions that can be triggered by mesh events:
| Action Type | Params | Description |
|-------------|--------|-------------|
| changeState | { targetStateId } | Switch to a specific state |
| toggleVisibility | - | Toggle mesh visibility |
| emit | { eventName, eventData } | Emit custom event |
| openUrl | { url } | Open URL in new tab |
| showInfo | { title, description } | Display info popup |
Lazy Loading Features
Features are code-split and loaded on demand:
// Main bundle: ~7KB
import { CabinViewer } from '@cabin3d/viewer';
// Optional: Import features directly (~4KB each)
import { MaterialsFeature } from '@cabin3d/viewer/features/materials';
import { MeshToggleFeature } from '@cabin3d/viewer/features/mesh-toggle';Bundle Sizes
| Bundle | Size (uncompressed) | |--------|---------------------| | Core | 7.07 KB | | Materials | 4.28 KB | | Mesh Toggle | 2.40 KB | | Mesh Transform | ~2.5 KB | | Mesh States | ~2.7 KB | | Mesh Events | ~3.4 KB | | Total | ~22 KB |
Props
| Prop | Type | Description |
|------|------|-------------|
| projectId | string | Required. Your project ID |
| apiUrl | string | API base URL (default: http://localhost:3000/api) |
| apiKey | string | Optional. API key for authentication (deprecated, use domain allowlist instead) |
| features | object | Feature configuration |
| ui | object | UI customization (theme, toolbar position) |
| className | string | CSS class for container |
| style | CSSProperties | Inline styles |
| loadingComponent | ReactNode | Custom loading state |
| errorComponent | FC<{error}> | Custom error state |
Security
For production, configure allowed domains in your dashboard instead of using API keys:
- Go to Project Settings → Viewer Settings → Domains
- Add your production domains
- Remove
apiKeyprop from<CabinViewer />
License
MIT
