@salsita-ai/sdk 0.50.0-develop.9ed52379e9 → 0.50.0-develop.a49722158a

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 (432) hide show
  1. package/README.md +2 -2
  2. package/dist/application-spec/core/components/component-filter.d.ts +53 -0
  3. package/dist/application-spec/core/components/component-filter.js +1 -0
  4. package/dist/application-spec/core/components/index.d.ts +2 -0
  5. package/dist/application-spec/core/components/index.js +1 -0
  6. package/dist/application-spec/core/components/sdk-code-ref.d.ts +9 -0
  7. package/dist/application-spec/core/components/sdk-code-ref.js +1 -0
  8. package/dist/application-spec/core/enhanced-json-value.d.ts +0 -68
  9. package/dist/application-spec/core/enhanced-json-value.js +1 -1
  10. package/dist/application-spec/core/expressions/index.d.ts +2 -0
  11. package/dist/application-spec/core/expressions/index.js +1 -0
  12. package/dist/application-spec/core/expressions/relation-expression-params.d.ts +11 -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/errors/diagnostic-codes.d.ts +9 -0
  34. package/dist/application-spec/errors/diagnostic-codes.js +1 -1
  35. package/dist/application-spec/errors/diagnostic-messages.d.ts +12 -0
  36. package/dist/application-spec/errors/diagnostic-messages.js +1 -1
  37. package/dist/application-spec/generate-application/code/project-scaffolding.d.ts +7 -0
  38. package/dist/application-spec/generate-application/code/project-scaffolding.js +2 -2
  39. package/dist/application-spec/generate-application/code/standalone-app-files.d.ts +2 -2
  40. package/dist/application-spec/generate-application/code/standalone-app-files.js +1 -1
  41. package/dist/application-spec/generate-application/code/templates/configurator-config.js +6 -4
  42. package/dist/application-spec/generate-application/code/templates/domain-spec/generate-component-optics.js +4 -4
  43. package/dist/application-spec/generate-application/code/templates/domain-spec/generate-defaults.d.ts +4 -2
  44. package/dist/application-spec/generate-application/code/templates/domain-spec/generate-defaults.js +1 -1
  45. package/dist/application-spec/generate-application/code/templates/domain-spec/generate-domain-block.js +48 -48
  46. package/dist/application-spec/generate-application/code/templates/domain-spec/generate-phx-globals.js +3 -0
  47. package/dist/application-spec/generate-application/code/templates/domain-spec/generate-relation-expressions.js +1 -1
  48. package/dist/application-spec/generate-application/code/templates/domain-spec/generate-resources.d.ts +6 -8
  49. package/dist/application-spec/generate-application/code/templates/domain-spec/generate-resources.js +11 -11
  50. package/dist/application-spec/generate-application/code/templates/domain-spec/index.d.ts +1 -1
  51. package/dist/application-spec/generate-application/code/templates/domain-spec/index.js +1 -1
  52. package/dist/application-spec/generate-application/code/templates/domain-spec.js +12 -12
  53. package/dist/application-spec/generate-application/code/templates/domain.js +3 -3
  54. package/dist/application-spec/generate-application/code/templates/globals.js +5 -4
  55. package/dist/application-spec/generate-application/code/templates/index.d.ts +0 -2
  56. package/dist/application-spec/generate-application/code/templates/index.js +1 -1
  57. package/dist/application-spec/generate-application/code/templates/package-json.js +1 -1
  58. package/dist/application-spec/generate-application/code/templates/skills/phoenix-review-skill.js +8 -2
  59. package/dist/application-spec/generate-application/code/templates/skills/phoenix-skill.js +1 -1
  60. package/dist/application-spec/generate-application/code/templates/skills/sdk-content.d.ts +11 -4
  61. package/dist/application-spec/generate-application/code/templates/skills/sdk-content.js +2 -2
  62. package/dist/application-spec/generate-application/code/templates/webhook-index.d.ts +6 -1
  63. package/dist/application-spec/generate-application/code/templates/webhook-index.js +10 -1
  64. package/dist/application-spec/generate-application/runtime/application-runtime.d.ts +1 -3
  65. package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-runtime.d.ts +1 -3
  66. package/dist/application-spec/generate-application/runtime/sections/build-sdk-optic.d.ts +1 -1
  67. package/dist/application-spec/generate-application/runtime/sections/build-sdk-optic.js +1 -1
  68. package/dist/application-spec/generate-application/runtime/sections/component-optic.d.ts +2 -1
  69. package/dist/application-spec/generate-application/runtime/sections/expression-runtime.js +1 -1
  70. package/dist/application-spec/generate-application/runtime/sections/transformers-runtime.d.ts +2 -4
  71. package/dist/application-spec/index.d.ts +1 -0
  72. package/dist/application-spec/index.js +1 -1
  73. package/dist/application-spec/parse-application-files/categorize/categorize-files.d.ts +6 -0
  74. package/dist/application-spec/parse-application-files/categorize/categorize-files.js +1 -1
  75. package/dist/application-spec/parse-application-files/categorize/module-analysis.d.ts +1 -1
  76. package/dist/application-spec/parse-application-files/categorize/module-analysis.js +1 -1
  77. package/dist/application-spec/parse-application-files/raw-spec/raw-assistant.d.ts +16 -3
  78. package/dist/application-spec/parse-application-files/raw-spec/raw-assistant.js +1 -1
  79. package/dist/application-spec/parse-application-files/raw-spec/raw-spec-file.js +1 -1
  80. package/dist/application-spec/parse-application-files/spec/application/admin-spec/admin-spec.js +1 -1
  81. package/dist/application-spec/parse-application-files/spec/application/configurator-spec/configurator-spec.js +1 -1
  82. package/dist/application-spec/parse-application-files/spec/components/component-entry.d.ts +1 -9
  83. package/dist/application-spec/parse-application-files/spec/components/component-entry.js +1 -1
  84. package/dist/application-spec/parse-application-files/spec/components/component-filter.d.ts +1 -54
  85. package/dist/application-spec/parse-application-files/spec/components/component-filter.js +1 -1
  86. package/dist/application-spec/parse-application-files/spec/components/index.d.ts +1 -1
  87. package/dist/application-spec/parse-application-files/spec/components/index.js +1 -1
  88. package/dist/application-spec/parse-application-files/spec/expressions/expression.d.ts +1 -28
  89. package/dist/application-spec/parse-application-files/spec/expressions/expression.js +1 -1
  90. package/dist/application-spec/parse-application-files/spec/expressions/expressions.d.ts +1 -1
  91. package/dist/application-spec/parse-application-files/spec/expressions/expressions.js +1 -1
  92. package/dist/application-spec/parse-application-files/spec/expressions/index.d.ts +0 -1
  93. package/dist/application-spec/parse-application-files/spec/expressions/index.js +1 -1
  94. package/dist/application-spec/parse-application-files/spec/expressions/memo-eval-plan.js +1 -1
  95. package/dist/application-spec/parse-application-files/spec/expressions/user-expressions.js +1 -1
  96. package/dist/application-spec/parse-application-files/spec/model/defaults.d.ts +7 -2
  97. package/dist/application-spec/parse-application-files/spec/model/defaults.js +1 -1
  98. package/dist/application-spec/parse-application-files/spec/relations/reference-field-type.d.ts +25 -0
  99. package/dist/application-spec/parse-application-files/spec/relations/reference-field-type.js +1 -0
  100. package/dist/application-spec/parse-application-files/spec/relations/relation-field-mismatch.d.ts +46 -0
  101. package/dist/application-spec/parse-application-files/spec/relations/relation-field-mismatch.js +1 -0
  102. package/dist/application-spec/parse-application-files/spec/relations/relations.d.ts +4 -3
  103. package/dist/application-spec/parse-application-files/spec/relations/relations.js +1 -1
  104. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-schema.js +1 -1
  105. package/dist/application-spec/parse-application-files/spec/sections/infra.d.ts +2 -0
  106. package/dist/application-spec/parse-application-files/spec/sections/infra.js +1 -1
  107. package/dist/client/phoenix-api.d.ts +96 -0
  108. package/dist/configurator/application-runtime/extensions/create-configurator-extensions.js +1 -1
  109. package/dist/configurator/application-runtime/extensions/selectors.d.ts +3 -1
  110. package/dist/configurator/application-runtime/extensions/selectors.js +1 -1
  111. package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.d.ts +3 -1
  112. package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.js +1 -1
  113. package/dist/configurator/application-runtime/extensions/systems/drag-and-drop-system/drag-and-drop-system.d.ts +3 -3
  114. package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.d.ts +14 -4
  115. package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.js +1 -1
  116. package/dist/configurator/application-runtime/extensions/systems/graphql-system/inject-assistant-lenses.d.ts +11 -0
  117. package/dist/configurator/application-runtime/extensions/systems/graphql-system/inject-assistant-lenses.js +1 -0
  118. package/dist/configurator/application-runtime/extensions/systems/layout-system/layout-system.d.ts +2 -2
  119. package/dist/configurator/application-runtime/extensions/systems/layout-system/layout-system.js +1 -1
  120. package/dist/configurator/common/configurator-interface.d.ts +17 -1
  121. package/dist/configurator/common/configurator-interface.js +1 -1
  122. package/dist/configurator/common/frontend-analytics.d.ts +2 -0
  123. package/dist/configurator/common/frontend-analytics.js +1 -1
  124. package/dist/configurator/common/index.d.ts +1 -0
  125. package/dist/configurator/common/index.js +1 -1
  126. package/dist/configurator/common/use-finalize-action.d.ts +27 -0
  127. package/dist/configurator/common/use-finalize-action.js +1 -0
  128. package/dist/configurator/configurator-context/configurator-hooks/layout-hooks.d.ts +1 -1
  129. package/dist/configurator/pages/DefaultConfiguratorLayout.d.ts +2 -2
  130. package/dist/configurator/pages/LoadedConfiguratorDetailPage.js +1 -1
  131. package/dist/configurator/pages/configurator-layout-name.d.ts +2 -0
  132. package/dist/configurator/pages/configurator-layout-name.js +1 -0
  133. package/dist/configurator/pages/default-configurator-elements.d.ts +7 -0
  134. package/dist/configurator/pages/default-configurator-elements.js +1 -0
  135. package/dist/configurator/pages/index.d.ts +1 -0
  136. package/dist/configurator/pages/index.js +1 -1
  137. package/dist/configurator/scene/scene-controls/hotspot-drag-handle/HotspotDragHandle.d.ts +1 -1
  138. package/dist/configurator/scene/scene-controls/hotspot-drag-handle/HotspotDragSceneHandle.d.ts +1 -1
  139. package/dist/configurator/scene/scene-entity/dimension-indicators/DimensionIndicator.d.ts +1 -1
  140. package/dist/configurator/scene/scene-entity/dimension-indicators/DimensionIndicatorInternal.d.ts +1 -1
  141. package/dist/configurator/scene/scene-entity/drag-and-drop/shared.d.ts +2 -2
  142. package/dist/configurator/scene/scene-entity/drag-and-drop/use-drag-object.d.ts +1 -1
  143. package/dist/configurator/scene/scene-entity/materials/RemoteReplacedMaterial.js +1 -1
  144. package/dist/configurator/scene/scene-entity/objects/SceneEntityObjectTextDecal.d.ts +1 -1
  145. package/dist/configurator/ui/assistant/AssistantMarkdown.d.ts +1 -1
  146. package/dist/configurator/ui/assistant/AssistantMarkdown.js +1 -1
  147. package/dist/configurator/ui/assistant/AssistantThread.js +1 -1
  148. package/dist/configurator/ui/assistant/entity-link/AssistantEntityLink.d.ts +13 -0
  149. package/dist/configurator/ui/assistant/entity-link/AssistantEntityLink.js +1 -0
  150. package/dist/configurator/ui/assistant/entity-link/entity-link-selection.d.ts +22 -0
  151. package/dist/configurator/ui/assistant/entity-link/entity-link-selection.js +1 -0
  152. package/dist/configurator/ui/assistant/entity-link/entity-link-state.d.ts +27 -0
  153. package/dist/configurator/ui/assistant/entity-link/entity-link-state.js +1 -0
  154. package/dist/configurator/ui/assistant/entity-link/entity-link-target.d.ts +5 -0
  155. package/dist/configurator/ui/assistant/entity-link/entity-link-target.js +1 -0
  156. package/dist/configurator/ui/assistant/entity-link/index.d.ts +3 -0
  157. package/dist/configurator/ui/assistant/entity-link/index.js +1 -0
  158. package/dist/configurator/ui/configurator-layout/sidebar-keys/index.d.ts +1 -0
  159. package/dist/configurator/ui/configurator-layout/sidebar-keys/index.js +1 -0
  160. package/dist/configurator/ui/configurator-layout/sidebar-keys/sidebar-keys.d.ts +11 -0
  161. package/dist/configurator/ui/configurator-layout/sidebar-keys/sidebar-keys.js +1 -0
  162. package/dist/configurator/ui/configurator-layout/slots/desktop-sidebar/DesktopSidebar.d.ts +101 -0
  163. package/dist/configurator/ui/configurator-layout/slots/desktop-sidebar/DesktopSidebar.js +16 -0
  164. package/dist/configurator/ui/configurator-layout/slots/desktop-sidebar/index.d.ts +1 -0
  165. package/dist/configurator/ui/configurator-layout/slots/desktop-sidebar/index.js +1 -0
  166. package/dist/configurator/ui/configurator-layout/slots/services/InvariantViolations.d.ts +4 -4
  167. package/dist/configurator/ui/configurator-layout/slots/services/InvariantViolations.js +1 -1
  168. package/dist/configurator/ui/configurator-layout/slots/services/SideEffects.d.ts +3 -6
  169. package/dist/configurator/ui/configurator-layout/slots/services/SideEffects.js +1 -1
  170. package/dist/configurator/ui/configurator-layout/slots/slots.d.ts +1 -0
  171. package/dist/configurator/ui/configurator-layout/slots/slots.js +1 -1
  172. package/dist/configurator/ui/configurator-layout/universal/DesktopLayout.d.ts +0 -74
  173. package/dist/configurator/ui/configurator-layout/universal/DesktopLayout.js +5 -20
  174. package/dist/configurator/ui/configurator-layout/universal/DesktopPreview.js +1 -1
  175. package/dist/configurator/ui/configurator-layout/universal/MobileLayout.js +4 -4
  176. package/dist/configurator/ui/configurator-layout/universal/MobilePreview.js +1 -1
  177. package/dist/configurator/ui/configurator-layout/universal/use-layout-scene-safe-area.d.ts +47 -0
  178. package/dist/configurator/ui/configurator-layout/universal/use-layout-scene-safe-area.js +1 -0
  179. package/dist/configurator/ui/configurator-layout/universal/use-scene-safe-area.d.ts +11 -26
  180. package/dist/configurator/ui/configurator-layout/universal/use-scene-safe-area.js +1 -1
  181. package/dist/configurator/ui/legacy/layout/steps/StepsNavigation.d.ts +2 -2
  182. package/dist/configurator/ui/phoenix-layout-elements/header/common/step-navigation/StepsNavigation.d.ts +2 -2
  183. package/dist/docs/complex-use-cases/filtered-controls-with-fix-dialog.mdx +16 -19
  184. package/dist/docs/complex-use-cases/sketching.mdx +0 -38
  185. package/dist/docs/controls/select-attribute/cardselect.mdx +9 -13
  186. package/dist/docs/dimensions/3d-scene-integration/dimension-indicators.mdx +2 -2
  187. package/dist/docs/dimensions/3d-scene-integration/editable-angle-indicator.mdx +1 -1
  188. package/dist/docs/dimensions/3d-scene-integration/editable-distance-dimension-indicator.mdx +1 -1
  189. package/dist/docs/dimensions/3d-scene-integration/list-selector-dimension-indicator.mdx +1 -1
  190. package/dist/docs/drag-and-drop/drag-handle.mdx +1 -1
  191. package/dist/docs/drag-and-drop/hotspot-handle.mdx +1 -1
  192. package/dist/docs/drag-and-drop/planes.mdx +1 -1
  193. package/dist/docs/drag-and-drop/rotatable.mdx +2 -2
  194. package/dist/docs/index.json +42 -1
  195. package/dist/docs/integrations/woocommerce/local-development.mdx +1 -1
  196. package/dist/docs/layouts/custom-layout.mdx +1400 -0
  197. package/dist/docs/layouts/header/fixed.mdx +2 -2
  198. package/dist/docs/layouts/header/floating.mdx +2 -2
  199. package/dist/docs/layouts/sidebar/in-a-custom-layout.mdx +380 -0
  200. package/dist/docs/layouts/sidebar-footer/basic.mdx +32 -77
  201. package/dist/docs/other-features/client-side-rendering/declarative.mdx +1 -1
  202. package/dist/docs/other-features/side-effects.mdx +8 -11
  203. package/dist/docs/other-features/transformers.mdx +4 -0
  204. package/dist/docs/other-features/violation-messages.mdx +52 -56
  205. package/dist/docs/phoenix/application-states.mdx +9 -7
  206. package/dist/docs/phoenix/configurator-interface.mdx +204 -82
  207. package/dist/docs/phoenix/domain-model.mdx +4 -0
  208. package/dist/docs/phoenix/finalization.mdx +1 -1
  209. package/dist/docs/phoenix/layout-slots.mdx +114 -0
  210. package/dist/docs/phoenix/phoenix-cli.mdx +27 -28
  211. package/dist/docs/phoenix/project-structure.mdx +18 -10
  212. package/dist/docs/phoenix/quickstart.mdx +2 -2
  213. package/dist/docs/phoenix/sdk-builder-workflow.mdx +2 -2
  214. package/dist/docs/phoenix/testing.mdx +140 -0
  215. package/dist/docs/phoenix/webhook-membership.mdx +10 -9
  216. package/dist/docs/phoenix/webhooks.mdx +19 -2
  217. package/dist/domain/admin-domain/implementation/common-admin-expressions.d.ts +26 -11
  218. package/dist/domain/admin-domain/implementation/common-admin-publish.d.ts +1 -2
  219. package/dist/domain/admin-domain/index.js +1 -1
  220. package/dist/domain/application/GetApplicationWrapperHandler.d.ts +1 -1
  221. package/dist/domain/application/application-config.d.ts +7 -3
  222. package/dist/domain/application/assistant/assistant-fields.d.ts +12 -0
  223. package/dist/domain/application/assistant/assistant-fields.js +1 -0
  224. package/dist/domain/application/assistant/assistant-manifest.d.ts +9 -1
  225. package/dist/domain/application/assistant/assistant-resolved-manifest.d.ts +38 -2
  226. package/dist/domain/application/assistant/assistant-resolved-manifest.js +1 -1
  227. package/dist/domain/application/assistant/build-assistant-lenses.d.ts +14 -0
  228. package/dist/domain/application/assistant/build-assistant-lenses.js +5 -0
  229. package/dist/domain/application/assistant/build-assistant-manifest.js +1 -1
  230. package/dist/domain/application/assistant/create-assistant-execution-schema.d.ts +32 -0
  231. package/dist/domain/application/assistant/create-assistant-execution-schema.js +1 -0
  232. package/dist/domain/application/assistant/generate-assistant-prompt.js +1 -1
  233. package/dist/domain/application/assistant/index.d.ts +6 -1
  234. package/dist/domain/application/assistant/index.js +1 -1
  235. package/dist/domain/application/assistant/session-setup/AssistantSetupHandler.d.ts +20 -0
  236. package/dist/domain/application/assistant/session-setup/AssistantSetupHandler.js +1 -0
  237. package/dist/domain/application/assistant/session-setup/apply-assistant-setup.d.ts +10 -0
  238. package/dist/domain/application/assistant/session-setup/apply-assistant-setup.js +1 -0
  239. package/dist/domain/application/assistant/session-setup/assistant-setup-manifest.d.ts +48 -0
  240. package/dist/domain/application/assistant/session-setup/assistant-setup-manifest.js +1 -0
  241. package/dist/domain/application/assistant/session-setup/create-session-assistant.d.ts +15 -0
  242. package/dist/domain/application/assistant/session-setup/create-session-assistant.js +1 -0
  243. package/dist/domain/application/assistant/session-setup/index.d.ts +4 -0
  244. package/dist/domain/application/assistant/session-setup/index.js +1 -0
  245. package/dist/domain/application/configurator-builder.d.ts +5 -2
  246. package/dist/domain/application/configurator-builder.js +1 -1
  247. package/dist/domain/application/create-initial-published-data.d.ts +5 -5
  248. package/dist/domain/application/index.d.ts +0 -1
  249. package/dist/domain/application/index.js +1 -1
  250. package/dist/domain/application/published-data/map-published-chunked-data.d.ts +1 -1
  251. package/dist/domain/application/published-data/map-published-data.d.ts +7 -3
  252. package/dist/domain/core/component/domain-component-optic.d.ts +1 -2
  253. package/dist/domain/core/component/domain-component-optic.js +2 -2
  254. package/dist/domain/core/component/system-component-filter.d.ts +15 -2
  255. package/dist/domain/core/expression/constant-expressions.d.ts +16 -8
  256. package/dist/domain/core/expression/custom-expression.d.ts +16 -3
  257. package/dist/domain/core/invariant/custom-invariants.d.ts +6 -14
  258. package/dist/domain/core/invariant/invariants.d.ts +1 -4
  259. package/dist/domain/core/invariant/violation.d.ts +50 -2
  260. package/dist/domain/core/invariant/violation.js +1 -1
  261. package/dist/domain/core/model/model.d.ts +1 -1
  262. package/dist/domain/core/recipe/recipes.d.ts +18 -50
  263. package/dist/domain/core/recipe/recipes.js +1 -1
  264. package/dist/domain/core/relation/all-of-type-relation.d.ts +8 -21
  265. package/dist/domain/core/relation/all-of-type-relation.js +1 -1
  266. package/dist/domain/core/relation/direct-relation.d.ts +13 -24
  267. package/dist/domain/core/relation/index.d.ts +0 -1
  268. package/dist/domain/core/relation/indirect-relation.d.ts +12 -67
  269. package/dist/domain/core/relation/indirect-relation.js +1 -1
  270. package/dist/domain/core/relation/relation-factory.d.ts +19 -15
  271. package/dist/domain/core/relation/relation-factory.js +1 -1
  272. package/dist/domain/core/state/state.d.ts +1 -1
  273. package/dist/domain/domain-runtime/access/selectors.d.ts +9 -0
  274. package/dist/domain/domain-runtime/access/selectors.js +1 -1
  275. package/dist/domain/domain-runtime/access/types.d.ts +2 -0
  276. package/dist/domain/domain-runtime/access/types.js +1 -1
  277. package/dist/domain/domain-runtime/domain-context/selectors.d.ts +14 -13
  278. package/dist/domain/domain-runtime/domain-context/selectors.js +1 -1
  279. package/dist/domain/domain-runtime/domain-context/types.d.ts +4 -1
  280. package/dist/domain/domain-runtime/domain-runtime.d.ts +32 -16
  281. package/dist/domain/domain-runtime/errors/implementation-error-collector.d.ts +19 -0
  282. package/dist/domain/domain-runtime/errors/implementation-error-collector.js +1 -0
  283. package/dist/domain/domain-runtime/errors/implementation-error.d.ts +46 -0
  284. package/dist/domain/domain-runtime/errors/implementation-error.js +1 -0
  285. package/dist/domain/domain-runtime/errors/implementation-errors.d.ts +13 -0
  286. package/dist/domain/domain-runtime/errors/implementation-errors.js +1 -0
  287. package/dist/domain/domain-runtime/errors/index.d.ts +4 -0
  288. package/dist/domain/domain-runtime/errors/index.js +1 -0
  289. package/dist/domain/domain-runtime/errors/to-implementation-errors.d.ts +10 -0
  290. package/dist/domain/domain-runtime/errors/to-implementation-errors.js +1 -0
  291. package/dist/domain/domain-runtime/index.d.ts +1 -0
  292. package/dist/domain/domain-runtime/index.js +1 -1
  293. package/dist/domain/domain-runtime/spec/create-violation-expressions.d.ts +24 -0
  294. package/dist/domain/domain-runtime/spec/create-violation-expressions.js +1 -0
  295. package/dist/domain/domain-runtime/spec/index.d.ts +1 -1
  296. package/dist/domain/domain-runtime/spec/index.js +1 -1
  297. package/dist/domain/domain-runtime/spec/initialize.d.ts +11 -0
  298. package/dist/domain/domain-runtime/spec/initialize.js +1 -1
  299. package/dist/domain/domain-runtime/spec/module-checks/index.d.ts +1 -1
  300. package/dist/domain/domain-runtime/spec/module-checks/project-modules.d.ts +8 -1
  301. package/dist/domain/domain-runtime/spec/module-checks/project-modules.js +1 -1
  302. package/dist/domain/domain-runtime/spec/module-checks/resolve-action-modules.js +1 -1
  303. package/dist/domain/domain-runtime/spec/module-checks/resolve-expression-modules.js +1 -1
  304. package/dist/domain/domain-runtime/spec/module-checks/resolve-imported-transformers.d.ts +7 -7
  305. package/dist/domain/domain-runtime/spec/module-checks/resolve-imported-transformers.js +1 -1
  306. package/dist/domain/domain-runtime/spec/module-checks/resolve-invariant-modules.d.ts +2 -2
  307. package/dist/domain/domain-runtime/spec/module-checks/resolve-invariant-modules.js +1 -1
  308. package/dist/domain/domain-runtime/spec/selectors.js +1 -1
  309. package/dist/domain/domain-runtime/spec/types.d.ts +2 -0
  310. package/dist/domain/extensions/attribute-switch.d.ts +1 -1
  311. package/dist/domain/extensions/attribute-switch.js +1 -1
  312. package/dist/domain/extensions/canvas-config.d.ts +1 -1
  313. package/dist/domain/extensions/client-side-image.d.ts +2 -2
  314. package/dist/domain/extensions/client-side-image.js +1 -1
  315. package/dist/domain/extensions/common/angle-indicator-schema.d.ts +2 -2
  316. package/dist/domain/extensions/common/dimension-indicator-schema.d.ts +2 -2
  317. package/dist/domain/extensions/editable-angle-indicator.d.ts +1 -1
  318. package/dist/domain/extensions/editable-distance-dimension-indicator.d.ts +1 -1
  319. package/dist/domain/extensions/editable-offset-indicator.d.ts +1 -1
  320. package/dist/domain/extensions/editable-offset-indicator.js +1 -1
  321. package/dist/domain/extensions/fit-selection-orthographic-camera-component.js +1 -1
  322. package/dist/domain/extensions/fit-selection-perspective-camera-component.js +1 -1
  323. package/dist/domain/extensions/horizontal-tabs.d.ts +2 -2
  324. package/dist/domain/extensions/list-selector-dimension-indicator.d.ts +1 -1
  325. package/dist/domain/extensions/navigation-steps.d.ts +2 -2
  326. package/dist/domain/extensions/offset-indicator.js +1 -1
  327. package/dist/domain/extensions/rotation-handle.d.ts +2 -2
  328. package/dist/domain/extensions/save-template-thumbnail.d.ts +2 -2
  329. package/dist/domain/extensions/save-template-thumbnail.js +1 -1
  330. package/dist/domain/extensions/scene-entity-animation.js +1 -1
  331. package/dist/domain/mocks/create-mock-domain-context.d.ts +1 -1
  332. package/dist/domain/mocks/create-mock-domain-runtime.d.ts +18 -9
  333. package/dist/domain/mocks/create-mock-domain-runtime.js +1 -1
  334. package/dist/domain/mocks/create-mock-domain.d.ts +1 -1
  335. package/dist/domain/mocks/create-mock-domain.js +1 -1
  336. package/dist/domain/mocks/create-mock-state.d.ts +2 -2
  337. package/dist/domain/mocks/create-test-application-config.d.ts +59 -0
  338. package/dist/domain/mocks/create-test-application-config.js +1 -0
  339. package/dist/domain/mocks/index.d.ts +1 -0
  340. package/dist/domain/mocks/index.js +1 -1
  341. package/dist/domain/projections/blender-projections/camera.d.ts +1 -1
  342. package/dist/domain/system-components/application-config.d.ts +2 -2
  343. package/dist/domain/system-components/child-entity-selection.js +1 -1
  344. package/dist/domain/system-components/collision-box-geometry.d.ts +4 -2
  345. package/dist/domain/system-components/collision-geometry.d.ts +4 -2
  346. package/dist/domain/system-components/collision-point-geometry.d.ts +4 -2
  347. package/dist/domain/system-components/common/material-override-schema.d.ts +2 -2
  348. package/dist/domain/system-components/common/recipes.d.ts +21 -3
  349. package/dist/domain/system-components/common/recipes.js +1 -1
  350. package/dist/domain/system-components/constraint.d.ts +10 -23
  351. package/dist/domain/system-components/drag-handle.d.ts +1 -1
  352. package/dist/domain/system-components/drag-handle.js +1 -1
  353. package/dist/domain/system-components/fit-entity-orthographic-camera-component.js +1 -1
  354. package/dist/domain/system-components/fit-entity-perspective-camera-component.js +1 -1
  355. package/dist/domain/system-components/fit-root-orthographic-camera-component.js +1 -1
  356. package/dist/domain/system-components/fit-root-perspective-camera-component.js +1 -1
  357. package/dist/domain/system-components/hotspot-drag-handle.d.ts +1 -1
  358. package/dist/domain/system-components/hotspot-drag-handle.js +1 -1
  359. package/dist/domain/system-components/material-swatch.js +1 -1
  360. package/dist/domain/system-components/scene-entity-material-override.d.ts +1 -1
  361. package/dist/domain/system-components/scene-entity-object-material-override.d.ts +1 -1
  362. package/dist/domain/system-components/scene-entity-object-text-decal.d.ts +1 -1
  363. package/dist/domain/system-components/scene-entity-object-transimissive-material-override.d.ts +1 -1
  364. package/dist/domain/system-components/scene-entity-transimissive-material-override.d.ts +1 -1
  365. package/dist/domain/system-components/static-orthographic-camera-component.js +1 -1
  366. package/dist/domain/system-components/static-perspective-camera-component.js +1 -1
  367. package/dist/domain/utils/eval-cache.d.ts +1 -1
  368. package/dist/domain/utils/map-nested-object.d.ts +1 -1
  369. package/dist/editor/domain-editors/use-implementation-files.d.ts +1 -2
  370. package/dist/editor/mocks/EditorMockContext.js +1 -1
  371. package/dist/legacy/domain-cli/gen/configurator-builder.js +13 -12
  372. package/dist/legacy/domain-cli/gen/constant-expressions.js +1 -1
  373. package/dist/legacy/domain-cli/gen/defaults.js +1 -7
  374. package/dist/legacy/domain-cli/gen/invariants.js +3 -4
  375. package/dist/legacy/domain-cli/gen/relations.js +1 -1
  376. package/dist/legacy/domain-cli/utils/cache.d.ts +1 -1
  377. package/dist/legacy/domain-cli/utils/cache.js +1 -1
  378. package/dist/legacy/domain-spec/__generated/domain-schema.d.ts +81 -68
  379. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/agents/claude-md.d.ts +1 -1
  380. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/agents/cursor-rules.d.ts +1 -1
  381. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/agents/shared-sdk-content.d.ts +1 -1
  382. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/agents/shared-sdk-content.js +7 -1
  383. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/domain-index.js +15 -13
  384. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/frontend-admin.js +18 -16
  385. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/frontend-configurator.js +14 -12
  386. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/frontend-embedded-admin.js +13 -11
  387. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/frontend-embedded-configurator.js +13 -11
  388. package/dist/phoenix/application-spec/application-spec-builder.js +1 -1
  389. package/dist/phoenix/commands/check.js +1 -1
  390. package/dist/phoenix/commands/deploy.js +1 -1
  391. package/dist/phoenix/commands/fix.js +1 -1
  392. package/dist/phoenix/commands/init.d.ts +4 -4
  393. package/dist/phoenix/commands/init.js +1 -1
  394. package/dist/phoenix/services/application-spec-project-watcher.js +3 -3
  395. package/dist/phoenix/services/source-archive.d.ts +1 -0
  396. package/dist/phoenix/services/source-archive.js +1 -1
  397. package/dist/phoenix/utils/git/index.d.ts +1 -0
  398. package/dist/phoenix/utils/git/index.js +1 -0
  399. package/dist/phoenix/utils/git/is-inside-git-repository.d.ts +6 -0
  400. package/dist/phoenix/utils/git/is-inside-git-repository.js +1 -0
  401. package/dist/phoenix/utils/index.d.ts +1 -0
  402. package/dist/phoenix/utils/index.js +1 -1
  403. package/dist/react-sdk/application-runtime/hooks/use-access-service.d.ts +2 -0
  404. package/dist/react-sdk/application-runtime/mocks/create-mock-application-runtime.js +1 -1
  405. package/dist/react-sdk/application-runtime/routing/access-service.d.ts +1 -0
  406. package/dist/react-sdk/application-runtime/runtime/create-application-runtime.d.ts +33 -16
  407. package/dist/react-sdk/application-runtime/runtime/create-application-runtime.js +1 -1
  408. package/dist/react-sdk/domain-context/common-hooks/use-configurator-messages.d.ts +30 -0
  409. package/dist/react-sdk/domain-context/common-hooks/use-configurator-messages.js +1 -0
  410. package/dist/react-sdk/domain-context/common-hooks/use-domain-context.d.ts +4 -4
  411. package/dist/react-sdk/domain-context/index.d.ts +2 -1
  412. package/dist/react-sdk/domain-context/index.js +1 -1
  413. package/dist/react-sdk/domain-context/system-hooks/navigation-hooks.d.ts +6 -0
  414. package/dist/react-sdk/domain-context/system-hooks/navigation-hooks.js +1 -1
  415. package/dist/react-sdk/domain-controls/hooks/use-angle-dimension.d.ts +1 -1
  416. package/dist/ui-sdk/theme/types.d.ts +1 -1
  417. package/dist/webhook/builders/webhook-builder.js +1 -1
  418. package/package.json +3 -3
  419. package/schema/domain-spec.json +1115 -209
  420. package/dist/application-spec/generate-application/code/templates/prettier-ignore.d.ts +0 -1
  421. package/dist/application-spec/generate-application/code/templates/prettier-ignore.js +0 -6
  422. package/dist/application-spec/generate-application/code/templates/webhook-implementation.d.ts +0 -1
  423. package/dist/application-spec/generate-application/code/templates/webhook-implementation.js +0 -7
  424. package/dist/domain/application/resource-builder.d.ts +0 -13
  425. package/dist/domain/application/resource-builder.js +0 -1
  426. package/dist/domain/core/relation/relation-implementation.d.ts +0 -7
  427. package/dist/domain/domain-runtime/spec/invariant-builder.d.ts +0 -26
  428. package/dist/domain/domain-runtime/spec/invariant-builder.js +0 -1
  429. /package/dist/{domain/core/relation/relation-implementation.js → application-spec/core/expressions/relation-expression-params.js} +0 -0
  430. /package/dist/application-spec/{core → enhanced-json}/eval-code-uses-self.d.ts +0 -0
  431. /package/dist/application-spec/{core → enhanced-json}/eval-code-uses-self.js +0 -0
  432. /package/dist/application-spec/{core → enhanced-json}/eval-to-constant.js +0 -0
