@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.
- package/dist/application-spec/generate-application/code/project-scaffolding.js +2 -2
- package/dist/application-spec/generate-application/code/templates/index.d.ts +1 -1
- package/dist/application-spec/generate-application/code/templates/index.js +1 -1
- package/dist/application-spec/generate-application/code/templates/phoenix-feedback-skill.js +14 -2
- package/dist/application-spec/generate-application/code/templates/phoenix-skill.d.ts +13 -1
- package/dist/application-spec/generate-application/code/templates/phoenix-skill.js +1 -1
- package/dist/configurator/application-runtime/extensions/systems/camera-control-system/camera-control-system.d.ts +7 -3
- package/dist/configurator/application-runtime/extensions/systems/camera-control-system/camera-control-system.js +1 -1
- package/dist/configurator/application-runtime/extensions/systems/camera-control-system/camera-system.d.ts +24 -1
- package/dist/configurator/application-runtime/extensions/systems/camera-control-system/camera-system.js +1 -1
- package/dist/docs/3d-scene/cameras/custom.mdx +4 -1
- package/dist/docs/3d-scene/cameras/fit-entity.mdx +2 -0
- package/dist/docs/3d-scene/cameras/fit-root.mdx +2 -0
- package/dist/docs/3d-scene/cameras/fit-selection.mdx +2 -0
- package/dist/docs/3d-scene/cameras/locks.mdx +439 -0
- package/dist/docs/3d-scene/cameras/static.mdx +2 -0
- package/dist/docs/3d-scene/cameras/transitions.mdx +2 -0
- package/dist/docs/index.json +12 -1
- package/dist/docs/phoenix/phoenix-cli.mdx +6 -5
- package/dist/docs/phoenix/project-structure.mdx +1 -1
- package/dist/docs/phoenix/sdk-builder-workflow.mdx +13 -8
- package/dist/domain/projections/common/camera-orbit.d.ts +37 -9
- package/dist/domain/projections/common/camera-orbit.js +1 -1
- package/dist/domain/projections/common/index.d.ts +1 -1
- package/dist/domain/projections/common/index.js +1 -1
- package/dist/domain/system-components/fit-entity-orthographic-camera-component.d.ts +8 -0
- package/dist/domain/system-components/fit-entity-orthographic-camera-component.js +1 -1
- package/dist/domain/system-components/fit-entity-perspective-camera-component.d.ts +8 -0
- package/dist/domain/system-components/fit-entity-perspective-camera-component.js +1 -1
- package/dist/domain/system-components/fit-root-orthographic-camera-component.d.ts +8 -0
- package/dist/domain/system-components/fit-root-orthographic-camera-component.js +1 -1
- package/dist/domain/system-components/fit-root-perspective-camera-component.d.ts +8 -0
- package/dist/domain/system-components/fit-root-perspective-camera-component.js +1 -1
- package/dist/domain/system-components/fit-selection-orthographic-camera-component.d.ts +8 -0
- package/dist/domain/system-components/fit-selection-orthographic-camera-component.js +1 -1
- package/dist/domain/system-components/fit-selection-perspective-camera-component.d.ts +8 -0
- package/dist/domain/system-components/fit-selection-perspective-camera-component.js +1 -1
- package/dist/domain/system-components/static-orthographic-camera-component.d.ts +8 -0
- package/dist/domain/system-components/static-orthographic-camera-component.js +1 -1
- package/dist/domain/system-components/static-perspective-camera-component.d.ts +8 -0
- package/dist/domain/system-components/static-perspective-camera-component.js +1 -1
- package/dist/legacy/domain-spec/__generated/domain-schema.d.ts +16 -0
- package/dist/phoenix/application-spec/project-files.js +1 -1
- package/dist/phoenix/cli.js +1 -1
- package/dist/phoenix/commands/deploy.js +2 -2
- package/dist/phoenix/commands/dev.js +4 -4
- package/dist/phoenix/commands/init.d.ts +2 -1
- package/dist/phoenix/commands/init.js +1 -1
- package/dist/phoenix/commands/login.js +2 -2
- package/dist/phoenix/commands/sync.js +1 -1
- package/dist/phoenix/services/cli-credentials.d.ts +3 -0
- package/dist/phoenix/services/cli-credentials.js +1 -1
- package/dist/phoenix/utils/index.d.ts +1 -1
- package/dist/phoenix/utils/index.js +1 -1
- package/dist/phoenix/utils/package-info.d.ts +9 -0
- package/dist/phoenix/utils/package-info.js +1 -1
- package/package.json +3 -3
- 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' */}
|
package/dist/docs/index.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt": "2026-10-
|
|
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.
|
|
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
|
|
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
|
|