@volter/editor-core 0.5.57
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/BUNDLED_NOTICES +1527 -0
- package/LICENSE +686 -0
- package/LICENSE-APACHE +202 -0
- package/NOTICE +18 -0
- package/README.md +24 -0
- package/dist/build/chunk-JS4D4XZ3.js +175 -0
- package/dist/build/chunk-JS4D4XZ3.js.map +7 -0
- package/dist/build/vite-plugin-product-contributions.js +100 -0
- package/dist/build/vite-plugin-product-contributions.js.map +7 -0
- package/dist/build/vite-plugin-shared-react.js +25 -0
- package/dist/build/vite-plugin-shared-react.js.map +7 -0
- package/dist/build/vite-plugin-shared-three.js +86 -0
- package/dist/build/vite-plugin-shared-three.js.map +7 -0
- package/dist/server/chunk-KMJC7AGO.js +98 -0
- package/dist/server/chunk-KMJC7AGO.js.map +7 -0
- package/dist/server/chunk-PSILUMGS.js +46 -0
- package/dist/server/chunk-PSILUMGS.js.map +7 -0
- package/dist/server/frame-proxy.js +255 -0
- package/dist/server/frame-proxy.js.map +7 -0
- package/dist/server/open-browser.js +53 -0
- package/dist/server/open-browser.js.map +7 -0
- package/dist/server/process-shutdown.js +127 -0
- package/dist/server/process-shutdown.js.map +7 -0
- package/dist/server/session-registry.js +141 -0
- package/dist/server/session-registry.js.map +7 -0
- package/dist/server/spawn-opener.js +11 -0
- package/dist/server/spawn-opener.js.map +7 -0
- package/dist/server/worktree-identity.js +11 -0
- package/dist/server/worktree-identity.js.map +7 -0
- package/dist-server/packaged.mjs +88816 -0
- package/package.json +118 -0
- package/scripts/build-plugins.mjs +14 -0
- package/scripts/build-server.mjs +26 -0
- package/scripts/build-session.mjs +9 -0
- package/server/account-credentials.ts +60 -0
- package/server/account-service.ts +1434 -0
- package/server/adapter-region-includes.ts +47 -0
- package/server/asset-catalog-v2.ts +175 -0
- package/server/asset-history-snapshots.ts +196 -0
- package/server/asset-ledger-store.ts +195 -0
- package/server/asset-library-routes.ts +1595 -0
- package/server/blender-wasm-artifact.ts +430 -0
- package/server/boot-timings.ts +106 -0
- package/server/canonical-path.ts +50 -0
- package/server/checkout-workspace-preflight.ts +349 -0
- package/server/cloud-asset-catalog.ts +158 -0
- package/server/coding-inference-launch.ts +237 -0
- package/server/collaboration-account-client.ts +202 -0
- package/server/collaboration-attribution.ts +122 -0
- package/server/collaboration-session.ts +934 -0
- package/server/comfyui-bridge.ts +248 -0
- package/server/console-ledger.ts +499 -0
- package/server/creation-site-transform.ts +304 -0
- package/server/creation-site-write.ts +648 -0
- package/server/data-file-serialize.ts +158 -0
- package/server/editor-brand-html.ts +72 -0
- package/server/editor-control-socket.ts +366 -0
- package/server/editor-server-options.ts +190 -0
- package/server/editor-server.ts +1483 -0
- package/server/editor-sse.ts +352 -0
- package/server/engine-provenance.ts +169 -0
- package/server/engine-source-restart.ts +117 -0
- package/server/frame-bridge.ts +115 -0
- package/server/frame-proxy.ts +490 -0
- package/server/frame-workbench.ts +259 -0
- package/server/frontend-handoff.ts +229 -0
- package/server/game-globals-shadow.ts +113 -0
- package/server/gameplay-session-retention.ts +149 -0
- package/server/gameplay-sessions.ts +123 -0
- package/server/generation-jobs.ts +201 -0
- package/server/generation-reconciler.ts +137 -0
- package/server/generative-execution-context.ts +59 -0
- package/server/git-workflow.ts +612 -0
- package/server/harness-chat-caller.ts +36 -0
- package/server/harness-chat-service.ts +1681 -0
- package/server/idle-shutdown.ts +170 -0
- package/server/js-profiling-policy.ts +52 -0
- package/server/launcher-settings.ts +84 -0
- package/server/local-asset-catalog.ts +410 -0
- package/server/managed-account-defaults.ts +22 -0
- package/server/mock-control-plane.ts +540 -0
- package/server/model-import-conversion.ts +345 -0
- package/server/native-credential-store.ts +162 -0
- package/server/open-browser.ts +118 -0
- package/server/packaged.ts +1192 -0
- package/server/play-stall.ts +126 -0
- package/server/process-shutdown.ts +218 -0
- package/server/product-presets.ts +55 -0
- package/server/project-build-artifact.ts +95 -0
- package/server/project-components.ts +100 -0
- package/server/project-dependency-invalidation.ts +114 -0
- package/server/project-file-scan.ts +235 -0
- package/server/project-hmr-files.ts +215 -0
- package/server/project-install-roots.ts +210 -0
- package/server/project-kinds.ts +71 -0
- package/server/project-mcp-servers.ts +82 -0
- package/server/project-module-freshness.ts +109 -0
- package/server/project-module-instance.ts +375 -0
- package/server/project-optimize-deps-entries.ts +827 -0
- package/server/project-output-writer.ts +679 -0
- package/server/project-package-origin.ts +63 -0
- package/server/project-root-surface.ts +831 -0
- package/server/project-scratch-path.ts +47 -0
- package/server/project-script-hmr.ts +619 -0
- package/server/project-serving-plugins.ts +236 -0
- package/server/project-tools.ts +653 -0
- package/server/project-validation.ts +250 -0
- package/server/project-verbs.ts +127 -0
- package/server/project-view.ts +52 -0
- package/server/project-watch.ts +1134 -0
- package/server/project-work-coordinator.ts +288 -0
- package/server/provider-credentials.ts +331 -0
- package/server/recent-projects-store.ts +98 -0
- package/server/redact-secrets.ts +71 -0
- package/server/repository-presence.ts +267 -0
- package/server/routes/account.ts +357 -0
- package/server/routes/agents.ts +60 -0
- package/server/routes/assets.ts +154 -0
- package/server/routes/build.ts +13 -0
- package/server/routes/collaboration.ts +595 -0
- package/server/routes/configurations.ts +419 -0
- package/server/routes/context.ts +264 -0
- package/server/routes/control-plane.ts +1811 -0
- package/server/routes/logs.ts +272 -0
- package/server/routes/project-identity.ts +302 -0
- package/server/routes/project-open.ts +429 -0
- package/server/routes/project-source.ts +686 -0
- package/server/routes/project-state.ts +383 -0
- package/server/routes/relay.ts +1029 -0
- package/server/routes/route-helpers.ts +29 -0
- package/server/routes/served-modules.ts +138 -0
- package/server/routes/session-tabs.ts +196 -0
- package/server/routes/settings.ts +121 -0
- package/server/routes/share-control.ts +486 -0
- package/server/routes/themes.ts +116 -0
- package/server/routes/tools.ts +230 -0
- package/server/routes/worktrees.ts +595 -0
- package/server/scoped-game-css.ts +461 -0
- package/server/server-utils.ts +1194 -0
- package/server/session-product.ts +112 -0
- package/server/session-registry.ts +230 -0
- package/server/share-claims.ts +138 -0
- package/server/share-host.ts +526 -0
- package/server/share-session-gateway.ts +1362 -0
- package/server/share-tunnel.ts +123 -0
- package/server/spawn-opener.ts +74 -0
- package/server/support/play/log-format.ts +76 -0
- package/server/support/play/session-record.ts +199 -0
- package/server/support/project/build-discipline.ts +718 -0
- package/server/support/project/inspection-node.ts +119 -0
- package/server/support/project/log-naming.ts +54 -0
- package/server/support/project/provenance.ts +290 -0
- package/server/support/project/run-name.ts +18 -0
- package/server/support/project/session-journal.ts +830 -0
- package/server/support/project/shared.ts +327 -0
- package/server/tab-bootstrap.ts +62 -0
- package/server/tab-heartbeat.ts +477 -0
- package/server/tab-lifecycle.ts +550 -0
- package/server/tab-presence.ts +1508 -0
- package/server/team-agent-mirror.ts +95 -0
- package/server/twin-auth.ts +138 -0
- package/server/vendored-lock-recorder.ts +968 -0
- package/server/worktree-identity.ts +121 -0
- package/server/worktree-management.ts +398 -0
- package/server/worktree-retention.d.mts +13 -0
- package/server/worktree-retention.mjs +205 -0
- package/src/EditorContext.tsx +169 -0
- package/src/account.ts +333 -0
- package/src/action-registry.ts +532 -0
- package/src/active-product.ts +74 -0
- package/src/active-project.ts +155 -0
- package/src/adapter-editor-config.ts +25 -0
- package/src/adapter-observation.ts +49 -0
- package/src/animation/stage-transport.ts +379 -0
- package/src/animation/three-clips-subject.ts +172 -0
- package/src/api/asset-library-wire.ts +45 -0
- package/src/api/assets.ts +365 -0
- package/src/api/base.ts +10 -0
- package/src/api/build.ts +99 -0
- package/src/api/git-wire.ts +56 -0
- package/src/api/logs.ts +92 -0
- package/src/api/project-identity.ts +74 -0
- package/src/api/project-open.ts +355 -0
- package/src/api/project-source.ts +162 -0
- package/src/api/project-state.ts +107 -0
- package/src/api/relay.ts +268 -0
- package/src/api/settings.ts +36 -0
- package/src/api/themes.ts +45 -0
- package/src/api/worktrees.ts +257 -0
- package/src/asset-compare-core.ts +171 -0
- package/src/asset-compare.ts +294 -0
- package/src/asset-editor-context.tsx +100 -0
- package/src/asset-events.ts +97 -0
- package/src/asset-inspector-actions.ts +87 -0
- package/src/asset-preview-framing.ts +357 -0
- package/src/asset-preview.ts +2802 -0
- package/src/asset-selection-viewer-registry.ts +113 -0
- package/src/asset-selection.ts +133 -0
- package/src/asset-workflow/asset-capabilities.ts +344 -0
- package/src/asset-workflow/asset-import-jobs.ts +106 -0
- package/src/asset-workflow/asset-ledger-backend.ts +126 -0
- package/src/asset-workflow/asset-ledger.ts +156 -0
- package/src/asset-workflow/asset-materialization-report.ts +140 -0
- package/src/asset-workflow/asset-pack-manifest.ts +320 -0
- package/src/asset-workflow/asset-types.ts +142 -0
- package/src/asset-workflow/asset-workflow-quality.ts +61 -0
- package/src/asset-workflow/audio-preview-player.ts +193 -0
- package/src/asset-workflow/audio-waveform.ts +22 -0
- package/src/asset-workflow/cloud-asset-client.ts +263 -0
- package/src/asset-workflow/folder-preview.ts +345 -0
- package/src/asset-workflow/hosted-asset-materialization.ts +236 -0
- package/src/asset-workflow/image-view-scale.ts +32 -0
- package/src/asset-workflow/import-contract.ts +124 -0
- package/src/asset-workflow/ledger-write-lock.ts +244 -0
- package/src/asset-workflow/model-inspection.ts +818 -0
- package/src/asset-workflow/pixi-spritesheet.ts +197 -0
- package/src/asset-workflow/preview-resource-lifetime.ts +44 -0
- package/src/asset-workflow/project-asset-commands.ts +23 -0
- package/src/asset-workflow/project-asset-health.ts +139 -0
- package/src/asset-workflow/project-asset-operations.ts +154 -0
- package/src/asset-workflow/project-asset-roots.ts +68 -0
- package/src/asset-workflow/project-content.ts +288 -0
- package/src/asset-workflow/project-source-index.ts +550 -0
- package/src/asset-workflow/thumbnail-system.ts +256 -0
- package/src/authoring/active-adapter.ts +199 -0
- package/src/authoring/active-systems.ts +422 -0
- package/src/authoring/adapter-key.ts +18 -0
- package/src/authoring/authoring-asset-url.ts +27 -0
- package/src/authoring/bootstrap-state.ts +49 -0
- package/src/authoring/boundary-authoring-adapter.ts +184 -0
- package/src/authoring/breakpoint-state.ts +43 -0
- package/src/authoring/canvas-scene-guides.ts +76 -0
- package/src/authoring/component-instance-root.ts +171 -0
- package/src/authoring/composite-authoring-adapter.ts +2110 -0
- package/src/authoring/consumer-actions.ts +520 -0
- package/src/authoring/css-numeric-style.ts +97 -0
- package/src/authoring/design-time-layers.ts +849 -0
- package/src/authoring/design-time-mount-registry.ts +235 -0
- package/src/authoring/design-time-settle.ts +343 -0
- package/src/authoring/edit-mode-authoring.ts +619 -0
- package/src/authoring/eyedropper-session.ts +60 -0
- package/src/authoring/instance-source-menu-register.ts +13 -0
- package/src/authoring/instance-source-menu.ts +135 -0
- package/src/authoring/layered-pick.ts +183 -0
- package/src/authoring/live-gesture-lock.ts +50 -0
- package/src/authoring/live-object-transform.ts +62 -0
- package/src/authoring/mount-failure-report.ts +154 -0
- package/src/authoring/mounted-root-subjects.ts +144 -0
- package/src/authoring/no-authoring-adapter.ts +55 -0
- package/src/authoring/null-inspection-subjects.tsx +75 -0
- package/src/authoring/object3d-document-persistence.ts +106 -0
- package/src/authoring/object3d-document-session-registry.ts +120 -0
- package/src/authoring/object3d-document-session.ts +1326 -0
- package/src/authoring/object3d-gesture-controller.ts +113 -0
- package/src/authoring/panel-authoring.ts +121 -0
- package/src/authoring/prefab-instance-inspector-section.tsx +237 -0
- package/src/authoring/prefab-instance-section-model.ts +55 -0
- package/src/authoring/project-authoring-session.ts +105 -0
- package/src/authoring/provenance.ts +99 -0
- package/src/authoring/quarks-particle-systems.ts +19 -0
- package/src/authoring/react-canvas-navigation.ts +255 -0
- package/src/authoring/react-design-canvas-style.ts +20 -0
- package/src/authoring/react-story-board.ts +937 -0
- package/src/authoring/selection-scope.ts +195 -0
- package/src/authoring/shell-document-ops.ts +169 -0
- package/src/authoring/shell-object3d-document-write-policy.ts +108 -0
- package/src/authoring/shell-viewport-policy.ts +48 -0
- package/src/authoring/source-object3d-authoring-adapter.ts +526 -0
- package/src/authoring/stories-scope.ts +35 -0
- package/src/authoring/story-board-chrome-fit.ts +107 -0
- package/src/authoring/story-board-presentation.ts +111 -0
- package/src/authoring/three-projection-core.ts +226 -0
- package/src/authoring/viewport-pick-context.ts +40 -0
- package/src/authoring/viewport-raycast.ts +240 -0
- package/src/authoring/viewport-tool-context.ts +73 -0
- package/src/authoring/world-canvas-viewport-state.ts +35 -0
- package/src/authoring/world-hidden-viewport.ts +152 -0
- package/src/authoring/world-pan-state.ts +198 -0
- package/src/authoring/world-session-state.ts +101 -0
- package/src/authoring/write-pipe.ts +173 -0
- package/src/availability-tick.ts +66 -0
- package/src/bitmap-label.ts +120 -0
- package/src/blender-tab-metrics.ts +161 -0
- package/src/board-open-actions.ts +20 -0
- package/src/boot-routing.ts +391 -0
- package/src/build-identity.ts +15 -0
- package/src/bytes-codec.ts +62 -0
- package/src/camera-authoring.ts +175 -0
- package/src/cancellation-reason.ts +58 -0
- package/src/canvas-preview-frames.ts +482 -0
- package/src/capture-camera-pose.ts +77 -0
- package/src/chrome-registry.ts +159 -0
- package/src/chrome-slot-registry.ts +91 -0
- package/src/collaboration-client.ts +264 -0
- package/src/collaboration-presence.ts +41 -0
- package/src/command-dispatch.ts +19 -0
- package/src/command-listener.ts +2743 -0
- package/src/command-registry.ts +70 -0
- package/src/component-board-registry.ts +205 -0
- package/src/component-states-registry.ts +200 -0
- package/src/components/AgentPresentationNotice.tsx +75 -0
- package/src/components/AlignToolbar.tsx +204 -0
- package/src/components/AppRoot.tsx +394 -0
- package/src/components/ApplicationMenus.tsx +415 -0
- package/src/components/AssetBrowser.tsx +2843 -0
- package/src/components/AssetEditorShell.tsx +216 -0
- package/src/components/AssetInspectorToolSection.tsx +124 -0
- package/src/components/BoardRulers.tsx +354 -0
- package/src/components/CameraInfo.tsx +81 -0
- package/src/components/CanvasSceneViewport.tsx +556 -0
- package/src/components/CapabilityCoverageSection.tsx +185 -0
- package/src/components/CenterDocuments.tsx +22 -0
- package/src/components/ChromeSlot.tsx +20 -0
- package/src/components/CodeView.tsx +470 -0
- package/src/components/CollaborationHeaderControl.css +74 -0
- package/src/components/CompactInspectorCard.tsx +190 -0
- package/src/components/CompactInspectorShell.tsx +39 -0
- package/src/components/ConsolePanel.css +55 -0
- package/src/components/ConsolePanel.tsx +270 -0
- package/src/components/DefaultEditorLayout.tsx +106 -0
- package/src/components/DocumentHeaderStrip.tsx +96 -0
- package/src/components/DocumentShelfRail.tsx +45 -0
- package/src/components/DocumentThumbnail.tsx +345 -0
- package/src/components/EditorLeaseGuard.tsx +372 -0
- package/src/components/ErrorBanner.tsx +123 -0
- package/src/components/FolderPreviewTile.tsx +159 -0
- package/src/components/GameHierarchy.tsx +3842 -0
- package/src/components/GameplaySessionTimeline.tsx +295 -0
- package/src/components/InspectionProjection.tsx +968 -0
- package/src/components/Inspector.tsx +233 -0
- package/src/components/InspectorCanvasPreview.tsx +35 -0
- package/src/components/InspectorFieldsSection.tsx +290 -0
- package/src/components/InspectorObjectPreview.tsx +57 -0
- package/src/components/InspectorStoriesSection.tsx +92 -0
- package/src/components/InspectorToolSection.tsx +96 -0
- package/src/components/InspectorTransformSection.tsx +245 -0
- package/src/components/LightExplorerPanel.tsx +433 -0
- package/src/components/MediaProperties.tsx +145 -0
- package/src/components/NonThreeAuthoringBootstrap.tsx +19 -0
- package/src/components/Object3DDocumentToolbar.css +100 -0
- package/src/components/Object3DDocumentToolbar.tsx +454 -0
- package/src/components/Object3DDocumentViewport.tsx +58 -0
- package/src/components/ProjectHeader.css +617 -0
- package/src/components/ProjectHeader.tsx +328 -0
- package/src/components/ProjectLayout.tsx +249 -0
- package/src/components/ReactCanvasControls.tsx +284 -0
- package/src/components/RootSelectionOverlay.tsx +3162 -0
- package/src/components/RootTextEditor.tsx +79 -0
- package/src/components/SaveStatus.tsx +70 -0
- package/src/components/StageHost.tsx +1954 -0
- package/src/components/StageOverlays.tsx +21 -0
- package/src/components/StartupErrorScreen.tsx +76 -0
- package/src/components/StartupLoadingScreen.tsx +65 -0
- package/src/components/StatsOverlay.tsx +78 -0
- package/src/components/SurfaceStateOverlay.tsx +24 -0
- package/src/components/ToolContributionSurfaces.tsx +65 -0
- package/src/components/ToolHost.tsx +370 -0
- package/src/components/ToolObject3DPreview.tsx +39 -0
- package/src/components/Toolbar.tsx +595 -0
- package/src/components/TransientHint.tsx +44 -0
- package/src/components/TransportStrip.tsx +174 -0
- package/src/components/VersionControlSection.tsx +470 -0
- package/src/components/VgaiLogo.css +83 -0
- package/src/components/VgaiLogo.tsx +35 -0
- package/src/components/ViewportControlsHint.tsx +60 -0
- package/src/components/ViewportFurniture.tsx +401 -0
- package/src/components/ViewportOverlay.tsx +145 -0
- package/src/components/ViewportOverlaysMenu.tsx +153 -0
- package/src/components/ViewportShadingMenu.tsx +300 -0
- package/src/components/ViewportViewMenu.tsx +101 -0
- package/src/components/WorkspaceDocumentSurface.tsx +204 -0
- package/src/components/WorkspaceUtilitySurface.tsx +22 -0
- package/src/components/WorktreeSwitcher.css +239 -0
- package/src/components/WorktreeSwitcher.tsx +890 -0
- package/src/components/account-documents.tsx +1162 -0
- package/src/components/asset-documents.tsx +812 -0
- package/src/components/asset-editor-persistence.ts +216 -0
- package/src/components/asset-selection-section.tsx +535 -0
- package/src/components/asset-thumbnails.tsx +312 -0
- package/src/components/asset-viewers/AudioViewer.tsx +201 -0
- package/src/components/asset-viewers/EntityModelDocument.tsx +122 -0
- package/src/components/asset-viewers/EnvironmentAssetDocument.tsx +440 -0
- package/src/components/asset-viewers/GenericJsonViewer.tsx +102 -0
- package/src/components/asset-viewers/ImageViewer.tsx +300 -0
- package/src/components/asset-viewers/JsonAssetDocument.tsx +93 -0
- package/src/components/asset-viewers/LiveModuleDocument.tsx +417 -0
- package/src/components/asset-viewers/LutAssetDocument.tsx +444 -0
- package/src/components/asset-viewers/ModelAssetDocument.tsx +105 -0
- package/src/components/asset-viewers/Object3DPreview.tsx +359 -0
- package/src/components/asset-viewers/OnlineAssetDetail.tsx +421 -0
- package/src/components/asset-viewers/PasteboardModuleDocument.tsx +67 -0
- package/src/components/asset-viewers/QuarksAssetDocument.tsx +526 -0
- package/src/components/asset-viewers/ShaderAssetDocument.tsx +743 -0
- package/src/components/asset-viewers/SourceAssetViewer.tsx +281 -0
- package/src/components/asset-viewers/SpritesheetSpriteView.tsx +102 -0
- package/src/components/asset-viewers/VideoViewer.tsx +101 -0
- package/src/components/asset-viewers/shader-source.ts +144 -0
- package/src/components/asset-workflow.css +731 -0
- package/src/components/board-guides.ts +150 -0
- package/src/components/compact-inspector.css +539 -0
- package/src/components/core-utilities.tsx +90 -0
- package/src/components/editor-notifications.css +40 -0
- package/src/components/engine-workspace.css +274 -0
- package/src/components/inspector-preview-section.tsx +238 -0
- package/src/components/inspector-property-grouping.ts +64 -0
- package/src/components/inspector-revert-label.ts +20 -0
- package/src/components/inspector-selection.ts +42 -0
- package/src/components/inspector-stories-gating.ts +171 -0
- package/src/components/inspector-transform-subject.ts +11 -0
- package/src/components/inspector-transform.ts +75 -0
- package/src/components/kind-documents.tsx +500 -0
- package/src/components/palette-action-publisher.tsx +140 -0
- package/src/components/primitives/DraftColorInput.tsx +74 -0
- package/src/components/product-shell.css +1829 -0
- package/src/components/project-tool-documents.tsx +410 -0
- package/src/components/scene-documents.tsx +223 -0
- package/src/components/stage-keyboard.tsx +37 -0
- package/src/components/stage-overlay-set.tsx +107 -0
- package/src/components/stage-presence-markers.ts +478 -0
- package/src/components/standard-viewport-dressing.ts +417 -0
- package/src/components/status-contributions.tsx +403 -0
- package/src/components/tool-documents.tsx +309 -0
- package/src/components/tool-schema-form.tsx +262 -0
- package/src/components/use-after-paint.ts +41 -0
- package/src/components/use-project-image-assets.ts +86 -0
- package/src/components/viewport-header-controls.css +90 -0
- package/src/components/viewport-surface-status.tsx +55 -0
- package/src/components/workspace-history.ts +32 -0
- package/src/components/workspace-static-panel-registry.tsx +152 -0
- package/src/components/workspace-surfaces.css +621 -0
- package/src/components/world-documents.tsx +586 -0
- package/src/components/world-overlay-gestures.ts +1694 -0
- package/src/composite-screenshot.ts +1641 -0
- package/src/console-sync.ts +131 -0
- package/src/constraint-helper.ts +338 -0
- package/src/content-entry-source-registry.ts +184 -0
- package/src/coverage/authoring-seam-evidence.ts +300 -0
- package/src/coverage/canvas-reveal.ts +192 -0
- package/src/coverage/design-time-surfaces.ts +101 -0
- package/src/coverage/live-seam-evidence.ts +11 -0
- package/src/coverage/ontology-invariants.ts +466 -0
- package/src/coverage/session-vitals.ts +501 -0
- package/src/coverage/system-seam-evidence.ts +72 -0
- package/src/crash-null-boundary.ts +36 -0
- package/src/creation-site-edit.ts +1479 -0
- package/src/creation-site-registry.ts +160 -0
- package/src/delegate-harness-registry.ts +143 -0
- package/src/document-context-registry.ts +177 -0
- package/src/document-open-registry.ts +200 -0
- package/src/document-preview-source.ts +20 -0
- package/src/document-renderer-session.ts +138 -0
- package/src/editor-api.ts +46 -0
- package/src/editor-chrome-capture.ts +139 -0
- package/src/editor-commands.ts +164 -0
- package/src/editor-console.ts +563 -0
- package/src/editor-current-view.ts +82 -0
- package/src/editor-document-probe.ts +881 -0
- package/src/editor-git-client.ts +115 -0
- package/src/editor-host-door.ts +462 -0
- package/src/editor-hotkeys.ts +842 -0
- package/src/editor-lease-view.ts +39 -0
- package/src/editor-lease.ts +415 -0
- package/src/editor-mode.ts +19 -0
- package/src/editor-notifications.ts +140 -0
- package/src/editor-presence.ts +563 -0
- package/src/editor-presentation-activity.ts +50 -0
- package/src/editor-presentation-notice.ts +42 -0
- package/src/editor-runtime.tsx +145 -0
- package/src/editor-server-response.ts +86 -0
- package/src/editor-session-attribution.ts +75 -0
- package/src/editor-session-mode.ts +54 -0
- package/src/editor-shell-store.ts +1304 -0
- package/src/editor-state-facets.ts +74 -0
- package/src/editor-styles.css +33 -0
- package/src/editor-view-presentation.ts +810 -0
- package/src/editor-viewport.ts +5302 -0
- package/src/entity-lod.ts +31 -0
- package/src/entity-object.ts +91 -0
- package/src/files/file-provider.ts +62 -0
- package/src/files/project-files.ts +264 -0
- package/src/finders/index.ts +136 -0
- package/src/finders/scenes-from-entrypoint-selection.ts +387 -0
- package/src/frame/bridge.tsx +1408 -0
- package/src/frame/product.ts +77 -0
- package/src/gameplay-dom-recording.ts +319 -0
- package/src/gameplay-export-state.ts +14 -0
- package/src/gameplay-replay.ts +417 -0
- package/src/gameplay-session-time.ts +9 -0
- package/src/gameplay-sessions.ts +204 -0
- package/src/harness-chat-types.ts +253 -0
- package/src/hierarchy-component-marks.ts +298 -0
- package/src/hierarchy-drop.ts +91 -0
- package/src/hierarchy-expansion-state.ts +80 -0
- package/src/hierarchy-header-slot.ts +52 -0
- package/src/hierarchy-internals.ts +197 -0
- package/src/hierarchy-kind-icon.ts +217 -0
- package/src/hierarchy-mark-reader.ts +73 -0
- package/src/hierarchy-menu-registry.ts +67 -0
- package/src/hierarchy-node-rows.ts +307 -0
- package/src/hierarchy-panel-view.ts +280 -0
- package/src/hierarchy-projection.ts +76 -0
- package/src/hierarchy-row-cache.ts +243 -0
- package/src/hierarchy-row-model.ts +308 -0
- package/src/hierarchy-rows.ts +11 -0
- package/src/hierarchy-walk.ts +86 -0
- package/src/history/editor-session.ts +25 -0
- package/src/history/history-commands.ts +147 -0
- package/src/history/history-delegate.ts +163 -0
- package/src/history/history-limit-notices.ts +43 -0
- package/src/history/history-service.ts +1173 -0
- package/src/history/persistence-coordinator.ts +35 -0
- package/src/history/project-file-history.ts +386 -0
- package/src/history/project-root-history-backends.ts +139 -0
- package/src/history/resource-registry.ts +209 -0
- package/src/history/snapshot-store.ts +103 -0
- package/src/history/source-history-backend.ts +546 -0
- package/src/history/types.ts +124 -0
- package/src/hmr-registration-group.ts +67 -0
- package/src/hmr-stable-react-context.ts +23 -0
- package/src/hotkeys.ts +188 -0
- package/src/inference-diagnostics.ts +69 -0
- package/src/initial-project.ts +80 -0
- package/src/inspection/active-subject.ts +578 -0
- package/src/inspection/active-surface.ts +142 -0
- package/src/inspection/compose.ts +1064 -0
- package/src/inspection/display.ts +170 -0
- package/src/inspection/document-subject.ts +109 -0
- package/src/inspection/game-subject.ts +85 -0
- package/src/inspection/model.ts +542 -0
- package/src/inspection/null-subject.ts +115 -0
- package/src/inspection/serialize.ts +357 -0
- package/src/inspection/use-active-inspection.ts +180 -0
- package/src/inspector-presentation.ts +201 -0
- package/src/inspector-section-registry.ts +221 -0
- package/src/instance-source-actions.ts +163 -0
- package/src/instanced-presentation.ts +164 -0
- package/src/js-heap.ts +71 -0
- package/src/key-actions.ts +91 -0
- package/src/keymap-presets.ts +401 -0
- package/src/layout-policy.ts +31 -0
- package/src/learn-links.ts +73 -0
- package/src/light-explorer-model.ts +134 -0
- package/src/live-canvas-frame.ts +55 -0
- package/src/live-document.ts +279 -0
- package/src/live-module-source.ts +230 -0
- package/src/live-session-registry.ts +220 -0
- package/src/live-transition.ts +633 -0
- package/src/manifest-project.ts +107 -0
- package/src/model-thumbnail.ts +523 -0
- package/src/native-selection-style.ts +202 -0
- package/src/object3d-document-write-policy.ts +137 -0
- package/src/packaged-runtime.ts +108 -0
- package/src/palettes/maya.palette.json +57 -0
- package/src/palettes/substance.palette.json +57 -0
- package/src/pasteboard-module.ts +81 -0
- package/src/performance-profiler.ts +367 -0
- package/src/performance-sources.ts +69 -0
- package/src/play-boot-phase.ts +145 -0
- package/src/presentation-surface.ts +248 -0
- package/src/project-adapter.ts +1143 -0
- package/src/project-asset-refresh.ts +26 -0
- package/src/project-declaration-refresh.ts +31 -0
- package/src/project-local-state.ts +118 -0
- package/src/project-manager.ts +243 -0
- package/src/project-module-changes.ts +163 -0
- package/src/project-module-split.ts +266 -0
- package/src/project-provenance.ts +115 -0
- package/src/project-ready.ts +42 -0
- package/src/project-session-reset.ts +67 -0
- package/src/project-shape.ts +68 -0
- package/src/project-tool-discovery.ts +66 -0
- package/src/project-tools.ts +107 -0
- package/src/project-work-types.ts +149 -0
- package/src/projection/three.ts +898 -0
- package/src/projection/types.ts +44 -0
- package/src/readiness.ts +113 -0
- package/src/reflection-probe-helper.ts +142 -0
- package/src/reported-play-state.ts +90 -0
- package/src/resolve-relative-specifier.ts +33 -0
- package/src/scene-document-plan.ts +315 -0
- package/src/scene-framing.ts +315 -0
- package/src/scene-live-open.ts +210 -0
- package/src/scene-view-fog.ts +89 -0
- package/src/scoped-game-css.ts +152 -0
- package/src/session-orphan-record.ts +193 -0
- package/src/session-tombstone.ts +126 -0
- package/src/settings/settings-provider.ts +82 -0
- package/src/settings-store.ts +345 -0
- package/src/shared-view-restore.ts +42 -0
- package/src/shell-store-door.ts +45 -0
- package/src/source-conflict.ts +122 -0
- package/src/spatial-handle-visuals.ts +332 -0
- package/src/stage-context.ts +341 -0
- package/src/stage-store-registry.ts +63 -0
- package/src/stale-chunk-recovery.ts +34 -0
- package/src/state-report-deferral.ts +73 -0
- package/src/storage/host-files-storage.ts +97 -0
- package/src/storage/http-storage.ts +174 -0
- package/src/storage/index.ts +75 -0
- package/src/storage/mem-storage.ts +149 -0
- package/src/storage/path-lock.ts +44 -0
- package/src/storage/paths.ts +26 -0
- package/src/storage/types.ts +116 -0
- package/src/stories/StoryComponentThumbnail.tsx +184 -0
- package/src/stories/StoryPreviewMount.tsx +306 -0
- package/src/stories/component-content-source.tsx +106 -0
- package/src/stories/component-states-source.ts +78 -0
- package/src/stories/compose-project-stories.ts +255 -0
- package/src/stories/pixi-story-model.ts +30 -0
- package/src/stories/prefabs-finder.ts +54 -0
- package/src/stories/prefabs-from-stories.ts +182 -0
- package/src/stories/project-story-discovery.ts +78 -0
- package/src/stories/project-story-regions.ts +24 -0
- package/src/stories/story-actions.ts +54 -0
- package/src/stories/story-capture-command.ts +124 -0
- package/src/stories/story-capture.ts +584 -0
- package/src/stories/story-declared-medium.ts +126 -0
- package/src/stories/story-discovery.ts +176 -0
- package/src/stories/story-document-openers.ts +36 -0
- package/src/stories/story-dom-runtime.ts +78 -0
- package/src/stories/story-grouping.ts +111 -0
- package/src/stories/story-lane.ts +88 -0
- package/src/stories/story-mount-turn.ts +27 -0
- package/src/stories/story-opener.ts +260 -0
- package/src/stories/story-pixi-preview.ts +408 -0
- package/src/stories/story-presentation.ts +215 -0
- package/src/stories/story-registry.ts +517 -0
- package/src/stories/story-three-preview.ts +807 -0
- package/src/stories/three-story-model.ts +96 -0
- package/src/story-three-preview-runtime.ts +56 -0
- package/src/surface-keyboard.ts +112 -0
- package/src/surface-state.ts +124 -0
- package/src/tab-bootstrap.js +628 -0
- package/src/tab-census.ts +198 -0
- package/src/tab-lifecycle-client.ts +196 -0
- package/src/theme-library.ts +844 -0
- package/src/theme-preference.ts +378 -0
- package/src/theme.css +3437 -0
- package/src/three-viewport/bone-selection-highlight.ts +119 -0
- package/src/three-viewport/camera-fit.ts +41 -0
- package/src/three-viewport/interactive-renderer.ts +132 -0
- package/src/three-viewport/selection-brackets.ts +310 -0
- package/src/three-viewport/selection-outline.ts +183 -0
- package/src/three-viewport/skeleton-helper.ts +61 -0
- package/src/three-viewport/source-color.ts +167 -0
- package/src/three-viewport/studio-environment.ts +96 -0
- package/src/three-viewport-presentation.ts +23 -0
- package/src/tool-contribution-play.ts +74 -0
- package/src/tool-loader.ts +1798 -0
- package/src/transform-mode-request.ts +57 -0
- package/src/transient-hint.ts +78 -0
- package/src/trigger-volume-helper.ts +116 -0
- package/src/ui-source/adapter-region-includes.ts +241 -0
- package/src/ui-source/ensure-import.ts +132 -0
- package/src/ui-source/file-region-resolver.ts +302 -0
- package/src/ui-source/inspect.ts +775 -0
- package/src/ui-source/oid-transform.ts +1845 -0
- package/src/ui-source/plan-csf-story.ts +196 -0
- package/src/ui-source/plan-extract-component.ts +421 -0
- package/src/ui-source/plan-fork-component.ts +621 -0
- package/src/ui-source/plan-named-style.ts +95 -0
- package/src/ui-source/plan-source-edit.ts +353 -0
- package/src/ui-source/r3f-contract-resolver.ts +327 -0
- package/src/ui-source/r3f-diagnostic-index.ts +165 -0
- package/src/ui-source/r3f-environment-binding.ts +133 -0
- package/src/ui-source/r3f-joint-binding.ts +251 -0
- package/src/ui-source/r3f-lod-binding.ts +91 -0
- package/src/ui-source/r3f-particle-binding.ts +235 -0
- package/src/ui-source/r3f-physics-binding.ts +483 -0
- package/src/ui-source/r3f-project-contracts.ts +444 -0
- package/src/ui-source/relative-import-specifier.ts +34 -0
- package/src/ui-source/reparent-guard.ts +544 -0
- package/src/ui-source/source-edit-request.ts +97 -0
- package/src/ui-source/source-write-backend.ts +618 -0
- package/src/ui-source/syntactic-prop-specs.ts +296 -0
- package/src/ui-source/tier-source-write-backend.ts +279 -0
- package/src/ui-source/ts-ast.ts +171 -0
- package/src/ui-source/utility-class-support.ts +169 -0
- package/src/ui-source/write-component-default.ts +133 -0
- package/src/ui-source/writer.ts +1989 -0
- package/src/viewport-activation-timings.ts +840 -0
- package/src/viewport-authoring-policy.ts +186 -0
- package/src/viewport-controls-hint.ts +57 -0
- package/src/viewport-door.ts +237 -0
- package/src/viewport-shading-boundary.ts +12 -0
- package/src/vite-error-surface.ts +141 -0
- package/src/wait-until.ts +37 -0
- package/src/workspace-areas.ts +156 -0
- package/src/workspace-aux-commands.ts +11 -0
- package/src/workspace-available-documents.ts +127 -0
- package/src/workspace-core-utilities.ts +31 -0
- package/src/workspace-document-ids.ts +59 -0
- package/src/workspace-document-registry.ts +612 -0
- package/src/workspace-document-restore.ts +145 -0
- package/src/workspace-host-commands.ts +141 -0
- package/src/workspace-persistence-gate.ts +40 -0
- package/src/workspace-play-utilities.ts +44 -0
- package/src/workspace-presets.ts +425 -0
- package/src/workspace-regions.ts +294 -0
- package/src/workspace-state-persistence.ts +536 -0
- package/src/workspace-static-panels.ts +73 -0
- package/src/workspace-status-registry.ts +121 -0
- package/src/workspace-style.ts +239 -0
- package/src/workspace-utility-commands.ts +74 -0
- package/src/workspace-utility-registry.ts +263 -0
- package/src/workspace-viewport-rect.ts +97 -0
- package/src/world-adoption.ts +115 -0
- package/src/world-document-routing.ts +104 -0
- package/vite-plugin-creation-site-write.ts +184 -0
- package/vite-plugin-creation-site.ts +80 -0
- package/vite-plugin-game-static.ts +303 -0
- package/vite-plugin-module-doorways.ts +336 -0
- package/vite-plugin-product-contributions.ts +197 -0
- package/vite-plugin-project-game-static.ts +125 -0
- package/vite-plugin-project-jsx-js.ts +65 -0
- package/vite-plugin-project-root-absolute-assets.ts +359 -0
- package/vite-plugin-shared-react.ts +420 -0
- package/vite-plugin-shared-three.ts +203 -0
- package/vite-plugin-ui-oid.ts +2106 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The editor's OWN connection state, as the lease guard measures it
|
|
3
|
+
* (`components/EditorLeaseGuard.tsx`), published for the status bar. VS
|
|
4
|
+
* Code shows its remote connection at the far left of the status bar and
|
|
5
|
+
* turns it red when the host is gone; this is that item's source. State
|
|
6
|
+
* lives here, events (the moment it changed) go to the notification stack.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type EditorLeaseView =
|
|
10
|
+
| { readonly kind: 'quiet' }
|
|
11
|
+
| { readonly kind: 'degraded'; readonly subject: 'busy' | 'stale' }
|
|
12
|
+
| {
|
|
13
|
+
readonly kind: 'void';
|
|
14
|
+
readonly reason: 'taken-over' | 'server-gone';
|
|
15
|
+
readonly reloadOffered: boolean;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
let _view: EditorLeaseView = { kind: 'quiet' };
|
|
19
|
+
let _version = 0;
|
|
20
|
+
const _listeners = new Set<() => void>();
|
|
21
|
+
|
|
22
|
+
export function editorLeaseView(): EditorLeaseView {
|
|
23
|
+
return _view;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function publishEditorLeaseView(view: EditorLeaseView): void {
|
|
27
|
+
_view = view;
|
|
28
|
+
_version += 1;
|
|
29
|
+
for (const fn of _listeners) fn();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function subscribeEditorLeaseView(listener: () => void): () => void {
|
|
33
|
+
_listeners.add(listener);
|
|
34
|
+
return () => _listeners.delete(listener);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function editorLeaseViewVersion(): number {
|
|
38
|
+
return _version;
|
|
39
|
+
}
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The editor tab is a SUPERVISED LEASE on its editor server.
|
|
3
|
+
*
|
|
4
|
+
* An editor tab is a client-side SPA: when its server dies UNGRACEFULLY (a
|
|
5
|
+
* killed process, a crash), the tab keeps rendering pixel-for-pixel as if
|
|
6
|
+
* nothing happened — and if a game is playing, it stays connected to the
|
|
7
|
+
* Colyseus room as an unowned "ghost" player that nothing ever removes. So the
|
|
8
|
+
* tab has to supervise its server.
|
|
9
|
+
*
|
|
10
|
+
* The graceful case (a clean `vgai close` sends a `tab-close` push) is handled
|
|
11
|
+
* elsewhere (tab-lifecycle-client.ts). This module is the UNGRACEFUL case that
|
|
12
|
+
* push never reaches.
|
|
13
|
+
*
|
|
14
|
+
* TWO independent readings, because ONE of them lies. The lease poll is a
|
|
15
|
+
* `fetch` on the browser's per-origin HTTP connection pool, so it reports
|
|
16
|
+
* "failed" for reasons that have nothing to do with the server being gone: a
|
|
17
|
+
* cold Vite dep-optimize floods that pool and the poll never leaves the queue
|
|
18
|
+
* before its own timeout; a server whose event loop is blocked answers nothing
|
|
19
|
+
* while its process is perfectly alive. The tab's duplex control channel
|
|
20
|
+
* (editor-presence.ts — a WebSocket, outside the HTTP pool) is the second
|
|
21
|
+
* reading, and it is the one that distinguishes them: a killed process drops
|
|
22
|
+
* that socket immediately, a merely BUSY one holds it open.
|
|
23
|
+
*
|
|
24
|
+
* Measured on 2026-08-09 against a live dev session: `kill -STOP` on the editor
|
|
25
|
+
* server produced the "Editor disconnected" scrim ~19s later while the control
|
|
26
|
+
* socket was still `readyState === OPEN` and duplex-granted. The tab had the
|
|
27
|
+
* evidence that its server was alive and never looked at it. That is the whole
|
|
28
|
+
* defect: missed polls alone are NOT evidence a session is gone.
|
|
29
|
+
*
|
|
30
|
+
* So a run of failed polls only VOIDS the lease when the control channel is
|
|
31
|
+
* ALSO down for that same run. Failed polls on a page whose channel is open
|
|
32
|
+
* degrade to a passive warning — never a blocking, input-eating overlay on a
|
|
33
|
+
* page whose server is about to answer again.
|
|
34
|
+
*
|
|
35
|
+
* ## The third reading: TIME, because a tab does not run on demand
|
|
36
|
+
*
|
|
37
|
+
* MEASURED twice on live sessions, 2026-08-19: after its server was SIGKILLed,
|
|
38
|
+
* the editor tab made ZERO network attempts of any kind — no lease poll, no
|
|
39
|
+
* control-socket reconnect, no heartbeat beat, though the last two are written
|
|
40
|
+
* to retry forever — and never came back, including after a replacement server
|
|
41
|
+
* appeared on the same port. Once from a hidden tab over four and a half
|
|
42
|
+
* minutes; once from a tab that was `visible` at the moment of the kill, over
|
|
43
|
+
* forty-five seconds.
|
|
44
|
+
*
|
|
45
|
+
* WHY the page stops is NOT established. Suspension, discard, and a lost
|
|
46
|
+
* renderer all fit, and the second run makes "the browser froze a background
|
|
47
|
+
* tab" an insufficient story on its own. What IS established is the part the
|
|
48
|
+
* design has to answer for: a page's own timers are not a thing this watchdog
|
|
49
|
+
* may assume will run, and the page carrying an overlay cannot be relied on to
|
|
50
|
+
* survive long enough to be read — which is why the durable note
|
|
51
|
+
* (`session-orphan-record.ts`) is a channel and not a nicety.
|
|
52
|
+
*
|
|
53
|
+
* So a poll COUNT never meant what it was read as. Three polls are ~12s of a
|
|
54
|
+
* foregrounded tab, minutes of a throttled one, and nothing at all of a stopped
|
|
55
|
+
* one — and now that the watchdog also polls on events (the socket dropping, the
|
|
56
|
+
* tab being shown again), three polls can land inside one second, which is a
|
|
57
|
+
* server between processes rather than a dead one. The bar that was intended
|
|
58
|
+
* all along is DARKNESS SUSTAINED FOR A WHILE, so the state carries the
|
|
59
|
+
* timestamp that says it ({@link LeaseWatchState.darkSince}).
|
|
60
|
+
*
|
|
61
|
+
* And a page may only testify about time it EXPERIENCED. A tab returning from
|
|
62
|
+
* a freeze knows nothing about the minutes it slept, so the shell forgets the
|
|
63
|
+
* run on wake ({@link forgetLeaseFailures}) and earns a fresh window rather
|
|
64
|
+
* than voiding on the strength of a gap it never observed.
|
|
65
|
+
*
|
|
66
|
+
* This file is the PURE half — the decision, the poll-folding reducer, and the
|
|
67
|
+
* recovery policy, all data-in/data-out so the whole thing is unit-testable
|
|
68
|
+
* with no browser, no timers, and no fetch. The watchdog
|
|
69
|
+
* (`EditorLeaseGuard.tsx`) is a thin shell that supplies the timers/fetch and
|
|
70
|
+
* renders the notice.
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
/** How a server on a port identifies itself to a polling tab. `project` is the
|
|
74
|
+
* canonical root it reports serving (or `null` for an idle server); `pid` is
|
|
75
|
+
* the server process id — the strongest signal that a port was taken over by
|
|
76
|
+
* a DIFFERENT session, since a replaced server on the same port has a new pid.
|
|
77
|
+
* `pid` is `null` for a server too old to report one (the `/__editor/project`
|
|
78
|
+
* `session` block is optional). */
|
|
79
|
+
export interface LeaseIdentity {
|
|
80
|
+
project: string | null;
|
|
81
|
+
pid: number | null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** The outcome of one poll: either the server answered with its identity, or
|
|
85
|
+
* the request failed (connection refused / non-OK / timeout). */
|
|
86
|
+
export type LeasePollResult = { ok: true; identity: LeaseIdentity } | { ok: false };
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* What the tab's own control channel said at the moment of a poll — the
|
|
90
|
+
* SECOND, independent liveness reading (see the module header).
|
|
91
|
+
*
|
|
92
|
+
* - `open` — the duplex socket to this server is connected. A dead process
|
|
93
|
+
* cannot hold a socket open, so this is POSITIVE evidence the session lives.
|
|
94
|
+
* - `closed` — the socket is down (or reconnecting). Consistent with a dead
|
|
95
|
+
* server; on its own it is not proof, which is why it only ever combines
|
|
96
|
+
* with failed polls.
|
|
97
|
+
* - `unknown` — no channel reading is available (a share-tunnelled tab whose
|
|
98
|
+
* downstream bridge exposes none). Treated exactly like `closed`: absent
|
|
99
|
+
* evidence must not make the watchdog MORE willing to keep a dead tab alive.
|
|
100
|
+
*/
|
|
101
|
+
export type LeaseChannelState = 'open' | 'closed' | 'unknown';
|
|
102
|
+
|
|
103
|
+
/** The rolling state the watchdog folds each poll into. Pure data. */
|
|
104
|
+
export interface LeaseWatchState {
|
|
105
|
+
/** Identity this tab attached to, captured from the first successful poll.
|
|
106
|
+
* `null` until then. Once set it never changes — it is what a later poll is
|
|
107
|
+
* compared against to detect a takeover. */
|
|
108
|
+
bootIdentity: LeaseIdentity | null;
|
|
109
|
+
/** Identity from the most recent SUCCESSFUL poll, or `null` when the most
|
|
110
|
+
* recent poll failed. */
|
|
111
|
+
lastIdentity: LeaseIdentity | null;
|
|
112
|
+
/** Consecutive failed polls since the last success. Reset to 0 on any
|
|
113
|
+
* success. */
|
|
114
|
+
consecutiveFailures: number;
|
|
115
|
+
/** The subset of that run whose polls ALSO found the control channel down —
|
|
116
|
+
* failures with no liveness evidence behind them. Voiding reads this
|
|
117
|
+
* counter, never `consecutiveFailures` alone. A single poll that saw an
|
|
118
|
+
* open channel resets it, because that poll proved the server alive. */
|
|
119
|
+
unbackedFailures: number;
|
|
120
|
+
/** The last moment this tab had POSITIVE EVIDENCE its server was alive — a
|
|
121
|
+
* poll that answered, or a poll that failed while the control socket was
|
|
122
|
+
* up — falling back to when the watch began, which is the earliest instant
|
|
123
|
+
* any claim about darkness could be measured from. `now - darkSince` is
|
|
124
|
+
* therefore "how long since we last saw a sign of life", and it is the
|
|
125
|
+
* clock half of the void bar.
|
|
126
|
+
*
|
|
127
|
+
* A count alone stopped meaning "a sustained window" once polls became
|
|
128
|
+
* EVENT-DRIVEN (the watchdog now re-polls the instant the control socket
|
|
129
|
+
* drops, and again when a hidden tab is shown). Three failures can then
|
|
130
|
+
* land inside a second — which is a dev server mid-restart, not a dead
|
|
131
|
+
* one. Equally, a backgrounded tab's interval is throttled to as slow as
|
|
132
|
+
* once a minute, so three polls can span minutes. Neither is what the
|
|
133
|
+
* threshold was ever trying to measure: the bar is DARKNESS FOR A WHILE,
|
|
134
|
+
* and only a timestamp says that. */
|
|
135
|
+
darkSince: number;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Default grace before a run of EVIDENCE-BACKED failed polls voids the lease.
|
|
139
|
+
* At the watchdog's ~4s cadence this is ~12s — long enough that an ordinary
|
|
140
|
+
* dev-server restart or HMR reload does not false-trigger, short enough to
|
|
141
|
+
* catch a real death promptly. */
|
|
142
|
+
export const DEFAULT_LEASE_FAILURE_THRESHOLD = 3;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* How long the evidence must stay dark before a void, ms — the bar the count
|
|
146
|
+
* used to stand in for.
|
|
147
|
+
*
|
|
148
|
+
* A dev server RESTARTS on its own, routinely: a source change relaunches it,
|
|
149
|
+
* and a cold dep re-optimize can hold it down for minutes. Every one of those
|
|
150
|
+
* windows opens exactly like a death — socket dropped, polls refused — and
|
|
151
|
+
* only its DURATION tells them apart at the start. So the watchdog must sit in
|
|
152
|
+
* the dark for a stretch no ordinary restart is shorter than before it says
|
|
153
|
+
* anything final. Ten seconds is that stretch: comfortably longer than the
|
|
154
|
+
* relaunch gap of a running server, short enough that a user who just clicked
|
|
155
|
+
* something is told inside one breath.
|
|
156
|
+
*
|
|
157
|
+
* Note the asymmetry, which is the whole point: being WRONG about "gone" costs
|
|
158
|
+
* the user a session, while being wrong about "busy" costs them a banner that
|
|
159
|
+
* clears itself. The clock buys the cheap error.
|
|
160
|
+
*/
|
|
161
|
+
export const LEASE_MIN_DARK_WINDOW_MS = 10_000;
|
|
162
|
+
|
|
163
|
+
/** The watchdog's poll cadence, ms. Exported so the shell and any test share
|
|
164
|
+
* one number. */
|
|
165
|
+
export const LEASE_POLL_INTERVAL_MS = 4_000;
|
|
166
|
+
|
|
167
|
+
/** `now` is when the watch begins: a tab that has never seen its server answer
|
|
168
|
+
* still needs a defensible instant to measure darkness from, and the moment
|
|
169
|
+
* it started looking is the only honest one. */
|
|
170
|
+
export function initialLeaseWatchState(now: number): LeaseWatchState {
|
|
171
|
+
return {
|
|
172
|
+
bootIdentity: null,
|
|
173
|
+
lastIdentity: null,
|
|
174
|
+
consecutiveFailures: 0,
|
|
175
|
+
unbackedFailures: 0,
|
|
176
|
+
darkSince: now,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Fold one poll result into the watch state.
|
|
182
|
+
*
|
|
183
|
+
* - A FAILURE clears `lastIdentity` (we no longer have a fresh reading) and
|
|
184
|
+
* increments the failure counter — and increments `unbackedFailures` too
|
|
185
|
+
* ONLY when the control channel was not open at that moment. A failed poll
|
|
186
|
+
* over a live socket is a busy server, and counts toward nothing that can
|
|
187
|
+
* void the tab.
|
|
188
|
+
* - A SUCCESS records the identity, adopts it as `bootIdentity` if we did not
|
|
189
|
+
* have one yet, and RESETS both counters — a single good poll clears a
|
|
190
|
+
* transient blip so a quick restart never accumulates toward a void.
|
|
191
|
+
*/
|
|
192
|
+
export function reduceLeasePoll(
|
|
193
|
+
state: LeaseWatchState,
|
|
194
|
+
result: LeasePollResult,
|
|
195
|
+
channel: LeaseChannelState,
|
|
196
|
+
now: number,
|
|
197
|
+
): LeaseWatchState {
|
|
198
|
+
if (!result.ok) {
|
|
199
|
+
const backed = channel === 'open';
|
|
200
|
+
return {
|
|
201
|
+
...state,
|
|
202
|
+
lastIdentity: null,
|
|
203
|
+
consecutiveFailures: state.consecutiveFailures + 1,
|
|
204
|
+
unbackedFailures: backed ? 0 : state.unbackedFailures + 1,
|
|
205
|
+
// An open socket is a sign of life even when the fetch missed, so it
|
|
206
|
+
// relights the clock exactly where it clears the counter. A dark poll
|
|
207
|
+
// leaves it alone: the stretch is measured from the LAST SIGN OF LIFE,
|
|
208
|
+
// not from the first failure — which is what makes it independent of how
|
|
209
|
+
// often the watchdog happens to be polling.
|
|
210
|
+
darkSince: backed ? now : state.darkSince,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
bootIdentity: state.bootIdentity ?? result.identity,
|
|
215
|
+
lastIdentity: result.identity,
|
|
216
|
+
consecutiveFailures: 0,
|
|
217
|
+
unbackedFailures: 0,
|
|
218
|
+
darkSince: now,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Drop the current failure run, keeping the identity this tab attached to.
|
|
224
|
+
*
|
|
225
|
+
* Called when the page comes back from a state where it was not RUNNING — a
|
|
226
|
+
* hidden tab the browser froze, a bfcache restore. The counters and the dark
|
|
227
|
+
* clock are testimony about a stretch of time, and a page that was suspended
|
|
228
|
+
* has none to give: its last readings are from before the gap and say nothing
|
|
229
|
+
* about the server now. Carrying them across would let one unlucky poll on a
|
|
230
|
+
* just-woken page (the socket has not finished reconnecting yet, so the
|
|
231
|
+
* channel reads `closed`) satisfy a threshold that was banked minutes ago and
|
|
232
|
+
* void a perfectly live session instantly.
|
|
233
|
+
*
|
|
234
|
+
* `bootIdentity` deliberately SURVIVES: who this tab attached to is not
|
|
235
|
+
* testimony about elapsed time, and it is what takeover and recovery are
|
|
236
|
+
* judged against.
|
|
237
|
+
*/
|
|
238
|
+
export function forgetLeaseFailures(state: LeaseWatchState, now: number): LeaseWatchState {
|
|
239
|
+
return {
|
|
240
|
+
bootIdentity: state.bootIdentity,
|
|
241
|
+
lastIdentity: null,
|
|
242
|
+
consecutiveFailures: 0,
|
|
243
|
+
unbackedFailures: 0,
|
|
244
|
+
darkSince: now,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** Whether two identities describe the same live lease — i.e. NOT a takeover.
|
|
249
|
+
* A takeover requires POSITIVE evidence that a DIFFERENT session now holds the
|
|
250
|
+
* port; absence of evidence is never a takeover. There are exactly two positive
|
|
251
|
+
* signals, and a `null` project is neither:
|
|
252
|
+
*
|
|
253
|
+
* 1. Two KNOWN, DIFFERENT projects — the port serves a different project than
|
|
254
|
+
* we booted on. A server is bound to one project, so a different project is
|
|
255
|
+
* a different session, even if the OS happened to recycle the same pid.
|
|
256
|
+
* 2. Two KNOWN, DIFFERENT pids — same (or absent) project but a REPLACED
|
|
257
|
+
* process: a second server for our project, which the tab bijection wants
|
|
258
|
+
* this now-stale tab to yield to.
|
|
259
|
+
*
|
|
260
|
+
* Everything else is the same lease. In particular a reading that reports NO
|
|
261
|
+
* project on the SAME pid — a transiently unreadable `vgai.project.json` save
|
|
262
|
+
* on our own still-live server — is absence of evidence, NOT a takeover; the
|
|
263
|
+
* pid positively identifies it as our own process. A `null` pid on either side
|
|
264
|
+
* (a server too old to report one) is likewise treated conservatively. */
|
|
265
|
+
function sameLease(a: LeaseIdentity, b: LeaseIdentity): boolean {
|
|
266
|
+
if (a.project !== null && b.project !== null && a.project !== b.project) return false;
|
|
267
|
+
if (a.pid !== null && b.pid !== null && a.pid !== b.pid) return false;
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export type LeaseVoidReason = 'server-gone' | 'taken-over';
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* - `live` — nothing to say.
|
|
275
|
+
* - `degraded` — the poll is failing but the control channel is up. The server
|
|
276
|
+
* is busy, or this page's HTTP pool is. Say so passively; change nothing.
|
|
277
|
+
* - `void` — the session this tab leased is gone. Tear the game down and tell
|
|
278
|
+
* the user.
|
|
279
|
+
*/
|
|
280
|
+
export type LeaseStatus = 'live' | 'degraded' | 'void';
|
|
281
|
+
|
|
282
|
+
export interface LeaseDecision {
|
|
283
|
+
status: LeaseStatus;
|
|
284
|
+
/** Which void. `null` for every non-void status. */
|
|
285
|
+
reason: LeaseVoidReason | null;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export interface LeaseDecisionInput {
|
|
289
|
+
consecutiveFailures: number;
|
|
290
|
+
/** Of that run, how many polls also had NO channel evidence (see
|
|
291
|
+
* `LeaseWatchState.unbackedFailures`). */
|
|
292
|
+
unbackedFailures: number;
|
|
293
|
+
failureThreshold: number;
|
|
294
|
+
/** How long it has been since the last sign of life, ms — `now - darkSince`,
|
|
295
|
+
* computed by the caller so this decision keeps no clock of its own. */
|
|
296
|
+
darkForMs: number;
|
|
297
|
+
/** The window that run must span before it can void (see
|
|
298
|
+
* {@link LEASE_MIN_DARK_WINDOW_MS}). */
|
|
299
|
+
minDarkWindowMs: number;
|
|
300
|
+
/** Identity this tab attached to at boot (`null` before the first success). */
|
|
301
|
+
bootIdentity: LeaseIdentity | null;
|
|
302
|
+
/** Identity from the latest successful poll (`null` when it failed). */
|
|
303
|
+
lastIdentity: LeaseIdentity | null;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Decide this tab's lease status — the whole policy, as a pure function.
|
|
308
|
+
*
|
|
309
|
+
* - `taken-over` — a poll SUCCEEDED but the server on the port is provably a
|
|
310
|
+
* DIFFERENT session than the one this tab attached to (a different known
|
|
311
|
+
* project, or a replaced process on a new pid — see `sameLease`). This is
|
|
312
|
+
* IMMEDIATE: no grace, even with zero failures, because the port has
|
|
313
|
+
* demonstrably changed hands. A server that merely reports no/unreadable
|
|
314
|
+
* project on the SAME pid is NOT a takeover.
|
|
315
|
+
* - `server-gone` — polls have failed `failureThreshold` times in a row, the
|
|
316
|
+
* control channel was down for that whole run, AND the run has lasted
|
|
317
|
+
* `minDarkWindowMs`. Three readings, because each alone has a benign
|
|
318
|
+
* explanation: a queued/timed-out poll on a busy server, a socket
|
|
319
|
+
* mid-reconnect, and — the one the clock covers — a burst of event-driven
|
|
320
|
+
* polls fired inside the second a restarting server is between processes.
|
|
321
|
+
* - `degraded` — a failure run that has not (yet) cleared all three bars:
|
|
322
|
+
* the channel is still up, or the darkness is still young. The session may
|
|
323
|
+
* be alive and merely unreachable; that is a warning, not a void. A run
|
|
324
|
+
* that will eventually void passes THROUGH this state, which is what puts
|
|
325
|
+
* an honest "not answering" notice on screen within a poll of the trouble
|
|
326
|
+
* starting instead of leaving the page silent for the whole window.
|
|
327
|
+
*
|
|
328
|
+
* Takeover is checked first: a successful poll that disagrees on identity is a
|
|
329
|
+
* stronger, faster signal than a failure run.
|
|
330
|
+
*/
|
|
331
|
+
export function decideLeaseState(input: LeaseDecisionInput): LeaseDecision {
|
|
332
|
+
const {
|
|
333
|
+
consecutiveFailures,
|
|
334
|
+
unbackedFailures,
|
|
335
|
+
failureThreshold,
|
|
336
|
+
darkForMs,
|
|
337
|
+
minDarkWindowMs,
|
|
338
|
+
bootIdentity,
|
|
339
|
+
lastIdentity,
|
|
340
|
+
} = input;
|
|
341
|
+
if (bootIdentity && lastIdentity && !sameLease(bootIdentity, lastIdentity)) {
|
|
342
|
+
return { status: 'void', reason: 'taken-over' };
|
|
343
|
+
}
|
|
344
|
+
if (consecutiveFailures >= failureThreshold) {
|
|
345
|
+
if (unbackedFailures >= failureThreshold && darkForMs >= minDarkWindowMs) {
|
|
346
|
+
return { status: 'void', reason: 'server-gone' };
|
|
347
|
+
}
|
|
348
|
+
return { status: 'degraded', reason: null };
|
|
349
|
+
}
|
|
350
|
+
return { status: 'live', reason: null };
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* After a `server-gone` void: whether one fresh poll proves the SAME project
|
|
355
|
+
* has reoccupied this port, so the tombstone page may RELOAD into the live
|
|
356
|
+
* editor instead of demanding a manual reopen.
|
|
357
|
+
*
|
|
358
|
+
* The revival contract (each piece decided, not defaulted):
|
|
359
|
+
* - AUTOMATIC, by full page reload. A reload boots a fresh SPA from the new
|
|
360
|
+
* server — the honest equivalent of the manual reopen the overlay used to
|
|
361
|
+
* demand — so "a dead tab never silently reconnects" still holds: nothing
|
|
362
|
+
* stale survives the reload.
|
|
363
|
+
* - Identity-gated: revival requires this tab's KNOWN boot project and a
|
|
364
|
+
* KNOWN, EQUAL reported project. A port reoccupied by a DIFFERENT project
|
|
365
|
+
* must never be reloaded into — that is exactly the silent retargeting the
|
|
366
|
+
* reserved-port rule exists to prevent. The pid is deliberately ignored:
|
|
367
|
+
* a revived server is a new process by definition.
|
|
368
|
+
*/
|
|
369
|
+
function revives(bootIdentity: LeaseIdentity | null, identity: LeaseIdentity): boolean {
|
|
370
|
+
if (!bootIdentity?.project || !identity.project) return false;
|
|
371
|
+
return identity.project === bootIdentity.project;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* What a `server-gone` tombstone should do when a poll starts answering again.
|
|
376
|
+
*
|
|
377
|
+
* - `resume` — the SAME PROCESS answered (equal, known pid). It never died;
|
|
378
|
+
* the void was drawn on a run of unreachable polls. Nothing about the page
|
|
379
|
+
* is stale, so it keeps its session: no reload, no lost camera/selection/
|
|
380
|
+
* undo stack. This is the branch that turns a hiccup back into a working
|
|
381
|
+
* editor instead of a restart.
|
|
382
|
+
* - `reload` — a DIFFERENT process now serves our SAME project: the server
|
|
383
|
+
* really did die and came back. The page IS stale, so the honest recovery is
|
|
384
|
+
* a full boot against the new server (the revival contract above).
|
|
385
|
+
* - `offer-reload` — something answers, but this tab never learned who it was
|
|
386
|
+
* (it died before its first successful poll, which is exactly what a
|
|
387
|
+
* mid-boot stall produces). It cannot verify the occupant, so it must not
|
|
388
|
+
* reload ITSELF — but leaving it a permanently dead window was the worst
|
|
389
|
+
* half of this defect. The notice offers the reload as a click and names it.
|
|
390
|
+
* - `none` — nothing answered, or a different known project holds the port.
|
|
391
|
+
*
|
|
392
|
+
* `taken-over` never recovers by any branch: that tab yielded to a live window
|
|
393
|
+
* the bijection chose, and reviving it would recreate the duplicate. The
|
|
394
|
+
* watchdog stops polling on that reason rather than encoding it here.
|
|
395
|
+
*/
|
|
396
|
+
export type LeaseRecovery = 'none' | 'resume' | 'reload' | 'offer-reload';
|
|
397
|
+
|
|
398
|
+
export function decideLeaseRecovery(
|
|
399
|
+
bootIdentity: LeaseIdentity | null,
|
|
400
|
+
result: LeasePollResult,
|
|
401
|
+
): LeaseRecovery {
|
|
402
|
+
if (!result.ok) return 'none';
|
|
403
|
+
const identity = result.identity;
|
|
404
|
+
if (
|
|
405
|
+
bootIdentity &&
|
|
406
|
+
bootIdentity.pid !== null &&
|
|
407
|
+
identity.pid === bootIdentity.pid &&
|
|
408
|
+
sameLease(bootIdentity, identity)
|
|
409
|
+
) {
|
|
410
|
+
return 'resume';
|
|
411
|
+
}
|
|
412
|
+
if (revives(bootIdentity, identity)) return 'reload';
|
|
413
|
+
if (!bootIdentity?.project) return 'offer-reload';
|
|
414
|
+
return 'none';
|
|
415
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The project root the session injected, for the code that must spell an
|
|
3
|
+
* ABSOLUTE path.
|
|
4
|
+
*
|
|
5
|
+
* The editor is served by its session (`vgai edit`) and runs inside the
|
|
6
|
+
* Code-OSS frame (ARCHITECTURE-CORE §The target shape rule 5), so a file read
|
|
7
|
+
* is a request to that session and `__VGAI_PROJECT_PATH__` is the folder it
|
|
8
|
+
* opened.
|
|
9
|
+
*
|
|
10
|
+
* Vite replaces the define at build time; `typeof` guards the one case where
|
|
11
|
+
* nothing injected it (a module loaded outside the session's own bundle).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
declare const __VGAI_PROJECT_PATH__: string;
|
|
15
|
+
|
|
16
|
+
/** Absolute project root injected by the session's dev/prod server. */
|
|
17
|
+
export function getProjectDefinePath(): string {
|
|
18
|
+
return typeof __VGAI_PROJECT_PATH__ !== 'undefined' ? __VGAI_PROJECT_PATH__ : '';
|
|
19
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EDITOR NOTIFICATIONS — the one door for a transient, event-shaped message
|
|
3
|
+
* the editor has for the human: an agent presented a view, the editor server
|
|
4
|
+
* went away, a document reloaded from disk. Persistent STATE — "the editor is
|
|
5
|
+
* disconnected", "play is running" — belongs on the status bar
|
|
6
|
+
* (`workspace-status-registry.ts`), where a glance answers it; a notification
|
|
7
|
+
* is for the moment something HAPPENED.
|
|
8
|
+
*
|
|
9
|
+
* ## WHO SHOWS IT: the workbench, and only the workbench
|
|
10
|
+
*
|
|
11
|
+
* A notification is the workbench's own toast, raised through
|
|
12
|
+
* `INotificationService` by the fork's `vgaiNotifications.ts`. This module is
|
|
13
|
+
* the door every caller already uses — `notify()`, which is also what
|
|
14
|
+
* `EditorHost.notify` delegates to — plus the by-id replace and dismiss
|
|
15
|
+
* contract both sides honour. It draws nothing itself, and there is no second
|
|
16
|
+
* surface for it to draw into.
|
|
17
|
+
*
|
|
18
|
+
* The payload maps exactly onto VS Code's, and both halves were measured
|
|
19
|
+
* rather than assumed: `NotificationAction.keeps` ("the card stays after this
|
|
20
|
+
* action runs") IS VS Code's SECONDARY action, while a plain action is
|
|
21
|
+
* PRIMARY; and `detail` is joined to the title with an em dash, because
|
|
22
|
+
* `NotificationMessage` is `string | Error` and NOT markdown.
|
|
23
|
+
*
|
|
24
|
+
* WHAT IS SAID BEFORE ANYONE CAN HEAR IT is queued, not dropped — see
|
|
25
|
+
* {@link setNotificationDelegate}. A message with nowhere else to go is still
|
|
26
|
+
* a message; it just waits for the surface instead of getting a second one.
|
|
27
|
+
*
|
|
28
|
+
* NOT here, and measured rather than assumed: `transient-hint.ts`. A hint is
|
|
29
|
+
* a stage-local, three-second whisper over the viewport (45 call sites), not
|
|
30
|
+
* an event-shaped message for the human — the workbench has no counterpart
|
|
31
|
+
* and it stays ours whole.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
export type NotificationTone = 'info' | 'warning' | 'error';
|
|
35
|
+
|
|
36
|
+
export interface NotificationAction {
|
|
37
|
+
readonly label: string;
|
|
38
|
+
readonly run: () => void;
|
|
39
|
+
/** The one action drawn as the primary button. */
|
|
40
|
+
readonly primary?: boolean;
|
|
41
|
+
/** Keep the card after this action runs (default: the card dismisses). */
|
|
42
|
+
readonly keeps?: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface EditorNotification {
|
|
46
|
+
readonly id: string;
|
|
47
|
+
readonly tone: NotificationTone;
|
|
48
|
+
/** One line, bold — what happened. */
|
|
49
|
+
readonly title: string;
|
|
50
|
+
/** The rest, plain — what it means, what to do. */
|
|
51
|
+
readonly detail?: string;
|
|
52
|
+
readonly actions?: readonly NotificationAction[];
|
|
53
|
+
/** Stay until dismissed. Default: warnings, errors and action-bearing
|
|
54
|
+
* cards stay; a plain info card hides after {@link INFO_NOTIFICATION_MS}. */
|
|
55
|
+
readonly sticky?: boolean;
|
|
56
|
+
/** Hide after {@link INFO_NOTIFICATION_MS} even though the tone is a
|
|
57
|
+
* warning — a report of a moment (a refused gesture), not a state. */
|
|
58
|
+
readonly fades?: boolean;
|
|
59
|
+
/** Called when the card leaves for any reason (dismiss, action, timeout). */
|
|
60
|
+
readonly onDismiss?: () => void;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* How a notification REACHES THE PERSON: the workbench's own
|
|
65
|
+
* `INotificationService`, installed by the fork's contribution once it has
|
|
66
|
+
* one. `show` returns the dismiss, exactly as {@link notify} does.
|
|
67
|
+
*/
|
|
68
|
+
export interface NotificationDelegate {
|
|
69
|
+
show(notification: EditorNotification): () => void;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let _delegate: NotificationDelegate | null = null;
|
|
73
|
+
/** Live notifications by id, so `dismissNotification(id)` — which several
|
|
74
|
+
* callers use instead of the closure `notify()` returns — reaches the toast
|
|
75
|
+
* too. Without it a card would be undismissable by id, silently. */
|
|
76
|
+
const _delegated = new Map<string, () => void>();
|
|
77
|
+
/**
|
|
78
|
+
* WHAT IS SAID BEFORE ANYONE CAN HEAR IT. The editor mounts before the
|
|
79
|
+
* contribution has `INotificationService` (a `ServicesAccessor` is valid only
|
|
80
|
+
* for the synchronous part of a command), and a `notify()` in that window is
|
|
81
|
+
* still a message. It waits here and is shown in order the moment the delegate
|
|
82
|
+
* arrives, rather than being drawn into a tray of our own that nobody looks at
|
|
83
|
+
* twice — there is one notification surface and it is the workbench's.
|
|
84
|
+
*/
|
|
85
|
+
let _pending: EditorNotification[] = [];
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Install how the frame shows one, and flush anything said before it could.
|
|
89
|
+
* Passing `null` stops delegation: further notifications wait again.
|
|
90
|
+
*/
|
|
91
|
+
export function setNotificationDelegate(delegate: NotificationDelegate | null): void {
|
|
92
|
+
_delegate = delegate;
|
|
93
|
+
if (!delegate) return;
|
|
94
|
+
const waiting = _pending;
|
|
95
|
+
_pending = [];
|
|
96
|
+
for (const notification of waiting) showThroughDelegate(delegate, notification);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Show one through the delegate, keeping the by-id dismiss reachable. */
|
|
100
|
+
function showThroughDelegate(
|
|
101
|
+
delegate: NotificationDelegate,
|
|
102
|
+
notification: EditorNotification,
|
|
103
|
+
): () => void {
|
|
104
|
+
// Replace-by-id is the door's contract on both sides.
|
|
105
|
+
_delegated.get(notification.id)?.();
|
|
106
|
+
const close = delegate.show(notification);
|
|
107
|
+
const dismiss = () => {
|
|
108
|
+
if (_delegated.get(notification.id) !== dismiss) return;
|
|
109
|
+
_delegated.delete(notification.id);
|
|
110
|
+
close();
|
|
111
|
+
notification.onDismiss?.();
|
|
112
|
+
};
|
|
113
|
+
_delegated.set(notification.id, dismiss);
|
|
114
|
+
return dismiss;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let _seq = 0;
|
|
118
|
+
|
|
119
|
+
/** Publish (or replace, by id). Returns the dismiss. */
|
|
120
|
+
export function notify(input: Omit<EditorNotification, 'id'> & { id?: string }): () => void {
|
|
121
|
+
const id = input.id ?? `notification-${++_seq}`;
|
|
122
|
+
const notification: EditorNotification = { ...input, id };
|
|
123
|
+
if (_delegate) return showThroughDelegate(_delegate, notification);
|
|
124
|
+
// Nothing can draw it yet. Queue, replacing any earlier message with this id
|
|
125
|
+
// — the same replace-by-id contract the delegate path has.
|
|
126
|
+
_pending = [..._pending.filter((n) => n.id !== id), notification];
|
|
127
|
+
return () => dismissNotification(id);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function dismissNotification(id: string): void {
|
|
131
|
+
const delegated = _delegated.get(id);
|
|
132
|
+
if (delegated) {
|
|
133
|
+
delegated();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const waiting = _pending.find((n) => n.id === id);
|
|
137
|
+
if (!waiting) return;
|
|
138
|
+
_pending = _pending.filter((n) => n.id !== id);
|
|
139
|
+
waiting.onDismiss?.();
|
|
140
|
+
}
|