@volter/editor-game 0.5.65
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 +661 -0
- package/NOTICE +23 -0
- package/contributions/asset-budget-asset.menu.ts +26 -0
- package/contributions/asset-budget.action.ts +18 -0
- package/contributions/asset-budget.document.tsx +21 -0
- package/contributions/asset-budget.menu.ts +22 -0
- package/contributions/audio-unlock.service.ts +17 -0
- package/contributions/audio.utility.tsx +15 -0
- package/contributions/autoplay.service.ts +48 -0
- package/contributions/bridge.command.ts +172 -0
- package/contributions/build-profiles.document.tsx +21 -0
- package/contributions/build-progress.status.tsx +45 -0
- package/contributions/build.action.ts +26 -0
- package/contributions/build.command.ts +27 -0
- package/contributions/build.header.tsx +37 -0
- package/contributions/build.menu.ts +31 -0
- package/contributions/build.service.ts +31 -0
- package/contributions/connection.status.tsx +43 -0
- package/contributions/coverage.service.ts +118 -0
- package/contributions/edit-mode-audio.service.ts +23 -0
- package/contributions/edit-mode-networking.service.ts +30 -0
- package/contributions/game-document.service.ts +25 -0
- package/contributions/game-eval.command.ts +210 -0
- package/contributions/game.layout.ts +19 -0
- package/contributions/gameplay.command.ts +255 -0
- package/contributions/generation.service.ts +96 -0
- package/contributions/generations.status.tsx +53 -0
- package/contributions/ingest.service.ts +80 -0
- package/contributions/instances.command.ts +75 -0
- package/contributions/navmesh.menu.ts +39 -0
- package/contributions/navmesh.service.ts +19 -0
- package/contributions/network.utility.tsx +17 -0
- package/contributions/play.command.ts +355 -0
- package/contributions/profiler.action.ts +16 -0
- package/contributions/profiler.menu.ts +22 -0
- package/contributions/profiler.utility.tsx +17 -0
- package/contributions/react/component-board.service.ts +21 -0
- package/contributions/react/design-time-mount.service.ts +60 -0
- package/contributions/react/pasteboard.action.ts +41 -0
- package/contributions/react/react-inspector.service.ts +85 -0
- package/contributions/react/story-documents.service.ts +44 -0
- package/contributions/scene-document.service.ts +32 -0
- package/contributions/state-watch.action.ts +17 -0
- package/contributions/state-watch.menu.ts +23 -0
- package/contributions/state-watch.utility.tsx +20 -0
- package/contributions/team-playtest.service.ts +124 -0
- package/contributions/three/camera-runtime.inspector.tsx +34 -0
- package/contributions/three/component-board.service.ts +24 -0
- package/contributions/three/component-verbs.command.ts +110 -0
- package/contributions/three/component-verbs.service.ts +92 -0
- package/contributions/three/constraints.inspector.tsx +34 -0
- package/contributions/three/model-asset-sections.service.ts +26 -0
- package/contributions/three/reflection-probe-capture.inspector.tsx +32 -0
- package/contributions/three/story-documents.service.ts +31 -0
- package/contributions/three/three-authoring.service.ts +66 -0
- package/contributions/transport.header.tsx +19 -0
- package/contributions/xstate-behavior.action.ts +42 -0
- package/contributions/xstate-behavior.document.tsx +73 -0
- package/contributions/xstate-behavior.inspector.tsx +28 -0
- package/contributions/xstate-behavior.menu.ts +23 -0
- package/package.json +144 -0
- package/src/asset-budget/AssetBudgetPanel.tsx +1172 -0
- package/src/asset-budget/asset-budget-model.ts +799 -0
- package/src/asset-budget/basis-encoder.ts +165 -0
- package/src/asset-budget/gltf-io.ts +154 -0
- package/src/asset-budget/gltf-optimize.ts +384 -0
- package/src/asset-budget/image-dims.ts +78 -0
- package/src/asset-budget/optimize-apply.ts +221 -0
- package/src/audio/AudioDebuggerPanel.tsx +457 -0
- package/src/audio/audio-debugger-model.ts +87 -0
- package/src/bridge/call.ts +60 -0
- package/src/bridge/dispatch.ts +459 -0
- package/src/bridge/live-frames.ts +25 -0
- package/src/bridge/screenshot.ts +316 -0
- package/src/build/BuildProfilesPanel.tsx +476 -0
- package/src/build/build-session.ts +247 -0
- package/src/build/format-bytes.ts +14 -0
- package/src/command-results.ts +36 -0
- package/src/coverage/live-authoring-surface.ts +30 -0
- package/src/coverage/live-project-verbs.ts +162 -0
- package/src/coverage/native-system-coverage.ts +143 -0
- package/src/coverage/root-coverage.ts +79 -0
- package/src/coverage/session-coverage.ts +193 -0
- package/src/design-system-stories/ApplicationChrome.stories.tsx +100 -0
- package/src/design-system-stories/InspectorNarrowBodies.stories.tsx +406 -0
- package/src/edit-mode/edit-mode-audio.ts +56 -0
- package/src/edit-mode/edit-mode-networking.ts +112 -0
- package/src/game-document/DevicePresetPicker.tsx +84 -0
- package/src/game-document/GameCaptureFrameButton.tsx +45 -0
- package/src/game-document/GameDocument.tsx +215 -0
- package/src/game-document/GamePanel.tsx +662 -0
- package/src/game-document/InstanceInspectorPicker.tsx +183 -0
- package/src/game-document/crowd-debug.ts +183 -0
- package/src/game-document/device-preview.ts +336 -0
- package/src/game-document/game-view-store.ts +107 -0
- package/src/game-document/physics-debug.ts +187 -0
- package/src/generation/GenerationActivity.tsx +421 -0
- package/src/generation/GenerationGallery.css +231 -0
- package/src/generation/generation-documents.tsx +338 -0
- package/src/generation/generation-jobs.ts +128 -0
- package/src/generation/generation-presentation.ts +257 -0
- package/src/host/adapter-reach.ts +445 -0
- package/src/host/adapter-runtime-bindings.ts +303 -0
- package/src/host/after-paint.ts +112 -0
- package/src/host/api/configurations.ts +66 -0
- package/src/host/authoring/babylon-authoring-adapter.ts +703 -0
- package/src/host/authoring/canvas-runtime-recognition.ts +50 -0
- package/src/host/authoring/contract-hierarchy-authoring.ts +144 -0
- package/src/host/authoring/contract-scenes-stories.ts +204 -0
- package/src/host/authoring/creation-site-related.ts +55 -0
- package/src/host/authoring/ephemeral-persistence.ts +29 -0
- package/src/host/authoring/gesture-persist.ts +84 -0
- package/src/host/authoring/ingest-data-writer.ts +232 -0
- package/src/host/authoring/ingest-source-persistence.ts +826 -0
- package/src/host/authoring/mount-isolated-pixi-screen.ts +250 -0
- package/src/host/authoring/mounted-authoring.ts +41 -0
- package/src/host/authoring/owned-pixi-ticker-listeners.ts +96 -0
- package/src/host/authoring/phaser-live-authoring-adapter.ts +265 -0
- package/src/host/authoring/pixi-authoring-adapter.ts +1554 -0
- package/src/host/authoring/pixi-creation-site-write-target.ts +60 -0
- package/src/host/authoring/pixi-isolation-assets.ts +25 -0
- package/src/host/authoring/pixi-live-write-target.ts +979 -0
- package/src/host/authoring/pixi-source-identity.ts +141 -0
- package/src/host/authoring/pixi-still-presentation.ts +71 -0
- package/src/host/authoring/pixi-structure-history.ts +237 -0
- package/src/host/authoring/pixi-transform-channels.ts +205 -0
- package/src/host/authoring/selection-remount-handoff.ts +23 -0
- package/src/host/authoring/source-persistence-backend.ts +373 -0
- package/src/host/authoring/source-refresh-revisions.ts +81 -0
- package/src/host/authoring/struct-write-pipe.ts +143 -0
- package/src/host/auto-frame-window.ts +89 -0
- package/src/host/binding-resolver.ts +393 -0
- package/src/host/browser-transpile.ts +631 -0
- package/src/host/canvas-entry-runtime.ts +95 -0
- package/src/host/components/CameraAuthoringOverlay.tsx +216 -0
- package/src/host/components/HeaderTelemetry.tsx +341 -0
- package/src/host/components/PixiIsolationSceneContent.tsx +280 -0
- package/src/host/components/ResolutionPicker.tsx +89 -0
- package/src/host/components/ThreeIsolationSceneContent.tsx +180 -0
- package/src/host/components/frame-debugger-model.ts +579 -0
- package/src/host/components/header-telemetry-model.ts +74 -0
- package/src/host/components/scene-document.tsx +447 -0
- package/src/host/components/utility-view-state.ts +87 -0
- package/src/host/components/world-root-stage-binding.tsx +133 -0
- package/src/host/components/world-root-stage.ts +1046 -0
- package/src/host/coverage/authoring-read-probe.ts +583 -0
- package/src/host/coverage/capability-coverage.ts +1587 -0
- package/src/host/coverage/coverage-accounting.ts +261 -0
- package/src/host/coverage/game-contract-seam-evidence.ts +82 -0
- package/src/host/coverage/project-verb-coverage.ts +148 -0
- package/src/host/coverage/system-adapter-coverage.ts +373 -0
- package/src/host/design-system-stories/StoryLayout.tsx +104 -0
- package/src/host/design-system-stories/fixtures/authoring.ts +247 -0
- package/src/host/design-system-stories/fixtures/editor-runtime.tsx +115 -0
- package/src/host/document-preview-three.ts +110 -0
- package/src/host/entry-adjudication.ts +89 -0
- package/src/host/game-location-guard.ts +150 -0
- package/src/host/game-module-access.ts +196 -0
- package/src/host/game-realm-page.ts +358 -0
- package/src/host/game-realm-reclaim.ts +55 -0
- package/src/host/game-realm-storage.ts +104 -0
- package/src/host/gameplay-export.ts +288 -0
- package/src/host/gameplay-recording.ts +717 -0
- package/src/host/gated-globals.ts +1511 -0
- package/src/host/history/json-history-resource.ts +254 -0
- package/src/host/ingest/registry.ts +261 -0
- package/src/host/instance-extract-actions.ts +120 -0
- package/src/host/instance-fork-actions.ts +120 -0
- package/src/host/play-control-hook.ts +26 -0
- package/src/host/playwright-shim.ts +479 -0
- package/src/host/projection/dom.ts +295 -0
- package/src/host/projection/pixi.ts +288 -0
- package/src/host/r3f-entry-runtime.ts +77 -0
- package/src/host/react-mount-runtime.ts +162 -0
- package/src/host/realm-services.ts +148 -0
- package/src/host/recording-preview.ts +106 -0
- package/src/host/roots/module-root.ts +203 -0
- package/src/host/roots/react-root.ts +181 -0
- package/src/host/same-realm-loop-gate.ts +544 -0
- package/src/host/scene-view-drawability.ts +69 -0
- package/src/host/sdk/tools.ts +31 -0
- package/src/host/served-bundle-runtime-modules.ts +331 -0
- package/src/host/server-log-bridge.ts +60 -0
- package/src/host/staged-projects.ts +24 -0
- package/src/host/stories/mounted-story-viewport-source.ts +64 -0
- package/src/host/stories/story-arg-descriptors.ts +50 -0
- package/src/host/stories/story-media-presence.ts +152 -0
- package/src/host/surface-content.ts +87 -0
- package/src/host/take-named-export.ts +23 -0
- package/src/host/three-ingest-runtime.ts +76 -0
- package/src/host/types-fastnoise-lite.d.ts +7 -0
- package/src/host/types-mikktspace.d.ts +20 -0
- package/src/host/types-troika-three-text.d.ts +7 -0
- package/src/host/use-active-performance-source.ts +53 -0
- package/src/host/viewport-pose-memory.ts +48 -0
- package/src/host/viewport-root-presentation.ts +40 -0
- package/src/ingest/active-ingest.ts +251 -0
- package/src/ingest/active-scene-navigation.ts +43 -0
- package/src/ingest/authoring/ingest-dom-surface-authoring.ts +191 -0
- package/src/ingest/authoring/ingest-root-adapter.ts +897 -0
- package/src/ingest/capture-wait-report.ts +122 -0
- package/src/ingest/deferred-ingest-play.ts +216 -0
- package/src/ingest/deferred-ingest-session.ts +23 -0
- package/src/ingest/discovery-public-ingest.ts +242 -0
- package/src/ingest/dom-stub-mark.ts +7 -0
- package/src/ingest/entry-load.ts +56 -0
- package/src/ingest/game-contract-realm.ts +34 -0
- package/src/ingest/game-pointer-lock.ts +89 -0
- package/src/ingest/held-scene-repaint.ts +73 -0
- package/src/ingest/host-surface-box.ts +174 -0
- package/src/ingest/ingest-boot-viewport.ts +54 -0
- package/src/ingest/ingest-canvas-scene-document.tsx +177 -0
- package/src/ingest/ingest-canvas-scene.ts +50 -0
- package/src/ingest/ingest-evidence-hook.ts +131 -0
- package/src/ingest/ingest-frame-snapshot.ts +183 -0
- package/src/ingest/ingest-play-commands.ts +134 -0
- package/src/ingest/ingest-play-control.ts +299 -0
- package/src/ingest/ingest-render-debug.ts +320 -0
- package/src/ingest/ingest-siblings.ts +487 -0
- package/src/ingest/ingest-status.ts +62 -0
- package/src/ingest/live-ingest-facet.ts +46 -0
- package/src/ingest/module-mode.ts +229 -0
- package/src/ingest/mount-canvas-ingest-root.ts +899 -0
- package/src/ingest/mount-coverage.ts +296 -0
- package/src/ingest/mount-dom-ingest-root.ts +282 -0
- package/src/ingest/mount-ingest-root.ts +744 -0
- package/src/ingest/mount-three-ingest-root.ts +366 -0
- package/src/ingest/resolve-canvas.ts +47 -0
- package/src/ingest/resolve-three.ts +123 -0
- package/src/ingest/served-bundle.ts +109 -0
- package/src/ingest/served-html-boot.ts +292 -0
- package/src/ingest/surface-canvas.ts +84 -0
- package/src/ingest/surface-dom.ts +84 -0
- package/src/ingest/surface-three.ts +128 -0
- package/src/ingest/types.ts +76 -0
- package/src/ingest/unmount-ingest-root.ts +224 -0
- package/src/navmesh/navmesh-actions.ts +27 -0
- package/src/navmesh/navmesh-handler.ts +237 -0
- package/src/navmesh/navmesh-workflow-store.ts +78 -0
- package/src/network/NetworkInspectorPanel.tsx +644 -0
- package/src/network/network-inspector-model.ts +225 -0
- package/src/play/play-boot-stall.ts +118 -0
- package/src/play/play-log-events.ts +26 -0
- package/src/play/play-mode.ts +2553 -0
- package/src/play/play-recording.ts +335 -0
- package/src/play/react-play-live-authoring.ts +165 -0
- package/src/play-bar/PlayBar.tsx +488 -0
- package/src/play-bar/PlayerCountPicker.tsx +100 -0
- package/src/profiler/FrameDebuggerPanel.tsx +458 -0
- package/src/profiler/PerformancePanel.tsx +1068 -0
- package/src/profiler/ProfilerPanel.tsx +53 -0
- package/src/profiler/frame-debugger-store.ts +97 -0
- package/src/profiler/main-thread-busy.ts +90 -0
- package/src/react/design-time-react-mount.ts +782 -0
- package/src/react/dom-authoring-adapter.ts +764 -0
- package/src/react/pasteboard-materialize.ts +154 -0
- package/src/react/react-inspector-section.tsx +2496 -0
- package/src/react/react-world-authoring-adapter.ts +3795 -0
- package/src/react/story-documents/story-args-section.ts +19 -0
- package/src/react/story-documents/story-documents.tsx +1380 -0
- package/src/react/story-paint-bounds.ts +94 -0
- package/src/react/ui-board-document.tsx +101 -0
- package/src/react/ui-board-title.ts +9 -0
- package/src/react/ui-component-board.ts +72 -0
- package/src/services/audio-pose-guard.ts +81 -0
- package/src/services/game-audio-unlock.ts +48 -0
- package/src/state-watch/StateWatchPanel.tsx +535 -0
- package/src/three/authoring/camera-runtime-inspector-section.tsx +135 -0
- package/src/three/authoring/constraint-inspector-section.tsx +189 -0
- package/src/three/authoring/design-time-renderer.ts +189 -0
- package/src/three/authoring/model-asset-inspector-section.css +41 -0
- package/src/three/authoring/model-asset-inspector-section.tsx +869 -0
- package/src/three/authoring/oid-source-persistence.ts +557 -0
- package/src/three/authoring/r3f-design-session.ts +1174 -0
- package/src/three/authoring/r3f-source-authoring-adapter.ts +5949 -0
- package/src/three/authoring/reflection-probe-inspector-section.tsx +76 -0
- package/src/three/authoring/spatial-audio-handles.ts +215 -0
- package/src/three/authoring/spatial-collider-handles.ts +229 -0
- package/src/three/authoring/spatial-joint-handles.ts +114 -0
- package/src/three/authoring/spatial-light-handles.ts +178 -0
- package/src/three/authoring/spatial-lod-handles.ts +93 -0
- package/src/three/authoring/spatial-particle-handles.ts +368 -0
- package/src/three/authoring/three-authoring-adapter.ts +1953 -0
- package/src/three/authoring/three-scene-identity.ts +19 -0
- package/src/three/authoring/three-spatial-handles.ts +161 -0
- package/src/three/authoring/typed-three-inspector.ts +528 -0
- package/src/three/component-verbs/extract-menu.ts +71 -0
- package/src/three/component-verbs/fork-menu.ts +78 -0
- package/src/three/component-verbs/internals-menu.ts +91 -0
- package/src/three/story-documents/three-story-documents.tsx +607 -0
- package/src/three/three-board/ThreeBoardDocument.tsx +888 -0
- package/src/three/three-board/board-framing.ts +412 -0
- package/src/three/three-board/board-layout.ts +401 -0
- package/src/three/three-board/board-scene.ts +901 -0
- package/src/three/three-board/three-component-board.ts +62 -0
- package/src/xstate/XStateBehaviorSection.tsx +130 -0
- package/src/xstate/XStateMachineInspector.tsx +566 -0
- package/src/xstate/character-animation-machine.fixture.ts +74 -0
- package/src/xstate/live-behaviors.ts +106 -0
- package/src/xstate/use-live-actor-state.ts +44 -0
- package/src/xstate/xstate-graph.ts +235 -0
- package/src/xstate/xstate-layout.ts +76 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE single live-ingest slot: the shape of a mounted `{ ingest }` root's
|
|
3
|
+
* session, the one module-level variable holding it, and the queue that keeps
|
|
4
|
+
* entry to it serial.
|
|
5
|
+
*
|
|
6
|
+
* This module deliberately holds STATE and nothing else — no mounting, no
|
|
7
|
+
* teardown, no measurement. `mount-three-ingest-root.ts` /
|
|
8
|
+
* `mount-canvas-ingest-root.ts` / `mount-dom-ingest-root.ts` publish into the
|
|
9
|
+
* slot, `unmount-ingest-root.ts` empties it, and everything that merely READS a
|
|
10
|
+
* live session (`ingest-play-control.ts`, `mount-coverage.ts`,
|
|
11
|
+
* `live-ingest-facet.ts`) imports from here. Keeping the slot a leaf is what
|
|
12
|
+
* makes that whole graph acyclic.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { EditorShellStore } from '@volter/editor-core/editor-shell-store';
|
|
16
|
+
import type { MeasuredLoop } from '../host/same-realm-loop-gate';
|
|
17
|
+
import type { DomAuthoringAdapter } from '../react/dom-authoring-adapter';
|
|
18
|
+
import type { AuthoringAdapter } from '@volter/editor-project/adapter/authoring';
|
|
19
|
+
import type { IngestMount } from './authoring/ingest-root-adapter';
|
|
20
|
+
import type { SiblingMount } from './ingest-siblings';
|
|
21
|
+
import type { MountCoverageInputs } from './mount-coverage';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The ONE lifecycle control the editor consumes for an ingested game.
|
|
25
|
+
*
|
|
26
|
+
* Mount code resolves the game's raw contract and the substrate's fallback
|
|
27
|
+
* into this shape once. Consumers never choose between those mechanisms:
|
|
28
|
+
* `start` owns the cold -> started edge, while `setPaused` is present only
|
|
29
|
+
* when one complete pause/resume mechanism exists. A missing capability keeps
|
|
30
|
+
* its exact reason on `pauseGap` so Edit can refuse loudly rather than treating
|
|
31
|
+
* a fabricated no-op function as a successful hold.
|
|
32
|
+
*/
|
|
33
|
+
export interface IngestLifecycleControl {
|
|
34
|
+
readonly start: () => void;
|
|
35
|
+
readonly setPaused?: ((paused: boolean) => void) | undefined;
|
|
36
|
+
readonly pauseGap?: string | undefined;
|
|
37
|
+
readonly step?: (() => void) | undefined;
|
|
38
|
+
readonly canStep?: (() => boolean) | undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface IngestSession {
|
|
42
|
+
store: EditorShellStore;
|
|
43
|
+
lifecycle: IngestLifecycleControl;
|
|
44
|
+
/** The capture path (a real authoring adapter over the live scene). */
|
|
45
|
+
mount?: IngestMount;
|
|
46
|
+
/** Keeps the captured game renderer sized to its container (host lifecycle). */
|
|
47
|
+
resizeObserver?: ResizeObserver;
|
|
48
|
+
/** Drops the store subscription that releases a game-held pointer lock when
|
|
49
|
+
* the input gate closes (`ingest/game-pointer-lock.ts`). */
|
|
50
|
+
inputGateUnsubscribe?: () => void;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface IngestSession2D {
|
|
54
|
+
store: EditorShellStore;
|
|
55
|
+
lifecycle: IngestLifecycleControl;
|
|
56
|
+
adapter?: AuthoringAdapter & { dispose(): void };
|
|
57
|
+
/** The realm host box the game appended into — Scene reparents this. */
|
|
58
|
+
hostEl?: HTMLElement;
|
|
59
|
+
/** Measured loop truth for a reachable canvas realm — a verdict CARRIES the
|
|
60
|
+
* evidence it was concluded from ({@link MeasuredLoop}). `null` when the
|
|
61
|
+
* probe ran but learned nothing (`reason` says what it looked at) — never a
|
|
62
|
+
* verdict forced out of an absence of evidence. */
|
|
63
|
+
loop?: () => { loop: MeasuredLoop | null; reason: string };
|
|
64
|
+
dispose: () => void;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface IngestSessionReact {
|
|
68
|
+
store: EditorShellStore;
|
|
69
|
+
lifecycle: IngestLifecycleControl;
|
|
70
|
+
stop: () => void;
|
|
71
|
+
/** Live DOM-authoring adapter; owns a session history resource. */
|
|
72
|
+
adapter: DomAuthoringAdapter;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The world kind of the currently-active ingest session, if any (F26):
|
|
77
|
+
* ingest is an ADAPTER ROUTE, not a kind of its own — D-V1
|
|
78
|
+
* collapsed the three per-kind session slots this used to fixed-priority-scan
|
|
79
|
+
* (`_session` / `_session2D` / `_sessionReact`) into the single `_active` slot
|
|
80
|
+
* below, so at most one session is ever active, of any kind, and
|
|
81
|
+
* {@link activeIngestKind} is just that slot's tag. The play surface
|
|
82
|
+
* ({@link import('./ingest-play-control').getIngestPlayControl}) and
|
|
83
|
+
* {@link isIngestActive} read it, never a slot directly.
|
|
84
|
+
*/
|
|
85
|
+
export type IngestKind = 'three' | 'canvas' | 'dom';
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* F26 slot collapse (D-V1): at most one ingest session is EVER active, of
|
|
89
|
+
* any kind — {@link activeIngest} is the single module-level slot for it.
|
|
90
|
+
* Pre-collapse, this was three independent per-kind singletons
|
|
91
|
+
* (`_session`/`_session2D`/`_sessionReact`, one per `IngestSession*` shape
|
|
92
|
+
* above), each with its own entry guard that only tore down its OWN slot —
|
|
93
|
+
* so switching kinds leaked the prior kind's live session (the F26
|
|
94
|
+
* residual).
|
|
95
|
+
*
|
|
96
|
+
* `siblings` (D-V2, composite): a composite manifest's OTHER roots, mounted
|
|
97
|
+
* beside this session's ingest world (`ingest-siblings.ts`'s
|
|
98
|
+
* `mountIngestSiblings`) — always `[]` for every non-composite entry path
|
|
99
|
+
* (the in-tree fixture glob route, a single-world manifest).
|
|
100
|
+
* `unmount-ingest-root.ts`'s `exitActiveIngest` disposes these, reverse order,
|
|
101
|
+
* in EVERY kind's teardown path — a sibling never outlives the session it was
|
|
102
|
+
* mounted beside.
|
|
103
|
+
*/
|
|
104
|
+
export type ActiveIngest = (
|
|
105
|
+
| { kind: 'three'; session: IngestSession }
|
|
106
|
+
| { kind: 'canvas'; session: IngestSession2D }
|
|
107
|
+
| { kind: 'dom'; session: IngestSessionReact }
|
|
108
|
+
) & {
|
|
109
|
+
siblings: SiblingMount[];
|
|
110
|
+
/** The mounted world's id — the SAME id every log line and the
|
|
111
|
+
* `__vgaiIngest` hook's `gameId` names. Carried on the slot (rather than
|
|
112
|
+
* re-derived) so the status facet can report WHICH world is live
|
|
113
|
+
* without a second source of truth (S-1). */
|
|
114
|
+
worldId: string;
|
|
115
|
+
/** The coverage inputs that can only be read AT MOUNT (see
|
|
116
|
+
* {@link MountCoverageInputs}). Absent until a mount path records them, and
|
|
117
|
+
* absent is honestly "not measured" — never a clean bill of health. */
|
|
118
|
+
coverage?: MountCoverageInputs;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
let _active: ActiveIngest | null = null;
|
|
122
|
+
|
|
123
|
+
/** The live session, or `null`. Read it; never cache it across an await. */
|
|
124
|
+
export function activeIngest(): ActiveIngest | null {
|
|
125
|
+
return _active;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** THE one write to the slot. Publishing happens only after a mount SUCCEEDS,
|
|
129
|
+
* which is what makes a non-null slot the mount evidence itself (S-1). */
|
|
130
|
+
export function setActiveIngest(next: ActiveIngest | null): void {
|
|
131
|
+
_active = next;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function activeIngestKind(): IngestKind | null {
|
|
135
|
+
return _active?.kind ?? null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** True if an unmodified game is currently ingested — ANY kind (F26). */
|
|
139
|
+
export function isIngestActive(): boolean {
|
|
140
|
+
return activeIngestKind() !== null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** True if an unmodified PixiJS game is currently ingested. */
|
|
144
|
+
export function isIngestActive2D(): boolean {
|
|
145
|
+
return _active?.kind === 'canvas';
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** True if an ingested native-React game is currently mounted. */
|
|
149
|
+
export function isIngestActiveReact(): boolean {
|
|
150
|
+
return _active?.kind === 'dom';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* D-V2: attach a composite's sibling handles onto the JUST-published session
|
|
155
|
+
* (set by the primary ingest world's own mount call, immediately before this
|
|
156
|
+
* runs) — a no-op if the slot is somehow no longer set (defensive only;
|
|
157
|
+
* nothing in that synchronous call chain clears it between the primary mount
|
|
158
|
+
* and this attach).
|
|
159
|
+
*/
|
|
160
|
+
export function attachIngestSiblings(siblings: readonly SiblingMount[]): void {
|
|
161
|
+
if (_active) _active.siblings = [...siblings];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Serializes ingest ENTRY, exactly as `play-mode.ts`'s `_enterQueue` does for
|
|
166
|
+
* play — and for the same reason it had to.
|
|
167
|
+
*
|
|
168
|
+
* Every entry point opens with a synchronous `exitActiveIngest()`, which reads
|
|
169
|
+
* the slot. But the slot is not assigned until AFTER the mount's awaits
|
|
170
|
+
* resolve. Two overlapping calls therefore both observe an empty slot,
|
|
171
|
+
* both tear down nothing, and both mount — leaving two live sessions. That is
|
|
172
|
+
* the react-rpg double mount: two React roots, two `<div id="window">`, two
|
|
173
|
+
* "Story Mode" buttons, intermittently, because it is a race rather than a
|
|
174
|
+
* code path. Nothing kind-specific about it; every entry point shares the
|
|
175
|
+
* shape, react merely had the test that caught it.
|
|
176
|
+
*
|
|
177
|
+
* Queued, the second caller's `exitActiveIngest()` runs after the first has
|
|
178
|
+
* published the slot, so it disposes it and exactly one session stays live.
|
|
179
|
+
* Pinned by `ingest-root-entry-race.test.ts`.
|
|
180
|
+
*/
|
|
181
|
+
let _enterQueue: Promise<void> = Promise.resolve();
|
|
182
|
+
|
|
183
|
+
export function serializeEntry<T>(run: () => Promise<T>): Promise<T> {
|
|
184
|
+
const result = _enterQueue.then(run);
|
|
185
|
+
// Advance unconditionally so one caller's rejection can never wedge every
|
|
186
|
+
// subsequent ingest attempt — each caller still observes its OWN failure
|
|
187
|
+
// through the promise returned here (same contract as play-mode's queue).
|
|
188
|
+
_enterQueue = result.then(
|
|
189
|
+
() => undefined,
|
|
190
|
+
() => undefined,
|
|
191
|
+
);
|
|
192
|
+
return result;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
import { registerLiveSession } from '@volter/editor-core/live-session-registry';
|
|
196
|
+
// THE INGEST LANE, as the host sees it (`live-session-registry.ts`). Playing
|
|
197
|
+
// is the host play-control latch, never an inferred loop
|
|
198
|
+
// (`editor-session-mode.ts` records why); a deferred-ingest play counts.
|
|
199
|
+
import { activeContractScenes } from './active-scene-navigation';
|
|
200
|
+
import { deferredIngestPlayActive } from './deferred-ingest-session';
|
|
201
|
+
import { armIngestFrameSnapshot, ingestFrameCanvas } from './ingest-frame-snapshot';
|
|
202
|
+
import { handleIngestPlayControl } from './ingest-play-commands';
|
|
203
|
+
import { getIngestPlayControl, ingestPlaying } from './ingest-play-control';
|
|
204
|
+
// `mount-coverage.ts` imports this module back, so the pair is a CYCLE — and
|
|
205
|
+
// the lane's report is read through it directly rather than pushed in by a
|
|
206
|
+
// setter. A setter is what a cycle cannot survive: `mount-coverage`'s
|
|
207
|
+
// module-scope `setIngestCoverageSource(...)` ran while this module's own body
|
|
208
|
+
// was still in its `let`'s temporal dead zone whenever the graph happened to
|
|
209
|
+
// evaluate this file first, and the editor failed to start with "Cannot access
|
|
210
|
+
// 'coverageSource' before initialization" (measured on `bunnymark`,
|
|
211
|
+
// 2026-09-17, when an import-sort reshuffle changed which file the graph
|
|
212
|
+
// reached first). `ingestCoverageReport` is a FUNCTION DECLARATION: ESM
|
|
213
|
+
// initializes it at instantiation, before any body runs, so reading it across
|
|
214
|
+
// the cycle is order-independent by construction.
|
|
215
|
+
import { ingestCoverageReport } from './mount-coverage';
|
|
216
|
+
import { exitActiveIngest } from './unmount-ingest-root';
|
|
217
|
+
|
|
218
|
+
registerLiveSession({
|
|
219
|
+
id: 'ingest',
|
|
220
|
+
priority: 1,
|
|
221
|
+
mounted: isIngestActive,
|
|
222
|
+
playing: () => ingestPlaying() || deferredIngestPlayActive(),
|
|
223
|
+
stop: exitActiveIngest,
|
|
224
|
+
instanceContainer: () => null,
|
|
225
|
+
frameCanvas: ingestFrameCanvas,
|
|
226
|
+
snapshotFrame: () => armIngestFrameSnapshot(),
|
|
227
|
+
// S-2 (the SimCity ledger): an ingest session OWNS its mount, and the
|
|
228
|
+
// first-party play boot cannot share it — `play`/`stop`/`pause`/`resume`/
|
|
229
|
+
// `step` drive the ingested game's own loop (`ingest-play-commands.ts`).
|
|
230
|
+
command: (cmd) => {
|
|
231
|
+
const control = isIngestActive() ? getIngestPlayControl() : null;
|
|
232
|
+
return control
|
|
233
|
+
? handleIngestPlayControl(control, cmd.type, { stopEndsRun: deferredIngestPlayActive() })
|
|
234
|
+
: null;
|
|
235
|
+
},
|
|
236
|
+
scenes: activeContractScenes,
|
|
237
|
+
surface: activeIngestKind,
|
|
238
|
+
coverage: ingestCoverageReport,
|
|
239
|
+
// An ingested native-React game renders as a DOM layer under the host
|
|
240
|
+
// React — no scene graph, no renderer to introspect — and authoring is
|
|
241
|
+
// deliberately off for react ingests (the editor never writes JSX back into
|
|
242
|
+
// an unmodified game). The game is hosted, sized and disposed; play it in
|
|
243
|
+
// the Game tab.
|
|
244
|
+
authoringRefusal: () =>
|
|
245
|
+
_active?.kind === 'dom'
|
|
246
|
+
? 'This is an ingested native-React game: its components render directly as a DOM layer ' +
|
|
247
|
+
'under the host React — there is no scene graph or renderer to introspect, and authoring ' +
|
|
248
|
+
'is deliberately off for react ingests (the editor never writes JSX back into an ' +
|
|
249
|
+
'unmodified game). The game is hosted, sized, and disposed; play it in the Game tab.'
|
|
250
|
+
: null,
|
|
251
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The BOUND scenes projection of whatever game is live in this session, or
|
|
3
|
+
* `null`.
|
|
4
|
+
*
|
|
5
|
+
* One question, one reader. The `open` verb's live half
|
|
6
|
+
* (`scene-live-open.ts`) needs the very object the mount installed — the same
|
|
7
|
+
* one the inspector's scene picker drives, carrying that mount's held-repaint
|
|
8
|
+
* seam — so it is resolved off the single ingest slot (`active-ingest.ts`),
|
|
9
|
+
* never rebuilt from the contract and never taken from a COMPOSITE adapter's
|
|
10
|
+
* `stories` pass-through (that is a fresh delegating object; the projection's
|
|
11
|
+
* `goToScene` does not survive it).
|
|
12
|
+
*
|
|
13
|
+
* The marker, not the session kind, is what decides: a plain `StoriesProvider`
|
|
14
|
+
* (a react world's design-time CSF states) is not a game's own screens and must
|
|
15
|
+
* not be navigated as though it were.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
type ContractScenesStories,
|
|
20
|
+
isContractScenesStories,
|
|
21
|
+
} from '../host/authoring/contract-scenes-stories';
|
|
22
|
+
import type { AuthoringAdapter } from '@volter/editor-project/adapter/authoring';
|
|
23
|
+
import { activeIngest } from './active-ingest';
|
|
24
|
+
|
|
25
|
+
export function activeContractScenes(): ContractScenesStories | null {
|
|
26
|
+
const active = activeIngest();
|
|
27
|
+
if (!active) return null;
|
|
28
|
+
// Every kind's own live authoring adapter — asked the same question, so a
|
|
29
|
+
// projection bound on a lane that does not have one today answers the day it
|
|
30
|
+
// does, without a second reader here.
|
|
31
|
+
const adapter: AuthoringAdapter | null | undefined = (() => {
|
|
32
|
+
switch (active.kind) {
|
|
33
|
+
case 'canvas':
|
|
34
|
+
return active.session.adapter;
|
|
35
|
+
case 'dom':
|
|
36
|
+
return active.session.adapter;
|
|
37
|
+
case 'three':
|
|
38
|
+
return active.session.mount?.authoring;
|
|
39
|
+
}
|
|
40
|
+
})();
|
|
41
|
+
const stories = adapter?.stories;
|
|
42
|
+
return isContractScenesStories(stories) ? stories : null;
|
|
43
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turns a readable canvas game's own DOM UI into an ordinary nested authoring
|
|
3
|
+
* surface. Detection stays at the mount boundary; once built, the editor sees
|
|
4
|
+
* only AuthoringAdapter/CompositeAuthoringAdapter currency.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { CompositeAuthoringAdapter } from '@volter/editor-core/authoring/composite-authoring-adapter';
|
|
8
|
+
import {
|
|
9
|
+
findCanvasUiElements,
|
|
10
|
+
findPrimaryCanvas,
|
|
11
|
+
type OverlayElement,
|
|
12
|
+
} from '../../host/coverage/capability-coverage';
|
|
13
|
+
import type { EditorShellStore } from '@volter/editor-core/editor-shell-store';
|
|
14
|
+
import { authoringJournal } from '../../host/history/json-history-resource';
|
|
15
|
+
import { sourceWriteBackendIfPrimed } from '@volter/editor-core/ui-source/tier-source-write-backend';
|
|
16
|
+
import { DomAuthoringAdapter, type DomElementLike } from '../../react/dom-authoring-adapter';
|
|
17
|
+
import {
|
|
18
|
+
type OidElementLike,
|
|
19
|
+
ReactRootAuthoringAdapter,
|
|
20
|
+
walkOidTree,
|
|
21
|
+
} from '../../react/react-world-authoring-adapter';
|
|
22
|
+
import type { AuthoringAdapter } from '@volter/editor-project/adapter';
|
|
23
|
+
import type { VgaiGameContract } from '@volter/editor-project/adapter/ingest/game-contract';
|
|
24
|
+
|
|
25
|
+
type ReadableDomRoot = OverlayElement & DomElementLike & { readonly ownerDocument?: Document };
|
|
26
|
+
|
|
27
|
+
function isReadableDomRoot(value: unknown): value is ReadableDomRoot {
|
|
28
|
+
const candidate = value as Partial<ReadableDomRoot> | null;
|
|
29
|
+
return (
|
|
30
|
+
candidate !== null &&
|
|
31
|
+
typeof candidate === 'object' &&
|
|
32
|
+
typeof candidate.tagName === 'string' &&
|
|
33
|
+
candidate.children !== undefined &&
|
|
34
|
+
typeof candidate.getBoundingClientRect === 'function'
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function iframeDocumentRoot(hostEl: HTMLElement): ReadableDomRoot | null {
|
|
39
|
+
try {
|
|
40
|
+
const iframe =
|
|
41
|
+
hostEl.tagName.toLowerCase() === 'iframe'
|
|
42
|
+
? (hostEl as HTMLIFrameElement)
|
|
43
|
+
: (hostEl.querySelector('iframe') as HTMLIFrameElement | null);
|
|
44
|
+
const body = iframe?.contentDocument?.body;
|
|
45
|
+
return isReadableDomRoot(body) ? body : null;
|
|
46
|
+
} catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function resolveGameDomRoot(
|
|
52
|
+
hostEl: HTMLElement,
|
|
53
|
+
declaredRoot: VgaiGameContract['root'] | undefined,
|
|
54
|
+
): ReadableDomRoot | null {
|
|
55
|
+
if (isReadableDomRoot(declaredRoot)) return declaredRoot;
|
|
56
|
+
return iframeDocumentRoot(hostEl) ?? (isReadableDomRoot(hostEl) ? hostEl : null);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function withMutationSubscription(
|
|
60
|
+
adapter: DomAuthoringAdapter | ReactRootAuthoringAdapter,
|
|
61
|
+
gameRoot: ReadableDomRoot,
|
|
62
|
+
): AuthoringAdapter {
|
|
63
|
+
const subscribe = (listener: () => void): (() => void) => {
|
|
64
|
+
const unsubscribe = adapter.subscribe(listener);
|
|
65
|
+
const Observer = gameRoot.ownerDocument?.defaultView?.MutationObserver;
|
|
66
|
+
if (!Observer) return unsubscribe;
|
|
67
|
+
const observer = new Observer(() => listener());
|
|
68
|
+
observer.observe(gameRoot as unknown as Node, {
|
|
69
|
+
childList: true,
|
|
70
|
+
subtree: true,
|
|
71
|
+
});
|
|
72
|
+
return () => {
|
|
73
|
+
observer.disconnect();
|
|
74
|
+
unsubscribe();
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
return new Proxy(adapter, {
|
|
78
|
+
get(target, property) {
|
|
79
|
+
if (property === 'subscribe') return subscribe;
|
|
80
|
+
const value = Reflect.get(target, property, target);
|
|
81
|
+
return typeof value === 'function' ? value.bind(target) : value;
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Which authoring adapter this mount's DOM UI gets — decided by ONE probe, the
|
|
88
|
+
* same probe and the same two outcomes `ingest-siblings.ts`'s
|
|
89
|
+
* `mountReactSibling` already uses for a react sibling layer: does the live DOM
|
|
90
|
+
* actually carry `data-oid` stamps?
|
|
91
|
+
*
|
|
92
|
+
* - **Stamped** ⇒ {@link ReactRootAuthoringAdapter} over the OID tree, with the
|
|
93
|
+
* dev-server source-write backend. Node ids are OID-derived, so the HUD's own
|
|
94
|
+
* JSX — the component the elements came from — is the subject, and a
|
|
95
|
+
* className/style/text edit prepares next-file text and commits it through
|
|
96
|
+
* `/__ui-source/write`. For a REPO-VENDORED game that endpoint's
|
|
97
|
+
* `writeEditableSource` routes the write through
|
|
98
|
+
* `server/vendored-lock-recorder.ts`, exactly as a three root's writes are
|
|
99
|
+
* routed — the choke point is the endpoint, not the adapter, so this lane
|
|
100
|
+
* inherits lock recording without knowing anything about vendoring.
|
|
101
|
+
* - **Unstamped** ⇒ {@link DomAuthoringAdapter} unchanged: structural ids
|
|
102
|
+
* over the live DOM and no write seam at all. That is the honest answer for
|
|
103
|
+
* DOM the stamping pipeline declined to instrument (a bundled/minified ingest,
|
|
104
|
+
* a foreign game excluded by identity) — never a write seam over source the
|
|
105
|
+
* editor cannot address.
|
|
106
|
+
*
|
|
107
|
+
* A PARTIALLY stamped tree counts as stamped: `walkOidTree` already skips
|
|
108
|
+
* untagged elements transparently and attaches their children to the nearest
|
|
109
|
+
* tagged ancestor, so this stays a single "any node at all?" question rather
|
|
110
|
+
* than a completeness one.
|
|
111
|
+
*
|
|
112
|
+
* The ROUTE gates the backend, not the adapter choice (`../ui-source/tier-
|
|
113
|
+
* source-write-backend.ts`): on a host that serves no `/__ui-source/*`
|
|
114
|
+
* recorder the OID lane still gives correct selection/inspection and reports
|
|
115
|
+
* its writes unavailable through `persistence.destination`.
|
|
116
|
+
*/
|
|
117
|
+
function domSurfaceAdapter(
|
|
118
|
+
liveUiRoot: DomElementLike & OidElementLike,
|
|
119
|
+
store: EditorShellStore,
|
|
120
|
+
/** The detected surface's own id — a held surface, so its journal is the
|
|
121
|
+
* world's and survives the ingest remounting (see the ownership block in
|
|
122
|
+
* `../history/json-history-resource.ts`). */
|
|
123
|
+
domUiJournalId: string,
|
|
124
|
+
): DomAuthoringAdapter | ReactRootAuthoringAdapter {
|
|
125
|
+
if (walkOidTree(liveUiRoot).nodes.size === 0) {
|
|
126
|
+
return new DomAuthoringAdapter(liveUiRoot, store, {
|
|
127
|
+
journal: authoringJournal(domUiJournalId),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
const writeBackend = sourceWriteBackendIfPrimed('An ingested game’s DOM UI surface');
|
|
131
|
+
return new ReactRootAuthoringAdapter(liveUiRoot, store, {
|
|
132
|
+
...(writeBackend ? { writeBackend } : {}),
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Preserve `primary` byte-for-byte when this mount has no readable canvas/UI
|
|
138
|
+
* split. Otherwise expose Canvas as the runtime surface and DOM UI as its
|
|
139
|
+
* nested document. The UI root's children are getters over live geometry, so
|
|
140
|
+
* menus that appear later enter the same surface without a mirror tree.
|
|
141
|
+
*/
|
|
142
|
+
export function withDetectedDomSurface(args: {
|
|
143
|
+
primary: AuthoringAdapter;
|
|
144
|
+
worldId: string;
|
|
145
|
+
hostEl: HTMLElement;
|
|
146
|
+
declaredRoot?: VgaiGameContract['root'];
|
|
147
|
+
store: EditorShellStore;
|
|
148
|
+
}): AuthoringAdapter {
|
|
149
|
+
const gameRoot = resolveGameDomRoot(args.hostEl, args.declaredRoot);
|
|
150
|
+
if (!gameRoot) return args.primary;
|
|
151
|
+
const canvas = findPrimaryCanvas(gameRoot);
|
|
152
|
+
if (!canvas || findCanvasUiElements(gameRoot, canvas).length === 0) return args.primary;
|
|
153
|
+
|
|
154
|
+
const domUiId = `${args.worldId}:dom-ui`;
|
|
155
|
+
// Composite world ids are editor identity and legitimately use `:`. A
|
|
156
|
+
// history resource id is a project-relative path and must not: conflating
|
|
157
|
+
// the two made ResourceRegistry parse `world:dom-ui/...` as a URL scheme and
|
|
158
|
+
// refuse every detected DOM sibling before the ingest could mount.
|
|
159
|
+
const domUiJournalId = `${args.worldId}/dom-ui`;
|
|
160
|
+
const liveUiRoot: DomElementLike & OidElementLike = {
|
|
161
|
+
tagName: 'vgai-dom-surface',
|
|
162
|
+
id: '',
|
|
163
|
+
className: '',
|
|
164
|
+
get children() {
|
|
165
|
+
const currentCanvas = findPrimaryCanvas(gameRoot);
|
|
166
|
+
return currentCanvas
|
|
167
|
+
? (findCanvasUiElements(gameRoot, currentCanvas) as DomElementLike[] & OidElementLike[])
|
|
168
|
+
: [];
|
|
169
|
+
},
|
|
170
|
+
// A synthetic container the host manufactured, not a rendered element —
|
|
171
|
+
// it carries no oid of its own, and `walkOidTree` transparently skips an
|
|
172
|
+
// untagged node, attaching the game's real HUD elements as tree roots.
|
|
173
|
+
getAttribute: () => null,
|
|
174
|
+
};
|
|
175
|
+
const dom = withMutationSubscription(
|
|
176
|
+
domSurfaceAdapter(liveUiRoot, args.store, domUiJournalId),
|
|
177
|
+
gameRoot,
|
|
178
|
+
);
|
|
179
|
+
const canvasId = `${args.worldId}:canvas`;
|
|
180
|
+
return new CompositeAuthoringAdapter([
|
|
181
|
+
{ worldId: canvasId, kind: 'canvas', adapter: args.primary },
|
|
182
|
+
{
|
|
183
|
+
worldId: domUiId,
|
|
184
|
+
kind: 'dom',
|
|
185
|
+
role: 'surface',
|
|
186
|
+
label: 'DOM UI',
|
|
187
|
+
parentRootId: canvasId,
|
|
188
|
+
adapter: dom,
|
|
189
|
+
},
|
|
190
|
+
]);
|
|
191
|
+
}
|