@volter/sdk 0.0.0-stage → 0.5.203
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/LICENSE +202 -0
- package/NOTICE +20 -0
- package/README.md +38 -3
- package/package.json +510 -4
- package/src/account.ts +210 -0
- package/src/chrome.ts +88 -0
- package/src/client.ts +1646 -0
- package/src/commands.ts +66 -0
- package/src/contributions.ts +619 -0
- package/src/css-numeric-style.ts +97 -0
- package/src/document-probe.ts +282 -0
- package/src/editor-view.ts +225 -0
- package/src/extension.ts +40 -0
- package/src/generations.ts +178 -0
- package/src/host.ts +1157 -0
- package/src/http-transport.browser.ts +14 -0
- package/src/http-transport.node.ts +19 -0
- package/src/index.ts +131 -0
- package/src/kit/CapabilityCoverageSection.tsx +185 -0
- package/src/kit/account-client.ts +333 -0
- package/src/kit/action-registry.ts +317 -0
- package/src/kit/active-product.ts +76 -0
- package/src/kit/active-project.ts +155 -0
- package/src/kit/adapter-editor-config.ts +25 -0
- package/src/kit/adapter-module.ts +7 -0
- package/src/kit/adapter-observation.ts +49 -0
- package/src/kit/animation/animation-clock.ts +479 -0
- package/src/kit/animation/stage-transport.ts +385 -0
- package/src/kit/api/assets.ts +365 -0
- package/src/kit/api/project-open.ts +355 -0
- package/src/kit/api/project-source.ts +180 -0
- package/src/kit/api/project-state.ts +110 -0
- package/src/kit/api/relay.ts +270 -0
- package/src/kit/api/themes.ts +45 -0
- package/src/kit/api-asset-library-wire.ts +45 -0
- package/src/kit/api-base.ts +10 -0
- package/src/kit/api-build.ts +99 -0
- package/src/kit/api-git-wire.ts +56 -0
- package/src/kit/api-logs.ts +92 -0
- package/src/kit/api-project-identity.ts +74 -0
- package/src/kit/api-settings.ts +36 -0
- package/src/kit/api-worktrees.ts +205 -0
- package/src/kit/asset-capabilities.ts +344 -0
- package/src/kit/asset-compare-core.ts +171 -0
- package/src/kit/asset-editor-context.tsx +101 -0
- package/src/kit/asset-events.ts +96 -0
- package/src/kit/asset-inspector-actions.ts +87 -0
- package/src/kit/asset-selection-viewer-registry.ts +113 -0
- package/src/kit/asset-selection.ts +146 -0
- package/src/kit/asset-thumbnails.ts +25 -0
- package/src/kit/asset-viewers.ts +115 -0
- package/src/kit/asset-workflow/asset-import-jobs.ts +106 -0
- package/src/kit/asset-workflow/asset-ledger-backend.ts +126 -0
- package/src/kit/asset-workflow/asset-ledger.ts +156 -0
- package/src/kit/asset-workflow/asset-materialization-report.ts +140 -0
- package/src/kit/asset-workflow/asset-pack-manifest.ts +320 -0
- package/src/kit/asset-workflow/asset-types.ts +142 -0
- package/src/kit/asset-workflow/audio-preview-player.ts +193 -0
- package/src/kit/asset-workflow/audio-waveform.ts +22 -0
- package/src/kit/asset-workflow/cloud-asset-client.ts +263 -0
- package/src/kit/asset-workflow/hosted-asset-materialization.ts +236 -0
- package/src/kit/asset-workflow/image-view-scale.ts +31 -0
- package/src/kit/asset-workflow/import-contract.ts +124 -0
- package/src/kit/asset-workflow/ledger-write-lock.ts +244 -0
- package/src/kit/asset-workflow/pixi-spritesheet.ts +197 -0
- package/src/kit/asset-workflow/preview-resource-lifetime.ts +44 -0
- package/src/kit/asset-workflow/project-asset-commands.ts +20 -0
- package/src/kit/asset-workflow/project-content.ts +288 -0
- package/src/kit/asset-workflow/project-source-index.ts +545 -0
- package/src/kit/asset-workflow/thumbnail-system.ts +256 -0
- package/src/kit/authoring/active-adapter.ts +200 -0
- package/src/kit/authoring/active-systems.ts +422 -0
- package/src/kit/authoring/adapter-key.ts +18 -0
- package/src/kit/authoring/authoring-asset-url.ts +27 -0
- package/src/kit/authoring/bootstrap-state.ts +49 -0
- package/src/kit/authoring/boundary-authoring-adapter.ts +189 -0
- package/src/kit/authoring/canvas-scene-guides.ts +84 -0
- package/src/kit/authoring/composite-authoring-adapter.ts +2109 -0
- package/src/kit/authoring/consumer-actions.ts +531 -0
- package/src/kit/authoring/design-time-layers.ts +852 -0
- package/src/kit/authoring/design-time-mount-registry.ts +244 -0
- package/src/kit/authoring/edit-mode-authoring.ts +637 -0
- package/src/kit/authoring/empty-project-authoring.ts +22 -0
- package/src/kit/authoring/instance-source-menu.ts +135 -0
- package/src/kit/authoring/layered-pick.ts +185 -0
- package/src/kit/authoring/mounted-root-subjects.ts +146 -0
- package/src/kit/authoring/no-authoring-adapter.ts +59 -0
- package/src/kit/authoring/object3d-document-persistence.ts +122 -0
- package/src/kit/authoring/panel-authoring.ts +121 -0
- package/src/kit/authoring/project-authoring-session.ts +105 -0
- package/src/kit/authoring/provenance.ts +99 -0
- package/src/kit/authoring/react-canvas-navigation.ts +259 -0
- package/src/kit/authoring/react-design-canvas-style.ts +20 -0
- package/src/kit/authoring/react-story-board.ts +917 -0
- package/src/kit/authoring/selection-scope.ts +195 -0
- package/src/kit/authoring/shell-document-ops.ts +169 -0
- package/src/kit/authoring/story-board-chrome-fit.ts +107 -0
- package/src/kit/authoring/story-board-presentation.ts +111 -0
- package/src/kit/authoring/three-root.ts +67 -0
- package/src/kit/authoring/viewport-tool-context.ts +73 -0
- package/src/kit/authoring/viewport-tool-owner.ts +38 -0
- package/src/kit/authoring/world-session-state.ts +101 -0
- package/src/kit/authoring-seam-evidence.ts +300 -0
- package/src/kit/availability-tick.ts +66 -0
- package/src/kit/bitmap-label.ts +120 -0
- package/src/kit/boot-routing.ts +392 -0
- package/src/kit/breakpoint-state.ts +43 -0
- package/src/kit/build-identity.ts +16 -0
- package/src/kit/bytes-codec.ts +62 -0
- package/src/kit/cancellation-reason.ts +58 -0
- package/src/kit/canvas-frames.ts +88 -0
- package/src/kit/capture-camera-pose.ts +77 -0
- package/src/kit/capture-size.ts +88 -0
- package/src/kit/chrome-registry.ts +159 -0
- package/src/kit/chrome-slot-registry.ts +91 -0
- package/src/kit/collaboration-client.ts +264 -0
- package/src/kit/collaboration-presence.ts +41 -0
- package/src/kit/command-dispatch.ts +19 -0
- package/src/kit/command-listener.ts +2182 -0
- package/src/kit/command-registry.ts +71 -0
- package/src/kit/component-board-registry.ts +205 -0
- package/src/kit/component-states-registry.ts +199 -0
- package/src/kit/components/AlignToolbar.tsx +204 -0
- package/src/kit/components/ApplicationMenus.tsx +372 -0
- package/src/kit/components/AssetEditorShell.tsx +216 -0
- package/src/kit/components/AssetInspectorToolSection.tsx +124 -0
- package/src/kit/components/BoardRulers.tsx +354 -0
- package/src/kit/components/CanvasAddNodeDialogs.tsx +529 -0
- package/src/kit/components/CanvasSceneViewport.tsx +1195 -0
- package/src/kit/components/ChromeSlot.tsx +20 -0
- package/src/kit/components/CodeView.tsx +470 -0
- package/src/kit/components/CompactInspectorShell.tsx +39 -0
- package/src/kit/components/ConsolePanel.tsx +273 -0
- package/src/kit/components/GameplaySessionTimeline.tsx +295 -0
- package/src/kit/components/InspectionProjection.tsx +932 -0
- package/src/kit/components/Inspector.tsx +270 -0
- package/src/kit/components/InspectorCanvasPreview.tsx +35 -0
- package/src/kit/components/InspectorFieldsSection.tsx +290 -0
- package/src/kit/components/InspectorStoriesSection.tsx +92 -0
- package/src/kit/components/InspectorToolSection.tsx +96 -0
- package/src/kit/components/InspectorTransformSection.tsx +245 -0
- package/src/kit/components/LightExplorerPanel.tsx +433 -0
- package/src/kit/components/MediaProperties.tsx +145 -0
- package/src/kit/components/ProjectHeader.tsx +328 -0
- package/src/kit/components/ReactCanvasControls.tsx +358 -0
- package/src/kit/components/RootSelectionOverlay.tsx +3688 -0
- package/src/kit/components/RootTextEditor.tsx +79 -0
- package/src/kit/components/SaveStatus.tsx +70 -0
- package/src/kit/components/SurfaceCrashBoundary.tsx +105 -0
- package/src/kit/components/SurfaceStateOverlay.tsx +24 -0
- package/src/kit/components/ToolContributionSurfaces.tsx +49 -0
- package/src/kit/components/ToolHost.tsx +380 -0
- package/src/kit/components/Toolbar.tsx +811 -0
- package/src/kit/components/TransientHint.tsx +44 -0
- package/src/kit/components/VersionControlSection.tsx +470 -0
- package/src/kit/components/ViewportOverlaysMenu.tsx +177 -0
- package/src/kit/components/VolterLogo.tsx +18 -0
- package/src/kit/components/WorktreeSwitcher.tsx +712 -0
- package/src/kit/components/account-documents.tsx +1162 -0
- package/src/kit/components/asset-documents.tsx +794 -0
- package/src/kit/components/asset-editor-persistence.ts +216 -0
- package/src/kit/components/asset-selection-section.tsx +545 -0
- package/src/kit/components/asset-thumbnails.tsx +307 -0
- package/src/kit/components/asset-viewers/AudioViewer.tsx +201 -0
- package/src/kit/components/asset-viewers/GenericJsonViewer.tsx +102 -0
- package/src/kit/components/asset-viewers/ImageViewer.tsx +300 -0
- package/src/kit/components/asset-viewers/JsonAssetDocument.tsx +98 -0
- package/src/kit/components/asset-viewers/OnlineAssetDetail.tsx +426 -0
- package/src/kit/components/asset-viewers/SourceAssetViewer.tsx +356 -0
- package/src/kit/components/asset-viewers/SpritesheetSpriteView.tsx +102 -0
- package/src/kit/components/asset-viewers/VideoViewer.tsx +101 -0
- package/src/kit/components/asset-viewers/shader-source.ts +144 -0
- package/src/kit/components/board-guides.ts +150 -0
- package/src/kit/components/canvas-scene-hotkeys.ts +37 -0
- package/src/kit/components/canvas-temporary-pivot.ts +34 -0
- package/src/kit/components/core-utilities.tsx +94 -0
- package/src/kit/components/inspector-preview-section.tsx +223 -0
- package/src/kit/components/inspector-revert-label.ts +20 -0
- package/src/kit/components/inspector-selection.ts +42 -0
- package/src/kit/components/inspector-stories-gating.ts +171 -0
- package/src/kit/components/inspector-transform-subject.ts +11 -0
- package/src/kit/components/inspector-transform.ts +88 -0
- package/src/kit/components/kind-documents.tsx +544 -0
- package/src/kit/components/primitives/DraftColorInput.tsx +74 -0
- package/src/kit/components/project-tool-documents.tsx +402 -0
- package/src/kit/components/scene-documents.tsx +221 -0
- package/src/kit/components/status-contributions.tsx +407 -0
- package/src/kit/components/tool-documents.tsx +302 -0
- package/src/kit/components/tool-schema-form.tsx +262 -0
- package/src/kit/components/use-after-paint.ts +41 -0
- package/src/kit/components/use-project-image-assets.ts +86 -0
- package/src/kit/components/workspace-history.ts +32 -0
- package/src/kit/components/world-documents.tsx +570 -0
- package/src/kit/components/world-overlay-gestures.ts +1939 -0
- package/src/kit/composite-screenshot.ts +2238 -0
- package/src/kit/content-entry-source-registry.ts +184 -0
- package/src/kit/contribution-surfaces.ts +48 -0
- package/src/kit/coverage/canvas-reveal.ts +192 -0
- package/src/kit/coverage/design-time-surfaces.ts +101 -0
- package/src/kit/coverage/ontology-invariants.ts +466 -0
- package/src/kit/coverage/session-vitals.ts +503 -0
- package/src/kit/crash-null-boundary.ts +36 -0
- package/src/kit/creation-site-edit.ts +1491 -0
- package/src/kit/creation-site-registry.ts +160 -0
- package/src/kit/delegate-harness-registry.ts +134 -0
- package/src/kit/document-areas.ts +70 -0
- package/src/kit/document-context-registry.ts +193 -0
- package/src/kit/document-open-registry.ts +200 -0
- package/src/kit/document-play-extension.ts +221 -0
- package/src/kit/document-preview-source.ts +20 -0
- package/src/kit/document-renderer-session.ts +138 -0
- package/src/kit/document-stage-sessions.ts +26 -0
- package/src/kit/document-viewports.ts +120 -0
- package/src/kit/editor-api.ts +46 -0
- package/src/kit/editor-chrome-capture.ts +136 -0
- package/src/kit/editor-commands.ts +176 -0
- package/src/kit/editor-console.ts +580 -0
- package/src/kit/editor-current-view.ts +56 -0
- package/src/kit/editor-document-probe.ts +1166 -0
- package/src/kit/editor-git-client.ts +115 -0
- package/src/kit/editor-hotkeys.ts +728 -0
- package/src/kit/editor-lease-view.ts +39 -0
- package/src/kit/editor-lease.ts +415 -0
- package/src/kit/editor-mode.ts +19 -0
- package/src/kit/editor-notifications.ts +140 -0
- package/src/kit/editor-presence.ts +563 -0
- package/src/kit/editor-presentation-activity.ts +58 -0
- package/src/kit/editor-presentation-notice.ts +42 -0
- package/src/kit/editor-runtime.tsx +147 -0
- package/src/kit/editor-server-response.ts +86 -0
- package/src/kit/editor-session-attribution.ts +85 -0
- package/src/kit/editor-session-mode.ts +54 -0
- package/src/kit/editor-state-facets.ts +74 -0
- package/src/kit/editor-view-presentation.ts +777 -0
- package/src/kit/environment-images.ts +58 -0
- package/src/kit/eyedropper-session.ts +60 -0
- package/src/kit/files/file-provider.ts +62 -0
- package/src/kit/files/project-files.ts +270 -0
- package/src/kit/finders/index.ts +137 -0
- package/src/kit/finders/scenes-from-entrypoint-selection.ts +387 -0
- package/src/kit/frame/frame-parts.ts +30 -0
- package/src/kit/framed-document-capture.ts +34 -0
- package/src/kit/game-globals-prelude.ts +143 -0
- package/src/kit/game-surface-defaults.ts +33 -0
- package/src/kit/gameplay-dom-recording.ts +318 -0
- package/src/kit/gameplay-export-state.ts +14 -0
- package/src/kit/gameplay-replay.ts +417 -0
- package/src/kit/gameplay-session-time.ts +9 -0
- package/src/kit/gameplay-sessions.ts +204 -0
- package/src/kit/hierarchy-component-marks.ts +298 -0
- package/src/kit/hierarchy-internals.ts +197 -0
- package/src/kit/hierarchy-kind-icon.ts +217 -0
- package/src/kit/hierarchy-menu-registry.ts +67 -0
- package/src/kit/hierarchy-node-rows.ts +307 -0
- package/src/kit/hierarchy-panel-view.ts +280 -0
- package/src/kit/hierarchy-projection.ts +76 -0
- package/src/kit/hierarchy-row-media.ts +45 -0
- package/src/kit/hierarchy-row-model.ts +308 -0
- package/src/kit/hierarchy-rows.ts +11 -0
- package/src/kit/hierarchy-walk.ts +86 -0
- package/src/kit/history/editor-session.ts +25 -0
- package/src/kit/history/history-commands.ts +147 -0
- package/src/kit/history/history-delegate.ts +187 -0
- package/src/kit/history/history-limit-notices.ts +43 -0
- package/src/kit/history/history-service.ts +1189 -0
- package/src/kit/history/persistence-coordinator.ts +35 -0
- package/src/kit/history/project-file-history.ts +386 -0
- package/src/kit/history/project-root-history-backends.ts +139 -0
- package/src/kit/history/resource-registry.ts +209 -0
- package/src/kit/history/snapshot-store.ts +103 -0
- package/src/kit/history/source-history-backend.ts +546 -0
- package/src/kit/history-types.ts +124 -0
- package/src/kit/hmr-registration-group.ts +67 -0
- package/src/kit/hmr-stable-react-context.ts +23 -0
- package/src/kit/hotkeys.ts +190 -0
- package/src/kit/inference-diagnostics.ts +69 -0
- package/src/kit/initial-project.ts +80 -0
- package/src/kit/inspection/active-subject.ts +571 -0
- package/src/kit/inspection/active-surface.ts +142 -0
- package/src/kit/inspection/compose-subject.ts +1055 -0
- package/src/kit/inspection/compose.ts +7 -0
- package/src/kit/inspection/display.ts +171 -0
- package/src/kit/inspection/document-subject.ts +109 -0
- package/src/kit/inspection/game-subject.ts +85 -0
- package/src/kit/inspection/null-subject.ts +119 -0
- package/src/kit/inspection/serialize.ts +357 -0
- package/src/kit/inspection/use-active-inspection.ts +180 -0
- package/src/kit/inspection-model.ts +542 -0
- package/src/kit/inspection-node-media.ts +58 -0
- package/src/kit/inspector-presentation.ts +203 -0
- package/src/kit/inspector-property-grouping.ts +64 -0
- package/src/kit/inspector-section-registry.ts +221 -0
- package/src/kit/instance-source-actions.ts +163 -0
- package/src/kit/js-heap.ts +71 -0
- package/src/kit/key-actions.ts +91 -0
- package/src/kit/keymap-presets.ts +428 -0
- package/src/kit/layout-policy.ts +31 -0
- package/src/kit/light-explorer-model.ts +134 -0
- package/src/kit/live-canvas-frame.ts +55 -0
- package/src/kit/live-document.ts +296 -0
- package/src/kit/live-gesture-lock.ts +50 -0
- package/src/kit/live-seam-evidence.ts +11 -0
- package/src/kit/live-session-registry.ts +220 -0
- package/src/kit/live-transition.ts +391 -0
- package/src/kit/manifest-project.ts +107 -0
- package/src/kit/module-fetch-diagnosis.ts +192 -0
- package/src/kit/mount-failure-report.ts +154 -0
- package/src/kit/native-selection-style.ts +497 -0
- package/src/kit/object3d-document-write-policy.ts +137 -0
- package/src/kit/packaged-runtime.ts +108 -0
- package/src/kit/palettes/maya.palette.json +57 -0
- package/src/kit/palettes/substance.palette.json +57 -0
- package/src/kit/performance-profiler.ts +367 -0
- package/src/kit/performance-sources.ts +69 -0
- package/src/kit/photograph-notice.ts +141 -0
- package/src/kit/play-boot-phase.ts +166 -0
- package/src/kit/play-camera-flight.ts +35 -0
- package/src/kit/png-encode.worker.ts +26 -0
- package/src/kit/presentation-surface.ts +248 -0
- package/src/kit/product-command.ts +90 -0
- package/src/kit/project-adapter.ts +1140 -0
- package/src/kit/project-asset-refresh.ts +23 -0
- package/src/kit/project-asset-roots.ts +68 -0
- package/src/kit/project-local-state.ts +151 -0
- package/src/kit/project-manager.ts +243 -0
- package/src/kit/project-module-changes.ts +201 -0
- package/src/kit/project-module-split.ts +270 -0
- package/src/kit/project-play-layers.ts +25 -0
- package/src/kit/project-provenance.ts +115 -0
- package/src/kit/project-ready.ts +42 -0
- package/src/kit/project-shape.ts +68 -0
- package/src/kit/project-tools.ts +107 -0
- package/src/kit/projection-types.ts +44 -0
- package/src/kit/readiness.ts +113 -0
- package/src/kit/renderer-resource-counts.ts +27 -0
- package/src/kit/reported-play-state.ts +90 -0
- package/src/kit/resolve-contributed-command.ts +14 -0
- package/src/kit/resolve-relative-specifier.ts +33 -0
- package/src/kit/retained-document-states.ts +91 -0
- package/src/kit/scene-document-plan.ts +320 -0
- package/src/kit/scene-live-open.ts +210 -0
- package/src/kit/scoped-game-css.ts +152 -0
- package/src/kit/served-url.ts +5 -0
- package/src/kit/session-close.ts +17 -0
- package/src/kit/session-tombstone.ts +127 -0
- package/src/kit/settings/settings-provider.ts +82 -0
- package/src/kit/settings-store.ts +348 -0
- package/src/kit/shell-document-state.ts +27 -0
- package/src/kit/shell-store-door.ts +45 -0
- package/src/kit/shell-store.ts +722 -0
- package/src/kit/source-conflict.ts +122 -0
- package/src/kit/stage-context.ts +377 -0
- package/src/kit/stage-invalidation.ts +25 -0
- package/src/kit/stage-store-registry.ts +69 -0
- package/src/kit/startup-failure.ts +80 -0
- package/src/kit/state-report-deferral.ts +73 -0
- package/src/kit/storage/host-files-storage.ts +97 -0
- package/src/kit/storage/http-storage.ts +174 -0
- package/src/kit/storage/index.ts +75 -0
- package/src/kit/storage/mem-storage.ts +158 -0
- package/src/kit/storage/path-lock.ts +44 -0
- package/src/kit/storage/paths.ts +26 -0
- package/src/kit/storage-types.ts +127 -0
- package/src/kit/stories/StoryPreviewMount.tsx +306 -0
- package/src/kit/stories/compose-project-stories.ts +255 -0
- package/src/kit/stories/prefabs-finder.ts +54 -0
- package/src/kit/stories/prefabs-from-stories.ts +182 -0
- package/src/kit/stories/project-story-regions.ts +24 -0
- package/src/kit/stories/story-capture.ts +579 -0
- package/src/kit/stories/story-declared-medium.ts +126 -0
- package/src/kit/stories/story-discovery.ts +176 -0
- package/src/kit/stories/story-dom-runtime.ts +78 -0
- package/src/kit/stories/story-grouping.ts +111 -0
- package/src/kit/stories/story-mount-turn.ts +27 -0
- package/src/kit/stories/story-presentation.ts +215 -0
- package/src/kit/stories/story-preview-component.ts +7 -0
- package/src/kit/stories/story-registry.ts +530 -0
- package/src/kit/stories-scope.ts +35 -0
- package/src/kit/story-document-openers.ts +36 -0
- package/src/kit/story-thumbnails.ts +47 -0
- package/src/kit/surface-keyboard.ts +101 -0
- package/src/kit/surface-state.ts +135 -0
- package/src/kit/system-seam-evidence.ts +72 -0
- package/src/kit/tab-census.ts +202 -0
- package/src/kit/tab-lifecycle-client.ts +227 -0
- package/src/kit/theme-library.ts +897 -0
- package/src/kit/theme-preference.ts +429 -0
- package/src/kit/three-viewport-presentation.ts +23 -0
- package/src/kit/tool-contribution-play.ts +74 -0
- package/src/kit/tool-loader.ts +1918 -0
- package/src/kit/transform-mode-request.ts +66 -0
- package/src/kit/transient-hint.ts +78 -0
- package/src/kit/transport-strip.tsx +174 -0
- package/src/kit/ui-source/adapter-region-includes.ts +238 -0
- package/src/kit/ui-source/file-region-resolver.ts +302 -0
- package/src/kit/ui-source/inspect.ts +775 -0
- package/src/kit/ui-source/source-write-backend.ts +605 -0
- package/src/kit/ui-source/tier-source-write-backend.ts +279 -0
- package/src/kit/user-local-state.ts +105 -0
- package/src/kit/viewport-activation-timings.ts +840 -0
- package/src/kit/viewport-editor-controls.ts +22 -0
- package/src/kit/viewport-presentation.ts +668 -0
- package/src/kit/viewport-surface-status.tsx +55 -0
- package/src/kit/wait-until.ts +37 -0
- package/src/kit/worker-call-metrics.ts +166 -0
- package/src/kit/workspace-areas.ts +191 -0
- package/src/kit/workspace-aux-commands.ts +11 -0
- package/src/kit/workspace-available-documents.ts +142 -0
- package/src/kit/workspace-core-utilities.ts +31 -0
- package/src/kit/workspace-document-ids.ts +59 -0
- package/src/kit/workspace-document-registry.ts +624 -0
- package/src/kit/workspace-document-restore.ts +146 -0
- package/src/kit/workspace-host-commands.ts +141 -0
- package/src/kit/workspace-persistence-gate.ts +40 -0
- package/src/kit/workspace-play-utilities.ts +44 -0
- package/src/kit/workspace-presets.ts +446 -0
- package/src/kit/workspace-regions.ts +276 -0
- package/src/kit/workspace-static-panels.ts +73 -0
- package/src/kit/workspace-status-registry.ts +121 -0
- package/src/kit/workspace-storage.ts +35 -0
- package/src/kit/workspace-style.ts +226 -0
- package/src/kit/workspace-utility-commands.ts +74 -0
- package/src/kit/workspace-utility-registry.ts +263 -0
- package/src/kit/world-adoption-event.ts +23 -0
- package/src/kit/world-adoption.ts +115 -0
- package/src/kit/world-canvas-viewport-state.ts +35 -0
- package/src/kit/world-document-routing.ts +104 -0
- package/src/kit/world-pan-state.ts +198 -0
- package/src/kit/write-pipe.ts +173 -0
- package/src/layout-arrangements.ts +5 -0
- package/src/layouts.tsx +108 -0
- package/src/looks.ts +16 -0
- package/src/project/output-roots.ts +73 -0
- package/src/project/tab-census.ts +155 -0
- package/src/project-tool-catalog.ts +104 -0
- package/src/selection.tsx +107 -0
- package/src/services.ts +18 -0
- package/src/session/build-report.ts +22 -0
- package/src/session/collaboration-types.ts +262 -0
- package/src/session/command-table.ts +327 -0
- package/src/session/discovery.ts +100 -0
- package/src/session/editor-brand.ts +48 -0
- package/src/session/editor-compatibility.ts +317 -0
- package/src/session/editor-control-lifecycle.ts +68 -0
- package/src/session/editor-control-protocol.ts +5 -0
- package/src/session/entrypoint-selection-readers.ts +66 -0
- package/src/session/entrypoint-selection-source.ts +120 -0
- package/src/session/game-css-scope.ts +30 -0
- package/src/session/hosted-attachment.ts +225 -0
- package/src/session/limited-view.ts +82 -0
- package/src/session/product-create.ts +24 -0
- package/src/session/product-locator.ts +478 -0
- package/src/session/project-module-url.ts +242 -0
- package/src/session/project-serving.ts +164 -0
- package/src/session/project-upgrade.ts +403 -0
- package/src/session/registry-format.ts +210 -0
- package/src/session/relative-path-guard.ts +56 -0
- package/src/session/scoped-game-css.ts +461 -0
- package/src/session/source-glob.ts +15 -0
- package/src/session/tool-contribution-convention.ts +123 -0
- package/src/session/workbench-locator.ts +712 -0
- package/src/session.ts +41 -0
- package/src/share.ts +160 -0
- package/src/source-analysis.ts +28 -0
- package/src/source-authoring.ts +439 -0
- package/src/tools/errors.ts +91 -0
- package/src/tools/provider-execution.ts +70 -0
- package/src/tools/registry.ts +341 -0
- package/src/tools/types.ts +159 -0
- package/src/transport.ts +100 -0
- package/src/types.ts +1693 -0
- package/src/views.ts +164 -0
- package/src/widgets/design-system.ts +93 -0
- package/src/widgets/editor-appearance.ts +151 -0
- package/src/widgets/editor-material.ts +83 -0
- package/src/widgets/icon-set-registry.ts +105 -0
- package/src/widgets/index.ts +71 -0
- package/src/widgets/inspector-widgets/AlignmentGrid.tsx +182 -0
- package/src/widgets/inspector-widgets/AssetSlotPicker.tsx +123 -0
- package/src/widgets/inspector-widgets/BorderEditor.tsx +309 -0
- package/src/widgets/inspector-widgets/ColorPicker.tsx +549 -0
- package/src/widgets/inspector-widgets/CurveEditor.tsx +359 -0
- package/src/widgets/inspector-widgets/FilterEditor.tsx +108 -0
- package/src/widgets/inspector-widgets/FontPicker.tsx +191 -0
- package/src/widgets/inspector-widgets/GradientEditor.tsx +623 -0
- package/src/widgets/inspector-widgets/ScrubbableInput.tsx +180 -0
- package/src/widgets/inspector-widgets/ShadowEditor.tsx +319 -0
- package/src/widgets/inspector-widgets/color-utils.ts +201 -0
- package/src/widgets/inspector-widgets/curve-utils.ts +212 -0
- package/src/widgets/inspector-widgets/index.ts +25 -0
- package/src/widgets/inspector-widgets/shared.tsx +140 -0
- package/src/widgets/interactive-edit-scope.ts +33 -0
- package/src/widgets/patterns/Dialog.tsx +140 -0
- package/src/widgets/patterns/Fields.tsx +44 -0
- package/src/widgets/patterns/List.tsx +25 -0
- package/src/widgets/patterns/StateSurface.tsx +40 -0
- package/src/widgets/patterns/Surfaces.tsx +122 -0
- package/src/widgets/patterns/Tabs.tsx +80 -0
- package/src/widgets/patterns/Toolbar.tsx +72 -0
- package/src/widgets/patterns/Tree.tsx +72 -0
- package/src/widgets/primitives/AnchoredMenu.tsx +260 -0
- package/src/widgets/primitives/Button.tsx +62 -0
- package/src/widgets/primitives/ColorInput.tsx +78 -0
- package/src/widgets/primitives/DraftTextInput.tsx +63 -0
- package/src/widgets/primitives/EditorIcon.tsx +157 -0
- package/src/widgets/primitives/FormControls.tsx +88 -0
- package/src/widgets/primitives/HoverPreview.tsx +96 -0
- package/src/widgets/primitives/JsonInput.tsx +113 -0
- package/src/widgets/primitives/Layout.tsx +100 -0
- package/src/widgets/primitives/Menu.tsx +161 -0
- package/src/widgets/primitives/NumberInput.tsx +169 -0
- package/src/widgets/primitives/Panel.tsx +80 -0
- package/src/widgets/primitives/SectionHeader.tsx +77 -0
- package/src/widgets/primitives/Text.tsx +54 -0
- package/src/widgets/primitives/ThemeRootPortal.tsx +52 -0
- package/src/widgets/primitives/Tooltip.tsx +204 -0
- package/src/widgets/primitives/Vec3Input.tsx +70 -0
- package/src/widgets/primitives/banner-tones.ts +32 -0
- package/src/widgets/primitives/clamp-to-viewport.ts +44 -0
- package/src/widgets/primitives/editor-icons.ts +254 -0
- package/src/widgets/primitives/panel-header-styles.ts +42 -0
- package/src/widgets/theme.ts +2841 -0
- package/src/widgets/z-index.ts +25 -0
|
@@ -0,0 +1,668 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VIEWPORT PRESENTATION — what a 3D viewport DOES, as data: its draw mode, where the light on
|
|
3
|
+
* the model comes from, what is drawn behind the scene, which overlays show, how its tools
|
|
4
|
+
* behave and how its world is oriented. The ruling is
|
|
5
|
+
* `docs/VIEWPORT-STAGE.md` §The ruling (owner, 2026-09-25): the LOOK holds colours only (the
|
|
6
|
+
* palette's `color.viewport`), and light and environment are per-view settings a person toggles,
|
|
7
|
+
* as they are in every target — Blender's shading popover, Godot's Preview Sun and Environment,
|
|
8
|
+
* Unity's Scene-view lighting and skybox toggles.
|
|
9
|
+
*
|
|
10
|
+
* THIS MODULE IS MEDIUM-NEUTRAL (ARCHITECTURE.md rule 5). It names no Three type: a studio
|
|
11
|
+
* light is a colour, an intensity and a direction, and the viewport that draws in a medium
|
|
12
|
+
* applies it. It holds four things:
|
|
13
|
+
* - the TYPES of the four settings;
|
|
14
|
+
* - STUDIO PRESETS, the light rigs a `studio` lighting names by id (Blender's studio lights are
|
|
15
|
+
* data, `release/datafiles/studiolights/studio/*.sl`; so are ours);
|
|
16
|
+
* - STARTING VALUES, declared by whoever builds a kind of stage (an integration or a product),
|
|
17
|
+
* beneath the person's choices and outside the look (`workspace-style.ts` never sees them,
|
|
18
|
+
* so a style switch cannot wipe a toggle and a toggle cannot make a style read as custom);
|
|
19
|
+
* - the PER-VIEW choices a person made, which the document that owns the view persists in its
|
|
20
|
+
* own blob (`workspace-document-restore.ts`), through {@link viewPresentationSnapshot} and
|
|
21
|
+
* {@link restoreViewPresentation}.
|
|
22
|
+
*
|
|
23
|
+
* Resolution, lowest first: the kit's defaults, the stage kind's starting values, the
|
|
24
|
+
* document's override, the person's per-view choices. Lighting and backdrop are kept PER DRAW
|
|
25
|
+
* MODE (Blender keeps its lighting per shading type), so switching to wireframe and back finds
|
|
26
|
+
* the lighting where it was left.
|
|
27
|
+
*/
|
|
28
|
+
import type { ShadingMode } from '../types';
|
|
29
|
+
|
|
30
|
+
/** The draw modes a viewport already has (`ShadingMode`), unchanged. */
|
|
31
|
+
export type ViewportDrawMode = ShadingMode;
|
|
32
|
+
|
|
33
|
+
/** An sRGB hex colour, `#rrggbb`. */
|
|
34
|
+
export type PresentationColor = string;
|
|
35
|
+
|
|
36
|
+
/** A light in a studio preset. `camera` lights move with the view (Blender's studio lights,
|
|
37
|
+
* Unity's headlight); `world` lights stay put. The direction points FROM the light. */
|
|
38
|
+
export interface StudioLight {
|
|
39
|
+
readonly color: PresentationColor;
|
|
40
|
+
readonly intensity: number;
|
|
41
|
+
readonly direction: readonly [number, number, number];
|
|
42
|
+
readonly space: 'camera' | 'world';
|
|
43
|
+
/** Blender's per-light specular colour and wrap (0..1), when the preset carries them. */
|
|
44
|
+
readonly specular?: PresentationColor;
|
|
45
|
+
readonly wrap?: number;
|
|
46
|
+
readonly castShadow?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface StudioPreset {
|
|
50
|
+
readonly id: string;
|
|
51
|
+
readonly title: string;
|
|
52
|
+
readonly lights: readonly StudioLight[];
|
|
53
|
+
readonly ambient: { readonly color: PresentationColor; readonly intensity: number };
|
|
54
|
+
/** Image-based light strength under this preset (0 turns it off). */
|
|
55
|
+
readonly environmentIntensity: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** What in the scene takes over from the view's own lighting, for `auto` (Godot: a directional
|
|
59
|
+
* light or a world environment, and an omni or spot light does not count; `light` is any light,
|
|
60
|
+
* the kit's own rule that a document's authored lighting wins). */
|
|
61
|
+
export type SceneTakeover = 'light' | 'directional-light' | 'environment';
|
|
62
|
+
|
|
63
|
+
export interface PreviewLighting {
|
|
64
|
+
/** Whether the scene's own lights also light the view (Godot's preview adds its sun to them;
|
|
65
|
+
* Blender's Material Preview draws without them: Scene Lights is off by default). */
|
|
66
|
+
readonly sceneLights: boolean;
|
|
67
|
+
readonly sun: {
|
|
68
|
+
readonly enabled: boolean;
|
|
69
|
+
readonly color: PresentationColor;
|
|
70
|
+
readonly energy: number;
|
|
71
|
+
/** Degrees above the horizon and clockwise from north (Godot: 60 and 150). */
|
|
72
|
+
readonly altitude: number;
|
|
73
|
+
readonly azimuth: number;
|
|
74
|
+
/** Shadow distance in scene units; 0 casts none. */
|
|
75
|
+
readonly shadowDistance: number;
|
|
76
|
+
};
|
|
77
|
+
readonly environment: {
|
|
78
|
+
readonly enabled: boolean;
|
|
79
|
+
/** A procedural sky (Godot's preview sky; Unity's default skybox). The horizon is the
|
|
80
|
+
* target's own derivation, so it is stated rather than computed here. */
|
|
81
|
+
readonly sky: {
|
|
82
|
+
readonly top: PresentationColor;
|
|
83
|
+
readonly horizon: PresentationColor;
|
|
84
|
+
readonly ground: PresentationColor;
|
|
85
|
+
/** How fast the horizon gives way to the top colour above it and to the ground below it
|
|
86
|
+
* (Godot's `sky_curve` 0.15 and `ground_curve` 0.02; Unity's horizon haze is a
|
|
87
|
+
* narrower band). */
|
|
88
|
+
readonly topCurve: number;
|
|
89
|
+
readonly groundCurve: number;
|
|
90
|
+
/** A cloud layer over the sky above the horizon (Unreal's `BP_Sky_Sphere`), or `null` for
|
|
91
|
+
* a clear sky (Godot, Unity). `cover` is the share of the sky clouded (0 to 1),
|
|
92
|
+
* `opacity` how much they hide the sky, `scale` how many cloud features span the sky. */
|
|
93
|
+
readonly clouds: { readonly cover: number; readonly opacity: number; readonly scale: number } | null;
|
|
94
|
+
};
|
|
95
|
+
/** A registered environment image (`kit/environment-images`) in place of the sky: the
|
|
96
|
+
* panorama drawn behind the scene and lit by (Blender's Material Preview HDRI; Unreal's
|
|
97
|
+
* preview-scene panorama). `null`: the procedural sky above. */
|
|
98
|
+
readonly image: string | null;
|
|
99
|
+
readonly energy: number;
|
|
100
|
+
/** Rotation about the vertical axis, degrees (Blender's Material Preview HDRI). */
|
|
101
|
+
readonly rotation: number;
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface ViewportLighting {
|
|
106
|
+
/** `studio`: the named preset, ignoring the scene's lights (Blender's Solid).
|
|
107
|
+
* `preview`: the preview sun and environment (Godot; Blender's Material Preview).
|
|
108
|
+
* `scene`: the scene's own lights and environment only (Blender's Rendered; Unity with
|
|
109
|
+
* scene lighting on). */
|
|
110
|
+
readonly source: 'studio' | 'preview' | 'scene';
|
|
111
|
+
/** When set, the view's own lighting gives way to the scene's when the scene has any of
|
|
112
|
+
* `takeover`, and `overridable` says whether the person may turn it back on (Godot: false).
|
|
113
|
+
* `null`: the source never changes on its own (Blender, Unity). */
|
|
114
|
+
readonly auto: { readonly takeover: readonly SceneTakeover[]; readonly overridable: boolean } | null;
|
|
115
|
+
readonly studioPreset: string;
|
|
116
|
+
readonly preview: PreviewLighting;
|
|
117
|
+
readonly tone: { readonly mapper: 'none' | 'filmic' | 'aces' | 'agx'; readonly exposure: number };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface ViewportBackdrop {
|
|
121
|
+
/** `fill`: the look's fill (its colour and form, `color.viewport`).
|
|
122
|
+
* `color`: this view's own colour (Blender's "Viewport" background).
|
|
123
|
+
* `environment`: the lighting environment drawn behind the scene.
|
|
124
|
+
* `scene`: the scene's own background (Unity's skybox toggle; Blender's "World").
|
|
125
|
+
* `transparent`: nothing (a capture over a page). */
|
|
126
|
+
readonly source: 'fill' | 'color' | 'environment' | 'scene' | 'transparent';
|
|
127
|
+
readonly color: PresentationColor;
|
|
128
|
+
/** For `environment`: how much of it shows over the fill, and how blurred (Blender's World
|
|
129
|
+
* Opacity and Blur, both defaulting to 0 in Material Preview). */
|
|
130
|
+
readonly opacity: number;
|
|
131
|
+
readonly blur: number;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface ViewportOverlays {
|
|
135
|
+
readonly grid: {
|
|
136
|
+
readonly visible: boolean;
|
|
137
|
+
/** Minor cells per major line (Blender 10, Godot 8). */
|
|
138
|
+
readonly majorEvery: number;
|
|
139
|
+
readonly planes: { readonly xz: boolean; readonly xy: boolean; readonly yz: boolean };
|
|
140
|
+
};
|
|
141
|
+
/** Any set of selection marks (Unity can show outline and wire together; Godot a box), and
|
|
142
|
+
* a dot at each selected object's origin (Blender's Origins overlay). */
|
|
143
|
+
readonly selection: {
|
|
144
|
+
readonly outline: boolean;
|
|
145
|
+
readonly wire: boolean;
|
|
146
|
+
readonly box: boolean;
|
|
147
|
+
readonly origins: boolean;
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* Which of the world's axis lines are drawn, named by the WORLD's axes: `floor` is the two
|
|
151
|
+
* that lie on the floor (Blender's X and Y), an object states each (Blender's X/Y/Z overlay
|
|
152
|
+
* toggles; Godot draws all three, Y vertical; Unity and Unreal none).
|
|
153
|
+
*/
|
|
154
|
+
readonly axes: 'floor' | { readonly x: boolean; readonly y: boolean; readonly z: boolean };
|
|
155
|
+
/** The navigation gizmo: `interactive` (a click turns the view to that axis — Blender's,
|
|
156
|
+
* Godot's, Unity's), `indicator` (drawn, not clicked — Unreal's axis triad) or `hidden`. */
|
|
157
|
+
readonly navigation: 'interactive' | 'indicator' | 'hidden';
|
|
158
|
+
/** The zoom and pan buttons under the navigation gizmo (Blender's navigation cluster; the
|
|
159
|
+
* game engines draw none), and the camera's position and target along the bottom (the
|
|
160
|
+
* editor's own readout; no reference draws one). Where they sit is the look's. */
|
|
161
|
+
readonly navigationControls: boolean;
|
|
162
|
+
readonly cameraReadout: boolean;
|
|
163
|
+
/** A floor under what the view shows, taking the preview sun's shadow (Unreal's preview
|
|
164
|
+
* floor, a Show toggle; the others show none). It lies at the content's lowest point, as
|
|
165
|
+
* Unreal's asset editors place theirs at the bottom of the mesh's bounds. */
|
|
166
|
+
readonly floor: { readonly visible: boolean; readonly color: PresentationColor };
|
|
167
|
+
/** A 2D view's drafting marks, each a switch in Godot's 2D View menu (Show Rulers, Show Guides,
|
|
168
|
+
* Show Origin, Show Viewport; all on by default there): the rulers along the view's edges, the
|
|
169
|
+
* guides dragged from them, the origin's axis lines, and the game's viewport rectangle (the
|
|
170
|
+
* manifest's `resolution` from the origin). `gridWhenSnapping` is the Grid submenu's Show When
|
|
171
|
+
* Snapping (the grid drawn only while the magnet is on); the Gizmos submenu's `position` (the
|
|
172
|
+
* selected node's origin handle), `lock` and `group` (marks on locked and grouped nodes) and
|
|
173
|
+
* `transformation` (the Move, Rotate and Scale tools' axis gizmo). */
|
|
174
|
+
readonly drafting: {
|
|
175
|
+
readonly rulers: boolean;
|
|
176
|
+
readonly guides: boolean;
|
|
177
|
+
readonly origin: boolean;
|
|
178
|
+
readonly viewport: boolean;
|
|
179
|
+
readonly gridWhenSnapping: boolean;
|
|
180
|
+
readonly position: boolean;
|
|
181
|
+
readonly lock: boolean;
|
|
182
|
+
readonly group: boolean;
|
|
183
|
+
readonly transformation: boolean;
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** How the stage's tools behave (function, ARCHITECTURE.md rule 7): the tool its shelf opens
|
|
188
|
+
* on, and what a box drag selects. Blender opens on Select Box and selects anything a box
|
|
189
|
+
* touches; the editor's own opens on the transform gizmo and selects what a box contains. */
|
|
190
|
+
export interface ViewportInteraction {
|
|
191
|
+
/** The tool the shelf opens on: Select (Blender's Select Box), the combined transform tool
|
|
192
|
+
* (the editor's own, Godot's Select gizmo), or one transform alone (Unity's and Unreal's
|
|
193
|
+
* Move). */
|
|
194
|
+
readonly bootTool: 'select' | 'transform' | 'move' | 'rotate' | 'scale';
|
|
195
|
+
readonly boxSelect: 'contain' | 'touch';
|
|
196
|
+
/**
|
|
197
|
+
* Which handles the combined transform tool offers beside its arrows and rings: scaling,
|
|
198
|
+
* rotating about the view axis (the outer ring), and moving freely (the centre). The
|
|
199
|
+
* editor's own offers all three; Godot's Select gizmo moves and rotates only.
|
|
200
|
+
*/
|
|
201
|
+
readonly transformHandles: {
|
|
202
|
+
readonly scale: boolean;
|
|
203
|
+
readonly viewRotate: boolean;
|
|
204
|
+
readonly freeMove: boolean;
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** How the world the stage presents is oriented: which of its axes is up, which is what the
|
|
209
|
+
* gizmos name their axes by (Blender's world is Z-up, presented through three's Y-up). */
|
|
210
|
+
export interface ViewportWorld {
|
|
211
|
+
readonly upAxis: 'y' | 'z';
|
|
212
|
+
/** Unreal's world is LEFT-handed (Z up, Y to the right of X), so its Y points the other
|
|
213
|
+
* way from Blender's; the gizmos name and draw their axes by it. */
|
|
214
|
+
readonly handedness: 'right' | 'left';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* THE STAGE'S CAMERA: its field of view and the direction it opens from — the view's function,
|
|
219
|
+
* never the look's. Each target holds its angle on its own side:
|
|
220
|
+
* - `vertical`: three's own and Godot's (`editors/3d/default_fov` 70, the camera keeping height);
|
|
221
|
+
* - `horizontal`: Unreal's (90 in its viewport settings);
|
|
222
|
+
* - `larger`: Blender's, whose lens angle is on the region's longer side (sensor fit AUTO);
|
|
223
|
+
* - `smaller`: Unity's (`kDefaultPerspectiveFov` 60, vertical when the view is wider than tall,
|
|
224
|
+
* `SceneView.GetVerticalFOV`).
|
|
225
|
+
* `opening` is the direction from the pivot to the eye when a document states none, in the
|
|
226
|
+
* stage's Y-up frame; a document stage opens along it (the world stage keeps its own framing).
|
|
227
|
+
*/
|
|
228
|
+
export interface ViewportCamera {
|
|
229
|
+
readonly fov: { readonly degrees: number; readonly axis: 'vertical' | 'horizontal' | 'larger' | 'smaller' };
|
|
230
|
+
readonly opening: readonly [number, number, number];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* X-RAY: surfaces drawn see-through at `alpha`, the edges behind them showing (Blender's shading
|
|
235
|
+
* X-Ray, kept per shading type: Solid off at 0.5, Wireframe on at 0 — no surface at all). The
|
|
236
|
+
* stage draws an enabled X-ray at alpha 0 by leaving the surfaces out of the draw; a partial
|
|
237
|
+
* alpha is drawn opaque.
|
|
238
|
+
*/
|
|
239
|
+
export interface ViewportXray {
|
|
240
|
+
readonly enabled: boolean;
|
|
241
|
+
readonly alpha: number;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** One draw mode's lighting, backdrop and X-ray. */
|
|
245
|
+
export interface ViewportModePresentation {
|
|
246
|
+
readonly lighting: ViewportLighting;
|
|
247
|
+
readonly backdrop: ViewportBackdrop;
|
|
248
|
+
readonly xray: ViewportXray;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** A view's whole presentation, resolved. */
|
|
252
|
+
export interface ViewportPresentation extends ViewportModePresentation {
|
|
253
|
+
readonly drawMode: ViewportDrawMode;
|
|
254
|
+
readonly overlays: ViewportOverlays;
|
|
255
|
+
readonly interaction: ViewportInteraction;
|
|
256
|
+
readonly world: ViewportWorld;
|
|
257
|
+
readonly camera: ViewportCamera;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
type DeepPartial<T> = { readonly [K in keyof T]?: T[K] extends readonly unknown[] ? T[K] : T[K] extends object ? DeepPartial<T[K]> : T[K] };
|
|
261
|
+
|
|
262
|
+
/** A partial presentation: starting values, a document override, a person's choices. Lighting
|
|
263
|
+
* and backdrop may be given for every mode (`all`) and per draw mode (`modes`). */
|
|
264
|
+
export interface PresentationLayer {
|
|
265
|
+
readonly drawMode?: ViewportDrawMode;
|
|
266
|
+
readonly overlays?: DeepPartial<ViewportOverlays>;
|
|
267
|
+
readonly interaction?: DeepPartial<ViewportInteraction>;
|
|
268
|
+
readonly world?: Partial<ViewportWorld>;
|
|
269
|
+
readonly camera?: DeepPartial<ViewportCamera>;
|
|
270
|
+
readonly all?: DeepPartial<ViewportModePresentation>;
|
|
271
|
+
readonly modes?: { readonly [M in ViewportDrawMode]?: DeepPartial<ViewportModePresentation> };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// ---- The kit's own defaults ------------------------------------------------------------------
|
|
275
|
+
|
|
276
|
+
/** The kit's studio rig: the lights a document stage wore before they were fitted to Blender —
|
|
277
|
+
* the dressing's key, warm (`#fff3dd`; engine `913444142` made it white), the viewport's own
|
|
278
|
+
* directional and ambient, and the image-based fill at three's default strength (engine
|
|
279
|
+
* `750b13fa9` set 0.3, later 0.85). */
|
|
280
|
+
export const KIT_STUDIO_PRESET: StudioPreset = Object.freeze<StudioPreset>({
|
|
281
|
+
id: 'kit',
|
|
282
|
+
title: 'Studio',
|
|
283
|
+
lights: [
|
|
284
|
+
{ color: '#fff3dd', intensity: 1.9, direction: [-6, -10, 4], space: 'world', castShadow: true },
|
|
285
|
+
{ color: '#ffffff', intensity: 1, direction: [-10, -20, -10], space: 'world' },
|
|
286
|
+
],
|
|
287
|
+
ambient: { color: '#ffffff', intensity: 0.5 },
|
|
288
|
+
environmentIntensity: 1,
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
export const KIT_PRESENTATION: ViewportPresentation = Object.freeze<ViewportPresentation>({
|
|
292
|
+
drawMode: 'solid',
|
|
293
|
+
lighting: {
|
|
294
|
+
source: 'studio',
|
|
295
|
+
// The kit's own rule, kept: a document whose content carries lights wears them.
|
|
296
|
+
auto: { takeover: ['light'], overridable: true },
|
|
297
|
+
studioPreset: KIT_STUDIO_PRESET.id,
|
|
298
|
+
// Godot's preview defaults (`node_3d_editor_plugin.cpp` `_load_default_preview_settings`),
|
|
299
|
+
// the one target that ships a preview sun and sky, so `preview` means something out of the box.
|
|
300
|
+
preview: {
|
|
301
|
+
sceneLights: true,
|
|
302
|
+
sun: { enabled: true, color: '#ffffff', energy: 1, altitude: 60, azimuth: 150, shadowDistance: 100 },
|
|
303
|
+
environment: {
|
|
304
|
+
enabled: true,
|
|
305
|
+
// Top (0.385, 0.454, 0.55) and ground (0.2, 0.169, 0.133); the horizon is Godot's own
|
|
306
|
+
// derivation from them (their mix, pulled halfway to its luminance x 3.333).
|
|
307
|
+
sky: { top: '#62748c', horizon: '#a9abaf', ground: '#332b22', topCurve: 0.15, groundCurve: 0.02, clouds: null },
|
|
308
|
+
image: null,
|
|
309
|
+
energy: 1,
|
|
310
|
+
rotation: 0,
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
tone: { mapper: 'aces', exposure: 1 },
|
|
314
|
+
},
|
|
315
|
+
backdrop: { source: 'fill', color: '#3d3d3d', opacity: 0, blur: 0 },
|
|
316
|
+
xray: { enabled: false, alpha: 1 },
|
|
317
|
+
overlays: {
|
|
318
|
+
grid: { visible: true, majorEvery: 10, planes: { xz: true, xy: false, yz: false } },
|
|
319
|
+
selection: { outline: true, wire: false, box: false, origins: false },
|
|
320
|
+
axes: 'floor',
|
|
321
|
+
navigation: 'interactive',
|
|
322
|
+
navigationControls: true,
|
|
323
|
+
cameraReadout: true,
|
|
324
|
+
floor: { visible: false, color: '#2b3038' },
|
|
325
|
+
drafting: {
|
|
326
|
+
rulers: true,
|
|
327
|
+
guides: true,
|
|
328
|
+
origin: true,
|
|
329
|
+
viewport: true,
|
|
330
|
+
// Godot's 2D default (measured on 4.7.1: no grid until grid snap is on).
|
|
331
|
+
gridWhenSnapping: true,
|
|
332
|
+
position: true,
|
|
333
|
+
lock: true,
|
|
334
|
+
group: true,
|
|
335
|
+
transformation: true,
|
|
336
|
+
},
|
|
337
|
+
},
|
|
338
|
+
interaction: {
|
|
339
|
+
bootTool: 'transform',
|
|
340
|
+
boxSelect: 'contain',
|
|
341
|
+
transformHandles: { scale: true, viewRotate: true, freeMove: true },
|
|
342
|
+
},
|
|
343
|
+
world: { upAxis: 'y', handedness: 'right' },
|
|
344
|
+
// The editor's own camera: three's 50° vertical, opening from the editor's three-quarter view
|
|
345
|
+
// (the Isometric preset's direction, 1 : 0.72 : 1).
|
|
346
|
+
camera: { fov: { degrees: 50, axis: 'vertical' }, opening: [0.6301, 0.4537, 0.6301] },
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// ---- Studio presets ----------------------------------------------------------------------------
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* THE DOCUMENT'S OWN STUDIO — the lights a document hands its stage to turn with the view
|
|
353
|
+
* (`ToolViewportDressing.viewLocked`): Blender's four Solid-mode lights, built by the Blender
|
|
354
|
+
* engine from Blender's own data. The stage draws them in place of a preset's lights; the preset
|
|
355
|
+
* itself carries none, and no image-based light (Blender's Solid mode has none).
|
|
356
|
+
*/
|
|
357
|
+
export const DOCUMENT_STUDIO_PRESET: StudioPreset = Object.freeze<StudioPreset>({
|
|
358
|
+
id: 'document',
|
|
359
|
+
title: "Document's studio",
|
|
360
|
+
lights: [],
|
|
361
|
+
ambient: { color: '#ffffff', intensity: 0 },
|
|
362
|
+
environmentIntensity: 0,
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
const presets = new Map<string, StudioPreset>([
|
|
366
|
+
[KIT_STUDIO_PRESET.id, KIT_STUDIO_PRESET],
|
|
367
|
+
[DOCUMENT_STUDIO_PRESET.id, DOCUMENT_STUDIO_PRESET],
|
|
368
|
+
]);
|
|
369
|
+
|
|
370
|
+
/** Register a studio preset (an integration's own studio lights). A duplicate id throws. */
|
|
371
|
+
export function registerStudioPreset(preset: StudioPreset): () => void {
|
|
372
|
+
if (presets.has(preset.id)) throw new Error(`registerStudioPreset: "${preset.id}" is already registered.`);
|
|
373
|
+
presets.set(preset.id, preset);
|
|
374
|
+
bump();
|
|
375
|
+
return () => {
|
|
376
|
+
if (presets.get(preset.id) !== preset) return;
|
|
377
|
+
presets.delete(preset.id);
|
|
378
|
+
bump();
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/** The preset for an id, or the kit's when the id is not (or no longer) registered. */
|
|
383
|
+
export function studioPreset(id: string): StudioPreset {
|
|
384
|
+
return presets.get(id) ?? KIT_STUDIO_PRESET;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export function studioPresets(): readonly StudioPreset[] {
|
|
388
|
+
return [...presets.values()];
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// ---- The grid switch ------------------------------------------------------------------------------
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* WHETHER A VIEW DRAWS ITS GRID — the person's grid toggle, which is the view's own choice
|
|
395
|
+
* (`overlays.grid.visible`): every door that toggles a grid (the viewport's button, the overlays
|
|
396
|
+
* menu, the Toggle Grid action, `set-grid`, a restored view) writes it here, per view, and the
|
|
397
|
+
* stage and the status read it back. One home; a view the person never touched shows its grid.
|
|
398
|
+
*/
|
|
399
|
+
export function viewGridVisible(viewId: string): boolean {
|
|
400
|
+
return viewPresentation(viewId).overlays.grid.visible;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/** A 2D view's drafting marks (`overlays.drafting`), read and switched like its grid. */
|
|
404
|
+
export function viewDrafting(viewId: string): ViewportOverlays['drafting'] {
|
|
405
|
+
return viewPresentation(viewId).overlays.drafting;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
export function setViewDrafting(viewId: string, choice: Partial<ViewportOverlays['drafting']>): void {
|
|
409
|
+
setViewPresentation(viewId, { overlays: { drafting: choice } });
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
export function setViewGridVisible(viewId: string, visible: boolean): void {
|
|
413
|
+
if (viewGridVisible(viewId) === visible) return;
|
|
414
|
+
setViewPresentation(viewId, { overlays: { grid: { visible } } });
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// ---- View presets --------------------------------------------------------------------------------
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* A NAMED VIEW: a whole presentation a person can put on a view at once — a target engine's
|
|
421
|
+
* default viewport (Godot's preview sun and sky, all three axis lines, the box, its Select
|
|
422
|
+
* gizmo), contributed as data by the package that knows it (`*.view.ts`). It is the view's
|
|
423
|
+
* FUNCTION and light, never its look: the style stays whatever the person wears.
|
|
424
|
+
*/
|
|
425
|
+
export interface ViewPreset {
|
|
426
|
+
readonly id: string;
|
|
427
|
+
readonly title: string;
|
|
428
|
+
readonly layer: PresentationLayer;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const viewPresetsById = new Map<string, ViewPreset>();
|
|
432
|
+
|
|
433
|
+
export function registerViewPreset(preset: ViewPreset): () => void {
|
|
434
|
+
if (viewPresetsById.has(preset.id)) throw new Error(`registerViewPreset: "${preset.id}" is already registered.`);
|
|
435
|
+
viewPresetsById.set(preset.id, preset);
|
|
436
|
+
bump();
|
|
437
|
+
return () => {
|
|
438
|
+
if (viewPresetsById.get(preset.id) !== preset) return;
|
|
439
|
+
viewPresetsById.delete(preset.id);
|
|
440
|
+
bump();
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export function viewPresets(): readonly ViewPreset[] {
|
|
445
|
+
return [...viewPresetsById.values()];
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/** Put a named view on a view: the person's choices become the preset's, whole. `false` for
|
|
449
|
+
* an id no package registered. */
|
|
450
|
+
export function applyViewPreset(viewId: string, presetId: string): boolean {
|
|
451
|
+
const preset = viewPresetsById.get(presetId);
|
|
452
|
+
if (!preset) return false;
|
|
453
|
+
// ONE write, so no listener sees the choices emptied in between; and a view that does not choose
|
|
454
|
+
// a draw mode keeps the one being drawn, rather than falling back to the kit's.
|
|
455
|
+
const current = views.get(viewId) ?? { stageKind: '', documentLayer: null, chosen: {} };
|
|
456
|
+
const drawMode = preset.layer.drawMode ?? current.chosen.drawMode;
|
|
457
|
+
views.set(viewId, { ...current, chosen: { ...preset.layer, ...(drawMode ? { drawMode } : {}) } });
|
|
458
|
+
bump();
|
|
459
|
+
return true;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// ---- Starting values, per kind of stage ---------------------------------------------------------
|
|
463
|
+
|
|
464
|
+
const starting = new Map<string, PresentationLayer>();
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Declare the starting presentation of a kind of stage (`'model'`, `'scene'`, …): the builder's
|
|
468
|
+
* say, beneath every person's choice. One declaration per kind; a second throws, as two builders
|
|
469
|
+
* of one stage would be a composition defect.
|
|
470
|
+
*/
|
|
471
|
+
export function registerStartingPresentation(stageKind: string, layer: PresentationLayer): () => void {
|
|
472
|
+
if (starting.has(stageKind)) {
|
|
473
|
+
throw new Error(`registerStartingPresentation: the "${stageKind}" stage already has starting values.`);
|
|
474
|
+
}
|
|
475
|
+
starting.set(stageKind, layer);
|
|
476
|
+
bump();
|
|
477
|
+
return () => {
|
|
478
|
+
if (starting.get(stageKind) !== layer) return;
|
|
479
|
+
starting.delete(stageKind);
|
|
480
|
+
bump();
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// ---- Per-view choices ---------------------------------------------------------------------------
|
|
485
|
+
|
|
486
|
+
interface ViewRecord {
|
|
487
|
+
readonly stageKind: string;
|
|
488
|
+
readonly documentLayer: PresentationLayer | null;
|
|
489
|
+
readonly chosen: PresentationLayer;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const views = new Map<string, ViewRecord>();
|
|
493
|
+
|
|
494
|
+
/** What the builder of a kind of stage declared as its starting presentation, or null. */
|
|
495
|
+
export function startingPresentation(stageKind: string): PresentationLayer | null {
|
|
496
|
+
return starting.get(stageKind) ?? null;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/** Whether a kind of stage keeps its lighting per draw mode, as Blender's shading types each keep
|
|
500
|
+
* theirs: its builder stated lighting for a draw mode. Then a person's lighting choice is the
|
|
501
|
+
* current mode's, not every mode's. */
|
|
502
|
+
export function stageLightsPerMode(stageKind: string): boolean {
|
|
503
|
+
const modes = starting.get(stageKind)?.modes;
|
|
504
|
+
return modes !== undefined && Object.values(modes).some((mode) => mode?.lighting !== undefined);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/** Bind a view to its kind of stage and its document's override. Idempotent; a person's
|
|
508
|
+
* choices already recorded for the view are kept. */
|
|
509
|
+
export function bindViewPresentation(
|
|
510
|
+
viewId: string,
|
|
511
|
+
stageKind: string,
|
|
512
|
+
documentLayer: PresentationLayer | null = null,
|
|
513
|
+
): void {
|
|
514
|
+
const current = views.get(viewId);
|
|
515
|
+
if (current && current.stageKind === stageKind && current.documentLayer === documentLayer) return;
|
|
516
|
+
views.set(viewId, { stageKind, documentLayer, chosen: current?.chosen ?? {} });
|
|
517
|
+
bump();
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/** Record a person's choice for a view (a toggle, a menu pick), merged over earlier ones. */
|
|
521
|
+
export function setViewPresentation(viewId: string, choice: PresentationLayer): void {
|
|
522
|
+
const current = views.get(viewId) ?? { stageKind: '', documentLayer: null, chosen: {} };
|
|
523
|
+
views.set(viewId, { ...current, chosen: mergeLayers(current.chosen, choice) });
|
|
524
|
+
bump();
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/** Forget a person's choices for a view: it falls back to its document and starting values. */
|
|
528
|
+
export function resetViewPresentation(viewId: string): void {
|
|
529
|
+
const current = views.get(viewId);
|
|
530
|
+
if (!current) return;
|
|
531
|
+
views.set(viewId, { ...current, chosen: {} });
|
|
532
|
+
bump();
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/** The person's choices for a view, as the document persists them. */
|
|
536
|
+
export function viewPresentationSnapshot(viewId: string): PresentationLayer {
|
|
537
|
+
return views.get(viewId)?.chosen ?? {};
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/** Put back the choices a document persisted (`restore` of its blob). */
|
|
541
|
+
export function restoreViewPresentation(viewId: string, chosen: PresentationLayer): void {
|
|
542
|
+
const current = views.get(viewId) ?? { stageKind: '', documentLayer: null, chosen: {} };
|
|
543
|
+
views.set(viewId, { ...current, chosen });
|
|
544
|
+
bump();
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/** How a view is bound — its kind of stage and whether its document states a layer — or `null`
|
|
548
|
+
* when no stage has bound it (a view id no stage draws). */
|
|
549
|
+
export function viewPresentationBinding(
|
|
550
|
+
viewId: string,
|
|
551
|
+
): { readonly stageKind: string; readonly documentLayer: PresentationLayer | null } | null {
|
|
552
|
+
const record = views.get(viewId);
|
|
553
|
+
return record && record.stageKind !== '' ? { stageKind: record.stageKind, documentLayer: record.documentLayer } : null;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/** What a view's LAST DRAW lit by, as its stage reports it: the source after `auto`, the
|
|
557
|
+
* preset, and which light sets were showing. The readout for an agent asking "is the view
|
|
558
|
+
* wearing what its presentation says". */
|
|
559
|
+
export interface ViewDrawReport {
|
|
560
|
+
readonly source: 'studio' | 'preview' | 'scene';
|
|
561
|
+
readonly presetId: string | null;
|
|
562
|
+
readonly presetLights: boolean;
|
|
563
|
+
readonly documentStudio: boolean | null;
|
|
564
|
+
readonly contentLights: number;
|
|
565
|
+
readonly contentLightsDarkened: number;
|
|
566
|
+
readonly environmentIntensity: number;
|
|
567
|
+
/** The environment image this draw showed (`null`: the procedural sky or none), and the one
|
|
568
|
+
* the view names while it is still loading or after it failed to load. */
|
|
569
|
+
readonly environmentImage: string | null;
|
|
570
|
+
readonly environmentImagePending: { readonly id: string; readonly state: 'unregistered' | 'loading' | 'failed' } | null;
|
|
571
|
+
readonly toneMapping: string;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
const drawReports = new Map<string, ViewDrawReport>();
|
|
575
|
+
|
|
576
|
+
export function reportViewDraw(viewId: string, report: ViewDrawReport): void {
|
|
577
|
+
drawReports.set(viewId, report);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
export function viewDrawReport(viewId: string): ViewDrawReport | null {
|
|
581
|
+
return drawReports.get(viewId) ?? null;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/** Every view a stage has bound, by id and kind of stage. */
|
|
585
|
+
export function boundViewPresentations(): readonly { readonly viewId: string; readonly stageKind: string }[] {
|
|
586
|
+
return [...views.entries()]
|
|
587
|
+
.filter(([, record]) => record.stageKind !== '')
|
|
588
|
+
.map(([viewId, record]) => ({ viewId, stageKind: record.stageKind }));
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
export function unbindViewPresentation(viewId: string): void {
|
|
592
|
+
if (views.delete(viewId)) bump();
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/** A view's presentation, resolved: kit, starting values, document, person — lighting and
|
|
596
|
+
* backdrop for the view's current draw mode. */
|
|
597
|
+
export function viewPresentation(viewId: string): ViewportPresentation {
|
|
598
|
+
const record = views.get(viewId);
|
|
599
|
+
const layers = [starting.get(record?.stageKind ?? ''), record?.documentLayer ?? undefined, record?.chosen].filter(
|
|
600
|
+
(layer): layer is PresentationLayer => layer !== undefined,
|
|
601
|
+
);
|
|
602
|
+
return resolvePresentation(layers);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/** Resolve layers (lowest first) over the kit's defaults. Pure; exported for a stage that
|
|
606
|
+
* resolves without a bound view (a capture, a preview). */
|
|
607
|
+
export function resolvePresentation(layers: readonly PresentationLayer[]): ViewportPresentation {
|
|
608
|
+
const drawMode = layers.reduce<ViewportDrawMode>((mode, layer) => layer.drawMode ?? mode, KIT_PRESENTATION.drawMode);
|
|
609
|
+
let mode: ViewportModePresentation = {
|
|
610
|
+
lighting: KIT_PRESENTATION.lighting,
|
|
611
|
+
backdrop: KIT_PRESENTATION.backdrop,
|
|
612
|
+
xray: KIT_PRESENTATION.xray,
|
|
613
|
+
};
|
|
614
|
+
let overlays: ViewportOverlays = KIT_PRESENTATION.overlays;
|
|
615
|
+
let interaction: ViewportInteraction = KIT_PRESENTATION.interaction;
|
|
616
|
+
let world: ViewportWorld = KIT_PRESENTATION.world;
|
|
617
|
+
let camera: ViewportCamera = KIT_PRESENTATION.camera;
|
|
618
|
+
for (const layer of layers) {
|
|
619
|
+
if (layer.all) mode = deepMerge(mode, layer.all);
|
|
620
|
+
const forMode = layer.modes?.[drawMode];
|
|
621
|
+
if (forMode) mode = deepMerge(mode, forMode);
|
|
622
|
+
if (layer.overlays) overlays = deepMerge(overlays, layer.overlays);
|
|
623
|
+
if (layer.interaction) interaction = deepMerge(interaction, layer.interaction);
|
|
624
|
+
if (layer.world) world = deepMerge(world, layer.world);
|
|
625
|
+
if (layer.camera) camera = deepMerge(camera, layer.camera);
|
|
626
|
+
}
|
|
627
|
+
return { drawMode, lighting: mode.lighting, backdrop: mode.backdrop, xray: mode.xray, overlays, interaction, world, camera };
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// ---- Change notification ------------------------------------------------------------------------
|
|
631
|
+
|
|
632
|
+
let version = 0;
|
|
633
|
+
const listeners = new Set<() => void>();
|
|
634
|
+
|
|
635
|
+
function bump(): void {
|
|
636
|
+
version += 1;
|
|
637
|
+
for (const listener of listeners) listener();
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
export function subscribeViewportPresentation(listener: () => void): () => void {
|
|
641
|
+
listeners.add(listener);
|
|
642
|
+
return () => listeners.delete(listener);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
export function viewportPresentationVersion(): number {
|
|
646
|
+
return version;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// ---- Merging -----------------------------------------------------------------------------------
|
|
650
|
+
|
|
651
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
652
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function deepMerge<T>(base: T, patch: unknown): T {
|
|
656
|
+
if (!isPlainObject(base) || !isPlainObject(patch)) return (patch === undefined ? base : patch) as T;
|
|
657
|
+
const out: Record<string, unknown> = { ...base };
|
|
658
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
659
|
+
if (value === undefined) continue;
|
|
660
|
+
out[key] = isPlainObject(value) && isPlainObject(out[key]) ? deepMerge(out[key], value) : value;
|
|
661
|
+
}
|
|
662
|
+
return out as T;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/** Merge two layers: `later` wins, per field, including per draw mode. */
|
|
666
|
+
export function mergeLayers(earlier: PresentationLayer, later: PresentationLayer): PresentationLayer {
|
|
667
|
+
return deepMerge(earlier, later);
|
|
668
|
+
}
|