@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,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE EDIT TAB ROW, as pure logic — what the adapter's scene table means for
|
|
3
|
+
* the workspace's open documents.
|
|
4
|
+
*
|
|
5
|
+
* ARCHITECTURE-CORE §Editor: "the scene table (entries, default, authorable
|
|
6
|
+
* flags, reach) is ONE adapter declaration with two clients — the Edit tab row
|
|
7
|
+
* projects it as isolation documents through the substrate family's piece
|
|
8
|
+
* mounts, and Play's `open()` walks it as live navigation." This module is the
|
|
9
|
+
* decision half of the FIRST client; `components/scene-documents.tsx` is the
|
|
10
|
+
* half that talks to the workspace registry, and `command-listener.ts`'s
|
|
11
|
+
* `open` verb resolves ONE entry through the very same table below — so the
|
|
12
|
+
* tab row and the verb can never disagree about what an entry is.
|
|
13
|
+
*
|
|
14
|
+
* ZERO INFERENCE. Every answer here is read off a DECLARED field of the
|
|
15
|
+
* entry — `authorable`, `reach`, `source`, `region`. Nothing is derived from a
|
|
16
|
+
* folder, a filename, a root, or the shape of the project: an entry the
|
|
17
|
+
* adapter did not declare simply is not here, and an entry it declared
|
|
18
|
+
* unreachable stays unreachable with the game's own reason attached.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { getProjectStoryModules } from '@volter/sdk/kit/stories/story-registry';
|
|
22
|
+
import type { DocumentEntry, SceneSource } from '@volter/project/adapter/adapter-module';
|
|
23
|
+
import type { ResolvedDocumentTable } from './project-adapter';
|
|
24
|
+
|
|
25
|
+
/** Document-id namespace for a per-scene isolation document. Distinct from
|
|
26
|
+
* `three-story:` ids (the story turntable, `@volter/editor-game`'s): a scene is
|
|
27
|
+
* reached through the entrypoint's own selection table, not through a story,
|
|
28
|
+
* and the two can legitimately name the same composition. */
|
|
29
|
+
export const SCENE_DOCUMENT_PREFIX = 'scene:';
|
|
30
|
+
|
|
31
|
+
/** The workspace document id for one entrypoint-selection scene entry. */
|
|
32
|
+
export function sceneDocumentId(entryId: string): string {
|
|
33
|
+
return `${SCENE_DOCUMENT_PREFIX}${entryId}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** A standing isolation tab — `scene:TitleScreen`, not `workspace:scene`. */
|
|
37
|
+
export function isIsolationSceneDocumentId(id: string): boolean {
|
|
38
|
+
return id.startsWith(SCENE_DOCUMENT_PREFIX);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* What the Edit workspace does with ONE scene-table entry.
|
|
43
|
+
*
|
|
44
|
+
* `no-document` is not a failure — a traversal entry and a declared-unreachable
|
|
45
|
+
* one are both honest table rows that simply own no Edit document. Its `reason`
|
|
46
|
+
* is the game's OWN sentence whenever the game supplied one (a `none` reach
|
|
47
|
+
* carries its `reason` verbatim), so a refusal quotes the adapter rather than
|
|
48
|
+
* paraphrasing it.
|
|
49
|
+
*/
|
|
50
|
+
export type SceneDocumentPlan =
|
|
51
|
+
/** Mounting the region IS opening this scene — it is the root's own standing
|
|
52
|
+
* document, which this entry merely NAMES. No second document. */
|
|
53
|
+
| { readonly kind: 'root-document'; readonly regionId: string; readonly title: string }
|
|
54
|
+
/** A viewport document of its own, mounting `source` in isolation. */
|
|
55
|
+
| {
|
|
56
|
+
readonly kind: 'isolation-document';
|
|
57
|
+
readonly documentId: string;
|
|
58
|
+
readonly title: string;
|
|
59
|
+
/** Owning root, carried into the document so its declared region — not
|
|
60
|
+
* its filename or export shape — decides the mounting surface. */
|
|
61
|
+
readonly regionId: string | null;
|
|
62
|
+
readonly source: SceneSource;
|
|
63
|
+
readonly isolationSetup?: SceneSource;
|
|
64
|
+
/** Present when this piece is also a running-game screen. Play's `open`
|
|
65
|
+
* still navigates live; Edit mounts the source in isolation and never
|
|
66
|
+
* holds the live host. */
|
|
67
|
+
readonly contractSceneId?: string;
|
|
68
|
+
}
|
|
69
|
+
/** A portable story mounts it — the existing story-document opener owns it. */
|
|
70
|
+
| { readonly kind: 'story-document'; readonly title: string; readonly storyId: string }
|
|
71
|
+
/**
|
|
72
|
+
* No Edit document, with the reason stated.
|
|
73
|
+
*
|
|
74
|
+
* `liveReach` separates "no document, and nothing else either" from "no
|
|
75
|
+
* document BECAUSE this scene is REACHED live" — the Edit workspace treats
|
|
76
|
+
* both the same (neither is a tab), while the `open` verb must route the
|
|
77
|
+
* second to the running game instead of refusing it, and must tell a caller
|
|
78
|
+
* with no game running to start one rather than that the scene is a dead end.
|
|
79
|
+
*
|
|
80
|
+
* Present means the live half REACHES this entry — it opens it and answers
|
|
81
|
+
* ok. Absent means it does not, which is two different things and
|
|
82
|
+
* deliberately one field: the live half may have no business with the entry
|
|
83
|
+
* at all (it answers `null` for `story`/`none`). A session that cannot
|
|
84
|
+
* remount still refuses `entrypoint-selection` coded; that is a session
|
|
85
|
+
* fact, not a reach fact — the intended path is remount-at-key and ends
|
|
86
|
+
* `ok: true`. {@link liveReachOf} is where that line is drawn.
|
|
87
|
+
*
|
|
88
|
+
* It is derived from REACH ALONE ({@link liveReachOf}), never from
|
|
89
|
+
* `authorable`, because the live half is the authority on this half of the
|
|
90
|
+
* question and it reads reach only (`scene-live-open.ts` switches on
|
|
91
|
+
* `entry.reach.kind`). A planner that also asked `authorable` here would call
|
|
92
|
+
* a screen the running game navigates to a dead end — the exact collapse this
|
|
93
|
+
* field exists to prevent.
|
|
94
|
+
*/
|
|
95
|
+
| {
|
|
96
|
+
readonly kind: 'no-document';
|
|
97
|
+
readonly reason: string;
|
|
98
|
+
readonly liveReach?: 'game-contract' | 'root-mount' | 'entrypoint-selection';
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* THE ONE DERIVATION of `liveReach`, from the entry's DECLARED reach and
|
|
103
|
+
* nothing else — never from `authorable`, so the two clients cannot disagree.
|
|
104
|
+
*
|
|
105
|
+
* It answers for exactly the reaches `openLiveSceneEntry` REACHES: it
|
|
106
|
+
* navigates a `game-contract` scene through the game's own contract, it
|
|
107
|
+
* shows the document a `root-mount` scene draws into, and it remounts play
|
|
108
|
+
* at an `entrypoint-selection` key. All three end `ok: true` when the
|
|
109
|
+
* session can honour them, so for all three the honest Edit-half refusal
|
|
110
|
+
* with nothing running is `SCENE_NAVIGATION_NOT_RUNNING` — start the game
|
|
111
|
+
* and ask again.
|
|
112
|
+
*
|
|
113
|
+
* A session that cannot remount still refuses `entrypoint-selection` with
|
|
114
|
+
* `SCENE_NOT_OPENABLE_LIVE` — that is the same shape as a `game-contract`
|
|
115
|
+
* scene whose running game publishes no contract (`SCENE_CONTRACT_UNAVAILABLE`).
|
|
116
|
+
* The field names the intended live path, not every session's ability to
|
|
117
|
+
* walk it.
|
|
118
|
+
*
|
|
119
|
+
* A spread rather than a value so `exactOptionalPropertyTypes` gets an ABSENT
|
|
120
|
+
* key for "no live reach", never an explicit `undefined`. Every `no-document`
|
|
121
|
+
* return below spreads it unconditionally — the two where it is currently
|
|
122
|
+
* provably empty included — so this function stays the SOLE decider and a
|
|
123
|
+
* later widening cannot leave a branch silently behind.
|
|
124
|
+
*/
|
|
125
|
+
function liveReachOf(entry: DocumentEntry): {
|
|
126
|
+
readonly liveReach?: 'game-contract' | 'root-mount' | 'entrypoint-selection';
|
|
127
|
+
} {
|
|
128
|
+
const { kind } = entry.reach;
|
|
129
|
+
return kind === 'game-contract' || kind === 'root-mount' || kind === 'entrypoint-selection'
|
|
130
|
+
? { liveReach: kind }
|
|
131
|
+
: {};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Plan ONE entry.
|
|
136
|
+
*
|
|
137
|
+
* Order matters: `authorable` is asked FIRST because a traversal entry
|
|
138
|
+
* (boot/loading choreography) earns no column whatever its reach — the flag is
|
|
139
|
+
* about what the entry IS, while reach is about how the running game gets
|
|
140
|
+
* there.
|
|
141
|
+
*
|
|
142
|
+
* That order decides the DOCUMENT, and only the document. `liveReach` is
|
|
143
|
+
* carried through it untouched: whether a running game can be sent to a screen
|
|
144
|
+
* is the game's contract's business, not the host's opinion of whether the
|
|
145
|
+
* screen is worth authoring, and the live half honours that by reading reach
|
|
146
|
+
* alone.
|
|
147
|
+
*/
|
|
148
|
+
export function planSceneDocument(entry: DocumentEntry): SceneDocumentPlan {
|
|
149
|
+
if (!entry.authorable) {
|
|
150
|
+
return {
|
|
151
|
+
kind: 'no-document',
|
|
152
|
+
...liveReachOf(entry),
|
|
153
|
+
reason:
|
|
154
|
+
`"${entry.id}" is declared TRAVERSAL (boot/loading choreography), not an authorable ` +
|
|
155
|
+
'composition, so it has no Edit document.',
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
switch (entry.reach.kind) {
|
|
159
|
+
case 'none':
|
|
160
|
+
// VERBATIM. The adapter wrote this sentence about its own game; a host
|
|
161
|
+
// paraphrase would be the host claiming to know why.
|
|
162
|
+
return { kind: 'no-document', ...liveReachOf(entry), reason: entry.reach.reason };
|
|
163
|
+
case 'root-mount':
|
|
164
|
+
return entry.region === null
|
|
165
|
+
? {
|
|
166
|
+
// Still LIVE-REACHED: `openLiveSceneEntry` shows the document the
|
|
167
|
+
// running game draws into and never looks at `region`, so the
|
|
168
|
+
// missing region costs this entry its Edit document and nothing
|
|
169
|
+
// else.
|
|
170
|
+
kind: 'no-document',
|
|
171
|
+
...liveReachOf(entry),
|
|
172
|
+
reason:
|
|
173
|
+
`"${entry.id}" is reached by mounting a region, but the entry names no region, ` +
|
|
174
|
+
'so there is no standing document it could be.',
|
|
175
|
+
}
|
|
176
|
+
: { kind: 'root-document', regionId: entry.region, title: entry.label };
|
|
177
|
+
case 'entrypoint-selection':
|
|
178
|
+
// ACTIVE means the entrypoint indexes its selection table with THIS key,
|
|
179
|
+
// so the region's standing document already mounts this composition —
|
|
180
|
+
// opening a second document for it would show the same thing twice under
|
|
181
|
+
// two titles. The entry names the standing document instead, exactly as
|
|
182
|
+
// `root-mount` does. A region-less entry keeps the isolation document:
|
|
183
|
+
// there is no honest mapping from "active" to a document without one, and
|
|
184
|
+
// inventing a region is what zero-inference forbids.
|
|
185
|
+
if (entry.reach.active === true && entry.region !== null) {
|
|
186
|
+
return { kind: 'root-document', regionId: entry.region, title: entry.label };
|
|
187
|
+
}
|
|
188
|
+
return entry.source
|
|
189
|
+
? {
|
|
190
|
+
kind: 'isolation-document',
|
|
191
|
+
documentId: sceneDocumentId(entry.id),
|
|
192
|
+
title: entry.label,
|
|
193
|
+
regionId: entry.region,
|
|
194
|
+
source: entry.source,
|
|
195
|
+
...(entry.isolationSetup ? { isolationSetup: entry.isolationSetup } : {}),
|
|
196
|
+
}
|
|
197
|
+
: {
|
|
198
|
+
kind: 'no-document',
|
|
199
|
+
...liveReachOf(entry),
|
|
200
|
+
reason:
|
|
201
|
+
`"${entry.id}" is selected at the entrypoint's \`${entry.reach.selection}\` slot, ` +
|
|
202
|
+
'but the finder could not resolve the module it names, so there is nothing to ' +
|
|
203
|
+
'mount in isolation.',
|
|
204
|
+
};
|
|
205
|
+
case 'game-contract':
|
|
206
|
+
// Authorable + a source module is an Edit piece: every authorable scene
|
|
207
|
+
// gets a tab (ARCHITECTURE-CORE §Editor). Play still navigates live
|
|
208
|
+
// through `liveReach`. Without a source there is nothing to isolate, so
|
|
209
|
+
// Edit stays silent and `open` asks the running game.
|
|
210
|
+
return entry.source
|
|
211
|
+
? {
|
|
212
|
+
kind: 'isolation-document',
|
|
213
|
+
documentId: sceneDocumentId(entry.id),
|
|
214
|
+
title: entry.label,
|
|
215
|
+
regionId: entry.region,
|
|
216
|
+
source: entry.source,
|
|
217
|
+
...(entry.isolationSetup ? { isolationSetup: entry.isolationSetup } : {}),
|
|
218
|
+
contractSceneId: entry.reach.sceneId,
|
|
219
|
+
}
|
|
220
|
+
: {
|
|
221
|
+
kind: 'no-document',
|
|
222
|
+
...liveReachOf(entry),
|
|
223
|
+
reason:
|
|
224
|
+
`"${entry.id}" is reached only through the running game's own navigation ` +
|
|
225
|
+
`(its scenes contract calls it "${entry.reach.sceneId}"), and the entry names no ` +
|
|
226
|
+
'source module, so there is no Edit document — open it while the game is running.',
|
|
227
|
+
};
|
|
228
|
+
case 'story':
|
|
229
|
+
return { kind: 'story-document', title: entry.label, storyId: entry.reach.storyId };
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** One row of the Edit tab row. */
|
|
234
|
+
export interface SceneTabRow {
|
|
235
|
+
readonly entry: DocumentEntry;
|
|
236
|
+
/** Never `no-document` — a row that owns no document is not a tab. */
|
|
237
|
+
readonly plan: Exclude<SceneDocumentPlan, { kind: 'no-document' }>;
|
|
238
|
+
/** The table's declared default entry. At most one row carries it. */
|
|
239
|
+
readonly isDefault: boolean;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* The whole Edit tab row: every AUTHORABLE SCENE the table declares that owns
|
|
244
|
+
* a document, in the table's own order.
|
|
245
|
+
*
|
|
246
|
+
* Prefabs are excluded deliberately, and they are not second-class for it: a
|
|
247
|
+
* prefab already surfaces through the boards and Content, and it opens as a
|
|
248
|
+
* sibling document on demand through the same verb (`open`). What a standing
|
|
249
|
+
* TAB means is "this is one of the game's scenes", which is exactly the
|
|
250
|
+
* `kind: 'scene'` declaration.
|
|
251
|
+
*/
|
|
252
|
+
export function sceneTabRow(table: ResolvedDocumentTable): readonly SceneTabRow[] {
|
|
253
|
+
const rows: SceneTabRow[] = [];
|
|
254
|
+
for (const entry of table.entries) {
|
|
255
|
+
if (entry.kind !== 'scene') continue;
|
|
256
|
+
const plan = planSceneDocument(entry);
|
|
257
|
+
if (plan.kind === 'no-document') continue;
|
|
258
|
+
rows.push({ entry, plan, isDefault: table.default === entry.id });
|
|
259
|
+
}
|
|
260
|
+
return rows;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** What Edit and Content actually present from one resolved scene table. */
|
|
264
|
+
export function authoringSurfaceFromTable(
|
|
265
|
+
table: ResolvedDocumentTable,
|
|
266
|
+
openDocumentIds: readonly string[],
|
|
267
|
+
availableDocumentIds: readonly string[],
|
|
268
|
+
): {
|
|
269
|
+
readonly sceneDocuments: number;
|
|
270
|
+
readonly isolationDocuments: number;
|
|
271
|
+
readonly openIsolationDocuments: number;
|
|
272
|
+
readonly availableIsolationDocuments: number;
|
|
273
|
+
readonly pieces: number;
|
|
274
|
+
/** Entries of kind `scene` — the surface prefabs are placed on; a table
|
|
275
|
+
* with none (a models or website project) has no prefabs by design. */
|
|
276
|
+
readonly sceneEntries: number;
|
|
277
|
+
/** Project story modules that did not load or compose: each one's prefab is missing from
|
|
278
|
+
* the table with no entry of its own to say so. */
|
|
279
|
+
readonly storyFailures: number;
|
|
280
|
+
} {
|
|
281
|
+
const tabs = sceneTabRow(table);
|
|
282
|
+
const isolationIds = tabs.flatMap((row) =>
|
|
283
|
+
row.plan.kind === 'isolation-document' ? [row.plan.documentId] : [],
|
|
284
|
+
);
|
|
285
|
+
const open = new Set(openDocumentIds);
|
|
286
|
+
const available = new Set(availableDocumentIds);
|
|
287
|
+
// A finder's OWN document kind (a `model` from `@volter/editor-blender`'s finder, a
|
|
288
|
+
// `page`, a `shot`) opens through that kind's document, never through the
|
|
289
|
+
// scene plan above — which answers `no-document` for it because it names
|
|
290
|
+
// no region. Those are documents Edit presents all the same; measured
|
|
291
|
+
// 2026-09-17, a models project reported "no authorable scene with a
|
|
292
|
+
// document to open" beside its open model document.
|
|
293
|
+
const kindDocuments = table.entries.filter(
|
|
294
|
+
(entry) => entry.authorable && entry.kind !== 'scene' && entry.kind !== 'prefab',
|
|
295
|
+
).length;
|
|
296
|
+
return {
|
|
297
|
+
sceneDocuments: tabs.length + kindDocuments,
|
|
298
|
+
isolationDocuments: isolationIds.length,
|
|
299
|
+
openIsolationDocuments: isolationIds.filter((id) => open.has(id)).length,
|
|
300
|
+
availableIsolationDocuments: isolationIds.filter((id) => available.has(id)).length,
|
|
301
|
+
pieces: table.entries.filter((entry) => entry.kind === 'prefab').length,
|
|
302
|
+
sceneEntries: table.entries.filter((entry) => entry.kind === 'scene').length,
|
|
303
|
+
storyFailures: getProjectStoryModules().filter((module_) => !module_.ok).length,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Isolation tabs ARE the Edit scene row. A generic Scene tab
|
|
309
|
+
* (`workspace:scene` / `workspace:canvas-scene`) would be a second surface
|
|
310
|
+
* for the same game — the live host, or an empty world — and only belongs
|
|
311
|
+
* when this table has no isolation documents (or also names a root document
|
|
312
|
+
* that Scene already is).
|
|
313
|
+
*/
|
|
314
|
+
export function isolationTabsReplaceGenericScene(table: ResolvedDocumentTable): boolean {
|
|
315
|
+
const rows = sceneTabRow(table);
|
|
316
|
+
return (
|
|
317
|
+
rows.some((row) => row.plan.kind === 'isolation-document') &&
|
|
318
|
+
!rows.some((row) => row.plan.kind === 'root-document')
|
|
319
|
+
);
|
|
320
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE SCENE TABLE'S SECOND CLIENT — `open()` against the RUNNING game.
|
|
3
|
+
*
|
|
4
|
+
* ARCHITECTURE-CORE §Editor: "the scene table … is ONE adapter declaration with
|
|
5
|
+
* two clients — the Edit tab row projects it as isolation documents … and
|
|
6
|
+
* Play's `open()` walks it as live navigation." `scene-document-plan.ts` +
|
|
7
|
+
* `components/scene-documents.tsx` are the first client; this module is the
|
|
8
|
+
* second, and both are reached through the ONE `open` verb in
|
|
9
|
+
* `command-listener.ts`, which asks this one first whenever a game is live.
|
|
10
|
+
*
|
|
11
|
+
* ZERO INFERENCE, twice over:
|
|
12
|
+
*
|
|
13
|
+
* 1. WHICH contract id an entry is, is the adapter's own declaration
|
|
14
|
+
* (`reach: { kind: 'game-contract', sceneId }`). Nothing here matches
|
|
15
|
+
* entry ids against contract ids, or assumes the two vocabularies coincide.
|
|
16
|
+
* 2. WHETHER that id is navigable is the GAME's answer, at the moment of use:
|
|
17
|
+
* the switch goes through the bound scenes projection's own
|
|
18
|
+
* `goToScene` (`authoring/contract-scenes-stories.ts`), which validates
|
|
19
|
+
* membership against the game's own `list()` and carries the mount's
|
|
20
|
+
* held-repaint seam. Calling the contract's `goTo` directly would route
|
|
21
|
+
* around both.
|
|
22
|
+
*
|
|
23
|
+
* Every seam is injected ({@link LiveSceneSession}) so the whole matrix is
|
|
24
|
+
* decidable headlessly — no mounted game, no workspace, no browser.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import type { LiveSceneTable } from '@volter/sdk/host';
|
|
28
|
+
import type { DocumentEntry } from '@volter/project/adapter/adapter-module';
|
|
29
|
+
|
|
30
|
+
/** What a live game session offers this verb. */
|
|
31
|
+
export interface LiveSceneSession {
|
|
32
|
+
/**
|
|
33
|
+
* The running game's BOUND scenes projection, or `null` when the game
|
|
34
|
+
* publishes none.
|
|
35
|
+
*
|
|
36
|
+
* "Bound" is load-bearing: this is the object the mount installed on the
|
|
37
|
+
* game's own authoring adapter — the same one the inspector's scene picker
|
|
38
|
+
* drives — so a switch made here is the same switch a click makes, held
|
|
39
|
+
* repaint and all.
|
|
40
|
+
*/
|
|
41
|
+
readonly scenes: () => LiveSceneTable | null;
|
|
42
|
+
/**
|
|
43
|
+
* Activate the document the running game is presented in; `false` when the
|
|
44
|
+
* session has none open. An ingested game's mount IS the running game, and
|
|
45
|
+
* it draws into the Game document — there is no second document a
|
|
46
|
+
* `root-mount` scene could name.
|
|
47
|
+
*/
|
|
48
|
+
readonly activateGameDocument: () => boolean;
|
|
49
|
+
/** The Game document's id, for the answer. */
|
|
50
|
+
readonly gameDocumentId: string;
|
|
51
|
+
/**
|
|
52
|
+
* Host remount of a native swap-slot scene: stop this play run and start a
|
|
53
|
+
* new one with the entrypoint's selection const rewritten to `key`. Absent
|
|
54
|
+
* when this session has no serve-time rewrite (an ingest root), which is
|
|
55
|
+
* when `entrypoint-selection` still refuses `SCENE_NOT_OPENABLE_LIVE`.
|
|
56
|
+
*/
|
|
57
|
+
readonly remountSelection?: (args: {
|
|
58
|
+
readonly selection: string;
|
|
59
|
+
readonly key: string;
|
|
60
|
+
readonly regionId: string;
|
|
61
|
+
}) => Promise<{ readonly ok: true } | { readonly ok: false; readonly error: string }>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** A coded refusal from the live half — the SDK matches `code`, never prose. */
|
|
65
|
+
export interface LiveSceneOpenRefusal {
|
|
66
|
+
readonly ok: false;
|
|
67
|
+
readonly code:
|
|
68
|
+
| 'SCENE_CONTRACT_UNAVAILABLE'
|
|
69
|
+
| 'SCENE_NOT_IN_CONTRACT'
|
|
70
|
+
| 'SCENE_SWITCH_FAILED'
|
|
71
|
+
| 'SCENE_NOT_OPENABLE_LIVE'
|
|
72
|
+
| 'SCENE_DOCUMENT_NOT_MOUNTED';
|
|
73
|
+
readonly error: string;
|
|
74
|
+
/** The ids the game itself published, when the refusal is about membership. */
|
|
75
|
+
readonly known?: readonly string[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export type LiveSceneOpenResult =
|
|
79
|
+
| {
|
|
80
|
+
readonly ok: true;
|
|
81
|
+
readonly documentId: string;
|
|
82
|
+
readonly title: string;
|
|
83
|
+
/** The game's own answer: what was asked for, and where it ended up. */
|
|
84
|
+
readonly scene?: { readonly requested: string; readonly current: string | null };
|
|
85
|
+
/**
|
|
86
|
+
* Present when this open restarted play rather than navigating a live
|
|
87
|
+
* contract — the swap slot is a module-level const, so the honest
|
|
88
|
+
* live path is remount-at-key.
|
|
89
|
+
*/
|
|
90
|
+
readonly restart?: true;
|
|
91
|
+
}
|
|
92
|
+
| LiveSceneOpenRefusal;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Open ONE scene-table entry against the running game, or answer `null` when
|
|
96
|
+
* this entry is not the live half's business — a `story` reach has a portable
|
|
97
|
+
* mount, and a `none` reach is the adapter's own declared dead end. Both are
|
|
98
|
+
* the Edit half's, unchanged, and answering `null` is how this module says so
|
|
99
|
+
* rather than duplicating those refusals.
|
|
100
|
+
*/
|
|
101
|
+
export async function openLiveSceneEntry(
|
|
102
|
+
entry: DocumentEntry,
|
|
103
|
+
session: LiveSceneSession,
|
|
104
|
+
): Promise<LiveSceneOpenResult | null> {
|
|
105
|
+
switch (entry.reach.kind) {
|
|
106
|
+
case 'game-contract': {
|
|
107
|
+
const scenes = session.scenes();
|
|
108
|
+
if (!scenes) {
|
|
109
|
+
return {
|
|
110
|
+
ok: false,
|
|
111
|
+
code: 'SCENE_CONTRACT_UNAVAILABLE',
|
|
112
|
+
error:
|
|
113
|
+
`open: "${entry.id}" is reached through the running game's own scenes contract, ` +
|
|
114
|
+
'and the game running in this session publishes none (`window.volterGame.scenes`).',
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
const switched = scenes.goToScene(entry.reach.sceneId);
|
|
118
|
+
if (!switched.ok) {
|
|
119
|
+
// The projection's own membership refusal, passed through: the game's
|
|
120
|
+
// `list()` is the authority on what it can be sent to, and a host
|
|
121
|
+
// paraphrase would be the host claiming to know the game's screens.
|
|
122
|
+
return {
|
|
123
|
+
ok: false,
|
|
124
|
+
code: 'SCENE_NOT_IN_CONTRACT',
|
|
125
|
+
error: `open: "${entry.id}" — ${switched.error}`,
|
|
126
|
+
known: switched.known,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
const settled = await switched.settled;
|
|
130
|
+
if (settled.error !== undefined) {
|
|
131
|
+
return {
|
|
132
|
+
ok: false,
|
|
133
|
+
code: 'SCENE_SWITCH_FAILED',
|
|
134
|
+
error: `open: "${entry.id}" — the game's own navigation failed: ${settled.error}`,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
// Activation is best-effort AFTER the switch: the navigation is the
|
|
138
|
+
// gesture, and a session with no Game document open (headless control,
|
|
139
|
+
// a workspace the user rearranged) still navigated the game.
|
|
140
|
+
session.activateGameDocument();
|
|
141
|
+
return {
|
|
142
|
+
ok: true,
|
|
143
|
+
documentId: session.gameDocumentId,
|
|
144
|
+
title: entry.label,
|
|
145
|
+
scene: { requested: settled.requested, current: settled.current },
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
case 'root-mount': {
|
|
149
|
+
// The running game IS this scene — mounting the region opened it — so the
|
|
150
|
+
// whole gesture is showing the document it draws into. REACHED, so the
|
|
151
|
+
// planner carries it as `liveReach: 'root-mount'` and the Edit half
|
|
152
|
+
// grades a no-game session `SCENE_NAVIGATION_NOT_RUNNING`: start the
|
|
153
|
+
// game and this returns ok. babylon-space-truckers' `splashScreen` is
|
|
154
|
+
// the shipped instance (`authorable: false`, so it owns no Edit
|
|
155
|
+
// document — pressing ▶ is the whole answer, not a defect in its
|
|
156
|
+
// declaration).
|
|
157
|
+
if (!session.activateGameDocument()) {
|
|
158
|
+
return {
|
|
159
|
+
ok: false,
|
|
160
|
+
code: 'SCENE_DOCUMENT_NOT_MOUNTED',
|
|
161
|
+
error: `open: "${entry.id}" is the running game itself, and no game document is open in this session.`,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return { ok: true, documentId: session.gameDocumentId, title: entry.label };
|
|
165
|
+
}
|
|
166
|
+
case 'entrypoint-selection': {
|
|
167
|
+
// The slot is a module-level CONST — the running module cannot change
|
|
168
|
+
// scenes. The honest live path is a host remount: stop this play run and
|
|
169
|
+
// start a new one with the entrypoint served at `key`. That is REAL
|
|
170
|
+
// navigation (the next mount executes that key), stated as a restart.
|
|
171
|
+
// A session with no remount seam (hosted bundle, ingest) still refuses
|
|
172
|
+
// coded — never an in-place fake.
|
|
173
|
+
const remount = session.remountSelection;
|
|
174
|
+
const regionId = entry.region;
|
|
175
|
+
if (!remount || regionId === null) {
|
|
176
|
+
return {
|
|
177
|
+
ok: false,
|
|
178
|
+
code: 'SCENE_NOT_OPENABLE_LIVE',
|
|
179
|
+
error:
|
|
180
|
+
`open: "${entry.id}" sits at the entrypoint's \`${entry.reach.selection}\` swap slot, ` +
|
|
181
|
+
'and this session has no live remount for a native swap-slot scene — a running game ' +
|
|
182
|
+
'cannot be sent there in place. Stop the game and open it as a document instead.',
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
const remounted = await remount({
|
|
186
|
+
selection: entry.reach.selection,
|
|
187
|
+
key: entry.reach.key,
|
|
188
|
+
regionId,
|
|
189
|
+
});
|
|
190
|
+
if (!remounted.ok) {
|
|
191
|
+
return {
|
|
192
|
+
ok: false,
|
|
193
|
+
code: 'SCENE_SWITCH_FAILED',
|
|
194
|
+
error: `open: "${entry.id}" — remount at \`${entry.reach.selection}["${entry.reach.key}"]\` failed: ${remounted.error}`,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
session.activateGameDocument();
|
|
198
|
+
return {
|
|
199
|
+
ok: true,
|
|
200
|
+
documentId: session.gameDocumentId,
|
|
201
|
+
title: entry.label,
|
|
202
|
+
scene: { requested: entry.reach.key, current: entry.reach.key },
|
|
203
|
+
restart: true,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
case 'story':
|
|
207
|
+
case 'none':
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The client half of SCOPED GAME CSS — fetch the open project's page-level
|
|
3
|
+
* stylesheet (already rewritten into `@scope ([data-volter-game-styles])` by
|
|
4
|
+
* `@volter/sdk/session/scoped-game-css`), install it ONCE, and report honestly when
|
|
5
|
+
* there is nothing to install.
|
|
6
|
+
*
|
|
7
|
+
* ONE `<style>` element per project, never one per surface. Every container
|
|
8
|
+
* that owns game DOM — each UI-board story card's content, each story
|
|
9
|
+
* document's preview, the ingest surface the game mounts into — becomes a
|
|
10
|
+
* scope root by wearing the attribute (`markGameCssScope`, `game-css-scope.ts`),
|
|
11
|
+
* and the single scoped sheet paints all of them. Duplicating the sheet per
|
|
12
|
+
* card would multiply a foreign game's whole stylesheet by the size of the
|
|
13
|
+
* board for no behavioural difference at all.
|
|
14
|
+
*
|
|
15
|
+
* ## The refusal path is the point
|
|
16
|
+
*
|
|
17
|
+
* A surface that shows a game's DOM with no page CSS looks BROKEN, not empty —
|
|
18
|
+
* the owner hit exactly that three times before this existed and read it as
|
|
19
|
+
* wreckage, because nothing on screen said what was missing. So this module's
|
|
20
|
+
* state is a value the surfaces read (`note`), not a silent side effect: a
|
|
21
|
+
* board card whose project declares no stylesheet, or whose stylesheet could
|
|
22
|
+
* not be read, or a browser with no `@scope`, says so on the card.
|
|
23
|
+
*
|
|
24
|
+
* `@scope` is the feature detection and it is deliberately a REFUSAL, not a
|
|
25
|
+
* fallback: without it the served sheet's `:scope` rules would not be
|
|
26
|
+
* contained, and installing it anyway would restyle the editor — the precise
|
|
27
|
+
* failure the whole feature exists to prevent.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { editorConsole } from '@volter/sdk/kit/editor-console';
|
|
31
|
+
import { editorServerJson } from '@volter/sdk/kit/editor-server-response';
|
|
32
|
+
|
|
33
|
+
/** What the open project's game CSS turned out to be. */
|
|
34
|
+
export interface ScopedGameStylesState {
|
|
35
|
+
/** True once the project's scoped stylesheet is attached to the document. */
|
|
36
|
+
readonly installed: boolean;
|
|
37
|
+
/** The declared stylesheet paths that were served, in emission order. */
|
|
38
|
+
readonly sources: readonly string[];
|
|
39
|
+
/**
|
|
40
|
+
* Surface-facing sentence naming what is missing, or `null` when the host
|
|
41
|
+
* has no evidence of a gap. Rendered ON a story card rather than logged,
|
|
42
|
+
* because the card is where the missing styling is SEEN.
|
|
43
|
+
*/
|
|
44
|
+
readonly note: string | null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const NOTHING_DECLARED: ScopedGameStylesState = { installed: false, sources: [], note: null };
|
|
48
|
+
|
|
49
|
+
/** `data-*` marker on the one installed element, so a re-install can find it. */
|
|
50
|
+
const STYLE_ELEMENT_MARKER = 'data-volter-scoped-game-css';
|
|
51
|
+
|
|
52
|
+
/** Per-project memo: the fetch+install runs once per open project, not per card. */
|
|
53
|
+
let pending: { projectRoot: string; state: Promise<ScopedGameStylesState> } | null = null;
|
|
54
|
+
|
|
55
|
+
/** Latest resolved state, for the synchronous readers (a board painting notes). */
|
|
56
|
+
let resolved: ScopedGameStylesState = NOTHING_DECLARED;
|
|
57
|
+
|
|
58
|
+
/** The installed sheet's own text — see {@link scopedGameStylesCssText}. */
|
|
59
|
+
let installedCss: string | null = null;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The installed stylesheet's text, or null when none is installed.
|
|
63
|
+
*
|
|
64
|
+
* Exists for ONE reader: the DOM capture leg (`composite-screenshot.ts`),
|
|
65
|
+
* which rasterizes a DETACHED clone of the surface through an SVG
|
|
66
|
+
* `foreignObject`. A detached fragment carries no document stylesheet, so
|
|
67
|
+
* without re-inlining this the look verb would photograph a correctly styled
|
|
68
|
+
* screen as unstyled — an instrument disagreeing with the product it is
|
|
69
|
+
* pointed at, which is worse than no instrument. (The same leg already
|
|
70
|
+
* re-inlines its motion freeze for the same structural reason.)
|
|
71
|
+
*/
|
|
72
|
+
export function scopedGameStylesCssText(): string | null {
|
|
73
|
+
return installedCss;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* True when this browser implements CSS `@scope`. Without it the served sheet
|
|
78
|
+
* cannot be contained, so nothing is installed at all.
|
|
79
|
+
*/
|
|
80
|
+
function supportsCssScope(): boolean {
|
|
81
|
+
return typeof (globalThis as { CSSScopeRule?: unknown }).CSSScopeRule === 'function';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Fetch and install the open project's scoped game stylesheet, at most once per
|
|
86
|
+
* project. Safe to call from every surface that mounts game DOM — later calls
|
|
87
|
+
* await the same promise.
|
|
88
|
+
*/
|
|
89
|
+
export async function ensureScopedGameStyles(projectRoot: string): Promise<ScopedGameStylesState> {
|
|
90
|
+
if (pending?.projectRoot === projectRoot) return pending.state;
|
|
91
|
+
const state = loadScopedGameStyles(projectRoot);
|
|
92
|
+
pending = { projectRoot, state };
|
|
93
|
+
return state;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* The most recent resolved state. Surfaces that paint a note read this
|
|
98
|
+
* synchronously AFTER awaiting {@link ensureScopedGameStyles} — it exists so a
|
|
99
|
+
* board does not have to thread the promise through its own layout pass.
|
|
100
|
+
*/
|
|
101
|
+
export function scopedGameStylesState(): ScopedGameStylesState {
|
|
102
|
+
return resolved;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function loadScopedGameStyles(projectRoot: string): Promise<ScopedGameStylesState> {
|
|
106
|
+
const next = await fetchScopedGameStyles(projectRoot);
|
|
107
|
+
resolved = next;
|
|
108
|
+
return next;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function fetchScopedGameStyles(projectRoot: string): Promise<ScopedGameStylesState> {
|
|
112
|
+
let payload: { css?: unknown; sources?: unknown; note?: unknown };
|
|
113
|
+
try {
|
|
114
|
+
const response = await fetch('/__editor/scoped-game-css');
|
|
115
|
+
// A page fallback in front of the route fails the reader below and lands
|
|
116
|
+
// in the same graceful no-op story discovery takes. Nothing is claimed
|
|
117
|
+
// about the project's styling.
|
|
118
|
+
payload = await editorServerJson<typeof payload>(
|
|
119
|
+
response,
|
|
120
|
+
'Could not read the project scoped game CSS',
|
|
121
|
+
);
|
|
122
|
+
} catch {
|
|
123
|
+
return NOTHING_DECLARED;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const css = typeof payload.css === 'string' ? payload.css : '';
|
|
127
|
+
const sources = Array.isArray(payload.sources) ? (payload.sources as string[]) : [];
|
|
128
|
+
const note = typeof payload.note === 'string' ? payload.note : null;
|
|
129
|
+
|
|
130
|
+
if (css.length === 0) return { installed: false, sources, note };
|
|
131
|
+
|
|
132
|
+
if (!supportsCssScope()) {
|
|
133
|
+
const refusal =
|
|
134
|
+
'unstyled — this browser has no CSS `@scope`, so the game stylesheet cannot be contained ' +
|
|
135
|
+
'to the game. Loading it anyway would restyle the editor.';
|
|
136
|
+
editorConsole.warn(`[scoped-game-css] ${refusal}`, 'authoring');
|
|
137
|
+
return { installed: false, sources, note: refusal };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
installStyleElement(projectRoot, css);
|
|
141
|
+
return { installed: true, sources, note };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Attach (or replace) the ONE style element carrying the project's game CSS. */
|
|
145
|
+
function installStyleElement(projectRoot: string, css: string): void {
|
|
146
|
+
const existing = document.head.querySelector(`style[${STYLE_ELEMENT_MARKER}]`);
|
|
147
|
+
const element = existing instanceof HTMLStyleElement ? existing : document.createElement('style');
|
|
148
|
+
element.setAttribute(STYLE_ELEMENT_MARKER, projectRoot);
|
|
149
|
+
element.textContent = css;
|
|
150
|
+
if (!element.isConnected) document.head.appendChild(element);
|
|
151
|
+
installedCss = css;
|
|
152
|
+
}
|