@salsita-ai/sdk 0.50.0-develop.c33986dc44 → 0.50.0-develop.ca5894b56c

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 (245) hide show
  1. package/README.md +1 -1
  2. package/dist/application-spec/core/enhanced-json-value.d.ts +2 -31
  3. package/dist/application-spec/core/enhanced-json-value.js +1 -1
  4. package/dist/application-spec/core/index.d.ts +1 -3
  5. package/dist/application-spec/core/index.js +1 -1
  6. package/dist/application-spec/core/json-schema.d.ts +2 -52
  7. package/dist/application-spec/core/markers/index.d.ts +1 -1
  8. package/dist/application-spec/core/markers/index.js +1 -1
  9. package/dist/application-spec/core/markers/msg-marker.d.ts +2 -28
  10. package/dist/application-spec/core/markers/msg-marker.js +1 -1
  11. package/dist/application-spec/core/markers/raw-eval-write-marker.d.ts +1 -1
  12. package/dist/application-spec/core/markers/raw-eval-write-marker.js +1 -1
  13. package/dist/application-spec/core/markers/raw-read-write-marker.js +1 -1
  14. package/dist/application-spec/errors/diagnostic-codes.d.ts +1 -0
  15. package/dist/application-spec/errors/diagnostic-codes.js +1 -1
  16. package/dist/application-spec/errors/diagnostic-messages.d.ts +1 -0
  17. package/dist/application-spec/errors/diagnostic-messages.js +1 -1
  18. package/dist/application-spec/generate-application/code/project-scaffolding.js +2 -2
  19. package/dist/application-spec/generate-application/code/templates/index.d.ts +2 -6
  20. package/dist/application-spec/generate-application/code/templates/index.js +1 -1
  21. package/dist/application-spec/generate-application/code/templates/skills/phoenix-skill-bundle.d.ts +10 -0
  22. package/dist/application-spec/generate-application/code/templates/skills/phoenix-skill-bundle.js +7 -0
  23. package/dist/application-spec/generate-application/code/templates/skills/phoenix-skill.js +1 -1
  24. package/dist/application-spec/generate-application/code/templates/skills/sdk-content.d.ts +1 -1
  25. package/dist/application-spec/generate-application/code/templates/skills/sdk-content.js +1 -1
  26. package/dist/application-spec/generate-application/runtime/factory-data/factory-data-runtime.d.ts +2 -2
  27. package/dist/application-spec/generate-application/runtime/factory-data/recipe-defaults.d.ts +1 -1
  28. package/dist/application-spec/generate-application/runtime/factory-data/recipe-defaults.js +1 -1
  29. package/dist/application-spec/generate-application/runtime/factory-data/static-violations.d.ts +1 -1
  30. package/dist/application-spec/generate-application/runtime/factory-data/static-violations.js +1 -1
  31. package/dist/application-spec/generate-application/runtime/generate-runtime-instance.d.ts +1 -1
  32. package/dist/application-spec/generate-application/runtime/generate-runtime-instance.js +1 -1
  33. package/dist/application-spec/generate-application/runtime/sections/build-sdk-optic.js +1 -1
  34. package/dist/application-spec/generate-application/runtime/sections/expression-runtime.d.ts +1 -1
  35. package/dist/application-spec/generate-application/runtime/sections/expression-runtime.js +1 -1
  36. package/dist/application-spec/generate-application/runtime/sections/generate-memo-eval-plan-runtime.d.ts +1 -1
  37. package/dist/application-spec/generate-application/runtime/sections/generate-memo-eval-plan-runtime.js +1 -1
  38. package/dist/application-spec/generate-application/runtime/sections/preset-runtime.d.ts +2 -3
  39. package/dist/application-spec/generate-application/runtime/sections/reflection-constants-runtime.d.ts +2 -1
  40. package/dist/application-spec/generate-application/runtime/spec-code/resolve-spec-code.d.ts +1 -1
  41. package/dist/application-spec/parse-application-files/parse-application-files.js +1 -1
  42. package/dist/application-spec/parse-application-files/raw-spec/raw-expressions.d.ts +1 -1
  43. package/dist/application-spec/parse-application-files/raw-spec/raw-expressions.js +1 -1
  44. package/dist/application-spec/parse-application-files/spec/model/aliases.d.ts +3 -9
  45. package/dist/application-spec/parse-application-files/spec/model/common-types.d.ts +6 -14
  46. package/dist/application-spec/parse-application-files/spec/model/defaults.d.ts +6 -31
  47. package/dist/application-spec/parse-application-files/spec/model/entity-types.d.ts +3 -8
  48. package/dist/application-spec/parse-application-files/spec/model/model-version-key.d.ts +3 -3
  49. package/dist/application-spec/parse-application-files/spec/model/names/action-name.d.ts +3 -7
  50. package/dist/application-spec/parse-application-files/spec/model/names/alias-name.d.ts +3 -7
  51. package/dist/application-spec/parse-application-files/spec/model/names/entity-or-alias-name.d.ts +3 -8
  52. package/dist/application-spec/parse-application-files/spec/model/names/entity-type-name.d.ts +3 -8
  53. package/dist/application-spec/parse-application-files/spec/model/names/expression-name.d.ts +3 -6
  54. package/dist/application-spec/parse-application-files/spec/model/names/instance-name.d.ts +3 -7
  55. package/dist/application-spec/parse-application-files/spec/model/names/invariant-name.d.ts +3 -3
  56. package/dist/application-spec/parse-application-files/spec/model/names/shape-property-name.d.ts +3 -11
  57. package/dist/application-spec/parse-application-files/spec/model/names/violation-type-name.d.ts +3 -3
  58. package/dist/application-spec/parse-application-files/spec/model/presets.d.ts +6 -29
  59. package/dist/client/authorized-client.d.ts +3 -1
  60. package/dist/client/authorized-client.js +1 -1
  61. package/dist/client/contract.d.ts +17 -0
  62. package/dist/client/index.d.ts +1 -1
  63. package/dist/client/local-authorized-client.d.ts +3 -1
  64. package/dist/client/local-authorized-client.js +1 -1
  65. package/dist/client/memory-authorized-client.d.ts +4 -1
  66. package/dist/client/memory-authorized-client.js +1 -1
  67. package/dist/client/phoenix-api.d.ts +228 -0
  68. package/dist/configurator/ConfiguratorApp.js +1 -1
  69. package/dist/configurator/application-runtime/extensions/selectors.js +1 -1
  70. package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.d.ts +13 -0
  71. package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.js +1 -1
  72. package/dist/configurator/application-runtime/extensions/systems/assistant-system/graphql-tools.d.ts +2 -1
  73. package/dist/configurator/application-runtime/extensions/systems/assistant-system/graphql-tools.js +1 -1
  74. package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.d.ts +15 -9
  75. package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.js +1 -1
  76. package/dist/configurator/common/configurator-interface.d.ts +2 -2
  77. package/dist/configurator/common/configurator-interface.js +1 -1
  78. package/dist/configurator/configurator-context/ConfiguratorDataProvider.js +1 -1
  79. package/dist/configurator/configurator-context/PresetSelectionProvider.js +1 -1
  80. package/dist/configurator/configurator-context/configurator-hooks/index.d.ts +0 -1
  81. package/dist/configurator/configurator-context/configurator-hooks/index.js +1 -1
  82. package/dist/configurator/configurator-context/index.d.ts +0 -4
  83. package/dist/configurator/configurator-context/index.js +1 -1
  84. package/dist/configurator/pages/ConfiguratorNotFoundPage.js +1 -1
  85. package/dist/configurator/pages/LoadedConfiguratorDetailPage.js +1 -1
  86. package/dist/configurator/pages/error-boundaries/DevContextFallback.js +1 -1
  87. package/dist/configurator/pages/error-boundaries/DevLoadingFallback.js +1 -1
  88. package/dist/configurator/ui/assistant/screenshots/ScreenshotThumbnails.d.ts +1 -1
  89. package/dist/configurator/ui/assistant/screenshots/ScreenshotThumbnails.js +1 -1
  90. package/dist/configurator/ui/assistant/screenshots/tool-screenshots.d.ts +12 -4
  91. package/dist/configurator/ui/assistant/screenshots/tool-screenshots.js +1 -1
  92. package/dist/configurator/ui/assistant/screenshots/use-screenshot-src.d.ts +8 -0
  93. package/dist/configurator/ui/assistant/screenshots/use-screenshot-src.js +1 -0
  94. package/dist/configurator/ui/configurator-layout/slots/canvas/GPUAccelerationDisabled.js +1 -1
  95. package/dist/configurator/ui/configurator-layout/slots/canvas/WebGLNotSupported.js +1 -1
  96. package/dist/configurator/ui/configurator-layout/slots/readonly-banner/CloneToEditDialog.js +1 -1
  97. package/dist/configurator/ui/legacy/chat/MobileChat.js +1 -1
  98. package/dist/configurator/ui/legacy/layout/common/CommonTopNavigation.js +1 -1
  99. package/dist/configurator/ui/legacy/layout/simple/SimpleSidebar.js +1 -1
  100. package/dist/configurator/ui/legacy/layout/simple/SimpleTopNavigation.js +1 -1
  101. package/dist/configurator/ui/legacy/layout/steps/StepsNavigation.js +1 -1
  102. package/dist/configurator/ui/legacy/layout/steps/StepsOwnerLayout.js +1 -1
  103. package/dist/configurator/ui/legacy/layout/steps/StepsSideBar.js +1 -1
  104. package/dist/configurator/ui/legacy/layout/tabs/TabsSidebar.js +1 -1
  105. package/dist/configurator/ui/phoenix-layout-elements/header/common/back-button/use-go-back-action.js +1 -1
  106. package/dist/configurator/ui/phoenix-layout-elements/header/common/step-navigation/StepsNavigation.js +1 -1
  107. package/dist/configurator/ui/phoenix-layout-elements/sidebar-footer/common/PreviousStepButton.js +1 -1
  108. package/dist/docs/3d-scene/cameras/custom.mdx +4 -2
  109. package/dist/docs/index.json +1 -1
  110. package/dist/docs/phoenix/domain-model.mdx +4 -1
  111. package/dist/docs/phoenix/phoenix-cli.mdx +14 -6
  112. package/dist/docs/phoenix/project-structure.mdx +21 -17
  113. package/dist/docs/phoenix/sdk-builder-workflow.mdx +2 -2
  114. package/dist/docs/phoenix/webhooks.mdx +5 -1
  115. package/dist/domain/application/assistant/build-assistant-manifest.js +1 -1
  116. package/dist/domain/application/assistant/generate-assistant-prompt.js +1 -1
  117. package/dist/domain/index.d.ts +1 -0
  118. package/dist/domain/index.js +1 -1
  119. package/dist/domain/spec/application/index.d.ts +1 -0
  120. package/dist/domain/spec/application/index.js +1 -0
  121. package/dist/{application-spec/core → domain/spec}/components/component-filter.d.ts +3 -2
  122. package/dist/domain/spec/enhanced-json-value.d.ts +30 -0
  123. package/dist/domain/spec/enhanced-json-value.js +1 -0
  124. package/dist/domain/spec/index.d.ts +7 -0
  125. package/dist/domain/spec/index.js +1 -0
  126. package/dist/domain/spec/json-schema.d.ts +52 -0
  127. package/dist/domain/spec/json-schema.js +1 -0
  128. package/dist/domain/spec/markers/index.d.ts +2 -0
  129. package/dist/domain/spec/markers/index.js +1 -0
  130. package/dist/domain/spec/markers/msg-marker.d.ts +28 -0
  131. package/dist/domain/spec/markers/msg-marker.js +1 -0
  132. package/dist/domain/spec/model/aliases.d.ts +9 -0
  133. package/dist/domain/spec/model/aliases.js +1 -0
  134. package/dist/domain/spec/model/common-types.d.ts +15 -0
  135. package/dist/domain/spec/model/common-types.js +1 -0
  136. package/dist/domain/spec/model/defaults.d.ts +33 -0
  137. package/dist/domain/spec/model/defaults.js +1 -0
  138. package/dist/domain/spec/model/entity-types.d.ts +8 -0
  139. package/dist/domain/spec/model/entity-types.js +1 -0
  140. package/dist/domain/spec/model/index.d.ts +7 -0
  141. package/dist/domain/spec/model/index.js +1 -0
  142. package/dist/domain/spec/model/model-version-key.d.ts +3 -0
  143. package/dist/domain/spec/model/model-version-key.js +1 -0
  144. package/dist/domain/spec/model/names/action-name.d.ts +7 -0
  145. package/dist/domain/spec/model/names/action-name.js +1 -0
  146. package/dist/domain/spec/model/names/alias-name.d.ts +7 -0
  147. package/dist/domain/spec/model/names/alias-name.js +1 -0
  148. package/dist/domain/spec/model/names/entity-or-alias-name.d.ts +8 -0
  149. package/dist/domain/spec/model/names/entity-or-alias-name.js +1 -0
  150. package/dist/domain/spec/model/names/entity-type-name.d.ts +8 -0
  151. package/dist/domain/spec/model/names/entity-type-name.js +1 -0
  152. package/dist/domain/spec/model/names/expression-name.d.ts +6 -0
  153. package/dist/domain/spec/model/names/expression-name.js +1 -0
  154. package/dist/domain/spec/model/names/index.d.ts +9 -0
  155. package/dist/domain/spec/model/names/index.js +1 -0
  156. package/dist/domain/spec/model/names/instance-name.d.ts +7 -0
  157. package/dist/domain/spec/model/names/instance-name.js +1 -0
  158. package/dist/domain/spec/model/names/invariant-name.d.ts +3 -0
  159. package/dist/domain/spec/model/names/invariant-name.js +1 -0
  160. package/dist/domain/spec/model/names/shape-property-name.d.ts +11 -0
  161. package/dist/domain/spec/model/names/shape-property-name.js +1 -0
  162. package/dist/domain/spec/model/names/violation-type-name.d.ts +3 -0
  163. package/dist/domain/spec/model/names/violation-type-name.js +1 -0
  164. package/dist/domain/spec/model/presets.d.ts +30 -0
  165. package/dist/domain/spec/model/presets.js +1 -0
  166. package/dist/phoenix/application-spec/generate-application-spec-project.d.ts +1 -0
  167. package/dist/phoenix/application-spec/generate-application-spec-project.js +2 -2
  168. package/dist/phoenix/application-spec/generate-project-files.d.ts +1 -0
  169. package/dist/phoenix/application-spec/index.d.ts +1 -0
  170. package/dist/phoenix/application-spec/index.js +1 -1
  171. package/dist/phoenix/application-spec/resolve-deployment-data.d.ts +11 -0
  172. package/dist/phoenix/application-spec/resolve-deployment-data.js +2 -2
  173. package/dist/phoenix/bin.js +1 -1
  174. package/dist/phoenix/cli.js +1 -1
  175. package/dist/phoenix/commands/build.js +1 -1
  176. package/dist/phoenix/commands/clean.js +1 -1
  177. package/dist/phoenix/commands/deploy.js +1 -1
  178. package/dist/phoenix/commands/dev.js +5 -5
  179. package/dist/phoenix/commands/generated-env-markers.d.ts +1 -0
  180. package/dist/phoenix/commands/generated-env-markers.js +1 -1
  181. package/dist/phoenix/commands/index.d.ts +1 -1
  182. package/dist/phoenix/commands/index.js +1 -1
  183. package/dist/phoenix/commands/sync.js +1 -1
  184. package/dist/phoenix/lingui/index.d.ts +1 -0
  185. package/dist/phoenix/lingui/index.js +1 -0
  186. package/dist/phoenix/{services → lingui}/lingui.d.ts +5 -2
  187. package/dist/phoenix/lingui/lingui.js +1 -0
  188. package/dist/phoenix/services/application-spec-project-watcher.d.ts +3 -1
  189. package/dist/phoenix/services/application-spec-project-watcher.js +2 -2
  190. package/dist/phoenix/services/index.d.ts +1 -0
  191. package/dist/phoenix/services/index.js +1 -1
  192. package/dist/phoenix/services/lingui-watcher.js +1 -1
  193. package/dist/phoenix/services/prepare-development-environment.js +1 -1
  194. package/dist/phoenix/services/vite-config.js +1 -1
  195. package/dist/react-sdk/application-runtime/domain-application-context/chat-system.d.ts +1 -1
  196. package/dist/{configurator/configurator-context → react-sdk}/configurator-routing/ConfiguratorRoutingProvider.d.ts +1 -1
  197. package/dist/react-sdk/configurator-routing/ConfiguratorRoutingProvider.js +1 -0
  198. package/dist/{configurator/configurator-context → react-sdk}/configurator-routing/OnGoToIndexHandler.d.ts +1 -2
  199. package/dist/react-sdk/configurator-routing/OnGoToIndexHandler.js +1 -0
  200. package/dist/react-sdk/configurator-routing/index.d.ts +3 -0
  201. package/dist/{configurator/configurator-context → react-sdk}/configurator-routing/index.js +1 -1
  202. package/dist/{configurator/configurator-context → react-sdk}/configurator-routing/use-power-routing-lifecycle.d.ts +2 -0
  203. package/dist/react-sdk/configurator-routing/use-power-routing-lifecycle.js +1 -0
  204. package/dist/react-sdk/domain-application-context/OnPresetLoadedHandler.js +1 -0
  205. package/dist/react-sdk/domain-application-context/chat-hooks.d.ts +4 -0
  206. package/dist/react-sdk/domain-application-context/chat-hooks.js +1 -0
  207. package/dist/react-sdk/domain-application-context/index.d.ts +2 -0
  208. package/dist/react-sdk/domain-application-context/index.js +1 -1
  209. package/dist/react-sdk/index.d.ts +3 -0
  210. package/dist/react-sdk/index.js +1 -1
  211. package/dist/react-sdk/integration/IntegrationSubscription.js +1 -0
  212. package/dist/react-sdk/integration/index.d.ts +1 -0
  213. package/dist/react-sdk/integration/index.js +1 -0
  214. package/dist/react-sdk/lifecycle-providers/LifecycleProviders.js +1 -0
  215. package/dist/react-sdk/lifecycle-providers/index.d.ts +1 -0
  216. package/dist/react-sdk/lifecycle-providers/index.js +1 -0
  217. package/package.json +3 -3
  218. package/dist/configurator/configurator-context/IntegrationSubscription.js +0 -1
  219. package/dist/configurator/configurator-context/LifecycleProviders.js +0 -1
  220. package/dist/configurator/configurator-context/OnPresetLoadedHandler.js +0 -1
  221. package/dist/configurator/configurator-context/configurator-hooks/chat-hooks.d.ts +0 -1
  222. package/dist/configurator/configurator-context/configurator-hooks/chat-hooks.js +0 -1
  223. package/dist/configurator/configurator-context/configurator-routing/ConfiguratorRoutingProvider.js +0 -1
  224. package/dist/configurator/configurator-context/configurator-routing/OnGoToIndexHandler.js +0 -1
  225. package/dist/configurator/configurator-context/configurator-routing/index.d.ts +0 -2
  226. package/dist/configurator/configurator-context/configurator-routing/use-power-routing-lifecycle.js +0 -1
  227. package/dist/phoenix/services/lingui.js +0 -1
  228. /package/dist/{application-spec/core → domain/spec/application}/application-instance.d.ts +0 -0
  229. /package/dist/{application-spec/core → domain/spec/application}/application-instance.js +0 -0
  230. /package/dist/{application-spec/core → domain/spec}/components/component-filter.js +0 -0
  231. /package/dist/{application-spec/core → domain/spec}/components/index.d.ts +0 -0
  232. /package/dist/{application-spec/core → domain/spec}/components/index.js +0 -0
  233. /package/dist/{application-spec/core → domain/spec}/components/sdk-code-ref.d.ts +0 -0
  234. /package/dist/{application-spec/core → domain/spec}/components/sdk-code-ref.js +0 -0
  235. /package/dist/{application-spec/core → domain/spec}/expressions/index.d.ts +0 -0
  236. /package/dist/{application-spec/core → domain/spec}/expressions/index.js +0 -0
  237. /package/dist/{application-spec/core → domain/spec}/expressions/relation-expression-params.d.ts +0 -0
  238. /package/dist/{application-spec/core → domain/spec}/expressions/relation-expression-params.js +0 -0
  239. /package/dist/{application-spec/core → domain/spec}/expressions/sdk-expression-implementation.d.ts +0 -0
  240. /package/dist/{application-spec/core → domain/spec}/expressions/sdk-expression-implementation.js +0 -0
  241. /package/dist/{application-spec/core → domain/spec}/markers/eval-marker.d.ts +0 -0
  242. /package/dist/{application-spec/core → domain/spec}/markers/eval-marker.js +0 -0
  243. /package/dist/{configurator/configurator-context → react-sdk/domain-application-context}/OnPresetLoadedHandler.d.ts +0 -0
  244. /package/dist/{configurator/configurator-context → react-sdk/integration}/IntegrationSubscription.d.ts +0 -0
  245. /package/dist/{configurator/configurator-context → react-sdk/lifecycle-providers}/LifecycleProviders.d.ts +0 -0
