@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,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keeping the editor's status snapshot off the interaction critical path.
|
|
3
|
+
*
|
|
4
|
+
* `collectState` (command-listener.ts) is not a read — it RE-DERIVES the whole
|
|
5
|
+
* status surface. Two families dominate, and both scale with the size of the
|
|
6
|
+
* project rather than with the size of the change:
|
|
7
|
+
*
|
|
8
|
+
* - the hierarchy walk behind `entities`/`entityCount`, and
|
|
9
|
+
* - the capability GRADING facets (`rootCoverage`, `systemCoverage`,
|
|
10
|
+
* `projectCoverage`, `authoringCoverage`, `ontologyInvariants`), each
|
|
11
|
+
* documented as re-derived on read; `rootCoverage`'s conformance probe
|
|
12
|
+
* walks the hierarchy AGAIN, once per mounted root, allocating a receipt
|
|
13
|
+
* object and a formatted sentence per node.
|
|
14
|
+
*
|
|
15
|
+
* Measured 2026-08-19 through the session journal on `examples/first-person`
|
|
16
|
+
* in play mode, 251 hierarchy nodes:
|
|
17
|
+
*
|
|
18
|
+
* collectState total 76ms = rootCoverage 30ms
|
|
19
|
+
* + 2 x hierarchy walk 23ms
|
|
20
|
+
* JSON.stringify of the 41KB result: 0.0ms. Sending it: 0.1ms.
|
|
21
|
+
*
|
|
22
|
+
* So none of the cost is serialization or transport; all of it is derivation,
|
|
23
|
+
* and it is superlinear in node count (56 nodes: 1.5ms per walk; 251 nodes:
|
|
24
|
+
* 23ms). On a game-heavy project the same function measured 1.2-1.3s of
|
|
25
|
+
* main-thread block.
|
|
26
|
+
*
|
|
27
|
+
* That would be survivable if it ran rarely. It does not: `reportLatestState`
|
|
28
|
+
* is wired to the store's own change notification, to restart-required, to the
|
|
29
|
+
* ingest capture wait, to the adapter load, to every command completion, and
|
|
30
|
+
* to focus/blur/visibilitychange. So the whole derivation ran on every
|
|
31
|
+
* selection click and several times over during a mount — which is why the
|
|
32
|
+
* defect reads as "the editor is slow to touch" while an idle tab is fine.
|
|
33
|
+
*
|
|
34
|
+
* The fix is not to derive less truth, it is to derive it OFF the critical
|
|
35
|
+
* path: an interaction reports immediately with the cheap fields fresh and the
|
|
36
|
+
* derived families reused from the last full snapshot, and schedules ONE
|
|
37
|
+
* deferred full collect that makes them current again. the editor's `status` command readers
|
|
38
|
+
* are never handed a snapshot older than that deferral, and the snapshot
|
|
39
|
+
* already carries its own age.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* How long the deferred full collect may wait for an idle moment before the
|
|
44
|
+
* browser runs it anyway. Short enough that "current shortly after the change"
|
|
45
|
+
* stays true for a the editor's `status` command reader; long enough that the interaction that
|
|
46
|
+
* triggered it paints first, which is the entire point.
|
|
47
|
+
*/
|
|
48
|
+
export const DEFERRED_FULL_REPORT_TIMEOUT_MS = 500;
|
|
49
|
+
|
|
50
|
+
interface IdleCallbackHost {
|
|
51
|
+
requestIdleCallback?: (callback: () => void, options?: { timeout?: number }) => number;
|
|
52
|
+
cancelIdleCallback?: (handle: number) => void;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Run `report` off the critical path — at the next idle moment, or when
|
|
57
|
+
* {@link DEFERRED_FULL_REPORT_TIMEOUT_MS} expires, whichever comes first.
|
|
58
|
+
* Returns the canceller the caller's teardown must run.
|
|
59
|
+
*
|
|
60
|
+
* Falls back to a task (`setTimeout`) where `requestIdleCallback` is missing
|
|
61
|
+
* (Safari before 17, and jsdom): still off the triggering handler's own frame,
|
|
62
|
+
* which is what makes the interaction cheap.
|
|
63
|
+
*/
|
|
64
|
+
export function scheduleDeferredFullReport(report: () => void): () => void {
|
|
65
|
+
const host = globalThis as IdleCallbackHost;
|
|
66
|
+
const idle = host.requestIdleCallback;
|
|
67
|
+
if (typeof idle === 'function') {
|
|
68
|
+
const handle = idle.call(globalThis, report, { timeout: DEFERRED_FULL_REPORT_TIMEOUT_MS });
|
|
69
|
+
return () => host.cancelIdleCallback?.call(globalThis, handle);
|
|
70
|
+
}
|
|
71
|
+
const handle = setTimeout(report, 0);
|
|
72
|
+
return () => clearTimeout(handle);
|
|
73
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `StorageBackend`, answered by the WORKBENCH'S OWN FILE SERVICE (U5).
|
|
3
|
+
*
|
|
4
|
+
* ARCHITECTURE-CORE §The core is Code-OSS: *"the storage backends → file
|
|
5
|
+
* system providers, the dev server as one provider"*. What that turned out to
|
|
6
|
+
* mean, measured: both product shapes already have a real file service over
|
|
7
|
+
* the project folder (desktop's disk provider, the REH's `vscode-remote://`),
|
|
8
|
+
* so there is nothing to PROVIDE — the door CALLS what exists
|
|
9
|
+
* (`files/project-files.ts` states the decision and the reason).
|
|
10
|
+
*
|
|
11
|
+
* This class is how the existing `StorageBackend` callers — forty-odd of
|
|
12
|
+
* them, from the asset browser to the provenance ledger — reach that file
|
|
13
|
+
* service without one of them changing. It is a DECORATOR, not a fourth
|
|
14
|
+
* backend: it overrides exactly the three operations where "is this the
|
|
15
|
+
* workbench's own write?" is a question that has an answer (`read`,
|
|
16
|
+
* `readBytes`, `write`) and delegates everything else to the tier's real
|
|
17
|
+
* backend. `remove`, `mkdir`, `stat`, `list` and `watch` are the session's
|
|
18
|
+
* still, because the file door deliberately carries no remove/mkdir — a
|
|
19
|
+
* member nobody reads is cut, and nothing in the frame needs them yet.
|
|
20
|
+
*
|
|
21
|
+
* THE ROOT DIFFERENCE IS THE WHOLE JOB. `StorageBackend`'s paths on the server
|
|
22
|
+
* tier are rooted at `<project>/public/` (`http-storage.ts`'s header) while
|
|
23
|
+
* the file door's are PROJECT-RELATIVE, because that is the spelling the frame
|
|
24
|
+
* can resolve against its workspace folder. This class is the `public/` view:
|
|
25
|
+
* one prefix, in one place, instead of forty callers learning a second
|
|
26
|
+
* spelling.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { projectFiles } from '../files/project-files';
|
|
30
|
+
import type { DirEntry, Stat, StorageBackend, WatchCallback } from '@volter/sdk/kit/storage-types';
|
|
31
|
+
|
|
32
|
+
export class HostFilesStorage implements StorageBackend {
|
|
33
|
+
readonly id: string;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param inner the tier's own backend, which still owns every operation the
|
|
37
|
+
* file door does not carry — and which is also the FALLBACK while the
|
|
38
|
+
* frame has taken ownership but not yet installed its provider (the mount
|
|
39
|
+
* window `files/file-provider.ts` describes).
|
|
40
|
+
* @param root the project-relative prefix these paths live under. Always
|
|
41
|
+
* `public` today; named rather than hardcoded so the prefix is visible at
|
|
42
|
+
* the call site that chose it.
|
|
43
|
+
*/
|
|
44
|
+
constructor(
|
|
45
|
+
private readonly inner: StorageBackend,
|
|
46
|
+
private readonly root = 'public',
|
|
47
|
+
) {
|
|
48
|
+
this.id = `frame:${inner.id}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private resolve(path: string): string {
|
|
52
|
+
const key = path.replace(/\\/g, '/').replace(/^\/+/, '');
|
|
53
|
+
return key ? `${this.root}/${key}` : this.root;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
read(path: string): Promise<string> {
|
|
57
|
+
return projectFiles.read(this.resolve(path));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
readBytes(path: string): Promise<Uint8Array> {
|
|
61
|
+
return projectFiles.readBytes(this.resolve(path));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
write(path: string, data: string | Uint8Array): Promise<void> {
|
|
65
|
+
return projectFiles.write(this.resolve(path), data);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async writeIfAbsent(path: string, data: string | Uint8Array): Promise<boolean> {
|
|
69
|
+
if (await this.exists(path)) return false;
|
|
70
|
+
await this.write(path, data);
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
list(dir: string): Promise<DirEntry[]> {
|
|
75
|
+
return this.inner.list(dir);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
stat(path: string): Promise<Stat | null> {
|
|
79
|
+
return this.inner.stat(path);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
exists(path: string): Promise<boolean> {
|
|
83
|
+
return projectFiles.exists(this.resolve(path));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
mkdir(path: string): Promise<void> {
|
|
87
|
+
return this.inner.mkdir(path);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
remove(path: string): Promise<void> {
|
|
91
|
+
return this.inner.remove(path);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
watch(callback: WatchCallback): () => void {
|
|
95
|
+
return this.inner.watch(callback);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-backed `StorageBackend` for the local (Node) editor.
|
|
3
|
+
*
|
|
4
|
+
* A thin adapter over the editor server's existing routes — it changes nothing
|
|
5
|
+
* about how the local editor talks to disk, it just presents that surface behind
|
|
6
|
+
* the shared interface. The backend is rooted at the project's `public/` folder,
|
|
7
|
+
* matching the server's `/__editor/assets` (list) and `/__editor/save-file`
|
|
8
|
+
* (write) routes; reads come from Vite's `/@fs/` static serving.
|
|
9
|
+
*
|
|
10
|
+
* Route mapping (paths are relative to `public/`):
|
|
11
|
+
* list → GET /__editor/assets?dir → { entries: [{name,type,size,mtime}] }
|
|
12
|
+
* write → POST /__editor/save-file ({path, content, encoding?})
|
|
13
|
+
* read → GET /@fs/{projectRoot}/public/{path}
|
|
14
|
+
* watch → EventSource /__editor/events (chokidar-backed)
|
|
15
|
+
*
|
|
16
|
+
* The CLI control channel, native dialogs, `export`, online asset library, and
|
|
17
|
+
* project/editor-state persistence are NOT file operations and live outside this
|
|
18
|
+
* interface (see types.ts and editor-api.ts).
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { bytesToBase64 } from '@volter/sdk/kit/bytes-codec';
|
|
22
|
+
import { assertEditorServerAnswered, editorServerJson } from '@volter/sdk/kit/editor-server-response';
|
|
23
|
+
import { sourceMutationAttribution } from '@volter/sdk/kit/editor-session-attribution';
|
|
24
|
+
import { handleProjectMutationFailure } from '@volter/sdk/kit/source-conflict';
|
|
25
|
+
import { dirAndBase, normalize } from './paths';
|
|
26
|
+
import type { DirEntry, Stat, StorageBackend, WatchCallback, WatchEvent } from '@volter/sdk/kit/storage-types';
|
|
27
|
+
|
|
28
|
+
const BASE = '/__editor';
|
|
29
|
+
|
|
30
|
+
interface ServerAssetEntry {
|
|
31
|
+
name: string;
|
|
32
|
+
type: 'file' | 'directory';
|
|
33
|
+
size: number;
|
|
34
|
+
mtime: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class HttpStorage implements StorageBackend {
|
|
38
|
+
readonly id = 'http';
|
|
39
|
+
|
|
40
|
+
/** Absolute project root on the server (used for /@fs reads). */
|
|
41
|
+
constructor(private readonly projectRoot: string) {}
|
|
42
|
+
|
|
43
|
+
async read(path: string): Promise<string> {
|
|
44
|
+
const res = await fetch(`/@fs/${this.projectRoot}/public/${normalize(path)}`);
|
|
45
|
+
if (!res.ok) throw new Error(`HttpStorage.read '${normalize(path)}': HTTP ${res.status}`);
|
|
46
|
+
return res.text();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async readBytes(path: string): Promise<Uint8Array> {
|
|
50
|
+
const res = await fetch(`/@fs/${this.projectRoot}/public/${normalize(path)}`);
|
|
51
|
+
if (!res.ok) throw new Error(`HttpStorage.readBytes '${normalize(path)}': HTTP ${res.status}`);
|
|
52
|
+
return new Uint8Array(await res.arrayBuffer());
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async write(path: string, data: string | Uint8Array): Promise<void> {
|
|
56
|
+
const body =
|
|
57
|
+
typeof data === 'string'
|
|
58
|
+
? { path: normalize(path), content: data, ...sourceMutationAttribution() }
|
|
59
|
+
: {
|
|
60
|
+
path: normalize(path),
|
|
61
|
+
content: bytesToBase64(data),
|
|
62
|
+
encoding: 'base64' as const,
|
|
63
|
+
...sourceMutationAttribution(),
|
|
64
|
+
};
|
|
65
|
+
const res = await fetch(`${BASE}/save-file`, {
|
|
66
|
+
method: 'POST',
|
|
67
|
+
headers: { 'Content-Type': 'application/json' },
|
|
68
|
+
body: JSON.stringify(body),
|
|
69
|
+
});
|
|
70
|
+
// This is the AUTOSAVE path. `!res.ok` alone let a page fallback report
|
|
71
|
+
// every save as written, so the editor kept showing saved work that no
|
|
72
|
+
// server had ever received.
|
|
73
|
+
assertEditorServerAnswered(res, `Write ${normalize(path)} failed`);
|
|
74
|
+
if (!res.ok) {
|
|
75
|
+
await handleProjectMutationFailure(res, {
|
|
76
|
+
label: `Write ${normalize(path)}`,
|
|
77
|
+
attempted: {
|
|
78
|
+
[`public/${normalize(path)}`]:
|
|
79
|
+
typeof data === 'string' ? data : `[${data.byteLength} binary bytes]`,
|
|
80
|
+
},
|
|
81
|
+
reapply: () => this.write(path, data),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async writeIfAbsent(path: string, data: string | Uint8Array): Promise<boolean> {
|
|
87
|
+
// Browser adaptation never uses HttpStorage: the Node endpoint owns its
|
|
88
|
+
// true `wx` write. Keep the interface honest for generic callers.
|
|
89
|
+
if (await this.exists(path)) return false;
|
|
90
|
+
await this.write(path, data);
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async list(dir: string): Promise<DirEntry[]> {
|
|
95
|
+
const res = await fetch(`${BASE}/assets?${new URLSearchParams({ dir: normalize(dir) })}`);
|
|
96
|
+
const { entries } = await editorServerJson<{ entries: ServerAssetEntry[] }>(
|
|
97
|
+
res,
|
|
98
|
+
`HttpStorage.list '${normalize(dir)}' failed`,
|
|
99
|
+
);
|
|
100
|
+
return entries.map((e) => {
|
|
101
|
+
const out: DirEntry = {
|
|
102
|
+
name: e.name,
|
|
103
|
+
path: normalize(dir ? `${dir}/${e.name}` : e.name),
|
|
104
|
+
type: e.type === 'directory' ? 'dir' : 'file',
|
|
105
|
+
mtime: e.mtime,
|
|
106
|
+
};
|
|
107
|
+
if (e.type === 'file') out.size = e.size;
|
|
108
|
+
out.mtime = e.mtime;
|
|
109
|
+
return out;
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async stat(path: string): Promise<Stat | null> {
|
|
114
|
+
// The server exposes listings, not a per-path stat. Derive from the parent
|
|
115
|
+
// listing — sufficient for the editor's needs (existence + type + size).
|
|
116
|
+
const [dir, base] = dirAndBase(path);
|
|
117
|
+
if (!base) return { path: '', type: 'dir', size: 0, mtime: 0 };
|
|
118
|
+
let entries: DirEntry[];
|
|
119
|
+
try {
|
|
120
|
+
entries = await this.list(dir);
|
|
121
|
+
} catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
const hit = entries.find((e) => e.name === base);
|
|
125
|
+
return hit
|
|
126
|
+
? { path: hit.path, type: hit.type, size: hit.size ?? 0, mtime: hit.mtime ?? 0 }
|
|
127
|
+
: null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async exists(path: string): Promise<boolean> {
|
|
131
|
+
return (await this.stat(path)) !== null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async mkdir(path: string): Promise<void> {
|
|
135
|
+
// The server has no empty-dir route; directories materialize when their
|
|
136
|
+
// first file is written (save-file creates parents). No-op here.
|
|
137
|
+
void path;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async remove(path: string): Promise<void> {
|
|
141
|
+
const res = await fetch(`${BASE}/save-file`, {
|
|
142
|
+
method: 'POST',
|
|
143
|
+
headers: { 'Content-Type': 'application/json' },
|
|
144
|
+
body: JSON.stringify({ path: normalize(path), delete: true, ...sourceMutationAttribution() }),
|
|
145
|
+
});
|
|
146
|
+
assertEditorServerAnswered(res, `Delete ${normalize(path)} failed`);
|
|
147
|
+
if (!res.ok) {
|
|
148
|
+
await handleProjectMutationFailure(res, {
|
|
149
|
+
label: `Delete ${normalize(path)}`,
|
|
150
|
+
attempted: { [`public/${normalize(path)}`]: null },
|
|
151
|
+
reapply: () => this.remove(path),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
watch(callback: WatchCallback): () => void {
|
|
157
|
+
const source = new EventSource(`${BASE}/events`);
|
|
158
|
+
const handler = (ev: MessageEvent) => {
|
|
159
|
+
try {
|
|
160
|
+
const data = JSON.parse(ev.data) as { type?: string; file?: string; path?: string };
|
|
161
|
+
const path = data.path ?? data.file;
|
|
162
|
+
if (!path) return;
|
|
163
|
+
callback({ type: (data.type as WatchEvent['type']) ?? 'update', path: normalize(path) });
|
|
164
|
+
} catch {
|
|
165
|
+
// Ignore non-JSON keepalive frames.
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
source.addEventListener('message', handler);
|
|
169
|
+
return () => {
|
|
170
|
+
source.removeEventListener('message', handler);
|
|
171
|
+
source.close();
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage backend selection — the `public/`-rooted file interface the editor's
|
|
3
|
+
* asset surfaces read and write through.
|
|
4
|
+
*
|
|
5
|
+
* ONE TIER, two implementations, and the second is a decorator:
|
|
6
|
+
* - `HttpStorage` — the session's own `/__editor/*` routes.
|
|
7
|
+
* - `HostFilesStorage` — the same view, written through `EditorHost.files`
|
|
8
|
+
* so a write is the WORKBENCH's own (U5).
|
|
9
|
+
* `MemStorage` is the design system's fixture backend and nothing else.
|
|
10
|
+
*
|
|
11
|
+
* Upstream code depends only on `StorageBackend` from `./types` and obtains
|
|
12
|
+
* an instance via `getStorageBackend()`.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { getProjectDefinePath } from '../editor-mode';
|
|
16
|
+
import { filesProviderInstalled } from '../files/file-provider';
|
|
17
|
+
import { HostFilesStorage } from './host-files-storage';
|
|
18
|
+
import { HttpStorage } from './http-storage';
|
|
19
|
+
import type { StorageBackend } from '@volter/sdk/kit/storage-types';
|
|
20
|
+
|
|
21
|
+
export { HttpStorage } from './http-storage';
|
|
22
|
+
export { MemStorage } from './mem-storage';
|
|
23
|
+
export type { DirEntry, HostCapabilities, Stat, StorageBackend, WatchEvent } from '@volter/sdk/kit/storage-types';
|
|
24
|
+
|
|
25
|
+
let _backend: StorageBackend | null = null;
|
|
26
|
+
/** The tier's own backend, kept beside the active one so the frame decorator
|
|
27
|
+
* never becomes its own delegate — see {@link tierStorageBackend}. */
|
|
28
|
+
let _tier: StorageBackend | null = null;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* THE TIER'S OWN backend — never the frame decorator, and the only thing the
|
|
32
|
+
* file door's host fill is allowed to stand on.
|
|
33
|
+
*
|
|
34
|
+
* Without this the fill would recurse: `projectFiles.read` falls back to
|
|
35
|
+
* `getStorageBackend()`, which under the frame is `HostFilesStorage`, whose
|
|
36
|
+
* `read` is `projectFiles.read`. One function, so the cycle cannot be written
|
|
37
|
+
* by accident.
|
|
38
|
+
*/
|
|
39
|
+
export function tierStorageBackend(): StorageBackend {
|
|
40
|
+
if (_backend && !(_backend instanceof HostFilesStorage)) return _backend;
|
|
41
|
+
if (!_tier) _tier = new HttpStorage(getProjectDefinePath());
|
|
42
|
+
return _tier;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The active storage backend: the session's own, wrapped by the frame's file
|
|
47
|
+
* door once its provider has landed.
|
|
48
|
+
*
|
|
49
|
+
* UNDER THE CODE-OSS FRAME the file system is the WORKBENCH'S, not the
|
|
50
|
+
* session's (U5; ARCHITECTURE-CORE §The core is Code-OSS, "the storage
|
|
51
|
+
* backends → file system providers"). `HostFilesStorage` is the `public/`
|
|
52
|
+
* view onto `EditorHost.files`, so an asset write is the workbench's own write
|
|
53
|
+
* on the URI it already holds — the same property that closed U4's redo open
|
|
54
|
+
* for source files. Everything the file door does not carry (remove, mkdir,
|
|
55
|
+
* stat, list, watch) stays this tier's, which is why the decorator keeps it
|
|
56
|
+
* rather than replacing it.
|
|
57
|
+
*
|
|
58
|
+
* Read at FIRST USE rather than at module load: the bridge takes ownership
|
|
59
|
+
* before the editor mounts, but the provider arrives with the contribution's
|
|
60
|
+
* own services, after — see `files/file-provider.ts`.
|
|
61
|
+
*/
|
|
62
|
+
export function getStorageBackend(): StorageBackend {
|
|
63
|
+
if (_backend) return _backend;
|
|
64
|
+
const tier = tierStorageBackend();
|
|
65
|
+
_backend = filesProviderInstalled() ? new HostFilesStorage(tier) : tier;
|
|
66
|
+
return _backend;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Override the active backend (the design system's fixture). */
|
|
70
|
+
export function setStorageBackend(backend: StorageBackend): void {
|
|
71
|
+
_backend = backend;
|
|
72
|
+
// An explicitly installed backend REPLACES the tier, so the file door's host
|
|
73
|
+
// fill stands on it too rather than on a stale one it would otherwise hold.
|
|
74
|
+
if (!(backend instanceof HostFilesStorage)) _tier = backend;
|
|
75
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure in-memory `StorageBackend` — a virtual filesystem backed by a `Map`.
|
|
3
|
+
*
|
|
4
|
+
* Nothing touches disk; files live as bytes in JS and vanish on reload. The
|
|
5
|
+
* most "virtual" of the backends, and the simplest — the design system's
|
|
6
|
+
* fixture (`design-system-stories/fixtures/editor-runtime.tsx`) is what uses
|
|
7
|
+
* it: a story needs a project-shaped backend and must write nothing.
|
|
8
|
+
*
|
|
9
|
+
* Implements the same interface as every other backend, so it is a drop-in via
|
|
10
|
+
* `setStorageBackend(new MemStorage())`.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { dirAndBase, normalize } from './paths';
|
|
14
|
+
import type { DirEntry, Stat, StorageBackend, WatchCallback, WatchEvent } from '@volter/sdk/kit/storage-types';
|
|
15
|
+
|
|
16
|
+
type Node = { type: 'file'; data: Uint8Array; mtime: number } | { type: 'dir'; mtime: number };
|
|
17
|
+
|
|
18
|
+
export class MemStorage implements StorageBackend {
|
|
19
|
+
readonly id = 'mem';
|
|
20
|
+
|
|
21
|
+
/** Normalized path → node. The root (`''`) is implicit. */
|
|
22
|
+
private readonly nodes = new Map<string, Node>();
|
|
23
|
+
private readonly watchers = new Set<WatchCallback>();
|
|
24
|
+
|
|
25
|
+
/** Optionally seed the VFS from a `{ path: contents }` map. */
|
|
26
|
+
constructor(seed?: Record<string, string | Uint8Array>) {
|
|
27
|
+
if (seed) for (const [path, data] of Object.entries(seed)) this.writeSync(path, data);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private emit(event: WatchEvent): void {
|
|
31
|
+
for (const cb of this.watchers) cb(event);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Ensure every ancestor directory of `path` exists. */
|
|
35
|
+
private ensureParents(path: string): void {
|
|
36
|
+
const parts = normalize(path).split('/');
|
|
37
|
+
parts.pop();
|
|
38
|
+
let cur = '';
|
|
39
|
+
for (const part of parts) {
|
|
40
|
+
cur = cur ? `${cur}/${part}` : part;
|
|
41
|
+
if (this.nodes.get(cur)?.type !== 'dir') this.nodes.set(cur, { type: 'dir', mtime: now() });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private writeSync(path: string, data: string | Uint8Array): boolean {
|
|
46
|
+
const norm = normalize(path);
|
|
47
|
+
this.ensureParents(norm);
|
|
48
|
+
const existed = this.nodes.get(norm)?.type === 'file';
|
|
49
|
+
const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : new Uint8Array(data);
|
|
50
|
+
this.nodes.set(norm, { type: 'file', data: bytes, mtime: now() });
|
|
51
|
+
return existed;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Put bytes in place WITHOUT announcing a change: a backend that fills itself lazily from
|
|
56
|
+
* somewhere else (the limited view's seed, `editor-core/view/page/project-store.ts`) is not
|
|
57
|
+
* editing the file when its first read arrives, so no watcher may hear it as one.
|
|
58
|
+
*/
|
|
59
|
+
seed(path: string, data: string | Uint8Array): void {
|
|
60
|
+
this.writeSync(path, data);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async read(path: string): Promise<string> {
|
|
64
|
+
return new TextDecoder().decode(await this.readBytes(path));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async readBytes(path: string): Promise<Uint8Array> {
|
|
68
|
+
const node = this.nodes.get(normalize(path));
|
|
69
|
+
if (node?.type !== 'file')
|
|
70
|
+
throw new Error(`MemStorage.read: no such file '${normalize(path)}'`);
|
|
71
|
+
// Reads own their bytes, just like File/HTTP backends. Asset serving
|
|
72
|
+
// transfers this buffer to a worker; sharing the stored buffer would
|
|
73
|
+
// detach the file itself and break every subsequent read.
|
|
74
|
+
return node.data.slice();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async write(path: string, data: string | Uint8Array): Promise<void> {
|
|
78
|
+
const existed = this.writeSync(path, data);
|
|
79
|
+
this.emit({ type: existed ? 'update' : 'create', path: normalize(path) });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async writeIfAbsent(path: string, data: string | Uint8Array): Promise<boolean> {
|
|
83
|
+
if (await this.exists(path)) return false;
|
|
84
|
+
this.writeSync(path, data);
|
|
85
|
+
this.emit({ type: 'create', path: normalize(path) });
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async list(dir: string): Promise<DirEntry[]> {
|
|
90
|
+
const dn = normalize(dir);
|
|
91
|
+
if (dn !== '' && this.nodes.get(dn)?.type !== 'dir') {
|
|
92
|
+
throw new Error(`MemStorage.list: no such directory '${dn}'`);
|
|
93
|
+
}
|
|
94
|
+
const entries: DirEntry[] = [];
|
|
95
|
+
for (const [path, node] of this.nodes) {
|
|
96
|
+
const [parent, base] = dirAndBase(path);
|
|
97
|
+
if (parent !== dn || !base) continue;
|
|
98
|
+
const entry: DirEntry = {
|
|
99
|
+
name: base,
|
|
100
|
+
path,
|
|
101
|
+
type: node.type === 'dir' ? 'dir' : 'file',
|
|
102
|
+
mtime: node.mtime,
|
|
103
|
+
};
|
|
104
|
+
if (node.type === 'file') entry.size = node.data.length;
|
|
105
|
+
entry.mtime = node.mtime;
|
|
106
|
+
entries.push(entry);
|
|
107
|
+
}
|
|
108
|
+
entries.sort((a, b) => {
|
|
109
|
+
if (a.type !== b.type) return a.type === 'dir' ? -1 : 1;
|
|
110
|
+
return a.name.localeCompare(b.name);
|
|
111
|
+
});
|
|
112
|
+
return entries;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async stat(path: string): Promise<Stat | null> {
|
|
116
|
+
const norm = normalize(path);
|
|
117
|
+
if (norm === '') return { path: '', type: 'dir', size: 0, mtime: 0 };
|
|
118
|
+
const node = this.nodes.get(norm);
|
|
119
|
+
if (!node) return null;
|
|
120
|
+
return node.type === 'file'
|
|
121
|
+
? { path: norm, type: 'file', size: node.data.length, mtime: node.mtime }
|
|
122
|
+
: { path: norm, type: 'dir', size: 0, mtime: node.mtime };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async exists(path: string): Promise<boolean> {
|
|
126
|
+
return (await this.stat(path)) !== null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async mkdir(path: string): Promise<void> {
|
|
130
|
+
const norm = normalize(path);
|
|
131
|
+
if (!norm || this.nodes.get(norm)?.type === 'dir') return;
|
|
132
|
+
this.ensureParents(norm);
|
|
133
|
+
this.nodes.set(norm, { type: 'dir', mtime: now() });
|
|
134
|
+
this.emit({ type: 'create', path: norm });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async remove(path: string): Promise<void> {
|
|
138
|
+
const norm = normalize(path);
|
|
139
|
+
if (!norm) return;
|
|
140
|
+
let removed = false;
|
|
141
|
+
for (const key of [...this.nodes.keys()]) {
|
|
142
|
+
if (key === norm || key.startsWith(`${norm}/`)) {
|
|
143
|
+
this.nodes.delete(key);
|
|
144
|
+
removed = true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (removed) this.emit({ type: 'remove', path: norm });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
watch(callback: WatchCallback): () => void {
|
|
151
|
+
this.watchers.add(callback);
|
|
152
|
+
return () => this.watchers.delete(callback);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function now(): number {
|
|
157
|
+
return Date.now();
|
|
158
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one cross-realm mutual exclusion a browser storage backend has:
|
|
3
|
+
* `navigator.locks`, keyed by backend id + path.
|
|
4
|
+
*
|
|
5
|
+
* Two callers matter and they MUST agree on the name, which is why this is a
|
|
6
|
+
* shared function rather than a string built at each site — and why the name is
|
|
7
|
+
* derived from the NORMALIZED path here rather than from whatever each caller
|
|
8
|
+
* happens to hold. Sharing the function is not enough on its own: one caller
|
|
9
|
+
* passes an already-normalized path and the other passes a module constant, so
|
|
10
|
+
* agreement used to rest on that constant being spelled in normalized form.
|
|
11
|
+
* Respell it as `/.volter/assets.json` and the two names diverge, both locks are
|
|
12
|
+
* "held", and mutual exclusion is silently gone with nothing failing. Deriving
|
|
13
|
+
* the name from `normalize()` makes the agreement structural.
|
|
14
|
+
*
|
|
15
|
+
* Callers:
|
|
16
|
+
* - the asset ledger's `.lock` compare-and-delete
|
|
17
|
+
* (`../asset-workflow/asset-ledger-backend.ts`) — several steps that must
|
|
18
|
+
* not interleave with each other OR with the create above.
|
|
19
|
+
*
|
|
20
|
+
* Scope, stated exactly: Web Locks span every same-origin realm — the two
|
|
21
|
+
* tabs on one project that motivate the ledger lock in the first place — and
|
|
22
|
+
* nothing else. A writer that is not same-origin JS (a Node process
|
|
23
|
+
* reaching the same files, i.e. `HttpStorage` racing the CLI) is not serialized
|
|
24
|
+
* by this, and cannot be from the browser side.
|
|
25
|
+
*
|
|
26
|
+
* Absent in a non-secure context and in Node (tests): callers then get the bare
|
|
27
|
+
* operation, which is the same exclusivity the platform itself offers.
|
|
28
|
+
*/
|
|
29
|
+
import { normalize } from '@volter/sdk/kit/storage/paths';
|
|
30
|
+
|
|
31
|
+
export function withPathLock<T>(
|
|
32
|
+
backendId: string,
|
|
33
|
+
path: string,
|
|
34
|
+
operation: () => Promise<T>,
|
|
35
|
+
): Promise<T> {
|
|
36
|
+
const locks = globalThis.navigator?.locks;
|
|
37
|
+
if (!locks) return operation();
|
|
38
|
+
// `LockManager.request`'s DOM typing infers its result from the callback's
|
|
39
|
+
// literal return type (`(lock) => T`), so an async callback types as
|
|
40
|
+
// `Promise<Promise<T>>`. The platform awaits the returned promise before
|
|
41
|
+
// releasing the lock — that flattening is the documented behavior, not an
|
|
42
|
+
// assumption — so the settled value really is `T`.
|
|
43
|
+
return locks.request(`volter:${backendId}:${normalize(path)}`, operation) as Promise<T>;
|
|
44
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path helpers for storage backends. Paths are POSIX-style, root-relative,
|
|
3
|
+
* '/'-separated, with no leading/trailing slashes.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** Split a path into non-empty segments, collapsing `.` and redundant slashes. */
|
|
7
|
+
export function segments(path: string): string[] {
|
|
8
|
+
return path.split('/').filter((s) => s.length > 0 && s !== '.');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Normalize to a canonical root-relative path (`''` for the root). */
|
|
12
|
+
export function normalize(path: string): string {
|
|
13
|
+
return segments(path).join('/');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Split into `[parentDir, baseName]`. Root entries have `parent === ''`. */
|
|
17
|
+
export function dirAndBase(path: string): [string, string] {
|
|
18
|
+
const segs = segments(path);
|
|
19
|
+
const base = segs.pop() ?? '';
|
|
20
|
+
return [segs.join('/'), base];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Join path parts into a normalized path. */
|
|
24
|
+
export function join(...parts: string[]): string {
|
|
25
|
+
return normalize(parts.join('/'));
|
|
26
|
+
}
|