@salsita-ai/sdk 0.50.0-develop.6d8967bc99 → 0.50.0-develop.6fc83d7732
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 +20 -15
- package/dist/admin/create-admin-pages-from-spec.d.ts +1 -1
- 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/index.d.ts +1 -0
- package/dist/application/index.js +1 -1
- package/dist/application/mount-application.d.ts +1 -1
- package/dist/application/mount-application.js +1 -1
- package/dist/application/mount-configurator.d.ts +19 -0
- package/dist/application/mount-configurator.js +1 -0
- package/dist/application/mounted-application-runtime.d.ts +4 -0
- package/dist/application/mounted-application-runtime.js +1 -0
- package/dist/application/register-web-component.d.ts +2 -0
- package/dist/application/register-web-component.js +1 -0
- 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/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/expressions/index.d.ts +2 -0
- package/dist/application-spec/core/expressions/index.js +1 -0
- package/dist/application-spec/core/expressions/relation-expression-params.d.ts +11 -0
- package/dist/application-spec/core/expressions/sdk-expression-implementation.d.ts +24 -0
- package/dist/application-spec/core/expressions/sdk-expression-implementation.js +1 -0
- package/dist/application-spec/core/index.d.ts +2 -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/enhanced-json/eval-to-constant.d.ts +14 -0
- 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 +23 -0
- package/dist/application-spec/errors/diagnostic-codes.js +1 -1
- package/dist/application-spec/errors/diagnostic-messages.d.ts +27 -1
- package/dist/application-spec/errors/diagnostic-messages.js +1 -1
- package/dist/application-spec/generate-application/code/common/code-imports.js +1 -1
- package/dist/application-spec/generate-application/code/common/env-reader.js +2 -2
- package/dist/application-spec/generate-application/code/common/index.d.ts +1 -0
- package/dist/application-spec/generate-application/code/common/index.js +1 -1
- package/dist/application-spec/generate-application/code/common/single-quoted-string.d.ts +6 -0
- package/dist/application-spec/generate-application/code/common/single-quoted-string.js +1 -0
- 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 +22 -14
- package/dist/application-spec/generate-application/code/project-scaffolding.js +2 -2
- package/dist/application-spec/generate-application/code/standalone-app-files.d.ts +2 -2
- package/dist/application-spec/generate-application/code/standalone-app-files.js +1 -1
- package/dist/application-spec/generate-application/code/story-files.js +1 -1
- package/dist/application-spec/generate-application/code/templates/admin-config.js +3 -0
- package/dist/application-spec/generate-application/code/templates/application-types.js +6 -6
- package/dist/application-spec/generate-application/code/templates/common/doc-comment.d.ts +2 -0
- package/dist/application-spec/generate-application/code/templates/common/doc-comment.js +1 -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/configurator-config.js +6 -4
- package/dist/application-spec/generate-application/code/templates/configurator-globals.d.ts +2 -1
- package/dist/application-spec/generate-application/code/templates/configurator-globals.js +19 -6
- package/dist/application-spec/generate-application/code/templates/domain-spec/factory-types/domain-factory-types.d.ts +35 -0
- package/dist/application-spec/generate-application/code/templates/domain-spec/factory-types/domain-factory-types.js +5 -0
- package/dist/application-spec/generate-application/code/templates/domain-spec/factory-types/index.d.ts +1 -0
- package/dist/application-spec/generate-application/code/templates/domain-spec/factory-types/index.js +1 -0
- package/dist/application-spec/generate-application/code/templates/domain-spec/generate-component-optics.d.ts +1 -1
- package/dist/application-spec/generate-application/code/templates/domain-spec/generate-component-optics.js +7 -7
- package/dist/application-spec/generate-application/code/templates/domain-spec/generate-defaults.d.ts +4 -2
- 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 +72 -76
- package/dist/application-spec/generate-application/code/templates/domain-spec/generate-invariants.d.ts +9 -1
- package/dist/application-spec/generate-application/code/templates/domain-spec/generate-invariants.js +1 -1
- 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-presets.js +1 -1
- package/dist/application-spec/generate-application/code/templates/domain-spec/generate-relation-expressions.js +1 -1
- 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 +21 -21
- package/dist/application-spec/generate-application/code/templates/domain-spec/generate-states.d.ts +1 -1
- package/dist/application-spec/generate-application/code/templates/domain-spec/generate-states.js +2 -2
- package/dist/application-spec/generate-application/code/templates/domain-spec/index.d.ts +3 -2
- 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/domain.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/global-definition/generate-action-types.js +7 -7
- package/dist/application-spec/generate-application/code/templates/global-definition/generate-assistant-form-element-types.d.ts +12 -0
- package/dist/application-spec/generate-application/code/templates/global-definition/generate-assistant-form-element-types.js +7 -0
- package/dist/application-spec/generate-application/code/templates/global-definition/generate-phx-types.d.ts +43 -0
- package/dist/application-spec/generate-application/code/templates/global-definition/generate-phx-types.js +33 -0
- package/dist/application-spec/generate-application/code/templates/global-definition/index.d.ts +2 -0
- package/dist/application-spec/generate-application/code/templates/global-definition/index.js +1 -1
- package/dist/application-spec/generate-application/code/templates/globals.js +24 -43
- package/dist/application-spec/generate-application/code/templates/index.d.ts +3 -10
- 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 +66 -42
- 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 +9 -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 +42 -18
- package/dist/application-spec/generate-application/code/templates/skills/sdk-content.js +6 -8
- 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/story/phx-module.d.ts +8 -2
- package/dist/application-spec/generate-application/code/templates/story/phx-module.js +15 -3
- package/dist/application-spec/generate-application/code/templates/story/wrapper.d.ts +3 -0
- package/dist/application-spec/generate-application/code/templates/story/wrapper.js +2 -1
- 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/webhook-builders.js +4 -3
- package/dist/application-spec/generate-application/code/templates/webhook-index.d.ts +6 -1
- package/dist/application-spec/generate-application/code/templates/webhook-index.js +10 -1
- package/dist/application-spec/generate-application/code/templates/webhooks-config.js +2 -3
- package/dist/application-spec/generate-application/implementations/phx-application-types.d.ts +25 -0
- package/dist/application-spec/generate-application/implementations/phx-application-types.js +1 -0
- package/dist/application-spec/generate-application/implementations/phx-domain.d.ts +36 -0
- package/dist/application-spec/generate-application/implementations/phx-domain.js +1 -0
- package/dist/application-spec/generate-application/implementations/phx-model.d.ts +24 -0
- package/dist/application-spec/generate-application/implementations/phx-model.js +1 -0
- package/dist/application-spec/generate-application/implementations/phx.d.ts +22 -0
- package/dist/application-spec/generate-application/implementations/phx.js +1 -0
- package/dist/application-spec/generate-application/index.d.ts +4 -0
- package/dist/application-spec/generate-application/index.js +1 -1
- package/dist/application-spec/generate-application/runtime/admin-runtime/admin-model-runtime.d.ts +6 -4
- package/dist/application-spec/generate-application/runtime/admin-runtime/admin-model-runtime.js +1 -1
- package/dist/application-spec/generate-application/runtime/admin-runtime/admin-runtime.d.ts +12 -4
- package/dist/application-spec/generate-application/runtime/admin-runtime/admin-runtime.js +1 -1
- package/dist/application-spec/generate-application/runtime/admin-runtime/admin-spec-runtime.d.ts +16 -0
- package/dist/application-spec/generate-application/runtime/admin-runtime/admin-spec-runtime.js +1 -0
- 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 +35 -10
- package/dist/application-spec/generate-application/runtime/application-runtime.js +1 -1
- package/dist/application-spec/generate-application/runtime/application-spec-slice.d.ts +71 -0
- package/dist/application-spec/generate-application/runtime/application-spec-slice.js +1 -0
- package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-model-runtime.d.ts +7 -5
- package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-model-runtime.js +1 -1
- package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-runtime.d.ts +12 -7
- package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-runtime.js +1 -1
- package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-spec-runtime.d.ts +16 -0
- package/dist/application-spec/generate-application/runtime/configurator-runtime/configurator-spec-runtime.js +1 -0
- package/dist/application-spec/generate-application/runtime/configurator-runtime/states-runtime.d.ts +2 -2
- package/dist/application-spec/generate-application/runtime/create-application.d.ts +57 -0
- package/dist/application-spec/generate-application/runtime/create-application.js +1 -0
- package/dist/application-spec/generate-application/runtime/factory-data/factory-data-runtime.d.ts +43 -0
- package/dist/application-spec/generate-application/runtime/factory-data/factory-data-runtime.js +1 -0
- package/dist/application-spec/generate-application/runtime/factory-data/index.d.ts +3 -0
- package/dist/application-spec/generate-application/runtime/factory-data/index.js +1 -0
- package/dist/application-spec/generate-application/runtime/factory-data/recipe-defaults.d.ts +13 -0
- package/dist/application-spec/generate-application/runtime/factory-data/recipe-defaults.js +1 -0
- package/dist/application-spec/generate-application/runtime/factory-data/static-violations.d.ts +16 -0
- package/dist/application-spec/generate-application/runtime/factory-data/static-violations.js +1 -0
- package/dist/application-spec/generate-application/runtime/index.d.ts +8 -0
- package/dist/application-spec/generate-application/runtime/index.js +1 -0
- package/dist/application-spec/generate-application/runtime/migrations/index.d.ts +1 -0
- package/dist/application-spec/generate-application/runtime/migrations/index.js +1 -0
- package/dist/application-spec/generate-application/runtime/migrations/migration-runtime.d.ts +53 -0
- package/dist/application-spec/generate-application/runtime/migrations/migration-runtime.js +1 -0
- package/dist/application-spec/generate-application/runtime/sections/assistant-runtime.d.ts +2 -2
- package/dist/application-spec/generate-application/runtime/sections/build-sdk-optic.d.ts +3 -2
- package/dist/application-spec/generate-application/runtime/sections/build-sdk-optic.js +1 -1
- package/dist/application-spec/generate-application/runtime/sections/build-user-bound-optic.d.ts +2 -1
- package/dist/application-spec/generate-application/runtime/sections/build-user-bound-optic.js +1 -1
- package/dist/application-spec/generate-application/runtime/sections/component-optic.d.ts +5 -1
- package/dist/application-spec/generate-application/runtime/sections/component-optic.js +1 -1
- 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 +15 -6
- package/dist/application-spec/generate-application/runtime/sections/expression-runtime.js +1 -1
- package/dist/application-spec/generate-application/runtime/sections/generate-memo-eval-plan-runtime.d.ts +9 -5
- package/dist/application-spec/generate-application/runtime/sections/generate-memo-eval-plan-runtime.js +1 -1
- package/dist/application-spec/generate-application/runtime/sections/reflection-constants-runtime.d.ts +3 -1
- package/dist/application-spec/generate-application/runtime/sections/reflection-constants-runtime.js +1 -1
- 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 +4 -6
- package/dist/application-spec/generate-application/runtime/spec-code/index.d.ts +2 -0
- package/dist/application-spec/generate-application/runtime/spec-code/index.js +1 -0
- package/dist/application-spec/generate-application/runtime/spec-code/resolve-spec-code.d.ts +18 -0
- package/dist/application-spec/generate-application/runtime/spec-code/resolve-spec-code.js +1 -0
- package/dist/application-spec/generate-application/runtime/spec-code/spec-code-module.d.ts +17 -0
- package/dist/application-spec/generate-application/runtime/spec-runtime.d.ts +21 -0
- package/dist/application-spec/generate-application/runtime/spec-runtime.js +1 -0
- 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 +28 -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 +103 -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/index.d.ts +1 -0
- package/dist/application-spec/index.js +1 -1
- package/dist/application-spec/parse-application-files/categorize/categorize-files.d.ts +34 -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/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/parse-yaml-spec-file.js +1 -1
- package/dist/application-spec/parse-application-files/raw-spec/raw-assistant.d.ts +47 -4
- 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/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-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/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/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/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-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/expression.d.ts +1 -28
- package/dist/application-spec/parse-application-files/spec/expressions/expression.js +1 -1
- package/dist/application-spec/parse-application-files/spec/expressions/expressions.d.ts +7 -3
- package/dist/application-spec/parse-application-files/spec/expressions/expressions.js +1 -1
- package/dist/application-spec/parse-application-files/spec/expressions/index.d.ts +0 -1
- package/dist/application-spec/parse-application-files/spec/expressions/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.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/defaults.d.ts +7 -2
- package/dist/application-spec/parse-application-files/spec/model/defaults.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/application-spec/parse-application-files/spec/relations/reference-field-type.d.ts +25 -0
- package/dist/application-spec/parse-application-files/spec/relations/reference-field-type.js +1 -0
- package/dist/application-spec/parse-application-files/spec/relations/relation-field-mismatch.d.ts +46 -0
- package/dist/application-spec/parse-application-files/spec/relations/relation-field-mismatch.js +1 -0
- package/dist/application-spec/parse-application-files/spec/relations/relations.d.ts +4 -3
- package/dist/application-spec/parse-application-files/spec/relations/relations.js +1 -1
- package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-form-elements.d.ts +9 -0
- package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-form-elements.js +1 -0
- package/dist/application-spec/parse-application-files/spec/sections/assistant/assistant-schema.d.ts +4 -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/authorized-client.d.ts +3 -1
- package/dist/client/authorized-client.js +1 -1
- package/dist/client/contract.d.ts +34 -0
- package/dist/client/contract.js +1 -1
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +1 -1
- package/dist/client/local-authorized-client.d.ts +3 -1
- package/dist/client/local-authorized-client.js +1 -1
- package/dist/client/memory-authorized-client.d.ts +3 -1
- package/dist/client/memory-authorized-client.js +1 -1
- package/dist/client/phoenix-api.d.ts +797 -121
- package/dist/configurator/ConfiguratorApp.js +1 -1
- package/dist/configurator/app-loader-content/DefaultAppLoaderContent.d.ts +2 -0
- package/dist/configurator/app-loader-content/DefaultAppLoaderContent.js +5 -0
- package/dist/configurator/app-loader-content/index.d.ts +1 -0
- package/dist/configurator/app-loader-content/index.js +1 -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 +130 -5
- package/dist/configurator/application-runtime/extensions/systems/assistant-system/assistant-system.js +1 -1
- package/dist/configurator/application-runtime/extensions/systems/assistant-system/graphql-tools.d.ts +39 -7
- package/dist/configurator/application-runtime/extensions/systems/assistant-system/graphql-tools.js +1 -1
- package/dist/configurator/application-runtime/extensions/systems/camera-control-system/camera-control-system.d.ts +1 -1
- package/dist/configurator/application-runtime/extensions/systems/canvas-system.d.ts +8 -0
- package/dist/configurator/application-runtime/extensions/systems/canvas-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/export-system/export-busy-error.d.ts +8 -0
- package/dist/configurator/application-runtime/extensions/systems/export-system/export-busy-error.js +1 -0
- package/dist/configurator/application-runtime/extensions/systems/export-system/export-system.d.ts +23 -1
- package/dist/configurator/application-runtime/extensions/systems/export-system/export-system.js +1 -1
- package/dist/configurator/application-runtime/extensions/systems/graphql-system/close-unclosed-braces.d.ts +12 -0
- package/dist/configurator/application-runtime/extensions/systems/graphql-system/close-unclosed-braces.js +1 -0
- package/dist/configurator/application-runtime/extensions/systems/graphql-system/entity-meta.d.ts +14 -1
- package/dist/configurator/application-runtime/extensions/systems/graphql-system/entity-meta.js +1 -1
- package/dist/configurator/application-runtime/extensions/systems/graphql-system/graphql-system.d.ts +64 -7
- 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/image-rendering-system/image-rendering-system.d.ts +14 -5
- package/dist/configurator/application-runtime/extensions/systems/image-rendering-system/image-rendering-system.js +1 -1
- package/dist/configurator/application-runtime/extensions/systems/index.d.ts +2 -2
- 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 +145 -5
- package/dist/configurator/common/configurator-interface.js +1 -1
- package/dist/configurator/common/index.d.ts +3 -1
- package/dist/configurator/common/index.js +1 -1
- package/dist/configurator/common/use-reset-camera.d.ts +6 -0
- package/dist/configurator/common/use-reset-camera.js +1 -0
- package/dist/configurator/configuration-not-found-page/BuiltInConfigurationNotFoundPage.d.ts +5 -0
- package/dist/configurator/configuration-not-found-page/BuiltInConfigurationNotFoundPage.js +1 -0
- package/dist/configurator/configuration-not-found-page/ConfigurationNotFoundPageElement.d.ts +6 -0
- package/dist/configurator/configuration-not-found-page/ConfigurationNotFoundPageElement.js +1 -0
- package/dist/configurator/configuration-not-found-page/index.d.ts +2 -0
- package/dist/configurator/configuration-not-found-page/index.js +1 -0
- package/dist/configurator/configurator-context/ConfiguratorResponsiveProvider.d.ts +12 -0
- package/dist/configurator/configurator-context/ConfiguratorResponsiveProvider.js +1 -0
- package/dist/configurator/configurator-context/PresetSelectionProvider.js +1 -1
- package/dist/configurator/configurator-context/PreviewContextProvider.js +1 -1
- package/dist/configurator/configurator-context/ViewportStateSync.d.ts +2 -2
- package/dist/configurator/configurator-context/ViewportStateSync.js +1 -1
- package/dist/configurator/configurator-context/configurator-hooks/layout-hooks.d.ts +11 -1
- package/dist/configurator/configurator-context/configurator-routing/ConfiguratorRoutingProvider.js +1 -1
- package/dist/configurator/configurator-error-page/BuiltInConfiguratorErrorPage.d.ts +6 -0
- package/dist/configurator/configurator-error-page/BuiltInConfiguratorErrorPage.js +1 -0
- package/dist/configurator/configurator-error-page/ConfiguratorErrorPageElement.d.ts +7 -0
- package/dist/configurator/configurator-error-page/ConfiguratorErrorPageElement.js +1 -0
- package/dist/configurator/configurator-error-page/index.d.ts +2 -0
- package/dist/configurator/configurator-error-page/index.js +1 -0
- package/dist/configurator/index.d.ts +10 -0
- package/dist/configurator/index.js +1 -1
- package/dist/configurator/pages/ConfiguratorErrorPage.d.ts +0 -1
- package/dist/configurator/pages/ConfiguratorErrorPage.js +1 -1
- package/dist/configurator/pages/ConfiguratorNotFoundPage.js +1 -1
- package/dist/configurator/pages/DefaultConfiguratorLayout.d.ts +7 -0
- package/dist/configurator/pages/DefaultConfiguratorLayout.js +1 -0
- package/dist/configurator/pages/DefaultNotAuthenticatedPage.d.ts +8 -0
- package/dist/configurator/pages/DefaultNotAuthenticatedPage.js +1 -0
- package/dist/configurator/pages/DefaultPresetUi.d.ts +7 -0
- package/dist/configurator/pages/DefaultPresetUi.js +1 -0
- package/dist/configurator/pages/LoadedConfiguratorDetailPage.js +1 -1
- package/dist/configurator/pages/NewConfiguratorPage.js +1 -1
- package/dist/configurator/pages/PresetCreationErrorPage.d.ts +1 -1
- package/dist/configurator/pages/PresetCreationErrorPage.js +1 -1
- package/dist/configurator/pages/RootPage.js +1 -1
- package/dist/configurator/pages/configurator-layout-name.d.ts +2 -0
- package/dist/configurator/pages/configurator-layout-name.js +1 -0
- package/dist/configurator/pages/default-configurator-elements.d.ts +13 -0
- package/dist/configurator/pages/default-configurator-elements.js +1 -0
- 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 +5 -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/scene-controls/BuiltInAddEntityFab.d.ts +3 -0
- package/dist/configurator/pages/default-elements/scene-controls/BuiltInAddEntityFab.js +1 -0
- package/dist/configurator/pages/default-elements/scene-controls/BuiltInDeleteEntityFab.d.ts +3 -0
- package/dist/configurator/pages/default-elements/scene-controls/BuiltInDeleteEntityFab.js +1 -0
- package/dist/configurator/pages/default-elements/scene-controls/BuiltInNavigationHotspot.d.ts +3 -0
- package/dist/configurator/pages/default-elements/scene-controls/BuiltInNavigationHotspot.js +1 -0
- package/dist/configurator/pages/default-elements/scene-controls/BuiltInSelectionHotspot.d.ts +3 -0
- package/dist/configurator/pages/default-elements/scene-controls/BuiltInSelectionHotspot.js +1 -0
- package/dist/configurator/pages/default-elements/scene-controls/BuiltInToolMenuFab.d.ts +3 -0
- package/dist/configurator/pages/default-elements/scene-controls/BuiltInToolMenuFab.js +1 -0
- package/dist/configurator/pages/default-elements/scene-controls/index.d.ts +6 -0
- package/dist/configurator/pages/default-elements/scene-controls/index.js +1 -0
- package/dist/configurator/pages/default-elements/scene-controls/scene-control-elements.d.ts +6 -0
- package/dist/configurator/pages/default-elements/scene-controls/scene-control-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/pages/error-boundaries/GeneralErrorBoundary.js +1 -1
- package/dist/configurator/pages/error-boundaries/LoadingErrorBoundary.js +1 -1
- package/dist/configurator/pages/index.d.ts +1 -0
- package/dist/configurator/pages/index.js +1 -1
- package/dist/configurator/preset-screen-elements/PresetDeviceNotSupportedElement.d.ts +4 -0
- package/dist/configurator/preset-screen-elements/PresetDeviceNotSupportedElement.js +1 -0
- package/dist/configurator/preset-screen-elements/PresetPageActionElement.d.ts +4 -0
- package/dist/configurator/preset-screen-elements/PresetPageActionElement.js +1 -0
- package/dist/configurator/preset-screen-elements/index.d.ts +2 -0
- package/dist/configurator/preset-screen-elements/index.js +1 -0
- package/dist/configurator/scene/index.d.ts +1 -0
- package/dist/configurator/scene/index.js +1 -1
- package/dist/configurator/scene/scene-controls/DeleteEntityFab.js +1 -1
- package/dist/configurator/scene/scene-controls/SelectionHotspot.js +1 -1
- package/dist/configurator/scene/scene-controls/ToolFab.d.ts +4 -0
- package/dist/configurator/scene/scene-controls/ToolFab.js +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-controls/use-scene-control-tooltip.d.ts +2 -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/scene/scene-entity/scene-controls/SceneEntityHtmlHotspots.js +1 -1
- package/dist/configurator/toolbar-actions/index.d.ts +3 -0
- package/dist/configurator/toolbar-actions/index.js +1 -0
- package/dist/configurator/toolbar-actions/use-assistant-panel.d.ts +18 -0
- package/dist/configurator/toolbar-actions/use-assistant-panel.js +1 -0
- package/dist/configurator/toolbar-actions/use-measurements.d.ts +35 -0
- package/dist/configurator/toolbar-actions/use-measurements.js +1 -0
- package/dist/configurator/toolbar-actions/use-toolbar-actions.d.ts +100 -0
- package/dist/configurator/toolbar-actions/use-toolbar-actions.js +1 -0
- package/dist/configurator/toolbar-actions/use-toolbar-help.d.ts +7 -0
- package/dist/configurator/toolbar-actions/use-toolbar-help.js +1 -0
- package/dist/configurator/ui/assistant/AssistantChips.d.ts +10 -3
- package/dist/configurator/ui/assistant/AssistantChips.js +1 -1
- package/dist/configurator/ui/assistant/AssistantComposer.d.ts +2 -1
- package/dist/configurator/ui/assistant/AssistantComposer.js +1 -1
- 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.d.ts +6 -2
- package/dist/configurator/ui/assistant/AssistantThread.js +1 -1
- package/dist/configurator/ui/assistant/alert/AssistantAlert.d.ts +8 -0
- package/dist/configurator/ui/assistant/alert/AssistantAlert.js +1 -0
- package/dist/configurator/ui/assistant/alert/index.d.ts +2 -0
- package/dist/configurator/ui/assistant/alert/index.js +1 -0
- package/dist/configurator/ui/assistant/alert/remark-alerts.d.ts +10 -0
- package/dist/configurator/ui/assistant/alert/remark-alerts.js +1 -0
- 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/index.d.ts +2 -0
- package/dist/configurator/ui/assistant/entity-link/index.js +1 -0
- 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/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-model.d.ts +37 -0
- package/dist/configurator/ui/assistant/form/form-model.js +1 -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 +4 -0
- package/dist/configurator/ui/assistant/form/index.js +1 -0
- package/dist/configurator/ui/assistant/form/parse-form-block.d.ts +10 -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/registry/built-in-form-elements.d.ts +3 -0
- package/dist/configurator/ui/assistant/form/registry/built-in-form-elements.js +1 -0
- package/dist/configurator/ui/assistant/form/registry/create-assistant-form-element.d.ts +31 -0
- package/dist/configurator/ui/assistant/form/registry/create-assistant-form-element.js +1 -0
- package/dist/configurator/ui/assistant/form/registry/form-element-definition.d.ts +16 -0
- package/dist/configurator/ui/assistant/form/registry/form-element-definition.js +1 -0
- package/dist/configurator/ui/assistant/form/registry/form-registry.d.ts +31 -0
- package/dist/configurator/ui/assistant/form/registry/form-registry.js +1 -0
- package/dist/configurator/ui/assistant/form/registry/index.d.ts +6 -0
- package/dist/configurator/ui/assistant/form/registry/index.js +1 -0
- package/dist/configurator/ui/assistant/form/registry/use-form-registry.d.ts +8 -0
- package/dist/configurator/ui/assistant/form/registry/use-form-registry.js +1 -0
- package/dist/configurator/ui/assistant/form-elements/FormCard.d.ts +9 -0
- package/dist/configurator/ui/assistant/form-elements/FormCard.js +1 -0
- package/dist/configurator/ui/assistant/form-elements/FormConfirm.d.ts +9 -0
- package/dist/configurator/ui/assistant/form-elements/FormConfirm.js +1 -0
- package/dist/configurator/ui/assistant/form-elements/FormGroup.d.ts +6 -0
- package/dist/configurator/ui/assistant/form-elements/FormGroup.js +1 -0
- package/dist/configurator/ui/assistant/form-elements/FormNumber.d.ts +9 -0
- package/dist/configurator/ui/assistant/form-elements/FormNumber.js +1 -0
- package/dist/configurator/ui/assistant/form-elements/FormOptions.d.ts +6 -0
- package/dist/configurator/ui/assistant/form-elements/FormOptions.js +1 -0
- package/dist/configurator/ui/assistant/form-elements/FormReply.d.ts +6 -0
- package/dist/configurator/ui/assistant/form-elements/FormReply.js +1 -0
- package/dist/configurator/ui/assistant/form-elements/FormSwatch.d.ts +10 -0
- package/dist/configurator/ui/assistant/form-elements/FormSwatch.js +1 -0
- package/dist/configurator/ui/assistant/form-elements/FormText.d.ts +6 -0
- package/dist/configurator/ui/assistant/form-elements/FormText.js +1 -0
- package/dist/configurator/ui/assistant/form-elements/clamp-form-number.d.ts +10 -0
- package/dist/configurator/ui/assistant/form-elements/clamp-form-number.js +1 -0
- package/dist/configurator/ui/assistant/form-elements/form-element-props.d.ts +38 -0
- package/dist/configurator/ui/assistant/form-elements/form-element-props.js +1 -0
- package/dist/configurator/ui/assistant/form-elements/index.d.ts +9 -0
- package/dist/configurator/ui/assistant/form-elements/index.js +1 -0
- package/dist/configurator/ui/assistant/index.d.ts +3 -0
- package/dist/configurator/ui/assistant/index.js +1 -1
- 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 +2 -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/screenshots/ScreenshotThumbnails.d.ts +9 -0
- package/dist/configurator/ui/assistant/screenshots/ScreenshotThumbnails.js +1 -0
- package/dist/configurator/ui/assistant/screenshots/index.d.ts +2 -0
- package/dist/configurator/ui/assistant/screenshots/index.js +1 -0
- package/dist/configurator/ui/assistant/screenshots/tool-screenshots.d.ts +14 -0
- package/dist/configurator/ui/assistant/screenshots/tool-screenshots.js +1 -0
- package/dist/configurator/ui/assistant/streamed-markdown.d.ts +4 -2
- package/dist/configurator/ui/assistant/streamed-markdown.js +2 -2
- package/dist/configurator/ui/assistant/thread-items.d.ts +21 -3
- package/dist/configurator/ui/assistant/thread-items.js +1 -1
- package/dist/configurator/ui/assistant/tool-chip.js +1 -1
- package/dist/configurator/ui/assistant/use-follow-stream.js +1 -1
- 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/canvas/LayoutCanvas.js +1 -1
- package/dist/configurator/ui/configurator-layout/slots/canvas/SharedCanvas.js +1 -1
- 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/mobile-tabs/use-mobile-tabs.d.ts +1 -1
- package/dist/configurator/ui/configurator-layout/slots/mobile-tabs/use-mobile-tabs.js +1 -1
- package/dist/configurator/ui/configurator-layout/slots/services/InvariantViolations.d.ts +4 -4
- 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/configurator-layout/slots/services/SideEffects.d.ts +3 -6
- package/dist/configurator/ui/configurator-layout/slots/services/SideEffects.js +1 -1
- package/dist/configurator/ui/configurator-layout/slots/sidebar-tabs/use-sidebar-tabs.d.ts +1 -1
- package/dist/configurator/ui/configurator-layout/slots/sidebar-tabs/use-sidebar-tabs.js +1 -1
- 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/configurator-layout/universal/DesktopPreview.js +1 -1
- package/dist/configurator/ui/configurator-layout/universal/ExportingOverlay.d.ts +5 -0
- package/dist/configurator/ui/configurator-layout/universal/ExportingOverlay.js +1 -0
- package/dist/configurator/ui/configurator-layout/universal/Layout.js +1 -1
- package/dist/configurator/ui/configurator-layout/universal/MobileLayout.js +4 -4
- package/dist/configurator/ui/configurator-layout/universal/MobilePreview.js +1 -1
- package/dist/configurator/ui/configurator-layout/universal/use-layout-scene-safe-area.d.ts +47 -0
- package/dist/configurator/ui/configurator-layout/universal/use-layout-scene-safe-area.js +1 -0
- package/dist/configurator/ui/configurator-layout/universal/use-scene-safe-area.d.ts +11 -26
- package/dist/configurator/ui/configurator-layout/universal/use-scene-safe-area.js +1 -1
- package/dist/configurator/ui/index.d.ts +1 -0
- package/dist/configurator/ui/index.js +1 -1
- package/dist/configurator/ui/legacy/layout/common/CommonFinalizeButton.js +1 -1
- package/dist/configurator/ui/legacy/layout/common/CommonSummaryButton.js +1 -1
- package/dist/configurator/ui/legacy/layout/common/CommonTopNavigation.js +1 -1
- package/dist/configurator/ui/legacy/layout/common/ReactiveInterfaceLayout.js +1 -1
- package/dist/configurator/ui/legacy/layout/common/ResponsiveFinalizeButton.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/simple/SimpleTopNavigation.js +1 -1
- package/dist/configurator/ui/legacy/layout/steps/StepsNavigation.d.ts +2 -2
- package/dist/configurator/ui/legacy/layout/steps/StepsNavigation.js +1 -1
- package/dist/configurator/ui/legacy/layout/steps/StepsOwnerLayout.js +1 -1
- package/dist/configurator/ui/legacy/layout/steps/StepsSidebarFooter.js +1 -1
- package/dist/configurator/ui/legacy/layout/tabs/TabsOwnerLayout.js +1 -1
- package/dist/configurator/ui/legacy/toolbar/common/DimensionUnitSelector.js +1 -1
- package/dist/configurator/ui/legacy/toolbar/common/MeasurementsIndicatorsSwitch.js +1 -1
- package/dist/configurator/ui/legacy/toolbar/desktop-toolbar/FullscreenToolbarAction.js +1 -1
- package/dist/configurator/ui/legacy/toolbar/desktop-toolbar/HelpToolbarAction.js +1 -1
- package/dist/configurator/ui/legacy/toolbar/desktop-toolbar/MeasurementsToolbarAction.js +1 -1
- package/dist/configurator/ui/legacy/toolbar/desktop-toolbar/UndoRedoToolbarGroup.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/AppLoader.d.ts +0 -7
- package/dist/configurator/ui/phoenix-layout-elements/AppLoader.js +2 -6
- package/dist/configurator/ui/phoenix-layout-elements/AssistantToolbarButton.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/EnterFullscreenToolbarButton.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/ExitFullscreenToolbarButton.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/HelpToolbarButton.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/LanguageMenuAction.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/LanguageToolbarButton.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/configurator/ui/phoenix-layout-elements/MeasurementsMenuAction.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/MeasurementsToolbarButton.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/RedoToolbarButton.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/ShareToolbarButton.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/UndoToolbarButton.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/header/common/back-button/use-go-back-action.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/header/common/horizontal-tabs/DesktopHorizontalTabs.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/header/common/step-navigation/DesktopNavigationSteps.d.ts +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/header/common/step-navigation/DesktopNavigationSteps.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/header/common/step-navigation/StepsNavigation.d.ts +2 -8
- package/dist/configurator/ui/phoenix-layout-elements/header/common/step-navigation/StepsNavigation.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/index.d.ts +8 -1
- package/dist/configurator/ui/phoenix-layout-elements/index.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/measurements/DimensionUnitSelector.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/measurements/MeasurementsIndicatorsSwitch.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/share/ShareModal.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/share/use-share.d.ts +7 -8
- package/dist/configurator/ui/phoenix-layout-elements/share/use-share.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/sidebar-footer/basic/BasicFinalizeButton.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/sidebar-footer/common/CustomFinalizeButton.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/sidebar-footer/common/EnterSummaryButton.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/sidebar-footer/common/FinalizeActions.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/sidebar-footer/common/NextStepButton.js +1 -1
- package/dist/configurator/ui/phoenix-layout-elements/sidebar-footer/common/PreviousStepButton.js +1 -1
- package/dist/configurator/ui/preset-layout/CategoryPresetUi.js +1 -1
- package/dist/configurator/ui/preset-layout/PresetUi.d.ts +4 -2
- package/dist/configurator/ui/preset-layout/PresetUi.js +1 -1
- package/dist/configurator/ui/preset-layout/index.d.ts +6 -1
- package/dist/configurator/ui/preset-layout/index.js +1 -1
- package/dist/configurator/ui/save-message-snackbars/SaveMessageSnackbars.d.ts +10 -0
- package/dist/configurator/ui/save-message-snackbars/SaveMessageSnackbars.js +1 -0
- package/dist/configurator/ui/save-message-snackbars/index.d.ts +1 -0
- package/dist/configurator/ui/save-message-snackbars/index.js +1 -0
- package/dist/docs/complex-use-cases/filtered-controls-with-fix-dialog.mdx +16 -19
- package/dist/docs/complex-use-cases/focus-mode.mdx +2 -0
- package/dist/docs/complex-use-cases/sketching.mdx +0 -38
- package/dist/docs/controls/select-attribute/cardselect.mdx +9 -13
- package/dist/docs/controls/select-child-entity/cardselect.mdx +1 -0
- 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/fabs/basic.mdx +6 -2
- package/dist/docs/index.json +136 -1
- package/dist/docs/integrations/webcomponent-api/basics.mdx +2 -0
- package/dist/docs/integrations/woocommerce/local-development.mdx +1 -1
- package/dist/docs/layouts/custom-layout.mdx +2597 -0
- package/dist/docs/layouts/gallery/ardent-compressor.mdx +3516 -0
- package/dist/docs/layouts/gallery/corvid-drone.mdx +3428 -0
- package/dist/docs/layouts/gallery/fenwick-window.mdx +2803 -0
- package/dist/docs/layouts/gallery/ferrule-bar-station.mdx +6567 -0
- package/dist/docs/layouts/gallery/kestrel-kettle.mdx +1858 -0
- package/dist/docs/layouts/gallery/nightjar-guitar.mdx +3698 -0
- package/dist/docs/layouts/gallery/sunfold-solar.mdx +2683 -0
- package/dist/docs/layouts/gallery/tidepool-aquascape.mdx +5203 -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/layouts/sidebar-footer/basic.mdx +32 -77
- package/dist/docs/other-features/client-side-rendering/declarative.mdx +2 -2
- package/dist/docs/other-features/error-5xx.mdx +1 -1
- package/dist/docs/other-features/navigation-hotspot.mdx +2 -0
- package/dist/docs/other-features/not-found-404.mdx +1 -1
- package/dist/docs/other-features/side-effects.mdx +8 -11
- package/dist/docs/other-features/transformers.mdx +4 -0
- package/dist/docs/other-features/violation-messages.mdx +52 -56
- package/dist/docs/phoenix/application-states.mdx +9 -7
- 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 +402 -81
- package/dist/docs/phoenix/deployments.mdx +3 -7
- package/dist/docs/phoenix/domain-model.mdx +6 -0
- package/dist/docs/phoenix/expression-context.mdx +11 -3
- package/dist/docs/phoenix/finalization.mdx +1 -1
- package/dist/docs/phoenix/getting-started.mdx +7 -4
- package/dist/docs/phoenix/layout-slots.mdx +116 -0
- package/dist/docs/phoenix/localization.mdx +10 -6
- package/dist/docs/phoenix/migration-guide.mdx +24 -11
- package/dist/docs/phoenix/phoenix-cli.mdx +112 -65
- package/dist/docs/phoenix/project-structure.mdx +80 -36
- package/dist/docs/phoenix/quickstart.mdx +9 -9
- package/dist/docs/phoenix/sdk-builder-workflow.mdx +19 -17
- package/dist/docs/phoenix/sessions-and-access.mdx +2 -0
- package/dist/docs/phoenix/testing.mdx +148 -0
- package/dist/docs/phoenix/webhook-membership.mdx +10 -9
- package/dist/docs/phoenix/webhooks.mdx +23 -6
- package/dist/docs/selection-and-highlight/hotspots.mdx +2 -0
- package/dist/docs/ux-guidelines/choosing-a-control.mdx +11 -0
- package/dist/domain/admin-domain/implementation/common-admin-expressions.d.ts +26 -11
- package/dist/domain/admin-domain/implementation/common-admin-publish.d.ts +1 -2
- package/dist/domain/admin-domain/index.d.ts +8 -3
- package/dist/domain/admin-domain/index.js +1 -1
- package/dist/domain/application/GetApplicationWrapperHandler.d.ts +2 -2
- package/dist/domain/application/GetThemeHandler.d.ts +1 -1
- package/dist/domain/application/GetWebComponentOptions.d.ts +1 -1
- package/dist/domain/application/admin-page-factories.d.ts +1 -2
- package/dist/domain/application/application-config.d.ts +9 -26
- 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 +27 -2
- package/dist/domain/application/assistant/assistant-resolved-manifest.d.ts +46 -3
- package/dist/domain/application/assistant/assistant-resolved-manifest.js +1 -1
- package/dist/domain/application/assistant/assistant-value-type.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/form/assistant-form-element-name.d.ts +11 -0
- package/dist/domain/application/assistant/form/assistant-form-element-name.js +8 -0
- package/dist/domain/application/assistant/form/assistant-form-elements.d.ts +174 -0
- package/dist/domain/application/assistant/form/assistant-form-elements.js +1 -0
- package/dist/domain/application/assistant/form/assistant-resource-path.d.ts +10 -0
- package/dist/domain/application/assistant/form/assistant-resource-path.js +1 -0
- package/dist/domain/application/assistant/form/index.d.ts +4 -0
- package/dist/domain/application/assistant/form/index.js +1 -0
- package/dist/domain/application/assistant/generate-assistant-prompt.d.ts +4 -11
- package/dist/domain/application/assistant/generate-assistant-prompt.js +1 -1
- package/dist/domain/application/assistant/index.d.ts +8 -2
- 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 +50 -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/assistant-mutations.d.ts +2 -1
- package/dist/domain/application/assistant-mutations.js +1 -1
- package/dist/domain/application/assistant-recipe-groups.d.ts +4 -2
- package/dist/domain/application/configurator-builder.d.ts +11 -2
- package/dist/domain/application/configurator-builder.js +1 -1
- package/dist/domain/application/create-initial-published-data.d.ts +6 -7
- package/dist/domain/application/index.d.ts +3 -7
- package/dist/domain/application/index.js +1 -1
- package/dist/domain/application/published-data/DataRowValidatorHandler.d.ts +1 -1
- package/dist/domain/application/published-data/FilterPublishedChunksHandler.d.ts +1 -1
- package/dist/domain/application/published-data/MapPublishedChunkedDataHandler.d.ts +1 -1
- package/dist/domain/application/published-data/MapPublishedRevisionDataHandler.d.ts +1 -1
- package/dist/domain/application/published-data/SheetValidatorHandler.d.ts +1 -1
- package/dist/domain/application/published-data/build-legend-data.d.ts +1 -1
- package/dist/domain/application/published-data/format-field-type.d.ts +1 -1
- package/dist/domain/application/published-data/map-data-to-sheet.d.ts +1 -1
- package/dist/domain/application/published-data/map-published-chunked-data.d.ts +3 -3
- package/dist/domain/application/published-data/map-published-data.d.ts +7 -3
- package/dist/domain/application/published-data/validate-sheet-data.d.ts +1 -1
- package/dist/domain/application/standard-properties.js +1 -1
- package/dist/domain/application/web-component-property.d.ts +3 -1
- package/dist/domain/application/web-component-property.js +1 -1
- package/dist/domain/available-system-components.d.ts +1 -1
- package/dist/domain/available-system-components.js +1 -1
- package/dist/domain/core/action/action-factory.d.ts +11 -0
- package/dist/domain/core/action/action.d.ts +71 -0
- package/dist/domain/core/action/action.js +1 -0
- package/dist/domain/core/action/index.d.ts +2 -0
- package/dist/domain/core/action/index.js +1 -0
- package/dist/domain/core/component/domain-component-optic.d.ts +29 -40
- package/dist/domain/core/component/domain-component-optic.js +2 -2
- package/dist/domain/core/component/legacy-filter.d.ts +2 -17
- package/dist/domain/core/component/system-component-filter.d.ts +36 -18
- package/dist/domain/core/data-schema/data-cell-value.d.ts +4 -0
- package/dist/domain/core/data-schema/data-cell-value.js +1 -0
- package/dist/domain/core/data-schema/data-schema-array-field.d.ts +5 -0
- package/dist/domain/core/data-schema/data-schema-array-field.js +1 -0
- package/dist/domain/core/data-schema/data-schema-field-object.d.ts +4 -0
- package/dist/domain/core/data-schema/data-schema-field-object.js +1 -0
- package/dist/domain/core/data-schema/data-schema-field.d.ts +2 -0
- package/dist/domain/core/data-schema/data-schema-field.js +1 -0
- package/dist/domain/core/data-schema/data-schema-localized-field.d.ts +4 -0
- package/dist/domain/core/data-schema/data-schema-localized-field.js +1 -0
- package/dist/domain/core/data-schema/data-schema-primitive-field.d.ts +5 -0
- package/dist/domain/core/data-schema/data-schema-primitive-field.js +1 -0
- package/dist/domain/core/data-schema/data-schema.d.ts +2 -0
- package/dist/domain/core/data-schema/data-schema.js +1 -0
- package/dist/domain/core/data-schema/index.d.ts +7 -0
- package/dist/domain/core/data-schema/index.js +1 -0
- package/dist/domain/core/domain-factory.d.ts +1 -1
- package/dist/domain/core/domain.d.ts +2 -2
- package/dist/domain/core/emission/index.d.ts +8 -0
- package/dist/domain/core/emission/index.js +1 -0
- package/dist/domain/core/emission/reset-state-emission.d.ts +12 -0
- package/dist/domain/core/emission/reset-state-emission.js +1 -0
- package/dist/domain/core/emission/state-emission.d.ts +4 -0
- package/dist/domain/core/emission/state-emission.js +1 -0
- package/dist/domain/core/emission/state-update-emission.d.ts +14 -0
- package/dist/domain/core/emission/state-update-emission.js +1 -0
- package/dist/domain/core/emission/state-validation-result.d.ts +18 -0
- package/dist/domain/core/emission/state-validation-result.js +1 -0
- package/dist/domain/core/emission/transformation-log.d.ts +2 -0
- package/dist/domain/core/emission/transformation-log.js +1 -0
- package/dist/domain/core/emission/transformer-log-entry.d.ts +6 -0
- package/dist/domain/core/emission/transformer-log-entry.js +1 -0
- package/dist/domain/core/emission/transformer-log.d.ts +11 -0
- package/dist/domain/core/emission/transformer-log.js +1 -0
- package/dist/domain/core/emission/transformer-phase.d.ts +1 -0
- package/dist/domain/core/emission/transformer-phase.js +1 -0
- package/dist/domain/core/expression/aliased-expressions.d.ts +10 -0
- package/dist/domain/core/expression/aliased-expressions.js +1 -0
- package/dist/domain/core/expression/constant-expressions.d.ts +16 -8
- package/dist/domain/core/expression/constant-expressions.js +1 -1
- package/dist/domain/core/expression/custom-expression.d.ts +21 -8
- package/dist/domain/core/expression/custom-expression.js +1 -1
- package/dist/domain/core/expression/expression.d.ts +15 -17
- package/dist/domain/core/expression/index.d.ts +1 -2
- package/dist/domain/core/expression/index.js +1 -1
- package/dist/domain/core/index.d.ts +5 -1
- package/dist/domain/core/index.js +1 -1
- package/dist/domain/core/invariant/custom-invariants.d.ts +8 -16
- package/dist/domain/core/invariant/index.d.ts +1 -1
- package/dist/domain/core/invariant/index.js +1 -1
- package/dist/domain/core/invariant/invariants.d.ts +4 -70
- package/dist/domain/core/invariant/invariants.js +1 -1
- package/dist/domain/core/invariant/violation.d.ts +51 -3
- package/dist/domain/core/invariant/violation.js +1 -1
- package/dist/domain/core/model/model.d.ts +1 -1
- package/dist/domain/core/recipe/index.d.ts +1 -0
- package/dist/domain/core/recipe/index.js +1 -0
- package/dist/domain/core/recipe/recipes.d.ts +23 -0
- package/dist/domain/core/recipe/recipes.js +1 -0
- package/dist/domain/core/relation/all-of-type-relation.d.ts +19 -0
- package/dist/domain/core/relation/all-of-type-relation.js +1 -0
- package/dist/domain/core/relation/component-factories.d.ts +14 -0
- package/dist/domain/core/relation/component-factories.js +1 -0
- package/dist/domain/core/relation/composed-of.d.ts +12 -0
- package/dist/domain/core/relation/composed-of.js +1 -0
- package/dist/domain/core/relation/direct-relation.d.ts +17 -0
- package/dist/domain/core/relation/index.d.ts +8 -0
- package/dist/domain/core/relation/index.js +1 -0
- package/dist/domain/core/relation/indirect-relation.d.ts +25 -0
- package/dist/domain/core/relation/indirect-relation.js +1 -0
- package/dist/domain/core/relation/reference-component.d.ts +17 -0
- package/dist/domain/core/relation/reference-component.js +1 -0
- package/dist/domain/core/relation/reference-field.d.ts +7 -0
- package/dist/domain/core/relation/reference-field.js +1 -0
- package/dist/domain/core/relation/relation-factory.d.ts +22 -0
- package/dist/domain/core/relation/relation-factory.js +1 -0
- package/dist/domain/core/schema/extended-schema-validator.js +1 -1
- package/dist/domain/core/state/migrations.d.ts +14 -45
- package/dist/domain/core/state/migrations.js +1 -1
- package/dist/domain/core/state/state.d.ts +1 -1
- package/dist/domain/domain-builder.d.ts +11 -8
- package/dist/domain/domain-builder.js +1 -1
- package/dist/domain/domain-context/action-system/action-system.d.ts +2 -2
- package/dist/domain/domain-context/action-system/action-system.js +1 -1
- package/dist/domain/domain-context/action-system/index.d.ts +1 -0
- package/dist/domain/domain-context/action-system/index.js +1 -0
- package/dist/domain/domain-context/analytics-system/index.d.ts +2 -0
- package/dist/domain/domain-context/analytics-system/index.js +1 -0
- package/dist/domain/domain-context/create-transformer.d.ts +1 -1
- package/dist/domain/domain-context/dimension-system/dimension-system.d.ts +2 -2
- package/dist/domain/domain-context/dimension-system/dimension-system.js +1 -1
- package/dist/domain/domain-context/dimension-system/index.d.ts +1 -0
- package/dist/domain/domain-context/dimension-system/index.js +1 -0
- package/dist/domain/domain-context/domain-context-systems.d.ts +7 -7
- package/dist/domain/domain-context/index.d.ts +8 -18
- package/dist/domain/domain-context/index.js +1 -1
- package/dist/domain/domain-context/navigation-system/index.d.ts +3 -0
- package/dist/domain/domain-context/navigation-system/index.js +1 -0
- package/dist/domain/domain-context/navigation-system/navigation-system.d.ts +2 -3
- package/dist/domain/domain-context/navigation-system/navigation-system.js +1 -1
- package/dist/domain/domain-context/navigation-system/selection-transformers.d.ts +1 -1
- package/dist/domain/domain-context/recipe-system/index.d.ts +1 -0
- package/dist/domain/domain-context/recipe-system/index.js +1 -0
- package/dist/domain/domain-context/recipe-system/recipe-cooker-system.d.ts +2 -3
- package/dist/domain/domain-context/recipe-system/recipe-cooker-system.js +1 -1
- package/dist/domain/domain-context/relation-transformers/cascade-deletes.d.ts +2 -2
- package/dist/domain/domain-context/relation-transformers/cascade-ref-pruning.d.ts +2 -2
- package/dist/domain/domain-context/relation-transformers/cascade-ref-pruning.js +1 -1
- package/dist/domain/domain-context/relation-transformers/create-entity-refs.d.ts +2 -2
- package/dist/domain/domain-context/relation-transformers/create-entity-refs.js +2 -2
- package/dist/domain/domain-context/relation-transformers/index.d.ts +4 -0
- package/dist/domain/domain-context/relation-transformers/index.js +1 -0
- package/dist/domain/domain-context/relation-transformers/replace-refs.d.ts +2 -2
- package/dist/domain/domain-context/relation-transformers/replace-refs.js +1 -1
- package/dist/domain/domain-context/store-system/index.d.ts +4 -0
- package/dist/domain/domain-context/store-system/index.js +1 -0
- package/dist/domain/domain-context/store-system/reconcile-projected-entities.js +1 -1
- package/dist/domain/domain-context/store-system/store-system.d.ts +7 -35
- package/dist/domain/domain-context/store-system/store-system.js +2 -2
- package/dist/domain/domain-context/store-system/system-state.d.ts +1 -1
- package/dist/domain/domain-context/transformers/actions-builder.js +1 -1
- package/dist/domain/domain-context/transformers/auto-snap-transformer.d.ts +5 -7
- package/dist/domain/domain-context/transformers/auto-snap-transformer.js +1 -1
- package/dist/domain/domain-context/transformers/chain-builder.d.ts +1 -1
- package/dist/domain/domain-context/transformers/context.d.ts +2 -3
- package/dist/domain/domain-context/transformers/context.js +1 -1
- package/dist/domain/domain-context/transformers/entity-builder.js +1 -1
- package/dist/domain/domain-context/transformers/index.d.ts +1 -1
- package/dist/domain/domain-context/transformers/index.js +1 -1
- package/dist/domain/domain-context/transformers/when-builder.d.ts +2 -3
- package/dist/domain/domain-context/transformers/when-builder.js +1 -1
- package/dist/domain/domain-context/undo-system.d.ts +29 -3
- package/dist/domain/domain-context/undo-system.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/application.d.ts +39 -0
- package/dist/domain/domain-runtime/application.js +1 -0
- package/dist/domain/domain-runtime/domain-context/action-context.d.ts +11 -0
- package/dist/domain/domain-runtime/domain-context/action-context.js +1 -0
- package/dist/domain/domain-runtime/domain-context/domain-context.js +1 -1
- package/dist/domain/domain-runtime/domain-context/expression-context.d.ts +8 -0
- package/dist/domain/domain-runtime/domain-context/expression-context.js +1 -0
- package/dist/domain/domain-runtime/domain-context/index.d.ts +3 -0
- package/dist/domain/domain-runtime/domain-context/runtime-expression-context.d.ts +18 -0
- package/dist/domain/domain-runtime/domain-context/runtime-expression-context.js +1 -0
- package/dist/domain/domain-runtime/domain-context/selectors.d.ts +20 -23
- 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 +128 -72
- package/dist/domain/domain-runtime/domain-runtime.js +1 -1
- package/dist/domain/domain-runtime/environment/initialize.d.ts +1 -1
- package/dist/domain/domain-runtime/environment/types.d.ts +1 -1
- package/dist/domain/domain-runtime/errors/implementation-error-collector.d.ts +21 -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 +2 -0
- package/dist/domain/domain-runtime/index.js +1 -1
- package/dist/domain/domain-runtime/spec/create-violation-expressions.d.ts +24 -0
- package/dist/domain/domain-runtime/spec/create-violation-expressions.js +1 -0
- package/dist/domain/domain-runtime/spec/index.d.ts +5 -0
- package/dist/domain/domain-runtime/spec/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/memoize-expression.d.ts +16 -0
- package/dist/domain/domain-runtime/spec/module-checks/element-registration.d.ts +125 -0
- package/dist/domain/domain-runtime/spec/module-checks/element-registration.js +1 -0
- package/dist/domain/domain-runtime/spec/module-checks/find-assistant-form-element-errors.d.ts +12 -0
- package/dist/domain/domain-runtime/spec/module-checks/find-assistant-form-element-errors.js +1 -0
- package/dist/domain/domain-runtime/spec/module-checks/handler-registration.d.ts +34 -0
- package/dist/domain/domain-runtime/spec/module-checks/handler-registration.js +1 -0
- package/dist/domain/domain-runtime/spec/module-checks/index.d.ts +10 -0
- package/dist/domain/domain-runtime/spec/module-checks/index.js +1 -0
- package/dist/domain/domain-runtime/spec/module-checks/merge-expression-modules.d.ts +23 -0
- package/dist/domain/domain-runtime/spec/module-checks/merge-expression-modules.js +1 -0
- package/dist/domain/domain-runtime/spec/module-checks/project-modules.d.ts +32 -0
- package/dist/domain/domain-runtime/spec/module-checks/project-modules.js +1 -0
- package/dist/domain/domain-runtime/spec/module-checks/resolve-action-modules.d.ts +4 -0
- package/dist/domain/domain-runtime/spec/module-checks/resolve-action-modules.js +1 -0
- package/dist/domain/domain-runtime/spec/module-checks/resolve-component-optic-modules.d.ts +4 -0
- package/dist/domain/domain-runtime/spec/module-checks/resolve-component-optic-modules.js +1 -0
- package/dist/domain/domain-runtime/spec/module-checks/resolve-expression-modules.d.ts +14 -0
- package/dist/domain/domain-runtime/spec/module-checks/resolve-expression-modules.js +1 -0
- package/dist/domain/domain-runtime/spec/module-checks/resolve-imported-transformers.d.ts +22 -0
- package/dist/domain/domain-runtime/spec/module-checks/resolve-imported-transformers.js +1 -0
- package/dist/domain/domain-runtime/spec/module-checks/resolve-invariant-modules.d.ts +6 -0
- package/dist/domain/domain-runtime/spec/module-checks/resolve-invariant-modules.js +1 -0
- package/dist/domain/domain-runtime/spec/prepare-action-implementations.d.ts +15 -0
- package/dist/domain/domain-runtime/spec/prepare-action-implementations.js +5 -0
- package/dist/domain/domain-runtime/spec/prepare-domain-expressions.d.ts +16 -0
- package/dist/domain/domain-runtime/spec/selectors.d.ts +8 -7
- package/dist/domain/domain-runtime/spec/selectors.js +1 -1
- package/dist/domain/domain-runtime/spec/types.d.ts +5 -2
- package/dist/domain/domain-runtime/web-component/actions.d.ts +3 -0
- package/dist/domain/domain-runtime/web-component/actions.js +1 -1
- package/dist/domain/domain-runtime/web-component/initialize.d.ts +1 -0
- package/dist/domain/domain-runtime/web-component/initialize.js +1 -1
- package/dist/domain/domain-runtime/web-component/selectors.d.ts +2 -0
- package/dist/domain/domain-runtime/web-component/selectors.js +1 -1
- package/dist/domain/domain-runtime/web-component/types.d.ts +6 -3
- package/dist/domain/extensions/attribute-switch.d.ts +8 -0
- package/dist/domain/extensions/attribute-switch.js +1 -0
- package/dist/domain/extensions/canvas-config.d.ts +87 -0
- package/dist/domain/extensions/child-entity-recipe-hierarchy.d.ts +21 -0
- package/dist/domain/extensions/child-entity-recipe-hierarchy.js +1 -0
- package/dist/domain/extensions/common/angle-indicator-schema.d.ts +292 -0
- package/dist/domain/extensions/common/angle-indicator-schema.js +1 -0
- package/dist/domain/extensions/common/dimension-indicator-schema.d.ts +607 -0
- package/dist/domain/extensions/common/index.d.ts +4 -0
- package/dist/domain/extensions/common/index.js +1 -0
- package/dist/domain/extensions/custom-header.d.ts +11 -0
- package/dist/domain/extensions/custom-header.js +1 -0
- package/dist/domain/extensions/custom-price-footer.js +1 -0
- package/dist/domain/extensions/custom-sidebar-footer.d.ts +11 -0
- package/dist/domain/extensions/custom-sidebar-footer.js +1 -0
- package/dist/domain/extensions/editable-angle-indicator.d.ts +39 -0
- package/dist/domain/extensions/editable-angle-indicator.js +1 -0
- package/dist/domain/extensions/editable-distance-dimension-indicator.d.ts +43 -0
- package/dist/domain/extensions/editable-distance-dimension-indicator.js +1 -0
- package/dist/domain/extensions/editable-offset-indicator.d.ts +42 -0
- package/dist/domain/extensions/editable-offset-indicator.js +1 -0
- package/dist/domain/extensions/editable-size-indicator.d.ts +39 -0
- package/dist/domain/extensions/editable-size-indicator.js +1 -0
- package/dist/domain/extensions/entity-header.d.ts +22 -0
- package/dist/domain/extensions/entity-header.js +1 -0
- package/dist/domain/extensions/export-images/agent-screenshot.d.ts +24 -0
- package/dist/domain/extensions/export-images/agent-screenshot.js +1 -0
- package/dist/domain/extensions/export-images/client-side-image.d.ts +14 -0
- package/dist/domain/extensions/export-images/client-side-image.js +1 -0
- package/dist/domain/extensions/export-images/index.d.ts +3 -0
- package/dist/domain/extensions/export-images/index.js +1 -0
- package/dist/domain/extensions/export-images/save-template-thumbnail.d.ts +14 -0
- package/dist/domain/extensions/export-images/save-template-thumbnail.js +1 -0
- package/dist/domain/extensions/finalize-component.js +1 -0
- package/dist/domain/extensions/fit-selection-orthographic-camera-component.js +1 -0
- package/dist/domain/extensions/fit-selection-perspective-camera-component.js +1 -0
- package/dist/domain/extensions/horizontal-tabs.d.ts +28 -0
- package/dist/domain/extensions/index.d.ts +68 -0
- package/dist/domain/extensions/index.js +1 -0
- package/dist/domain/extensions/list-selector-dimension-indicator.d.ts +31 -0
- package/dist/domain/extensions/list-selector-dimension-indicator.js +1 -0
- package/dist/domain/extensions/navigation-steps.d.ts +19 -0
- package/dist/domain/extensions/offset-indicator.js +1 -0
- package/dist/domain/extensions/property-page.d.ts +16 -0
- package/dist/domain/extensions/property-page.js +1 -0
- package/dist/domain/extensions/replace-entity-recipe-hierarchy.d.ts +21 -0
- package/dist/domain/extensions/replace-entity-recipe-hierarchy.js +1 -0
- package/dist/domain/extensions/rotation-handle.d.ts +53 -0
- package/dist/domain/extensions/scene-entity-add-fab.d.ts +32 -0
- package/dist/domain/extensions/scene-entity-add-fab.js +1 -0
- package/dist/domain/extensions/scene-entity-animation.js +1 -0
- package/dist/domain/extensions/scene-entity-delete-fab.d.ts +19 -0
- package/dist/domain/extensions/scene-entity-delete-fab.js +1 -0
- package/dist/domain/extensions/scene-entity-html-hotspot.d.ts +27 -0
- package/dist/domain/extensions/scene-entity-html-hotspot.js +1 -0
- package/dist/domain/extensions/scene-entity-navigation-hotspot.d.ts +20 -0
- package/dist/domain/extensions/scene-entity-navigation-hotspot.js +1 -0
- package/dist/domain/extensions/scene-entity-object-html-hotspot.d.ts +31 -0
- package/dist/domain/extensions/scene-entity-object-html-hotspot.js +1 -0
- package/dist/domain/extensions/scene-entity-select-hotspot.d.ts +5 -0
- package/dist/domain/extensions/scene-entity-select-hotspot.js +1 -0
- package/dist/domain/extensions/sketching-edit.d.ts +28 -0
- package/dist/domain/extensions/sketching-edit.js +1 -0
- package/dist/domain/extensions/summary-component.js +1 -0
- package/dist/domain/extensions/tool-menu-fab.d.ts +40 -0
- package/dist/domain/extensions/tool-menu-fab.js +1 -0
- package/dist/domain/extensions/toolbar.d.ts +13 -0
- package/dist/domain/extensions/toolbar.js +1 -0
- package/dist/domain/index.d.ts +2 -8
- package/dist/domain/index.js +1 -1
- package/dist/domain/mocks/create-mock-domain-context.d.ts +1 -1
- package/dist/domain/mocks/create-mock-domain-runtime-services.d.ts +1 -1
- package/dist/domain/mocks/create-mock-domain-runtime.d.ts +37 -19
- package/dist/domain/mocks/create-mock-domain-runtime.js +1 -1
- package/dist/domain/mocks/create-mock-domain.d.ts +1 -1
- package/dist/domain/mocks/create-mock-domain.js +1 -1
- package/dist/domain/mocks/create-mock-state.d.ts +2 -2
- 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/projections/blender-projections/blender-projections.d.ts +1 -2
- package/dist/domain/projections/blender-projections/camera.d.ts +2 -2
- package/dist/domain/projections/collision-projections.d.ts +1 -1
- package/dist/domain/projections/composition-projections.d.ts +1 -1
- package/dist/domain/projections/composition-projections.js +1 -1
- package/dist/domain/projections/constraint-projections.d.ts +1 -1
- package/dist/domain/projections/index.d.ts +1 -1
- package/dist/domain/projections/position-projections.d.ts +1 -1
- package/dist/domain/projections/projected-entities-projections.d.ts +1 -1
- package/dist/domain/projections/snapping-projections.d.ts +1 -1
- package/dist/domain/projections/validation-projections.d.ts +3 -19
- package/dist/domain/sketching/sketching-projections.d.ts +1 -1
- package/dist/domain/system-components/application-config.d.ts +2 -2
- package/dist/domain/system-components/child-entity-selection.d.ts +1 -2
- package/dist/domain/system-components/child-entity-selection.js +1 -1
- 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/index.d.ts +0 -4
- package/dist/domain/system-components/common/index.js +1 -1
- package/dist/domain/system-components/common/material-override-schema.d.ts +2 -2
- package/dist/domain/system-components/common/recipes.d.ts +21 -4
- package/dist/domain/system-components/common/recipes.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/domain/system-components/constraint.d.ts +10 -23
- package/dist/domain/system-components/drag-handle.d.ts +1 -1
- package/dist/domain/system-components/drag-handle.js +1 -1
- package/dist/domain/system-components/fit-entity-orthographic-camera-component.js +1 -1
- package/dist/domain/system-components/fit-entity-perspective-camera-component.js +1 -1
- package/dist/domain/system-components/fit-root-orthographic-camera-component.js +1 -1
- package/dist/domain/system-components/fit-root-perspective-camera-component.js +1 -1
- package/dist/domain/system-components/hotspot-drag-handle.d.ts +1 -1
- package/dist/domain/system-components/hotspot-drag-handle.js +1 -1
- package/dist/domain/system-components/index.d.ts +0 -68
- package/dist/domain/system-components/index.js +1 -1
- package/dist/domain/system-components/material-swatch.js +1 -1
- package/dist/domain/system-components/phoenix-entity-elements/admin-data-table-sidebar.d.ts +1 -1
- package/dist/domain/system-components/phoenix-layout-elements/admin-connected-resource-page.d.ts +1 -1
- package/dist/domain/system-components/phoenix-layout-elements/admin-data-table-actions.d.ts +1 -1
- package/dist/domain/system-components/phoenix-layout-elements/admin-data-table-page.d.ts +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/domain/system-components/static-orthographic-camera-component.js +1 -1
- package/dist/domain/system-components/static-perspective-camera-component.js +1 -1
- package/dist/domain/utils/eval-cache.d.ts +1 -1
- package/dist/domain/utils/map-nested-object.d.ts +1 -1
- package/dist/domain/utils/memo-eval.d.ts +2 -2
- package/dist/editor/Editor.d.ts +7 -0
- package/dist/editor/Editor.js +1 -1
- package/dist/editor/domain-editors/DomainSpecContext.js +1 -1
- package/dist/editor/domain-editors/apply-spec-update.d.ts +10 -2
- package/dist/editor/domain-editors/apply-spec-update.js +1 -1
- package/dist/editor/domain-editors/use-implementation-files.d.ts +7 -16
- package/dist/editor/domain-editors/use-implementation-files.js +1 -1
- package/dist/editor/mocks/CreateMockYaml.js +2 -2
- package/dist/editor/mocks/EditorMockContext.d.ts +2 -0
- package/dist/editor/mocks/EditorMockContext.js +1 -1
- package/dist/editor/screenshot/ScreenshotHandler.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/gen/configurator-builder.js +13 -12
- package/dist/legacy/domain-cli/gen/constant-expressions.js +1 -3
- package/dist/legacy/domain-cli/gen/defaults.js +1 -7
- package/dist/legacy/domain-cli/gen/implementation.js +7 -11
- package/dist/legacy/domain-cli/gen/invariants.js +7 -6
- package/dist/legacy/domain-cli/gen/relations.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 +176 -148
- package/dist/legacy/domain-spec/spec/domain-spec.js +1 -1
- 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 +13 -0
- package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/domain-index.js +17 -15
- package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/env-example.d.ts +1 -1
- package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/env-example.js +4 -0
- package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/frontend-admin.js +15 -14
- package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/frontend-configurator.js +14 -12
- package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/frontend-embedded-admin.js +14 -13
- package/dist/legacy/phoenix-cli/project-gen/generators/file-templates/frontend-embedded-configurator.js +13 -11
- 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 +3 -3
- package/dist/phoenix/services/lingui.d.ts +8 -0
- package/dist/phoenix/services/lingui.js +1 -1
- package/dist/phoenix/services/source-archive.d.ts +1 -0
- package/dist/phoenix/services/source-archive.js +1 -1
- package/dist/phoenix/services/swc.js +1 -1
- package/dist/phoenix/services/vite-config.js +1 -1
- package/dist/phoenix/utils/git/index.d.ts +1 -0
- package/dist/phoenix/utils/git/index.js +1 -0
- package/dist/phoenix/utils/git/is-inside-git-repository.d.ts +6 -0
- package/dist/phoenix/utils/git/is-inside-git-repository.js +1 -0
- package/dist/phoenix/utils/index.d.ts +5 -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/application-runtime/common-services/analytics/analytics-service.d.ts +1 -1
- package/dist/react-sdk/application-runtime/common-services/analytics/frontend-analytics.d.ts +52 -0
- package/dist/react-sdk/application-runtime/common-services/analytics/frontend-analytics.js +1 -0
- package/dist/react-sdk/application-runtime/common-services/analytics/index.d.ts +1 -0
- package/dist/react-sdk/application-runtime/common-services/analytics/index.js +1 -1
- package/dist/react-sdk/application-runtime/common-services/initialize.d.ts +2 -2
- package/dist/react-sdk/application-runtime/hooks/index.d.ts +2 -0
- package/dist/react-sdk/application-runtime/hooks/index.js +1 -1
- package/dist/react-sdk/application-runtime/hooks/loading/index.d.ts +5 -0
- package/dist/react-sdk/application-runtime/hooks/loading/index.js +1 -0
- package/dist/react-sdk/application-runtime/hooks/loading/loader-host-context.d.ts +11 -0
- package/dist/react-sdk/application-runtime/hooks/loading/loader-host-context.js +1 -0
- package/dist/react-sdk/application-runtime/hooks/loading/use-is-awaiting-loading-phase.d.ts +8 -0
- package/dist/react-sdk/application-runtime/hooks/loading/use-is-awaiting-loading-phase.js +1 -0
- package/dist/react-sdk/application-runtime/hooks/loading/use-is-loader-hosted.d.ts +7 -0
- package/dist/react-sdk/application-runtime/hooks/loading/use-is-loader-hosted.js +1 -0
- package/dist/react-sdk/application-runtime/hooks/loading/use-loading-actions.d.ts +9 -0
- package/dist/react-sdk/application-runtime/hooks/loading/use-loading-actions.js +1 -0
- package/dist/react-sdk/application-runtime/hooks/loading/use-loading-application.d.ts +24 -0
- package/dist/react-sdk/application-runtime/hooks/loading/use-loading-application.js +1 -0
- package/dist/react-sdk/application-runtime/hooks/responsive/index.d.ts +3 -0
- package/dist/react-sdk/application-runtime/hooks/responsive/index.js +1 -0
- package/dist/react-sdk/application-runtime/hooks/responsive/use-responsive-actions.d.ts +9 -0
- package/dist/react-sdk/application-runtime/hooks/responsive/use-responsive-actions.js +1 -0
- package/dist/react-sdk/application-runtime/hooks/responsive/use-responsive-application.d.ts +26 -0
- package/dist/react-sdk/application-runtime/hooks/responsive/use-responsive-application.js +1 -0
- package/dist/react-sdk/application-runtime/hooks/responsive/use-responsive-state.d.ts +10 -0
- package/dist/react-sdk/application-runtime/hooks/responsive/use-responsive-state.js +1 -0
- package/dist/react-sdk/application-runtime/hooks/responsive/use-responsive-store.d.ts +24 -0
- package/dist/react-sdk/application-runtime/hooks/responsive/use-responsive-store.js +1 -0
- package/dist/react-sdk/application-runtime/hooks/use-access-service.d.ts +7 -0
- package/dist/react-sdk/application-runtime/hooks/use-access-service.js +1 -1
- package/dist/react-sdk/application-runtime/hooks/use-phoenix-element-slots.d.ts +2 -1
- package/dist/react-sdk/application-runtime/hooks/use-web-component-readonly-sync.d.ts +4 -11
- package/dist/react-sdk/application-runtime/hooks/use-web-component-readonly-sync.js +1 -1
- package/dist/react-sdk/application-runtime/hooks/use-web-component.d.ts +5 -0
- package/dist/react-sdk/application-runtime/hooks/use-web-component.js +1 -1
- package/dist/react-sdk/application-runtime/host-properties/actions.d.ts +15 -0
- package/dist/react-sdk/application-runtime/host-properties/actions.js +1 -0
- package/dist/react-sdk/application-runtime/host-properties/connect-host-properties.d.ts +30 -0
- package/dist/react-sdk/application-runtime/host-properties/connect-host-properties.js +1 -0
- package/dist/react-sdk/application-runtime/host-properties/domain-host-properties.d.ts +7 -0
- package/dist/react-sdk/application-runtime/host-properties/domain-host-properties.js +1 -0
- package/dist/react-sdk/application-runtime/host-properties/host-property-descriptors.d.ts +21 -0
- package/dist/react-sdk/application-runtime/host-properties/host-property-descriptors.js +1 -0
- package/dist/react-sdk/application-runtime/host-properties/host-property-scopes.d.ts +42 -0
- package/dist/react-sdk/application-runtime/host-properties/host-property-scopes.js +1 -0
- package/dist/react-sdk/application-runtime/host-properties/index.d.ts +8 -0
- package/dist/react-sdk/application-runtime/host-properties/index.js +1 -0
- package/dist/react-sdk/application-runtime/host-properties/initialize.d.ts +7 -0
- package/dist/react-sdk/application-runtime/host-properties/initialize.js +1 -0
- package/dist/react-sdk/application-runtime/host-properties/selectors.d.ts +54 -0
- package/dist/react-sdk/application-runtime/host-properties/selectors.js +1 -0
- package/dist/react-sdk/application-runtime/host-properties/types.d.ts +18 -0
- package/dist/react-sdk/application-runtime/host-properties/types.js +1 -0
- package/dist/react-sdk/application-runtime/index.d.ts +3 -0
- package/dist/react-sdk/application-runtime/integration/initialize.d.ts +7 -6
- package/dist/react-sdk/application-runtime/integration/initialize.js +1 -1
- package/dist/react-sdk/application-runtime/integration/selectors.d.ts +11 -2
- package/dist/react-sdk/application-runtime/integration/selectors.js +1 -1
- package/dist/react-sdk/application-runtime/integration/types.d.ts +5 -2
- package/dist/react-sdk/application-runtime/loading/actions.d.ts +45 -0
- package/dist/react-sdk/application-runtime/loading/actions.js +1 -0
- package/dist/react-sdk/application-runtime/loading/index.d.ts +4 -0
- package/dist/react-sdk/application-runtime/loading/index.js +1 -0
- package/dist/react-sdk/application-runtime/loading/initialize.d.ts +3 -0
- package/dist/react-sdk/application-runtime/loading/initialize.js +1 -0
- package/dist/react-sdk/application-runtime/loading/selectors.d.ts +18 -0
- package/dist/react-sdk/application-runtime/loading/selectors.js +1 -0
- package/dist/react-sdk/application-runtime/loading/types.d.ts +59 -0
- package/dist/react-sdk/application-runtime/loading/types.js +1 -0
- package/dist/react-sdk/application-runtime/mocks/create-mock-application-runtime.js +1 -1
- package/dist/react-sdk/application-runtime/provider/ApplicationRuntime.d.ts +6 -0
- package/dist/react-sdk/application-runtime/provider/ApplicationRuntime.js +1 -1
- package/dist/react-sdk/application-runtime/provider/ApplicationType.d.ts +7 -0
- package/dist/react-sdk/application-runtime/provider/ApplicationType.js +1 -1
- package/dist/react-sdk/application-runtime/responsive/actions.d.ts +47 -0
- package/dist/react-sdk/application-runtime/responsive/actions.js +1 -0
- package/dist/react-sdk/application-runtime/responsive/index.d.ts +4 -0
- package/dist/react-sdk/application-runtime/responsive/index.js +1 -0
- package/dist/react-sdk/application-runtime/responsive/initialize.d.ts +3 -0
- package/dist/react-sdk/application-runtime/responsive/initialize.js +1 -0
- package/dist/react-sdk/application-runtime/responsive/selectors.d.ts +20 -0
- package/dist/react-sdk/application-runtime/responsive/selectors.js +1 -0
- package/dist/react-sdk/application-runtime/responsive/types.d.ts +104 -0
- package/dist/react-sdk/application-runtime/responsive/types.js +1 -0
- package/dist/react-sdk/application-runtime/routing/access-service.d.ts +1 -0
- package/dist/react-sdk/application-runtime/routing/routing-service.d.ts +34 -2
- package/dist/react-sdk/application-runtime/routing/routing-service.js +1 -1
- package/dist/react-sdk/application-runtime/runtime/create-application-runtime.d.ts +366 -46
- package/dist/react-sdk/application-runtime/runtime/create-application-runtime.js +1 -1
- package/dist/react-sdk/domain-application-context/BackendDataProvider.js +1 -1
- package/dist/react-sdk/domain-application-context/DomainLifecycleBoundary.js +1 -1
- package/dist/react-sdk/domain-application-context/FrontendDataProvider.js +1 -1
- package/dist/react-sdk/domain-application-context/PublishedDataProvider.js +1 -1
- package/dist/react-sdk/domain-context/common-hooks/index.d.ts +9 -0
- package/dist/react-sdk/domain-context/common-hooks/index.js +1 -0
- package/dist/react-sdk/domain-context/common-hooks/use-configuration-save-state.d.ts +23 -0
- package/dist/react-sdk/domain-context/common-hooks/use-configuration-save-state.js +1 -0
- package/dist/react-sdk/domain-context/common-hooks/use-configurator-messages.d.ts +41 -0
- package/dist/react-sdk/domain-context/common-hooks/use-configurator-messages.js +1 -0
- package/dist/react-sdk/domain-context/common-hooks/use-domain-context.d.ts +10 -9
- package/dist/react-sdk/domain-context/component-hooks/index.d.ts +1 -0
- package/dist/react-sdk/domain-context/component-hooks/index.js +1 -0
- package/dist/react-sdk/domain-context/component-hooks/use-component.js +1 -1
- package/dist/react-sdk/domain-context/entity-hooks/use-domain-entity-state.js +1 -1
- package/dist/react-sdk/domain-context/entity-hooks/use-entity-price.js +1 -1
- package/dist/react-sdk/domain-context/entity-hooks/use-root-entity-id.js +1 -1
- package/dist/react-sdk/domain-context/entity-hooks/use-root-entity-state.js +1 -1
- package/dist/react-sdk/domain-context/index.d.ts +9 -1
- package/dist/react-sdk/domain-context/index.js +1 -1
- package/dist/react-sdk/domain-context/system-hooks/composition-hooks.js +1 -1
- package/dist/react-sdk/domain-context/system-hooks/dimension-hooks.js +1 -1
- package/dist/react-sdk/domain-context/system-hooks/generate-share-link-hooks.d.ts +8 -0
- package/dist/react-sdk/domain-context/system-hooks/generate-share-link-hooks.js +1 -0
- package/dist/react-sdk/domain-context/system-hooks/hover-hooks.js +1 -1
- package/dist/react-sdk/domain-context/system-hooks/locale-switch-hooks.d.ts +24 -0
- package/dist/react-sdk/domain-context/system-hooks/locale-switch-hooks.js +1 -0
- package/dist/react-sdk/domain-context/system-hooks/navigation-hooks.d.ts +11 -0
- package/dist/react-sdk/domain-context/system-hooks/navigation-hooks.js +1 -1
- package/dist/react-sdk/domain-context/system-hooks/selection-hooks.js +1 -1
- package/dist/react-sdk/domain-context/system-hooks/share-links-hooks.d.ts +32 -0
- package/dist/react-sdk/domain-context/system-hooks/share-links-hooks.js +1 -0
- package/dist/react-sdk/domain-context/system-hooks/step-navigation-hooks.d.ts +57 -0
- package/dist/react-sdk/domain-context/system-hooks/step-navigation-hooks.js +1 -0
- package/dist/react-sdk/domain-context/system-hooks/tab-navigation-hooks.d.ts +61 -0
- package/dist/react-sdk/domain-context/system-hooks/tab-navigation-hooks.js +1 -0
- package/dist/react-sdk/domain-context/system-hooks/undo-redo-hooks.d.ts +20 -0
- package/dist/react-sdk/domain-context/system-hooks/undo-redo-hooks.js +1 -0
- package/dist/react-sdk/domain-controls/hooks/use-angle-dimension.d.ts +1 -1
- package/dist/react-sdk/handlers/FinalizeActionRunner.d.ts +13 -0
- package/dist/react-sdk/handlers/FinalizeActionRunner.js +1 -0
- package/dist/react-sdk/handlers/FinalizeActionsHost.d.ts +10 -0
- package/dist/react-sdk/handlers/FinalizeActionsHost.js +1 -0
- package/dist/react-sdk/handlers/FinalizeActionsProvider.d.ts +11 -0
- package/dist/react-sdk/handlers/FinalizeActionsProvider.js +1 -0
- package/dist/react-sdk/handlers/finalize-actions-context.d.ts +6 -0
- package/dist/react-sdk/handlers/finalize-actions-context.js +1 -0
- package/dist/react-sdk/handlers/finalize-actions-store.d.ts +23 -0
- package/dist/react-sdk/handlers/finalize-actions-store.js +1 -0
- package/dist/react-sdk/handlers/index.d.ts +6 -0
- package/dist/react-sdk/handlers/index.js +1 -1
- package/dist/react-sdk/handlers/use-finalize-action.d.ts +32 -0
- package/dist/react-sdk/handlers/use-finalize-action.js +1 -0
- package/dist/react-sdk/handlers/use-finalize-actions.d.ts +53 -0
- package/dist/react-sdk/handlers/use-finalize-actions.js +1 -0
- package/dist/react-sdk/index.d.ts +2 -0
- package/dist/react-sdk/index.js +1 -1
- package/dist/react-sdk/loading-state/LoaderHost.d.ts +20 -0
- package/dist/react-sdk/loading-state/LoaderHost.js +1 -0
- package/dist/react-sdk/loading-state/built-in-loading-phase.d.ts +9 -0
- package/dist/react-sdk/loading-state/built-in-loading-phase.js +1 -0
- package/dist/react-sdk/loading-state/create-app-loader.d.ts +18 -0
- package/dist/react-sdk/loading-state/create-app-loader.js +1 -0
- package/dist/react-sdk/loading-state/index.d.ts +4 -2
- package/dist/react-sdk/loading-state/index.js +1 -1
- package/dist/react-sdk/loading-state/loading-state-hooks.d.ts +22 -0
- package/dist/react-sdk/loading-state/loading-state-hooks.js +1 -0
- package/dist/react-sdk/page-elements/ConfigurationNotFoundPagePropsProvider.d.ts +17 -0
- package/dist/react-sdk/page-elements/ConfigurationNotFoundPagePropsProvider.js +1 -0
- package/dist/react-sdk/page-elements/ConfiguratorErrorPagePropsProvider.d.ts +17 -0
- package/dist/react-sdk/page-elements/ConfiguratorErrorPagePropsProvider.js +1 -0
- package/dist/react-sdk/page-elements/configurator-error-page-props.d.ts +20 -0
- package/dist/react-sdk/page-elements/configurator-error-page-props.js +1 -0
- package/dist/react-sdk/page-elements/create-configuration-not-found-page.d.ts +15 -0
- package/dist/react-sdk/page-elements/create-configuration-not-found-page.js +1 -0
- package/dist/react-sdk/page-elements/create-configurator-error-page.d.ts +14 -0
- package/dist/react-sdk/page-elements/create-configurator-error-page.js +1 -0
- package/dist/react-sdk/page-elements/create-preset-ui.d.ts +9 -0
- package/dist/react-sdk/page-elements/create-preset-ui.js +1 -0
- package/dist/react-sdk/page-elements/index.d.ts +6 -0
- package/dist/react-sdk/page-elements/index.js +1 -0
- package/dist/react-sdk/scene-control-elements/AddEntityFabElement.d.ts +36 -0
- package/dist/react-sdk/scene-control-elements/AddEntityFabElement.js +1 -0
- package/dist/react-sdk/scene-control-elements/DeleteEntityFabElement.d.ts +44 -0
- package/dist/react-sdk/scene-control-elements/DeleteEntityFabElement.js +1 -0
- package/dist/react-sdk/scene-control-elements/NavigationHotspotElement.d.ts +38 -0
- package/dist/react-sdk/scene-control-elements/NavigationHotspotElement.js +1 -0
- package/dist/react-sdk/scene-control-elements/SelectionHotspotElement.d.ts +30 -0
- package/dist/react-sdk/scene-control-elements/SelectionHotspotElement.js +1 -0
- package/dist/react-sdk/scene-control-elements/ToolMenuFabElement.d.ts +30 -0
- package/dist/react-sdk/scene-control-elements/ToolMenuFabElement.js +1 -0
- package/dist/react-sdk/scene-control-elements/create-add-entity-fab.d.ts +9 -0
- package/dist/react-sdk/scene-control-elements/create-add-entity-fab.js +1 -0
- package/dist/react-sdk/scene-control-elements/create-delete-entity-fab.d.ts +9 -0
- package/dist/react-sdk/scene-control-elements/create-delete-entity-fab.js +1 -0
- package/dist/react-sdk/scene-control-elements/create-navigation-hotspot.d.ts +10 -0
- package/dist/react-sdk/scene-control-elements/create-navigation-hotspot.js +1 -0
- package/dist/react-sdk/scene-control-elements/create-selection-hotspot.d.ts +9 -0
- package/dist/react-sdk/scene-control-elements/create-selection-hotspot.js +1 -0
- package/dist/react-sdk/scene-control-elements/create-tool-menu-fab.d.ts +9 -0
- package/dist/react-sdk/scene-control-elements/create-tool-menu-fab.js +1 -0
- package/dist/react-sdk/scene-control-elements/index.d.ts +11 -0
- package/dist/react-sdk/scene-control-elements/index.js +1 -0
- package/dist/react-sdk/scene-control-elements/scene-control-tooltip-visibility.d.ts +5 -0
- package/dist/react-sdk/scene-control-elements/scene-control-tooltip-visibility.js +1 -0
- package/dist/react-sdk/session/NotAuthenticatedPage.d.ts +4 -0
- package/dist/react-sdk/session/NotAuthenticatedPage.js +1 -1
- package/dist/react-sdk/session/SessionServiceProvider.js +1 -1
- package/dist/react-sdk/web-component/create-web-component.js +1 -1
- package/dist/react-sdk/web-component/god-mode/god-mode-registry.d.ts +30 -0
- package/dist/react-sdk/web-component/god-mode/god-mode-registry.js +1 -0
- package/dist/react-sdk/web-component/god-mode/index.d.ts +1 -0
- package/dist/react-sdk/web-component/god-mode/index.js +1 -0
- package/dist/ui-sdk/icons/__generated/Lightbulb.d.ts +3 -0
- package/dist/ui-sdk/icons/__generated/Lightbulb.js +1 -0
- package/dist/ui-sdk/icons/__generated/XCircle.d.ts +3 -0
- package/dist/ui-sdk/icons/__generated/XCircle.js +1 -0
- package/dist/ui-sdk/icons/__generated/index.d.ts +4 -0
- package/dist/ui-sdk/icons/__generated/index.js +1 -1
- package/dist/ui-sdk/icons/icons-registry.js +1 -1
- package/dist/ui-sdk/responsive-application/ResponsiveApplicationProvider.d.ts +9 -0
- package/dist/ui-sdk/responsive-application/ResponsiveApplicationProvider.js +1 -1
- package/dist/ui-sdk/responsive-application/index.d.ts +1 -0
- package/dist/ui-sdk/responsive-application/responsive-state-store.d.ts +59 -0
- package/dist/ui-sdk/responsive-application/responsive-state-store.js +1 -0
- package/dist/ui-sdk/responsive-application/use-store-responsive-info.d.ts +7 -0
- package/dist/ui-sdk/responsive-application/use-store-responsive-info.js +1 -0
- package/dist/ui-sdk/theme/types.d.ts +5 -1
- package/dist/webhook/builders/common.d.ts +5 -0
- package/dist/webhook/builders/webhook-builder.js +1 -1
- package/package.json +58 -36
- package/schema/domain-spec.json +1341 -217
- package/dist/application-spec/core/eval-to-constant.d.ts +0 -14
- package/dist/application-spec/generate-application/code/templates/env-ci.d.ts +0 -1
- package/dist/application-spec/generate-application/code/templates/env-ci.js +0 -25
- package/dist/application-spec/generate-application/code/templates/env-example.d.ts +0 -1
- package/dist/application-spec/generate-application/code/templates/env-example.js +0 -90
- 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/application-spec/generate-application/code/templates/prettier-ignore.d.ts +0 -1
- package/dist/application-spec/generate-application/code/templates/prettier-ignore.js +0 -6
- package/dist/application-spec/generate-application/code/templates/private-assets-readme.d.ts +0 -1
- package/dist/application-spec/generate-application/code/templates/private-assets-readme.js +0 -24
- package/dist/application-spec/generate-application/code/templates/public-assets-readme.d.ts +0 -1
- package/dist/application-spec/generate-application/code/templates/public-assets-readme.js +0 -24
- package/dist/application-spec/generate-application/code/templates/readme.d.ts +0 -1
- package/dist/application-spec/generate-application/code/templates/readme.js +0 -23
- package/dist/application-spec/generate-application/code/templates/skills/agents-md.d.ts +0 -1
- package/dist/application-spec/generate-application/code/templates/skills/agents-md.js +0 -2
- package/dist/application-spec/generate-application/code/templates/webhook-implementation.d.ts +0 -1
- package/dist/application-spec/generate-application/code/templates/webhook-implementation.js +0 -7
- package/dist/configurator/common/frontend-analytics.d.ts +0 -46
- package/dist/configurator/common/frontend-analytics.js +0 -1
- package/dist/configurator/ui/phoenix-layout-elements/language/use-locale-options.d.ts +0 -4
- package/dist/configurator/ui/phoenix-layout-elements/language/use-locale-options.js +0 -1
- package/dist/configurator/ui/phoenix-layout-elements/measurements/use-measurements-visibility.d.ts +0 -4
- package/dist/configurator/ui/phoenix-layout-elements/measurements/use-measurements-visibility.js +0 -1
- package/dist/domain/actions/action-factory.d.ts +0 -10
- package/dist/domain/actions/prepare-action-implementations.d.ts +0 -15
- package/dist/domain/actions/prepare-action-implementations.js +0 -5
- package/dist/domain/application/element-registration.d.ts +0 -110
- package/dist/domain/application/element-registration.js +0 -1
- package/dist/domain/application/handler-registration.d.ts +0 -34
- package/dist/domain/application/handler-registration.js +0 -1
- package/dist/domain/application/resolve-imported-transformers.d.ts +0 -23
- package/dist/domain/application/resolve-imported-transformers.js +0 -1
- package/dist/domain/application/resource-builder.d.ts +0 -13
- package/dist/domain/application/resource-builder.js +0 -1
- package/dist/domain/core/action.d.ts +0 -66
- package/dist/domain/core/expression/memoize-expression.d.ts +0 -17
- package/dist/domain/core/expression/prepare-domain-expressions.d.ts +0 -16
- package/dist/domain/domain-context/recipe-system/recipes.d.ts +0 -55
- package/dist/domain/domain-context/recipe-system/recipes.js +0 -1
- package/dist/domain/project-modules/index.d.ts +0 -1
- package/dist/domain/project-modules/index.js +0 -1
- package/dist/domain/project-modules/project-modules.d.ts +0 -41
- package/dist/domain/project-modules/project-modules.js +0 -1
- package/dist/domain/relations/all-of-type-relation.d.ts +0 -30
- package/dist/domain/relations/all-of-type-relation.js +0 -1
- package/dist/domain/relations/component-factories.d.ts +0 -14
- package/dist/domain/relations/component-factories.js +0 -1
- package/dist/domain/relations/composed-of.d.ts +0 -12
- package/dist/domain/relations/composed-of.js +0 -1
- package/dist/domain/relations/direct-relation.d.ts +0 -27
- package/dist/domain/relations/indirect-relation.d.ts +0 -78
- package/dist/domain/relations/indirect-relation.js +0 -1
- package/dist/domain/relations/reference-component.d.ts +0 -16
- package/dist/domain/relations/reference-component.js +0 -1
- package/dist/domain/relations/reference-field.d.ts +0 -7
- package/dist/domain/relations/relation-factory.d.ts +0 -17
- package/dist/domain/relations/relation-factory.js +0 -1
- package/dist/domain/relations/relation-implementation.d.ts +0 -5
- package/dist/domain/system-components/attribute-switch.d.ts +0 -8
- package/dist/domain/system-components/attribute-switch.js +0 -1
- package/dist/domain/system-components/canvas-config.d.ts +0 -87
- package/dist/domain/system-components/child-entity-recipe-hierarchy.d.ts +0 -21
- package/dist/domain/system-components/child-entity-recipe-hierarchy.js +0 -1
- package/dist/domain/system-components/client-side-image.d.ts +0 -14
- package/dist/domain/system-components/client-side-image.js +0 -1
- package/dist/domain/system-components/common/angle-indicator-schema.d.ts +0 -292
- package/dist/domain/system-components/common/angle-indicator-schema.js +0 -1
- package/dist/domain/system-components/common/dimension-indicator-schema.d.ts +0 -607
- package/dist/domain/system-components/custom-header.d.ts +0 -11
- package/dist/domain/system-components/custom-header.js +0 -1
- package/dist/domain/system-components/custom-price-footer.js +0 -1
- package/dist/domain/system-components/custom-sidebar-footer.d.ts +0 -11
- package/dist/domain/system-components/custom-sidebar-footer.js +0 -1
- package/dist/domain/system-components/editable-angle-indicator.d.ts +0 -38
- package/dist/domain/system-components/editable-angle-indicator.js +0 -1
- package/dist/domain/system-components/editable-distance-dimension-indicator.d.ts +0 -42
- package/dist/domain/system-components/editable-distance-dimension-indicator.js +0 -1
- package/dist/domain/system-components/editable-offset-indicator.d.ts +0 -41
- package/dist/domain/system-components/editable-offset-indicator.js +0 -1
- package/dist/domain/system-components/editable-size-indicator.d.ts +0 -38
- package/dist/domain/system-components/editable-size-indicator.js +0 -1
- package/dist/domain/system-components/entity-header.d.ts +0 -22
- package/dist/domain/system-components/entity-header.js +0 -1
- package/dist/domain/system-components/finalize-component.js +0 -1
- package/dist/domain/system-components/fit-selection-orthographic-camera-component.js +0 -1
- package/dist/domain/system-components/fit-selection-perspective-camera-component.js +0 -1
- package/dist/domain/system-components/horizontal-tabs.d.ts +0 -28
- package/dist/domain/system-components/list-selector-dimension-indicator.d.ts +0 -30
- package/dist/domain/system-components/list-selector-dimension-indicator.js +0 -1
- package/dist/domain/system-components/navigation-steps.d.ts +0 -19
- package/dist/domain/system-components/offset-indicator.js +0 -1
- package/dist/domain/system-components/property-page.d.ts +0 -16
- package/dist/domain/system-components/property-page.js +0 -1
- package/dist/domain/system-components/replace-entity-recipe-hierarchy.d.ts +0 -21
- package/dist/domain/system-components/replace-entity-recipe-hierarchy.js +0 -1
- package/dist/domain/system-components/rotation-handle.d.ts +0 -53
- package/dist/domain/system-components/save-template-thumbnail.d.ts +0 -14
- package/dist/domain/system-components/save-template-thumbnail.js +0 -1
- package/dist/domain/system-components/scene-entity-add-fab.d.ts +0 -32
- package/dist/domain/system-components/scene-entity-add-fab.js +0 -1
- package/dist/domain/system-components/scene-entity-animation.js +0 -1
- package/dist/domain/system-components/scene-entity-delete-fab.d.ts +0 -19
- package/dist/domain/system-components/scene-entity-delete-fab.js +0 -1
- package/dist/domain/system-components/scene-entity-html-hotspot.d.ts +0 -27
- package/dist/domain/system-components/scene-entity-html-hotspot.js +0 -1
- package/dist/domain/system-components/scene-entity-navigation-hotspot.d.ts +0 -20
- package/dist/domain/system-components/scene-entity-navigation-hotspot.js +0 -1
- package/dist/domain/system-components/scene-entity-object-html-hotspot.d.ts +0 -31
- package/dist/domain/system-components/scene-entity-object-html-hotspot.js +0 -1
- package/dist/domain/system-components/scene-entity-select-hotspot.d.ts +0 -5
- package/dist/domain/system-components/scene-entity-select-hotspot.js +0 -1
- package/dist/domain/system-components/sketching-edit.d.ts +0 -28
- package/dist/domain/system-components/sketching-edit.js +0 -1
- package/dist/domain/system-components/summary-component.js +0 -1
- package/dist/domain/system-components/tool-menu-fab.d.ts +0 -40
- package/dist/domain/system-components/tool-menu-fab.js +0 -1
- package/dist/domain/system-components/toolbar.d.ts +0 -13
- package/dist/domain/system-components/toolbar.js +0 -1
- package/dist/phoenix/commands/generate-project-files.d.ts +0 -6
- package/dist/phoenix/commands/generate-project-files.js +0 -1
- package/dist/react-sdk/application-runtime/runtime/god-mode.d.ts +0 -19
- package/dist/react-sdk/application-runtime/runtime/god-mode.js +0 -1
- package/dist/react-sdk/loading-state/LoadingStateProvider.d.ts +0 -31
- package/dist/react-sdk/loading-state/LoadingStateProvider.js +0 -1
- package/dist/react-sdk/loading-state/loading-state-manager.d.ts +0 -32
- package/dist/react-sdk/loading-state/loading-state-manager.js +0 -1
- /package/dist/{domain/core/action.js → application-spec/core/expressions/relation-expression-params.js} +0 -0
- /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
- /package/dist/{domain/relations/reference-field.js → application-spec/generate-application/runtime/spec-code/spec-code-module.js} +0 -0
- /package/dist/{domain/relations/relation-implementation.js → application-spec/parse-application-files/spec/expressions/expression-read.js} +0 -0
- /package/dist/domain/{actions → core/action}/action-factory.js +0 -0
- /package/dist/domain/{relations → core/relation}/direct-relation.js +0 -0
- /package/dist/domain/{core/expression → domain-runtime/spec}/memoize-expression.js +0 -0
- /package/dist/domain/{core/expression → domain-runtime/spec}/prepare-domain-expressions.js +0 -0
- /package/dist/domain/{system-components → extensions}/angle-indicator.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/angle-indicator.js +0 -0
- /package/dist/domain/{system-components → extensions}/autorotate-camera-component.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/autorotate-camera-component.js +0 -0
- /package/dist/domain/{system-components → extensions}/basic-sidebar-footer.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/basic-sidebar-footer.js +0 -0
- /package/dist/domain/{system-components → extensions}/camera-based-hidden-entity-object.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/camera-based-hidden-entity-object.js +0 -0
- /package/dist/domain/{system-components → extensions}/camera-based-hidden-entity.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/camera-based-hidden-entity.js +0 -0
- /package/dist/domain/{system-components → extensions}/canvas-config.js +0 -0
- /package/dist/domain/{system-components → extensions}/canvas-custom-environment.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/canvas-custom-environment.js +0 -0
- /package/dist/domain/{system-components → extensions}/canvas-padding.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/canvas-padding.js +0 -0
- /package/dist/domain/{system-components → extensions}/canvas-preset-environment.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/canvas-preset-environment.js +0 -0
- /package/dist/domain/{system-components → extensions}/color-value.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/color-value.js +0 -0
- /package/dist/domain/{system-components → extensions}/common/dimension-indicator-schema.js +0 -0
- /package/dist/domain/{system-components → extensions}/common/dimension-indicators.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/common/dimension-indicators.js +0 -0
- /package/dist/domain/{system-components → extensions}/common/editable-indicator-button-variant.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/common/editable-indicator-button-variant.js +0 -0
- /package/dist/domain/{system-components → extensions}/custom-price-footer.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/dimension-indicator.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/dimension-indicator.js +0 -0
- /package/dist/domain/{system-components → extensions}/exit-tool-mode-fab.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/exit-tool-mode-fab.js +0 -0
- /package/dist/domain/{system-components → extensions}/finalize-action.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/finalize-action.js +0 -0
- /package/dist/domain/{system-components → extensions}/finalize-component.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/fit-selection-orthographic-camera-component.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/fit-selection-perspective-camera-component.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/fixed-header.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/fixed-header.js +0 -0
- /package/dist/domain/{system-components → extensions}/floating-header.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/floating-header.js +0 -0
- /package/dist/domain/{system-components → extensions}/hidden-header.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/hidden-header.js +0 -0
- /package/dist/domain/{system-components → extensions}/hidden-sidebar-footer.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/hidden-sidebar-footer.js +0 -0
- /package/dist/domain/{system-components → extensions}/horizontal-tabs.js +0 -0
- /package/dist/domain/{system-components → extensions}/multilingual-text-value.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/multilingual-text-value.js +0 -0
- /package/dist/domain/{system-components → extensions}/navigation-steps.js +0 -0
- /package/dist/domain/{system-components → extensions}/offset-anchors.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/offset-anchors.js +0 -0
- /package/dist/domain/{system-components → extensions}/offset-indicator.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/offset.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/offset.js +0 -0
- /package/dist/domain/{system-components → extensions}/other-action.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/other-action.js +0 -0
- /package/dist/domain/{system-components → extensions}/read-sidebar-footer.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/read-sidebar-footer.js +0 -0
- /package/dist/domain/{system-components → extensions}/rotatable.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/rotatable.js +0 -0
- /package/dist/domain/{system-components → extensions}/rotation-handle.js +0 -0
- /package/dist/domain/{system-components → extensions}/scene-entity-animation.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/scene-entity-control.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/scene-entity-control.js +0 -0
- /package/dist/domain/{system-components → extensions}/scene-entity-floor-object.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/scene-entity-floor-object.js +0 -0
- /package/dist/domain/{system-components → extensions}/scene-entity-wall-object.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/scene-entity-wall-object.js +0 -0
- /package/dist/domain/{system-components → extensions}/selectable-component.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/selectable-component.js +0 -0
- /package/dist/domain/{system-components → extensions}/sidebar-tabs.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/sidebar-tabs.js +0 -0
- /package/dist/domain/{system-components → extensions}/simple-summary.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/simple-summary.js +0 -0
- /package/dist/domain/{system-components → extensions}/size.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/size.js +0 -0
- /package/dist/domain/{system-components → extensions}/sketching-draw.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/sketching-draw.js +0 -0
- /package/dist/domain/{system-components → extensions}/summary-component.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/text-value.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/text-value.js +0 -0
- /package/dist/domain/{system-components → extensions}/tool-controls.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/tool-controls.js +0 -0
- /package/dist/domain/{system-components → extensions}/universal-sidebar-footer.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/universal-sidebar-footer.js +0 -0
- /package/dist/domain/{system-components → extensions}/view-controls.d.ts +0 -0
- /package/dist/domain/{system-components → extensions}/view-controls.js +0 -0
|
@@ -0,0 +1,3428 @@
|
|
|
1
|
+
# Corvid Drone
|
|
2
|
+
|
|
3
|
+
Build a pro tool in which an engineer configures a product and a client reads the result. A drone maker, Corvid Aero, has its engineers configure the K6 survey drone for each customer: the frame, the arm length, the landing gear, the propellers, the motors' anodising, the payload with its camera tilt, and the livery. The layout is a flight deck over a full-bleed scene: a rail of the spec's tabs, a translucent panel with the active tab's controls, a HUD with the price, the all-up weight and the mode, the spec's view controls and a log line for the configurator's messages. The client opens the same configuration from a page that sets the configurator read-only, and gets the same tabs as a spec sheet. The project writes the layout as one element; the spec holds the tabs, the controls and the tab each belongs to, the views, the looks and the rule between the winch and the frame.
|
|
4
|
+
|
|
5
|
+
- [Try it](#try-it)
|
|
6
|
+
- [What the example demonstrates](#what-the-example-demonstrates)
|
|
7
|
+
- [A deck over the scene](#a-deck-over-the-scene)
|
|
8
|
+
- [Tabs from the spec](#tabs-from-the-spec)
|
|
9
|
+
- [Controls that follow the tab](#controls-that-follow-the-tab)
|
|
10
|
+
- [Arm length and camera tilt](#arm-length-and-camera-tilt)
|
|
11
|
+
- [A target on the drone that opens a tab](#a-target-on-the-drone-that-opens-a-tab)
|
|
12
|
+
- [Messages in the HUD](#messages-in-the-hud)
|
|
13
|
+
- [The spec's views](#the-specs-views)
|
|
14
|
+
- [The client's spec sheet](#the-clients-spec-sheet)
|
|
15
|
+
- [The stage and the model](#the-stage-and-the-model)
|
|
16
|
+
- [On a phone](#on-a-phone)
|
|
17
|
+
- [Test cases](#test-cases)
|
|
18
|
+
- [What this layout leaves out](#what-this-layout-leaves-out)
|
|
19
|
+
- [Related pages](#related-pages)
|
|
20
|
+
- [Complete Implementation](#complete-implementation)
|
|
21
|
+
|
|
22
|
+
## Try it
|
|
23
|
+
|
|
24
|
+
The client's view is under [The client's spec sheet](#the-clients-spec-sheet), the phone version under [On a phone](#on-a-phone).
|
|
25
|
+
|
|
26
|
+
## What the example demonstrates
|
|
27
|
+
|
|
28
|
+
[Custom Layout](?path=/docs/layouts-custom-layout--docs) explains how a project layout works: the element name, the scene, the safe area and the hooks. This page is a finished design built the same way, for a product with tabs and two audiences, and it shows the pieces Custom Layout does not use:
|
|
29
|
+
|
|
30
|
+
| Piece | Where |
|
|
31
|
+
| -------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
|
32
|
+
| `phx.frontend.useTabNavigation()` for the spec's `sidebarTabs`, as a rail and as a bottom bar | `DroneTabs`, [Tabs from the spec](#tabs-from-the-spec) |
|
|
33
|
+
| Controls filtered to a tab with `$filter.navigationStates`, each checked with `phx.frontend.useComponent()` | `DroneFields`, [Controls that follow the tab](#controls-that-follow-the-tab) |
|
|
34
|
+
| `phx.frontend.useDistanceDimension()` and `useAngleDimension()`, staging with `update` and committing with `resolve` | `ArmLengthField`, `GimbalTiltField`, [Arm length and camera tilt](#arm-length-and-camera-tilt) |
|
|
35
|
+
| `phx.frontend.useAttributeSwitch()` for the landing gear | `GearField` |
|
|
36
|
+
| A `sceneEntityObjectHtmlHotspot` whose entity element switches the tab, hidden from a viewer with `$filter.modes` | `PayloadHotspot`, [A target on the drone](#a-target-on-the-drone-that-opens-a-tab) |
|
|
37
|
+
| `phx.frontend.useConfiguratorMessages()` as a HUD log line | `MessageLog`, [Messages in the HUD](#messages-in-the-hud) |
|
|
38
|
+
| `LayoutSlots.DesktopViewControls` and `MobileViewControls` for the spec's `viewControls`, with `useResetCamera()` | `ViewDock`, [The spec's views](#the-specs-views) |
|
|
39
|
+
| Read mode from the host's `readonly` property, read with `phx.frontend.useAccessService()`, in a **Read Mode** story | `mocks/viewer.yaml`, [The client's spec sheet](#the-clients-spec-sheet) |
|
|
40
|
+
| A morph target, an object group and `sceneEntityObjectLocalMaterialOverride`, on a `canvasCustomEnvironment` stage | `DroneHangar`, [The stage and the model](#the-stage-and-the-model) |
|
|
41
|
+
|
|
42
|
+
## A deck over the scene
|
|
43
|
+
|
|
44
|
+
`src/elements/ConfiguratorLayout.tsx` keeps the scene mounted and lays the desktop or the phone deck over it. `phx.frontend.useActiveViewports()` tells them apart by the width of the configurator's container, not the window.
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
import { Box } from '@mui/material'
|
|
48
|
+
|
|
49
|
+
import { LayoutSlots } from '@salsita-ai/sdk/configurator'
|
|
50
|
+
|
|
51
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
52
|
+
import { DesktopDeck } from '../_shared/DesktopDeck.js'
|
|
53
|
+
import { PhoneDeck } from '../_shared/PhoneDeck.js'
|
|
54
|
+
import { CORVID } from '../_shared/tokens.js'
|
|
55
|
+
|
|
56
|
+
// A pro tool over a full-bleed scene: the spec's tabs, a panel with the active tab's controls, a HUD with the
|
|
57
|
+
// readouts, and the spec's view controls. The canvas stays mounted; what lies over it is the desktop or the phone
|
|
58
|
+
// deck, and each calls `useSceneSafeArea` itself, so only the one on screen tells the camera where the drone goes.
|
|
59
|
+
// The layout shows messages in its own log line, so it does not mount `LayoutSlots.Services`.
|
|
60
|
+
const ConfiguratorLayout = () => {
|
|
61
|
+
// `null` until the container has been measured; only the scene shows until then
|
|
62
|
+
const viewports = phx.frontend.useActiveViewports()
|
|
63
|
+
const isDesktop = viewports?.includes('desktop') ?? false
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<Box
|
|
67
|
+
component="div"
|
|
68
|
+
sx={{ position: 'absolute', inset: 0, overflow: 'clip', bgcolor: CORVID.graphite, color: CORVID.ink }}
|
|
69
|
+
>
|
|
70
|
+
<LayoutSlots.SharedCanvas />
|
|
71
|
+
{viewports !== null && (isDesktop ? <DesktopDeck /> : <PhoneDeck />)}
|
|
72
|
+
</Box>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export default phx.frontend.createCustomPhoenixLayoutElement('ConfiguratorLayout', ConfiguratorLayout)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
- **The scene is full-bleed.** The canvas fills the configurator, and the rail, the panel and the HUD lie over it. The panel is translucent with a blur, so the hangar shows through it.
|
|
80
|
+
- **Each deck measures its own safe area.** `DesktopDeck` attaches `safeAreaRef` to a box in the free area right of the panel, below the HUD and above the dock; `PhoneDeck` to the scene above the sheet. The camera frames the drone in that box, so the drone never sits under the panel.
|
|
81
|
+
- **Fullscreen is a preview.** `phx.frontend.useIsFullscreenActive()` removes the rail, the panel, the HUD and the log; the dock with the camera reset and the exit stays, and on a desktop the view controls with it, as [Interaction Modes → Full-screen preview](?path=/docs/ux-guidelines-interaction-modes--docs#full-screen-preview) recommends. The fullscreen button records `FrontendAnalyticsFactory.FullScreenModeToggled()` through `phx.frontend.useAnalytics()`, like the built-in one.
|
|
82
|
+
|
|
83
|
+
## Tabs from the spec
|
|
84
|
+
|
|
85
|
+
The tabs are product structure, so they are in the spec: one `sidebarTabs` component on the drone, a navigation state per tab and an icon from the PhxIcon set.
|
|
86
|
+
|
|
87
|
+
```yaml
|
|
88
|
+
navigationStates:
|
|
89
|
+
- airframe
|
|
90
|
+
- propulsion
|
|
91
|
+
- payload
|
|
92
|
+
- livery
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```yaml
|
|
96
|
+
components:
|
|
97
|
+
Drone:
|
|
98
|
+
sidebarTabs:
|
|
99
|
+
main:
|
|
100
|
+
tabs:
|
|
101
|
+
- label: Airframe
|
|
102
|
+
icon: Cube
|
|
103
|
+
navigationStates: [airframe]
|
|
104
|
+
- label: Propulsion
|
|
105
|
+
icon: ArrowClockwise
|
|
106
|
+
navigationStates: [propulsion]
|
|
107
|
+
- label: Payload
|
|
108
|
+
icon: Scan
|
|
109
|
+
navigationStates: [payload]
|
|
110
|
+
- label: Livery
|
|
111
|
+
icon: PaletteBold
|
|
112
|
+
navigationStates: [livery]
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`DroneTabs` draws them with `phx.frontend.useTabNavigation()`: a rail of icons on a desktop, a segmented bar on a phone. Each tab comes with its resolved `label`, the spec's `icon`, which `PhxIcon` from `@salsita-ai/sdk/ui` renders, and `isActive`; `select(index)` enters the tab's first navigation state, as the built-in tabs do. The list is a `tablist` whose tabs control the panel, and the arrow keys move between them.
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
import { Box, ButtonBase, Typography } from '@mui/material'
|
|
119
|
+
import type { KeyboardEvent } from 'react'
|
|
120
|
+
|
|
121
|
+
import { PhxIcon } from '@salsita-ai/sdk/ui'
|
|
122
|
+
|
|
123
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
124
|
+
import { CORVID } from './tokens.js'
|
|
125
|
+
|
|
126
|
+
// The panel the tabs control; `TabPanel` carries this id
|
|
127
|
+
export const TAB_PANEL_ID = 'corvid-tab-panel'
|
|
128
|
+
|
|
129
|
+
interface DroneTabsProps {
|
|
130
|
+
readonly orientation: 'vertical' | 'horizontal'
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// The spec's `sidebarTabs` as a tab list: a rail of icons on a desktop, a segmented bar at the bottom of a phone.
|
|
134
|
+
// `useTabNavigation` gives each tab its resolved label, the PhxIcon name the spec sets and `isActive`; `select` enters
|
|
135
|
+
// the tab's navigation state, and the controls the spec filters to that state become active. Switching tabs is
|
|
136
|
+
// navigation, not editing, so a viewer switches too.
|
|
137
|
+
export const DroneTabs = ({ orientation }: DroneTabsProps) => {
|
|
138
|
+
const { tabs, select } = phx.frontend.useTabNavigation()
|
|
139
|
+
const vertical = orientation === 'vertical'
|
|
140
|
+
const nextKey = vertical ? 'ArrowDown' : 'ArrowRight'
|
|
141
|
+
const previousKey = vertical ? 'ArrowUp' : 'ArrowLeft'
|
|
142
|
+
|
|
143
|
+
// The arrow keys move between the tabs and enter the one they reach, as in a native tab list
|
|
144
|
+
const handleKeyDown = (event: KeyboardEvent<HTMLElement>, index: number) => {
|
|
145
|
+
const step = event.key === nextKey ? 1 : event.key === previousKey ? -1 : 0
|
|
146
|
+
|
|
147
|
+
if (step === 0) {
|
|
148
|
+
return
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
event.preventDefault()
|
|
152
|
+
const next = (index + step + tabs.length) % tabs.length
|
|
153
|
+
select(next)
|
|
154
|
+
event.currentTarget.parentElement?.querySelectorAll<HTMLElement>('[role="tab"]')[next]?.focus()
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<Box
|
|
159
|
+
component="div"
|
|
160
|
+
role="tablist"
|
|
161
|
+
aria-label="Configuration"
|
|
162
|
+
aria-orientation={orientation}
|
|
163
|
+
sx={{ display: 'flex', flexDirection: vertical ? 'column' : 'row', gap: vertical ? 0.5 : 0, blockSize: '100%' }}
|
|
164
|
+
>
|
|
165
|
+
{tabs.map(tab => (
|
|
166
|
+
<ButtonBase
|
|
167
|
+
key={tab.state}
|
|
168
|
+
role="tab"
|
|
169
|
+
id={`corvid-tab-${tab.index}`}
|
|
170
|
+
aria-selected={tab.isActive}
|
|
171
|
+
aria-controls={TAB_PANEL_ID}
|
|
172
|
+
tabIndex={tab.isActive ? 0 : -1}
|
|
173
|
+
onClick={() => select(tab.index)}
|
|
174
|
+
onKeyDown={event => handleKeyDown(event, tab.index)}
|
|
175
|
+
sx={{
|
|
176
|
+
position: 'relative',
|
|
177
|
+
flex: vertical ? 'none' : '1 1 0',
|
|
178
|
+
display: 'flex',
|
|
179
|
+
flexDirection: 'column',
|
|
180
|
+
alignItems: 'center',
|
|
181
|
+
justifyContent: 'center',
|
|
182
|
+
gap: 0.75,
|
|
183
|
+
minBlockSize: vertical ? 68 : 'auto',
|
|
184
|
+
color: tab.isActive ? CORVID.lime : CORVID.muted,
|
|
185
|
+
bgcolor: tab.isActive ? 'rgba(198, 244, 50, 0.07)' : 'transparent',
|
|
186
|
+
transition: 'color 160ms, background-color 160ms',
|
|
187
|
+
// The active tab's marker: a bar on the rail's outer edge, or along the top of the bar's segment
|
|
188
|
+
'&::before': {
|
|
189
|
+
content: '""',
|
|
190
|
+
position: 'absolute',
|
|
191
|
+
insetBlock: vertical ? '14px' : '0 auto',
|
|
192
|
+
insetInlineStart: vertical ? 0 : '18%',
|
|
193
|
+
insetInlineEnd: vertical ? 'auto' : '18%',
|
|
194
|
+
inlineSize: vertical ? 2 : 'auto',
|
|
195
|
+
blockSize: vertical ? 'auto' : 2,
|
|
196
|
+
bgcolor: CORVID.lime,
|
|
197
|
+
opacity: tab.isActive ? 1 : 0,
|
|
198
|
+
transition: 'opacity 160ms',
|
|
199
|
+
},
|
|
200
|
+
'&:hover': { color: tab.isActive ? CORVID.lime : CORVID.ink },
|
|
201
|
+
'&.Mui-focusVisible': { outline: `1px solid ${CORVID.lime}`, outlineOffset: -4 },
|
|
202
|
+
}}
|
|
203
|
+
>
|
|
204
|
+
{tab.icon !== undefined && <PhxIcon name={tab.icon} fontSize="medium" />}
|
|
205
|
+
<Typography
|
|
206
|
+
component="span"
|
|
207
|
+
sx={{ fontFamily: CORVID.mono, fontSize: 9.5, letterSpacing: '0.14em', textTransform: 'uppercase' }}
|
|
208
|
+
>
|
|
209
|
+
{tab.label}
|
|
210
|
+
</Typography>
|
|
211
|
+
</ButtonBase>
|
|
212
|
+
))}
|
|
213
|
+
</Box>
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
The panel's heading is the active tab's label, from the same hook in `TabPanel`. Use `useTabNavigation()` rather than `LayoutSlots.useSidebarTabs()` in a layout of your own: it is a member of `phx.frontend`, reads `horizontalTabs` too, and gives each tab its navigation states.
|
|
219
|
+
|
|
220
|
+
## Controls that follow the tab
|
|
221
|
+
|
|
222
|
+
Each control component carries the tab it belongs to as a filter, so it is active only while its tab is:
|
|
223
|
+
|
|
224
|
+
```yaml
|
|
225
|
+
components:
|
|
226
|
+
Drone:
|
|
227
|
+
attributeSelection:
|
|
228
|
+
frame:
|
|
229
|
+
value:
|
|
230
|
+
$bind: frame
|
|
231
|
+
options:
|
|
232
|
+
- label: Quad
|
|
233
|
+
description: Four motors, the lightest and longest-flying
|
|
234
|
+
value: Quad
|
|
235
|
+
- label: Hexa
|
|
236
|
+
description: Six motors; lands safely with one motor out
|
|
237
|
+
value: Hexa
|
|
238
|
+
$filter:
|
|
239
|
+
navigationStates: [airframe]
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
The layout lists every control once, in the order the panel shows them, and each renders only while its component is active. Which tab shows which control is then decided by the spec alone: moving `props` to the Airframe tab is a change of its filter, not of the layout.
|
|
243
|
+
|
|
244
|
+
```tsx
|
|
245
|
+
import { Box } from '@mui/material'
|
|
246
|
+
|
|
247
|
+
import { ArmLengthField } from './ArmLengthField.js'
|
|
248
|
+
import { GearField } from './GearField.js'
|
|
249
|
+
import { GimbalTiltField } from './GimbalTiltField.js'
|
|
250
|
+
import { OptionField } from './OptionField.js'
|
|
251
|
+
import { FRAME_VISUALS, LIVERY_VISUALS, MOTOR_VISUALS, PAYLOAD_VISUALS, PROP_VISUALS } from './visuals.js'
|
|
252
|
+
|
|
253
|
+
interface DroneFieldsProps {
|
|
254
|
+
readonly readOnly: boolean
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Every control of the spec, in the order the panel shows them. Each one renders only while its spec component is
|
|
258
|
+
// active, so the spec's `$filter.navigationStates` decides which tab shows it and `$filter.active` hides the tilt
|
|
259
|
+
// without the camera. The labels are the layout's: `attributeSelection` and the dimensions carry none.
|
|
260
|
+
export const DroneFields = ({ readOnly }: DroneFieldsProps) => {
|
|
261
|
+
return (
|
|
262
|
+
<Box component="div" sx={{ display: 'grid', gap: readOnly ? 1.5 : 3 }}>
|
|
263
|
+
<OptionField instanceName="frame" label="Frame" visuals={FRAME_VISUALS} readOnly={readOnly} />
|
|
264
|
+
<ArmLengthField readOnly={readOnly} />
|
|
265
|
+
<GearField readOnly={readOnly} />
|
|
266
|
+
<OptionField instanceName="props" label="Propellers" visuals={PROP_VISUALS} readOnly={readOnly} />
|
|
267
|
+
<OptionField instanceName="motorColor" label="Motor anodising" visuals={MOTOR_VISUALS} readOnly={readOnly} />
|
|
268
|
+
<OptionField instanceName="payload" label="Payload" visuals={PAYLOAD_VISUALS} readOnly={readOnly} />
|
|
269
|
+
<GimbalTiltField readOnly={readOnly} />
|
|
270
|
+
<OptionField instanceName="livery" label="Livery" visuals={LIVERY_VISUALS} readOnly={readOnly} />
|
|
271
|
+
</Box>
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
The control hooks throw for a component that a `$filter` makes inactive, so every field first asks `phx.frontend.useComponent({ entityId, componentType, instanceName })` for its component, with the component type from `@salsita-ai/sdk/domain`, and renders nothing while it gets `null`. `OptionField` does this for each `attributeSelection`, then draws its options as tiles: the layout's drawing or colour chip per option `value`, the spec's `label` as the title and the chosen option's `description` below. An option an invariant rules out is hatched; see [Messages in the HUD](#messages-in-the-hud).
|
|
277
|
+
|
|
278
|
+
The landing gear is an `attributeSwitch`: `GearField` calls `phx.frontend.useAttributeSwitch(droneId, 'retractableGear')` and gives `state.value` and `onChange()` to a switch.
|
|
279
|
+
|
|
280
|
+
## Arm length and camera tilt
|
|
281
|
+
|
|
282
|
+
The arm length is a `distanceDimension` in millimetres, the data unit, and the camera's tilt an `angleDimension`, which the spec stores in radians and shows in degrees. The tilt is filtered twice: to the Payload tab, and to a drone that carries the camera.
|
|
283
|
+
|
|
284
|
+
```yaml
|
|
285
|
+
components:
|
|
286
|
+
Drone:
|
|
287
|
+
distanceDimension:
|
|
288
|
+
armLength:
|
|
289
|
+
value:
|
|
290
|
+
$bind: armLength
|
|
291
|
+
min: 280
|
|
292
|
+
max: 380
|
|
293
|
+
$filter:
|
|
294
|
+
navigationStates: [airframe]
|
|
295
|
+
angleDimension:
|
|
296
|
+
gimbalTilt:
|
|
297
|
+
value:
|
|
298
|
+
$bind: gimbalTilt
|
|
299
|
+
min: 0
|
|
300
|
+
max: 1.5707963268
|
|
301
|
+
unit: degree
|
|
302
|
+
decimalPlaces: 0
|
|
303
|
+
# Only the camera tilts
|
|
304
|
+
$filter:
|
|
305
|
+
navigationStates: [payload]
|
|
306
|
+
active:
|
|
307
|
+
$eval: self.payload === 'Camera'
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
`phx.frontend.useDistanceDimension(droneId, 'armLength')` returns the value in the display unit with `valueWithUnit`, the range, `formatValue()` for any other length, and two functions: `update(value)` stages a value, which the scene shows at once, and `resolve()` commits it, or rolls it back when it breaks an invariant. `HudSlider` calls `update` from the slider's `onChange` and `resolve` from `onChangeCommitted`, so the arms grow under the thumb and the change is committed, and saved, once the thumb is let go.
|
|
311
|
+
|
|
312
|
+
```tsx
|
|
313
|
+
import { distanceDimensionComponent } from '@salsita-ai/sdk/domain'
|
|
314
|
+
|
|
315
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
316
|
+
import { HudSlider } from './HudSlider.js'
|
|
317
|
+
import { SpecRow } from './SpecRow.js'
|
|
318
|
+
|
|
319
|
+
const STEP = 10
|
|
320
|
+
|
|
321
|
+
interface ArmLengthFieldProps {
|
|
322
|
+
readonly readOnly: boolean
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const ArmLengthControl = ({ readOnly }: ArmLengthFieldProps) => {
|
|
326
|
+
const droneId = phx.frontend.useRootEntityId()
|
|
327
|
+
const arm = phx.frontend.useDistanceDimension(droneId, 'armLength')
|
|
328
|
+
// Motor to motor across the drone: twice the arm, which is measured from the centre
|
|
329
|
+
const span = `Motor to motor ${arm.formatValue(arm.value * 2, true)}`
|
|
330
|
+
|
|
331
|
+
if (readOnly) {
|
|
332
|
+
return <SpecRow label="Arm length" value={arm.valueWithUnit} detail={span} />
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return (
|
|
336
|
+
<HudSlider
|
|
337
|
+
id="corvid-arm-length"
|
|
338
|
+
label="Arm length"
|
|
339
|
+
valueText={arm.valueWithUnit}
|
|
340
|
+
value={arm.value}
|
|
341
|
+
min={arm.min}
|
|
342
|
+
max={arm.max}
|
|
343
|
+
step={STEP}
|
|
344
|
+
minLabel={arm.formatValue(arm.min, true)}
|
|
345
|
+
maxLabel={arm.formatValue(arm.max, true)}
|
|
346
|
+
detail={span}
|
|
347
|
+
onChange={arm.update}
|
|
348
|
+
onCommit={arm.resolve}
|
|
349
|
+
/>
|
|
350
|
+
)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// The spec's `distanceDimension` for the arm length, in the display unit. Moving the slider stages each value with
|
|
354
|
+
// `update`, so the arms grow under the thumb through the spec's morph target, and letting go commits it with
|
|
355
|
+
// `resolve`. The field exists only while the spec's component is active.
|
|
356
|
+
export const ArmLengthField = (props: ArmLengthFieldProps) => {
|
|
357
|
+
const droneId = phx.frontend.useRootEntityId()
|
|
358
|
+
const component = phx.frontend.useComponent({
|
|
359
|
+
entityId: droneId,
|
|
360
|
+
componentType: distanceDimensionComponent,
|
|
361
|
+
instanceName: 'armLength',
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
return component === null ? null : <ArmLengthControl {...props} />
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
`phx.frontend.useAngleDimension(droneId, 'gimbalTilt')` works the same way in the spec's `unit`: `value`, `min` and `max` in degrees, `unitLabel` "°", and `update` in degrees. `GimbalTiltField` adds a quarter dial that shows the camera's line of sight.
|
|
369
|
+
|
|
370
|
+
```tsx
|
|
371
|
+
import { angleDimensionComponent } from '@salsita-ai/sdk/domain'
|
|
372
|
+
|
|
373
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
374
|
+
import { CORVID } from '../_shared/tokens.js'
|
|
375
|
+
import { HudSlider } from './HudSlider.js'
|
|
376
|
+
import { SpecRow } from './SpecRow.js'
|
|
377
|
+
|
|
378
|
+
interface TiltDialProps {
|
|
379
|
+
readonly degrees: number
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// A quarter dial from the horizon down to the ground, with the camera's line of sight on it
|
|
383
|
+
const TiltDial = ({ degrees }: TiltDialProps) => {
|
|
384
|
+
const radians = (degrees * Math.PI) / 180
|
|
385
|
+
const x = 6 + 40 * Math.cos(radians)
|
|
386
|
+
const y = 6 + 40 * Math.sin(radians)
|
|
387
|
+
|
|
388
|
+
return (
|
|
389
|
+
<svg viewBox="0 0 52 52" width={52} height={52} aria-hidden fill="none">
|
|
390
|
+
<path d="M6 6h44M6 6v44" stroke={CORVID.line} />
|
|
391
|
+
<path d="M46 6A40 40 0 0 1 6 46" stroke={CORVID.faint} strokeDasharray="2 3" />
|
|
392
|
+
<path d={`M6 6L${x} ${y}`} stroke={CORVID.lime} strokeWidth={1.6} />
|
|
393
|
+
<circle cx={x} cy={y} r={2.5} fill={CORVID.lime} />
|
|
394
|
+
<rect x={2} y={2} width={8} height={8} fill={CORVID.graphite} stroke={CORVID.lime} />
|
|
395
|
+
</svg>
|
|
396
|
+
)
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const describe = (degrees: number) => {
|
|
400
|
+
if (degrees <= 0) {
|
|
401
|
+
return 'Level with the horizon, for inspection'
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (degrees >= 90) {
|
|
405
|
+
return 'Straight down, for mapping'
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return `${degrees}° below the horizon`
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
interface GimbalTiltFieldProps {
|
|
412
|
+
readonly readOnly: boolean
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const GimbalTiltControl = ({ readOnly }: GimbalTiltFieldProps) => {
|
|
416
|
+
const droneId = phx.frontend.useRootEntityId()
|
|
417
|
+
const tilt = phx.frontend.useAngleDimension(droneId, 'gimbalTilt')
|
|
418
|
+
const valueText = `${tilt.value}${tilt.unitLabel}`
|
|
419
|
+
const min = tilt.min ?? 0
|
|
420
|
+
const max = tilt.max ?? 90
|
|
421
|
+
|
|
422
|
+
if (readOnly) {
|
|
423
|
+
return <SpecRow label="Gimbal tilt" value={valueText} detail={describe(tilt.value)} />
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return (
|
|
427
|
+
<HudSlider
|
|
428
|
+
id="corvid-gimbal-tilt"
|
|
429
|
+
label="Gimbal tilt"
|
|
430
|
+
valueText={valueText}
|
|
431
|
+
value={tilt.value}
|
|
432
|
+
min={min}
|
|
433
|
+
max={max}
|
|
434
|
+
step={1}
|
|
435
|
+
minLabel={`${min}${tilt.unitLabel} HORIZON`}
|
|
436
|
+
maxLabel={`${max}${tilt.unitLabel} NADIR`}
|
|
437
|
+
detail={describe(tilt.value)}
|
|
438
|
+
adornment={<TiltDial degrees={tilt.value} />}
|
|
439
|
+
onChange={tilt.update}
|
|
440
|
+
onCommit={tilt.resolve}
|
|
441
|
+
/>
|
|
442
|
+
)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// The spec's `angleDimension` for the camera's tilt, in degrees as the spec's `unit` asks; the spec stores radians and
|
|
446
|
+
// turns the gimbal's pivot group by them. The slider stages with `update` and commits with `resolve`. The spec makes
|
|
447
|
+
// the component active only on the Payload tab and only while the camera is fitted, so the field checks first.
|
|
448
|
+
export const GimbalTiltField = (props: GimbalTiltFieldProps) => {
|
|
449
|
+
const droneId = phx.frontend.useRootEntityId()
|
|
450
|
+
const component = phx.frontend.useComponent({
|
|
451
|
+
entityId: droneId,
|
|
452
|
+
componentType: angleDimensionComponent,
|
|
453
|
+
instanceName: 'gimbalTilt',
|
|
454
|
+
})
|
|
455
|
+
|
|
456
|
+
return component === null ? null : <GimbalTiltControl {...props} />
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
## A target on the drone that opens a tab
|
|
461
|
+
|
|
462
|
+
A `sceneEntityObjectHtmlHotspot` pins an element to one of the entity's objects, here the payload mount, and moves with it. Its `name` is an entity element of the project. The filter shows it only to the engineer, only outside the Payload tab and not in fullscreen.
|
|
463
|
+
|
|
464
|
+
```yaml
|
|
465
|
+
components:
|
|
466
|
+
Drone:
|
|
467
|
+
sceneEntityObjectHtmlHotspot:
|
|
468
|
+
payloadMount:
|
|
469
|
+
target: payloadMount
|
|
470
|
+
name: PayloadHotspot
|
|
471
|
+
$filter:
|
|
472
|
+
modes: [owner]
|
|
473
|
+
navigationStates: [airframe, propulsion, livery]
|
|
474
|
+
fullscreen: false
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
`PayloadHotspot` renders a target and a label, and calls `select('payload')` of `phx.frontend.useTabNavigation()`. The tab then becomes active, the panel shows the payload controls, and the target disappears, because its filter leaves out the Payload tab.
|
|
478
|
+
|
|
479
|
+
```tsx
|
|
480
|
+
import { Box, ButtonBase } from '@mui/material'
|
|
481
|
+
|
|
482
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
483
|
+
import { CORVID } from '../_shared/tokens.js'
|
|
484
|
+
|
|
485
|
+
// Rendered by the spec's `sceneEntityObjectHtmlHotspot` on the payload mount: a target on the drone that opens the
|
|
486
|
+
// Payload tab. The spec's `$filter` shows it only to the engineer and only outside that tab.
|
|
487
|
+
export default phx.frontend.createCustomPhoenixEntityElement('PayloadHotspot', () => {
|
|
488
|
+
const { select } = phx.frontend.useTabNavigation()
|
|
489
|
+
|
|
490
|
+
return (
|
|
491
|
+
<ButtonBase
|
|
492
|
+
aria-label="Payload: open the Payload tab"
|
|
493
|
+
onClick={() => select('payload')}
|
|
494
|
+
sx={{
|
|
495
|
+
display: 'flex',
|
|
496
|
+
alignItems: 'center',
|
|
497
|
+
gap: 1,
|
|
498
|
+
// The target's centre sits on the mount; the label hangs to its right
|
|
499
|
+
transform: 'translate(-14px, 0)',
|
|
500
|
+
color: CORVID.lime,
|
|
501
|
+
'&:hover .corvid-hotspot-label, &.Mui-focusVisible .corvid-hotspot-label': {
|
|
502
|
+
bgcolor: CORVID.lime,
|
|
503
|
+
color: CORVID.graphite,
|
|
504
|
+
},
|
|
505
|
+
}}
|
|
506
|
+
>
|
|
507
|
+
<Box
|
|
508
|
+
component="span"
|
|
509
|
+
aria-hidden
|
|
510
|
+
sx={{
|
|
511
|
+
position: 'relative',
|
|
512
|
+
display: 'grid',
|
|
513
|
+
placeItems: 'center',
|
|
514
|
+
inlineSize: 28,
|
|
515
|
+
blockSize: 28,
|
|
516
|
+
// A ring that keeps widening and fading, so the target reads as live
|
|
517
|
+
'&::after': {
|
|
518
|
+
content: '""',
|
|
519
|
+
position: 'absolute',
|
|
520
|
+
inset: 0,
|
|
521
|
+
borderRadius: '50%',
|
|
522
|
+
border: `1px solid ${CORVID.lime}`,
|
|
523
|
+
animation: 'corvid-ping 2s ease-out infinite',
|
|
524
|
+
},
|
|
525
|
+
'@keyframes corvid-ping': {
|
|
526
|
+
from: { transform: 'scale(0.6)', opacity: 0.9 },
|
|
527
|
+
to: { transform: 'scale(1.6)', opacity: 0 },
|
|
528
|
+
},
|
|
529
|
+
}}
|
|
530
|
+
>
|
|
531
|
+
<svg viewBox="0 0 28 28" width={28} height={28} fill="none" stroke="currentColor" strokeWidth={1.4}>
|
|
532
|
+
<circle cx={14} cy={14} r={8} fill="rgba(20, 23, 28, 0.6)" />
|
|
533
|
+
<path d="M14 2v6M14 20v6M2 14h6M20 14h6" />
|
|
534
|
+
<circle cx={14} cy={14} r={1.6} fill="currentColor" />
|
|
535
|
+
</svg>
|
|
536
|
+
</Box>
|
|
537
|
+
<Box
|
|
538
|
+
component="span"
|
|
539
|
+
className="corvid-hotspot-label"
|
|
540
|
+
sx={{
|
|
541
|
+
paddingInline: 1,
|
|
542
|
+
paddingBlock: 0.5,
|
|
543
|
+
fontFamily: CORVID.mono,
|
|
544
|
+
fontSize: 10.5,
|
|
545
|
+
letterSpacing: '0.16em',
|
|
546
|
+
whiteSpace: 'nowrap',
|
|
547
|
+
color: CORVID.lime,
|
|
548
|
+
bgcolor: 'rgba(20, 23, 28, 0.8)',
|
|
549
|
+
border: `1px solid ${CORVID.lime}`,
|
|
550
|
+
transition: 'background-color 160ms, color 160ms',
|
|
551
|
+
}}
|
|
552
|
+
>
|
|
553
|
+
PAYLOAD ›
|
|
554
|
+
</Box>
|
|
555
|
+
</ButtonBase>
|
|
556
|
+
)
|
|
557
|
+
})
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
The hotspot sits at the origin of the object named by `target`. The model gives the mount's node its origin on the mount, and the loader places every part at the scene's origin, so the spec moves the mount back with `offsetZ: 90` in `sceneEntityObjectProperties`; see [The stage and the model](#the-stage-and-the-model).
|
|
561
|
+
|
|
562
|
+
The hotspot's own `offsetX`, `offsetY` and `offsetZ` are applied inside the object, in the unit of its model: on this model in metres, so `offsetY: 0.15` lifts the target 150 mm and `offsetY: 150` sends it 150 m away. Giving the node its origin where the target belongs, as here, needs no offset.
|
|
563
|
+
|
|
564
|
+
## Messages in the HUD
|
|
565
|
+
|
|
566
|
+
The delivery winch needs six motors. The rule is a `fixable` invariant, whose condition is in `src/invariants/drone.ts`:
|
|
567
|
+
|
|
568
|
+
```yaml
|
|
569
|
+
invariants:
|
|
570
|
+
Drone:
|
|
571
|
+
winchNeedsHexa:
|
|
572
|
+
type: winchNeedsHexa
|
|
573
|
+
severity: fixable
|
|
574
|
+
message: The delivery winch needs the hexa frame to lift its load.
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
On a quad the winch option comes back `disabled` with the message as its `hint`, and on a winch drone the quad option does. `OptionField` hatches the option and shows the hint below the tiles, in orange. The tile stays clickable: choosing it anyway makes the change, the invariant rejects it, and the configurator reports the rejection as a message. Choosing the LiDAR carries a hint from the `lidar-base-station` transformer, which is a message too.
|
|
578
|
+
|
|
579
|
+
`MessageLog` shows them as the HUD's log line. `phx.frontend.useConfiguratorMessages(onMessage)` calls it once per message with its text resolved: a `violation` with the invariant's `message` and its `severity`, a `sideEffect` with the transformer's hint, and the errors and save failures. The line tags each by kind and returns to **READY** after a few seconds. The layout mounts no `LayoutSlots.Services`, so the messages are not shown twice as snackbars; see [Custom Layout → Show messages](?path=/docs/layouts-custom-layout--docs#show-messages).
|
|
580
|
+
|
|
581
|
+
```tsx
|
|
582
|
+
import { Box } from '@mui/material'
|
|
583
|
+
import { useEffect, useState } from 'react'
|
|
584
|
+
|
|
585
|
+
import type { ConfiguratorMessage } from '@salsita-ai/sdk/react'
|
|
586
|
+
|
|
587
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
588
|
+
import { CORVID, GLASS_SX } from './tokens.js'
|
|
589
|
+
|
|
590
|
+
const VISIBLE_MS = 9000
|
|
591
|
+
|
|
592
|
+
interface LogEntry {
|
|
593
|
+
readonly message: ConfiguratorMessage
|
|
594
|
+
readonly time: string
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// The tag before each line, by the kind of message and, for a violation, its severity
|
|
598
|
+
const tagOf = (message: ConfiguratorMessage) => {
|
|
599
|
+
if (message.kind === 'violation') {
|
|
600
|
+
return message.violation.severity === 'fixable' || message.violation.severity === 'fatal'
|
|
601
|
+
? { label: 'REJECTED', color: CORVID.orange }
|
|
602
|
+
: { label: message.violation.severity.toUpperCase(), color: CORVID.cyan }
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
if (message.kind === 'sideEffect') {
|
|
606
|
+
return { label: 'NOTE', color: CORVID.cyan }
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
if (message.kind === 'saveRecovery') {
|
|
610
|
+
return { label: 'SAVED', color: CORVID.lime }
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
return { label: 'ERROR', color: CORVID.orange }
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
interface MessageLogProps {
|
|
617
|
+
readonly compact: boolean
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// The configurator's messages as the HUD's log line: a rejected change, the hint a change carries, a failed save.
|
|
621
|
+
// `useConfiguratorMessages` hands each one over with its text resolved; the line shows the latest for a few seconds,
|
|
622
|
+
// then returns to its idle state.
|
|
623
|
+
export const MessageLog = ({ compact }: MessageLogProps) => {
|
|
624
|
+
const [entry, setEntry] = useState<LogEntry | null>(null)
|
|
625
|
+
const { accessInfo } = phx.frontend.useAccessService()
|
|
626
|
+
|
|
627
|
+
phx.frontend.useConfiguratorMessages(message => {
|
|
628
|
+
setEntry({ message, time: new Date().toLocaleTimeString('en-GB') })
|
|
629
|
+
})
|
|
630
|
+
|
|
631
|
+
useEffect(() => {
|
|
632
|
+
if (entry === null) {
|
|
633
|
+
return
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const timeout = setTimeout(() => setEntry(null), VISIBLE_MS)
|
|
637
|
+
|
|
638
|
+
return () => clearTimeout(timeout)
|
|
639
|
+
}, [entry])
|
|
640
|
+
|
|
641
|
+
const tag = entry ? tagOf(entry.message) : { label: 'READY', color: CORVID.faint }
|
|
642
|
+
const idle = accessInfo.viewMode ? 'Viewing a shared configuration' : 'Every change saves on its own'
|
|
643
|
+
|
|
644
|
+
return (
|
|
645
|
+
<Box
|
|
646
|
+
component="div"
|
|
647
|
+
role="status"
|
|
648
|
+
aria-live="polite"
|
|
649
|
+
sx={{
|
|
650
|
+
...GLASS_SX,
|
|
651
|
+
display: 'flex',
|
|
652
|
+
alignItems: 'baseline',
|
|
653
|
+
gap: compact ? 1 : 1.5,
|
|
654
|
+
minInlineSize: 0,
|
|
655
|
+
maxInlineSize: compact ? 'none' : 560,
|
|
656
|
+
paddingInline: 1.5,
|
|
657
|
+
paddingBlock: 1,
|
|
658
|
+
fontFamily: CORVID.mono,
|
|
659
|
+
fontSize: compact ? 11 : 12,
|
|
660
|
+
lineHeight: 1.5,
|
|
661
|
+
}}
|
|
662
|
+
>
|
|
663
|
+
<Box component="span" aria-hidden sx={{ color: CORVID.lime }}>
|
|
664
|
+
>
|
|
665
|
+
</Box>
|
|
666
|
+
{entry && !compact && (
|
|
667
|
+
<Box component="span" sx={{ color: CORVID.faint }}>
|
|
668
|
+
{entry.time}
|
|
669
|
+
</Box>
|
|
670
|
+
)}
|
|
671
|
+
<Box component="span" sx={{ flex: 'none', color: tag.color, letterSpacing: '0.12em' }}>
|
|
672
|
+
{tag.label}
|
|
673
|
+
</Box>
|
|
674
|
+
<Box component="span" sx={{ minInlineSize: 0, color: entry ? CORVID.ink : CORVID.muted }}>
|
|
675
|
+
{entry ? entry.message.text : idle}
|
|
676
|
+
</Box>
|
|
677
|
+
</Box>
|
|
678
|
+
)
|
|
679
|
+
}
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
## The spec's views
|
|
683
|
+
|
|
684
|
+
Three views, each with its own camera filtered by `viewStates`: an orbit around the drone, a top view and a low view of the payload. The spec's `viewControls` list them with a label and an icon.
|
|
685
|
+
|
|
686
|
+
```yaml
|
|
687
|
+
viewStates:
|
|
688
|
+
- orbit
|
|
689
|
+
- top
|
|
690
|
+
- payloadView
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
```yaml
|
|
694
|
+
components:
|
|
695
|
+
Drone:
|
|
696
|
+
viewControls:
|
|
697
|
+
main:
|
|
698
|
+
items:
|
|
699
|
+
- label: Orbit
|
|
700
|
+
value: orbit
|
|
701
|
+
icon: CameraRotate
|
|
702
|
+
- label: Top
|
|
703
|
+
value: top
|
|
704
|
+
icon: GridFour
|
|
705
|
+
- label: Payload
|
|
706
|
+
value: payloadView
|
|
707
|
+
icon: Scan
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
`ViewDock` mounts the built-in controls for them: `LayoutSlots.DesktopViewControls`, a row of toggles, on a desktop, and `LayoutSlots.MobileViewControls`, one button that opens the list, on a phone. Both read the component and set the view state themselves, so the dock needs no state of its own. The built-in controls draw with the MUI theme, so the dock wraps them in a `ThemeProvider` with a dark palette, which also reaches the list a phone opens in a popper, and restyles the toggles through their MUI classes, `toggleButtonGroupClasses` and `toggleButtonClasses` from `@mui/material`. It adds the camera reset from `phx.frontend.useResetCamera()` and the fullscreen toggle. The HUD names the active view with `phx.frontend.useActiveViewState()` and the label of the matching item.
|
|
711
|
+
|
|
712
|
+
```tsx
|
|
713
|
+
import {
|
|
714
|
+
Box,
|
|
715
|
+
Divider,
|
|
716
|
+
IconButton,
|
|
717
|
+
ThemeProvider,
|
|
718
|
+
Tooltip,
|
|
719
|
+
createTheme,
|
|
720
|
+
toggleButtonClasses,
|
|
721
|
+
toggleButtonGroupClasses,
|
|
722
|
+
} from '@mui/material'
|
|
723
|
+
import type { Theme } from '@mui/material'
|
|
724
|
+
|
|
725
|
+
import { FrontendAnalyticsFactory, LayoutSlots } from '@salsita-ai/sdk/configurator'
|
|
726
|
+
import { PhxIcon } from '@salsita-ai/sdk/ui'
|
|
727
|
+
|
|
728
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
729
|
+
import { CORVID, GLASS_SX } from './tokens.js'
|
|
730
|
+
|
|
731
|
+
// The built-in view controls draw with the MUI theme's palette. A dark palette makes them part of the deck, and with
|
|
732
|
+
// them the list of views a phone opens, which renders in a popper outside the dock.
|
|
733
|
+
const deckTheme = (outer: Theme) =>
|
|
734
|
+
createTheme(outer, {
|
|
735
|
+
palette: {
|
|
736
|
+
mode: 'dark',
|
|
737
|
+
primary: { main: CORVID.lime },
|
|
738
|
+
text: { primary: CORVID.ink, secondary: CORVID.muted },
|
|
739
|
+
background: { paper: CORVID.panelSolid },
|
|
740
|
+
divider: CORVID.line,
|
|
741
|
+
action: { hover: 'rgba(232, 237, 242, 0.06)' },
|
|
742
|
+
},
|
|
743
|
+
})
|
|
744
|
+
|
|
745
|
+
interface DockButtonProps {
|
|
746
|
+
readonly label: string
|
|
747
|
+
readonly icon: string
|
|
748
|
+
readonly onClick: () => void
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
const DockButton = ({ label, icon, onClick }: DockButtonProps) => {
|
|
752
|
+
return (
|
|
753
|
+
<Tooltip title={label}>
|
|
754
|
+
<IconButton
|
|
755
|
+
aria-label={label}
|
|
756
|
+
onClick={onClick}
|
|
757
|
+
sx={{
|
|
758
|
+
inlineSize: 36,
|
|
759
|
+
blockSize: 36,
|
|
760
|
+
borderRadius: 0,
|
|
761
|
+
color: CORVID.muted,
|
|
762
|
+
'&:hover': { color: CORVID.lime, bgcolor: 'rgba(198, 244, 50, 0.08)' },
|
|
763
|
+
}}
|
|
764
|
+
>
|
|
765
|
+
<PhxIcon name={icon} />
|
|
766
|
+
</IconButton>
|
|
767
|
+
</Tooltip>
|
|
768
|
+
)
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
interface ViewDockProps {
|
|
772
|
+
readonly placement: 'desktop' | 'phone'
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// The spec's view controls, mounted from `LayoutSlots`, with the camera reset and fullscreen beside them. On a
|
|
776
|
+
// desktop `DesktopViewControls` is a row of toggles; on a phone `MobileViewControls` is one button that opens the
|
|
777
|
+
// list of views, left out in fullscreen as the UX guidelines ask. The dock gives the built-in controls a dark theme
|
|
778
|
+
// and restyles the toggles through their MUI classes.
|
|
779
|
+
export const ViewDock = ({ placement }: ViewDockProps) => {
|
|
780
|
+
const resetCamera = phx.frontend.useResetCamera()
|
|
781
|
+
const isFullscreen = phx.frontend.useIsFullscreenActive()
|
|
782
|
+
const setFullscreen = phx.frontend.useSetFullscreen()
|
|
783
|
+
const analytics = phx.frontend.useAnalytics()
|
|
784
|
+
|
|
785
|
+
// The same event the built-in fullscreen button records, for entering and for leaving
|
|
786
|
+
const toggleFullscreen = () => {
|
|
787
|
+
analytics.push(FrontendAnalyticsFactory.FullScreenModeToggled())
|
|
788
|
+
setFullscreen(!isFullscreen)
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
return (
|
|
792
|
+
<ThemeProvider theme={deckTheme}>
|
|
793
|
+
<Box
|
|
794
|
+
component="div"
|
|
795
|
+
role="toolbar"
|
|
796
|
+
aria-label="View"
|
|
797
|
+
sx={{
|
|
798
|
+
...GLASS_SX,
|
|
799
|
+
display: 'flex',
|
|
800
|
+
alignItems: 'center',
|
|
801
|
+
gap: 0.5,
|
|
802
|
+
padding: 0.5,
|
|
803
|
+
color: CORVID.ink,
|
|
804
|
+
[`& .${toggleButtonGroupClasses.root}`]: {
|
|
805
|
+
gap: 0.5,
|
|
806
|
+
padding: 0,
|
|
807
|
+
bgcolor: 'transparent',
|
|
808
|
+
boxShadow: 'none',
|
|
809
|
+
borderRadius: 0,
|
|
810
|
+
},
|
|
811
|
+
[`& .${toggleButtonClasses.root}`]: {
|
|
812
|
+
gap: 1,
|
|
813
|
+
minBlockSize: 36,
|
|
814
|
+
paddingInline: 1.5,
|
|
815
|
+
border: 0,
|
|
816
|
+
borderRadius: 0,
|
|
817
|
+
color: CORVID.muted,
|
|
818
|
+
'& .MuiTypography-root': {
|
|
819
|
+
fontFamily: CORVID.mono,
|
|
820
|
+
fontSize: 11,
|
|
821
|
+
letterSpacing: '0.14em',
|
|
822
|
+
textTransform: 'uppercase',
|
|
823
|
+
},
|
|
824
|
+
'&:hover': { color: CORVID.ink, bgcolor: 'rgba(232, 237, 242, 0.06)' },
|
|
825
|
+
},
|
|
826
|
+
[`& .${toggleButtonClasses.root}.${toggleButtonClasses.selected}`]: {
|
|
827
|
+
color: CORVID.graphite,
|
|
828
|
+
bgcolor: CORVID.lime,
|
|
829
|
+
'&:hover': { bgcolor: CORVID.lime },
|
|
830
|
+
},
|
|
831
|
+
// The phone's view button sits on the dock, not on a paper of its own
|
|
832
|
+
'& .MuiPaper-root': { bgcolor: 'transparent', boxShadow: 'none' },
|
|
833
|
+
}}
|
|
834
|
+
>
|
|
835
|
+
{placement === 'desktop' && <LayoutSlots.DesktopViewControls />}
|
|
836
|
+
{placement === 'phone' && !isFullscreen && <LayoutSlots.MobileViewControls />}
|
|
837
|
+
{(placement === 'desktop' || !isFullscreen) && (
|
|
838
|
+
<Divider orientation="vertical" flexItem sx={{ borderColor: CORVID.line, marginBlock: 0.5 }} />
|
|
839
|
+
)}
|
|
840
|
+
<DockButton label="Reset camera" icon="ArrowCounterClockwise" onClick={resetCamera} />
|
|
841
|
+
<DockButton
|
|
842
|
+
label={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}
|
|
843
|
+
icon={isFullscreen ? 'ArrowsIn' : 'ArrowsOut'}
|
|
844
|
+
onClick={toggleFullscreen}
|
|
845
|
+
/>
|
|
846
|
+
</Box>
|
|
847
|
+
</ThemeProvider>
|
|
848
|
+
)
|
|
849
|
+
}
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
## The client's spec sheet
|
|
853
|
+
|
|
854
|
+
The client opens the configuration from a page that sets the web component's `readonly` property. The mock template `mocks/viewer.yaml`, **Client viewer** in the editor's **Mocks** panel, keeps `access: owner`, sets the property with `webComponentProperties: { readonly: true }` and opens a finished `configuration`: a hexa with 340 mm arms, three-blade propellers, cyan motors, the LiDAR, retractable gear and the Arctic livery. The **Read Mode** story pins that template.
|
|
855
|
+
|
|
856
|
+
The `readonly` property sets `phx.frontend.useAccessService().accessInfo.viewMode` to `true`, as a configuration shared for viewing does, and switches the spec's filter mode to read. `TabPanel` reads it and passes `readOnly` to the fields:
|
|
857
|
+
|
|
858
|
+
```tsx
|
|
859
|
+
import { Box, Typography } from '@mui/material'
|
|
860
|
+
|
|
861
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
862
|
+
import { DroneFields } from '../_controls/DroneFields.js'
|
|
863
|
+
import { TAB_PANEL_ID } from './DroneTabs.js'
|
|
864
|
+
import { CORVID, LABEL_SX } from './tokens.js'
|
|
865
|
+
|
|
866
|
+
interface TabPanelProps {
|
|
867
|
+
readonly compact: boolean
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// The active tab's heading and controls. The heading is the tab's label from the spec; the controls are whichever
|
|
871
|
+
// the spec activates for the tab. A viewer gets the same tabs with every control as a line of the spec sheet.
|
|
872
|
+
export const TabPanel = ({ compact }: TabPanelProps) => {
|
|
873
|
+
const { tabs, activeTab } = phx.frontend.useTabNavigation()
|
|
874
|
+
const { accessInfo } = phx.frontend.useAccessService()
|
|
875
|
+
const readOnly = accessInfo.viewMode
|
|
876
|
+
const position = `${String((activeTab?.index ?? 0) + 1).padStart(2, '0')} / ${String(tabs.length).padStart(2, '0')}`
|
|
877
|
+
|
|
878
|
+
return (
|
|
879
|
+
<Box
|
|
880
|
+
component="section"
|
|
881
|
+
role="tabpanel"
|
|
882
|
+
id={TAB_PANEL_ID}
|
|
883
|
+
aria-labelledby={activeTab ? `corvid-tab-${activeTab.index}` : undefined}
|
|
884
|
+
sx={{ display: 'grid', gap: compact ? 2 : 3 }}
|
|
885
|
+
>
|
|
886
|
+
<Box component="header" sx={{ display: 'grid', gap: 0.5 }}>
|
|
887
|
+
<Typography sx={{ ...LABEL_SX, color: readOnly ? CORVID.cyan : CORVID.lime }}>
|
|
888
|
+
{position} · {readOnly ? 'SPEC SHEET' : 'CONFIGURE'}
|
|
889
|
+
</Typography>
|
|
890
|
+
<Typography
|
|
891
|
+
component="h2"
|
|
892
|
+
sx={{ fontFamily: CORVID.heading, fontWeight: 600, fontSize: compact ? 20 : 26, lineHeight: 1.15 }}
|
|
893
|
+
>
|
|
894
|
+
{activeTab?.label}
|
|
895
|
+
</Typography>
|
|
896
|
+
</Box>
|
|
897
|
+
<DroneFields readOnly={readOnly} />
|
|
898
|
+
</Box>
|
|
899
|
+
)
|
|
900
|
+
}
|
|
901
|
+
```
|
|
902
|
+
|
|
903
|
+
- **Every control becomes a line of the spec sheet.** Each field still reads its value through its hook, but renders `SpecRow`, with the chosen option, the length or the angle and a line of detail, instead of the control. The hooks do not check read mode themselves.
|
|
904
|
+
- **The tabs stay.** The spec sheet is long, so the client browses it by tab; switching tabs is navigation, not editing. This departs on purpose from [Interaction Modes → View-only mode](?path=/docs/ux-guidelines-interaction-modes--docs#view-only-mode), which hides the editing navigation.
|
|
905
|
+
- **The HUD says SPEC SHEET · VIEW ONLY**, in cyan, where the engineer sees ENGINEERING · EDIT.
|
|
906
|
+
- **The target on the payload is gone.** Its `$filter.modes: [owner]` leaves it out in read mode, whichever of the two routes set it.
|
|
907
|
+
|
|
908
|
+
[Custom Layout → Serve read mode](?path=/docs/layouts-custom-layout--docs#serve-read-mode) shows the other route, a configuration opened with `read-revision` access; the layout reads both the same way.
|
|
909
|
+
|
|
910
|
+
## The stage and the model
|
|
911
|
+
|
|
912
|
+
The spec's `canvasCustomEnvironment` names `DroneHangar`, a layout element rendered inside the scene: a dark backdrop, drei's `Grid` as a measured floor 320 mm below the drone, a landing ring, a contact shadow, and `Lightformer` panels that show only in the reflections. The scene is in millimetres.
|
|
913
|
+
|
|
914
|
+
```tsx
|
|
915
|
+
import { ContactShadows, Environment, Grid, Lightformer } from '@react-three/drei'
|
|
916
|
+
|
|
917
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
918
|
+
import { CORVID } from '../_shared/tokens.js'
|
|
919
|
+
|
|
920
|
+
// The floor lies 320 mm below the drone's centre, so the drone hovers over it on either landing gear
|
|
921
|
+
const FLOOR_Y = -320
|
|
922
|
+
|
|
923
|
+
// The stage named by the spec's `canvasCustomEnvironment`: a dark hangar with a measured floor, a landing ring and a
|
|
924
|
+
// soft shadow. Light panels exist only in the reflections, so the shell and the anodised motors have something to
|
|
925
|
+
// mirror and no image has to be downloaded. The scene is in millimetres.
|
|
926
|
+
export default phx.frontend.createCustomPhoenixLayoutElement('DroneHangar', () => {
|
|
927
|
+
return (
|
|
928
|
+
<>
|
|
929
|
+
<color attach="background" args={[CORVID.graphite]} />
|
|
930
|
+
<ambientLight intensity={0.6} />
|
|
931
|
+
<directionalLight position={[-700, 1500, 1100]} intensity={2.4} />
|
|
932
|
+
<directionalLight position={[1200, 500, 600]} intensity={0.7} />
|
|
933
|
+
{/* A cool rim light from behind outlines the drone against the dark hangar */}
|
|
934
|
+
<directionalLight position={[600, 700, -1400]} intensity={1.8} color={CORVID.cyan} />
|
|
935
|
+
|
|
936
|
+
<Environment resolution={256} frames={1}>
|
|
937
|
+
<color attach="background" args={['#262b32']} />
|
|
938
|
+
{/* A long softbox above, two tall strips at the sides and a dim fill from the front */}
|
|
939
|
+
<Lightformer form="rect" intensity={3.2} position={[0, 6, 0]} rotation-x={Math.PI / 2} scale={[10, 3, 1]} />
|
|
940
|
+
<Lightformer form="rect" intensity={2.4} position={[-6, 1.5, 3]} rotation-y={Math.PI / 2} scale={[1, 7, 1]} />
|
|
941
|
+
<Lightformer form="rect" intensity={1.6} position={[6, 1.5, -2]} rotation-y={-Math.PI / 2} scale={[1, 7, 1]} />
|
|
942
|
+
<Lightformer form="rect" intensity={1} position={[0, 0, 8]} rotation-y={Math.PI} scale={[8, 2, 1]} />
|
|
943
|
+
</Environment>
|
|
944
|
+
|
|
945
|
+
<Grid
|
|
946
|
+
position={[0, FLOOR_Y, 0]}
|
|
947
|
+
infiniteGrid
|
|
948
|
+
cellSize={50}
|
|
949
|
+
cellThickness={0.6}
|
|
950
|
+
cellColor="#262c34"
|
|
951
|
+
sectionSize={250}
|
|
952
|
+
sectionThickness={1}
|
|
953
|
+
sectionColor="#3a4652"
|
|
954
|
+
fadeDistance={4200}
|
|
955
|
+
fadeStrength={2}
|
|
956
|
+
/>
|
|
957
|
+
{/* The landing ring, drawn just above the floor so the grid does not cut through it */}
|
|
958
|
+
<mesh rotation-x={-Math.PI / 2} position={[0, FLOOR_Y + 1, 0]}>
|
|
959
|
+
<ringGeometry args={[440, 445, 128]} />
|
|
960
|
+
<meshBasicMaterial color={CORVID.lime} transparent opacity={0.5} toneMapped={false} />
|
|
961
|
+
</mesh>
|
|
962
|
+
<ContactShadows position={[0, FLOOR_Y + 2, 0]} scale={1600} far={440} blur={2.6} opacity={0.8} resolution={512} />
|
|
963
|
+
</>
|
|
964
|
+
)
|
|
965
|
+
})
|
|
966
|
+
```
|
|
967
|
+
|
|
968
|
+
The drone is `public/models/gallery/drone.glb`, generated by `scripts/models/drone.ts` in the Storybook package from three.js geometry: two carbon plates with a lofted canopy, quad and hexa arm sets, outrunner motors, two- and three-blade propellers, fixed and folded landing gear, three payloads and the light strips. Each part is a named node the spec loads with `sceneEntityRemoteObject`, and shows or hides with `visible` in `sceneEntityObjectProperties`. Three techniques carry the continuous choices:
|
|
969
|
+
|
|
970
|
+
- **A morph target for the arm length.** Every part at the arm tips, the arms, the motors, the propellers and the light pods, carries a `Reach` morph target from 280 mm to 380 mm. `morphTargetInfluences` maps the attribute onto it:
|
|
971
|
+
|
|
972
|
+
```yaml
|
|
973
|
+
components:
|
|
974
|
+
Drone:
|
|
975
|
+
sceneEntityObjectProperties:
|
|
976
|
+
armsQuad:
|
|
977
|
+
visible:
|
|
978
|
+
$eval: self.frame === 'Quad'
|
|
979
|
+
morphTargetInfluences:
|
|
980
|
+
Reach:
|
|
981
|
+
value:
|
|
982
|
+
$read: armLength
|
|
983
|
+
scaleZero: 280
|
|
984
|
+
scaleOne: 380
|
|
985
|
+
```
|
|
986
|
+
|
|
987
|
+
- **An object group for the tilt.** The camera and its lens have their node origin on the gimbal's tilt axis. The `gimbalPivot` group sits on that axis and turns by the attribute, in radians; the two objects are parented into it with `targetParentGroup`:
|
|
988
|
+
|
|
989
|
+
```yaml
|
|
990
|
+
components:
|
|
991
|
+
Drone:
|
|
992
|
+
sceneEntityObjectGroup:
|
|
993
|
+
gimbalPivot:
|
|
994
|
+
visible:
|
|
995
|
+
$eval: self.payload === 'Camera'
|
|
996
|
+
offsetY: -70
|
|
997
|
+
offsetZ: 90
|
|
998
|
+
rotationX:
|
|
999
|
+
$read: gimbalTilt
|
|
1000
|
+
sceneEntityObjectProperties:
|
|
1001
|
+
gimbalCamera:
|
|
1002
|
+
targetParentGroup: gimbalPivot
|
|
1003
|
+
gimbalLens:
|
|
1004
|
+
targetParentGroup: gimbalPivot
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
- **Look-dev in YAML.** The GLB's materials are plain colours; `sceneEntityObjectLocalMaterialOverride` makes each object's look, named by the object's instance name: the anodised motors, the canopy's livery with its clearcoat, the glowing lights and the lenses. The colours come from expressions in `src/expressions/drone.ts`, which also compute the price and the weight.
|
|
1008
|
+
|
|
1009
|
+
```yaml
|
|
1010
|
+
components:
|
|
1011
|
+
Drone:
|
|
1012
|
+
sceneEntityObjectLocalMaterialOverride:
|
|
1013
|
+
motorsQuad:
|
|
1014
|
+
materialsToOverride: [Motor]
|
|
1015
|
+
materialType: physical
|
|
1016
|
+
color:
|
|
1017
|
+
$read: $motorColor
|
|
1018
|
+
metalness: 0.95
|
|
1019
|
+
roughness: 0.3
|
|
1020
|
+
clearcoat: 0.5
|
|
1021
|
+
clearcoatRoughness: 0.2
|
|
1022
|
+
ledsQuad:
|
|
1023
|
+
materialsToOverride: [LED]
|
|
1024
|
+
color:
|
|
1025
|
+
$read: $ledColor
|
|
1026
|
+
emissive:
|
|
1027
|
+
$read: $ledColor
|
|
1028
|
+
emissiveIntensity: 2.6
|
|
1029
|
+
toneMapped: false
|
|
1030
|
+
```
|
|
1031
|
+
|
|
1032
|
+
The script and its helper live in `scripts/models/`, outside the example's `src/`, because every file under `src/` is part of this page.
|
|
1033
|
+
|
|
1034
|
+
## On a phone
|
|
1035
|
+
|
|
1036
|
+
The same layout in a frame 390 pixels wide: the scene at the top with a compact HUD, the active tab's controls in a sheet of fixed height, and the tabs as a segmented bar at the bottom. The sheet keeps its height on every tab, so the drone does not jump when the tab changes. `LayoutSlots.MobileViewControls` is one button in the dock that opens the list of views. Opened on its own from the sidebar, the **Phone** story sets Storybook's viewport to 390 × 844.
|
|
1037
|
+
|
|
1038
|
+
## Test cases
|
|
1039
|
+
|
|
1040
|
+
- The page opens on the **Airframe** tab: a quad with 300 mm arms on fixed skids, hovering over the hangar floor inside the landing ring. The HUD shows "CORVID K6", ENGINEERING · EDIT, **$13,220.00**, "AUW 3.81 kg" and "VIEW ORBIT"; the log line reads "READY".
|
|
1041
|
+
- **Hexa** shows six arms, motors and propellers, and raises the price to $15,620.00 and the weight to 4.44 kg.
|
|
1042
|
+
- Dragging the **Arm length** slider grows the arms, motors and propellers under the thumb; letting go commits the length. The End key sets 380 mm with "Motor to motor 760 mm", and on the hexa the price becomes $16,100.00 and the weight 4.56 kg.
|
|
1043
|
+
- **Retractable gear** folds the legs flat under the belly and adds $650 and 0.13 kg; switching it off brings the skids back.
|
|
1044
|
+
- The **Propulsion** tab shows the propellers and the motors' anodising. **Three-blade** adds a blade to every propeller and $60 a motor; **Ember** turns the motors orange and adds $180.
|
|
1045
|
+
- Outside the **Payload** tab a target labelled "PAYLOAD ›" sits on the payload mount. Clicking it opens the **Payload** tab, and the target disappears.
|
|
1046
|
+
- On the **Payload** tab, **Gimbal tilt** turns the camera: the End key points it straight down at 90° ("Straight down, for mapping"), the Home key levels it at 0°, and the dial beside the value follows.
|
|
1047
|
+
- On a quad the **Delivery winch** tile is hatched, and "The delivery winch needs the hexa frame to lift its load." is shown below the payloads. Clicking it keeps the camera, and the log line shows "REJECTED" with the same sentence.
|
|
1048
|
+
- On a hexa the winch is accepted and hangs a line and a hook under the nose; the **Quad** tile on the Airframe tab is then hatched with the same sentence.
|
|
1049
|
+
- **LiDAR** hangs the scanner under the nose, removes **Gimbal tilt**, adds $7,600 and shows "NOTE LiDAR missions need RTK positioning, so a GNSS base station ships with the drone." in the log line.
|
|
1050
|
+
- On the **Livery** tab, **Arctic** turns the canopy white with cyan lights, and **Signal** orange with white lights, for $250 more.
|
|
1051
|
+
- **Top** looks down on the drone, **Payload** shows the payload close up from below the nose, and **Orbit** returns; the HUD names the view. **Reset camera** undoes a drag or a zoom.
|
|
1052
|
+
- **Fullscreen** leaves the drone and the dock only; **Exit fullscreen** brings back the rail, the panel, the HUD and the log line.
|
|
1053
|
+
- With a tab focused, the arrow keys move to the next or previous tab and open it.
|
|
1054
|
+
- The **Read Mode** story opens a finished drone with SPEC SHEET · VIEW ONLY in the HUD, **$24,650.00** and "AUW 4.92 kg": a hexa with 340 mm arms, retractable gear, three-blade propellers, cyan motors, the LiDAR and the Arctic livery. Every tab shows spec-sheet lines, with no tiles, sliders or switch, and no target on the drone.
|
|
1055
|
+
- In the **Phone** story the tabs are a bar at the bottom and the controls a sheet above it; the same changes, the target, the rejected winch in the log line and the liveries work there. The view button in the dock opens the list of views, and fullscreen leaves the reset and the exit.
|
|
1056
|
+
- The browser console shows no errors.
|
|
1057
|
+
|
|
1058
|
+
## What this layout leaves out
|
|
1059
|
+
|
|
1060
|
+
- **Finalizing.** The engineer's work is the saved configuration; the drone is not ordered from here. A layout adds an order with `phx.frontend.useFinalizeActions()`, as [Kestrel Kettle](?path=/docs/layouts-gallery-kestrel-kettle--docs#add-to-cart) does.
|
|
1061
|
+
- **Undo and redo, sharing and measurements.** The dock has the view actions only. A layout adds undo and redo with `phx.frontend.useUndoRedo()`, see [Configurator Interface → Hooks for a Project Layout](?path=/docs/phoenix-configurator-interface--docs#hooks-for-a-project-layout).
|
|
1062
|
+
- **Editing a copy.** The client cannot clone the spec sheet to change it; the local editor reopens a local clone in read mode, so the example does not offer it.
|
|
1063
|
+
- **The built-in arrangement.** Header, sidebar, tabs, snackbars and the preview start screen are not mounted; [Layout Slots](?path=/docs/phoenix-layout-slots--docs) lists every slot a project layout can mount.
|
|
1064
|
+
|
|
1065
|
+
## Related pages
|
|
1066
|
+
|
|
1067
|
+
- [Custom Layout](?path=/docs/layouts-custom-layout--docs) explains a project layout step by step and shows read mode by a shared link.
|
|
1068
|
+
- [Kestrel Kettle](?path=/docs/layouts-gallery-kestrel-kettle--docs) is a single-page product with a start screen and the built-in snackbars.
|
|
1069
|
+
- [Configurator Interface → Hooks for a Project Layout](?path=/docs/phoenix-configurator-interface--docs#hooks-for-a-project-layout) lists every hook used here, with the steps and tabs paragraph.
|
|
1070
|
+
- [Layout Slots](?path=/docs/phoenix-layout-slots--docs) describes `DesktopViewControls`, `MobileViewControls` and `SharedCanvas`.
|
|
1071
|
+
- [Sidebar Tabs](?path=/docs/layouts-sidebar-tabs--docs) shows the built-in tabs for the same `sidebarTabs` component.
|
|
1072
|
+
|
|
1073
|
+
## Complete Implementation
|
|
1074
|
+
|
|
1075
|
+
{/* AUTO-GENERATED SECTION: This section is automatically updated by 'phoenix story build' */}
|
|
1076
|
+
|
|
1077
|
+
<details>
|
|
1078
|
+
<summary>Click to view the complete implementation files</summary>
|
|
1079
|
+
|
|
1080
|
+
### ./spec/domain.yaml
|
|
1081
|
+
|
|
1082
|
+
```yaml
|
|
1083
|
+
model:
|
|
1084
|
+
latest:
|
|
1085
|
+
entityTypes:
|
|
1086
|
+
- Drone
|
|
1087
|
+
aliases:
|
|
1088
|
+
RootEntity:
|
|
1089
|
+
- Drone
|
|
1090
|
+
commonTypes:
|
|
1091
|
+
Frame:
|
|
1092
|
+
enum:
|
|
1093
|
+
- Quad
|
|
1094
|
+
- Hexa
|
|
1095
|
+
Props:
|
|
1096
|
+
enum:
|
|
1097
|
+
- TwoBlade
|
|
1098
|
+
- ThreeBlade
|
|
1099
|
+
MotorColor:
|
|
1100
|
+
enum:
|
|
1101
|
+
- Titanium
|
|
1102
|
+
- Lime
|
|
1103
|
+
- Cyan
|
|
1104
|
+
- Ember
|
|
1105
|
+
Payload:
|
|
1106
|
+
enum:
|
|
1107
|
+
- Camera
|
|
1108
|
+
- Lidar
|
|
1109
|
+
- Winch
|
|
1110
|
+
Livery:
|
|
1111
|
+
enum:
|
|
1112
|
+
- Graphite
|
|
1113
|
+
- Arctic
|
|
1114
|
+
- Signal
|
|
1115
|
+
shapes:
|
|
1116
|
+
Drone:
|
|
1117
|
+
frame:
|
|
1118
|
+
$ref: '#/model/latest/commonTypes/Frame'
|
|
1119
|
+
armLength:
|
|
1120
|
+
type: number
|
|
1121
|
+
props:
|
|
1122
|
+
$ref: '#/model/latest/commonTypes/Props'
|
|
1123
|
+
motorColor:
|
|
1124
|
+
$ref: '#/model/latest/commonTypes/MotorColor'
|
|
1125
|
+
payload:
|
|
1126
|
+
$ref: '#/model/latest/commonTypes/Payload'
|
|
1127
|
+
gimbalTilt:
|
|
1128
|
+
type: number
|
|
1129
|
+
retractableGear:
|
|
1130
|
+
type: boolean
|
|
1131
|
+
livery:
|
|
1132
|
+
$ref: '#/model/latest/commonTypes/Livery'
|
|
1133
|
+
|
|
1134
|
+
presets:
|
|
1135
|
+
- rootEntityType: Drone
|
|
1136
|
+
initialDomainEntities:
|
|
1137
|
+
- id: Drone-1
|
|
1138
|
+
type: Drone
|
|
1139
|
+
frame: Quad
|
|
1140
|
+
armLength: 300
|
|
1141
|
+
props: TwoBlade
|
|
1142
|
+
motorColor: Titanium
|
|
1143
|
+
payload: Camera
|
|
1144
|
+
gimbalTilt: 0.5235987756
|
|
1145
|
+
retractableGear: false
|
|
1146
|
+
livery: Graphite
|
|
1147
|
+
|
|
1148
|
+
# One navigation state per tab; each control is active only in its tab's state
|
|
1149
|
+
navigationStates:
|
|
1150
|
+
- airframe
|
|
1151
|
+
- propulsion
|
|
1152
|
+
- payload
|
|
1153
|
+
- livery
|
|
1154
|
+
|
|
1155
|
+
viewStates:
|
|
1156
|
+
- orbit
|
|
1157
|
+
- top
|
|
1158
|
+
- payloadView
|
|
1159
|
+
|
|
1160
|
+
expressions:
|
|
1161
|
+
Drone:
|
|
1162
|
+
$price:
|
|
1163
|
+
type: number
|
|
1164
|
+
$weightLabel:
|
|
1165
|
+
type: string
|
|
1166
|
+
$shellColor:
|
|
1167
|
+
type: string
|
|
1168
|
+
$shellClearcoat:
|
|
1169
|
+
type: number
|
|
1170
|
+
$ledColor:
|
|
1171
|
+
type: string
|
|
1172
|
+
$motorColor:
|
|
1173
|
+
type: string
|
|
1174
|
+
|
|
1175
|
+
invariants:
|
|
1176
|
+
Drone:
|
|
1177
|
+
winchNeedsHexa:
|
|
1178
|
+
type: winchNeedsHexa
|
|
1179
|
+
severity: fixable
|
|
1180
|
+
message: The delivery winch needs the hexa frame to lift its load.
|
|
1181
|
+
|
|
1182
|
+
transformers:
|
|
1183
|
+
- name: lidar-base-station
|
|
1184
|
+
|
|
1185
|
+
components:
|
|
1186
|
+
Drone:
|
|
1187
|
+
detail:
|
|
1188
|
+
main:
|
|
1189
|
+
title: Corvid K6
|
|
1190
|
+
subtitle:
|
|
1191
|
+
$read: $weightLabel
|
|
1192
|
+
description: A survey drone for mapping and inspection. Pick the airframe, the propulsion, the payload and the livery; the engineer's configuration becomes the client's spec sheet.
|
|
1193
|
+
price:
|
|
1194
|
+
main:
|
|
1195
|
+
price:
|
|
1196
|
+
$read: $price
|
|
1197
|
+
|
|
1198
|
+
sidebarTabs:
|
|
1199
|
+
main:
|
|
1200
|
+
tabs:
|
|
1201
|
+
- label: Airframe
|
|
1202
|
+
icon: Cube
|
|
1203
|
+
navigationStates: [airframe]
|
|
1204
|
+
- label: Propulsion
|
|
1205
|
+
icon: ArrowClockwise
|
|
1206
|
+
navigationStates: [propulsion]
|
|
1207
|
+
- label: Payload
|
|
1208
|
+
icon: Scan
|
|
1209
|
+
navigationStates: [payload]
|
|
1210
|
+
- label: Livery
|
|
1211
|
+
icon: PaletteBold
|
|
1212
|
+
navigationStates: [livery]
|
|
1213
|
+
|
|
1214
|
+
attributeSelection:
|
|
1215
|
+
frame:
|
|
1216
|
+
value:
|
|
1217
|
+
$bind: frame
|
|
1218
|
+
options:
|
|
1219
|
+
- label: Quad
|
|
1220
|
+
description: Four motors, the lightest and longest-flying
|
|
1221
|
+
value: Quad
|
|
1222
|
+
- label: Hexa
|
|
1223
|
+
description: Six motors; lands safely with one motor out
|
|
1224
|
+
value: Hexa
|
|
1225
|
+
$filter:
|
|
1226
|
+
navigationStates: [airframe]
|
|
1227
|
+
props:
|
|
1228
|
+
value:
|
|
1229
|
+
$bind: props
|
|
1230
|
+
options:
|
|
1231
|
+
- label: Two-blade
|
|
1232
|
+
description: Efficient in cruise, for the longest flights
|
|
1233
|
+
value: TwoBlade
|
|
1234
|
+
- label: Three-blade
|
|
1235
|
+
description: More thrust and steadier in gusts, a little shorter in the air
|
|
1236
|
+
value: ThreeBlade
|
|
1237
|
+
$filter:
|
|
1238
|
+
navigationStates: [propulsion]
|
|
1239
|
+
motorColor:
|
|
1240
|
+
value:
|
|
1241
|
+
$bind: motorColor
|
|
1242
|
+
options:
|
|
1243
|
+
- label: Titanium
|
|
1244
|
+
description: Bare anodised aluminium
|
|
1245
|
+
value: Titanium
|
|
1246
|
+
- label: Lime
|
|
1247
|
+
description: Signal lime anodising
|
|
1248
|
+
value: Lime
|
|
1249
|
+
- label: Cyan
|
|
1250
|
+
description: Glacier cyan anodising
|
|
1251
|
+
value: Cyan
|
|
1252
|
+
- label: Ember
|
|
1253
|
+
description: Ember orange anodising
|
|
1254
|
+
value: Ember
|
|
1255
|
+
$filter:
|
|
1256
|
+
navigationStates: [propulsion]
|
|
1257
|
+
payload:
|
|
1258
|
+
value:
|
|
1259
|
+
$bind: payload
|
|
1260
|
+
options:
|
|
1261
|
+
- label: Gimbal camera
|
|
1262
|
+
description: 45 MP stabilised camera on a tilting gimbal
|
|
1263
|
+
value: Camera
|
|
1264
|
+
- label: LiDAR
|
|
1265
|
+
description: 240,000 points a second, 360° around
|
|
1266
|
+
value: Lidar
|
|
1267
|
+
- label: Delivery winch
|
|
1268
|
+
description: Lowers up to 3 kg on a 12 m line
|
|
1269
|
+
value: Winch
|
|
1270
|
+
$filter:
|
|
1271
|
+
navigationStates: [payload]
|
|
1272
|
+
livery:
|
|
1273
|
+
value:
|
|
1274
|
+
$bind: livery
|
|
1275
|
+
options:
|
|
1276
|
+
- label: Graphite
|
|
1277
|
+
description: Satin graphite with lime lights
|
|
1278
|
+
value: Graphite
|
|
1279
|
+
- label: Arctic
|
|
1280
|
+
description: Gloss white with cyan lights
|
|
1281
|
+
value: Arctic
|
|
1282
|
+
- label: Signal
|
|
1283
|
+
description: High-visibility orange with white lights
|
|
1284
|
+
value: Signal
|
|
1285
|
+
$filter:
|
|
1286
|
+
navigationStates: [livery]
|
|
1287
|
+
distanceDimension:
|
|
1288
|
+
armLength:
|
|
1289
|
+
value:
|
|
1290
|
+
$bind: armLength
|
|
1291
|
+
min: 280
|
|
1292
|
+
max: 380
|
|
1293
|
+
$filter:
|
|
1294
|
+
navigationStates: [airframe]
|
|
1295
|
+
angleDimension:
|
|
1296
|
+
gimbalTilt:
|
|
1297
|
+
value:
|
|
1298
|
+
$bind: gimbalTilt
|
|
1299
|
+
min: 0
|
|
1300
|
+
max: 1.5707963268
|
|
1301
|
+
unit: degree
|
|
1302
|
+
decimalPlaces: 0
|
|
1303
|
+
# Only the camera tilts
|
|
1304
|
+
$filter:
|
|
1305
|
+
navigationStates: [payload]
|
|
1306
|
+
active:
|
|
1307
|
+
$eval: self.payload === 'Camera'
|
|
1308
|
+
attributeSwitch:
|
|
1309
|
+
retractableGear:
|
|
1310
|
+
value:
|
|
1311
|
+
$bind: retractableGear
|
|
1312
|
+
$filter:
|
|
1313
|
+
navigationStates: [airframe]
|
|
1314
|
+
|
|
1315
|
+
viewControls:
|
|
1316
|
+
main:
|
|
1317
|
+
items:
|
|
1318
|
+
- label: Orbit
|
|
1319
|
+
value: orbit
|
|
1320
|
+
icon: CameraRotate
|
|
1321
|
+
- label: Top
|
|
1322
|
+
value: top
|
|
1323
|
+
icon: GridFour
|
|
1324
|
+
- label: Payload
|
|
1325
|
+
value: payloadView
|
|
1326
|
+
icon: Scan
|
|
1327
|
+
|
|
1328
|
+
# Pinned to the payload mount; an engineer outside the Payload tab clicks it to get there. A viewer never sees it,
|
|
1329
|
+
# and fullscreen leaves it out
|
|
1330
|
+
sceneEntityObjectHtmlHotspot:
|
|
1331
|
+
payloadMount:
|
|
1332
|
+
target: payloadMount
|
|
1333
|
+
name: PayloadHotspot
|
|
1334
|
+
$filter:
|
|
1335
|
+
modes: [owner]
|
|
1336
|
+
navigationStates: [airframe, propulsion, livery]
|
|
1337
|
+
fullscreen: false
|
|
1338
|
+
|
|
1339
|
+
canvasConfig:
|
|
1340
|
+
main:
|
|
1341
|
+
highlightingDisabled: true
|
|
1342
|
+
canvasCustomEnvironment:
|
|
1343
|
+
main:
|
|
1344
|
+
name: DroneHangar
|
|
1345
|
+
fitRootPerspectiveCamera:
|
|
1346
|
+
orbit:
|
|
1347
|
+
priority: 2
|
|
1348
|
+
fov: 30
|
|
1349
|
+
near: 10
|
|
1350
|
+
far: 20000
|
|
1351
|
+
minDistance: 500
|
|
1352
|
+
maxDistance: 5000
|
|
1353
|
+
azimuthAngle: 0.62
|
|
1354
|
+
polarAngle: 1.1
|
|
1355
|
+
minPolarAngle: 0.05
|
|
1356
|
+
maxPolarAngle: 1.8
|
|
1357
|
+
fitToBox: true
|
|
1358
|
+
fitMeshChanges: false
|
|
1359
|
+
$filter:
|
|
1360
|
+
viewStates: [orbit]
|
|
1361
|
+
top:
|
|
1362
|
+
priority: 2
|
|
1363
|
+
fov: 30
|
|
1364
|
+
near: 10
|
|
1365
|
+
far: 20000
|
|
1366
|
+
minDistance: 500
|
|
1367
|
+
maxDistance: 5000
|
|
1368
|
+
azimuthAngle: 0
|
|
1369
|
+
polarAngle: 0.001
|
|
1370
|
+
minPolarAngle: 0
|
|
1371
|
+
maxPolarAngle: 0.6
|
|
1372
|
+
fitToBox: true
|
|
1373
|
+
fitMeshChanges: false
|
|
1374
|
+
$filter:
|
|
1375
|
+
viewStates: [top]
|
|
1376
|
+
staticPerspectiveCamera:
|
|
1377
|
+
payloadView:
|
|
1378
|
+
priority: 2
|
|
1379
|
+
fov: 30
|
|
1380
|
+
near: 10
|
|
1381
|
+
far: 20000
|
|
1382
|
+
distance: 1050
|
|
1383
|
+
minDistance: 400
|
|
1384
|
+
maxDistance: 2400
|
|
1385
|
+
azimuthAngle: 0.5
|
|
1386
|
+
polarAngle: 1.7
|
|
1387
|
+
minPolarAngle: 1.2
|
|
1388
|
+
maxPolarAngle: 2.4
|
|
1389
|
+
lookAtX: 0
|
|
1390
|
+
lookAtY: -60
|
|
1391
|
+
lookAtZ: 90
|
|
1392
|
+
$filter:
|
|
1393
|
+
viewStates: [payloadView]
|
|
1394
|
+
|
|
1395
|
+
sceneEntityRemoteObject:
|
|
1396
|
+
bodyPlates:
|
|
1397
|
+
fileUrl: /models/gallery/drone.glb
|
|
1398
|
+
objectName: BodyPlates
|
|
1399
|
+
dimensionUnit: m
|
|
1400
|
+
canopy:
|
|
1401
|
+
fileUrl: /models/gallery/drone.glb
|
|
1402
|
+
objectName: Canopy
|
|
1403
|
+
dimensionUnit: m
|
|
1404
|
+
noseSensors:
|
|
1405
|
+
fileUrl: /models/gallery/drone.glb
|
|
1406
|
+
objectName: NoseSensors
|
|
1407
|
+
dimensionUnit: m
|
|
1408
|
+
ledStrip:
|
|
1409
|
+
fileUrl: /models/gallery/drone.glb
|
|
1410
|
+
objectName: LedStrip
|
|
1411
|
+
dimensionUnit: m
|
|
1412
|
+
gps:
|
|
1413
|
+
fileUrl: /models/gallery/drone.glb
|
|
1414
|
+
objectName: Gps
|
|
1415
|
+
dimensionUnit: m
|
|
1416
|
+
armsQuad:
|
|
1417
|
+
fileUrl: /models/gallery/drone.glb
|
|
1418
|
+
objectName: ArmsQuad
|
|
1419
|
+
dimensionUnit: m
|
|
1420
|
+
armsHexa:
|
|
1421
|
+
fileUrl: /models/gallery/drone.glb
|
|
1422
|
+
objectName: ArmsHexa
|
|
1423
|
+
dimensionUnit: m
|
|
1424
|
+
motorsQuad:
|
|
1425
|
+
fileUrl: /models/gallery/drone.glb
|
|
1426
|
+
objectName: MotorsQuad
|
|
1427
|
+
dimensionUnit: m
|
|
1428
|
+
motorsHexa:
|
|
1429
|
+
fileUrl: /models/gallery/drone.glb
|
|
1430
|
+
objectName: MotorsHexa
|
|
1431
|
+
dimensionUnit: m
|
|
1432
|
+
ledsQuad:
|
|
1433
|
+
fileUrl: /models/gallery/drone.glb
|
|
1434
|
+
objectName: LedsQuad
|
|
1435
|
+
dimensionUnit: m
|
|
1436
|
+
ledsHexa:
|
|
1437
|
+
fileUrl: /models/gallery/drone.glb
|
|
1438
|
+
objectName: LedsHexa
|
|
1439
|
+
dimensionUnit: m
|
|
1440
|
+
propsQuadTwoBlade:
|
|
1441
|
+
fileUrl: /models/gallery/drone.glb
|
|
1442
|
+
objectName: PropsQuadTwoBlade
|
|
1443
|
+
dimensionUnit: m
|
|
1444
|
+
propsQuadThreeBlade:
|
|
1445
|
+
fileUrl: /models/gallery/drone.glb
|
|
1446
|
+
objectName: PropsQuadThreeBlade
|
|
1447
|
+
dimensionUnit: m
|
|
1448
|
+
propsHexaTwoBlade:
|
|
1449
|
+
fileUrl: /models/gallery/drone.glb
|
|
1450
|
+
objectName: PropsHexaTwoBlade
|
|
1451
|
+
dimensionUnit: m
|
|
1452
|
+
propsHexaThreeBlade:
|
|
1453
|
+
fileUrl: /models/gallery/drone.glb
|
|
1454
|
+
objectName: PropsHexaThreeBlade
|
|
1455
|
+
dimensionUnit: m
|
|
1456
|
+
gearFixed:
|
|
1457
|
+
fileUrl: /models/gallery/drone.glb
|
|
1458
|
+
objectName: GearFixed
|
|
1459
|
+
dimensionUnit: m
|
|
1460
|
+
gearFolded:
|
|
1461
|
+
fileUrl: /models/gallery/drone.glb
|
|
1462
|
+
objectName: GearFolded
|
|
1463
|
+
dimensionUnit: m
|
|
1464
|
+
payloadMount:
|
|
1465
|
+
fileUrl: /models/gallery/drone.glb
|
|
1466
|
+
objectName: PayloadMount
|
|
1467
|
+
dimensionUnit: m
|
|
1468
|
+
gimbalYoke:
|
|
1469
|
+
fileUrl: /models/gallery/drone.glb
|
|
1470
|
+
objectName: GimbalYoke
|
|
1471
|
+
dimensionUnit: m
|
|
1472
|
+
gimbalCamera:
|
|
1473
|
+
fileUrl: /models/gallery/drone.glb
|
|
1474
|
+
objectName: GimbalCamera
|
|
1475
|
+
dimensionUnit: m
|
|
1476
|
+
gimbalLens:
|
|
1477
|
+
fileUrl: /models/gallery/drone.glb
|
|
1478
|
+
objectName: GimbalLens
|
|
1479
|
+
dimensionUnit: m
|
|
1480
|
+
lidarPuck:
|
|
1481
|
+
fileUrl: /models/gallery/drone.glb
|
|
1482
|
+
objectName: LidarPuck
|
|
1483
|
+
dimensionUnit: m
|
|
1484
|
+
lidarWindow:
|
|
1485
|
+
fileUrl: /models/gallery/drone.glb
|
|
1486
|
+
objectName: LidarWindow
|
|
1487
|
+
dimensionUnit: m
|
|
1488
|
+
winch:
|
|
1489
|
+
fileUrl: /models/gallery/drone.glb
|
|
1490
|
+
objectName: Winch
|
|
1491
|
+
dimensionUnit: m
|
|
1492
|
+
|
|
1493
|
+
# The camera turns about the gimbal's tilt axis, 70 mm below the belly and 90 mm forward of the centre
|
|
1494
|
+
sceneEntityObjectGroup:
|
|
1495
|
+
gimbalPivot:
|
|
1496
|
+
visible:
|
|
1497
|
+
$eval: self.payload === 'Camera'
|
|
1498
|
+
offsetY: -70
|
|
1499
|
+
offsetZ: 90
|
|
1500
|
+
rotationX:
|
|
1501
|
+
$read: gimbalTilt
|
|
1502
|
+
|
|
1503
|
+
# The arm length morphs every part at the arm tips: 280 mm is the model's shape, 380 mm its `Reach` target
|
|
1504
|
+
sceneEntityObjectProperties:
|
|
1505
|
+
armsQuad:
|
|
1506
|
+
visible:
|
|
1507
|
+
$eval: self.frame === 'Quad'
|
|
1508
|
+
morphTargetInfluences:
|
|
1509
|
+
Reach:
|
|
1510
|
+
value:
|
|
1511
|
+
$read: armLength
|
|
1512
|
+
scaleZero: 280
|
|
1513
|
+
scaleOne: 380
|
|
1514
|
+
armsHexa:
|
|
1515
|
+
visible:
|
|
1516
|
+
$eval: self.frame === 'Hexa'
|
|
1517
|
+
morphTargetInfluences:
|
|
1518
|
+
Reach:
|
|
1519
|
+
value:
|
|
1520
|
+
$read: armLength
|
|
1521
|
+
scaleZero: 280
|
|
1522
|
+
scaleOne: 380
|
|
1523
|
+
motorsQuad:
|
|
1524
|
+
visible:
|
|
1525
|
+
$eval: self.frame === 'Quad'
|
|
1526
|
+
morphTargetInfluences:
|
|
1527
|
+
Reach:
|
|
1528
|
+
value:
|
|
1529
|
+
$read: armLength
|
|
1530
|
+
scaleZero: 280
|
|
1531
|
+
scaleOne: 380
|
|
1532
|
+
motorsHexa:
|
|
1533
|
+
visible:
|
|
1534
|
+
$eval: self.frame === 'Hexa'
|
|
1535
|
+
morphTargetInfluences:
|
|
1536
|
+
Reach:
|
|
1537
|
+
value:
|
|
1538
|
+
$read: armLength
|
|
1539
|
+
scaleZero: 280
|
|
1540
|
+
scaleOne: 380
|
|
1541
|
+
ledsQuad:
|
|
1542
|
+
visible:
|
|
1543
|
+
$eval: self.frame === 'Quad'
|
|
1544
|
+
morphTargetInfluences:
|
|
1545
|
+
Reach:
|
|
1546
|
+
value:
|
|
1547
|
+
$read: armLength
|
|
1548
|
+
scaleZero: 280
|
|
1549
|
+
scaleOne: 380
|
|
1550
|
+
ledsHexa:
|
|
1551
|
+
visible:
|
|
1552
|
+
$eval: self.frame === 'Hexa'
|
|
1553
|
+
morphTargetInfluences:
|
|
1554
|
+
Reach:
|
|
1555
|
+
value:
|
|
1556
|
+
$read: armLength
|
|
1557
|
+
scaleZero: 280
|
|
1558
|
+
scaleOne: 380
|
|
1559
|
+
propsQuadTwoBlade:
|
|
1560
|
+
visible:
|
|
1561
|
+
$eval: self.frame === 'Quad' && self.props === 'TwoBlade'
|
|
1562
|
+
morphTargetInfluences:
|
|
1563
|
+
Reach:
|
|
1564
|
+
value:
|
|
1565
|
+
$read: armLength
|
|
1566
|
+
scaleZero: 280
|
|
1567
|
+
scaleOne: 380
|
|
1568
|
+
propsQuadThreeBlade:
|
|
1569
|
+
visible:
|
|
1570
|
+
$eval: self.frame === 'Quad' && self.props === 'ThreeBlade'
|
|
1571
|
+
morphTargetInfluences:
|
|
1572
|
+
Reach:
|
|
1573
|
+
value:
|
|
1574
|
+
$read: armLength
|
|
1575
|
+
scaleZero: 280
|
|
1576
|
+
scaleOne: 380
|
|
1577
|
+
propsHexaTwoBlade:
|
|
1578
|
+
visible:
|
|
1579
|
+
$eval: self.frame === 'Hexa' && self.props === 'TwoBlade'
|
|
1580
|
+
morphTargetInfluences:
|
|
1581
|
+
Reach:
|
|
1582
|
+
value:
|
|
1583
|
+
$read: armLength
|
|
1584
|
+
scaleZero: 280
|
|
1585
|
+
scaleOne: 380
|
|
1586
|
+
propsHexaThreeBlade:
|
|
1587
|
+
visible:
|
|
1588
|
+
$eval: self.frame === 'Hexa' && self.props === 'ThreeBlade'
|
|
1589
|
+
morphTargetInfluences:
|
|
1590
|
+
Reach:
|
|
1591
|
+
value:
|
|
1592
|
+
$read: armLength
|
|
1593
|
+
scaleZero: 280
|
|
1594
|
+
scaleOne: 380
|
|
1595
|
+
gearFixed:
|
|
1596
|
+
visible:
|
|
1597
|
+
$eval: '!self.retractableGear'
|
|
1598
|
+
gearFolded:
|
|
1599
|
+
visible:
|
|
1600
|
+
$read: retractableGear
|
|
1601
|
+
payloadMount:
|
|
1602
|
+
offsetZ: 90
|
|
1603
|
+
gimbalYoke:
|
|
1604
|
+
visible:
|
|
1605
|
+
$eval: self.payload === 'Camera'
|
|
1606
|
+
gimbalCamera:
|
|
1607
|
+
targetParentGroup: gimbalPivot
|
|
1608
|
+
gimbalLens:
|
|
1609
|
+
targetParentGroup: gimbalPivot
|
|
1610
|
+
lidarPuck:
|
|
1611
|
+
visible:
|
|
1612
|
+
$eval: self.payload === 'Lidar'
|
|
1613
|
+
lidarWindow:
|
|
1614
|
+
visible:
|
|
1615
|
+
$eval: self.payload === 'Lidar'
|
|
1616
|
+
winch:
|
|
1617
|
+
visible:
|
|
1618
|
+
$eval: self.payload === 'Winch'
|
|
1619
|
+
|
|
1620
|
+
# Look-dev: each override recolours the material of one object, named by its instance name
|
|
1621
|
+
sceneEntityObjectLocalMaterialOverride:
|
|
1622
|
+
canopy:
|
|
1623
|
+
materialsToOverride: [Shell]
|
|
1624
|
+
materialType: physical
|
|
1625
|
+
color:
|
|
1626
|
+
$read: $shellColor
|
|
1627
|
+
metalness: 0.15
|
|
1628
|
+
roughness: 0.38
|
|
1629
|
+
clearcoat:
|
|
1630
|
+
$read: $shellClearcoat
|
|
1631
|
+
clearcoatRoughness: 0.12
|
|
1632
|
+
motorsQuad:
|
|
1633
|
+
materialsToOverride: [Motor]
|
|
1634
|
+
materialType: physical
|
|
1635
|
+
color:
|
|
1636
|
+
$read: $motorColor
|
|
1637
|
+
metalness: 0.95
|
|
1638
|
+
roughness: 0.3
|
|
1639
|
+
clearcoat: 0.5
|
|
1640
|
+
clearcoatRoughness: 0.2
|
|
1641
|
+
motorsHexa:
|
|
1642
|
+
materialsToOverride: [Motor]
|
|
1643
|
+
materialType: physical
|
|
1644
|
+
color:
|
|
1645
|
+
$read: $motorColor
|
|
1646
|
+
metalness: 0.95
|
|
1647
|
+
roughness: 0.3
|
|
1648
|
+
clearcoat: 0.5
|
|
1649
|
+
clearcoatRoughness: 0.2
|
|
1650
|
+
ledStrip:
|
|
1651
|
+
materialsToOverride: [LED]
|
|
1652
|
+
color:
|
|
1653
|
+
$read: $ledColor
|
|
1654
|
+
emissive:
|
|
1655
|
+
$read: $ledColor
|
|
1656
|
+
emissiveIntensity: 2.2
|
|
1657
|
+
toneMapped: false
|
|
1658
|
+
ledsQuad:
|
|
1659
|
+
materialsToOverride: [LED]
|
|
1660
|
+
color:
|
|
1661
|
+
$read: $ledColor
|
|
1662
|
+
emissive:
|
|
1663
|
+
$read: $ledColor
|
|
1664
|
+
emissiveIntensity: 2.6
|
|
1665
|
+
toneMapped: false
|
|
1666
|
+
ledsHexa:
|
|
1667
|
+
materialsToOverride: [LED]
|
|
1668
|
+
color:
|
|
1669
|
+
$read: $ledColor
|
|
1670
|
+
emissive:
|
|
1671
|
+
$read: $ledColor
|
|
1672
|
+
emissiveIntensity: 2.6
|
|
1673
|
+
toneMapped: false
|
|
1674
|
+
gimbalLens:
|
|
1675
|
+
materialsToOverride: [Lens]
|
|
1676
|
+
materialType: physical
|
|
1677
|
+
color: '#05070a'
|
|
1678
|
+
metalness: 0.2
|
|
1679
|
+
roughness: 0.04
|
|
1680
|
+
clearcoat: 1
|
|
1681
|
+
clearcoatRoughness: 0.02
|
|
1682
|
+
iridescence: 0.6
|
|
1683
|
+
iridescenceIor: 1.6
|
|
1684
|
+
lidarWindow:
|
|
1685
|
+
materialsToOverride: [Lens]
|
|
1686
|
+
materialType: physical
|
|
1687
|
+
color: '#0a0f14'
|
|
1688
|
+
metalness: 0.3
|
|
1689
|
+
roughness: 0.06
|
|
1690
|
+
clearcoat: 1
|
|
1691
|
+
clearcoatRoughness: 0.03
|
|
1692
|
+
noseSensors:
|
|
1693
|
+
materialsToOverride: [Lens]
|
|
1694
|
+
materialType: physical
|
|
1695
|
+
color: '#05070a'
|
|
1696
|
+
metalness: 0.2
|
|
1697
|
+
roughness: 0.04
|
|
1698
|
+
clearcoat: 1
|
|
1699
|
+
clearcoatRoughness: 0.02
|
|
1700
|
+
```
|
|
1701
|
+
|
|
1702
|
+
### ./src/\_controls/ArmLengthField.tsx
|
|
1703
|
+
|
|
1704
|
+
```tsx
|
|
1705
|
+
import { distanceDimensionComponent } from '@salsita-ai/sdk/domain'
|
|
1706
|
+
|
|
1707
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
1708
|
+
import { HudSlider } from './HudSlider.js'
|
|
1709
|
+
import { SpecRow } from './SpecRow.js'
|
|
1710
|
+
|
|
1711
|
+
const STEP = 10
|
|
1712
|
+
|
|
1713
|
+
interface ArmLengthFieldProps {
|
|
1714
|
+
readonly readOnly: boolean
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
const ArmLengthControl = ({ readOnly }: ArmLengthFieldProps) => {
|
|
1718
|
+
const droneId = phx.frontend.useRootEntityId()
|
|
1719
|
+
const arm = phx.frontend.useDistanceDimension(droneId, 'armLength')
|
|
1720
|
+
// Motor to motor across the drone: twice the arm, which is measured from the centre
|
|
1721
|
+
const span = `Motor to motor ${arm.formatValue(arm.value * 2, true)}`
|
|
1722
|
+
|
|
1723
|
+
if (readOnly) {
|
|
1724
|
+
return <SpecRow label="Arm length" value={arm.valueWithUnit} detail={span} />
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
return (
|
|
1728
|
+
<HudSlider
|
|
1729
|
+
id="corvid-arm-length"
|
|
1730
|
+
label="Arm length"
|
|
1731
|
+
valueText={arm.valueWithUnit}
|
|
1732
|
+
value={arm.value}
|
|
1733
|
+
min={arm.min}
|
|
1734
|
+
max={arm.max}
|
|
1735
|
+
step={STEP}
|
|
1736
|
+
minLabel={arm.formatValue(arm.min, true)}
|
|
1737
|
+
maxLabel={arm.formatValue(arm.max, true)}
|
|
1738
|
+
detail={span}
|
|
1739
|
+
onChange={arm.update}
|
|
1740
|
+
onCommit={arm.resolve}
|
|
1741
|
+
/>
|
|
1742
|
+
)
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
// The spec's `distanceDimension` for the arm length, in the display unit. Moving the slider stages each value with
|
|
1746
|
+
// `update`, so the arms grow under the thumb through the spec's morph target, and letting go commits it with
|
|
1747
|
+
// `resolve`. The field exists only while the spec's component is active.
|
|
1748
|
+
export const ArmLengthField = (props: ArmLengthFieldProps) => {
|
|
1749
|
+
const droneId = phx.frontend.useRootEntityId()
|
|
1750
|
+
const component = phx.frontend.useComponent({
|
|
1751
|
+
entityId: droneId,
|
|
1752
|
+
componentType: distanceDimensionComponent,
|
|
1753
|
+
instanceName: 'armLength',
|
|
1754
|
+
})
|
|
1755
|
+
|
|
1756
|
+
return component === null ? null : <ArmLengthControl {...props} />
|
|
1757
|
+
}
|
|
1758
|
+
```
|
|
1759
|
+
|
|
1760
|
+
### ./src/\_controls/DroneFields.tsx
|
|
1761
|
+
|
|
1762
|
+
```tsx
|
|
1763
|
+
import { Box } from '@mui/material'
|
|
1764
|
+
|
|
1765
|
+
import { ArmLengthField } from './ArmLengthField.js'
|
|
1766
|
+
import { GearField } from './GearField.js'
|
|
1767
|
+
import { GimbalTiltField } from './GimbalTiltField.js'
|
|
1768
|
+
import { OptionField } from './OptionField.js'
|
|
1769
|
+
import { FRAME_VISUALS, LIVERY_VISUALS, MOTOR_VISUALS, PAYLOAD_VISUALS, PROP_VISUALS } from './visuals.js'
|
|
1770
|
+
|
|
1771
|
+
interface DroneFieldsProps {
|
|
1772
|
+
readonly readOnly: boolean
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
// Every control of the spec, in the order the panel shows them. Each one renders only while its spec component is
|
|
1776
|
+
// active, so the spec's `$filter.navigationStates` decides which tab shows it and `$filter.active` hides the tilt
|
|
1777
|
+
// without the camera. The labels are the layout's: `attributeSelection` and the dimensions carry none.
|
|
1778
|
+
export const DroneFields = ({ readOnly }: DroneFieldsProps) => {
|
|
1779
|
+
return (
|
|
1780
|
+
<Box component="div" sx={{ display: 'grid', gap: readOnly ? 1.5 : 3 }}>
|
|
1781
|
+
<OptionField instanceName="frame" label="Frame" visuals={FRAME_VISUALS} readOnly={readOnly} />
|
|
1782
|
+
<ArmLengthField readOnly={readOnly} />
|
|
1783
|
+
<GearField readOnly={readOnly} />
|
|
1784
|
+
<OptionField instanceName="props" label="Propellers" visuals={PROP_VISUALS} readOnly={readOnly} />
|
|
1785
|
+
<OptionField instanceName="motorColor" label="Motor anodising" visuals={MOTOR_VISUALS} readOnly={readOnly} />
|
|
1786
|
+
<OptionField instanceName="payload" label="Payload" visuals={PAYLOAD_VISUALS} readOnly={readOnly} />
|
|
1787
|
+
<GimbalTiltField readOnly={readOnly} />
|
|
1788
|
+
<OptionField instanceName="livery" label="Livery" visuals={LIVERY_VISUALS} readOnly={readOnly} />
|
|
1789
|
+
</Box>
|
|
1790
|
+
)
|
|
1791
|
+
}
|
|
1792
|
+
```
|
|
1793
|
+
|
|
1794
|
+
### ./src/\_controls/GearField.tsx
|
|
1795
|
+
|
|
1796
|
+
```tsx
|
|
1797
|
+
import { Box, Switch, Typography } from '@mui/material'
|
|
1798
|
+
|
|
1799
|
+
import { attributeSwitchComponent } from '@salsita-ai/sdk/domain'
|
|
1800
|
+
|
|
1801
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
1802
|
+
import { CORVID, LABEL_SX } from '../_shared/tokens.js'
|
|
1803
|
+
import { SpecRow } from './SpecRow.js'
|
|
1804
|
+
|
|
1805
|
+
interface GearFieldProps {
|
|
1806
|
+
readonly readOnly: boolean
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
const describe = (retractable: boolean) => {
|
|
1810
|
+
return retractable
|
|
1811
|
+
? 'The legs fold flat under the belly in flight, for a clear view all round.'
|
|
1812
|
+
: 'Fixed carbon skids, the lightest and simplest.'
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
const GearSwitch = ({ readOnly }: GearFieldProps) => {
|
|
1816
|
+
const droneId = phx.frontend.useRootEntityId()
|
|
1817
|
+
const gear = phx.frontend.useAttributeSwitch(droneId, 'retractableGear')
|
|
1818
|
+
const isOn = gear.state.value
|
|
1819
|
+
|
|
1820
|
+
if (readOnly) {
|
|
1821
|
+
return <SpecRow label="Landing gear" value={isOn ? 'Retractable' : 'Fixed skids'} detail={describe(isOn)} />
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
return (
|
|
1825
|
+
<Box component="section" sx={{ display: 'grid', gap: 0.75 }}>
|
|
1826
|
+
<Box component="div" sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 2 }}>
|
|
1827
|
+
<Typography component="h3" id="corvid-gear-label" sx={LABEL_SX}>
|
|
1828
|
+
Retractable gear
|
|
1829
|
+
</Typography>
|
|
1830
|
+
<Switch
|
|
1831
|
+
checked={isOn}
|
|
1832
|
+
disabled={gear.disabled}
|
|
1833
|
+
onChange={gear.onChange}
|
|
1834
|
+
inputProps={{ 'aria-labelledby': 'corvid-gear-label' }}
|
|
1835
|
+
sx={{
|
|
1836
|
+
'& .MuiSwitch-switchBase': { color: CORVID.muted },
|
|
1837
|
+
'& .MuiSwitch-switchBase.Mui-checked': { color: CORVID.lime },
|
|
1838
|
+
'& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': { bgcolor: CORVID.lime, opacity: 0.4 },
|
|
1839
|
+
'& .MuiSwitch-track': { bgcolor: CORVID.line, opacity: 1 },
|
|
1840
|
+
}}
|
|
1841
|
+
/>
|
|
1842
|
+
</Box>
|
|
1843
|
+
<Typography sx={{ fontFamily: CORVID.mono, fontSize: 11.5, lineHeight: 1.4, color: CORVID.muted }}>
|
|
1844
|
+
{describe(isOn)}
|
|
1845
|
+
</Typography>
|
|
1846
|
+
</Box>
|
|
1847
|
+
)
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
// The spec's `attributeSwitch` for the landing gear: `useAttributeSwitch` returns `state.value` and `onChange()`,
|
|
1851
|
+
// which writes the opposite value. The field exists only while the spec's component is active.
|
|
1852
|
+
export const GearField = (props: GearFieldProps) => {
|
|
1853
|
+
const droneId = phx.frontend.useRootEntityId()
|
|
1854
|
+
const component = phx.frontend.useComponent({
|
|
1855
|
+
entityId: droneId,
|
|
1856
|
+
componentType: attributeSwitchComponent,
|
|
1857
|
+
instanceName: 'retractableGear',
|
|
1858
|
+
})
|
|
1859
|
+
|
|
1860
|
+
return component === null ? null : <GearSwitch {...props} />
|
|
1861
|
+
}
|
|
1862
|
+
```
|
|
1863
|
+
|
|
1864
|
+
### ./src/\_controls/GimbalTiltField.tsx
|
|
1865
|
+
|
|
1866
|
+
```tsx
|
|
1867
|
+
import { angleDimensionComponent } from '@salsita-ai/sdk/domain'
|
|
1868
|
+
|
|
1869
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
1870
|
+
import { CORVID } from '../_shared/tokens.js'
|
|
1871
|
+
import { HudSlider } from './HudSlider.js'
|
|
1872
|
+
import { SpecRow } from './SpecRow.js'
|
|
1873
|
+
|
|
1874
|
+
interface TiltDialProps {
|
|
1875
|
+
readonly degrees: number
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
// A quarter dial from the horizon down to the ground, with the camera's line of sight on it
|
|
1879
|
+
const TiltDial = ({ degrees }: TiltDialProps) => {
|
|
1880
|
+
const radians = (degrees * Math.PI) / 180
|
|
1881
|
+
const x = 6 + 40 * Math.cos(radians)
|
|
1882
|
+
const y = 6 + 40 * Math.sin(radians)
|
|
1883
|
+
|
|
1884
|
+
return (
|
|
1885
|
+
<svg viewBox="0 0 52 52" width={52} height={52} aria-hidden fill="none">
|
|
1886
|
+
<path d="M6 6h44M6 6v44" stroke={CORVID.line} />
|
|
1887
|
+
<path d="M46 6A40 40 0 0 1 6 46" stroke={CORVID.faint} strokeDasharray="2 3" />
|
|
1888
|
+
<path d={`M6 6L${x} ${y}`} stroke={CORVID.lime} strokeWidth={1.6} />
|
|
1889
|
+
<circle cx={x} cy={y} r={2.5} fill={CORVID.lime} />
|
|
1890
|
+
<rect x={2} y={2} width={8} height={8} fill={CORVID.graphite} stroke={CORVID.lime} />
|
|
1891
|
+
</svg>
|
|
1892
|
+
)
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
const describe = (degrees: number) => {
|
|
1896
|
+
if (degrees <= 0) {
|
|
1897
|
+
return 'Level with the horizon, for inspection'
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
if (degrees >= 90) {
|
|
1901
|
+
return 'Straight down, for mapping'
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
return `${degrees}° below the horizon`
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
interface GimbalTiltFieldProps {
|
|
1908
|
+
readonly readOnly: boolean
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
const GimbalTiltControl = ({ readOnly }: GimbalTiltFieldProps) => {
|
|
1912
|
+
const droneId = phx.frontend.useRootEntityId()
|
|
1913
|
+
const tilt = phx.frontend.useAngleDimension(droneId, 'gimbalTilt')
|
|
1914
|
+
const valueText = `${tilt.value}${tilt.unitLabel}`
|
|
1915
|
+
const min = tilt.min ?? 0
|
|
1916
|
+
const max = tilt.max ?? 90
|
|
1917
|
+
|
|
1918
|
+
if (readOnly) {
|
|
1919
|
+
return <SpecRow label="Gimbal tilt" value={valueText} detail={describe(tilt.value)} />
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
return (
|
|
1923
|
+
<HudSlider
|
|
1924
|
+
id="corvid-gimbal-tilt"
|
|
1925
|
+
label="Gimbal tilt"
|
|
1926
|
+
valueText={valueText}
|
|
1927
|
+
value={tilt.value}
|
|
1928
|
+
min={min}
|
|
1929
|
+
max={max}
|
|
1930
|
+
step={1}
|
|
1931
|
+
minLabel={`${min}${tilt.unitLabel} HORIZON`}
|
|
1932
|
+
maxLabel={`${max}${tilt.unitLabel} NADIR`}
|
|
1933
|
+
detail={describe(tilt.value)}
|
|
1934
|
+
adornment={<TiltDial degrees={tilt.value} />}
|
|
1935
|
+
onChange={tilt.update}
|
|
1936
|
+
onCommit={tilt.resolve}
|
|
1937
|
+
/>
|
|
1938
|
+
)
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
// The spec's `angleDimension` for the camera's tilt, in degrees as the spec's `unit` asks; the spec stores radians and
|
|
1942
|
+
// turns the gimbal's pivot group by them. The slider stages with `update` and commits with `resolve`. The spec makes
|
|
1943
|
+
// the component active only on the Payload tab and only while the camera is fitted, so the field checks first.
|
|
1944
|
+
export const GimbalTiltField = (props: GimbalTiltFieldProps) => {
|
|
1945
|
+
const droneId = phx.frontend.useRootEntityId()
|
|
1946
|
+
const component = phx.frontend.useComponent({
|
|
1947
|
+
entityId: droneId,
|
|
1948
|
+
componentType: angleDimensionComponent,
|
|
1949
|
+
instanceName: 'gimbalTilt',
|
|
1950
|
+
})
|
|
1951
|
+
|
|
1952
|
+
return component === null ? null : <GimbalTiltControl {...props} />
|
|
1953
|
+
}
|
|
1954
|
+
```
|
|
1955
|
+
|
|
1956
|
+
### ./src/\_controls/HudSlider.tsx
|
|
1957
|
+
|
|
1958
|
+
```tsx
|
|
1959
|
+
import { Box, Slider, Typography, sliderClasses } from '@mui/material'
|
|
1960
|
+
import type { ReactNode } from 'react'
|
|
1961
|
+
|
|
1962
|
+
import { CORVID, LABEL_SX } from '../_shared/tokens.js'
|
|
1963
|
+
|
|
1964
|
+
interface HudSliderProps {
|
|
1965
|
+
readonly id: string
|
|
1966
|
+
readonly label: string
|
|
1967
|
+
readonly valueText: string
|
|
1968
|
+
readonly value: number
|
|
1969
|
+
readonly min: number
|
|
1970
|
+
readonly max: number
|
|
1971
|
+
readonly step: number
|
|
1972
|
+
readonly minLabel: string
|
|
1973
|
+
readonly maxLabel: string
|
|
1974
|
+
readonly detail: string
|
|
1975
|
+
readonly adornment?: ReactNode
|
|
1976
|
+
// Stages a value while the thumb moves, so the scene follows it
|
|
1977
|
+
readonly onChange: (value: number) => void
|
|
1978
|
+
// Commits the staged value when the thumb is let go
|
|
1979
|
+
readonly onCommit: () => void
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
// A dimension as a readout and a slider, for the arm length and the gimbal tilt
|
|
1983
|
+
export const HudSlider = (props: HudSliderProps) => {
|
|
1984
|
+
const { id, label, valueText, value, min, max, step, minLabel, maxLabel, detail, adornment, onChange, onCommit } =
|
|
1985
|
+
props
|
|
1986
|
+
|
|
1987
|
+
return (
|
|
1988
|
+
<Box component="section" sx={{ display: 'grid', gap: 0.75 }}>
|
|
1989
|
+
<Box component="div" sx={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', gap: 2 }}>
|
|
1990
|
+
<Box component="div" sx={{ display: 'grid', gap: 0.5 }}>
|
|
1991
|
+
<Typography component="h3" id={`${id}-label`} sx={LABEL_SX}>
|
|
1992
|
+
{label}
|
|
1993
|
+
</Typography>
|
|
1994
|
+
<Typography
|
|
1995
|
+
sx={{
|
|
1996
|
+
fontFamily: CORVID.mono,
|
|
1997
|
+
fontSize: 26,
|
|
1998
|
+
lineHeight: 1,
|
|
1999
|
+
color: CORVID.lime,
|
|
2000
|
+
fontVariantNumeric: 'tabular-nums',
|
|
2001
|
+
}}
|
|
2002
|
+
>
|
|
2003
|
+
{valueText}
|
|
2004
|
+
</Typography>
|
|
2005
|
+
</Box>
|
|
2006
|
+
{adornment}
|
|
2007
|
+
</Box>
|
|
2008
|
+
<Slider
|
|
2009
|
+
value={value}
|
|
2010
|
+
min={min}
|
|
2011
|
+
max={max}
|
|
2012
|
+
step={step}
|
|
2013
|
+
onChange={(_, next) => onChange(Array.isArray(next) ? (next[0] ?? min) : next)}
|
|
2014
|
+
onChangeCommitted={() => onCommit()}
|
|
2015
|
+
getAriaValueText={() => valueText}
|
|
2016
|
+
slotProps={{ input: { 'aria-labelledby': `${id}-label` } }}
|
|
2017
|
+
sx={{
|
|
2018
|
+
color: CORVID.lime,
|
|
2019
|
+
blockSize: 2,
|
|
2020
|
+
paddingBlock: 1.75,
|
|
2021
|
+
[`& .${sliderClasses.rail}`]: { bgcolor: CORVID.line, opacity: 1 },
|
|
2022
|
+
[`& .${sliderClasses.track}`]: { border: 0 },
|
|
2023
|
+
[`& .${sliderClasses.thumb}`]: {
|
|
2024
|
+
inlineSize: 14,
|
|
2025
|
+
blockSize: 14,
|
|
2026
|
+
borderRadius: 0,
|
|
2027
|
+
transform: 'translate(-50%, -50%) rotate(45deg)',
|
|
2028
|
+
bgcolor: CORVID.graphite,
|
|
2029
|
+
border: `2px solid ${CORVID.lime}`,
|
|
2030
|
+
boxShadow: 'none',
|
|
2031
|
+
'&:hover, &.Mui-focusVisible': { boxShadow: `0 0 0 6px rgba(198, 244, 50, 0.16)` },
|
|
2032
|
+
},
|
|
2033
|
+
}}
|
|
2034
|
+
/>
|
|
2035
|
+
<Box
|
|
2036
|
+
component="div"
|
|
2037
|
+
sx={{
|
|
2038
|
+
display: 'flex',
|
|
2039
|
+
justifyContent: 'space-between',
|
|
2040
|
+
fontFamily: CORVID.mono,
|
|
2041
|
+
fontSize: 10.5,
|
|
2042
|
+
color: CORVID.faint,
|
|
2043
|
+
}}
|
|
2044
|
+
>
|
|
2045
|
+
<span>{minLabel}</span>
|
|
2046
|
+
<span>{maxLabel}</span>
|
|
2047
|
+
</Box>
|
|
2048
|
+
<Typography sx={{ fontFamily: CORVID.mono, fontSize: 12, color: CORVID.muted }}>{detail}</Typography>
|
|
2049
|
+
</Box>
|
|
2050
|
+
)
|
|
2051
|
+
}
|
|
2052
|
+
```
|
|
2053
|
+
|
|
2054
|
+
### ./src/\_controls/OptionField.tsx
|
|
2055
|
+
|
|
2056
|
+
```tsx
|
|
2057
|
+
import { Box, ButtonBase, Typography } from '@mui/material'
|
|
2058
|
+
import type { KeyboardEvent, ReactNode } from 'react'
|
|
2059
|
+
|
|
2060
|
+
import { attributeSelectionComponent } from '@salsita-ai/sdk/domain'
|
|
2061
|
+
|
|
2062
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
2063
|
+
import { CORVID, LABEL_SX, bracketsSx } from '../_shared/tokens.js'
|
|
2064
|
+
import { SpecRow } from './SpecRow.js'
|
|
2065
|
+
|
|
2066
|
+
// Arrow keys move the selection between the tiles, as in a native radio group
|
|
2067
|
+
const ARROW_STEPS: Readonly<Record<string, number | undefined>> = {
|
|
2068
|
+
ArrowRight: 1,
|
|
2069
|
+
ArrowDown: 1,
|
|
2070
|
+
ArrowLeft: -1,
|
|
2071
|
+
ArrowUp: -1,
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
interface OptionFieldProps {
|
|
2075
|
+
readonly instanceName: string
|
|
2076
|
+
readonly label: string
|
|
2077
|
+
// How each option looks, keyed by the spec's option values
|
|
2078
|
+
readonly visuals: Readonly<Record<string, ReactNode>>
|
|
2079
|
+
readonly readOnly: boolean
|
|
2080
|
+
}
|
|
2081
|
+
|
|
2082
|
+
const OptionTiles = ({ instanceName, label, visuals, readOnly }: OptionFieldProps) => {
|
|
2083
|
+
const resolve = phx.frontend.useResolveLocalizable()
|
|
2084
|
+
const droneId = phx.frontend.useRootEntityId()
|
|
2085
|
+
const { value, options } = phx.frontend.useAttributeSelection(droneId, instanceName)
|
|
2086
|
+
const selected = options.find(option => option.value === value)
|
|
2087
|
+
const hint = options.find(option => option.disabled && option.hint)?.hint
|
|
2088
|
+
|
|
2089
|
+
if (readOnly) {
|
|
2090
|
+
return (
|
|
2091
|
+
<SpecRow
|
|
2092
|
+
label={label}
|
|
2093
|
+
value={resolve(selected?.title) ?? ''}
|
|
2094
|
+
detail={resolve(selected?.description)}
|
|
2095
|
+
visual={visuals[String(value)]}
|
|
2096
|
+
/>
|
|
2097
|
+
)
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
const handleKeyDown = (event: KeyboardEvent<HTMLElement>, index: number) => {
|
|
2101
|
+
const step = ARROW_STEPS[event.key]
|
|
2102
|
+
|
|
2103
|
+
if (step === undefined) {
|
|
2104
|
+
return
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
event.preventDefault()
|
|
2108
|
+
// The next enabled option in the arrow's direction, wrapping around
|
|
2109
|
+
const count = options.length
|
|
2110
|
+
const next = Array.from(
|
|
2111
|
+
{ length: count - 1 },
|
|
2112
|
+
(_, offset) => (((index + step * (offset + 1)) % count) + count) % count
|
|
2113
|
+
).find(candidate => !options[candidate]?.disabled)
|
|
2114
|
+
|
|
2115
|
+
if (next === undefined) {
|
|
2116
|
+
return
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
options[next]?.onSelect()
|
|
2120
|
+
event.currentTarget.parentElement?.querySelectorAll<HTMLElement>('[role="radio"]')[next]?.focus()
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
return (
|
|
2124
|
+
<Box component="section" sx={{ display: 'grid', gap: 1.25 }}>
|
|
2125
|
+
<Box component="div" sx={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 2 }}>
|
|
2126
|
+
<Typography component="h3" id={`corvid-${instanceName}-label`} sx={LABEL_SX}>
|
|
2127
|
+
{label}
|
|
2128
|
+
</Typography>
|
|
2129
|
+
<Typography sx={{ fontFamily: CORVID.mono, fontSize: 12, color: CORVID.lime }}>
|
|
2130
|
+
{resolve(selected?.title)}
|
|
2131
|
+
</Typography>
|
|
2132
|
+
</Box>
|
|
2133
|
+
|
|
2134
|
+
<Box
|
|
2135
|
+
component="div"
|
|
2136
|
+
role="radiogroup"
|
|
2137
|
+
aria-labelledby={`corvid-${instanceName}-label`}
|
|
2138
|
+
sx={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(66px, 1fr))', gap: 1 }}
|
|
2139
|
+
>
|
|
2140
|
+
{options.map((option, index) => {
|
|
2141
|
+
const isSelected = option.value === value
|
|
2142
|
+
const title = resolve(option.title)
|
|
2143
|
+
|
|
2144
|
+
return (
|
|
2145
|
+
<ButtonBase
|
|
2146
|
+
key={String(option.value)}
|
|
2147
|
+
role="radio"
|
|
2148
|
+
aria-checked={isSelected}
|
|
2149
|
+
aria-disabled={option.disabled}
|
|
2150
|
+
title={option.hint ?? title}
|
|
2151
|
+
tabIndex={isSelected ? 0 : -1}
|
|
2152
|
+
onClick={option.onSelect}
|
|
2153
|
+
onKeyDown={event => handleKeyDown(event, index)}
|
|
2154
|
+
sx={{
|
|
2155
|
+
display: 'grid',
|
|
2156
|
+
justifyItems: 'start',
|
|
2157
|
+
alignContent: 'space-between',
|
|
2158
|
+
gap: 1.25,
|
|
2159
|
+
minBlockSize: 80,
|
|
2160
|
+
padding: 1.25,
|
|
2161
|
+
textAlign: 'start',
|
|
2162
|
+
color: isSelected ? CORVID.lime : CORVID.ink,
|
|
2163
|
+
border: `1px solid ${isSelected ? 'rgba(198, 244, 50, 0.5)' : CORVID.line}`,
|
|
2164
|
+
bgcolor: isSelected ? 'rgba(198, 244, 50, 0.08)' : 'rgba(232, 237, 242, 0.02)',
|
|
2165
|
+
...(isSelected ? bracketsSx(CORVID.lime, 7) : {}),
|
|
2166
|
+
// A ruled-out option is struck through with a hatch; it stays clickable, and the change it makes is
|
|
2167
|
+
// rejected with the rule's message in the log
|
|
2168
|
+
...(option.disabled && {
|
|
2169
|
+
opacity: 0.42,
|
|
2170
|
+
backgroundImage:
|
|
2171
|
+
'repeating-linear-gradient(135deg, transparent 0 6px, rgba(232, 237, 242, 0.08) 6px 7px)',
|
|
2172
|
+
}),
|
|
2173
|
+
transition: 'border-color 160ms, background-color 160ms, color 160ms',
|
|
2174
|
+
'&:hover': { borderColor: option.disabled ? CORVID.line : 'rgba(198, 244, 50, 0.5)' },
|
|
2175
|
+
'&.Mui-focusVisible': { outline: `1px solid ${CORVID.lime}`, outlineOffset: 2 },
|
|
2176
|
+
}}
|
|
2177
|
+
>
|
|
2178
|
+
<Box component="span" aria-hidden sx={{ display: 'grid', inlineSize: 28, blockSize: 28 }}>
|
|
2179
|
+
{visuals[String(option.value)]}
|
|
2180
|
+
</Box>
|
|
2181
|
+
<Typography
|
|
2182
|
+
component="span"
|
|
2183
|
+
sx={{ fontFamily: CORVID.heading, fontWeight: 500, fontSize: 12.5, lineHeight: 1.2 }}
|
|
2184
|
+
>
|
|
2185
|
+
{title}
|
|
2186
|
+
</Typography>
|
|
2187
|
+
</ButtonBase>
|
|
2188
|
+
)
|
|
2189
|
+
})}
|
|
2190
|
+
</Box>
|
|
2191
|
+
|
|
2192
|
+
<Typography
|
|
2193
|
+
sx={{
|
|
2194
|
+
minBlockSize: '2.8em',
|
|
2195
|
+
fontFamily: CORVID.mono,
|
|
2196
|
+
fontSize: 11.5,
|
|
2197
|
+
lineHeight: 1.4,
|
|
2198
|
+
color: hint ? CORVID.orange : CORVID.muted,
|
|
2199
|
+
}}
|
|
2200
|
+
>
|
|
2201
|
+
{hint ?? resolve(selected?.description)}
|
|
2202
|
+
</Typography>
|
|
2203
|
+
</Box>
|
|
2204
|
+
)
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
// One `attributeSelection` of the spec as a group of tiles, or a spec-sheet line for a viewer. An option an
|
|
2208
|
+
// invariant rules out comes back `disabled` with the rule's message as `hint`. The field exists only while the spec's
|
|
2209
|
+
// component is active: the control hooks throw for an inactive component.
|
|
2210
|
+
export const OptionField = (props: OptionFieldProps) => {
|
|
2211
|
+
const droneId = phx.frontend.useRootEntityId()
|
|
2212
|
+
const component = phx.frontend.useComponent({
|
|
2213
|
+
entityId: droneId,
|
|
2214
|
+
componentType: attributeSelectionComponent,
|
|
2215
|
+
instanceName: props.instanceName,
|
|
2216
|
+
})
|
|
2217
|
+
|
|
2218
|
+
return component === null ? null : <OptionTiles {...props} />
|
|
2219
|
+
}
|
|
2220
|
+
```
|
|
2221
|
+
|
|
2222
|
+
### ./src/\_controls/SpecRow.tsx
|
|
2223
|
+
|
|
2224
|
+
```tsx
|
|
2225
|
+
import { Box, Typography } from '@mui/material'
|
|
2226
|
+
import type { ReactNode } from 'react'
|
|
2227
|
+
|
|
2228
|
+
import { CORVID, LABEL_SX } from '../_shared/tokens.js'
|
|
2229
|
+
|
|
2230
|
+
interface SpecRowProps {
|
|
2231
|
+
readonly label: string
|
|
2232
|
+
readonly value: string
|
|
2233
|
+
readonly detail?: string | undefined
|
|
2234
|
+
readonly visual?: ReactNode
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
// One control as a line of the spec sheet, which is what a viewer sees in place of the control
|
|
2238
|
+
export const SpecRow = ({ label, value, detail, visual }: SpecRowProps) => {
|
|
2239
|
+
return (
|
|
2240
|
+
<Box
|
|
2241
|
+
component="div"
|
|
2242
|
+
sx={{ display: 'grid', gap: 0.5, paddingBlockEnd: 1.5, borderBlockEnd: `1px dashed ${CORVID.line}` }}
|
|
2243
|
+
>
|
|
2244
|
+
<Box component="div" sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
|
2245
|
+
<Typography component="span" sx={{ ...LABEL_SX, flex: 1 }}>
|
|
2246
|
+
{label}
|
|
2247
|
+
</Typography>
|
|
2248
|
+
{visual !== undefined && (
|
|
2249
|
+
<Box component="span" aria-hidden sx={{ display: 'grid', color: CORVID.cyan, inlineSize: 22, blockSize: 22 }}>
|
|
2250
|
+
{visual}
|
|
2251
|
+
</Box>
|
|
2252
|
+
)}
|
|
2253
|
+
<Typography
|
|
2254
|
+
component="span"
|
|
2255
|
+
sx={{ fontFamily: CORVID.heading, fontWeight: 500, fontSize: 15, color: CORVID.ink }}
|
|
2256
|
+
>
|
|
2257
|
+
{value}
|
|
2258
|
+
</Typography>
|
|
2259
|
+
</Box>
|
|
2260
|
+
{detail && (
|
|
2261
|
+
<Typography sx={{ fontFamily: CORVID.mono, fontSize: 11.5, color: CORVID.muted }}>{detail}</Typography>
|
|
2262
|
+
)}
|
|
2263
|
+
</Box>
|
|
2264
|
+
)
|
|
2265
|
+
}
|
|
2266
|
+
```
|
|
2267
|
+
|
|
2268
|
+
### ./src/\_controls/visuals.tsx
|
|
2269
|
+
|
|
2270
|
+
```tsx
|
|
2271
|
+
import { Box } from '@mui/material'
|
|
2272
|
+
import type { ReactNode } from 'react'
|
|
2273
|
+
|
|
2274
|
+
// How each option of the spec looks on its tile, keyed by the option's value: line drawings for the parts, colour
|
|
2275
|
+
// chips for the finishes. They are the layout's own; the spec's options carry no images.
|
|
2276
|
+
|
|
2277
|
+
const glyph = (path: string) => (
|
|
2278
|
+
<svg viewBox="0 0 24 24" width="100%" height="100%" fill="none" stroke="currentColor" strokeWidth={1.4}>
|
|
2279
|
+
<path d={path} strokeLinecap="round" strokeLinejoin="round" />
|
|
2280
|
+
</svg>
|
|
2281
|
+
)
|
|
2282
|
+
|
|
2283
|
+
const chip = (background: string) => (
|
|
2284
|
+
<Box
|
|
2285
|
+
component="span"
|
|
2286
|
+
sx={{
|
|
2287
|
+
display: 'block',
|
|
2288
|
+
inlineSize: '100%',
|
|
2289
|
+
blockSize: '100%',
|
|
2290
|
+
borderRadius: '50%',
|
|
2291
|
+
background,
|
|
2292
|
+
boxShadow: 'inset 0 0 0 1px rgba(255, 255, 255, 0.18)',
|
|
2293
|
+
}}
|
|
2294
|
+
/>
|
|
2295
|
+
)
|
|
2296
|
+
|
|
2297
|
+
// A shell colour with a dot of the lights' colour
|
|
2298
|
+
const livery = (shell: string, lights: string) =>
|
|
2299
|
+
chip(`radial-gradient(circle at 72% 72%, ${lights} 0 16%, transparent 19%), ${shell}`)
|
|
2300
|
+
|
|
2301
|
+
export const FRAME_VISUALS: Readonly<Record<string, ReactNode>> = {
|
|
2302
|
+
Quad: glyph(
|
|
2303
|
+
'M8 8l8 8M16 8l-8 8M10 12a2 2 0 1 0 4 0a2 2 0 1 0-4 0M3.5 5.5a2.5 2.5 0 1 0 5 0a2.5 2.5 0 1 0-5 0M15.5 5.5a2.5 2.5 0 1 0 5 0a2.5 2.5 0 1 0-5 0M3.5 18.5a2.5 2.5 0 1 0 5 0a2.5 2.5 0 1 0-5 0M15.5 18.5a2.5 2.5 0 1 0 5 0a2.5 2.5 0 1 0-5 0'
|
|
2304
|
+
),
|
|
2305
|
+
Hexa: glyph(
|
|
2306
|
+
'M5 12h14M8.5 6l7 12M15.5 6l-7 12M1.5 12a2 2 0 1 0 4 0a2 2 0 1 0-4 0M18.5 12a2 2 0 1 0 4 0a2 2 0 1 0-4 0M5.5 4.5a2 2 0 1 0 4 0a2 2 0 1 0-4 0M14.5 4.5a2 2 0 1 0 4 0a2 2 0 1 0-4 0M5.5 19.5a2 2 0 1 0 4 0a2 2 0 1 0-4 0M14.5 19.5a2 2 0 1 0 4 0a2 2 0 1 0-4 0'
|
|
2307
|
+
),
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
export const PROP_VISUALS: Readonly<Record<string, ReactNode>> = {
|
|
2311
|
+
TwoBlade: glyph(
|
|
2312
|
+
'M10.5 12a1.5 1.5 0 1 0 3 0a1.5 1.5 0 1 0-3 0M12 10.5C11 6 12.5 3 14 2.5c.8 2-.2 5.5-2 8M12 13.5c1 4.5-.5 7.5-2 8c-.8-2 .2-5.5 2-8'
|
|
2313
|
+
),
|
|
2314
|
+
ThreeBlade: glyph(
|
|
2315
|
+
'M10.5 12a1.5 1.5 0 1 0 3 0a1.5 1.5 0 1 0-3 0M12 10.5C11 6 12.5 3 14 2.5c.8 2-.2 5.5-2 8M13.3 12.8c4 1.8 6.2 4.4 6 6c-2 .5-5-1.5-6.6-4.6M10.7 12.8c-3.5 2.7-6.9 3.1-8 2c.7-1.9 4-3.6 7.2-3.6'
|
|
2316
|
+
),
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2319
|
+
export const PAYLOAD_VISUALS: Readonly<Record<string, ReactNode>> = {
|
|
2320
|
+
Camera: glyph('M4 7.5h16v11H4zM8.5 13a3.5 3.5 0 1 0 7 0a3.5 3.5 0 1 0-7 0M12 4.5v3M9 4.5h6'),
|
|
2321
|
+
Lidar: glyph(
|
|
2322
|
+
'M6 6.5c0-1.4 2.7-2.5 6-2.5s6 1.1 6 2.5v11c0 1.4-2.7 2.5-6 2.5s-6-1.1-6-2.5zM6 10.5c0 1.4 2.7 2.5 6 2.5s6-1.1 6-2.5M2 12h2M20 12h2'
|
|
2323
|
+
),
|
|
2324
|
+
Winch: glyph('M6 3.5h12v6H6zM12 9.5v7M12 16.5a2.5 2.5 0 1 1-2.5 2.5'),
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2327
|
+
export const MOTOR_VISUALS: Readonly<Record<string, ReactNode>> = {
|
|
2328
|
+
Titanium: chip('linear-gradient(135deg, #e3e6ea 0%, #8c939b 55%, #c9ced3 100%)'),
|
|
2329
|
+
Lime: chip('linear-gradient(135deg, #e3f78c 0%, #9cc417 55%, #c6f432 100%)'),
|
|
2330
|
+
Cyan: chip('linear-gradient(135deg, #9beff7 0%, #1aa3b5 55%, #3ed6e8 100%)'),
|
|
2331
|
+
Ember: chip('linear-gradient(135deg, #ffc196 0%, #e2561a 55%, #ff8a3d 100%)'),
|
|
2332
|
+
}
|
|
2333
|
+
|
|
2334
|
+
export const LIVERY_VISUALS: Readonly<Record<string, ReactNode>> = {
|
|
2335
|
+
Graphite: livery('#4a525c', '#c6f432'),
|
|
2336
|
+
Arctic: livery('#e9edf0', '#3ed6e8'),
|
|
2337
|
+
Signal: livery('#ff6a1a', '#f4f7fa'),
|
|
2338
|
+
}
|
|
2339
|
+
```
|
|
2340
|
+
|
|
2341
|
+
### ./src/\_shared/DesktopDeck.tsx
|
|
2342
|
+
|
|
2343
|
+
```tsx
|
|
2344
|
+
import { Box, Typography } from '@mui/material'
|
|
2345
|
+
|
|
2346
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
2347
|
+
import { DroneTabs } from './DroneTabs.js'
|
|
2348
|
+
import { HudReadout } from './HudReadout.js'
|
|
2349
|
+
import { MessageLog } from './MessageLog.js'
|
|
2350
|
+
import { TabPanel } from './TabPanel.js'
|
|
2351
|
+
import { ViewDock } from './ViewDock.js'
|
|
2352
|
+
import { CORVID, GLASS_SX, bracketsSx } from './tokens.js'
|
|
2353
|
+
|
|
2354
|
+
const RAIL_WIDTH = 84
|
|
2355
|
+
const PANEL_WIDTH = 384
|
|
2356
|
+
const GUTTER = 20
|
|
2357
|
+
|
|
2358
|
+
// The brand's mark: a hexagon with a wing's chevron
|
|
2359
|
+
const MARK = 'M16 3 27 9.5v13L16 29 5 22.5v-13ZM10 19l6-6 6 6'
|
|
2360
|
+
|
|
2361
|
+
// The desktop deck: the tab rail on the left, the active tab's panel beside it, and the free area over the rest of
|
|
2362
|
+
// the scene with the HUD at its top right, the message log and the view dock along its bottom. Fullscreen is a
|
|
2363
|
+
// preview: only the view dock stays, and the drone takes the whole canvas.
|
|
2364
|
+
export const DesktopDeck = () => {
|
|
2365
|
+
const { containerRef, safeAreaRef } = phx.frontend.useSceneSafeArea()
|
|
2366
|
+
const isFullscreen = phx.frontend.useIsFullscreenActive()
|
|
2367
|
+
const { accessInfo } = phx.frontend.useAccessService()
|
|
2368
|
+
|
|
2369
|
+
return (
|
|
2370
|
+
<Box
|
|
2371
|
+
component="div"
|
|
2372
|
+
ref={containerRef}
|
|
2373
|
+
sx={{ position: 'absolute', inset: 0, display: 'flex', fontFamily: CORVID.mono, pointerEvents: 'none' }}
|
|
2374
|
+
>
|
|
2375
|
+
{!isFullscreen && (
|
|
2376
|
+
<>
|
|
2377
|
+
<Box
|
|
2378
|
+
component="nav"
|
|
2379
|
+
aria-label="Corvid K6"
|
|
2380
|
+
sx={{
|
|
2381
|
+
flex: `0 0 ${RAIL_WIDTH}px`,
|
|
2382
|
+
display: 'flex',
|
|
2383
|
+
flexDirection: 'column',
|
|
2384
|
+
gap: 3,
|
|
2385
|
+
paddingBlock: 2.5,
|
|
2386
|
+
paddingInline: 1,
|
|
2387
|
+
bgcolor: 'rgba(12, 14, 18, 0.9)',
|
|
2388
|
+
borderInlineEnd: `1px solid ${CORVID.line}`,
|
|
2389
|
+
pointerEvents: 'auto',
|
|
2390
|
+
}}
|
|
2391
|
+
>
|
|
2392
|
+
<Box component="div" sx={{ display: 'grid', justifyItems: 'center', gap: 0.5, color: CORVID.lime }}>
|
|
2393
|
+
<svg viewBox="0 0 32 32" width={30} height={30} fill="none" stroke="currentColor" strokeWidth={1.5}>
|
|
2394
|
+
<path d={MARK} strokeLinejoin="round" />
|
|
2395
|
+
</svg>
|
|
2396
|
+
<Typography sx={{ fontFamily: CORVID.mono, fontSize: 11, letterSpacing: '0.2em', color: CORVID.ink }}>
|
|
2397
|
+
K6
|
|
2398
|
+
</Typography>
|
|
2399
|
+
</Box>
|
|
2400
|
+
<DroneTabs orientation="vertical" />
|
|
2401
|
+
</Box>
|
|
2402
|
+
|
|
2403
|
+
<Box
|
|
2404
|
+
component="div"
|
|
2405
|
+
sx={{
|
|
2406
|
+
flex: `0 0 ${PANEL_WIDTH}px`,
|
|
2407
|
+
display: 'flex',
|
|
2408
|
+
minBlockSize: 0,
|
|
2409
|
+
padding: `${GUTTER}px 0 ${GUTTER}px ${GUTTER}px`,
|
|
2410
|
+
}}
|
|
2411
|
+
>
|
|
2412
|
+
<Box
|
|
2413
|
+
component="div"
|
|
2414
|
+
sx={{
|
|
2415
|
+
...GLASS_SX,
|
|
2416
|
+
// Lime corners while editing, cyan on the spec sheet, as the HUD's mode
|
|
2417
|
+
...bracketsSx(accessInfo.viewMode ? CORVID.cyan : CORVID.lime),
|
|
2418
|
+
flex: 1,
|
|
2419
|
+
overflowY: 'auto',
|
|
2420
|
+
overscrollBehavior: 'contain',
|
|
2421
|
+
padding: 3,
|
|
2422
|
+
pointerEvents: 'auto',
|
|
2423
|
+
scrollbarWidth: 'thin',
|
|
2424
|
+
scrollbarColor: `${CORVID.line} transparent`,
|
|
2425
|
+
}}
|
|
2426
|
+
>
|
|
2427
|
+
<TabPanel compact={false} />
|
|
2428
|
+
</Box>
|
|
2429
|
+
</Box>
|
|
2430
|
+
</>
|
|
2431
|
+
)}
|
|
2432
|
+
|
|
2433
|
+
<Box component="div" sx={{ position: 'relative', flex: '1 1 0', minInlineSize: 0 }}>
|
|
2434
|
+
{/* The camera fits the drone into this box: below the HUD, above the dock, clear of the edges */}
|
|
2435
|
+
<Box component="div" ref={safeAreaRef} sx={{ position: 'absolute', inset: '112px 24px 84px' }} />
|
|
2436
|
+
{!isFullscreen && (
|
|
2437
|
+
<Box
|
|
2438
|
+
component="div"
|
|
2439
|
+
sx={{ position: 'absolute', insetBlockStart: GUTTER, insetInlineEnd: GUTTER, pointerEvents: 'auto' }}
|
|
2440
|
+
>
|
|
2441
|
+
<HudReadout compact={false} />
|
|
2442
|
+
</Box>
|
|
2443
|
+
)}
|
|
2444
|
+
<Box
|
|
2445
|
+
component="div"
|
|
2446
|
+
sx={{
|
|
2447
|
+
position: 'absolute',
|
|
2448
|
+
insetBlockEnd: GUTTER,
|
|
2449
|
+
insetInline: GUTTER,
|
|
2450
|
+
display: 'flex',
|
|
2451
|
+
alignItems: 'flex-end',
|
|
2452
|
+
justifyContent: 'space-between',
|
|
2453
|
+
gap: 3,
|
|
2454
|
+
}}
|
|
2455
|
+
>
|
|
2456
|
+
{!isFullscreen && <MessageLog compact={false} />}
|
|
2457
|
+
<Box component="div" sx={{ flex: 'none', marginInlineStart: 'auto', pointerEvents: 'auto' }}>
|
|
2458
|
+
<ViewDock placement="desktop" />
|
|
2459
|
+
</Box>
|
|
2460
|
+
</Box>
|
|
2461
|
+
</Box>
|
|
2462
|
+
</Box>
|
|
2463
|
+
)
|
|
2464
|
+
}
|
|
2465
|
+
```
|
|
2466
|
+
|
|
2467
|
+
### ./src/\_shared/DroneTabs.tsx
|
|
2468
|
+
|
|
2469
|
+
```tsx
|
|
2470
|
+
import { Box, ButtonBase, Typography } from '@mui/material'
|
|
2471
|
+
import type { KeyboardEvent } from 'react'
|
|
2472
|
+
|
|
2473
|
+
import { PhxIcon } from '@salsita-ai/sdk/ui'
|
|
2474
|
+
|
|
2475
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
2476
|
+
import { CORVID } from './tokens.js'
|
|
2477
|
+
|
|
2478
|
+
// The panel the tabs control; `TabPanel` carries this id
|
|
2479
|
+
export const TAB_PANEL_ID = 'corvid-tab-panel'
|
|
2480
|
+
|
|
2481
|
+
interface DroneTabsProps {
|
|
2482
|
+
readonly orientation: 'vertical' | 'horizontal'
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2485
|
+
// The spec's `sidebarTabs` as a tab list: a rail of icons on a desktop, a segmented bar at the bottom of a phone.
|
|
2486
|
+
// `useTabNavigation` gives each tab its resolved label, the PhxIcon name the spec sets and `isActive`; `select` enters
|
|
2487
|
+
// the tab's navigation state, and the controls the spec filters to that state become active. Switching tabs is
|
|
2488
|
+
// navigation, not editing, so a viewer switches too.
|
|
2489
|
+
export const DroneTabs = ({ orientation }: DroneTabsProps) => {
|
|
2490
|
+
const { tabs, select } = phx.frontend.useTabNavigation()
|
|
2491
|
+
const vertical = orientation === 'vertical'
|
|
2492
|
+
const nextKey = vertical ? 'ArrowDown' : 'ArrowRight'
|
|
2493
|
+
const previousKey = vertical ? 'ArrowUp' : 'ArrowLeft'
|
|
2494
|
+
|
|
2495
|
+
// The arrow keys move between the tabs and enter the one they reach, as in a native tab list
|
|
2496
|
+
const handleKeyDown = (event: KeyboardEvent<HTMLElement>, index: number) => {
|
|
2497
|
+
const step = event.key === nextKey ? 1 : event.key === previousKey ? -1 : 0
|
|
2498
|
+
|
|
2499
|
+
if (step === 0) {
|
|
2500
|
+
return
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2503
|
+
event.preventDefault()
|
|
2504
|
+
const next = (index + step + tabs.length) % tabs.length
|
|
2505
|
+
select(next)
|
|
2506
|
+
event.currentTarget.parentElement?.querySelectorAll<HTMLElement>('[role="tab"]')[next]?.focus()
|
|
2507
|
+
}
|
|
2508
|
+
|
|
2509
|
+
return (
|
|
2510
|
+
<Box
|
|
2511
|
+
component="div"
|
|
2512
|
+
role="tablist"
|
|
2513
|
+
aria-label="Configuration"
|
|
2514
|
+
aria-orientation={orientation}
|
|
2515
|
+
sx={{ display: 'flex', flexDirection: vertical ? 'column' : 'row', gap: vertical ? 0.5 : 0, blockSize: '100%' }}
|
|
2516
|
+
>
|
|
2517
|
+
{tabs.map(tab => (
|
|
2518
|
+
<ButtonBase
|
|
2519
|
+
key={tab.state}
|
|
2520
|
+
role="tab"
|
|
2521
|
+
id={`corvid-tab-${tab.index}`}
|
|
2522
|
+
aria-selected={tab.isActive}
|
|
2523
|
+
aria-controls={TAB_PANEL_ID}
|
|
2524
|
+
tabIndex={tab.isActive ? 0 : -1}
|
|
2525
|
+
onClick={() => select(tab.index)}
|
|
2526
|
+
onKeyDown={event => handleKeyDown(event, tab.index)}
|
|
2527
|
+
sx={{
|
|
2528
|
+
position: 'relative',
|
|
2529
|
+
flex: vertical ? 'none' : '1 1 0',
|
|
2530
|
+
display: 'flex',
|
|
2531
|
+
flexDirection: 'column',
|
|
2532
|
+
alignItems: 'center',
|
|
2533
|
+
justifyContent: 'center',
|
|
2534
|
+
gap: 0.75,
|
|
2535
|
+
minBlockSize: vertical ? 68 : 'auto',
|
|
2536
|
+
color: tab.isActive ? CORVID.lime : CORVID.muted,
|
|
2537
|
+
bgcolor: tab.isActive ? 'rgba(198, 244, 50, 0.07)' : 'transparent',
|
|
2538
|
+
transition: 'color 160ms, background-color 160ms',
|
|
2539
|
+
// The active tab's marker: a bar on the rail's outer edge, or along the top of the bar's segment
|
|
2540
|
+
'&::before': {
|
|
2541
|
+
content: '""',
|
|
2542
|
+
position: 'absolute',
|
|
2543
|
+
insetBlock: vertical ? '14px' : '0 auto',
|
|
2544
|
+
insetInlineStart: vertical ? 0 : '18%',
|
|
2545
|
+
insetInlineEnd: vertical ? 'auto' : '18%',
|
|
2546
|
+
inlineSize: vertical ? 2 : 'auto',
|
|
2547
|
+
blockSize: vertical ? 'auto' : 2,
|
|
2548
|
+
bgcolor: CORVID.lime,
|
|
2549
|
+
opacity: tab.isActive ? 1 : 0,
|
|
2550
|
+
transition: 'opacity 160ms',
|
|
2551
|
+
},
|
|
2552
|
+
'&:hover': { color: tab.isActive ? CORVID.lime : CORVID.ink },
|
|
2553
|
+
'&.Mui-focusVisible': { outline: `1px solid ${CORVID.lime}`, outlineOffset: -4 },
|
|
2554
|
+
}}
|
|
2555
|
+
>
|
|
2556
|
+
{tab.icon !== undefined && <PhxIcon name={tab.icon} fontSize="medium" />}
|
|
2557
|
+
<Typography
|
|
2558
|
+
component="span"
|
|
2559
|
+
sx={{ fontFamily: CORVID.mono, fontSize: 9.5, letterSpacing: '0.14em', textTransform: 'uppercase' }}
|
|
2560
|
+
>
|
|
2561
|
+
{tab.label}
|
|
2562
|
+
</Typography>
|
|
2563
|
+
</ButtonBase>
|
|
2564
|
+
))}
|
|
2565
|
+
</Box>
|
|
2566
|
+
)
|
|
2567
|
+
}
|
|
2568
|
+
```
|
|
2569
|
+
|
|
2570
|
+
### ./src/\_shared/HudReadout.tsx
|
|
2571
|
+
|
|
2572
|
+
```tsx
|
|
2573
|
+
import { Box, Typography } from '@mui/material'
|
|
2574
|
+
|
|
2575
|
+
import { detailComponent, viewControlsComponent } from '@salsita-ai/sdk/domain'
|
|
2576
|
+
|
|
2577
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
2578
|
+
import { CORVID, GLASS_SX, LABEL_SX, bracketsSx } from './tokens.js'
|
|
2579
|
+
|
|
2580
|
+
interface HudReadoutProps {
|
|
2581
|
+
readonly compact: boolean
|
|
2582
|
+
}
|
|
2583
|
+
|
|
2584
|
+
// The HUD's readouts: the price, the all-up weight, the active view and the mode. The weight is the subtitle of the
|
|
2585
|
+
// spec's `detail` component, which an expression computes from the choices; the view's name is the label the spec's
|
|
2586
|
+
// `viewControls` give it. A viewer sees the mode change to the spec sheet.
|
|
2587
|
+
export const HudReadout = ({ compact }: HudReadoutProps) => {
|
|
2588
|
+
const detail = phx.frontend.useFirstRootComponent(detailComponent)
|
|
2589
|
+
const name = phx.frontend.useLocalizable(detail?.state.title)
|
|
2590
|
+
const weight = phx.frontend.useLocalizable(detail?.state.subtitle)
|
|
2591
|
+
const price = phx.frontend.useRootEntityPrice()
|
|
2592
|
+
const viewControls = phx.frontend.useFirstRootComponent(viewControlsComponent)
|
|
2593
|
+
const viewState = phx.frontend.useActiveViewState()
|
|
2594
|
+
const resolve = phx.frontend.useResolveLocalizable()
|
|
2595
|
+
const { accessInfo } = phx.frontend.useAccessService()
|
|
2596
|
+
const readOnly = accessInfo.viewMode
|
|
2597
|
+
const view = viewControls?.state.items.find(item => item.value === viewState)
|
|
2598
|
+
const modeColor = readOnly ? CORVID.cyan : CORVID.lime
|
|
2599
|
+
|
|
2600
|
+
const mode = (
|
|
2601
|
+
<Typography
|
|
2602
|
+
component="span"
|
|
2603
|
+
sx={{
|
|
2604
|
+
display: 'inline-flex',
|
|
2605
|
+
alignItems: 'center',
|
|
2606
|
+
gap: 1,
|
|
2607
|
+
fontFamily: CORVID.mono,
|
|
2608
|
+
fontSize: compact ? 9.5 : 10.5,
|
|
2609
|
+
letterSpacing: '0.16em',
|
|
2610
|
+
color: modeColor,
|
|
2611
|
+
whiteSpace: 'nowrap',
|
|
2612
|
+
// A steady square for the spec sheet, a pulsing dot while editing
|
|
2613
|
+
'&::before': {
|
|
2614
|
+
content: '""',
|
|
2615
|
+
inlineSize: 7,
|
|
2616
|
+
blockSize: 7,
|
|
2617
|
+
borderRadius: readOnly ? 0 : '50%',
|
|
2618
|
+
bgcolor: modeColor,
|
|
2619
|
+
animation: readOnly ? 'none' : 'corvid-pulse 1.6s ease-in-out infinite',
|
|
2620
|
+
},
|
|
2621
|
+
'@keyframes corvid-pulse': { '50%': { opacity: 0.25 } },
|
|
2622
|
+
}}
|
|
2623
|
+
>
|
|
2624
|
+
{readOnly ? 'SPEC SHEET · VIEW ONLY' : 'ENGINEERING · EDIT'}
|
|
2625
|
+
</Typography>
|
|
2626
|
+
)
|
|
2627
|
+
|
|
2628
|
+
if (compact) {
|
|
2629
|
+
return (
|
|
2630
|
+
<Box
|
|
2631
|
+
component="div"
|
|
2632
|
+
role="group"
|
|
2633
|
+
aria-label="Readouts"
|
|
2634
|
+
sx={{
|
|
2635
|
+
...GLASS_SX,
|
|
2636
|
+
display: 'grid',
|
|
2637
|
+
gridTemplateColumns: '1fr auto',
|
|
2638
|
+
alignItems: 'center',
|
|
2639
|
+
columnGap: 2,
|
|
2640
|
+
rowGap: 0.5,
|
|
2641
|
+
paddingInline: 1.5,
|
|
2642
|
+
paddingBlock: 1,
|
|
2643
|
+
}}
|
|
2644
|
+
>
|
|
2645
|
+
<Typography sx={{ fontFamily: CORVID.heading, fontWeight: 600, fontSize: 18, lineHeight: 1.2 }}>
|
|
2646
|
+
{price}
|
|
2647
|
+
</Typography>
|
|
2648
|
+
<Typography sx={{ fontFamily: CORVID.mono, fontSize: 12, color: CORVID.ink, textAlign: 'end' }}>
|
|
2649
|
+
{weight}
|
|
2650
|
+
</Typography>
|
|
2651
|
+
{mode}
|
|
2652
|
+
<Typography sx={{ ...LABEL_SX, fontSize: 9.5, textAlign: 'end' }}>{name}</Typography>
|
|
2653
|
+
</Box>
|
|
2654
|
+
)
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2657
|
+
return (
|
|
2658
|
+
<Box
|
|
2659
|
+
component="div"
|
|
2660
|
+
role="group"
|
|
2661
|
+
aria-label="Readouts"
|
|
2662
|
+
sx={{
|
|
2663
|
+
...GLASS_SX,
|
|
2664
|
+
...bracketsSx(modeColor, 8),
|
|
2665
|
+
display: 'grid',
|
|
2666
|
+
gap: 1,
|
|
2667
|
+
minInlineSize: 300,
|
|
2668
|
+
paddingInline: 2.25,
|
|
2669
|
+
paddingBlock: 1.75,
|
|
2670
|
+
}}
|
|
2671
|
+
>
|
|
2672
|
+
<Box component="div" sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 3 }}>
|
|
2673
|
+
<Typography sx={LABEL_SX}>{name}</Typography>
|
|
2674
|
+
{mode}
|
|
2675
|
+
</Box>
|
|
2676
|
+
<Typography sx={{ fontFamily: CORVID.heading, fontWeight: 600, fontSize: 30, lineHeight: 1.1 }}>
|
|
2677
|
+
{price}
|
|
2678
|
+
</Typography>
|
|
2679
|
+
<Box
|
|
2680
|
+
component="div"
|
|
2681
|
+
sx={{ display: 'flex', gap: 2.5, fontFamily: CORVID.mono, fontSize: 12, letterSpacing: '0.08em' }}
|
|
2682
|
+
>
|
|
2683
|
+
<Box component="span">{weight}</Box>
|
|
2684
|
+
{view && (
|
|
2685
|
+
<Box component="span" sx={{ color: CORVID.muted }}>
|
|
2686
|
+
VIEW {resolve(view.label)?.toUpperCase()}
|
|
2687
|
+
</Box>
|
|
2688
|
+
)}
|
|
2689
|
+
</Box>
|
|
2690
|
+
</Box>
|
|
2691
|
+
)
|
|
2692
|
+
}
|
|
2693
|
+
```
|
|
2694
|
+
|
|
2695
|
+
### ./src/\_shared/MessageLog.tsx
|
|
2696
|
+
|
|
2697
|
+
```tsx
|
|
2698
|
+
import { Box } from '@mui/material'
|
|
2699
|
+
import { useEffect, useState } from 'react'
|
|
2700
|
+
|
|
2701
|
+
import type { ConfiguratorMessage } from '@salsita-ai/sdk/react'
|
|
2702
|
+
|
|
2703
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
2704
|
+
import { CORVID, GLASS_SX } from './tokens.js'
|
|
2705
|
+
|
|
2706
|
+
const VISIBLE_MS = 9000
|
|
2707
|
+
|
|
2708
|
+
interface LogEntry {
|
|
2709
|
+
readonly message: ConfiguratorMessage
|
|
2710
|
+
readonly time: string
|
|
2711
|
+
}
|
|
2712
|
+
|
|
2713
|
+
// The tag before each line, by the kind of message and, for a violation, its severity
|
|
2714
|
+
const tagOf = (message: ConfiguratorMessage) => {
|
|
2715
|
+
if (message.kind === 'violation') {
|
|
2716
|
+
return message.violation.severity === 'fixable' || message.violation.severity === 'fatal'
|
|
2717
|
+
? { label: 'REJECTED', color: CORVID.orange }
|
|
2718
|
+
: { label: message.violation.severity.toUpperCase(), color: CORVID.cyan }
|
|
2719
|
+
}
|
|
2720
|
+
|
|
2721
|
+
if (message.kind === 'sideEffect') {
|
|
2722
|
+
return { label: 'NOTE', color: CORVID.cyan }
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2725
|
+
if (message.kind === 'saveRecovery') {
|
|
2726
|
+
return { label: 'SAVED', color: CORVID.lime }
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2729
|
+
return { label: 'ERROR', color: CORVID.orange }
|
|
2730
|
+
}
|
|
2731
|
+
|
|
2732
|
+
interface MessageLogProps {
|
|
2733
|
+
readonly compact: boolean
|
|
2734
|
+
}
|
|
2735
|
+
|
|
2736
|
+
// The configurator's messages as the HUD's log line: a rejected change, the hint a change carries, a failed save.
|
|
2737
|
+
// `useConfiguratorMessages` hands each one over with its text resolved; the line shows the latest for a few seconds,
|
|
2738
|
+
// then returns to its idle state.
|
|
2739
|
+
export const MessageLog = ({ compact }: MessageLogProps) => {
|
|
2740
|
+
const [entry, setEntry] = useState<LogEntry | null>(null)
|
|
2741
|
+
const { accessInfo } = phx.frontend.useAccessService()
|
|
2742
|
+
|
|
2743
|
+
phx.frontend.useConfiguratorMessages(message => {
|
|
2744
|
+
setEntry({ message, time: new Date().toLocaleTimeString('en-GB') })
|
|
2745
|
+
})
|
|
2746
|
+
|
|
2747
|
+
useEffect(() => {
|
|
2748
|
+
if (entry === null) {
|
|
2749
|
+
return
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2752
|
+
const timeout = setTimeout(() => setEntry(null), VISIBLE_MS)
|
|
2753
|
+
|
|
2754
|
+
return () => clearTimeout(timeout)
|
|
2755
|
+
}, [entry])
|
|
2756
|
+
|
|
2757
|
+
const tag = entry ? tagOf(entry.message) : { label: 'READY', color: CORVID.faint }
|
|
2758
|
+
const idle = accessInfo.viewMode ? 'Viewing a shared configuration' : 'Every change saves on its own'
|
|
2759
|
+
|
|
2760
|
+
return (
|
|
2761
|
+
<Box
|
|
2762
|
+
component="div"
|
|
2763
|
+
role="status"
|
|
2764
|
+
aria-live="polite"
|
|
2765
|
+
sx={{
|
|
2766
|
+
...GLASS_SX,
|
|
2767
|
+
display: 'flex',
|
|
2768
|
+
alignItems: 'baseline',
|
|
2769
|
+
gap: compact ? 1 : 1.5,
|
|
2770
|
+
minInlineSize: 0,
|
|
2771
|
+
maxInlineSize: compact ? 'none' : 560,
|
|
2772
|
+
paddingInline: 1.5,
|
|
2773
|
+
paddingBlock: 1,
|
|
2774
|
+
fontFamily: CORVID.mono,
|
|
2775
|
+
fontSize: compact ? 11 : 12,
|
|
2776
|
+
lineHeight: 1.5,
|
|
2777
|
+
}}
|
|
2778
|
+
>
|
|
2779
|
+
<Box component="span" aria-hidden sx={{ color: CORVID.lime }}>
|
|
2780
|
+
>
|
|
2781
|
+
</Box>
|
|
2782
|
+
{entry && !compact && (
|
|
2783
|
+
<Box component="span" sx={{ color: CORVID.faint }}>
|
|
2784
|
+
{entry.time}
|
|
2785
|
+
</Box>
|
|
2786
|
+
)}
|
|
2787
|
+
<Box component="span" sx={{ flex: 'none', color: tag.color, letterSpacing: '0.12em' }}>
|
|
2788
|
+
{tag.label}
|
|
2789
|
+
</Box>
|
|
2790
|
+
<Box component="span" sx={{ minInlineSize: 0, color: entry ? CORVID.ink : CORVID.muted }}>
|
|
2791
|
+
{entry ? entry.message.text : idle}
|
|
2792
|
+
</Box>
|
|
2793
|
+
</Box>
|
|
2794
|
+
)
|
|
2795
|
+
}
|
|
2796
|
+
```
|
|
2797
|
+
|
|
2798
|
+
### ./src/\_shared/PhoneDeck.tsx
|
|
2799
|
+
|
|
2800
|
+
```tsx
|
|
2801
|
+
import { Box } from '@mui/material'
|
|
2802
|
+
|
|
2803
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
2804
|
+
import { DroneTabs } from './DroneTabs.js'
|
|
2805
|
+
import { HudReadout } from './HudReadout.js'
|
|
2806
|
+
import { MessageLog } from './MessageLog.js'
|
|
2807
|
+
import { TabPanel } from './TabPanel.js'
|
|
2808
|
+
import { ViewDock } from './ViewDock.js'
|
|
2809
|
+
import { CORVID, GLASS_SX } from './tokens.js'
|
|
2810
|
+
|
|
2811
|
+
const SHEET_HEIGHT = 312
|
|
2812
|
+
const BAR_HEIGHT = 64
|
|
2813
|
+
const GUTTER = 12
|
|
2814
|
+
|
|
2815
|
+
// The phone deck: the scene at the top with the HUD over it, a sheet of fixed height with the active tab's controls,
|
|
2816
|
+
// and the tabs as a segmented bar at the bottom. The sheet keeps its height on every tab, so the drone does not jump
|
|
2817
|
+
// when the tab changes. Fullscreen is a preview: only the reset and the exit stay.
|
|
2818
|
+
export const PhoneDeck = () => {
|
|
2819
|
+
const { containerRef, safeAreaRef } = phx.frontend.useSceneSafeArea()
|
|
2820
|
+
const isFullscreen = phx.frontend.useIsFullscreenActive()
|
|
2821
|
+
|
|
2822
|
+
return (
|
|
2823
|
+
<Box
|
|
2824
|
+
component="div"
|
|
2825
|
+
ref={containerRef}
|
|
2826
|
+
sx={{
|
|
2827
|
+
position: 'absolute',
|
|
2828
|
+
inset: 0,
|
|
2829
|
+
display: 'flex',
|
|
2830
|
+
flexDirection: 'column',
|
|
2831
|
+
fontFamily: CORVID.mono,
|
|
2832
|
+
pointerEvents: 'none',
|
|
2833
|
+
}}
|
|
2834
|
+
>
|
|
2835
|
+
<Box component="div" sx={{ position: 'relative', flex: '1 1 0', minBlockSize: 0 }}>
|
|
2836
|
+
{/* The camera fits the drone into this box: below the HUD, above the log and the view controls */}
|
|
2837
|
+
<Box component="div" ref={safeAreaRef} sx={{ position: 'absolute', inset: '92px 16px 72px' }} />
|
|
2838
|
+
{!isFullscreen && (
|
|
2839
|
+
<Box
|
|
2840
|
+
component="div"
|
|
2841
|
+
sx={{ position: 'absolute', insetBlockStart: GUTTER, insetInline: GUTTER, pointerEvents: 'auto' }}
|
|
2842
|
+
>
|
|
2843
|
+
<HudReadout compact />
|
|
2844
|
+
</Box>
|
|
2845
|
+
)}
|
|
2846
|
+
<Box
|
|
2847
|
+
component="div"
|
|
2848
|
+
sx={{
|
|
2849
|
+
position: 'absolute',
|
|
2850
|
+
insetBlockEnd: GUTTER,
|
|
2851
|
+
insetInline: GUTTER,
|
|
2852
|
+
display: 'flex',
|
|
2853
|
+
alignItems: 'flex-end',
|
|
2854
|
+
justifyContent: 'space-between',
|
|
2855
|
+
gap: 1,
|
|
2856
|
+
}}
|
|
2857
|
+
>
|
|
2858
|
+
{!isFullscreen && <MessageLog compact />}
|
|
2859
|
+
<Box component="div" sx={{ flex: 'none', marginInlineStart: 'auto', pointerEvents: 'auto' }}>
|
|
2860
|
+
<ViewDock placement="phone" />
|
|
2861
|
+
</Box>
|
|
2862
|
+
</Box>
|
|
2863
|
+
</Box>
|
|
2864
|
+
|
|
2865
|
+
{!isFullscreen && (
|
|
2866
|
+
<>
|
|
2867
|
+
<Box
|
|
2868
|
+
component="div"
|
|
2869
|
+
sx={{
|
|
2870
|
+
...GLASS_SX,
|
|
2871
|
+
flex: `0 0 ${SHEET_HEIGHT}px`,
|
|
2872
|
+
overflowY: 'auto',
|
|
2873
|
+
overscrollBehavior: 'contain',
|
|
2874
|
+
padding: 2.5,
|
|
2875
|
+
borderInline: 'none',
|
|
2876
|
+
borderBlockEnd: 'none',
|
|
2877
|
+
pointerEvents: 'auto',
|
|
2878
|
+
}}
|
|
2879
|
+
>
|
|
2880
|
+
<TabPanel compact />
|
|
2881
|
+
</Box>
|
|
2882
|
+
<Box
|
|
2883
|
+
component="nav"
|
|
2884
|
+
aria-label="Corvid K6"
|
|
2885
|
+
sx={{
|
|
2886
|
+
flex: `0 0 ${BAR_HEIGHT}px`,
|
|
2887
|
+
bgcolor: 'rgba(12, 14, 18, 0.96)',
|
|
2888
|
+
borderBlockStart: `1px solid ${CORVID.line}`,
|
|
2889
|
+
pointerEvents: 'auto',
|
|
2890
|
+
}}
|
|
2891
|
+
>
|
|
2892
|
+
<DroneTabs orientation="horizontal" />
|
|
2893
|
+
</Box>
|
|
2894
|
+
</>
|
|
2895
|
+
)}
|
|
2896
|
+
</Box>
|
|
2897
|
+
)
|
|
2898
|
+
}
|
|
2899
|
+
```
|
|
2900
|
+
|
|
2901
|
+
### ./src/\_shared/TabPanel.tsx
|
|
2902
|
+
|
|
2903
|
+
```tsx
|
|
2904
|
+
import { Box, Typography } from '@mui/material'
|
|
2905
|
+
|
|
2906
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
2907
|
+
import { DroneFields } from '../_controls/DroneFields.js'
|
|
2908
|
+
import { TAB_PANEL_ID } from './DroneTabs.js'
|
|
2909
|
+
import { CORVID, LABEL_SX } from './tokens.js'
|
|
2910
|
+
|
|
2911
|
+
interface TabPanelProps {
|
|
2912
|
+
readonly compact: boolean
|
|
2913
|
+
}
|
|
2914
|
+
|
|
2915
|
+
// The active tab's heading and controls. The heading is the tab's label from the spec; the controls are whichever
|
|
2916
|
+
// the spec activates for the tab. A viewer gets the same tabs with every control as a line of the spec sheet.
|
|
2917
|
+
export const TabPanel = ({ compact }: TabPanelProps) => {
|
|
2918
|
+
const { tabs, activeTab } = phx.frontend.useTabNavigation()
|
|
2919
|
+
const { accessInfo } = phx.frontend.useAccessService()
|
|
2920
|
+
const readOnly = accessInfo.viewMode
|
|
2921
|
+
const position = `${String((activeTab?.index ?? 0) + 1).padStart(2, '0')} / ${String(tabs.length).padStart(2, '0')}`
|
|
2922
|
+
|
|
2923
|
+
return (
|
|
2924
|
+
<Box
|
|
2925
|
+
component="section"
|
|
2926
|
+
role="tabpanel"
|
|
2927
|
+
id={TAB_PANEL_ID}
|
|
2928
|
+
aria-labelledby={activeTab ? `corvid-tab-${activeTab.index}` : undefined}
|
|
2929
|
+
sx={{ display: 'grid', gap: compact ? 2 : 3 }}
|
|
2930
|
+
>
|
|
2931
|
+
<Box component="header" sx={{ display: 'grid', gap: 0.5 }}>
|
|
2932
|
+
<Typography sx={{ ...LABEL_SX, color: readOnly ? CORVID.cyan : CORVID.lime }}>
|
|
2933
|
+
{position} · {readOnly ? 'SPEC SHEET' : 'CONFIGURE'}
|
|
2934
|
+
</Typography>
|
|
2935
|
+
<Typography
|
|
2936
|
+
component="h2"
|
|
2937
|
+
sx={{ fontFamily: CORVID.heading, fontWeight: 600, fontSize: compact ? 20 : 26, lineHeight: 1.15 }}
|
|
2938
|
+
>
|
|
2939
|
+
{activeTab?.label}
|
|
2940
|
+
</Typography>
|
|
2941
|
+
</Box>
|
|
2942
|
+
<DroneFields readOnly={readOnly} />
|
|
2943
|
+
</Box>
|
|
2944
|
+
)
|
|
2945
|
+
}
|
|
2946
|
+
```
|
|
2947
|
+
|
|
2948
|
+
### ./src/\_shared/ViewDock.tsx
|
|
2949
|
+
|
|
2950
|
+
```tsx
|
|
2951
|
+
import {
|
|
2952
|
+
Box,
|
|
2953
|
+
Divider,
|
|
2954
|
+
IconButton,
|
|
2955
|
+
ThemeProvider,
|
|
2956
|
+
Tooltip,
|
|
2957
|
+
createTheme,
|
|
2958
|
+
toggleButtonClasses,
|
|
2959
|
+
toggleButtonGroupClasses,
|
|
2960
|
+
} from '@mui/material'
|
|
2961
|
+
import type { Theme } from '@mui/material'
|
|
2962
|
+
|
|
2963
|
+
import { FrontendAnalyticsFactory, LayoutSlots } from '@salsita-ai/sdk/configurator'
|
|
2964
|
+
import { PhxIcon } from '@salsita-ai/sdk/ui'
|
|
2965
|
+
|
|
2966
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
2967
|
+
import { CORVID, GLASS_SX } from './tokens.js'
|
|
2968
|
+
|
|
2969
|
+
// The built-in view controls draw with the MUI theme's palette. A dark palette makes them part of the deck, and with
|
|
2970
|
+
// them the list of views a phone opens, which renders in a popper outside the dock.
|
|
2971
|
+
const deckTheme = (outer: Theme) =>
|
|
2972
|
+
createTheme(outer, {
|
|
2973
|
+
palette: {
|
|
2974
|
+
mode: 'dark',
|
|
2975
|
+
primary: { main: CORVID.lime },
|
|
2976
|
+
text: { primary: CORVID.ink, secondary: CORVID.muted },
|
|
2977
|
+
background: { paper: CORVID.panelSolid },
|
|
2978
|
+
divider: CORVID.line,
|
|
2979
|
+
action: { hover: 'rgba(232, 237, 242, 0.06)' },
|
|
2980
|
+
},
|
|
2981
|
+
})
|
|
2982
|
+
|
|
2983
|
+
interface DockButtonProps {
|
|
2984
|
+
readonly label: string
|
|
2985
|
+
readonly icon: string
|
|
2986
|
+
readonly onClick: () => void
|
|
2987
|
+
}
|
|
2988
|
+
|
|
2989
|
+
const DockButton = ({ label, icon, onClick }: DockButtonProps) => {
|
|
2990
|
+
return (
|
|
2991
|
+
<Tooltip title={label}>
|
|
2992
|
+
<IconButton
|
|
2993
|
+
aria-label={label}
|
|
2994
|
+
onClick={onClick}
|
|
2995
|
+
sx={{
|
|
2996
|
+
inlineSize: 36,
|
|
2997
|
+
blockSize: 36,
|
|
2998
|
+
borderRadius: 0,
|
|
2999
|
+
color: CORVID.muted,
|
|
3000
|
+
'&:hover': { color: CORVID.lime, bgcolor: 'rgba(198, 244, 50, 0.08)' },
|
|
3001
|
+
}}
|
|
3002
|
+
>
|
|
3003
|
+
<PhxIcon name={icon} />
|
|
3004
|
+
</IconButton>
|
|
3005
|
+
</Tooltip>
|
|
3006
|
+
)
|
|
3007
|
+
}
|
|
3008
|
+
|
|
3009
|
+
interface ViewDockProps {
|
|
3010
|
+
readonly placement: 'desktop' | 'phone'
|
|
3011
|
+
}
|
|
3012
|
+
|
|
3013
|
+
// The spec's view controls, mounted from `LayoutSlots`, with the camera reset and fullscreen beside them. On a
|
|
3014
|
+
// desktop `DesktopViewControls` is a row of toggles; on a phone `MobileViewControls` is one button that opens the
|
|
3015
|
+
// list of views, left out in fullscreen as the UX guidelines ask. The dock gives the built-in controls a dark theme
|
|
3016
|
+
// and restyles the toggles through their MUI classes.
|
|
3017
|
+
export const ViewDock = ({ placement }: ViewDockProps) => {
|
|
3018
|
+
const resetCamera = phx.frontend.useResetCamera()
|
|
3019
|
+
const isFullscreen = phx.frontend.useIsFullscreenActive()
|
|
3020
|
+
const setFullscreen = phx.frontend.useSetFullscreen()
|
|
3021
|
+
const analytics = phx.frontend.useAnalytics()
|
|
3022
|
+
|
|
3023
|
+
// The same event the built-in fullscreen button records, for entering and for leaving
|
|
3024
|
+
const toggleFullscreen = () => {
|
|
3025
|
+
analytics.push(FrontendAnalyticsFactory.FullScreenModeToggled())
|
|
3026
|
+
setFullscreen(!isFullscreen)
|
|
3027
|
+
}
|
|
3028
|
+
|
|
3029
|
+
return (
|
|
3030
|
+
<ThemeProvider theme={deckTheme}>
|
|
3031
|
+
<Box
|
|
3032
|
+
component="div"
|
|
3033
|
+
role="toolbar"
|
|
3034
|
+
aria-label="View"
|
|
3035
|
+
sx={{
|
|
3036
|
+
...GLASS_SX,
|
|
3037
|
+
display: 'flex',
|
|
3038
|
+
alignItems: 'center',
|
|
3039
|
+
gap: 0.5,
|
|
3040
|
+
padding: 0.5,
|
|
3041
|
+
color: CORVID.ink,
|
|
3042
|
+
[`& .${toggleButtonGroupClasses.root}`]: {
|
|
3043
|
+
gap: 0.5,
|
|
3044
|
+
padding: 0,
|
|
3045
|
+
bgcolor: 'transparent',
|
|
3046
|
+
boxShadow: 'none',
|
|
3047
|
+
borderRadius: 0,
|
|
3048
|
+
},
|
|
3049
|
+
[`& .${toggleButtonClasses.root}`]: {
|
|
3050
|
+
gap: 1,
|
|
3051
|
+
minBlockSize: 36,
|
|
3052
|
+
paddingInline: 1.5,
|
|
3053
|
+
border: 0,
|
|
3054
|
+
borderRadius: 0,
|
|
3055
|
+
color: CORVID.muted,
|
|
3056
|
+
'& .MuiTypography-root': {
|
|
3057
|
+
fontFamily: CORVID.mono,
|
|
3058
|
+
fontSize: 11,
|
|
3059
|
+
letterSpacing: '0.14em',
|
|
3060
|
+
textTransform: 'uppercase',
|
|
3061
|
+
},
|
|
3062
|
+
'&:hover': { color: CORVID.ink, bgcolor: 'rgba(232, 237, 242, 0.06)' },
|
|
3063
|
+
},
|
|
3064
|
+
[`& .${toggleButtonClasses.root}.${toggleButtonClasses.selected}`]: {
|
|
3065
|
+
color: CORVID.graphite,
|
|
3066
|
+
bgcolor: CORVID.lime,
|
|
3067
|
+
'&:hover': { bgcolor: CORVID.lime },
|
|
3068
|
+
},
|
|
3069
|
+
// The phone's view button sits on the dock, not on a paper of its own
|
|
3070
|
+
'& .MuiPaper-root': { bgcolor: 'transparent', boxShadow: 'none' },
|
|
3071
|
+
}}
|
|
3072
|
+
>
|
|
3073
|
+
{placement === 'desktop' && <LayoutSlots.DesktopViewControls />}
|
|
3074
|
+
{placement === 'phone' && !isFullscreen && <LayoutSlots.MobileViewControls />}
|
|
3075
|
+
{(placement === 'desktop' || !isFullscreen) && (
|
|
3076
|
+
<Divider orientation="vertical" flexItem sx={{ borderColor: CORVID.line, marginBlock: 0.5 }} />
|
|
3077
|
+
)}
|
|
3078
|
+
<DockButton label="Reset camera" icon="ArrowCounterClockwise" onClick={resetCamera} />
|
|
3079
|
+
<DockButton
|
|
3080
|
+
label={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}
|
|
3081
|
+
icon={isFullscreen ? 'ArrowsIn' : 'ArrowsOut'}
|
|
3082
|
+
onClick={toggleFullscreen}
|
|
3083
|
+
/>
|
|
3084
|
+
</Box>
|
|
3085
|
+
</ThemeProvider>
|
|
3086
|
+
)
|
|
3087
|
+
}
|
|
3088
|
+
```
|
|
3089
|
+
|
|
3090
|
+
### ./src/\_shared/tokens.ts
|
|
3091
|
+
|
|
3092
|
+
```ts
|
|
3093
|
+
// The flight deck's palette and type. Poppins is loaded by the page; labels and figures use the system's monospace.
|
|
3094
|
+
export const CORVID = {
|
|
3095
|
+
graphite: '#14171c',
|
|
3096
|
+
panel: 'rgba(20, 23, 28, 0.72)',
|
|
3097
|
+
panelSolid: '#191d23',
|
|
3098
|
+
ink: '#e8edf2',
|
|
3099
|
+
muted: 'rgba(232, 237, 242, 0.64)',
|
|
3100
|
+
faint: 'rgba(232, 237, 242, 0.4)',
|
|
3101
|
+
line: 'rgba(232, 237, 242, 0.14)',
|
|
3102
|
+
lime: '#c6f432',
|
|
3103
|
+
cyan: '#3ed6e8',
|
|
3104
|
+
orange: '#ff8a3d',
|
|
3105
|
+
mono: "ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace",
|
|
3106
|
+
heading: "Poppins, 'Helvetica Neue', Arial, sans-serif",
|
|
3107
|
+
} as const
|
|
3108
|
+
|
|
3109
|
+
// Small capitals with wide tracking, for labels and readouts
|
|
3110
|
+
export const LABEL_SX = {
|
|
3111
|
+
fontFamily: CORVID.mono,
|
|
3112
|
+
fontSize: 11,
|
|
3113
|
+
letterSpacing: '0.14em',
|
|
3114
|
+
textTransform: 'uppercase',
|
|
3115
|
+
color: CORVID.muted,
|
|
3116
|
+
} as const
|
|
3117
|
+
|
|
3118
|
+
// A translucent panel that lets the scene show through, blurred
|
|
3119
|
+
export const GLASS_SX = {
|
|
3120
|
+
bgcolor: CORVID.panel,
|
|
3121
|
+
backdropFilter: 'blur(14px)',
|
|
3122
|
+
border: `1px solid ${CORVID.line}`,
|
|
3123
|
+
} as const
|
|
3124
|
+
|
|
3125
|
+
/** Corner brackets: four 1-pixel strokes `size` long at each corner, drawn as background gradients. */
|
|
3126
|
+
export const bracketsSx = (color: string, size = 10) => {
|
|
3127
|
+
const stroke = `linear-gradient(${color}, ${color})`
|
|
3128
|
+
|
|
3129
|
+
return {
|
|
3130
|
+
backgroundImage: Array.from({ length: 8 }, () => stroke).join(', '),
|
|
3131
|
+
backgroundRepeat: 'no-repeat',
|
|
3132
|
+
backgroundSize: Array.from({ length: 4 }, () => `${size}px 1px, 1px ${size}px`).join(', '),
|
|
3133
|
+
backgroundPosition:
|
|
3134
|
+
'left top, left top, right top, right top, left bottom, left bottom, right bottom, right bottom',
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3137
|
+
```
|
|
3138
|
+
|
|
3139
|
+
### ./src/elements/ConfiguratorLayout.tsx
|
|
3140
|
+
|
|
3141
|
+
```tsx
|
|
3142
|
+
import { Box } from '@mui/material'
|
|
3143
|
+
|
|
3144
|
+
import { LayoutSlots } from '@salsita-ai/sdk/configurator'
|
|
3145
|
+
|
|
3146
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
3147
|
+
import { DesktopDeck } from '../_shared/DesktopDeck.js'
|
|
3148
|
+
import { PhoneDeck } from '../_shared/PhoneDeck.js'
|
|
3149
|
+
import { CORVID } from '../_shared/tokens.js'
|
|
3150
|
+
|
|
3151
|
+
// A pro tool over a full-bleed scene: the spec's tabs, a panel with the active tab's controls, a HUD with the
|
|
3152
|
+
// readouts, and the spec's view controls. The canvas stays mounted; what lies over it is the desktop or the phone
|
|
3153
|
+
// deck, and each calls `useSceneSafeArea` itself, so only the one on screen tells the camera where the drone goes.
|
|
3154
|
+
// The layout shows messages in its own log line, so it does not mount `LayoutSlots.Services`.
|
|
3155
|
+
const ConfiguratorLayout = () => {
|
|
3156
|
+
// `null` until the container has been measured; only the scene shows until then
|
|
3157
|
+
const viewports = phx.frontend.useActiveViewports()
|
|
3158
|
+
const isDesktop = viewports?.includes('desktop') ?? false
|
|
3159
|
+
|
|
3160
|
+
return (
|
|
3161
|
+
<Box
|
|
3162
|
+
component="div"
|
|
3163
|
+
sx={{ position: 'absolute', inset: 0, overflow: 'clip', bgcolor: CORVID.graphite, color: CORVID.ink }}
|
|
3164
|
+
>
|
|
3165
|
+
<LayoutSlots.SharedCanvas />
|
|
3166
|
+
{viewports !== null && (isDesktop ? <DesktopDeck /> : <PhoneDeck />)}
|
|
3167
|
+
</Box>
|
|
3168
|
+
)
|
|
3169
|
+
}
|
|
3170
|
+
|
|
3171
|
+
export default phx.frontend.createCustomPhoenixLayoutElement('ConfiguratorLayout', ConfiguratorLayout)
|
|
3172
|
+
```
|
|
3173
|
+
|
|
3174
|
+
### ./src/elements/DroneHangar.tsx
|
|
3175
|
+
|
|
3176
|
+
```tsx
|
|
3177
|
+
import { ContactShadows, Environment, Grid, Lightformer } from '@react-three/drei'
|
|
3178
|
+
|
|
3179
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
3180
|
+
import { CORVID } from '../_shared/tokens.js'
|
|
3181
|
+
|
|
3182
|
+
// The floor lies 320 mm below the drone's centre, so the drone hovers over it on either landing gear
|
|
3183
|
+
const FLOOR_Y = -320
|
|
3184
|
+
|
|
3185
|
+
// The stage named by the spec's `canvasCustomEnvironment`: a dark hangar with a measured floor, a landing ring and a
|
|
3186
|
+
// soft shadow. Light panels exist only in the reflections, so the shell and the anodised motors have something to
|
|
3187
|
+
// mirror and no image has to be downloaded. The scene is in millimetres.
|
|
3188
|
+
export default phx.frontend.createCustomPhoenixLayoutElement('DroneHangar', () => {
|
|
3189
|
+
return (
|
|
3190
|
+
<>
|
|
3191
|
+
<color attach="background" args={[CORVID.graphite]} />
|
|
3192
|
+
<ambientLight intensity={0.6} />
|
|
3193
|
+
<directionalLight position={[-700, 1500, 1100]} intensity={2.4} />
|
|
3194
|
+
<directionalLight position={[1200, 500, 600]} intensity={0.7} />
|
|
3195
|
+
{/* A cool rim light from behind outlines the drone against the dark hangar */}
|
|
3196
|
+
<directionalLight position={[600, 700, -1400]} intensity={1.8} color={CORVID.cyan} />
|
|
3197
|
+
|
|
3198
|
+
<Environment resolution={256} frames={1}>
|
|
3199
|
+
<color attach="background" args={['#262b32']} />
|
|
3200
|
+
{/* A long softbox above, two tall strips at the sides and a dim fill from the front */}
|
|
3201
|
+
<Lightformer form="rect" intensity={3.2} position={[0, 6, 0]} rotation-x={Math.PI / 2} scale={[10, 3, 1]} />
|
|
3202
|
+
<Lightformer form="rect" intensity={2.4} position={[-6, 1.5, 3]} rotation-y={Math.PI / 2} scale={[1, 7, 1]} />
|
|
3203
|
+
<Lightformer form="rect" intensity={1.6} position={[6, 1.5, -2]} rotation-y={-Math.PI / 2} scale={[1, 7, 1]} />
|
|
3204
|
+
<Lightformer form="rect" intensity={1} position={[0, 0, 8]} rotation-y={Math.PI} scale={[8, 2, 1]} />
|
|
3205
|
+
</Environment>
|
|
3206
|
+
|
|
3207
|
+
<Grid
|
|
3208
|
+
position={[0, FLOOR_Y, 0]}
|
|
3209
|
+
infiniteGrid
|
|
3210
|
+
cellSize={50}
|
|
3211
|
+
cellThickness={0.6}
|
|
3212
|
+
cellColor="#262c34"
|
|
3213
|
+
sectionSize={250}
|
|
3214
|
+
sectionThickness={1}
|
|
3215
|
+
sectionColor="#3a4652"
|
|
3216
|
+
fadeDistance={4200}
|
|
3217
|
+
fadeStrength={2}
|
|
3218
|
+
/>
|
|
3219
|
+
{/* The landing ring, drawn just above the floor so the grid does not cut through it */}
|
|
3220
|
+
<mesh rotation-x={-Math.PI / 2} position={[0, FLOOR_Y + 1, 0]}>
|
|
3221
|
+
<ringGeometry args={[440, 445, 128]} />
|
|
3222
|
+
<meshBasicMaterial color={CORVID.lime} transparent opacity={0.5} toneMapped={false} />
|
|
3223
|
+
</mesh>
|
|
3224
|
+
<ContactShadows position={[0, FLOOR_Y + 2, 0]} scale={1600} far={440} blur={2.6} opacity={0.8} resolution={512} />
|
|
3225
|
+
</>
|
|
3226
|
+
)
|
|
3227
|
+
})
|
|
3228
|
+
```
|
|
3229
|
+
|
|
3230
|
+
### ./src/elements/PayloadHotspot.tsx
|
|
3231
|
+
|
|
3232
|
+
```tsx
|
|
3233
|
+
import { Box, ButtonBase } from '@mui/material'
|
|
3234
|
+
|
|
3235
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
3236
|
+
import { CORVID } from '../_shared/tokens.js'
|
|
3237
|
+
|
|
3238
|
+
// Rendered by the spec's `sceneEntityObjectHtmlHotspot` on the payload mount: a target on the drone that opens the
|
|
3239
|
+
// Payload tab. The spec's `$filter` shows it only to the engineer and only outside that tab.
|
|
3240
|
+
export default phx.frontend.createCustomPhoenixEntityElement('PayloadHotspot', () => {
|
|
3241
|
+
const { select } = phx.frontend.useTabNavigation()
|
|
3242
|
+
|
|
3243
|
+
return (
|
|
3244
|
+
<ButtonBase
|
|
3245
|
+
aria-label="Payload: open the Payload tab"
|
|
3246
|
+
onClick={() => select('payload')}
|
|
3247
|
+
sx={{
|
|
3248
|
+
display: 'flex',
|
|
3249
|
+
alignItems: 'center',
|
|
3250
|
+
gap: 1,
|
|
3251
|
+
// The target's centre sits on the mount; the label hangs to its right
|
|
3252
|
+
transform: 'translate(-14px, 0)',
|
|
3253
|
+
color: CORVID.lime,
|
|
3254
|
+
'&:hover .corvid-hotspot-label, &.Mui-focusVisible .corvid-hotspot-label': {
|
|
3255
|
+
bgcolor: CORVID.lime,
|
|
3256
|
+
color: CORVID.graphite,
|
|
3257
|
+
},
|
|
3258
|
+
}}
|
|
3259
|
+
>
|
|
3260
|
+
<Box
|
|
3261
|
+
component="span"
|
|
3262
|
+
aria-hidden
|
|
3263
|
+
sx={{
|
|
3264
|
+
position: 'relative',
|
|
3265
|
+
display: 'grid',
|
|
3266
|
+
placeItems: 'center',
|
|
3267
|
+
inlineSize: 28,
|
|
3268
|
+
blockSize: 28,
|
|
3269
|
+
// A ring that keeps widening and fading, so the target reads as live
|
|
3270
|
+
'&::after': {
|
|
3271
|
+
content: '""',
|
|
3272
|
+
position: 'absolute',
|
|
3273
|
+
inset: 0,
|
|
3274
|
+
borderRadius: '50%',
|
|
3275
|
+
border: `1px solid ${CORVID.lime}`,
|
|
3276
|
+
animation: 'corvid-ping 2s ease-out infinite',
|
|
3277
|
+
},
|
|
3278
|
+
'@keyframes corvid-ping': {
|
|
3279
|
+
from: { transform: 'scale(0.6)', opacity: 0.9 },
|
|
3280
|
+
to: { transform: 'scale(1.6)', opacity: 0 },
|
|
3281
|
+
},
|
|
3282
|
+
}}
|
|
3283
|
+
>
|
|
3284
|
+
<svg viewBox="0 0 28 28" width={28} height={28} fill="none" stroke="currentColor" strokeWidth={1.4}>
|
|
3285
|
+
<circle cx={14} cy={14} r={8} fill="rgba(20, 23, 28, 0.6)" />
|
|
3286
|
+
<path d="M14 2v6M14 20v6M2 14h6M20 14h6" />
|
|
3287
|
+
<circle cx={14} cy={14} r={1.6} fill="currentColor" />
|
|
3288
|
+
</svg>
|
|
3289
|
+
</Box>
|
|
3290
|
+
<Box
|
|
3291
|
+
component="span"
|
|
3292
|
+
className="corvid-hotspot-label"
|
|
3293
|
+
sx={{
|
|
3294
|
+
paddingInline: 1,
|
|
3295
|
+
paddingBlock: 0.5,
|
|
3296
|
+
fontFamily: CORVID.mono,
|
|
3297
|
+
fontSize: 10.5,
|
|
3298
|
+
letterSpacing: '0.16em',
|
|
3299
|
+
whiteSpace: 'nowrap',
|
|
3300
|
+
color: CORVID.lime,
|
|
3301
|
+
bgcolor: 'rgba(20, 23, 28, 0.8)',
|
|
3302
|
+
border: `1px solid ${CORVID.lime}`,
|
|
3303
|
+
transition: 'background-color 160ms, color 160ms',
|
|
3304
|
+
}}
|
|
3305
|
+
>
|
|
3306
|
+
PAYLOAD ›
|
|
3307
|
+
</Box>
|
|
3308
|
+
</ButtonBase>
|
|
3309
|
+
)
|
|
3310
|
+
})
|
|
3311
|
+
```
|
|
3312
|
+
|
|
3313
|
+
### ./src/expressions/drone.ts
|
|
3314
|
+
|
|
3315
|
+
```ts
|
|
3316
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
3317
|
+
|
|
3318
|
+
const MOTORS = { Quad: 4, Hexa: 6 }
|
|
3319
|
+
|
|
3320
|
+
// Prices in US dollars
|
|
3321
|
+
const BASE_PRICE = 8900
|
|
3322
|
+
const FRAME_PRICE = { Quad: 0, Hexa: 2400 }
|
|
3323
|
+
// Per millimetre of arm beyond the shortest, 280 mm
|
|
3324
|
+
const ARM_PRICE_PER_MM = 6
|
|
3325
|
+
const PROP_PRICE = { TwoBlade: 0, ThreeBlade: 60 }
|
|
3326
|
+
const MOTOR_COLOR_PRICE = { Titanium: 0, Lime: 180, Cyan: 180, Ember: 180 }
|
|
3327
|
+
const PAYLOAD_PRICE = { Camera: 4200, Lidar: 11800, Winch: 1900 }
|
|
3328
|
+
const RETRACTABLE_GEAR_PRICE = 650
|
|
3329
|
+
const LIVERY_PRICE = { Graphite: 0, Arctic: 0, Signal: 250 }
|
|
3330
|
+
|
|
3331
|
+
// Weights in grams. The body includes the battery; an arm weighs more the longer it is.
|
|
3332
|
+
const BODY_WEIGHT = 1650
|
|
3333
|
+
const ARM_WEIGHT = (armLength: number) => 60 + 0.25 * armLength
|
|
3334
|
+
const MOTOR_WEIGHT = 160
|
|
3335
|
+
const PROP_WEIGHT = { TwoBlade: 22, ThreeBlade: 30 }
|
|
3336
|
+
const PAYLOAD_WEIGHT = { Camera: 680, Lidar: 920, Winch: 540 }
|
|
3337
|
+
const GEAR_WEIGHT = { fixed: 210, retractable: 340 }
|
|
3338
|
+
|
|
3339
|
+
// How each livery renders: the spec's overrides of the canopy and of the lights read these
|
|
3340
|
+
const LIVERIES = {
|
|
3341
|
+
Graphite: { shell: '#4a525c', clearcoat: 0.5, lights: '#c6f432' },
|
|
3342
|
+
Arctic: { shell: '#e9edf0', clearcoat: 0.8, lights: '#3ed6e8' },
|
|
3343
|
+
Signal: { shell: '#ff6a1a', clearcoat: 0.6, lights: '#f4f7fa' },
|
|
3344
|
+
}
|
|
3345
|
+
|
|
3346
|
+
const MOTOR_COLORS = { Titanium: '#a7adb4', Lime: '#a6d21c', Cyan: '#25b9cc', Ember: '#ff6f24' }
|
|
3347
|
+
|
|
3348
|
+
export default phx.domain.implementCustomExpressions({
|
|
3349
|
+
Drone: {
|
|
3350
|
+
$price: s => {
|
|
3351
|
+
const motors = MOTORS[s.frame]
|
|
3352
|
+
|
|
3353
|
+
return (
|
|
3354
|
+
BASE_PRICE +
|
|
3355
|
+
FRAME_PRICE[s.frame] +
|
|
3356
|
+
(s.armLength - 280) * ARM_PRICE_PER_MM +
|
|
3357
|
+
motors * PROP_PRICE[s.props] +
|
|
3358
|
+
MOTOR_COLOR_PRICE[s.motorColor] +
|
|
3359
|
+
PAYLOAD_PRICE[s.payload] +
|
|
3360
|
+
(s.retractableGear ? RETRACTABLE_GEAR_PRICE : 0) +
|
|
3361
|
+
LIVERY_PRICE[s.livery]
|
|
3362
|
+
)
|
|
3363
|
+
},
|
|
3364
|
+
// The all-up weight, ready to fly: the `detail` component's subtitle reads it
|
|
3365
|
+
$weightLabel: s => {
|
|
3366
|
+
const motors = MOTORS[s.frame]
|
|
3367
|
+
const grams =
|
|
3368
|
+
BODY_WEIGHT +
|
|
3369
|
+
motors * (ARM_WEIGHT(s.armLength) + MOTOR_WEIGHT + PROP_WEIGHT[s.props]) +
|
|
3370
|
+
PAYLOAD_WEIGHT[s.payload] +
|
|
3371
|
+
GEAR_WEIGHT[s.retractableGear ? 'retractable' : 'fixed']
|
|
3372
|
+
|
|
3373
|
+
return `AUW ${(grams / 1000).toFixed(2)} kg`
|
|
3374
|
+
},
|
|
3375
|
+
$shellColor: s => {
|
|
3376
|
+
return LIVERIES[s.livery].shell
|
|
3377
|
+
},
|
|
3378
|
+
$shellClearcoat: s => {
|
|
3379
|
+
return LIVERIES[s.livery].clearcoat
|
|
3380
|
+
},
|
|
3381
|
+
$ledColor: s => {
|
|
3382
|
+
return LIVERIES[s.livery].lights
|
|
3383
|
+
},
|
|
3384
|
+
$motorColor: s => {
|
|
3385
|
+
return MOTOR_COLORS[s.motorColor]
|
|
3386
|
+
},
|
|
3387
|
+
},
|
|
3388
|
+
})
|
|
3389
|
+
```
|
|
3390
|
+
|
|
3391
|
+
### ./src/invariants/drone.ts
|
|
3392
|
+
|
|
3393
|
+
```ts
|
|
3394
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
3395
|
+
|
|
3396
|
+
// The winch's load needs the thrust of six motors. The spec marks the violation `fixable`, so a change that breaks it
|
|
3397
|
+
// is rejected, and the layout's message log shows the spec's message.
|
|
3398
|
+
export default phx.domain.implementCustomInvariants({
|
|
3399
|
+
Drone: {
|
|
3400
|
+
winchNeedsHexa: s => {
|
|
3401
|
+
return s.payload !== 'Winch' || s.frame === 'Hexa'
|
|
3402
|
+
},
|
|
3403
|
+
},
|
|
3404
|
+
})
|
|
3405
|
+
```
|
|
3406
|
+
|
|
3407
|
+
### ./src/transformers/lidar-base-station.ts
|
|
3408
|
+
|
|
3409
|
+
```ts
|
|
3410
|
+
import { msg } from '@lingui/core/macro'
|
|
3411
|
+
|
|
3412
|
+
import { phx } from '../../.phoenix/phx.js'
|
|
3413
|
+
|
|
3414
|
+
// Choosing the LiDAR carries a hint, which reaches the layout as a side-effect message
|
|
3415
|
+
export default phx.frontend.transformerBuilder.when
|
|
3416
|
+
.entity(['Drone'])
|
|
3417
|
+
.modified({ atFields: ['payload'] })
|
|
3418
|
+
.then.overrideMatchingUpdate(({ update }) => {
|
|
3419
|
+
if (update.stateDelta.payload !== 'Lidar') {
|
|
3420
|
+
return [update]
|
|
3421
|
+
}
|
|
3422
|
+
|
|
3423
|
+
return [{ ...update, hint: msg`LiDAR missions need RTK positioning, so a GNSS base station ships with the drone.` }]
|
|
3424
|
+
})
|
|
3425
|
+
.build('lidar-base-station')
|
|
3426
|
+
```
|
|
3427
|
+
|
|
3428
|
+
</details>
|