@@ -1 +1 @@
1
- import{jsx as o,jsxs as t,Fragment as e}from"react/jsx-runtime";import{useCallback as a,useMemo as i}from"react";import{customPriceFooterComponent as n,navigationStepsComponent as r,sidebarTabsComponent as s}from"#package/domain";import{useActiveNavigationState as m,useComponents as l,useDomain as p,useFirstRootComponent as c,usePresetEmbeddedState as f,useResolveLocalizable as d,useRootEntityPrice as g,useSelectedDomainEntityId as u,useStoreSelector as v}from"#package/react-sdk";import{MobileControls as b}from"#package/ui-sdk";import{useConfiguratorRouting as j,usePresetSelection as S}from"../../../../configurator-context/index.js";import{LayoutSlots as x}from"../../../configurator-layout/index.js";import{LayoutDialog as C}from"../../../phoenix-layout-elements/LayoutDialog.js";import{LayoutFooter as y,LayoutToolbarDesktop as k,LayoutToolbarDesktopFullScreen as T}from"../../../phoenix-layout-elements/index.js";import{SaveMessageSnackbars as I}from"../../../save-message-snackbars/index.js";import{FullscreenDesktopToolbar as h}from"../../toolbar/desktop-toolbar/FullscreenDesktopToolbar.js";import{CommonDesktopToolbar as N}from"../common/CommonDesktopToolbar.js";import{CommonFinalizePage as w}from"../common/CommonFinalizePage.js";import{DefaultViewOnlyMessage as A}from"../common/DefaultViewOnlyMessage.js";import{InvariantViolations as D}from"../common/InvariantViolations.js";import{ReactiveInterfaceLayout as E}from"../common/ReactiveInterfaceLayout.js";import{SideEffects as L}from"../common/SideEffects.js";import{ToolControls as M}from"../common/ToolControls.js";import{ViewControls as P}from"../common/ViewControls.js";import{StepsNavigation as V}from"./StepsNavigation.js";import{StepsSideBar as F}from"./StepsSideBar.js";export const StepsOwnerLayout=()=>{let{storeSystem:O,navigationSystem:B}=p(),H=d(),R=m(),U=u(),z=c(r),$=c(n),q=l({componentTypes:[s],entityIds:[U]}),G=i(()=>z?.state.steps??[],[z]),J=i(()=>(q??[]).flatMap(o=>o.state.tabs).map(o=>{let t=o.navigationStates[0];if(!t)throw Error(`Tab ${t} has no navigation states!`);return{label:H(o.label),icon:o.icon,value:t,isActive:o.navigationStates.includes(R)}}),[q,R,H]),K=i(()=>{let o=J.find(o=>o.isActive);return o?.value??"none"},[J]),Q=v(o=>U===o.state.rootEntityId,[U]),W=g(),X=j(),{presetCategories:Y}=S(),Z=f(),_=Y.flatMap(o=>o.presets).length>1,oo=!Z&&_,ot=a(()=>{let o=G.findIndex(o=>o.navigationStates.includes(R)),t=G[o-1];if(!t){oo&&X.goToIndex();return}let e=t.navigationStates[0];e&&O.stageAndCommit([B.getNavigationUpdate(e)])},[B,R,G,oo,X,O]),oe=a(()=>{let o=G.findIndex(o=>o.navigationStates.includes(R)),t=G[o+1];if(!t)return;let e=t.navigationStates[0];e&&O.stageAndCommit([B.getNavigationUpdate(e)])},[B,R,G,O]),oa=i(()=>G.findIndex(o=>o.navigationStates.includes(R))===G.length-1,[G,R]);return o(E,{canvas:o(x.Canvas,{}),sidebar:o(F,{price:W,tabs:J,activeTab:K,isRootPage:Q,customPriceNode:$?.state.node,isLastStep:oa,onNextStepClick:oe,onPreviousStepClick:ot}),viewControls:o(P,{}),toolControls:o(M,{}),topNavigation:o(V,{steps:G}),branding:null,toolbar:o(k,{defaultSlot:()=>o(N,{})}),fullScreenToolbar:o(T,{defaultSlot:()=>o(h,{})}),overlay:t(e,{children:[o(D,{}),o(I,{}),o(w,{}),o(C,{})]}),mobileControls:o(b.ViewOnlyMode,{message:o(A,{})}),mobileTopNavigation:null,mobileHeaderActionButton:null,mobileHeaderCenter:null,sideEffects:o(L,{}),footer:o(y,{})})};
1
+ import{jsx as o,jsxs as t,Fragment as e}from"react/jsx-runtime";import{useCallback as a,useMemo as i}from"react";import{customPriceFooterComponent as n,navigationStepsComponent as r,sidebarTabsComponent as s}from"#package/domain";import{useActiveNavigationState as m,useComponents as l,useConfiguratorRouting as p,useDomain as c,useFirstRootComponent as f,usePresetEmbeddedState as d,useResolveLocalizable as g,useRootEntityPrice as u,useSelectedDomainEntityId as v,useStoreSelector as b}from"#package/react-sdk";import{MobileControls as j}from"#package/ui-sdk";import{usePresetSelection as S}from"../../../../configurator-context/index.js";import{LayoutSlots as x}from"../../../configurator-layout/index.js";import{LayoutDialog as C}from"../../../phoenix-layout-elements/LayoutDialog.js";import{LayoutFooter as y,LayoutToolbarDesktop as k,LayoutToolbarDesktopFullScreen as T}from"../../../phoenix-layout-elements/index.js";import{SaveMessageSnackbars as I}from"../../../save-message-snackbars/index.js";import{FullscreenDesktopToolbar as h}from"../../toolbar/desktop-toolbar/FullscreenDesktopToolbar.js";import{CommonDesktopToolbar as N}from"../common/CommonDesktopToolbar.js";import{CommonFinalizePage as w}from"../common/CommonFinalizePage.js";import{DefaultViewOnlyMessage as A}from"../common/DefaultViewOnlyMessage.js";import{InvariantViolations as D}from"../common/InvariantViolations.js";import{ReactiveInterfaceLayout as E}from"../common/ReactiveInterfaceLayout.js";import{SideEffects as L}from"../common/SideEffects.js";import{ToolControls as M}from"../common/ToolControls.js";import{ViewControls as P}from"../common/ViewControls.js";import{StepsNavigation as V}from"./StepsNavigation.js";import{StepsSideBar as F}from"./StepsSideBar.js";export const StepsOwnerLayout=()=>{let{storeSystem:O,navigationSystem:B}=c(),H=g(),R=m(),U=v(),z=f(r),$=f(n),q=l({componentTypes:[s],entityIds:[U]}),G=i(()=>z?.state.steps??[],[z]),J=i(()=>(q??[]).flatMap(o=>o.state.tabs).map(o=>{let t=o.navigationStates[0];if(!t)throw Error(`Tab ${t} has no navigation states!`);return{label:H(o.label),icon:o.icon,value:t,isActive:o.navigationStates.includes(R)}}),[q,R,H]),K=i(()=>{let o=J.find(o=>o.isActive);return o?.value??"none"},[J]),Q=b(o=>U===o.state.rootEntityId,[U]),W=u(),X=p(),{presetCategories:Y}=S(),Z=d(),_=Y.flatMap(o=>o.presets).length>1,oo=!Z&&_,ot=a(()=>{let o=G.findIndex(o=>o.navigationStates.includes(R)),t=G[o-1];if(!t){oo&&X.goToIndex();return}let e=t.navigationStates[0];e&&O.stageAndCommit([B.getNavigationUpdate(e)])},[B,R,G,oo,X,O]),oe=a(()=>{let o=G.findIndex(o=>o.navigationStates.includes(R)),t=G[o+1];if(!t)return;let e=t.navigationStates[0];e&&O.stageAndCommit([B.getNavigationUpdate(e)])},[B,R,G,O]),oa=i(()=>G.findIndex(o=>o.navigationStates.includes(R))===G.length-1,[G,R]);return o(E,{canvas:o(x.Canvas,{}),sidebar:o(F,{price:W,tabs:J,activeTab:K,isRootPage:Q,customPriceNode:$?.state.node,isLastStep:oa,onNextStepClick:oe,onPreviousStepClick:ot}),viewControls:o(P,{}),toolControls:o(M,{}),topNavigation:o(V,{steps:G}),branding:null,toolbar:o(k,{defaultSlot:()=>o(N,{})}),fullScreenToolbar:o(T,{defaultSlot:()=>o(h,{})}),overlay:t(e,{children:[o(D,{}),o(I,{}),o(w,{}),o(C,{})]}),mobileControls:o(j.ViewOnlyMode,{message:o(A,{})}),mobileTopNavigation:null,mobileHeaderActionButton:null,mobileHeaderCenter:null,sideEffects:o(L,{}),footer:o(y,{})})};
@@ -1 +1 @@
1
- import{jsx as t}from"react/jsx-runtime";import{AnimatePresence as e}from"motion/react";import{memo as o,useCallback as r}from"react";import{useDomain as a}from"#package/react-sdk";import{Sidebar as i,TabNavigation as m}from"#package/ui-sdk";import{useChatEnabled as n}from"../../../../configurator-context/index.js";import{ChatInteractionContainer as p}from"../../chat/ChatInteractionContainer.js";import{PropertyPagePanel as s}from"../../property-page/PropertyPagePanel.js";import{StepsSidebarFooter as c}from"./StepsSidebarFooter.js";export const StepsSideBar=o(o=>{let{price:d,activeTab:f,tabs:g,isRootPage:l,customPriceNode:u,isLastStep:h,onNextStepClick:P,onPreviousStepClick:S}=o,j=n(),{navigationSystem:k,storeSystem:x}=a(),C=r(t=>{x.stageAndCommit([k.getNavigationUpdate(t)])},[k,x]);return t(i.Layout,{header:null,content:t(s,{isRootPage:l}),footer:t(e,{initial:!1,mode:"wait",children:t(c,{customPriceNode:u,formattedPrice:d,isLastStep:h,onNextStepClick:P,onPreviousStepClick:S})}),chat:j&&t(p,{}),aside:g.length>1&&t(m,{onChange:C,value:f,tabs:g})})});
1
+ import{jsx as t}from"react/jsx-runtime";import{AnimatePresence as e}from"motion/react";import{memo as o,useCallback as r}from"react";import{useChatEnabled as a,useDomain as i}from"#package/react-sdk";import{Sidebar as m,TabNavigation as n}from"#package/ui-sdk";import{ChatInteractionContainer as p}from"../../chat/ChatInteractionContainer.js";import{PropertyPagePanel as s}from"../../property-page/PropertyPagePanel.js";import{StepsSidebarFooter as c}from"./StepsSidebarFooter.js";export const StepsSideBar=o(o=>{let{price:d,activeTab:f,tabs:g,isRootPage:l,customPriceNode:u,isLastStep:h,onNextStepClick:P,onPreviousStepClick:S}=o,k=a(),{navigationSystem:C,storeSystem:j}=i(),x=r(t=>{j.stageAndCommit([C.getNavigationUpdate(t)])},[C,j]);return t(m.Layout,{header:null,content:t(s,{isRootPage:l}),footer:t(e,{initial:!1,mode:"wait",children:t(c,{customPriceNode:u,formattedPrice:d,isLastStep:h,onNextStepClick:P,onPreviousStepClick:S})}),chat:k&&t(p,{}),aside:g.length>1&&t(n,{onChange:x,value:f,tabs:g})})});
@@ -1 +1 @@
1
- import{jsx as t,jsxs as o}from"react/jsx-runtime";import{AnimatePresence as e,motion as a}from"motion/react";import{memo as r,useCallback as i}from"react";import{useDomain as m}from"#package/react-sdk";import{AnimationTarget as n,Sidebar as s,TabNavigation as c}from"#package/ui-sdk";import{useChatEnabled as u}from"../../../../configurator-context/index.js";import{ChatInteractionContainer as p}from"../../chat/ChatInteractionContainer.js";import{CommonFinalizeButton as d}from"../common/CommonFinalizeButton.js";import{CommonSidebarFooter as l}from"../common/CommonSidebarFooter.js";import{DesktopSummaryButton as f}from"../common/DesktopSummaryButton.js";import{TabsSidebarContent as y}from"./TabsSidebarContent.js";let h={transition:{type:"spring",duration:.3},variants:{initial:{opacity:0,y:16},animate:{opacity:1,y:0},exit:{opacity:0,y:16}}};export const TabsSidebar=r(r=>{let{price:S,isSummaryActive:g,onEnterSummary:v,activeTab:B,tabs:j,summaryPage:b,onLeaveSummary:P,isRootPage:x,customPriceNode:C,hasSummary:T}=r,w=u(),{navigationSystem:k,storeSystem:F}=m(),N=i(t=>{F.stageAndCommit([k.getNavigationUpdate(t)])},[k,F]);return t(s.Layout,{header:null,content:t(y,{summaryPage:b,isSummaryActive:g,onLeaveSummary:P,isRootPage:x}),footer:o(e,{initial:!1,mode:"wait",children:[!T&&t(l,{customPriceNode:C,formattedPrice:S,actionButton:t(d,{}),showTemplateButton:!0,showSaveTemplateButton:!0}),T&&!g&&t(a.div,{...h,...n,children:t(l,{customPriceNode:C,actionButton:t(f,{onEnterSummary:v}),formattedPrice:S,showSaveTemplateButton:!1})},"sidebarFooterSummary"),T&&g&&t(a.div,{...h,...n,children:t(l,{customPriceNode:C,actionButton:t(d,{}),formattedPrice:S,showTemplateButton:!0,showSaveTemplateButton:!0})},"sidebarFooterSummary")]}),chat:w&&t(p,{}),aside:j.length>1&&t(c,{onChange:N,value:B,tabs:j})})});
1
+ import{jsx as t,jsxs as o}from"react/jsx-runtime";import{AnimatePresence as e,motion as a}from"motion/react";import{memo as r,useCallback as m}from"react";import{useChatEnabled as i,useDomain as n}from"#package/react-sdk";import{AnimationTarget as s,Sidebar as c,TabNavigation as u}from"#package/ui-sdk";import{ChatInteractionContainer as p}from"../../chat/ChatInteractionContainer.js";import{CommonFinalizeButton as d}from"../common/CommonFinalizeButton.js";import{CommonSidebarFooter as l}from"../common/CommonSidebarFooter.js";import{DesktopSummaryButton as y}from"../common/DesktopSummaryButton.js";import{TabsSidebarContent as f}from"./TabsSidebarContent.js";let h={transition:{type:"spring",duration:.3},variants:{initial:{opacity:0,y:16},animate:{opacity:1,y:0},exit:{opacity:0,y:16}}};export const TabsSidebar=r(r=>{let{price:S,isSummaryActive:g,onEnterSummary:v,activeTab:B,tabs:b,summaryPage:j,onLeaveSummary:P,isRootPage:C,customPriceNode:T,hasSummary:w}=r,k=i(),{navigationSystem:x,storeSystem:F}=n(),N=m(t=>{F.stageAndCommit([x.getNavigationUpdate(t)])},[x,F]);return t(c.Layout,{header:null,content:t(f,{summaryPage:j,isSummaryActive:g,onLeaveSummary:P,isRootPage:C}),footer:o(e,{initial:!1,mode:"wait",children:[!w&&t(l,{customPriceNode:T,formattedPrice:S,actionButton:t(d,{}),showTemplateButton:!0,showSaveTemplateButton:!0}),w&&!g&&t(a.div,{...h,...s,children:t(l,{customPriceNode:T,actionButton:t(y,{onEnterSummary:v}),formattedPrice:S,showSaveTemplateButton:!1})},"sidebarFooterSummary"),w&&g&&t(a.div,{...h,...s,children:t(l,{customPriceNode:T,actionButton:t(d,{}),formattedPrice:S,showTemplateButton:!0,showSaveTemplateButton:!0})},"sidebarFooterSummary")]}),chat:k&&t(p,{}),aside:b.length>1&&t(u,{onChange:N,value:B,tabs:b})})});
@@ -1 +1 @@
1
- import{useMemo as e}from"react";import{FrontendAnalyticsFactory as o,useAccessService as t,useAnalyticsService as i,useDomainApplication as a,useIntegrationState as r}from"#package/react-sdk";import{useConfiguratorRouting as d,usePresetSelection as n,usePreview as s}from"../../../../../configurator-context/index.js";import{useCloseAction as c}from"../../../../../integration/index.js";export const useGoBackAction=()=>{let l=i(),{configurationPersistSystem:p}=a(),u=d(),{presetCategories:m}=n(),{isPreview:w,isPreviewEnabled:f,leave:g}=s(),{accessInfo:x}=t(),k=m.flatMap(e=>e.presets).length>1,v=r(),C=c();return e(()=>v.embeddedView?null:k?async()=>{l.push(o.GoToProductsClicked()),x.viewMode||await p.save(),u.goToIndex()}:null!==C?async()=>{l.push(o.CloseConfiguratorClicked()),x.viewMode||await p.save(),await C()}:f&&!w?()=>{l.push(o.ExpandedViewClosed()),g()}:null,[l,p,k,v.embeddedView,w,f,g,u,C,x.viewMode])};
1
+ import{useMemo as e}from"react";import{FrontendAnalyticsFactory as o,useAccessService as t,useAnalyticsService as i,useConfiguratorRouting as a,useDomainApplication as r,useIntegrationState as d}from"#package/react-sdk";import{usePresetSelection as n,usePreview as s}from"../../../../../configurator-context/index.js";import{useCloseAction as c}from"../../../../../integration/index.js";export const useGoBackAction=()=>{let l=i(),{configurationPersistSystem:p}=r(),u=a(),{presetCategories:m}=n(),{isPreview:w,isPreviewEnabled:f,leave:g}=s(),{accessInfo:x}=t(),k=m.flatMap(e=>e.presets).length>1,v=d(),C=c();return e(()=>v.embeddedView?null:k?async()=>{l.push(o.GoToProductsClicked()),x.viewMode||await p.save(),u.goToIndex()}:null!==C?async()=>{l.push(o.CloseConfiguratorClicked()),x.viewMode||await p.save(),await C()}:f&&!w?()=>{l.push(o.ExpandedViewClosed()),g()}:null,[l,p,k,v.embeddedView,w,f,g,u,C,x.viewMode])};
@@ -1 +1 @@
1
- import{jsx as t,jsxs as e}from"react/jsx-runtime";import{Trans as o}from"@lingui/react";import{memo as a,useCallback as i}from"react";import{FrontendAnalyticsFactory as r,useAnalyticsService as s,usePresetEmbeddedState as c}from"#package/react-sdk";import{TopNavigation as p,useResponsive as l}from"#package/ui-sdk";import{useConfiguratorRouting as n,usePresetSelection as m}from"../../../../../configurator-context/index.js";export const StepsNavigation=a(a=>{let{steps:d,activeIndex:u,goTo:f}=a,g=s(),k=n(),{presetCategories:x}=m(),h=c(),C=x.flatMap(t=>t.presets).length>1,b=!h&&C,{isTablet:j}=l(),v=i(()=>{g.push(r.GoToProductsClicked()),k.goToIndex()},[g,k]);return e(p.Root,{activeStep:u+ +!!b,nonLinear:!0,compact:j,fullWidth:!0,children:[b&&t(p.Step,{isCompleted:!0,onClick:v,compact:j,children:t(o,{id:"sdk.layout.navigation.steps.first.label",message:"Products"})}),d.map(e=>{let o="complete"===e.status;return t(p.Step,{isCompleted:o,isDisabled:!o,compact:j,onClick:()=>f(e.index),children:e.label},e.label)})]})});
1
+ import{jsx as t,jsxs as e}from"react/jsx-runtime";import{Trans as o}from"@lingui/react";import{memo as a,useCallback as i}from"react";import{FrontendAnalyticsFactory as r,useAnalyticsService as s,useConfiguratorRouting as c,usePresetEmbeddedState as p}from"#package/react-sdk";import{TopNavigation as l,useResponsive as n}from"#package/ui-sdk";import{usePresetSelection as m}from"../../../../../configurator-context/index.js";export const StepsNavigation=a(a=>{let{steps:d,activeIndex:u,goTo:f}=a,g=s(),k=c(),{presetCategories:x}=m(),h=p(),C=x.flatMap(t=>t.presets).length>1,b=!h&&C,{isTablet:j}=n(),v=i(()=>{g.push(r.GoToProductsClicked()),k.goToIndex()},[g,k]);return e(l.Root,{activeStep:u+ +!!b,nonLinear:!0,compact:j,fullWidth:!0,children:[b&&t(l.Step,{isCompleted:!0,onClick:v,compact:j,children:t(o,{id:"sdk.layout.navigation.steps.first.label",message:"Products"})}),d.map(e=>{let o="complete"===e.status;return t(l.Step,{isCompleted:o,isDisabled:!o,compact:j,onClick:()=>f(e.index),children:e.label},e.label)})]})});
@@ -1 +1 @@
1
- import{jsx as e}from"react/jsx-runtime";import{IconButton as t,Tooltip as r}from"@mui/material";import{memo as o,useCallback as i}from"react";import{createPhoenixLayoutElement as a,previousStepButtonSchema as m}from"#package/domain";import{FrontendAnalyticsFactory as s,useAnalyticsService as n,useLocalizable as l,useStepNavigation as p}from"#package/react-sdk";import{PhxIcon as c,useResponsive as d}from"#package/ui-sdk";import{useBackToPresetEnabled as u,useConfiguratorRouting as f,useSteps as k}from"../../../../configurator-context/index.js";let g={id:"sdk.layout.sidebar.previousStep.label",message:"Previous step"};export const PreviousStepButton=a({schema:m,component:o(o=>{let{icon:a}=o,m=l(g),x=n(),h=u(),v=f(),{canGoPrevious:S,previous:b}=p(),{isDesktop:j}=d(),{previousStepDisabled:C}=k();return e(r,{title:m,placement:"top",children:e("span",{style:{alignSelf:"center"},children:e(t,{variant:"muted",color:"default",size:j?"medium":"small",disabled:C,onClick:i(()=>{S?b():(x.push(s.PreviousStepButtonClicked()),h&&v.goToIndex())},[x,h,S,b,v]),children:e(c,{name:a??"CaretLeft"})})})})})});
1
+ import{jsx as e}from"react/jsx-runtime";import{IconButton as t,Tooltip as r}from"@mui/material";import{memo as o,useCallback as i}from"react";import{createPhoenixLayoutElement as a,previousStepButtonSchema as m}from"#package/domain";import{FrontendAnalyticsFactory as s,useAnalyticsService as n,useConfiguratorRouting as l,useLocalizable as p,useStepNavigation as c}from"#package/react-sdk";import{PhxIcon as d,useResponsive as u}from"#package/ui-sdk";import{useBackToPresetEnabled as f,useSteps as k}from"../../../../configurator-context/index.js";let g={id:"sdk.layout.sidebar.previousStep.label",message:"Previous step"};export const PreviousStepButton=a({schema:m,component:o(o=>{let{icon:a}=o,m=p(g),x=n(),h=f(),v=l(),{canGoPrevious:S,previous:b}=c(),{isDesktop:j}=u(),{previousStepDisabled:C}=k();return e(r,{title:m,placement:"top",children:e("span",{style:{alignSelf:"center"},children:e(t,{variant:"muted",color:"default",size:j?"medium":"small",disabled:C,onClick:i(()=>{S?b():(x.push(s.PreviousStepButtonClicked()),h&&v.goToIndex())},[x,h,S,b,v]),children:e(d,{name:a??"CaretLeft"})})})})})});
@@ -49,7 +49,8 @@ Custom camera behavior is implemented via `scheduleCameraTransition` inside an `
49
49
  ```tsx
