@salsita-ai/sdk 0.49.0-develop.cbee856178 → 0.49.0-develop.f5fc5cfc25

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.
Files changed (58) hide show
  1. package/dist/application-spec/generate-application/code/project-scaffolding.js +2 -2
  2. package/dist/application-spec/generate-application/code/templates/index.d.ts +1 -1
  3. package/dist/application-spec/generate-application/code/templates/index.js +1 -1
  4. package/dist/application-spec/generate-application/code/templates/phoenix-feedback-skill.js +14 -2
  5. package/dist/application-spec/generate-application/code/templates/phoenix-skill.d.ts +13 -1
  6. package/dist/application-spec/generate-application/code/templates/phoenix-skill.js +1 -1
  7. package/dist/configurator/application-runtime/extensions/systems/camera-control-system/camera-control-system.d.ts +7 -3
  8. package/dist/configurator/application-runtime/extensions/systems/camera-control-system/camera-control-system.js +1 -1
  9. package/dist/configurator/application-runtime/extensions/systems/camera-control-system/camera-system.d.ts +24 -1
  10. package/dist/configurator/application-runtime/extensions/systems/camera-control-system/camera-system.js +1 -1
  11. package/dist/docs/3d-scene/cameras/custom.mdx +4 -1
  12. package/dist/docs/3d-scene/cameras/fit-entity.mdx +2 -0
  13. package/dist/docs/3d-scene/cameras/fit-root.mdx +2 -0
  14. package/dist/docs/3d-scene/cameras/fit-selection.mdx +2 -0
  15. package/dist/docs/3d-scene/cameras/locks.mdx +439 -0
  16. package/dist/docs/3d-scene/cameras/static.mdx +2 -0
  17. package/dist/docs/3d-scene/cameras/transitions.mdx +2 -0
  18. package/dist/docs/index.json +12 -1
  19. package/dist/docs/phoenix/phoenix-cli.mdx +6 -5
  20. package/dist/docs/phoenix/project-structure.mdx +1 -1
  21. package/dist/docs/phoenix/sdk-builder-workflow.mdx +13 -8
  22. package/dist/domain/projections/common/camera-orbit.d.ts +37 -9
  23. package/dist/domain/projections/common/camera-orbit.js +1 -1
  24. package/dist/domain/projections/common/index.d.ts +1 -1
  25. package/dist/domain/projections/common/index.js +1 -1
  26. package/dist/domain/system-components/fit-entity-orthographic-camera-component.d.ts +8 -0
  27. package/dist/domain/system-components/fit-entity-orthographic-camera-component.js +1 -1
  28. package/dist/domain/system-components/fit-entity-perspective-camera-component.d.ts +8 -0
  29. package/dist/domain/system-components/fit-entity-perspective-camera-component.js +1 -1
  30. package/dist/domain/system-components/fit-root-orthographic-camera-component.d.ts +8 -0
  31. package/dist/domain/system-components/fit-root-orthographic-camera-component.js +1 -1
  32. package/dist/domain/system-components/fit-root-perspective-camera-component.d.ts +8 -0
  33. package/dist/domain/system-components/fit-root-perspective-camera-component.js +1 -1
  34. package/dist/domain/system-components/fit-selection-orthographic-camera-component.d.ts +8 -0
  35. package/dist/domain/system-components/fit-selection-orthographic-camera-component.js +1 -1
  36. package/dist/domain/system-components/fit-selection-perspective-camera-component.d.ts +8 -0
  37. package/dist/domain/system-components/fit-selection-perspective-camera-component.js +1 -1
  38. package/dist/domain/system-components/static-orthographic-camera-component.d.ts +8 -0
  39. package/dist/domain/system-components/static-orthographic-camera-component.js +1 -1
  40. package/dist/domain/system-components/static-perspective-camera-component.d.ts +8 -0
  41. package/dist/domain/system-components/static-perspective-camera-component.js +1 -1
  42. package/dist/legacy/domain-spec/__generated/domain-schema.d.ts +16 -0
  43. package/dist/phoenix/application-spec/project-files.js +1 -1
  44. package/dist/phoenix/cli.js +1 -1
  45. package/dist/phoenix/commands/deploy.js +2 -2
  46. package/dist/phoenix/commands/dev.js +4 -4
  47. package/dist/phoenix/commands/init.d.ts +2 -1
  48. package/dist/phoenix/commands/init.js +1 -1
  49. package/dist/phoenix/commands/login.js +2 -2
  50. package/dist/phoenix/commands/sync.js +1 -1
  51. package/dist/phoenix/services/cli-credentials.d.ts +3 -0
  52. package/dist/phoenix/services/cli-credentials.js +1 -1
  53. package/dist/phoenix/utils/index.d.ts +1 -1
  54. package/dist/phoenix/utils/index.js +1 -1
  55. package/dist/phoenix/utils/package-info.d.ts +9 -0
  56. package/dist/phoenix/utils/package-info.js +1 -1
  57. package/package.json +3 -3
  58. package/schema/domain-spec.json +208 -0
