@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.
- package/dist/assets/{axe-BJjvBeOZ.js → axe-N4cNyzfs.js} +1 -1
- package/dist/assets/{browserAll-Dn0pgD87.js → browserAll-BIBT0Z2H.js} +1 -1
- package/dist/assets/{create-runtime-BF9OD-U8.js → create-runtime-DxLhdk4U.js} +1 -1
- package/dist/assets/{game-u6c2Patu.js → game-4NbNRROm.js} +1 -1
- package/dist/assets/{game-umrHIqFB.js → game-B3UH5LjS.js} +1 -1
- package/dist/assets/{game-BR0GBNgF.js → game-ByxRLJi1.js} +1 -1
- package/dist/assets/{game-DeTGdgYt.js → game-CJ5BqOYG.js} +1 -1
- package/dist/assets/{game-DQxhCYsZ.js → game-CSJm1kwB.js} +1 -1
- package/dist/assets/{game-CLNm2g0y.js → game-D9Mo4L3Q.js} +1 -1
- package/dist/assets/{game-B346oj_f.js → game-DJmU9BQF.js} +1 -1
- package/dist/assets/{game-BCNN63uV.js → game-ZqGLgxRj.js} +1 -1
- package/dist/assets/{game-loop-t5W_oqxz.js → game-loop-9YP76JB1.js} +1 -1
- package/dist/assets/{game-source-DzE9taML.js → game-source-DLKG9pmj.js} +1 -1
- package/dist/assets/{index-DbQzGLxv.js → index-8A4JLNIp.js} +902 -892
- package/dist/assets/{index-CgcH1RpF.js → index-Brzr17q0.js} +1 -1
- package/dist/assets/{index-D3tcNB2T.js → index-CnKtvrru.js} +1 -1
- package/dist/assets/{index-Fc8Ezr20.js → index-D-WR11Jw.js} +1 -1
- package/dist/assets/index-DiUacMXR.css +1 -0
- package/dist/assets/{ingest-siblings-CW_xbvrF.js → ingest-siblings-Cmx39OBK.js} +1 -1
- package/dist/assets/{mount-manifest-DgUscyaD.js → mount-manifest-kzdZgkkc.js} +1 -1
- package/dist/assets/{react-18-C4tVCii_.js → react-18-DvWsdwa_.js} +1 -1
- package/dist/assets/webworkerAll-C9GR-KrR.js +1 -0
- package/dist/assets/{world-documents-BSaA5YIF.js → world-documents-DM_aIMAW.js} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/packaged.mjs +27125 -1075
- package/package.json +2 -2
- package/server/asset-history-snapshots.ts +189 -0
- package/server/asset-library-routes.ts +404 -53
- package/server/catalog-import-recipe.ts +97 -0
- package/server/catalog-reimport.ts +141 -0
- package/server/cloud-asset-catalog.ts +71 -5
- package/server/dev.ts +1 -1
- package/server/editor-server.ts +106 -51
- package/server/editor-sse.ts +5 -0
- package/server/generative-execution-context.ts +45 -0
- package/server/model-import-conversion.ts +345 -0
- package/server/packaged.ts +1 -1
- package/server/project-hmr-files.ts +3 -5
- package/server/project-output-writer.ts +440 -36
- package/server/project-tools.ts +529 -0
- package/server/server-utils.ts +14 -11
- package/src/EditorContext.tsx +4 -4
- package/src/action-registry.ts +12 -0
- package/src/asset-editor-context.tsx +9 -0
- package/src/asset-events.ts +3 -15
- package/src/asset-selection.ts +3 -0
- package/src/asset-workflow/asset-capabilities.ts +40 -0
- package/src/asset-workflow/asset-import-jobs.ts +108 -0
- package/src/asset-workflow/asset-library-query.ts +8 -1
- package/src/asset-workflow/asset-workflow-quality.ts +2 -0
- package/src/asset-workflow/import-contract.ts +2 -0
- package/src/asset-workflow/model-inspection.ts +92 -1
- package/src/asset-workflow/preview-resource-lifetime.ts +44 -0
- package/src/asset-workflow/project-asset-commands.ts +1 -0
- package/src/asset-workflow/project-asset-health.ts +3 -6
- package/src/asset-workflow/project-asset-operations.ts +119 -4
- package/src/command-listener.ts +37 -9
- package/src/components/ApplicationMenus.tsx +13 -15
- package/src/components/AssetBrowser.tsx +301 -26
- package/src/components/AssetImportDetails.tsx +207 -92
- package/src/components/AssetImportJobStatus.tsx +55 -0
- package/src/components/CenterDocuments.tsx +28 -1
- package/src/components/CommandPalette.tsx +2 -2
- package/src/components/DefaultEditorLayout.tsx +4 -0
- package/src/components/InspectorToolSection.tsx +12 -10
- package/src/components/OnlineAssetBrowser.tsx +197 -11
- package/src/components/ToolHost.tsx +8 -8
- package/src/components/ViewportOverlay.tsx +13 -26
- package/src/components/ViewportPanel.tsx +19 -14
- package/src/components/ViewportShadingMenu.tsx +103 -0
- package/src/components/WorkspaceDock.tsx +103 -17
- package/src/components/asset-documents.tsx +38 -11
- package/src/components/asset-editor-persistence.ts +300 -5
- package/src/components/asset-selection-section.tsx +83 -9
- package/src/components/asset-viewers/AudioViewer.tsx +4 -7
- package/src/components/asset-viewers/ModelViewer.tsx +168 -5
- package/src/components/asset-viewers/OnlineAssetDetail.tsx +52 -12
- package/src/components/asset-viewers/SourceAssetViewer.tsx +7 -26
- package/src/components/asset-workflow.css +30 -3
- package/src/components/primitives/editor-icons.ts +1 -0
- package/src/components/project-tool-documents.tsx +298 -0
- package/src/components/status-contributions.tsx +38 -0
- package/src/components/tool-documents.tsx +20 -17
- package/src/components/tool-schema-form.tsx +258 -0
- package/src/components/workspace-dock.css +16 -0
- package/src/editor-api.ts +159 -22
- package/src/editor-store.ts +6 -5
- package/src/editor-viewport.ts +12 -7
- package/src/game-viewport-state.ts +28 -0
- package/src/model-thumbnail.ts +97 -9
- package/src/play-mode.ts +30 -3
- package/src/project-tool-discovery.ts +5 -5
- package/src/{project-operations.ts → project-tools.ts} +39 -26
- package/src/scene-sync.ts +0 -67
- package/src/storage/handle-storage.ts +5 -1
- package/src/storage/http-storage.ts +7 -2
- package/src/storage/mem-storage.ts +7 -1
- package/src/storage/types.ts +2 -0
- package/src/theme-css.ts +17 -7
- package/src/tool-loader.ts +152 -241
- package/src/viewport-shading-boundary.ts +15 -0
- package/src/widgets/index.ts +2 -1
- package/src/workspace-dock-controller.ts +8 -0
- package/src/workspace-document-registry.ts +4 -4
- package/src/workspace-state-persistence.ts +36 -10
- package/template/.claude/skills/editor/SKILL.md +13 -3
- package/template/AGENTS.md +100 -83
- package/template/package.json +16 -0
- package/template/public/scenes/default.vscn.json +1 -2
- package/template/scripts/playtest.ts +7 -37
- package/template/server/rooms/game-room.ts +2 -2
- package/template/src/data/README.md +3 -2
- package/template/src/data/tuning.data.json +1 -3
- package/template/src/data/tuning.schema.json +0 -12
- package/template/src/data/tuning.schema.ts +2 -9
- package/template/src/data/tuning.ts +1 -1
- package/template/src/scripts/registry.ts +0 -3
- package/template/src/tools/README.md +151 -16
- package/template/src/tools/autoplay.tool.ts +31 -0
- package/template/src/tools/autoplay.ts +15 -0
- package/template/src/tools/tuning.document.tsx +107 -0
- package/template/src/tools/tuning.tool.ts +24 -0
- package/template/src/tools/tuning.ts +9 -0
- package/template/src/ui/Button.stories.tsx +1 -1
- package/template/tests/acceptance/example.spec.ts +4 -59
- package/template/vgai.game.json +1 -1
- package/dist/assets/index-bAGhEnnO.css +0 -1
- package/dist/assets/webworkerAll-BA4aq7xq.js +0 -1
- package/dist/project-scripts.bundle.js +0 -3917
- package/server/project-operations.ts +0 -343
- package/src/components/project-operation-documents.tsx +0 -226
- package/template/src/operations/README.md +0 -88
- package/template/src/scripts/components/data-spinner.ts +0 -86
- package/template/src/scripts/components/spin-lap-math.ts +0 -27
- package/template/src/tools/example.tool.tsx +0 -97
- package/template/src/tools/spin.tool.tsx +0 -101
- package/template/tests/logic/example.test.ts +0 -31
|
@@ -4,14 +4,16 @@ import {
|
|
|
4
4
|
assetImportPlanSchema,
|
|
5
5
|
importRecipePath,
|
|
6
6
|
} from '../asset-workflow/import-contract';
|
|
7
|
-
import { Button, danger, SectionHeader, Select, TextInput, text } from '../design-system';
|
|
7
|
+
import { Button, Checkbox, danger, SectionHeader, Select, TextInput, text } from '../design-system';
|
|
8
|
+
import { revealInFinder } from '../editor-api';
|
|
8
9
|
import { getStorageBackend } from '../storage';
|
|
9
|
-
import { writeProjectAsset } from './asset-editor-persistence';
|
|
10
|
+
import { reimportProjectAssetWithHistory, writeProjectAsset } from './asset-editor-persistence';
|
|
10
11
|
|
|
11
12
|
export function AssetImportDetails({ assetPath }: { assetPath: string }) {
|
|
12
13
|
const [saved, setSaved] = useState<AssetImportPlan | null>(null);
|
|
13
14
|
const [draft, setDraft] = useState<AssetImportPlan | null>(null);
|
|
14
15
|
const [error, setError] = useState<string | null>(null);
|
|
16
|
+
const [reimporting, setReimporting] = useState(false);
|
|
15
17
|
useEffect(() => {
|
|
16
18
|
let cancelled = false;
|
|
17
19
|
const path = importRecipePath(assetPath);
|
|
@@ -77,91 +79,185 @@ export function AssetImportDetails({ assetPath }: { assetPath: string }) {
|
|
|
77
79
|
setDraft((current) =>
|
|
78
80
|
current ? { ...current, settings: { ...current.settings, [key]: value } } : current,
|
|
79
81
|
);
|
|
82
|
+
const conversionSettingsSupported = draft.backend === 'threejs';
|
|
80
83
|
return (
|
|
81
84
|
<>
|
|
82
85
|
<SectionHeader label="Import settings">
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
event
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
86
|
+
{conversionSettingsSupported ? (
|
|
87
|
+
<>
|
|
88
|
+
<label>
|
|
89
|
+
Scale{' '}
|
|
90
|
+
<TextInput
|
|
91
|
+
aria-label="Import scale"
|
|
92
|
+
type="number"
|
|
93
|
+
min="0.0001"
|
|
94
|
+
step="0.01"
|
|
95
|
+
value={draft.settings.scale}
|
|
96
|
+
onChange={(event) => setSetting('scale', Number(event.target.value))}
|
|
97
|
+
/>
|
|
98
|
+
</label>
|
|
99
|
+
<label>
|
|
100
|
+
Units{' '}
|
|
101
|
+
<Select
|
|
102
|
+
aria-label="Source units"
|
|
103
|
+
value={draft.settings.sourceUnits}
|
|
104
|
+
onChange={(event) =>
|
|
105
|
+
setSetting(
|
|
106
|
+
'sourceUnits',
|
|
107
|
+
event.target.value as AssetImportPlan['settings']['sourceUnits'],
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
>
|
|
111
|
+
{['auto', 'mm', 'cm', 'm', 'in', 'ft'].map((value) => (
|
|
112
|
+
<option key={value}>{value}</option>
|
|
113
|
+
))}
|
|
114
|
+
</Select>
|
|
115
|
+
</label>
|
|
116
|
+
<label>
|
|
117
|
+
Up axis{' '}
|
|
118
|
+
<Select
|
|
119
|
+
aria-label="Source up axis"
|
|
120
|
+
value={draft.settings.upAxis}
|
|
121
|
+
onChange={(event) =>
|
|
122
|
+
setSetting('upAxis', event.target.value as AssetImportPlan['settings']['upAxis'])
|
|
123
|
+
}
|
|
124
|
+
>
|
|
125
|
+
{['auto', 'x', 'y', 'z'].map((value) => (
|
|
126
|
+
<option key={value}>{value}</option>
|
|
127
|
+
))}
|
|
128
|
+
</Select>
|
|
129
|
+
</label>
|
|
130
|
+
<label>
|
|
131
|
+
Forward axis{' '}
|
|
132
|
+
<Select
|
|
133
|
+
aria-label="Source forward axis"
|
|
134
|
+
value={draft.settings.forwardAxis}
|
|
135
|
+
onChange={(event) =>
|
|
136
|
+
setSetting(
|
|
137
|
+
'forwardAxis',
|
|
138
|
+
event.target.value as AssetImportPlan['settings']['forwardAxis'],
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
>
|
|
142
|
+
{['auto', 'x', '-x', 'y', '-y', 'z', '-z'].map((value) => (
|
|
143
|
+
<option key={value}>{value}</option>
|
|
144
|
+
))}
|
|
145
|
+
</Select>
|
|
146
|
+
</label>
|
|
147
|
+
<label>
|
|
148
|
+
Normals{' '}
|
|
149
|
+
<Select
|
|
150
|
+
aria-label="Normal handling"
|
|
151
|
+
value={draft.settings.normals}
|
|
152
|
+
onChange={(event) =>
|
|
153
|
+
setSetting(
|
|
154
|
+
'normals',
|
|
155
|
+
event.target.value as AssetImportPlan['settings']['normals'],
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
>
|
|
159
|
+
{['preserve', 'generate', 'recalculate'].map((value) => (
|
|
160
|
+
<option key={value}>{value}</option>
|
|
161
|
+
))}
|
|
162
|
+
</Select>
|
|
163
|
+
</label>
|
|
164
|
+
<label>
|
|
165
|
+
Tangents{' '}
|
|
166
|
+
<Select
|
|
167
|
+
aria-label="Tangent handling"
|
|
168
|
+
value={draft.settings.tangents}
|
|
169
|
+
onChange={(event) =>
|
|
170
|
+
setSetting(
|
|
171
|
+
'tangents',
|
|
172
|
+
event.target.value as AssetImportPlan['settings']['tangents'],
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
>
|
|
176
|
+
{['preserve', 'generate', 'discard'].map((value) => (
|
|
177
|
+
<option key={value}>{value}</option>
|
|
178
|
+
))}
|
|
179
|
+
</Select>
|
|
180
|
+
</label>
|
|
181
|
+
<label>
|
|
182
|
+
Materials{' '}
|
|
183
|
+
<Select
|
|
184
|
+
aria-label="Material handling"
|
|
185
|
+
value={draft.settings.materials}
|
|
186
|
+
onChange={(event) =>
|
|
187
|
+
setSetting(
|
|
188
|
+
'materials',
|
|
189
|
+
event.target.value as AssetImportPlan['settings']['materials'],
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
>
|
|
193
|
+
{['import', 'discard'].map((value) => (
|
|
194
|
+
<option key={value}>{value}</option>
|
|
195
|
+
))}
|
|
196
|
+
</Select>
|
|
197
|
+
</label>
|
|
198
|
+
<label>
|
|
199
|
+
Textures{' '}
|
|
200
|
+
<Select
|
|
201
|
+
aria-label="Texture handling"
|
|
202
|
+
value={draft.settings.textures}
|
|
203
|
+
onChange={(event) =>
|
|
204
|
+
setSetting(
|
|
205
|
+
'textures',
|
|
206
|
+
event.target.value as AssetImportPlan['settings']['textures'],
|
|
207
|
+
)
|
|
208
|
+
}
|
|
209
|
+
>
|
|
210
|
+
{['copy', 'embed', 'discard'].map((value) => (
|
|
211
|
+
<option key={value}>{value}</option>
|
|
212
|
+
))}
|
|
213
|
+
</Select>
|
|
214
|
+
</label>
|
|
215
|
+
<label>
|
|
216
|
+
Animation clips{' '}
|
|
217
|
+
<TextInput
|
|
218
|
+
aria-label="Animation clips"
|
|
219
|
+
placeholder="All clips"
|
|
220
|
+
value={draft.settings.animations.join(', ')}
|
|
221
|
+
onChange={(event) =>
|
|
222
|
+
setSetting(
|
|
223
|
+
'animations',
|
|
224
|
+
event.target.value
|
|
225
|
+
.split(',')
|
|
226
|
+
.map((value) => value.trim())
|
|
227
|
+
.filter(Boolean),
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
/>
|
|
231
|
+
</label>
|
|
232
|
+
<label>
|
|
233
|
+
Optimization{' '}
|
|
234
|
+
<Select
|
|
235
|
+
aria-label="Mesh optimization"
|
|
236
|
+
value={draft.settings.meshOptimization}
|
|
237
|
+
onChange={(event) =>
|
|
238
|
+
setSetting(
|
|
239
|
+
'meshOptimization',
|
|
240
|
+
event.target.value as AssetImportPlan['settings']['meshOptimization'],
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
>
|
|
244
|
+
{['none', 'safe', 'aggressive'].map((value) => (
|
|
245
|
+
<option key={value}>{value}</option>
|
|
246
|
+
))}
|
|
247
|
+
</Select>
|
|
248
|
+
</label>
|
|
249
|
+
<label>
|
|
250
|
+
<Checkbox
|
|
251
|
+
aria-label="Retain source files"
|
|
252
|
+
checked={draft.settings.retainSource}
|
|
253
|
+
onChange={(event) => setSetting('retainSource', event.target.checked)}
|
|
254
|
+
/>{' '}
|
|
255
|
+
Retain source files
|
|
256
|
+
</label>
|
|
257
|
+
</>
|
|
258
|
+
) : (
|
|
259
|
+
<div>This runtime-native asset is copied without conversion settings.</div>
|
|
260
|
+
)}
|
|
165
261
|
<div style={{ color: changes.length ? text[1] : text[3] }}>
|
|
166
262
|
Dry-run impact:{' '}
|
|
167
263
|
{changes.length ? `rebuild output; changed ${changes.join(', ')}` : 'no output changes'}
|
|
@@ -186,15 +282,34 @@ export function AssetImportDetails({ assetPath }: { assetPath: string }) {
|
|
|
186
282
|
</Button>
|
|
187
283
|
<Button
|
|
188
284
|
type="button"
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
285
|
+
disabled={reimporting}
|
|
286
|
+
onClick={async () => {
|
|
287
|
+
setReimporting(true);
|
|
288
|
+
setError(null);
|
|
289
|
+
try {
|
|
290
|
+
await reimportProjectAssetWithHistory(assetPath, draft);
|
|
291
|
+
setSaved(draft);
|
|
292
|
+
} catch (cause) {
|
|
293
|
+
setError(cause instanceof Error ? cause.message : String(cause));
|
|
294
|
+
} finally {
|
|
295
|
+
setReimporting(false);
|
|
296
|
+
}
|
|
297
|
+
}}
|
|
298
|
+
>
|
|
299
|
+
{reimporting ? 'Reimporting…' : 'Reimport'}
|
|
300
|
+
</Button>
|
|
301
|
+
<Button
|
|
302
|
+
type="button"
|
|
303
|
+
onClick={async () => {
|
|
304
|
+
setError(null);
|
|
305
|
+
try {
|
|
306
|
+
await revealInFinder(assetPath);
|
|
307
|
+
} catch (cause) {
|
|
308
|
+
setError(cause instanceof Error ? cause.message : String(cause));
|
|
309
|
+
}
|
|
310
|
+
}}
|
|
196
311
|
>
|
|
197
|
-
|
|
312
|
+
Reveal source
|
|
198
313
|
</Button>
|
|
199
314
|
</div>
|
|
200
315
|
{error && <div style={{ color: danger }}>{error}</div>}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { useSyncExternalStore } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
assetImportJobs,
|
|
4
|
+
assetImportJobsVersion,
|
|
5
|
+
cancelAssetImportJob,
|
|
6
|
+
clearFinishedAssetImportJobs,
|
|
7
|
+
subscribeAssetImportJobs,
|
|
8
|
+
} from '../asset-workflow/asset-import-jobs';
|
|
9
|
+
import { Button } from '../design-system';
|
|
10
|
+
|
|
11
|
+
export function AssetImportJobStatus() {
|
|
12
|
+
useSyncExternalStore(subscribeAssetImportJobs, assetImportJobsVersion);
|
|
13
|
+
const jobs = assetImportJobs();
|
|
14
|
+
if (!jobs.length) return null;
|
|
15
|
+
const summary: Record<string, number> = {};
|
|
16
|
+
for (const job of jobs) summary[job.status] = (summary[job.status] ?? 0) + 1;
|
|
17
|
+
return (
|
|
18
|
+
<div role="status" aria-live="polite" className="vgai-asset-import-jobs">
|
|
19
|
+
<strong>Imports</strong> ·{' '}
|
|
20
|
+
{Object.entries(summary)
|
|
21
|
+
.map(([status, count]) => `${count} ${status}`)
|
|
22
|
+
.join(' · ')}
|
|
23
|
+
{jobs.map((job) => (
|
|
24
|
+
<span key={job.id} title={job.message} data-status={job.status}>
|
|
25
|
+
{job.label}: {job.stage}{' '}
|
|
26
|
+
{job.total ? `${Math.round((job.loaded / job.total) * 100)}%` : job.status}
|
|
27
|
+
{job.status === 'running' && (
|
|
28
|
+
<Button
|
|
29
|
+
type="button"
|
|
30
|
+
variant="ghost"
|
|
31
|
+
size="compact"
|
|
32
|
+
aria-label={`Cancel ${job.label}`}
|
|
33
|
+
onClick={() => cancelAssetImportJob(job.id)}
|
|
34
|
+
>
|
|
35
|
+
Cancel
|
|
36
|
+
</Button>
|
|
37
|
+
)}
|
|
38
|
+
{(job.status === 'failed' || job.status === 'cancelled') && job.retry && (
|
|
39
|
+
<Button type="button" variant="ghost" size="compact" onClick={job.retry}>
|
|
40
|
+
Retry {job.label}
|
|
41
|
+
</Button>
|
|
42
|
+
)}
|
|
43
|
+
{job.status === 'failed' && job.message && (
|
|
44
|
+
<span className="vgai-asset-import-error">{job.message}</span>
|
|
45
|
+
)}
|
|
46
|
+
</span>
|
|
47
|
+
))}
|
|
48
|
+
{jobs.some((job) => job.status !== 'running') && (
|
|
49
|
+
<Button type="button" variant="ghost" size="compact" onClick={clearFinishedAssetImportJobs}>
|
|
50
|
+
Clear
|
|
51
|
+
</Button>
|
|
52
|
+
)}
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -47,7 +47,19 @@ import {
|
|
|
47
47
|
import { DisclosureIcon } from '../design-system';
|
|
48
48
|
import { useEditorStore } from '../EditorContext';
|
|
49
49
|
import { editorConsole } from '../editor-console';
|
|
50
|
-
import {
|
|
50
|
+
import {
|
|
51
|
+
gameViewportShadingMode,
|
|
52
|
+
gameViewportShadingVersion,
|
|
53
|
+
setGameDocumentShadingMode,
|
|
54
|
+
subscribeGameViewportShading,
|
|
55
|
+
} from '../game-viewport-state';
|
|
56
|
+
import {
|
|
57
|
+
gameViewportShadingSupported,
|
|
58
|
+
getGameProfiler,
|
|
59
|
+
resizeGame,
|
|
60
|
+
setGameContainer,
|
|
61
|
+
setGameViewportShadingMode,
|
|
62
|
+
} from '../play-mode';
|
|
51
63
|
import { GAME_DOCUMENT_ID, SCENE_DOCUMENT_ID } from '../workspace-document-ids';
|
|
52
64
|
import {
|
|
53
65
|
activateWorkspaceDocument,
|
|
@@ -64,6 +76,7 @@ import { openDebuggerPanel } from './debugger-panel-state';
|
|
|
64
76
|
import { GamePerformanceOverlay } from './GamePerformanceOverlay';
|
|
65
77
|
import { MuteButton, type Resolution, ResolutionPicker } from './PlayBar';
|
|
66
78
|
import { ViewportPanel } from './ViewportPanel';
|
|
79
|
+
import { ViewportShadingMenu } from './ViewportShadingMenu';
|
|
67
80
|
|
|
68
81
|
/** Stable identity (§7.1) of the single active-scene center document. The
|
|
69
82
|
* SLOT is stable while the scene it shows can change (root activation,
|
|
@@ -286,7 +299,10 @@ function GameStatsMenu() {
|
|
|
286
299
|
* Transport (play/pause/stop) deliberately stays in the global `PlayBar`.
|
|
287
300
|
*/
|
|
288
301
|
export function GameDocumentToolbar(_props: WorkspaceDocumentContentProps) {
|
|
302
|
+
const store = useEditorStore();
|
|
303
|
+
useSyncExternalStore(store.subscribe, store.getSnapshot);
|
|
289
304
|
useSyncExternalStore(subscribeGameView, gameViewVersion);
|
|
305
|
+
useSyncExternalStore(subscribeGameViewportShading, gameViewportShadingVersion);
|
|
290
306
|
return (
|
|
291
307
|
<div
|
|
292
308
|
data-testid="game-doc-toolbar"
|
|
@@ -304,6 +320,11 @@ export function GameDocumentToolbar(_props: WorkspaceDocumentContentProps) {
|
|
|
304
320
|
scale={_gameScale}
|
|
305
321
|
title="Change game resolution"
|
|
306
322
|
/>
|
|
323
|
+
<ViewportShadingMenu
|
|
324
|
+
mode={gameViewportShadingMode()}
|
|
325
|
+
onChange={setGameDocumentShadingMode}
|
|
326
|
+
disabled={store.playState !== 'stopped' && !gameViewportShadingSupported()}
|
|
327
|
+
/>
|
|
307
328
|
<MuteButton />
|
|
308
329
|
<GameStatsMenu />
|
|
309
330
|
</div>
|
|
@@ -372,6 +393,12 @@ function GamePanel({ style }: { style?: React.CSSProperties }) {
|
|
|
372
393
|
setGameScale(scale);
|
|
373
394
|
}, [scale]);
|
|
374
395
|
|
|
396
|
+
// A new play session mounts fresh adapter instances. Re-apply this Game
|
|
397
|
+
// viewport's own session-local diagnostic mode after every stopped→running edge.
|
|
398
|
+
useEffect(() => {
|
|
399
|
+
if (isRunning) setGameViewportShadingMode(gameViewportShadingMode());
|
|
400
|
+
}, [isRunning]);
|
|
401
|
+
|
|
375
402
|
return (
|
|
376
403
|
<div style={{ overflow: 'hidden', position: 'relative', flexDirection: 'column', ...style }}>
|
|
377
404
|
<div
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
import { useEditorStore, useHistoryCommandSnapshot, useHistoryCommands } from '../EditorContext';
|
|
23
23
|
import { zIndex } from '../z-index';
|
|
24
24
|
import { buildDataAssetActions } from './data-documents';
|
|
25
|
-
import {
|
|
25
|
+
import { buildProjectToolActions } from './project-tool-documents';
|
|
26
26
|
import { buildStoryActions } from './story-documents';
|
|
27
27
|
import { buildToolActions } from './tool-documents';
|
|
28
28
|
import { buildXStateMachineActions } from './xstate-documents';
|
|
@@ -70,7 +70,7 @@ export function CommandPalette({ onClose }: CommandPaletteProps) {
|
|
|
70
70
|
// machines. All read live snapshots; the palette mounts fresh per open.
|
|
71
71
|
const documents = [
|
|
72
72
|
...buildDataAssetActions(store),
|
|
73
|
-
...
|
|
73
|
+
...buildProjectToolActions(store),
|
|
74
74
|
...buildStoryActions(store),
|
|
75
75
|
...buildToolActions(store),
|
|
76
76
|
...buildXStateMachineActions(),
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
decideExternalSceneChangeAction,
|
|
10
10
|
} from '../asset-events';
|
|
11
11
|
import { installAssetSelectionAutoClear } from '../asset-selection';
|
|
12
|
+
import { connectAssetImportProgressEvents } from '../asset-workflow/asset-import-jobs';
|
|
12
13
|
import { getActiveAuthoring, hasAuthoringOverride } from '../authoring/active-adapter';
|
|
13
14
|
import {
|
|
14
15
|
switchFocusedWorld,
|
|
@@ -32,6 +33,7 @@ import { getProjectFileHistory } from '../history/project-file-history';
|
|
|
32
33
|
import { getStorageBackend } from '../storage';
|
|
33
34
|
import { installAuxiliaryEvents } from '../workspace-aux-commands';
|
|
34
35
|
import { installUtilityAutoOpen, installUtilityEvents } from '../workspace-utility-commands';
|
|
36
|
+
import { connectProjectAssetReimportEvents } from './asset-editor-persistence';
|
|
35
37
|
import { ensureBuildContributionsRegistered } from './build-documents';
|
|
36
38
|
import { useCenterDocuments } from './CenterDocuments';
|
|
37
39
|
import { CommandPalette } from './CommandPalette';
|
|
@@ -168,6 +170,8 @@ export function DefaultEditorLayout({
|
|
|
168
170
|
// W2 (§5.1) — an ENTITY selection change drops the asset selection, so the
|
|
169
171
|
// Inspector goes back to being purely entity-contextual (asset-selection.ts).
|
|
170
172
|
useEffect(() => installAssetSelectionAutoClear(store), [store]);
|
|
173
|
+
useEffect(() => connectAssetImportProgressEvents(), []);
|
|
174
|
+
useEffect(() => connectProjectAssetReimportEvents(), []);
|
|
171
175
|
const [prefabSaveState, setPrefabSaveState] = useState<{ entityId: string; name: string } | null>(
|
|
172
176
|
null,
|
|
173
177
|
);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { themeVars } from '../design-system';
|
|
1
|
+
import { EditorIcon, editorIcons, themeVars } from '../design-system';
|
|
2
2
|
/**
|
|
3
3
|
* Inspector-tool section host (W3, docs/DATA-TOOLS-DESIGN.md §3.2/§3.4) —
|
|
4
4
|
* renders one project tool (`placement: 'inspector'`) as a section in the
|
|
5
5
|
* ONE inspector shell (`Inspector.tsx`), via `inspector-section-registry`.
|
|
6
6
|
*
|
|
7
|
-
* The chrome is the editor's (§3.4): a bordered section with
|
|
8
|
-
*
|
|
7
|
+
* The chrome is the editor's (§3.4): a bordered section with a semantic tool
|
|
8
|
+
* icon in its title row, matching
|
|
9
9
|
* the shell's own section styling (`Inspector.tsx`'s Stories block). What the
|
|
10
10
|
* tool renders inside is its own business — plain React, ideally built from
|
|
11
11
|
* `@editor/widgets` so fields sit flush with the built-in sections.
|
|
@@ -26,20 +26,23 @@ import { themeVars } from '../design-system';
|
|
|
26
26
|
|
|
27
27
|
import type { AuthoringAdapter } from '@engine/adapter';
|
|
28
28
|
import { EditorSelectionProvider } from '@engine/react/use-selection';
|
|
29
|
-
import type {
|
|
29
|
+
import type { ToolInspectorContributionProps } from '@vgai/editor-sdk/contributions';
|
|
30
30
|
import type { ComponentType } from 'react';
|
|
31
|
+
import type { ProjectToolCatalogEntry } from '../project-tools';
|
|
32
|
+
import { getToolContributionClient } from '../tool-loader';
|
|
31
33
|
import { groupLabelStyle } from './inspector-widgets/shared';
|
|
32
34
|
import { ToolErrorBoundary } from './ToolHost';
|
|
33
35
|
|
|
34
36
|
export interface InspectorToolSectionProps {
|
|
35
37
|
/** `ToolConfig.id` — the section's `data-testid` suffix (`inspector-tool-<id>`). */
|
|
36
38
|
id: string;
|
|
37
|
-
/**
|
|
39
|
+
/** Contribution title shown after the semantic tool icon. */
|
|
38
40
|
title: string;
|
|
39
41
|
/** Project-relative source file, shown in crash/teaching messages. */
|
|
40
42
|
file: string;
|
|
41
43
|
/** The tool module's default export. */
|
|
42
|
-
|
|
44
|
+
tool: ProjectToolCatalogEntry;
|
|
45
|
+
Component: ComponentType<ToolInspectorContributionProps>;
|
|
43
46
|
/** Live adapter + selection, as the section registry hands them over. */
|
|
44
47
|
adapter: AuthoringAdapter;
|
|
45
48
|
nodeId: string | null;
|
|
@@ -50,6 +53,7 @@ export function InspectorToolSection({
|
|
|
50
53
|
id,
|
|
51
54
|
title,
|
|
52
55
|
file,
|
|
56
|
+
tool,
|
|
53
57
|
Component,
|
|
54
58
|
adapter,
|
|
55
59
|
nodeId,
|
|
@@ -61,14 +65,12 @@ export function InspectorToolSection({
|
|
|
61
65
|
style={{ padding: '8px', borderTop: `1px solid ${themeVars.boundary.default}`, marginTop: 4 }}
|
|
62
66
|
>
|
|
63
67
|
<div style={{ ...groupLabelStyle, marginBottom: 4 }}>
|
|
64
|
-
<
|
|
65
|
-
⚙
|
|
66
|
-
</span>
|
|
68
|
+
<EditorIcon icon={editorIcons.tool.extension} tone="muted" style={{ marginRight: 4 }} />
|
|
67
69
|
{title}
|
|
68
70
|
</div>
|
|
69
71
|
<ToolErrorBoundary file={file}>
|
|
70
72
|
<EditorSelectionProvider adapter={adapter} nodeId={nodeId}>
|
|
71
|
-
<Component node={node} nodeId={nodeId} />
|
|
73
|
+
<Component tool={tool} client={getToolContributionClient()} node={node} nodeId={nodeId} />
|
|
72
74
|
</EditorSelectionProvider>
|
|
73
75
|
</ToolErrorBoundary>
|
|
74
76
|
</div>
|