@salsita-ai/sdk 0.50.0-develop.016963b09e → 0.50.0-develop.08837efd9c
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/dist/application-spec/core/components/component-filter.d.ts +53 -0
- package/dist/application-spec/core/components/component-filter.js +1 -0
- package/dist/application-spec/core/components/index.d.ts +2 -0
- package/dist/application-spec/core/components/index.js +1 -0
- package/dist/application-spec/core/components/sdk-code-ref.d.ts +9 -0
- package/dist/application-spec/core/components/sdk-code-ref.js +1 -0
- package/dist/application-spec/core/enhanced-json-value.d.ts +0 -68
- package/dist/application-spec/core/enhanced-json-value.js +1 -1
- package/dist/application-spec/core/index.d.ts +1 -0
- package/dist/application-spec/core/index.js +1 -1
- package/dist/application-spec/core/json-value.d.ts +1 -1
- package/dist/application-spec/{core → enhanced-json}/eval-to-constant.d.ts +1 -1
- package/dist/application-spec/enhanced-json/index.d.ts +4 -0
- package/dist/application-spec/enhanced-json/index.js +1 -0
- package/dist/application-spec/enhanced-json/is-enhanced-json-array.d.ts +2 -0
- package/dist/application-spec/enhanced-json/is-enhanced-json-array.js +1 -0
- package/dist/application-spec/enhanced-json/resolve-enhanced-json-value.d.ts +18 -0
- package/dist/application-spec/enhanced-json/resolve-enhanced-json-value.js +1 -0
- package/dist/application-spec/enhanced-json/schema-from-value.d.ts +24 -0
- package/dist/application-spec/enhanced-json/schema-from-value.js +1 -0
- package/dist/application-spec/enhanced-json/stringify-enhanced-json-value.d.ts +14 -0
- package/dist/application-spec/enhanced-json/stringify-enhanced-json-value.js +1 -0
- package/dist/application-spec/enhanced-json/validate-enhanced-json-value-caps.d.ts +12 -0
- package/dist/application-spec/enhanced-json/validate-enhanced-json-value-caps.js +1 -0
- package/dist/application-spec/enhanced-json/validate-no-legacy-code-ref.d.ts +13 -0
- package/dist/application-spec/enhanced-json/validate-no-legacy-code-ref.js +1 -0
- 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 +8 -0
- package/dist/application-spec/errors/diagnostic-messages.js +1 -1
- package/dist/application-spec/generate-application/code/templates/domain-spec/generate-component-optics.js +4 -4
- package/dist/application-spec/generate-application/code/templates/domain-spec/generate-defaults.js +1 -1
- package/dist/application-spec/generate-application/code/templates/domain-spec/generate-domain-block.js +41 -40
- package/dist/application-spec/generate-application/code/templates/domain-spec/generate-phx-globals.js +3 -0
- package/dist/application-spec/generate-application/code/templates/domain-spec/generate-resources.d.ts +6 -8
- package/dist/application-spec/generate-application/code/templates/domain-spec/generate-resources.js +11 -11
- package/dist/application-spec/generate-application/code/templates/domain-spec/index.d.ts +1 -1
- package/dist/application-spec/generate-application/code/templates/domain-spec/index.js +1 -1
- package/dist/application-spec/generate-application/code/templates/domain-spec.js +12 -12
- package/dist/application-spec/generate-application/code/templates/globals.js +5 -4
- package/dist/application-spec/generate-application/code/templates/skills/phoenix-review-skill.js +8 -2
- package/dist/application-spec/generate-application/code/templates/skills/sdk-content.d.ts +3 -2
- package/dist/application-spec/generate-application/code/templates/skills/sdk-content.js +2 -2
- package/dist/application-spec/generate-application/runtime/sections/build-sdk-optic.d.ts +1 -1
- package/dist/application-spec/generate-application/runtime/sections/build-sdk-optic.js +1 -1
- package/dist/application-spec/index.d.ts +1 -0
- package/dist/application-spec/index.js +1 -1
- package/dist/application-spec/parse-application-files/categorize/module-analysis.d.ts +1 -1
- package/dist/application-spec/parse-application-files/categorize/module-analysis.js +1 -1
- package/dist/application-spec/parse-application-files/raw-spec/raw-assistant.d.ts +16 -3
- package/dist/application-spec/parse-application-files/raw-spec/raw-assistant.js +1 -1
- package/dist/application-spec/parse-application-files/raw-spec/raw-spec-file.js +1 -1
- package/dist/application-spec/parse-application-files/spec/components/component-entry.d.ts +1 -9
- package/dist/application-spec/parse-application-files/spec/components/component-entry.js +1 -1
- package/dist/application-spec/parse-application-files/spec/components/component-filter.d.ts +1 -54
- package/dist/application-spec/parse-application-files/spec/components/component-filter.js +1 -1
- package/dist/application-spec/parse-application-files/spec/components/index.d.ts +1 -1
- package/dist/application-spec/parse-application-files/spec/components/index.js +1 -1
- package/dist/application-spec/parse-application-files/spec/expressions/memo-eval-plan.js +1 -1
- package/dist/application-spec/parse-application-files/spec/expressions/user-expressions.js +1 -1
- package/dist/application-spec/parse-application-files/spec/model/defaults.js +1 -1
- package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-schema.js +1 -1
- package/dist/application-spec/parse-application-files/spec/sections/infra.d.ts +2 -0
- package/dist/application-spec/parse-application-files/spec/sections/infra.js +1 -1
- package/dist/client/phoenix-api.d.ts +96 -0
- package/dist/configurator/application-runtime/extensions/create-configurator-extensions.js +1 -1
- package/dist/configurator/application-runtime/extensions/selectors.d.ts +3 -1
- package/dist/configurator/application-runtime/extensions/selectors.js +1 -1
- package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.d.ts +3 -1
- package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.js +1 -1
- package/dist/configurator/application-runtime/extensions/systems/drag-and-drop-system/drag-and-drop-system.d.ts +3 -3
- package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.d.ts +14 -4
- package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.js +1 -1
- package/dist/configurator/application-runtime/extensions/systems/graphql-system/inject-assistant-lenses.d.ts +11 -0
- package/dist/configurator/application-runtime/extensions/systems/graphql-system/inject-assistant-lenses.js +1 -0
- package/dist/configurator/application-runtime/extensions/systems/layout-system/layout-system.d.ts +2 -2
- package/dist/configurator/application-runtime/extensions/systems/layout-system/layout-system.js +1 -1
- package/dist/configurator/common/configurator-interface.d.ts +5 -1
- package/dist/configurator/common/configurator-interface.js +1 -1
- package/dist/configurator/common/frontend-analytics.d.ts +2 -0
- package/dist/configurator/common/frontend-analytics.js +1 -1
- package/dist/configurator/configurator-context/configurator-hooks/layout-hooks.d.ts +1 -1
- package/dist/configurator/scene/scene-controls/hotspot-drag-handle/HotspotDragHandle.d.ts +1 -1
- package/dist/configurator/scene/scene-controls/hotspot-drag-handle/HotspotDragSceneHandle.d.ts +1 -1
- package/dist/configurator/scene/scene-entity/dimension-indicators/DimensionIndicator.d.ts +1 -1
- package/dist/configurator/scene/scene-entity/dimension-indicators/DimensionIndicatorInternal.d.ts +1 -1
- package/dist/configurator/scene/scene-entity/drag-and-drop/shared.d.ts +2 -2
- package/dist/configurator/scene/scene-entity/drag-and-drop/use-drag-object.d.ts +1 -1
- package/dist/configurator/scene/scene-entity/materials/RemoteReplacedMaterial.js +1 -1
- package/dist/configurator/scene/scene-entity/objects/SceneEntityObjectTextDecal.d.ts +1 -1
- package/dist/configurator/ui/assistant/AssistantMarkdown.d.ts +1 -1
- package/dist/configurator/ui/assistant/AssistantMarkdown.js +1 -1
- package/dist/configurator/ui/assistant/AssistantThread.js +1 -1
- package/dist/configurator/ui/assistant/entity-link/AssistantEntityLink.d.ts +13 -0
- package/dist/configurator/ui/assistant/entity-link/AssistantEntityLink.js +1 -0
- package/dist/configurator/ui/assistant/entity-link/entity-link-selection.d.ts +22 -0
- package/dist/configurator/ui/assistant/entity-link/entity-link-selection.js +1 -0
- package/dist/configurator/ui/assistant/entity-link/entity-link-state.d.ts +27 -0
- package/dist/configurator/ui/assistant/entity-link/entity-link-state.js +1 -0
- package/dist/configurator/ui/assistant/entity-link/entity-link-target.d.ts +5 -0
- package/dist/configurator/ui/assistant/entity-link/entity-link-target.js +1 -0
- package/dist/configurator/ui/assistant/entity-link/index.d.ts +3 -0
- package/dist/configurator/ui/assistant/entity-link/index.js +1 -0
- package/dist/configurator/ui/configurator-layout/sidebar-keys/index.d.ts +1 -0
- package/dist/configurator/ui/configurator-layout/sidebar-keys/index.js +1 -0
- package/dist/configurator/ui/configurator-layout/sidebar-keys/sidebar-keys.d.ts +11 -0
- package/dist/configurator/ui/configurator-layout/sidebar-keys/sidebar-keys.js +1 -0
- package/dist/configurator/ui/configurator-layout/slots/desktop-sidebar/DesktopSidebar.d.ts +101 -0
- package/dist/configurator/ui/configurator-layout/slots/desktop-sidebar/DesktopSidebar.js +16 -0
- package/dist/configurator/ui/configurator-layout/slots/desktop-sidebar/index.d.ts +1 -0
- package/dist/configurator/ui/configurator-layout/slots/desktop-sidebar/index.js +1 -0
- package/dist/configurator/ui/configurator-layout/slots/slots.d.ts +1 -0
- package/dist/configurator/ui/configurator-layout/slots/slots.js +1 -1
- package/dist/configurator/ui/configurator-layout/universal/DesktopLayout.d.ts +0 -74
- package/dist/configurator/ui/configurator-layout/universal/DesktopLayout.js +5 -20
- package/dist/configurator/ui/legacy/layout/steps/StepsNavigation.d.ts +2 -2
- package/dist/configurator/ui/phoenix-layout-elements/header/common/step-navigation/StepsNavigation.d.ts +2 -2
- package/dist/docs/dimensions/3d-scene-integration/dimension-indicators.mdx +2 -2
- package/dist/docs/dimensions/3d-scene-integration/editable-angle-indicator.mdx +1 -1
- package/dist/docs/dimensions/3d-scene-integration/editable-distance-dimension-indicator.mdx +1 -1
- package/dist/docs/dimensions/3d-scene-integration/list-selector-dimension-indicator.mdx +1 -1
- package/dist/docs/drag-and-drop/drag-handle.mdx +1 -1
- package/dist/docs/drag-and-drop/hotspot-handle.mdx +1 -1
- package/dist/docs/drag-and-drop/planes.mdx +1 -1
- package/dist/docs/drag-and-drop/rotatable.mdx +2 -2
- package/dist/docs/index.json +32 -1
- package/dist/docs/layouts/custom-layout.mdx +981 -0
- package/dist/docs/layouts/header/fixed.mdx +2 -2
- package/dist/docs/layouts/header/floating.mdx +2 -2
- package/dist/docs/layouts/sidebar/in-a-custom-layout.mdx +380 -0
- package/dist/docs/other-features/client-side-rendering/declarative.mdx +1 -1
- package/dist/docs/other-features/transformers.mdx +4 -0
- package/dist/docs/phoenix/configurator-interface.mdx +8 -2
- package/dist/docs/phoenix/project-structure.mdx +2 -1
- package/dist/docs/phoenix/testing.mdx +140 -0
- package/dist/docs/phoenix/webhook-membership.mdx +10 -9
- package/dist/docs/phoenix/webhooks.mdx +1 -1
- package/dist/domain/application/assistant/assistant-fields.d.ts +12 -0
- package/dist/domain/application/assistant/assistant-fields.js +1 -0
- package/dist/domain/application/assistant/assistant-manifest.d.ts +9 -1
- package/dist/domain/application/assistant/assistant-resolved-manifest.d.ts +38 -2
- package/dist/domain/application/assistant/assistant-resolved-manifest.js +1 -1
- package/dist/domain/application/assistant/build-assistant-lenses.d.ts +14 -0
- package/dist/domain/application/assistant/build-assistant-lenses.js +5 -0
- package/dist/domain/application/assistant/build-assistant-manifest.js +1 -1
- package/dist/domain/application/assistant/create-assistant-execution-schema.d.ts +32 -0
- package/dist/domain/application/assistant/create-assistant-execution-schema.js +1 -0
- package/dist/domain/application/assistant/generate-assistant-prompt.js +1 -1
- package/dist/domain/application/assistant/index.d.ts +6 -1
- package/dist/domain/application/assistant/index.js +1 -1
- package/dist/domain/application/assistant/session-setup/AssistantSetupHandler.d.ts +20 -0
- package/dist/domain/application/assistant/session-setup/AssistantSetupHandler.js +1 -0
- package/dist/domain/application/assistant/session-setup/apply-assistant-setup.d.ts +10 -0
- package/dist/domain/application/assistant/session-setup/apply-assistant-setup.js +1 -0
- package/dist/domain/application/assistant/session-setup/assistant-setup-manifest.d.ts +48 -0
- package/dist/domain/application/assistant/session-setup/assistant-setup-manifest.js +1 -0
- package/dist/domain/application/assistant/session-setup/create-session-assistant.d.ts +15 -0
- package/dist/domain/application/assistant/session-setup/create-session-assistant.js +1 -0
- package/dist/domain/application/assistant/session-setup/index.d.ts +4 -0
- package/dist/domain/application/assistant/session-setup/index.js +1 -0
- package/dist/domain/application/configurator-builder.d.ts +1 -1
- package/dist/domain/application/configurator-builder.js +1 -1
- package/dist/domain/application/index.d.ts +0 -1
- package/dist/domain/application/index.js +1 -1
- package/dist/domain/core/expression/constant-expressions.d.ts +16 -8
- package/dist/domain/core/invariant/violation.d.ts +50 -2
- package/dist/domain/core/invariant/violation.js +1 -1
- package/dist/domain/domain-runtime/access/selectors.d.ts +9 -0
- package/dist/domain/domain-runtime/access/selectors.js +1 -1
- package/dist/domain/domain-runtime/access/types.d.ts +2 -0
- package/dist/domain/domain-runtime/access/types.js +1 -1
- package/dist/domain/domain-runtime/domain-context/selectors.js +1 -1
- package/dist/domain/domain-runtime/domain-context/types.d.ts +4 -1
- package/dist/domain/domain-runtime/domain-runtime.d.ts +16 -0
- package/dist/domain/domain-runtime/errors/implementation-error-collector.d.ts +19 -0
- package/dist/domain/domain-runtime/errors/implementation-error-collector.js +1 -0
- package/dist/domain/domain-runtime/errors/implementation-error.d.ts +46 -0
- package/dist/domain/domain-runtime/errors/implementation-error.js +1 -0
- package/dist/domain/domain-runtime/errors/implementation-errors.d.ts +13 -0
- package/dist/domain/domain-runtime/errors/implementation-errors.js +1 -0
- package/dist/domain/domain-runtime/errors/index.d.ts +4 -0
- package/dist/domain/domain-runtime/errors/index.js +1 -0
- package/dist/domain/domain-runtime/errors/to-implementation-errors.d.ts +10 -0
- package/dist/domain/domain-runtime/errors/to-implementation-errors.js +1 -0
- package/dist/domain/domain-runtime/index.d.ts +1 -0
- package/dist/domain/domain-runtime/index.js +1 -1
- package/dist/domain/domain-runtime/spec/initialize.d.ts +11 -0
- package/dist/domain/domain-runtime/spec/initialize.js +1 -1
- package/dist/domain/domain-runtime/spec/module-checks/project-modules.d.ts +8 -1
- package/dist/domain/domain-runtime/spec/module-checks/project-modules.js +1 -1
- package/dist/domain/domain-runtime/spec/module-checks/resolve-action-modules.js +1 -1
- package/dist/domain/domain-runtime/spec/module-checks/resolve-expression-modules.js +1 -1
- package/dist/domain/domain-runtime/spec/module-checks/resolve-imported-transformers.js +1 -1
- package/dist/domain/domain-runtime/spec/module-checks/resolve-invariant-modules.js +1 -1
- package/dist/domain/extensions/canvas-config.d.ts +1 -1
- package/dist/domain/extensions/client-side-image.d.ts +2 -2
- package/dist/domain/extensions/client-side-image.js +1 -1
- package/dist/domain/extensions/common/angle-indicator-schema.d.ts +2 -2
- package/dist/domain/extensions/common/dimension-indicator-schema.d.ts +2 -2
- package/dist/domain/extensions/editable-angle-indicator.d.ts +1 -1
- package/dist/domain/extensions/editable-distance-dimension-indicator.d.ts +1 -1
- package/dist/domain/extensions/editable-offset-indicator.d.ts +1 -1
- package/dist/domain/extensions/editable-offset-indicator.js +1 -1
- package/dist/domain/extensions/horizontal-tabs.d.ts +2 -2
- package/dist/domain/extensions/list-selector-dimension-indicator.d.ts +1 -1
- package/dist/domain/extensions/navigation-steps.d.ts +2 -2
- package/dist/domain/extensions/offset-indicator.js +1 -1
- package/dist/domain/extensions/rotation-handle.d.ts +2 -2
- package/dist/domain/extensions/save-template-thumbnail.d.ts +2 -2
- package/dist/domain/extensions/save-template-thumbnail.js +1 -1
- package/dist/domain/mocks/create-mock-domain-runtime.d.ts +9 -0
- package/dist/domain/mocks/create-mock-domain-runtime.js +1 -1
- package/dist/domain/mocks/create-mock-domain.js +1 -1
- package/dist/domain/mocks/create-test-application-config.d.ts +59 -0
- package/dist/domain/mocks/create-test-application-config.js +1 -0
- package/dist/domain/mocks/index.d.ts +1 -0
- package/dist/domain/mocks/index.js +1 -1
- package/dist/domain/system-components/application-config.d.ts +2 -2
- package/dist/domain/system-components/collision-box-geometry.d.ts +4 -2
- package/dist/domain/system-components/collision-geometry.d.ts +4 -2
- package/dist/domain/system-components/collision-point-geometry.d.ts +4 -2
- package/dist/domain/system-components/common/material-override-schema.d.ts +2 -2
- package/dist/domain/system-components/constraint.d.ts +10 -23
- package/dist/domain/system-components/drag-handle.d.ts +1 -1
- package/dist/domain/system-components/hotspot-drag-handle.d.ts +1 -1
- package/dist/domain/system-components/material-swatch.js +1 -1
- package/dist/domain/system-components/scene-entity-material-override.d.ts +1 -1
- package/dist/domain/system-components/scene-entity-object-material-override.d.ts +1 -1
- package/dist/domain/system-components/scene-entity-object-text-decal.d.ts +1 -1
- package/dist/domain/system-components/scene-entity-object-transimissive-material-override.d.ts +1 -1
- package/dist/domain/system-components/scene-entity-transimissive-material-override.d.ts +1 -1
- package/dist/editor/mocks/EditorMockContext.js +1 -1
- package/dist/legacy/domain-cli/gen/configurator-builder.js +13 -12
- package/dist/legacy/domain-cli/gen/constant-expressions.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 +16 -5
- package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/agents/claude-md.d.ts +1 -1
- package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/agents/cursor-rules.d.ts +1 -1
- package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/agents/shared-sdk-content.d.ts +1 -1
- package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/agents/shared-sdk-content.js +1 -1
- package/dist/react-sdk/application-runtime/hooks/use-access-service.d.ts +2 -0
- package/dist/react-sdk/application-runtime/mocks/create-mock-application-runtime.js +1 -1
- package/dist/react-sdk/application-runtime/routing/access-service.d.ts +1 -0
- package/dist/react-sdk/application-runtime/runtime/create-application-runtime.d.ts +17 -0
- package/dist/react-sdk/application-runtime/runtime/create-application-runtime.js +1 -1
- package/dist/react-sdk/domain-context/index.d.ts +1 -1
- package/dist/react-sdk/domain-context/index.js +1 -1
- package/dist/react-sdk/domain-context/system-hooks/navigation-hooks.d.ts +6 -0
- package/dist/react-sdk/domain-context/system-hooks/navigation-hooks.js +1 -1
- package/dist/react-sdk/domain-controls/hooks/use-angle-dimension.d.ts +1 -1
- package/dist/ui-sdk/theme/types.d.ts +1 -1
- package/dist/webhook/builders/webhook-builder.js +1 -1
- package/package.json +3 -3
- package/schema/domain-spec.json +209 -16
- package/dist/domain/application/resource-builder.d.ts +0 -13
- package/dist/domain/application/resource-builder.js +0 -1
- /package/dist/application-spec/{core → enhanced-json}/eval-code-uses-self.d.ts +0 -0
- /package/dist/application-spec/{core → enhanced-json}/eval-code-uses-self.js +0 -0
- /package/dist/application-spec/{core → enhanced-json}/eval-to-constant.js +0 -0
|
@@ -145,7 +145,7 @@ interface HorizontalTab {
|
|
|
145
145
|
/**
|
|
146
146
|
* List of navigation states when the tab is active. First one will be used for activation.
|
|
147
147
|
*/
|
|
148
|
-
readonly navigationStates: string[]
|
|
148
|
+
readonly navigationStates: readonly string[]
|
|
149
149
|
}
|
|
150
150
|
```
|
|
151
151
|
|
|
@@ -158,7 +158,7 @@ type HorizontalTabsComponentState = {
|
|
|
158
158
|
/**
|
|
159
159
|
* List of tabs in the horizontal tabs.
|
|
160
160
|
*/
|
|
161
|
-
readonly tabs: HorizontalTab[]
|
|
161
|
+
readonly tabs: readonly HorizontalTab[]
|
|
162
162
|
}
|
|
163
163
|
```
|
|
164
164
|
|
|
@@ -110,11 +110,11 @@ The configuration uses these component types:
|
|
|
110
110
|
```typescript
|
|
111
111
|
export interface NavigationStep {
|
|
112
112
|
readonly label: Localizable
|
|
113
|
-
readonly navigationStates: string[]
|
|
113
|
+
readonly navigationStates: readonly string[]
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
export type NavigationStepsComponentState = {
|
|
117
|
-
readonly steps: NavigationStep[]
|
|
117
|
+
readonly steps: readonly NavigationStep[]
|
|
118
118
|
}
|
|
119
119
|
```
|
|
120
120
|
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
# Sidebar in a Custom Layout
|
|
2
|
+
|
|
3
|
+
- [Use case](#use-case)
|
|
4
|
+
- [Guide](#guide)
|
|
5
|
+
- [Example](#example)
|
|
6
|
+
- [Known gaps](#known-gaps)
|
|
7
|
+
- [Test cases](#test-cases)
|
|
8
|
+
- [Complete Implementation](#complete-implementation)
|
|
9
|
+
|
|
10
|
+
## Use case
|
|
11
|
+
|
|
12
|
+
A project wants the configurator arranged differently, for example with the sidebar on the left, but the sidebar itself is right as it is: the spec's tabs, property pages and sidebar footer, with their spacing, scrolling and empty states. The project writes its own layout, a `ConfiguratorLayout` element, and places the built-in sidebar in it as one piece, `LayoutSlots.DesktopSidebar`. The spec keeps describing what the sidebar shows; the layout only decides where it goes.
|
|
13
|
+
|
|
14
|
+
Build the sidebar's controls yourself instead when they have to look like the brand's own, as [Custom Layout](?path=/docs/layouts-custom-layout--docs) does; [Configurator Interface → Loaded Configuration UI](?path=/docs/phoenix-configurator-interface--docs#loaded-configuration-ui) lists what a project layout can use. Stay with the built-in layout when its arrangement fits: [Sidebar - General](?path=/docs/layouts-sidebar-general--docs) describes what the spec configures in the sidebar.
|
|
15
|
+
|
|
16
|
+
## Guide
|
|
17
|
+
|
|
18
|
+
The layout registers itself under the name `ConfiguratorLayout` and mounts the scene, the safe area and the sidebar:
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
// src/elements/ConfiguratorLayout.tsx
|
|
22
|
+
import { LayoutSlots, useSceneSafeArea } from '@salsita-ai/sdk/configurator'
|
|
23
|
+
|
|
24
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
25
|
+
|
|
26
|
+
const GUTTER = 24
|
|
27
|
+
const SIDEBAR_WIDTH = 380
|
|
28
|
+
|
|
29
|
+
const ConfiguratorLayout = () => {
|
|
30
|
+
const { containerRef, safeAreaRef } = useSceneSafeArea()
|
|
31
|
+
const isFullscreen = phx.frontend.useIsFullscreenActive()
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div ref={containerRef} style={{ position: 'absolute', inset: 0 }}>
|
|
35
|
+
<LayoutSlots.Services />
|
|
36
|
+
<LayoutSlots.SharedCanvas />
|
|
37
|
+
|
|
38
|
+
{!isFullscreen && (
|
|
39
|
+
<aside
|
|
40
|
+
style={{
|
|
41
|
+
position: 'absolute',
|
|
42
|
+
insetBlock: GUTTER,
|
|
43
|
+
insetInlineStart: GUTTER,
|
|
44
|
+
inlineSize: SIDEBAR_WIDTH,
|
|
45
|
+
}}
|
|
46
|
+
>
|
|
47
|
+
<LayoutSlots.DesktopSidebar side="start" />
|
|
48
|
+
</aside>
|
|
49
|
+
)}
|
|
50
|
+
|
|
51
|
+
<div
|
|
52
|
+
ref={safeAreaRef}
|
|
53
|
+
style={{
|
|
54
|
+
position: 'absolute',
|
|
55
|
+
insetBlock: 0,
|
|
56
|
+
insetInlineStart: isFullscreen ? 0 : SIDEBAR_WIDTH + 2 * GUTTER,
|
|
57
|
+
insetInlineEnd: 0,
|
|
58
|
+
pointerEvents: 'none',
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
<div style={{ position: 'absolute', insetBlockEnd: GUTTER, insetInlineStart: GUTTER, pointerEvents: 'auto' }}>
|
|
62
|
+
<LayoutSlots.DesktopToolbar />
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default phx.frontend.createCustomPhoenixLayoutElement('ConfiguratorLayout', ConfiguratorLayout)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
- **Size the container.** Without `width`, `LayoutSlots.DesktopSidebar` fills the width and height of its container. The layout positions the container above the canvas and gives it a size, here a fixed width and the full height less a gutter. The sidebar's content scrolls inside that height and its footer stays at the bottom.
|
|
73
|
+
- **Say which side it is on.** The sidebar's tabs sit outside it. With `side="start"` they sit on its right, toward the scene; the default, `side="end"`, puts them on its left, as in the built-in layout.
|
|
74
|
+
- **Frame the scene beside it.** `useSceneSafeArea()` measures the free region right of the sidebar, so the camera frames the table there.
|
|
75
|
+
- **Decide what fullscreen does.** In fullscreen the sidebar stays in place, transparent and inert. This layout removes it while `phx.frontend.useIsFullscreenActive()` is set and lets the safe area start at the left edge, so the table is framed in the whole canvas. The toolbar's fullscreen button sets the flag.
|
|
76
|
+
- **Read mode.** The same element renders a configuration shared for viewing. The sidebar handles read mode itself: it applies the spec's `$filter.modes`, so the tabs, which the spec limits to owners, are gone and the footer shows the price only. The banner that offers **Edit configuration** to a viewer is the layout's to mount: the built-in layout shows `LayoutSlots.DesktopReadonlyBanner` in read mode outside fullscreen, and it renders only when the spec sets `shareCloneToEdit` and the viewer's role may create configurations. This spec does not set it, so this layout mounts no banner.
|
|
77
|
+
|
|
78
|
+
`LayoutSlots.DesktopSidebar` takes two optional props:
|
|
79
|
+
|
|
80
|
+
| Prop | Type | Default | Meaning |
|
|
81
|
+
| ------- | -------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
82
|
+
| `width` | CSS width | none | Fixed inline size; the sidebar then takes its content's height unless its container stretches it. Without it the sidebar fills its container's width and height. |
|
|
83
|
+
| `side` | `'start'` \| `'end'` | `'end'` | The side of the layout the sidebar is on; its tabs sit on the other side of it. |
|
|
84
|
+
|
|
85
|
+
Its look follows the theme component `PhxDesktopSidebar`, as in the built-in layout, see [Theming](?path=/docs/phoenix-theming--docs).
|
|
86
|
+
|
|
87
|
+
## Example
|
|
88
|
+
|
|
89
|
+
The spec is a table with one property, its edge finish, on a **Design** tab and a short text on a **Details** tab, and a price. Nothing in the YAML refers to the layout.
|
|
90
|
+
|
|
91
|
+
## Known gaps
|
|
92
|
+
|
|
93
|
+
- The finalize button is disabled unless the spec configures `finalizeButton` or `finalizePage`, as in the built-in layout. A `finalizePage` is not shown by a project layout unless the layout renders it.
|
|
94
|
+
- `LayoutSlots.DesktopSidebar` is the desktop sidebar. At phone width a project layout needs its own arrangement, for example the `Mobile*` members of `LayoutSlots` in a drawer.
|
|
95
|
+
- The tabs overlap the scene beside the sidebar; the safe area in this example does not leave room for them.
|
|
96
|
+
- `miniSidebar` does not shrink the sidebar: the built-in layout does that, and here the sidebar fills its container. A layout that honours it reads `LayoutSlots.useSidebarContent().hasMiniSidebar` and gives the container no fixed height, anchored at the bottom for example, so the sidebar takes its content's height.
|
|
97
|
+
|
|
98
|
+
## Test cases
|
|
99
|
+
|
|
100
|
+
- The sidebar sits on the left with its tabs on its right; the table is framed in the space right of the sidebar.
|
|
101
|
+
- **Design** shows the edge finishes; choosing one changes the table. **Details** shows the text.
|
|
102
|
+
- The footer shows the price and the finalize button.
|
|
103
|
+
- Fullscreen removes the sidebar and frames the table in the whole canvas; leaving it brings the sidebar back.
|
|
104
|
+
- With the **Read User** mock template the tabs are gone and the footer shows the price only.
|
|
105
|
+
|
|
106
|
+
## Complete Implementation
|
|
107
|
+
|
|
108
|
+
{/* AUTO-GENERATED SECTION: This section is automatically updated by 'phoenix story build' */}
|
|
109
|
+
|
|
110
|
+
<details>
|
|
111
|
+
<summary>Click to view the complete implementation files</summary>
|
|
112
|
+
|
|
113
|
+
### ./spec/domain.yaml
|
|
114
|
+
|
|
115
|
+
```yaml
|
|
116
|
+
model:
|
|
117
|
+
latest:
|
|
118
|
+
entityTypes:
|
|
119
|
+
- Table
|
|
120
|
+
aliases:
|
|
121
|
+
RootEntity:
|
|
122
|
+
- Table
|
|
123
|
+
commonTypes:
|
|
124
|
+
EdgeFinish:
|
|
125
|
+
enum:
|
|
126
|
+
- None
|
|
127
|
+
- Facet
|
|
128
|
+
- Round
|
|
129
|
+
- TreeTrunk
|
|
130
|
+
shapes:
|
|
131
|
+
Table:
|
|
132
|
+
edgeFinish:
|
|
133
|
+
$ref: '#/model/latest/commonTypes/EdgeFinish'
|
|
134
|
+
|
|
135
|
+
presets:
|
|
136
|
+
- rootEntityType: 'Table'
|
|
137
|
+
initialDomainEntities:
|
|
138
|
+
- id: 'Table-1'
|
|
139
|
+
type: 'Table'
|
|
140
|
+
edgeFinish: 'None'
|
|
141
|
+
|
|
142
|
+
navigationStates:
|
|
143
|
+
- design
|
|
144
|
+
- details
|
|
145
|
+
|
|
146
|
+
invariants:
|
|
147
|
+
Table:
|
|
148
|
+
trunkDisabled:
|
|
149
|
+
types:
|
|
150
|
+
- 'trunkDisabled'
|
|
151
|
+
|
|
152
|
+
components:
|
|
153
|
+
Table:
|
|
154
|
+
propertyPage:
|
|
155
|
+
design:
|
|
156
|
+
$filter:
|
|
157
|
+
navigationStates:
|
|
158
|
+
- design
|
|
159
|
+
children:
|
|
160
|
+
- kind: propertyContainerGroup
|
|
161
|
+
props:
|
|
162
|
+
spacing: large
|
|
163
|
+
children:
|
|
164
|
+
- kind: propertyContainerItem
|
|
165
|
+
props:
|
|
166
|
+
title: Edge Finish
|
|
167
|
+
description: Select the edge finish for the table.
|
|
168
|
+
children:
|
|
169
|
+
- kind: attributeCardSelector
|
|
170
|
+
props:
|
|
171
|
+
instanceName: edgeFinish
|
|
172
|
+
details:
|
|
173
|
+
$filter:
|
|
174
|
+
navigationStates:
|
|
175
|
+
- details
|
|
176
|
+
children:
|
|
177
|
+
- kind: propertyContainerItem
|
|
178
|
+
props:
|
|
179
|
+
title: Dimensions
|
|
180
|
+
description: 1600 × 800 mm, 750 mm high. Solid oak top on powder-coated steel legs.
|
|
181
|
+
sidebarTabs:
|
|
182
|
+
main:
|
|
183
|
+
$filter:
|
|
184
|
+
modes:
|
|
185
|
+
- owner
|
|
186
|
+
tabs:
|
|
187
|
+
- label: Design
|
|
188
|
+
navigationStates:
|
|
189
|
+
- design
|
|
190
|
+
- label: Details
|
|
191
|
+
navigationStates:
|
|
192
|
+
- details
|
|
193
|
+
price:
|
|
194
|
+
main:
|
|
195
|
+
price: 1200
|
|
196
|
+
fitRootPerspectiveCamera:
|
|
197
|
+
main:
|
|
198
|
+
priority: 1
|
|
199
|
+
fov: 35
|
|
200
|
+
near: 100
|
|
201
|
+
far: 10000
|
|
202
|
+
minDistance: 1000
|
|
203
|
+
maxDistance: 9000
|
|
204
|
+
distance: 5000
|
|
205
|
+
azimuthAngle: 1.5
|
|
206
|
+
polarAngle: 1.1
|
|
207
|
+
fitMeshChanges: true
|
|
208
|
+
animateOnLoad: true
|
|
209
|
+
sceneEntityRemoteObject:
|
|
210
|
+
top:
|
|
211
|
+
fileUrl: '/models/tables.gltf'
|
|
212
|
+
objectName: RECHTHOEK_RECTANGLE
|
|
213
|
+
dimensionUnit: m
|
|
214
|
+
leg:
|
|
215
|
+
fileUrl: '/models/tables.gltf'
|
|
216
|
+
objectName: AXL_LEG
|
|
217
|
+
dimensionUnit: m
|
|
218
|
+
sceneEntityObjectProperties:
|
|
219
|
+
top:
|
|
220
|
+
offsetY: 820
|
|
221
|
+
morphTargetInfluences:
|
|
222
|
+
Facet:
|
|
223
|
+
value:
|
|
224
|
+
$read: $edgeFinishFacet
|
|
225
|
+
scaleOne: 1
|
|
226
|
+
scaleZero: 0
|
|
227
|
+
Bol:
|
|
228
|
+
value:
|
|
229
|
+
$read: $edgeFinishRound
|
|
230
|
+
scaleOne: 1
|
|
231
|
+
scaleZero: 0
|
|
232
|
+
Boomstamrand:
|
|
233
|
+
value:
|
|
234
|
+
$read: $edgeFinishTreeTrunk
|
|
235
|
+
scaleOne: 1
|
|
236
|
+
scaleZero: 0
|
|
237
|
+
leg:
|
|
238
|
+
morphTargetInfluences:
|
|
239
|
+
Length:
|
|
240
|
+
value: 1000
|
|
241
|
+
scaleOne: 3000
|
|
242
|
+
scaleZero: 1400
|
|
243
|
+
Width:
|
|
244
|
+
value: 700
|
|
245
|
+
scaleOne: 1300
|
|
246
|
+
scaleZero: 600
|
|
247
|
+
Height:
|
|
248
|
+
value: 780
|
|
249
|
+
scaleOne: 780
|
|
250
|
+
scaleZero: 740
|
|
251
|
+
attributeSelection:
|
|
252
|
+
edgeFinish:
|
|
253
|
+
value:
|
|
254
|
+
$bind: edgeFinish
|
|
255
|
+
options:
|
|
256
|
+
$read: $edgeFinishOptions
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### ./src/elements/ConfiguratorLayout.tsx
|
|
260
|
+
|
|
261
|
+
```tsx
|
|
262
|
+
import { LayoutSlots, useSceneSafeArea } from '@salsita-ai/sdk/configurator'
|
|
263
|
+
|
|
264
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
265
|
+
|
|
266
|
+
const GUTTER = 24
|
|
267
|
+
const SIDEBAR_WIDTH = 380
|
|
268
|
+
|
|
269
|
+
const ConfiguratorLayout = () => {
|
|
270
|
+
const { containerRef, safeAreaRef } = useSceneSafeArea()
|
|
271
|
+
const isFullscreen = phx.frontend.useIsFullscreenActive()
|
|
272
|
+
|
|
273
|
+
return (
|
|
274
|
+
<div ref={containerRef} style={{ position: 'absolute', inset: 0 }}>
|
|
275
|
+
<LayoutSlots.Services />
|
|
276
|
+
<LayoutSlots.SharedCanvas />
|
|
277
|
+
|
|
278
|
+
{!isFullscreen && (
|
|
279
|
+
<aside
|
|
280
|
+
style={{
|
|
281
|
+
position: 'absolute',
|
|
282
|
+
insetBlock: GUTTER,
|
|
283
|
+
insetInlineStart: GUTTER,
|
|
284
|
+
inlineSize: SIDEBAR_WIDTH,
|
|
285
|
+
}}
|
|
286
|
+
>
|
|
287
|
+
<LayoutSlots.DesktopSidebar side="start" />
|
|
288
|
+
</aside>
|
|
289
|
+
)}
|
|
290
|
+
|
|
291
|
+
<div
|
|
292
|
+
ref={safeAreaRef}
|
|
293
|
+
style={{
|
|
294
|
+
position: 'absolute',
|
|
295
|
+
insetBlock: 0,
|
|
296
|
+
insetInlineStart: isFullscreen ? 0 : SIDEBAR_WIDTH + 2 * GUTTER,
|
|
297
|
+
insetInlineEnd: 0,
|
|
298
|
+
pointerEvents: 'none',
|
|
299
|
+
}}
|
|
300
|
+
>
|
|
301
|
+
<div style={{ position: 'absolute', insetBlockEnd: GUTTER, insetInlineStart: GUTTER, pointerEvents: 'auto' }}>
|
|
302
|
+
<LayoutSlots.DesktopToolbar />
|
|
303
|
+
</div>
|
|
304
|
+
</div>
|
|
305
|
+
</div>
|
|
306
|
+
)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export default phx.frontend.createCustomPhoenixLayoutElement('ConfiguratorLayout', ConfiguratorLayout)
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### ./src/expressions/expressions.ts
|
|
313
|
+
|
|
314
|
+
```ts
|
|
315
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
316
|
+
|
|
317
|
+
export default phx.domain.implementCustomExpressions({
|
|
318
|
+
Table: {
|
|
319
|
+
$edgeFinishFacet: s => {
|
|
320
|
+
return s.edgeFinish === 'Facet' ? 1 : 0
|
|
321
|
+
},
|
|
322
|
+
$edgeFinishRound: s => {
|
|
323
|
+
return s.edgeFinish === 'Round' ? 1 : 0
|
|
324
|
+
},
|
|
325
|
+
$edgeFinishTreeTrunk: s => {
|
|
326
|
+
return s.edgeFinish === 'TreeTrunk' ? 1 : 0
|
|
327
|
+
},
|
|
328
|
+
$edgeFinishOptions: () => {
|
|
329
|
+
return [
|
|
330
|
+
{
|
|
331
|
+
label: 'None',
|
|
332
|
+
img: '/images/edge-none.png',
|
|
333
|
+
value: phx.domain.model.EdgeFinish.None,
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
label: 'Facet',
|
|
337
|
+
img: '/images/edge-facet.png',
|
|
338
|
+
value: phx.domain.model.EdgeFinish.Facet,
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
label: 'Round',
|
|
342
|
+
img: '/images/edge-round.png',
|
|
343
|
+
value: phx.domain.model.EdgeFinish.Round,
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
label: 'TreeTrunk',
|
|
347
|
+
img: '/images/edge-tree-trunk.png',
|
|
348
|
+
value: phx.domain.model.EdgeFinish.TreeTrunk,
|
|
349
|
+
},
|
|
350
|
+
]
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
})
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### ./src/invariants/invariants.ts
|
|
357
|
+
|
|
358
|
+
```ts
|
|
359
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
360
|
+
|
|
361
|
+
export default phx.domain.implementCustomInvariants({
|
|
362
|
+
Table: {
|
|
363
|
+
trunkDisabled: s => {
|
|
364
|
+
if (s.edgeFinish === 'TreeTrunk') {
|
|
365
|
+
return [
|
|
366
|
+
{
|
|
367
|
+
type: 'trunkDisabled',
|
|
368
|
+
severity: 'fatal',
|
|
369
|
+
message: 'TreeTrunk finish is disabled because stuff!',
|
|
370
|
+
},
|
|
371
|
+
]
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return []
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
})
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
</details>
|
|
@@ -229,7 +229,7 @@ export type ClientSideImageComponentState = {
|
|
|
229
229
|
/** Output format: PNG (with transparency) or JPEG */
|
|
230
230
|
readonly fileFormat: 'PNG' | 'JPEG'
|
|
231
231
|
/** Optional background color (hex string) */
|
|
232
|
-
readonly backgroundColor?:
|
|
232
|
+
readonly backgroundColor?: HexColor
|
|
233
233
|
|
|
234
234
|
/** Tool state the image is rendered in (pins component filters keyed on tool state) */
|
|
235
235
|
readonly toolState: string
|
|
@@ -433,6 +433,10 @@ The following sample application has all the above described transformers active
|
|
|
433
433
|
- When the view changes to `Y`, the navigation state is changed to `C`.
|
|
434
434
|
- When any entity is deselected in view `Z`, the view is changed to `X`.
|
|
435
435
|
|
|
436
|
+
The example's own tests under `__tests__/` check these cases with `createMockDomainContext` on the config
|
|
437
|
+
`createTestApplicationConfig` builds from the generated application; [Testing](?path=/docs/phoenix-testing--docs)
|
|
438
|
+
shows how a project's test sets that up.
|
|
439
|
+
|
|
436
440
|
## Complete Implementation
|
|
437
441
|
|
|
438
442
|
{/* AUTO-GENERATED SECTION: This section is automatically updated by 'phoenix story build' */}
|
|
@@ -161,6 +161,10 @@ const toolState = phx.frontend.useActiveToolState()
|
|
|
161
161
|
const navigate = phx.frontend.useNavigate()
|
|
162
162
|
const pushNavigate = phx.frontend.usePushNavigate()
|
|
163
163
|
const popNavigate = phx.frontend.usePopNavigate()
|
|
164
|
+
|
|
165
|
+
// Fullscreen: a flag in the configurator's state that the layout honours; it does not put the browser into fullscreen
|
|
166
|
+
const isFullscreen = phx.frontend.useIsFullscreenActive()
|
|
167
|
+
const setFullscreen = phx.frontend.useSetFullscreen() // setFullscreen(true) or setFullscreen(false)
|
|
164
168
|
```
|
|
165
169
|
|
|
166
170
|
### Controls
|
|
@@ -386,7 +390,7 @@ const PresetOverlay = phx.frontend.createPresetDeviceNotSupported(SmallScreenNot
|
|
|
386
390
|
|
|
387
391
|
### Loaded Configuration UI
|
|
388
392
|
|
|
389
|
-
A layout element registered under the name `ConfiguratorLayout` replaces the whole UI of a loaded configuration, in owner and in read mode, whatever `layout` the spec sets. Without it, Phoenix renders the layout the spec selects.
|
|
393
|
+
A layout element registered under the name `ConfiguratorLayout` replaces the whole UI of a loaded configuration, in owner and in read mode, whatever `layout` the spec sets. Without it, Phoenix renders the layout the spec selects. [Custom Layout](?path=/docs/layouts-custom-layout--docs) is a worked example: a fully custom product page with its panel on the left, built from the scene, the safe-area hook and state hooks.
|
|
390
394
|
|
|
391
395
|
```typescript
|
|
392
396
|
// src/elements/ConfiguratorLayout.tsx
|
|
@@ -417,7 +421,9 @@ export default phx.frontend.createCustomPhoenixLayoutElement('ConfiguratorLayout
|
|
|
417
421
|
- `useSceneSafeArea()`, called without arguments, tells the camera which part of the canvas the UI leaves free. Attach `containerRef` to the element that covers the whole canvas and `safeAreaRef` to the region the scene should stay visible in: the hook measures the gap between the two and the camera frames the scene inside the safe area. Without it the scene is framed in the whole canvas. The safe-area element lies over the canvas, so it sets `pointerEvents: 'none'` to leave orbiting and clicking to the scene. A `canvasPadding` component in the spec scales the measured gaps, see [Canvas Padding](?path=/docs/layouts-canvas-padding--docs).
|
|
418
422
|
- The layout has to mount `LayoutSlots.SharedCanvas`, or the project sets `appLoader: legacy`. Otherwise the app loader stays on screen.
|
|
419
423
|
- The layout composes the rest from what `@salsita-ai/sdk/configurator` exports: `LayoutSlots.Services` and the other members of `LayoutSlots`.
|
|
420
|
-
-
|
|
424
|
+
- `LayoutSlots.DesktopSidebar` is the built-in desktop sidebar as one piece: the spec's tabs, back button, property pages and sidebar footer with their spacing, scrolling and empty states, and the assistant panel. Use it when the spec's property pages are what the customer should see and only their place changes, for example a sidebar on the left; build the controls yourself when they have to look like the brand's own. Without `width` it fills its container, so the layout positions and sizes that container; `miniSidebar` does not shrink it, so a layout that honours it reads `LayoutSlots.useSidebarContent().hasMiniSidebar` and lets the container take the sidebar's height. Its tabs sit outside it on the left; `side="start"` puts them on its right, for a sidebar on the left. In fullscreen it stays in place, transparent and inert, so a layout that wants the space back removes it while `phx.frontend.useIsFullscreenActive()` is set. See [Sidebar in a Custom Layout](?path=/docs/layouts-sidebar-in-a-custom-layout--docs).
|
|
425
|
+
- Nothing of the built-in layouts is rendered for it. `Legacy.CommonFinalizePage` shows the page of the `finalizePage` component in a dialog while one of its finalize navigation states is active, so a layout that shows a finalize button mounts it; without it the button changes the navigation state and nothing appears. It is marked deprecated and is the only exported piece that does this. `LayoutDialog` renders only what the project registers with `createLayoutDialog` and is not part of the finalize flow. The preview start screen is `DesktopPreview` or `MobilePreview`, which the layout shows while `usePreview().isPreview` is set. Both previews frame the scene themselves, so the layout renders the component that calls `useSceneSafeArea()` only while `isPreview` is not set; it then measures when it mounts after the preview. The assistant panel is available to a project layout only inside `LayoutSlots.DesktopSidebar`.
|
|
426
|
+
- The layout reads the state it has to react to from hooks. Read mode: `phx.frontend.useAccessService().accessInfo.viewMode`. Fullscreen: `phx.frontend.useIsFullscreenActive()` reads it and `phx.frontend.useSetFullscreen()` sets it; the layout decides what to hide. Preview: `usePreview()` from `@salsita-ai/sdk/configurator`.
|
|
421
427
|
- Any project layout element named `ConfiguratorLayout` takes over the page.
|
|
422
428
|
|
|
423
429
|
## TransformerBuilder
|
|
@@ -53,7 +53,8 @@ Every file in `components/`, `elements/`, `expressions/`, `actions/`, `invariant
|
|
|
53
53
|
(and their `admin/` counterparts) is imported by its location, so its default export must be what that folder's `phx`
|
|
54
54
|
factory returns, or an array of those. A file that exports anything else, such as a plain React component in
|
|
55
55
|
`elements/`, stops the application at start with an `Invalid default export in <file>` error that names the expected
|
|
56
|
-
shape.
|
|
56
|
+
shape. When several files in `handlers/`, `elements/`, `transformers/`, `components/`, `admin/elements/` and
|
|
57
|
+
`admin/components/` are wrong, the error lists all of them, one numbered line per file. Keep plain components and helpers in a `_*` folder.
|
|
57
58
|
|
|
58
59
|
### Admin Folders (src/admin/)
|
|
59
60
|
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Testing
|
|
2
|
+
|
|
3
|
+
- [Your application in a test](#your-application-in-a-test)
|
|
4
|
+
- [Run the domain on a preset](#run-the-domain-on-a-preset)
|
|
5
|
+
- [Test a transformer](#test-a-transformer)
|
|
6
|
+
- [Mock a domain context](#mock-a-domain-context)
|
|
7
|
+
- [Options](#options)
|
|
8
|
+
|
|
9
|
+
A project's tests check its own logic, such as an expression on a preset, a transformer that completes an
|
|
10
|
+
edit, a Blender projection or a migration, against the application the project ships. They run with
|
|
11
|
+
Vitest in Node, without a browser or a server.
|
|
12
|
+
|
|
13
|
+
## Your application in a test
|
|
14
|
+
|
|
15
|
+
`createTestApplicationConfig` from `@salsita-ai/sdk/domain` is the way a test builds the project's
|
|
16
|
+
application. It takes the generated application the test imports and returns its application config,
|
|
17
|
+
typed with the project's entities, states and data, with no type argument at the call:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { createTestApplicationConfig } from '@salsita-ai/sdk/domain'
|
|
21
|
+
|
|
22
|
+
import { configuratorBuilder } from '../../.phoenix/domain.js'
|
|
23
|
+
|
|
24
|
+
const config = createTestApplicationConfig(configuratorBuilder)
|
|
25
|
+
|
|
26
|
+
config.domain.presets // the presets of spec/presets
|
|
27
|
+
config.initialPublishedData // the published data the application starts with
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`configuratorBuilder` from `.phoenix/domain.js` is the domain of the configurator: the model, expressions,
|
|
31
|
+
invariants, actions, migrations, presets and the components declared in YAML. `configuratorConfig` from
|
|
32
|
+
`.phoenix/configurator-config.js` is the whole configurator as the browser runs it, with every component,
|
|
33
|
+
handler, element and transformer; it reads the frontend environment when it is imported, so a test that
|
|
34
|
+
uses it stubs `phx.frontendEnv` first.
|
|
35
|
+
|
|
36
|
+
A test passes the generated export to the helper and does not call the export's methods: they are how the
|
|
37
|
+
SDK assembles an application, not an interface for tests, while the helper's call does not depend on that
|
|
38
|
+
assembly.
|
|
39
|
+
|
|
40
|
+
## Run the domain on a preset
|
|
41
|
+
|
|
42
|
+
The config goes into `createDomainRuntime`, which runs the same systems as the configurator:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { vi } from 'vitest'
|
|
46
|
+
|
|
47
|
+
import { NoopAnalyticsSystem, createDomainRuntime, createTestApplicationConfig } from '@salsita-ai/sdk/domain'
|
|
48
|
+
|
|
49
|
+
import { configuratorBuilder, createI18n } from '../../.phoenix/domain.js'
|
|
50
|
+
|
|
51
|
+
const logger = {
|
|
52
|
+
info: vi.fn(),
|
|
53
|
+
error: vi.fn(),
|
|
54
|
+
warn: vi.fn(),
|
|
55
|
+
debug: vi.fn(),
|
|
56
|
+
verbose: vi.fn(),
|
|
57
|
+
appendToGlobalContext: vi.fn(),
|
|
58
|
+
}
|
|
59
|
+
const config = createTestApplicationConfig(configuratorBuilder)
|
|
60
|
+
const runtime = createDomainRuntime({
|
|
61
|
+
config,
|
|
62
|
+
services: { analyticsService: new NoopAnalyticsSystem(), i18n: createI18n(), logger },
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
runtime.actions.domainContext.configurator.setExecutionMode('finalize')
|
|
66
|
+
runtime.actions.domainData.setDomainData({
|
|
67
|
+
publishedData: config.initialPublishedData,
|
|
68
|
+
backendData: undefined,
|
|
69
|
+
frontendData: {},
|
|
70
|
+
})
|
|
71
|
+
runtime.actions.sessionContext.setSessionContext({ role: 'anonymous', data: {} })
|
|
72
|
+
|
|
73
|
+
const preset = config.domain.presets[0]!
|
|
74
|
+
runtime.actions.loadedData.completeConfiguratorLoad({
|
|
75
|
+
initialRevisionId: 'test',
|
|
76
|
+
initialEntities: preset.initialDomainEntities,
|
|
77
|
+
modelVersion: config.domain.domainFactory.modelVersion,
|
|
78
|
+
rootEntityType: preset.rootEntityType,
|
|
79
|
+
schemaIdentity: runtime.selectors.spec.getConfiguratorFactory(),
|
|
80
|
+
})
|
|
81
|
+
runtime.selectors.domainContext.configurator.getDomainContext().initialize()
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Test a transformer
|
|
85
|
+
|
|
86
|
+
The domain of `configuratorBuilder` has no code-attached components and no transformers. A test adds the
|
|
87
|
+
ones it needs, the way the configurator and the webhooks add them:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
// Files that read `phx.domain` at module scope are imported after `.phoenix/domain.js`.
|
|
91
|
+
const { customComponentOptics } = await import('../../.phoenix/configurator-domain-code.js')
|
|
92
|
+
const { default: addCookingTop } = await import('../transformers/add-cooking-top.js')
|
|
93
|
+
|
|
94
|
+
const config = createTestApplicationConfig(configuratorBuilder, {
|
|
95
|
+
componentModules: customComponentOptics,
|
|
96
|
+
transformers: {
|
|
97
|
+
importedTransformers: [{ origin: 'src/transformers/add-cooking-top.ts', exports: addCookingTop }],
|
|
98
|
+
specTransformers: [{ name: 'addCookingTop', priority: 30 }],
|
|
99
|
+
},
|
|
100
|
+
})
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`specTransformers` are the entries of the `transformers` section of `spec/application.yaml` that the test
|
|
104
|
+
runs, with the priorities the spec gives them.
|
|
105
|
+
|
|
106
|
+
## Mock a domain context
|
|
107
|
+
|
|
108
|
+
`createMockState`, `createMockDomain` and `createMockDomainContext` take the same config and check the
|
|
109
|
+
mock data against the project's types:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import { createMockDomainContext, createTestApplicationConfig } from '@salsita-ai/sdk/domain'
|
|
113
|
+
|
|
114
|
+
const config = createTestApplicationConfig(configuratorBuilder)
|
|
115
|
+
const preset = config.domain.presets[0]!
|
|
116
|
+
const { domainContext, destroy } = createMockDomainContext(config, {
|
|
117
|
+
entities: preset.initialDomainEntities,
|
|
118
|
+
rootEntityType: preset.rootEntityType,
|
|
119
|
+
publishedData: config.initialPublishedData,
|
|
120
|
+
frontendData: {},
|
|
121
|
+
sessionData: {},
|
|
122
|
+
backendData: undefined,
|
|
123
|
+
mode: 'owner',
|
|
124
|
+
role: 'anonymous',
|
|
125
|
+
transformers: [],
|
|
126
|
+
})
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Options
|
|
130
|
+
|
|
131
|
+
| Option | What it does |
|
|
132
|
+
| ------------------ | ------------------------------------------------------------------------------------------------------------------ |
|
|
133
|
+
| `componentModules` | Component files added after the components the application already has, each `{ origin, exports }` |
|
|
134
|
+
| `transformers` | Replaces the application's transformers: files (`importedTransformers`) and spec entries (`specTransformers`) |
|
|
135
|
+
| `tag` | The tag of the custom element the application mounts as; without it the application's own (`salsita-configurator`) |
|
|
136
|
+
|
|
137
|
+
## See Also
|
|
138
|
+
|
|
139
|
+
- [Best Practices](?path=/docs/phoenix-best-practices--docs) - Where custom code lives
|
|
140
|
+
- [Transformers](?path=/docs/other-features-transformers--docs) - Writing transformers
|
|
@@ -4,21 +4,22 @@ Build component controls with React and browser SDKs without pulling them into t
|
|
|
4
4
|
|
|
5
5
|
## Choose Where Each File Runs
|
|
6
6
|
|
|
7
|
-
| User file
|
|
8
|
-
|
|
|
9
|
-
| Component or admin component without a directive
|
|
10
|
-
| Component or admin component with `'use webhook'`
|
|
11
|
-
| Ordinary handler
|
|
12
|
-
| Published-data or
|
|
13
|
-
| Any other handler with `'use webhook'`
|
|
14
|
-
| File under `src/webhooks/`
|
|
7
|
+
| User file | Frontend | Webhook |
|
|
8
|
+
| ---------------------------------------------------- | -------- | ------- |
|
|
9
|
+
| Component or admin component without a directive | Yes | No |
|
|
10
|
+
| Component or admin component with `'use webhook'` | Yes | Yes |
|
|
11
|
+
| Ordinary handler | Yes | No |
|
|
12
|
+
| Published-data, validator or assistant setup handler | Yes | Yes |
|
|
13
|
+
| Any other handler with `'use webhook'` | Yes | Yes |
|
|
14
|
+
| File under `src/webhooks/` | No | Yes |
|
|
15
15
|
|
|
16
|
-
Published-data and
|
|
16
|
+
Published-data, validator and assistant setup handlers are detected when they register one of these APIs:
|
|
17
17
|
|
|
18
18
|
- `createMapPublishedRevisionDataHandler`
|
|
19
19
|
- `createMapPublishedChunkedDataHandler`
|
|
20
20
|
- `createDataRowValidatorHandler`
|
|
21
21
|
- `createSheetValidatorHandler`
|
|
22
|
+
- `phx.domain.createAssistantSetupHandler`
|
|
22
23
|
|
|
23
24
|
## Pin a Server-Safe Component
|
|
24
25
|
|
|
@@ -148,7 +148,7 @@ resources:
|
|
|
148
148
|
s3Key: clientImage
|
|
149
149
|
```
|
|
150
150
|
|
|
151
|
-
These resources are automatically made available as typed properties in task handlers via the `resources` parameter.
|
|
151
|
+
These resources are automatically made available as typed properties in task handlers via the `resources` parameter. Two static resources with the same name are an error: `phoenix check` and `phoenix build` report the second entry (`DUPLICATE_RESOURCE_NAME`).
|
|
152
152
|
|
|
153
153
|
#### Task Payload
|
|
154
154
|
|