@@ -0,0 +1,439 @@
1
+ # Camera - Locks
2
+
3
+ Keep a user's chosen viewing angle or magnification while the configurator moves between related product views. Camera locks place cameras into named groups: give two cameras the same explicit group and the destination camera retains that part of the live view when it activates.
4
+
5
+ - [Apply camera locks](#apply-camera-locks)
6
+ - [Choose what to preserve](#choose-what-to-preserve)
7
+ - [Understand transition behavior](#understand-transition-behavior)
8
+ - [Try the example](#try-the-example)
9
+ - [Example](#example)
10
+ - [Complete Implementation](#complete-implementation)
11
+
12
+ ## Apply camera locks
13
+
14
+ Assign the same literal group name to the outgoing and incoming cameras that should share a live view. For perspective cameras, `distanceLockGroup` preserves dolly distance and `rotationLockGroup` preserves the live azimuth and polar angles:
15
+
16
+ ```yaml
17
+ components:
18
+ Scene:
19
+ fitRootPerspectiveCamera:
20
+ overview:
21
+ distanceLockGroup: product-view
22
+ rotationLockGroup: product-view
23
+ Product:
24
+ fitEntityPerspectiveCamera:
25
+ detail:
26
+ distanceLockGroup: product-view
27
+ rotationLockGroup: product-view
28
+ ```
29
+
30
+ When the Product camera activates after the Scene camera, it still calculates its normal target from Product, but it uses the user's current distance and rotation. The same applies in the other direction. Group names have no predefined meaning; choose stable names that describe the related camera views in your configurator.
31
+
32
+ Orthographic cameras use `zoomLockGroup` instead of `distanceLockGroup`:
33
+
34
+ ```yaml
35
+ components:
36
+ Scene:
37
+ fitRootOrthographicCamera:
38
+ overview:
39
+ zoomLockGroup: product-view
40
+ rotationLockGroup: product-view
41
+ Product:
42
+ fitEntityOrthographicCamera:
43
+ detail:
44
+ zoomLockGroup: product-view
45
+ rotationLockGroup: product-view
46
+ ```
47
+
48
+ Add the group property to every camera that participates. Group names match exactly and case-sensitively, including whitespace. A missing group, `null`, or a different string means that aspect follows the destination camera normally; two missing or `null` groups never create a lock.
49
+
50
+ ## Choose what to preserve
51
+
52
+ Distance or zoom and rotation are independent. Configure only the behavior the product flow needs:
53
+
54
+ - Set only `rotationLockGroup` to retain the viewing angle while the destination camera calculates its normal fit distance or zoom.
55
+ - Set only `distanceLockGroup` on perspective cameras to retain dolly distance while applying the destination orientation.
56
+ - Set only `zoomLockGroup` on orthographic cameras to retain zoom while applying the destination orientation.
57
+ - Set both relevant groups to translate the retained view around the destination camera's newly calculated target.
58
+
59
+ Lock groups are available on fit-root, fit-entity, fit-selection, and static cameras. A camera can belong to different groups for rotation and magnification by using different names for the two aspects.
60
+
61
+ ## Understand transition behavior
62
+
63
+ The destination camera still owns its target, boundary behavior, and limits. Preserved distance and zoom are clamped to its configured limits. Preserved angles use the same limit resolution as a normally applied camera: finite two-sided ranges clamp during the transition, while a one-sided azimuth limit is placed on its allowed side within one revolution of the preserved angle, so the angle stays where it is and the limit is reached by orbiting toward it. This can intentionally crop a newly fitted target: a lock preserves the user's view rather than guaranteeing that all destination content remains visible.
64
+
65
+ A matching group also applies when the active camera refits itself after mesh, bounds, or canvas-padding changes. This is useful with `fitMeshChanges`: geometry can change the target or fit while the chosen live aspects remain stable. A refit only preserves an aspect whose live value is a settled or user-chosen view: when it interrupts an unfinished transition that was still animating that aspect toward its destination, such as the first load, a Reset, or a switch from a camera with different groups, the refit completes the destination view instead of freezing the animation midway.
66
+
67
+ Locks apply only between cameras of the same projection type. Switching between perspective and orthographic cameras uses the destination view normally, even when their group names match. Projection parameters such as perspective `fov` and orthographic `frustumSize` are not preserved; keep them consistent when a visual projection snap would be distracting.
68
+
69
+ Reset Camera bypasses all locks for that action and restores the active camera's configured view. The next automatic transition uses locks normally again. Export rendering remains independent from the interactive camera's lock history.
70
+
71
+ ## Try the example
72
+
73
+ The example uses dynamic expressions and sidebar switches only to make each lock easy to turn on and off while experimenting. A normal integration should use stable literal group names as shown above. Object B and the full-scene camera share a group, while Object A uses a different group:
74
+
75
+ - Object A: a fit-selection camera with the `object-a` group.
76
+ - Object B: a fit-entity camera activated by `$filter.selectedEntityIds`, with the `object-b-and-root` group.
77
+ - Nothing selected: a fit-root camera with the `object-b-and-root` group.
78
+
79
+ 1. Leave both lock switches on, click Object B to select it, then orbit and zoom or dolly to an obvious view.
80
+ 2. Click the empty canvas to clear the selection. The target changes to the whole scene, but the live rotation and magnification stay because B and the root share both groups.
81
+ 3. Turn off one lock and repeat B → Nothing selected. The enabled aspect stays; the disabled aspect follows the destination camera.
82
+ 4. Click Object A to select it. Its groups are different, so the configured A view is applied normally.
83
+ 5. While B or Nothing selected is active, toggle **Show B extra part**. `fitMeshChanges` refits the same camera from mesh-derived bounds. An unlocked distance or zoom fits the larger shape while a locked rotation stays unchanged.
84
+ 6. With both locks enabled, switch Perspective ↔ Orthographic. Projection changes never preserve the live view.
85
+ 7. Change the live view and press the camera button in the toolbar. Reset Camera bypasses the locks once and restores the active camera's configured fit and angles.
86
+ 8. In the perspective view with nothing selected, dolly fully out to the root camera's `maxDistance` of 9000, then click Object B. The preserved distance is clamped to B's `maxDistance` of 6500, so the camera moves closer while the rotation stays. In the orthographic view, zoom fully in on Object B and clear the selection: B's `maxZoom` of 3.5 is clamped to the root camera's 3.
87
+
88
+ For the camera activation rules used here, see [Fit Selection](?path=/docs/3d-scene-cameras-fit-selection--docs), [Fit Entity](?path=/docs/3d-scene-cameras-fit-entity--docs), [Fit Root](?path=/docs/3d-scene-cameras-fit-root--docs), and [Camera Transitions](?path=/docs/3d-scene-cameras-transitions--docs).
89
+
90
+ ## Example
91
+
92
+ Use the sidebar to toggle each lock independently and change B's fitted mesh, the control over the canvas to switch projection, and the camera toolbar button to reset.
93
+
94
+ ## Complete Implementation
95
+
96
+ {/* AUTO-GENERATED SECTION: This section is automatically updated by 'phoenix story build' */}
97
+
98
+ <details>
99
+ <summary>Click to view the complete implementation files</summary>
100
+
101
+ ### ./spec/domain.yaml
102
+
103
+ ```yaml
104
+ model:
105
+ latest:
106
+ entityTypes:
107
+ - Scene
108
+ - ObjectA
109
+ - ObjectB
110
+ aliases:
111
+ RootEntity:
112
+ - Scene
113
+ shapes:
114
+ Scene:
115
+ lockRotation:
116
+ type: boolean
117
+ lockZoomOrDistance:
118
+ type: boolean
119
+ showBExtraPart:
120
+ type: boolean
121
+
122
+ relations:
123
+ Scene:
124
+ $objectA:
125
+ kind: allOfType
126
+ target: ObjectA
127
+ cardinality: 1-1
128
+ composedOf: true
129
+ $objectB:
130
+ kind: allOfType
131
+ target: ObjectB
132
+ cardinality: 1-1
133
+ composedOf: true
134
+ ObjectA:
135
+ $scene:
136
+ kind: allOfType
137
+ target: Scene
138
+ cardinality: 1-1
139
+ composedOf: false
140
+ ObjectB:
141
+ $scene:
142
+ kind: allOfType
143
+ target: Scene
144
+ cardinality: 1-1
145
+ composedOf: false
146
+
147
+ presets:
148
+ - name: Camera locks
149
+ description: Compare independent rotation and zoom or distance locks while changing camera targets.
150
+ rootEntityType: Scene
151
+ initialDomainEntities:
152
+ - id: Scene-1
153
+ type: Scene
154
+ lockRotation: true
155
+ lockZoomOrDistance: true
156
+ showBExtraPart: false
157
+ - id: ObjectA-1
158
+ type: ObjectA
159
+ - id: ObjectB-1
160
+ type: ObjectB
161
+
162
+ viewStates:
163
+ - perspective
164
+ - orthographic
165
+
166
+ # This example resolves lock groups from boolean attributes only so readers can
167
+ # compare locked and unlocked behavior interactively. In an application, assign
168
+ # stable literal lock groups directly to the related camera configurations.
169
+ components:
170
+ Scene:
171
+ applicationConfig:
172
+ main:
173
+ layout: universal
174
+ ignoreSelectionInSidebar: true
175
+ miniSidebar: true
176
+ splitSidebar: true
177
+ distanceDataUnit: mm
178
+ toolbar:
179
+ main:
180
+ desktop:
181
+ - kind: toolbarButtonGroup
182
+ children:
183
+ - kind: resetCameraToolbarButton
184
+ props:
185
+ icon: CameraRotate
186
+ mobile:
187
+ - kind: toolbarButtonGroup
188
+ children:
189
+ - kind: resetCameraToolbarButton
190
+ viewControls:
191
+ projection:
192
+ items:
193
+ - label: Perspective
194
+ value: perspective
195
+ icon: Eye
196
+ - label: Orthographic
197
+ value: orthographic
198
+ icon: Square
199
+ fitRootPerspectiveCamera:
200
+ nothingSelected:
201
+ near: 10
202
+ far: 20000
203
+ fov: 42
204
+ azimuthAngle: 0.55
205
+ polarAngle: 1.05
206
+ minDistance: 1200
207
+ maxDistance: 9000
208
+ distanceLockGroup:
209
+ $read: $distanceLockGroup
210
+ rotationLockGroup:
211
+ $read: $rotationLockGroup
212
+ fitMeshChanges: true
213
+ $filter:
214
+ viewStates: [perspective]
215
+ fitRootOrthographicCamera:
216
+ nothingSelected:
217
+ near: 10
218
+ far: 20000
219
+ frustumSize: 5000
220
+ distance: 6000
221
+ azimuthAngle: 0.55
222
+ polarAngle: 1.05
223
+ minZoom: 0.4
224
+ maxZoom: 3
225
+ zoomLockGroup:
226
+ $read: $distanceLockGroup
227
+ rotationLockGroup:
228
+ $read: $rotationLockGroup
229
+ fitMeshChanges: true
230
+ $filter:
231
+ viewStates: [orthographic]
232
+ attributeSwitch:
233
+ lockRotation:
234
+ value:
235
+ $bind: lockRotation
236
+ lockZoomOrDistance:
237
+ value:
238
+ $bind: lockZoomOrDistance
239
+ showBExtraPart:
240
+ value:
241
+ $bind: showBExtraPart
242
+
243
+ ObjectA:
244
+ fitSelectionPerspectiveCamera:
245
+ selected:
246
+ priority: 10
247
+ near: 10
248
+ far: 20000
249
+ fov: 42
250
+ azimuthAngle: -0.85
251
+ polarAngle: 1.25
252
+ minDistance: 700
253
+ maxDistance: 5000
254
+ distanceLockGroup:
255
+ $read: $distanceLockGroup
256
+ rotationLockGroup:
257
+ $read: $rotationLockGroup
258
+ $filter:
259
+ viewStates: [perspective]
260
+ fitSelectionOrthographicCamera:
261
+ selected:
262
+ priority: 10
263
+ near: 10
264
+ far: 20000
265
+ frustumSize: 5000
266
+ distance: 4000
267
+ azimuthAngle: -0.85
268
+ polarAngle: 1.25
269
+ minZoom: 0.5
270
+ maxZoom: 4
271
+ zoomLockGroup:
272
+ $read: $distanceLockGroup
273
+ rotationLockGroup:
274
+ $read: $rotationLockGroup
275
+ $filter:
276
+ viewStates: [orthographic]
277
+ sceneEntityBoxObject:
278
+ body:
279
+ width: 900
280
+ height: 1200
281
+ depth: 700
282
+ anchor: center
283
+ sceneEntityProperties:
284
+ main:
285
+ offsetX: -1600
286
+ offsetY: 600
287
+ sceneEntityLocalMaterialOverride:
288
+ main:
289
+ materialsToOverride: [BoxMaterial]
290
+ color: '#4f7cac'
291
+ roughness: 0.65
292
+ selectable:
293
+ main: {}
294
+
295
+ ObjectB:
296
+ fitEntityPerspectiveCamera:
297
+ selected:
298
+ priority: 10
299
+ near: 10
300
+ far: 20000
301
+ # Projection settings are intentionally identical to the root camera.
302
+ # Distance/zoom locks preserve controls state, not projection parameters;
303
+ # changing these would cause an immediate visual snap before the pan.
304
+ fov: 42
305
+ azimuthAngle: 1.0
306
+ polarAngle: 0.9
307
+ minDistance: 900
308
+ maxDistance: 6500
309
+ distanceLockGroup:
310
+ $read: $distanceLockGroup
311
+ rotationLockGroup:
312
+ $read: $rotationLockGroup
313
+ fitMeshChanges: true
314
+ $filter:
315
+ viewStates: [perspective]
316
+ selectedEntityIds:
317
+ $read: id
318
+ fitEntityOrthographicCamera:
319
+ selected:
320
+ priority: 10
321
+ near: 10
322
+ far: 20000
323
+ frustumSize: 5000
324
+ distance: 4500
325
+ azimuthAngle: 1.0
326
+ polarAngle: 0.9
327
+ minZoom: 0.4
328
+ maxZoom: 3.5
329
+ zoomLockGroup:
330
+ $read: $distanceLockGroup
331
+ rotationLockGroup:
332
+ $read: $rotationLockGroup
333
+ fitMeshChanges: true
334
+ $filter:
335
+ viewStates: [orthographic]
336
+ selectedEntityIds:
337
+ $read: id
338
+ sceneEntityBoxObject:
339
+ body:
340
+ width: 1100
341
+ height: 900
342
+ depth: 850
343
+ anchor: center
344
+ extra:
345
+ width: 1900
346
+ height: 350
347
+ depth: 500
348
+ anchor: center
349
+ $filter:
350
+ active:
351
+ $read: $showExtraPart
352
+ sceneEntityObjectProperties:
353
+ extra:
354
+ offsetX: 800
355
+ offsetY: 750
356
+ offsetZ: -250
357
+ $filter:
358
+ active:
359
+ $read: $showExtraPart
360
+ sceneEntityProperties:
361
+ main:
362
+ offsetX: 1600
363
+ offsetY: 450
364
+ sceneEntityLocalMaterialOverride:
365
+ main:
366
+ materialsToOverride: [BoxMaterial]
367
+ color: '#d9822b'
368
+ roughness: 0.65
369
+ selectable:
370
+ main: {}
371
+ ```
372
+
373
+ ### ./src/components/CameraLockControls.tsx
374
+
375
+ ```tsx
376
+ import { propertyPageComponent } from '@salsita-ai/sdk/domain'
377
+ import { AttributeSwitchSelector } from '@salsita-ai/sdk/react'
378
+ import { PropertyContainer } from '@salsita-ai/sdk/ui'
379
+
380
+ import { phx } from '../../.phoenix/phx.js'
381
+
382
+ export default [
383
+ phx.domain.attachComponent
384
+ .withComponent(propertyPageComponent, 'main')
385
+ .withEntity('Scene')
386
+ .attach(s => ({
387
+ page: (
388
+ <PropertyContainer.Group spacing="large">
389
+ <PropertyContainer.Item
390
+ title="Try the camera locks"
391
+ description="Object B and the full-scene camera share lock groups, while Object A uses different groups. Orbit and zoom or dolly on Object B, then switch between camera targets to compare what is preserved. Disable either lock to make that part of the view follow the destination camera instead."
392
+ />
393
+ <AttributeSwitchSelector
394
+ entityId={s.id}
395
+ instanceName="lockRotation"
396
+ title="Lock rotation"
397
+ description="Preserve live azimuth and polar angles."
398
+ />
399
+ <AttributeSwitchSelector
400
+ entityId={s.id}
401
+ instanceName="lockZoomOrDistance"
402
+ title="Lock zoom / distance"
403
+ description="Preserve perspective distance or orthographic zoom."
404
+ />
405
+ <AttributeSwitchSelector
406
+ entityId={s.id}
407
+ instanceName="showBExtraPart"
408
+ title="Show B extra part"
409
+ description="Change B's mesh bounds to trigger a same-camera refit."
410
+ />
411
+ </PropertyContainer.Group>
412
+ ),
413
+ })),
414
+ ]
415
+ ```
416
+
417
+ ### ./src/expressions/camera-locks.ts
418
+
419
+ ```ts
420
+ import { phx } from '../../.phoenix/phx.js'
421
+
422
+ export default phx.domain.implementCustomExpressions({
423
+ Scene: {
424
+ $distanceLockGroup: s => (s.lockZoomOrDistance ? 'object-b-and-root' : null),
425
+ $rotationLockGroup: s => (s.lockRotation ? 'object-b-and-root' : null),
426
+ },
427
+ ObjectA: {
428
+ $distanceLockGroup: s => (s.$scene.lockZoomOrDistance ? 'object-a' : null),
429
+ $rotationLockGroup: s => (s.$scene.lockRotation ? 'object-a' : null),
430
+ },
431
+ ObjectB: {
432
+ $distanceLockGroup: s => (s.$scene.lockZoomOrDistance ? 'object-b-and-root' : null),
433
+ $rotationLockGroup: s => (s.$scene.lockRotation ? 'object-b-and-root' : null),
434
+ $showExtraPart: s => s.$scene.showBExtraPart,
435
+ },
436
+ })
437
+ ```
438
+
439
+ </details>
@@ -541,6 +541,8 @@ Choose perspective cameras for natural, immersive viewing experiences. Choose or
541
541
 
542
542
  ## Example
543
543
 
544
+ To retain the user's live rotation, distance, or zoom when switching between static camera views that share a lock group, see the [Camera Locks example](?path=/docs/3d-scene-cameras-locks--docs).
545
+
544
546
  This example shows multiple static cameras combining both perspective and orthographic projections positioned to showcase the model from different specific angles. Each camera maintains its configured position while still allowing user interaction within defined limits.
545
547
 
546
548
  Try using the view controls at the top of the interface to switch between different camera perspectives. Notice how the 3D and Front views provide natural perspective viewing with depth, while the Top and Left views use true orthographic projection with `frustumSize` for technical accuracy. The orthographic views use `animateTransition: false` to provide instant, precise positioning ideal for technical examination.
@@ -117,6 +117,8 @@ The camera system uses memoization to maintain the same component instance when
117
117
 
118
118
  This example combines all three transition scenarios. Use the step navigation to see component instance changes, adjust the radius slider in "Bounding Box" view to see geometry-based transitions, and toggle the box visibility in "Fit Mesh" view to see mesh-change transitions.
119
119
 
120
+ To preserve a user's live rotation, distance, or zoom across selected camera transitions, see the [Camera Locks example](?path=/docs/3d-scene-cameras-locks--docs).
121
+
120
122
  ## Complete Implementation
121
123
 
122
124
  {/* AUTO-GENERATED SECTION: This section is automatically updated by 'phoenix story build' */}
@@ -1,5 +1,5 @@
1
1
  {
2
- "generatedAt": "2026-10-07T10:56:14.429Z",
2
+ "generatedAt": "2026-10-07T15:22:23.840Z",
3
3
  "categories": [
4
4
  {
5
5
  "name": "Phoenix",
@@ -1381,6 +1381,17 @@
1381
1381
  ],
1382
1382
  "source": "storybook-example"
1383
1383
  },
1384
+ {
1385
+ "title": "Locks",
1386
+ "description": "Keep a user's chosen viewing angle or magnification while the configurator moves between related product views. Camera locks place cameras into named groups: give two cameras the same explicit group...",
1387
+ "path": "docs/3d-scene/cameras/locks.mdx",
1388
+ "category": [
1389
+ "3d scene",
1390
+ "Cameras",
1391
+ "Locks"
1392
+ ],
1393
+ "source": "storybook-example"
1394
+ },
1384
1395
  {
1385
1396
  "title": "Simple",
1386
1397
  "description": "This example demonstrates the default camera behavior in Phoenix configurators. When no camera components are configured, the system provides built-in camera controls with sensible defaults, giving...",
@@ -17,7 +17,7 @@ The Phoenix CLI (`phoenix` command) is the unified tool for all configurator dev
17
17
  `phoenix init [folder]` starts a configurator project in `[folder]` (default: the working directory):
18
18
 
19
19
  ```bash
20
- npx -p @salsita-ai/sdk phoenix init my-configurator
20
+ npx -p @salsita-ai/sdk@canary phoenix init my-configurator
21
21
  cd my-configurator
22
22
  pnpm install
23
23
  pnpm phoenix sync
@@ -30,6 +30,7 @@ pnpm phoenix dev
30
30
  1. Refuses to run when a `spec/` folder already exists, so it never touches an existing project.
31
31
  2. Writes a `package.json` that depends on the CLI's SDK version when the folder has none. An existing `package.json` is kept, as long as it depends on the SDK.
32
32
  3. Writes a starter spec (`spec/application.yaml`, `spec/configurator.yaml`) with one resizable, recolorable box, and switches `package.json` to the application-spec pipeline.
33
+ 4. Writes `pnpm-workspace.yaml` with the pnpm settings the first install needs: the build scripts of the SDK's dependencies are allowed or denied, and `@salsita-ai/*` packages are exempt from pnpm's minimum release age. A project inside a pnpm monorepo gets no file; the workspace root holds these settings.
33
34
 
34
35
  It asks nothing, writes no `.env` and contacts no server. `phoenix sync` then generates every other project file, including `AGENTS.md` and the agent skills described in [Project Structure](?path=/docs/phoenix-project-structure--docs).
35
36
 
@@ -45,7 +46,7 @@ The starter `spec/application.yaml` starts with `project: default`. `project` na
45
46
 
46
47
  ### How `phoenix dev` authenticates
47
48
 
48
- - **Signed in with `phoenix login`** (SDK-builder workspaces): nothing else is needed. On start, `phoenix dev` asks the server for your development environment in the project; the server creates it on the first run. Every user gets their own development environment, so teammates don't overwrite each other's schema or webhook tunnel. The server comes from `--serverUrl`, then `SERVER_URL` in `.env`, then the region (`--region` or `PHOENIX_REGION`, default `eu`).
49
+ - **Signed in with `phoenix login`** (SDK-builder workspaces): nothing else is needed. On start, `phoenix dev` asks the server for your development environment in the project; the server creates it on the first run. Every user gets their own development environment, so teammates don't overwrite each other's schema or webhook tunnel. The server comes from `--serverUrl`, then `SERVER_URL` in `.env`, then the region (`--region` or `PHOENIX_REGION`, default `eu`). `phoenix dev` hands that server to the preview as `SERVER_URL`, so the preview talks to the server the development deployment was prepared on even without a `.env`, and prints the preview address as `Preview: <url>` once the dev server is up (also written to `.phoenix/phoenix-dev.log`).
49
50
  - **With a Studio development environment**: create the project and a development environment in PHX Studio and paste its `.env` block (`SERVER_URL`, `PROJECT_SLUG`, `ENVIRONMENT_SLUG`, `DEVELOPMENT_KEY`) into `.env`. A `DEVELOPMENT_KEY` always takes precedence over the sign-in, and `PROJECT_SLUG` overrides the starter spec's `project: default`, which a development key cannot resolve.
50
51
 
51
52
  ## Local Development Setup
@@ -358,7 +359,7 @@ Removes all build artifacts (`.phoenix/`, `node_modules/`), reinstalls dependenc
358
359
 
359
360
  ### `phoenix sync`
360
361
 
361
- Syncs project dependencies and regenerates all project files without building Vite bundles or webhooks. Faster than `clean` — use this for routine updates after changing spec files or upgrading dependencies.
362
+ Syncs project dependencies and regenerates all project files without building Vite bundles or webhooks. Faster than `clean` — use this for routine updates after changing spec files or upgrading dependencies. Before installing, it adds the pnpm settings `phoenix init` writes to a standalone project's `pnpm-workspace.yaml` when they are missing; values you set there are kept.
362
363
 
363
364
  | Flag | Env Variable | Default | Description |
364
365
  | ---------------- | --------------- | ------- | ------------------------------ |
@@ -571,7 +572,7 @@ Signs the CLI in to the SDK-builder workspace `<customer>`:
571
572
  2. Studio sends a one-time code back to the CLI on `127.0.0.1`.
572
573
  3. The CLI exchanges the code for a token.
573
574
 
574
- The token is stored in `~/.phoenix/credentials.json`, readable only by you, per server; `PHOENIX_CREDENTIALS_FILE` moves the file. The token works until you revoke it. Signing in again to the same server revokes the token it replaces; the CLI warns if the server could not revoke it.
575
+ The token is stored in `~/.phoenix/credentials.json`, readable only by you, per server, together with the Studio URL of the sign-in; `PHOENIX_CREDENTIALS_FILE` moves the file. The token works until you revoke it. Signing in again to the same server revokes the token it replaces; the CLI warns if the server could not revoke it.
575
576
 
576
577
  | Flag | Default | Description |
577
578
  | --------------------- | ------------------------------------ | ------------------------------------------------ |
@@ -590,7 +591,7 @@ Revokes the token on the server and removes it from the credentials file. `--reg
590
591
 
591
592
  ### `phoenix deploy`
592
593
 
593
- Validates the spec, packs and uploads the project, starts its build on the server and follows it until the configurator is live. `--region` or `--serverUrl` selects the server, as for `login`; `--no-wait` returns as soon as the build starts.
594
+ Validates the spec, adds the pnpm settings `phoenix init` writes to `pnpm-workspace.yaml` when they are missing, packs and uploads the project, starts its build on the server and follows it until the configurator is live. Once the build starts it prints `Follow the build in Studio: <url>`, the build's page in the Studio you signed in through; a sign-in stored by an older CLI has no Studio URL, so sign in again to get the link. `--region` or `--serverUrl` selects the server, as for `login`; `--no-wait` returns as soon as the build starts.
594
595
 
595
596
  ### `phoenix feedback <message>`
596
597
 
@@ -197,7 +197,7 @@ type CustomMaterial = Phx['AdminModel']['CustomMaterial']
197
197
 
198
198
  ### Agent Guidance
199
199
 
200
- Coding agents (Claude Code, Codex, Cursor) read `AGENTS.md` and the Phoenix agent skills. `AGENTS.md` is written once with the build commands, the do-not-edit list and a pointer to the skills; after that it is yours. The `phoenix`, `phoenix-cli`, `phoenix-feedback` and `phoenix-review` skills carry the SDK conventions, CLI tooling and review checklist, and the `phoenix-create-configurator` skill walks the agent from your product documents and assets to a reviewed configurator in five stages. Phoenix keeps them in step with the installed SDK, including the reference files inside a skill folder. Other skill folders are never touched.
200
+ Coding agents (Claude Code, Codex, Cursor) read `AGENTS.md` and the Phoenix agent skills. `AGENTS.md` is written once with the build commands, the do-not-edit list and a pointer to the skills; after that it is yours. The `phoenix`, `phoenix-cli`, `phoenix-feedback` and `phoenix-review` skills carry the SDK conventions, CLI tooling and review checklist, and the `phoenix-create-configurator` skill walks the agent from your product documents and assets to a reviewed configurator in five stages. Phoenix keeps them in step with the installed SDK, including the reference files inside a skill folder. During the internal beta, a project created from a canary build of the SDK gets one more section in the `phoenix` skill: once per conversation the agent checks for a newer canary and, after telling you, updates the project's SDK and runs `phoenix sync`. Other skill folders are never touched.
201
201
 
202
202
  Projects created by earlier SDK versions had this guidance in `CLAUDE.md`, `.cursor/rules/phoenix.mdc` and `commands/review.md`. Sync removes the SDK block from the first two, keeps your own text around it, deletes a file that held nothing else, and deletes the review commands; use the `phoenix-review` skill instead of `/review`.
203
203
 
@@ -4,27 +4,30 @@ An SDK-builder workspace lets you build one configurator from the application sp
4
4
 
5
5
  Your workspace comes with one project and one `production` environment. Its first deployment is a hello-world configurator, so you have a live link before you change anything.
6
6
 
7
- Your workspace's Studio shows the setup, the link to your configurator and the setup steps with your workspace filled in. Studio already names the CLI `salsita`, the name it is being renamed to; until that rename ships, use the `phoenix` commands on this page. If the first deployment fails, Studio shows why and lets you retry it.
7
+ Your workspace's Studio shows the setup, the link to your configurator and the setup steps with your workspace filled in. If the first deployment fails, Studio shows why and lets you retry it.
8
8
 
9
9
  ## Before you start
10
10
 
11
11
  - **Package access:** `@salsita-ai/sdk` is public on npm; `npx` and `pnpm install` download it without a registry token.
12
12
  - **Workspace details:** you need your workspace slug and its Phoenix region (`eu` or `us`), which you also received with your workspace.
13
13
  - **Tools:** Node.js and pnpm.
14
+ - **SDK build:** during the internal beta, use the `canary` build of the SDK, as the commands below do. It is published from every change to the platform, so it is newer than the last release.
14
15
 
15
16
  ## 1. Create a project
16
17
 
17
18
  ```bash
18
- npx -p @salsita-ai/sdk phoenix init my-configurator
19
+ npx -p @salsita-ai/sdk@canary phoenix init my-configurator
19
20
  cd my-configurator
20
21
  pnpm install
21
22
  pnpm phoenix sync
22
23
  ```
23
24
 
24
- `phoenix init [folder]` writes the starter project: `package.json` and the starter spec, `spec/application.yaml` and `spec/configurator.yaml`, which describe one box you can resize and recolour. The starter depends on the same SDK version as the CLI that created it. `phoenix sync` then adds the rest of the project: package scripts, webhooks, README and lint config.
25
+ `phoenix init [folder]` writes the starter project: `package.json`, `pnpm-workspace.yaml` and the starter spec, `spec/application.yaml` and `spec/configurator.yaml`, which describe one box you can resize and recolour. The starter depends on the same SDK version as the CLI that created it. `phoenix sync` then adds the rest of the project: package scripts, webhooks, README and lint config.
25
26
 
26
27
  `init` only writes these files: it asks nothing and contacts no server. It refuses a folder that already has a `spec/` folder. A `package.json` already in the folder is kept, as long as it depends on the SDK.
27
28
 
29
+ `pnpm-workspace.yaml` holds the pnpm settings the install needs: it allows the build scripts of the SDK's dependencies and exempts `@salsita-ai/*` packages from pnpm's minimum release age, so a build of the SDK published today installs. The file is yours to edit, for example to allow the build script of a dependency you add. `phoenix sync` adds these entries when they are missing and keeps the values you set. A project inside a pnpm monorepo gets no file of its own; the workspace root holds the settings.
30
+
28
31
  The starter `spec/application.yaml` begins with `project: default`, which means the default project of the workspace you sign in to. Leave it as it is; a workspace has one project.
29
32
 
30
33
  ## 2. Sign in
@@ -54,12 +57,14 @@ The CLI stores the token in `~/.phoenix/credentials.json`, readable only by you,
54
57
  pnpm phoenix dev
55
58
  ```
56
59
 
57
- Signed in, `phoenix dev` needs no `.env` and nothing set up in Studio. When it starts, it asks the server for your development environment in the project the spec names. The server creates that environment the first time and finds it again on every later run. `phoenix dev` prints the workspace, project and environment it works in, prepares a development deployment there and opens the webhook tunnel, as for any Phoenix project (see [Phoenix CLI](?path=/docs/phoenix-phoenix-cli--docs)).
60
+ Signed in, `phoenix dev` needs no `.env` and nothing set up in Studio. When it starts, it asks the server for your development environment in the project the spec names. The server creates that environment the first time and finds it again on every later run. `phoenix dev` prints the workspace, project and environment it works in, prepares a development deployment there and opens the webhook tunnel, as for any Phoenix project (see [Phoenix CLI](?path=/docs/phoenix-phoenix-cli--docs)). It then prints the address of the preview as `Preview: <url>`; the preview talks to the server the deployment was prepared on.
58
61
 
59
62
  Every user of the workspace gets their own development environment, so a teammate who clones the project and signs in doesn't overwrite your schema or take over your webhooks. The development environment is separate from `production`: nothing you do in `phoenix dev` changes the live configurator.
60
63
 
61
64
  `pnpm dev:local` still runs the project without any server, with state kept in the browser.
62
65
 
66
+ Coding agents follow the generated `phoenix` skill: before their first change they start `phoenix dev` and open the preview in your browser, and after `phoenix deploy` starts a build they open its page in Studio.
67
+
63
68
  To build the configurator with your coding agent, give it your product documents, prices, pictures, drawings or models and ask it to create the configurator. The generated `phoenix-create-configurator` skill guides it through five stages: a representative first slice you can try early, a plan of the whole configuration experience, options and features added in small increments, finalization, and a complete review. It studies the SDK documentation before building, keeps a `configurator-spec.md` in the project as the record of what is decided and what remains open, and asks you only for decisions it cannot derive from your materials.
64
69
 
65
70
  ## 4. Deploy
@@ -71,11 +76,11 @@ pnpm phoenix deploy
71
76
  `phoenix deploy` goes through these steps:
72
77
 
73
78
  1. It validates the application spec.
74
- 2. It packs the project files: the `.yaml` files in `spec`, everything in `src`, `mocks`, `assets`, `public` and `templates`, and `package.json`, `pnpm-lock.yaml`, `.gitignore`, `CLAUDE.md` and `.cursor/rules/phoenix.mdc`. Every `node_modules`, `.phoenix` and `__generated` folder, every `.DS_Store` and `._*` file and every `.env` and `.env.*` file are left out, at any depth.
79
+ 2. It packs the project files: the `.yaml` files in `spec`, everything in `src`, `mocks`, `assets`, `public` and `templates`, and `package.json`, `pnpm-lock.yaml`, `pnpm-workspace.yaml`, `.gitignore`, `CLAUDE.md` and `.cursor/rules/phoenix.mdc`. Every `node_modules`, `.phoenix` and `__generated` folder, every `.DS_Store` and `._*` file and every `.env` and `.env.*` file are left out, at any depth.
75
80
  3. It uploads the archive, which may be up to 200 MB.
76
- 4. It starts the build and follows it until your configurator is live, then prints its URL.
81
+ 4. It starts the build, prints a link to the build's page in Studio and follows the build until your configurator is live, then prints its URL.
77
82
 
78
- On the server, the build runs without platform secrets. It sees only your own environment variables and the public platform values, the [build environment](?path=/docs/phoenix-phoenix-cli--docs) that `phoenix deployment:build-envs` writes. The trusted deploy step then publishes the frontend, webhooks and assets.
83
+ On the server, the install follows your `pnpm-lock.yaml` and `pnpm-workspace.yaml`, so it allows the same build scripts and package versions as `pnpm install` on your computer. The build runs without platform secrets. It sees only your own environment variables and the public platform values, the [build environment](?path=/docs/phoenix-phoenix-cli--docs) that `phoenix deployment:build-envs` writes. The trusted deploy step then publishes the frontend, webhooks and assets.
79
84
 
80
85
  If the build fails, is cancelled or takes too long, `phoenix deploy` prints the end of the build log and exits with an error. `--no-wait` returns as soon as the build starts.
81
86
 
@@ -90,7 +95,7 @@ Tell us what is broken, confusing or missing, from wherever you are:
90
95
  - In Studio, **Send beta feedback** in the sidebar opens a short form.
91
96
  - In the terminal, `pnpm phoenix feedback "<message>" --type bug|idea|other` sends it with your sign-in. See the [CLI reference](?path=/docs/phoenix-phoenix-cli--docs).
92
97
 
93
- Coding agents working in your project use the same command, guided by the generated `phoenix-feedback` skill. They never send on their own: an agent shows you a draft, right away when the problem is crucial and otherwise at the end of the task, and sends it only after you approve it. They are told never to include secrets, customer data or your project's source.
98
+ Coding agents working in your project use the same command, guided by the generated `phoenix-feedback` skill. They never send on their own: an agent shows you a draft, right away when the problem is crucial and otherwise at the end of the task, and sends it only after you approve it. They are told never to include secrets, customer data or your project's source. After a deploy goes live, the agent also asks you, once per conversation, to rate building with Phoenix from 0 to 10; it tells you the answer goes to the Phoenix team, shows the message and sends it only after you approve it.
94
99
 
95
100
  ## Known limits
96
101