create-aura3d 1.3.3 → 1.4.1
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/README.md +113 -0
- package/dist/cli.js +10 -0
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/showcase-spec-artifacts.d.ts +105 -0
- package/dist/showcase-spec-artifacts.d.ts.map +1 -0
- package/dist/showcase-spec-artifacts.js +143 -0
- package/dist/showcase-spec-artifacts.js.map +1 -0
- package/dist/showcase-spec-category-plan.d.ts +15 -0
- package/dist/showcase-spec-category-plan.d.ts.map +1 -0
- package/dist/showcase-spec-category-plan.js +45 -0
- package/dist/showcase-spec-category-plan.js.map +1 -0
- package/dist/showcase-spec-compiler.d.ts +5 -0
- package/dist/showcase-spec-compiler.d.ts.map +1 -0
- package/dist/showcase-spec-compiler.js +240 -0
- package/dist/showcase-spec-compiler.js.map +1 -0
- package/dist/showcase-spec-evidence.d.ts +7 -0
- package/dist/showcase-spec-evidence.d.ts.map +1 -0
- package/dist/showcase-spec-evidence.js +881 -0
- package/dist/showcase-spec-evidence.js.map +1 -0
- package/dist/showcase-spec-game-geometry-extractor.d.ts +21 -0
- package/dist/showcase-spec-game-geometry-extractor.d.ts.map +1 -0
- package/dist/showcase-spec-game-geometry-extractor.js +725 -0
- package/dist/showcase-spec-game-geometry-extractor.js.map +1 -0
- package/dist/showcase-spec-game-replacement-evidence.d.ts +14 -0
- package/dist/showcase-spec-game-replacement-evidence.d.ts.map +1 -0
- package/dist/showcase-spec-game-replacement-evidence.js +219 -0
- package/dist/showcase-spec-game-replacement-evidence.js.map +1 -0
- package/dist/showcase-spec-game-template-evidence.d.ts +72 -0
- package/dist/showcase-spec-game-template-evidence.d.ts.map +1 -0
- package/dist/showcase-spec-game-template-evidence.js +435 -0
- package/dist/showcase-spec-game-template-evidence.js.map +1 -0
- package/dist/showcase-spec-gameplay-evidence.d.ts +3 -0
- package/dist/showcase-spec-gameplay-evidence.d.ts.map +1 -0
- package/dist/showcase-spec-gameplay-evidence.js +108 -0
- package/dist/showcase-spec-gameplay-evidence.js.map +1 -0
- package/dist/showcase-spec-parser.d.ts +3 -0
- package/dist/showcase-spec-parser.d.ts.map +1 -0
- package/dist/showcase-spec-parser.js +690 -0
- package/dist/showcase-spec-parser.js.map +1 -0
- package/dist/showcase-spec-platformer-artifacts.d.ts +3 -0
- package/dist/showcase-spec-platformer-artifacts.d.ts.map +1 -0
- package/dist/showcase-spec-platformer-artifacts.js +274 -0
- package/dist/showcase-spec-platformer-artifacts.js.map +1 -0
- package/dist/showcase-spec-racing-artifacts.d.ts +3 -0
- package/dist/showcase-spec-racing-artifacts.d.ts.map +1 -0
- package/dist/showcase-spec-racing-artifacts.js +273 -0
- package/dist/showcase-spec-racing-artifacts.js.map +1 -0
- package/dist/showcase-spec-replacement-apply.d.ts +10 -0
- package/dist/showcase-spec-replacement-apply.d.ts.map +1 -0
- package/dist/showcase-spec-replacement-apply.js +166 -0
- package/dist/showcase-spec-replacement-apply.js.map +1 -0
- package/dist/showcase-spec-replacement-candidates.d.ts +16 -0
- package/dist/showcase-spec-replacement-candidates.d.ts.map +1 -0
- package/dist/showcase-spec-replacement-candidates.js +244 -0
- package/dist/showcase-spec-replacement-candidates.js.map +1 -0
- package/dist/showcase-spec-replacement-game-geometry.d.ts +4 -0
- package/dist/showcase-spec-replacement-game-geometry.d.ts.map +1 -0
- package/dist/showcase-spec-replacement-game-geometry.js +175 -0
- package/dist/showcase-spec-replacement-game-geometry.js.map +1 -0
- package/dist/showcase-spec-replacement-manifest.d.ts +41 -0
- package/dist/showcase-spec-replacement-manifest.d.ts.map +1 -0
- package/dist/showcase-spec-replacement-manifest.js +62 -0
- package/dist/showcase-spec-replacement-manifest.js.map +1 -0
- package/dist/showcase-spec-replacement-values.d.ts +9 -0
- package/dist/showcase-spec-replacement-values.d.ts.map +1 -0
- package/dist/showcase-spec-replacement-values.js +41 -0
- package/dist/showcase-spec-replacement-values.js.map +1 -0
- package/dist/showcase-spec-replacement.d.ts +13 -0
- package/dist/showcase-spec-replacement.d.ts.map +1 -0
- package/dist/showcase-spec-replacement.js +144 -0
- package/dist/showcase-spec-replacement.js.map +1 -0
- package/dist/showcase-spec-types.d.ts +319 -0
- package/dist/showcase-spec-types.d.ts.map +1 -0
- package/dist/showcase-spec-types.js +2 -0
- package/dist/showcase-spec-types.js.map +1 -0
- package/package.json +45 -11
- package/templates/animation-channel/package.json +1 -1
- package/templates/animation-studio/package.json +5 -5
- package/templates/animation-studio/scripts/animation-scene.ts +1 -1
- package/templates/animation-studio/scripts/author-office-scene.ts +1 -1
- package/templates/animation-studio/scripts/build-characters.ts +1 -1
- package/templates/animation-studio/src/director/director-heuristics.ts +2 -2
- package/templates/animation-studio/src/director/{prompt-to-scene.ts → prompt-scene.ts} +1 -1
- package/templates/character-controller/package.json +3 -3
- package/templates/cinematic-scene/package.json +1 -1
- package/templates/cinematic-scene/tests/screenshot.spec.ts +2 -4
- package/templates/episode-builder/package.json +1 -1
- package/templates/falling-blocks-starter/README.md +18 -0
- package/templates/falling-blocks-starter/index.html +23 -0
- package/templates/falling-blocks-starter/package.json +21 -0
- package/templates/falling-blocks-starter/playwright.config.ts +12 -0
- package/templates/falling-blocks-starter/public/aura-assets/showcaseBlockfallCabinet.679d52fe.glb +0 -0
- package/templates/falling-blocks-starter/public/aura-assets/showcaseBlockfallCabinet.thumb.svg +1 -0
- package/templates/falling-blocks-starter/src/aura-assets.ts +20 -0
- package/templates/falling-blocks-starter/src/main.ts +358 -0
- package/templates/falling-blocks-starter/tests/playable.spec.ts +85 -0
- package/templates/falling-blocks-starter/tests/route-health.spec.ts +34 -0
- package/templates/falling-blocks-starter/tsconfig.json +13 -0
- package/templates/fighting-game/package.json +1 -1
- package/templates/mini-game/README.md +13 -0
- package/templates/mini-game/index.html +1 -1
- package/templates/mini-game/package.json +2 -2
- package/templates/mini-game/src/main.ts +251 -21
- package/templates/mini-game/tests/playable.spec.ts +49 -0
- package/templates/mini-game/tests/screenshot.spec.ts +37 -32
- package/templates/product-viewer/package.json +1 -1
- package/templates/product-viewer/tests/screenshot.spec.ts +3 -5
- package/templates/prompt-animation-channel/package.json +1 -1
- package/templates/racing-starter/README.md +27 -0
- package/templates/racing-starter/index.html +23 -0
- package/templates/racing-starter/package.json +21 -0
- package/templates/racing-starter/playwright.config.ts +12 -0
- package/templates/racing-starter/public/aura-assets/showcaseCleanSportsCar.1c69283f.glb +0 -0
- package/templates/racing-starter/public/aura-assets/showcaseCleanSportsCar.thumb.svg +1 -0
- package/templates/racing-starter/public/aura-assets/showcaseReadableKartCircuit.5cbb912e.glb +0 -0
- package/templates/racing-starter/public/aura-assets/showcaseReadableKartCircuit.thumb.svg +1 -0
- package/templates/racing-starter/src/aura-assets.ts +36 -0
- package/templates/racing-starter/src/main.ts +280 -0
- package/templates/racing-starter/tests/playable.spec.ts +93 -0
- package/templates/racing-starter/tests/route-health.spec.ts +40 -0
- package/templates/racing-starter/tsconfig.json +13 -0
- package/templates/three-compat-architecture-interior/package.json +1 -1
- package/templates/three-compat-asset-inspector/package.json +1 -1
- package/templates/three-compat-character-viewer/package.json +1 -1
- package/templates/three-compat-custom-threejs-migration/package.json +1 -1
- package/templates/three-compat-large-scene/package.json +1 -1
- package/templates/three-compat-material-authoring/package.json +1 -1
- package/templates/three-compat-postprocess-scene/package.json +1 -1
- package/templates/three-compat-premium-product-viewer/package.json +1 -1
- package/templates/animation-channel/public/aura-assets/moon-garden-feature-frame.png +0 -0
- package/templates/animation-channel/test-results/.last-run.json +0 -4
- package/templates/animation-channel/tests/reports/prompt-animation/cartoon-sample-episode.png +0 -0
- package/templates/animation-studio/dist/episodes/prompt-to-scene-proof/two-robots-fixing-a-car.document.json +0 -680
- package/templates/animation-studio/dist/episodes/scene/episode-3d.webm +0 -0
- package/templates/animation-studio/dist/episodes/scene/frames/action.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/frames/dialogue.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/frames/final.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/frames/first.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/frames/mouth-closed.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/frames/mouth-open.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/render-live-summary.json +0 -9763
- package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/broken.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/chefs.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/customer.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/friend-1.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/friend-2.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/worker-1.png +0 -0
- package/templates/animation-studio/dist/episodes/scene/skeleton-overlays/worker-2.png +0 -0
- package/templates/animation-studio/dist/generated/second-scene.document.json +0 -1210
- package/templates/animation-studio/dist/generated/studio-edited.document.json +0 -1196
- package/templates/animation-studio/dist/scene/inshader-accept.document.json +0 -1
- package/templates/animation-studio/dist/scene/inshader-test.document.json +0 -1
- package/templates/animation-studio/dist/scene/integ.document.json +0 -1
- package/templates/animation-studio/dist/scene/working.document.json +0 -673
- package/templates/animation-studio/dist/scene/working.history.json +0 -1
- package/templates/animation-studio/node_modules.publish-aside/.vite/deps/_metadata.json +0 -8
- package/templates/animation-studio/node_modules.publish-aside/.vite/deps/package.json +0 -3
- package/templates/animation-studio/package-lock.json +0 -3503
- package/templates/animation-studio/studio/dist/assets/index-C7f8tHfi.css +0 -1
- package/templates/animation-studio/studio/dist/assets/index-CLo3GXyN.js +0 -43
- package/templates/animation-studio/studio/dist/index.html +0 -19
- package/templates/animation-studio/tests/reports/animation-studio/hifi-cast-validation.json +0 -938
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineConfig } from "@playwright/test";
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
webServer: {
|
|
5
|
+
command: "npm run dev -- --port 5179",
|
|
6
|
+
url: "http://127.0.0.1:5179",
|
|
7
|
+
reuseExistingServer: !process.env.CI
|
|
8
|
+
},
|
|
9
|
+
use: {
|
|
10
|
+
baseURL: "http://127.0.0.1:5179"
|
|
11
|
+
}
|
|
12
|
+
});
|
package/templates/falling-blocks-starter/public/aura-assets/showcaseBlockfallCabinet.679d52fe.glb
ADDED
|
Binary file
|
package/templates/falling-blocks-starter/public/aura-assets/showcaseBlockfallCabinet.thumb.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 180"><rect width="320" height="180" fill="#101720"/><path d="M80 126 160 46l80 80-80 28z" fill="#77a7ff"/><text x="160" y="160" text-anchor="middle" font-family="Arial" font-size="18" fill="#f4f7fb">showcaseBlockfallCabinet 2x2x3.27</text></svg>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { defineAuraAssets } from "@aura3d/engine";
|
|
2
|
+
|
|
3
|
+
export const assets = defineAuraAssets({
|
|
4
|
+
cabinetModel: {
|
|
5
|
+
type: "model",
|
|
6
|
+
format: "glb",
|
|
7
|
+
url: "/aura-assets/showcaseBlockfallCabinet.679d52fe.glb",
|
|
8
|
+
bounds: [2, 2, 3.27],
|
|
9
|
+
hash: "sha256-679d52fe0c7bf99373fc873f3c3892548f6c5758ae9c81be91f09acae6a35a36",
|
|
10
|
+
metadata: {
|
|
11
|
+
materials: ["arcade_machine"],
|
|
12
|
+
animations: [],
|
|
13
|
+
textures: ["image-0", "image-1", "image-2", "image-3"],
|
|
14
|
+
license: "CC-BY-4.0",
|
|
15
|
+
author: "Dmitry Blagodaryov",
|
|
16
|
+
sourcePage: "https://huggingface.co/datasets/allenai/objaverse/blob/main/glbs/000-012/f73986356b6e4d72b7a889279837aec2.glb",
|
|
17
|
+
thumbnailUrl: "/aura-assets/showcaseBlockfallCabinet.thumb.svg"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
} as const);
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import {
|
|
2
|
+
camera,
|
|
3
|
+
createAuraApp,
|
|
4
|
+
game,
|
|
5
|
+
lights,
|
|
6
|
+
material,
|
|
7
|
+
model,
|
|
8
|
+
primitives,
|
|
9
|
+
scene,
|
|
10
|
+
type AuraNodeInput
|
|
11
|
+
} from "@aura3d/engine";
|
|
12
|
+
import { assets } from "./aura-assets";
|
|
13
|
+
|
|
14
|
+
declare global {
|
|
15
|
+
interface Window {
|
|
16
|
+
__AURA3D_FALLING_BLOCKS_STARTER__?: FallingBlocksStarterEvidence;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type Piece = "I" | "J" | "L" | "O" | "S" | "T" | "Z";
|
|
21
|
+
type Cell = Piece | null;
|
|
22
|
+
|
|
23
|
+
interface FallingBlocksStarterEvidence {
|
|
24
|
+
readonly frame: number;
|
|
25
|
+
readonly score: number;
|
|
26
|
+
readonly lines: number;
|
|
27
|
+
readonly checksum: string;
|
|
28
|
+
readonly active: { readonly kind: Piece; readonly x: number; readonly y: number; readonly rotation: number } | null;
|
|
29
|
+
readonly hold: Piece | null;
|
|
30
|
+
readonly gameOver: boolean;
|
|
31
|
+
readonly events: readonly string[];
|
|
32
|
+
readonly lineClearProof: {
|
|
33
|
+
readonly lines: number;
|
|
34
|
+
readonly events: readonly string[];
|
|
35
|
+
readonly checksum: string;
|
|
36
|
+
};
|
|
37
|
+
readonly evidence: unknown;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const boardWidth = 10;
|
|
41
|
+
const boardHeight = 22;
|
|
42
|
+
const hiddenRows = 2;
|
|
43
|
+
const cellSize = 0.24;
|
|
44
|
+
const boardOrigin = { x: -1.2, y: -2.18 };
|
|
45
|
+
const pieceColors: Record<Piece, string> = {
|
|
46
|
+
I: "#5ed7df",
|
|
47
|
+
J: "#7fa5df",
|
|
48
|
+
L: "#dfbd65",
|
|
49
|
+
O: "#dccf78",
|
|
50
|
+
S: "#84d09a",
|
|
51
|
+
T: "#b987d0",
|
|
52
|
+
Z: "#d88791"
|
|
53
|
+
};
|
|
54
|
+
const activeShapes: Record<Piece, readonly (readonly { readonly x: number; readonly y: number }[])[]> = {
|
|
55
|
+
I: [
|
|
56
|
+
[{ x: 0, y: 1 }, { x: 1, y: 1 }, { x: 2, y: 1 }, { x: 3, y: 1 }],
|
|
57
|
+
[{ x: 2, y: 0 }, { x: 2, y: 1 }, { x: 2, y: 2 }, { x: 2, y: 3 }],
|
|
58
|
+
[{ x: 0, y: 2 }, { x: 1, y: 2 }, { x: 2, y: 2 }, { x: 3, y: 2 }],
|
|
59
|
+
[{ x: 1, y: 0 }, { x: 1, y: 1 }, { x: 1, y: 2 }, { x: 1, y: 3 }]
|
|
60
|
+
],
|
|
61
|
+
J: [
|
|
62
|
+
[{ x: 0, y: 0 }, { x: 0, y: 1 }, { x: 1, y: 1 }, { x: 2, y: 1 }],
|
|
63
|
+
[{ x: 1, y: 0 }, { x: 2, y: 0 }, { x: 1, y: 1 }, { x: 1, y: 2 }],
|
|
64
|
+
[{ x: 0, y: 1 }, { x: 1, y: 1 }, { x: 2, y: 1 }, { x: 2, y: 2 }],
|
|
65
|
+
[{ x: 1, y: 0 }, { x: 1, y: 1 }, { x: 0, y: 2 }, { x: 1, y: 2 }]
|
|
66
|
+
],
|
|
67
|
+
L: [
|
|
68
|
+
[{ x: 2, y: 0 }, { x: 0, y: 1 }, { x: 1, y: 1 }, { x: 2, y: 1 }],
|
|
69
|
+
[{ x: 1, y: 0 }, { x: 1, y: 1 }, { x: 1, y: 2 }, { x: 2, y: 2 }],
|
|
70
|
+
[{ x: 0, y: 1 }, { x: 1, y: 1 }, { x: 2, y: 1 }, { x: 0, y: 2 }],
|
|
71
|
+
[{ x: 0, y: 0 }, { x: 1, y: 0 }, { x: 1, y: 1 }, { x: 1, y: 2 }]
|
|
72
|
+
],
|
|
73
|
+
O: [
|
|
74
|
+
[{ x: 1, y: 0 }, { x: 2, y: 0 }, { x: 1, y: 1 }, { x: 2, y: 1 }],
|
|
75
|
+
[{ x: 1, y: 0 }, { x: 2, y: 0 }, { x: 1, y: 1 }, { x: 2, y: 1 }],
|
|
76
|
+
[{ x: 1, y: 0 }, { x: 2, y: 0 }, { x: 1, y: 1 }, { x: 2, y: 1 }],
|
|
77
|
+
[{ x: 1, y: 0 }, { x: 2, y: 0 }, { x: 1, y: 1 }, { x: 2, y: 1 }]
|
|
78
|
+
],
|
|
79
|
+
S: [
|
|
80
|
+
[{ x: 1, y: 0 }, { x: 2, y: 0 }, { x: 0, y: 1 }, { x: 1, y: 1 }],
|
|
81
|
+
[{ x: 1, y: 0 }, { x: 1, y: 1 }, { x: 2, y: 1 }, { x: 2, y: 2 }],
|
|
82
|
+
[{ x: 1, y: 1 }, { x: 2, y: 1 }, { x: 0, y: 2 }, { x: 1, y: 2 }],
|
|
83
|
+
[{ x: 0, y: 0 }, { x: 0, y: 1 }, { x: 1, y: 1 }, { x: 1, y: 2 }]
|
|
84
|
+
],
|
|
85
|
+
T: [
|
|
86
|
+
[{ x: 1, y: 0 }, { x: 0, y: 1 }, { x: 1, y: 1 }, { x: 2, y: 1 }],
|
|
87
|
+
[{ x: 1, y: 0 }, { x: 1, y: 1 }, { x: 2, y: 1 }, { x: 1, y: 2 }],
|
|
88
|
+
[{ x: 0, y: 1 }, { x: 1, y: 1 }, { x: 2, y: 1 }, { x: 1, y: 2 }],
|
|
89
|
+
[{ x: 1, y: 0 }, { x: 0, y: 1 }, { x: 1, y: 1 }, { x: 1, y: 2 }]
|
|
90
|
+
],
|
|
91
|
+
Z: [
|
|
92
|
+
[{ x: 0, y: 0 }, { x: 1, y: 0 }, { x: 1, y: 1 }, { x: 2, y: 1 }],
|
|
93
|
+
[{ x: 2, y: 0 }, { x: 1, y: 1 }, { x: 2, y: 1 }, { x: 1, y: 2 }],
|
|
94
|
+
[{ x: 0, y: 1 }, { x: 1, y: 1 }, { x: 1, y: 2 }, { x: 2, y: 2 }],
|
|
95
|
+
[{ x: 1, y: 0 }, { x: 0, y: 1 }, { x: 1, y: 1 }, { x: 0, y: 2 }]
|
|
96
|
+
]
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const input = game.input({
|
|
100
|
+
actions: {
|
|
101
|
+
left: ["KeyA", "ArrowLeft"],
|
|
102
|
+
right: ["KeyD", "ArrowRight"],
|
|
103
|
+
rotateRight: ["KeyW", "ArrowUp", "KeyX"],
|
|
104
|
+
rotateLeft: ["KeyQ", "KeyZ"],
|
|
105
|
+
softDrop: ["KeyS", "ArrowDown"],
|
|
106
|
+
hardDrop: ["Space"],
|
|
107
|
+
hold: ["KeyC", "ShiftLeft", "ShiftRight"],
|
|
108
|
+
reset: ["KeyR"]
|
|
109
|
+
},
|
|
110
|
+
bufferMs: 100
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const falling = game.fallingBlocks({
|
|
114
|
+
width: boardWidth,
|
|
115
|
+
height: boardHeight,
|
|
116
|
+
hiddenRows,
|
|
117
|
+
seed: 7,
|
|
118
|
+
gravityFrames: 999,
|
|
119
|
+
lockDelayFrames: 18
|
|
120
|
+
});
|
|
121
|
+
const routeEvents = game.eventLog({ label: "falling blocks starter events", maxEvents: 18 });
|
|
122
|
+
const hud = game.hud.bindings([
|
|
123
|
+
game.hud.score({ valuePath: "appState.score" }),
|
|
124
|
+
game.hud.objective({ valuePath: "appState.objective" }),
|
|
125
|
+
game.hud.eventLog({ valuePath: "appState.events" })
|
|
126
|
+
]);
|
|
127
|
+
const lineClearProof = createLineClearProof();
|
|
128
|
+
|
|
129
|
+
setupPracticeBoard();
|
|
130
|
+
|
|
131
|
+
const app = createAuraApp("#app", {
|
|
132
|
+
diagnostics: { overlay: true, performancePanel: true },
|
|
133
|
+
scene: buildScene()
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const boardNodes = Array.from({ length: boardHeight - hiddenRows }, (_, rowIndex) =>
|
|
137
|
+
Array.from({ length: boardWidth }, (_, x) => app.nodes.require(`board-cell-${x}-${rowIndex + hiddenRows}`))
|
|
138
|
+
);
|
|
139
|
+
const activeNodes = Array.from({ length: 4 }, (_, index) => app.nodes.require(`active-cell-${index}`));
|
|
140
|
+
const holdNode = app.nodes.require("hold-preview");
|
|
141
|
+
const hudRoot = createHud();
|
|
142
|
+
let objective = "Clear the prepared line. Move, rotate, hold, or hard drop.";
|
|
143
|
+
let tickAccumulator = 0;
|
|
144
|
+
|
|
145
|
+
app.onFrame(({ dt }: { readonly dt: number }) => {
|
|
146
|
+
input.update(dt);
|
|
147
|
+
if (input.pressed("reset")) {
|
|
148
|
+
setupPracticeBoard();
|
|
149
|
+
routeEvents.push({ type: "reset", label: "reset" });
|
|
150
|
+
objective = "Clear the prepared line. Move, rotate, hold, or hard drop.";
|
|
151
|
+
}
|
|
152
|
+
if (input.pressed("left")) recordKitEvents(falling.move(-1));
|
|
153
|
+
if (input.pressed("right")) recordKitEvents(falling.move(1));
|
|
154
|
+
if (input.pressed("rotateRight")) recordKitEvents(falling.rotate(1));
|
|
155
|
+
if (input.pressed("rotateLeft")) recordKitEvents(falling.rotate(-1));
|
|
156
|
+
if (input.pressed("softDrop")) recordKitEvents(falling.softDrop());
|
|
157
|
+
if (input.pressed("hold")) recordKitEvents(falling.hold());
|
|
158
|
+
if (input.pressed("hardDrop")) recordKitEvents(falling.hardDrop());
|
|
159
|
+
|
|
160
|
+
tickAccumulator += dt;
|
|
161
|
+
while (tickAccumulator >= 1 / 30) {
|
|
162
|
+
recordKitEvents(falling.tick(1));
|
|
163
|
+
tickAccumulator -= 1 / 30;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const state = falling.snapshot();
|
|
167
|
+
renderBoard(state);
|
|
168
|
+
renderHud(state);
|
|
169
|
+
publishEvidence(state);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
renderBoard(falling.snapshot());
|
|
173
|
+
renderHud(falling.snapshot());
|
|
174
|
+
publishEvidence(falling.snapshot());
|
|
175
|
+
|
|
176
|
+
function recordKitEvents(state: ReturnType<typeof falling.snapshot>): void {
|
|
177
|
+
for (const event of state.events) {
|
|
178
|
+
routeEvents.push({
|
|
179
|
+
type: event.type,
|
|
180
|
+
label: event.piece ? `${event.type}:${event.piece}` : event.type,
|
|
181
|
+
severity: event.type === "line-clear" ? "success" : event.type === "game-over" ? "warning" : "info",
|
|
182
|
+
frame: event.frame
|
|
183
|
+
});
|
|
184
|
+
if (event.type === "line-clear") objective = `Line clear. ${state.lines} line cleared. Press R to replay.`;
|
|
185
|
+
if (event.type === "hold") objective = "Held piece. Press R or continue.";
|
|
186
|
+
if (event.type === "rotate") objective = "Rotation accepted.";
|
|
187
|
+
if (event.type === "move") objective = "Move accepted.";
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function setupPracticeBoard(): void {
|
|
192
|
+
falling.reset(7);
|
|
193
|
+
falling.setBoard(createPracticeBoard());
|
|
194
|
+
falling.setActive({ kind: "I", x: 3, y: boardHeight - 3, rotation: 0 });
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function createPracticeBoard(): Cell[][] {
|
|
198
|
+
const board = Array.from({ length: boardHeight }, () => Array.from({ length: boardWidth }, () => null as Cell));
|
|
199
|
+
board[boardHeight - 1] = board[boardHeight - 1].map((_, x) => (x >= 3 && x <= 6 ? null : "O"));
|
|
200
|
+
board[boardHeight - 2][0] = "J";
|
|
201
|
+
board[boardHeight - 2][9] = "L";
|
|
202
|
+
return board;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function buildScene() {
|
|
206
|
+
const nodes: AuraNodeInput[] = [
|
|
207
|
+
model(assets.cabinetModel, { name: "typed arcade cabinet", castShadow: true })
|
|
208
|
+
.position(1.75, 0.28, -0.55)
|
|
209
|
+
.scale(0.2),
|
|
210
|
+
primitives.box({ name: "falling blocks board backplate", material: material.pbr({ color: "#172027", roughness: 0.82, metallic: 0.04 }) })
|
|
211
|
+
.position(0, 0, -0.08)
|
|
212
|
+
.scale([2.76, 5.25, 0.08]),
|
|
213
|
+
primitives.box({ name: "falling blocks board frame", material: material.neon({ color: "#8ee8d5", emissive: "#8ee8d5", emissiveIntensity: 0.22 }) })
|
|
214
|
+
.position(0, 0, -0.02)
|
|
215
|
+
.scale([2.92, 5.42, 0.04]),
|
|
216
|
+
...boardCellNodes(),
|
|
217
|
+
...activeCellNodes(),
|
|
218
|
+
primitives.box({ name: "hold preview", material: material.neon({ color: "#b987d0", emissive: "#b987d0", emissiveIntensity: 0.38 }) })
|
|
219
|
+
.position(-2.02, 1.92, 0.08)
|
|
220
|
+
.scale([0.42, 0.42, 0.16])
|
|
221
|
+
.runtime(game.runtimeNode("hold-preview", { tags: ["hold", "runtime"] }))
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
return scene()
|
|
225
|
+
.background("#06090d")
|
|
226
|
+
.addMany(nodes)
|
|
227
|
+
.add(lights.ambient({ name: "blockfall ambient", intensity: 0.36, color: "#e8fbff" }))
|
|
228
|
+
.add(lights.directional({ name: "blockfall key", position: [3.8, 6.2, 5], intensity: 1.15, color: "#ffffff" }))
|
|
229
|
+
.camera(camera.perspective({ position: [0.25, 1.2, 7.2], target: [0.1, 0.1, 0], fov: 38 }));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function boardCellNodes(): AuraNodeInput[] {
|
|
233
|
+
const nodes: AuraNodeInput[] = [];
|
|
234
|
+
for (let y = hiddenRows; y < boardHeight; y += 1) {
|
|
235
|
+
for (let x = 0; x < boardWidth; x += 1) {
|
|
236
|
+
nodes.push(
|
|
237
|
+
primitives.box({ name: `board cell ${x} ${y}`, material: material.pbr({ color: "#dccf78", roughness: 0.62, metallic: 0.02 }) })
|
|
238
|
+
.position(...cellPosition(x, y), 0)
|
|
239
|
+
.scale([cellSize * 0.86, cellSize * 0.86, 0.12])
|
|
240
|
+
.runtime(game.runtimeNode(`board-cell-${x}-${y}`, { tags: ["board-cell", "runtime"] }))
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return nodes;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function activeCellNodes(): AuraNodeInput[] {
|
|
248
|
+
return Array.from({ length: 4 }, (_, index) =>
|
|
249
|
+
primitives.box({ name: `active piece cell ${index}`, material: material.neon({ color: "#5ed7df", emissive: "#5ed7df", emissiveIntensity: 0.4 }) })
|
|
250
|
+
.position(-3, -3, 0.12)
|
|
251
|
+
.scale([cellSize * 0.92, cellSize * 0.92, 0.16])
|
|
252
|
+
.runtime(game.runtimeNode(`active-cell-${index}`, { tags: ["active-cell", "runtime"] }))
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function renderBoard(state: ReturnType<typeof falling.snapshot>): void {
|
|
257
|
+
for (let y = hiddenRows; y < boardHeight; y += 1) {
|
|
258
|
+
for (let x = 0; x < boardWidth; x += 1) {
|
|
259
|
+
const node = boardNodes[y - hiddenRows][x];
|
|
260
|
+
const cell = state.board[y][x];
|
|
261
|
+
node.setVisible(Boolean(cell)).setPosition(...cellPosition(x, y), 0);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
activeNodes.forEach((node) => node.setVisible(false));
|
|
266
|
+
if (state.active) {
|
|
267
|
+
const cells = activeShapes[state.active.kind][state.active.rotation] ?? activeShapes[state.active.kind][0];
|
|
268
|
+
cells.forEach((cell, index) => {
|
|
269
|
+
const x = state.active!.x + cell.x;
|
|
270
|
+
const y = state.active!.y + cell.y;
|
|
271
|
+
activeNodes[index]
|
|
272
|
+
.setVisible(y >= hiddenRows)
|
|
273
|
+
.setPosition(...cellPosition(x, y), 0.14)
|
|
274
|
+
.setScale([cellSize * 0.94, cellSize * 0.94, 0.16]);
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
holdNode.setVisible(Boolean(state.hold));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function cellPosition(x: number, y: number): [number, number] {
|
|
281
|
+
return [
|
|
282
|
+
boardOrigin.x + (x + 0.5) * cellSize,
|
|
283
|
+
boardOrigin.y + (boardHeight - hiddenRows - (y - hiddenRows) - 0.5) * cellSize
|
|
284
|
+
];
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function createHud(): HTMLElement {
|
|
288
|
+
const root = document.createElement("aside");
|
|
289
|
+
root.id = "falling-blocks-starter-hud";
|
|
290
|
+
root.style.cssText = [
|
|
291
|
+
"position:absolute",
|
|
292
|
+
"left:16px",
|
|
293
|
+
"top:16px",
|
|
294
|
+
"z-index:5",
|
|
295
|
+
"min-width:310px",
|
|
296
|
+
"font:600 13px/1.35 Inter, system-ui, sans-serif",
|
|
297
|
+
"color:#f5fbff",
|
|
298
|
+
"background:rgba(3,9,14,0.78)",
|
|
299
|
+
"border:1px solid rgba(125,220,235,0.34)",
|
|
300
|
+
"border-radius:8px",
|
|
301
|
+
"padding:12px",
|
|
302
|
+
"pointer-events:none"
|
|
303
|
+
].join(";");
|
|
304
|
+
document.body.append(root);
|
|
305
|
+
return root;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function renderHud(state: ReturnType<typeof falling.snapshot>): void {
|
|
309
|
+
hudRoot.innerHTML = [
|
|
310
|
+
`<strong>Aura3D Falling Blocks Starter</strong>`,
|
|
311
|
+
`<div>Score ${state.score} | Lines ${state.lines} | Level ${state.level}</div>`,
|
|
312
|
+
`<div>Active ${state.active?.kind ?? "--"} | Hold ${state.hold ?? "--"} | Checksum ${state.checksum.slice(0, 8)}</div>`,
|
|
313
|
+
`<div>${objective}</div>`,
|
|
314
|
+
`<div>Move arrows/A-D. Rotate W/Q. Drop Space. Hold C. Reset R.</div>`
|
|
315
|
+
].join("");
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function publishEvidence(state: ReturnType<typeof falling.snapshot>): void {
|
|
319
|
+
const evidence = app.evidence({
|
|
320
|
+
input,
|
|
321
|
+
events: routeEvents,
|
|
322
|
+
hud,
|
|
323
|
+
appState: {
|
|
324
|
+
score: state.score,
|
|
325
|
+
objective,
|
|
326
|
+
events: routeEvents.events().map((event: { readonly label: string }) => event.label)
|
|
327
|
+
},
|
|
328
|
+
assets: {
|
|
329
|
+
typedAssets: Object.keys(assets).length,
|
|
330
|
+
missingAssets: []
|
|
331
|
+
},
|
|
332
|
+
source: { expectsGame: true }
|
|
333
|
+
});
|
|
334
|
+
window.__AURA3D_FALLING_BLOCKS_STARTER__ = {
|
|
335
|
+
frame: state.frame,
|
|
336
|
+
score: state.score,
|
|
337
|
+
lines: state.lines,
|
|
338
|
+
checksum: state.checksum,
|
|
339
|
+
active: state.active,
|
|
340
|
+
hold: state.hold,
|
|
341
|
+
gameOver: state.gameOver,
|
|
342
|
+
events: routeEvents.events().map((event: { readonly label: string }) => event.label),
|
|
343
|
+
lineClearProof,
|
|
344
|
+
evidence
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function createLineClearProof(): FallingBlocksStarterEvidence["lineClearProof"] {
|
|
349
|
+
const proof = game.fallingBlocks({ width: boardWidth, height: boardHeight, hiddenRows, seed: 7, gravityFrames: 999 });
|
|
350
|
+
proof.setBoard(createPracticeBoard());
|
|
351
|
+
proof.setActive({ kind: "I", x: 3, y: boardHeight - 3, rotation: 0 });
|
|
352
|
+
const cleared = proof.hardDrop();
|
|
353
|
+
return {
|
|
354
|
+
lines: cleared.lines,
|
|
355
|
+
events: cleared.events.map((event) => event.piece ? `${event.type}:${event.piece}` : event.type),
|
|
356
|
+
checksum: cleared.checksum
|
|
357
|
+
};
|
|
358
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { expect, test } from "@playwright/test";
|
|
2
|
+
|
|
3
|
+
test("falling-blocks starter responds to keyboard input and clears a line", async ({ page }) => {
|
|
4
|
+
await page.goto("/");
|
|
5
|
+
await page.waitForFunction(() => document.body.dataset.aura3dReady === "true", undefined, { timeout: 45_000 });
|
|
6
|
+
await page.waitForFunction(() => Boolean((window as unknown as { __AURA3D_FALLING_BLOCKS_STARTER__?: unknown }).__AURA3D_FALLING_BLOCKS_STARTER__));
|
|
7
|
+
|
|
8
|
+
const initial = await fallingState(page);
|
|
9
|
+
expect(initial.active?.kind).toBe("I");
|
|
10
|
+
expect(initial.active?.x).toBe(3);
|
|
11
|
+
expect(initial.lineClearProof.lines).toBe(1);
|
|
12
|
+
expect(initial.lineClearProof.events).toContain("line-clear");
|
|
13
|
+
|
|
14
|
+
await tapKey(page, "ArrowRight");
|
|
15
|
+
await page.waitForTimeout(120);
|
|
16
|
+
const moved = await fallingState(page);
|
|
17
|
+
expect(moved.active?.x).toBe((initial.active?.x ?? 0) + 1);
|
|
18
|
+
|
|
19
|
+
await tapKey(page, "KeyR");
|
|
20
|
+
await page.waitForTimeout(120);
|
|
21
|
+
await tapKey(page, "ArrowUp");
|
|
22
|
+
await page.waitForTimeout(120);
|
|
23
|
+
const rotated = await fallingState(page);
|
|
24
|
+
expect(rotated.active?.rotation).not.toBe(initial.active?.rotation);
|
|
25
|
+
|
|
26
|
+
await tapKey(page, "KeyR");
|
|
27
|
+
await page.waitForTimeout(120);
|
|
28
|
+
await tapKey(page, "KeyC");
|
|
29
|
+
await page.waitForTimeout(120);
|
|
30
|
+
const held = await fallingState(page);
|
|
31
|
+
expect(held.hold).toBe("I");
|
|
32
|
+
expect(held.events).toEqual(expect.arrayContaining(["hold:I"]));
|
|
33
|
+
|
|
34
|
+
await tapKey(page, "KeyR");
|
|
35
|
+
await page.waitForTimeout(120);
|
|
36
|
+
await tapKey(page, "Space");
|
|
37
|
+
await page.waitForTimeout(160);
|
|
38
|
+
const cleared = await fallingState(page);
|
|
39
|
+
expect(cleared.lines).toBe(1);
|
|
40
|
+
expect(cleared.score).toBeGreaterThanOrEqual(100);
|
|
41
|
+
expect(cleared.events).toEqual(expect.arrayContaining(["hard-drop:I", "line-clear"]));
|
|
42
|
+
|
|
43
|
+
await tapKey(page, "KeyR");
|
|
44
|
+
await page.waitForTimeout(120);
|
|
45
|
+
const reset = await fallingState(page);
|
|
46
|
+
expect(reset.lines).toBe(0);
|
|
47
|
+
expect(reset.active?.kind).toBe("I");
|
|
48
|
+
expect(reset.events.some((event) => event.includes("reset"))).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
async function fallingState(page: import("@playwright/test").Page): Promise<{
|
|
52
|
+
readonly score: number;
|
|
53
|
+
readonly lines: number;
|
|
54
|
+
readonly active: { readonly kind: string; readonly x: number; readonly rotation: number } | null;
|
|
55
|
+
readonly hold: string | null;
|
|
56
|
+
readonly events: readonly string[];
|
|
57
|
+
readonly lineClearProof: {
|
|
58
|
+
readonly lines: number;
|
|
59
|
+
readonly events: readonly string[];
|
|
60
|
+
};
|
|
61
|
+
}> {
|
|
62
|
+
return await page.evaluate(() => {
|
|
63
|
+
const state = (window as unknown as {
|
|
64
|
+
readonly __AURA3D_FALLING_BLOCKS_STARTER__?: {
|
|
65
|
+
readonly score: number;
|
|
66
|
+
readonly lines: number;
|
|
67
|
+
readonly active: { readonly kind: string; readonly x: number; readonly rotation: number } | null;
|
|
68
|
+
readonly hold: string | null;
|
|
69
|
+
readonly events: readonly string[];
|
|
70
|
+
readonly lineClearProof: {
|
|
71
|
+
readonly lines: number;
|
|
72
|
+
readonly events: readonly string[];
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
}).__AURA3D_FALLING_BLOCKS_STARTER__;
|
|
76
|
+
if (!state) throw new Error("Missing __AURA3D_FALLING_BLOCKS_STARTER__ state.");
|
|
77
|
+
return state;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function tapKey(page: import("@playwright/test").Page, key: string, holdMs = 90): Promise<void> {
|
|
82
|
+
await page.keyboard.down(key);
|
|
83
|
+
await page.waitForTimeout(holdMs);
|
|
84
|
+
await page.keyboard.up(key);
|
|
85
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { expect, test } from "@playwright/test";
|
|
4
|
+
|
|
5
|
+
test.setTimeout(60_000);
|
|
6
|
+
|
|
7
|
+
test("Aura3D falling-blocks starter reaches ready state with line-clear evidence", async ({ page }) => {
|
|
8
|
+
await page.goto("/");
|
|
9
|
+
await expect.poll(() => page.locator("body").getAttribute("data-aura3d-ready"), { timeout: 45_000 }).toBe("true");
|
|
10
|
+
|
|
11
|
+
const drawCalls = Number(await page.locator("body").getAttribute("data-aura3d-draw-calls"));
|
|
12
|
+
const routeState = await page.evaluate(() => {
|
|
13
|
+
const value: unknown = Reflect.get(window, "__AURA3D_FALLING_BLOCKS_STARTER__");
|
|
14
|
+
const state = isRecord(value) ? value : {};
|
|
15
|
+
const lineClearProofValue = state.lineClearProof;
|
|
16
|
+
const lineClearProof = isRecord(lineClearProofValue) ? lineClearProofValue : {};
|
|
17
|
+
return {
|
|
18
|
+
score: typeof state.score === "number" ? state.score : -1,
|
|
19
|
+
lines: typeof state.lines === "number" ? state.lines : -1,
|
|
20
|
+
proofLines: typeof lineClearProof.lines === "number" ? lineClearProof.lines : 0
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function isRecord(recordValue: unknown): recordValue is Readonly<Record<string, unknown>> {
|
|
24
|
+
return typeof recordValue === "object" && recordValue !== null && !Array.isArray(recordValue);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
expect(drawCalls).toBeGreaterThan(0);
|
|
29
|
+
expect(routeState.score).toBeGreaterThanOrEqual(0);
|
|
30
|
+
expect(routeState.lines).toBeGreaterThanOrEqual(0);
|
|
31
|
+
expect(routeState.proofLines).toBeGreaterThan(0);
|
|
32
|
+
mkdirSync(resolve("tests/reports"), { recursive: true });
|
|
33
|
+
writeFileSync(resolve("tests/reports/route-health.json"), `${JSON.stringify({ ready: true, drawCalls, routeState }, null, 2)}\n`);
|
|
34
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
"allowSyntheticDefaultImports": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"types": ["vite/client"]
|
|
11
|
+
},
|
|
12
|
+
"include": ["src/**/*.ts", "tests/**/*.ts"]
|
|
13
|
+
}
|
|
@@ -3,6 +3,19 @@
|
|
|
3
3
|
Small interactive scene starter for agent-authored controls, movement, and route
|
|
4
4
|
health checks.
|
|
5
5
|
|
|
6
|
+
Status: playable starter, not a production game claim. This template uses a
|
|
7
|
+
typed player asset, the public `game.platformer(...)` kit, runtime nodes, HUD
|
|
8
|
+
evidence, keyboard input, reset, scoring, and screenshot tests. It proves the
|
|
9
|
+
starter boots and responds to keyboard input through the root `@aura3d/engine`
|
|
10
|
+
safe API; it does not prove production art direction, skinned animation,
|
|
11
|
+
advanced PBR, WebGPU, postprocess, or commercial game polish.
|
|
12
|
+
|
|
13
|
+
Before presenting a route from this template as production-quality, replace any
|
|
14
|
+
placeholder art with typed primary assets from the Aura3D CLI, keep
|
|
15
|
+
`model(assets.x)` for real assets, prove each claimed mechanic with keyboard
|
|
16
|
+
tests, and capture desktop/mobile screenshots where the main subject is
|
|
17
|
+
readable.
|
|
18
|
+
|
|
6
19
|
```bash
|
|
7
20
|
npm install
|
|
8
21
|
npm run dev
|
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
"typecheck": "tsc --noEmit",
|
|
9
9
|
"build": "npm run typecheck && vite build",
|
|
10
10
|
"preview": "vite preview --host 127.0.0.1",
|
|
11
|
-
"test": "playwright test tests/route-health.spec.ts tests/screenshot.spec.ts --workers=1"
|
|
11
|
+
"test": "playwright test tests/route-health.spec.ts tests/playable.spec.ts tests/screenshot.spec.ts --workers=1"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@aura3d/engine": "1.
|
|
14
|
+
"@aura3d/engine": "1.4.1"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"@playwright/test": "^1.52.0",
|