@salsita-ai/sdk 0.50.0-develop.8c8c362f86 → 0.50.0-develop.8e3768cb11

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 (293) hide show
  1. package/dist/application-spec/core/components/component-filter.d.ts +53 -0
  2. package/dist/application-spec/core/components/component-filter.js +1 -0
  3. package/dist/application-spec/core/components/index.d.ts +2 -0
  4. package/dist/application-spec/core/components/index.js +1 -0
  5. package/dist/application-spec/core/components/sdk-code-ref.d.ts +9 -0
  6. package/dist/application-spec/core/components/sdk-code-ref.js +1 -0
  7. package/dist/application-spec/core/enhanced-json-value.d.ts +0 -68
  8. package/dist/application-spec/core/enhanced-json-value.js +1 -1
  9. package/dist/application-spec/core/expressions/index.d.ts +2 -0
  10. package/dist/application-spec/core/expressions/index.js +1 -0
  11. package/dist/application-spec/core/expressions/relation-expression-params.d.ts +11 -0
  12. package/dist/application-spec/core/expressions/relation-expression-params.js +1 -0
  13. package/dist/application-spec/core/expressions/sdk-expression-implementation.d.ts +24 -0
  14. package/dist/application-spec/core/expressions/sdk-expression-implementation.js +1 -0
  15. package/dist/application-spec/core/index.d.ts +2 -0
  16. package/dist/application-spec/core/index.js +1 -1
  17. package/dist/application-spec/core/json-value.d.ts +1 -1
  18. package/dist/application-spec/{core → enhanced-json}/eval-to-constant.d.ts +1 -1
  19. package/dist/application-spec/enhanced-json/index.d.ts +4 -0
  20. package/dist/application-spec/enhanced-json/index.js +1 -0
  21. package/dist/application-spec/enhanced-json/is-enhanced-json-array.d.ts +2 -0
  22. package/dist/application-spec/enhanced-json/is-enhanced-json-array.js +1 -0
  23. package/dist/application-spec/enhanced-json/resolve-enhanced-json-value.d.ts +18 -0
  24. package/dist/application-spec/enhanced-json/resolve-enhanced-json-value.js +1 -0
  25. package/dist/application-spec/enhanced-json/schema-from-value.d.ts +24 -0
  26. package/dist/application-spec/enhanced-json/schema-from-value.js +1 -0
  27. package/dist/application-spec/enhanced-json/stringify-enhanced-json-value.d.ts +14 -0
  28. package/dist/application-spec/enhanced-json/stringify-enhanced-json-value.js +1 -0
  29. package/dist/application-spec/enhanced-json/validate-enhanced-json-value-caps.d.ts +12 -0
  30. package/dist/application-spec/enhanced-json/validate-enhanced-json-value-caps.js +1 -0
  31. package/dist/application-spec/enhanced-json/validate-no-legacy-code-ref.d.ts +13 -0
  32. package/dist/application-spec/enhanced-json/validate-no-legacy-code-ref.js +1 -0
  33. package/dist/application-spec/generate-application/code/generate-application-code.d.ts +3 -2
  34. package/dist/application-spec/generate-application/code/generate-application-code.js +1 -1
  35. package/dist/application-spec/generate-application/code/project-scaffolding.d.ts +9 -4
  36. package/dist/application-spec/generate-application/code/project-scaffolding.js +2 -2
  37. package/dist/application-spec/generate-application/code/standalone-app-files.d.ts +2 -2
  38. package/dist/application-spec/generate-application/code/standalone-app-files.js +1 -1
  39. package/dist/application-spec/generate-application/code/templates/configurator-config.js +6 -4
  40. package/dist/application-spec/generate-application/code/templates/domain-spec/generate-component-optics.js +4 -4
  41. package/dist/application-spec/generate-application/code/templates/domain-spec/generate-defaults.js +1 -1
  42. package/dist/application-spec/generate-application/code/templates/gitignore.js +1 -2
  43. package/dist/application-spec/generate-application/code/templates/index.d.ts +1 -1
  44. package/dist/application-spec/generate-application/code/templates/index.js +1 -1
  45. package/dist/application-spec/generate-application/code/templates/package-json.js +1 -1
  46. package/dist/application-spec/generate-application/code/templates/root-tsconfig.d.ts +6 -0
  47. package/dist/application-spec/generate-application/code/templates/root-tsconfig.js +2 -0
  48. package/dist/application-spec/generate-application/code/templates/skills/phoenix-review-skill.js +1 -1
  49. package/dist/application-spec/generate-application/code/templates/skills/phoenix-skill.js +1 -1
  50. package/dist/application-spec/generate-application/code/templates/skills/sdk-content.d.ts +10 -4
  51. package/dist/application-spec/generate-application/code/templates/skills/sdk-content.js +2 -2
  52. package/dist/application-spec/generate-application/code/templates/tsconfig.d.ts +5 -0
  53. package/dist/application-spec/generate-application/code/templates/tsconfig.js +1 -1
  54. package/dist/application-spec/generate-application/code/templates/webhook-index.d.ts +6 -1
  55. package/dist/application-spec/generate-application/code/templates/webhook-index.js +10 -1
  56. package/dist/application-spec/generate-application/index.d.ts +2 -0
  57. package/dist/application-spec/generate-application/index.js +1 -1
  58. package/dist/application-spec/generate-application/runtime/admin-runtime/admin-runtime.d.ts +6 -2
  59. package/dist/application-spec/generate-application/runtime/admin-runtime/admin-runtime.js +1 -1
  60. package/dist/application-spec/generate-application/runtime/application-runtime.d.ts +8 -1
  61. package/dist/application-spec/generate-application/runtime/application-runtime.js +1 -1
  62. package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-runtime.d.ts +5 -1
  63. package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-runtime.js +1 -1
  64. package/dist/application-spec/generate-application/runtime/factory-data/factory-data-runtime.d.ts +43 -0
  65. package/dist/application-spec/generate-application/runtime/factory-data/factory-data-runtime.js +1 -0
  66. package/dist/application-spec/generate-application/runtime/factory-data/index.d.ts +3 -0
  67. package/dist/application-spec/generate-application/runtime/factory-data/index.js +1 -0
  68. package/dist/application-spec/generate-application/runtime/factory-data/recipe-defaults.d.ts +13 -0
  69. package/dist/application-spec/generate-application/runtime/factory-data/recipe-defaults.js +1 -0
  70. package/dist/application-spec/generate-application/runtime/factory-data/static-violations.d.ts +16 -0
  71. package/dist/application-spec/generate-application/runtime/factory-data/static-violations.js +1 -0
  72. package/dist/application-spec/generate-application/runtime/migrations/index.d.ts +1 -0
  73. package/dist/application-spec/generate-application/runtime/migrations/index.js +1 -0
  74. package/dist/application-spec/generate-application/runtime/migrations/migration-runtime.d.ts +53 -0
  75. package/dist/application-spec/generate-application/runtime/migrations/migration-runtime.js +1 -0
  76. package/dist/application-spec/generate-application/runtime/sections/build-sdk-optic.d.ts +1 -1
  77. package/dist/application-spec/generate-application/runtime/sections/build-sdk-optic.js +1 -1
  78. package/dist/application-spec/generate-application/runtime/sections/expression-runtime.js +1 -1
  79. package/dist/application-spec/index.d.ts +1 -0
  80. package/dist/application-spec/index.js +1 -1
  81. package/dist/application-spec/parse-application-files/categorize/categorize-files.d.ts +16 -0
  82. package/dist/application-spec/parse-application-files/categorize/categorize-files.js +1 -1
  83. package/dist/application-spec/parse-application-files/categorize/earlier-generated-tsconfig.d.ts +9 -0
  84. package/dist/application-spec/parse-application-files/categorize/earlier-generated-tsconfig.js +1 -0
  85. package/dist/application-spec/parse-application-files/raw-spec/raw-spec-file.js +1 -1
  86. package/dist/application-spec/parse-application-files/spec/components/component-entry.d.ts +1 -9
  87. package/dist/application-spec/parse-application-files/spec/components/component-entry.js +1 -1
  88. package/dist/application-spec/parse-application-files/spec/components/component-filter.d.ts +1 -54
  89. package/dist/application-spec/parse-application-files/spec/components/component-filter.js +1 -1
  90. package/dist/application-spec/parse-application-files/spec/components/index.d.ts +1 -1
  91. package/dist/application-spec/parse-application-files/spec/components/index.js +1 -1
  92. package/dist/application-spec/parse-application-files/spec/expressions/expression.d.ts +1 -28
  93. package/dist/application-spec/parse-application-files/spec/expressions/expression.js +1 -1
  94. package/dist/application-spec/parse-application-files/spec/expressions/expressions.d.ts +1 -1
  95. package/dist/application-spec/parse-application-files/spec/expressions/expressions.js +1 -1
  96. package/dist/application-spec/parse-application-files/spec/expressions/index.d.ts +0 -1
  97. package/dist/application-spec/parse-application-files/spec/expressions/index.js +1 -1
  98. package/dist/application-spec/parse-application-files/spec/expressions/memo-eval-plan.js +1 -1
  99. package/dist/application-spec/parse-application-files/spec/expressions/user-expressions.js +1 -1
  100. package/dist/application-spec/parse-application-files/spec/model/defaults.js +1 -1
  101. package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.d.ts +29 -0
  102. package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.js +1 -1
  103. package/dist/configurator/application-runtime/extensions/systems/assistant-system/graphql-tools.d.ts +7 -1
  104. package/dist/configurator/application-runtime/extensions/systems/assistant-system/graphql-tools.js +1 -1
  105. package/dist/configurator/application-runtime/extensions/systems/drag-and-drop-system/drag-and-drop-system.d.ts +3 -3
  106. package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.d.ts +6 -0
  107. package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.js +1 -1
  108. package/dist/configurator/application-runtime/extensions/systems/layout-system/layout-system.d.ts +2 -2
  109. package/dist/configurator/common/configurator-interface.d.ts +22 -1
  110. package/dist/configurator/common/configurator-interface.js +1 -1
  111. package/dist/configurator/common/frontend-analytics.d.ts +2 -0
  112. package/dist/configurator/common/frontend-analytics.js +1 -1
  113. package/dist/configurator/common/index.d.ts +2 -0
  114. package/dist/configurator/common/index.js +1 -1
  115. package/dist/configurator/common/use-finalize-action.d.ts +31 -0
  116. package/dist/configurator/common/use-finalize-action.js +1 -0
  117. package/dist/configurator/common/use-reset-camera.d.ts +6 -0
  118. package/dist/configurator/common/use-reset-camera.js +1 -0
  119. package/dist/configurator/configurator-context/configurator-hooks/layout-hooks.d.ts +1 -1
  120. package/dist/configurator/pages/DefaultConfiguratorLayout.d.ts +2 -2
  121. package/dist/configurator/pages/LoadedConfiguratorDetailPage.js +1 -1
  122. package/dist/configurator/pages/configurator-layout-name.d.ts +2 -0
  123. package/dist/configurator/pages/configurator-layout-name.js +1 -0
  124. package/dist/configurator/pages/default-configurator-elements.d.ts +7 -0
  125. package/dist/configurator/pages/default-configurator-elements.js +1 -0
  126. package/dist/configurator/pages/index.d.ts +1 -0
  127. package/dist/configurator/pages/index.js +1 -1
  128. package/dist/configurator/scene/scene-controls/hotspot-drag-handle/HotspotDragHandle.d.ts +1 -1
  129. package/dist/configurator/scene/scene-controls/hotspot-drag-handle/HotspotDragSceneHandle.d.ts +1 -1
  130. package/dist/configurator/scene/scene-entity/dimension-indicators/DimensionIndicator.d.ts +1 -1
  131. package/dist/configurator/scene/scene-entity/dimension-indicators/DimensionIndicatorInternal.d.ts +1 -1
  132. package/dist/configurator/scene/scene-entity/drag-and-drop/shared.d.ts +2 -2
  133. package/dist/configurator/scene/scene-entity/drag-and-drop/use-drag-object.d.ts +1 -1
  134. package/dist/configurator/scene/scene-entity/materials/RemoteReplacedMaterial.js +1 -1
  135. package/dist/configurator/scene/scene-entity/objects/SceneEntityObjectTextDecal.d.ts +1 -1
  136. package/dist/configurator/ui/assistant/AssistantChips.d.ts +8 -2
  137. package/dist/configurator/ui/assistant/AssistantChips.js +1 -1
  138. package/dist/configurator/ui/assistant/AssistantMarkdown.d.ts +1 -1
  139. package/dist/configurator/ui/assistant/AssistantMarkdown.js +1 -1
  140. package/dist/configurator/ui/assistant/AssistantThread.js +1 -1
  141. package/dist/configurator/ui/assistant/entity-link/AssistantEntityLink.d.ts +13 -0
  142. package/dist/configurator/ui/assistant/entity-link/AssistantEntityLink.js +1 -0
  143. package/dist/configurator/ui/assistant/entity-link/entity-link-selection.d.ts +22 -0
  144. package/dist/configurator/ui/assistant/entity-link/entity-link-selection.js +1 -0
  145. package/dist/configurator/ui/assistant/entity-link/entity-link-state.d.ts +27 -0
  146. package/dist/configurator/ui/assistant/entity-link/entity-link-state.js +1 -0
  147. package/dist/configurator/ui/assistant/entity-link/entity-link-target.d.ts +5 -0
  148. package/dist/configurator/ui/assistant/entity-link/entity-link-target.js +1 -0
  149. package/dist/configurator/ui/assistant/entity-link/index.d.ts +3 -0
  150. package/dist/configurator/ui/assistant/entity-link/index.js +1 -0
  151. package/dist/configurator/ui/configurator-layout/slots/services/InvariantViolations.d.ts +4 -4
  152. package/dist/configurator/ui/configurator-layout/slots/services/InvariantViolations.js +1 -1
  153. package/dist/configurator/ui/configurator-layout/slots/services/SideEffects.d.ts +3 -6
  154. package/dist/configurator/ui/configurator-layout/slots/services/SideEffects.js +1 -1
  155. package/dist/configurator/ui/legacy/layout/steps/StepsNavigation.d.ts +2 -2
  156. package/dist/configurator/ui/phoenix-layout-elements/header/common/step-navigation/StepsNavigation.d.ts +2 -2
  157. package/dist/docs/dimensions/3d-scene-integration/dimension-indicators.mdx +2 -2
  158. package/dist/docs/dimensions/3d-scene-integration/editable-angle-indicator.mdx +1 -1
  159. package/dist/docs/dimensions/3d-scene-integration/editable-distance-dimension-indicator.mdx +1 -1
  160. package/dist/docs/dimensions/3d-scene-integration/list-selector-dimension-indicator.mdx +1 -1
  161. package/dist/docs/drag-and-drop/drag-handle.mdx +1 -1
  162. package/dist/docs/drag-and-drop/hotspot-handle.mdx +1 -1
  163. package/dist/docs/drag-and-drop/planes.mdx +1 -1
  164. package/dist/docs/drag-and-drop/rotatable.mdx +2 -2
  165. package/dist/docs/index.json +11 -1
  166. package/dist/docs/layouts/custom-layout.mdx +1323 -179
  167. package/dist/docs/layouts/header/fixed.mdx +2 -2
  168. package/dist/docs/layouts/header/floating.mdx +2 -2
  169. package/dist/docs/layouts/sidebar/in-a-custom-layout.mdx +2 -2
  170. package/dist/docs/layouts/sidebar-footer/basic.mdx +32 -77
  171. package/dist/docs/other-features/client-side-rendering/declarative.mdx +1 -1
  172. package/dist/docs/phoenix/application-states.mdx +9 -7
  173. package/dist/docs/phoenix/configurator-interface.mdx +134 -18
  174. package/dist/docs/phoenix/finalization.mdx +1 -1
  175. package/dist/docs/phoenix/layout-slots.mdx +116 -0
  176. package/dist/docs/phoenix/migration-guide.mdx +19 -8
  177. package/dist/docs/phoenix/phoenix-cli.mdx +26 -8
  178. package/dist/docs/phoenix/project-structure.mdx +20 -5
  179. package/dist/docs/phoenix/sdk-builder-workflow.mdx +2 -2
  180. package/dist/docs/phoenix/webhooks.mdx +17 -0
  181. package/dist/domain/application/application-config.d.ts +3 -1
  182. package/dist/domain/application/assistant/generate-assistant-prompt.js +1 -1
  183. package/dist/domain/application/configurator-builder.d.ts +4 -1
  184. package/dist/domain/application/configurator-builder.js +1 -1
  185. package/dist/domain/core/invariant/violation.d.ts +50 -2
  186. package/dist/domain/core/invariant/violation.js +1 -1
  187. package/dist/domain/core/model/model.d.ts +1 -1
  188. package/dist/domain/domain-context/index.d.ts +1 -1
  189. package/dist/domain/domain-context/undo-system.d.ts +28 -1
  190. package/dist/domain/domain-context/undo-system.js +1 -1
  191. package/dist/domain/domain-runtime/spec/initialize.js +1 -1
  192. package/dist/domain/domain-runtime/spec/selectors.js +1 -1
  193. package/dist/domain/domain-runtime/spec/types.d.ts +2 -0
  194. package/dist/domain/extensions/attribute-switch.d.ts +1 -1
  195. package/dist/domain/extensions/attribute-switch.js +1 -1
  196. package/dist/domain/extensions/canvas-config.d.ts +1 -1
  197. package/dist/domain/extensions/client-side-image.d.ts +2 -2
  198. package/dist/domain/extensions/client-side-image.js +1 -1
  199. package/dist/domain/extensions/common/angle-indicator-schema.d.ts +2 -2
  200. package/dist/domain/extensions/common/dimension-indicator-schema.d.ts +2 -2
  201. package/dist/domain/extensions/editable-angle-indicator.d.ts +1 -1
  202. package/dist/domain/extensions/editable-distance-dimension-indicator.d.ts +1 -1
  203. package/dist/domain/extensions/editable-offset-indicator.d.ts +1 -1
  204. package/dist/domain/extensions/editable-offset-indicator.js +1 -1
  205. package/dist/domain/extensions/fit-selection-orthographic-camera-component.js +1 -1
  206. package/dist/domain/extensions/fit-selection-perspective-camera-component.js +1 -1
  207. package/dist/domain/extensions/horizontal-tabs.d.ts +2 -2
  208. package/dist/domain/extensions/list-selector-dimension-indicator.d.ts +1 -1
  209. package/dist/domain/extensions/navigation-steps.d.ts +2 -2
  210. package/dist/domain/extensions/offset-indicator.js +1 -1
  211. package/dist/domain/extensions/rotation-handle.d.ts +2 -2
  212. package/dist/domain/extensions/save-template-thumbnail.d.ts +2 -2
  213. package/dist/domain/extensions/save-template-thumbnail.js +1 -1
  214. package/dist/domain/extensions/scene-entity-animation.js +1 -1
  215. package/dist/domain/system-components/application-config.d.ts +2 -2
  216. package/dist/domain/system-components/child-entity-selection.js +1 -1
  217. package/dist/domain/system-components/collision-box-geometry.d.ts +4 -2
  218. package/dist/domain/system-components/collision-geometry.d.ts +4 -2
  219. package/dist/domain/system-components/collision-point-geometry.d.ts +4 -2
  220. package/dist/domain/system-components/common/material-override-schema.d.ts +2 -2
  221. package/dist/domain/system-components/common/recipes.d.ts +21 -3
  222. package/dist/domain/system-components/common/recipes.js +1 -1
  223. package/dist/domain/system-components/constraint.d.ts +10 -23
  224. package/dist/domain/system-components/drag-handle.d.ts +1 -1
  225. package/dist/domain/system-components/drag-handle.js +1 -1
  226. package/dist/domain/system-components/fit-entity-orthographic-camera-component.js +1 -1
  227. package/dist/domain/system-components/fit-entity-perspective-camera-component.js +1 -1
  228. package/dist/domain/system-components/fit-root-orthographic-camera-component.js +1 -1
  229. package/dist/domain/system-components/fit-root-perspective-camera-component.js +1 -1
  230. package/dist/domain/system-components/hotspot-drag-handle.d.ts +1 -1
  231. package/dist/domain/system-components/hotspot-drag-handle.js +1 -1
  232. package/dist/domain/system-components/material-swatch.js +1 -1
  233. package/dist/domain/system-components/scene-entity-material-override.d.ts +1 -1
  234. package/dist/domain/system-components/scene-entity-object-material-override.d.ts +1 -1
  235. package/dist/domain/system-components/scene-entity-object-text-decal.d.ts +1 -1
  236. package/dist/domain/system-components/scene-entity-object-transimissive-material-override.d.ts +1 -1
  237. package/dist/domain/system-components/scene-entity-transimissive-material-override.d.ts +1 -1
  238. package/dist/domain/system-components/static-orthographic-camera-component.js +1 -1
  239. package/dist/domain/system-components/static-perspective-camera-component.js +1 -1
  240. package/dist/i18n/locales/cs/messages.js +1 -1
  241. package/dist/i18n/locales/da/messages.js +1 -1
  242. package/dist/i18n/locales/de/messages.js +1 -1
  243. package/dist/i18n/locales/de-AT/messages.js +1 -1
  244. package/dist/i18n/locales/en/messages.js +1 -1
  245. package/dist/i18n/locales/en-AU/messages.js +1 -1
  246. package/dist/i18n/locales/en-GB/messages.js +1 -1
  247. package/dist/i18n/locales/es/messages.js +1 -1
  248. package/dist/i18n/locales/fr/messages.js +1 -1
  249. package/dist/i18n/locales/hu/messages.js +1 -1
  250. package/dist/i18n/locales/it/messages.js +1 -1
  251. package/dist/i18n/locales/nl/messages.js +1 -1
  252. package/dist/i18n/locales/pl/messages.js +1 -1
  253. package/dist/i18n/locales/pt/messages.js +1 -1
  254. package/dist/i18n/locales/ro/messages.js +1 -1
  255. package/dist/i18n/locales/sk/messages.js +1 -1
  256. package/dist/i18n/locales/tr/messages.js +1 -1
  257. package/dist/legacy/domain-cli/utils/cache.d.ts +1 -1
  258. package/dist/legacy/domain-cli/utils/cache.js +1 -1
  259. package/dist/legacy/domain-spec/__generated/domain-schema.d.ts +81 -68
  260. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/agents/claude-md.d.ts +1 -1
  261. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/agents/cursor-rules.d.ts +1 -1
  262. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/agents/shared-sdk-content.d.ts +1 -1
  263. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/agents/shared-sdk-content.js +7 -1
  264. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/frontend-admin.js +18 -16
  265. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/frontend-configurator.js +14 -12
  266. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/frontend-embedded-admin.js +13 -11
  267. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/frontend-embedded-configurator.js +13 -11
  268. package/dist/phoenix/application-spec/application-spec-builder.js +1 -1
  269. package/dist/phoenix/application-spec/project-files.js +1 -1
  270. package/dist/phoenix/commands/check.js +1 -1
  271. package/dist/phoenix/commands/deploy.js +1 -1
  272. package/dist/phoenix/commands/dev.js +5 -5
  273. package/dist/phoenix/commands/fix.js +1 -1
  274. package/dist/phoenix/services/application-spec-project-watcher.d.ts +2 -0
  275. package/dist/phoenix/services/application-spec-project-watcher.js +3 -3
  276. package/dist/phoenix/services/source-archive.d.ts +1 -0
  277. package/dist/phoenix/services/source-archive.js +1 -1
  278. package/dist/phoenix/services/swc.js +1 -1
  279. package/dist/react-sdk/domain-context/common-hooks/use-configurator-messages.d.ts +30 -0
  280. package/dist/react-sdk/domain-context/common-hooks/use-configurator-messages.js +1 -0
  281. package/dist/react-sdk/domain-context/index.d.ts +2 -1
  282. package/dist/react-sdk/domain-context/index.js +1 -1
  283. package/dist/react-sdk/domain-context/system-hooks/navigation-hooks.d.ts +5 -0
  284. package/dist/react-sdk/domain-context/system-hooks/navigation-hooks.js +1 -1
  285. package/dist/react-sdk/domain-controls/hooks/use-angle-dimension.d.ts +1 -1
  286. package/dist/ui-sdk/theme/types.d.ts +1 -1
  287. package/package.json +33 -29
  288. package/schema/domain-spec.json +1115 -209
  289. package/dist/application-spec/generate-application/code/templates/webhook-implementation.d.ts +0 -1
  290. package/dist/application-spec/generate-application/code/templates/webhook-implementation.js +0 -7
  291. /package/dist/application-spec/{core → enhanced-json}/eval-code-uses-self.d.ts +0 -0
  292. /package/dist/application-spec/{core → enhanced-json}/eval-code-uses-self.js +0 -0
  293. /package/dist/application-spec/{core → enhanced-json}/eval-to-constant.js +0 -0