@@ -153,10 +153,10 @@ The `formatControlViolationMessage` function is where the magic happens. It rece
153
153
  3. **Actionable guidance**: Tell users exactly what values are valid or how to fix the problem
154
154
  4. **Consistent terminology**: Use the same terms and units throughout your application
155
155
 
156
- Implement violations with custom message formatting in your domain TypeScript file:
156
+ Implement violations with custom message formatting in a file of `src/invariants/`:
157
157
 
158
158
  ```typescript
159
- // domain/index.ts
159
+ // src/invariants/invariants.ts
160
160
 
161
161
  // Pre-define translatable labels for nested localization
162
162
  const colorLabels = {
@@ -166,60 +166,56 @@ const colorLabels = {
166
166
  yellow: msg`yellow`,
167
167
  } as const
168
168
 
169
- const violationExpressions = invariantBuilder
170
- .withInvariants(
171
- implementCustomInvariants({
172
- Scene: {
173
- itIsNotZero: s => {
174
- if (s.x !== 0) {
175
- return [
176
- {
177
- type: 'itIsNotZero',
178
- severity: 'fatal',
179
- message: 'Scene is not zero',
180
- controlViolationPriority: 1,
181
- formatControlViolationMessage: () => {
182
- return msg({
183
- message: `Scene is not zero: The position would be ${s.x} which is not equal to 0`,
184
- })
185
- },
186
- },
187
- ]
188
- }
189
- return []
190
- },
191
- // ... tooMuchRight omitted for brevity ...
192
-
193
- // Static violation: severity, message and controlViolationPriority
194
- // are declared in YAML. Implementation only returns a boolean.
195
- wayTooFarRight: s => s.x < 200,
196
-
197
- colorPositionIncompatible: s => {
198
- const compatibleColors = ['red', 'blue', 'green'] as const
199
- if (s.x >= 200 && !compatibleColors.includes(s.color)) {
200
- return [
201
- {
202
- type: 'colorPositionIncompatible',
203
- severity: 'fatal',
204
- message: 'Color is not compatible with far right position',
205
- controlViolationPriority: 3,
206
- formatControlViolationMessage: (_emission, { i18n }) => {
207
- const labels = new Intl.ListFormat(i18n.locale, {
208
- style: 'long',
209
- type: 'disjunction',
210
- }).format(compatibleColors.map(c => i18n.t(colorLabels[c])))
211
-
212
- return msg({ message: `Far right position is only available for ${labels}` })
213
- },
214
- },
215
- ]
216
- }
217
- return []
218
- },
219
- },
220
- })
221
- )
222
- .buildViolationExpressions()
169
+ export default phx.domain.implementCustomInvariants({
170
+ Buckyball: {
171
+ itIsNotZero: s => {
172
+ if (s.x !== 0) {
173
+ return [
174
+ {
175
+ type: 'itIsNotZero',
176
+ severity: 'fatal',
177
+ message: 'Scene is not zero',
178
+ controlViolationPriority: 1,
179
+ formatControlViolationMessage: () => {
180
+ return msg({
181
+ message: `Scene is not zero: The position would be ${s.x} which is not equal to 0`,
182
+ })
183
+ },
184
+ },
185
+ ]
186
+ }
187
+ return []
188
+ },
189
+ // ... tooMuchRight omitted for brevity ...
190
+
191
+ // Static violation: severity, message and controlViolationPriority
192
+ // are declared in YAML. Implementation only returns a boolean.
193
+ wayTooFarRight: s => s.x < 200,
194
+
195
+ colorPositionIncompatible: s => {
196
+ const compatibleColors = ['red', 'blue', 'green'] as const
197
+ if (s.x >= 200 && !compatibleColors.includes(s.color)) {
198
+ return [
199
+ {
200
+ type: 'colorPositionIncompatible',
201
+ severity: 'fatal',
202
+ message: 'Color is not compatible with far right position',
203
+ controlViolationPriority: 3,
204
+ formatControlViolationMessage: (_emission, { i18n }) => {
205
+ const labels = new Intl.ListFormat(i18n.locale, {
206
+ style: 'long',
207
+ type: 'disjunction',
208
+ }).format(compatibleColors.map(c => i18n.t(colorLabels[c])))
209
+
210
+ return msg({ message: `Far right position is only available for ${labels}` })
211
+ },
212
+ },
213
+ ]
214
+ }
215
+ return []
216
+ },
217
+ },
218
+ })
223
219
  ```
