@vgai/editor 0.4.0 → 0.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.
Files changed (137) hide show
  1. package/dist/assets/{axe-BJjvBeOZ.js → axe-N4cNyzfs.js} +1 -1
  2. package/dist/assets/{browserAll-Dn0pgD87.js → browserAll-BIBT0Z2H.js} +1 -1
  3. package/dist/assets/{create-runtime-BF9OD-U8.js → create-runtime-DxLhdk4U.js} +1 -1
  4. package/dist/assets/{game-u6c2Patu.js → game-4NbNRROm.js} +1 -1
  5. package/dist/assets/{game-umrHIqFB.js → game-B3UH5LjS.js} +1 -1
  6. package/dist/assets/{game-BR0GBNgF.js → game-ByxRLJi1.js} +1 -1
  7. package/dist/assets/{game-DeTGdgYt.js → game-CJ5BqOYG.js} +1 -1
  8. package/dist/assets/{game-DQxhCYsZ.js → game-CSJm1kwB.js} +1 -1
  9. package/dist/assets/{game-CLNm2g0y.js → game-D9Mo4L3Q.js} +1 -1
  10. package/dist/assets/{game-B346oj_f.js → game-DJmU9BQF.js} +1 -1
  11. package/dist/assets/{game-BCNN63uV.js → game-ZqGLgxRj.js} +1 -1
  12. package/dist/assets/{game-loop-t5W_oqxz.js → game-loop-9YP76JB1.js} +1 -1
  13. package/dist/assets/{game-source-DzE9taML.js → game-source-DLKG9pmj.js} +1 -1
  14. package/dist/assets/{index-DbQzGLxv.js → index-8A4JLNIp.js} +902 -892
  15. package/dist/assets/{index-CgcH1RpF.js → index-Brzr17q0.js} +1 -1
  16. package/dist/assets/{index-D3tcNB2T.js → index-CnKtvrru.js} +1 -1
  17. package/dist/assets/{index-Fc8Ezr20.js → index-D-WR11Jw.js} +1 -1
  18. package/dist/assets/index-DiUacMXR.css +1 -0
  19. package/dist/assets/{ingest-siblings-CW_xbvrF.js → ingest-siblings-Cmx39OBK.js} +1 -1
  20. package/dist/assets/{mount-manifest-DgUscyaD.js → mount-manifest-kzdZgkkc.js} +1 -1
  21. package/dist/assets/{react-18-C4tVCii_.js → react-18-DvWsdwa_.js} +1 -1
  22. package/dist/assets/webworkerAll-C9GR-KrR.js +1 -0
  23. package/dist/assets/{world-documents-BSaA5YIF.js → world-documents-DM_aIMAW.js} +1 -1
  24. package/dist/index.html +2 -2
  25. package/dist-server/packaged.mjs +27125 -1075
  26. package/package.json +2 -2
  27. package/server/asset-history-snapshots.ts +189 -0
  28. package/server/asset-library-routes.ts +404 -53
  29. package/server/catalog-import-recipe.ts +97 -0
  30. package/server/catalog-reimport.ts +141 -0
  31. package/server/cloud-asset-catalog.ts +71 -5
  32. package/server/dev.ts +1 -1
  33. package/server/editor-server.ts +106 -51
  34. package/server/editor-sse.ts +5 -0
  35. package/server/generative-execution-context.ts +45 -0
  36. package/server/model-import-conversion.ts +345 -0
  37. package/server/packaged.ts +1 -1
  38. package/server/project-hmr-files.ts +3 -5
  39. package/server/project-output-writer.ts +440 -36
  40. package/server/project-tools.ts +529 -0
  41. package/server/server-utils.ts +14 -11
  42. package/src/EditorContext.tsx +4 -4
  43. package/src/action-registry.ts +12 -0
  44. package/src/asset-editor-context.tsx +9 -0
  45. package/src/asset-events.ts +3 -15
  46. package/src/asset-selection.ts +3 -0
  47. package/src/asset-workflow/asset-capabilities.ts +40 -0
  48. package/src/asset-workflow/asset-import-jobs.ts +108 -0
  49. package/src/asset-workflow/asset-library-query.ts +8 -1
  50. package/src/asset-workflow/asset-workflow-quality.ts +2 -0
  51. package/src/asset-workflow/import-contract.ts +2 -0
  52. package/src/asset-workflow/model-inspection.ts +92 -1
  53. package/src/asset-workflow/preview-resource-lifetime.ts +44 -0
  54. package/src/asset-workflow/project-asset-commands.ts +1 -0
  55. package/src/asset-workflow/project-asset-health.ts +3 -6
  56. package/src/asset-workflow/project-asset-operations.ts +119 -4
  57. package/src/command-listener.ts +37 -9
  58. package/src/components/ApplicationMenus.tsx +13 -15
  59. package/src/components/AssetBrowser.tsx +301 -26
  60. package/src/components/AssetImportDetails.tsx +207 -92
  61. package/src/components/AssetImportJobStatus.tsx +55 -0
  62. package/src/components/CenterDocuments.tsx +28 -1
  63. package/src/components/CommandPalette.tsx +2 -2
  64. package/src/components/DefaultEditorLayout.tsx +4 -0
  65. package/src/components/InspectorToolSection.tsx +12 -10
  66. package/src/components/OnlineAssetBrowser.tsx +197 -11
  67. package/src/components/ToolHost.tsx +8 -8
  68. package/src/components/ViewportOverlay.tsx +13 -26
  69. package/src/components/ViewportPanel.tsx +19 -14
  70. package/src/components/ViewportShadingMenu.tsx +103 -0
  71. package/src/components/WorkspaceDock.tsx +103 -17
  72. package/src/components/asset-documents.tsx +38 -11
  73. package/src/components/asset-editor-persistence.ts +300 -5
  74. package/src/components/asset-selection-section.tsx +83 -9
  75. package/src/components/asset-viewers/AudioViewer.tsx +4 -7
  76. package/src/components/asset-viewers/ModelViewer.tsx +168 -5
  77. package/src/components/asset-viewers/OnlineAssetDetail.tsx +52 -12
  78. package/src/components/asset-viewers/SourceAssetViewer.tsx +7 -26
  79. package/src/components/asset-workflow.css +30 -3
  80. package/src/components/primitives/editor-icons.ts +1 -0
  81. package/src/components/project-tool-documents.tsx +298 -0
  82. package/src/components/status-contributions.tsx +38 -0
  83. package/src/components/tool-documents.tsx +20 -17
  84. package/src/components/tool-schema-form.tsx +258 -0
  85. package/src/components/workspace-dock.css +16 -0
  86. package/src/editor-api.ts +159 -22
  87. package/src/editor-store.ts +6 -5
  88. package/src/editor-viewport.ts +12 -7
  89. package/src/game-viewport-state.ts +28 -0
  90. package/src/model-thumbnail.ts +97 -9
  91. package/src/play-mode.ts +30 -3
  92. package/src/project-tool-discovery.ts +5 -5
  93. package/src/{project-operations.ts → project-tools.ts} +39 -26
  94. package/src/scene-sync.ts +0 -67
  95. package/src/storage/handle-storage.ts +5 -1
  96. package/src/storage/http-storage.ts +7 -2
  97. package/src/storage/mem-storage.ts +7 -1
  98. package/src/storage/types.ts +2 -0
  99. package/src/theme-css.ts +17 -7
  100. package/src/tool-loader.ts +152 -241
  101. package/src/viewport-shading-boundary.ts +15 -0
  102. package/src/widgets/index.ts +2 -1
  103. package/src/workspace-dock-controller.ts +8 -0
  104. package/src/workspace-document-registry.ts +4 -4
  105. package/src/workspace-state-persistence.ts +36 -10
  106. package/template/.claude/skills/editor/SKILL.md +13 -3
  107. package/template/AGENTS.md +100 -83
  108. package/template/package.json +16 -0
  109. package/template/public/scenes/default.vscn.json +1 -2
  110. package/template/scripts/playtest.ts +7 -37
  111. package/template/server/rooms/game-room.ts +2 -2
  112. package/template/src/data/README.md +3 -2
  113. package/template/src/data/tuning.data.json +1 -3
  114. package/template/src/data/tuning.schema.json +0 -12
  115. package/template/src/data/tuning.schema.ts +2 -9
  116. package/template/src/data/tuning.ts +1 -1
  117. package/template/src/scripts/registry.ts +0 -3
  118. package/template/src/tools/README.md +151 -16
  119. package/template/src/tools/autoplay.tool.ts +31 -0
  120. package/template/src/tools/autoplay.ts +15 -0
  121. package/template/src/tools/tuning.document.tsx +107 -0
  122. package/template/src/tools/tuning.tool.ts +24 -0
  123. package/template/src/tools/tuning.ts +9 -0
  124. package/template/src/ui/Button.stories.tsx +1 -1
  125. package/template/tests/acceptance/example.spec.ts +4 -59
  126. package/template/vgai.game.json +1 -1
  127. package/dist/assets/index-bAGhEnnO.css +0 -1
  128. package/dist/assets/webworkerAll-BA4aq7xq.js +0 -1
  129. package/dist/project-scripts.bundle.js +0 -3917
  130. package/server/project-operations.ts +0 -343
  131. package/src/components/project-operation-documents.tsx +0 -226
  132. package/template/src/operations/README.md +0 -88
  133. package/template/src/scripts/components/data-spinner.ts +0 -86
  134. package/template/src/scripts/components/spin-lap-math.ts +0 -27
  135. package/template/src/tools/example.tool.tsx +0 -97
  136. package/template/src/tools/spin.tool.tsx +0 -101
  137. package/template/tests/logic/example.test.ts +0 -31