50
50
  import { useEffect } from 'react'
51
51
 
52
- import { CameraControlType, createOnConfiguratorReadyProvider, useConfigurator } from '@salsita-ai/sdk/configurator'
52
+ import { CameraControlType, useConfigurator } from '@salsita-ai/sdk/configurator'
53
+ import { createOnConfiguratorReadyProvider } from '@salsita-ai/sdk/react'
53
54
 
54
55
  export default createOnConfiguratorReadyProvider(({ children }) => {
55
56
  const { cameraControlSystem } = useConfigurator()
@@ -238,7 +239,8 @@ components:
238
239
  ```tsx
239
240
  import { useEffect } from 'react'
240
241
 
241
- import { CameraControlType, createOnConfiguratorReadyProvider, useConfigurator } from '@salsita-ai/sdk/configurator'
242
+ import { CameraControlType, useConfigurator } from '@salsita-ai/sdk/configurator'
243
+ import { createOnConfiguratorReadyProvider } from '@salsita-ai/sdk/react'
242
244
 
243
245
  export default createOnConfiguratorReadyProvider(({ children }) => {
244
246
  const { cameraControlSystem } = useConfigurator()
@@ -1,5 +1,5 @@
1
1
  {
2
- "generatedAt": "2026-10-17T20:59:41.472Z",
2
+ "generatedAt": "2026-10-19T08:23:55.313Z",
3
3
  "categories": [
4
4
  {
5
5
  "name": "Phoenix",
@@ -842,7 +842,10 @@ Phoenix automatically:
842
842
  During `phoenix dev`, schema-compatible configurator changes can refresh the mounted application in
843
843
  place. Changes to the domain model itself are rejected from live application and require a restart;
844
844
  this preserves the last valid runtime and loaded configuration until migration and deployment can be
845
- coordinated atomically.
845
+ coordinated atomically. With server persistence, restart `phoenix dev` itself: it logs an error that
846
+ asks for the restart, and registers the changed model with its development deployment when it starts
847
+ again. Then open the preview address it prints rather than a tab whose address has `?cid=`, which
848
+ loads a configuration saved with the earlier model.
846
849
 
847
850
  ### The phx Global Namespace
848
851
 
@@ -164,6 +164,8 @@ A project has one generated env file. Phoenix keeps it in `.phoenix/`, as `.phoe
164
164
  - The deployment and environment commands (`deployment:prepare`, `deployment:prepare-dev`, `deployment:prepare-local`, `deployment:build-envs` and the others with an `--outFile` option) write `.env.generated` in the project root unless `--outFile` or `OUT_FILE` names another file. Deploy pipelines read it there. Once one of them has written it, `phoenix dev` and `phoenix build` use it.
165
165
  - Everything that reads environment variables follows the same rule: the root `.env.generated` when it exists, otherwise `.phoenix/.env.generated`. The two files are never merged.
166
166
  - `phoenix clean` removes `.phoenix/` and with it `.phoenix/.env.generated`; the next `phoenix dev` prepares the development deployment again. A root `.env.generated` stays.
167
+ - `phoenix dev` starts on the deployment the file names, and prepares a new one when the file was written for another project, environment or server, when the spec registers something on the server the deployment was not prepared with (the domain model, the published data schema, the auth hook, the roles that may create configurations, or the assistant), and on `--force`. A running `phoenix dev` does not prepare again after such a change: it logs an error that asks for a restart.
168
+ - Asset contents do not trigger preparation. After changing uploaded assets, restart with `phoenix dev --force` to prepare and upload them again.
167
169
 
168
170
  This holds for a project on the application-spec pipeline. In a project on the legacy pipeline (see [Current and legacy project tooling](#current-and-legacy-project-tooling)), `phoenix dev` always reads and writes `.env.generated` in the project root.
169
171
 
@@ -305,14 +307,16 @@ Starts the full development environment: Vite dev server, development webhook tu
305
307
  | `--environmentSlug` | `ENVIRONMENT_SLUG` | package.json name | Environment slug |
306
308
  | `--withLingui` | `WITH_LINGUI` | `false` | Enable i18n extraction/compilation and watch i18n sources during dev |
307
309
  | `--persistence` | `PERSISTENCE_MODE` | `server` | Persistence mode |
308
- | `--force` / `-f` | `FORCE_REBUILD` | `false` | Force full rebuild, skip cache |
310
+ | `--force` / `-f` | `FORCE_REBUILD` | `false` | Force full rebuild, skip cache, and prepare the development deployment again |
309
311
  | `--serverUrl` | `SERVER_URL` | the region's server | Phoenix server URL |
310
312
  | `--region` | `PHOENIX_REGION` | `eu` | Region of the signed-in workspace, used when no server URL is set |
311
313
  | `--skipUpload` / `--su` | `SKIP_UPLOAD` | `false` | Skip the S3 asset upload when the development deployment is prepared at startup |
312
314
 
313
315
  With the application-spec pipeline, `phoenix dev` creates an empty `src/` folder when the project has none, so that it rebuilds when the first source file, such as `src/webhooks/index.ts`, is added.
314
316
 
315
- With the application-spec pipeline, the SDK's own Lingui CLI runs with the configuration in `.phoenix/.linguirc`, both for `--withLingui` and when `phoenix sync` and `phoenix build` extract and compile the catalogs; see [Root Level Files](?path=/docs/phoenix-project-structure--docs#root-level-files). The dev server and `phoenix build` compile the Lingui macros (`msg`, `t`, `Trans` and the others, imported from `@lingui/core/macro`, `@lingui/react/macro` or `@lingui/macro`) with the SDK's own Lingui macro plugin and the same configuration, so the project needs neither `babel-plugin-macros` nor `@lingui/macro` for them. Babel macros of other packages are not compiled.
317
+ By default, with the application-spec pipeline, `phoenix dev` bootstraps missing compiled catalogs for every configured locale at startup, so a fresh project can start without a prior sync. It compiles existing `.po` translations as they are and extracts a locale only when its `.po` source is also missing. Existing compiled catalogs and `.po` sources stay unchanged. New or changed messages still need `phoenix sync` or continuous updates enabled by `--withLingui` or `WITH_LINGUI=true`.
318
+
319
+ The SDK's own Lingui CLI uses the configuration generated in `.phoenix/.linguirc`, both for `--withLingui` and when `phoenix sync` and `phoenix build` extract and compile the catalogs; see [Root Level Files](?path=/docs/phoenix-project-structure--docs#root-level-files). The dev server and `phoenix build` compile the Lingui macros (`msg`, `t`, `Trans` and the others, imported from `@lingui/core/macro`, `@lingui/react/macro` or `@lingui/macro`) with the SDK's own Lingui macro plugin and the same configuration, so the project needs neither `babel-plugin-macros` nor `@lingui/macro` for them. Babel macros of other packages are not compiled.
316
320
 
317
321
  In `server` persistence mode the project comes from `--projectSlug`, `PROJECT_SLUG` or `project` in the spec, and the credential from `DEVELOPMENT_KEY` or the `phoenix login` sign-in; see [Starting a New Project](#starting-a-new-project). When signed in, the environment is always your own development environment and `--environmentSlug` is ignored.
318
322
 
@@ -336,7 +340,11 @@ Restart the application after fixing either failure; a subscriber or generation
336
340
  observed the attempted update.
337
341
 
338
342
  Domain-model changes are not applied live yet. Phoenix rejects them with a restart-required
339
- diagnostic so the mounted application cannot observe a new model with old configuration data. Other
343
+ diagnostic so the mounted application cannot observe a new model with old configuration data. With
344
+ server persistence, `phoenix dev` logs an error that asks for its own restart, because the
345
+ development deployment keeps the schema it was prepared with until `phoenix dev` starts again and
346
+ prepares it. After the restart, open the preview address it prints: a tab whose address has `?cid=`
347
+ loads a configuration saved with the earlier model, which the new model rejects. Other
340
348
  generated bootstrap, admin, data, environment, and host-integration surfaces should also be treated
341
349
  as restart-required unless their feature documentation explicitly states live-update support.
342
350
 
@@ -609,7 +617,7 @@ Known limits:
609
617
  - Binary assets, CSS files, locale catalog changes, dependency changes and edits requiring a full application restart are unsupported.
610
618
  - Storybook `story-dev` has no variant previews. A project on the legacy pipeline has no variant previews; the command says so and exits.
611
619
 
612
- Generated projects carry the `phoenix-variants` agent skill, which tells coding agents when to offer options, how to write each kind and what to do with your answer.
620
+ Generated projects carry the `phoenix` agent skill, whose linked Variants guide tells coding agents when to offer options, how to write each kind and what to do with your answer.
613
621
 
614
622
  ## SDK Development
615
623
 
@@ -750,7 +758,7 @@ Revokes the token on the server and removes it from the credentials file. `--reg
750
758
 
751
759
  ### `phoenix deploy`
752
760
 
753
- Validates the spec, packs and uploads the project, starts its build on the server and follows it until the configurator is live. Before the upload the server compares the domain model with the deployed one: a model that changed under an already deployed model version is refused with the reason, and needs a new model version and a [migration](?path=/docs/phoenix-migrations--docs). Once the build starts it prints `Follow the build in Studio: <url>`, the build's page in the Studio you signed in through; a sign-in stored by an older CLI has no Studio URL, so sign in again to get the link. After `Your configurator is live at <url>` it prints one line for coding agents, which tells them to offer the run evaluation of the `phoenix-feedback` skill once you have nothing more to build; you can ignore it when you run the command yourself. `--region` or `--serverUrl` selects the server, as for `login`; `--no-wait` returns as soon as the build starts.
761
+ Validates the spec, packs and uploads the project, starts its build on the server and follows it until the configurator is live. Before the upload the server compares the domain model with the deployed one: a model that changed under an already deployed model version is refused with the reason, and needs a new model version and a [migration](?path=/docs/phoenix-migrations--docs). Once the build starts it prints `Follow the build in Studio: <url>`, the build's page in the Studio you signed in through; a sign-in stored by an older CLI has no Studio URL, so sign in again to get the link. After `Your configurator is live at <url>` it prints one line for coding agents, which tells them to offer the run evaluation in the Feedback guide linked from the `phoenix` skill once you have nothing more to build; you can ignore it when you run the command yourself. `--region` or `--serverUrl` selects the server, as for `login`; `--no-wait` returns as soon as the build starts.
754
762
 
755
763
  To keep the conversation that led to a build, place a reviewed UTF-8 transcript in `conversation.log` at the project root before deploying. Deploy automatically includes it in the private source archive. Download the build's sources from Studio to read it; access is the same as for the rest of that build's sources. The build extractor skips the log, so it is not an application build input or a public asset. Missing logs produce a warning and deployment continues. Directories, links and special files at that path are rejected.
756
764
 
@@ -779,7 +787,7 @@ Pass the message as the argument for a short note, or `--file` for a longer repo
779
787
 
780
788
  A message holds at most 100,000 characters, from the argument or from the file. Besides the message and the flags, the CLI sends only the SDK version, the operating system and the Node.js version; no file of the project is attached, and the Phoenix team reads a build's sources through the build's page in Studio. The project must belong to the signed-in workspace, otherwise the server refuses the feedback; without a project, the feedback is sent on its own.
781
789
 
782
- Generated projects carry the `phoenix-feedback` agent skill. It tells coding agents when to draft feedback, to show you the draft and send it only after you approve it, and how to write a report: the build id with its Studio page link, the live configurator link, what the agent was doing, what happened and what was expected, the exact error or output, the workaround and a condensed conversation log. When you say you are done, or a deploy went live and you confirm the run is finished, the agent asks you to evaluate the run, one question at a time: minutes spent, then a score from 0 to 10 for the agent's guidance, the configurator's functionality, its UX, the quality of the 3D, the UI visuals and the overall result, then your comments. It never picks a score for you and sends nothing when you decline. Secrets, credentials and personal data never go into a message; file paths, spec and code snippets and your own words may, once you approved the draft that contains them. The command needs a server with API 1.40.0 or newer; a message over 5000 characters needs API 1.43.0 or newer.
790
+ Generated projects carry the `phoenix` agent skill. Its linked Feedback guide tells coding agents when to draft feedback, to show you the draft and send it only after you approve it, and how to write a report: the build id with its Studio page link, the live configurator link, what the agent was doing, what happened and what was expected, the exact error or output, the workaround and a condensed conversation log. When you say you are done, or a deploy went live and you confirm the run is finished, the agent asks you to evaluate the run, one question at a time: minutes spent, then a score from 0 to 10 for the agent's guidance, the configurator's functionality, its UX, the quality of the 3D, the UI visuals and the overall result, then your comments. It never picks a score for you and sends nothing when you decline. Secrets, credentials and personal data never go into a message; file paths, spec and code snippets and your own words may, once you approved the draft that contains them. The command needs a server with API 1.40.0 or newer; a message over 5000 characters needs API 1.43.0 or newer.
783
791
 
784
792
  ## Next Steps
785
793
 
@@ -199,21 +199,21 @@ type CustomMaterial = Phx['AdminModel']['CustomMaterial']
199
199
 
200
200
  ### Root Level Files
201
201
 
202
- | File | Auto-Generated? | Notes |
203
- | ------------------------------------------------------ | --------------- | ------------------------------------- |
204
- | `package.json` | No | Yours; see Dependencies |
205
- | `tsconfig.json` | Once | Yours; see below |
206
- | `eslint.config.js` | Once | Yours; see below |
207
- | `prettier.config.js` | Once | Yours; see below |
208
- | `.prettierignore` | No | Optional and your own; see below |
209
- | `pnpm-workspace.yaml` | No | Only with pnpm; see Package manager |
210
- | `.gitignore` | Partially | Only in a git repository; see Markers |
211
- | `.env` | No | Git-ignored, local config |
212
- | `.env.example` | No | Template for env vars |
213
- | `.env.generated` | Yes | Optional, git-ignored; see below |
214
- | `README.md` | No | Project documentation |
215
- | `AGENTS.md` | No | Your own coding-agent instructions |
216
- | `.claude/skills/phoenix*/`, `.agents/skills/phoenix*/` | Yes | Phoenix agent skills |
202
+ | File | Auto-Generated? | Notes |
203
+ | ---------------------------------------------------- | --------------- | ------------------------------------- |
204
+ | `package.json` | No | Yours; see Dependencies |
205
+ | `tsconfig.json` | Once | Yours; see below |
206
+ | `eslint.config.js` | Once | Yours; see below |
207
+ | `prettier.config.js` | Once | Yours; see below |
208
+ | `.prettierignore` | No | Optional and your own; see below |
209
+ | `pnpm-workspace.yaml` | No | Only with pnpm; see Package manager |
210
+ | `.gitignore` | Partially | Only in a git repository; see Markers |
211
+ | `.env` | No | Git-ignored, local config |
212
+ | `.env.example` | No | Template for env vars |
213
+ | `.env.generated` | Yes | Optional, git-ignored; see below |
214
+ | `README.md` | No | Project documentation |
215
+ | `AGENTS.md` | No | Your own coding-agent instructions |
216
+ | `.claude/skills/phoenix/`, `.agents/skills/phoenix/` | Yes | Phoenix agent skills |
217
217
 
218
218
  Sync does not write `README.md`, `AGENTS.md`, `.env.example`, `.env.ci` or the `README.md` files in `assets/public/` and `assets/private/`. Earlier SDK versions wrote them once; a project that has them keeps them, and they are yours to edit.
219
219
 
@@ -242,9 +242,13 @@ With the application-spec pipeline, the Lingui configuration is `.phoenix/.lingu
242
242
 
243
243
  ### Agent Guidance
244
244
 
245
- Coding agents (Claude Code, Codex, Cursor) read the Phoenix agent skills. An `AGENTS.md` is yours to add for your own instructions; sync does not write one. The `phoenix`, `phoenix-cli`, `phoenix-feedback` and `phoenix-review` skills carry the SDK conventions, CLI tooling and review checklist, and the `phoenix-create-configurator` skill walks the agent from your product documents and assets to a reviewed configurator in five stages. The `phoenix-variants` skill has the agent show you several options over the dev preview when a decision is a matter of taste, and build the one you pick. Phoenix keeps them in step with the installed SDK, including the reference files inside a skill folder. During the internal beta, a project created from a canary build of the SDK gets one more section in the `phoenix` skill: once per conversation the agent checks for a newer canary and, after telling you, updates the project's SDK, runs `phoenix deps`, installs, then runs `phoenix sync`. Other skill folders are never touched.
245
+ Coding agents (Claude Code, Codex, Cursor) enter through the single `phoenix` skill under `.claude/skills/phoenix/` or `.agents/skills/phoenix/`. Its `SKILL.md` routes to the existing project, CLI, configurator workflow, code review, variants and feedback guides in `references/`. Agents read the guides for their task rather than the whole tree. The configurator workflow retains its five stages and supporting discovery, UX, asset-conversion and appearance-review references.
246
246
 
247
- Projects created by earlier SDK versions had this guidance in `CLAUDE.md`, `.cursor/rules/phoenix.mdc` and `commands/review.md`. Sync removes the SDK block from the first two, keeps your own text around it, deletes a file that held nothing else, and deletes the review commands; use the `phoenix-review` skill instead of `/review`.
247
+ Phoenix keeps this guidance in step with the installed SDK. During the internal beta, a project created from a canary build gets the canary-update instructions in the linked project guide: once per conversation the agent checks for a newer canary and, after telling you, updates the SDK, runs `phoenix deps`, installs, then runs `phoenix sync`.
248
+
249
+ An `AGENTS.md` is yours to add; sync does not write one. Sync removes retired `phoenix-*` skill folders but leaves unrelated skills alone. If your own instructions name an earlier separate skill such as `phoenix-review`, point them at `phoenix` and the same task instead.
250
+
251
+ Projects created by earlier SDK versions had this guidance in `CLAUDE.md`, `.cursor/rules/phoenix.mdc` and `commands/review.md`. Sync removes the SDK block from the first two, keeps your own text around it, deletes a file that held nothing else, and deletes the review commands; ask `phoenix` to review instead of using `/review`.
248
252
 
249
253
  ### Markers for Partial Auto-Generation
250
254
 
@@ -65,7 +65,7 @@ Every user who signs the CLI in gets their own development environment, so a tea
65
65
 
66
66
  Coding agents follow the generated `phoenix` skill: before their first change they start `phoenix dev` and open the preview in your browser, and after `phoenix deploy` starts a build they open its page in Studio.
67
67
 
68
- To build the configurator with your coding agent, give it your product documents, prices, pictures, drawings or models and ask it to create the configurator. The generated `phoenix-create-configurator` skill guides it through five stages: a representative first slice you can try early, a plan of the whole configuration experience, options and features added in small increments, finalization, and a complete review. It studies the SDK documentation before building, keeps a `configurator-spec.md` in the project as the record of what is decided and what remains open, and asks you only for decisions it cannot derive from your materials.
68
+ To build the configurator with your coding agent, give it your product documents, prices, pictures, drawings or models and ask it to create the configurator. The generated `phoenix` skill’s Configurator workflow guide takes it through five stages: a representative first slice you can try early, a plan of the whole configuration experience, options and features added in small increments, finalization, and a complete review. It studies the SDK documentation before building, keeps a `configurator-spec.md` in the project as the record of what is decided and what remains open, and asks you only for decisions it cannot derive from your materials.
69
69
 
70
70
  ## 4. Deploy
71
71
 
@@ -99,7 +99,7 @@ Tell us what is broken, confusing or missing, from wherever you are:
99
99
  - In the terminal, `pnpm phoenix feedback "<message>" --type bug|idea|other` sends it with your sign-in, and `--file <path>` sends a text file instead of the argument. See the [CLI reference](?path=/docs/phoenix-phoenix-cli--docs).
100
100
  - On a build's page in Studio, **Download** → **Sources** downloads the archive `phoenix deploy` uploaded for that build, so a report only needs to name the build.
101
101
 
102
- Coding agents working in your project use the same command, guided by the generated `phoenix-feedback` skill. They never send on their own: an agent shows you a draft, right away when the problem is crucial and otherwise at the end of the task, and sends it only after you approve it. A report names the build with its Studio page link and the live configurator, says what the agent was doing, what happened and what it expected, quotes the exact error and ends with a condensed conversation log. Agents are told never to include secrets, credentials or personal data; parts of your project and your own words go in only through a draft you approved. When you are done with the configurator, the agent also asks you, once per conversation, to evaluate the run: minutes spent, then scores from 0 to 10 for the agent's guidance, functionality, UX, 3D quality, UI visuals and the overall result, and your comments. It asks one question at a time, never picks a score for you, tells you the answers go to the Phoenix team, shows the message and sends it only after you approve it.
102
+ Coding agents working in your project use the same command, guided by the generated `phoenix` skill’s Feedback guide. They never send on their own: an agent shows you a draft, right away when the problem is crucial and otherwise at the end of the task, and sends it only after you approve it. A report names the build with its Studio page link and the live configurator, says what the agent was doing, what happened and what it expected, quotes the exact error and ends with a condensed conversation log. Agents are told never to include secrets, credentials or personal data; parts of your project and your own words go in only through a draft you approved. When you are done with the configurator, the agent also asks you, once per conversation, to evaluate the run: minutes spent, then scores from 0 to 10 for the agent's guidance, functionality, UX, 3D quality, UI visuals and the overall result, and your comments. It asks one question at a time, never picks a score for you, tells you the answers go to the Phoenix team, shows the message and sends it only after you approve it.
103
103
 
104
104
  ## Known limits
105
105
 
@@ -36,7 +36,11 @@ Phoenix provides domain-aware builders through the global `phx.webhooks` namespa
36
36
 
37
37
  Your webhook is the default export of `src/webhooks/index.ts`. With the application-spec pipeline (see [Current and legacy project tooling](?path=/docs/phoenix-phoenix-cli--docs#current-and-legacy-project-tooling)), the file is yours to create; sync does not write it. A project without it gets a built-in webhook, so it builds and deploys with no webhook code. The built-in webhook has one handler, an anonymous auth handler, which the platform calls only when `spec/application.yaml` sets `useAuthHook`. The legacy pipeline writes the file with that webhook on the first sync.
38
38
 
39
- Create `src/webhooks/index.ts` when the project needs its own handlers: finalization, backend data, or an auth handler that does more than start an anonymous session. A project that sets `useAuthHook` or `useClerk` needs its own auth handler, because the built-in one answers only anonymous requests. The smallest file is the built-in webhook itself:
39
+ Create `src/webhooks/index.ts` when the project needs its own handlers: finalization, backend data, or an auth handler that does more than start an anonymous session.
40
+
41
+ If `useAuthHook` or `useClerk` is enabled without this file, sync, build, clean and dev report `AUTH_WITHOUT_PROJECT_WEBHOOK` and generation continues with the anonymous-only built-in webhook. This warning also applies when `useAuthHook: false` is explicit alongside `useClerk: true`. Add a default-exported webhook with the required Clerk or custom auth handlers, or disable the settings if they are unintended.
42
+
43
+ The smallest file is the built-in webhook itself:
40
44
 
41
45
  ```ts
42
46
  // src/webhooks/index.ts
@@ -1 +1 @@
1
- import{GraphQLBoolean as e,GraphQLEnumType as t,GraphQLID as n,GraphQLInputObjectType as i,GraphQLInterfaceType as s,GraphQLList as r,GraphQLNonNull as o,GraphQLObjectType as a,GraphQLSchema as p,GraphQLString as c,printSchema as d,validateSchema as l}from"graphql";import{GraphQLJSON as h,createAssistantValueType as w}from"./assistant-value-type.js";import{buildAssistantLenses as y}from"./build-assistant-lenses.js";import{createAssistantExecutionSchema as u}from"./create-assistant-execution-schema.js";let m="AnyDomainEntity",f="RootEntity",g=[["navigationState","Active navigation state.","navigationStates"],["viewState","Active view state.","viewStates"],["toolState","Active tool state.","toolStates"]],b={section:"childRecipes",mutation:"addChild"},v={section:"replaceRecipes",mutation:"replaceEntity"},j=(e,t)=>Object.entries(e).find(([e])=>e===t)?.[1],$=(e,t)=>{if(!t||0===t.length)return e;let n=`One of: ${t.map(e=>`"${e}"`).join(", ")}.`;return e?/[.!?]$/.test(e)?`${e} ${n}`:`${e}. ${n}`:n},O=({target:e,cardinality:t},n)=>{let i=n(e);switch(t){case"1-1":return new o(i);case"0-1":return i;case"0-N":case"1-N":return new o(new r(new o(i)))}},E=(e,t,i)=>{let s={id:{type:new o(n),description:i?`Unique id of the entity, assigned by the configurator, e.g. "${i}-1".`:void 0},type:{type:new o(c),description:i?`Entity type name, e.g. "${i}".`:void 0}};for(let n of e)s[n.name]={type:"edge"===n.type.kind?O(n.type,t):w(n.type.type),description:n.description};return s},R=e=>Object.fromEntries(e.map(({name:e,source:t,localized:n})=>[e,n?{source:t,localized:!0}:{source:t}])),k={sameForAllInstances:e=>`Same recipe groups and recipes for every ${e}.`,constantDuringSession:()=>"Fixed for the session."},T={sameForAllInstances:e=>`Same for every ${e}.`,constantDuringSession:()=>"Fixed for the session."},I=(e,t,n)=>e.map(e=>` ${t[e](n)}`).join(""),N=({section:e},{groups:t,hints:n},{entityType:i,purpose:s,recipeGroup:a})=>{let p=t.filter(({dryRuns:e})=>!e).map(({groupId:e})=>e);return 0===t.length?{}:{[e]:{type:new o(new r(new o(a))),description:`The recipe groups for ${s} this ${i}: ${t.map(({groupId:e})=>e).join(", ")}. A group is listed only while the configurator offers it on the entity.${p.length>0?` Recipes of these groups cannot be dry-run: ${p.join(", ")}.`:""}${I(n,k,i)}`}}},A=({entityType:e,childRecipes:n,replaceRecipes:i,optionsEdges:s,screenshots:p},{recipeGroup:c,fieldOption:d,screenshot:l})=>new a({name:`${e}Meta`,fields:Object.assign(N(b,n,{entityType:e,purpose:"adding children to",recipeGroup:c}),N(v,i,{entityType:e,purpose:"replacing",recipeGroup:c}),...s.map(({options:{edgeName:t,field:n,validate:i,dryRuns:s},hints:a})=>({[t]:{type:new r(new o(d)),description:`Values "${n}" accepts on this ${e}. Null when there is no choice to make.${i?"":" Not checked on write."}${s?"":" Cannot be dry-run."}${I(a,T,e)}`}})),0===p.length?{}:{screenshot:{type:l,args:{view:{type:new o(new t({name:`${e}ScreenshotView`,values:Object.fromEntries(p.map(({view:e,description:t})=>[e,{description:t}]))}))}},description:"Renders this entity from a predefined view. The picture follows the tool result as an image labelled with `image`. Each one costs tokens and blocks the user's configurator while it renders: ask only when seeing the design answers something the data cannot. The images of one call share a size budget; past it, further screenshots come back null. Null as well when the view does not apply to this entity right now."}})}),S=e=>Object.entries(e).map(([e,t])=>`${e} (${t.map(({groupId:e})=>e).join(", ")})`);export const buildAssistantManifest=t=>{var O,k,T,I,N;let M,D,F,W,{entityTypes:x,aliases:C,exampleType:q,fields:G,states:U,deletableTypes:K,writableTypes:V,actionFields:z,optionsEdges:P}=t,L=Object.fromEntries(Object.entries(t.childRecipes).map(([e,t])=>[e,t.groups])),Q=Object.fromEntries(Object.entries(t.replaceRecipes).map(([e,t])=>[e,t.groups])),B=new Map,H=new Map,J=new Map,X=e=>{let[t,...n]=Object.entries(C).find(([t])=>t===e)?.[1]??[];return(t&&0===n.length?H.get(t):void 0)??J.get(e)??Z},Y=e=>H.get(e)??X(e),Z=new s({name:m,description:"Every entity of the configuration.",fields:()=>E(G[m]??[],Y,q)});for(let e of(J.set(m,Z),Object.keys(C)))e!==m&&J.set(e,new s({name:e,interfaces:[Z],fields:()=>E(G[e]??[],Y)}));let _={};for(let e of x){let t=[m,...Object.entries(C).filter(([t,n])=>t!==m&&n.includes(e)).map(([e])=>e)],n=G[e]??[];_[e]=R(n),H.set(e,new a({name:e,interfaces:t.flatMap(e=>J.get(e)??[]),fields:()=>{let t=B.get(e);return{...E(n,Y),...t?{meta:{type:new o(t),description:"What can be done to this entity right now."}}:{}}}}))}let ee=(O=()=>X(J.has(f)?f:m),k=()=>X(m),M=new a({name:"DryRunError",description:"One reason a change cannot be made right now.",fields:{type:{type:new o(c),description:"Kind of the problem."},entityId:{type:n,description:"The entity the problem is on, when it is on one."},message:{type:c,description:"The problem as the user would be told it."}}}),new a({name:"DryRun",description:"What using a recipe or writing an option would do, tried out on the configuration the query reads. Nothing is changed.",fields:()=>({ok:{type:new o(e),description:"Whether the change would succeed on the configuration the query reads."},root:{type:O(),description:"The root entity of the configuration as it would be after the change; null when ok is false. Everything selected under it reads that configuration."},changedEntities:{type:new o(new r(new o(k()))),description:"Entities the change would add or modify; empty when ok is false. One whose id is not in the configuration would be added, and its real id may differ. An entity written to is listed even when its values stay the same."},deletedEntityIds:{type:new o(new r(new o(n))),description:"The ids of the entities the change would remove; empty when ok is false."},sideEffects:{type:new o(new r(new o(c))),description:"The major or unexpected things the configurator would change along with it; empty when ok is false."},errors:{type:new o(new r(new o(M))),description:"Why the change cannot be made; empty when ok is true."}})})),et={recipeGroup:(T=[...Object.keys(L).length>0?[b.mutation]:[],...Object.keys(Q).length>0?[v.mutation]:[]],D=T.join(" or "),F=new a({name:"Recipe",description:`${T.includes("addChild")?"One thing that can be added to the entity (under childRecipes)":"One thing the entity can be replaced with (under replaceRecipes)"}${T.length>1?" or that it can be replaced with (under replaceRecipes)":""}.`,fields:{key:{type:new o(c),description:`Pass to ${D} as recipeKey. Valid only for the listing it came from.`},name:{type:new o(c),description:"Name shown to the user."},description:{type:c},imageUrl:{type:c,description:"Thumbnail shown to the user."},dryRun:{type:ee,description:"What using this recipe would do. Null when it cannot be tried."}}}),new a({name:"RecipeGroup",description:"A group of recipes for this entity.",fields:{groupId:{type:new o(c),description:`Pass to ${D} as recipeGroupId.`},name:{type:new o(c),description:"Name shown to the user."},description:{type:c},recipes:{type:new o(new r(new o(F)))}}})),fieldOption:new a({name:"FieldOption",description:"One value a field accepts. Writing a value outside the list is rejected unless the field description says it is not checked.",fields:{value:{type:new o(h),description:"The value to write to the field."},name:{type:c,description:"Name shown to the user."},description:{type:c},imageUrl:{type:c,description:"Image shown to the user."},color:{type:c,description:'Color shown to the user, as "#rrggbb".'},dryRun:{type:ee,description:"What writing this value would do. Null when it cannot be tried."}}}),screenshot:new a({name:"Screenshot",description:"A screenshot of an entity. The picture itself follows the tool result.",fields:{image:{type:new o(n),description:"Label of the image that follows the tool result."}}})};for(let e of x){let n=j(t.childRecipes,e)??{groups:[],hints:[]},i=j(t.replaceRecipes,e)??{groups:[],hints:[]},s=j(P,e)??[],r=j(t.screenshots,e)??[];(n.groups.length>0||i.groups.length>0||s.length>0||r.length>0)&&B.set(e,A({entityType:e,childRecipes:n,replaceRecipes:i,optionsEdges:s.map(t=>({...t,hints:(C[f]??[]).includes(e)?t.hints.filter(e=>"sameForAllInstances"!==e):t.hints})),screenshots:r},et))}let en=new p({query:(I=X(J.has(f)?f:m),N=X(m),W=(e=>{let t={};for(let[n,i,s]of g){let r=e[s];r.length>1&&(t[n]={type:new o(c),description:$(i,r)})}if(0!==Object.keys(t).length)return new a({name:"Navigation",description:"The active UI states of the configurator.",fields:t})})(U),new a({name:"Query",fields:{root:{type:new o(I),description:"The root entity of the configuration."},entities:{type:new o(new r(N)),args:{ids:{type:new r(new o(n))}},description:"Every entity, or the given ids in order with null for an unknown id."},selectedEntity:{type:new o(N),description:"The selected entity, or the root entity when nothing is selected."},...W?{navigation:{type:new o(W)}}:{}}})),mutation:(({deletableTypes:e,writableTypes:t,childRecipes:s,replaceRecipes:p,actionFields:d,objectTypes:l,anyDomainEntity:h,targetOutputType:y})=>{let u,m;if(0!==e.length||0!==t.length||0!==Object.keys(s).length||0!==Object.keys(p).length||0!==d.length)return new a({name:"Mutation",fields:Object.assign(0===e.length?{}:{deleteEntity:{type:new o(new r(new o(n))),args:{id:{type:new o(n)}},description:`Deletes the entity and everything composed into it. Deletable types: ${e.join(", ")}. Returns the ids removed, the given one first.`}},0===(u=S(s)).length?{}:{addChild:{type:new o(new r(new o(h))),args:{parentId:{type:new o(n)},recipeGroupId:{type:new o(c)},recipeKey:{type:new o(c)}},description:`Adds a child to the entity parentId from a recipe listed under its meta { childRecipes }. Types with child recipes: ${u.join("; ")}. Returns the entities added, the recipe's own first; empty when they were removed again.`}},0===(m=S(p)).length?{}:{replaceEntity:{type:new o(new r(new o(h))),args:{entityId:{type:new o(n)},recipeGroupId:{type:new o(c)},recipeKey:{type:new o(c)}},description:`Replaces the entity entityId from a recipe listed under its meta { replaceRecipes }. Types with replace recipes: ${m.join("; ")}. The replacement is a new entity: it has a new id and the field values of its recipe, and entityId no longer exists. Returns the entities added, the replacement first; empty when they were removed again.`}},...t.flatMap(e=>{let t=l.get(e.entityType);return t?[(({entityType:e,fields:t},s)=>{let r=Object.fromEntries(t.map(({name:e,type:t,description:n})=>[e,{type:w(t),description:n}]));return{[`modify${e}`]:{type:s,args:{id:{type:new o(n)},delta:{type:new o(new i({name:`${e}Delta`,fields:r}))}},description:`Sets the fields named in delta on one ${e}. Returns the entity after the change, or null when the change removed it.`}}})(e,t)]:[]}),Object.fromEntries(d.map(e=>{var t;return[e.fieldName,{type:new o(new a({name:(t=e.fieldName,`${t.charAt(0).toUpperCase()+t.slice(1)}Result`),description:`What ${e.fieldName} returns, read from the configuration after the change.`,fields:()=>Object.fromEntries(Object.entries(e.result).map(([e,t])=>[e,{type:"edge"===t.kind?(({target:e,list:t,nullable:n},i)=>{let s=t?new r(new o(i(e))):i(e);return n?s:new o(s)})(t,y):w(t.type),description:t.description}]))})),args:Object.fromEntries(Object.entries(e.args).map(([e,t])=>[e,{type:w(t.type),description:t.description}])),description:e.description}]})))})})({deletableTypes:K,writableTypes:V,childRecipes:L,replaceRecipes:Q,actionFields:z,objectTypes:H,anyDomainEntity:X(m),targetOutputType:Y}),types:[...J.values(),...H.values()]}),ei=l(en);if(ei.length>0)throw Error(ei.map(e=>e.message).join(" "));let es=d(en);return{sdl:es,navigationFields:g.filter(([,,e])=>U[e].length>1).map(([e])=>e),fields:_,deletableTypes:K,writableFields:Object.fromEntries(V.map(({entityType:e,fields:t})=>[e,Object.fromEntries(t.map(({name:e,property:t})=>[e,t]))])),childRecipes:L,replaceRecipes:Q,fieldOptions:Object.fromEntries(x.flatMap(e=>{let t=j(P,e);return t?[[e,t.map(({options:e})=>e)]]:[]})),screenshots:Object.fromEntries(x.flatMap(e=>{let n=j(t.screenshots,e);return n?[[e,n]]:[]})),actions:Object.fromEntries(z.map(({fieldName:e,actionName:t})=>[e,t])),lenses:y(t,u(es)),formElements:t.formElements}};
1
+ import{GraphQLBoolean as e,GraphQLEnumType as t,GraphQLID as n,GraphQLInputObjectType as i,GraphQLInterfaceType as s,GraphQLList as r,GraphQLNonNull as o,GraphQLObjectType as a,GraphQLSchema as p,GraphQLString as d,printSchema as c,validateSchema as l}from"graphql";import{GraphQLJSON as h,createAssistantValueType as w}from"./assistant-value-type.js";import{buildAssistantLenses as y}from"./build-assistant-lenses.js";import{createAssistantExecutionSchema as u}from"./create-assistant-execution-schema.js";let m="AnyDomainEntity",f="RootEntity",g=[["navigationState","Active navigation state.","navigationStates"],["viewState","Active view state.","viewStates"],["toolState","Active tool state.","toolStates"]],b={section:"childRecipes",mutation:"addChild"},v={section:"replaceRecipes",mutation:"replaceEntity"},j=(e,t)=>Object.entries(e).find(([e])=>e===t)?.[1],$=(e,t)=>{if(!t||0===t.length)return e;let n=`One of: ${t.map(e=>`"${e}"`).join(", ")}.`;return e?/[.!?]$/.test(e)?`${e} ${n}`:`${e}. ${n}`:n},O=({target:e,cardinality:t},n)=>{let i=n(e);switch(t){case"1-1":return new o(i);case"0-1":return i;case"0-N":case"1-N":return new o(new r(new o(i)))}},E=(e,t,i)=>{let s={id:{type:new o(n),description:i?`Unique id of the entity, assigned by the configurator, e.g. "${i}-1".`:void 0},type:{type:new o(d),description:i?`Entity type name, e.g. "${i}".`:void 0}};for(let n of e)s[n.name]={type:"edge"===n.type.kind?O(n.type,t):w(n.type.type),description:n.description};return s},R=e=>Object.fromEntries(e.map(({name:e,source:t,localized:n})=>[e,n?{source:t,localized:!0}:{source:t}])),k={sameForAllInstances:e=>`Same recipe groups and recipes for every ${e}.`,constantDuringSession:()=>"Fixed for the session."},T={sameForAllInstances:e=>`Same for every ${e}.`,constantDuringSession:()=>"Fixed for the session."},I=(e,t,n)=>e.map(e=>` ${t[e](n)}`).join(""),N=({section:e},{groups:t,hints:n},{entityType:i,purpose:s,recipeGroup:a})=>{let p=t.filter(({dryRuns:e})=>!e).map(({groupId:e})=>e);return 0===t.length?{}:{[e]:{type:new o(new r(new o(a))),description:`The recipe groups for ${s} this ${i}: ${t.map(({groupId:e})=>e).join(", ")}. A group is listed only while the configurator offers it on the entity.${p.length>0?` Recipes of these groups cannot be dry-run: ${p.join(", ")}.`:""}${I(n,k,i)}`}}},A=({entityType:e,childRecipes:n,replaceRecipes:i,optionsEdges:s,screenshots:p},{recipeGroup:d,fieldOption:c,screenshot:l})=>new a({name:`${e}Meta`,fields:Object.assign(N(b,n,{entityType:e,purpose:"adding children to",recipeGroup:d}),N(v,i,{entityType:e,purpose:"replacing",recipeGroup:d}),...s.map(({options:{edgeName:t,field:n,validate:i,dryRuns:s},hints:a})=>({[t]:{type:new r(new o(c)),description:`Values "${n}" accepts on this ${e}. Null when there is no choice to make.${i?"":" Not checked on write."}${s?"":" Cannot be dry-run."}${I(a,T,e)}`}})),0===p.length?{}:{screenshot:{type:l,args:{view:{type:new o(new t({name:`${e}ScreenshotView`,values:Object.fromEntries(p.map(({view:e,description:t})=>[e,{description:t}]))}))}},description:"Renders this entity from a predefined view. The picture follows the tool result as an image labelled with `image`. Null when the view does not apply to this entity right now."}})}),S=e=>Object.entries(e).map(([e,t])=>`${e} (${t.map(({groupId:e})=>e).join(", ")})`);export const buildAssistantManifest=t=>{var O,k,T,I,N;let M,D,F,W,{entityTypes:x,aliases:C,exampleType:q,fields:G,states:U,deletableTypes:K,writableTypes:V,actionFields:P,optionsEdges:z}=t,L=Object.fromEntries(Object.entries(t.childRecipes).map(([e,t])=>[e,t.groups])),Q=Object.fromEntries(Object.entries(t.replaceRecipes).map(([e,t])=>[e,t.groups])),B=new Map,H=new Map,J=new Map,X=e=>{let[t,...n]=Object.entries(C).find(([t])=>t===e)?.[1]??[];return(t&&0===n.length?H.get(t):void 0)??J.get(e)??Z},Y=e=>H.get(e)??X(e),Z=new s({name:m,description:"Every entity of the configuration.",fields:()=>E(G[m]??[],Y,q)});for(let e of(J.set(m,Z),Object.keys(C)))e!==m&&J.set(e,new s({name:e,interfaces:[Z],fields:()=>E(G[e]??[],Y)}));let _={};for(let e of x){let t=[m,...Object.entries(C).filter(([t,n])=>t!==m&&n.includes(e)).map(([e])=>e)],n=G[e]??[];_[e]=R(n),H.set(e,new a({name:e,interfaces:t.flatMap(e=>J.get(e)??[]),fields:()=>{let t=B.get(e);return{...E(n,Y),...t?{meta:{type:new o(t),description:"What can be done to this entity right now."}}:{}}}}))}let ee=(O=()=>X(J.has(f)?f:m),k=()=>X(m),M=new a({name:"DryRunError",description:"One reason a change cannot be made right now.",fields:{type:{type:new o(d),description:"Kind of the problem."},entityId:{type:n,description:"The entity the problem is on, when it is on one."},message:{type:d,description:"The problem as the user would be told it."}}}),new a({name:"DryRun",description:"What using a recipe or writing an option would do, tried out on the configuration the query reads. Nothing is changed.",fields:()=>({ok:{type:new o(e),description:"Whether the change would succeed on the configuration the query reads."},root:{type:O(),description:"The root entity of the configuration as it would be after the change; null when ok is false. Everything selected under it reads that configuration."},changedEntities:{type:new o(new r(new o(k()))),description:"Entities the change would add or modify; empty when ok is false. One whose id is not in the configuration would be added, and its real id may differ. An entity written to is listed even when its values stay the same."},deletedEntityIds:{type:new o(new r(new o(n))),description:"The ids of the entities the change would remove; empty when ok is false."},sideEffects:{type:new o(new r(new o(d))),description:"The major or unexpected things the configurator would change along with it; empty when ok is false."},errors:{type:new o(new r(new o(M))),description:"Why the change cannot be made; empty when ok is true."}})})),et={recipeGroup:(T=[...Object.keys(L).length>0?[b.mutation]:[],...Object.keys(Q).length>0?[v.mutation]:[]],D=T.join(" or "),F=new a({name:"Recipe",description:`${T.includes("addChild")?"One thing that can be added to the entity (under childRecipes)":"One thing the entity can be replaced with (under replaceRecipes)"}${T.length>1?" or that it can be replaced with (under replaceRecipes)":""}.`,fields:{key:{type:new o(d),description:`Pass to ${D} as recipeKey. Valid only for the listing it came from.`},name:{type:new o(d),description:"Name shown to the user."},description:{type:d},imageUrl:{type:d,description:"Thumbnail shown to the user."},dryRun:{type:ee,description:"What using this recipe would do. Null when it cannot be tried."}}}),new a({name:"RecipeGroup",description:"A group of recipes for this entity.",fields:{groupId:{type:new o(d),description:`Pass to ${D} as recipeGroupId.`},name:{type:new o(d),description:"Name shown to the user."},description:{type:d},recipes:{type:new o(new r(new o(F)))}}})),fieldOption:new a({name:"FieldOption",description:"One value a field accepts. Writing a value outside the list is rejected unless the field description says it is not checked.",fields:{value:{type:new o(h),description:"The value to write to the field."},name:{type:d,description:"Name shown to the user."},description:{type:d},imageUrl:{type:d,description:"Image shown to the user."},color:{type:d,description:'Color shown to the user, as "#rrggbb".'},dryRun:{type:ee,description:"What writing this value would do. Null when it cannot be tried."}}}),screenshot:new a({name:"Screenshot",description:"A screenshot of an entity. The picture itself follows the tool result.",fields:{image:{type:new o(n),description:"Label of the image that follows the tool result."}}})};for(let e of x){let n=j(t.childRecipes,e)??{groups:[],hints:[]},i=j(t.replaceRecipes,e)??{groups:[],hints:[]},s=j(z,e)??[],r=j(t.screenshots,e)??[];(n.groups.length>0||i.groups.length>0||s.length>0||r.length>0)&&B.set(e,A({entityType:e,childRecipes:n,replaceRecipes:i,optionsEdges:s.map(t=>({...t,hints:(C[f]??[]).includes(e)?t.hints.filter(e=>"sameForAllInstances"!==e):t.hints})),screenshots:r},et))}let en=new p({query:(I=X(J.has(f)?f:m),N=X(m),W=(e=>{let t={};for(let[n,i,s]of g){let r=e[s];r.length>1&&(t[n]={type:new o(d),description:$(i,r)})}if(0!==Object.keys(t).length)return new a({name:"Navigation",description:"The active UI states of the configurator.",fields:t})})(U),new a({name:"Query",fields:{root:{type:new o(I),description:"The root entity of the configuration."},entities:{type:new o(new r(N)),args:{ids:{type:new r(new o(n))}},description:"Every entity, or the given ids in order with null for an unknown id."},selectedEntity:{type:new o(N),description:"The selected entity, or the root entity when nothing is selected."},...W?{navigation:{type:new o(W)}}:{}}})),mutation:(({deletableTypes:e,writableTypes:t,childRecipes:s,replaceRecipes:p,actionFields:c,objectTypes:l,anyDomainEntity:h,targetOutputType:y})=>{let u,m;if(0!==e.length||0!==t.length||0!==Object.keys(s).length||0!==Object.keys(p).length||0!==c.length)return new a({name:"Mutation",fields:Object.assign(0===e.length?{}:{deleteEntity:{type:new o(new r(new o(n))),args:{id:{type:new o(n)}},description:`Deletes the entity and everything composed into it. Deletable types: ${e.join(", ")}. Returns the ids removed, the given one first.`}},0===(u=S(s)).length?{}:{addChild:{type:new o(new r(new o(h))),args:{parentId:{type:new o(n)},recipeGroupId:{type:new o(d)},recipeKey:{type:new o(d)}},description:`Adds a child to the entity parentId from a recipe listed under its meta { childRecipes }. Types with child recipes: ${u.join("; ")}. Returns the entities added, the recipe's own first; empty when they were removed again.`}},0===(m=S(p)).length?{}:{replaceEntity:{type:new o(new r(new o(h))),args:{entityId:{type:new o(n)},recipeGroupId:{type:new o(d)},recipeKey:{type:new o(d)}},description:`Replaces the entity entityId from a recipe listed under its meta { replaceRecipes }. Types with replace recipes: ${m.join("; ")}. The replacement is a new entity: it has a new id and the field values of its recipe, and entityId no longer exists. Returns the entities added, the replacement first; empty when they were removed again.`}},...t.flatMap(e=>{let t=l.get(e.entityType);return t?[(({entityType:e,fields:t},s)=>{let r=Object.fromEntries(t.map(({name:e,type:t,description:n})=>[e,{type:w(t),description:n}]));return{[`modify${e}`]:{type:s,args:{id:{type:new o(n)},delta:{type:new o(new i({name:`${e}Delta`,fields:r}))}},description:`Sets the fields named in delta on one ${e}. Returns the entity after the change, or null when the change removed it.`}}})(e,t)]:[]}),Object.fromEntries(c.map(e=>{var t;return[e.fieldName,{type:new o(new a({name:(t=e.fieldName,`${t.charAt(0).toUpperCase()+t.slice(1)}Result`),description:`What ${e.fieldName} returns, read from the configuration after the change.`,fields:()=>Object.fromEntries(Object.entries(e.result).map(([e,t])=>[e,{type:"edge"===t.kind?(({target:e,list:t,nullable:n},i)=>{let s=t?new r(new o(i(e))):i(e);return n?s:new o(s)})(t,y):w(t.type),description:t.description}]))})),args:Object.fromEntries(Object.entries(e.args).map(([e,t])=>[e,{type:w(t.type),description:t.description}])),description:e.description}]})))})})({deletableTypes:K,writableTypes:V,childRecipes:L,replaceRecipes:Q,actionFields:P,objectTypes:H,anyDomainEntity:X(m),targetOutputType:Y}),types:[...J.values(),...H.values()]}),ei=l(en);if(ei.length>0)throw Error(ei.map(e=>e.message).join(" "));let es=c(en);return{sdl:es,navigationFields:g.filter(([,,e])=>U[e].length>1).map(([e])=>e),fields:_,deletableTypes:K,writableFields:Object.fromEntries(V.map(({entityType:e,fields:t})=>[e,Object.fromEntries(t.map(({name:e,property:t})=>[e,t]))])),childRecipes:L,replaceRecipes:Q,fieldOptions:Object.fromEntries(x.flatMap(e=>{let t=j(z,e);return t?[[e,t.map(({options:e})=>e)]]:[]})),screenshots:Object.fromEntries(x.flatMap(e=>{let n=j(t.screenshots,e);return n?[[e,n]]:[]})),actions:Object.fromEntries(P.map(({fieldName:e,actionName:t})=>[e,t])),lenses:y(t,u(es)),formElements:t.formElements}};
@@ -1 +1 @@
1
- import{ASSISTANT_BUILT_IN_FORM_ELEMENTS as e}from"./form/index.js";let t=[["Containers",["group","choice","choices"]],["Options",["option"]],["Inputs",["input"]]];export const formElementSignature=({name:e,role:t,attributes:a})=>{let n=["label",...Object.entries(a).map(([e,t])=>((e,{type:t,enum:a})=>a?`${e}="${a.join("|")}"`:"string"===t?e:`${e}="<${t}>"`)(e,t))];return"option"===t||"input"===t?`<${e} ${n.join(" ")} />`:`<${e} ${n.join(" ")}>…</${e}>`};export const generateAssistantPrompt=a=>{let n,s=Object.values(a.childRecipes??{}).some(e=>e.length>0),i=Object.values(a.replaceRecipes??{}).some(e=>e.length>0),o=Object.values(a.fieldOptions??{}).some(e=>e.length>0),r=Object.keys(a.actions??{}).length>0,h=(a.deletableTypes?.length??0)>0||Object.values(a.writableFields??{}).some(e=>Object.keys(e).length>0)||s||i||r,l=s||i||o,d=[...s?["`childRecipes` is what can be added to it"]:[],...i?["`replaceRecipes` what it can be replaced with"]:[],...o?["`<field>Options` the values a field accepts"]:[]],c=["`selected`",...(a.navigationFields??[]).map(e=>`\`${e}\``)].join(", ");return[["Tools","- Put related operations in one call and select only the fields you need.",...h?[`- Use a mutation inside \`query\` to answer a what-if about a change ${l?"the lists under `meta` do not show":"before making it"}.`,"- Use `apply` only for what the user asked for."]:["- Nothing in this configurator can be changed through you. Say so when the user asks for a change."],"- When an operation fails, its `errors` say why and what to send instead."],...h?[["After a change","- A change is made only when the `apply` result has `committed: true`. Otherwise `errors` or `message` says what happened; without either, nothing changed. Never say a change was made when it was not.","- `sideEffects` names the major or unexpected things the configurator changed along with your change. Tell the user about them when committed; otherwise they describe what would happen. Smaller adjustments are not listed.","- A mutation returns the state after the change: use the ids and values it returned, not the ones you sent.",...r?["- When one mutation does the whole of what the user asked, use it instead of building the same change from several others."]:[]]]:[],...l?[["What can be done to an entity",`- \`meta\` lists what is possible right now: ${d.join(", ")}. Read the list before such a change and before telling the user what is available.`,`- You can select \`dryRun\` on ${[...s||i?["recipes"]:[],...o?["options"]:[]].join(" or ")} to look ahead and pick one that meets what the user asked for. It is optional before a change.`,"- Read the lists again after a change, unless their descriptions say they stay the same."]]:[],["The `[changes]` block","- Every user message starts with it: a glimpse of what the user did in the configurator since your last turn.",`- \`[changes: none]\`, or only ${c} lines: what you know still holds. Do not query it again.`,"- Other lines are a hint at what to read again: `A` added, `D` deleted, `M` modified with the fields. Text after `#` says what caused the line; `reset: undo/redo` says the user undid or redid changes.","- Read computed fields again when a listed change may affect them.","- `[changes: unavailable]` and `[changes: too many (n)]`: read again before relying on anything you read earlier.",...h?["- Your applied changes are not listed, except those waiting for user approval: they appear once approved."]:[]],["Reading the schema","- Take ids from results.",...(a.deletableTypes?.length??0)>0||i?["- The id of a deleted or replaced entity is gone and may later be given to another entity."]:[],`- A field whose name starts with \`_\` is computed: read it${h?", never write it":""}.`,"- Numbers are in the unit the field description names. Convert when the user uses another unit."],["Links","- You may link an entity of the current configuration when you speak about that very entity: `[the left burner](entity:CookingTop-1)`. It helps the user find it in the configurator.",...l?["- Whenever you name recipes or options you read from `meta`, in a list, a table or a sentence, link each one: `[Single gas burner](recipe:KitchenRange-1/top/gas_single)` or `[Oak](option:Cabinet-2/finishOptions/oak)`. The link shows the user its picture or color and its description, so never draw previews yourself. The target is the entity id, the recipe group id or the options field you read, and the key or value, each encoded as in a URL."]:[]],["Replies","- Write Markdown only, never HTML: the panel does not render it.","- An image, `![alt](path)`, is shown only when its path is relative.","- To make a warning or a key note stand out, write a GitHub alert: a blockquote whose first line is only `[!WARNING]` or `[!NOTE]`. Use it sparingly."],["Forms","- To ask the user something they answer by picking or typing, end your reply with a form: a fenced code block with the language `form`. It holds only elements, written as lowercase tags with string attributes, without Markdown or text.","- Every element takes `label`, the text the user sees; on a container or an input it also names the answer. An option or an input whose signature lists `value` takes it: an option sends it instead of its label, and an input starts with it.",'- A bare attribute in a signature takes any text, `name="<type>"` takes a value of that type written as text, such as `min="<number>"`, and `name="a|b"` takes one of the words listed.',"- Options can also stand on their own, outside a `choice` or `choices`.","- An element or attribute not listed here, or an element other than an option inside a `choice` or `choices`, makes the whole form fail.","- The answer is the user's next message. Each container or input gives a line `Label: value`, or the bare value without a label; the lines of a labelled container are indented by two spaces under it, a `choices` lists each picked option on its own line, and `—` marks what was left unanswered.",'- Example: `<choice label="Plinth"><reply label="Black" /><reply label="Oak" /></choice>` is answered with `Plinth: Oak`.',"- The chat shows the answer as the user's own message, so a `value` reads as natural words, never as an id, key or code. Give `value` only when the label alone would be unclear as an answer, and map the answer's labels back to ids yourself.",...(n=[...e,...a.formElements??[]],t.flatMap(([e,t])=>{let a=n.filter(({role:e})=>t.includes(e));return 0===a.length?[]:[`- ${e}:`,...a.map(e=>` - \`${formElementSignature(e)}\`: ${(({description:e,required:t=[]})=>0===t.length?e:`${e} Requires ${t.map(e=>`\`${e}\``).join(", ")}.`)(e)}`)]}))],...a.lenses?.declarations?[["Built-in fragments","- These fragments are already defined for every operation. Spread one by name on a field of its type, as in `field { ...FragmentName }`, instead of listing its fields.","- A fragment is a starting selection: add the other fields you need next to the spread, by name from the schema.","- Never define a fragment with one of these names.","",a.lenses.declarations]]:[]].map(e=>e.join("\n")).join("\n\n")};
1
+ import{ASSISTANT_BUILT_IN_FORM_ELEMENTS as e}from"./form/index.js";let t=[["Containers",["group","choice","choices"]],["Options",["option"]],["Inputs",["input"]]];export const formElementSignature=({name:e,role:t,attributes:a})=>{let n=["label",...Object.entries(a).map(([e,t])=>((e,{type:t,enum:a})=>a?`${e}="${a.join("|")}"`:"string"===t?e:`${e}="<${t}>"`)(e,t))];return"option"===t||"input"===t?`<${e} ${n.join(" ")} />`:`<${e} ${n.join(" ")}>…</${e}>`};export const generateAssistantPrompt=a=>{let n,s=Object.values(a.childRecipes??{}).some(e=>e.length>0),o=Object.values(a.replaceRecipes??{}).some(e=>e.length>0),i=Object.values(a.fieldOptions??{}).some(e=>e.length>0),r=Object.keys(a.actions??{}).length>0,h=Object.values(a.screenshots??{}).some(e=>e.length>0),l=(a.deletableTypes?.length??0)>0||Object.values(a.writableFields??{}).some(e=>Object.keys(e).length>0)||s||o||r,d=s||o||i,u=[...s?["`childRecipes` is what can be added to it"]:[],...o?["`replaceRecipes` what it can be replaced with"]:[],...i?["`<field>Options` the values a field accepts"]:[]],c=["`selected`",...(a.navigationFields??[]).map(e=>`\`${e}\``)].join(", ");return[["Tools","- Put related operations in one call and select only the fields you need.",...l?[`- Use a mutation inside \`query\` to answer a what-if about a change ${d?"the lists under `meta` do not show":"before making it"}.`,"- Use `apply` only for what the user asked for."]:["- Nothing in this configurator can be changed through you. Say so when the user asks for a change."],"- When an operation fails, its `errors` say why and what to send instead."],...l?[["After a change","- A change is made only when the `apply` result has `committed: true`. Otherwise `errors` or `message` says what happened; without either, nothing changed. Never say a change was made when it was not.","- `sideEffects` names the major or unexpected things the configurator changed along with your change. Tell the user about them when committed; otherwise they describe what would happen. Smaller adjustments are not listed.","- A mutation returns the state after the change: use the ids and values it returned, not the ones you sent.",...r?["- When one mutation does the whole of what the user asked, use it instead of building the same change from several others."]:[]]]:[],...d?[["What can be done to an entity",`- \`meta\` lists what is possible right now: ${u.join(", ")}. Read the list before such a change and before telling the user what is available.`,`- You can select \`dryRun\` on ${[...s||o?["recipes"]:[],...i?["options"]:[]].join(" or ")} to look ahead and pick one that meets what the user asked for. It is optional before a change.`,"- Read the lists again after a change, unless their descriptions say they stay the same."]]:[],...h?[["Seeing the design","- `meta { screenshot(view: …) { image } }` renders an entity from one of the views its `<Type>ScreenshotView` lists. The picture follows the tool result.","- Take one when the user asks how the design looks, or when you are not sure something is laid out right: what sits where, what overlaps or lines up. Otherwise rely on the data.","- Each one costs tokens and blocks the user's configurator while it renders."]]:[],["The `[changes]` block","- Every user message starts with it: a glimpse of what the user did in the configurator since your last turn.",`- \`[changes: none]\`, or only ${c} lines: what you know still holds. Do not query it again.`,"- Other lines are a hint at what to read again: `A` added, `D` deleted, `M` modified with the fields. Text after `#` says what caused the line; `reset: undo/redo` says the user undid or redid changes.","- Read computed fields again when a listed change may affect them.","- `[changes: unavailable]` and `[changes: too many (n)]`: read again before relying on anything you read earlier.",...l?["- Your applied changes are not listed, except those waiting for user approval: they appear once approved."]:[]],["Reading the schema","- Take ids from results.",...(a.deletableTypes?.length??0)>0||o?["- The id of a deleted or replaced entity is gone and may later be given to another entity."]:[],`- A field whose name starts with \`_\` is computed: read it${l?", never write it":""}.`,"- Numbers are in the unit the field description names. Convert when the user uses another unit."],["Links","- You may link an entity of the current configuration when you speak about that very entity: `[the left burner](entity:CookingTop-1)`. It helps the user find it in the configurator.",...d?["- Whenever you name recipes or options you read from `meta`, in a list, a table or a sentence, link each one: `[Single gas burner](recipe:KitchenRange-1/top/gas_single)` or `[Oak](option:Cabinet-2/finishOptions/oak)`. The link shows the user its picture or color and its description, so never draw previews yourself. The target is the entity id, the recipe group id or the options field you read, and the key or value, each encoded as in a URL."]:[]],["Replies","- Write Markdown only, never HTML: the panel does not render it.","- An image, `![alt](path)`, is shown only when its path is relative.","- To make a warning or a key note stand out, write a GitHub alert: a blockquote whose first line is only `[!WARNING]` or `[!NOTE]`. Use it sparingly."],["Forms","- To ask the user something they answer by picking or typing, end your reply with a form: a fenced code block with the language `form`. It holds only elements, written as lowercase tags with string attributes, without Markdown or text.","- Every element takes `label`, the text the user sees; on a container or an input it also names the answer. An option or an input whose signature lists `value` takes it: an option sends it instead of its label, and an input starts with it.",'- A bare attribute in a signature takes any text, `name="<type>"` takes a value of that type written as text, such as `min="<number>"`, and `name="a|b"` takes one of the words listed.',"- Options can also stand on their own, outside a `choice` or `choices`.","- An element or attribute not listed here, or an element other than an option inside a `choice` or `choices`, makes the whole form fail.","- The answer is the user's next message. Each container or input gives a line `Label: value`, or the bare value without a label; the lines of a labelled container are indented by two spaces under it, a `choices` lists each picked option on its own line, and `—` marks what was left unanswered.",'- Example: `<choice label="Plinth"><reply label="Black" /><reply label="Oak" /></choice>` is answered with `Plinth: Oak`.',"- The chat shows the answer as the user's own message, so a `value` reads as natural words, never as an id, key or code. Give `value` only when the label alone would be unclear as an answer, and map the answer's labels back to ids yourself.",...(n=[...e,...a.formElements??[]],t.flatMap(([e,t])=>{let a=n.filter(({role:e})=>t.includes(e));return 0===a.length?[]:[`- ${e}:`,...a.map(e=>` - \`${formElementSignature(e)}\`: ${(({description:e,required:t=[]})=>0===t.length?e:`${e} Requires ${t.map(e=>`\`${e}\``).join(", ")}.`)(e)}`)]}))],...a.lenses?.declarations?[["Built-in fragments","- These fragments are already defined for every operation. Spread one by name on a field of its type, as in `field { ...FragmentName }`, instead of listing its fields.","- A fragment is a starting selection: add the other fields you need next to the spread, by name from the schema.","- Never define a fragment with one of these names.","",a.lenses.declarations]]:[]].map(e=>e.join("\n")).join("\n\n")};
@@ -16,3 +16,4 @@ export { getAllPhxDefinitions } from './system-definitions.js';
16
16
  export { availableSystemComponents, componentsByName } from './available-system-components.js';
17
17
  export * from './mocks/index.js';
18
18
  export * from './domain-runtime/index.js';
19
+ export * from './spec/index.js';
@@ -1 +1 @@
1
- export*from"./core/index.js";export{DomainBuilder}from"./domain-builder.js";export{memoEval,isMemoizedArray,isMemoizedObject}from"./utils/memo-eval.js";export{cloneEvalCache,getEvalCache,setEvalCache,createEvalCacheToken}from"./utils/eval-cache.js";export{areProfilerLabelsEnabled,setProfilerLabelsEnabled}from"./utils/profiler-label.js";export*from"./dimensions/index.js";export*from"./domain-context/index.js";export*from"./system-components/index.js";export*from"./extensions/index.js";export*from"./sketching/index.js";export*from"./projections/index.js";export*from"./admin-domain/index.js";export*from"./application/index.js";export*from"./scene/index.js";export{getAllPhxDefinitions}from"./system-definitions.js";export{availableSystemComponents,componentsByName}from"./available-system-components.js";export*from"./mocks/index.js";export*from"./domain-runtime/index.js";
1
+ export*from"./core/index.js";export{DomainBuilder}from"./domain-builder.js";export{memoEval,isMemoizedArray,isMemoizedObject}from"./utils/memo-eval.js";export{cloneEvalCache,getEvalCache,setEvalCache,createEvalCacheToken}from"./utils/eval-cache.js";export{areProfilerLabelsEnabled,setProfilerLabelsEnabled}from"./utils/profiler-label.js";export*from"./dimensions/index.js";export*from"./domain-context/index.js";export*from"./system-components/index.js";export*from"./extensions/index.js";export*from"./sketching/index.js";export*from"./projections/index.js";export*from"./admin-domain/index.js";export*from"./application/index.js";export*from"./scene/index.js";export{getAllPhxDefinitions}from"./system-definitions.js";export{availableSystemComponents,componentsByName}from"./available-system-components.js";export*from"./mocks/index.js";export*from"./domain-runtime/index.js";export*from"./spec/index.js";
@@ -0,0 +1 @@
1
+ export * from './application-instance.js';
@@ -0,0 +1 @@
1
+ export*from"./application-instance.js";
@@ -17,7 +17,8 @@ export type FilterCriterion<T> = {
17
17
  readonly code: string;
18
18
  };
19
19
  /**
20
- * Parsed component filter controlling when the component is active. Two
20
+ * Parsed component filter controlling when the component is active, as the application spec
21
+ * declares it under `$filter`; `application-spec` exports it as `ComponentFilter`. Two
21
22
  * mutually-exclusive shapes:
22
23
  *
23
24
  * - `'gradual'` — zero or more per-criterion fields; compiles to one entry per
@@ -29,7 +30,7 @@ export type FilterCriterion<T> = {
29
30
  *
30
31
  * See `components.md §"Simple expression filter"` / `§"Declarative filter"`.
31
32
  */
32
- export type ComponentFilter = {
33
+ export type SpecComponentFilter = {
33
34
  readonly kind: 'gradual';
34
35
  readonly navigationStates?: FilterCriterion<readonly string[]>;
35
36
  readonly viewStates?: FilterCriterion<readonly string[]>;
@@ -0,0 +1,30 @@
1
+ import type { EvalMarker, MsgMarker } from './markers/index.js';
2
+ /**
3
+ * A JSON-serialisable value augmented with Phoenix structural markers:
4
+ * `$msg` (i18n) and `$eval` (self-scoped code).
5
+ *
6
+ * Single shared shape across components, expressions, and defaults — lowered
7
+ * either to TypeScript source via `stringifyEnhancedJsonValue` or to a
8
+ * runtime value via `resolveEnhancedJsonValue`, both of `application-spec`.
9
+ *
10
+ * Which markers are allowed at a given slot is controlled by
11
+ * `EnhancedJsonValueCaps` of `application-spec`, not by the type — all markers parse
12
+ * identically, capability checks kick in at lowering time.
13
+ */
14
+ export type EnhancedJsonValue = string | number | boolean | null | MsgMarker | EvalMarker | EnhancedJsonArray | EnhancedJsonObject;
15
+ /** Array form of {@link EnhancedJsonValue} — extracted as a named interface so `z.lazy` can close the recursion. */
16
+ export interface EnhancedJsonArray extends ReadonlyArray<EnhancedJsonValue> {
17
+ }
18
+ /** Object form of {@link EnhancedJsonValue} — extracted for the same reason as {@link EnhancedJsonArray}. */
19
+ export interface EnhancedJsonObject {
20
+ readonly [key: string]: EnhancedJsonValue;
21
+ }
22
+ /** Runtime `self` accessor — shape-agnostic entity state. */
23
+ export type SelfAccessor = object;
24
+ /**
25
+ * Deterministic string key for a value — sorted object keys, JSON-encoded.
26
+ * Used by the expression runtime's reference store to preserve an evaluator when the
27
+ * underlying value is structurally unchanged (mirrors the content-hash pattern
28
+ * used for components).
29
+ */
30
+ export declare const canonicalEnhancedJsonValueKey: (value: EnhancedJsonValue) => string;
@@ -0,0 +1 @@
1
+ export const canonicalEnhancedJsonValueKey=r=>{let e=r=>{if(null===r||"object"!=typeof r)return r;if(Array.isArray(r))return r.map(e);let t={};for(let n of Object.keys(r).sort())t[n]=e(r[n]);return t};return JSON.stringify(e(r))};
@@ -0,0 +1,7 @@
1
+ export * from './application/index.js';
2
+ export * from './components/index.js';
3
+ export * from './enhanced-json-value.js';
4
+ export * from './expressions/index.js';
5
+ export type * from './json-schema.js';
6
+ export * from './markers/index.js';
7
+ export type * from './model/index.js';
@@ -0,0 +1 @@
1
+ export*from"./application/index.js";export*from"./components/index.js";export*from"./enhanced-json-value.js";export*from"./expressions/index.js";export*from"./markers/index.js";
@@ -0,0 +1,52 @@
1
+ import type { JsonSchemaType } from '../core/index.js';
2
+ /**
3
+ * Plain AJV-ready JSON Schema — the output of `toRuntimeSchema` in `application-spec`.
4
+ *
5
+ * Identical in structure to `EnhancedJsonSchemaType` except the Phoenix-only
6
+ * extensions (`tsType`, `import`, `references`) are gone; those are
7
+ * stripped by `stripCustomKeywords` before this type is produced. All `$ref`
8
+ * targets are local (`#/$defs/{name}`) — no external references remain. Shapes
9
+ * stored under `$defs` are themselves `JsonSchema`s.
10
+ *
11
+ * Use this type anywhere the runtime AJV shape is required (validators,
12
+ * serializers, migration tools). Schema producers (`Shapes`, `CommonTypes`,
13
+ * relation inference) keep using `EnhancedJsonSchemaType`.
14
+ */
15
+ export interface JsonSchema {
16
+ readonly id?: string;
17
+ readonly $ref?: string;
18
+ readonly $schema?: string;
19
+ readonly $defs?: Readonly<Record<string, JsonSchema>>;
20
+ readonly title?: string;
21
+ readonly description?: string;
22
+ readonly default?: unknown;
23
+ readonly format?: string;
24
+ readonly multipleOf?: number;
25
+ readonly maximum?: number;
26
+ readonly exclusiveMaximum?: boolean;
27
+ readonly minimum?: number;
28
+ readonly exclusiveMinimum?: boolean;
29
+ readonly maxLength?: number;
30
+ readonly minLength?: number;
31
+ readonly pattern?: string;
32
+ readonly additionalItems?: boolean | JsonSchema;
33
+ readonly items?: JsonSchema | readonly JsonSchema[];
34
+ readonly maxItems?: number;
35
+ readonly minItems?: number;
36
+ readonly uniqueItems?: boolean;
37
+ readonly maxProperties?: number;
38
+ readonly minProperties?: number;
39
+ readonly required?: readonly string[];
40
+ readonly additionalProperties?: boolean | JsonSchema;
41
+ readonly definitions?: Readonly<Record<string, JsonSchema>>;
42
+ readonly properties?: Readonly<Record<string, JsonSchema>>;
43
+ readonly patternProperties?: Readonly<Record<string, JsonSchema>>;
44
+ readonly dependencies?: Readonly<Record<string, JsonSchema | readonly string[]>>;
45
+ readonly enum?: readonly unknown[];
46
+ readonly type?: JsonSchemaType | readonly JsonSchemaType[];
47
+ readonly allOf?: readonly JsonSchema[];
48
+ readonly anyOf?: readonly JsonSchema[];
49
+ readonly oneOf?: readonly JsonSchema[];
50
+ readonly not?: JsonSchema;
51
+ readonly [k: string]: unknown;
52
+ }
@@ -0,0 +1 @@
1
+ export{};
@@ -0,0 +1,2 @@
1
+ export * from './eval-marker.js';
2
+ export * from './msg-marker.js';
@@ -0,0 +1 @@
1
+ export*from"./eval-marker.js";export*from"./msg-marker.js";
@@ -0,0 +1,28 @@
1
+ import type { MessageDescriptor } from '@lingui/core';
2
+ import { z } from 'zod';
3
+ /** Key literal for the `$msg` marker in raw YAML. */
4
+ export declare const MSG_MARKER_KEY: '$msg';
5
+ /** Key literal for the optional `$msgId` qualifier on `$msg`. */
6
+ export declare const MSG_ID_MARKER_KEY: '$msgId';
7
+ /**
8
+ * YAML marker produced by `$msg: 'text'` / `$msg: 'text', $msgId: 'id'` entries.
9
+ * `$msgId` is a qualifier on `$msg`, not a standalone marker.
10
+ */
11
+ export interface MsgMarker {
12
+ readonly $msg: string;
13
+ readonly $msgId?: string;
14
+ }
15
+ /** Zod decoder for {@link MsgMarker}. Strict — rejects extra keys. */
16
+ export declare const msgMarkerSchema: z.ZodObject<{
17
+ $msg: z.ZodString;
18
+ $msgId: z.ZodOptional<z.ZodString>;
19
+ }, "strict", z.ZodTypeAny, {
20
+ $msg: string;
21
+ $msgId?: string | undefined;
22
+ }, {
23
+ $msg: string;
24
+ $msgId?: string | undefined;
25
+ }>;
26
+ /** Structural type guard for {@link MsgMarker}. */
27
+ export declare const isMsgMarker: (value: unknown) => value is MsgMarker;
28
+ export declare const createMessageDescriptor: (marker: MsgMarker) => MessageDescriptor;
@@ -0,0 +1 @@
1
+ import{generateMessageId as s}from"@lingui/message-utils/generateMessageId";import{z as e}from"zod";export const MSG_MARKER_KEY="$msg";export const MSG_ID_MARKER_KEY="$msgId";export const msgMarkerSchema=e.object({$msg:e.string(),$msgId:e.string().optional()}).strict();export const isMsgMarker=s=>msgMarkerSchema.safeParse(s).success;export const createMessageDescriptor=e=>({id:e.$msgId??s(e.$msg),message:e.$msg});