@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
package/src/play/play-mode.ts
CHANGED
|
@@ -113,6 +113,7 @@ import {
|
|
|
113
113
|
nodeKeyedPhysics,
|
|
114
114
|
type TransformProvider,
|
|
115
115
|
} from '@volter/editor-project/adapter';
|
|
116
|
+
import { physicsOwnerOf } from '@volter/editor-project/adapter';
|
|
116
117
|
import { assertNever } from '@volter/editor-project/adapter/adapter-surface';
|
|
117
118
|
import { declaredRoots, rootById } from '@volter/editor-project/adapter/manifest-interpreter';
|
|
118
119
|
import { readOidSourceAnchors } from '../three/authoring/oid-source-persistence';
|
|
@@ -151,7 +152,7 @@ export interface PlayModeContext {
|
|
|
151
152
|
*
|
|
152
153
|
* ITS IDENTITY IS THE MOUNT ID, and there is only ever one id for it.
|
|
153
154
|
* `resolveAllRootEntries` opens a mount epoch; every project module of this
|
|
154
|
-
* instance is served under it as `?
|
|
155
|
+
* instance is served under it as `?volter-mount=<id>`; browser module identity
|
|
155
156
|
* is per-url, so that id IS the module-graph boundary; and `gated-globals.ts`
|
|
156
157
|
* resolves this instance's realm and input gate by reading the same id back
|
|
157
158
|
* off the url. Registering it under any second name would be two identities
|
|
@@ -164,7 +165,7 @@ export interface PlayModeContext {
|
|
|
164
165
|
* interleaved log streams and read, later, as a game bug.
|
|
165
166
|
*/
|
|
166
167
|
interface PlayInstance {
|
|
167
|
-
/** The mount id — see above. What `?
|
|
168
|
+
/** The mount id — see above. What `?volter-mount=` carries, what the realm and
|
|
168
169
|
* input gate are keyed by, what `setActiveSystems` registers under and what
|
|
169
170
|
* the session wire addresses. Empty until a composition has resolved. */
|
|
170
171
|
/** The element this instance mounted into (the primary's is the live
|
|
@@ -364,8 +365,8 @@ export function markRestartRequired(reason: string): void {
|
|
|
364
365
|
*
|
|
365
366
|
* They are kept because the unresolved-console ledger names conditions BY
|
|
366
367
|
* THEIR TEXT, and a remount is the event that resolves them: measured
|
|
367
|
-
* 2026-08-29, `
|
|
368
|
-
* own named warning stayed in `
|
|
368
|
+
* 2026-08-29, `volter-game-editor restart` reported "↻ Restarted — session ready" while its
|
|
369
|
+
* own named warning stayed in `volter-game-editor console` forever, because the ledger's
|
|
369
370
|
* automatic clearing rule is a PAGE LOAD and a remount is not one — only
|
|
370
371
|
* `game.reloadPage()` could silence a warning the named verb had already
|
|
371
372
|
* fixed. `clearRestartRequired` now reports them resolved (ledger clearing
|
|
@@ -427,7 +428,7 @@ let _playEpoch = 0;
|
|
|
427
428
|
//
|
|
428
429
|
// PD-1: this used to be nulled by `exitPlayMode()`, which meant the errors of
|
|
429
430
|
// a run that FAILED became invisible the instant the failed run rolled back —
|
|
430
|
-
// `
|
|
431
|
+
// `volter-game-editor status` reported `pageErrors: []` for a play that had just thrown, the
|
|
431
432
|
// exact "every diagnostic says healthy" symptom. The fence's job is to exclude
|
|
432
433
|
// a PREVIOUS run's noise, and the next `enterPlayMode` re-stamping it does
|
|
433
434
|
// that; dropping it on exit only ever hid the evidence of the last run.
|
|
@@ -437,7 +438,7 @@ let _playStartedAtMs: number | null = null;
|
|
|
437
438
|
//
|
|
438
439
|
// Why an end and not just a start: with an open-ended window every editor error
|
|
439
440
|
// logged AFTER a run stopped still counted as "during the play run", so it fell
|
|
440
|
-
// into the play-fenced `consoleErrors` facet — which `
|
|
441
|
+
// into the play-fenced `consoleErrors` facet — which `volter-game-editor status` renders only
|
|
441
442
|
// while play is live. One play run, and every later editor-frame error went
|
|
442
443
|
// invisible again, which is the exact defect the session-lifetime facets exist
|
|
443
444
|
// to close. Stamped at the END of `exitPlayMode`, so a FAILED run's errors (all
|
|
@@ -795,14 +796,14 @@ async function installPlayRootAuthoring(
|
|
|
795
796
|
store.shell.notifyIngestEdit();
|
|
796
797
|
|
|
797
798
|
// Dev/e2e diagnostic handle — the SAME pattern `ingest/mount-ingest-root.ts`'s
|
|
798
|
-
// `window.
|
|
799
|
+
// `window.__volterIngest`/`window.__volterIngest2D` use:
|
|
799
800
|
// `store.saveNow()` (Ctrl/Cmd+S) and `_autoSave()` are both structurally
|
|
800
801
|
// guarded OFF while ANY authoring override is active (`hasAuthoringOverride()`
|
|
801
802
|
// / the play-state check) — the adapter's OWN `persistence.save()` is the
|
|
802
803
|
// only way an override session's edits reach disk, so tests/tooling need a
|
|
803
804
|
// handle to call it directly, exactly as the ingest sessions expose.
|
|
804
805
|
if (import.meta.env.DEV) {
|
|
805
|
-
(window as unknown as Record<string, unknown>)['
|
|
806
|
+
(window as unknown as Record<string, unknown>)['__volterMultiRoot'] = {
|
|
806
807
|
adapter: composite,
|
|
807
808
|
worldIds: children.map((c) => c.worldId),
|
|
808
809
|
store,
|
|
@@ -838,7 +839,7 @@ function exitPlayRootAuthoring(store: EditorShellStore): void {
|
|
|
838
839
|
_instance.journalSession = '';
|
|
839
840
|
}
|
|
840
841
|
if (import.meta.env.DEV) {
|
|
841
|
-
delete (window as unknown as Record<string, unknown>)['
|
|
842
|
+
delete (window as unknown as Record<string, unknown>)['__volterMultiRoot'];
|
|
842
843
|
}
|
|
843
844
|
}
|
|
844
845
|
/**
|
|
@@ -923,7 +924,7 @@ if (import.meta.hot) import.meta.hot.dispose(stopAssetReload);
|
|
|
923
924
|
|
|
924
925
|
// --- Script HMR ---
|
|
925
926
|
if (import.meta.hot) {
|
|
926
|
-
import.meta.hot.on('
|
|
927
|
+
import.meta.hot.on('volter:restart-required', (data: { file: string }) => {
|
|
927
928
|
if (!_instance.session) return;
|
|
928
929
|
const file = data.file.split('/').pop() ?? data.file;
|
|
929
930
|
markRestartRequired(`${file} changed and cannot be applied safely while the game is running.`);
|
|
@@ -937,12 +938,12 @@ if (import.meta.hot) {
|
|
|
937
938
|
// PlayBar's Restart button lights up (variant 'primary') carrying this
|
|
938
939
|
// reason, and ONE click remounts every root from fresh source and
|
|
939
940
|
// re-enters play (`enterPlayMode` re-imports the entry with a
|
|
940
|
-
// cache-busting query — see `loadProjectScripts`). `
|
|
941
|
+
// cache-busting query — see `loadProjectScripts`). `volter-game-editor status` reports
|
|
941
942
|
// the same pending-restart state via `collectState().restartRequired`, so
|
|
942
943
|
// agents get the signal humans get. EDIT-mode behavior is unchanged
|
|
943
944
|
// (`_instance.session` is null there; absorb-by-remount stays as landed — see
|
|
944
945
|
// r3f-design-session.ts).
|
|
945
|
-
import.meta.hot.on('
|
|
946
|
+
import.meta.hot.on('volter:r3f-entry-update', (data: { file: string }) => {
|
|
946
947
|
if (!_instance.session) return;
|
|
947
948
|
const file = data.file.split('/').pop() ?? data.file;
|
|
948
949
|
markRestartRequired(
|
|
@@ -950,7 +951,7 @@ if (import.meta.hot) {
|
|
|
950
951
|
);
|
|
951
952
|
warnRestartRequired(`Restart required: ${file} changed while playing.`);
|
|
952
953
|
});
|
|
953
|
-
import.meta.hot.on('
|
|
954
|
+
import.meta.hot.on('volter:script-update', async (data: { file: string }) => {
|
|
954
955
|
// Project-tool source belongs to editor chrome. The tool contribution
|
|
955
956
|
// store re-imports and remounts it; no game root can become stale from an
|
|
956
957
|
// editor-only document changing.
|
|
@@ -1010,7 +1011,7 @@ export function bindPlayMode(store: EditorShellStore): void {
|
|
|
1010
1011
|
_ctx = { store, gameContainer: null! }; // set dynamically from the live document's container
|
|
1011
1012
|
// THE BARE-KEY YIELD IS THE FRAME'S. While Play runs the game's keys are the
|
|
1012
1013
|
// game's, and what decides is the workbench: our stage actions carry a
|
|
1013
|
-
// `when` clause over `
|
|
1014
|
+
// `when` clause over `volter.stage.focused`/`volter.play`, so a bare key reaches
|
|
1014
1015
|
// the game rather than a shell binding, and a ⌘-chord stays the workbench's.
|
|
1015
1016
|
// The realm gate tracks the same predicate independently (`gated-globals.ts`
|
|
1016
1017
|
// and `surface-keyboard.ts`), which is what keeps a game's raw
|
|
@@ -1031,7 +1032,7 @@ export function isPlayModeActive(): boolean {
|
|
|
1031
1032
|
* Narrow relay accessor — the live play session's virtual input targets
|
|
1032
1033
|
* and the Game root's loop, for `command-listener.ts`'s
|
|
1033
1034
|
* `inject-input`/`set-time-scale`/`set-seed` cases. This is exactly the
|
|
1034
|
-
* accessor the
|
|
1035
|
+
* accessor the volter-sdk honest-gap jsdocs prescribed
|
|
1035
1036
|
* (`play/input-operations.ts`, `play/control-operations.ts`): `_instance.session` is
|
|
1036
1037
|
* module-private, so the relay needs this one exported read. Everything else
|
|
1037
1038
|
* the relay reads (state providers, debug commands) flows through
|
|
@@ -1040,7 +1041,7 @@ export function isPlayModeActive(): boolean {
|
|
|
1040
1041
|
* `getInputTarget(worldId?)` (D15/T-D15.5 — review objection 2's fix)
|
|
1041
1042
|
* is one resolution for every input door: both the SAME `getDebugRegistry(game).getVirtualInputTarget(worldId)`
|
|
1042
1043
|
* — one resolution function (`debug-registry.ts`'s `resolveInputRootId`),
|
|
1043
|
-
* so `inject-input` (this accessor) and `window.
|
|
1044
|
+
* so `inject-input` (this accessor) and `window.__volter.input.*`
|
|
1044
1045
|
* (`debug-bridge.ts`) can never disagree about which world an unqualified
|
|
1045
1046
|
* actuation targets again. `null` when nothing is registered for the
|
|
1046
1047
|
* resolved id (or no `Game` is running at all); throws the registry's own
|
|
@@ -1048,7 +1049,7 @@ export function isPlayModeActive(): boolean {
|
|
|
1048
1049
|
*
|
|
1049
1050
|
* `runTicks` (D15/T-D15.4) is added the SAME way: reached via
|
|
1050
1051
|
* `getDebugRegistry(game).getRunTicksTarget()` — the identical accessor
|
|
1051
|
-
* `runtime/debug-bridge.ts`'s `window.
|
|
1052
|
+
* `runtime/debug-bridge.ts`'s `window.__volter.runTicks` (door a) goes
|
|
1052
1053
|
* through, so the editor relay's `run-ticks` case (door b, → `play.runTicks`)
|
|
1053
1054
|
* calls byte-identical behavior (D17). `null` only when no `Game` is running
|
|
1054
1055
|
* at all (`_instance.session` is `null`) — a live `Game` always wires a run-ticks
|
|
@@ -1285,7 +1286,7 @@ export function resizeGame(width: number, height: number, pixelRatio?: number):
|
|
|
1285
1286
|
|
|
1286
1287
|
/**
|
|
1287
1288
|
* Serializes every `enterPlayMode()` invocation (the ghost-runtime bug:
|
|
1288
|
-
* `
|
|
1289
|
+
* `volter-game-editor play` on an already-playing/still-booting session left the
|
|
1289
1290
|
* PREVIOUS runtime alive, ticking and rendering alongside the new one).
|
|
1290
1291
|
*
|
|
1291
1292
|
* Root cause: `enterPlayModeInner`'s own `if (_instance.session) exitPlayMode()`
|
|
@@ -1305,7 +1306,7 @@ export function resizeGame(width: number, height: number, pixelRatio?: number):
|
|
|
1305
1306
|
* size exceeded` — not a hypothetical). In the field this window is entered
|
|
1306
1307
|
* whenever a caller re-issues `play` before the browser has acked the first
|
|
1307
1308
|
* (a slow scene boot outliving the relay's/SDK's own `play.start` timeout is
|
|
1308
|
-
* the documented trigger — `packages/
|
|
1309
|
+
* the documented trigger — `packages/volter-sdk/src/play/transport.ts`'s
|
|
1309
1310
|
* `PLAY_START_TIMEOUT_MS`/`editor-server.ts`'s `PLAY_COMMAND_TIMEOUT_MS` —
|
|
1310
1311
|
* and `play.start`'s own contract is explicitly "start (or restart)", so a
|
|
1311
1312
|
* caller retrying after a timeout is using the API as documented, not
|
|
@@ -1403,16 +1404,16 @@ async function startAutoLaunchedAdapterPlay(store: EditorShellStore): Promise<bo
|
|
|
1403
1404
|
/**
|
|
1404
1405
|
* Enter play mode: create a game canvas, start game, disable editor controls.
|
|
1405
1406
|
*
|
|
1406
|
-
* `explicitSeed` (D15/T-D15.6, objection-4 fix — `
|
|
1407
|
+
* `explicitSeed` (D15/T-D15.6, objection-4 fix — `volter-game-editor play --seed <n>`)
|
|
1407
1408
|
* — the CLI's `play` command relays it through as `cmd['seed']`
|
|
1408
1409
|
* (`command-listener.ts`'s `'play'` case); it is the "explicit config"
|
|
1409
1410
|
* leg of `resolveDeterminismSeed`'s precedence (highest — beats
|
|
1410
|
-
* `manifest.determinism.defaultSeed`/`?
|
|
1411
|
+
* `manifest.determinism.defaultSeed`/`?volter-seed=` on the editor's own
|
|
1411
1412
|
* page URL), threaded into whichever mount path this play resolves to
|
|
1412
1413
|
* below, exactly like `mountManifestRoots`'s own `opts.seed` already is
|
|
1413
1414
|
* for a standalone boot.
|
|
1414
1415
|
*
|
|
1415
|
-
* `runName` (optional — `
|
|
1416
|
+
* `runName` (optional — `volter-game-editor play --name <text>`) is FINDABILITY and nothing
|
|
1416
1417
|
* else: the server slugifies it into this run's `logs/play-*.jsonl` filename
|
|
1417
1418
|
* and its session-journal line, so "the run where I tested the boss fight" is
|
|
1418
1419
|
* a grep instead of timestamp archaeology. No registry, no uniqueness check —
|
|
@@ -1469,7 +1470,7 @@ async function enterPlayModeInner(
|
|
|
1469
1470
|
// `enterPlayMode` owns the begin/end pair; this function owns the marks.
|
|
1470
1471
|
// Play must never start while the boot-time project bootstrap is still in
|
|
1471
1472
|
// flight. Resolves immediately when no bootstrap is pending; gating HERE
|
|
1472
|
-
// rather than in the Play button covers every caller — UI, `
|
|
1473
|
+
// rather than in the Play button covers every caller — UI, `volter-game-editor play` relay,
|
|
1473
1474
|
// SDK.
|
|
1474
1475
|
markPlayBootPhase('waiting for the project bootstrap to settle');
|
|
1475
1476
|
await projectBootstrapSettled();
|
|
@@ -1712,9 +1713,9 @@ async function enterPlayModeInner(
|
|
|
1712
1713
|
entries,
|
|
1713
1714
|
width: rw,
|
|
1714
1715
|
height: rh,
|
|
1715
|
-
// D15/T-D15.6 — `
|
|
1716
|
+
// D15/T-D15.6 — `volter-game-editor play --seed`'s explicit config leg; `undefined`
|
|
1716
1717
|
// (the overwhelmingly common case) leaves `mountManifestRoots`'s own
|
|
1717
|
-
// manifest/`?
|
|
1718
|
+
// manifest/`?volter-seed=` precedence untouched.
|
|
1718
1719
|
seed: explicitSeed,
|
|
1719
1720
|
playtest,
|
|
1720
1721
|
});
|
|
@@ -1733,7 +1734,7 @@ async function enterPlayModeInner(
|
|
|
1733
1734
|
// PD-3 — close the window and report LOUDLY. A split does not stop the
|
|
1734
1735
|
// game (both copies run; they just disagree), so this is an error-level
|
|
1735
1736
|
// report rather than a throw: the failure mode being fixed is SILENCE,
|
|
1736
|
-
// not a crash. `collectState` carries the same reports to `
|
|
1737
|
+
// not a crash. `collectState` carries the same reports to `volter-game-editor status`,
|
|
1737
1738
|
// and `editorConsole.error` reaches the editor console panel and the
|
|
1738
1739
|
// play-log sink the CLI reads back.
|
|
1739
1740
|
for (const split of endProjectModuleSplitWatch(project.rootPath)) {
|
|
@@ -1744,7 +1745,7 @@ async function enterPlayModeInner(
|
|
|
1744
1745
|
// mount, and `mountManifestRoots` resolving IS that answer — including the
|
|
1745
1746
|
// roots' own async setup, which it awaits. So every one of these roots
|
|
1746
1747
|
// reports `declared`, and no measured wait stands anywhere behind them
|
|
1747
|
-
// (`readiness.ts`; published as `
|
|
1748
|
+
// (`readiness.ts`; published as `volter-game-editor status`'s `readiness` facet).
|
|
1748
1749
|
for (const rootId of _hostMountedReadyRootIds) {
|
|
1749
1750
|
recordRootReadiness({ rootId, mechanism: 'host-mount', source: 'declared', state: 'ready' });
|
|
1750
1751
|
}
|
|
@@ -1861,7 +1862,7 @@ async function enterPlayModeInner(
|
|
|
1861
1862
|
store.setEcsSyncTransform((id, obj) => {
|
|
1862
1863
|
// `commit` refuses an id this adapter cannot resolve rather than
|
|
1863
1864
|
// returning as if the write landed — so ask before driving it.
|
|
1864
|
-
if (!physics || physics
|
|
1865
|
+
if (!physics || physicsOwnerOf(physics, id) === 'unresolved') return;
|
|
1865
1866
|
physics.commit(id, {
|
|
1866
1867
|
position: obj.position.toArray() as [number, number, number],
|
|
1867
1868
|
rotation: obj.quaternion.toArray() as [number, number, number, number],
|
|
@@ -1928,7 +1929,7 @@ async function enterPlayModeInner(
|
|
|
1928
1929
|
* cardinality half of multiplayer authoring (see the `_additional` doc).
|
|
1929
1930
|
*
|
|
1930
1931
|
* Valid only while the primary is playing. The returned id is the instance's
|
|
1931
|
-
* mount id (what `?
|
|
1932
|
+
* mount id (what `?volter-mount=` carries and what `game.instance(id)`
|
|
1932
1933
|
* addresses). This does the INSTANCE subset of `enterPlayModeInner` and none
|
|
1933
1934
|
* of its session/focus work: it resolves its own mount epoch, mounts the roots
|
|
1934
1935
|
* into `container`, registers a performance source and its System adapters
|
|
@@ -2483,7 +2484,7 @@ editorHost().live.register({
|
|
|
2483
2484
|
});
|
|
2484
2485
|
|
|
2485
2486
|
// What only Play knows of the state report (`host.session.reportFacet`):
|
|
2486
|
-
// `
|
|
2487
|
+
// `volter-game-editor status --json` spreads these beside the host's own fields.
|
|
2487
2488
|
editorHost().session.reportFacet(() => ({
|
|
2488
2489
|
// The live loop's time-scale, so `play.status` reports the real applied
|
|
2489
2490
|
// value after a `set-time-scale` instead of an honest-gap null.
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
* EVERY RELAYED PLAY RECORDS, AND AN IDLE RUN STOPS ITSELF.
|
|
3
3
|
*
|
|
4
4
|
* The defect this closes is not a missing feature — the recorder
|
|
5
|
-
* (`gameplay-recording.ts`) already existed, behind `
|
|
5
|
+
* (`gameplay-recording.ts`) already existed, behind `volter-game-editor eval
|
|
6
6
|
* 'editor.recording.start()'`. The defect is that verifying a game through a
|
|
7
7
|
* single screenshot answers a temporal question by luck, and a capability an
|
|
8
8
|
* agent has to remember to switch on is one it does not use. So there is no
|
|
9
|
-
* flag: `
|
|
9
|
+
* flag: `volter-game-editor play` starts a recording, full stop, and the evidence exists
|
|
10
10
|
* whether or not anyone thought to ask for it.
|
|
11
11
|
*
|
|
12
12
|
* WHY THE RELAYED PLAY AND NOT THE PLAY BUTTON. The binding is in
|
|
13
|
-
* `play.command.ts`'s `'play'` handler, so it covers `
|
|
13
|
+
* `play.command.ts`'s `'play'` handler, so it covers `volter-game-editor play`, the SDK's
|
|
14
14
|
* `play.start`, and nothing else. Clicking Play in the editor is a HUMAN at
|
|
15
15
|
* the surface: they are the witness, the idle rule below ("no session
|
|
16
16
|
* commands and no player input") is meaningless for them, and auto-stopping
|
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
* script threw — leaves a growing file that nobody can open. The idle window
|
|
22
22
|
* is what converts an abandoned run into finished evidence.
|
|
23
23
|
*
|
|
24
|
-
* WHAT RECORDING DOES *NOT* DO: gate the still (2026-08-29). `
|
|
24
|
+
* WHAT RECORDING DOES *NOT* DO: gate the still (2026-08-29). `volter-game-editor screenshot`
|
|
25
25
|
* used to REFUSE while a recording was live, on the argument that a single
|
|
26
26
|
* frame answers a temporal question by luck. The argument is right and the
|
|
27
|
-
* refusal was still the defect: because play always records, one `
|
|
28
|
-
* killed `
|
|
27
|
+
* refusal was still the defect: because play always records, one `volter-game-editor play`
|
|
28
|
+
* killed `volter-game-editor screenshot` for the rest of the session — and "what does it
|
|
29
29
|
* look like" is a question a still answers exactly, and the one the whole
|
|
30
30
|
* look/modeling lane asks (measured, cold fox #3: the loop is play → look).
|
|
31
31
|
* Discouraging a wrong question by making a right one impossible is not a
|
|
@@ -56,7 +56,7 @@ import type { AudioRecordingHandle } from '@volter/editor-project/adapter';
|
|
|
56
56
|
*
|
|
57
57
|
* ONE constant, editor-side, and deliberately generous: the thing it must
|
|
58
58
|
* never do is cut a run that someone is still working with. Two minutes is
|
|
59
|
-
* longer than any gap between an agent's own commands (`
|
|
59
|
+
* longer than any gap between an agent's own commands (`volter-game-editor eval` round
|
|
60
60
|
* trips are seconds) and longer than a human's pause at the keyboard, while
|
|
61
61
|
* still being short enough that an abandoned run becomes a readable file
|
|
62
62
|
* inside the same working session rather than the next day.
|
|
@@ -100,7 +100,7 @@ let live: LivePlayRecording | null = null;
|
|
|
100
100
|
* The recorder's own "already active" guard is released at the END of
|
|
101
101
|
* `stopGameplayRecording` (it awaits the in-flight frame, the recorder's stop
|
|
102
102
|
* event, and the chunk uploads). `live` is cleared at the START. So a restart —
|
|
103
|
-
* `
|
|
103
|
+
* `volter-game-editor play` over a running play, which tears down through `exitPlayMode`'s
|
|
104
104
|
* fire-and-forget teardown and then starts the next recording — would look
|
|
105
105
|
* finished to this module while the recorder was still busy, and the next start
|
|
106
106
|
* would refuse. This handle is what a start waits on; without it the second
|
|
@@ -169,7 +169,7 @@ export function lastPlayRecording(): GameplayRecordingCapture | null {
|
|
|
169
169
|
* Two callers, and the pair is the whole definition of "idle": every relayed
|
|
170
170
|
* session command (`host.session.onCommandDispatched`, subscribed below) and
|
|
171
171
|
* every player input event (the listeners installed below). A run is idle only
|
|
172
|
-
* when BOTH have gone quiet — an agent driving through `
|
|
172
|
+
* when BOTH have gone quiet — an agent driving through `volter-game-editor eval` and a human
|
|
173
173
|
* holding a movement key are each, alone, enough to keep it alive.
|
|
174
174
|
*/
|
|
175
175
|
export function notePlayActivity(): void {
|
|
@@ -194,7 +194,7 @@ function installInputListeners(): () => void {
|
|
|
194
194
|
*
|
|
195
195
|
* Never throws: a browser that cannot record must not be a browser that cannot
|
|
196
196
|
* PLAY. The failure is reported to the editor console (which is what
|
|
197
|
-
* `
|
|
197
|
+
* `volter-game-editor console` and every command's trailing console summary read) and play
|
|
198
198
|
* continues with no clip, rather than an ack that refuses a working game
|
|
199
199
|
* because its evidence path is unavailable.
|
|
200
200
|
*/
|
|
@@ -291,7 +291,7 @@ export async function endPlayRecording(
|
|
|
291
291
|
return stop;
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
-
/** One sentence, used by both the auto-stop note and the `
|
|
294
|
+
/** One sentence, used by both the auto-stop note and the `volter-game-editor screenshot`
|
|
295
295
|
* refusal, so the two doors cannot describe the same clip differently. */
|
|
296
296
|
export function describeCapture(capture: GameplayRecordingCapture): string {
|
|
297
297
|
const seconds = Math.round(capture.durationMs / 100) / 10;
|
|
@@ -329,7 +329,7 @@ async function autoStopIdlePlay(): Promise<void> {
|
|
|
329
329
|
|
|
330
330
|
// HALF OF "IS THIS RUN IDLE" — the other half is player input, watched
|
|
331
331
|
// above. Every relayed command counts: an agent that is still driving a game
|
|
332
|
-
// through `
|
|
332
|
+
// through `volter-game-editor eval` is not idle, whatever the command was, and a stamp
|
|
333
333
|
// filed per verb would quietly exclude whichever verb someone forgot.
|
|
334
334
|
// No-op unless a recording is live.
|
|
335
335
|
editorHost().session.onCommandDispatched(() => notePlayActivity());
|
package/src/play-bar/PlayBar.tsx
CHANGED
|
@@ -67,7 +67,7 @@ function SaveRegimeIndicator({ regime }: { regime: 'ephemeral' | null }) {
|
|
|
67
67
|
<span
|
|
68
68
|
role="img"
|
|
69
69
|
aria-label={label}
|
|
70
|
-
className="
|
|
70
|
+
className="volter-play-edit-regime"
|
|
71
71
|
data-testid="play-edit-regime"
|
|
72
72
|
data-regime={regime}
|
|
73
73
|
title={label}
|
|
@@ -141,7 +141,7 @@ export function PlayBarView({
|
|
|
141
141
|
<Inline
|
|
142
142
|
role="toolbar"
|
|
143
143
|
aria-label="Game transport"
|
|
144
|
-
className="
|
|
144
|
+
className="volter-playbar"
|
|
145
145
|
gap={2}
|
|
146
146
|
align="center"
|
|
147
147
|
>
|
|
@@ -231,7 +231,7 @@ export function PlayBar({ placement = 'launcher' }: { placement?: 'launcher' | '
|
|
|
231
231
|
<Inline
|
|
232
232
|
role="toolbar"
|
|
233
233
|
aria-label="Game transport"
|
|
234
|
-
className="
|
|
234
|
+
className="volter-playbar"
|
|
235
235
|
gap={2}
|
|
236
236
|
align="center"
|
|
237
237
|
>
|
|
@@ -336,7 +336,7 @@ function PlayOptions() {
|
|
|
336
336
|
const { settings } = editorHost();
|
|
337
337
|
const keepPanelsVisible = useSyncExternalStore(
|
|
338
338
|
settings.subscribe,
|
|
339
|
-
() => settings.get('
|
|
339
|
+
() => settings.get('volter.play.keepPanelsVisible') === true,
|
|
340
340
|
);
|
|
341
341
|
const [open, setOpen] = useState(false);
|
|
342
342
|
const anchor = useRef<HTMLButtonElement>(null);
|
|
@@ -358,7 +358,7 @@ function PlayOptions() {
|
|
|
358
358
|
role="menuitemcheckbox"
|
|
359
359
|
aria-checked={keepPanelsVisible}
|
|
360
360
|
onSelect={() => {
|
|
361
|
-
settings.set('
|
|
361
|
+
settings.set('volter.play.keepPanelsVisible', !keepPanelsVisible);
|
|
362
362
|
setOpen(false);
|
|
363
363
|
}}
|
|
364
364
|
>
|
|
@@ -420,14 +420,14 @@ function RunPicker({ run, disabled }: { readonly run: RunState; readonly disable
|
|
|
420
420
|
const selected = run.configurations.find((c) => c.id === run.selectedId);
|
|
421
421
|
const label = selected?.id === PLAY_ID ? 'Play' : (selected?.id ?? 'Play');
|
|
422
422
|
return (
|
|
423
|
-
<div className="
|
|
423
|
+
<div className="volter-playbar-popover-anchor">
|
|
424
424
|
<Button
|
|
425
425
|
ref={ref}
|
|
426
426
|
variant="ghost"
|
|
427
427
|
size="compact"
|
|
428
428
|
aria-haspopup="menu"
|
|
429
429
|
aria-expanded={open}
|
|
430
|
-
aria-controls={open ? '
|
|
430
|
+
aria-controls={open ? 'volter-run-configuration-menu' : undefined}
|
|
431
431
|
data-testid="run-configuration-picker"
|
|
432
432
|
disabled={disabled}
|
|
433
433
|
onClick={() => setOpen((value) => !value)}
|
|
@@ -438,7 +438,7 @@ function RunPicker({ run, disabled }: { readonly run: RunState; readonly disable
|
|
|
438
438
|
</Button>
|
|
439
439
|
{open && (
|
|
440
440
|
<AnchoredMenu
|
|
441
|
-
id="
|
|
441
|
+
id="volter-run-configuration-menu"
|
|
442
442
|
anchorRef={ref}
|
|
443
443
|
align="start"
|
|
444
444
|
gap={4}
|
|
@@ -55,14 +55,14 @@ export function InstanceCountPicker() {
|
|
|
55
55
|
|
|
56
56
|
const count = playerCountFromExtras(extra);
|
|
57
57
|
return (
|
|
58
|
-
<div className="
|
|
58
|
+
<div className="volter-playbar-popover-anchor">
|
|
59
59
|
<Button
|
|
60
60
|
ref={ref}
|
|
61
61
|
variant="ghost"
|
|
62
62
|
size="comfortable"
|
|
63
63
|
aria-haspopup="menu"
|
|
64
64
|
aria-expanded={open}
|
|
65
|
-
aria-controls={open ? '
|
|
65
|
+
aria-controls={open ? 'volter-player-count-menu' : undefined}
|
|
66
66
|
onClick={() => setOpen((value) => !value)}
|
|
67
67
|
title={`${instanceLabel(count)} — choose how many runtimes Play mounts side by side`}
|
|
68
68
|
>
|
|
@@ -73,7 +73,7 @@ export function InstanceCountPicker() {
|
|
|
73
73
|
</Button>
|
|
74
74
|
{open && (
|
|
75
75
|
<AnchoredMenu
|
|
76
|
-
id="
|
|
76
|
+
id="volter-player-count-menu"
|
|
77
77
|
anchorRef={ref}
|
|
78
78
|
gap={4}
|
|
79
79
|
onDismiss={() => setOpen(false)}
|
|
@@ -69,7 +69,7 @@ function DrawRow({
|
|
|
69
69
|
data-testid="frame-draw-row"
|
|
70
70
|
data-draw-index={draw.index}
|
|
71
71
|
data-selected={selected || undefined}
|
|
72
|
-
className="
|
|
72
|
+
className="volter-frame-draw-row"
|
|
73
73
|
onClick={onSelect}
|
|
74
74
|
>
|
|
75
75
|
<span style={{ ...MONO, color: themeVars.content.dim, width: 34, display: 'inline-block' }}>
|
|
@@ -381,7 +381,7 @@ export function FrameDebuggerPanel() {
|
|
|
381
381
|
data-testid="frame-debugger"
|
|
382
382
|
// §2.31 P2 amendment: text-dense output region reads over a local
|
|
383
383
|
// frosted layer (inert for non-frost themes).
|
|
384
|
-
className="
|
|
384
|
+
className="volter-content-frost"
|
|
385
385
|
style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', fontSize: 12 }}
|
|
386
386
|
>
|
|
387
387
|
{/* Header: capture control + honest scope statement + history strip */}
|
|
@@ -68,7 +68,7 @@ function downloadCapture(profiler: PerformanceProfiler): void {
|
|
|
68
68
|
const url = URL.createObjectURL(new Blob([profiler.exportJSON()], { type: 'application/json' }));
|
|
69
69
|
const anchor = document.createElement('a');
|
|
70
70
|
anchor.href = url;
|
|
71
|
-
anchor.download = `
|
|
71
|
+
anchor.download = `volter-performance-${new Date().toISOString().replaceAll(':', '-')}.json`;
|
|
72
72
|
anchor.click();
|
|
73
73
|
URL.revokeObjectURL(url);
|
|
74
74
|
}
|
|
@@ -84,7 +84,7 @@ function downloadTrace(profiler: PerformanceProfiler, processName: string): void
|
|
|
84
84
|
const url = URL.createObjectURL(new Blob([JSON.stringify(trace)], { type: 'application/json' }));
|
|
85
85
|
const anchor = document.createElement('a');
|
|
86
86
|
anchor.href = url;
|
|
87
|
-
anchor.download = `
|
|
87
|
+
anchor.download = `volter-trace-${new Date().toISOString().replaceAll(':', '-')}.json`;
|
|
88
88
|
anchor.click();
|
|
89
89
|
URL.revokeObjectURL(url);
|
|
90
90
|
}
|
|
@@ -874,7 +874,7 @@ export function PerformancePanel() {
|
|
|
874
874
|
|
|
875
875
|
return (
|
|
876
876
|
<div
|
|
877
|
-
className="
|
|
877
|
+
className="volter-content-frost"
|
|
878
878
|
style={{
|
|
879
879
|
height: '100%',
|
|
880
880
|
overflow: 'auto',
|
|
@@ -937,7 +937,7 @@ export function PerformancePanel() {
|
|
|
937
937
|
<Button
|
|
938
938
|
size="compact"
|
|
939
939
|
data-testid="profiler-export-trace"
|
|
940
|
-
onClick={() => downloadTrace(profiler, source?.label ?? '
|
|
940
|
+
onClick={() => downloadTrace(profiler, source?.label ?? 'volter')}
|
|
941
941
|
>
|
|
942
942
|
Export Chrome trace
|
|
943
943
|
</Button>
|
|
@@ -146,7 +146,7 @@ async function seedRootViewportFromManifest(): Promise<void> {
|
|
|
146
146
|
const current = getRootCanvasViewport();
|
|
147
147
|
if (current.width !== null || current.height !== null) return;
|
|
148
148
|
try {
|
|
149
|
-
const text = await readProjectTextFile('
|
|
149
|
+
const text = await readProjectTextFile('volter.project.json');
|
|
150
150
|
if (!text) return;
|
|
151
151
|
const manifest = JSON.parse(text) as {
|
|
152
152
|
resolution?: { width?: unknown; height?: unknown };
|
|
@@ -282,7 +282,7 @@ export async function mountReactDesignLayer(
|
|
|
282
282
|
// The join is by component NAME, not function identity, and that is
|
|
283
283
|
// measured rather than assumed: a story module is imported at its own
|
|
284
284
|
// `/@fs/<path>?t=<now>` url (`story-discovery.ts`) while a root entry is
|
|
285
|
-
// imported at the mount epoch's `?
|
|
285
|
+
// imported at the mount epoch's `?volter-mount=<n>` url
|
|
286
286
|
// (`project-module-url.ts`), and browser ES-module identity is per-url — so
|
|
287
287
|
// the component object a story's meta holds is never the object the entry
|
|
288
288
|
// loader returns. `pickComponentPreviewStory` (`story-registry.ts`) is the
|
|
@@ -328,7 +328,7 @@ export async function mountReactDesignLayer(
|
|
|
328
328
|
// names an action that cannot be taken. Opening on the first story is that
|
|
329
329
|
// board's documented behaviour, not a degraded one. Measured on the
|
|
330
330
|
// repo-vendored `racing-game` ingest, whose manifest declares one ingest
|
|
331
|
-
// root and no dom root: a permanent unresolvable warning on every `
|
|
331
|
+
// root and no dom root: a permanent unresolvable warning on every `volter`
|
|
332
332
|
// command, for a board that was working exactly as designed.
|
|
333
333
|
if (candidate.path && !defaultPortableStory) {
|
|
334
334
|
editorConsole.warn(
|
|
@@ -512,8 +512,8 @@ export async function mountReactDesignLayer(
|
|
|
512
512
|
designTokens: () => {
|
|
513
513
|
const content = board.activeContent();
|
|
514
514
|
const scopeElement =
|
|
515
|
-
content.closest?.('[data-
|
|
516
|
-
content.querySelector?.('[data-
|
|
515
|
+
content.closest?.('[data-volter-game-styles]') ??
|
|
516
|
+
content.querySelector?.('[data-volter-game-styles]') ??
|
|
517
517
|
content;
|
|
518
518
|
return getDesignTokens(
|
|
519
519
|
getComputedStyle(scopeElement) as unknown as Parameters<typeof getDesignTokens>[0],
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* DIRECTLY and record the same write into an in-memory
|
|
25
25
|
* {@link DomStyleEdits} document that is never saved anywhere. (Until
|
|
26
26
|
* 2026-08-02 that document was flushed to a per-world JSON sidecar under
|
|
27
|
-
* the project's `.
|
|
27
|
+
* the project's `.volter/`; the owner deleted that system outright — no
|
|
28
28
|
* compat read, no replacement.) There is NO `SourceWriteBackend` import
|
|
29
29
|
* anywhere in this file and NO `/__ui-source` traffic of any kind, ever —
|
|
30
30
|
* this is the one hard invariant the design doc's §4 NOTs pin (AC-K7).
|
|
@@ -328,8 +328,8 @@ export class DomAuthoringAdapter implements AuthoringAdapter {
|
|
|
328
328
|
* root: a fresh snapshot every call (same "the DOM is live and may
|
|
329
329
|
* re-render, never cached" discipline as
|
|
330
330
|
* `hierarchy.roots()`/`hierarchy.node()` above), not a count taken once at
|
|
331
|
-
* construction. Backs `
|
|
332
|
-
* — one of the four legs
|
|
331
|
+
* construction. Backs `__volterIngestReact.domEvidence().hierarchyNodeCount`
|
|
332
|
+
* — one of the four legs the react-ingest MOUNTED bar requires
|
|
333
333
|
* (`clearsReactIngestMountedBar`, `packages/editor/src/doctor/report.ts`).
|
|
334
334
|
*/
|
|
335
335
|
nodeCount(): number {
|
|
@@ -415,7 +415,7 @@ function GradientRow({ prop, raw, disabled, testId, commit }: KindRowProps): Rea
|
|
|
415
415
|
<Button
|
|
416
416
|
type="button"
|
|
417
417
|
variant="secondary"
|
|
418
|
-
className="
|
|
418
|
+
className="volter-field-trigger"
|
|
419
419
|
data-testid={testId}
|
|
420
420
|
disabled={disabled}
|
|
421
421
|
onClick={() => commit(prop.path, composeGradient(DEFAULT_GRADIENT))}
|
|
@@ -601,7 +601,7 @@ function CheckboxRow({ prop, raw, disabled, testId, commit }: KindRowProps): Rea
|
|
|
601
601
|
}}
|
|
602
602
|
>
|
|
603
603
|
<Checkbox
|
|
604
|
-
className="
|
|
604
|
+
className="volter-checkbox"
|
|
605
605
|
data-testid={testId}
|
|
606
606
|
checked={raw !== false}
|
|
607
607
|
disabled={disabled}
|