@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,373 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE `SystemAdapters` TWO-STATE RULE FOR A NATIVE MOUNT — the pure half of
|
|
3
|
+
* `@volter/editor-game/coverage/native-system-coverage.ts`.
|
|
4
|
+
*
|
|
5
|
+
* ## The defect this closes
|
|
6
|
+
*
|
|
7
|
+
* `@volter/editor-game/coverage/root-coverage.ts` made coverage provenance-neutral for the
|
|
8
|
+
* `editor.*` family — every mounted root, native or ingested, gets a row per
|
|
9
|
+
* `AuthoringAdapter` provider. It deliberately keeps ONLY that family, on the
|
|
10
|
+
* grounds that the remaining facts are "the ingested lane's own questions".
|
|
11
|
+
* That is true of `contract.*` (a native game declares no `window.vgaiGame`
|
|
12
|
+
* and never will) and false of `system.*`: a `SystemAdapters` slot is read off
|
|
13
|
+
* the editor's OWN live registry (`authoring/active-systems.ts`), which a
|
|
14
|
+
* native game fills exactly as an ingested one does. So the slots went
|
|
15
|
+
* unreported for every first-party game, and the measured case is a
|
|
16
|
+
* first-party Colyseus project whose Network panel is dark in precisely the
|
|
17
|
+
* same way a single-player project's is — with nothing standing anywhere to
|
|
18
|
+
* say which of the two it is.
|
|
19
|
+
*
|
|
20
|
+
* ## Why an absence here can be a positive answer at all
|
|
21
|
+
*
|
|
22
|
+
* A foreign game's silence is ambiguous by construction: an omitted key means
|
|
23
|
+
* "unsupported", which is the same shape as "nobody looked" — which is exactly
|
|
24
|
+
* why the contract needs an explicit `{ present: false, evidence }` record.
|
|
25
|
+
* A native game has no such ambiguity for the ENGINE-OWNED slots, because the
|
|
26
|
+
* engine BUILT the bag (`runtime/game.ts`'s `computeSystemAdapters` merges what
|
|
27
|
+
* each mounted root's `mounted.systems` carries). An unbound engine-owned slot
|
|
28
|
+
* on a live native mount is therefore the engine's own statement that no
|
|
29
|
+
* mounted root built that subsystem — not an unasked question.
|
|
30
|
+
*
|
|
31
|
+
* `networking` is the exception, and it is the whole point. Nothing engine-side
|
|
32
|
+
* builds it: a game registers a `NetworkingAdapter` itself. So its absence is
|
|
33
|
+
* ruled out only by the PROJECT'S OWN FILE — no `server` block in
|
|
34
|
+
* `vgai.project.json` means there is no room to describe. A game that DOES
|
|
35
|
+
* declare a room and binds nothing is `unanswered`, i.e. a standing warning.
|
|
36
|
+
*
|
|
37
|
+
* ## Provenance neutrality
|
|
38
|
+
*
|
|
39
|
+
* Nothing here consults a route, a game id, or a provenance flag. Both
|
|
40
|
+
* measurers answer the same question — "is this slot at a terminal state, and
|
|
41
|
+
* what is the evidence?" — over the facts their own lane actually has, and each
|
|
42
|
+
* carries its own `fix` text so the shared row builder in
|
|
43
|
+
* `coverage/capability-coverage.ts` never has to ask who produced the measurement.
|
|
44
|
+
*
|
|
45
|
+
* Pure over its inputs, so the whole table is unit-testable with no browser and
|
|
46
|
+
* no live session.
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
import type { DeclaredSystemAbsence } from '@volter/game-runtime/runtime/game';
|
|
50
|
+
import type { SystemAdapters } from '@volter/editor-project/adapter/system-adapter';
|
|
51
|
+
import {
|
|
52
|
+
SYSTEM_ADAPTER_SLOTS,
|
|
53
|
+
type SystemAdapterMeasurement,
|
|
54
|
+
type SystemAdapterSlot,
|
|
55
|
+
} from './capability-coverage';
|
|
56
|
+
import { inspectSystemAdapterSeam } from '@volter/editor-core/coverage/system-seam-evidence';
|
|
57
|
+
|
|
58
|
+
/** What the native derivation needs to know that the live registry cannot tell
|
|
59
|
+
* it. Both facts are read off things that already exist; neither is a guess. */
|
|
60
|
+
export interface NativeProjectFacts {
|
|
61
|
+
/**
|
|
62
|
+
* `vgai.project.json` declares a `server` block (a Colyseus room) — read from
|
|
63
|
+
* the active project view (`project-manager.ts`'s `ActiveProject.server`).
|
|
64
|
+
* The decisive project-level declaration for whether this game is networked.
|
|
65
|
+
*/
|
|
66
|
+
readonly declaresServer: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* A game is actually MOUNTED right now. False means the registry is empty
|
|
69
|
+
* because nothing is running, not because no root built a subsystem — and an
|
|
70
|
+
* empty registry read as a fistful of positive absences would be the exact
|
|
71
|
+
* wrong answer, so nothing is terminal in that case.
|
|
72
|
+
*/
|
|
73
|
+
readonly mounted: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* What the mounted roots' own `systems` tables said with `absent(reason)` —
|
|
76
|
+
* `Game.declaredSystemAbsences`, harvested through
|
|
77
|
+
* `binding.observation.entrySystems` at mount.
|
|
78
|
+
*
|
|
79
|
+
* This is the DETECT-OR-DECLARE half, and it outranks every generic excuse
|
|
80
|
+
* below: the table's evidence is the GAME's own words about its own source,
|
|
81
|
+
* where {@link ENGINE_OWNED_EVIDENCE} can only say what the engine would have
|
|
82
|
+
* had to build. A game that declares nothing loses nothing — it falls through
|
|
83
|
+
* to exactly the derivation it had before.
|
|
84
|
+
*/
|
|
85
|
+
readonly declaredAbsences: readonly DeclaredSystemAbsence[];
|
|
86
|
+
/**
|
|
87
|
+
* The absences above were RETAINED from this session's last play mount
|
|
88
|
+
* rather than read off a live Game. The declaration is a static fact about
|
|
89
|
+
* the entry's `systems` table, so it remains the game's own answer while
|
|
90
|
+
* stopped — but the row says which reading it is, because "as of the last
|
|
91
|
+
* mount" and "live right now" are different claims.
|
|
92
|
+
*/
|
|
93
|
+
readonly absencesFromLastMount?: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* The project's own dependency names — the DETECT half of detect-or-declare.
|
|
96
|
+
*
|
|
97
|
+
* `null` means the question was not answered (an unlanded fetch, an
|
|
98
|
+
* unreadable package.json) and MUST NOT be read as "no libraries":
|
|
99
|
+
* an unasked question that reassures is exactly the failure this family
|
|
100
|
+
* exists to prevent, so a `null` detector simply stands down and every row
|
|
101
|
+
* reports what it would have without it.
|
|
102
|
+
*/
|
|
103
|
+
readonly dependencies: readonly string[] | null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Libraries whose presence CONTRADICTS an empty slot — a project shipping one
|
|
108
|
+
* of these is carrying the subsystem, whoever failed to bind it.
|
|
109
|
+
*
|
|
110
|
+
* Deliberately narrow, because a false positive here turns a correct terminal
|
|
111
|
+
* row into a permanent false work order. `@colyseus/schema` is the worked
|
|
112
|
+
* exclusion: it is shared state TYPES, which a single-player game legitimately
|
|
113
|
+
* depends on to compile against a room definition it never joins — only the
|
|
114
|
+
* CLIENT (`colyseus.js`) is evidence that this game talks to a server.
|
|
115
|
+
* `renderDebug`, `camera` and `debug` have no library tell at all (the host
|
|
116
|
+
* derives them from its own wiring), so they are absent from this table and
|
|
117
|
+
* nothing about them is ever inferred from a dependency list.
|
|
118
|
+
*/
|
|
119
|
+
const SLOT_LIBRARIES: Partial<Record<SystemAdapterSlot, readonly string[]>> = {
|
|
120
|
+
physics: ['@react-three/rapier', '@dimforge/rapier3d-compat', '@dimforge/rapier2d-compat'],
|
|
121
|
+
networking: ['colyseus.js'],
|
|
122
|
+
navigation: ['recast-navigation', '@recast-navigation/core'],
|
|
123
|
+
audio: ['tone', 'howler'],
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/** The libraries this project ships for `slot`, or `[]`. */
|
|
127
|
+
function shippedLibraries(slot: SystemAdapterSlot, facts: NativeProjectFacts): readonly string[] {
|
|
128
|
+
if (facts.dependencies === null) return [];
|
|
129
|
+
const tells = SLOT_LIBRARIES[slot];
|
|
130
|
+
if (!tells) return [];
|
|
131
|
+
return tells.filter((tell) => facts.dependencies?.includes(tell));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* The fix for a slot whose library ships but whose adapter is unbound — and it
|
|
136
|
+
* names BOTH spellings of a terminal answer, because either is legitimate and
|
|
137
|
+
* the author is the only one who knows which is true.
|
|
138
|
+
*/
|
|
139
|
+
function contradictionFix(slot: SystemAdapterSlot, libraries: readonly string[]): string {
|
|
140
|
+
return (
|
|
141
|
+
`this project depends on ${libraries.join(' + ')}, so "nothing built a ${slot} world" is not ` +
|
|
142
|
+
`a settled answer. Reach a terminal state either way: BIND it — declare a ${slot} adapter in ` +
|
|
143
|
+
`the root entry's \`systems\` table (the engine ships \`rapierPhysicsSystem()\` for the R3F ` +
|
|
144
|
+
`Rapier bridge; other slots take the game's own adapter) — or DECLARE the absence with ` +
|
|
145
|
+
`\`absent(reason)\` in that same table. The reason must NAME the dependency and say why it ` +
|
|
146
|
+
`is there while the subsystem is not (a transitive pull, a leftover, a build-only use); a ` +
|
|
147
|
+
`declared absence blind to the shipped dependency grades malformed, not terminal.`
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Whether a declared-absence reason ENGAGES with a contradicting shipped
|
|
153
|
+
* library — mechanically: it names the package, or a DISTINCTIVE word of the
|
|
154
|
+
* package's own name (`@react-three/rapier` is acknowledged by a reason that
|
|
155
|
+
* says "rapier"). A reason that names the evidence is the game answering the
|
|
156
|
+
* contradiction; one that does not never saw it.
|
|
157
|
+
*
|
|
158
|
+
* The candidates are word-boundary matches on the package TAIL's tokens with
|
|
159
|
+
* ecosystem/generic words dropped — measured adversarially before this
|
|
160
|
+
* shape: bare-substring segment matching let "pure three.js visual scene"
|
|
161
|
+
* acknowledge `@react-three/rapier` (via `three`), "backwards compat"
|
|
162
|
+
* acknowledge `rapier3d-compat` (via `compat`), and "milestone" contain
|
|
163
|
+
* `tone`. When the tail is ENTIRELY generic (`@recast-navigation/core`),
|
|
164
|
+
* the scope's own words become the candidates instead — the package still
|
|
165
|
+
* has a name, and the reason must say it.
|
|
166
|
+
*/
|
|
167
|
+
function reasonAcknowledges(reason: string, library: string): boolean {
|
|
168
|
+
// The full name counts — but a PLAIN name needs word boundaries, or
|
|
169
|
+
// "milestone" would contain `tone`. A scoped/punctuated name is already
|
|
170
|
+
// too distinctive to appear by accident.
|
|
171
|
+
const escaped = library.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
172
|
+
if (/^[a-z0-9]+$/i.test(library)) {
|
|
173
|
+
if (new RegExp(`\\b${escaped}\\b`, 'i').test(reason)) return true;
|
|
174
|
+
} else if (reason.toLowerCase().includes(library.toLowerCase())) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
const GENERIC = new Set([
|
|
178
|
+
'compat',
|
|
179
|
+
'core',
|
|
180
|
+
'client',
|
|
181
|
+
'server',
|
|
182
|
+
'node',
|
|
183
|
+
'react',
|
|
184
|
+
'three',
|
|
185
|
+
'libs',
|
|
186
|
+
'api',
|
|
187
|
+
'sdk',
|
|
188
|
+
'util',
|
|
189
|
+
'utils',
|
|
190
|
+
'plugin',
|
|
191
|
+
'plugins',
|
|
192
|
+
'tools',
|
|
193
|
+
'types',
|
|
194
|
+
'browser',
|
|
195
|
+
]);
|
|
196
|
+
const tokensOf = (part: string): string[] =>
|
|
197
|
+
part
|
|
198
|
+
.toLowerCase()
|
|
199
|
+
.split(/[.\-_/]+/)
|
|
200
|
+
.filter((token) => token.length >= 4 && !GENERIC.has(token));
|
|
201
|
+
const [scopePart, tailPart] = library.toLowerCase().startsWith('@')
|
|
202
|
+
? [library.slice(1, library.indexOf('/')), library.slice(library.indexOf('/') + 1)]
|
|
203
|
+
: ['', library];
|
|
204
|
+
let candidates = tokensOf(tailPart);
|
|
205
|
+
if (candidates.length === 0) candidates = tokensOf(scopePart);
|
|
206
|
+
// A digit-suffixed token also answers to its stem: `rapier3d` ⇒ `rapier`.
|
|
207
|
+
const stems = candidates
|
|
208
|
+
.map((token) => token.replace(/\d+d?$/, ''))
|
|
209
|
+
.filter((stem) => stem.length >= 4);
|
|
210
|
+
return [...new Set([...candidates, ...stems])].some((token) =>
|
|
211
|
+
new RegExp(`\\b${token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`, 'i').test(reason),
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** The file fact that rules `networking` out, in the file's own terms. */
|
|
216
|
+
const NO_SERVER_EVIDENCE =
|
|
217
|
+
"this project's vgai.project.json declares no `server` block, so there is no room for a " +
|
|
218
|
+
'NetworkingAdapter to describe';
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Why an unbound ENGINE-OWNED slot is terminal on a live native mount: what the
|
|
222
|
+
* engine would have had to build for the slot to be filled, so a reader can
|
|
223
|
+
* check the claim against the root's own source rather than take it on faith.
|
|
224
|
+
*
|
|
225
|
+
* Keyed by slot rather than listed, so a slot with no entry falls through to
|
|
226
|
+
* `unanswered` (a work order) instead of silently acquiring an excuse. `debug`
|
|
227
|
+
* is deliberately absent: the game-scoped registry binds it unconditionally
|
|
228
|
+
* (`runtime/game.ts`'s `computeSystemAdapters` seeds it before walking the
|
|
229
|
+
* roots), so an unbound `debug` on a live mount means something is genuinely
|
|
230
|
+
* wrong and must stay loud.
|
|
231
|
+
*/
|
|
232
|
+
const ENGINE_OWNED_EVIDENCE: Partial<Record<SystemAdapterSlot, string>> = {
|
|
233
|
+
physics:
|
|
234
|
+
'no mounted root built a physics world — the engine seeds this slot from the root that ' +
|
|
235
|
+
'creates one (a `setup` root, or an R3F tree using the Rapier bridge), so nothing in this ' +
|
|
236
|
+
'game simulates bodies',
|
|
237
|
+
navigation:
|
|
238
|
+
"no mounted root registered a navigation adapter — the engine seeds this slot from a world's " +
|
|
239
|
+
'own NavMeshManager registration, so this game has no navmesh',
|
|
240
|
+
audio:
|
|
241
|
+
'no mounted root built an audio context — the engine seeds this slot from the root that ' +
|
|
242
|
+
'creates one, so there is no audio graph for the host to mute, meter or graph',
|
|
243
|
+
camera:
|
|
244
|
+
'no mounted root registered a camera-ownership system — the engine ships ' +
|
|
245
|
+
'`createCameraOwnershipSystem` for a game that takes camera control (a cutscene rig), so ' +
|
|
246
|
+
"this game's camera is gameplay-owned throughout",
|
|
247
|
+
renderDebug:
|
|
248
|
+
'no mounted root captured a real WebGL2 render context to derive one from — the engine seeds ' +
|
|
249
|
+
'this slot only where the root owns the capture wiring ' +
|
|
250
|
+
'(`adapter/setup-three-root-adapter.ts`), so the Frame debugger and the render-memory view ' +
|
|
251
|
+
'have nothing to show for this game',
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
/** The fix a native game's author can actually act on, per slot. Never the
|
|
255
|
+
* ingested lane's `window.vgaiGame` text, which would be false here. */
|
|
256
|
+
function nativeSlotFix(slot: SystemAdapterSlot, facts: NativeProjectFacts): string {
|
|
257
|
+
if (!facts.mounted) {
|
|
258
|
+
return 'nothing is mounted, so the live system registry has not been asked yet — enter Play, or open a project whose roots mount, and the slot reports its real state';
|
|
259
|
+
}
|
|
260
|
+
if (slot === 'networking') {
|
|
261
|
+
return (
|
|
262
|
+
"register a NetworkingAdapter from the game's own code " +
|
|
263
|
+
"(`ctx.registerSystemAdapter('networking', …)`) — this project declares a `server` block, " +
|
|
264
|
+
"so the editor's Network panel is dark for a game that IS networked"
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
return `register a ${slot} adapter from the game's own code (\`ctx.registerSystemAdapter('${slot}', …)\`), or build the subsystem the engine seeds it from`;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* The terminal state of every `SystemAdapters` slot for a live NATIVE mount.
|
|
272
|
+
*
|
|
273
|
+
* `bound` is read off the editor's own live registry — the very object its
|
|
274
|
+
* panels call, so a slot that "registered" but never reached the registry
|
|
275
|
+
* reports honestly. Everything else is decided by the facts above, and a bound
|
|
276
|
+
* adapter always wins because the registry is the stronger measurement.
|
|
277
|
+
*/
|
|
278
|
+
export function measureNativeSystemAdapters(
|
|
279
|
+
active: SystemAdapters,
|
|
280
|
+
facts: NativeProjectFacts,
|
|
281
|
+
): readonly SystemAdapterMeasurement[] {
|
|
282
|
+
const declared = new Map(facts.declaredAbsences.map((a) => [a.slot, a]));
|
|
283
|
+
return SYSTEM_ADAPTER_SLOTS.map((slot): SystemAdapterMeasurement => {
|
|
284
|
+
const absence = declared.get(slot);
|
|
285
|
+
if (active[slot] != null) {
|
|
286
|
+
// A DECLARED absence contradicted by a live adapter. Both statements are
|
|
287
|
+
// about the same slot and they cannot both be true, so neither terminal
|
|
288
|
+
// state may be printed as if it were settled: `malformed` is the existing
|
|
289
|
+
// loud state for a declaration that could not be honoured, and it is the
|
|
290
|
+
// honest verdict here. Which one is wrong is the author's to find — the
|
|
291
|
+
// row names both halves so they can.
|
|
292
|
+
if (absence) {
|
|
293
|
+
return {
|
|
294
|
+
slot,
|
|
295
|
+
state: 'malformed',
|
|
296
|
+
evidence:
|
|
297
|
+
`root "${absence.rootId}" declares this slot absent — "${absence.reason}" — but a ` +
|
|
298
|
+
`${slot} adapter is bound on the live mount. One of the two is wrong: either the ` +
|
|
299
|
+
'declaration is stale (delete the `absent()` marker) or something registers an ' +
|
|
300
|
+
'adapter the game does not mean to have.',
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
return {
|
|
304
|
+
slot,
|
|
305
|
+
state: 'bound',
|
|
306
|
+
proof: inspectSystemAdapterSeam({
|
|
307
|
+
slot,
|
|
308
|
+
adapter: active[slot] as NonNullable<SystemAdapters[typeof slot]>,
|
|
309
|
+
subject: 'game',
|
|
310
|
+
}),
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
if (!facts.mounted) return { slot, state: 'unanswered', fix: nativeSlotFix(slot, facts) };
|
|
314
|
+
// The game's own answer outranks every derived excuse below — it is a
|
|
315
|
+
// statement about this game's source, where the fallbacks can only describe
|
|
316
|
+
// what the engine would have had to build.
|
|
317
|
+
const libraries = shippedLibraries(slot, facts);
|
|
318
|
+
if (absence) {
|
|
319
|
+
// DECLARED absent while the project ships the library. The declaration
|
|
320
|
+
// may still be right (a transitive pull, a leftover, a build-only use) —
|
|
321
|
+
// but only if it ENGAGES with the evidence: a reason that names the
|
|
322
|
+
// dependency is the game saying why it is there without the subsystem,
|
|
323
|
+
// and is honored as the game's answer. A reason blind to a shipped
|
|
324
|
+
// library is loud, naming both halves, exactly like the bound-adapter
|
|
325
|
+
// contradiction above — a terminal row there would stop anyone ever
|
|
326
|
+
// checking.
|
|
327
|
+
const unacknowledged = libraries.filter((lib) => !reasonAcknowledges(absence.reason, lib));
|
|
328
|
+
if (unacknowledged.length > 0) {
|
|
329
|
+
return {
|
|
330
|
+
slot,
|
|
331
|
+
state: 'malformed',
|
|
332
|
+
evidence:
|
|
333
|
+
`root "${absence.rootId}" declares this slot absent — "${absence.reason}" — but this ` +
|
|
334
|
+
`project depends on ${unacknowledged.join(' + ')} and the reason does not mention ` +
|
|
335
|
+
'it. Either the absence is stale, or it holds and the reason should say why that ' +
|
|
336
|
+
'dependency is present without the subsystem (naming it).',
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
const lastMountNote = facts.absencesFromLastMount
|
|
340
|
+
? ' — the declaration read at this session’s last play mount; play to re-read it live'
|
|
341
|
+
: '';
|
|
342
|
+
return {
|
|
343
|
+
slot,
|
|
344
|
+
state: 'empty',
|
|
345
|
+
attestedBy: 'game',
|
|
346
|
+
evidence:
|
|
347
|
+
libraries.length > 0
|
|
348
|
+
? `declared absent by root "${absence.rootId}": ${absence.reason} ` +
|
|
349
|
+
`(the project does depend on ${libraries.join(' + ')}; the reason engages with that)${lastMountNote}`
|
|
350
|
+
: `declared absent by root "${absence.rootId}": ${absence.reason}${lastMountNote}`,
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
// Nothing declared, and the library IS here: the host's inference below
|
|
354
|
+
// would print a confident absence over real evidence to the contrary. That
|
|
355
|
+
// is an UNANSWERED question — the only state that is a work order — not a
|
|
356
|
+
// terminal one.
|
|
357
|
+
if (libraries.length > 0) {
|
|
358
|
+
return { slot, state: 'unanswered', fix: contradictionFix(slot, libraries) };
|
|
359
|
+
}
|
|
360
|
+
// Everything below is the HOST reasoning from an unfilled slot, not the
|
|
361
|
+
// game speaking. Tagging it says so in the row, and names the mechanism
|
|
362
|
+
// that would let the game answer for itself.
|
|
363
|
+
if (slot === 'networking') {
|
|
364
|
+
return facts.declaresServer
|
|
365
|
+
? { slot, state: 'unanswered', fix: nativeSlotFix(slot, facts) }
|
|
366
|
+
: { slot, state: 'empty', attestedBy: 'host', evidence: NO_SERVER_EVIDENCE };
|
|
367
|
+
}
|
|
368
|
+
const evidence = ENGINE_OWNED_EVIDENCE[slot];
|
|
369
|
+
return evidence
|
|
370
|
+
? { slot, state: 'empty', attestedBy: 'host', evidence }
|
|
371
|
+
: { slot, state: 'unanswered', fix: nativeSlotFix(slot, facts) };
|
|
372
|
+
});
|
|
373
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { EditorBadge } from '@volter/editor-sdk/widgets';
|
|
2
|
+
import type { CSSProperties, ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
export function DesignSystemPage({
|
|
5
|
+
eyebrow,
|
|
6
|
+
title,
|
|
7
|
+
summary,
|
|
8
|
+
children,
|
|
9
|
+
testId,
|
|
10
|
+
}: {
|
|
11
|
+
eyebrow: string;
|
|
12
|
+
title: string;
|
|
13
|
+
summary: string;
|
|
14
|
+
children: ReactNode;
|
|
15
|
+
testId?: string;
|
|
16
|
+
}) {
|
|
17
|
+
return (
|
|
18
|
+
<main className="ds-page" data-testid={testId}>
|
|
19
|
+
<header className="ds-page-header">
|
|
20
|
+
<span className="ds-eyebrow">{eyebrow}</span>
|
|
21
|
+
<h1 className="ds-page-title">{title}</h1>
|
|
22
|
+
<p className="ds-page-summary">{summary}</p>
|
|
23
|
+
</header>
|
|
24
|
+
{children}
|
|
25
|
+
</main>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function StorySection({
|
|
30
|
+
title,
|
|
31
|
+
description,
|
|
32
|
+
children,
|
|
33
|
+
}: {
|
|
34
|
+
title: string;
|
|
35
|
+
description?: string;
|
|
36
|
+
children: ReactNode;
|
|
37
|
+
}) {
|
|
38
|
+
return (
|
|
39
|
+
<section className="ds-section">
|
|
40
|
+
<header className="ds-section-header">
|
|
41
|
+
<h2 className="ds-section-title">{title}</h2>
|
|
42
|
+
{description && <p className="ds-section-description">{description}</p>}
|
|
43
|
+
</header>
|
|
44
|
+
{children}
|
|
45
|
+
</section>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function Specimen({
|
|
50
|
+
title,
|
|
51
|
+
description,
|
|
52
|
+
children,
|
|
53
|
+
}: {
|
|
54
|
+
title: string;
|
|
55
|
+
description?: string;
|
|
56
|
+
children: ReactNode;
|
|
57
|
+
}) {
|
|
58
|
+
return (
|
|
59
|
+
<article className="ds-specimen">
|
|
60
|
+
<header className="ds-specimen-header">
|
|
61
|
+
<h3 className="ds-specimen-title">{title}</h3>
|
|
62
|
+
{description && <p className="ds-specimen-description">{description}</p>}
|
|
63
|
+
</header>
|
|
64
|
+
{children}
|
|
65
|
+
</article>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function TokenSwatch({ name, value }: { name: string; value: string }) {
|
|
70
|
+
return (
|
|
71
|
+
<div className="ds-token">
|
|
72
|
+
<div className="ds-token-swatch" style={{ background: value }} />
|
|
73
|
+
<div className="ds-token-meta">
|
|
74
|
+
<span className="ds-token-name">{name}</span>
|
|
75
|
+
<span className="ds-token-value">{value}</span>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function Measure({ name, value, width }: { name: string; value: string; width: number }) {
|
|
82
|
+
return (
|
|
83
|
+
<div className="ds-measure-row">
|
|
84
|
+
<span className="ds-token-name">{name}</span>
|
|
85
|
+
<span className="ds-measure" style={{ width: `${width}px` }} />
|
|
86
|
+
<span className="ds-token-value">{value}</span>
|
|
87
|
+
</div>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function StateRow({ label, children }: { label: string; children: ReactNode }) {
|
|
92
|
+
return (
|
|
93
|
+
<>
|
|
94
|
+
<span className="ds-state-label">{label}</span>
|
|
95
|
+
<div className="ds-demo-row">{children}</div>
|
|
96
|
+
</>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function CoverageStatus({ layer }: { layer: string }) {
|
|
101
|
+
return <EditorBadge>{layer}</EditorBadge>;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export const boundedControl: CSSProperties = { width: 220 };
|