224
220
 
225
221
  ### Internationalization with Dynamic Content
@@ -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
 
@@ -25,8 +25,9 @@ The configurator interface is generated in `.phoenix/configurator-interface.ts`:
25
25
 
26
26
  ```typescript
27
27
  // .phoenix/configurator-interface.ts (AUTO-GENERATED - DO NOT EDIT)
28
- import {'{'} createConfiguratorInterface {'}'} from '@salsita-ai/sdk/configurator'
29
- import {'{'} configuratorBuilder {'}'} from './domain.js'
28
+ import { createConfiguratorInterface } from '@salsita-ai/sdk/configurator'
29
+
30
+ import { configuratorBuilder } from './domain.js'
30
31
 
31
32
  export const configuratorInterface = createConfiguratorInterface(configuratorBuilder)
32
33
 
@@ -64,9 +65,9 @@ type CustomMaterial = Phx['AdminModel']['CustomMaterial']
64
65
  Access the configurator interface via `phx.frontend`:
65
66
 
66
67
  ```typescript
67
- export const CustomComponent = () => {'{'}
68
+ export const CustomComponent = () => {
68
69
  // Access domain systems
69
- const {'{'} navigationSystem, storeSystem {'}'} = phx.frontend.useDomain()
70
+ const { navigationSystem, storeSystem } = phx.frontend.useDomain()
70
71
 
71
72
  // Entity state hooks
72
73
  const rootEntity = phx.frontend.useRootEntityState()
@@ -76,11 +77,11 @@ export const CustomComponent = () => {'{'}
76
77
  const selectEntity = phx.frontend.useSelectEntity()
77
78
 
78
79
  // Type-safe entity access
79
- if (selectedEntity && Cabinet.is(selectedEntity)) {'{'}
80
+ if (selectedEntity && Cabinet.is(selectedEntity)) {
80
81
  // selectedEntity is now typed as $Cabinet
81
82
  console.log(selectedEntity.width, selectedEntity.$area)
82
- {'}'}
83
- {'}'}
83
+ }
84
+ }
84
85
  ```
85
86
 
86
87
  ## Core Hooks
@@ -90,16 +91,16 @@ The configurator interface provides 100+ hooks organized by category. Use TypeSc
90
91
  ### Domain & Application Context
91
92
 
92
93
  ```typescript