@@ -145,7 +145,7 @@ interface HorizontalTab {
145
145
  /**
146
146
  * List of navigation states when the tab is active. First one will be used for activation.
147
147
  */
148
- readonly navigationStates: string[]
148
+ readonly navigationStates: readonly string[]
149
149
  }
150
150
  ```
151
151
 
@@ -158,7 +158,7 @@ type HorizontalTabsComponentState = {
158
158
  /**
159
159
  * List of tabs in the horizontal tabs.
160
160
  */
161
- readonly tabs: HorizontalTab[]
161
+ readonly tabs: readonly HorizontalTab[]
162
162
  }
163
163
  ```
164
164
 
@@ -110,11 +110,11 @@ The configuration uses these component types:
110
110
  ```typescript
111
111
  export interface NavigationStep {
112
112
  readonly label: Localizable
113
- readonly navigationStates: string[]
113
+ readonly navigationStates: readonly string[]
114
114
  }
115
115
 
116
116
  export type NavigationStepsComponentState = {
117
- readonly steps: NavigationStep[]
117
+ readonly steps: readonly NavigationStep[]
118
118
  }
119
119
  ```
120
120
 
@@ -90,8 +90,8 @@ The spec is a table with one property, its edge finish, on a **Design** tab and
90
90
 
91
91
  ## Known gaps