@@ -1,16 +1,151 @@
1
- # src/tools — project tools
2
-
3
- Editor dev/tuning panels live here: `<name>.tool.tsx` exporting
4
- `tool = defineTool({...})` plus a default React component — the editor
5
- discovers them automatically. Placements: `'document'` (opens as a center
6
- workspace document via the palette's `Open Tool: <title>` action —
7
- `example.tool.tsx`, the shipped worked example; `'dock'` is a deprecated
8
- alias), `'utility'` (a bottom utility tab for transient output), and
9
- `'inspector'` (⚙ section in the Inspector when its `match(node, adapter)`
10
- accepts the selection, e.g. `adapter.components?.list(node.id)`
11
- `spin.tool.tsx`). Inside an inspector tool, `useSelection()` (from
12
- `@engine/react/use-selection`) returns the selected node, its attached
13
- components, and each component's own authored config, live. Build panels
14
- from `@editor/widgets` (the editor's own widget kit + tokens) so they look
15
- native. Editor-only: `npm run build` never bundles this folder see
16
- `docs/DATA-TOOLS-DESIGN.md` §3–§4 in the engine checkout.
1
+ # src/tools — registered project callables
2
+
3
+ A tool is an ordinary JavaScript/TypeScript function explicitly registered in
4
+ `package.json#vgai.tools`. The callable module exports `tool = defineTool(...)`
5
+ from `@vgai/sdk/tools`; keep reusable behavior in an ordinary sibling module so
6
+ game code and npm scripts can call it directly.
7
+
8
+ Every registered tool is discoverable and callable through:
9
+
10
+ - `import { tools } from '@vgai/live'` → `tools.list()` / `tools.call(...)`
11
+ - `vgai run --list` / `vgai run <name>`
12
+ - the editor's Project Tools catalog and schema-driven fallback form
13
+
14
+ A registration may also list editor `contributions`. Each one names an ordinary
15
+ React module, a stable contribution point, and a title. Its default component
16
+ receives `{ tool, client }` and calls `client.runProjectTool(tool.name, ...)`;
17
+ it must not contain a second copy of the tool's behavior. The supported points
18
+ are `workspace.document`, `workspace.utility`, and `selection.inspector` (the
19
+ last also exports `match(node, adapter)`). Prefer a focused custom contribution
20
+ for a substantial workflow; the generated form and Advanced JSON remain the
21
+ universal fallback.
22
+
23
+ Contribution UI is editor chrome. Use ordinary React for layout and state, and
24
+ import shared interactive controls from `@editor/widgets` so buttons, fields,
25
+ menus, focus rings, disabled states, density, and theme switching match the
26
+ rest of the editor. Inline styles are appropriate for layout and data-driven
27
+ values, not for forking the editor's control paint.
28
+
29
+ Match the contribution point's native shape: documents can use a bounded,
30
+ scrollable workspace; inspector sections should be dense and one-column;
31
+ utilities should be compact rows or small status blocks rather than document
32
+ forms dropped into the bottom dock. Keep badges to short statuses or counts.
33
+ Buttons and badges are intrinsically sized; set `width: '100%'` only when a
34
+ full-width action is intentional.
35
+
36
+ ```tsx
37
+ import { Button, TextInput } from '@editor/widgets';
38
+ ```
39
+
40
+ ```json
41
+ {
42
+ "entry": "./src/tools/tuning.tool.ts",
43
+ "contributions": [
44
+ {
45
+ "id": "tuning",
46
+ "point": "workspace.document",
47
+ "title": "Tuning",
48
+ "entry": "./src/tools/tuning.document.tsx"
49
+ }
50
+ ]
51
+ }
52
+ ```
53
+
54
+ The starter registers `project.tuning` and `project.autoplay`. Both callable
55
+ implementations deliberately report `TOOL_NOT_CONFIGURED` until replaced with
56
+ game-specific behavior. `npm run playtest` calls the same `runAutoplay`
57
+ function as the registered autoplay tool.
58
+
59
+ Installed libraries can advertise helper tools through their own
60
+ `package.json#vgai.tools`; the humanoid bake is one example. Registration adds
61
+ addressability only—there is no extension base class, tool lifecycle, scripting
62
+ language, or domain switch in the host.
63
+
64
+ ## Provider-native generated asset
65
+
66
+ Install Fal only in a project that authors Fal-backed assets:
67
+
68
+ ```sh
69
+ npm install --save-dev @fal-ai/client@1.10.1 @vgai/fal-client-compat
70
+ ```
71
+
72
+ Keep the useful implementation as ordinary code. This mock-first example uses
73
+ the upstream Fal call unchanged, validates the PNG, and hands the accepted file
74
+ to the host once. Direct and managed execution replace only the client Fetch
75
+ configuration; they do not change `fal.run(...)` or add provenance bookkeeping.
76
+
77
+ ```ts
78
+ // src/tools/generate-icon.ts
79
+ import { createFalClient } from '@fal-ai/client';
80
+ import { createMockFalFetch } from '@vgai/fal-client-compat/mock';
81
+ import type { ToolContext } from '@vgai/sdk/tools';
82
+
83
+ export interface GenerateIconInput {
84
+ prompt: string;
85
+ }
86
+
87
+ export async function generateIcon(input: GenerateIconInput, ctx: ToolContext) {
88
+ if (!ctx.projectOutputs) throw new Error('The project-output writer is unavailable.');
89
+ const fal = createFalClient({
90
+ credentials: 'vgai-mock',
91
+ fetch: createMockFalFetch(),
92
+ });
93
+ const generated = await fal.run('fal-ai/nano-banana', {
94
+ input: { prompt: input.prompt, output_format: 'png' },
95
+ });
96
+ const file = generated.data.images[0];
97
+ if (!file) throw new Error('Fal returned no image.');
98
+ const response = await fetch(file.url);
99
+ if (!response.ok) throw new Error(`Fal image download failed: HTTP ${response.status}.`);
100
+ const bytes = new Uint8Array(await response.arrayBuffer());
101
+ const png = [137, 80, 78, 71, 13, 10, 26, 10];
102
+ if (!png.every((byte, index) => bytes[index] === byte)) {
103
+ throw new Error('Fal output is not a PNG.');
104
+ }
105
+ const path = 'public/generated/icon.png';
106
+ const committed = await ctx.projectOutputs.write([
107
+ { path, content: bytes, mediaType: 'image/png', role: 'asset' },
108
+ ]);
109
+ return {
110
+ path,
111
+ bytes: bytes.byteLength,
112
+ provenanceOperationId: committed.provenanceOperationId!,
113
+ };
114
+ }
115
+ ```
116
+
117
+ The externally callable definition is deliberately thin:
118
+
119
+ ```ts
120
+ // src/tools/generate-icon.tool.ts
121
+ import { defineTool } from '@vgai/sdk/tools';
122
+ import { z } from 'zod';
123
+ import { generateIcon } from './generate-icon';
124
+
125
+ export const tool = defineTool({
126
+ name: 'project.icon.generate',
127
+ summary: 'Generate a project icon with Fal.',
128
+ description: 'Runs the project-owned native Fal call and commits one validated PNG.',
129
+ input: z.object({ prompt: z.string().min(1) }),
130
+ result: z.object({
131
+ path: z.string(),
132
+ bytes: z.number().int().positive(),
133
+ provenanceOperationId: z.string(),
134
+ }),
135
+ errors: [],
136
+ requires: { project: true },
137
+ host: 'node',
138
+ mutates: true,
139
+ supportsDryRun: false,
140
+ longRunning: true,
141
+ permission: { risk: 'write', summary: 'Writes one generated PNG into this project.' },
142
+ impl: generateIcon,
143
+ });
144
+ ```
145
+
146
+ Register `"./src/tools/generate-icon.tool.ts"` in `package.json#vgai.tools`,
147
+ then call it with a normal JSON input file:
148
+
149
+ ```sh
150
+ npm run vgai -- run project.icon.generate --input generate-icon.input.json --yes
151
+ ```
@@ -0,0 +1,31 @@
1
+ import { defineTool } from '@vgai/sdk/tools';
2
+ import { z } from 'zod';
3
+ import { runAutoplay } from './autoplay';
4
+
5
+ export const tool = defineTool({
6
+ name: 'project.autoplay',
7
+ summary: 'Autoplay this game.',
8
+ description:
9
+ 'Runs the game-authored @vgai/live interaction and acceptance loop. The starter implementation deliberately fails until the game has an honest playable path.',
10
+ input: z.object({}),
11
+ result: z.object({
12
+ passed: z.boolean(),
13
+ summary: z.string(),
14
+ artifacts: z.array(z.string()),
15
+ }),
16
+ errors: [
17
+ {
18
+ code: 'TOOL_NOT_CONFIGURED',
19
+ summary: 'The starter autoplay tool has not been implemented for this game.',
20
+ },
21
+ ],
22
+ requires: { project: true, editor: true, play: true },
23
+ host: 'node',
24
+ mutates: false,
25
+ supportsDryRun: false,
26
+ longRunning: true,
27
+ permission: { risk: 'read', summary: 'Drives and observes the running game.' },
28
+ async impl() {
29
+ return runAutoplay();
30
+ },
31
+ });
@@ -0,0 +1,15 @@
1
+ import { ToolError } from '@vgai/sdk/tools';
2
+
3
+ export interface AutoplayResult {
4
+ passed: boolean;
5
+ summary: string;
6
+ artifacts: string[];
7
+ }
8
+
9
+ /** Replace this stub with ordinary `@vgai/live` calls that drive and verify honest input. */
10
+ export async function runAutoplay(): Promise<AutoplayResult> {
11
+ throw new ToolError(
12
+ 'TOOL_NOT_CONFIGURED',
13
+ 'project.autoplay is a deliberate starter stub. Implement the first honest player interaction with @vgai/live as soon as the game becomes playable.',
14
+ );
15
+ }
@@ -0,0 +1,107 @@
1
+ import { Button } from '@editor/widgets';
2
+ import type { ToolContributionProps } from '@vgai/editor-sdk/contributions';
3
+ import { useState } from 'react';
4
+
5
+ /**
6
+ * Starter document for the game-specific tuning tool. Keep this UI small and
7
+ * intentional: its only job is to collect useful inputs and call the tool.
8
+ */
9
+ export default function TuningDocument({ tool, client }: ToolContributionProps) {
10
+ const [result, setResult] = useState<unknown>(null);
11
+ const [running, setRunning] = useState(false);
12
+
13
+ const verify = async () => {
14
+ setRunning(true);
15
+ try {
16
+ setResult(await client.runProjectTool(tool.name, {}, { confirm: true }));
17
+ } finally {
18
+ setRunning(false);
19
+ }
20
+ };
21
+
22
+ return (
23
+ <main
24
+ style={{
25
+ padding: 24,
26
+ fontFamily: 'var(--vgai-font-sans)',
27
+ maxWidth: 760,
28
+ margin: '0 auto',
29
+ color: 'inherit',
30
+ }}
31
+ data-testid="tuning-starter"
32
+ >
33
+ <section
34
+ style={{
35
+ overflow: 'hidden',
36
+ border: '1px solid var(--vgai-boundary-default)',
37
+ borderRadius: 'var(--vgai-radius-md)',
38
+ background: 'var(--vgai-surface-panel)',
39
+ }}
40
+ >
41
+ <div style={{ padding: '20px 22px' }}>
42
+ <div
43
+ style={{
44
+ color: 'var(--vgai-content-dim)',
45
+ fontSize: 11,
46
+ fontWeight: 700,
47
+ letterSpacing: '0.08em',
48
+ textTransform: 'uppercase',
49
+ }}
50
+ >
51
+ Starter contribution · not configured
52
+ </div>
53
+ <h2 style={{ margin: '7px 0 6px', fontSize: 20 }}>Game tuning</h2>
54
+ <p
55
+ style={{
56
+ margin: 0,
57
+ maxWidth: 620,
58
+ color: 'var(--vgai-content-muted)',
59
+ lineHeight: 1.55,
60
+ }}
61
+ >
62
+ Replace the schema in <code>tuning.tool.ts</code>, implement the data changes in{' '}
63
+ <code>tuning.ts</code>, then replace this starter with focused controls for the values
64
+ designers should change. Keep the behavior in the registered tool.
65
+ </p>
66
+ </div>
67
+ <footer
68
+ style={{
69
+ display: 'flex',
70
+ alignItems: 'center',
71
+ justifyContent: 'space-between',
72
+ gap: 16,
73
+ padding: '12px 22px',
74
+ borderTop: '1px solid var(--vgai-boundary-default)',
75
+ background: 'var(--vgai-surface-chrome)',
76
+ }}
77
+ >
78
+ <span style={{ color: 'var(--vgai-content-dim)', fontSize: 12 }}>
79
+ Calls <code>{tool.name}</code>
80
+ </span>
81
+ <Button
82
+ variant="solid"
83
+ disabled={running}
84
+ aria-busy={running}
85
+ onClick={() => void verify()}
86
+ >
87
+ {running ? 'Checking…' : 'Check callable stub'}
88
+ </Button>
89
+ </footer>
90
+ </section>
91
+ {result !== null && (
92
+ <pre
93
+ style={{
94
+ marginTop: 12,
95
+ padding: 12,
96
+ overflow: 'auto',
97
+ border: '1px solid var(--vgai-boundary-default)',
98
+ borderRadius: 'var(--vgai-radius-sm)',
99
+ background: 'var(--vgai-surface-inset)',
100
+ }}
101
+ >
102
+ {JSON.stringify(result, null, 2)}
103
+ </pre>
104
+ )}
105
+ </main>
106
+ );
107
+ }
@@ -0,0 +1,24 @@
1
+ import { defineTool } from '@vgai/sdk/tools';
2
+ import { z } from 'zod';
3
+ import { runTuning } from './tuning';
4
+
5
+ export const tool = defineTool({
6
+ name: 'project.tuning',
7
+ summary: 'Tune this game.',
8
+ description:
9
+ 'Starter contract for the game-specific tuning tool. Replace its schema and ordinary implementation before use.',
10
+ input: z.object({}),
11
+ result: z.object({ updated: z.array(z.string()) }),
12
+ errors: [
13
+ {
14
+ code: 'TOOL_NOT_CONFIGURED',
15
+ summary: 'The starter tuning tool has not been customized for this game.',
16
+ },
17
+ ],
18
+ requires: { project: true },
19
+ host: 'node',
20
+ mutates: true,
21
+ supportsDryRun: false,
22
+ permission: { risk: 'write', summary: "Updates this project's game-specific tuning data." },
23
+ impl: runTuning,
24
+ });
@@ -0,0 +1,9 @@
1
+ import { ToolError } from '@vgai/sdk/tools';
2
+
3
+ /** Replace this stub with ordinary project code that updates the game's real tuning data. */
4
+ export async function runTuning(_input: Record<string, never>): Promise<{ updated: string[] }> {
5
+ throw new ToolError(
6
+ 'TOOL_NOT_CONFIGURED',
7
+ 'project.tuning is a deliberate starter stub. Define the game-specific input schema, update its real tuning data, and make the game consume those values before using this tool.',
8
+ );
9
+ }
@@ -2,7 +2,7 @@
2
2
  * Worked CSF example — C3 (spec §9, docs/AI-NATIVE-AUTHORING-IMPLEMENTATION-SPEC.md
3
3
  * "Add CSF Discovery and Portable Story Mounting"). A conventional
4
4
  * `*.stories.tsx` file next to the component it documents, same "the file is
5
- * the document" doctrine `src/tools/example.tool.tsx` and `src/data/`'s
5
+ * the document" doctrine `src/tools/tuning.document.tsx` and `src/data/`'s
6
6
  * trio already follow in this project. In a React-root project, add
7
7
  * `parameters: { vgai: { root: '<root-id>' } }` to associate a CSF document;
8
8
  * the editor then shows it beneath that root in the main hierarchy and mounts
@@ -1,64 +1,9 @@
1
- /**
2
- * L2 — acceptance layer (`docs/ACCEPTANCE-DRIVER-SPEC.md` §2.3): the canonical
3
- * worked example. Run with `npm run test:acceptance` (`vgai probe`) — never
4
- * `npx playwright test` (see `@vgai/probe`'s PROBE_ENV_MISSING error). Drives
5
- * the template's shipped example component, `DataSpinner`
6
- * (`src/scripts/components/data-spinner.ts`).
7
- */
8
-
1
+ /** Replace this bridge smoke test with game-specific honest-input acceptance specs. */
9
2
  import { expect, test } from '@vgai/probe';
10
3
 
11
- // The built-in providers every project gets for free, no game code required
12
- // (Layer A, Task 1.2) a small parametrized loop over an array of cases,
13
- // rather than one test per case copy-pasted.
14
- const BUILT_IN_PROVIDERS = ['time', 'input.actions'];
15
-
16
- test('the debug bridge is alive before any game-specific state is asserted on', async ({
17
- game,
18
- }) => {
19
- // proves the whole pipe (page → bridge → fixture) before any game code —
20
- // a one-shot read races cold boot (sim may not have ticked yet under load),
21
- // so this is a waitFor like every other time-dependent read in this file.
22
- await game.waitFor((s) => (s('time') as { tick: number }).tick > 0, { simSeconds: 10 });
23
- });
24
-
25
- for (const name of BUILT_IN_PROVIDERS) {
26
- test(`built-in provider "${name}" is enumerated`, async ({ game }) => {
27
- const providers = await game.providers();
28
- expect(providers.map((p) => p.name)).toContain(name);
29
- });
30
- }
31
-
32
- test('spinner: fixture reset + honest-input boost + observable state + events + screenshot', async ({
33
- game,
34
- }) => {
35
- // Self-degrading (gem-runner friction, #146 ledger): this test proves the
36
- // TEMPLATE's shipped example component. The moment you replace the default
37
- // scene with your own game — no Box, no DataSpinner — it skips with this
38
- // message instead of failing your suite red. Replace it with specs for
39
- // YOUR game's providers (and delete this one) as your first real spec.
40
- const providerNames = (await game.providers()).map((p) => p.name);
41
- test.skip(
42
- !providerNames.includes('spinner'),
43
- 'the default scene\'s DataSpinner is gone (you replaced the scene — good); ' +
44
- 'replace this example spec with specs for your own game state',
45
- );
46
-
47
- // Fixture (declared cheat, D16): jump to a known lapsCompleted before
48
- // proving anything — never a substitute for the honest-input path below.
49
- await game.command('spinner.reset');
50
-
51
- // Proof: drive the SAME action a human would (see
52
- // public/inputmaps/default.inputmap.json's 'attack' binding) — DataSpinner
53
- // reads it via ctx.input.isPressed, not a synthetic backdoor.
54
- await game.input.hold('attack', { simSeconds: 4 });
55
-
56
- await game.waitFor((s) => (s('spinner') as { lapsCompleted: number }).lapsCompleted >= 1, {
4
+ test('the debug bridge is alive', async ({ game }) => {
5
+ await game.waitFor((state) => (state('time') as { tick: number }).tick > 0, {
57
6
  simSeconds: 10,
58
7
  });
59
- // one-shot reads are fine once a waitFor has established the state
60
- const spinner = (await game.state('spinner')) as { lapsCompleted: number };
61
- expect(spinner.lapsCompleted).toBeGreaterThanOrEqual(1);
62
- await game.events.expect(['spin-lap']);
63
- await game.screenshot('after-boosted-spin');
8
+ expect((await game.providers()).map((provider) => provider.name)).toContain('input.actions');
64
9
  });
@@ -6,7 +6,7 @@
6
6
  "editorPort": 5173,
7
7
  "version": "0.1.0",
8
8
  "engine": {
9
- "version": "0.4.0"
9
+ "version": "0.4.1"
10
10
  },
11
11
  "resolution": {
12
12
  "width": 1280,