@salsita-ai/sdk 0.50.0-develop.c28cfb7ded → 0.50.0-develop.d0eaeed131
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -14
- package/dist/admin/default-admin-elements.d.ts +9 -0
- package/dist/admin/default-admin-elements.js +1 -0
- package/dist/admin/default-elements/built-in-entity-element.d.ts +10 -0
- package/dist/admin/default-elements/built-in-entity-element.js +1 -0
- package/dist/admin/default-elements/built-in-layout-element.d.ts +10 -0
- package/dist/admin/default-elements/built-in-layout-element.js +1 -0
- package/dist/admin/default-elements/index.d.ts +2 -0
- package/dist/admin/default-elements/index.js +1 -0
- package/dist/admin/index.d.ts +1 -0
- package/dist/admin/index.js +1 -1
- package/dist/application-spec/caching/content-diff.js +1 -1
- package/dist/application-spec/core/application-file.d.ts +0 -2
- package/dist/application-spec/core/application-file.js +1 -1
- package/dist/application-spec/errors/diagnostic-codes.d.ts +5 -0
- package/dist/application-spec/errors/diagnostic-codes.js +1 -1
- package/dist/application-spec/errors/diagnostic-messages.d.ts +5 -0
- package/dist/application-spec/errors/diagnostic-messages.js +1 -1
- package/dist/application-spec/generate-application/code/domain-runtime-files.js +1 -1
- package/dist/application-spec/generate-application/code/generate-application-code.d.ts +3 -2
- package/dist/application-spec/generate-application/code/generate-application-code.js +1 -1
- package/dist/application-spec/generate-application/code/project-scaffolding.d.ts +15 -14
- package/dist/application-spec/generate-application/code/project-scaffolding.js +2 -2
- package/dist/application-spec/generate-application/code/templates/admin-config.js +3 -0
- package/dist/application-spec/generate-application/code/templates/common/format-property-key.d.ts +7 -0
- package/dist/application-spec/generate-application/code/templates/common/format-property-key.js +1 -0
- package/dist/application-spec/generate-application/code/templates/common/schema-to-type-string.js +3 -3
- package/dist/application-spec/generate-application/code/templates/eslint-config.d.ts +5 -0
- package/dist/application-spec/generate-application/code/templates/eslint-config.js +4 -5
- package/dist/application-spec/generate-application/code/templates/frontend-config.js +3 -5
- package/dist/application-spec/generate-application/code/templates/gitignore.js +0 -3
- package/dist/application-spec/generate-application/code/templates/globals.js +7 -5
- package/dist/application-spec/generate-application/code/templates/index.d.ts +3 -2
- package/dist/application-spec/generate-application/code/templates/index.js +1 -1
- package/dist/application-spec/generate-application/code/templates/lingui-rc.d.ts +5 -0
- package/dist/application-spec/generate-application/code/templates/lingui-rc.js +4 -4
- package/dist/application-spec/generate-application/code/templates/prettier-config.d.ts +5 -0
- package/dist/application-spec/generate-application/code/templates/prettier-config.js +4 -5
- package/dist/application-spec/generate-application/code/templates/root-tsconfig.d.ts +6 -0
- package/dist/application-spec/generate-application/code/templates/root-tsconfig.js +2 -0
- package/dist/application-spec/generate-application/code/templates/skills/phoenix-cli-skill.js +1 -1
- package/dist/application-spec/generate-application/code/templates/skills/phoenix-create-configurator-skill.js +1 -1
- package/dist/application-spec/generate-application/code/templates/skills/phoenix-feedback-skill.js +5 -5
- package/dist/application-spec/generate-application/code/templates/skills/phoenix-review-skill.js +3 -3
- package/dist/application-spec/generate-application/code/templates/skills/phoenix-skill.js +1 -1
- package/dist/application-spec/generate-application/code/templates/skills/phoenix-variants-skill.js +2 -2
- package/dist/application-spec/generate-application/code/templates/skills/sdk-content.d.ts +16 -4
- package/dist/application-spec/generate-application/code/templates/skills/sdk-content.js +2 -2
- package/dist/application-spec/generate-application/code/templates/spec-code/collect-spec-code.d.ts +58 -0
- package/dist/application-spec/generate-application/code/templates/spec-code/collect-spec-code.js +1 -0
- package/dist/application-spec/generate-application/code/templates/spec-code/index.d.ts +2 -0
- package/dist/application-spec/generate-application/code/templates/spec-code/index.js +1 -0
- package/dist/application-spec/generate-application/code/templates/spec-code/spec-code.d.ts +18 -0
- package/dist/application-spec/generate-application/code/templates/spec-code/spec-code.js +41 -0
- package/dist/application-spec/generate-application/code/templates/spec-messages/index.d.ts +1 -0
- package/dist/application-spec/generate-application/code/templates/spec-messages/index.js +1 -0
- package/dist/application-spec/generate-application/code/templates/spec-messages/spec-messages.d.ts +20 -0
- package/dist/application-spec/generate-application/code/templates/spec-messages/spec-messages.js +6 -0
- package/dist/application-spec/generate-application/code/templates/tsconfig.d.ts +5 -0
- package/dist/application-spec/generate-application/code/templates/tsconfig.js +1 -1
- package/dist/application-spec/generate-application/code/templates/webhooks-config.js +2 -3
- package/dist/application-spec/generate-application/runtime/admin-runtime/admin-model-runtime.d.ts +4 -3
- package/dist/application-spec/generate-application/runtime/admin-runtime/admin-runtime.d.ts +2 -2
- package/dist/application-spec/generate-application/runtime/admin-runtime/states-runtime.d.ts +2 -2
- package/dist/application-spec/generate-application/runtime/application-runtime.d.ts +6 -5
- package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-model-runtime.d.ts +4 -3
- package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-runtime.d.ts +2 -2
- package/dist/application-spec/generate-application/runtime/configurator-runtime/states-runtime.d.ts +2 -2
- package/dist/application-spec/generate-application/runtime/migrations/migration-runtime.d.ts +2 -2
- package/dist/application-spec/generate-application/runtime/sections/assistant-runtime.d.ts +2 -2
- package/dist/application-spec/generate-application/runtime/sections/data-runtime.d.ts +4 -4
- package/dist/application-spec/generate-application/runtime/sections/expression-runtime.d.ts +3 -2
- package/dist/application-spec/generate-application/runtime/sections/theme-runtime.d.ts +2 -2
- package/dist/application-spec/generate-application/runtime/sections/transformers-runtime.d.ts +3 -3
- package/dist/application-spec/generate-application/runtime-spec/decode-runtime-spec.d.ts +9 -0
- package/dist/application-spec/generate-application/runtime-spec/decode-runtime-spec.js +1 -0
- package/dist/application-spec/generate-application/runtime-spec/encode-runtime-spec.d.ts +8 -0
- package/dist/application-spec/generate-application/runtime-spec/encode-runtime-spec.js +1 -0
- package/dist/application-spec/generate-application/runtime-spec/encoded-runtime-spec.d.ts +27 -0
- package/dist/application-spec/generate-application/runtime-spec/encoded-runtime-spec.js +1 -0
- package/dist/application-spec/generate-application/runtime-spec/index.d.ts +5 -0
- package/dist/application-spec/generate-application/runtime-spec/index.js +1 -0
- package/dist/application-spec/generate-application/runtime-spec/runtime-spec.d.ts +68 -0
- package/dist/application-spec/generate-application/runtime-spec/runtime-spec.js +1 -0
- package/dist/application-spec/generate-application/runtime-spec/spec-code-id.d.ts +40 -0
- package/dist/application-spec/generate-application/runtime-spec/spec-code-id.js +1 -0
- package/dist/application-spec/parse-application-files/categorize/categorize-files.d.ts +28 -0
- package/dist/application-spec/parse-application-files/categorize/categorize-files.js +1 -1
- package/dist/application-spec/parse-application-files/categorize/earlier-generated-editor-configs.d.ts +16 -0
- package/dist/application-spec/parse-application-files/categorize/earlier-generated-editor-configs.js +9 -0
- package/dist/application-spec/parse-application-files/categorize/earlier-generated-tsconfig.d.ts +9 -0
- package/dist/application-spec/parse-application-files/categorize/earlier-generated-tsconfig.js +1 -0
- package/dist/application-spec/parse-application-files/raw-spec/parse-yaml-spec-file.js +1 -1
- package/dist/application-spec/parse-application-files/spec/application/admin-spec/admin-spec.js +1 -1
- package/dist/application-spec/parse-application-files/spec/application/configurator-spec/configurator-spec.js +1 -1
- package/dist/application-spec/parse-application-files/spec/common/index.d.ts +1 -0
- package/dist/application-spec/parse-application-files/spec/common/index.js +1 -1
- package/dist/application-spec/parse-application-files/spec/common/value-type/format-value-type.d.ts +3 -0
- package/dist/application-spec/parse-application-files/spec/common/value-type/format-value-type.js +1 -0
- package/dist/application-spec/parse-application-files/spec/common/value-type/index.d.ts +5 -0
- package/dist/application-spec/parse-application-files/spec/common/value-type/index.js +1 -0
- package/dist/application-spec/parse-application-files/spec/common/value-type/intersect-value-types.d.ts +6 -0
- package/dist/application-spec/parse-application-files/spec/common/value-type/intersect-value-types.js +1 -0
- package/dist/application-spec/parse-application-files/spec/common/value-type/is-assignable-value-type.d.ts +8 -0
- package/dist/application-spec/parse-application-files/spec/common/value-type/is-assignable-value-type.js +1 -0
- package/dist/application-spec/parse-application-files/spec/common/value-type/value-type-at.d.ts +10 -0
- package/dist/application-spec/parse-application-files/spec/common/value-type/value-type-at.js +1 -0
- package/dist/application-spec/parse-application-files/spec/common/value-type/value-type.d.ts +98 -0
- package/dist/application-spec/parse-application-files/spec/common/value-type/value-type.js +1 -0
- package/dist/application-spec/parse-application-files/spec/components/bindings/component-path.d.ts +13 -0
- package/dist/application-spec/parse-application-files/spec/components/bindings/component-path.js +1 -0
- package/dist/application-spec/parse-application-files/spec/components/bindings/index.d.ts +2 -0
- package/dist/application-spec/parse-application-files/spec/components/bindings/index.js +1 -1
- package/dist/application-spec/parse-application-files/spec/components/bindings/read-binding-type.d.ts +24 -0
- package/dist/application-spec/parse-application-files/spec/components/bindings/read-binding-type.js +1 -0
- package/dist/application-spec/parse-application-files/spec/components/component-state-type.d.ts +8 -0
- package/dist/application-spec/parse-application-files/spec/components/component-state-type.js +1 -0
- package/dist/application-spec/parse-application-files/spec/components/filter-criterion-types.d.ts +13 -0
- package/dist/application-spec/parse-application-files/spec/components/filter-criterion-types.js +1 -0
- package/dist/application-spec/parse-application-files/spec/components/static-entity-id.d.ts +14 -0
- package/dist/application-spec/parse-application-files/spec/components/static-entity-id.js +1 -0
- package/dist/application-spec/parse-application-files/spec/components/user-component-entry.d.ts +6 -0
- package/dist/application-spec/parse-application-files/spec/components/user-component-entry.js +1 -1
- package/dist/application-spec/parse-application-files/spec/components/user-components.d.ts +7 -4
- package/dist/application-spec/parse-application-files/spec/components/user-components.js +1 -1
- package/dist/application-spec/parse-application-files/spec/expressions/expression-read-types.d.ts +24 -0
- package/dist/application-spec/parse-application-files/spec/expressions/expression-read-types.js +1 -0
- package/dist/application-spec/parse-application-files/spec/expressions/expression-read.d.ts +13 -0
- package/dist/application-spec/parse-application-files/spec/expressions/expression-read.js +1 -0
- package/dist/application-spec/parse-application-files/spec/expressions/expression-value-type.d.ts +9 -0
- package/dist/application-spec/parse-application-files/spec/expressions/expression-value-type.js +1 -0
- package/dist/application-spec/parse-application-files/spec/expressions/expressions.d.ts +6 -2
- package/dist/application-spec/parse-application-files/spec/expressions/expressions.js +1 -1
- package/dist/application-spec/parse-application-files/spec/expressions/user-expressions.d.ts +3 -1
- package/dist/application-spec/parse-application-files/spec/expressions/user-expressions.js +1 -1
- package/dist/application-spec/parse-application-files/spec/invariants/invariants.d.ts +2 -2
- package/dist/application-spec/parse-application-files/spec/invariants/invariants.js +1 -1
- package/dist/application-spec/parse-application-files/spec/model/index.d.ts +1 -0
- package/dist/application-spec/parse-application-files/spec/model/index.js +1 -1
- package/dist/application-spec/parse-application-files/spec/model/model-type-resolution.d.ts +10 -0
- package/dist/application-spec/parse-application-files/spec/model/model-type-resolution.js +1 -0
- package/dist/client/phoenix-api.d.ts +367 -0
- package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.d.ts +7 -1
- package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.js +1 -1
- package/dist/configurator/pages/DefaultConfiguratorLayout.d.ts +3 -2
- package/dist/configurator/pages/LoadedConfiguratorDetailPage.js +1 -1
- package/dist/configurator/pages/default-configurator-elements.d.ts +2 -1
- package/dist/configurator/pages/default-configurator-elements.js +1 -1
- package/dist/configurator/pages/default-elements/built-in-entity-element.d.ts +10 -0
- package/dist/configurator/pages/default-elements/built-in-entity-element.js +1 -0
- package/dist/configurator/pages/default-elements/built-in-layout-element.d.ts +10 -0
- package/dist/configurator/pages/default-elements/built-in-layout-element.js +1 -0
- package/dist/configurator/pages/default-elements/domain-control-elements.d.ts +6 -0
- package/dist/configurator/pages/default-elements/domain-control-elements.js +1 -0
- package/dist/configurator/pages/default-elements/index.d.ts +4 -0
- package/dist/configurator/pages/default-elements/index.js +1 -0
- package/dist/configurator/pages/default-elements/recipe-panel-elements.d.ts +3 -0
- package/dist/configurator/pages/default-elements/recipe-panel-elements.js +1 -0
- package/dist/configurator/pages/default-elements/sidebar-footer-elements.d.ts +7 -0
- package/dist/configurator/pages/default-elements/sidebar-footer-elements.js +1 -0
- package/dist/configurator/pages/default-elements/toolbar-elements.d.ts +7 -0
- package/dist/configurator/pages/default-elements/toolbar-elements.js +1 -0
- package/dist/configurator/ui/SaveMessageSnackbars.d.ts +10 -0
- package/dist/configurator/ui/SaveMessageSnackbars.js +1 -0
- package/dist/configurator/ui/assistant/AssistantMarkdown.d.ts +7 -2
- package/dist/configurator/ui/assistant/AssistantMarkdown.js +1 -1
- package/dist/configurator/ui/assistant/AssistantPanel.js +1 -1
- package/dist/configurator/ui/assistant/AssistantThread.js +1 -1
- package/dist/configurator/ui/assistant/entity-link/AssistantEntityLink.d.ts +1 -1
- package/dist/configurator/ui/assistant/entity-link/AssistantEntityLink.js +1 -1
- package/dist/configurator/ui/assistant/entity-link/entity-link-state.d.ts +1 -1
- package/dist/configurator/ui/assistant/entity-link/index.d.ts +0 -1
- package/dist/configurator/ui/assistant/entity-link/index.js +1 -1
- package/dist/configurator/ui/assistant/form/AssistantForm.d.ts +19 -0
- package/dist/configurator/ui/assistant/form/AssistantForm.js +1 -0
- package/dist/configurator/ui/assistant/form/built-in-form-registry.d.ts +10 -0
- package/dist/configurator/ui/assistant/form/built-in-form-registry.js +1 -0
- package/dist/configurator/ui/assistant/form/compose-answer.d.ts +11 -0
- package/dist/configurator/ui/assistant/form/compose-answer.js +1 -0
- package/dist/configurator/ui/assistant/form/describe-build-errors.d.ts +6 -0
- package/dist/configurator/ui/assistant/form/describe-build-errors.js +1 -0
- package/dist/configurator/ui/assistant/form/form-element-definition.d.ts +14 -0
- package/dist/configurator/ui/assistant/form/form-element-definition.js +1 -0
- package/dist/configurator/ui/assistant/form/form-model.d.ts +38 -0
- package/dist/configurator/ui/assistant/form/form-model.js +1 -0
- package/dist/configurator/ui/assistant/form/form-registry.d.ts +31 -0
- package/dist/configurator/ui/assistant/form/form-registry.js +8 -0
- package/dist/configurator/ui/assistant/form/form-state.d.ts +4 -0
- package/dist/configurator/ui/assistant/form/form-state.js +1 -0
- package/dist/configurator/ui/assistant/form/index.d.ts +2 -0
- package/dist/configurator/ui/assistant/form/index.js +1 -0
- package/dist/configurator/ui/assistant/form/parse-form-block.d.ts +9 -0
- package/dist/configurator/ui/assistant/form/parse-form-block.js +1 -0
- package/dist/configurator/ui/assistant/form/read-form-attributes.d.ts +13 -0
- package/dist/configurator/ui/assistant/form/read-form-attributes.js +1 -0
- package/dist/configurator/ui/assistant/form-elements/FormGroup.d.ts +3 -0
- package/dist/configurator/ui/assistant/form-elements/FormGroup.js +1 -0
- package/dist/configurator/ui/assistant/form-elements/FormOptions.d.ts +3 -0
- package/dist/configurator/ui/assistant/form-elements/FormOptions.js +1 -0
- package/dist/configurator/ui/assistant/form-elements/FormReply.d.ts +3 -0
- package/dist/configurator/ui/assistant/form-elements/FormReply.js +1 -0
- package/dist/configurator/ui/assistant/form-elements/FormText.d.ts +3 -0
- package/dist/configurator/ui/assistant/form-elements/FormText.js +1 -0
- package/dist/configurator/ui/assistant/form-elements/form-element-props.d.ts +34 -0
- package/dist/configurator/ui/assistant/form-elements/form-element-props.js +1 -0
- package/dist/configurator/ui/assistant/form-elements/index.d.ts +5 -0
- package/dist/configurator/ui/assistant/form-elements/index.js +1 -0
- package/dist/configurator/ui/assistant/reference-link/AssistantReferenceLink.d.ts +16 -0
- package/dist/configurator/ui/assistant/reference-link/AssistantReferenceLink.js +1 -0
- package/dist/configurator/ui/assistant/reference-link/assistant-link-target.d.ts +34 -0
- package/dist/configurator/ui/assistant/reference-link/assistant-link-target.js +1 -0
- package/dist/configurator/ui/assistant/reference-link/assistant-reference-details.d.ts +25 -0
- package/dist/configurator/ui/assistant/reference-link/assistant-reference-details.js +1 -0
- package/dist/configurator/ui/assistant/reference-link/index.d.ts +3 -0
- package/dist/configurator/ui/assistant/reference-link/index.js +1 -0
- package/dist/configurator/ui/assistant/reference-link/reference-styles.d.ts +12 -0
- package/dist/configurator/ui/assistant/reference-link/reference-styles.js +1 -0
- package/dist/configurator/ui/assistant/reference-link/use-assistant-reference-details.d.ts +8 -0
- package/dist/configurator/ui/assistant/reference-link/use-assistant-reference-details.js +1 -0
- package/dist/configurator/ui/assistant/resource-image/AssistantImage.d.ts +9 -0
- package/dist/configurator/ui/assistant/resource-image/AssistantImage.js +1 -0
- package/dist/configurator/ui/assistant/resource-image/index.d.ts +3 -0
- package/dist/configurator/ui/assistant/resource-image/index.js +1 -0
- package/dist/configurator/ui/assistant/resource-image/resource-path.d.ts +11 -0
- package/dist/configurator/ui/assistant/resource-image/resource-path.js +1 -0
- package/dist/configurator/ui/assistant/resource-image/use-resource-image-url.d.ts +5 -0
- package/dist/configurator/ui/assistant/resource-image/use-resource-image-url.js +1 -0
- package/dist/configurator/ui/assistant/streamed-markdown.d.ts +2 -0
- package/dist/configurator/ui/assistant/streamed-markdown.js +2 -2
- package/dist/configurator/ui/assistant/thread-items.d.ts +8 -1
- package/dist/configurator/ui/assistant/thread-items.js +1 -1
- package/dist/configurator/ui/assistant/use-follow-stream.js +1 -1
- package/dist/configurator/ui/configurator-layout/slots/services/InvariantViolations.js +1 -1
- package/dist/configurator/ui/configurator-layout/slots/services/Services.js +1 -1
- package/dist/configurator/ui/legacy/layout/common/SummaryReadLayout.js +1 -1
- package/dist/configurator/ui/legacy/layout/simple/SimpleOwnerLayout.js +1 -1
- package/dist/configurator/ui/legacy/layout/steps/StepsOwnerLayout.js +1 -1
- package/dist/configurator/ui/legacy/layout/tabs/TabsOwnerLayout.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/LayoutCustomOwnerUi.d.ts +1 -0
- package/dist/configurator/ui/phoenix-layout-elements/LayoutCustomOwnerUi.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/LayoutCustomReadUi.d.ts +1 -0
- package/dist/configurator/ui/phoenix-layout-elements/LayoutCustomReadUi.js +1 -1
- package/dist/docs/controls/select-child-entity/cardselect.mdx +1 -0
- package/dist/docs/fabs/basic.mdx +2 -2
- package/dist/docs/index.json +1 -1
- package/dist/docs/layouts/custom-layout.mdx +324 -15
- package/dist/docs/phoenix/clerk-sign-in.mdx +1 -1
- package/dist/docs/phoenix/components.mdx +6 -0
- package/dist/docs/phoenix/configurator-interface.mdx +10 -10
- package/dist/docs/phoenix/deployments.mdx +2 -6
- package/dist/docs/phoenix/domain-model.mdx +2 -0
- package/dist/docs/phoenix/getting-started.mdx +6 -3
- package/dist/docs/phoenix/layout-slots.mdx +9 -9
- package/dist/docs/phoenix/localization.mdx +10 -6
- package/dist/docs/phoenix/migration-guide.mdx +24 -11
- package/dist/docs/phoenix/phoenix-cli.mdx +65 -20
- package/dist/docs/phoenix/project-structure.mdx +66 -32
- package/dist/docs/phoenix/quickstart.mdx +7 -7
- package/dist/docs/phoenix/sdk-builder-workflow.mdx +7 -7
- package/dist/docs/phoenix/testing.mdx +8 -0
- package/dist/docs/phoenix/webhooks.mdx +4 -4
- package/dist/domain/application/assistant/assistant-form-elements.d.ts +28 -0
- package/dist/domain/application/assistant/assistant-form-elements.js +1 -0
- package/dist/domain/application/assistant/generate-assistant-prompt.js +1 -1
- package/dist/domain/application/assistant/index.d.ts +2 -0
- package/dist/domain/application/assistant/index.js +1 -1
- package/dist/domain/application/assistant-recipe-groups.d.ts +3 -1
- package/dist/domain/core/schema/extended-schema-validator.js +1 -1
- package/dist/domain/domain-runtime/spec/module-checks/element-registration.d.ts +12 -11
- package/dist/domain/domain-runtime/spec/module-checks/element-registration.js +1 -1
- package/dist/domain/domain-runtime/spec/module-checks/index.d.ts +1 -1
- package/dist/domain/domain-runtime/spec/module-checks/index.js +1 -1
- package/dist/domain/domain-runtime/spec/selectors.js +1 -1
- package/dist/domain/system-components/child-entity-selection.js +1 -1
- package/dist/domain/system-components/common/scene-entity-control-schema.d.ts +3 -0
- package/dist/domain/system-components/common/scene-entity-control-schema.js +1 -1
- package/dist/globals/index.d.ts +2 -0
- package/dist/i18n/locales/cs/messages.js +1 -1
- package/dist/i18n/locales/da/messages.js +1 -1
- package/dist/i18n/locales/de/messages.js +1 -1
- package/dist/i18n/locales/de-AT/messages.js +1 -1
- package/dist/i18n/locales/en/messages.js +1 -1
- package/dist/i18n/locales/en-AU/messages.js +1 -1
- package/dist/i18n/locales/en-GB/messages.js +1 -1
- package/dist/i18n/locales/es/messages.js +1 -1
- package/dist/i18n/locales/fr/messages.js +1 -1
- package/dist/i18n/locales/hu/messages.js +1 -1
- package/dist/i18n/locales/it/messages.js +1 -1
- package/dist/i18n/locales/nl/messages.js +1 -1
- package/dist/i18n/locales/pl/messages.js +1 -1
- package/dist/i18n/locales/pt/messages.js +1 -1
- package/dist/i18n/locales/ro/messages.js +1 -1
- package/dist/i18n/locales/sk/messages.js +1 -1
- package/dist/i18n/locales/tr/messages.js +1 -1
- package/dist/legacy/domain-cli/utils/cache.d.ts +1 -1
- package/dist/legacy/domain-cli/utils/cache.js +1 -1
- package/dist/legacy/domain-spec/__generated/domain-schema.d.ts +79 -80
- package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/frontend-admin.js +10 -11
- package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/frontend-embedded-admin.js +9 -10
- package/dist/phoenix/application-spec/application-spec-builder.d.ts +0 -2
- package/dist/phoenix/application-spec/application-spec-builder.js +1 -1
- package/dist/phoenix/application-spec/generate-application-spec-project.d.ts +0 -2
- package/dist/phoenix/application-spec/generate-application-spec-project.js +2 -2
- package/dist/phoenix/application-spec/generate-application-spec-story.js +2 -2
- package/dist/phoenix/application-spec/generate-project-files.d.ts +0 -2
- package/dist/phoenix/application-spec/project-files.js +1 -1
- package/dist/phoenix/bin.d.ts +2 -0
- package/dist/phoenix/bin.js +2 -0
- package/dist/phoenix/check-configs/built-in-eslint-config.d.ts +9 -0
- package/dist/phoenix/check-configs/built-in-eslint-config.js +1 -0
- package/dist/phoenix/check-configs/eslint-config.d.ts +10 -0
- package/dist/phoenix/check-configs/eslint-config.js +1 -0
- package/dist/phoenix/check-configs/index.d.ts +8 -0
- package/dist/phoenix/check-configs/index.js +1 -0
- package/dist/phoenix/check-configs/typescript-project.d.ts +5 -0
- package/dist/phoenix/check-configs/typescript-project.js +1 -0
- package/dist/phoenix/cli.d.ts +0 -1
- package/dist/phoenix/cli.js +1 -2
- package/dist/phoenix/commands/build.js +1 -1
- package/dist/phoenix/commands/check.js +1 -1
- package/dist/phoenix/commands/clean.js +1 -1
- package/dist/phoenix/commands/deploy.js +2 -2
- package/dist/phoenix/commands/deps/deps.d.ts +73 -0
- package/dist/phoenix/commands/deps/deps.js +3 -0
- package/dist/phoenix/commands/deps/index.d.ts +1 -0
- package/dist/phoenix/commands/deps/index.js +1 -0
- package/dist/phoenix/commands/deps/sdk-manifest.d.ts +12 -0
- package/dist/phoenix/commands/deps/sdk-manifest.js +1 -0
- package/dist/phoenix/commands/dev.js +5 -5
- package/dist/phoenix/commands/fix.js +1 -1
- package/dist/phoenix/commands/index.d.ts +1 -0
- package/dist/phoenix/commands/index.js +1 -1
- package/dist/phoenix/commands/init.d.ts +17 -5
- package/dist/phoenix/commands/init.js +1 -1
- package/dist/phoenix/commands/link.js +1 -1
- package/dist/phoenix/commands/prettier.js +1 -1
- package/dist/phoenix/commands/sync.js +1 -1
- package/dist/phoenix/commands/unlink.js +1 -1
- package/dist/phoenix/services/application-spec-project-watcher.d.ts +2 -2
- package/dist/phoenix/services/application-spec-project-watcher.js +2 -2
- package/dist/phoenix/services/lingui.d.ts +8 -0
- package/dist/phoenix/services/lingui.js +1 -1
- package/dist/phoenix/services/swc.js +1 -1
- package/dist/phoenix/services/vite-config.js +1 -1
- package/dist/phoenix/utils/index.d.ts +4 -2
- package/dist/phoenix/utils/index.js +1 -1
- package/dist/phoenix/utils/node-version/index.d.ts +1 -0
- package/dist/phoenix/utils/node-version/index.js +1 -0
- package/dist/phoenix/utils/node-version/supported-node-version.d.ts +12 -0
- package/dist/phoenix/utils/node-version/supported-node-version.js +4 -0
- package/dist/phoenix/utils/package-info.d.ts +0 -9
- package/dist/phoenix/utils/package-info.js +1 -1
- package/dist/phoenix/utils/package-manager/index.d.ts +2 -0
- package/dist/phoenix/utils/package-manager/index.js +1 -0
- package/dist/phoenix/utils/package-manager/package-manager-commands.d.ts +19 -0
- package/dist/phoenix/utils/package-manager/package-manager-commands.js +1 -0
- package/dist/phoenix/utils/package-manager/pnpm-project.d.ts +9 -0
- package/dist/phoenix/utils/package-manager/pnpm-project.js +1 -0
- package/dist/phoenix/utils/run-command.d.ts +11 -0
- package/dist/phoenix/utils/run-command.js +1 -1
- package/dist/phoenix/utils/sdk-tool/index.d.ts +1 -0
- package/dist/phoenix/utils/sdk-tool/index.js +1 -0
- package/dist/phoenix/utils/sdk-tool/resolve-sdk-tool-bin.d.ts +15 -0
- package/dist/phoenix/utils/sdk-tool/resolve-sdk-tool-bin.js +1 -0
- package/dist/react-sdk/domain-application-context/DomainLifecycleBoundary.js +1 -1
- package/dist/react-sdk/domain-context/common-hooks/use-configurator-messages.d.ts +15 -4
- package/dist/react-sdk/domain-context/common-hooks/use-configurator-messages.js +1 -1
- package/dist/ui-sdk/theme/types.d.ts +1 -1
- package/package.json +16 -12
- package/schema/domain-spec.json +86 -8
- package/dist/application-spec/generate-application/code/templates/nvmrc.d.ts +0 -1
- package/dist/application-spec/generate-application/code/templates/nvmrc.js +0 -2
- package/dist/application-spec/generate-application/code/templates/package-json.d.ts +0 -10
- package/dist/application-spec/generate-application/code/templates/package-json.js +0 -1
- package/dist/configurator/ui/assistant/entity-link/entity-link-target.d.ts +0 -5
- package/dist/configurator/ui/assistant/entity-link/entity-link-target.js +0 -1
- package/dist/phoenix/commands/generate-project-files.d.ts +0 -6
- package/dist/phoenix/commands/generate-project-files.js +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Custom Layout
|
|
2
2
|
|
|
3
|
-
Make the configurator look like the brand's own product page. A furniture shop wants its table configurator to read like the rest of its site: a floating panel on the left with the product's name, the edge finishes as picture cards, the price in large type and one clear call to action, and the table in the space beside it. On a phone the panel becomes a sheet at the bottom, and a button on the table's edge offers the finishes right where they apply. The project writes that page as one React component. Phoenix supplies the 3D scene, the product's state and a few hooks; every pixel around the scene is the project's own.
|
|
3
|
+
Make the configurator look like the brand's own product page. A furniture shop wants its table configurator to read like the rest of its site: a floating panel on the left with the product's name, the edge finishes as picture cards, the price in large type and one clear call to action, and the table in the space beside it. On a phone the panel becomes a sheet at the bottom, and a button on the table's edge offers the finishes right where they apply, and the layout's own buttons switch between the whole table and a close-up of its edge. The project writes that page as one React component. Phoenix supplies the 3D scene, the product's state and a few hooks; every pixel around the scene is the project's own.
|
|
4
4
|
|
|
5
5
|
- [Choose this approach](#choose-this-approach)
|
|
6
6
|
- [Register the layout](#register-the-layout)
|
|
@@ -13,6 +13,7 @@ Make the configurator look like the brand's own product page. A furniture shop w
|
|
|
13
13
|
- [Serve read mode](#serve-read-mode)
|
|
14
14
|
- [Fit a phone](#fit-a-phone)
|
|
15
15
|
- [Put a control on the product](#put-a-control-on-the-product)
|
|
16
|
+
- [Control the scene](#control-the-scene)
|
|
16
17
|
- [The spec the layout reads](#the-spec-the-layout-reads)
|
|
17
18
|
- [Try the example](#try-the-example)
|
|
18
19
|
- [Test cases](#test-cases)
|
|
@@ -44,6 +45,7 @@ import { BottomSheet } from '../_shared/BottomSheet.js'
|
|
|
44
45
|
import { FullscreenToggle } from '../_shared/FullscreenToggle.js'
|
|
45
46
|
import { MessageToast } from '../_shared/MessageToast.js'
|
|
46
47
|
import { ProductPanel } from '../_shared/ProductPanel.js'
|
|
48
|
+
import { SceneControls } from '../_shared/SceneControls.js'
|
|
47
49
|
|
|
48
50
|
const GUTTER = 24
|
|
49
51
|
const PANEL_WIDTH = 380
|
|
@@ -122,14 +124,26 @@ const ConfiguratorLayout = () => {
|
|
|
122
124
|
sx={{
|
|
123
125
|
position: 'absolute',
|
|
124
126
|
pointerEvents: 'auto',
|
|
125
|
-
// Desktop: bottom left. Phone: at the top beside the fullscreen button,
|
|
127
|
+
// Desktop: bottom left, above the row of view controls. Phone: at the top beside the fullscreen button,
|
|
128
|
+
// clear of the sheet's handle and the view controls.
|
|
126
129
|
...(isDesktop
|
|
127
|
-
? { insetBlockEnd: GUTTER, insetInlineStart: GUTTER }
|
|
130
|
+
? { insetBlockEnd: 2 * GUTTER + TOUCH_TARGET, insetInlineStart: GUTTER }
|
|
128
131
|
: { insetBlockStart: gutter, insetInline: `${gutter}px ${2 * gutter + TOUCH_TARGET}px` }),
|
|
129
132
|
}}
|
|
130
133
|
>
|
|
131
134
|
<MessageToast />
|
|
132
135
|
</Box>
|
|
136
|
+
{/* A phone in fullscreen keeps only the reset, as the built-in layout does. Below 1024 pixels (`tablet`) the
|
|
137
|
+
free area beside the panel is narrow, so the reset shows only its icon there too. */}
|
|
138
|
+
<Box
|
|
139
|
+
component="div"
|
|
140
|
+
sx={{ position: 'absolute', insetBlockEnd: gutter, insetInlineEnd: gutter, pointerEvents: 'auto' }}
|
|
141
|
+
>
|
|
142
|
+
<SceneControls
|
|
143
|
+
compact={!isDesktop || (viewports?.includes('tablet') ?? false)}
|
|
144
|
+
resetOnly={!isDesktop && isFullscreen}
|
|
145
|
+
/>
|
|
146
|
+
</Box>
|
|
133
147
|
</Box>
|
|
134
148
|
)}
|
|
135
149
|
|
|
@@ -319,6 +333,8 @@ export const ProductPanel = ({ collapsed = false, inSheet = false }: ProductPane
|
|
|
319
333
|
| `phx.frontend.useComponents({ componentTypes: [finalizeActionComponent] })` | The spec's `finalizeAction` components; the panel offers the primary one in its review step. `finalizeActionComponent` comes from `@salsita-ai/sdk/domain`. |
|
|
320
334
|
| `phx.frontend.useFinalizeAction(handler)` | Run the finalize action's handler, with its pending state, its error and whether finalizing is possible; see [Request a quote](#request-a-quote). |
|
|
321
335
|
| `phx.frontend.useIsFullscreenActive()` and `phx.frontend.useSetFullscreen()` | Read the fullscreen flag, and set it from the layout's own button. |
|
|
336
|
+
| `phx.frontend.useActiveViewState()` and `phx.frontend.useSetViewState()` | Read the active view state, one of the spec's `viewStates`, to mark the active view button, and make another one active; see [Control the scene](#control-the-scene). |
|
|
337
|
+
| `phx.frontend.useResetCamera()` | Move the camera back to the framing of the active camera, undoing the customer's orbit, pan and zoom. |
|
|
322
338
|
| `useActiveViewports()` from `@salsita-ai/sdk/react` | The width classes of the configurator's container, or `null` before it is measured; the layout renders its phone arrangement unless it includes `desktop`, see [Fit a phone](#fit-a-phone). |
|
|
323
339
|
| `phx.frontend.useConfiguratorMessages(onMessage)` | Receive violations, side-effect hints and errors one by one, with their text resolved, for the layout's own toast; see [Show messages](#show-messages). |
|
|
324
340
|
| `useResolveLocalizable()` from `@salsita-ai/sdk/react` | Turns the localizable texts of the spec into strings in the current language. [Localization](?path=/docs/phoenix-localization--docs) describes it and `LocalizableMessage`. |
|
|
@@ -524,7 +540,7 @@ The button stays in the free area in both states, so the way back is always wher
|
|
|
524
540
|
|
|
525
541
|
## Show messages
|
|
526
542
|
|
|
527
|
-
When a change is rejected because it breaks an invariant, when a committed change carries a hint,
|
|
543
|
+
When a change is rejected because it breaks an invariant, when a committed change carries a hint, when an error is thrown while a change is validated, or when saving the configuration fails and later succeeds again, the configurator has a message for the customer. `phx.frontend.useConfiguratorMessages(onMessage)` calls `onMessage` once for each, with the text already in the customer's language, and the layout shows it its own way. Here a toast shows a message for six seconds, colored by its severity, at the bottom left of the free area above the view controls on a desktop, and at its top on a phone:
|
|
528
544
|
|
|
529
545
|
```tsx
|
|
530
546
|
import { Alert } from '@mui/material'
|
|
@@ -543,7 +559,7 @@ const severityOf = (message: ConfiguratorMessage) => {
|
|
|
543
559
|
return VIOLATION_SEVERITY[message.violation.severity]
|
|
544
560
|
}
|
|
545
561
|
|
|
546
|
-
return message.kind === 'error' ? 'error' : 'info'
|
|
562
|
+
return message.kind === 'error' || message.kind === 'saveError' ? 'error' : 'info'
|
|
547
563
|
}
|
|
548
564
|
|
|
549
565
|
export const MessageToast = () => {
|
|
@@ -580,7 +596,7 @@ export const MessageToast = () => {
|
|
|
580
596
|
}
|
|
581
597
|
```
|
|
582
598
|
|
|
583
|
-
The hook takes a new function on every render without missing a message, so passing the state setter, or an inline function, is fine. `message.kind` is `'violation'`, `'sideEffect'` or `'
|
|
599
|
+
The hook takes a new function on every render without missing a message, so passing the state setter, or an inline function, is fine. `message.kind` is `'violation'`, `'sideEffect'`, `'error'`, `'saveError'` or `'saveRecovery'`; the toast shows an error and a failed save as errors, a hint and the save that succeeded again as information. A violation also carries `entityId` and the `violation` itself, with its `severity`. A rejected change brings one message for each violation of the rejected state, including notices and warnings that were already present, so the toast keeps the most severe message while it is shown instead of letting a later notice replace the reason for the rejection. The layout mounts the toast in the safe-area element, which sets `pointerEvents: 'none'`, so the toast's wrapper switches pointer events back on for the close button.
|
|
584
600
|
|
|
585
601
|
To give the toast something to show, the spec registers one transformer. Choosing **Tree trunk** keeps the change and adds a hint to it, which arrives as a `sideEffect` message:
|
|
586
602
|
|
|
@@ -603,7 +619,7 @@ export default phx.frontend.transformerBuilder.when
|
|
|
603
619
|
.build('tree-trunk-delivery')
|
|
604
620
|
```
|
|
605
621
|
|
|
606
|
-
[Configurator Interface → Loaded Configuration UI](?path=/docs/phoenix-configurator-interface--docs#loaded-configuration-ui) lists the messages the hook does not deliver:
|
|
622
|
+
[Configurator Interface → Loaded Configuration UI](?path=/docs/phoenix-configurator-interface--docs#loaded-configuration-ui) lists the messages the hook does not deliver: errors of the built-in finalize and other actions stay snackbars, and a failure to start from a preset is raised on the preset screen.
|
|
607
623
|
|
|
608
624
|
## Request a quote
|
|
609
625
|
|
|
@@ -1015,9 +1031,154 @@ export default phx.frontend.createCustomPhoenixEntityElement('EdgeFinishHotspot'
|
|
|
1015
1031
|
|
|
1016
1032
|
[Html Hotspots](?path=/docs/controls-scene-html-hotspots--docs) describes the component and its other properties.
|
|
1017
1033
|
|
|
1034
|
+
## Control the scene
|
|
1035
|
+
|
|
1036
|
+
The four finishes differ by a few millimetres on a table two metres long, so from the default distance the difference is hard to judge. The layout offers two views, **Whole table** and **Edge close-up**, a close and low look at a corner of the top, and a **Reset view** button that brings back the framing after the customer has orbited, panned or zoomed.
|
|
1037
|
+
|
|
1038
|
+
**Views are view states.** The spec lists them, and each camera says by its `$filter` in which view states and at which widths it applies. The two close-up cameras have the highest priorities and apply only in the `edge` view state, the desktop one with `desktop` and the phone one with `mobile`, a little farther away. In the `overview` view state, the first in the list and so the initial one, only the two whole-table cameras of [Fit a phone](#fit-a-phone) apply. A camera always looks at the middle of the table, so the close-up shows the near corner, where the edge profile is seen on both sides:
|
|
1039
|
+
|
|
1040
|
+
```yaml
|
|
1041
|
+
viewStates:
|
|
1042
|
+
- overview
|
|
1043
|
+
- edge
|
|
1044
|
+
|
|
1045
|
+
components:
|
|
1046
|
+
Table:
|
|
1047
|
+
fitRootPerspectiveCamera:
|
|
1048
|
+
edge:
|
|
1049
|
+
priority: 4
|
|
1050
|
+
fov: 35
|
|
1051
|
+
near: 100
|
|
1052
|
+
far: 10000
|
|
1053
|
+
minDistance: 800
|
|
1054
|
+
maxDistance: 9000
|
|
1055
|
+
distance: 2000
|
|
1056
|
+
azimuthAngle: 0.5
|
|
1057
|
+
polarAngle: 1.15
|
|
1058
|
+
fitMeshChanges: true
|
|
1059
|
+
$filter:
|
|
1060
|
+
viewports: [desktop]
|
|
1061
|
+
viewStates: [edge]
|
|
1062
|
+
edgePhone:
|
|
1063
|
+
priority: 3
|
|
1064
|
+
fov: 35
|
|
1065
|
+
near: 100
|
|
1066
|
+
far: 10000
|
|
1067
|
+
minDistance: 800
|
|
1068
|
+
maxDistance: 12000
|
|
1069
|
+
distance: 3000
|
|
1070
|
+
azimuthAngle: 0.5
|
|
1071
|
+
polarAngle: 1.15
|
|
1072
|
+
fitMeshChanges: true
|
|
1073
|
+
$filter:
|
|
1074
|
+
viewports: [mobile]
|
|
1075
|
+
viewStates: [edge]
|
|
1076
|
+
```
|
|
1077
|
+
|
|
1078
|
+
**The buttons.** `phx.frontend.useActiveViewState()` returns the active view state and `phx.frontend.useSetViewState()` a function that makes another one active; the camera then moves to the camera of that view state. `phx.frontend.useResetCamera()` returns a function that moves the camera back to the framing of the active camera, the whole table or the close-up:
|
|
1079
|
+
|
|
1080
|
+
```tsx
|
|
1081
|
+
import { Box, Button, Tooltip } from '@mui/material'
|
|
1082
|
+
|
|
1083
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
1084
|
+
|
|
1085
|
+
// The spec's view states, each with the label of the button that shows it
|
|
1086
|
+
const VIEWS = [
|
|
1087
|
+
{ state: 'overview', label: 'Whole table' },
|
|
1088
|
+
{ state: 'edge', label: 'Edge close-up' },
|
|
1089
|
+
] as const
|
|
1090
|
+
|
|
1091
|
+
const RESET_ICON = 'M4 12a8 8 0 1 0 2.4-5.7M4 4v4h4'
|
|
1092
|
+
|
|
1093
|
+
const SURFACE_SX = {
|
|
1094
|
+
borderRadius: 999,
|
|
1095
|
+
bgcolor: 'background.paper',
|
|
1096
|
+
boxShadow: '0 4px 16px rgba(0, 0, 0, 0.12)',
|
|
1097
|
+
} as const
|
|
1098
|
+
|
|
1099
|
+
const BUTTON_SX = {
|
|
1100
|
+
minBlockSize: 44,
|
|
1101
|
+
minInlineSize: 44,
|
|
1102
|
+
borderRadius: 999,
|
|
1103
|
+
textTransform: 'none',
|
|
1104
|
+
fontWeight: 600,
|
|
1105
|
+
whiteSpace: 'nowrap',
|
|
1106
|
+
} as const
|
|
1107
|
+
|
|
1108
|
+
interface SceneControlsProps {
|
|
1109
|
+
// The reset button shows only its icon, with its label as tooltip and accessible name
|
|
1110
|
+
readonly compact?: boolean
|
|
1111
|
+
// Only the reset button, without the view buttons
|
|
1112
|
+
readonly resetOnly?: boolean
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
// The layout's own camera controls: one button per view state of the spec, and a reset that undoes an orbit
|
|
1116
|
+
export const SceneControls = ({ compact = false, resetOnly = false }: SceneControlsProps) => {
|
|
1117
|
+
const activeView = phx.frontend.useActiveViewState()
|
|
1118
|
+
const setViewState = phx.frontend.useSetViewState()
|
|
1119
|
+
const resetCamera = phx.frontend.useResetCamera()
|
|
1120
|
+
const resetIcon = (
|
|
1121
|
+
<svg viewBox="0 0 24 24" width={20} height={20} fill="none" stroke="currentColor" strokeWidth={2}>
|
|
1122
|
+
<path d={RESET_ICON} />
|
|
1123
|
+
</svg>
|
|
1124
|
+
)
|
|
1125
|
+
|
|
1126
|
+
return (
|
|
1127
|
+
<Box component="div" sx={{ display: 'flex', gap: 1 }}>
|
|
1128
|
+
{!resetOnly && (
|
|
1129
|
+
<Box component="div" role="group" aria-label="View" sx={{ ...SURFACE_SX, display: 'flex' }}>
|
|
1130
|
+
{VIEWS.map(view => {
|
|
1131
|
+
const isActive = activeView === view.state
|
|
1132
|
+
|
|
1133
|
+
return (
|
|
1134
|
+
<Button
|
|
1135
|
+
key={view.state}
|
|
1136
|
+
aria-pressed={isActive}
|
|
1137
|
+
onClick={() => setViewState(view.state)}
|
|
1138
|
+
sx={{
|
|
1139
|
+
...BUTTON_SX,
|
|
1140
|
+
paddingInline: 2,
|
|
1141
|
+
bgcolor: isActive ? 'text.primary' : 'transparent',
|
|
1142
|
+
color: isActive ? 'background.paper' : 'text.primary',
|
|
1143
|
+
'&:hover': { bgcolor: isActive ? 'text.primary' : 'action.hover' },
|
|
1144
|
+
}}
|
|
1145
|
+
>
|
|
1146
|
+
{view.label}
|
|
1147
|
+
</Button>
|
|
1148
|
+
)
|
|
1149
|
+
})}
|
|
1150
|
+
</Box>
|
|
1151
|
+
)}
|
|
1152
|
+
<Tooltip title={compact ? 'Reset view' : ''}>
|
|
1153
|
+
<Button
|
|
1154
|
+
onClick={resetCamera}
|
|
1155
|
+
aria-label={compact ? 'Reset view' : undefined}
|
|
1156
|
+
startIcon={compact ? undefined : resetIcon}
|
|
1157
|
+
sx={{
|
|
1158
|
+
...SURFACE_SX,
|
|
1159
|
+
...BUTTON_SX,
|
|
1160
|
+
paddingInline: compact ? 0 : 2,
|
|
1161
|
+
color: 'text.primary',
|
|
1162
|
+
'&:hover': { bgcolor: 'background.paper', boxShadow: '0 6px 20px rgba(0, 0, 0, 0.18)' },
|
|
1163
|
+
}}
|
|
1164
|
+
>
|
|
1165
|
+
{compact ? resetIcon : 'Reset view'}
|
|
1166
|
+
</Button>
|
|
1167
|
+
</Tooltip>
|
|
1168
|
+
</Box>
|
|
1169
|
+
)
|
|
1170
|
+
}
|
|
1171
|
+
```
|
|
1172
|
+
|
|
1173
|
+
- The view buttons are a group with `aria-pressed`, so a screen reader announces which view is active, and the active one is drawn dark, not only in another colour.
|
|
1174
|
+
- On a desktop the controls sit at the bottom right of the free area and the toast above its bottom left; on a phone they sit at the bottom of the free area, just above the sheet. On a phone, and on a desktop arrangement below 1024 pixels, where `tablet` is also active and the free area beside the panel is narrow, **Reset view** shows only its icon, with its label as tooltip and accessible name.
|
|
1175
|
+
- In fullscreen the controls stay on a desktop. On a phone the view buttons are hidden and **Reset view** stays, as [Interaction Modes](?path=/docs/ux-guidelines-interaction-modes--docs#full-screen-preview) describes for the built-in layout: on a phone the canvas is small and the product comes first, and the way back to the framed product is still needed after an orbit.
|
|
1176
|
+
- The controls stay in the review step and in read mode: changing the view changes nothing in the configuration, and a viewer may look at the product as closely as the customer.
|
|
1177
|
+
- The hooks need no `viewControls` component. That component configures the built-in layout's view controls and has no effect here.
|
|
1178
|
+
|
|
1018
1179
|
## The spec the layout reads
|
|
1019
1180
|
|
|
1020
|
-
The layout reads four components of the root entity and its navigation states; the scene components are in the complete implementation below.
|
|
1181
|
+
The layout reads four components of the root entity and its navigation and view states; the scene components are in the complete implementation below. `SceneControls` names the view states `overview` and `edge` in its code, so they must match the spec's `viewStates`.
|
|
1021
1182
|
|
|
1022
1183
|
```yaml
|
|
1023
1184
|
navigationStates:
|
|
@@ -1025,6 +1186,10 @@ navigationStates:
|
|
|
1025
1186
|
- checkout
|
|
1026
1187
|
- requested
|
|
1027
1188
|
|
|
1189
|
+
viewStates:
|
|
1190
|
+
- overview
|
|
1191
|
+
- edge
|
|
1192
|
+
|
|
1028
1193
|
components:
|
|
1029
1194
|
Table:
|
|
1030
1195
|
detail:
|
|
@@ -1077,7 +1242,7 @@ The same layout in a frame 390 pixels wide, as on a phone. Opened on its own fro
|
|
|
1077
1242
|
|
|
1078
1243
|
- The panel is on the left and the table is centred in the area to the right of it, not in the whole canvas.
|
|
1079
1244
|
- Choosing **Tree trunk** moves the check mark to that card, changes the edge of the tabletop and raises the price from $1,290.00 to $1,630.00.
|
|
1080
|
-
- Choosing **Tree trunk** also shows "Tree trunk edges are cut by hand, so delivery takes three weeks longer." at the bottom of the free area, until its close button is clicked or six seconds pass. Choosing another finish shows nothing.
|
|
1245
|
+
- Choosing **Tree trunk** also shows "Tree trunk edges are cut by hand, so delivery takes three weeks longer." at the bottom left of the free area, above the view controls, until its close button is clicked or six seconds pass. Choosing another finish shows nothing.
|
|
1081
1246
|
- Tab reaches the selected card, and the arrow keys move the selection to the next or previous card.
|
|
1082
1247
|
- Dragging in the area to the right of the panel orbits the camera.
|
|
1083
1248
|
- **Review and request a quote** replaces the cards with the chosen finish and offers **Request a quote**; **Back to the configuration** brings the cards back.
|
|
@@ -1092,14 +1257,18 @@ The same layout in a frame 390 pixels wide, as on a phone. Opened on its own fro
|
|
|
1092
1257
|
- On the phone, the review step, **Request a quote** and the thank-you message work with the sheet collapsed and open.
|
|
1093
1258
|
- On the phone, choosing **Tree trunk** shows the hint at the top of the free area, beside the fullscreen button.
|
|
1094
1259
|
- On the phone, the fullscreen button removes the sheet and the table moves to the centre of the canvas; pressing it again brings the sheet back in the position it had.
|
|
1260
|
+
- **Edge close-up** moves the camera close to the near corner of the top, where the edge profile shows; the button is drawn dark and the other light. **Whole table** returns to the whole table.
|
|
1261
|
+
- After dragging in the free area to orbit the table, **Reset view** returns to the framing of the active view, the whole table or the close-up.
|
|
1262
|
+
- The view buttons and **Reset view** sit at the bottom right of the free area on a desktop and just above the sheet on a phone, and overlap neither the panel, the sheet, the button on the edge nor the toast.
|
|
1263
|
+
- In fullscreen the view controls stay on a desktop. On a phone the view buttons are hidden and **Reset view** stays; after an orbit it restores the framing.
|
|
1095
1264
|
|
|
1096
1265
|
## Known gaps
|
|
1097
1266
|
|
|
1098
1267
|
A project layout has only what it renders. This one leaves out the following, each of which a product may need. [Layout Slots](?path=/docs/phoenix-layout-slots--docs) describes every `LayoutSlots` member named here.
|
|
1099
1268
|
|
|
1100
|
-
- **Messages that stay snackbars.** The toast shows violations, side effects and
|
|
1101
|
-
- **Toolbar actions.** Undo and redo,
|
|
1102
|
-
- **
|
|
1269
|
+
- **Messages that stay snackbars.** The toast shows violations, side effects, errors and failed saves, and the finalize button shows its own error. Two kinds of message stay snackbars. An error of a built-in action, such as one from `LayoutSlots.DesktopToolbar`, renders into `LayoutSlots.DesktopSnackbarsRoot`; this layout mounts neither, and a layout that mounts such an action mounts the root too, or the snackbar appears outside the configurator. A failure to start from a preset appears on the preset screen, before this layout mounts; a project replaces that screen with `phx.frontend.createPresetUi`, see [Configurator Interface → Preset UI](?path=/docs/phoenix-configurator-interface--docs#preset-ui).
|
|
1270
|
+
- **Toolbar actions.** Undo and redo, measurements, sharing and language switching are not offered. A layout can mount `LayoutSlots.DesktopToolbar` for the toolbar the spec configures, or build its own buttons.
|
|
1271
|
+
- **The spec's view controls, tool controls and leaving a tool mode.** A spec can configure view controls and tool controls, and a tool mode has a button that leaves it. The built-in layout places them with `LayoutSlots.DesktopViewControls`, `LayoutSlots.DesktopToolControls` and `LayoutSlots.DesktopCallToAction`; this layout mounts none of them, so the spec's `viewControls` and `toolControls` do not appear. Its views come from its own buttons instead, see [Control the scene](#control-the-scene), and it has no tool modes.
|
|
1103
1272
|
- **A project's layout dialog.** A dialog the project registers with `phx.frontend.createLayoutDialog` is shown by `LayoutDialog`, which the built-in layout mounts. This layout does not; a layout that needs the dialog mounts `LayoutDialog` from `@salsita-ai/sdk/configurator`.
|
|
1104
1273
|
- **Selection.** The table has no parts to select. A product with selectable parts reads `phx.frontend.useSelectedDomainEntityId()` and shows the controls of the selected part.
|
|
1105
1274
|
- **Clone to edit in read mode.** With `shareCloneToEdit` set, the built-in layout's `LayoutSlots.DesktopReadonlyBanner` offers **Edit configuration** to a viewer; this layout's "View only" label offers nothing. A layout mounts the banner, or builds its own action with `LayoutSlots.useIsCloningAllowed()` and `LayoutSlots.CloneToEditDialog`.
|
|
@@ -1158,6 +1327,10 @@ navigationStates:
|
|
|
1158
1327
|
- checkout
|
|
1159
1328
|
- requested
|
|
1160
1329
|
|
|
1330
|
+
viewStates:
|
|
1331
|
+
- overview
|
|
1332
|
+
- edge
|
|
1333
|
+
|
|
1161
1334
|
components:
|
|
1162
1335
|
Table:
|
|
1163
1336
|
detail:
|
|
@@ -1229,6 +1402,34 @@ components:
|
|
|
1229
1402
|
fitMeshChanges: true
|
|
1230
1403
|
$filter:
|
|
1231
1404
|
viewports: [mobile]
|
|
1405
|
+
edge:
|
|
1406
|
+
priority: 4
|
|
1407
|
+
fov: 35
|
|
1408
|
+
near: 100
|
|
1409
|
+
far: 10000
|
|
1410
|
+
minDistance: 800
|
|
1411
|
+
maxDistance: 9000
|
|
1412
|
+
distance: 2000
|
|
1413
|
+
azimuthAngle: 0.5
|
|
1414
|
+
polarAngle: 1.15
|
|
1415
|
+
fitMeshChanges: true
|
|
1416
|
+
$filter:
|
|
1417
|
+
viewports: [desktop]
|
|
1418
|
+
viewStates: [edge]
|
|
1419
|
+
edgePhone:
|
|
1420
|
+
priority: 3
|
|
1421
|
+
fov: 35
|
|
1422
|
+
near: 100
|
|
1423
|
+
far: 10000
|
|
1424
|
+
minDistance: 800
|
|
1425
|
+
maxDistance: 12000
|
|
1426
|
+
distance: 3000
|
|
1427
|
+
azimuthAngle: 0.5
|
|
1428
|
+
polarAngle: 1.15
|
|
1429
|
+
fitMeshChanges: true
|
|
1430
|
+
$filter:
|
|
1431
|
+
viewports: [mobile]
|
|
1432
|
+
viewStates: [edge]
|
|
1232
1433
|
sceneEntityRemoteObject:
|
|
1233
1434
|
top:
|
|
1234
1435
|
fileUrl: /models/tables.gltf
|
|
@@ -1645,7 +1846,7 @@ const severityOf = (message: ConfiguratorMessage) => {
|
|
|
1645
1846
|
return VIOLATION_SEVERITY[message.violation.severity]
|
|
1646
1847
|
}
|
|
1647
1848
|
|
|
1648
|
-
return message.kind === 'error' ? 'error' : 'info'
|
|
1849
|
+
return message.kind === 'error' || message.kind === 'saveError' ? 'error' : 'info'
|
|
1649
1850
|
}
|
|
1650
1851
|
|
|
1651
1852
|
export const MessageToast = () => {
|
|
@@ -1879,6 +2080,101 @@ export const ReviewActions = ({ handler, label, onBack }: ReviewActionsProps) =>
|
|
|
1879
2080
|
}
|
|
1880
2081
|
```
|
|
1881
2082
|
|
|
2083
|
+
### ./src/\_shared/SceneControls.tsx
|
|
2084
|
+
|
|
2085
|
+
```tsx
|
|
2086
|
+
import { Box, Button, Tooltip } from '@mui/material'
|
|
2087
|
+
|
|
2088
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
2089
|
+
|
|
2090
|
+
// The spec's view states, each with the label of the button that shows it
|
|
2091
|
+
const VIEWS = [
|
|
2092
|
+
{ state: 'overview', label: 'Whole table' },
|
|
2093
|
+
{ state: 'edge', label: 'Edge close-up' },
|
|
2094
|
+
] as const
|
|
2095
|
+
|
|
2096
|
+
const RESET_ICON = 'M4 12a8 8 0 1 0 2.4-5.7M4 4v4h4'
|
|
2097
|
+
|
|
2098
|
+
const SURFACE_SX = {
|
|
2099
|
+
borderRadius: 999,
|
|
2100
|
+
bgcolor: 'background.paper',
|
|
2101
|
+
boxShadow: '0 4px 16px rgba(0, 0, 0, 0.12)',
|
|
2102
|
+
} as const
|
|
2103
|
+
|
|
2104
|
+
const BUTTON_SX = {
|
|
2105
|
+
minBlockSize: 44,
|
|
2106
|
+
minInlineSize: 44,
|
|
2107
|
+
borderRadius: 999,
|
|
2108
|
+
textTransform: 'none',
|
|
2109
|
+
fontWeight: 600,
|
|
2110
|
+
whiteSpace: 'nowrap',
|
|
2111
|
+
} as const
|
|
2112
|
+
|
|
2113
|
+
interface SceneControlsProps {
|
|
2114
|
+
// The reset button shows only its icon, with its label as tooltip and accessible name
|
|
2115
|
+
readonly compact?: boolean
|
|
2116
|
+
// Only the reset button, without the view buttons
|
|
2117
|
+
readonly resetOnly?: boolean
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
// The layout's own camera controls: one button per view state of the spec, and a reset that undoes an orbit
|
|
2121
|
+
export const SceneControls = ({ compact = false, resetOnly = false }: SceneControlsProps) => {
|
|
2122
|
+
const activeView = phx.frontend.useActiveViewState()
|
|
2123
|
+
const setViewState = phx.frontend.useSetViewState()
|
|
2124
|
+
const resetCamera = phx.frontend.useResetCamera()
|
|
2125
|
+
const resetIcon = (
|
|
2126
|
+
<svg viewBox="0 0 24 24" width={20} height={20} fill="none" stroke="currentColor" strokeWidth={2}>
|
|
2127
|
+
<path d={RESET_ICON} />
|
|
2128
|
+
</svg>
|
|
2129
|
+
)
|
|
2130
|
+
|
|
2131
|
+
return (
|
|
2132
|
+
<Box component="div" sx={{ display: 'flex', gap: 1 }}>
|
|
2133
|
+
{!resetOnly && (
|
|
2134
|
+
<Box component="div" role="group" aria-label="View" sx={{ ...SURFACE_SX, display: 'flex' }}>
|
|
2135
|
+
{VIEWS.map(view => {
|
|
2136
|
+
const isActive = activeView === view.state
|
|
2137
|
+
|
|
2138
|
+
return (
|
|
2139
|
+
<Button
|
|
2140
|
+
key={view.state}
|
|
2141
|
+
aria-pressed={isActive}
|
|
2142
|
+
onClick={() => setViewState(view.state)}
|
|
2143
|
+
sx={{
|
|
2144
|
+
...BUTTON_SX,
|
|
2145
|
+
paddingInline: 2,
|
|
2146
|
+
bgcolor: isActive ? 'text.primary' : 'transparent',
|
|
2147
|
+
color: isActive ? 'background.paper' : 'text.primary',
|
|
2148
|
+
'&:hover': { bgcolor: isActive ? 'text.primary' : 'action.hover' },
|
|
2149
|
+
}}
|
|
2150
|
+
>
|
|
2151
|
+
{view.label}
|
|
2152
|
+
</Button>
|
|
2153
|
+
)
|
|
2154
|
+
})}
|
|
2155
|
+
</Box>
|
|
2156
|
+
)}
|
|
2157
|
+
<Tooltip title={compact ? 'Reset view' : ''}>
|
|
2158
|
+
<Button
|
|
2159
|
+
onClick={resetCamera}
|
|
2160
|
+
aria-label={compact ? 'Reset view' : undefined}
|
|
2161
|
+
startIcon={compact ? undefined : resetIcon}
|
|
2162
|
+
sx={{
|
|
2163
|
+
...SURFACE_SX,
|
|
2164
|
+
...BUTTON_SX,
|
|
2165
|
+
paddingInline: compact ? 0 : 2,
|
|
2166
|
+
color: 'text.primary',
|
|
2167
|
+
'&:hover': { bgcolor: 'background.paper', boxShadow: '0 6px 20px rgba(0, 0, 0, 0.18)' },
|
|
2168
|
+
}}
|
|
2169
|
+
>
|
|
2170
|
+
{compact ? resetIcon : 'Reset view'}
|
|
2171
|
+
</Button>
|
|
2172
|
+
</Tooltip>
|
|
2173
|
+
</Box>
|
|
2174
|
+
)
|
|
2175
|
+
}
|
|
2176
|
+
```
|
|
2177
|
+
|
|
1882
2178
|
### ./src/elements/ConfiguratorLayout.tsx
|
|
1883
2179
|
|
|
1884
2180
|
```tsx
|
|
@@ -1893,6 +2189,7 @@ import { BottomSheet } from '../_shared/BottomSheet.js'
|
|
|
1893
2189
|
import { FullscreenToggle } from '../_shared/FullscreenToggle.js'
|
|
1894
2190
|
import { MessageToast } from '../_shared/MessageToast.js'
|
|
1895
2191
|
import { ProductPanel } from '../_shared/ProductPanel.js'
|
|
2192
|
+
import { SceneControls } from '../_shared/SceneControls.js'
|
|
1896
2193
|
|
|
1897
2194
|
const GUTTER = 24
|
|
1898
2195
|
const PANEL_WIDTH = 380
|
|
@@ -1971,14 +2268,26 @@ const ConfiguratorLayout = () => {
|
|
|
1971
2268
|
sx={{
|
|
1972
2269
|
position: 'absolute',
|
|
1973
2270
|
pointerEvents: 'auto',
|
|
1974
|
-
// Desktop: bottom left. Phone: at the top beside the fullscreen button,
|
|
2271
|
+
// Desktop: bottom left, above the row of view controls. Phone: at the top beside the fullscreen button,
|
|
2272
|
+
// clear of the sheet's handle and the view controls.
|
|
1975
2273
|
...(isDesktop
|
|
1976
|
-
? { insetBlockEnd: GUTTER, insetInlineStart: GUTTER }
|
|
2274
|
+
? { insetBlockEnd: 2 * GUTTER + TOUCH_TARGET, insetInlineStart: GUTTER }
|
|
1977
2275
|
: { insetBlockStart: gutter, insetInline: `${gutter}px ${2 * gutter + TOUCH_TARGET}px` }),
|
|
1978
2276
|
}}
|
|
1979
2277
|
>
|
|
1980
2278
|
<MessageToast />
|
|
1981
2279
|
</Box>
|
|
2280
|
+
{/* A phone in fullscreen keeps only the reset, as the built-in layout does. Below 1024 pixels (`tablet`) the
|
|
2281
|
+
free area beside the panel is narrow, so the reset shows only its icon there too. */}
|
|
2282
|
+
<Box
|
|
2283
|
+
component="div"
|
|
2284
|
+
sx={{ position: 'absolute', insetBlockEnd: gutter, insetInlineEnd: gutter, pointerEvents: 'auto' }}
|
|
2285
|
+
>
|
|
2286
|
+
<SceneControls
|
|
2287
|
+
compact={!isDesktop || (viewports?.includes('tablet') ?? false)}
|
|
2288
|
+
resetOnly={!isDesktop && isFullscreen}
|
|
2289
|
+
/>
|
|
2290
|
+
</Box>
|
|
1982
2291
|
</Box>
|
|
1983
2292
|
)}
|
|
1984
2293
|
|
|
@@ -14,7 +14,7 @@ useAuthHook: true
|
|
|
14
14
|
useClerk: true
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
`@clerk/clerk-react` then becomes a dependency the project declares: run `phoenix deps` through your package manager (for example `pnpm phoenix deps`) and install to add it, because `phoenix sync`, `dev` and `build` stop while it is missing. `phoenix sync` generates typed Clerk environment readers. In a project on the legacy pipeline (see [Current and legacy project tooling](?path=/docs/phoenix-phoenix-cli--docs#current-and-legacy-project-tooling)), `phoenix sync` adds the dependency itself. `phoenix deployment:prepare-local` writes the deployment's public `CLERK_PUBLISHABLE_KEY` to `.env.generated`; application source should read it from `phx.frontendEnv`, not hardcode a key.
|
|
18
18
|
|
|
19
19
|
## Configure the auth webhook
|
|
20
20
|
|
|
@@ -273,6 +273,12 @@ components:
|
|
|
273
273
|
|
|
274
274
|
`$bind` is essential for form controls that need to show current state and accept changes, any UI element that both displays and modifies data, and interactive controls like attribute selectors, toggles, sliders, and input fields.
|
|
275
275
|
|
|
276
|
+
### Types of bound values
|
|
277
|
+
|
|
278
|
+
The build checks that the field a `$read` or `$bind` reads fits the prop it is read into, the filter criterion, or, for `$filter: { $read: field }`, the whole filter. A `string` field read into a number prop, a nullable field read into a text, or an array read into a boolean is reported as `COMPONENT_BINDING_TYPE_MISMATCH` at the prop. A field whose schema gives its type only as `tsType` is not checked here. Expressions are checked where their type is settled.
|
|
279
|
+
|
|
280
|
+
A prop or filter criterion that takes an entity id, such as the `value` of `childEntitySelection` or `selectedEntityIds`, cannot hold an id written in YAML: an entity id is a branded type that only comes from the domain state. Bind it to a field or expression that holds the id; a written id is reported as `COMPONENT_STATIC_ENTITY_ID`.
|
|
281
|
+
|
|
276
282
|
### `$eval` - Inline Expressions
|
|
277
283
|
|
|
278
284
|
`$eval` allows simple dynamic calculations directly in YAML without defining separate `$expression` implementations in TypeScript. It can appear wherever `$read` is allowed — including in component filter `active` properties — and is mutually exclusive with `$read` on the same property, but both can coexist on the same component instance across different properties.
|
|
@@ -222,7 +222,7 @@ const controlViolations = phx.frontend.useControlViolations([])
|
|
|
222
222
|
// Finalize violations
|
|
223
223
|
const { disabled, hints } = phx.frontend.useFinalizeViolations()
|
|
224
224
|
|
|
225
|
-
// Messages for the user: violations of a rejected change, side-effect hints and
|
|
225
|
+
// Messages for the user: violations of a rejected change, side-effect hints, errors and failed saves, one call each
|
|
226
226
|
phx.frontend.useConfiguratorMessages(message => showToast(message.text))
|
|
227
227
|
|
|
228
228
|
// Map violations to hints
|
|
@@ -433,10 +433,10 @@ export default phx.frontend.createCustomPhoenixLayoutElement('ConfiguratorLayout
|
|
|
433
433
|
- `LayoutSlots.DesktopSidebar` is the built-in desktop sidebar as one piece: the spec's tabs, back button, property pages and sidebar footer with their spacing, scrolling and empty states, and the assistant panel. Use it when the spec's property pages are what the customer should see and only their place changes, for example a sidebar on the left; build the controls yourself when they have to look like the brand's own. Without `width` it fills its container, so the layout positions and sizes that container; `miniSidebar` does not shrink it, so a layout that honours it reads `LayoutSlots.useSidebarContent().hasMiniSidebar` and lets the container take the sidebar's height. Its tabs sit outside it on the left; `side="start"` puts them on its right, for a sidebar on the left. In fullscreen it stays in place, transparent and inert, so a layout that wants the space back removes it while `phx.frontend.useIsFullscreenActive()` is set. See [Sidebar in a Custom Layout](?path=/docs/layouts-sidebar-in-a-custom-layout--docs).
|
|
434
434
|
- Nothing of the built-in layouts is rendered for it. A project layout finalizes over the spec's `finalizeAction` components, see the finalize paragraph of [Hooks for a Project Layout](#hooks-for-a-project-layout). The `finalizePage` and `finalizeButton` components are legacy: they cannot be declared in YAML, only in TypeScript, and `Legacy.CommonFinalizePage`, which is deprecated, shows a `finalizePage` for projects that still have one. `LayoutDialog` renders only what the project registers with `createLayoutDialog` and is not part of the finalize flow. The preview start screen is `DesktopPreview` or `MobilePreview`, which the layout shows while `usePreview().isPreview` is set. Both previews frame the scene themselves, so the layout renders the component that calls `useSceneSafeArea()` only while `isPreview` is not set; it then measures when it mounts after the preview. The assistant panel is available to a project layout only inside `LayoutSlots.DesktopSidebar`.
|
|
435
435
|
- The layout reads the state it has to react to from hooks, listed with what each returns in [Hooks for a Project Layout](#hooks-for-a-project-layout).
|
|
436
|
-
- Messages for the user come one of two ways. `LayoutSlots.Services` shows violations of a rejected change, side-effect hints and
|
|
436
|
+
- Messages for the user come one of two ways. `LayoutSlots.Services` shows violations of a rejected change, side-effect hints, errors and failed saves as the built-in snackbars, which render into `LayoutSlots.DesktopSnackbarsRoot` or `LayoutSlots.MobileSnackbarsRoot` wherever the layout mounts it. `phx.frontend.useConfiguratorMessages(onMessage)` hands the same messages to the layout's own UI instead, with their text resolved, as the toast in [Custom Layout](?path=/docs/layouts-custom-layout--docs#show-messages) does; mount one of the two, not both, or every message is shown twice. A failure to save the configuration, and the first successful save after it, are messages too, so a layout that mounts neither shows no sign of a failed save. An error of the built-in finalize and other actions stays a snackbar either way. It renders into the snackbar root, so a layout that mounts none shows it outside the configurator, in the page's `body`. A failure to start a configuration from a preset is not a message of the hook either: it happens on the preset screen, before any `ConfiguratorLayout` mounts. In production with more than one preset it is a snackbar there, also on a preset screen replaced with `createPresetUi` (see [Preset UI](#preset-ui)), which reads the error from `phx.frontend.usePresetSelection().selectionError` and mounts `PresetSnackbarsRoot` to keep the snackbar inside the configurator. A finalize action the layout runs with `phx.frontend.useFinalizeAction` reports its error as that hook's `error` instead, neither as a message nor as a snackbar.
|
|
437
437
|
- Any project layout element named `ConfiguratorLayout` takes over the page.
|
|
438
438
|
|
|
439
|
-
`defaultConfiguratorElements`, exported from `@salsita-ai/sdk/configurator`, holds the built-in UI as a `ConfiguratorLayout` registration that renders the layout the spec selects. Generated code registers these defaults with `withDefaultElements`, apart from the project's elements, so the built-in UI is a registration like any other; a project element registered under the same name wins over a default.
|
|
439
|
+
`defaultConfiguratorElements`, exported from `@salsita-ai/sdk/configurator`, holds the built-in UI as a `ConfiguratorLayout` registration that renders the layout the spec selects. Generated code registers these defaults with `withDefaultElements`, apart from the project's elements, so the built-in UI is a registration like any other; a project element registered under the same name wins over a default. The list also holds the configurator's built-in controls and layout elements under the kinds the spec names them by, such as `attributeSelect` or `undoToolbarButton`, so a project element registered under one of those names replaces the built-in one wherever the spec names that kind. A config assembled by hand, without generated code, calls `withDefaultElements(defaultConfiguratorElements)` too; the lists are the only way the built-in elements are registered, so without it the page shows a "not found" box instead of the configurator, and a project layout shows one in place of every built-in control. The admin mounts a full configurator, so its list, `defaultAdminElements` from `@salsita-ai/sdk/admin`, holds the configurator's defaults followed by the admin's built-in pages, sidebars and actions; generated admin code passes it to `withDefaultElements` in place of `defaultConfiguratorElements`, because each call replaces the defaults passed before.
|
|
440
440
|
|
|
441
441
|
#### Hooks for a Project Layout
|
|
442
442
|
|
|
@@ -481,14 +481,14 @@ Texts from the spec, such as option titles and the `detail` fields, are `Localiz
|
|
|
481
481
|
|
|
482
482
|
**Violations**
|
|
483
483
|
|
|
484
|
-
| Hook | Returns
|
|
485
|
-
| ------------------------------------------------- |
|
|
486
|
-
| `phx.frontend.useStateValidity()` | The validity of the last committed state: `{ valid: true, severity: null }`, or `{ valid: true, severity: 'notice' \| 'warning', entityViolations }`. A change that leaves a `fixable` or `fatal` violation is rejected and never committed, and neither is a change during which an error was thrown, so neither is seen here.
|
|
487
|
-
| `phx.frontend.useFinalizeViolations()` | `{ disabled, hints, violations }`. `disabled` is `true` while a `warning`, `fixable` or `fatal` violation holds or the configuration failed to save; `hints` is the violations' text, or `undefined`.
|
|
488
|
-
| `phx.frontend.useConfiguratorMessages(onMessage)` | Nothing. It calls `onMessage(message)` once per message: `{ kind: 'violation', entityId, violation, text }` for each violation of the rejected state, including notices and warnings already present, so `violation.severity` tells the reason for the rejection (`fixable` or `fatal`) from them, `{ kind: 'sideEffect', text }` for each hint of a committed update,
|
|
489
|
-
| `phx.frontend.useFinalizeAction(handler)` | `{ run, isPending, error, disabled, hints }` for the `finalizeAction` whose `handler` it is given; see the Finalize paragraph below for how to call it. `run()` records the finalize-button click for analytics and runs the project's handler, and does nothing while `disabled` or `isPending`; `error` is what the handler's last run rejected with, or `null`; `disabled` and `hints` are those of `useFinalizeViolations()`. The type of the result is `UseFinalizeActionResult` from `@salsita-ai/sdk/configurator`, which also exports the hook.
|
|
484
|
+
| Hook | Returns |
|
|
485
|
+
| ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
486
|
+
| `phx.frontend.useStateValidity()` | The validity of the last committed state: `{ valid: true, severity: null }`, or `{ valid: true, severity: 'notice' \| 'warning', entityViolations }`. A change that leaves a `fixable` or `fatal` violation is rejected and never committed, and neither is a change during which an error was thrown, so neither is seen here. |
|
|
487
|
+
| `phx.frontend.useFinalizeViolations()` | `{ disabled, hints, violations }`. `disabled` is `true` while a `warning`, `fixable` or `fatal` violation holds or the configuration failed to save; `hints` is the violations' text, or `undefined`. |
|
|
488
|
+
| `phx.frontend.useConfiguratorMessages(onMessage)` | Nothing. It calls `onMessage(message)` once per message: `{ kind: 'violation', entityId, violation, text }` for each violation of the rejected state, including notices and warnings already present, so `violation.severity` tells the reason for the rejection (`fixable` or `fatal`) from them, `{ kind: 'sideEffect', text }` for each hint of a committed update, `{ kind: 'error', error, text }` when an error is thrown while a change is validated, with `text` the generic "Oops, something went wrong", `{ kind: 'saveError', error, text }` each time saving the configuration fails, with `error` what the save rejected with and `text` a generic message, and `{ kind: 'saveRecovery', text }` when a save succeeds after a failed one, with `text` "Configuration updated". `text` is resolved to the current language; a violation's is its `message`, or its `type` without one, and the built-in snackbar prefixes it with `entityId`. Hints committed before the layout mounted are not delivered, nor is a save that failed before it mounted; when the most recent change was rejected or threw, its messages are delivered again when the hook mounts. `onMessage` may be a new function on every render. On `phx.frontend` the message is typed by the project, so `violation.type` narrows to its violation types; from `@salsita-ai/sdk/react` the type is `ConfiguratorMessage`. |
|
|
489
|
+
| `phx.frontend.useFinalizeAction(handler)` | `{ run, isPending, error, disabled, hints }` for the `finalizeAction` whose `handler` it is given; see the Finalize paragraph below for how to call it. `run()` records the finalize-button click for analytics and runs the project's handler, and does nothing while `disabled` or `isPending`; `error` is what the handler's last run rejected with, or `null`; `disabled` and `hints` are those of `useFinalizeViolations()`. The type of the result is `UseFinalizeActionResult` from `@salsita-ai/sdk/configurator`, which also exports the hook. |
|
|
490
490
|
|
|
491
|
-
`LayoutSlots.Services` shows
|
|
491
|
+
`LayoutSlots.Services` shows every message of `useConfiguratorMessages` as a snackbar, `saveError` and `saveRecovery` included; see the messages paragraph of [Loaded Configuration UI](#loaded-configuration-ui) for choosing between the two and for the messages that stay snackbars.
|
|
492
492
|
|
|
493
493
|
**Finalize**
|
|
494
494
|
|
|
@@ -281,9 +281,7 @@ Phoenix provides two helper functions for reading environment variables with val
|
|
|
281
281
|
**`createEnvReader`** - General purpose environment reader used in frontend and deployment contexts:
|
|
282
282
|
|
|
283
283
|
```typescript
|
|
284
|
-
import { bool, str } from '
|
|
285
|
-
|
|
286
|
-
import { createEnvReader } from '@salsita-ai/sdk/common/env'
|
|
284
|
+
import { bool, createEnvReader, str } from '@salsita-ai/sdk/common/env'
|
|
287
285
|
|
|
288
286
|
// Create a reader function
|
|
289
287
|
const readEnv = createEnvReader({
|
|
@@ -301,9 +299,7 @@ console.log(env.SERVER_URL) // Typed and validated
|
|
|
301
299
|
|
|
302
300
|
```typescript
|
|
303
301
|
// webhooks/webhooks.config.ts
|
|
304
|
-
import { str } from '
|
|
305
|
-
|
|
306
|
-
import { createEnvReader } from '@salsita-ai/sdk/common/env'
|
|
302
|
+
import { createEnvReader, str } from '@salsita-ai/sdk/common/env'
|
|
307
303
|
|
|
308
304
|
export const readRuntimeConfig = createEnvReader({
|
|
309
305
|
AWS_S3_PUBLIC_RESOURCES_BUCKET: str(),
|
|
@@ -329,6 +329,8 @@ components:
|
|
|
329
329
|
|
|
330
330
|
The code generator will automatically create an expression with the same name and infer the return type based on usage context.
|
|
331
331
|
|
|
332
|
+
The type of an expression must share values with the type every component expects where it is read, and with its declared type. An expression declared as a number and read into a `title`, or one read both into a `title` and into `offsetX`, has no value its implementation could return, so the build reports `EXPRESSION_READ_TYPE_CONFLICT` at the expression and names each place it is read. A type given only as `tsType` is not checked here.
|
|
333
|
+
|
|
332
334
|
## Constants
|
|
333
335
|
|
|
334
336
|
Constants are pre-generated expressions that return fixed values. Under the hood, they are implemented as expressions but are automatically generated by the Phoenix system, so you don't need to write TypeScript implementation code for them.
|