93
- const ExampleComponent = () => {'{'}
94
+ const ExampleComponent = () => {
94
95
  // Core domain systems
95
- const {'{'} navigationSystem, storeSystem, entitySystem {'}'} = phx.frontend.useDomain()
96
+ const { navigationSystem, storeSystem, entitySystem } = phx.frontend.useDomain()
96
97
 
97
98
  // Domain application context
98
- const {'{'} configurationPersistSystem {'}'} = phx.frontend.useDomainApplication()
99
+ const { configurationPersistSystem } = phx.frontend.useDomainApplication()
99
100
 
100
101
  // Full configurator context
101
- const {'{'} imageRenderingSystem {'}'} = phx.frontend.useConfigurator()
102
- {'}'}
102
+ const { imageRenderingSystem } = phx.frontend.useConfigurator()
103
+ }
103
104
  ```
104
105
 
105
106
  ### State Subscriptions
@@ -157,26 +158,31 @@ const viewState = phx.frontend.useActiveViewState()
157
158
  const toolState = phx.frontend.useActiveToolState()
158
159
 
159
160
  // Navigation actions
160
- const navigate = phx.frontend.useNavigate()
161
- const pushNavigate = phx.frontend.usePushNavigate()
162
- 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
+
165
+ // Fullscreen: a flag in the configurator's state that the layout honours; it does not put the browser into fullscreen
166
+ const isFullscreen = phx.frontend.useIsFullscreenActive()
167
+ const setFullscreen = phx.frontend.useSetFullscreen() // setFullscreen(true) or setFullscreen(false)
163
168
  ```
