@volter/editor-game 0.5.68 → 0.5.70
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 +2 -2
- 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 +11 -11
- 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 +4 -4
- 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 +3 -3
- 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 +11 -11
- 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 +15 -15
- 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 +10 -10
- 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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THE BUILD PROFILES PANEL — the configuration surface the `build-profiles`
|
|
3
3
|
* document draws: the project's build-role configurations as a target list,
|
|
4
|
-
* the name/resolution/app-id fields it writes back into `
|
|
4
|
+
* the name/resolution/app-id fields it writes back into `volter.project.json`
|
|
5
5
|
* through the project's file history, the Build trigger, and the finished
|
|
6
6
|
* build's report. The LOG is not here: it streams into the Build Output tab
|
|
7
7
|
* (the native Output panel’s Build channel), which is the whole point of the split — a
|
|
@@ -49,7 +49,7 @@ interface ProjectConfig {
|
|
|
49
49
|
appId: string;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
const MANIFEST_PATH = '
|
|
52
|
+
const MANIFEST_PATH = 'volter.project.json';
|
|
53
53
|
|
|
54
54
|
const DEFAULT_CONFIG: ProjectConfig = {
|
|
55
55
|
name: 'My Game',
|
|
@@ -216,7 +216,7 @@ export function BuildProfilesPanel() {
|
|
|
216
216
|
<div style={fieldRowStyle}>
|
|
217
217
|
<label style={labelStyle}>Name</label>
|
|
218
218
|
<TextInput
|
|
219
|
-
className="
|
|
219
|
+
className="volter-input"
|
|
220
220
|
style={{ flex: 1 }}
|
|
221
221
|
value={config.name}
|
|
222
222
|
onChange={(e) => updateConfig({ name: e.target.value })}
|
|
@@ -228,7 +228,7 @@ export function BuildProfilesPanel() {
|
|
|
228
228
|
<div style={{ display: 'flex', alignItems: 'center', gap: spaceVar[2], flex: 1 }}>
|
|
229
229
|
<TextInput
|
|
230
230
|
type="number"
|
|
231
|
-
className="
|
|
231
|
+
className="volter-input"
|
|
232
232
|
style={{ width: 0, flex: 1 }}
|
|
233
233
|
value={config.resolution.width}
|
|
234
234
|
onChange={(e) => {
|
|
@@ -239,7 +239,7 @@ export function BuildProfilesPanel() {
|
|
|
239
239
|
<span style={{ fontSize: fontSizeVar.sm, color: text[3] }}>×</span>
|
|
240
240
|
<TextInput
|
|
241
241
|
type="number"
|
|
242
|
-
className="
|
|
242
|
+
className="volter-input"
|
|
243
243
|
style={{ width: 0, flex: 1 }}
|
|
244
244
|
value={config.resolution.height}
|
|
245
245
|
onChange={(e) => {
|
|
@@ -253,7 +253,7 @@ export function BuildProfilesPanel() {
|
|
|
253
253
|
<div style={fieldRowStyle}>
|
|
254
254
|
<label style={labelStyle}>App ID</label>
|
|
255
255
|
<TextInput
|
|
256
|
-
className="
|
|
256
|
+
className="volter-input"
|
|
257
257
|
style={{ flex: 1 }}
|
|
258
258
|
value={config.appId}
|
|
259
259
|
onChange={(e) => updateConfig({ appId: e.target.value })}
|
|
@@ -309,7 +309,7 @@ export function BuildProfilesPanel() {
|
|
|
309
309
|
<a
|
|
310
310
|
href={getDownloadUrl(artifact)}
|
|
311
311
|
download={artifact}
|
|
312
|
-
className="
|
|
312
|
+
className="volter-btn"
|
|
313
313
|
data-variant="secondary"
|
|
314
314
|
data-size="default"
|
|
315
315
|
style={{
|
|
@@ -394,7 +394,7 @@ function BuildReportSummary({ report }: { report: BuildReport }) {
|
|
|
394
394
|
position: 'absolute',
|
|
395
395
|
inset: 0,
|
|
396
396
|
width: `${Math.max(2, (file.bytes / largest) * 100)}%`,
|
|
397
|
-
background: 'color-mix(in srgb, var(--
|
|
397
|
+
background: 'color-mix(in srgb, var(--volter-accent) 16%, transparent)',
|
|
398
398
|
}}
|
|
399
399
|
/>
|
|
400
400
|
<div
|
|
@@ -552,8 +552,8 @@ function CanvasBoardContent({ active }: WorkspaceDocumentContentProps) {
|
|
|
552
552
|
return (
|
|
553
553
|
<div
|
|
554
554
|
data-testid="canvas-board"
|
|
555
|
-
data-
|
|
556
|
-
data-
|
|
555
|
+
data-volter-backdrop-color={active ? REACT_DESIGN_CANVAS_COLOR : undefined}
|
|
556
|
+
data-volter-backdrop-policy={active ? 'dark-frost' : undefined}
|
|
557
557
|
style={{
|
|
558
558
|
position: 'absolute',
|
|
559
559
|
inset: 0,
|
|
@@ -51,6 +51,6 @@ export const canvasComponentBoard: ComponentBoard = {
|
|
|
51
51
|
`This project has no ${CANVAS_COMPONENTS_TITLE} board (${CANVAS_COMPONENTS_DOCUMENT_ID}): ` +
|
|
52
52
|
"board presence keys on STORIES, not on roots, and none of this project's stories declares " +
|
|
53
53
|
'the "canvas" medium. Declare it — a manifest root entry naming the story\'s module, or a ' +
|
|
54
|
-
'`
|
|
54
|
+
'`volter.adapter.ts` regionInclude that covers it — and the board appears. `volter-game-editor console` ' +
|
|
55
55
|
'names every story it could not place.',
|
|
56
56
|
};
|
|
@@ -120,7 +120,7 @@ function createDesignGame(): GameInternal {
|
|
|
120
120
|
* which part of the unbounded authored plane is visible. */
|
|
121
121
|
function createDesignCanvas(): HTMLCanvasElement {
|
|
122
122
|
const canvas = document.createElement('canvas');
|
|
123
|
-
canvas.dataset['
|
|
123
|
+
canvas.dataset['volterCanvasSceneSurface'] = 'true';
|
|
124
124
|
canvas.style.position = 'absolute';
|
|
125
125
|
canvas.style.inset = '0';
|
|
126
126
|
canvas.style.width = '100%';
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* It reads the session's own singletons plus two project files the browser
|
|
6
6
|
* cannot read for itself, and hands the facts to the pure derivation. The
|
|
7
|
-
* report goes out the doors coverage already uses — `
|
|
7
|
+
* report goes out the doors coverage already uses — `volter-game-editor status`'s
|
|
8
8
|
* `projectCoverage` facet, and the editor console via
|
|
9
9
|
* `coverage/session-coverage.ts`, which unions this family with the other two
|
|
10
10
|
* so one headline counts them all. No new transport, no new panel: the console
|
|
@@ -59,7 +59,7 @@ let _filesFor: string | null = null;
|
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
61
|
* Drop the cached file facts so the next read re-asks the dev server. Called
|
|
62
|
-
* on `
|
|
62
|
+
* on `volter-game-editor restart` (a restart is precisely "my project's files changed —
|
|
63
63
|
* re-read them"; the probe-measured failure was the dependency detector
|
|
64
64
|
* grading rows against a package.json edit two restarts old) and by tests.
|
|
65
65
|
*/
|
|
@@ -148,7 +148,7 @@ export function projectVerbFacts(): ProjectVerbFacts | null {
|
|
|
148
148
|
|
|
149
149
|
/**
|
|
150
150
|
* The `project.*` report for the open project, re-derived on every read — the
|
|
151
|
-
* scripts a project declares change while it is open (`
|
|
151
|
+
* scripts a project declares change while it is open (`volter-game-editor add` writes them),
|
|
152
152
|
* so a cached answer would start lying.
|
|
153
153
|
*/
|
|
154
154
|
export function projectVerbCoverage(): CapabilityCoverageReport | null {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* It reads the session's own singletons and hands the facts to the pure
|
|
6
6
|
* derivation. The report goes out the two doors coverage already uses —
|
|
7
|
-
* `
|
|
7
|
+
* `volter-game-editor status`'s `systemCoverage` facet, and the editor console via
|
|
8
8
|
* `coverage/session-coverage.ts`, which unions this family with the others so
|
|
9
9
|
* one headline counts them all — so an unbound slot becomes a STANDING
|
|
10
10
|
* condition rather than a one-off line. No new transport, no new panel, and no
|
|
@@ -129,8 +129,8 @@ export function nativeSystemCoverage(): CapabilityCoverageReport | null {
|
|
|
129
129
|
// NetworkingAdapter for a single-player game, telling its author to
|
|
130
130
|
// register one because "this project declares a `server` block" when it
|
|
131
131
|
// declares nothing of the sort. A matchmaker is a listener; no port, no
|
|
132
|
-
// server. (`
|
|
133
|
-
// `packages/
|
|
132
|
+
// server. (`volter-game-editor edit`'s auto-boot had the identical bug — see
|
|
133
|
+
// `packages/volter-cli/src/colyseus-autoboot.ts`'s `readDeclaredProcess`.)
|
|
134
134
|
declaresServer: (project.config.configurations ?? []).some(
|
|
135
135
|
(c) => c.kind === 'process' && typeof (c as { port?: unknown }).port === 'number',
|
|
136
136
|
),
|
|
@@ -23,13 +23,13 @@
|
|
|
23
23
|
* Two doors, one derivation — the same shape `ingest/mount-coverage.ts` already
|
|
24
24
|
* uses:
|
|
25
25
|
*
|
|
26
|
-
* - `
|
|
26
|
+
* - `volter-game-editor status`, through {@link rootCoverageFacet}, so an agent reads the
|
|
27
27
|
* same answer without opening the editor;
|
|
28
28
|
* - the editor console, through `coverage/session-coverage.ts`, which unions
|
|
29
29
|
* this family with the game- and project-scoped ones so the warning's
|
|
30
30
|
* headline counts every seam rather than this family's 21. The console
|
|
31
31
|
* ledger turns that into a standing unresolved condition, loud on every
|
|
32
|
-
* `
|
|
32
|
+
* `volter` command until it is closed.
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
35
|
import { measureAdapter } from '../host/adapter-reach';
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
*
|
|
25
25
|
* It owns assembly and the console door. Every family still derives itself, in
|
|
26
26
|
* its own file, from its own facts; nothing here decides a verdict. The
|
|
27
|
-
* per-family facets on `
|
|
27
|
+
* per-family facets on `volter-game-editor status` (`command-listener.ts`) stay separate for
|
|
28
28
|
* the same reason they always were — a machine reader wants the rows keyed by
|
|
29
29
|
* subject, and it is the HUMAN-facing sentence that has to be a union.
|
|
30
30
|
*/
|
|
@@ -90,7 +90,7 @@ export function sessionCoverageReport(
|
|
|
90
90
|
*
|
|
91
91
|
* Keyed on the rendered TEXT rather than a mount or project token, because the
|
|
92
92
|
* union's inputs come and go inside one session (a root mounts, a design
|
|
93
|
-
* session attaches, `
|
|
93
|
+
* session attaches, `volter-game-editor add` lands a script) and the honest rule is "say it
|
|
94
94
|
* when the answer changes". A recurrence after a real change is a new condition
|
|
95
95
|
* and gets said again; the server ledger sums repeats of an unchanged one.
|
|
96
96
|
*/
|
|
@@ -73,10 +73,10 @@ function RailColumn({ children }: { readonly children: ReactNode }) {
|
|
|
73
73
|
width: RAIL_WIDTH,
|
|
74
74
|
height: 600,
|
|
75
75
|
display: 'flex',
|
|
76
|
-
border: '1px solid var(--
|
|
76
|
+
border: '1px solid var(--volter-border-2)',
|
|
77
77
|
borderRadius: 8,
|
|
78
78
|
overflow: 'hidden',
|
|
79
|
-
background: 'var(--
|
|
79
|
+
background: 'var(--volter-bg-1)',
|
|
80
80
|
}}
|
|
81
81
|
>
|
|
82
82
|
{children}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* W2c (F13) — Game-tab device preset dropdown. Chrome-device-mode-style:
|
|
3
3
|
* picking a preset constrains the game mount to that device's CSS resolution
|
|
4
4
|
* + DPR (letterboxed/scaled by `GamePanel`), phone/tablet presets add the
|
|
5
|
-
* safe-area overlay + `--
|
|
5
|
+
* safe-area overlay + `--volter-safe-area-inset-*` CSS vars and auto-enable
|
|
6
6
|
* pointer-as-touch. State lives in `../device-preview.ts`; this component is
|
|
7
7
|
* pure UI over it.
|
|
8
8
|
*/
|
|
@@ -30,18 +30,18 @@ export function DevicePresetPicker({
|
|
|
30
30
|
const isFit = active.kind === 'fit';
|
|
31
31
|
|
|
32
32
|
return (
|
|
33
|
-
<div className="
|
|
33
|
+
<div className="volter-playbar-popover-anchor">
|
|
34
34
|
<Button
|
|
35
35
|
ref={ref}
|
|
36
36
|
variant="ghost"
|
|
37
37
|
size="comfortable"
|
|
38
38
|
aria-haspopup="menu"
|
|
39
39
|
aria-expanded={open}
|
|
40
|
-
aria-controls={open ? '
|
|
40
|
+
aria-controls={open ? 'volter-device-preset-menu' : undefined}
|
|
41
41
|
data-testid="device-preset-picker"
|
|
42
42
|
onClick={() => setOpen((value) => !value)}
|
|
43
43
|
title={
|
|
44
|
-
'Device preview — constrain the game to a device resolution + DPR. Phone/tablet presets show the safe area, set --
|
|
44
|
+
'Device preview — constrain the game to a device resolution + DPR. Phone/tablet presets show the safe area, set --volter-safe-area-inset-top/right/bottom/left CSS vars on the game mount, and send mouse input as touch.'
|
|
45
45
|
}
|
|
46
46
|
>
|
|
47
47
|
<EditorIcon icon={faMobileScreenButton} size="md" />
|
|
@@ -49,9 +49,9 @@ export function DevicePresetPicker({
|
|
|
49
49
|
</Button>
|
|
50
50
|
{open && (
|
|
51
51
|
<AnchoredMenu
|
|
52
|
-
id="
|
|
52
|
+
id="volter-device-preset-menu"
|
|
53
53
|
anchorRef={ref}
|
|
54
|
-
className="
|
|
54
|
+
className="volter-resolution-menu"
|
|
55
55
|
gap={4}
|
|
56
56
|
onDismiss={() => setOpen(false)}
|
|
57
57
|
>
|
|
@@ -60,7 +60,7 @@ function GameDebugMenu() {
|
|
|
60
60
|
data-testid="game-debug-menu"
|
|
61
61
|
aria-haspopup="menu"
|
|
62
62
|
aria-expanded={open}
|
|
63
|
-
aria-controls={open ? '
|
|
63
|
+
aria-controls={open ? 'volter-game-debug-menu' : undefined}
|
|
64
64
|
onClick={() => setOpen((value) => !value)}
|
|
65
65
|
>
|
|
66
66
|
<span>Debug</span>
|
|
@@ -68,7 +68,7 @@ function GameDebugMenu() {
|
|
|
68
68
|
</Button>
|
|
69
69
|
{open && (
|
|
70
70
|
<AnchoredMenu
|
|
71
|
-
id="
|
|
71
|
+
id="volter-game-debug-menu"
|
|
72
72
|
anchorRef={ref}
|
|
73
73
|
gap={3}
|
|
74
74
|
onDismiss={() => setOpen(false)}
|
|
@@ -74,7 +74,7 @@ import {
|
|
|
74
74
|
* surfaces (stacked canvases + React DOM layers). */
|
|
75
75
|
export function collectGameSurfaces(container: HTMLElement): Element[] {
|
|
76
76
|
return Array.from(container.children).filter(
|
|
77
|
-
(node) => (node as HTMLElement).dataset?.['
|
|
77
|
+
(node) => (node as HTMLElement).dataset?.['volterRootSurface'] === 'true',
|
|
78
78
|
);
|
|
79
79
|
}
|
|
80
80
|
|
|
@@ -250,7 +250,7 @@ function InstanceViewport({ index }: { index: number }) {
|
|
|
250
250
|
focused={mountedId != null && focused === mountedId}
|
|
251
251
|
onFocus={mountedId ? () => setFocusedInstance(mountedId) : undefined}
|
|
252
252
|
>
|
|
253
|
-
<div ref={ref} data-
|
|
253
|
+
<div ref={ref} data-volter-instance-viewport="" style={{ position: 'absolute', inset: 0 }} />
|
|
254
254
|
</InstanceSlot>
|
|
255
255
|
);
|
|
256
256
|
}
|
|
@@ -355,7 +355,7 @@ export function GamePanel({ style }: { style?: React.CSSProperties }) {
|
|
|
355
355
|
// the same call — and switching resolution DURING play would silently drop
|
|
356
356
|
// the running game's canvases with the removed element. Only nodes the game
|
|
357
357
|
// HOST mounted are
|
|
358
|
-
// stashed (world surfaces carry `data-
|
|
358
|
+
// stashed (world surfaces carry `data-volter-root-surface`): the fill/device branches are keyed below so
|
|
359
359
|
// React really remounts, but if it ever aliases the container DOM node
|
|
360
360
|
// again, a blind childNodes copy would capture React-owned children too —
|
|
361
361
|
// that exact aliasing put the incoming inner div in the stash and made
|
|
@@ -412,7 +412,7 @@ export function GamePanel({ style }: { style?: React.CSSProperties }) {
|
|
|
412
412
|
// which consults `deviceEmulatedPixelRatio()` once the session exists.)
|
|
413
413
|
// What size this game is PRESENTED at. Two things can name one and the precedence lives in
|
|
414
414
|
// `resolvePresentedSize`: a device preset the user picked wins, and otherwise the project's own
|
|
415
|
-
// declared `resolution` (`
|
|
415
|
+
// declared `resolution` (`volter.project.json`) puts the game on screen at its authored logical
|
|
416
416
|
// size, uniformly scaled and letterboxed by the branch below — presentation scaling, never a
|
|
417
417
|
// stretch mode. A project that declares neither fills the panel exactly as before.
|
|
418
418
|
const declaredResolution = project?.config.resolution;
|
|
@@ -474,16 +474,16 @@ export function GamePanel({ style }: { style?: React.CSSProperties }) {
|
|
|
474
474
|
|
|
475
475
|
// Safe-area contract: the game MOUNT container always carries the
|
|
476
476
|
// inset CSS vars (0px unless the active preset declares insets), so game
|
|
477
|
-
// UI can style with `var(--
|
|
477
|
+
// UI can style with `var(--volter-safe-area-inset-top, 0px)` etc. Real
|
|
478
478
|
// `env(safe-area-inset-*)` cannot be injected from a same-document host
|
|
479
479
|
// (env() is UA-supplied for the top-level viewport only) — the vars + the
|
|
480
480
|
// visual overlay below ARE the emulation.
|
|
481
481
|
const safeArea = preset.safeArea;
|
|
482
482
|
const safeAreaVars = {
|
|
483
|
-
'--
|
|
484
|
-
'--
|
|
485
|
-
'--
|
|
486
|
-
'--
|
|
483
|
+
'--volter-safe-area-inset-top': `${safeArea?.top ?? 0}px`,
|
|
484
|
+
'--volter-safe-area-inset-right': `${safeArea?.right ?? 0}px`,
|
|
485
|
+
'--volter-safe-area-inset-bottom': `${safeArea?.bottom ?? 0}px`,
|
|
486
|
+
'--volter-safe-area-inset-left': `${safeArea?.left ?? 0}px`,
|
|
487
487
|
} as React.CSSProperties;
|
|
488
488
|
|
|
489
489
|
return (
|
|
@@ -138,7 +138,7 @@ export function InstanceInspectorPicker() {
|
|
|
138
138
|
const warning = (diagnostics?.loop.firedWhileHeld ?? 0) > 0;
|
|
139
139
|
|
|
140
140
|
return (
|
|
141
|
-
<div className="
|
|
141
|
+
<div className="volter-playbar-popover-anchor">
|
|
142
142
|
<Button
|
|
143
143
|
ref={ref}
|
|
144
144
|
variant="ghost"
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* sets CSS custom properties on the game mount container
|
|
13
13
|
* (`data-testid="game-runtime-container"`), the element game canvases and
|
|
14
14
|
* react-world DOM layers mount into:
|
|
15
|
-
* --
|
|
16
|
-
* Game UI reads them with `var(--
|
|
15
|
+
* --volter-safe-area-inset-top / -right / -bottom / -left (CSS px)
|
|
16
|
+
* Game UI reads them with `var(--volter-safe-area-inset-top, 0px)`.
|
|
17
17
|
* Recorded choice: real `env(safe-area-inset-*)` injection is NOT
|
|
18
18
|
* possible from a same-document host (env() values come from the UA for
|
|
19
19
|
* the top-level viewport only), so the overlay + CSS vars ARE the
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
* event before the clone.
|
|
42
42
|
*
|
|
43
43
|
* Persistence: preset + touch override are the PROJECT settings layer's
|
|
44
|
-
* `devicePreview` (`settings-store.ts` → `.
|
|
44
|
+
* `devicePreview` (`settings-store.ts` → `.volter/settings.json`, committed):
|
|
45
45
|
* the frame a game is designed for is a fact about the project, shared by
|
|
46
46
|
* everyone who opens it.
|
|
47
47
|
*/
|
|
@@ -331,5 +331,5 @@ function _stateForE2E() {
|
|
|
331
331
|
}
|
|
332
332
|
|
|
333
333
|
if (typeof window !== 'undefined') {
|
|
334
|
-
(window as unknown as Record<string, unknown>)['
|
|
334
|
+
(window as unknown as Record<string, unknown>)['__volterDevicePreview'] = _stateForE2E;
|
|
335
335
|
}
|
|
@@ -136,7 +136,7 @@ function _updateContactPoints(group: THREE.Group, contacts: Float32Array | null)
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
/**
|
|
139
|
-
* e2e hook (mirrors the world root's stage's `
|
|
139
|
+
* e2e hook (mirrors the world root's stage's `__volterViewport`): mechanical state for
|
|
140
140
|
* The question this answers — is the Rapier debug wireframe live in the
|
|
141
141
|
* game scene with real geometry, and how many contact markers are drawn.
|
|
142
142
|
*/
|
|
@@ -167,7 +167,7 @@ function _stateForE2E(): {
|
|
|
167
167
|
};
|
|
168
168
|
}
|
|
169
169
|
if (typeof window !== 'undefined') {
|
|
170
|
-
(window as unknown as Record<string, unknown>)['
|
|
170
|
+
(window as unknown as Record<string, unknown>)['__volterPhysicsDebug'] = _stateForE2E;
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
function _teardown(): void {
|
|
@@ -70,13 +70,13 @@ function OutputPreview({
|
|
|
70
70
|
|
|
71
71
|
if (src && output.kind === 'image') {
|
|
72
72
|
const preview = (
|
|
73
|
-
<img className="
|
|
73
|
+
<img className="volter-generation-output-media" src={src} alt={output.name} loading="lazy" />
|
|
74
74
|
);
|
|
75
75
|
return onOpen ? (
|
|
76
76
|
// The preview itself opens the asset — the gesture a gallery implies.
|
|
77
77
|
<Button
|
|
78
78
|
variant="ghost"
|
|
79
|
-
className="
|
|
79
|
+
className="volter-generation-output-open"
|
|
80
80
|
onClick={onOpen}
|
|
81
81
|
aria-label={`Open ${output.name}`}
|
|
82
82
|
>
|
|
@@ -91,7 +91,7 @@ function OutputPreview({
|
|
|
91
91
|
// Deliberately no autoplay and no `muted` autoplay trick: a gallery that
|
|
92
92
|
// starts sound is the failure this panel is replacing.
|
|
93
93
|
<video
|
|
94
|
-
className="
|
|
94
|
+
className="volter-generation-output-media"
|
|
95
95
|
src={src}
|
|
96
96
|
controls
|
|
97
97
|
preload="metadata"
|
|
@@ -102,7 +102,7 @@ function OutputPreview({
|
|
|
102
102
|
}
|
|
103
103
|
if (src && output.kind === 'audio') {
|
|
104
104
|
return (
|
|
105
|
-
<div className="
|
|
105
|
+
<div className="volter-generation-output-audio">
|
|
106
106
|
<AudioAssetThumb url={src} name={output.name} />
|
|
107
107
|
<audio src={src} controls preload="metadata" title={output.name} />
|
|
108
108
|
</div>
|
|
@@ -112,7 +112,7 @@ function OutputPreview({
|
|
|
112
112
|
return <ModelThumbnail url={output.url} />;
|
|
113
113
|
}
|
|
114
114
|
return (
|
|
115
|
-
<div className="
|
|
115
|
+
<div className="volter-generation-output-file">
|
|
116
116
|
<EditorIcon icon={output.kind === 'model' ? faFile : faFileLines} />
|
|
117
117
|
<Text variant="caption" tone="muted">
|
|
118
118
|
{output.name}
|
|
@@ -160,7 +160,7 @@ function StateMedia({ job, state }: { job: GenerationJob; state: GenerationCardS
|
|
|
160
160
|
const pending = state === 'queued' || state === 'running' || state === 'saving';
|
|
161
161
|
const failing = state === 'failed' || state === 'save-failed';
|
|
162
162
|
return (
|
|
163
|
-
<div className="
|
|
163
|
+
<div className="volter-generation-state-media" data-state={state}>
|
|
164
164
|
<StateSurface
|
|
165
165
|
compact
|
|
166
166
|
tone={STATE_TONE[state]}
|
|
@@ -172,7 +172,7 @@ function StateMedia({ job, state }: { job: GenerationJob; state: GenerationCardS
|
|
|
172
172
|
// Determinate ONLY when the provider reported progress; otherwise the
|
|
173
173
|
// native indeterminate bar, which claims nothing.
|
|
174
174
|
<progress
|
|
175
|
-
className="
|
|
175
|
+
className="volter-generation-progress"
|
|
176
176
|
aria-label={`${job.label} progress`}
|
|
177
177
|
{...(state === 'running' && job.progress !== undefined
|
|
178
178
|
? { value: job.progress, max: 1 }
|
|
@@ -195,7 +195,7 @@ function CardActions({
|
|
|
195
195
|
run: (action: () => Promise<void>) => void;
|
|
196
196
|
}) {
|
|
197
197
|
return (
|
|
198
|
-
<div className="
|
|
198
|
+
<div className="volter-generation-card-actions">
|
|
199
199
|
{generationSaveRetryable(job, error) && (
|
|
200
200
|
// There is NO add-to-project gate: the server accepts a finished
|
|
201
201
|
// output on its own. This appears only once a save has actually
|
|
@@ -250,7 +250,7 @@ function GenerationCard({
|
|
|
250
250
|
|
|
251
251
|
return (
|
|
252
252
|
<article
|
|
253
|
-
className="
|
|
253
|
+
className="volter-generation-card"
|
|
254
254
|
data-state={state}
|
|
255
255
|
aria-busy={generationJobIsActive(job)}
|
|
256
256
|
aria-label={generationTitle(job)}
|
|
@@ -265,31 +265,31 @@ function GenerationCard({
|
|
|
265
265
|
}
|
|
266
266
|
}}
|
|
267
267
|
>
|
|
268
|
-
<div className="
|
|
268
|
+
<div className="volter-generation-card-media" data-outputs={Math.min(outputs.length, 4)}>
|
|
269
269
|
{outputs.length === 0 ? (
|
|
270
270
|
<StateMedia job={job} state={state} />
|
|
271
271
|
) : (
|
|
272
272
|
outputs.map((output) => (
|
|
273
|
-
<div className="
|
|
273
|
+
<div className="volter-generation-output" key={output.path} title={output.path}>
|
|
274
274
|
<OutputPreview output={output} onOpen={openOutput(output)} />
|
|
275
275
|
</div>
|
|
276
276
|
))
|
|
277
277
|
)}
|
|
278
278
|
</div>
|
|
279
279
|
|
|
280
|
-
<div className="
|
|
281
|
-
<div className="
|
|
280
|
+
<div className="volter-generation-card-body">
|
|
281
|
+
<div className="volter-generation-card-heading">
|
|
282
282
|
<Text variant="label" truncate title={generationTitle(job)}>
|
|
283
283
|
{generationTitle(job)}
|
|
284
284
|
</Text>
|
|
285
|
-
<EditorBadge className="
|
|
285
|
+
<EditorBadge className="volter-generation-chip" data-state={state}>
|
|
286
286
|
{GENERATION_STATE_LABEL[state]}
|
|
287
287
|
{state === 'running' && job.progress !== undefined
|
|
288
288
|
? ` · ${Math.round(job.progress * 100)}%`
|
|
289
289
|
: ''}
|
|
290
290
|
</EditorBadge>
|
|
291
291
|
{unread && (
|
|
292
|
-
<EditorBadge className="
|
|
292
|
+
<EditorBadge className="volter-generation-chip" data-state="new">
|
|
293
293
|
New
|
|
294
294
|
</EditorBadge>
|
|
295
295
|
)}
|
|
@@ -305,7 +305,7 @@ function GenerationCard({
|
|
|
305
305
|
</Text>
|
|
306
306
|
|
|
307
307
|
{prompt && (
|
|
308
|
-
<p className="
|
|
308
|
+
<p className="volter-generation-card-prompt" title={prompt}>
|
|
309
309
|
{prompt}
|
|
310
310
|
</p>
|
|
311
311
|
)}
|
|
@@ -317,7 +317,7 @@ function GenerationCard({
|
|
|
317
317
|
// text promises nothing and the retry button carries it instead.
|
|
318
318
|
<EditorBanner
|
|
319
319
|
tone={error || state === 'save-failed' ? 'error' : 'warning'}
|
|
320
|
-
className="
|
|
320
|
+
className="volter-generation-card-alert"
|
|
321
321
|
>
|
|
322
322
|
{error ??
|
|
323
323
|
(state === 'save-failed'
|
|
@@ -363,8 +363,8 @@ export function GenerationActivity() {
|
|
|
363
363
|
const storeError = generationJobsError();
|
|
364
364
|
|
|
365
365
|
return (
|
|
366
|
-
<div className="
|
|
367
|
-
<EditorToolbar label="Generations" compact className="
|
|
366
|
+
<div className="volter-generation-gallery">
|
|
367
|
+
<EditorToolbar label="Generations" compact className="volter-generation-gallery-bar">
|
|
368
368
|
<Text variant="label">Generations</Text>
|
|
369
369
|
<Text variant="caption" tone="muted">
|
|
370
370
|
{jobs.length} {jobs.length === 1 ? 'generation' : 'generations'}
|
|
@@ -385,14 +385,14 @@ export function GenerationActivity() {
|
|
|
385
385
|
</EditorToolbar>
|
|
386
386
|
|
|
387
387
|
{storeError && (
|
|
388
|
-
<EditorBanner tone="error" className="
|
|
388
|
+
<EditorBanner tone="error" className="volter-generation-gallery-alert">
|
|
389
389
|
{storeError}
|
|
390
390
|
</EditorBanner>
|
|
391
391
|
)}
|
|
392
392
|
|
|
393
393
|
{jobs.length === 0 ? (
|
|
394
394
|
<StateSurface
|
|
395
|
-
className="
|
|
395
|
+
className="volter-generation-gallery-empty"
|
|
396
396
|
title="No generated assets yet"
|
|
397
397
|
description="Preview generated images, video, audio and models here. Create an asset to get started."
|
|
398
398
|
action={
|
|
@@ -402,7 +402,7 @@ export function GenerationActivity() {
|
|
|
402
402
|
}
|
|
403
403
|
/>
|
|
404
404
|
) : (
|
|
405
|
-
<section className="
|
|
405
|
+
<section className="volter-generation-grid" aria-label="Generated assets">
|
|
406
406
|
{jobs.map((job) => (
|
|
407
407
|
<GenerationCard
|
|
408
408
|
key={job.id}
|