@salsita-ai/sdk 0.50.0-develop.10b1931917 → 0.50.0-develop.1162eef277

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 (279) hide show
  1. package/README.md +11 -11
  2. package/dist/application-spec/generate-application/code/common/index.d.ts +1 -0
  3. package/dist/application-spec/generate-application/code/common/index.js +1 -1
  4. package/dist/application-spec/generate-application/code/common/project-module-list.d.ts +8 -0
  5. package/dist/application-spec/generate-application/code/common/project-module-list.js +3 -0
  6. package/dist/application-spec/generate-application/code/domain-runtime-files.js +1 -1
  7. package/dist/application-spec/generate-application/code/project-scaffolding.js +2 -2
  8. package/dist/application-spec/generate-application/code/templates/admin-domain-code.d.ts +1 -2
  9. package/dist/application-spec/generate-application/code/templates/admin-domain-code.js +6 -14
  10. package/dist/application-spec/generate-application/code/templates/admin-frontend-code.d.ts +1 -2
  11. package/dist/application-spec/generate-application/code/templates/admin-frontend-code.js +4 -4
  12. package/dist/application-spec/generate-application/code/templates/configurator-domain-code.d.ts +1 -2
  13. package/dist/application-spec/generate-application/code/templates/configurator-domain-code.js +7 -16
  14. package/dist/application-spec/generate-application/code/templates/configurator-frontend-code.d.ts +1 -2
  15. package/dist/application-spec/generate-application/code/templates/configurator-frontend-code.js +7 -9
  16. package/dist/application-spec/generate-application/code/templates/domain-spec/generate-assistant.d.ts +2 -2
  17. package/dist/application-spec/generate-application/code/templates/domain-spec/generate-assistant.js +1 -1
  18. package/dist/application-spec/generate-application/code/templates/domain-spec.js +4 -4
  19. package/dist/application-spec/generate-application/code/templates/domain.js +3 -3
  20. package/dist/application-spec/generate-application/code/templates/index.d.ts +1 -0
  21. package/dist/application-spec/generate-application/code/templates/index.js +1 -1
  22. package/dist/application-spec/generate-application/code/templates/skills/agents-md.js +1 -1
  23. package/dist/application-spec/generate-application/code/templates/skills/phoenix-cli-skill.js +1 -1
  24. package/dist/application-spec/generate-application/code/templates/skills/phoenix-create-configurator-skill.js +43 -13
  25. package/dist/application-spec/generate-application/code/templates/skills/phoenix-feedback-skill.js +39 -16
  26. package/dist/application-spec/generate-application/code/templates/skills/phoenix-review-skill.js +2 -2
  27. package/dist/application-spec/generate-application/code/templates/skills/phoenix-skill.js +1 -1
  28. package/dist/application-spec/generate-application/code/templates/skills/phoenix-variants-skill.d.ts +1 -0
  29. package/dist/application-spec/generate-application/code/templates/skills/phoenix-variants-skill.js +6 -0
  30. package/dist/application-spec/generate-application/code/templates/skills/sdk-content.d.ts +30 -3
  31. package/dist/application-spec/generate-application/code/templates/skills/sdk-content.js +19 -5
  32. package/dist/application-spec/generate-application/code/templates/webhook-builders.js +8 -4
  33. package/dist/application-spec/generate-application/runtime/sections/assistant-runtime.d.ts +4 -3
  34. package/dist/application-spec/generate-application/runtime/sections/assistant-runtime.js +1 -1
  35. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-field-type.d.ts +3 -8
  36. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-field-type.js +1 -1
  37. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-schema.d.ts +9 -43
  38. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-schema.js +1 -1
  39. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-server-settings.d.ts +2 -2
  40. package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant.d.ts +5 -21
  41. package/dist/client/phoenix-api.d.ts +58 -30
  42. package/dist/configurator/application-runtime/extensions/selectors.js +1 -1
  43. package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.d.ts +158 -13
  44. package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.js +1 -1
  45. package/dist/configurator/application-runtime/extensions/systems/assistant-system/change-recorder.d.ts +85 -0
  46. package/dist/configurator/application-runtime/extensions/systems/assistant-system/change-recorder.js +1 -0
  47. package/dist/configurator/application-runtime/extensions/systems/assistant-system/graphql-tools.d.ts +8 -3
  48. package/dist/configurator/application-runtime/extensions/systems/assistant-system/graphql-tools.js +1 -1
  49. package/dist/configurator/application-runtime/extensions/systems/graphql-system/entity-meta.d.ts +4 -0
  50. package/dist/configurator/application-runtime/extensions/systems/graphql-system/entity-meta.js +1 -1
  51. package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.d.ts +16 -1
  52. package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.js +1 -1
  53. package/dist/configurator/application-runtime/extensions/systems/graphql-system/side-effects.d.ts +9 -0
  54. package/dist/configurator/application-runtime/extensions/systems/graphql-system/side-effects.js +1 -0
  55. package/dist/configurator/application-runtime/extensions/systems/index.d.ts +1 -1
  56. package/dist/configurator/application-runtime/extensions/systems/layout-system/layout-system.d.ts +2 -0
  57. package/dist/configurator/application-runtime/extensions/systems/layout-system/layout-system.js +1 -1
  58. package/dist/configurator/common/frontend-analytics.d.ts +21 -21
  59. package/dist/configurator/index.d.ts +0 -1
  60. package/dist/configurator/index.js +1 -1
  61. package/dist/configurator/pages/LoadedConfiguratorDetailPage.js +1 -1
  62. package/dist/configurator/scene/scene-analytics/scene-analytics.d.ts +2 -2
  63. package/dist/configurator/ui/assistant/AssistantChips.d.ts +11 -0
  64. package/dist/configurator/ui/assistant/AssistantChips.js +1 -0
  65. package/dist/configurator/ui/assistant/AssistantComposer.d.ts +6 -0
  66. package/dist/configurator/ui/assistant/AssistantComposer.js +1 -0
  67. package/dist/configurator/ui/assistant/AssistantMarkdown.d.ts +7 -0
  68. package/dist/configurator/ui/assistant/AssistantMarkdown.js +1 -0
  69. package/dist/configurator/ui/assistant/AssistantPanel.d.ts +11 -0
  70. package/dist/configurator/ui/assistant/AssistantPanel.js +1 -0
  71. package/dist/configurator/ui/assistant/AssistantThread.d.ts +6 -0
  72. package/dist/configurator/ui/assistant/AssistantThread.js +1 -0
  73. package/dist/configurator/ui/assistant/assistant-failure.d.ts +18 -0
  74. package/dist/configurator/ui/assistant/assistant-failure.js +1 -0
  75. package/dist/configurator/ui/assistant/index.d.ts +2 -0
  76. package/dist/configurator/ui/assistant/index.js +1 -0
  77. package/dist/configurator/ui/assistant/streamed-markdown.d.ts +6 -0
  78. package/dist/configurator/ui/assistant/streamed-markdown.js +2 -0
  79. package/dist/configurator/ui/assistant/theme.d.ts +2 -0
  80. package/dist/configurator/ui/assistant/theme.js +1 -0
  81. package/dist/configurator/ui/assistant/thread-items.d.ts +37 -0
  82. package/dist/configurator/ui/assistant/thread-items.js +1 -0
  83. package/dist/configurator/ui/assistant/tool-chip.d.ts +44 -0
  84. package/dist/configurator/ui/assistant/tool-chip.js +1 -0
  85. package/dist/configurator/ui/assistant/use-follow-stream.d.ts +17 -0
  86. package/dist/configurator/ui/assistant/use-follow-stream.js +1 -0
  87. package/dist/configurator/ui/assistant/use-is-assistant-panel-open.d.ts +5 -0
  88. package/dist/configurator/ui/assistant/use-is-assistant-panel-open.js +1 -0
  89. package/dist/configurator/ui/configurator-layout/universal/DesktopLayout.js +5 -5
  90. package/dist/configurator/ui/configurator-layout/universal/MobileLayout.d.ts +2 -0
  91. package/dist/configurator/ui/configurator-layout/universal/MobileLayout.js +4 -4
  92. package/dist/configurator/ui/legacy/mobile-controls/MobileControls.d.ts +1 -1
  93. package/dist/configurator/ui/phoenix-layout-elements/AssistantToolbarButton.d.ts +10 -0
  94. package/dist/configurator/ui/phoenix-layout-elements/AssistantToolbarButton.js +1 -0
  95. package/dist/configurator/ui/phoenix-layout-elements/index.d.ts +1 -0
  96. package/dist/configurator/ui/phoenix-layout-elements/index.js +1 -1
  97. package/dist/docs/complex-use-cases/filtered-controls-with-fix-dialog.mdx +84 -98
  98. package/dist/docs/complex-use-cases/navigation-reset-selection.mdx +18 -12
  99. package/dist/docs/index.json +1 -1
  100. package/dist/docs/layouts/custom-price.mdx +14 -16
  101. package/dist/docs/layouts/sidebar/tabs.mdx +24 -21
  102. package/dist/docs/layouts/toolbar.mdx +6 -0
  103. package/dist/docs/other-features/domain-actions.mdx +1 -1
  104. package/dist/docs/other-features/side-effects.mdx +68 -34
  105. package/dist/docs/other-features/transformers.mdx +1 -1
  106. package/dist/docs/phoenix/admin.mdx +11 -26
  107. package/dist/docs/phoenix/finalization.mdx +1 -1
  108. package/dist/docs/phoenix/integration/headless-mode-external.mdx +5 -5
  109. package/dist/docs/phoenix/integration/widget-mode-external.mdx +5 -5
  110. package/dist/docs/phoenix/localization.mdx +3 -22
  111. package/dist/docs/phoenix/phoenix-cli.mdx +96 -13
  112. package/dist/docs/phoenix/project-structure.mdx +9 -2
  113. package/dist/docs/phoenix/sdk-builder-workflow.mdx +7 -5
  114. package/dist/docs/recipes-and-delete/recipe-hierarchy-selection.mdx +15 -13
  115. package/dist/docs/relations/hierarchy.mdx +13 -2
  116. package/dist/domain/actions/action-factory.d.ts +3 -2
  117. package/dist/domain/application/application-config.d.ts +25 -75
  118. package/dist/domain/application/assistant/assistant-manifest.d.ts +61 -0
  119. package/dist/domain/application/assistant/assistant-manifest.js +1 -0
  120. package/dist/domain/application/assistant/assistant-resolved-manifest.d.ts +95 -0
  121. package/dist/domain/application/assistant/assistant-resolved-manifest.js +1 -0
  122. package/dist/domain/application/assistant/assistant-value-type.d.ts +9 -0
  123. package/dist/domain/application/assistant/assistant-value-type.js +1 -0
  124. package/dist/domain/application/assistant/build-assistant-manifest.d.ts +4 -0
  125. package/dist/domain/application/assistant/build-assistant-manifest.js +1 -0
  126. package/dist/{phoenix/application-spec → domain/application/assistant}/generate-assistant-prompt.d.ts +6 -5
  127. package/dist/domain/application/assistant/generate-assistant-prompt.js +1 -0
  128. package/dist/domain/application/assistant/index.d.ts +4 -0
  129. package/dist/domain/application/assistant/index.js +1 -0
  130. package/dist/domain/application/assistant-field-options.d.ts +1 -1
  131. package/dist/domain/application/assistant-mutations.d.ts +1 -1
  132. package/dist/domain/application/assistant-recipe-groups.d.ts +1 -1
  133. package/dist/domain/application/configurator-builder.d.ts +33 -18
  134. package/dist/domain/application/configurator-builder.js +1 -1
  135. package/dist/domain/application/element-registration.d.ts +5 -0
  136. package/dist/domain/application/element-registration.js +1 -1
  137. package/dist/domain/application/handler-registration.d.ts +5 -0
  138. package/dist/domain/application/handler-registration.js +1 -1
  139. package/dist/domain/application/index.d.ts +5 -4
  140. package/dist/domain/application/index.js +1 -1
  141. package/dist/domain/application/resolve-imported-transformers.d.ts +11 -1
  142. package/dist/domain/application/resolve-imported-transformers.js +1 -1
  143. package/dist/domain/components/domain-component-optic.d.ts +3 -0
  144. package/dist/domain/components/domain-component-optic.js +2 -2
  145. package/dist/domain/components/updates.d.ts +16 -0
  146. package/dist/domain/core/action.d.ts +1 -1
  147. package/dist/domain/domain-builder.d.ts +10 -1
  148. package/dist/domain/domain-builder.js +1 -1
  149. package/dist/domain/domain-context/index.d.ts +1 -1
  150. package/dist/domain/domain-context/store-system/store-system.js +1 -1
  151. package/dist/domain/domain-context/transformers/chain-builder.d.ts +1 -1
  152. package/dist/domain/domain-runtime/domain-runtime.d.ts +4 -4
  153. package/dist/domain/domain-runtime/spec/initialize.js +1 -1
  154. package/dist/domain/domain-runtime/spec/selectors.d.ts +2 -2
  155. package/dist/domain/domain-runtime/spec/selectors.js +1 -1
  156. package/dist/domain/domain-runtime/spec/types.d.ts +3 -3
  157. package/dist/domain/index.d.ts +3 -2
  158. package/dist/domain/index.js +1 -1
  159. package/dist/domain/invariants/invariants.d.ts +9 -0
  160. package/dist/domain/invariants/invariants.js +1 -1
  161. package/dist/domain/mocks/create-mock-domain-runtime.d.ts +3 -3
  162. package/dist/domain/mocks/create-mock-domain-runtime.js +1 -1
  163. package/dist/domain/project-modules/index.d.ts +1 -0
  164. package/dist/domain/project-modules/index.js +1 -0
  165. package/dist/domain/project-modules/project-modules.d.ts +41 -0
  166. package/dist/domain/project-modules/project-modules.js +1 -0
  167. package/dist/domain/system-components/common/dimension-indicator-schema.d.ts +1 -1
  168. package/dist/domain/system-components/common/dimension-indicator-schema.js +1 -1
  169. package/dist/domain/system-components/phoenix-layout-elements/assistant-toolbar-button.d.ts +44 -0
  170. package/dist/domain/system-components/phoenix-layout-elements/assistant-toolbar-button.js +1 -0
  171. package/dist/domain/system-components/phoenix-layout-elements/index.d.ts +1 -0
  172. package/dist/domain/system-components/phoenix-layout-elements/index.js +1 -1
  173. package/dist/domain/system-components/phoenix-layout-elements/phoenix-layout-element.d.ts +2 -1
  174. package/dist/domain/system-components/phoenix-layout-elements/phoenix-layout-element.js +1 -1
  175. package/dist/editor/Editor.js +1 -1
  176. package/dist/editor/domain-editors/use-implementation-files.d.ts +7 -6
  177. package/dist/editor/domain-editors/use-implementation-files.js +1 -1
  178. package/dist/editor/graphql/GraphQLPanel.js +1 -1
  179. package/dist/editor/variants/VariantAnswerFooter.d.ts +17 -0
  180. package/dist/editor/variants/VariantAnswerFooter.js +1 -0
  181. package/dist/editor/variants/VariantPreviewHandler.d.ts +6 -0
  182. package/dist/editor/variants/VariantPreviewHandler.js +1 -0
  183. package/dist/editor/variants/variant-preview-styles.d.ts +49 -0
  184. package/dist/editor/variants/variant-preview-styles.js +1 -0
  185. package/dist/i18n/locales/cs/messages.js +1 -1
  186. package/dist/i18n/locales/da/messages.js +1 -1
  187. package/dist/i18n/locales/de/messages.js +1 -1
  188. package/dist/i18n/locales/de-AT/messages.js +1 -1
  189. package/dist/i18n/locales/en/messages.js +1 -1
  190. package/dist/i18n/locales/en-AU/messages.js +1 -1
  191. package/dist/i18n/locales/en-GB/messages.js +1 -1
  192. package/dist/i18n/locales/es/messages.js +1 -1
  193. package/dist/i18n/locales/fr/messages.js +1 -1
  194. package/dist/i18n/locales/hu/messages.js +1 -1
  195. package/dist/i18n/locales/it/messages.js +1 -1
  196. package/dist/i18n/locales/nl/messages.js +1 -1
  197. package/dist/i18n/locales/pl/messages.js +1 -1
  198. package/dist/i18n/locales/pt/messages.js +1 -1
  199. package/dist/i18n/locales/ro/messages.js +1 -1
  200. package/dist/i18n/locales/sk/messages.js +1 -1
  201. package/dist/i18n/locales/tr/messages.js +1 -1
  202. package/dist/legacy/domain-cli/gen/configurator-builder.js +21 -8
  203. package/dist/legacy/domain-cli/utils/cache.d.ts +1 -1
  204. package/dist/legacy/domain-cli/utils/cache.js +1 -1
  205. package/dist/legacy/domain-spec/__generated/domain-schema.d.ts +6 -0
  206. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/frontend-admin.js +18 -19
  207. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/frontend-configurator.js +15 -16
  208. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/frontend-embedded-admin.js +22 -23
  209. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/frontend-embedded-configurator.js +15 -16
  210. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/generator-shared.d.ts +7 -2
  211. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/generator-shared.js +8 -12
  212. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/index.d.ts +1 -1
  213. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/index.js +1 -1
  214. package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/webhook-builders.js +7 -7
  215. package/dist/phoenix/application-spec/application-spec-builder.js +1 -1
  216. package/dist/phoenix/application-spec/resolve-deployment-data.d.ts +4 -3
  217. package/dist/phoenix/application-spec/resolve-deployment-data.js +2 -2
  218. package/dist/phoenix/cli.js +1 -1
  219. package/dist/phoenix/commands/deploy.js +2 -2
  220. package/dist/phoenix/commands/dev.js +5 -5
  221. package/dist/phoenix/commands/feedback.d.ts +2 -1
  222. package/dist/phoenix/commands/feedback.js +1 -1
  223. package/dist/phoenix/commands/index.d.ts +1 -0
  224. package/dist/phoenix/commands/index.js +1 -1
  225. package/dist/phoenix/commands/prepare-deployment-command.js +2 -2
  226. package/dist/phoenix/commands/prepare-local-environment-command.js +1 -1
  227. package/dist/phoenix/commands/story-dev.js +2 -2
  228. package/dist/phoenix/commands/variants.d.ts +35 -0
  229. package/dist/phoenix/commands/variants.js +1 -0
  230. package/dist/phoenix/editor-server/commands-router.d.ts +68 -1
  231. package/dist/phoenix/editor-server/commands-router.js +1 -1
  232. package/dist/phoenix/editor-server/editor-router.d.ts +65 -0
  233. package/dist/phoenix/editor-server/editor-server.d.ts +3 -3
  234. package/dist/phoenix/editor-server/editor-server.js +1 -1
  235. package/dist/phoenix/editor-server/protocol.d.ts +1 -0
  236. package/dist/phoenix/editor-server/spec-router.js +1 -1
  237. package/dist/phoenix/editor-server/trpc.d.ts +3 -0
  238. package/dist/phoenix/services/application-spec-project-watcher.d.ts +13 -2
  239. package/dist/phoenix/services/application-spec-project-watcher.js +3 -3
  240. package/dist/phoenix/services/index.d.ts +4 -0
  241. package/dist/phoenix/services/index.js +1 -1
  242. package/dist/phoenix/services/phoenix-application.d.ts +21 -0
  243. package/dist/phoenix/services/phoenix-application.js +1 -0
  244. package/dist/phoenix/services/phoenix-store.d.ts +48 -0
  245. package/dist/phoenix/services/phoenix-store.js +1 -0
  246. package/dist/phoenix/services/phx-variant-previews-plugin.d.ts +9 -0
  247. package/dist/phoenix/services/phx-variant-previews-plugin.js +1 -0
  248. package/dist/phoenix/services/prepare-development-environment.js +1 -1
  249. package/dist/phoenix/services/variant-file-session.d.ts +36 -0
  250. package/dist/phoenix/services/variant-file-session.js +1 -0
  251. package/dist/phoenix/services/variant-preview-service.d.ts +80 -0
  252. package/dist/phoenix/services/variant-preview-service.js +1 -0
  253. package/dist/phoenix/services/vite-config.js +1 -1
  254. package/dist/react-sdk/application-runtime/common-services/analytics/analytics-service.d.ts +2 -2
  255. package/dist/react-sdk/application-runtime/common-services/analytics/domain-controls-analytics.d.ts +21 -21
  256. package/dist/react-sdk/application-runtime/hooks/data/use-published-data.d.ts +1 -1
  257. package/dist/react-sdk/application-runtime/hooks/use-domain-application-props.d.ts +5 -2
  258. package/dist/react-sdk/application-runtime/hooks/use-spec.d.ts +1 -1
  259. package/dist/react-sdk/application-runtime/mocks/create-mock-application-runtime.d.ts +1 -1
  260. package/dist/react-sdk/application-runtime/mocks/create-mock-application-runtime.js +1 -1
  261. package/dist/react-sdk/application-runtime/runtime/create-application-runtime.d.ts +4 -4
  262. package/dist/ui-sdk/common/use-viewport-size.d.ts +4 -1
  263. package/dist/ui-sdk/common/use-viewport-size.js +1 -1
  264. package/dist/ui-sdk/components/mobile-controls/index.d.ts +1 -1
  265. package/dist/ui-sdk/theme/types.d.ts +4 -0
  266. package/dist/webhook/builders/common.d.ts +3 -0
  267. package/dist/webhook/builders/webhook-builder.d.ts +1 -1
  268. package/dist/webhook/builders/webhook-builder.js +1 -1
  269. package/package.json +5 -8
  270. package/schema/domain-spec.json +24 -0
  271. package/dist/configurator/assistant/index.d.ts +0 -1
  272. package/dist/configurator/assistant/index.js +0 -1
  273. package/dist/configurator/assistant/use-assistant-chat.d.ts +0 -20
  274. package/dist/configurator/assistant/use-assistant-chat.js +0 -1
  275. package/dist/extras/assistant/AssistantChat.d.ts +0 -6
  276. package/dist/extras/assistant/AssistantChat.js +0 -1
  277. package/dist/extras/assistant/index.d.ts +0 -1
  278. package/dist/extras/assistant/index.js +0 -1
  279. package/dist/phoenix/application-spec/generate-assistant-prompt.js +0 -1