164
169
 
165
170
  ### Controls
166
171
 
167
172
  ```typescript
168
- // Control hooks for UI inputs
169
- const swatchSelection = phx.frontend.useSwatchSelection(entityId, controlId)
170
- const textValue = phx.frontend.useTextValue(entityId, controlId)
171
- const attributeSelection = phx.frontend.useAttributeSelection(entityId, controlId)
172
- const attributeSwitch = phx.frontend.useAttributeSwitch(entityId, controlId)
173
- const distanceDimension = phx.frontend.useDistanceDimension(entityId, controlId)
174
- const angleDimension = phx.frontend.useAngleDimension(entityId, controlId)
175
- const genericDimension = phx.frontend.useGenericDimension(entityId, controlId)
173
+ // Control hooks for UI inputs: the entity id and the instance name of the control component on it
174
+ // useSwatchSelection requires its third argument, the fixable violation types
175
+ const swatchSelection = phx.frontend.useSwatchSelection(entityId, instanceName, [])
176
+ const textValue = phx.frontend.useTextValue(entityId, instanceName)
177
+ const attributeSelection = phx.frontend.useAttributeSelection(entityId, instanceName)
178
+ const attributeSwitch = phx.frontend.useAttributeSwitch(entityId, instanceName)
179
+ const distanceDimension = phx.frontend.useDistanceDimension(entityId, instanceName)
180
+ const angleDimension = phx.frontend.useAngleDimension(entityId, instanceName)
181
+ const genericDimension = phx.frontend.useGenericDimension(entityId, instanceName)
176
182
 
177
183
  // Update control
178
184
  const updateControl = phx.frontend.useUpdateControl()
179
- const updatedControl = phx.frontend.useUpdatedControl(entityId, controlId)
185
+ const updatedControl = phx.frontend.useUpdatedControl(updates)
180
186
  ```