92
92
 
93
- - The finalize button is disabled unless the spec configures `finalizeButton` or `finalizePage`, as in the built-in layout. A `finalizePage` is not shown by a project layout unless the layout renders it.
94
- - `LayoutSlots.DesktopSidebar` is the desktop sidebar. At phone width a project layout needs its own arrangement, for example the `Mobile*` members of `LayoutSlots` in a drawer.
93
+ - The footer is the basic sidebar footer, as in the built-in layout when the spec configures none. Its finalize button is disabled unless the spec configures the legacy `finalizeButton` or `finalizePage` components, which are declared only in TypeScript, and a project layout shows a `finalizePage` only if it renders it. New projects finalize over `finalizeAction` components, which the universal sidebar footer offers when the spec configures `universalSidebarFooter`.
94
+ - `LayoutSlots.DesktopSidebar` is the desktop sidebar. At phone width a project layout needs its own arrangement, for example the `Mobile*` members of `LayoutSlots`, listed in [Layout Slots](?path=/docs/phoenix-layout-slots--docs#phone-layout), in a drawer.
95
95
  - The tabs overlap the scene beside the sidebar; the safe area in this example does not leave room for them.
96
96
  - `miniSidebar` does not shrink the sidebar: the built-in layout does that, and here the sidebar fills its container. A layout that honours it reads `LayoutSlots.useSidebarContent().hasMiniSidebar` and gives the container no fixed height, anchored at the bottom for example, so the sidebar takes its content's height.
97
97
 
@@ -41,11 +41,11 @@ The basic sidebar footer adapts its layout based on what's available. The finali
41
41
 
42
42
  For more control over the sidebar footer, see these related examples:
43
43
 
44
- - **[Phoenix Handlers](../?path=/docs/layouts-phoenix-handlers)**: Learn how to create custom action handlers for sidebar footer buttons
45
- - **[Read Sidebar Footer](../?path=/docs/layouts-sidebar-footer-read)**: Display price in read-only mode without action buttons
46
- - **[Universal Sidebar Footer](../?path=/docs/layouts-sidebar-footer-universal)**: Add custom text and multiple action buttons while keeping the standard layout
47
- - **[Custom Sidebar Footer](../?path=/docs/layouts-sidebar-footer-custom)**: Replace the sidebar footer with your own React components for complete control
48
- - **[Hidden Sidebar Footer](../?path=/docs/layouts-sidebar-footer-hidden)**: Remove the sidebar footer entirely for embedded or immersive experiences
44
+ - **[Phoenix Handlers](?path=/docs/layouts-phoenix-handlers--docs)**: Learn how to create custom action handlers for sidebar footer buttons
45
+ - **[Read Sidebar Footer](?path=/docs/layouts-sidebar-footer-read--docs)**: Display price in read-only mode without action buttons
46
+ - **[Universal Sidebar Footer](?path=/docs/layouts-sidebar-footer-universal--docs)**: Add custom text and multiple action buttons while keeping the standard layout
47
+ - **[Custom Sidebar Footer](?path=/docs/layouts-sidebar-footer-custom--docs)**: Replace the sidebar footer with your own React components for complete control
48
+ - **[Hidden Sidebar Footer](?path=/docs/layouts-sidebar-footer-hidden--docs)**: Remove the sidebar footer entirely for embedded or immersive experiences
49
49
 
50
50
  ## Guide
51
51
 
@@ -98,84 +98,39 @@ This configuration gives you a fully functional sidebar footer with price displa
98
98
 
99
99
  ### Customizing finalize behavior
100
100
 
101
- The sidebar footer's finalize button can be customized using two different component types. These components let you either replace the button itself or define what happens when the user clicks finalize.
101
+ New projects finalize with `finalizeAction` components and the [Universal Sidebar Footer](?path=/docs/layouts-sidebar-footer-universal--docs), which runs the action's handler. The basic sidebar footer's own customization, the `finalizeButton` and `finalizePage` components, is legacy: the spec parser rejects both in YAML, so a project that keeps them declares them in TypeScript.
102
102
 
103
- #### Using finalizeButton component
103
+ The basic sidebar footer checks for them in this order:
104
104
 
105
- The `finalizeButton` component replaces the default finalize button with your custom React component. This gives you complete control over the button's appearance and behavior.
105
+ 1. **finalizeButton**: if configured, its `button` React node is rendered instead of the finalize button, and implements the finalization and the disabled state itself
106
+ 2. **finalizePage**: if no custom button exists, a default button navigates to the first of the component's `finalizeNavigationStates`, and the built-in layout shows its `page` in a dialog while one of those states is active
107
+ 3. **No-op fallback**: if neither component is configured, a disabled button appears with the hint "Finalize feature is not configured"
106
108
 
107
- ```yaml
108
- components:
109
- Scene:
110
- finalizeButton:
111
- customFinalize:
112
- button: <CustomFinalizeButtonComponent />
113
- ```
114
-
115
- **TypeScript Interface**:
116
-
117
- ```typescript
118
- /**
119
- * State for a custom finalize button component.
120
- */
121
- interface FinalizeButtonComponentState {
122
- /**
123
- * Custom React node to render as the finalize button.
124
- * This completely replaces the default finalize button.
125
- */
126
- readonly button: ReactNode
127
- }
128
- ```
109
+ A legacy `finalizePage` is attached in TypeScript, as the [Universal](?path=/docs/layouts-universal--docs) example does in `src/components/components.tsx`:
129
110
 
130
- Your custom button component receives no special props. You must implement all the finalization logic yourself, including calling the appropriate handlers and managing the button's disabled state based on validation errors.
131
-
132
- #### Using finalizePage component
133
-
134
- The `finalizePage` component opens a modal page when the user clicks finalize. This is useful for multi-step checkout flows, order forms, or any scenario where you need to collect additional information before completing the order.
135
-
136
- ```yaml
137
- components:
138
- Scene:
139
- finalizePage:
140
- checkoutFlow:
141
- finalizeNavigationStates: ['checkout', 'payment', 'confirmation']
142
- page: <CheckoutFormComponent />
143
- ```
111
+ ```tsx
112
+ import { finalizePageComponent, memoEval } from '@salsita-ai/sdk/domain'
144
113
 
145
- **TypeScript Interface**:
114
+ // CheckoutPage is the project's own React component
115
+ import { CheckoutPage } from '../_shared/CheckoutPage.js'
146
116
 
147
- ```typescript
148
- /**
149
- * State for a finalize page component that opens a modal.
150
- */
151
- interface FinalizePageComponentState {
152
- /**
153
- * The navigation states that are active when finalization is in progress.
154
- * The first state in this array is used for initial navigation when the
155
- * finalize button is clicked.
156
- */
157
- readonly finalizeNavigationStates: string[]
117
+ // `phx` is a global in an application project; a Storybook example imports it from '../../.phoenix/phx.js'
158
118
 
159
- /**
160
- * React node to render as the page content in the finalize modal.
161
- */
162
- readonly page: ReactNode
163
- }
119
+ export default phx.domain.attachComponent
120
+ .withComponent(finalizePageComponent, 'default')
121
+ .withEntity('Scene')
122
+ .attach(
123
+ memoEval(
124
+ () => [],
125
+ () => ({
126
+ finalizeNavigationStates: [phx.applicationTypes.NavigationState.finalize],
127
+ page: <CheckoutPage />,
128
+ })
129
+ )
130
+ )
164
131
  ```
165
132
 
166
- When the user clicks the finalize button, the system navigates to the first state in `finalizeNavigationStates`. Your page component can then navigate between these states to create a multi-step flow. All states must be defined in your navigation configuration.
167
-
168
- #### Priority and fallback behavior
169
-
170
- The sidebar footer checks for finalize components in this order:
171
-
172
- 1. **finalizeButton**: If configured, this component is rendered as-is with no modifications
173
- 2. **finalizePage**: If no custom button exists, a default button is shown that opens the specified page
174
- 3. **No-op fallback**: If neither component is configured, a disabled button appears with the hint "Finalize feature is not configured"
175
-
176
- This priority system lets you customize either the button appearance or the finalization flow, but not both simultaneously. If you need both, use `finalizeButton` and implement the page navigation logic within your custom component.
177
-
178
- See the [Finalize Page Layout](../?path=/docs/layouts-simple-layout-finalize-page) example for a complete implementation of the finalize page pattern.
133
+ The states in `finalizeNavigationStates` must be listed in the spec's `navigationStates`. The universal sidebar footer never opens a `finalizePage`.
179
134
 
180
135
  ### Navigation steps influence
181
136
 
@@ -278,7 +233,7 @@ Use `simpleSummary` for basic summaries that just need to display a list of conf
278
233
 
279
234
  Both component types require a `summaryNavigationState` that must exist in your navigation configuration. The sidebar footer checks the current navigation state against this property to determine whether the summary is currently active.
280
235
 
281
- See the [Summary Layout](../?path=/docs/layouts-simple-layout-summary) example for a complete summary implementation.
236
+ See the [Sidebar Summary](?path=/docs/layouts-sidebar-summary--docs) example for a complete summary implementation.
282
237
 
283
238
  ### Custom price sidebar footer
284
239
 
@@ -317,11 +272,11 @@ When `customPriceSidebarFooter` is configured:
317
272
  - The sidebar footer layout remains unchanged - only the price display is replaced
318
273
  - Action buttons (finalize, next/previous, etc.) are not affected
319
274
 
320
- This component affects **only** the price display, not the overall sidebar footer structure. If you need to change the entire sidebar footer layout, use the [Universal Sidebar Footer](../?path=/docs/layouts-sidebar-footer-universal) or [Custom Sidebar Footer](../?path=/docs/layouts-sidebar-footer-custom) instead.
275
+ This component affects **only** the price display, not the overall sidebar footer structure. If you need to change the entire sidebar footer layout, use the [Universal Sidebar Footer](?path=/docs/layouts-sidebar-footer-universal--docs) or [Custom Sidebar Footer](?path=/docs/layouts-sidebar-footer-custom--docs) instead.
321
276
 
322
277
  The custom price node receives no special props. You must access price data yourself through the configurator hooks like `useRootEntityPrice()` or by reading entity attributes directly.
323
278
 
324
- See the [Custom Price Sidebar Footer](../?path=/docs/layouts-custom-price) example for implementation details and best practices.
279
+ See the [Custom Price Sidebar Footer](?path=/docs/layouts-custom-price--docs) example for implementation details and best practices.
325
280
 
326
281
  ## Example
327
282
 
@@ -229,7 +229,7 @@ export type ClientSideImageComponentState = {
229
229
  /** Output format: PNG (with transparency) or JPEG */
230
230
  readonly fileFormat: 'PNG' | 'JPEG'
231
231
  /** Optional background color (hex string) */
232
- readonly backgroundColor?: string
232
+ readonly backgroundColor?: HexColor
233
233
 
234
234
  /** Tool state the image is rendered in (pins component filters keyed on tool state) */
235
235
  readonly toolState: string
@@ -223,22 +223,24 @@ components:
223
223
 
224
224
  staticPerspectiveCamera:
225
225
  overview:
226
- position: [0, 10, 10]
227
- target: [0, 0, 0]
226
+ distance: 14
227
+ polarAngle: 0.8
228
+ azimuthAngle: 0
228
229
  $filter:
229
230
  viewStates: ['overview']
230
231
 
231
232
  detail:
232
- position: [2, 2, 2]
233
- target: [0, 0, 0]
233
+ distance: 3.5
234
+ polarAngle: 0.95
235
+ azimuthAngle: 0.8
234
236
  $filter:
235
237
  viewStates: ['closeup']
236
238
 
237
239
  staticOrthographicCamera:
238
240
  blueprint:
239
- position: [0, 20, 0]
240
- target: [0, 0, 0]
241
- zoom: 1.5
241
+ distance: 20
242
+ polarAngle: 0
243
+ frustumSize: 12
242
244
  $filter:
243
245
  viewStates: ['blueprint']
244
246
 
@@ -158,30 +158,35 @@ const viewState = phx.frontend.useActiveViewState()
158
158
  const toolState = phx.frontend.useActiveToolState()
159
159
 
160
160
  // Navigation actions
161
- const navigate = phx.frontend.useNavigate()
162
- const pushNavigate = phx.frontend.usePushNavigate()
163
- const popNavigate = phx.frontend.usePopNavigate()
161
+ const navigate = phx.frontend.useNavigate() // navigate(state) replaces the active state and clears the history
162
+ const pushNavigate = phx.frontend.usePushNavigate() // pushNavigate(state) remembers the active state for popNavigate
163
+ const popNavigate = phx.frontend.usePopNavigate() // popNavigate() returns to the remembered state, if there is one
164
164
 
165
165
  // Fullscreen: a flag in the configurator's state that the layout honours; it does not put the browser into fullscreen
166
166
  const isFullscreen = phx.frontend.useIsFullscreenActive()
167
167
  const setFullscreen = phx.frontend.useSetFullscreen() // setFullscreen(true) or setFullscreen(false)
168
+
169
+ // View state and camera
170
+ const setViewState = phx.frontend.useSetViewState() // setViewState(state) makes one of the spec's viewStates active
171
+ const resetCamera = phx.frontend.useResetCamera() // resetCamera() undoes the user's orbit, pan and zoom
168
172
  ```
169
173
 
170
174
  ### Controls
171
175
 
172
176
  ```typescript
173
- // Control hooks for UI inputs
174
- const swatchSelection = phx.frontend.useSwatchSelection(entityId, controlId)
175
- const textValue = phx.frontend.useTextValue(entityId, controlId)
176
- const attributeSelection = phx.frontend.useAttributeSelection(entityId, controlId)
177
- const attributeSwitch = phx.frontend.useAttributeSwitch(entityId, controlId)
178
- const distanceDimension = phx.frontend.useDistanceDimension(entityId, controlId)
179
- const angleDimension = phx.frontend.useAngleDimension(entityId, controlId)
180
- const genericDimension = phx.frontend.useGenericDimension(entityId, controlId)
177
+ // Control hooks for UI inputs: the entity id and the instance name of the control component on it
178
+ // useSwatchSelection requires its third argument, the fixable violation types
179
+ const swatchSelection = phx.frontend.useSwatchSelection(entityId, instanceName, [])
180
+ const textValue = phx.frontend.useTextValue(entityId, instanceName)
181
+ const attributeSelection = phx.frontend.useAttributeSelection(entityId, instanceName)
182
+ const attributeSwitch = phx.frontend.useAttributeSwitch(entityId, instanceName)
183
+ const distanceDimension = phx.frontend.useDistanceDimension(entityId, instanceName)
184
+ const angleDimension = phx.frontend.useAngleDimension(entityId, instanceName)
185
+ const genericDimension = phx.frontend.useGenericDimension(entityId, instanceName)
181
186
 
182
187
  // Update control
183
188
  const updateControl = phx.frontend.useUpdateControl()
184
- const updatedControl = phx.frontend.useUpdatedControl(entityId, controlId)
189
+ const updatedControl = phx.frontend.useUpdatedControl(updates)
185
190
  ```
186
191
 
187
192
  ### Recipes & Entity Creation
@@ -207,15 +212,19 @@ const deletedEntity = phx.frontend.useDeletedEntity()
207
212
  ### Validation & Violations
208
213
 
209
214
  ```typescript
210
- // State validity
215
+ // Validity of the last committed state; a rejected change never becomes committed, so it is not seen here
211
216
  const validity = phx.frontend.useStateValidity()
212
217
 
213
- // Control violations
214
- const controlViolations = phx.frontend.useControlViolations(entityId, controlId)
218
+ // Control violations: takes the violation types to ignore and returns a function that maps a prepared
219
+ // state emission to { disabled, hints, violations }
220
+ const controlViolations = phx.frontend.useControlViolations([])
215
221
 
216
222
  // Finalize violations
217
223
  const { disabled, hints } = phx.frontend.useFinalizeViolations()
218
224
 
225
+ // Messages for the user: violations of a rejected change, side-effect hints and errors, one call each
226
+ phx.frontend.useConfiguratorMessages(message => showToast(message.text))
227
+
219
228
  // Map violations to hints
220
229
  const mapViolationHints = phx.frontend.useMapViolationHints()
221
230
  ```
@@ -420,12 +429,119 @@ export default phx.frontend.createCustomPhoenixLayoutElement('ConfiguratorLayout
420
429
  - `LayoutSlots.SharedCanvas` renders the scene. It is absolutely positioned and fills its nearest positioned ancestor, so the layout's root is positioned and covers the page. The layout positions its own UI above the canvas and after it in the markup, as the `aside` does; unpositioned content is painted under the canvas.
421
430
  - `useSceneSafeArea()`, called without arguments, tells the camera which part of the canvas the UI leaves free. Attach `containerRef` to the element that covers the whole canvas and `safeAreaRef` to the region the scene should stay visible in: the hook measures the gap between the two and the camera frames the scene inside the safe area. Without it the scene is framed in the whole canvas. The safe-area element lies over the canvas, so it sets `pointerEvents: 'none'` to leave orbiting and clicking to the scene. A `canvasPadding` component in the spec scales the measured gaps, see [Canvas Padding](?path=/docs/layouts-canvas-padding--docs).
422
431
  - The layout has to mount `LayoutSlots.SharedCanvas`, or the project sets `appLoader: legacy`. Otherwise the app loader stays on screen.
423
- - The layout composes the rest from what `@salsita-ai/sdk/configurator` exports: `LayoutSlots.Services` and the other members of `LayoutSlots`.
432
+ - The layout composes the rest from what `@salsita-ai/sdk/configurator` exports: `LayoutSlots.Services` and the other members of `LayoutSlots`, which [Layout Slots](?path=/docs/phoenix-layout-slots--docs) lists with what each shows, what feeds it, what it renders when the spec configures nothing for it and what it does in read mode.
424
433
  - `LayoutSlots.DesktopSidebar` is the built-in desktop sidebar as one piece: the spec's tabs, back button, property pages and sidebar footer with their spacing, scrolling and empty states, and the assistant panel. Use it when the spec's property pages are what the customer should see and only their place changes, for example a sidebar on the left; build the controls yourself when they have to look like the brand's own. Without `width` it fills its container, so the layout positions and sizes that container; `miniSidebar` does not shrink it, so a layout that honours it reads `LayoutSlots.useSidebarContent().hasMiniSidebar` and lets the container take the sidebar's height. Its tabs sit outside it on the left; `side="start"` puts them on its right, for a sidebar on the left. In fullscreen it stays in place, transparent and inert, so a layout that wants the space back removes it while `phx.frontend.useIsFullscreenActive()` is set. See [Sidebar in a Custom Layout](?path=/docs/layouts-sidebar-in-a-custom-layout--docs).
425
- - Nothing of the built-in layouts is rendered for it. `Legacy.CommonFinalizePage` shows the page of the `finalizePage` component in a dialog while one of its finalize navigation states is active, so a layout that shows a finalize button mounts it; without it the button changes the navigation state and nothing appears. It is marked deprecated and is the only exported piece that does this. `LayoutDialog` renders only what the project registers with `createLayoutDialog` and is not part of the finalize flow. The preview start screen is `DesktopPreview` or `MobilePreview`, which the layout shows while `usePreview().isPreview` is set. Both previews frame the scene themselves, so the layout renders the component that calls `useSceneSafeArea()` only while `isPreview` is not set; it then measures when it mounts after the preview. The assistant panel is available to a project layout only inside `LayoutSlots.DesktopSidebar`.
426
- - The layout reads the state it has to react to from hooks. Read mode: `phx.frontend.useAccessService().accessInfo.viewMode`. Fullscreen: `phx.frontend.useIsFullscreenActive()` reads it and `phx.frontend.useSetFullscreen()` sets it; the layout decides what to hide. Preview: `usePreview()` from `@salsita-ai/sdk/configurator`.
434
+ - Nothing of the built-in layouts is rendered for it. A project layout finalizes over the spec's `finalizeAction` components, see the finalize paragraph of [Hooks for a Project Layout](#hooks-for-a-project-layout). The `finalizePage` and `finalizeButton` components are legacy: they cannot be declared in YAML, only in TypeScript, and `Legacy.CommonFinalizePage`, which is deprecated, shows a `finalizePage` for projects that still have one. `LayoutDialog` renders only what the project registers with `createLayoutDialog` and is not part of the finalize flow. The preview start screen is `DesktopPreview` or `MobilePreview`, which the layout shows while `usePreview().isPreview` is set. Both previews frame the scene themselves, so the layout renders the component that calls `useSceneSafeArea()` only while `isPreview` is not set; it then measures when it mounts after the preview. The assistant panel is available to a project layout only inside `LayoutSlots.DesktopSidebar`.
435
+ - The layout reads the state it has to react to from hooks, listed with what each returns in [Hooks for a Project Layout](#hooks-for-a-project-layout).
436
+ - Messages for the user come one of two ways. `LayoutSlots.Services` shows violations of a rejected change, side-effect hints and errors as the built-in snackbars, which render into `LayoutSlots.DesktopSnackbarsRoot` or `LayoutSlots.MobileSnackbarsRoot` wherever the layout mounts it. `phx.frontend.useConfiguratorMessages(onMessage)` hands the same messages to the layout's own UI instead, with their text resolved, as the toast in [Custom Layout](?path=/docs/layouts-custom-layout--docs#show-messages) does; mount one of the two, not both, or every message is shown twice. Some messages stay snackbars either way: a failure to save the configuration and an error of the built-in finalize and other actions. They render into the snackbar root, so a layout that mounts none shows them outside the configurator, in the page's `body`. An error while a preset is applied appears on the preset screen, outside the layout. A finalize action the layout runs with `phx.frontend.useFinalizeAction` reports its error as that hook's `error` instead, neither as a message nor as a snackbar.
427
437
  - Any project layout element named `ConfiguratorLayout` takes over the page.
428
438
 
439
+ `defaultConfiguratorElements`, exported from `@salsita-ai/sdk/configurator`, holds the built-in UI as a `ConfiguratorLayout` registration that renders the layout the spec selects. Generated code registers these defaults with `withDefaultElements`, apart from the project's elements, so the built-in UI is a registration like any other; a project element registered under the same name wins over a default.
440
+
441
+ #### Hooks for a Project Layout
442
+
443
+ A project layout reads the product and the configurator's state through hooks, and changes them through the functions the hooks return. Each row names where its hook comes from; the `phx.frontend` hooks in these tables are also exported from `@salsita-ai/sdk/react` under the same name. No hook looks at read mode: the control hooks and the navigation and fullscreen setters keep working while `accessInfo.viewMode` is `true`, so the layout decides what a viewer can change.
444
+
445
+ **The product**
446
+
447
+ | Hook | Returns |
448
+ | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
449
+ | `phx.frontend.useRootEntityId()` | The id of the root entity, which the control hooks take. |
450
+ | `phx.frontend.useSelectedDomainEntityId()` | The id of the selected entity, or the root entity's id while nothing is selected. |
451
+ | `phx.frontend.useFirstRootComponent(componentType)` | The first component of that type on the root entity as `{ componentInstance, state }`, or `null` without one. It takes a component type such as `detailComponent` from `@salsita-ai/sdk/domain`, or an array of them. The `detail` component's `state` is `{ title, subtitle, description, thumbnailUrl? }`, all `Localizable`. |
452
+ | `phx.frontend.useComponents({ componentTypes, entityIds?, instanceNames? })` | Every matching component on any entity, as `{ componentInstance, state }[]`. Its type on `phx.frontend` includes `null`, but it returns an empty array when nothing matches. |
453
+ | `phx.frontend.useRootEntityPrice()` and `phx.frontend.useEntityPrice(entityId)` | The value of the entity's `price` component formatted as currency for the current locale, in the component's `currency` or `USD`, or `null` without a `price` component. |
454
+
455
+ **Controls**
456
+
457
+ | Hook | Returns |
458
+ | ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
459
+ | `phx.frontend.useAttributeSelection(entityId, instanceName)` | `{ value, options }`. Each option has `title` and `description` (`Localizable`, the spec's `label` and `description`), `value`, `imageSrc` (the spec's `img`), `disabled`, `error`, `hint` (a `string` saying why it is disabled, or `undefined`) and `onSelect()`, which writes the option; the selected option's `onSelect` does nothing. It throws when the entity has no `attributeSelection` component named `instanceName`. |
460
+ | `phx.frontend.useAttributeSwitch(entityId, instanceName)` | `{ state, onChange, disabled, error, hint }`: `state.value` is the boolean and `onChange()` writes its opposite. It throws without the component. |
461
+
462
+ The other control hooks in [Controls](#controls) take the same two arguments, `useSwatchSelection` a required third. `useSwatchSelection` returns `{ value, options }`, where `value` is the selected swatch key and each option is `{ swatchName, onClick, swatchKey, selected, thumbnailUrl, disabled, color, hint?, trace? }`; `useTextValue` and the dimension hooks return `{ value, hints, hasErrors, update, resolve, trace }` instead, where `update(value)` stages a value the scene shows at once (`useTextValue`'s is an `onChange` handler, `update(event, value)`) and `resolve()` commits it, or rolls it back when it breaks an invariant. Their TypeScript types give the exact shapes, and [Custom Controls](?path=/docs/controls-custom--docs) lists them.
463
+
464
+ **Texts**
465
+
466
+ Texts from the spec, such as option titles and the `detail` fields, are `Localizable`, not `string`. `useResolveLocalizable()` from `@salsita-ai/sdk/react` returns a function that turns one into a string in the current language, which suits a list; `useLocalizable(text)` and `<LocalizableMessage>` resolve a single text, see [Localization](?path=/docs/phoenix-localization--docs).
467
+
468
+ **Mode, fullscreen, preview and navigation**
469
+
470
+ | Hook | Returns |
471
+ | ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
472
+ | `phx.frontend.useAccessService()` | `{ configurationId, accessInfo }`. `accessInfo.viewMode` is `true` while a configuration shared for viewing is open, and also while the host sets the `readonly` property. |
473
+ | `phx.frontend.useIsFullscreenActive()` and `phx.frontend.useSetFullscreen()` | The fullscreen flag, a `boolean`, and a function `(enabled: boolean) => void` that sets it. |
474
+ | `usePreview()` from `@salsita-ai/sdk/configurator` | `{ isPreview, isExpanded, isPreviewEnabled, isHidden, enter, leave }`. The layout shows a preview start screen while `isPreview` is `true`. |
475
+ | `phx.frontend.useActiveNavigationState()` | The active navigation state, one of the spec's `navigationStates`. |
476
+ | `phx.frontend.usePushNavigate()` and `phx.frontend.usePopNavigate()` | `(state) => void`, which enters a state and remembers the active one, and `() => void`, which returns to the remembered state and does nothing when there is none. `phx.frontend.useNavigate()` returns `(state) => void`, which enters a state and forgets the history. |
477
+ | `phx.frontend.useActiveViewState()` and `phx.frontend.useActiveToolState()` | The active view state and tool state. |
478
+ | `phx.frontend.useSetViewState()` | `(state) => void`, which makes the given view state, one of the spec's `viewStates`, the active one; setting the active state again keeps it. Cameras and components filtered by `viewStates` follow the change. |
479
+ | `phx.frontend.useResetCamera()` | `() => void`, which moves the camera back to the framing of the active camera configuration, undoing the user's orbit, pan and zoom, as the toolbar's reset button does. Besides `phx.frontend`, `@salsita-ai/sdk/configurator` exports it, not `@salsita-ai/sdk/react`. |
480
+ | `useActiveViewports()` from `@salsita-ai/sdk/react` | The responsive breakpoints the configurator's container satisfies, as `Viewport[]`, or `null` before it is measured. It follows the container, not the window, so it is the switch between a desktop and a phone layout. `desktop` is a container wider than 768 pixels and `mobile` one at most 768 pixels wide; `phone` is narrower than 480 and `tablet` between 480 and 1024 pixels; `landscape` and `portrait` follow the browser window's orientation. The built-in layout uses its phone arrangement unless `desktop` is active. |
481
+
482
+ **Violations**
483
+
484
+ | Hook | Returns |
485
+ | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
486
+ | `phx.frontend.useStateValidity()` | The validity of the last committed state: `{ valid: true, severity: null }`, or `{ valid: true, severity: 'notice' \| 'warning', entityViolations }`. A change that leaves a `fixable` or `fatal` violation is rejected and never committed, and neither is a change during which an error was thrown, so neither is seen here. |
487
+ | `phx.frontend.useFinalizeViolations()` | `{ disabled, hints, violations }`. `disabled` is `true` while a `warning`, `fixable` or `fatal` violation holds or the configuration failed to save; `hints` is the violations' text, or `undefined`. |
488
+ | `phx.frontend.useConfiguratorMessages(onMessage)` | Nothing. It calls `onMessage(message)` once per message: `{ kind: 'violation', entityId, violation, text }` for each violation of the rejected state, including notices and warnings already present, so `violation.severity` tells the reason for the rejection (`fixable` or `fatal`) from them, `{ kind: 'sideEffect', text }` for each hint of a committed update, and `{ kind: 'error', error, text }` when an error is thrown while a change is validated, with `text` the generic "Oops, something went wrong". `text` is resolved to the current language; a violation's is its `message`, or its `type` without one, and the built-in snackbar prefixes it with `entityId`. Hints committed before the layout mounted are not delivered; when the most recent change was rejected or threw, its messages are delivered again when the hook mounts. `onMessage` may be a new function on every render. On `phx.frontend` the message is typed by the project, so `violation.type` narrows to its violation types; from `@salsita-ai/sdk/react` the type is `ConfiguratorMessage`. |
489
+ | `phx.frontend.useFinalizeAction(handler)` | `{ run, isPending, error, disabled, hints }` for the `finalizeAction` whose `handler` it is given; see the Finalize paragraph below for how to call it. `run()` records the finalize-button click for analytics and runs the project's handler, and does nothing while `disabled` or `isPending`; `error` is what the handler's last run rejected with, or `null`; `disabled` and `hints` are those of `useFinalizeViolations()`. The type of the result is `UseFinalizeActionResult` from `@salsita-ai/sdk/configurator`, which also exports the hook. |
490
+
491
+ `LayoutSlots.Services` shows the messages of `useConfiguratorMessages` as snackbars; see the messages paragraph of [Loaded Configuration UI](#loaded-configuration-ui) for choosing between the two and for the messages that stay snackbars.
492
+
493
+ **Finalize**
494
+
495
+ A project layout finalizes over the spec's `finalizeAction` components. `phx.frontend.useComponents({ componentTypes: [finalizeActionComponent] })`, with `finalizeActionComponent` from `@salsita-ai/sdk/domain`, lists them; each `state` is `{ variant, label, title, icon, handler }`, where `variant` is `'primary'` or `'secondary'` and `handler` names the project's handler registered with `phx.frontend.createPhoenixHandler`. The built-in footer offers the first `'primary'` action, or else the first action, as its button and the others in a menu; a project layout follows the same rule when it offers one action.
496
+
497
+ Each action gets a component of its own that calls `phx.frontend.useFinalizeAction(action.state.handler)` once, never in a loop or conditionally, because the hook runs the project's handler factory, which calls hooks. The layout wraps that component in `<PhoenixHandlerBoundary name={action.state.handler}>` from `@salsita-ai/sdk/react`, so an edited handler takes effect without a reload. The hook returns:
498
+
499
+ - `run()`, which records the finalize-button click for analytics and then runs the handler. It does nothing while `disabled` or `isPending`.
500
+ - `isPending`, `true` while the handler runs.
501
+ - `error`, what the handler's last run rejected with, or `null`; a rejection that is not an `Error` is wrapped in one, with the value as its `cause`. It is cleared when the action runs again. No snackbar is raised; the layout shows the error its own way.
502
+ - `disabled` and `hints`, those of `useFinalizeViolations()`: finalizing is not possible while a `warning`, `fixable` or `fatal` violation holds or the configuration failed to save, and `hints` says why.
503
+
504
+ `isPending` and `error` belong to the mounted component: when it unmounts during a run, the handler keeps running, and a component mounted afterwards starts with `isPending` false and no `error`.
505
+
506
+ ```tsx
507
+ import { finalizeActionComponent } from '@salsita-ai/sdk/domain'
508
+ import { PhoenixHandlerBoundary, useResolveLocalizable } from '@salsita-ai/sdk/react'
509
+
510
+ const FinalizeButton = ({ handler, label }: { handler: string; label: string }) => {
511
+ const { run, isPending, error, disabled, hints } = phx.frontend.useFinalizeAction(handler)
512
+
513
+ return (
514
+ <div>
515
+ <button type="button" disabled={disabled || isPending} onClick={run} title={hints}>
516
+ {isPending ? 'Sending…' : label}
517
+ </button>
518
+ {error && <p role="alert">{error.message}</p>}
519
+ </div>
520
+ )
521
+ }
522
+
523
+ export const Finalize = () => {
524
+ const { accessInfo } = phx.frontend.useAccessService()
525
+ const resolve = useResolveLocalizable()
526
+ const actions = phx.frontend.useComponents({ componentTypes: [finalizeActionComponent] }) ?? []
527
+ const primary = actions.find(action => action.state.variant === 'primary') ?? actions[0]
528
+
529
+ if (accessInfo.viewMode || !primary) {
530
+ return null
531
+ }
532
+
533
+ return (
534
+ <PhoenixHandlerBoundary name={primary.state.handler}>
535
+ <FinalizeButton handler={primary.state.handler} label={resolve(primary.state.label)} />
536
+ </PhoenixHandlerBoundary>
537
+ )
538
+ }
539
+ ```
540
+
541
+ The hook does not check read mode, so the layout hides finalizing while `accessInfo.viewMode` is `true`, as above. [Custom Layout](?path=/docs/layouts-custom-layout--docs#request-a-quote) runs a finalize action this way from its review step.
542
+
543
+ Instead of its own buttons, a project layout can mount `<FinalizeActions />` from `@salsita-ai/sdk/configurator`: the built-in footer's finalize buttons, the primary action plus a menu of the others, disabled with the hints of `useFinalizeViolations()`, with a spinner while the handler runs and a snackbar when it fails. It renders nothing without a `finalizeAction`, and it does not check read mode either.
544
+
429
545
  ## TransformerBuilder
430
546
 
431
547
  Use `transformerBuilder` to create entity transformers:
@@ -15,7 +15,7 @@ Finalization is Phoenix's system for completing configurator sessions and proces
15
15
 
16
16
  > For integration-specific finalization flows — how host-specific data is passed in and how the host is notified on completion (event dispatch in `headless`, redirect in `handoff`) — see [Finalization (Internal)](?path=/docs/phoenix-integration-finalization-internal--docs) and [Finalization (External)](?path=/docs/phoenix-integration-finalization-external--docs).
17
17
 
18
- Phoenix selects one primary footer action and places additional finalization actions in its menu. A configured `finalizePage` opens in a dialog. Built-in finalization actions use configuration validity to disable the footer action and provide pending and generic error states, including for add-to-cart handlers. See [Universal Sidebar Footer](?path=/docs/layouts-sidebar-footer-universal--docs) for the layout behavior.
18
+ The universal sidebar footer offers the spec's `finalizeAction` components: Phoenix selects one primary footer action and places additional finalization actions in its menu. It never opens a `finalizePage`; the `finalizePage` and `finalizeButton` components are legacy, declared only in TypeScript, and used by the basic sidebar footer and the legacy layouts. Built-in finalization actions use configuration validity to disable the footer action and provide pending and generic error states, including for add-to-cart handlers. See [Universal Sidebar Footer](?path=/docs/layouts-sidebar-footer-universal--docs) for the layout behavior. A project layout (`ConfiguratorLayout`) offers the same actions with its own buttons through `phx.frontend.useFinalizeAction`, which runs an action's handler and returns its pending state, its error and the disabled state with hints; see the Finalize paragraph of [Configurator Interface → Hooks for a Project Layout](?path=/docs/phoenix-configurator-interface--docs#hooks-for-a-project-layout).
19
19
 
20
20
  ## How Finalization Works
21
21