@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 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\` layers are base64 u16 tiles (atlas region + 1, 0 empty), and
347
- \`"sprite/Tilemap": { "atlas": ..., "data": ..., "tileSize": [1, 1] }\`. Edit at runtime with
348
- \`setTile(world, map, x, y, tile)\`; only that chunk re-uploads.
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 docs # regenerate this block, .agents/, and .shard/schemas/
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({ gpu: options.gpu, canvas: options.canvas, target: options.target })
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("physics3d")) app.addPlugin(physics3dPlugin);
48
- if (names.has("physics2d")) app.addPlugin(physics2dPlugin);
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);
@@ -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.1",
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.1",
20
- "@aethervtt/shard-assets": "0.0.1",
21
- "@aethervtt/shard-audio": "0.0.1",
22
- "@aethervtt/shard-core": "0.0.1",
23
- "@aethervtt/shard-gltf": "0.0.1",
24
- "@aethervtt/shard-gpu": "0.0.1",
25
- "@aethervtt/shard-input": "0.0.1",
26
- "@aethervtt/shard-nav": "0.0.1",
27
- "@aethervtt/shard-noise": "0.0.1",
28
- "@aethervtt/shard-particles": "0.0.1",
29
- "@aethervtt/shard-physics": "0.0.1",
30
- "@aethervtt/shard-platform": "0.0.1",
31
- "@aethervtt/shard-procgen": "0.0.1",
32
- "@aethervtt/shard-protocol": "0.0.1",
33
- "@aethervtt/shard-render": "0.0.1",
34
- "@aethervtt/shard-runtime": "0.0.1",
35
- "@aethervtt/shard-save": "0.0.1",
36
- "@aethervtt/shard-scene": "0.0.1",
37
- "@aethervtt/shard-sprite": "0.0.1",
38
- "@aethervtt/shard-terrain": "0.0.1",
39
- "@aethervtt/shard-text": "0.0.1",
40
- "@aethervtt/shard-transform": "0.0.1",
41
- "@aethervtt/shard-ui": "0.0.1"
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": "d6c5c6aef4675575c878b02cf2c8f738b4190229"
57
+ "buildSha": "8e14c9896f82a3554b156f1ffdd68f2cc3e7d78a"
54
58
  }
55
59
  }