181
187
 
182
188
  ### Recipes & Entity Creation
@@ -202,14 +208,18 @@ const deletedEntity = phx.frontend.useDeletedEntity()
202
208
  ### Validation & Violations
203
209
 
204
210
  ```typescript
205
- // State validity
211
+ // Validity of the last committed state; a rejected change never becomes committed, so it is not seen here
206
212
  const validity = phx.frontend.useStateValidity()
207
213
 
208
- // Control violations
209
- const controlViolations = phx.frontend.useControlViolations(entityId, controlId)
214
+ // Control violations: takes the violation types to ignore and returns a function that maps a prepared
215
+ // state emission to { disabled, hints, violations }
216
+ const controlViolations = phx.frontend.useControlViolations([])
210
217
 
211
218
  // Finalize violations
212
- const {'{'} disabled, hints {'}'} = phx.frontend.useFinalizeViolations()
219
+ const { disabled, hints } = phx.frontend.useFinalizeViolations()
220
+
221
+ // Messages for the user: violations of a rejected change, side-effect hints and errors, one call each
222
+ phx.frontend.useConfiguratorMessages(message => showToast(message.text))
213
223
 
214
224
  // Map violations to hints
215
225
  const mapViolationHints = phx.frontend.useMapViolationHints()
@@ -284,70 +294,70 @@ The interface provides factory functions for creating handlers and providers:
284
294
 
285
295
  ```typescript
