@aethervtt/shard-project 0.0.1 → 0.1.0
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/dist/docs.js +38 -4
- package/dist/host.d.ts +6 -1
- package/dist/host.js +20 -5
- package/dist/manifest.d.ts +6 -0
- package/dist/manifest.js +13 -1
- package/package.json +29 -25
package/dist/docs.js
CHANGED
|
@@ -7,6 +7,7 @@ import { allComponents } from "@aethervtt/shard-core";
|
|
|
7
7
|
import { allGenerators } from "@aethervtt/shard-procgen";
|
|
8
8
|
import { saveJsonSchema } from "@aethervtt/shard-save";
|
|
9
9
|
import { prefabJsonSchema, sceneJsonSchema } from "@aethervtt/shard-scene";
|
|
10
|
+
import { capturePlanJsonSchema, perfRecordJsonSchema } from "@aethervtt/shard-verify";
|
|
10
11
|
import { BUILTIN_PLUGINS, manifestJsonSchema } from "./manifest.js";
|
|
11
12
|
const GENERATED_START = "<!-- shard:generated -->";
|
|
12
13
|
const GENERATED_END = "<!-- /shard:generated -->";
|
|
@@ -343,9 +344,15 @@ Add \`"sprite"\` to \`plugins\` in \`shard.json\`. 2D uses the same renderer as
|
|
|
343
344
|
"duration": 0.1 }, ...], "loop": "loop", "events": [{ "frame": 2, "name": "step" }] }\`, then
|
|
344
345
|
\`"sprite/SpriteAnimation": { "clip": { "path": "assets/walk.clip.json" } }\`. Read events with
|
|
345
346
|
\`world.reader(SpriteAnimationEvent)\`.
|
|
346
|
-
4. Tilemaps: \`level.tilemap.json\`
|
|
347
|
-
\`
|
|
348
|
-
\`
|
|
347
|
+
4. Tilemaps: \`level.tilemap.json\` names tiles by atlas region in a \`palette\`, and stores each layer
|
|
348
|
+
as \`chunks\` of rows: \`"0,0": ["grass*12 stone*4", "grass stone:fx . water*13"]\` (runs of
|
|
349
|
+
\`name[:flags][*count]\`, \`.\` empty, flags \`fx\`/\`fy\`/\`r90\` joined by \`+\`), then
|
|
350
|
+
\`"sprite/Tilemap": { "atlas": ..., "data": ..., "tileSize": [1, 1] }\`. Read and edit cells by
|
|
351
|
+
name with MCP \`tilemap_read\` / \`tilemap_edit\` (live; \`save: true\` writes the file) or
|
|
352
|
+
\`shard tiles read|edit\`; \`shard tiles <file> --encoding base64\` compacts a big generated map.
|
|
353
|
+
In code: \`setTile(world, map, x, y, data.tileId('stone'))\`; only that chunk re-uploads. On a
|
|
354
|
+
tabletop, add \`"render/GroundLayer": { "band": 10 }\` and lay it flat (\`tilemapOnGround()\`'s
|
|
355
|
+
rotation): it draws among the ground bands, lit by 3D lights, and picks return the cell.
|
|
349
356
|
5. Pixel art: \`"render/PixelPerfect": { "pixelsPerUnit": 16 }\` on an orthographic camera.
|
|
350
357
|
6. Check: \`shard validate\`, MCP \`preview_asset\` on the atlas (regions outlined and numbered) or
|
|
351
358
|
the clip, and \`render.describe\` \u2192 \`sprites\` (draw calls, sprites per layer, tilemap chunks).
|
|
@@ -1237,6 +1244,26 @@ g.label(position, 'target', [1, 1, 1, 1])
|
|
|
1237
1244
|
- \`shard screenshot <scene> --out shot.png --frames 60\` renders headless (no window needed).
|
|
1238
1245
|
- With the MCP server: \`screenshot\` returns the image; \`step\` advances frames; \`press\`/\`hold\`
|
|
1239
1246
|
drive input; \`query_entities\` / \`get_entity\` read state; \`recent_errors\` shows what failed.
|
|
1247
|
+
`,
|
|
1248
|
+
"check-in-browsers.md": `# Check it in real browsers
|
|
1249
|
+
|
|
1250
|
+
Headless screenshots are for regressions. Filtering, color and canvas compositing only show in a
|
|
1251
|
+
browser, and so do real frame times.
|
|
1252
|
+
|
|
1253
|
+
1. Write a plan (\`.shard/schemas/capture-plan.schema.json\`): the URL (\`shard dev\`'s, or a host
|
|
1254
|
+
page), browsers, DPRs, viewport, and shots with a \`state\` the page's
|
|
1255
|
+
\`installCapturePage(app, { apply })\` understands. \`shard dev\` pages take shots as they are.
|
|
1256
|
+
2. \`shard capture plan.json --out captures/latest --json\` takes them, runs \`steps\` and their
|
|
1257
|
+
\`expect\` checks in every client, and records each \`scenario\` (\`records/*.json\`).
|
|
1258
|
+
3. \`shard compare captures/latest --approved captures/approved --json\` diffs against approved
|
|
1259
|
+
images (\u0394E2000 and SSIM) and writes \`report.html\`. Changed or new shots fail.
|
|
1260
|
+
4. A change you meant: \`shard approve <shot> --reason "why it looks different"\`. A change you
|
|
1261
|
+
didn't: fix it. Approvals without a reason are refused.
|
|
1262
|
+
5. \`shard perf-check captures/latest/records --plan plan.json\` fails on a broken threshold and
|
|
1263
|
+
names the metric, the value and the budget.
|
|
1264
|
+
|
|
1265
|
+
With the MCP server attached to a running page: \`metrics_reset\`, exercise the scene, then
|
|
1266
|
+
\`metrics_record\` returns the same record the scripts write.
|
|
1240
1267
|
`
|
|
1241
1268
|
};
|
|
1242
1269
|
function agentsBlock(manifest) {
|
|
@@ -1252,13 +1279,15 @@ component schemas, game code is ECS (components, systems) in \`scripts/\`.
|
|
|
1252
1279
|
shard validate --json # manifest, assets, scenes: every error with a path
|
|
1253
1280
|
shard import --json # import new and changed asset files
|
|
1254
1281
|
shard mv <from> <to> # move an asset and rewrite references to it
|
|
1282
|
+
shard tiles read <map> # tilemap cells as rows; tiles edit <map> --cell x,y=name
|
|
1255
1283
|
shard run --frames 600 # headless run
|
|
1256
1284
|
shard screenshot ${manifest.startScene} --out shot.png
|
|
1257
1285
|
shard test --json # gameplay tests in tests/
|
|
1258
1286
|
shard check --json # type-check scripts: file, line, column
|
|
1259
1287
|
shard gen <generator> --seeds 1-9 --out sheet.png # preview a generator's outputs
|
|
1260
1288
|
shard dev # play it in a browser; saves hot reload in place
|
|
1261
|
-
shard
|
|
1289
|
+
shard capture plan.json # real-browser shots and perf records; then compare, approve, perf-check
|
|
1290
|
+
shard docs # regenerate this block, .agents/, and .shard/schemas/
|
|
1262
1291
|
shard mcp # MCP server for this project (see .mcp.json)
|
|
1263
1292
|
\`\`\`
|
|
1264
1293
|
|
|
@@ -1309,6 +1338,11 @@ function generateDocs(manifest, errors) {
|
|
|
1309
1338
|
".shard/schemas/prefab.schema.json": `${JSON.stringify(prefabJsonSchema(), null, 2)}
|
|
1310
1339
|
`,
|
|
1311
1340
|
".shard/schemas/save.schema.json": `${JSON.stringify(saveJsonSchema(), null, 2)}
|
|
1341
|
+
`,
|
|
1342
|
+
// Browser verification (0062): other renderers' pages validate their records against it.
|
|
1343
|
+
".shard/schemas/perf-record.schema.json": `${JSON.stringify(perfRecordJsonSchema(), null, 2)}
|
|
1344
|
+
`,
|
|
1345
|
+
".shard/schemas/capture-plan.schema.json": `${JSON.stringify(capturePlanJsonSchema(), null, 2)}
|
|
1312
1346
|
`
|
|
1313
1347
|
};
|
|
1314
1348
|
for (const [name, content] of Object.entries(SKILLS))
|
package/dist/host.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { GpuContext } from '@aethervtt/shard-gpu';
|
|
1
|
+
import type { BakedTranslations, GpuContext } from '@aethervtt/shard-gpu';
|
|
2
2
|
import type { AudioBackend, InputSource, Platform } from '@aethervtt/shard-platform';
|
|
3
3
|
import { type RenderTarget } from '@aethervtt/shard-render';
|
|
4
4
|
import { App, type Plugin } from '@aethervtt/shard-runtime';
|
|
@@ -12,6 +12,11 @@ export interface BuildAppOptions {
|
|
|
12
12
|
canvas?: HTMLCanvasElement | OffscreenCanvas;
|
|
13
13
|
/** Headless stand-in for the window (CLI screenshots). */
|
|
14
14
|
target?: RenderTarget;
|
|
15
|
+
/**
|
|
16
|
+
* The project's baked WebGL2 translations (`shard shaders bake`), or their URL: on WebGL2, the
|
|
17
|
+
* shaders they cover load without naga (0064). WebGPU never reads them.
|
|
18
|
+
*/
|
|
19
|
+
webgl2Shaders?: string | URL | BakedTranslations;
|
|
15
20
|
inputSource?: InputSource;
|
|
16
21
|
/** Where sound goes (`platform.audio`). Without one, audio records voices (headless). */
|
|
17
22
|
audio?: AudioBackend;
|
package/dist/host.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { animationPlugin } from "@aethervtt/shard-animation";
|
|
2
2
|
import { audioPlugin } from "@aethervtt/shard-audio";
|
|
3
|
+
import { controlsPlugin } from "@aethervtt/shard-controls";
|
|
4
|
+
import { dicePlugin } from "@aethervtt/shard-dice";
|
|
3
5
|
import { gltfPlugin } from "@aethervtt/shard-gltf";
|
|
4
|
-
import { inputPlugin } from "@aethervtt/shard-input";
|
|
6
|
+
import { gesturesPlugin, inputPlugin } from "@aethervtt/shard-input";
|
|
5
7
|
import { loadNavCache, Nav, navGridPlugin, navPlugin } from "@aethervtt/shard-nav";
|
|
6
8
|
import { noisePlugin } from "@aethervtt/shard-noise";
|
|
7
9
|
import { particlesPlugin } from "@aethervtt/shard-particles";
|
|
@@ -20,8 +22,13 @@ import { uiPlugin } from "@aethervtt/shard-ui";
|
|
|
20
22
|
function buildApp(options) {
|
|
21
23
|
const { manifest } = options;
|
|
22
24
|
const names = new Set(manifest.plugins);
|
|
23
|
-
if (names.has("sprite") || names.has("text") || names.has("particles") || names.has("animation"))
|
|
25
|
+
if (names.has("sprite") || names.has("text") || names.has("particles") || names.has("animation") || names.has("dice"))
|
|
24
26
|
names.add("render/forward");
|
|
27
|
+
if (names.has("controls")) {
|
|
28
|
+
names.add("gestures");
|
|
29
|
+
names.add("render/forward");
|
|
30
|
+
}
|
|
31
|
+
if (names.has("gestures")) names.add("input");
|
|
25
32
|
if (names.has("render/forward")) {
|
|
26
33
|
names.add("render");
|
|
27
34
|
names.add("core/transform");
|
|
@@ -33,7 +40,12 @@ function buildApp(options) {
|
|
|
33
40
|
if (names.has("core/transform")) app.addPlugin(TransformPlugin);
|
|
34
41
|
if (names.has("render")) {
|
|
35
42
|
app.addPlugin(
|
|
36
|
-
renderPlugin({
|
|
43
|
+
renderPlugin({
|
|
44
|
+
gpu: options.gpu,
|
|
45
|
+
canvas: options.canvas,
|
|
46
|
+
target: options.target,
|
|
47
|
+
webgl2: options.webgl2Shaders ? { shaders: options.webgl2Shaders } : void 0
|
|
48
|
+
})
|
|
37
49
|
);
|
|
38
50
|
}
|
|
39
51
|
if (names.has("render/forward")) {
|
|
@@ -44,9 +56,12 @@ function buildApp(options) {
|
|
|
44
56
|
if (names.has("text")) app.addPlugin(textPlugin);
|
|
45
57
|
if (names.has("particles")) app.addPlugin(particlesPlugin);
|
|
46
58
|
if (names.has("animation")) app.addPlugin(animationPlugin);
|
|
47
|
-
if (names.has("
|
|
48
|
-
if (names.has("
|
|
59
|
+
if (names.has("dice")) app.addPlugin(dicePlugin());
|
|
60
|
+
if (names.has("physics3d")) app.addPlugin(physics3dPlugin());
|
|
61
|
+
if (names.has("physics2d")) app.addPlugin(physics2dPlugin());
|
|
49
62
|
if (names.has("input")) app.addPlugin(inputPlugin({ source: options.inputSource }));
|
|
63
|
+
if (names.has("gestures")) app.addPlugin(gesturesPlugin);
|
|
64
|
+
if (names.has("controls")) app.addPlugin(controlsPlugin);
|
|
50
65
|
if (names.has("audio")) app.addPlugin(audioPlugin({ backend: options.audio }));
|
|
51
66
|
if (names.has("ui")) app.addPlugin(uiPlugin);
|
|
52
67
|
if (names.has("nav")) app.addPlugin(navPlugin);
|
package/dist/manifest.d.ts
CHANGED
|
@@ -18,6 +18,9 @@ export declare const Manifest: import("@aethervtt/shard-core").ComponentDef<{
|
|
|
18
18
|
readonly procgen: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").InferFields<{
|
|
19
19
|
cacheSize: import("@aethervtt/shard-core").FieldType<number, "u32">;
|
|
20
20
|
}>, "object">;
|
|
21
|
+
readonly graphics: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").InferFields<{
|
|
22
|
+
baseline: import("@aethervtt/shard-core").FieldType<"optional" | "required", "u8">;
|
|
23
|
+
}>, "object">;
|
|
21
24
|
readonly plugins: import("@aethervtt/shard-core").FieldType<string[], "object">;
|
|
22
25
|
}>;
|
|
23
26
|
export type ManifestValue = Infer<typeof Manifest>;
|
|
@@ -34,11 +37,14 @@ export declare const BUILTIN_PLUGINS: {
|
|
|
34
37
|
readonly physics3d: 'Rapier 3D rigid bodies, colliders, joints, raycasts (includes core/transform)';
|
|
35
38
|
readonly physics2d: 'Rapier 2D physics in the XY plane (includes core/transform)';
|
|
36
39
|
readonly input: 'keyboard, mouse, gamepad, touch, action maps';
|
|
40
|
+
readonly gestures: 'taps, long presses, drags, pinches, twists and the wheel as Gesture events, with claims and Escape to cancel (includes input)';
|
|
41
|
+
readonly controls: 'OrbitControls and MapControls on cameras, syncViews, PlaneDrag to drag entities along a plane, controls.describe (includes gestures, render/forward)';
|
|
37
42
|
readonly audio: 'AudioSource, AudioListener, playSound, buses and ducking, spatial audio, voice limits (includes core/transform)';
|
|
38
43
|
readonly ui: 'UiNode trees under a UiRoot: flexbox layout, text, images, buttons, sliders, text fields, world-anchored markers, themes (includes core/transform)';
|
|
39
44
|
readonly nav: 'navigation: NavGrid A* (2D), NavMesh baked by Recast from NavSource geometry, OffMeshLink, path queries, NavAgent steering (includes core/transform)';
|
|
40
45
|
readonly 'nav/grid': 'navigation on NavGrids only, without loading the Recast WASM (includes core/transform)';
|
|
41
46
|
readonly terrain: 'planets: Planet cube-sphere terrain from noise graphs with LOD, geomorphing, oceans, biomes, collider chunks around characters, navmeshes on the surface; terrain.* methods (includes core/transform; add render/forward to draw it, physics3d for colliders)';
|
|
47
|
+
readonly dice: 'the dice table (0054): dice skins, face layouts and effect recipes as data, dice.play and dice.describe, dice/RollRequest in scenes; tracks record on the calling thread (includes render/forward)';
|
|
42
48
|
readonly 'core/transform': 'Transform and hierarchy propagation';
|
|
43
49
|
};
|
|
44
50
|
export declare function manifestJsonSchema(): JsonSchema;
|
package/dist/manifest.js
CHANGED
|
@@ -39,9 +39,18 @@ const Manifest = defineSchema(
|
|
|
39
39
|
},
|
|
40
40
|
{ description: "Generators (0042): output cache limits." }
|
|
41
41
|
),
|
|
42
|
+
graphics: t.struct(
|
|
43
|
+
{
|
|
44
|
+
baseline: t.enum(["optional", "required"], {
|
|
45
|
+
default: "optional",
|
|
46
|
+
description: "required: the project must run at the baseline tier (WebGPU compatibility mode, WebGL2), and shard validate checks it. optional: what the tier can't run is reported when it runs."
|
|
47
|
+
})
|
|
48
|
+
},
|
|
49
|
+
{ description: "Graphics tiers (0064)." }
|
|
50
|
+
),
|
|
42
51
|
plugins: t.list(t.string, {
|
|
43
52
|
default: ["render/forward", "input"],
|
|
44
|
-
description: "Engine plugins to enable, by name: render, render/forward, sprite, text, particles, animation, physics3d, physics2d, input, audio, ui, nav, nav/grid, core/transform."
|
|
53
|
+
description: "Engine plugins to enable, by name: render, render/forward, sprite, text, particles, animation, physics3d, physics2d, input, gestures, controls, audio, ui, nav, nav/grid, dice, core/transform."
|
|
45
54
|
})
|
|
46
55
|
},
|
|
47
56
|
{ description: "shard.json: the project manifest." }
|
|
@@ -89,11 +98,14 @@ const BUILTIN_PLUGINS = {
|
|
|
89
98
|
physics3d: "Rapier 3D rigid bodies, colliders, joints, raycasts (includes core/transform)",
|
|
90
99
|
physics2d: "Rapier 2D physics in the XY plane (includes core/transform)",
|
|
91
100
|
input: "keyboard, mouse, gamepad, touch, action maps",
|
|
101
|
+
gestures: "taps, long presses, drags, pinches, twists and the wheel as Gesture events, with claims and Escape to cancel (includes input)",
|
|
102
|
+
controls: "OrbitControls and MapControls on cameras, syncViews, PlaneDrag to drag entities along a plane, controls.describe (includes gestures, render/forward)",
|
|
92
103
|
audio: "AudioSource, AudioListener, playSound, buses and ducking, spatial audio, voice limits (includes core/transform)",
|
|
93
104
|
ui: "UiNode trees under a UiRoot: flexbox layout, text, images, buttons, sliders, text fields, world-anchored markers, themes (includes core/transform)",
|
|
94
105
|
nav: "navigation: NavGrid A* (2D), NavMesh baked by Recast from NavSource geometry, OffMeshLink, path queries, NavAgent steering (includes core/transform)",
|
|
95
106
|
"nav/grid": "navigation on NavGrids only, without loading the Recast WASM (includes core/transform)",
|
|
96
107
|
terrain: "planets: Planet cube-sphere terrain from noise graphs with LOD, geomorphing, oceans, biomes, collider chunks around characters, navmeshes on the surface; terrain.* methods (includes core/transform; add render/forward to draw it, physics3d for colliders)",
|
|
108
|
+
dice: "the dice table (0054): dice skins, face layouts and effect recipes as data, dice.play and dice.describe, dice/RollRequest in scenes; tracks record on the calling thread (includes render/forward)",
|
|
97
109
|
"core/transform": "Transform and hierarchy propagation"
|
|
98
110
|
};
|
|
99
111
|
function manifestJsonSchema() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aethervtt/shard-project",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -16,29 +16,33 @@
|
|
|
16
16
|
"README.md"
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@aethervtt/shard-animation": "0.0
|
|
20
|
-
"@aethervtt/shard-assets": "0.0
|
|
21
|
-
"@aethervtt/shard-audio": "0.0
|
|
22
|
-
"@aethervtt/shard-
|
|
23
|
-
"@aethervtt/shard-
|
|
24
|
-
"@aethervtt/shard-
|
|
25
|
-
"@aethervtt/shard-
|
|
26
|
-
"@aethervtt/shard-
|
|
27
|
-
"@aethervtt/shard-
|
|
28
|
-
"@aethervtt/shard-
|
|
29
|
-
"@aethervtt/shard-
|
|
30
|
-
"@aethervtt/shard-
|
|
31
|
-
"@aethervtt/shard-
|
|
32
|
-
"@aethervtt/shard-
|
|
33
|
-
"@aethervtt/shard-
|
|
34
|
-
"@aethervtt/shard-
|
|
35
|
-
"@aethervtt/shard-
|
|
36
|
-
"@aethervtt/shard-
|
|
37
|
-
"@aethervtt/shard-
|
|
38
|
-
"@aethervtt/shard-
|
|
39
|
-
"@aethervtt/shard-
|
|
40
|
-
"@aethervtt/shard-
|
|
41
|
-
"@aethervtt/shard-
|
|
19
|
+
"@aethervtt/shard-animation": "0.1.0",
|
|
20
|
+
"@aethervtt/shard-assets": "0.1.0",
|
|
21
|
+
"@aethervtt/shard-audio": "0.1.0",
|
|
22
|
+
"@aethervtt/shard-controls": "0.1.0",
|
|
23
|
+
"@aethervtt/shard-core": "0.1.0",
|
|
24
|
+
"@aethervtt/shard-dice": "0.1.0",
|
|
25
|
+
"@aethervtt/shard-fog": "0.1.0",
|
|
26
|
+
"@aethervtt/shard-gltf": "0.1.0",
|
|
27
|
+
"@aethervtt/shard-gpu": "0.1.0",
|
|
28
|
+
"@aethervtt/shard-input": "0.1.0",
|
|
29
|
+
"@aethervtt/shard-nav": "0.1.0",
|
|
30
|
+
"@aethervtt/shard-noise": "0.1.0",
|
|
31
|
+
"@aethervtt/shard-particles": "0.1.0",
|
|
32
|
+
"@aethervtt/shard-physics": "0.1.0",
|
|
33
|
+
"@aethervtt/shard-platform": "0.1.0",
|
|
34
|
+
"@aethervtt/shard-procgen": "0.1.0",
|
|
35
|
+
"@aethervtt/shard-protocol": "0.1.0",
|
|
36
|
+
"@aethervtt/shard-render": "0.1.0",
|
|
37
|
+
"@aethervtt/shard-runtime": "0.1.0",
|
|
38
|
+
"@aethervtt/shard-save": "0.1.0",
|
|
39
|
+
"@aethervtt/shard-scene": "0.1.0",
|
|
40
|
+
"@aethervtt/shard-sprite": "0.1.0",
|
|
41
|
+
"@aethervtt/shard-terrain": "0.1.0",
|
|
42
|
+
"@aethervtt/shard-text": "0.1.0",
|
|
43
|
+
"@aethervtt/shard-transform": "0.1.0",
|
|
44
|
+
"@aethervtt/shard-ui": "0.1.0",
|
|
45
|
+
"@aethervtt/shard-verify": "0.1.0"
|
|
42
46
|
},
|
|
43
47
|
"repository": {
|
|
44
48
|
"type": "git",
|
|
@@ -50,6 +54,6 @@
|
|
|
50
54
|
"provenance": true
|
|
51
55
|
},
|
|
52
56
|
"shard": {
|
|
53
|
-
"buildSha": "
|
|
57
|
+
"buildSha": "8e14c9896f82a3554b156f1ffdd68f2cc3e7d78a"
|
|
54
58
|
}
|
|
55
59
|
}
|