@volter/editor-game 0.5.68 → 0.5.69
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/NOTICE +7 -7
- package/contributions/autoplay.service.ts +1 -1
- package/contributions/build-profiles.document.tsx +1 -1
- package/contributions/build-progress.status.tsx +2 -2
- package/contributions/build.header.tsx +1 -1
- package/contributions/connection.status.tsx +1 -1
- package/contributions/coverage.service.ts +2 -2
- package/contributions/game-eval.command.ts +6 -6
- package/contributions/gameplay.command.ts +2 -2
- package/contributions/generations.status.tsx +1 -1
- package/contributions/ingest.service.ts +2 -2
- package/contributions/play.command.ts +6 -6
- package/contributions/react/react-inspector.service.ts +1 -1
- package/package.json +8 -8
- package/serving/index.ts +1 -1
- package/src/asset-budget/AssetBudgetPanel.tsx +1 -1
- package/src/asset-budget/asset-budget-model.ts +14 -14
- package/src/asset-budget/optimize-apply.ts +8 -8
- package/src/bridge/call.ts +3 -3
- package/src/bridge/dispatch.ts +12 -12
- package/src/bridge/screenshot.ts +6 -6
- package/src/build/BuildProfilesPanel.tsx +8 -8
- package/src/canvas/canvas-board/CanvasBoardDocument.tsx +2 -2
- package/src/canvas/canvas-board/canvas-component-board.ts +1 -1
- package/src/canvas/canvas-design-mount.ts +1 -1
- package/src/coverage/live-project-verbs.ts +3 -3
- package/src/coverage/native-system-coverage.ts +3 -3
- package/src/coverage/root-coverage.ts +2 -2
- package/src/coverage/session-coverage.ts +2 -2
- package/src/design-system-stories/InspectorNarrowBodies.stories.tsx +2 -2
- package/src/game-document/DevicePresetPicker.tsx +6 -6
- package/src/game-document/GameCaptureFrameButton.tsx +1 -1
- package/src/game-document/GameDocument.tsx +2 -2
- package/src/game-document/GamePanel.tsx +9 -9
- package/src/game-document/InstanceInspectorPicker.tsx +1 -1
- package/src/game-document/device-preview.ts +4 -4
- package/src/game-document/physics-debug.ts +2 -2
- package/src/generation/GenerationActivity.tsx +22 -22
- package/src/generation/GenerationGallery.css +79 -79
- package/src/generation/generation-documents.tsx +25 -25
- package/src/generation/generation-presentation.ts +1 -1
- package/src/host/adapter-reach.ts +1 -1
- package/src/host/adapter-runtime-bindings.ts +5 -5
- package/src/host/after-paint.ts +3 -3
- package/src/host/api/configurations.ts +1 -1
- package/src/host/authoring/canvas-runtime-recognition.ts +2 -2
- package/src/host/authoring/contract-hierarchy-authoring.ts +6 -6
- package/src/host/authoring/contract-scenes-stories.ts +5 -5
- package/src/host/authoring/ingest-data-writer.ts +1 -1
- package/src/host/authoring/ingest-source-persistence.ts +2 -2
- package/src/host/authoring/mount-isolated-pixi-screen.ts +1 -1
- package/src/host/authoring/pixi-authoring-adapter.ts +60 -4
- package/src/host/authoring/pixi-creatable-kinds.ts +18 -0
- package/src/host/authoring/pixi-live-write-target.ts +6 -5
- package/src/host/authoring/pixi-source-write-target.ts +5 -2
- package/src/host/authoring/pixi-structure-history.ts +3 -3
- package/src/host/binding-resolver.ts +6 -6
- package/src/host/browser-transpile.ts +10 -10
- package/src/host/canvas-preview-frames.ts +1 -1
- package/src/host/components/CameraAuthoringOverlay.tsx +14 -14
- package/src/host/components/HeaderTelemetry.tsx +11 -11
- package/src/host/components/PixiIsolationSceneContent.tsx +2 -2
- package/src/host/components/ResolutionPicker.tsx +4 -4
- package/src/host/components/scene-document.tsx +3 -3
- package/src/host/components/world-root-stage-binding.tsx +1 -1
- package/src/host/components/world-root-stage.ts +1 -1
- package/src/host/coverage/authoring-read-probe.ts +1 -1
- package/src/host/coverage/capability-coverage.ts +22 -22
- package/src/host/coverage/game-contract-seam-evidence.ts +8 -8
- package/src/host/coverage/project-verb-coverage.ts +1 -1
- package/src/host/coverage/system-adapter-coverage.ts +19 -6
- package/src/host/game-location-guard.ts +3 -3
- package/src/host/game-module-access.ts +6 -6
- package/src/host/game-realm-page.ts +3 -3
- package/src/host/game-realm-reclaim.ts +1 -1
- package/src/host/gameplay-recording.ts +5 -5
- package/src/host/gated-globals.ts +13 -13
- package/src/host/ingest/registry.ts +14 -14
- package/src/host/play-control-hook.ts +1 -1
- package/src/host/playwright-shim.ts +2 -2
- package/src/host/projection/pixi.ts +2 -2
- package/src/host/react-mount-runtime.ts +1 -1
- package/src/host/roots/canvas-root.tsx +2 -2
- package/src/host/roots/r3f-root.tsx +1 -1
- package/src/host/scene-view-drawability.ts +1 -1
- package/src/host/sdk/tools.ts +1 -1
- package/src/host/staged-projects.ts +1 -1
- package/src/host/stories/story-pixi-preview.ts +1 -1
- package/src/host/stories/story-three-preview.ts +1 -1
- package/src/host/three-ingest-runtime.ts +1 -1
- package/src/ingest/active-ingest.ts +1 -1
- package/src/ingest/authoring/ingest-dom-surface-authoring.ts +4 -4
- package/src/ingest/authoring/ingest-root-adapter.ts +11 -11
- package/src/ingest/capture-wait-report.ts +4 -4
- package/src/ingest/discovery-public-ingest.ts +4 -4
- package/src/ingest/dom-stub-mark.ts +1 -1
- package/src/ingest/entry-load.ts +1 -1
- package/src/ingest/game-contract-realm.ts +3 -3
- package/src/ingest/game-pointer-lock.ts +1 -1
- package/src/ingest/ingest-canvas-scene-document.tsx +2 -2
- package/src/ingest/ingest-evidence-hook.ts +6 -6
- package/src/ingest/ingest-frame-snapshot.ts +2 -2
- package/src/ingest/ingest-play-commands.ts +1 -1
- package/src/ingest/ingest-play-control.ts +11 -11
- package/src/ingest/ingest-render-debug.ts +5 -5
- package/src/ingest/ingest-siblings.ts +3 -3
- package/src/ingest/module-mode.ts +2 -2
- package/src/ingest/mount-canvas-ingest-root.ts +10 -10
- package/src/ingest/mount-coverage.ts +3 -3
- package/src/ingest/mount-dom-ingest-root.ts +7 -7
- package/src/ingest/mount-ingest-root.ts +10 -10
- package/src/ingest/mount-three-ingest-root.ts +5 -5
- package/src/ingest/resolve-three.ts +2 -2
- package/src/ingest/served-bundle.ts +6 -6
- package/src/ingest/served-html-boot.ts +8 -8
- package/src/ingest/surface-dom.ts +9 -9
- package/src/ingest/surface-three.ts +1 -1
- package/src/ingest/types.ts +1 -1
- package/src/ingest/unmount-ingest-root.ts +3 -3
- package/src/network/NetworkInspectorPanel.tsx +2 -2
- package/src/play/play-boot-stall.ts +1 -1
- package/src/play/play-mode.ts +31 -30
- package/src/play/play-recording.ts +12 -12
- package/src/play-bar/PlayBar.tsx +8 -8
- package/src/play-bar/PlayerCountPicker.tsx +3 -3
- package/src/profiler/FrameDebuggerPanel.tsx +2 -2
- package/src/profiler/PerformancePanel.tsx +4 -4
- package/src/react/design-time-react-mount.ts +5 -5
- package/src/react/dom-authoring-adapter.ts +3 -3
- package/src/react/react-inspector-section.tsx +2 -2
- package/src/react/react-world-authoring-adapter.ts +20 -20
- package/src/react/story-documents/story-documents.tsx +4 -4
- package/src/react/ui-component-board.ts +1 -1
- package/src/runtime/adapter/audio-meter.ts +1 -1
- package/src/runtime/adapter/ingest/contract-debug-adapter.ts +4 -4
- package/src/runtime/adapter/ingest/contract-system-adapters.ts +9 -9
- package/src/runtime/adapter/ingest/merge-debug-adapters.ts +2 -2
- package/src/runtime/adapter/ingest/observation-debug-adapter.ts +1 -1
- package/src/runtime/adapter/native-debug-module.ts +5 -5
- package/src/runtime/audio/pose-guard.ts +1 -1
- package/src/runtime/core/game-scoped-slot.ts +1 -1
- package/src/runtime/create-runtime.ts +14 -14
- package/src/runtime/debug-bridge.ts +25 -25
- package/src/runtime/debug-registry.ts +17 -17
- package/src/runtime/dev/instruments.ts +1 -1
- package/src/runtime/dev/performance-profiler.ts +2 -2
- package/src/runtime/dev/register-render-vitals.ts +2 -2
- package/src/runtime/dev/static-batch-advisor.ts +3 -3
- package/src/runtime/dev-build.ts +3 -3
- package/src/runtime/game.ts +5 -5
- package/src/runtime/input-router.ts +1 -1
- package/src/runtime/mount-manifest.ts +9 -9
- package/src/runtime/pixi/authoring.ts +1 -1
- package/src/runtime/pixi/ingest.ts +1 -1
- package/src/runtime/presentation.ts +1 -1
- package/src/runtime/render-control.ts +21 -21
- package/src/runtime/render-seed.ts +3 -3
- package/src/runtime/run-ticks-settled.ts +1 -1
- package/src/services/game-audio.ts +3 -3
- package/src/services/game-network.ts +4 -4
- package/src/three/authoring/model-asset-inspector-section.css +15 -15
- package/src/three/authoring/model-asset-inspector-section.tsx +7 -7
- package/src/three/authoring/r3f-design-session.ts +10 -10
- package/src/three/authoring/r3f-source-authoring-adapter.ts +5 -5
- package/src/three/authoring/three-authoring-adapter.ts +5 -4
- package/src/three/authoring/typed-three-inspector.ts +1 -1
- package/src/three/story-documents/three-story-documents.tsx +3 -3
- package/src/three/three-board/board-scene.ts +9 -9
- package/src/three/three-board/three-component-board.ts +1 -1
|
@@ -247,7 +247,7 @@ export interface PixiAuthoringOptions {
|
|
|
247
247
|
* `pickable` at all — a stage with no mapped surface cannot answer where a
|
|
248
248
|
* client point landed, and guessing one would fabricate a hit.
|
|
249
249
|
*
|
|
250
|
-
* When the element carries `data-
|
|
250
|
+
* When the element carries `data-volter-stage-width`/`-height`, those are the
|
|
251
251
|
* stage's LOGICAL size and the mapping divides the measured rect by them —
|
|
252
252
|
* which is what lets a design surface presented through the shared pan/zoom
|
|
253
253
|
* camera (a CSS scale) pick correctly. Without them the mapping is 1:1,
|
|
@@ -413,6 +413,11 @@ function defaultLabelFor(kind: string): string {
|
|
|
413
413
|
* `pixi` is THIS SURFACE's namespace ({@link PixiAuthoringOptions.pixi}): a
|
|
414
414
|
* node built from another graph's classes is a foreign object in the world's
|
|
415
415
|
* own display list, and the world's renderer is the one that has to draw it. */
|
|
416
|
+
/** A ParticleContainer, from whichever copy of Pixi made it: it adds particles, never children. */
|
|
417
|
+
function isParticleContainer(object: Container | null | undefined): boolean {
|
|
418
|
+
return !!object && typeof (object as { addParticle?: unknown }).addParticle === 'function';
|
|
419
|
+
}
|
|
420
|
+
|
|
416
421
|
function createDisplayObject(pixi: CanvasPixiNamespace, kind: string): Container | null {
|
|
417
422
|
switch (kind) {
|
|
418
423
|
case 'container':
|
|
@@ -439,6 +444,18 @@ function createDisplayObject(pixi: CanvasPixiNamespace, kind: string): Container
|
|
|
439
444
|
return new pixi.BitmapText({ text: 'BitmapText', style: { fill: 0xffffff, fontSize: 24 } });
|
|
440
445
|
case 'htmlText':
|
|
441
446
|
return new pixi.HTMLText({ text: 'HTMLText', style: { fill: 0xffffff, fontSize: 24 } });
|
|
447
|
+
case 'meshPlane': {
|
|
448
|
+
const plane = new pixi.MeshPlane({ texture: pixi.Texture.WHITE });
|
|
449
|
+
plane.setSize(100, 100);
|
|
450
|
+
return plane;
|
|
451
|
+
}
|
|
452
|
+
case 'perspectiveMesh': {
|
|
453
|
+
const mesh = new pixi.PerspectiveMesh({ texture: pixi.Texture.WHITE });
|
|
454
|
+
mesh.setSize(100, 100);
|
|
455
|
+
return mesh;
|
|
456
|
+
}
|
|
457
|
+
case 'particleContainer':
|
|
458
|
+
return new pixi.ParticleContainer();
|
|
442
459
|
default:
|
|
443
460
|
return null;
|
|
444
461
|
}
|
|
@@ -589,7 +606,18 @@ function transformForBoxPatch(
|
|
|
589
606
|
scale: [...session.transform.scale],
|
|
590
607
|
};
|
|
591
608
|
const rotate = patch['rotate'];
|
|
592
|
-
if (rotate !== undefined)
|
|
609
|
+
if (rotate !== undefined) {
|
|
610
|
+
let turn = localTurn(session, rotate);
|
|
611
|
+
const step = patch['rotationStep'];
|
|
612
|
+
if (step !== undefined && step > 0) {
|
|
613
|
+
// Configure Snap's absolute step lands the node's own rotation on the step from the offset.
|
|
614
|
+
const [, , z, w] = session.transform.rotation;
|
|
615
|
+
const start = (2 * Math.atan2(z, w) * 180) / Math.PI;
|
|
616
|
+
const offset = patch['rotationOffset'] ?? 0;
|
|
617
|
+
turn = Math.round((start + turn - offset) / step) * step + offset - start;
|
|
618
|
+
}
|
|
619
|
+
next.rotation = rotateTransformZ(session.transform, turn);
|
|
620
|
+
}
|
|
593
621
|
const width = patch['width'];
|
|
594
622
|
if (width !== undefined && session.rect.width > 0) {
|
|
595
623
|
next.scale[0] = session.transform.scale[0] * Math.max(0.0001, width / session.rect.width);
|
|
@@ -817,10 +845,38 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
|
|
|
817
845
|
const structure = this.target.structure ?? this.liveStructure;
|
|
818
846
|
// A placed creation reaches the structure in the new node's PARENT's own space: the target
|
|
819
847
|
// writes `x`/`y` as authored, and only this adapter knows the frame `rects` answer in.
|
|
848
|
+
// A ParticleContainer holds particles its code adds, never display objects: Pixi's `addChild`
|
|
849
|
+
// on one throws, so nothing is created or moved into it.
|
|
850
|
+
const holdsNoChildren = (parentId: string | null | undefined): boolean =>
|
|
851
|
+
!!parentId && isParticleContainer(this.projector.object(parentId));
|
|
852
|
+
const noChildren = 'a ParticleContainer holds particles added with addParticle, not display objects.';
|
|
820
853
|
this.structure = {
|
|
821
854
|
...structure,
|
|
822
855
|
create: (kind, parentId, at) =>
|
|
823
|
-
|
|
856
|
+
holdsNoChildren(parentId)
|
|
857
|
+
? { id: '', ack: this.refuseStructure(noChildren) }
|
|
858
|
+
: structure.create(kind, parentId, at ? this.pointInParent(parentId ?? null, at) : undefined),
|
|
859
|
+
...(structure.reparent
|
|
860
|
+
? {
|
|
861
|
+
reparent: (id: string, newParentId: string | null) =>
|
|
862
|
+
holdsNoChildren(newParentId) ? this.refuseStructure(noChildren) : structure.reparent!(id, newParentId),
|
|
863
|
+
}
|
|
864
|
+
: {}),
|
|
865
|
+
...(structure.paste
|
|
866
|
+
? {
|
|
867
|
+
paste: (parentId: string | null) =>
|
|
868
|
+
holdsNoChildren(parentId) ? this.refuseStructure(noChildren) : structure.paste!(parentId),
|
|
869
|
+
}
|
|
870
|
+
: {}),
|
|
871
|
+
...(structure.canPaste
|
|
872
|
+
? { canPaste: (parentId: string | null) => !holdsNoChildren(parentId) && structure.canPaste!(parentId) }
|
|
873
|
+
: {}),
|
|
874
|
+
...(structure.creatableKinds
|
|
875
|
+
? {
|
|
876
|
+
creatableKinds: (parentId: string | null) =>
|
|
877
|
+
holdsNoChildren(parentId) ? [] : structure.creatableKinds!(parentId),
|
|
878
|
+
}
|
|
879
|
+
: {}),
|
|
824
880
|
};
|
|
825
881
|
this.assetDrop =
|
|
826
882
|
this.target.assetDrop ??
|
|
@@ -1039,7 +1095,7 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
|
|
|
1039
1095
|
private containerFor(parentId: string | null): Container | null {
|
|
1040
1096
|
if (!parentId) return this.root;
|
|
1041
1097
|
const object = this.projector.object(parentId);
|
|
1042
|
-
if (!object || typeof object.addChild !== 'function') return null;
|
|
1098
|
+
if (!object || typeof object.addChild !== 'function' || isParticleContainer(object)) return null;
|
|
1043
1099
|
return object;
|
|
1044
1100
|
}
|
|
1045
1101
|
|
|
@@ -59,4 +59,22 @@ export const PIXI_CREATABLE_KINDS: readonly CreatableKind[] = [
|
|
|
59
59
|
extends: 'container',
|
|
60
60
|
description: 'Draws vector shapes (rectangles, circles, paths) from its draw callback.',
|
|
61
61
|
},
|
|
62
|
+
{
|
|
63
|
+
kind: 'meshPlane',
|
|
64
|
+
label: 'MeshPlane',
|
|
65
|
+
extends: 'container',
|
|
66
|
+
description: 'A textured grid of vertices that can be bent and deformed.',
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
kind: 'perspectiveMesh',
|
|
70
|
+
label: 'PerspectiveMesh',
|
|
71
|
+
extends: 'meshPlane',
|
|
72
|
+
description: 'A texture drawn in perspective between four corner points.',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
kind: 'particleContainer',
|
|
76
|
+
label: 'ParticleContainer',
|
|
77
|
+
extends: 'container',
|
|
78
|
+
description: 'Draws many lightweight particles in one batch, added from code with addParticle.',
|
|
79
|
+
},
|
|
62
80
|
];
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
* moved, and the persistence parameter is what this unit added.
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
|
+
import { physicsOwnerOf } from '@volter/editor-project/adapter';
|
|
33
34
|
import type {
|
|
34
35
|
AuthoringAdapter2D,
|
|
35
36
|
Overlay2D,
|
|
@@ -554,7 +555,7 @@ export function createLiveCanvasWriteTarget(
|
|
|
554
555
|
* `literalsFor` is what a rendering one needs. A revert or an apply is a
|
|
555
556
|
* deliberate act with somewhere to await, so it must not silently do nothing
|
|
556
557
|
* merely because nothing happened to have described this node first (an agent
|
|
557
|
-
* driving the provider through `
|
|
558
|
+
* driving the provider through `volter-game-editor eval` never does).
|
|
558
559
|
*/
|
|
559
560
|
const literalsNow = async (
|
|
560
561
|
id: string,
|
|
@@ -807,7 +808,7 @@ export function createLiveCanvasWriteTarget(
|
|
|
807
808
|
beginTransformEdit(id: string): void {
|
|
808
809
|
historyResource?.assertCanMutate();
|
|
809
810
|
const display = a2d.displayObject(id);
|
|
810
|
-
if (display && physics
|
|
811
|
+
if (display && physicsOwnerOf(physics, display) === 'physics') physics.freeze(display);
|
|
811
812
|
editStartState = captureHistoryState();
|
|
812
813
|
editBaseline = persist ? (a2d.getTransform(id) ?? undefined) : undefined;
|
|
813
814
|
editMovedOrigin = false;
|
|
@@ -816,7 +817,7 @@ export function createLiveCanvasWriteTarget(
|
|
|
816
817
|
writeTransform(id: string, next: Transform2DValue): void {
|
|
817
818
|
historyResource?.assertCanMutate();
|
|
818
819
|
const display = a2d.displayObject(id);
|
|
819
|
-
if (display && physics
|
|
820
|
+
if (display && physicsOwnerOf(physics, display) === 'physics') {
|
|
820
821
|
physics.commit(display, next.position, next.rotation);
|
|
821
822
|
}
|
|
822
823
|
a2d.setTransform(id, next);
|
|
@@ -829,7 +830,7 @@ export function createLiveCanvasWriteTarget(
|
|
|
829
830
|
// source write is attempted.
|
|
830
831
|
endTransformEdit(id: string): void | Promise<WriteAck> {
|
|
831
832
|
const display = a2d.displayObject(id);
|
|
832
|
-
if (display && physics
|
|
833
|
+
if (display && physicsOwnerOf(physics, display) === 'physics') physics.unfreeze(display);
|
|
833
834
|
const label = `Transform ${labelOf(id)}`;
|
|
834
835
|
const before = editStartState;
|
|
835
836
|
const baseline = editBaseline;
|
|
@@ -907,7 +908,7 @@ export function createLiveCanvasWriteTarget(
|
|
|
907
908
|
* channel a refused transform does: they asked for source writes, so each
|
|
908
909
|
* one that did not land matters individually. Unarmed, the first structural
|
|
909
910
|
* edit of the session raises the warning that reaches the product's own
|
|
910
|
-
* doors (`
|
|
911
|
+
* doors (`volter-game-editor status`, the session journal, one ack to clear) — a whole
|
|
911
912
|
* class of this author's edits will not be saved, which is exactly what an
|
|
912
913
|
* unresolved warning is for — and every later op logs, because repeating an
|
|
913
914
|
* ack-requiring entry per created node would flood the set with the lane
|
|
@@ -119,12 +119,15 @@ const CREATE_SNIPPETS: Readonly<Record<string, string>> = {
|
|
|
119
119
|
nineSliceSprite: '<pixiNineSliceSprite label="NineSliceSprite" texture={Texture.WHITE} width={100} height={100} />',
|
|
120
120
|
bitmapText: '<pixiBitmapText label="BitmapText" text="BitmapText" />',
|
|
121
121
|
htmlText: '<pixiHTMLText label="HTMLText" text="HTMLText" />',
|
|
122
|
+
meshPlane: '<pixiMeshPlane label="MeshPlane" texture={Texture.WHITE} width={100} height={100} />',
|
|
123
|
+
perspectiveMesh: '<pixiPerspectiveMesh label="PerspectiveMesh" texture={Texture.WHITE} width={100} height={100} />',
|
|
124
|
+
particleContainer: '<pixiParticleContainer label="ParticleContainer" />',
|
|
122
125
|
};
|
|
123
126
|
/** A kind whose snippet names Pixi's `Texture`, which the file must then import. */
|
|
124
|
-
const TEXTURE_SNIPPET_KINDS = new Set(['animatedSprite', 'tilingSprite', 'nineSliceSprite']);
|
|
127
|
+
const TEXTURE_SNIPPET_KINDS = new Set(['animatedSprite', 'tilingSprite', 'nineSliceSprite', 'meshPlane', 'perspectiveMesh']);
|
|
125
128
|
|
|
126
129
|
function isHiddenProp(name: string): boolean {
|
|
127
|
-
return HIDDEN_PROPS.has(name) || /^on[A-Z]/.test(name) || name.startsWith('
|
|
130
|
+
return HIDDEN_PROPS.has(name) || /^on[A-Z]/.test(name) || name.startsWith('__volter');
|
|
128
131
|
}
|
|
129
132
|
|
|
130
133
|
/** Present a source identifier the way native engine inspectors present
|
|
@@ -42,7 +42,7 @@ const ROOT_UID = '#root';
|
|
|
42
42
|
* `AuthoringAdapter2D` uses for `__authId`, for the same reason (there is no
|
|
43
43
|
* side table that survives the object outliving one walk). */
|
|
44
44
|
interface StructureStamped {
|
|
45
|
-
|
|
45
|
+
__volterStructUid?: string;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/** A node's own local pose, as plain numbers. Recorded because `reparent`
|
|
@@ -185,10 +185,10 @@ export class CanvasStructureHistory {
|
|
|
185
185
|
private uidOf(object: Container): string {
|
|
186
186
|
if (object === this.root) return ROOT_UID;
|
|
187
187
|
const stamped = object as Container & StructureStamped;
|
|
188
|
-
const existing = stamped.
|
|
188
|
+
const existing = stamped.__volterStructUid;
|
|
189
189
|
if (existing && this.byUid.get(existing) === object) return existing;
|
|
190
190
|
const uid = existing ?? `s${crypto.randomUUID()}`;
|
|
191
|
-
stamped.
|
|
191
|
+
stamped.__volterStructUid = uid;
|
|
192
192
|
this.byUid.set(uid, object);
|
|
193
193
|
return uid;
|
|
194
194
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* A game is a native program plus a declaration of itself; the editor is a
|
|
6
6
|
* universal client of that declaration. This module is where the declaration
|
|
7
|
-
* is READ: the manifest root, the project's parsed `
|
|
7
|
+
* is READ: the manifest root, the project's parsed `volter.adapter.ts`, and the
|
|
8
8
|
* entry module's own static surface, resolved against ONE realm
|
|
9
9
|
* (`realm-services.ts` — dev or packaged).
|
|
10
10
|
*
|
|
@@ -85,7 +85,7 @@ export interface ResolveRootBindingOptions {
|
|
|
85
85
|
* Resolve one manifest root against one realm.
|
|
86
86
|
*
|
|
87
87
|
* `adapterDef` is an INPUT, never something this function goes and fetches:
|
|
88
|
-
* `
|
|
88
|
+
* `volter.adapter.ts` loading stays in `project-adapter.ts`, and the binding is
|
|
89
89
|
* the hand-off's shape rather than a second loader.
|
|
90
90
|
*/
|
|
91
91
|
export async function resolveRootBinding(
|
|
@@ -252,7 +252,7 @@ function rejectCapturedIngestAsRootAdapter(root: ResolvedAdapterRoot): never {
|
|
|
252
252
|
export interface ResolveAllRootEntriesOptions {
|
|
253
253
|
/**
|
|
254
254
|
* Remount the named region's entrypoint at this swap-slot key. Only that
|
|
255
|
-
* world's entry URL carries the override; siblings share `?
|
|
255
|
+
* world's entry URL carries the override; siblings share `?volter-mount=`
|
|
256
256
|
* alone so the module graph stays one instance.
|
|
257
257
|
*/
|
|
258
258
|
readonly selectionOverride?:
|
|
@@ -276,7 +276,7 @@ interface ResolvedComposition {
|
|
|
276
276
|
* resolvers would give each root its own generation, which is the defect.
|
|
277
277
|
*
|
|
278
278
|
* The COMPOSITION level is likewise where the project's two ambient inputs are
|
|
279
|
-
* fetched — the realm's services and the parsed `
|
|
279
|
+
* fetched — the realm's services and the parsed `volter.adapter.ts` — because
|
|
280
280
|
* both are properties of the open project rather than of any one root, and
|
|
281
281
|
* asking once per composition is what keeps every root of a game bound to the
|
|
282
282
|
* same declaration. `resolveRootBinding` itself still fetches neither: it is
|
|
@@ -302,7 +302,7 @@ async function resolveComposition(
|
|
|
302
302
|
out.push({ world, resolved });
|
|
303
303
|
}
|
|
304
304
|
// Stringified because that is the form the id travels in everywhere else:
|
|
305
|
-
// the entry url's `?
|
|
305
|
+
// the entry url's `?volter-mount=` value, and therefore the key
|
|
306
306
|
// `gated-globals.ts` reads back off a module url.
|
|
307
307
|
return { roots: out, mountId: String(epoch) };
|
|
308
308
|
}
|
|
@@ -351,7 +351,7 @@ export async function resolveAllRoots(
|
|
|
351
351
|
*
|
|
352
352
|
* It also returns this composition's `mountId` — the identity the whole
|
|
353
353
|
* isolation layer is keyed by. It is minted here (one epoch per composition),
|
|
354
|
-
* rides every project module url as `?
|
|
354
|
+
* rides every project module url as `?volter-mount=`, and is what
|
|
355
355
|
* `gated-globals.ts` resolves a realm and an input gate under.
|
|
356
356
|
*
|
|
357
357
|
* It does NOT return the entry modules' harvested `debug`/`systems` bindings.
|
|
@@ -174,7 +174,7 @@ export function registerRuntimeModules(modules: Record<string, ModuleNamespace>)
|
|
|
174
174
|
}
|
|
175
175
|
// Exposed on globalThis so shim modules (which run in their own module scope)
|
|
176
176
|
// can reach the live namespaces by specifier.
|
|
177
|
-
(globalThis as Record<string, unknown>)['
|
|
177
|
+
(globalThis as Record<string, unknown>)['__volterModules'] = Object.fromEntries(runtimeModules);
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
/**
|
|
@@ -259,7 +259,7 @@ function loadRuntimeModule(spec: string): Promise<void> {
|
|
|
259
259
|
|
|
260
260
|
/**
|
|
261
261
|
* The call-time half of a literal dynamic import in project code. The rewrite
|
|
262
|
-
* turns `import('tone')` into `globalThis.
|
|
262
|
+
* turns `import('tone')` into `globalThis.__volterImportRuntimeModule('tone')`,
|
|
263
263
|
* so the load — and the wall, if the browser editor does not ship it — happens
|
|
264
264
|
* exactly when the project's own code executes the import, never earlier.
|
|
265
265
|
*/
|
|
@@ -267,7 +267,7 @@ export async function importRuntimeModule(spec: string): Promise<ModuleNamespace
|
|
|
267
267
|
if (!runtimeModules.has(spec)) await loadRuntimeModule(spec);
|
|
268
268
|
return runtimeModules.get(spec) as ModuleNamespace;
|
|
269
269
|
}
|
|
270
|
-
(globalThis as Record<string, unknown>)['
|
|
270
|
+
(globalThis as Record<string, unknown>)['__volterImportRuntimeModule'] = importRuntimeModule;
|
|
271
271
|
|
|
272
272
|
/**
|
|
273
273
|
* Bring every static bare import live, then rewrite by the lexer's own byte
|
|
@@ -304,7 +304,7 @@ export async function resolveAndRewriteBareImports(code: string): Promise<string
|
|
|
304
304
|
out = `${out.slice(0, record.s)}${shimUrlFor(record.n)}${out.slice(record.e)}`;
|
|
305
305
|
} else if (record.d >= 0) {
|
|
306
306
|
// Literal dynamic import: swap the whole `import(...)` expression.
|
|
307
|
-
out = `${out.slice(0, record.ss)}globalThis.
|
|
307
|
+
out = `${out.slice(0, record.ss)}globalThis.__volterImportRuntimeModule(${JSON.stringify(
|
|
308
308
|
record.n,
|
|
309
309
|
)})${out.slice(record.se)}`;
|
|
310
310
|
}
|
|
@@ -379,7 +379,7 @@ function shimUrlFor(spec: string): string {
|
|
|
379
379
|
// `registerRuntimeModules` user) — the same wall, said the same way.
|
|
380
380
|
if (!ns) throw unregisteredRuntimeModules([spec]);
|
|
381
381
|
|
|
382
|
-
const lines = [`const __m = globalThis.
|
|
382
|
+
const lines = [`const __m = globalThis.__volterModules[${JSON.stringify(spec)}];`];
|
|
383
383
|
for (const key of Object.keys(ns)) {
|
|
384
384
|
if (key === 'default' || !IDENT_RE.test(key) || RESERVED_WORDS.has(key)) continue;
|
|
385
385
|
lines.push(`export const ${key} = __m[${JSON.stringify(key)}];`);
|
|
@@ -432,7 +432,7 @@ export async function transpileToModuleUrl(
|
|
|
432
432
|
|
|
433
433
|
/**
|
|
434
434
|
* Prepend the game-globals lexical shadow to project source — the browser-side
|
|
435
|
-
* analogue of the dev server's `
|
|
435
|
+
* analogue of the dev server's `volter-game-globals` Vite plugin
|
|
436
436
|
* (`server/dev.ts` + `shouldShadowGameGlobals`). The server shadows per-module
|
|
437
437
|
* because Vite transforms EVERY module indiscriminately and must be told which
|
|
438
438
|
* ones are game code; the browser has no such firehose — {@link importFromSource}
|
|
@@ -541,7 +541,7 @@ export function loaderFor(path: string): esbuild.Loader {
|
|
|
541
541
|
function resolveServedModulePath(importer: string, specifier: string): string {
|
|
542
542
|
if (specifier.startsWith('/')) return specifier;
|
|
543
543
|
if (/^(?:https?:|blob:|data:)/.test(specifier)) return specifier;
|
|
544
|
-
const origin = globalThis.location?.origin ?? 'https://
|
|
544
|
+
const origin = globalThis.location?.origin ?? 'https://volter.invalid';
|
|
545
545
|
const resolved = new URL(specifier, new URL(importer, origin));
|
|
546
546
|
return resolved.origin === origin
|
|
547
547
|
? `${resolved.pathname}${resolved.search}${resolved.hash}`
|
|
@@ -572,7 +572,7 @@ export async function bundleServedModule(
|
|
|
572
572
|
define: PROJECT_MODULE_DEFINES,
|
|
573
573
|
plugins: [
|
|
574
574
|
{
|
|
575
|
-
name: '
|
|
575
|
+
name: 'volter-served-module',
|
|
576
576
|
setup(build) {
|
|
577
577
|
build.onResolve({ filter: /.*/ }, (args) => {
|
|
578
578
|
if (
|
|
@@ -586,9 +586,9 @@ export async function bundleServedModule(
|
|
|
586
586
|
args.kind === 'entry-point'
|
|
587
587
|
? args.path
|
|
588
588
|
: resolveServedModulePath(args.importer, args.path);
|
|
589
|
-
return { path, namespace: '
|
|
589
|
+
return { path, namespace: 'volter-served' };
|
|
590
590
|
});
|
|
591
|
-
build.onLoad({ filter: /.*/, namespace: '
|
|
591
|
+
build.onLoad({ filter: /.*/, namespace: 'volter-served' }, async (args) => {
|
|
592
592
|
const response = await fetchImpl(args.path);
|
|
593
593
|
if (!response.ok) {
|
|
594
594
|
throw new Error(
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* five callers never had a story in hand (`authoring/canvas-design-mount.ts`,
|
|
18
18
|
* `authoring/pixi-still-presentation.ts`, `components/world-root-stage.ts`,
|
|
19
19
|
* `live-canvas-frame.ts`, and the ingest lane's canvas mount); moving it out is
|
|
20
|
-
* the part of the CSF estate's exit to `@
|
|
20
|
+
* the part of the CSF estate's exit to `@volter/game` (WORK.md §The open-source
|
|
21
21
|
* launch, phase 1 unit 3) that does not wait on anything else.
|
|
22
22
|
*/
|
|
23
23
|
|
|
@@ -56,13 +56,13 @@ function CameraPreview({ previewRef }: CameraAuthoringOverlayProps) {
|
|
|
56
56
|
: 16 / 9;
|
|
57
57
|
|
|
58
58
|
return (
|
|
59
|
-
<div className="
|
|
60
|
-
<Inline className="
|
|
59
|
+
<div className="volter-camera-preview" data-testid="camera-preview">
|
|
60
|
+
<Inline className="volter-camera-preview-header" gap={4} align="center">
|
|
61
61
|
<EditorIcon icon={faCamera} size="sm" />
|
|
62
62
|
<Text
|
|
63
63
|
variant="label"
|
|
64
64
|
truncate
|
|
65
|
-
className="
|
|
65
|
+
className="volter-camera-preview-title"
|
|
66
66
|
title={`${preview.name} · ${preview.lens}`}
|
|
67
67
|
>
|
|
68
68
|
{preview.name}
|
|
@@ -77,7 +77,7 @@ function CameraPreview({ previewRef }: CameraAuthoringOverlayProps) {
|
|
|
77
77
|
<EditorIcon icon={faThumbtack} size="sm" />
|
|
78
78
|
</IconButton>
|
|
79
79
|
</Tooltip>
|
|
80
|
-
<div ref={menuRef} className="
|
|
80
|
+
<div ref={menuRef} className="volter-camera-preview-menu-anchor">
|
|
81
81
|
<Tooltip text="Camera actions">
|
|
82
82
|
<IconButton
|
|
83
83
|
aria-label="Camera actions"
|
|
@@ -89,7 +89,7 @@ function CameraPreview({ previewRef }: CameraAuthoringOverlayProps) {
|
|
|
89
89
|
</IconButton>
|
|
90
90
|
</Tooltip>
|
|
91
91
|
{menuOpen ? (
|
|
92
|
-
<EditorPopover className="
|
|
92
|
+
<EditorPopover className="volter-camera-preview-menu">
|
|
93
93
|
<Button
|
|
94
94
|
variant="ghost"
|
|
95
95
|
size="compact"
|
|
@@ -130,7 +130,7 @@ function CameraPreview({ previewRef }: CameraAuthoringOverlayProps) {
|
|
|
130
130
|
</Inline>
|
|
131
131
|
<div
|
|
132
132
|
ref={previewRef}
|
|
133
|
-
className="
|
|
133
|
+
className="volter-camera-preview-image"
|
|
134
134
|
style={{ aspectRatio: String(Math.max(0.5, Math.min(3, aspect))) }}
|
|
135
135
|
role="img"
|
|
136
136
|
aria-label={`Live view through ${preview.name}`}
|
|
@@ -142,13 +142,13 @@ function CameraPreview({ previewRef }: CameraAuthoringOverlayProps) {
|
|
|
142
142
|
|
|
143
143
|
function CompositionGuides() {
|
|
144
144
|
return (
|
|
145
|
-
<div className="
|
|
146
|
-
<div className="
|
|
147
|
-
<div className="
|
|
148
|
-
<div className="
|
|
149
|
-
<div className="
|
|
150
|
-
<div className="
|
|
151
|
-
<div className="
|
|
145
|
+
<div className="volter-camera-guides" aria-hidden="true">
|
|
146
|
+
<div className="volter-camera-guide volter-camera-guide-v1" />
|
|
147
|
+
<div className="volter-camera-guide volter-camera-guide-v2" />
|
|
148
|
+
<div className="volter-camera-guide volter-camera-guide-h1" />
|
|
149
|
+
<div className="volter-camera-guide volter-camera-guide-h2" />
|
|
150
|
+
<div className="volter-camera-safe-frame" />
|
|
151
|
+
<div className="volter-camera-center-mark" />
|
|
152
152
|
</div>
|
|
153
153
|
);
|
|
154
154
|
}
|
|
@@ -163,7 +163,7 @@ function CameraViewBanner() {
|
|
|
163
163
|
<EditorSurface
|
|
164
164
|
variant="overlay"
|
|
165
165
|
border
|
|
166
|
-
className="
|
|
166
|
+
className="volter-camera-view-banner volter-chrome-island volter-glass-island"
|
|
167
167
|
data-testid="camera-view-banner"
|
|
168
168
|
>
|
|
169
169
|
<Inline gap={6} align="center">
|
|
@@ -172,13 +172,13 @@ function AudioTelemetry() {
|
|
|
172
172
|
: 'Audio quiet';
|
|
173
173
|
|
|
174
174
|
return (
|
|
175
|
-
<div className="
|
|
175
|
+
<div className="volter-header-audio" data-muted={muted || undefined} data-available={!!adapter}>
|
|
176
176
|
<Button
|
|
177
177
|
ref={buttonRef}
|
|
178
178
|
type="button"
|
|
179
179
|
variant="ghost"
|
|
180
180
|
size="compact"
|
|
181
|
-
className="
|
|
181
|
+
className="volter-header-telemetry-button"
|
|
182
182
|
data-testid="header-audio-telemetry"
|
|
183
183
|
aria-label={`${stateLabel}; open audio controls`}
|
|
184
184
|
aria-haspopup="menu"
|
|
@@ -187,7 +187,7 @@ function AudioTelemetry() {
|
|
|
187
187
|
onClick={() => setMenuOpen((open) => !open)}
|
|
188
188
|
>
|
|
189
189
|
<EditorIcon icon={muted ? faVolumeXmark : faVolumeHigh} size="sm" />
|
|
190
|
-
<span className="
|
|
190
|
+
<span className="volter-audio-vu" aria-hidden="true">
|
|
191
191
|
{Array.from({ length: AUDIO_METER_SEGMENTS }, (_, index) => (
|
|
192
192
|
<i key={index} data-filled={index < filled || undefined} />
|
|
193
193
|
))}
|
|
@@ -268,7 +268,7 @@ function PerformanceTelemetry() {
|
|
|
268
268
|
type="button"
|
|
269
269
|
variant="ghost"
|
|
270
270
|
size="compact"
|
|
271
|
-
className="
|
|
271
|
+
className="volter-header-telemetry-button volter-header-performance"
|
|
272
272
|
data-testid="header-performance-telemetry"
|
|
273
273
|
data-performance-tone={tone}
|
|
274
274
|
disabled={!source}
|
|
@@ -277,24 +277,24 @@ function PerformanceTelemetry() {
|
|
|
277
277
|
onClick={() => profilerView.open('profiler')}
|
|
278
278
|
>
|
|
279
279
|
<svg
|
|
280
|
-
className="
|
|
280
|
+
className="volter-performance-graph"
|
|
281
281
|
aria-hidden="true"
|
|
282
282
|
viewBox="0 0 72 18"
|
|
283
283
|
width="72"
|
|
284
284
|
height="18"
|
|
285
285
|
>
|
|
286
286
|
<line
|
|
287
|
-
className="
|
|
287
|
+
className="volter-performance-budget"
|
|
288
288
|
x1="0"
|
|
289
289
|
x2="72"
|
|
290
290
|
y1={18 - (FRAME_BUDGET_60_FPS_MS / 50) * 18}
|
|
291
291
|
y2={18 - (FRAME_BUDGET_60_FPS_MS / 50) * 18}
|
|
292
292
|
/>
|
|
293
|
-
{points && <polyline className="
|
|
293
|
+
{points && <polyline className="volter-performance-sparkline" points={points} />}
|
|
294
294
|
</svg>
|
|
295
|
-
<span className="
|
|
296
|
-
<strong className="
|
|
297
|
-
<span className="
|
|
295
|
+
<span className="volter-performance-readout">
|
|
296
|
+
<strong className="volter-header-fps">{fps ?? '—'} FPS</strong>
|
|
297
|
+
<span className="volter-header-frame-ms">{frameMs?.toFixed(1) ?? '—'} ms</span>
|
|
298
298
|
</span>
|
|
299
299
|
</Button>
|
|
300
300
|
);
|
|
@@ -322,7 +322,7 @@ export function HeaderTelemetry() {
|
|
|
322
322
|
if (activeChromeRegions().telemetry === 'hidden') return null;
|
|
323
323
|
return (
|
|
324
324
|
<Inline
|
|
325
|
-
className="
|
|
325
|
+
className="volter-header-telemetry volter-chrome-island volter-glass-island"
|
|
326
326
|
data-island-scale="compact"
|
|
327
327
|
align="center"
|
|
328
328
|
gap={1}
|
|
@@ -184,8 +184,8 @@ export function PixiIsolationSceneContent({
|
|
|
184
184
|
ref={containerRef}
|
|
185
185
|
data-testid="scene-document-pixi-isolation"
|
|
186
186
|
data-document-id={documentId}
|
|
187
|
-
data-
|
|
188
|
-
data-
|
|
187
|
+
data-volter-backdrop-color={active ? CANVAS_SCENE_BACKGROUND : undefined}
|
|
188
|
+
data-volter-backdrop-policy={active ? 'dark-frost' : undefined}
|
|
189
189
|
onPointerDown={() => setActiveScope('viewport')}
|
|
190
190
|
style={{
|
|
191
191
|
position: 'absolute',
|
|
@@ -42,14 +42,14 @@ export function ResolutionPicker({
|
|
|
42
42
|
const label = isFill ? 'Fill' : `${resolution.width}×${resolution.height}`;
|
|
43
43
|
|
|
44
44
|
return (
|
|
45
|
-
<div className="
|
|
45
|
+
<div className="volter-playbar-popover-anchor">
|
|
46
46
|
<Button
|
|
47
47
|
ref={ref}
|
|
48
48
|
variant="ghost"
|
|
49
49
|
size="comfortable"
|
|
50
50
|
aria-haspopup="menu"
|
|
51
51
|
aria-expanded={open}
|
|
52
|
-
aria-controls={open ? '
|
|
52
|
+
aria-controls={open ? 'volter-resolution-picker-menu' : undefined}
|
|
53
53
|
onClick={() => setOpen((value) => !value)}
|
|
54
54
|
title={title}
|
|
55
55
|
>
|
|
@@ -63,9 +63,9 @@ export function ResolutionPicker({
|
|
|
63
63
|
</Button>
|
|
64
64
|
{open && (
|
|
65
65
|
<AnchoredMenu
|
|
66
|
-
id="
|
|
66
|
+
id="volter-resolution-picker-menu"
|
|
67
67
|
anchorRef={ref}
|
|
68
|
-
className="
|
|
68
|
+
className="volter-resolution-menu"
|
|
69
69
|
gap={4}
|
|
70
70
|
onDismiss={() => setOpen(false)}
|
|
71
71
|
>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* `Scene` (the subject, not its entry file — see SCENE_DOCUMENT_TITLE),
|
|
4
4
|
* closeable and discoverable in Content, content = the world root's stage.
|
|
5
5
|
*
|
|
6
|
-
* Transcribed from the Scene half of
|
|
6
|
+
* Transcribed from the Scene half of volter's `components/CenterDocuments.tsx`.
|
|
7
7
|
* The kit keeps the other half (`@volter/editor-core/components/CenterDocuments`
|
|
8
8
|
* `useCenterDocuments`): it binds the Game document's tab, reports the
|
|
9
9
|
* live/stopped edge and closes every document on session teardown. What moved
|
|
@@ -195,7 +195,7 @@ export class ViewportErrorBoundary extends Component<
|
|
|
195
195
|
editorConsole.error(`Viewport failed to start: ${error.message}`, 'editor');
|
|
196
196
|
// Keep the component stack in the browser console for developer diagnosis.
|
|
197
197
|
// biome-ignore lint/suspicious/noConsole: renderer startup failures need their React component stack in developer tools as well as the user-facing editor Console entry
|
|
198
|
-
console.error('[
|
|
198
|
+
console.error('[volter] Viewport failed to start', error, info.componentStack);
|
|
199
199
|
|
|
200
200
|
if (isTransientRendererStartupFailure(error) && this.autoRetryCount < MAX_AUTO_RETRIES) {
|
|
201
201
|
this.clearPendingRetry();
|
|
@@ -251,7 +251,7 @@ export class ViewportErrorBoundary extends Component<
|
|
|
251
251
|
</div>
|
|
252
252
|
<Button
|
|
253
253
|
type="button"
|
|
254
|
-
className="
|
|
254
|
+
className="volter-btn"
|
|
255
255
|
data-testid="retry-viewport"
|
|
256
256
|
onClick={this.handleManualRetry}
|
|
257
257
|
>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* (`@volter/editor-threejs/kit/components/world-root-binding`): the world's surface,
|
|
4
4
|
* its stage, and the overlays the stage shows only over a world.
|
|
5
5
|
*
|
|
6
|
-
* `Overlays` is the world-root half of
|
|
6
|
+
* `Overlays` is the world-root half of volter's `stage-overlay-set.tsx` — every
|
|
7
7
|
* part it rendered only when `worldRoot` held (the root selection layer, the
|
|
8
8
|
* camera-authoring pin, the scene's
|
|
9
9
|
* surface-state card). The rest of that overlay set is the kit's
|
|
@@ -454,7 +454,7 @@ export function installWorldRootStage(options: WorldRootStageOptions): WorldRoot
|
|
|
454
454
|
// DEV/e2e hook: lets the per-game feature matrix assert the viewport camera
|
|
455
455
|
// reframes (view presets / focus / orbit) on an ingested scene.
|
|
456
456
|
if (import.meta.env.DEV) {
|
|
457
|
-
(window as unknown as Record<string, unknown>)['
|
|
457
|
+
(window as unknown as Record<string, unknown>)['__volterViewport'] = viewport;
|
|
458
458
|
}
|
|
459
459
|
|
|
460
460
|
// --- Composer rebuild ---
|
|
@@ -510,7 +510,7 @@ function probeDocumentProviders(adapter: AuthoringAdapter, id: string, record: R
|
|
|
510
510
|
record(
|
|
511
511
|
'editor.assetDrop.accepts',
|
|
512
512
|
'operation',
|
|
513
|
-
() => adapter.assetDrop?.accepts(id, '
|
|
513
|
+
() => adapter.assetDrop?.accepts(id, '__volter_read_probe__'),
|
|
514
514
|
(value) => typeof value === 'boolean',
|
|
515
515
|
);
|
|
516
516
|
}
|