286
296
  // Runs early in app startup, before session auth
287
- const OnApplicationStart = phx.frontend.createOnApplicationStartProvider(({'{'} children {'}'}) => {'{'}
288
- return <YourProvider>{'{'}children{'}'}</YourProvider>
289
- {'}'})
297
+ const OnApplicationStart = phx.frontend.createOnApplicationStartProvider(({ children }) => {
298
+ return <YourProvider>{children}</YourProvider>
299
+ })
290
300
 
291
301
  // Runs after session auth, before domain data loading
292
- const OnSessionReady = phx.frontend.createOnSessionReadyProvider(({'{'} children {'}'}) => {'{'}
293
- return <YourProvider>{'{'}children{'}'}</YourProvider>
294
- {'}'})
302
+ const OnSessionReady = phx.frontend.createOnSessionReadyProvider(({ children }) => {
303
+ return <YourProvider>{children}</YourProvider>
304
+ })
295
305
 
296
306
  // Runs after published data is parsed, before frontend/backend loading
297
- const OnPublishedDataReady = phx.frontend.createOnPublishedDataReadyProvider(({'{'} children {'}'}) => {'{'}
298
- return <YourProvider>{'{'}children{'}'}</YourProvider>
299
- {'}'})
307
+ const OnPublishedDataReady = phx.frontend.createOnPublishedDataReadyProvider(({ children }) => {
308
+ return <YourProvider>{children}</YourProvider>
309
+ })
300
310
 
301
311
  // Runs after frontend collection and parsing, before backend loading
302
- const OnFrontendDataReady = phx.frontend.createOnFrontendDataReadyProvider(({'{'} children {'}'}) => {'{'}
303
- return <YourProvider>{'{'}children{'}'}</YourProvider>
304
- {'}'})
312
+ const OnFrontendDataReady = phx.frontend.createOnFrontendDataReadyProvider(({ children }) => {
313
+ return <YourProvider>{children}</YourProvider>
314
+ })
305
315
 
306
316
  // Runs after backend loading, before preset selection and configurator initialization
307
- const OnBackendDataReady = phx.frontend.createOnBackendDataReadyProvider(({'{'} children {'}'}) => {'{'}
308
- return <YourProvider>{'{'}children{'}'}</YourProvider>
309
- {'}'})
317
+ const OnBackendDataReady = phx.frontend.createOnBackendDataReadyProvider(({ children }) => {
318
+ return <YourProvider>{children}</YourProvider>
319
+ })
310
320
 
311
321
  // Runs after routing and preset selection are ready
312
- const OnRoutingReady = phx.frontend.createOnRoutingReadyProvider(({'{'} children {'}'}) => {'{'}
313
- return <YourProvider>{'{'}children{'}'}</YourProvider>
314
- {'}'})
322
+ const OnRoutingReady = phx.frontend.createOnRoutingReadyProvider(({ children }) => {
323
+ return <YourProvider>{children}</YourProvider>
324
+ })
315
325
 
316
326
  // Wraps OwnerUi and ReadUi pages
317
- const OnConfiguratorReady = phx.frontend.createOnConfiguratorReadyProvider(({'{'} children {'}'}) => {'{'}
318
- return <YourProvider>{'{'}children{'}'}</YourProvider>
319
- {'}'})
327
+ const OnConfiguratorReady = phx.frontend.createOnConfiguratorReadyProvider(({ children }) => {
328
+ return <YourProvider>{children}</YourProvider>
329
+ })
320
330
  ```
321
331
 
322
332
  ### Data Handlers
323
333
 
324
334
  ```typescript
325
335
  // Collect frontend data during initialization
326
- const collectFrontendData = phx.frontend.createCollectFrontendDataHandler(async (context) => {'{'}
327
- return {'{'} customData: await fetchCustomData() {'}'}
328
- {'}'})
336
+ const collectFrontendData = phx.frontend.createCollectFrontendDataHandler(async context => {
337
+ return { customData: await fetchCustomData() }
338
+ })
329
339
 
330
340
  // Override which published chunks are fetched by the frontend.
331
341
  // See Publishing page for full documentation.
332
- const filterPublishedChunks = phx.frontend.createFilterPublishedChunksHandler(() => {'{'}
342
+ const filterPublishedChunks = phx.frontend.createFilterPublishedChunksHandler(() => {
333
343
  const session = phx.frontend.useSession()
334
344
 
335
- return useMemo(() => {'{'}
345
+ return useMemo(() => {
336
346
  if (session.sessionPayload?.role === 'admin') return ['styling', 'catalog-na']
337
347
  return ['styling']
338
- {'}'}, [session.sessionPayload?.role])
339
- {'}'})
348
+ }, [session.sessionPayload?.role])
349
+ })
340
350
 
341
351
  // Runs after preset data is loaded
342
- const onPresetLoaded = phx.frontend.createOnPresetLoadedHandler((entities, presetData) => {'{'}
352
+ const onPresetLoaded = phx.frontend.createOnPresetLoadedHandler((entities, presetData) => {
343
353
  // Can modify preset entities before rendering
344
354
  return entities
345
- {'}'})
355
+ })
346
356
 
347
357
  // Intercept navigation to index/presets page
348
- const onGoToIndex = phx.frontend.createOnGoToIndexHandler(() => {'{'}
358
+ const onGoToIndex = phx.frontend.createOnGoToIndexHandler(() => {
349
359
  // Custom logic before navigating
350
- {'}'})
360
+ })
351
361
  ```
352
362
 
353
363
  ### UI Components
@@ -385,46 +395,158 @@ const PresetOverlay = phx.frontend.createPresetDeviceNotSupported(SmallScreenNot
385
395
 
386
396
  ### Loaded Configuration UI
387
397
 
388
- A layout element registered under the name `ConfiguratorLayout` replaces the whole UI of a loaded configuration, in owner and in read mode, whatever `layout` the spec sets. Without it, Phoenix renders the layout the spec selects.
398
+ A layout element registered under the name `ConfiguratorLayout` replaces the whole UI of a loaded configuration, in owner and in read mode, whatever `layout` the spec sets. Without it, Phoenix renders the layout the spec selects. [Custom Layout](?path=/docs/layouts-custom-layout--docs) is a worked example: a fully custom product page with its panel on the left, built from the scene, the safe-area hook and state hooks.
389
399
 
390
400
  ```typescript
391
401
  // src/elements/ConfiguratorLayout.tsx
392
- import { LayoutSlots } from '@salsita-ai/sdk/configurator'
402
+ import { LayoutSlots, useSceneSafeArea } from '@salsita-ai/sdk/configurator'
403
+
404
+ const SIDEBAR_WIDTH = 320
393
405
 
