@volter/sdk 0.0.0-stage → 0.5.204
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 +1168 -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 +329 -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 +669 -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,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asset SELECTION state (W2) — the "single click selects" half of the selection-vs-open
|
|
3
|
+
* contract:
|
|
4
|
+
*
|
|
5
|
+
* - single click in the Project/Assets browser SELECTS the asset and shows
|
|
6
|
+
* compact preview/metadata in the Inspector (the registered
|
|
7
|
+
* `components/asset-selection-section.tsx` section reads this store);
|
|
8
|
+
* - double click / Enter OPENS the full viewer as a center document
|
|
9
|
+
* (`components/asset-documents.tsx`);
|
|
10
|
+
* - drag remains a placement gesture and never touches this store.
|
|
11
|
+
*
|
|
12
|
+
* A tiny module-scope store in the house `useSyncExternalStore` shape
|
|
13
|
+
* (mirrors `inspector-section-registry.ts`): version counter + subscribe +
|
|
14
|
+
* `__resetForTest`. It deliberately does NOT touch the EditorShellStore's entity
|
|
15
|
+
* selection — clicking an asset must never deselect/reselect scene entities
|
|
16
|
+
* (§6.1 "selection never unexpectedly..."); instead the Inspector gives the
|
|
17
|
+
* asset domain precedence while retaining that entity selection, and the asset is auto-cleared
|
|
18
|
+
* when the ENTITY selection changes (the user moved on — see
|
|
19
|
+
* `installAssetSelectionAutoClear`).
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import type { AssetCapabilities } from '@volter/sdk/kit/asset-capabilities';
|
|
23
|
+
|
|
24
|
+
export type AssetKind = 'model' | 'image' | 'video' | 'audio' | 'json' | 'source';
|
|
25
|
+
|
|
26
|
+
export interface OnlineAssetInfo {
|
|
27
|
+
source: 'polyhaven' | 'ambientcg' | 'local';
|
|
28
|
+
id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
thumbnailUrl: string;
|
|
31
|
+
type: string;
|
|
32
|
+
tags: string[];
|
|
33
|
+
license?: string;
|
|
34
|
+
author?: string;
|
|
35
|
+
sourceUrl?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** The §5.1 compact-selection payload — enough for the Inspector section to
|
|
39
|
+
* render preview + metadata without re-deriving browser state. */
|
|
40
|
+
export interface SelectedAsset {
|
|
41
|
+
/** Project serving path, or a stable `online:<source>:<id>` document key. */
|
|
42
|
+
readonly path: string;
|
|
43
|
+
/** Source-owned file name as authored (§8 — `crate.png`). */
|
|
44
|
+
readonly name: string;
|
|
45
|
+
readonly kind: AssetKind | 'component' | 'folder' | 'unknown';
|
|
46
|
+
/** Portable-CSF preview identity for a source-owned prefab/component. */
|
|
47
|
+
readonly componentPreview?: {
|
|
48
|
+
readonly surface: 'three' | 'canvas' | 'dom' | 'unknown';
|
|
49
|
+
readonly sourcePath: string;
|
|
50
|
+
};
|
|
51
|
+
/** File size in bytes when the browser knows it. */
|
|
52
|
+
readonly sizeBytes?: number;
|
|
53
|
+
readonly capabilities?: AssetCapabilities;
|
|
54
|
+
readonly health?: 'healthy' | 'source-only' | 'unsupported';
|
|
55
|
+
readonly sourcePath?: string;
|
|
56
|
+
readonly selectionCount?: number;
|
|
57
|
+
readonly selectionTotalBytes?: number;
|
|
58
|
+
readonly selectionFormats?: readonly string[];
|
|
59
|
+
readonly dependencies?: readonly string[];
|
|
60
|
+
readonly references?: readonly { ownerPath: string; jsonPath: string }[];
|
|
61
|
+
readonly healthCodes?: readonly string[];
|
|
62
|
+
/** Selection came from the external acquisition Library rather than the
|
|
63
|
+
* project's owned file tree. `online` carries the provider identity and is
|
|
64
|
+
* intentionally absent from project assets. */
|
|
65
|
+
readonly origin?: 'project' | 'library';
|
|
66
|
+
readonly online?: OnlineAssetInfo;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let _selected: SelectedAsset | null = null;
|
|
70
|
+
let _version = 0;
|
|
71
|
+
const _listeners = new Set<() => void>();
|
|
72
|
+
|
|
73
|
+
function notifyChanged(): void {
|
|
74
|
+
_version++;
|
|
75
|
+
for (const fn of _listeners) fn();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Subscribe to selection changes. Returns an unsubscribe function. */
|
|
79
|
+
export function subscribeAssetSelection(fn: () => void): () => void {
|
|
80
|
+
_listeners.add(fn);
|
|
81
|
+
return () => {
|
|
82
|
+
_listeners.delete(fn);
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Monotonic change counter — the `getSnapshot` for `useSyncExternalStore`. */
|
|
87
|
+
export function assetSelectionVersion(): number {
|
|
88
|
+
return _version;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** The currently selected asset, or `null`. */
|
|
92
|
+
export function getSelectedAsset(): SelectedAsset | null {
|
|
93
|
+
return _selected;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Select an asset (single-click). Idempotent on the same path. */
|
|
97
|
+
export function setSelectedAsset(asset: SelectedAsset): void {
|
|
98
|
+
if (_selected && JSON.stringify(_selected) === JSON.stringify(asset)) return;
|
|
99
|
+
_selected = asset;
|
|
100
|
+
notifyChanged();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Clear the asset selection (no-op when already clear). */
|
|
104
|
+
export function clearSelectedAsset(): void {
|
|
105
|
+
if (_selected === null) return;
|
|
106
|
+
_selected = null;
|
|
107
|
+
notifyChanged();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** The narrow store surface the auto-clear needs (testable with a fake). */
|
|
111
|
+
export interface AssetSelectionAutoClearStore {
|
|
112
|
+
subscribe(listener: () => void): () => void;
|
|
113
|
+
readonly selectedEntityIds: ReadonlySet<string>;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function entitySelectionKey(store: AssetSelectionAutoClearStore): string {
|
|
117
|
+
return [...store.selectedEntityIds].sort().join('\n');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Auto-clear wiring (installed once per editor session by the layout): when
|
|
122
|
+
* the ENTITY selection changes while an asset is selected, the user has moved
|
|
123
|
+
* on — drop the asset selection so the Inspector goes back to being purely
|
|
124
|
+
* entity-contextual. The entity-selection snapshot is captured on every asset
|
|
125
|
+
* selection SET (not at install time), so only a change that happens AFTER
|
|
126
|
+
* the asset click clears it.
|
|
127
|
+
*/
|
|
128
|
+
export function installAssetSelectionAutoClear(store: AssetSelectionAutoClearStore): () => void {
|
|
129
|
+
let snapshot = entitySelectionKey(store);
|
|
130
|
+
const unsubscribeSelf = subscribeAssetSelection(() => {
|
|
131
|
+
if (_selected) snapshot = entitySelectionKey(store);
|
|
132
|
+
});
|
|
133
|
+
const unsubscribeStore = store.subscribe(() => {
|
|
134
|
+
if (_selected && entitySelectionKey(store) !== snapshot) clearSelectedAsset();
|
|
135
|
+
});
|
|
136
|
+
return () => {
|
|
137
|
+
unsubscribeSelf();
|
|
138
|
+
unsubscribeStore();
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Test-only reset (mirrors `__resetInspectorSectionRegistryForTest`). */
|
|
143
|
+
export function __resetAssetSelectionForTest(): void {
|
|
144
|
+
_selected = null;
|
|
145
|
+
notifyChanged();
|
|
146
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ASSET THUMBNAILS BY THE MEDIUM THAT CAN DRAW THEM — a model file's picture is
|
|
3
|
+
* rendered offscreen by the integration that loads that format. The kit's asset
|
|
4
|
+
* browser asks here whether a file has a rendered thumbnail and queues the
|
|
5
|
+
* render; a file no renderer accepts keeps its icon.
|
|
6
|
+
*/
|
|
7
|
+
export interface AssetThumbnailRenderer {
|
|
8
|
+
accepts(url: string): boolean;
|
|
9
|
+
/** A data URL of the file's picture. */
|
|
10
|
+
render(url: string): Promise<string>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let renderers: readonly AssetThumbnailRenderer[] = [];
|
|
14
|
+
|
|
15
|
+
export function registerAssetThumbnailRenderer(renderer: AssetThumbnailRenderer): () => void {
|
|
16
|
+
renderers = [...renderers, renderer];
|
|
17
|
+
return () => {
|
|
18
|
+
renderers = renderers.filter((existing) => existing !== renderer);
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** The first registered renderer that draws `url`, or null. */
|
|
23
|
+
export function assetThumbnailRenderer(url: string): AssetThumbnailRenderer | null {
|
|
24
|
+
return renderers.find((renderer) => renderer.accepts(url)) ?? null;
|
|
25
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ASSET VIEWERS BY ROUTE — the Asset Lab's native views for the formats a media
|
|
3
|
+
* integration renders (a model, an environment map, a LUT, a shader, a live
|
|
4
|
+
* modeling module, an entity's model). The kit's asset documents render the
|
|
5
|
+
* registered viewer for a route and their own fallback when none is: a
|
|
6
|
+
* composition with no three.js integration still opens the file, as text or as
|
|
7
|
+
* a file with no renderer. One registration per route; `useAssetViewer` re-renders
|
|
8
|
+
* when one arrives, so a document restored before its integration loaded
|
|
9
|
+
* upgrades in place.
|
|
10
|
+
*/
|
|
11
|
+
import { type ComponentType, createElement, type ReactNode, useSyncExternalStore } from 'react';
|
|
12
|
+
|
|
13
|
+
export type AssetViewerRoute =
|
|
14
|
+
| 'model'
|
|
15
|
+
| 'environment'
|
|
16
|
+
| 'lut'
|
|
17
|
+
| 'shader'
|
|
18
|
+
| 'module'
|
|
19
|
+
| 'entity-model'
|
|
20
|
+
/** A compact live preview of a model file (an online asset's detail). */
|
|
21
|
+
| 'model-preview';
|
|
22
|
+
|
|
23
|
+
export interface AssetViewerProps {
|
|
24
|
+
readonly documentId: string;
|
|
25
|
+
/** The asset's project path; empty for an `entity-model`. */
|
|
26
|
+
readonly assetPath: string;
|
|
27
|
+
readonly displayName: string;
|
|
28
|
+
readonly active: boolean;
|
|
29
|
+
/** `entity-model`: the live entity whose model this is. */
|
|
30
|
+
readonly entityId?: string;
|
|
31
|
+
/** `module`: the project root the module is served from. */
|
|
32
|
+
readonly projectRoot?: string;
|
|
33
|
+
/** `model-preview`: a material file applied to the model. */
|
|
34
|
+
readonly materialPath?: string;
|
|
35
|
+
/** What to show when the viewer finds nothing it renders (a `module` that
|
|
36
|
+
* builds no Object3D). */
|
|
37
|
+
readonly fallback?: ReactNode;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const viewers = new Map<AssetViewerRoute, ComponentType<AssetViewerProps>>();
|
|
41
|
+
const listeners = new Set<() => void>();
|
|
42
|
+
let version = 0;
|
|
43
|
+
|
|
44
|
+
function changed(): void {
|
|
45
|
+
version += 1;
|
|
46
|
+
for (const listener of listeners) listener();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Register the viewer for one route. Returns the removal. */
|
|
50
|
+
export function registerAssetViewer(
|
|
51
|
+
route: AssetViewerRoute,
|
|
52
|
+
viewer: ComponentType<AssetViewerProps>,
|
|
53
|
+
): () => void {
|
|
54
|
+
const existing = viewers.get(route);
|
|
55
|
+
if (existing && existing !== viewer) throw new Error(`An asset viewer for "${route}" is already registered.`);
|
|
56
|
+
viewers.set(route, viewer);
|
|
57
|
+
changed();
|
|
58
|
+
return () => {
|
|
59
|
+
if (viewers.get(route) !== viewer) return;
|
|
60
|
+
viewers.delete(route);
|
|
61
|
+
changed();
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function subscribe(listener: () => void): () => void {
|
|
66
|
+
listeners.add(listener);
|
|
67
|
+
return () => listeners.delete(listener);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The registered viewer for `route`, or null; re-renders when one arrives. */
|
|
71
|
+
export function useAssetViewer(route: AssetViewerRoute): ComponentType<AssetViewerProps> | null {
|
|
72
|
+
useSyncExternalStore(subscribe, () => version, () => version);
|
|
73
|
+
return viewers.get(route) ?? null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** The registered viewer for `route` with `props`, or `whenUnregistered` when this
|
|
77
|
+
* composition registered none. `props.fallback` still reaches the viewer. */
|
|
78
|
+
export function AssetViewerSlot({
|
|
79
|
+
route,
|
|
80
|
+
whenUnregistered,
|
|
81
|
+
...props
|
|
82
|
+
}: AssetViewerProps & { readonly route: AssetViewerRoute; readonly whenUnregistered: ReactNode }): ReactNode {
|
|
83
|
+
const Viewer = useAssetViewer(route);
|
|
84
|
+
return Viewer ? createElement(Viewer, props) : whenUnregistered;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* A JSON FORMAT A MEDIUM RECOGNIZES BY ITS CONTENT — three.quarks writes plain
|
|
89
|
+
* Object3D JSON with no extension of its own, so only the file's structure can
|
|
90
|
+
* say it is a particle system. The kit's JSON document asks each registered
|
|
91
|
+
* recognizer in turn and shows the generic JSON viewer when none claims it.
|
|
92
|
+
*/
|
|
93
|
+
export interface JsonContentViewer {
|
|
94
|
+
/** The parsed content this viewer renders, or null when it is not its format. */
|
|
95
|
+
recognize(assetPath: string, signal: AbortSignal): Promise<unknown | null>;
|
|
96
|
+
readonly Viewer: ComponentType<AssetViewerProps & { readonly content: unknown }>;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let jsonViewers: readonly JsonContentViewer[] = [];
|
|
100
|
+
|
|
101
|
+
export function registerJsonContentViewer(viewer: JsonContentViewer): () => void {
|
|
102
|
+
jsonViewers = [...jsonViewers, viewer];
|
|
103
|
+
changed();
|
|
104
|
+
return () => {
|
|
105
|
+
if (!jsonViewers.includes(viewer)) return;
|
|
106
|
+
jsonViewers = jsonViewers.filter((existing) => existing !== viewer);
|
|
107
|
+
changed();
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** The registered JSON recognizers, in registration order — a new array only
|
|
112
|
+
* when the set changes, so an effect keyed on it re-runs exactly then. */
|
|
113
|
+
export function useJsonContentViewers(): readonly JsonContentViewer[] {
|
|
114
|
+
return useSyncExternalStore(subscribe, () => jsonViewers, () => jsonViewers);
|
|
115
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { connectEvents } from '@volter/sdk/kit/editor-api';
|
|
2
|
+
import type { AssetImportStage } from './import-contract';
|
|
3
|
+
|
|
4
|
+
export interface AssetImportJob {
|
|
5
|
+
id: string;
|
|
6
|
+
label: string;
|
|
7
|
+
stage: AssetImportStage;
|
|
8
|
+
loaded: number;
|
|
9
|
+
total: number;
|
|
10
|
+
status: 'running' | 'imported' | 'skipped' | 'failed' | 'cancelled';
|
|
11
|
+
message?: string;
|
|
12
|
+
controller: AbortController;
|
|
13
|
+
retry?: () => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const jobs = new Map<string, AssetImportJob>();
|
|
17
|
+
const listeners = new Set<() => void>();
|
|
18
|
+
let version = 0;
|
|
19
|
+
function notify(): void {
|
|
20
|
+
version++;
|
|
21
|
+
for (const listener of listeners) listener();
|
|
22
|
+
}
|
|
23
|
+
export function assetImportJobsVersion(): number {
|
|
24
|
+
return version;
|
|
25
|
+
}
|
|
26
|
+
export function subscribeAssetImportJobs(listener: () => void): () => void {
|
|
27
|
+
listeners.add(listener);
|
|
28
|
+
return () => {
|
|
29
|
+
listeners.delete(listener);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function assetImportJobs(): readonly AssetImportJob[] {
|
|
33
|
+
return [...jobs.values()];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function beginAssetImportJob(id: string, label: string): AssetImportJob {
|
|
37
|
+
const existing = jobs.get(id);
|
|
38
|
+
if (existing?.status === 'running') return existing;
|
|
39
|
+
const job: AssetImportJob = {
|
|
40
|
+
id,
|
|
41
|
+
label,
|
|
42
|
+
stage: 'fetch',
|
|
43
|
+
loaded: 0,
|
|
44
|
+
total: 0,
|
|
45
|
+
status: 'running',
|
|
46
|
+
controller: new AbortController(),
|
|
47
|
+
};
|
|
48
|
+
jobs.set(id, job);
|
|
49
|
+
notify();
|
|
50
|
+
return job;
|
|
51
|
+
}
|
|
52
|
+
export function updateAssetImportJob(
|
|
53
|
+
id: string,
|
|
54
|
+
patch: Partial<Omit<AssetImportJob, 'id' | 'controller'>>,
|
|
55
|
+
): void {
|
|
56
|
+
const job = jobs.get(id);
|
|
57
|
+
if (!job) return;
|
|
58
|
+
Object.assign(job, patch);
|
|
59
|
+
notify();
|
|
60
|
+
}
|
|
61
|
+
export function cancelAssetImportJob(id: string): void {
|
|
62
|
+
const job = jobs.get(id);
|
|
63
|
+
if (!job || job.status !== 'running') return;
|
|
64
|
+
job.controller.abort(new DOMException('Import cancelled', 'AbortError'));
|
|
65
|
+
job.status = 'cancelled';
|
|
66
|
+
job.message = 'Cancelled before project commit.';
|
|
67
|
+
notify();
|
|
68
|
+
}
|
|
69
|
+
export function clearFinishedAssetImportJobs(): void {
|
|
70
|
+
for (const [id, job] of jobs) if (job.status !== 'running') jobs.delete(id);
|
|
71
|
+
notify();
|
|
72
|
+
}
|
|
73
|
+
export function resetAssetImportJobs(): void {
|
|
74
|
+
jobs.clear();
|
|
75
|
+
notify();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Keep jobs progressing independently of whether the Library document is mounted. */
|
|
79
|
+
export function connectAssetImportProgressEvents(): () => void {
|
|
80
|
+
const source = connectEvents();
|
|
81
|
+
const listener = (event: MessageEvent) => {
|
|
82
|
+
try {
|
|
83
|
+
const update = JSON.parse(event.data as string) as {
|
|
84
|
+
jobId?: string;
|
|
85
|
+
stage?: AssetImportStage;
|
|
86
|
+
loaded?: number;
|
|
87
|
+
total?: number;
|
|
88
|
+
message?: string;
|
|
89
|
+
};
|
|
90
|
+
if (!update.jobId || !update.stage) return;
|
|
91
|
+
updateAssetImportJob(update.jobId, {
|
|
92
|
+
stage: update.stage,
|
|
93
|
+
loaded: Math.max(0, update.loaded ?? 0),
|
|
94
|
+
total: Math.max(0, update.total ?? 0),
|
|
95
|
+
...(update.message ? { message: update.message } : {}),
|
|
96
|
+
});
|
|
97
|
+
} catch {
|
|
98
|
+
// Optional progress cannot break a running import.
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
source.addEventListener('asset-import-progress', listener);
|
|
102
|
+
return () => {
|
|
103
|
+
source.removeEventListener('asset-import-progress', listener);
|
|
104
|
+
source.close();
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `StorageBackend` side of the asset provenance ledger (D-AP3) — the
|
|
3
|
+
* browser/isomorphic sibling of `packages/editor-core/server/asset-ledger-store.ts`,
|
|
4
|
+
* which reads/writes `.volter/assets.json` via `node:fs` for the local
|
|
5
|
+
* (HttpStorage-backed) Node editor. Every environment materializes through the
|
|
6
|
+
* SAME record shape (`../asset-workflow/asset-ledger.ts`'s pure schema) — only
|
|
7
|
+
* the I/O side differs, exactly like `storage/seed.ts`'s `readProjectManifest`
|
|
8
|
+
* reading `volter.project.json` through a `StorageBackend` instead of `node:fs`.
|
|
9
|
+
*
|
|
10
|
+
* Used by `hosted-asset-materialization.ts` (D-AP4 — a project's declared
|
|
11
|
+
* packs).
|
|
12
|
+
*
|
|
13
|
+
* Concurrency: the same two-half protection the Node side takes, over this
|
|
14
|
+
* side's I/O (see `./ledger-write-lock.ts`). The in-realm chain covers one tab
|
|
15
|
+
* — several declared entries materializing in one pass, the Library panel and a
|
|
16
|
+
* reopen retry overlapping — and the `.lock` file covers the case only another
|
|
17
|
+
* realm can see: two tabs open on the same project, both retrying their
|
|
18
|
+
* declared packs. `writeIfAbsent` is honest about not being O_EXCL on
|
|
19
|
+
* handle APIs (it "serializes same-origin writers and rechecks at handle
|
|
20
|
+
* acquisition"), which is exactly the guarantee that matters here — the racing
|
|
21
|
+
* writers ARE same-origin — so it is the strongest primitive available and not
|
|
22
|
+
* a claim of more.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { withPathLock } from '../storage/path-lock';
|
|
26
|
+
import type { StorageBackend } from '@volter/sdk/kit/storage-types';
|
|
27
|
+
import {
|
|
28
|
+
ASSET_LEDGER_PATH,
|
|
29
|
+
type AssetLedger,
|
|
30
|
+
type AssetLedgerEntry,
|
|
31
|
+
normalizeLedgerPath,
|
|
32
|
+
parseAssetLedgerJson,
|
|
33
|
+
serializeAssetLedger,
|
|
34
|
+
sourceHashFromSha256,
|
|
35
|
+
upsertAssetLedgerEntry,
|
|
36
|
+
} from './asset-ledger';
|
|
37
|
+
import { createSerializer, withExclusiveLock } from './ledger-write-lock';
|
|
38
|
+
|
|
39
|
+
/** Read the ledger via `backend`. Absent → `{}`. Corrupt → throws (same
|
|
40
|
+
* contract as the Node `asset-ledger-store.ts` — a provenance record that
|
|
41
|
+
* silently drops entries is worse than no record). */
|
|
42
|
+
export async function readAssetLedgerFromBackend(backend: StorageBackend): Promise<AssetLedger> {
|
|
43
|
+
if (!(await backend.exists(ASSET_LEDGER_PATH))) return {};
|
|
44
|
+
return parseAssetLedgerJson(await backend.read(ASSET_LEDGER_PATH));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function writeAssetLedgerToBackend(
|
|
48
|
+
backend: StorageBackend,
|
|
49
|
+
ledger: AssetLedger,
|
|
50
|
+
): Promise<void> {
|
|
51
|
+
await backend.write(ASSET_LEDGER_PATH, serializeAssetLedger(ledger));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface BackendMaterializationRecord {
|
|
55
|
+
/** Project-relative destination, e.g. `public/asset-library/local/x/y.glb`. */
|
|
56
|
+
readonly destPath: string;
|
|
57
|
+
/** `source:id` — the library's identity for this asset. */
|
|
58
|
+
readonly key: string;
|
|
59
|
+
/** License string as the library reported it, if it reported one. */
|
|
60
|
+
readonly license?: string | undefined;
|
|
61
|
+
/** The LIBRARY's sha256 of the source bytes. Omit when the library has
|
|
62
|
+
* none — never substitute a hash of the bytes just written (anti-shim). */
|
|
63
|
+
readonly sourceSha256?: string | undefined;
|
|
64
|
+
readonly materializedAt?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const LEDGER_LOCK_PATH = `${ASSET_LEDGER_PATH}.lock`;
|
|
68
|
+
const serializeLedgerWrite = createSerializer();
|
|
69
|
+
/** Stable per-backend key for the in-realm chain (`backend.id` is shared by
|
|
70
|
+
* every instance of a backend TYPE, so it cannot key this on its own). */
|
|
71
|
+
const backendKeys = new WeakMap<StorageBackend, string>();
|
|
72
|
+
let nextBackendKey = 0;
|
|
73
|
+
|
|
74
|
+
function ledgerWriteKey(backend: StorageBackend): string {
|
|
75
|
+
const existing = backendKeys.get(backend);
|
|
76
|
+
if (existing !== undefined) return existing;
|
|
77
|
+
nextBackendKey += 1;
|
|
78
|
+
const key = `${backend.id}#${nextBackendKey}`;
|
|
79
|
+
backendKeys.set(backend, key);
|
|
80
|
+
return key;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Record one materialization in the project's ledger, via `backend`. */
|
|
84
|
+
export async function recordAssetMaterializationInBackend(
|
|
85
|
+
backend: StorageBackend,
|
|
86
|
+
record: BackendMaterializationRecord,
|
|
87
|
+
): Promise<void> {
|
|
88
|
+
const entry: AssetLedgerEntry = {
|
|
89
|
+
key: record.key,
|
|
90
|
+
...(record.license ? { license: record.license } : {}),
|
|
91
|
+
...(record.sourceSha256 ? { sourceHash: sourceHashFromSha256(record.sourceSha256) } : {}),
|
|
92
|
+
materializedAt: record.materializedAt ?? new Date().toISOString(),
|
|
93
|
+
};
|
|
94
|
+
await serializeLedgerWrite(ledgerWriteKey(backend), () =>
|
|
95
|
+
withExclusiveLock(
|
|
96
|
+
{
|
|
97
|
+
createIfAbsent: (content) => backend.writeIfAbsent(LEDGER_LOCK_PATH, content),
|
|
98
|
+
read: async () =>
|
|
99
|
+
(await backend.exists(LEDGER_LOCK_PATH)) ? backend.read(LEDGER_LOCK_PATH) : null,
|
|
100
|
+
// Compare-and-delete (see `./ledger-write-lock.ts`'s `ExclusiveLockIO`),
|
|
101
|
+
// and its three steps must not interleave with another realm's — so
|
|
102
|
+
// they run under the SAME path lock `HandleStorage.writeIfAbsent` takes
|
|
103
|
+
// (`../storage/path-lock.ts`). That makes this side genuinely atomic
|
|
104
|
+
// against every same-origin tab, so it needs no `rename` takeover
|
|
105
|
+
// like the `node:fs` side.
|
|
106
|
+
// NOT covered: `HttpStorage`, whose peer writer is the Node server/CLI
|
|
107
|
+
// rather than another tab — Web Locks cannot reach a separate process,
|
|
108
|
+
// and neither can anything else available in the page.
|
|
109
|
+
removeIfHeldBy: (expected) =>
|
|
110
|
+
withPathLock(backend.id, LEDGER_LOCK_PATH, async () => {
|
|
111
|
+
if (!(await backend.exists(LEDGER_LOCK_PATH))) return;
|
|
112
|
+
if ((await backend.read(LEDGER_LOCK_PATH)) !== expected) return;
|
|
113
|
+
await backend.remove(LEDGER_LOCK_PATH);
|
|
114
|
+
}),
|
|
115
|
+
},
|
|
116
|
+
async () => {
|
|
117
|
+
const ledger = await readAssetLedgerFromBackend(backend);
|
|
118
|
+
await writeAssetLedgerToBackend(
|
|
119
|
+
backend,
|
|
120
|
+
upsertAssetLedgerEntry(ledger, normalizeLedgerPath(record.destPath), entry),
|
|
121
|
+
);
|
|
122
|
+
},
|
|
123
|
+
{ owner: backend.id },
|
|
124
|
+
),
|
|
125
|
+
);
|
|
126
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The project-owned asset provenance ledger — `.volter/assets.json` (D-AP3).
|
|
3
|
+
*
|
|
4
|
+
* Assets follow the capability model: the asset library is the SOURCE, a
|
|
5
|
+
* project owns its COPIES. Every materialization — the editor's Library-panel
|
|
6
|
+
* download, the template/example `asset-packs:sync` script, the hosted seed —
|
|
7
|
+
* records the copy here, keyed by PROJECT-RELATIVE destination path.
|
|
8
|
+
*
|
|
9
|
+
* One ledger, not per-file sidecars: sidecars pollute asset directories and
|
|
10
|
+
* break tooling that globs them.
|
|
11
|
+
*
|
|
12
|
+
* The ledger is the license/attribution record and the drift input. Comparing a
|
|
13
|
+
* file's current hash to `sourceHash` distinguishes "the user edited their copy"
|
|
14
|
+
* (legitimate; git reports it) from "the library moved" (reported, never
|
|
15
|
+
* auto-applied — there is deliberately no `volter asset update`).
|
|
16
|
+
*
|
|
17
|
+
* ANTI-SHIM: `license` and `sourceHash` are OPTIONAL and are recorded only when
|
|
18
|
+
* the library actually supplied them. We never hash the bytes we just wrote and
|
|
19
|
+
* call the result a `sourceHash` — that would make every copy look pristine
|
|
20
|
+
* forever and destroy the drift signal the field exists for. A materialization
|
|
21
|
+
* path with no library-supplied hash records the key and the timestamp and
|
|
22
|
+
* nothing else. Today that is the polyhaven/ambientcg remote proxy, whose
|
|
23
|
+
* upstream APIs publish no per-file digest; the local/cloud catalog paths
|
|
24
|
+
* (34,412 assets) carry `sha256` and populate the field.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { isContainedRelativePath } from '@volter/sdk/session/relative-path-guard';
|
|
28
|
+
import { z } from 'zod';
|
|
29
|
+
|
|
30
|
+
/** Project-relative location of the ledger. */
|
|
31
|
+
export const ASSET_LEDGER_PATH = '.volter/assets.json';
|
|
32
|
+
|
|
33
|
+
/** The library's asset identity: `source:id` (see `onlineAssetKey`). */
|
|
34
|
+
export function assetKey(source: string, id: string): string {
|
|
35
|
+
return `${source}:${id}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const AssetLedgerEntrySchema = z
|
|
39
|
+
.object({
|
|
40
|
+
/** `source:id` — the library's own identity for the asset. */
|
|
41
|
+
key: z.string().min(1),
|
|
42
|
+
/** SPDX-ish license string as the library reported it, when it reported one. */
|
|
43
|
+
license: z.string().min(1).optional(),
|
|
44
|
+
/** The LIBRARY's digest of the source bytes — never a re-hash of our copy. */
|
|
45
|
+
sourceHash: z
|
|
46
|
+
.string()
|
|
47
|
+
.regex(/^sha256-[a-f0-9]{64}$/, 'sourceHash must be "sha256-<64 hex>"')
|
|
48
|
+
.optional(),
|
|
49
|
+
/** ISO-8601 instant the copy was written into this project. */
|
|
50
|
+
materializedAt: z.string().datetime(),
|
|
51
|
+
})
|
|
52
|
+
.strict();
|
|
53
|
+
|
|
54
|
+
export const AssetLedgerSchema = z.record(z.string().min(1), AssetLedgerEntrySchema);
|
|
55
|
+
|
|
56
|
+
export type AssetLedgerEntry = z.infer<typeof AssetLedgerEntrySchema>;
|
|
57
|
+
export type AssetLedger = Record<string, AssetLedgerEntry>;
|
|
58
|
+
|
|
59
|
+
/** Format a raw hex digest as the ledger's `sourceHash`. Rejects non-digests
|
|
60
|
+
* loudly rather than storing something that only looks like a hash. */
|
|
61
|
+
export function sourceHashFromSha256(sha256: string): string {
|
|
62
|
+
if (!/^[a-f0-9]{64}$/.test(sha256)) {
|
|
63
|
+
throw new Error(`Not a sha256 digest, refusing to record it as a sourceHash: ${sha256}`);
|
|
64
|
+
}
|
|
65
|
+
return `sha256-${sha256}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** The raw hex digest inside a `sha256-…` sourceHash, or null for other forms. */
|
|
69
|
+
export function sha256FromSourceHash(sourceHash: string): string | null {
|
|
70
|
+
const match = /^sha256-([a-f0-9]{64})$/.exec(sourceHash);
|
|
71
|
+
return match?.[1] ?? null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Normalize a ledger key: project-relative, forward slashes, no leading `./`. */
|
|
75
|
+
export function normalizeLedgerPath(path: string): string {
|
|
76
|
+
const normalized = path.replaceAll('\\', '/').replace(/^\.\//, '').replace(/^\/+/, '');
|
|
77
|
+
if (!normalized) throw new Error('An asset ledger path cannot be empty.');
|
|
78
|
+
if (!isContainedRelativePath(normalized)) {
|
|
79
|
+
throw new Error(`An asset ledger path cannot escape the project: ${path}`);
|
|
80
|
+
}
|
|
81
|
+
return normalized;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Parse a ledger document. Throws (loudly) on anything malformed — a
|
|
85
|
+
* provenance record that silently drops entries is worse than no record. */
|
|
86
|
+
export function parseAssetLedger(input: unknown): AssetLedger {
|
|
87
|
+
return AssetLedgerSchema.parse(input);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Parse ledger JSON text; an absent ledger is `{}`, a CORRUPT one throws. */
|
|
91
|
+
export function parseAssetLedgerJson(text: string | null | undefined): AssetLedger {
|
|
92
|
+
if (text == null || text.trim() === '') return {};
|
|
93
|
+
let parsed: unknown;
|
|
94
|
+
try {
|
|
95
|
+
parsed = JSON.parse(text);
|
|
96
|
+
} catch (cause) {
|
|
97
|
+
throw new Error(`${ASSET_LEDGER_PATH} is not valid JSON: ${String(cause)}`);
|
|
98
|
+
}
|
|
99
|
+
return parseAssetLedger(parsed);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Record one materialization. Pure: returns a new, key-sorted ledger. */
|
|
103
|
+
export function upsertAssetLedgerEntry(
|
|
104
|
+
ledger: AssetLedger,
|
|
105
|
+
destPath: string,
|
|
106
|
+
entry: AssetLedgerEntry,
|
|
107
|
+
): AssetLedger {
|
|
108
|
+
const key = normalizeLedgerPath(destPath);
|
|
109
|
+
const validated = AssetLedgerEntrySchema.parse(entry);
|
|
110
|
+
const merged: AssetLedger = { ...ledger, [key]: validated };
|
|
111
|
+
const sorted: AssetLedger = {};
|
|
112
|
+
for (const path of Object.keys(merged).sort()) sorted[path] = merged[path]!;
|
|
113
|
+
return sorted;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Serialize for disk — stable key order, trailing newline. */
|
|
117
|
+
export function serializeAssetLedger(ledger: AssetLedger): string {
|
|
118
|
+
const sorted: AssetLedger = {};
|
|
119
|
+
for (const path of Object.keys(ledger).sort()) sorted[path] = ledger[path]!;
|
|
120
|
+
return `${JSON.stringify(sorted, null, 2)}\n`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export type AssetLedgerDrift =
|
|
124
|
+
| { readonly status: 'untracked' }
|
|
125
|
+
| { readonly status: 'unknown-source-hash' }
|
|
126
|
+
| { readonly status: 'pristine' }
|
|
127
|
+
| {
|
|
128
|
+
readonly status: 'edited-locally';
|
|
129
|
+
readonly sourceHash: string;
|
|
130
|
+
readonly currentHash: string;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Classify one copy against its ledger entry. `currentHash` is the sha256 of
|
|
135
|
+
* the bytes on disk RIGHT NOW.
|
|
136
|
+
*
|
|
137
|
+
* This deliberately cannot tell "user edited" from "library moved" on its own —
|
|
138
|
+
* both show as a hash difference. The ledger supplies one half (what we copied);
|
|
139
|
+
* re-reading the library supplies the other. Callers that have not consulted the
|
|
140
|
+
* library must report `edited-locally` as "differs from what was materialized",
|
|
141
|
+
* never as "the library moved".
|
|
142
|
+
*/
|
|
143
|
+
export function classifyAssetLedgerDrift(
|
|
144
|
+
ledger: AssetLedger,
|
|
145
|
+
destPath: string,
|
|
146
|
+
currentSha256: string,
|
|
147
|
+
): AssetLedgerDrift {
|
|
148
|
+
const entry = ledger[normalizeLedgerPath(destPath)];
|
|
149
|
+
if (!entry) return { status: 'untracked' };
|
|
150
|
+
if (!entry.sourceHash) return { status: 'unknown-source-hash' };
|
|
151
|
+
const expected = sha256FromSourceHash(entry.sourceHash);
|
|
152
|
+
if (expected === null) return { status: 'unknown-source-hash' };
|
|
153
|
+
return expected === currentSha256
|
|
154
|
+
? { status: 'pristine' }
|
|
155
|
+
: { status: 'edited-locally', sourceHash: expected, currentHash: currentSha256 };
|
|
156
|
+
}
|