@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
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@vgai/editor",
3
3
  "author": "Volter AI, Inc.",
4
4
  "license": "AGPL-3.0-only",
5
- "version": "0.4.0",
5
+ "version": "0.4.1",
6
6
  "type": "module",
7
7
  "repository": {
8
8
  "type": "git",
@@ -38,7 +38,7 @@
38
38
  "@fortawesome/free-solid-svg-icons": "^7.2.0",
39
39
  "@fortawesome/react-fontawesome": "^3.2.0",
40
40
  "@pixi/tilemap": "^5.0.0",
41
- "@vgai/engine": "0.4.0",
41
+ "@vgai/engine": "0.4.1",
42
42
  "axe-core": "^4.12.1",
43
43
  "chokidar": "^5.0.0",
44
44
  "dockview-react": "^7.0.2",
@@ -0,0 +1,189 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { createReadStream } from 'node:fs';
3
+ import { cp, mkdir, mkdtemp, readFile, readdir, rename, rm, stat, writeFile } from 'node:fs/promises';
4
+ import { basename, dirname, join, relative, resolve, sep } from 'node:path';
5
+ import { PROJECT_PROVENANCE_PATH } from '../../vgai-sdk/src/project/provenance';
6
+ import { isPathInside } from './server-utils';
7
+
8
+ interface AssetHistorySnapshotMetadata {
9
+ version: 1;
10
+ assetPath: string;
11
+ destination: string;
12
+ assetPresent: boolean;
13
+ provenancePresent: boolean;
14
+ }
15
+
16
+ async function pathExists(path: string): Promise<boolean> {
17
+ try {
18
+ await stat(path);
19
+ return true;
20
+ } catch (error) {
21
+ if (error instanceof Error && 'code' in error && error.code === 'ENOENT') return false;
22
+ throw error;
23
+ }
24
+ }
25
+
26
+ function normalizedAssetPath(assetPath: string): string {
27
+ const normalized = assetPath.replaceAll('\\', '/').replace(/^\/+/, '');
28
+ if (
29
+ !normalized.startsWith('asset-library/') ||
30
+ normalized.includes('\0') ||
31
+ normalized.split('/').includes('..') ||
32
+ basename(normalized) === ''
33
+ ) {
34
+ throw new Error('Asset history paths must name a file under public/asset-library/.');
35
+ }
36
+ return normalized;
37
+ }
38
+
39
+ async function hashFile(hash: ReturnType<typeof createHash>, path: string): Promise<void> {
40
+ const info = await stat(path);
41
+ hash.update(`file:${info.size}:`);
42
+ for await (const chunk of createReadStream(path)) hash.update(chunk as Buffer);
43
+ }
44
+
45
+ async function hashDirectory(
46
+ hash: ReturnType<typeof createHash>,
47
+ root: string,
48
+ directory: string,
49
+ ): Promise<void> {
50
+ const entries = (await readdir(directory, { withFileTypes: true })).sort((left, right) =>
51
+ left.name.localeCompare(right.name),
52
+ );
53
+ for (const entry of entries) {
54
+ const absolute = join(directory, entry.name);
55
+ const path = relative(root, absolute).split(sep).join('/');
56
+ if (entry.isDirectory()) {
57
+ hash.update(`directory:${path}\0`);
58
+ await hashDirectory(hash, root, absolute);
59
+ } else if (entry.isFile()) {
60
+ hash.update(`path:${path}\0`);
61
+ await hashFile(hash, absolute);
62
+ } else {
63
+ throw new Error(`Asset history cannot snapshot special filesystem entry ${absolute}.`);
64
+ }
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Content-addressed, server-resident snapshots for one imported asset directory.
70
+ * History stores only the stable digest token in browser memory; exact model,
71
+ * dependency, recipe, thumbnail, and provenance bytes stay under `.vgai/tmp`.
72
+ */
73
+ export class AssetHistorySnapshots {
74
+ private readonly projectRoot: string;
75
+ private readonly snapshotRoot: string;
76
+
77
+ constructor(private readonly publicRoot: string) {
78
+ this.projectRoot = dirname(publicRoot);
79
+ this.snapshotRoot = resolve(
80
+ this.projectRoot,
81
+ '.vgai',
82
+ 'tmp',
83
+ 'asset-history-snapshots',
84
+ );
85
+ }
86
+
87
+ async capture(assetPath: string): Promise<string> {
88
+ const normalized = normalizedAssetPath(assetPath);
89
+ const destination = resolve(this.publicRoot, dirname(normalized));
90
+ if (!isPathInside(this.publicRoot, destination)) {
91
+ throw new Error('Asset history destination escapes public/.');
92
+ }
93
+ const provenance = resolve(this.projectRoot, PROJECT_PROVENANCE_PATH);
94
+ const metadata: AssetHistorySnapshotMetadata = {
95
+ version: 1,
96
+ assetPath: normalized,
97
+ destination: relative(this.projectRoot, destination).split(sep).join('/'),
98
+ assetPresent: await pathExists(destination),
99
+ provenancePresent: await pathExists(provenance),
100
+ };
101
+ const hash = createHash('sha256');
102
+ hash.update(JSON.stringify(metadata));
103
+ if (metadata.assetPresent) await hashDirectory(hash, destination, destination);
104
+ if (metadata.provenancePresent) {
105
+ hash.update('provenance\0');
106
+ await hashFile(hash, provenance);
107
+ }
108
+ const token = hash.digest('hex');
109
+ const snapshot = resolve(this.snapshotRoot, token);
110
+ if (await pathExists(snapshot)) return token;
111
+
112
+ await mkdir(this.snapshotRoot, { recursive: true });
113
+ const staging = await mkdtemp(resolve(this.snapshotRoot, `.capture-${randomUUID()}-`));
114
+ try {
115
+ if (metadata.assetPresent) await cp(destination, resolve(staging, 'asset'), { recursive: true });
116
+ if (metadata.provenancePresent) await cp(provenance, resolve(staging, 'provenance.json'));
117
+ await writeFile(resolve(staging, 'metadata.json'), `${JSON.stringify(metadata, null, 2)}\n`);
118
+ try {
119
+ await rename(staging, snapshot);
120
+ } catch (error) {
121
+ if (!(await pathExists(snapshot))) throw error;
122
+ }
123
+ } finally {
124
+ await rm(staging, { recursive: true, force: true });
125
+ }
126
+ return token;
127
+ }
128
+
129
+ async restore(assetPath: string, token: string): Promise<void> {
130
+ const normalized = normalizedAssetPath(assetPath);
131
+ if (!/^[a-f0-9]{64}$/.test(token)) throw new Error('Invalid asset history snapshot token.');
132
+ const snapshot = resolve(this.snapshotRoot, token);
133
+ const metadata = JSON.parse(
134
+ await readFile(resolve(snapshot, 'metadata.json'), 'utf8'),
135
+ ) as AssetHistorySnapshotMetadata;
136
+ if (metadata.version !== 1 || metadata.assetPath !== normalized) {
137
+ throw new Error('Asset history snapshot identity does not match the selected asset.');
138
+ }
139
+ const destination = resolve(this.projectRoot, metadata.destination);
140
+ const expectedDestination = resolve(this.publicRoot, dirname(normalized));
141
+ if (destination !== expectedDestination || !isPathInside(this.publicRoot, destination)) {
142
+ throw new Error('Asset history snapshot destination is invalid.');
143
+ }
144
+ const provenance = resolve(this.projectRoot, PROJECT_PROVENANCE_PATH);
145
+ const transaction = await mkdtemp(
146
+ resolve(this.projectRoot, '.vgai', 'tmp', `.asset-history-restore-${randomUUID()}-`),
147
+ );
148
+ const stagedAsset = resolve(transaction, 'staged-asset');
149
+ const backupAsset = resolve(transaction, 'backup-asset');
150
+ const stagedProvenance = resolve(transaction, 'staged-provenance.json');
151
+ const backupProvenance = resolve(transaction, 'backup-provenance.json');
152
+ let assetBackedUp = false;
153
+ let assetInstalled = false;
154
+ let provenanceBackedUp = false;
155
+ let provenanceInstalled = false;
156
+ try {
157
+ if (metadata.assetPresent) await cp(resolve(snapshot, 'asset'), stagedAsset, { recursive: true });
158
+ if (metadata.provenancePresent) {
159
+ await cp(resolve(snapshot, 'provenance.json'), stagedProvenance);
160
+ }
161
+ await mkdir(dirname(destination), { recursive: true });
162
+ if (await pathExists(destination)) {
163
+ await rename(destination, backupAsset);
164
+ assetBackedUp = true;
165
+ }
166
+ if (metadata.assetPresent) {
167
+ await rename(stagedAsset, destination);
168
+ assetInstalled = true;
169
+ }
170
+ await mkdir(dirname(provenance), { recursive: true });
171
+ if (await pathExists(provenance)) {
172
+ await rename(provenance, backupProvenance);
173
+ provenanceBackedUp = true;
174
+ }
175
+ if (metadata.provenancePresent) {
176
+ await rename(stagedProvenance, provenance);
177
+ provenanceInstalled = true;
178
+ }
179
+ } catch (error) {
180
+ if (provenanceInstalled) await rm(provenance, { force: true });
181
+ if (provenanceBackedUp) await rename(backupProvenance, provenance);
182
+ if (assetInstalled) await rm(destination, { recursive: true, force: true });
183
+ if (assetBackedUp) await rename(backupAsset, destination);
184
+ throw error;
185
+ } finally {
186
+ await rm(transaction, { recursive: true, force: true });
187
+ }
188
+ }
189
+ }