394
406
  export default phx.frontend.createCustomPhoenixLayoutElement('ConfiguratorLayout', () => {
407
+ const { containerRef, safeAreaRef } = useSceneSafeArea()
408
+
395
409
  return (
396
- <>
410
+ <div ref={containerRef} style={{ position: 'absolute', inset: 0 }}>
397
411
  <LayoutSlots.Services />
398
412
  <LayoutSlots.SharedCanvas />
399
- <aside style={{ position: 'absolute', insetBlock: 0, insetInlineStart: 0, inlineSize: 320 }}>
413
+ <aside style={{ position: 'absolute', insetBlock: 0, insetInlineStart: 0, inlineSize: SIDEBAR_WIDTH }}>
400
414
  Project sidebar
401
415
  </aside>
402
- </>
416
+ <div
417
+ ref={safeAreaRef}
418
+ style={{ position: 'absolute', insetBlock: 0, insetInlineStart: SIDEBAR_WIDTH, insetInlineEnd: 0, pointerEvents: 'none' }}
419
+ />
420
+ </div>
403
421
  )
404
422
  })
405
423
  ```
406
424
 
407
- - `LayoutSlots.SharedCanvas` renders the scene and fills the page. The layout positions its own UI above it and after it in the markup, as the `aside` does; unpositioned content is painted under the canvas.
425
+ - `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.
426
+ - `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).
408
427
  - The layout has to mount `LayoutSlots.SharedCanvas`, or the project sets `appLoader: legacy`. Otherwise the app loader stays on screen.
409
- - The layout composes the rest from what `@salsita-ai/sdk/configurator` exports: `LayoutSlots.Services`, the other members of `LayoutSlots`, and `useSceneSafeArea`.
410
- - Nothing of the built-in layouts is rendered for it. The finalize page is `Legacy.CommonFinalizePage`, the dialog is `LayoutDialog`, and the preview start screen is `DesktopPreview` or `MobilePreview`, which the layout shows while `usePreview().isPreview` is set. The assistant panel is not available to a project layout.
428
+ - 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.
429
+ - `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).
430
+ - 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`.
431
+ - 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).
432
+ - 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.
411
433
  - Any project layout element named `ConfiguratorLayout` takes over the page.
412
434
 
435
+ `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.
436
+
437
+ #### Hooks for a Project Layout
438
+
439
+ 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.
440
+
441
+ **The product**
442
+
443
+ | Hook | Returns |
444
+ | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
445
+ | `phx.frontend.useRootEntityId()` | The id of the root entity, which the control hooks take. |
446
+ | `phx.frontend.useSelectedDomainEntityId()` | The id of the selected entity, or the root entity's id while nothing is selected. |
447
+ | `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`. |
448
+ | `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. |
449
+ | `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. |
450
+
451
+ **Controls**
452
+
453
+ | Hook | Returns |
454
+ | ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
455
+ | `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`. |
456
+ | `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. |
457
+
458
+ 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.
459
+
460
+ **Texts**
461
+
462
+ 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).
463
+
464
+ **Mode, fullscreen, preview and navigation**
465
+
466
+ | Hook | Returns |
467
+ | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
468
+ | `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. |
469
+ | `phx.frontend.useIsFullscreenActive()` and `phx.frontend.useSetFullscreen()` | The fullscreen flag, a `boolean`, and a function `(enabled: boolean) => void` that sets it. |
470
+ | `usePreview()` from `@salsita-ai/sdk/configurator` | `{ isPreview, isExpanded, isPreviewEnabled, isHidden, enter, leave }`. The layout shows a preview start screen while `isPreview` is `true`. |
471
+ | `phx.frontend.useActiveNavigationState()` | The active navigation state, one of the spec's `navigationStates`. |
472
+ | `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. |
473
+ | `phx.frontend.useActiveViewState()` and `phx.frontend.useActiveToolState()` | The active view state and tool state. |
474
+ | `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. |
475
+
476
+ **Violations**
477
+
478
+ | Hook | Returns |
479
+ | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
480
+ | `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. |
481
+ | `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`. |
482
+ | `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`. |
483
+ | `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. |
484
+
485
+ `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.
486
+
487
+ **Finalize**
488
+
489
+ 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.
490
+
491
+ 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:
492
+
493
+ - `run()`, which records the finalize-button click for analytics and then runs the handler. It does nothing while `disabled` or `isPending`.
494
+ - `isPending`, `true` while the handler runs.
495
+ - `error`, what the handler's last run rejected with, or `null`. It is cleared when the action runs again. No snackbar is raised; the layout shows the error its own way.
496
+ - `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.
497
+
498
+ ```tsx
499
+ import { finalizeActionComponent } from '@salsita-ai/sdk/domain'
500
+ import { PhoenixHandlerBoundary, useResolveLocalizable } from '@salsita-ai/sdk/react'
501
+
502
+ const FinalizeButton = ({ handler, label }: { handler: string; label: string }) => {
503
+ const { run, isPending, error, disabled, hints } = phx.frontend.useFinalizeAction(handler)
504
+
505
+ return (
506
+ <div>
507
+ <button type="button" disabled={disabled || isPending} onClick={run} title={hints}>
508
+ {isPending ? 'Sending…' : label}
509
+ </button>
510
+ {error && <p role="alert">{error.message}</p>}
511
+ </div>
512
+ )
513
+ }
514
+
515
+ export const Finalize = () => {
516
+ const { accessInfo } = phx.frontend.useAccessService()
517
+ const resolve = useResolveLocalizable()
518
+ const actions = phx.frontend.useComponents({ componentTypes: [finalizeActionComponent] }) ?? []
519
+ const primary = actions.find(action => action.state.variant === 'primary') ?? actions[0]
520
+
521
+ if (accessInfo.viewMode || !primary) {
522
+ return null
523
+ }
524
+
525
+ return (
526
+ <PhoenixHandlerBoundary name={primary.state.handler}>
527
+ <FinalizeButton handler={primary.state.handler} label={resolve(primary.state.label)} />
528
+ </PhoenixHandlerBoundary>
529
+ )
530
+ }
531
+ ```
532
+
533
+ 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.
534
+
535
+ 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.
536
+
413
537
  ## TransformerBuilder
414
538
 
415
539
  Use `transformerBuilder` to create entity transformers:
416
540
 
417
541
  ```typescript
418
542
  // Create a transformer that runs when specific conditions are met
419
- const cabinetTransformer = phx.frontend.transformerBuilder
420
- .when(Cabinet.is)
421
- .do((cabinet, context) => {'{'}
422
- // Transform cabinet data
423
- return {'{'}
424
- ...cabinet,
425
- $computedField: calculateSomething(cabinet)
426
- {'}'}
427
- {'}'})
543
+ const cabinetTransformer = phx.frontend.transformerBuilder.when(Cabinet.is).do((cabinet, context) => {
544
+ // Transform cabinet data
545
+ return {
546
+ ...cabinet,
547
+ $computedField: calculateSomething(cabinet),
548
+ }
549
+ })
428
550
  ```
429
551
 
430
552
  **Note**: The deprecated `createTransformer` function should not be used for new implementations.
@@ -434,19 +556,19 @@ const cabinetTransformer = phx.frontend.transformerBuilder
434
556
  The `updates` factory creates type-safe entity updates:
435
557
 
436
558
  ```typescript
437
- const {'{'} storeSystem {'}'} = phx.frontend.useDomain()
559
+ const { storeSystem } = phx.frontend.useDomain()
438
560
 
439
561
  // Create new entity
440
- const createUpdates = phx.frontend.updates.create({'{'}
562
+ const createUpdates = phx.frontend.updates.create({
441
563
  type: 'Cabinet',
442
564
  width: 800,
443
565
  height: 2000,
444
- {'}'})
566
+ })
445
567
 
446
568
  // Modify existing entity
447
- const modifyUpdates = phx.frontend.updates.modify(cabinetId, {'{'}
569
+ const modifyUpdates = phx.frontend.updates.modify(cabinetId, {
448
570
  width: 1000,
449
- {'}'})
571
+ })
450
572
 
451
573
  // Delete entity
452
574
  const deleteUpdates = phx.frontend.updates.delete(cabinetId)