@@ -6,7 +6,7 @@ import type { GraphQLSystem } from '../graphql-system/graphql-system.js';
6
6
  * that moment, never against ones captured when its reply began streaming.
7
7
  */
8
8
  export interface AssistantToolDependencies {
9
- readonly getGraphQLSystem: () => Pick<GraphQLSystem, 'prepare' | 'execute'>;
9
+ readonly getGraphQLSystem: () => Pick<GraphQLSystem, 'prepare' | 'execute' | 'readSideEffects'>;
10
10
  readonly getStoreSystem: () => {
11
11
  readonly isInitialized: boolean;
12
12
  readonly stateSlices: {
@@ -18,11 +18,14 @@ export interface AssistantToolDependencies {
18
18
  };
19
19
  };
20
20
  readonly getActiveMode: () => DomainMode;
21
+ /** Forgets the changes recorded for the next change record, so it never lists what the agent itself changed. */
22
+ readonly clearChanges: () => void;
21
23
  }
22
24
  /**
23
25
  * Browser implementation of the assistant's `query` tool: prepares the operations from the
24
26
  * committed state and drops their emissions, so mutations in the list show a hypothetical state and
25
- * the configuration, the undo stack and the saved revision stay as they were.
27
+ * the configuration, the undo stack and the saved revision stay as they were. The side effects of
28
+ * the dropped emissions are still reported, as what the changes would do.
26
29
  */
27
30
  export declare const createQueryClientTool: (dependencies: AssistantToolDependencies) => import("@tanstack/ai").ClientTool<{
28
31
  type: string;
@@ -88,7 +91,9 @@ export declare const createQueryClientTool: (dependencies: AssistantToolDependen
88
91
  /**
89
92
  * Browser implementation of the assistant's `apply` tool: hands the operations to
90
93
  * `GraphQLSystem.execute`, which commits their changes as one undo step, and returns its result,
91
- * including why nothing was committed when a commit was held or lost to a conflict. Only an owner
94
+ * including why nothing was committed when a commit was held or lost to a conflict. A commit clears
95
+ * the recorded changes; a held one does not, so it shows in the next change record once the user
96
+ * approves it. Only an owner
92
97
  * session may change the configuration; the check lives here and not in the `GraphQLSystem`, whose
93
98
  * other caller, the editor panel, has its own rules.
94
99
  */
@@ -1 +1 @@
1
- import{AssistantGraphQLToolInput as e,applyTool as t,queryTool as o}from"@salsita-ai/contracts/assistant";let a=e=>({results:[],committed:!1,message:e}),i=e=>JSON.parse(JSON.stringify(e)),s=t=>{let o=e.safeParse(t);if(!o.success){let e=o.error.issues.map(e=>`${e.path.join(".")}: ${e.message}`).join("; ");return{ok:!1,refusal:a(`Nothing was run, because the tool input is not a list of operations (${e}). Send { "operations": [{ "query": "<one GraphQL operation>" }] } with at least one item.`)}}return{ok:!0,operations:o.data.operations.map(({query:e,variables:t,operationName:o})=>({document:e,variables:t??void 0,operationName:o??void 0}))}};export const createQueryClientTool=e=>o.client(async t=>{let o=s(t);if(!o.ok)return o.refusal;let n=e.getStoreSystem();return n.isInitialized?i({results:(await e.getGraphQLSystem().prepare(n.stateSlices.committed.value.state,o.operations)).map(({result:e})=>e),committed:!1}):a("Nothing was run, because the configuration is not loaded yet, so there is no state to query. Call the tool again once the configurator has finished loading.")});export const createApplyClientTool=e=>t.client(async t=>{if("owner"!==e.getActiveMode())return a("Nothing was run, because changes cannot be applied in this session: the user may view this configuration but not change it. The query tool still works here, including mutations, to show what a change would do without keeping it.");let o=s(t);return o.ok?i(await e.getGraphQLSystem().execute(o.operations)):o.refusal});
1
+ import{AssistantGraphQLToolInput as e,applyTool as t,queryTool as o}from"@salsita-ai/contracts/assistant";let a=e=>({results:[],committed:!1,message:e,sideEffects:[]}),i=e=>JSON.parse(JSON.stringify(e)),s=t=>{let o=e.safeParse(t);if(!o.success){let e=o.error.issues.map(e=>`${e.path.join(".")}: ${e.message}`).join("; ");return{ok:!1,refusal:a(`Nothing was run, because the tool input is not a list of operations (${e}). Send { "operations": [{ "query": "<one GraphQL operation>" }] } with at least one item.`)}}return{ok:!0,operations:o.data.operations.map(({query:e,variables:t,operationName:o})=>({document:e,variables:t??void 0,operationName:o??void 0}))}};export const createQueryClientTool=e=>o.client(async t=>{let o=s(t);if(!o.ok)return o.refusal;let n=e.getStoreSystem();if(!n.isInitialized)return a("Nothing was run, because the configuration is not loaded yet, so there is no state to query. Call the tool again once the configurator has finished loading.");let r=e.getGraphQLSystem(),l=await r.prepare(n.stateSlices.committed.value.state,o.operations);return i({results:l.map(({result:e})=>e),committed:!1,sideEffects:r.readSideEffects(l)})});export const createApplyClientTool=e=>t.client(async t=>{if("owner"!==e.getActiveMode())return a("Nothing was run, because changes cannot be applied in this session: the user may view this configuration but not change it. The query tool still works here, including mutations, to show what a change would do without keeping it.");let o=s(t);if(!o.ok)return o.refusal;let n=await e.getGraphQLSystem().execute(o.operations);return n.committed&&e.clearChanges(),i(n)});
@@ -47,12 +47,14 @@ export declare const createEntityMeta: (source: EntityMetaSource, context: Entit
47
47
  root: null;
48
48
  changedEntities: never[];
49
49
  deletedEntityIds: never[];
50
+ sideEffects: never[];
50
51
  errors: readonly DryRunError[];
51
52
  } | {
52
53
  ok: boolean;
53
54
  root: unknown;
54
55
  changedEntities: {}[];
55
56
  deletedEntityIds: DomainEntityId<string>[];
57
+ sideEffects: readonly string[];
56
58
  errors: never[];
57
59
  } | null;
58
60
  }[];
@@ -71,12 +73,14 @@ export declare const createEntityMeta: (source: EntityMetaSource, context: Entit
71
73
  root: null;
72
74
  changedEntities: never[];
73
75
  deletedEntityIds: never[];
76
+ sideEffects: never[];
74
77
  errors: readonly DryRunError[];
75
78
  } | {
76
79
  ok: boolean;
77
80
  root: unknown;
78
81
  changedEntities: {}[];
79
82
  deletedEntityIds: DomainEntityId<string>[];
83
+ sideEffects: readonly string[];
80
84
  errors: never[];
81
85
  } | null;
82
86
  }[];
@@ -1 +1 @@
1
- import{findAssistantFieldOptions as e,findAssistantRecipeGroups as t,modifyEntityUpdateTag as i,prepareAssistantRecipeUpdates as r,readAssistantFieldOptions as n,readAssistantRecipeGroup as s,systemUpdateFactory as a}from"#package/domain";let o=e=>({ok:!1,root:null,changedEntities:[],deletedEntityIds:[],errors:e}),d=(e,{state:t},{recipes:i,prepareStateEmission:r,find$Entity:n})=>{let{validity:s,state:a}=r(e,t,"committed");if(s.valid){let e=new Map(t.entities.map(e=>[e.id,e])),i=new Set(a.entities.map(e=>e.id));return{ok:!0,root:n(a.rootEntityId,a),changedEntities:a.entities.filter(t=>e.get(t.id)!==t).flatMap(e=>n(e.id,a)??[]),deletedEntityIds:t.entities.map(e=>e.id).filter(e=>!i.has(e)),errors:[]}}return o("uncaught"===s.severity?[{type:"uncaught",entityId:null,message:s.error.message}]:s.entityViolations.filter(({violation:e})=>e.severity===s.severity).map(({entityId:e,violation:t})=>({type:t.type,entityId:e,message:void 0===t.message?null:i.resolveLocalizable(t.message)})))};export const createEntityMeta=(l,c)=>{let p=e=>()=>t(c.manifest,e,l.entity.type).flatMap(t=>{let i=s(e,t,l.entity.id,l.state,c.recipes);return i?[((e,t,i)=>{let{group:n,label:s,description:a,recipes:l}=e,{resolveLocalizable:c}=i.recipes;return{groupId:n.groupId,name:void 0===s?n.instanceName:c(s),description:n.description??(void 0===a?null:c(a)),recipes:l.map(n=>((e,t,i,n)=>{let{key:s,item:a}=e,{resolveLocalizable:l}=n.recipes;return{key:s,name:l(a.title),description:void 0===a.description?null:l(a.description),imageUrl:void 0===a.thumbnailUrl?null:l(a.thumbnailUrl),dryRun:()=>t.group.dryRuns?((e,t,i,n)=>{let{updates:s,error:a}=((e,t,{entity:i,state:n},{recipes:s})=>{try{return{updates:r(e,t,i.id,n,s),error:void 0}}catch(e){if("child"===t.target.kind)throw e;return{updates:void 0,error:e instanceof Error?e.message:String(e)}}})(e,t,i,n);return void 0===s?o([{type:"uncaught",entityId:null,message:a}]):d(s,i,n)})(e,t,i,n):null}})(n,e,t,i))}})(i,l,c)]:[]});return{...Object.fromEntries(e(c.manifest,l.entity.type).map(e=>[e.edgeName,()=>n(e,l.entity.id,l.state,c.recipes)?.map(t=>({...t,dryRun:()=>e.dryRuns?(({field:e},{value:t},r,n)=>{let{entity:s}=r,l=Object.entries(Object.entries(n.manifest.writableFields??{}).find(([e])=>e===s.type)?.[1]??{}).find(([t])=>t===e)?.[1];if(void 0===l)throw Error(`The options of "${e}" on "${s.id}" cannot be dry-run: the schema lists them, but the configurator has no writable field "${e}" on ${s.type}. Regenerate the application so the schema matches the configurator.`);let c=r.state.entities.find(e=>e.id===s.id),p={[l]:t},u=c?n.validateEntity({...c,...p}):{success:!0};return u.success?d([a.DomainEntityModification({entityId:s.id,stateDelta:p,updateTag:i})],r,n):o([{type:"uncaught",entityId:s.id,message:`Writing ${JSON.stringify(t)} to "${e}" would make "${s.id}" an invalid ${s.type}: ${u.errors.join("; ")}.${e===l?"":` The message names the stored property: ${e} is stored as ${l}.`} The option comes from the configurator and cannot be corrected from here, so do not write it.`}])})(e,t,l,c):null}))??null])),childRecipes:p("child"),replaceRecipes:p("replace")}};
1
+ import{findAssistantFieldOptions as e,findAssistantRecipeGroups as t,modifyEntityUpdateTag as i,prepareAssistantRecipeUpdates as r,readAssistantFieldOptions as n,readAssistantRecipeGroup as s,systemUpdateFactory as a}from"#package/domain";import{readSideEffects as o}from"./side-effects.js";let d=e=>({ok:!1,root:null,changedEntities:[],deletedEntityIds:[],sideEffects:[],errors:e}),l=(e,{state:t},{recipes:i,prepareStateEmission:r,find$Entity:n})=>{let{validity:s,state:a,updates:l}=r(e,t,"committed");if(s.valid){let e=new Map(t.entities.map(e=>[e.id,e])),r=new Set(a.entities.map(e=>e.id));return{ok:!0,root:n(a.rootEntityId,a),changedEntities:a.entities.filter(t=>e.get(t.id)!==t).flatMap(e=>n(e.id,a)??[]),deletedEntityIds:t.entities.map(e=>e.id).filter(e=>!r.has(e)),sideEffects:o(l,i.resolveLocalizable),errors:[]}}return d("uncaught"===s.severity?[{type:"uncaught",entityId:null,message:s.error.message}]:s.entityViolations.filter(({violation:e})=>e.severity===s.severity).map(({entityId:e,violation:t})=>({type:t.type,entityId:e,message:void 0===t.message?null:i.resolveLocalizable(t.message)})))};export const createEntityMeta=(o,c)=>{let p=e=>()=>t(c.manifest,e,o.entity.type).flatMap(t=>{let i=s(e,t,o.entity.id,o.state,c.recipes);return i?[((e,t,i)=>{let{group:n,label:s,description:a,recipes:o}=e,{resolveLocalizable:c}=i.recipes;return{groupId:n.groupId,name:void 0===s?n.instanceName:c(s),description:n.description??(void 0===a?null:c(a)),recipes:o.map(n=>((e,t,i,n)=>{let{key:s,item:a}=e,{resolveLocalizable:o}=n.recipes;return{key:s,name:o(a.title),description:void 0===a.description?null:o(a.description),imageUrl:void 0===a.thumbnailUrl?null:o(a.thumbnailUrl),dryRun:()=>t.group.dryRuns?((e,t,i,n)=>{let{updates:s,error:a}=((e,t,{entity:i,state:n},{recipes:s})=>{try{return{updates:r(e,t,i.id,n,s),error:void 0}}catch(e){if("child"===t.target.kind)throw e;return{updates:void 0,error:e instanceof Error?e.message:String(e)}}})(e,t,i,n);return void 0===s?d([{type:"uncaught",entityId:null,message:a}]):l(s,i,n)})(e,t,i,n):null}})(n,e,t,i))}})(i,o,c)]:[]});return{...Object.fromEntries(e(c.manifest,o.entity.type).map(e=>[e.edgeName,()=>n(e,o.entity.id,o.state,c.recipes)?.map(t=>({...t,dryRun:()=>e.dryRuns?(({field:e},{value:t},r,n)=>{let{entity:s}=r,o=Object.entries(Object.entries(n.manifest.writableFields??{}).find(([e])=>e===s.type)?.[1]??{}).find(([t])=>t===e)?.[1];if(void 0===o)throw Error(`The options of "${e}" on "${s.id}" cannot be dry-run: the schema lists them, but the configurator has no writable field "${e}" on ${s.type}. Regenerate the application so the schema matches the configurator.`);let c=r.state.entities.find(e=>e.id===s.id),p={[o]:t},u=c?n.validateEntity({...c,...p}):{success:!0};return u.success?l([a.DomainEntityModification({entityId:s.id,stateDelta:p,updateTag:i})],r,n):d([{type:"uncaught",entityId:s.id,message:`Writing ${JSON.stringify(t)} to "${e}" would make "${s.id}" an invalid ${s.type}: ${u.errors.join("; ")}.${e===o?"":` The message names the stored property: ${e} is stored as ${o}.`} The option comes from the configurator and cannot be corrected from here, so do not write it.`}])})(e,t,o,c):null}))??null])),childRecipes:p("child"),replaceRecipes:p("replace")}};
@@ -1,4 +1,5 @@
1
1
  import { type ExecutionResult } from 'graphql';
2
+ import { type Localizable } from '@salsita-ai/common/utils';
2
3
  import { type $DomainEntity, type DomainContextSelectors, type DomainEntity, type DomainServicesSelectors, type DomainState, type SpecSelectors, type StateUpdateEmission } from '#package/domain';
3
4
  /** One single-operation GraphQL document with its own variables, as executed by {@link GraphQLSystem}. */
4
5
  export interface GraphQLOperation {
@@ -26,13 +27,22 @@ export interface PreparedOperation {
26
27
  /**
27
28
  * What {@link GraphQLSystem.execute} returns: the results of the operations as prepared from the
28
29
  * committed state, whether their changes were committed as one undo step, and, when the operations
29
- * succeeded but their changes were not committed, the reason addressed to the caller.
30
+ * succeeded but their changes were not committed, the reason addressed to the caller. `sideEffects`
31
+ * describes the prepared changes whether or not they were committed, so a caller can weigh a held
32
+ * commit before the user approves it.
30
33
  */
31
34
  export interface GraphQLExecution {
32
35
  readonly results: readonly ExecutionResult[];
33
36
  readonly committed: boolean;
34
37
  readonly message?: string;
38
+ /** What the configurator did, or would do, beyond what the operations asked for, in applied order. */
39
+ readonly sideEffects: readonly string[];
35
40
  }
41
+ /**
42
+ * Resolves a localizable value, or each item of a list of them, to a string in the current locale.
43
+ * `null` and values that are not localizable pass through, so the schema's nullability decides.
44
+ */
45
+ export declare const localize: (value: unknown, resolveLocalizable: (message: Localizable) => string) => unknown;
36
46
  /**
37
47
  * Executes GraphQL operations against the schema generated from the application spec `assistant`
38
48
  * manifest, resolving it against the committed domain state. Agent-independent: the assistant's
@@ -75,6 +85,11 @@ export declare class GraphQLSystem {
75
85
  * "not run" error instead.
76
86
  */
77
87
  prepare: (originalState: AnyDomainState, operations: readonly GraphQLOperation[]) => Promise<readonly PreparedOperation[]>;
88
+ /**
89
+ * The side effects of prepared operations: the hints of every update of their emissions, as the
90
+ * agent is told them, in applied order.
91
+ */
92
+ readSideEffects: (prepared: readonly PreparedOperation[]) => readonly string[];
78
93
  private readonly _prepareAndCommit;
79
94
  /**
80
95
  * Prepares the operations from the committed state and commits their changes as one emission,
@@ -1 +1 @@
1
- import{GraphQLError as e,buildSchema as t,defaultFieldResolver as i,getNamedType as o,graphql as a,validateSchema as n}from"graphql";import{assertExistence as r,createSelector as s,isMessageDescriptor as l,isMultilingualString as c}from"@salsita-ai/common/utils";import{DomainEntityIdSchema as h,findAssistantMutation as d}from"#package/domain";import{createEntityMeta as m}from"./entity-meta.js";let p={errors:[new e('No assistant manifest in the application spec. The GraphQL schema is generated from the "assistant" section. Add an "assistant" section to a spec file to expose a schema.')]},u={errors:[new e("The configuration is not loaded yet, so there is no state to query. Run the query again once the configurator has finished loading.")]},f=new RegExp(h.pattern),y=e=>"object"==typeof e&&null!==e&&"type"in e&&"string"==typeof e.type&&"id"in e&&"string"==typeof e.id,g=(e,t)=>"object"==typeof e&&null!==e?Reflect.get(e,t):void 0,v=e=>e instanceof Error?e.message:String(e),S=e=>/[.!?]$/.test(e)?e:`${e}.`,_=e=>"object"==typeof e&&null!==e&&Object.getPrototypeOf(e)===Object.prototype,x=(e,t)=>{let i=_(e)?e._raw:void 0;if(y(i))return i;if(!Array.isArray(e)&&!_(e))return e;if(t.has(e))throw Error("The value contains itself. Return ids or copies of the values instead of objects that reference each other.");let o=new Set([...t,e]);return Array.isArray(e)?e.map(e=>x(e,o)):Object.fromEntries(Object.entries(e).map(([e,t])=>[e,x(t,o)]))},w=(e,t)=>Array.isArray(e)?e.map(e=>w(e,t)):"string"==typeof e||l(e)||c(e)?t(e):e,$=t=>new e(`The GraphQL schema generated from the "assistant" manifest is invalid: ${S(t)} Regenerate the application so the schema is rebuilt from the current spec.`),E=e=>(e.errors?.length??0)>0,b=t=>({errors:[new e(`Not run: operation ${t+1} failed, so the operations after it were skipped and no change of the list takes effect. Fix the failing operation and send the whole list again.`)]});export class GraphQLSystem{_context;_selectSchema=s([s.leaf(e=>e)],e=>{try{let i=t(e),o=n(i);return o.length>0?$(o.map(e=>e.message).join(" ")):i}catch(e){return $(v(e))}});_selectFieldResolver=s([s.leaf(e=>e)],t=>(a,n,r,s)=>{if(s.parentType===s.schema.getQueryType())return i(a,n,r,s);if(s.parentType===s.schema.getMutationType())return this._resolveMutation(s.fieldName,String(s.path.key),n,r);if(!y(a))return i(a,n,r,s);if("meta"===s.fieldName){let e;return m({entity:a,state:("object"==typeof(e=g(a,"_state"))&&null!==e&&"entities"in e&&"rootEntityId"in e?e:void 0)??r.state},{manifest:r.manifest,recipes:this._recipeContext(),prepareStateEmission:(e,t,i)=>this._context.domainContextSelectors.getStoreSystem().prepareStateEmission(e,t,i),find$Entity:(e,t)=>this._find$Entity(e,t),validateEntity:e=>this._context.domainContextSelectors.getDomain().validateEntity(e)})}let{source:l,localized:c}=t[s.parentType.name]?.[s.fieldName]??{source:s.fieldName},h=((t,i)=>{try{return g(t,i)}catch(o){throw new e(`Expression "${i}" failed on ${y(t)?t.id:"an entity"}: ${S(v(o))} The expression threw while the query read it; fix the expression or the configuration state it reads.`,{originalError:o instanceof Error?o:void 0})}})(a,l);return c?w(h,this._context.domainServicesSelectors.getResolveLocalizable()):l.startsWith("$")&&"JSON"===o(s.returnType).name?((t,i,o)=>{try{return x(o,new Set)}catch(o){throw new e(`Expression "${i}" returned a value that is not plain serializable data on ${y(t)?t.id:"an entity"}: ${S(v(o))} A JSON field can only return data that serializes to JSON.`,{originalError:o instanceof Error?o:void 0})}})(a,l,h):h});constructor(e){this._context=e}_get$Entity=(e,t)=>this._context.domainContextSelectors.getDomain().get$Entity(e,t);_find$Entity=(e,t)=>this._context.domainContextSelectors.getDomain().getEntity(e,t)?this._get$Entity(e,t):null;_recipeContext=()=>({componentReader:this._context.domainContextSelectors.getComponentReader(),recipeCooker:this._context.domainContextSelectors.getRecipeCookerSystem(),resolveLocalizable:this._context.domainServicesSelectors.getResolveLocalizable()});_rootValue={root:(e,{state:t})=>this._get$Entity(t.rootEntityId,t),entities:({ids:t},{state:i})=>{if(null==t)return i.entities.map(e=>this._get$Entity(e.id,i));var o=i.rootEntityId;let a=t.filter(e=>!f.test(e));if(a.length>0)throw new e(`${a.map(e=>`"${e}"`).join(", ")} ${1===a.length?"is not an entity id":"are not entity ids"}. Entity ids have the form "<Type>-<number>", e.g. "${o}". Use ids as returned by the "id" field.`);return t.map(e=>f.test(e)?this._find$Entity(e,i):null)},selectedEntity:(e,{state:t})=>this._find$Entity(this._context.domainContextSelectors.getNavigationSystem().getActiveSelectionState(t),t),navigation:(e,{state:t})=>{let i=this._context.domainContextSelectors.getNavigationSystem();return{navigationState:i.getActiveNavigationState(t),viewState:i.getActiveViewState(t),toolState:i.getActiveToolState(t)}}};_resolveType=e=>y(e)?e.type:void 0;initialize=()=>{};_resolveMutation=(t,i,o,a)=>{if(void 0!==a.failedMutationField)throw new e(`Not run: the earlier field "${a.failedMutationField}" of this operation failed, so "${i}" was skipped and no change of the operation takes effect. Fix the failing field and send the operation again.`);try{return this._runMutation(t,o,a)}catch(e){throw a.failedMutationField=i,e}};_runMutation=(t,i,o)=>{let a=d(t,o.manifest);if(!a)throw new e(`The schema declares the mutation "${t}", but the configurator has no implementation for it. Regenerate the application so the schema matches the configurator.`);let n=o.state,r=a.implementation(i,n,{manifest:o.manifest,action:this._context.domainContextSelectors.getActionSystem().createContext(n),resolveLocalizable:this._context.domainServicesSelectors.getResolveLocalizable()});if(r.updates.length>0){let i=this._context.domainContextSelectors.getStoreSystem().prepareStateEmission(r.updates,n,"committed");if(!i.validity.valid)throw new e(`The changes of "${t}" were not applied, because the resulting configuration is invalid: ${(e=>{if("uncaught"===e.severity)return`validating it threw: ${S(v(e.error))}`;let t=[...new Set(e.entityViolations.filter(({violation:t})=>t.severity===e.severity).map(({entityId:e})=>e))];return`it has ${e.severity} violations on ${t.join(", ")}.`})(i.validity)} Change the request so the configuration stays valid.`);o.emissions.push(i),o.state=i.state}return r.readResult({previousState:n,state:o.state})};_setup=()=>{let t=this._context.specSelectors.getAssistant();if(!t)return{ready:!1,result:p};let i=this._selectSchema(t.sdl);return i instanceof e?{ready:!1,result:{errors:[i]}}:{ready:!0,assistant:t,schema:i,fieldResolver:this._selectFieldResolver(t.fields)}};prepare=async(e,t)=>{let i,o=this._setup();if(!o.ready)return t.map(()=>({result:o.result,emissions:[]}));let n={manifest:o.assistant,state:e,emissions:[],failedMutationField:void 0},r=[];for(let[e,s]of t.entries())if(void 0===i){let t=n.emissions.length;n.failedMutationField=void 0;let l=await a({schema:o.schema,source:s.document,rootValue:this._rootValue,contextValue:n,variableValues:s.variables,operationName:s.operationName,fieldResolver:o.fieldResolver,typeResolver:this._resolveType});r.push({result:l,emissions:n.emissions.slice(t)}),E(l)&&(i=e)}else r.push({result:b(i),emissions:[]});return r};_prepareAndCommit=async e=>{let t,i,o,a=this._context.domainContextSelectors.getStoreSystem(),n=await this.prepare(a.stateSlices.committed.value.state,e),s=n.map(({result:e})=>e),l=n.flatMap(e=>e.emissions);return s.some(E)||0===l.length?{results:s,outcome:"none"}:{results:s,outcome:a.commitPrepared((t=r(l[0]),i=r(l[l.length-1]),o=l.some(e=>void 0!==e.transformationLog),{kind:"committed",previousState:t.previousState,state:i.state,updates:l.flatMap(e=>e.updates),validity:i.validity,transformationLog:o?l.flatMap(e=>e.transformationLog??[]):void 0}))}};execute=async e=>{if(!this._context.domainContextSelectors.getStoreSystem().isInitialized)return{results:e.map(()=>u),committed:!1};let t=await this._prepareAndCommit(e),{results:i,outcome:o}="conflict"===t.outcome?await this._prepareAndCommit(e):t;return"committed"===o?{results:i,committed:!0}:"held"===o?{results:i,committed:!1,message:"The changes were not committed yet: the application held them, usually to ask the user for approval. They may still be applied once the user approves, and the next turn shows whether they were. Do not send the operations again."}:"conflict"===o?{results:i,committed:!1,message:"The changes were not committed, because the configuration changed while they were being applied. The results describe a state that was discarded. Query the current configuration and send the operations again if they still make sense."}:{results:i,committed:!1}}}
1
+ import{GraphQLError as e,buildSchema as t,defaultFieldResolver as i,getNamedType as o,graphql as a,validateSchema as n}from"graphql";import{assertExistence as r,createSelector as s,isMessageDescriptor as l,isMultilingualString as c}from"@salsita-ai/common/utils";import{DomainEntityIdSchema as h,findAssistantMutation as d}from"#package/domain";import{createEntityMeta as m}from"./entity-meta.js";import{readSideEffects as p}from"./side-effects.js";let f={errors:[new e('No assistant manifest in the application spec. The GraphQL schema is generated from the "assistant" section. Add an "assistant" section to a spec file to expose a schema.')]},u={errors:[new e("The configuration is not loaded yet, so there is no state to query. Run the query again once the configurator has finished loading.")]},y=new RegExp(h.pattern),g=e=>"object"==typeof e&&null!==e&&"type"in e&&"string"==typeof e.type&&"id"in e&&"string"==typeof e.id,v=(e,t)=>"object"==typeof e&&null!==e?Reflect.get(e,t):void 0,S=e=>e instanceof Error?e.message:String(e),x=e=>/[.!?]$/.test(e)?e:`${e}.`,_=e=>"object"==typeof e&&null!==e&&Object.getPrototypeOf(e)===Object.prototype,w=(e,t)=>{let i=_(e)?e._raw:void 0;if(g(i))return i;if(!Array.isArray(e)&&!_(e))return e;if(t.has(e))throw Error("The value contains itself. Return ids or copies of the values instead of objects that reference each other.");let o=new Set([...t,e]);return Array.isArray(e)?e.map(e=>w(e,o)):Object.fromEntries(Object.entries(e).map(([e,t])=>[e,w(t,o)]))};export const localize=(e,t)=>Array.isArray(e)?e.map(e=>localize(e,t)):"string"==typeof e||l(e)||c(e)?t(e):e;let E=t=>new e(`The GraphQL schema generated from the "assistant" manifest is invalid: ${x(t)} Regenerate the application so the schema is rebuilt from the current spec.`),$=e=>(e.errors?.length??0)>0,b=t=>({errors:[new e(`Not run: operation ${t+1} failed, so the operations after it were skipped and no change of the list takes effect. Fix the failing operation and send the whole list again.`)]});export class GraphQLSystem{_context;_selectSchema=s([s.leaf(e=>e)],e=>{try{let i=t(e),o=n(i);return o.length>0?E(o.map(e=>e.message).join(" ")):i}catch(e){return E(S(e))}});_selectFieldResolver=s([s.leaf(e=>e)],t=>(a,n,r,s)=>{if(s.parentType===s.schema.getQueryType())return i(a,n,r,s);if(s.parentType===s.schema.getMutationType())return this._resolveMutation(s.fieldName,String(s.path.key),n,r);if(!g(a))return i(a,n,r,s);if("meta"===s.fieldName){let e;return m({entity:a,state:("object"==typeof(e=v(a,"_state"))&&null!==e&&"entities"in e&&"rootEntityId"in e?e:void 0)??r.state},{manifest:r.manifest,recipes:this._recipeContext(),prepareStateEmission:(e,t,i)=>this._context.domainContextSelectors.getStoreSystem().prepareStateEmission(e,t,i),find$Entity:(e,t)=>this._find$Entity(e,t),validateEntity:e=>this._context.domainContextSelectors.getDomain().validateEntity(e)})}let{source:l,localized:c}=t[s.parentType.name]?.[s.fieldName]??{source:s.fieldName},h=((t,i)=>{try{return v(t,i)}catch(o){throw new e(`Expression "${i}" failed on ${g(t)?t.id:"an entity"}: ${x(S(o))} The expression threw while the query read it; fix the expression or the configuration state it reads.`,{originalError:o instanceof Error?o:void 0})}})(a,l);return c?localize(h,this._context.domainServicesSelectors.getResolveLocalizable()):l.startsWith("$")&&"JSON"===o(s.returnType).name?((t,i,o)=>{try{return w(o,new Set)}catch(o){throw new e(`Expression "${i}" returned a value that is not plain serializable data on ${g(t)?t.id:"an entity"}: ${x(S(o))} A JSON field can only return data that serializes to JSON.`,{originalError:o instanceof Error?o:void 0})}})(a,l,h):h});constructor(e){this._context=e}_get$Entity=(e,t)=>this._context.domainContextSelectors.getDomain().get$Entity(e,t);_find$Entity=(e,t)=>this._context.domainContextSelectors.getDomain().getEntity(e,t)?this._get$Entity(e,t):null;_recipeContext=()=>({componentReader:this._context.domainContextSelectors.getComponentReader(),recipeCooker:this._context.domainContextSelectors.getRecipeCookerSystem(),resolveLocalizable:this._context.domainServicesSelectors.getResolveLocalizable()});_rootValue={root:(e,{state:t})=>this._get$Entity(t.rootEntityId,t),entities:({ids:t},{state:i})=>{if(null==t)return i.entities.map(e=>this._get$Entity(e.id,i));var o=i.rootEntityId;let a=t.filter(e=>!y.test(e));if(a.length>0)throw new e(`${a.map(e=>`"${e}"`).join(", ")} ${1===a.length?"is not an entity id":"are not entity ids"}. Entity ids have the form "<Type>-<number>", e.g. "${o}". Use ids as returned by the "id" field.`);return t.map(e=>y.test(e)?this._find$Entity(e,i):null)},selectedEntity:(e,{state:t})=>this._find$Entity(this._context.domainContextSelectors.getNavigationSystem().getActiveSelectionState(t),t),navigation:(e,{state:t})=>{let i=this._context.domainContextSelectors.getNavigationSystem();return{navigationState:i.getActiveNavigationState(t),viewState:i.getActiveViewState(t),toolState:i.getActiveToolState(t)}}};_resolveType=e=>g(e)?e.type:void 0;initialize=()=>{};_resolveMutation=(t,i,o,a)=>{if(void 0!==a.failedMutationField)throw new e(`Not run: the earlier field "${a.failedMutationField}" of this operation failed, so "${i}" was skipped and no change of the operation takes effect. Fix the failing field and send the operation again.`);try{return this._runMutation(t,o,a)}catch(e){throw a.failedMutationField=i,e}};_runMutation=(t,i,o)=>{let a=d(t,o.manifest);if(!a)throw new e(`The schema declares the mutation "${t}", but the configurator has no implementation for it. Regenerate the application so the schema matches the configurator.`);let n=o.state,r=a.implementation(i,n,{manifest:o.manifest,action:this._context.domainContextSelectors.getActionSystem().createContext(n),resolveLocalizable:this._context.domainServicesSelectors.getResolveLocalizable()});if(r.updates.length>0){let i=this._context.domainContextSelectors.getStoreSystem().prepareStateEmission(r.updates,n,"committed");if(!i.validity.valid)throw new e(`The changes of "${t}" were not applied, because the resulting configuration is invalid: ${(e=>{if("uncaught"===e.severity)return`validating it threw: ${x(S(e.error))}`;let t=[...new Set(e.entityViolations.filter(({violation:t})=>t.severity===e.severity).map(({entityId:e})=>e))];return`it has ${e.severity} violations on ${t.join(", ")}.`})(i.validity)} Change the request so the configuration stays valid.`);o.emissions.push(i),o.state=i.state}return r.readResult({previousState:n,state:o.state})};_setup=()=>{let t=this._context.specSelectors.getAssistant();if(!t)return{ready:!1,result:f};let i=this._selectSchema(t.sdl);return i instanceof e?{ready:!1,result:{errors:[i]}}:{ready:!0,assistant:t,schema:i,fieldResolver:this._selectFieldResolver(t.fields)}};prepare=async(e,t)=>{let i,o=this._setup();if(!o.ready)return t.map(()=>({result:o.result,emissions:[]}));let n={manifest:o.assistant,state:e,emissions:[],failedMutationField:void 0},r=[];for(let[e,s]of t.entries())if(void 0===i){let t=n.emissions.length;n.failedMutationField=void 0;let l=await a({schema:o.schema,source:s.document,rootValue:this._rootValue,contextValue:n,variableValues:s.variables,operationName:s.operationName,fieldResolver:o.fieldResolver,typeResolver:this._resolveType});r.push({result:l,emissions:n.emissions.slice(t)}),$(l)&&(i=e)}else r.push({result:b(i),emissions:[]});return r};readSideEffects=e=>{let t=this._context.domainServicesSelectors.getResolveLocalizable();return e.flatMap(({emissions:e})=>e.flatMap(e=>p(e.updates,t)))};_prepareAndCommit=async e=>{let t,i,o,a=this._context.domainContextSelectors.getStoreSystem(),n=await this.prepare(a.stateSlices.committed.value.state,e),s=n.map(({result:e})=>e),l=this.readSideEffects(n),c=n.flatMap(e=>e.emissions);return s.some($)||0===c.length?{results:s,sideEffects:l,outcome:"none"}:{results:s,sideEffects:l,outcome:a.commitPrepared((t=r(c[0]),i=r(c[c.length-1]),o=c.some(e=>void 0!==e.transformationLog),{kind:"committed",previousState:t.previousState,state:i.state,updates:c.flatMap(e=>e.updates),validity:i.validity,transformationLog:o?c.flatMap(e=>e.transformationLog??[]):void 0}))}};execute=async e=>{if(!this._context.domainContextSelectors.getStoreSystem().isInitialized)return{results:e.map(()=>u),committed:!1,sideEffects:[]};let t=await this._prepareAndCommit(e),{results:i,sideEffects:o,outcome:a}="conflict"===t.outcome?await this._prepareAndCommit(e):t;return"committed"===a?{results:i,committed:!0,sideEffects:o}:"held"===a?{results:i,committed:!1,message:"The changes were not committed yet: the application held them, usually to ask the user for approval. They may still be applied once the user approves, and the next turn shows whether they were. Do not send the operations again.",sideEffects:o}:"conflict"===a?{results:i,committed:!1,message:"The changes were not committed, because the configuration changed while they were being applied. The results describe a state that was discarded. Query the current configuration and send the operations again if they still make sense.",sideEffects:o}:{results:i,committed:!1,sideEffects:o}}}
@@ -0,0 +1,9 @@
1
+ import type { Localizable } from '@salsita-ai/common/utils';
2
+ import type { DataUpdate, DomainEntity } from '#package/domain';
3
+ /**
4
+ * What the updates of an emission tell the assistant agent the configurator did beyond what was
5
+ * asked, in applied order with duplicates kept, the way the user gets them as snackbars. An update's
6
+ * `agentHint` replaces its user-facing `hint` for the agent, `null` keeps the update from the agent,
7
+ * and an update with neither says nothing. Empty text counts as none, as it does for the snackbar.
8
+ */
9
+ export declare const readSideEffects: (updates: ReadonlyArray<DataUpdate<DomainEntity<string>, DomainEntity<string>>>, resolveLocalizable: (message: Localizable) => string) => readonly string[];
@@ -0,0 +1 @@
1
+ export const readSideEffects=(e,t)=>e.flatMap(({hint:e,agentHint:a})=>null===a?[]:void 0!==a?a?[a]:[]:e?[t(e)]:[]);
@@ -1,5 +1,5 @@
1
1
  export { AnimationSystem, type AnimationSystemContext } from './animation-system.js';
2
- export { type AssistantClientTool, AssistantSystem, type AssistantSystemContext, } from './assistant-system/assistant-system.js';
2
+ export { type AssistantChatSnapshot, type AssistantClientTool, type AssistantPanelSnapshot, AssistantSystem, type AssistantSystemContext, } from './assistant-system/assistant-system.js';
3
3
  export { CameraControlSystem, CameraControlType } from './camera-control-system/camera-control-system.js';
4
4
  export { type AnyCameraSystem, CameraSystem, type CameraSystemContext } from './camera-control-system/camera-system.js';
5
5
  export { createCameraControls } from './camera-control-system/create-camera-controls.js';
@@ -25,6 +25,8 @@ export declare class LayoutSystem<ENTITIES extends DomainEntity<string>> {
25
25
  * whatever was true when it was created.
26
26
  */
27
27
  private get _toolbarHelpExists();
28
+ /** Whether the application spec has an `assistant` section; read on demand for the same reason. */
29
+ private get _hasAssistant();
28
30
  initialize: () => void;
29
31
  getCleanUpPropertyPageStateUpdates: (entityId: DomainEntityId<string>, state: DomainState<ENTITIES>) => import("#package/domain").ComponentModification<import("#package/domain").SystemComponentType<"layoutStateComponent", import("./layout-state.js").LayoutStateComponentState, "propertyPageStates">>[];
30
32
  getLayoutConfig: (state: DomainState<ENTITIES>) => {
@@ -1 +1 @@
1
- import{P as t,match as e}from"ts-pattern";import{SystemState as o,applicationConfigComponent as n,customSummaryPageComponent as a,entityHeaderComponent as i,navigationStepsComponent as r,propertyPageComponent as s,sidebarTabsComponent as l,simpleSummaryComponent as d,toolbarComponent as c}from"#package/domain";import{layoutStateComponent as g}from"./layout-state.js";import{toolbarHelpElementName as u}from"./toolbar-help-element.js";export class LayoutSystem{_context;_toolbarCache=new WeakMap;state;constructor(t){this._context=t,this.state=new o({...this._context,selectors:this._context.domainContextSelectors,componentType:g,getInitialState:()=>({propertyPageStates:{}})})}get _toolbarHelpExists(){return void 0!==this._context.specSelectors.getElementSlots().layoutElements[u]}initialize=()=>{this.state.initialize()};getCleanUpPropertyPageStateUpdates=(t,e)=>this.state.getUpdates(e=>{if(!e.propertyPageStates[t])return null;let o={...e.propertyPageStates};return delete o[t],{...e,propertyPageStates:o}},e);getLayoutConfig=t=>{let e=this._context.domainContextSelectors.getComponentReader().readFirstRootComponent({componentTypes:[n]},t),o=e?.state.shareDisabled??!1,a=o||(e?.state.shareOwnerLinkDisabled??!1),i=o||(e?.state.shareReadRevisionLinkDisabled??!1);return{layout:e?.state.layout??"universal",ignoreSelectionInSidebar:e?.state.ignoreSelectionInSidebar??!1,miniSidebar:e?.state.miniSidebar??!1,splitSidebar:e?.state.splitSidebar??!1,showSelectionBackButton:e?.state.showSelectionBackButton??!0,shareDisabled:a&&i,availableLocales:e?.state.availableLocales??["en"],indicatorSwitch:e?.state.indicatorSwitch??!0}};getSidebarSummaryPage=t=>{let e=this._context.domainContextSelectors.getNavigationSystem().getActiveNavigationState(t),o=this._context.domainContextSelectors.getComponentReader().readFirstRootComponent({componentTypes:[a,d]},t),n=e===o?.state.summaryNavigationState,i=o?.state.summaryNavigationState;return{page:o,isSummaryActive:n,summaryNavigationState:i}};getEnterSidebarSummaryUpdates=t=>{let{summaryNavigationState:e}=this.getSidebarSummaryPage(t);return e?[this._context.domainContextSelectors.getNavigationSystem().getPushNavigationUpdate(e,t)]:[]};getLeaveSidebarSummaryUpdates=t=>{let e=this._context.domainContextSelectors.getNavigationSystem().getPopNavigationUpdate(t);if(e)return[e];let o=this._context.domainContextSelectors.getNavigationStates().at(0);return o?(this._context.domainServicesSelectors.getLogger().error("There is no navigation state to pop, falling back to the initial navigation state. Check whether the history stack is not broken by some custom navigation logic."),[this._context.domainContextSelectors.getNavigationSystem().getNavigationUpdate(o)]):(this._context.domainServicesSelectors.getLogger().error("There is no navigation state to pop and no initial navigation state to fallback to, this should not happen"),[])};getPushSidebarStackPageUpdates=(t,e,o,n)=>this.state.getUpdates(n=>{let a=n.propertyPageStates[t]??{customState:{},stackPages:[]};return{...n,propertyPageStates:{...n.propertyPageStates,[t]:{...a,stackPages:[...a.stackPages,{props:o,component:e}]}}}},n);getPopSidebarStackPageUpdates=(t,e)=>this.state.getUpdates(e=>{let o=e.propertyPageStates[t]??{customState:{},stackPages:[]};return{...e,propertyPageStates:{...e.propertyPageStates,[t]:{...o,stackPages:o.stackPages.slice(0,-1)}}}},e);getSidebarBackUpdates=t=>{let e=this._context.domainContextSelectors.getNavigationSystem().getHasStackToPop(t),o=this._context.domainContextSelectors.getNavigationSystem().getIsRootSelected(t),{renderedStackedPage:n,summaryPage:a,activePropertyPageId:i}=this.getSidebarContent(t);if(a)return this.getLeaveSidebarSummaryUpdates(t);if(n&&i)return this.getPopSidebarStackPageUpdates(i,t);if(e){let e=this._context.domainContextSelectors.getNavigationSystem().getPopNavigationUpdate(t);return e?[e]:[]}return o?[]:[this._context.domainContextSelectors.getNavigationSystem().getDeselectUpdate()]};getSidebarContent=t=>{let e=this._context.domainContextSelectors.getNavigationSystem().getActiveSelectionState(t),o=this.state.get(t),n=this.getLayoutConfig(t),a=this._context.domainContextSelectors.getComponentReader().readManyComponents({componentTypes:[l],entityIds:[e]},t).some(t=>t.state.tabs.length>0),r=n.miniSidebar&&!a;r&&a&&this._context.domainServicesSelectors.getLogger().warn("Mini sidebar is not active because the layout contains tabs. Only layouts without tabs can have mini sidebar.");let d=this._context.domainContextSelectors.getComponentReader().readManyComponents({componentTypes:[s]},t),{isSummaryActive:c,page:g}=this.getSidebarSummaryPage(t),u=this._context.domainContextSelectors.getComponentReader().readManyComponents({componentTypes:[i]},t),p=this._context.domainContextSelectors.getCompositionProjections().getTree(t).map.get(e);if(!p)throw Error(`Selected entity ${e} not found in composition tree`);let m=this._context.domainContextSelectors.getCompositionProjections().getTree(t).map.get(t.rootEntityId);if(!m)throw Error(`Root entity ${t.rootEntityId} not found in composition tree`);let S=(n.ignoreSelectionInSidebar?[m]:[p,...p.ancestors()]).map(t=>{let e=n.ignoreSelectionInSidebar?d:d.filter(e=>e.componentInstance.entityId===t.id),o=u.filter(e=>e.componentInstance.entityId===t.id);return{id:t.id,propertyPages:e,header:o[0]??null}}),h=S[0]?.id,b=(h?o.propertyPageStates[h]?.stackPages??[]:[]).at(-1)??null,k=this._context.domainContextSelectors.getNavigationSystem().getHasStackToPop(t),x=this._context.domainContextSelectors.getNavigationSystem().getIsRootSelected(t),y=c&&k||k||!!b||!x&&n.showSelectionBackButton&&!n.ignoreSelectionInSidebar;return c?{summaryPage:g,propertyPages:[],isBackAvailable:y,hasMiniSidebar:r,hasSplitSidebar:n.splitSidebar}:{summaryPage:null,propertyPages:S,renderedStackedPage:b,activePropertyPageId:h,isBackAvailable:y,hasMiniSidebar:r,hasSplitSidebar:n.splitSidebar}};getToolbarState=o=>{let n=this._toolbarHelpExists,a=this._toolbarCache.get(o);if(a&&a.toolbarHelpExists===n)return a.result;let i=this._context.domainContextSelectors.getExpressionContext(),r=this._context.domainContextSelectors.getNavigationSystem().getActiveFullscreenState(o),s="read"===i.mode,l=this._context.domainContextSelectors.getComponentReader().readFirstRootComponent({componentTypes:[c]},o),d=this._context.domainContextSelectors.getDimensionSystem().getDimensionConfig(o),g=this.getLayoutConfig(o),u="read"===this._context.domainContextSelectors.getExpressionContext().mode,p=g.availableLocales.length>1,m=!u&&!g.shareDisabled,S=g.indicatorSwitch,h=d.availableDimensions.length>1||S,b=e(l).returnType().with(t.nonNullable,t=>t.state).with(t.nullish,()=>e([r,s]).returnType().with([!1,!1],()=>({desktop:[{kind:"toolbarButtonGroup",children:[{kind:"enterFullscreenToolbarButton"}]},{kind:"toolbarButtonGroup",children:[{kind:"resetCameraToolbarButton"}]},...p?[{kind:"toolbarButtonGroup",children:[{kind:"languageToolbarButton"}]}]:[],...m||h?[{kind:"toolbarButtonGroup",children:[...m?[{kind:"shareToolbarButton"}]:[],...h?[{kind:"measurementsToolbarButton"}]:[],...n?[{kind:"helpToolbarButton"}]:[]]}]:[],{kind:"toolbarButtonGroup",children:[{kind:"undoToolbarButton"},{kind:"redoToolbarButton"}]}],mobile:[{kind:"toolbarButtonGroup",children:[{kind:"undoToolbarButton"},{kind:"redoToolbarButton"}]},{kind:"toolbarButtonGroup",children:[{kind:"resetCameraToolbarButton"}]},{kind:"toolbarButtonGroup",children:[{kind:"enterFullscreenToolbarButton"}]}],mobileMenu:[...m?[{kind:"shareMenuAction"}]:[],...h?[{kind:"measurementsMenuAction"}]:[],...p?[{kind:"languageMenuAction"}]:[],...n?[{kind:"helpMenuAction"}]:[]]})).with([!0,!1],()=>({desktop:[{kind:"toolbarButtonGroup",children:[{kind:"exitFullscreenToolbarButton"}]}],mobile:[{kind:"toolbarButtonGroup",children:[{kind:"exitFullscreenToolbarButton"}]}],mobileMenu:[]})).with([!1,!0],()=>({desktop:[{kind:"toolbarButtonGroup",children:[{kind:"enterFullscreenToolbarButton"}]},{kind:"toolbarButtonGroup",children:[{kind:"resetCameraToolbarButton"}]},...p?[{kind:"toolbarButtonGroup",children:[{kind:"languageToolbarButton"}]}]:[],...h?[{kind:"toolbarButtonGroup",children:[...h?[{kind:"measurementsToolbarButton"}]:[]]}]:[],...n?[{kind:"helpToolbarButton"}]:[]],mobile:[{kind:"toolbarButtonGroup",children:[{kind:"resetCameraToolbarButton"}]},{kind:"toolbarButtonGroup",children:[{kind:"enterFullscreenToolbarButton"}]}],mobileMenu:[...h?[{kind:"measurementsMenuAction"}]:[],...p?[{kind:"languageMenuAction"}]:[],...n?[{kind:"helpMenuAction"}]:[]]})).with([!0,!0],()=>({desktop:[{kind:"toolbarButtonGroup",children:[{kind:"exitFullscreenToolbarButton"}]}],mobile:[{kind:"toolbarButtonGroup",children:[{kind:"exitFullscreenToolbarButton"}]}],mobileMenu:[]})).exhaustive()).exhaustive();return this._toolbarCache.set(o,{toolbarHelpExists:n,result:b}),b};getNavigationSteps=t=>{let e=this._context.domainContextSelectors.getComponentReader().readFirstRootComponent({componentTypes:[r]},t);return e?.state.steps??[]}}
1
+ import{P as t,match as e}from"ts-pattern";import{SystemState as o,applicationConfigComponent as n,customSummaryPageComponent as a,entityHeaderComponent as i,navigationStepsComponent as r,propertyPageComponent as s,sidebarTabsComponent as l,simpleSummaryComponent as d,toolbarComponent as c}from"#package/domain";import{layoutStateComponent as g}from"./layout-state.js";import{toolbarHelpElementName as u}from"./toolbar-help-element.js";export class LayoutSystem{_context;_toolbarCache=new WeakMap;state;constructor(t){this._context=t,this.state=new o({...this._context,selectors:this._context.domainContextSelectors,componentType:g,getInitialState:()=>({propertyPageStates:{}})})}get _toolbarHelpExists(){return void 0!==this._context.specSelectors.getElementSlots().layoutElements[u]}get _hasAssistant(){return void 0!==this._context.specSelectors.getAssistant()}initialize=()=>{this.state.initialize()};getCleanUpPropertyPageStateUpdates=(t,e)=>this.state.getUpdates(e=>{if(!e.propertyPageStates[t])return null;let o={...e.propertyPageStates};return delete o[t],{...e,propertyPageStates:o}},e);getLayoutConfig=t=>{let e=this._context.domainContextSelectors.getComponentReader().readFirstRootComponent({componentTypes:[n]},t),o=e?.state.shareDisabled??!1,a=o||(e?.state.shareOwnerLinkDisabled??!1),i=o||(e?.state.shareReadRevisionLinkDisabled??!1);return{layout:e?.state.layout??"universal",ignoreSelectionInSidebar:e?.state.ignoreSelectionInSidebar??!1,miniSidebar:e?.state.miniSidebar??!1,splitSidebar:e?.state.splitSidebar??!1,showSelectionBackButton:e?.state.showSelectionBackButton??!0,shareDisabled:a&&i,availableLocales:e?.state.availableLocales??["en"],indicatorSwitch:e?.state.indicatorSwitch??!0}};getSidebarSummaryPage=t=>{let e=this._context.domainContextSelectors.getNavigationSystem().getActiveNavigationState(t),o=this._context.domainContextSelectors.getComponentReader().readFirstRootComponent({componentTypes:[a,d]},t),n=e===o?.state.summaryNavigationState,i=o?.state.summaryNavigationState;return{page:o,isSummaryActive:n,summaryNavigationState:i}};getEnterSidebarSummaryUpdates=t=>{let{summaryNavigationState:e}=this.getSidebarSummaryPage(t);return e?[this._context.domainContextSelectors.getNavigationSystem().getPushNavigationUpdate(e,t)]:[]};getLeaveSidebarSummaryUpdates=t=>{let e=this._context.domainContextSelectors.getNavigationSystem().getPopNavigationUpdate(t);if(e)return[e];let o=this._context.domainContextSelectors.getNavigationStates().at(0);return o?(this._context.domainServicesSelectors.getLogger().error("There is no navigation state to pop, falling back to the initial navigation state. Check whether the history stack is not broken by some custom navigation logic."),[this._context.domainContextSelectors.getNavigationSystem().getNavigationUpdate(o)]):(this._context.domainServicesSelectors.getLogger().error("There is no navigation state to pop and no initial navigation state to fallback to, this should not happen"),[])};getPushSidebarStackPageUpdates=(t,e,o,n)=>this.state.getUpdates(n=>{let a=n.propertyPageStates[t]??{customState:{},stackPages:[]};return{...n,propertyPageStates:{...n.propertyPageStates,[t]:{...a,stackPages:[...a.stackPages,{props:o,component:e}]}}}},n);getPopSidebarStackPageUpdates=(t,e)=>this.state.getUpdates(e=>{let o=e.propertyPageStates[t]??{customState:{},stackPages:[]};return{...e,propertyPageStates:{...e.propertyPageStates,[t]:{...o,stackPages:o.stackPages.slice(0,-1)}}}},e);getSidebarBackUpdates=t=>{let e=this._context.domainContextSelectors.getNavigationSystem().getHasStackToPop(t),o=this._context.domainContextSelectors.getNavigationSystem().getIsRootSelected(t),{renderedStackedPage:n,summaryPage:a,activePropertyPageId:i}=this.getSidebarContent(t);if(a)return this.getLeaveSidebarSummaryUpdates(t);if(n&&i)return this.getPopSidebarStackPageUpdates(i,t);if(e){let e=this._context.domainContextSelectors.getNavigationSystem().getPopNavigationUpdate(t);return e?[e]:[]}return o?[]:[this._context.domainContextSelectors.getNavigationSystem().getDeselectUpdate()]};getSidebarContent=t=>{let e=this._context.domainContextSelectors.getNavigationSystem().getActiveSelectionState(t),o=this.state.get(t),n=this.getLayoutConfig(t),a=this._context.domainContextSelectors.getComponentReader().readManyComponents({componentTypes:[l],entityIds:[e]},t).some(t=>t.state.tabs.length>0),r=n.miniSidebar&&!a;r&&a&&this._context.domainServicesSelectors.getLogger().warn("Mini sidebar is not active because the layout contains tabs. Only layouts without tabs can have mini sidebar.");let d=this._context.domainContextSelectors.getComponentReader().readManyComponents({componentTypes:[s]},t),{isSummaryActive:c,page:g}=this.getSidebarSummaryPage(t),u=this._context.domainContextSelectors.getComponentReader().readManyComponents({componentTypes:[i]},t),p=this._context.domainContextSelectors.getCompositionProjections().getTree(t).map.get(e);if(!p)throw Error(`Selected entity ${e} not found in composition tree`);let m=this._context.domainContextSelectors.getCompositionProjections().getTree(t).map.get(t.rootEntityId);if(!m)throw Error(`Root entity ${t.rootEntityId} not found in composition tree`);let h=(n.ignoreSelectionInSidebar?[m]:[p,...p.ancestors()]).map(t=>{let e=n.ignoreSelectionInSidebar?d:d.filter(e=>e.componentInstance.entityId===t.id),o=u.filter(e=>e.componentInstance.entityId===t.id);return{id:t.id,propertyPages:e,header:o[0]??null}}),S=h[0]?.id,b=(S?o.propertyPageStates[S]?.stackPages??[]:[]).at(-1)??null,k=this._context.domainContextSelectors.getNavigationSystem().getHasStackToPop(t),x=this._context.domainContextSelectors.getNavigationSystem().getIsRootSelected(t),y=c&&k||k||!!b||!x&&n.showSelectionBackButton&&!n.ignoreSelectionInSidebar;return c?{summaryPage:g,propertyPages:[],isBackAvailable:y,hasMiniSidebar:r,hasSplitSidebar:n.splitSidebar}:{summaryPage:null,propertyPages:h,renderedStackedPage:b,activePropertyPageId:S,isBackAvailable:y,hasMiniSidebar:r,hasSplitSidebar:n.splitSidebar}};getToolbarState=o=>{let n=this._toolbarHelpExists,a=this._hasAssistant,i=this._toolbarCache.get(o);if(i&&i.toolbarHelpExists===n&&i.hasAssistant===a)return i.result;let r=this._context.domainContextSelectors.getExpressionContext(),s=this._context.domainContextSelectors.getNavigationSystem().getActiveFullscreenState(o),l="read"===r.mode,d=this._context.domainContextSelectors.getComponentReader().readFirstRootComponent({componentTypes:[c]},o),g=this._context.domainContextSelectors.getDimensionSystem().getDimensionConfig(o),u=this.getLayoutConfig(o),p="read"===this._context.domainContextSelectors.getExpressionContext().mode,m=u.availableLocales.length>1,h=!p&&!u.shareDisabled,S=u.indicatorSwitch,b=g.availableDimensions.length>1||S,k=a?[{kind:"toolbarButtonGroup",children:[{kind:"assistantToolbarButton"}]}]:[],x=e(d).returnType().with(t.nonNullable,t=>t.state).with(t.nullish,()=>e([s,l]).returnType().with([!1,!1],()=>({desktop:[{kind:"toolbarButtonGroup",children:[{kind:"enterFullscreenToolbarButton"}]},{kind:"toolbarButtonGroup",children:[{kind:"resetCameraToolbarButton"}]},...m?[{kind:"toolbarButtonGroup",children:[{kind:"languageToolbarButton"}]}]:[],...h||b?[{kind:"toolbarButtonGroup",children:[...h?[{kind:"shareToolbarButton"}]:[],...b?[{kind:"measurementsToolbarButton"}]:[],...n?[{kind:"helpToolbarButton"}]:[]]}]:[],{kind:"toolbarButtonGroup",children:[{kind:"undoToolbarButton"},{kind:"redoToolbarButton"}]},...k],mobile:[{kind:"toolbarButtonGroup",children:[{kind:"undoToolbarButton"},{kind:"redoToolbarButton"}]},{kind:"toolbarButtonGroup",children:[{kind:"resetCameraToolbarButton"}]},{kind:"toolbarButtonGroup",children:[{kind:"enterFullscreenToolbarButton"}]},...k],mobileMenu:[...h?[{kind:"shareMenuAction"}]:[],...b?[{kind:"measurementsMenuAction"}]:[],...m?[{kind:"languageMenuAction"}]:[],...n?[{kind:"helpMenuAction"}]:[]]})).with([!0,!1],()=>({desktop:[{kind:"toolbarButtonGroup",children:[{kind:"exitFullscreenToolbarButton"}]}],mobile:[{kind:"toolbarButtonGroup",children:[{kind:"exitFullscreenToolbarButton"}]}],mobileMenu:[]})).with([!1,!0],()=>({desktop:[{kind:"toolbarButtonGroup",children:[{kind:"enterFullscreenToolbarButton"}]},{kind:"toolbarButtonGroup",children:[{kind:"resetCameraToolbarButton"}]},...m?[{kind:"toolbarButtonGroup",children:[{kind:"languageToolbarButton"}]}]:[],...b?[{kind:"toolbarButtonGroup",children:[...b?[{kind:"measurementsToolbarButton"}]:[]]}]:[],...n?[{kind:"helpToolbarButton"}]:[],...k],mobile:[{kind:"toolbarButtonGroup",children:[{kind:"resetCameraToolbarButton"}]},{kind:"toolbarButtonGroup",children:[{kind:"enterFullscreenToolbarButton"}]},...k],mobileMenu:[...b?[{kind:"measurementsMenuAction"}]:[],...m?[{kind:"languageMenuAction"}]:[],...n?[{kind:"helpMenuAction"}]:[]]})).with([!0,!0],()=>({desktop:[{kind:"toolbarButtonGroup",children:[{kind:"exitFullscreenToolbarButton"}]}],mobile:[{kind:"toolbarButtonGroup",children:[{kind:"exitFullscreenToolbarButton"}]}],mobileMenu:[]})).exhaustive()).exhaustive();return this._toolbarCache.set(o,{toolbarHelpExists:n,hasAssistant:a,result:x}),x};getNavigationSteps=t=>{let e=this._context.domainContextSelectors.getComponentReader().readFirstRootComponent({componentTypes:[r]},t);return e?.state.steps??[]}}
@@ -1,25 +1,25 @@
1
1
  import { type EntityId } from '#package/domain';
2
- declare const CloseConfiguratorClicked: () => import("../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
3
- declare const PreviousStepButtonClicked: () => import("../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
4
- declare const NextStepButtonClicked: () => import("../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
5
- declare const FinalizeButtonClicked: () => import("../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
6
- declare const SummaryButtonClicked: () => import("../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
7
- declare const TopNavigationStepClicked: (step: string) => import("../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
8
- declare const HorizontalTabClicked: (tab: string) => import("../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
9
- declare const GoToProductsClicked: () => import("../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
10
- declare const ExpandedViewClosed: () => import("../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
11
- declare const ProductSelected: (productType: string, productName: string) => import("../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
12
- declare const SelectionHotspotClicked: (id: string) => import("../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
13
- declare const RedoButtonClicked: () => import("../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
14
- declare const UndoButtonClicked: () => import("../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
15
- declare const GlobalDimensionsButtonClicked: (dimensionUnit: string) => import("../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
16
- declare const MeasurementsMenuToggled: () => import("../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
17
- declare const MeasurementsMenuClosed: () => import("../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
18
- declare const DimensionIndicatorsToggled: () => import("../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
19
- declare const FullScreenModeToggled: () => import("../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
20
- declare const HelpDialogOpened: () => import("../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
21
- declare const HelpDialogClosed: () => import("../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
22
- declare const EntityDeleteHotspotClicked: (id: EntityId) => import("../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
2
+ declare const CloseConfiguratorClicked: () => import("#package/domain").UserActionEvent;
3
+ declare const PreviousStepButtonClicked: () => import("#package/domain").UserActionEvent;
4
+ declare const NextStepButtonClicked: () => import("#package/domain").UserActionEvent;
5
+ declare const FinalizeButtonClicked: () => import("#package/domain").UserActionEvent;
6
+ declare const SummaryButtonClicked: () => import("#package/domain").UserActionEvent;
7
+ declare const TopNavigationStepClicked: (step: string) => import("#package/domain").UserActionEvent;
8
+ declare const HorizontalTabClicked: (tab: string) => import("#package/domain").UserActionEvent;
9
+ declare const GoToProductsClicked: () => import("#package/domain").UserActionEvent;
10
+ declare const ExpandedViewClosed: () => import("#package/domain").UserActionEvent;
11
+ declare const ProductSelected: (productType: string, productName: string) => import("#package/domain").UserActionEvent;
12
+ declare const SelectionHotspotClicked: (id: string) => import("#package/domain").UserActionEvent;
13
+ declare const RedoButtonClicked: () => import("#package/domain").UserActionEvent;
14
+ declare const UndoButtonClicked: () => import("#package/domain").UserActionEvent;
15
+ declare const GlobalDimensionsButtonClicked: (dimensionUnit: string) => import("#package/domain").UserActionEvent;
16
+ declare const MeasurementsMenuToggled: () => import("#package/domain").UserActionEvent;
17
+ declare const MeasurementsMenuClosed: () => import("#package/domain").UserActionEvent;
18
+ declare const DimensionIndicatorsToggled: () => import("#package/domain").UserActionEvent;
19
+ declare const FullScreenModeToggled: () => import("#package/domain").UserActionEvent;
20
+ declare const HelpDialogOpened: () => import("#package/domain").UserActionEvent;
21
+ declare const HelpDialogClosed: () => import("#package/domain").UserActionEvent;
22
+ declare const EntityDeleteHotspotClicked: (id: EntityId) => import("#package/domain").UserActionEvent;
23
23
  export declare const FrontendAnalyticsFactory: {
24
24
  CloseConfiguratorClicked: typeof CloseConfiguratorClicked;
25
25
  PreviousStepButtonClicked: typeof PreviousStepButtonClicked;
@@ -1,6 +1,5 @@
1
1
  export { ConfiguratorApp } from './ConfiguratorApp.js';
2
2
  export * from './application-runtime/index.js';
3
- export * from './assistant/index.js';
4
3
  export * from './common/index.js';
5
4
  export * from './configurator-context/index.js';
6
5
  export * from './pages/index.js';
@@ -1 +1 @@
1
- export{ConfiguratorApp}from"./ConfiguratorApp.js";export*from"./application-runtime/index.js";export*from"./assistant/index.js";export*from"./common/index.js";export*from"./configurator-context/index.js";export*from"./pages/index.js";export*from"./scene/index.js";export*from"./ui/index.js";export*from"./integration/index.js";
1
+ export{ConfiguratorApp}from"./ConfiguratorApp.js";export*from"./application-runtime/index.js";export*from"./common/index.js";export*from"./configurator-context/index.js";export*from"./pages/index.js";export*from"./scene/index.js";export*from"./ui/index.js";export*from"./integration/index.js";
@@ -1 +1 @@
1
- import{jsx as o,jsxs as e,Fragment as i}from"react/jsx-runtime";import r from"mui-modal-provider";import{memo as n,useCallback as t,useMemo as a}from"react";import{match as d}from"ts-pattern";import{InvalidAccessKeyError as s}from"#package/client";import{BuiltInLoadingPhase as u,CommonAnalyticsFactory as l,DomainLifecycleBoundary as m,LoadedDataProvider as c,useAccessService as f,useAnalyticsService as p,useAuthorizedClient as g,useConfiguratorDomainFactory as v,useConfiguratorLoadStatus as w,useConfiguratorRawLoadedData as y,useExpectPhase as h,useLoadedDataActions as I,useLoadingError as C,useLoadingPhase as b,useLogger as j,useRoutingService as x}from"#package/react-sdk";import{IntegrationSubscription as k,OnConfiguratorReadyProvider as L,ViewportStateSync as U,useLayoutSystemConfig as M}from"../configurator-context/index.js";import{Layout as S,Legacy as E}from"../ui/index.js";import{LayoutCustomOwnerUi as F,LayoutCustomReadUi as R,LayoutDialog as D}from"../ui/phoenix-layout-elements/index.js";import{ConfiguratorNotFoundPage as T}from"./ConfiguratorNotFoundPage.js";import{DevContextFallback as P}from"./error-boundaries/DevContextFallback.js";import{DevLoadingFallback as A}from"./error-boundaries/DevLoadingFallback.js";import{LoadingErrorBoundary as O}from"./error-boundaries/LoadingErrorBoundary.js";import{useLoad as V}from"./use-load.js";let q=n(n=>{let{viewMode:t}=n,s=M(),{ownerUi:u,readUi:l}=a(()=>d(s.layout).with("simple",()=>({ownerUi:o(E.SimpleOwnerLayout,{}),readUi:o(E.SummaryReadLayout,{})})).with("tabs",()=>({ownerUi:o(E.TabsOwnerLayout,{}),readUi:o(E.SummaryReadLayout,{})})).with("steps",()=>({ownerUi:o(E.StepsOwnerLayout,{}),readUi:o(E.SummaryReadLayout,{})})).with("universal",()=>({ownerUi:o(S,{}),readUi:o(S,{})})).with("custom",()=>({ownerUi:o(F,{}),readUi:o(R,{})})).exhaustive(),[s.layout]);return o(r,{children:e(L,{children:[t?l:u,"universal"===s.layout&&e(i,{children:[o(E.CommonFinalizePage,{}),o(D,{})]})]})})});export const LoadedConfiguratorDetailPage=()=>{h(u.SceneAssets);let{configurationId:i,accessInfo:r}=f(),n=g(),a=x(),d=j(),L=p(),{startConfiguratorLoad:M,completeConfiguratorLoad:S,failConfiguratorLoad:E,resetConfiguratorLoad:F}=I(),{isLoading:R,isError:D}=w(),z=y(),B=v(),G=t(()=>r.viewMode&&void 0!==r.revisionId?n.getConfigurationRevision(i,r.revisionId):n.getConfiguration(i),[r.revisionId,r.viewMode,n,i]),N=t(()=>{M()},[M]),H=t(o=>{S({initialRevisionId:o.revisionId,initialEntities:o.data,modelVersion:o.modelVersion,rootEntityType:o.rootEntityType,schemaIdentity:B}),L.push(l.ConfigurationLoaded(o.configurationId,o.revisionId,r.viewMode?"read":"owner")),d.appendToGlobalContext({configurationId:o.configurationId,revisionId:o.revisionId,mode:r.viewMode}),d.info("Configuration loaded.")},[r.viewMode,L,S,B,d]),J=t(o=>{if(E(o),o instanceof s)return void a.rejectAccessToken(i,r.raw);let e=o instanceof Error?o:Error("Configuration loading failed with an unknown error.",{cause:o});d.error("Configuration loading failed because the API request did not succeed. Verify the configuration ID and access token.",{configurationId:i},e)},[r.raw,i,E,d,a]),K=t(()=>{F()},[F]);return(V({key:[n,i,r.revisionId,r.viewMode],load:G,onStart:N,onSuccess:H,onError:J,onCleanup:K}),b({id:u.DataFetch,label:"Loading configuration…"},R),C(D),D)?o(T,{}):null===z?null:o(c,{value:z,children:o(O,{fallbackRender:e=>o(A,{...e}),children:o(m,{type:"configurator",children:o(O,{FallbackComponent:P,children:e(k,{children:[o(U,{}),o(q,{viewMode:r.viewMode})]})})})})})};
1
+ import{jsx as e,jsxs as o,Fragment as i}from"react/jsx-runtime";import r from"mui-modal-provider";import{memo as n,useCallback as t,useEffect as a,useMemo as d}from"react";import{match as s}from"ts-pattern";import{InvalidAccessKeyError as l}from"#package/client";import{BuiltInLoadingPhase as u,CommonAnalyticsFactory as m,DomainLifecycleBoundary as c,LoadedDataProvider as f,useAccessService as p,useAnalyticsService as g,useAuthorizedClient as v,useConfiguratorDomainFactory as w,useConfiguratorLoadStatus as y,useConfiguratorRawLoadedData as h,useExpectPhase as I,useLoadedDataActions as j,useLoadingError as x,useLoadingPhase as C,useLogger as b,useRoutingService as k,useSpecAssistant as L}from"#package/react-sdk";import{useConfigurator as U}from"../application-runtime/index.js";import{IntegrationSubscription as M,OnConfiguratorReadyProvider as S,ViewportStateSync as E,useLayoutSystemConfig as F}from"../configurator-context/index.js";import{Layout as R,Legacy as D}from"../ui/index.js";import{LayoutCustomOwnerUi as T,LayoutCustomReadUi as P,LayoutDialog as A}from"../ui/phoenix-layout-elements/index.js";import{ConfiguratorNotFoundPage as O}from"./ConfiguratorNotFoundPage.js";import{DevContextFallback as V}from"./error-boundaries/DevContextFallback.js";import{DevLoadingFallback as q}from"./error-boundaries/DevLoadingFallback.js";import{LoadingErrorBoundary as z}from"./error-boundaries/LoadingErrorBoundary.js";import{useLoad as B}from"./use-load.js";let G=n(n=>{let{viewMode:t}=n,l=F();(()=>{let{assistantSystem:e}=U(),o=null!=L();a(()=>{if(o)return e.activate()},[e,o])})();let{ownerUi:u,readUi:m}=d(()=>s(l.layout).with("simple",()=>({ownerUi:e(D.SimpleOwnerLayout,{}),readUi:e(D.SummaryReadLayout,{})})).with("tabs",()=>({ownerUi:e(D.TabsOwnerLayout,{}),readUi:e(D.SummaryReadLayout,{})})).with("steps",()=>({ownerUi:e(D.StepsOwnerLayout,{}),readUi:e(D.SummaryReadLayout,{})})).with("universal",()=>({ownerUi:e(R,{}),readUi:e(R,{})})).with("custom",()=>({ownerUi:e(T,{}),readUi:e(P,{})})).exhaustive(),[l.layout]);return e(r,{children:o(S,{children:[t?m:u,"universal"===l.layout&&o(i,{children:[e(D.CommonFinalizePage,{}),e(A,{})]})]})})});export const LoadedConfiguratorDetailPage=()=>{I(u.SceneAssets);let{configurationId:i,accessInfo:r}=p(),n=v(),a=k(),d=b(),s=g(),{startConfiguratorLoad:L,completeConfiguratorLoad:U,failConfiguratorLoad:S,resetConfiguratorLoad:F}=j(),{isLoading:R,isError:D}=y(),T=h(),P=w(),A=t(()=>r.viewMode&&void 0!==r.revisionId?n.getConfigurationRevision(i,r.revisionId):n.getConfiguration(i),[r.revisionId,r.viewMode,n,i]),N=t(()=>{L()},[L]),H=t(e=>{U({initialRevisionId:e.revisionId,initialEntities:e.data,modelVersion:e.modelVersion,rootEntityType:e.rootEntityType,schemaIdentity:P}),s.push(m.ConfigurationLoaded(e.configurationId,e.revisionId,r.viewMode?"read":"owner")),d.appendToGlobalContext({configurationId:e.configurationId,revisionId:e.revisionId,mode:r.viewMode}),d.info("Configuration loaded.")},[r.viewMode,s,U,P,d]),J=t(e=>{if(S(e),e instanceof l)return void a.rejectAccessToken(i,r.raw);let o=e instanceof Error?e:Error("Configuration loading failed with an unknown error.",{cause:e});d.error("Configuration loading failed because the API request did not succeed. Verify the configuration ID and access token.",{configurationId:i},o)},[r.raw,i,S,d,a]),K=t(()=>{F()},[F]);return(B({key:[n,i,r.revisionId,r.viewMode],load:A,onStart:N,onSuccess:H,onError:J,onCleanup:K}),C({id:u.DataFetch,label:"Loading configuration…"},R),x(D),D)?e(O,{}):null===T?null:e(f,{value:T,children:e(z,{fallbackRender:o=>e(q,{...o}),children:e(c,{type:"configurator",children:e(z,{FallbackComponent:V,children:o(M,{children:[e(E,{}),e(G,{viewMode:r.viewMode})]})})})})})};
@@ -1,5 +1,5 @@
1
- declare const PerspectiveCameraDragEvent: () => import("../../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
2
- declare const OrthographicCameraDragEvent: () => import("../../../domain/domain-context/analytics-system/analytics-contracts").UserActionEvent;
1
+ declare const PerspectiveCameraDragEvent: () => import("#package/domain").UserActionEvent;
2
+ declare const OrthographicCameraDragEvent: () => import("#package/domain").UserActionEvent;
3
3
  export declare const SceneAnalyticsFactory: {
4
4
  PerspectiveCameraDragEvent: typeof PerspectiveCameraDragEvent;
5
5
  OrthographicCameraDragEvent: typeof OrthographicCameraDragEvent;
@@ -0,0 +1,11 @@
1
+ import type { AssistantToolItem } from './thread-items.js';
2
+ export interface AssistantToolChipsProps {
3
+ readonly tools: readonly AssistantToolItem[];
4
+ /** True while the reply runs and this row is its newest block: the assistant is at work on it. */
5
+ readonly isActive: boolean;
6
+ }
7
+ /**
8
+ * A row of notes on what the assistant did in a reply, one per tool call. A click on a note logs
9
+ * the input and the result of its call to the console.
10
+ */
11
+ export declare const AssistantToolChips: import("react").MemoExoticComponent<({ tools, isActive }: AssistantToolChipsProps) => import("react/jsx-runtime").JSX.Element>;
@@ -0,0 +1 @@
1
+ import{jsx as t,jsxs as e}from"react/jsx-runtime";import{Tooltip as a,styled as i}from"@mui/material";import{memo as s,useCallback as o}from"react";import{match as n}from"ts-pattern";import{useLocalizable as r}from"#package/react-sdk";import{PhxIcon as l}from"#package/ui-sdk";import{AssistantPanelName as d}from"./theme.js";let m={id:"sdk.assistant.tool.updateDesign.label",message:"Update design"},c={id:"sdk.assistant.tool.readDesign.label",message:"Read design"},p={id:"sdk.assistant.tool.mistake.hint",message:"The assistant made a mistake."},h={id:"sdk.assistant.tool.cancelled.hint",message:"Stopped before it finished."},g={id:"sdk.assistant.tool.noResult.hint",message:"The result of this step was lost. It may or may not have been done."},f={id:"sdk.assistant.tool.toolBudget.hint",message:"Not done: the assistant reached its limit of steps for one message."},u=i("div",{name:d,slot:"chips"})(({theme:{palette:t,spacing:e,typography:a}})=>({...a.caption,alignItems:"start",color:t.text.secondary,display:"grid",gap:e(.75),gridTemplateColumns:"min-content minmax(0, 1fr)"})),k=i("span",{name:d,slot:"activity"})(({theme:{typography:t}})=>({blockSize:`calc(${t.caption.lineHeight} * 1em)`,display:"grid",placeItems:"center"})),x=i(l,{name:d,slot:"activityIcon"})({fontSize:14,'[data-chips="active"] &':{animation:"phx-assistant-turn 2.4s linear infinite","@media (prefers-reduced-motion: reduce)":{animation:"none"}},"@keyframes phx-assistant-turn":{to:{rotate:"360deg"}}}),w=i("span",{name:d,slot:"chip"})(({theme:{palette:t,spacing:e}})=>({cursor:"default",display:"inline-block",whiteSpace:"nowrap","&:not(:last-child)::after":{color:t.text.secondary,content:'"\xb7"',display:"inline-block",marginInline:e(.75),textDecoration:"none"},'&[data-chip="failed"]':{color:t.error.main},'&[data-chip="flawed"]':{textDecoration:"line-through",textDecorationColor:t.error.main}})),y=s(({tool:e})=>{let{chip:i,input:s,result:l}=e,d=r(m),u=r(c),k=r(p),x=r(h),y=r(g),b=r(f),v=n(i.label).with({kind:"text"},({text:t})=>t).with({kind:"updateDesign"},()=>d).with({kind:"readDesign"},()=>u).exhaustive(),C=o(()=>{console.info(`Assistant tool call "${v}"`,{input:s,result:l})},[s,v,l]);return t(a,{title:n(i).with({errorCode:"cancelled"},()=>x).with({errorCode:"no_result"},()=>y).with({errorCode:"tool_budget"},()=>b).with({state:"failed"},{state:"flawed"},()=>k).otherwise(()=>""),placement:"top-start",children:t(w,{onClick:C,"data-chip":i.state,children:v})})});export const AssistantToolChips=s(({tools:a,isActive:i})=>e(u,{"data-chips":i?"active":"done","aria-busy":i,children:[t(k,{children:t(x,{name:"Gear"})}),t("div",{children:a.map(e=>t(y,{tool:e},e.id))})]}));
@@ -0,0 +1,6 @@
1
+ /**
2
+ * The message input of the panel. Enter sends and Shift+Enter adds a line. While a reply runs the
3
+ * send control is a Stop control and the input stays editable, so the next message can be written.
4
+ * A thread this session may not continue disables the input and says why.
5
+ */
6
+ export declare const AssistantComposer: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1 @@
1
+ import{jsx as e,jsxs as a}from"react/jsx-runtime";import{Trans as i}from"@lingui/react";import{IconButton as o,InputBase as r,Tooltip as t,styled as n}from"@mui/material";import{useCallback as s,useRef as d,useState as l}from"react";import{useEagerObservableSelector as m,useLocalizable as c}from"#package/react-sdk";import{PhxIcon as p,useResponsive as u}from"#package/ui-sdk";import{useConfigurator as b}from"../../application-runtime/index.js";import{AssistantPanelName as g}from"./theme.js";let h={id:"sdk.assistant.composer.placeholder",message:"Ask or tell me what to change…"},k={id:"sdk.assistant.composer.send.label",message:"Send"},f={id:"sdk.assistant.composer.stop.label",message:"Stop"},x=n("form",{name:g,slot:"composer"})(({theme:{palette:e,spacing:a}})=>({borderBlockStart:`1px solid ${e.divider}`,display:"grid",gap:a(.75),paddingBlock:a(1.25),paddingInline:`var(--phx-sidebar-padding, ${a(2)})`})),y=n("div",{name:g,slot:"inputFrame"})(({theme:{palette:e,spacing:a}})=>({alignItems:"flex-end",border:`1px solid ${e.divider}`,borderRadius:12,display:"flex",gap:a(1),paddingBlock:a(.5),paddingInline:`${a(1.5)} ${a(.5)}`,"&:focus-within":{borderColor:e.primary.main},"&:has(textarea:disabled)":{background:e.action.disabledBackground}})),v=n(r,{name:g,slot:"input"})(({theme:{spacing:e,typography:a}})=>({...a.body2,flex:1,paddingBlock:e(.75)})),B=n(o,{name:g,slot:"sendButton"})(({theme:{palette:e}})=>({background:e.primary.main,borderRadius:8,color:e.primary.contrastText,"&:hover":{background:e.primary.dark},"&.Mui-disabled":{background:e.action.disabledBackground,color:e.action.disabled}})),C=n(o,{name:g,slot:"stopButton"})(({theme:{palette:e}})=>({background:e.text.primary,borderRadius:8,color:e.background.paper,"&:hover":{background:e.text.secondary}})),R=n("span")({background:"currentColor",blockSize:10,borderRadius:2,inlineSize:10,margin:5}),S=n("div",{name:g,slot:"hint"})(({theme:{palette:e,typography:a}})=>({...a.caption,color:e.text.secondary}));export const AssistantComposer=()=>{let{assistantSystem:o}=b(),[r,n]=l(""),g=d(null),{isDesktop:I}=u(),$=c(h),j=c(k),w=c(f),{isReplying:z,isContinuable:F}=m(o.chat$,e=>({isReplying:e.isLoading,isContinuable:e.isContinuable}),[]),A=r.trim(),D=F&&!z&&A.length>0,P=s(()=>{D&&(n(""),o.send(A),I||g.current?.blur())},[o,D,A,I]),T=s(e=>{e.preventDefault(),P()},[P]),E=s(e=>{"Enter"!==e.key||e.shiftKey||e.nativeEvent.isComposing||(e.preventDefault(),P())},[P]),K=s(()=>{o.stop()},[o]);return a(x,{onSubmit:T,children:[!F&&e(S,{role:"status",children:e(i,{id:"sdk.assistant.composer.notContinuable",message:"This conversation can be read here, but it can no longer be continued."})}),a(y,{children:[e(v,{multiline:!0,maxRows:6,placeholder:$,value:r,disabled:!F,inputRef:g,onChange:e=>n(e.target.value),onKeyDown:E,onFocus:()=>o.setInputFocused(!0),onBlur:()=>o.setInputFocused(!1),inputProps:{"aria-label":$}}),z?e(t,{title:w,placement:"top",children:e(C,{size:"small","aria-label":w,onClick:K,children:e(R,{})})}):e(t,{title:j,placement:"top",children:e("span",{children:e(B,{type:"submit",size:"small","aria-label":j,disabled:!D,children:e(p,{name:"PaperPlaneRightBold",fontSize:"small"})})})})]}),e(S,{children:e(i,{id:"sdk.assistant.composer.disclaimer",message:"AI can be wrong. Check the result."})})]})};
@@ -0,0 +1,7 @@
1
+ export interface AssistantMarkdownProps {
2
+ readonly markdown: string;
3
+ /** True while the text is still arriving, so markup the model has not finished is completed. */
4
+ readonly isStreaming: boolean;
5
+ }
6
+ /** Assistant text of the thread: Markdown with lists, tables, links and code, without a bubble. */
7
+ export declare const AssistantMarkdown: import("react").MemoExoticComponent<({ markdown, isStreaming }: AssistantMarkdownProps) => import("react/jsx-runtime").JSX.Element>;
@@ -0,0 +1 @@
1
+ import{jsx as r}from"react/jsx-runtime";import{styled as e}from"@mui/material";import{memo as o}from"react";import a from"react-markdown";import i from"remark-gfm";import{completeStreamedMarkdown as n}from"./streamed-markdown.js";import{AssistantPanelName as t}from"./theme.js";let l=e("div",{name:t,slot:"assistantMessage"})(({theme:{palette:r,shape:e,spacing:o,typography:a}})=>({overflowWrap:"anywhere","& > :first-child":{marginBlockStart:0},"& > :last-child":{marginBlockEnd:0},"p, ul, ol, pre, blockquote":{marginBlock:o(1)},"h1, h2, h3, h4, h5, h6":{...a.subtitle2,marginBlock:o(1.5,.5)},"ul, ol":{paddingInlineStart:o(2.5)},a:{color:r.primary.main},code:{background:r.action.hover,borderRadius:e.borderRadius/2,fontFamily:"ui-monospace, Menlo, monospace",fontSize:"0.9em",paddingInline:o(.5)},pre:{background:r.action.hover,borderRadius:e.borderRadius,overflowX:"auto",padding:o(1),code:{background:"none",padding:0}},blockquote:{borderInlineStart:`2px solid ${r.divider}`,color:r.text.secondary,marginInline:0,paddingInlineStart:o(1.5)},"th, td":{borderBlockEnd:`1px solid ${r.divider}`,padding:o(.5,1),textAlign:"start",verticalAlign:"top"}})),d=e("div")({overflowX:"auto",table:{borderCollapse:"collapse",inlineSize:"100%"}}),m={a:({node:e,children:o,...a})=>r("a",{...a,target:"_blank",rel:"noopener noreferrer",children:o}),table:({node:e,children:o,...a})=>r(d,{children:r("table",{...a,children:o})})},s=[i],c=["img"];export const AssistantMarkdown=o(({markdown:e,isStreaming:o})=>r(l,{children:r(a,{components:m,remarkPlugins:s,disallowedElements:c,children:o?n(e):e})}));
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The attribute the panel carries on its root. The layouts key their styles on it: what the panel
3
+ * covers is hidden, and a sidebar shorter than its slot is stretched to the height the panel needs.
4
+ */
5
+ export declare const ASSISTANT_PANEL_SLOT = "slot[assistant]";
6
+ /**
7
+ * The built-in assistant panel: a header with the title and a close control, the thread and the
8
+ * composer. It covers the sidebar it is rendered in, and renders nothing while it is closed or when
9
+ * the application spec has no `assistant` section.
10
+ */
11
+ export declare const AssistantPanel: () => import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1 @@
1
+ import{jsx as e,jsxs as t}from"react/jsx-runtime";import{IconButton as s,Tooltip as a,Typography as i,styled as n}from"@mui/material";import{useCallback as r}from"react";import{useLocalizable as o}from"#package/react-sdk";import{PhxIcon as l}from"#package/ui-sdk";import{useConfigurator as m}from"../../application-runtime/index.js";import{AssistantComposer as d}from"./AssistantComposer.js";import{AssistantThread as p}from"./AssistantThread.js";import{AssistantPanelName as c}from"./theme.js";import{useIsAssistantPanelOpen as u}from"./use-is-assistant-panel-open.js";let b={id:"sdk.assistant.panel.title",message:"Assistant"},f={id:"sdk.assistant.panel.close.label",message:"Close assistant"};export const ASSISTANT_PANEL_SLOT="slot[assistant]";let g=n("section",{name:c,slot:"root"})(({theme:{palette:e}})=>({background:`var(--phx-sidebar-background, ${e.background.paper})`,border:"var(--phx-sidebar-border, 0)",borderRadius:"inherit",display:"grid",gridTemplateColumns:"minmax(0, 1fr)",gridTemplateRows:"min-content minmax(0, 1fr) min-content",inset:0,overflow:"hidden",position:"absolute",zIndex:1})),h=n("header",{name:c,slot:"header"})(({theme:{palette:e,spacing:t}})=>({alignItems:"center",borderBlockEnd:`1px solid ${e.divider}`,display:"flex",gap:t(1),justifyContent:"space-between",paddingBlock:t(1),paddingInline:`var(--phx-sidebar-padding, ${t(2)}) ${t(1)}`})),x=n(i,{name:c,slot:"title"})({fontWeight:600}),k=n(s,{name:c,slot:"closeButton"})({}),j=e=>{e.target instanceof Element&&e.target.closest("button")&&e.preventDefault()};export const AssistantPanel=()=>{let{assistantSystem:s}=m(),i=u(),n=o(b),c=o(f),v=r(()=>{s.setOpen(!1)},[s]);return i?t(g,{"data-sidebar":"slot[assistant]","aria-label":n,onMouseDown:j,children:[t(h,{children:[e(x,{variant:"subtitle1",component:"h2",children:n}),e(a,{title:c,placement:"top",children:e(k,{size:"small","aria-label":c,onClick:v,children:e(l,{name:"X"})})})]}),e(p,{}),e(d,{})]}):null};
@@ -0,0 +1,6 @@
1
+ /**
2
+ * The conversation: user bubbles, assistant Markdown, tool chips, and what the panel
3
+ * has to say itself, which is a greeting for an empty thread, a "Thinking…" line and a failure with
4
+ * its way out. It follows a streaming reply until the user scrolls up.
5
+ */
6
+ export declare const AssistantThread: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1 @@
1
+ import{jsx as e,jsxs as i}from"react/jsx-runtime";import{Trans as a}from"@lingui/react";import{Button as t,styled as s}from"@mui/material";import{useMemo as r}from"react";import{match as n}from"ts-pattern";import{useEagerObservableValue as o}from"#package/react-sdk";import{PhxIcon as l}from"#package/ui-sdk";import{useConfigurator as d}from"../../application-runtime/index.js";import{AssistantToolChips as m}from"./AssistantChips.js";import{AssistantMarkdown as p}from"./AssistantMarkdown.js";import{describeAssistantFailure as c}from"./assistant-failure.js";import{AssistantPanelName as h}from"./theme.js";import{buildThreadView as g}from"./thread-items.js";import{useFollowStream as u}from"./use-follow-stream.js";let f=s("div",{name:h,slot:"thread"})({display:"grid",position:"relative"}),k=s("div",{name:h,slot:"messages"})(({theme:{spacing:e,typography:i}})=>({...i.body2,display:"flex",flexDirection:"column",gap:e(1.5),overflowY:"auto",overscrollBehavior:"contain",paddingBlock:e(2),paddingInline:`var(--phx-sidebar-padding, ${e(2)})`,"& > :first-child":{marginBlockStart:"auto"}})),v=s("div",{name:h,slot:"userMessage"})(({theme:{palette:e,spacing:i}})=>({alignSelf:"flex-end",background:`hsl(from ${e.primary.main} h s l / 12%)`,borderRadius:"14px 14px 4px 14px",maxInlineSize:"85%",overflowWrap:"anywhere",paddingBlock:i(1),paddingInline:i(1.5),whiteSpace:"pre-wrap"})),w=s("div",{name:h,slot:"greeting"})({}),b=s("div",{name:h,slot:"status"})(({theme:{palette:e,typography:i}})=>({...i.caption,color:e.text.secondary})),x=s("div",{name:h,slot:"notice"})(({theme:{palette:e,shape:i,spacing:a}})=>({alignItems:"center",background:e.action.hover,borderRadius:i.borderRadius,display:"flex",flexWrap:"wrap",gap:a(1),justifyContent:"space-between",paddingBlock:a(1),paddingInline:a(1.5)})),y=s(t,{name:h,slot:"newReplyButton"})(({theme:{palette:e,spacing:i}})=>({background:e.text.primary,borderRadius:"calc(infinity * 1px)",color:e.background.paper,insetBlockEnd:i(1),insetInlineStart:"50%",position:"absolute",translate:"-50% 0","&:hover":{background:e.text.secondary}})),j=({item:i,isActive:a})=>n(i).with({kind:"user"},({text:i})=>e(v,{children:i})).with({kind:"text"},({markdown:i,isStreaming:a})=>e(p,{markdown:i,isStreaming:a})).with({kind:"tools"},({tools:i})=>e(m,{tools:i,isActive:a})).exhaustive();export const AssistantThread=()=>{let{assistantSystem:s}=d(),m=o(s.chat$),{messages:p,isLoading:h}=m,{items:v,isThinking:T}=r(()=>g(p,h),[p,h]),I=r(()=>v.filter(({kind:e})=>"user"===e).length,[v]),S=m.error?c(m.failedStatus):void 0,{scrollerRef:A,hasUnseenContent:B,handleScroll:R,scrollToEnd:C}=u(r(()=>({messages:p,isThinking:T,failure:S?.kind}),[p,T,S?.kind]),I);return i(f,{children:[i(k,{ref:A,onScroll:R,role:"log","aria-live":"polite",children:[m.isLoaded&&0===v.length&&!h&&!S&&e(w,{children:e(a,{id:"sdk.assistant.thread.greeting",message:"Hi. I can answer questions about this design and change it with you. Tell me what you need."})}),v.map(i=>e(j,{item:i,isActive:h&&i===v.at(-1)},i.id)),T&&e(b,{children:e(a,{id:"sdk.assistant.thread.thinking",message:"Thinking…"})}),S&&i(x,{role:"alert",children:[e("span",{children:n(S.kind).with("notAvailable",()=>e(a,{id:"sdk.assistant.failure.notAvailable",message:"The assistant is not available here."})).with("busy",()=>e(a,{id:"sdk.assistant.failure.busy",message:"The assistant is still working on an earlier message."})).with("unavailable",()=>e(a,{id:"sdk.assistant.failure.unavailable",message:"The assistant is unavailable right now."})).exhaustive()}),S.canRetry&&e(t,{size:"small",variant:"outlined",color:"inherit",onClick:s.reloadThread,children:e(a,{id:"sdk.assistant.failure.retry.label",message:"Try again"})})]})]}),B&&e(y,{size:"small",onClick:C,endIcon:e(l,{name:"CaretDown"}),children:e(a,{id:"sdk.assistant.thread.newReply.label",message:"New reply"})})]})};
@@ -0,0 +1,18 @@
1
+ /**
2
+ * What the panel tells the user when the assistant failed:
3
+ * - `notAvailable`: the server refuses this configuration or customer, and asking again will not help
4
+ * - `busy`: the server is still answering an earlier message of this thread
5
+ * - `unavailable`: anything else, including a dropped connection and a reply that broke off
6
+ */
7
+ export type AssistantFailureKind = 'notAvailable' | 'busy' | 'unavailable';
8
+ export interface AssistantFailure {
9
+ readonly kind: AssistantFailureKind;
10
+ /** True when loading the thread again may help, so the panel offers "Try again". */
11
+ readonly canRetry: boolean;
12
+ }
13
+ /**
14
+ * Maps a failed assistant request to what the panel shows, by the HTTP status the server answered
15
+ * with. A failure without a status never reached the server or broke off mid-reply; it gets the same
16
+ * generic message as an unknown status, because the chat client does not tell those apart.
17
+ */
18
+ export declare const describeAssistantFailure: (failedStatus: number | undefined) => AssistantFailure;
@@ -0,0 +1 @@
1
+ export const describeAssistantFailure=a=>403===a?{kind:"notAvailable",canRetry:!1}:409===a?{kind:"busy",canRetry:!0}:{kind:"unavailable",canRetry:!0};
@@ -0,0 +1,2 @@
1
+ export { AssistantPanel, ASSISTANT_PANEL_SLOT } from './AssistantPanel.js';
2
+ export { useIsAssistantPanelOpen } from './use-is-assistant-panel-open.js';
@@ -0,0 +1 @@
1
+ export{AssistantPanel,ASSISTANT_PANEL_SLOT}from"./AssistantPanel.js";export{useIsAssistantPanelOpen}from"./use-is-assistant-panel-open.js";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Makes the Markdown of a reply that is still streaming renderable as it stands, so half-written
3
+ * markup does not flash as raw characters: an open code fence is closed, an inline code span, a link
4
+ * or a table header the model has not finished is held back, and open bold text is closed.
5
+ */
6
+ export declare const completeStreamedMarkdown: (text: string) => string;
@@ -0,0 +1,2 @@
1
+ let e=(e,t)=>e.split(t).length-1,t=/^\|?\s*:?-+:?\s*(\|\s*:?-+:?\s*)*\|?\s*$/;export const completeStreamedMarkdown=l=>{if(e(l,"```")%2==1)return`${l}
2
+ \`\`\``;let n=l.lastIndexOf("```"),s=-1===n?"":l.slice(0,n+3),r=l.slice(s.length),i=(e=>{let l=e.split("\n"),n=l.length;for(;n>0&&l[n-1]?.trimStart().startsWith("|");)n-=1;let s=l.slice(n),r=s[1],i=null!=r&&s.length>2&&t.test(r);return 0===s.length||i?e:l.slice(0,n).join("\n")})((e(r,"`")%2==1?r.slice(0,r.lastIndexOf("`")):r).replace(/!?\[([^\]\n]*)\]\([^)\n]*$/,"$1").replace(/!?\[[^\]\n]*$/,"")),c=e(i,"**")%2==1?`${i}**`:i;return`${s}${c}`};
@@ -0,0 +1,2 @@
1
+ /** Theme component name of the built-in assistant panel; its slots are listed in the ui-sdk theme types. */
2
+ export declare const AssistantPanelName: 'PhxAssistantPanel';
@@ -0,0 +1 @@
1
+ export const AssistantPanelName="PhxAssistantPanel";
@@ -0,0 +1,37 @@
1
+ import type { UIMessage } from '@tanstack/ai-client';
2
+ import { type AssistantToolChip } from './tool-chip.js';
3
+ /** A tool call of the thread, with what its chip shows and what a click on the chip logs. */
4
+ export interface AssistantToolItem {
5
+ readonly id: string;
6
+ readonly chip: AssistantToolChip;
7
+ readonly input: unknown;
8
+ readonly result: unknown;
9
+ }
10
+ /**
11
+ * One block of the thread, in reading order: a user bubble, a stretch of assistant Markdown, or a
12
+ * row of chips for the tool calls that follow each other without text in between.
13
+ */
14
+ export type AssistantThreadItem = {
15
+ readonly kind: 'user';
16
+ readonly id: string;
17
+ readonly text: string;
18
+ } | {
19
+ readonly kind: 'text';
20
+ readonly id: string;
21
+ readonly markdown: string;
22
+ readonly isStreaming: boolean;
23
+ } | {
24
+ readonly kind: 'tools';
25
+ readonly id: string;
26
+ readonly tools: readonly AssistantToolItem[];
27
+ };
28
+ export interface AssistantThreadView {
29
+ readonly items: readonly AssistantThreadItem[];
30
+ /** True while a reply runs and neither streaming text nor a row of tool chips shows that it does. */
31
+ readonly isThinking: boolean;
32
+ }
33
+ /**
34
+ * Lays the messages of the chat out as the blocks the thread renders. Reasoning is left out; tool
35
+ * results are not shown on their own but decide the state of the chip of their call.
36
+ */
37
+ export declare const buildThreadView: (messages: ReadonlyArray<UIMessage>, isReplying: boolean) => AssistantThreadView;
@@ -0,0 +1 @@
1
+ import{describeToolCall as t}from"./tool-chip.js";let e=t=>{try{return JSON.parse(t)}catch{return t}},r=t=>t.map(t=>"text"===t.type?t.content:"").join("").trim();export const buildThreadView=(o,n)=>{let l=(t=>{let r=new Map;for(let o of t)for(let t of o.parts)"tool-result"===t.type&&r.set(t.toolCallId,{result:"string"==typeof t.content?e(t.content):t.content,error:"error"===t.state?t.error??"The tool call failed.":void 0});return r})(o),i=[],s=o.at(-1),a=t=>{let e=i.at(-1);e?.kind==="tools"?i[i.length-1]={...e,tools:[...e.tools,t]}:i.push({kind:"tools",id:t.id,tools:[t]})};for(let e of o){if("user"===e.role){let t=r(e.parts);t&&i.push({kind:"user",id:e.id,text:t});continue}let o=n&&e===s;e.parts.forEach((r,n)=>{let s=`${e.id}:${n}`;if("text"===r.type){if(r.content.trim()){let t=o&&n===e.parts.length-1;i.push({kind:"text",id:s,markdown:r.content,isStreaming:t})}return}if("tool-call"!==r.type)return;let p=l.get(r.id),d=r.input,u=r.output??p?.result;a({id:r.id,input:d,result:u,chip:t({name:r.name,input:d,result:u,error:p?.error,isReplyRunning:o})})})}let p=i.at(-1),d=s?.role==="assistant"&&(p?.kind==="text"&&p.isStreaming||p?.kind==="tools");return{items:i,isThinking:n&&!d}};