@vgai/editor 0.4.0-canary.20260715.1 → 0.4.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/assets/{axe-CyiTVRi6.js → axe-BJjvBeOZ.js} +1 -1
- package/dist/assets/{browserAll-fGZ_P1P8.js → browserAll-Dn0pgD87.js} +1 -1
- package/dist/assets/{create-runtime-BzSHBTYw.js → create-runtime-BF9OD-U8.js} +1 -1
- package/dist/assets/{game-NUAnyPkS.js → game-B346oj_f.js} +1 -1
- package/dist/assets/{game-uR_lv3Mp.js → game-BCNN63uV.js} +1 -1
- package/dist/assets/{game-HqybI_X7.js → game-BR0GBNgF.js} +1 -1
- package/dist/assets/{game-DQ7YHNS8.js → game-CLNm2g0y.js} +1 -1
- package/dist/assets/{game-DX6Obomf.js → game-DQxhCYsZ.js} +1 -1
- package/dist/assets/{game-DoUJj8vY.js → game-DeTGdgYt.js} +1 -1
- package/dist/assets/{game-loop-o2CcBMFY.js → game-loop-t5W_oqxz.js} +1 -1
- package/dist/assets/{game-source-C8HM7gbP.js → game-source-DzE9taML.js} +1 -1
- package/dist/assets/{game-ck_dNIE6.js → game-u6c2Patu.js} +1 -1
- package/dist/assets/{game-hIf5LD4E.js → game-umrHIqFB.js} +1 -1
- package/dist/assets/{index-CoSRCKPS.js → index-CgcH1RpF.js} +1 -1
- package/dist/assets/{index-D93bBuUl.js → index-D3tcNB2T.js} +1 -1
- package/dist/assets/{index-B-HA1uu3.js → index-DbQzGLxv.js} +769 -769
- package/dist/assets/{index-CSBAZwMx.js → index-Fc8Ezr20.js} +1 -1
- package/dist/assets/index-bAGhEnnO.css +1 -0
- package/dist/assets/{ingest-siblings-CCkkE2h7.js → ingest-siblings-CW_xbvrF.js} +1 -1
- package/dist/assets/{mount-manifest-B2ECssU2.js → mount-manifest-DgUscyaD.js} +1 -1
- package/dist/assets/{react-18-Bmj05S8J.js → react-18-C4tVCii_.js} +1 -1
- package/dist/assets/webworkerAll-BA4aq7xq.js +1 -0
- package/dist/assets/{world-documents-DXPcAEA0.js → world-documents-BSaA5YIF.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +3 -3
- package/src/asset-editor-context.tsx +130 -0
- package/src/asset-preview.ts +4 -0
- package/src/asset-selection.ts +9 -4
- package/src/asset-workflow/thumbnail-system.ts +16 -7
- package/src/components/AssetBrowser.tsx +15 -7
- package/src/components/AssetEditorShell.tsx +42 -112
- package/src/components/AssetImportDetails.tsx +24 -6
- package/src/components/AssetLibraryPanel.tsx +1 -1
- package/src/components/GameHierarchy.tsx +26 -1
- package/src/components/Inspector.tsx +38 -2
- package/src/components/OnlineAssetBrowser.tsx +40 -409
- package/src/components/WorkspaceDock.tsx +6 -12
- package/src/components/asset-documents.tsx +41 -12
- package/src/components/asset-selection-section.tsx +34 -1
- package/src/components/asset-viewers/ModelViewer.tsx +7 -4
- package/src/components/asset-viewers/OnlineAssetDetail.tsx +233 -205
- package/src/components/asset-workflow.css +39 -220
- package/src/editor-api.ts +1 -1
- package/template/vgai.game.json +1 -1
- package/dist/assets/index-bu01fqch.css +0 -1
- package/dist/assets/webworkerAll-BC6gMOLZ.js +0 -1
|
@@ -12,14 +12,14 @@
|
|
|
12
12
|
* open-asset`) keeps its meaning unchanged:
|
|
13
13
|
*
|
|
14
14
|
* - project asset → the serving path itself (`/textures/crate.png`)
|
|
15
|
-
* -
|
|
15
|
+
* - legacy entity editor key → `asset-lab:entity:<entityId>`
|
|
16
16
|
* - online asset → `online:<source>:<id>`
|
|
17
17
|
*
|
|
18
18
|
* Titles are source-owned file names as authored (§8 — `crate.png`, never a
|
|
19
19
|
* technology bucket); the online document keeps the catalog's own asset name.
|
|
20
20
|
*
|
|
21
21
|
* Per-document RUNTIME state (an online asset resolving to a local path after
|
|
22
|
-
* download) lives in
|
|
22
|
+
* download) lives in an HMR-stable module spec map in the house
|
|
23
23
|
* `useSyncExternalStore` shape — the registry's descriptors stay immutable,
|
|
24
24
|
* and `AssetDocumentContent` re-renders from the spec map. Specs are dropped
|
|
25
25
|
* on document dispose (close / project teardown via
|
|
@@ -60,7 +60,7 @@ import './asset-workflow.css';
|
|
|
60
60
|
export interface AssetDocumentSpec {
|
|
61
61
|
readonly assetPath: string;
|
|
62
62
|
readonly kind: AssetKind;
|
|
63
|
-
/** Entity source for a session-only Asset
|
|
63
|
+
/** Entity source for a session-only live-object Asset Editor. Never serialized. */
|
|
64
64
|
readonly entityId?: string;
|
|
65
65
|
/** Present while this document shows an online (not-yet-downloaded) asset. */
|
|
66
66
|
readonly online?: OnlineAssetInfo;
|
|
@@ -74,8 +74,20 @@ export interface AssetDocumentMutation {
|
|
|
74
74
|
paths?: readonly string[];
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
const
|
|
78
|
-
const
|
|
77
|
+
const hotData = import.meta.hot?.data;
|
|
78
|
+
const _specs =
|
|
79
|
+
(hotData?.['vgai:asset-document-specs'] as Map<string, AssetDocumentSpec> | undefined) ??
|
|
80
|
+
new Map<string, AssetDocumentSpec>();
|
|
81
|
+
const _pathAliases =
|
|
82
|
+
(hotData?.['vgai:asset-document-path-aliases'] as Map<string, string> | undefined) ??
|
|
83
|
+
new Map<string, string>();
|
|
84
|
+
if (hotData) {
|
|
85
|
+
// Workspace descriptors intentionally survive compatible Fast Refreshes.
|
|
86
|
+
// Their specs must survive with them or the next persistence snapshot drops
|
|
87
|
+
// the open document and leaves a titled-but-empty Dockview tab on reload.
|
|
88
|
+
hotData['vgai:asset-document-specs'] = _specs;
|
|
89
|
+
hotData['vgai:asset-document-path-aliases'] = _pathAliases;
|
|
90
|
+
}
|
|
79
91
|
let _version = 0;
|
|
80
92
|
const _listeners = new Set<() => void>();
|
|
81
93
|
|
|
@@ -113,7 +125,7 @@ export interface AssetDocumentStore {
|
|
|
113
125
|
setActiveViewportTab(tab: 'scene' | 'game'): void;
|
|
114
126
|
}
|
|
115
127
|
|
|
116
|
-
/**
|
|
128
|
+
/** A live-entity Asset Editor additionally needs the object map for its §8 title. */
|
|
117
129
|
export interface EntityAssetDocumentStore extends AssetDocumentStore {
|
|
118
130
|
readonly objectMap: Map<string, THREE.Object3D>;
|
|
119
131
|
}
|
|
@@ -159,7 +171,7 @@ export function openAssetDocument(
|
|
|
159
171
|
return openSpecDocument(store, id, title, { assetPath, kind }, { sourcePath: assetPath });
|
|
160
172
|
}
|
|
161
173
|
|
|
162
|
-
/** Open the Asset
|
|
174
|
+
/** Open the typed Asset Editor for a live entity (legacy id retained for API compatibility). */
|
|
163
175
|
export function openEntityAssetDocument(store: EntityAssetDocumentStore, entityId: string): void {
|
|
164
176
|
const object = store.objectMap.get(entityId);
|
|
165
177
|
if (!object) return;
|
|
@@ -277,6 +289,8 @@ function AssetViewerBody({
|
|
|
277
289
|
if (spec.unavailableReason) {
|
|
278
290
|
return (
|
|
279
291
|
<AssetEditorShell
|
|
292
|
+
documentId={id}
|
|
293
|
+
active={active}
|
|
280
294
|
type={spec.kind}
|
|
281
295
|
title={spec.assetPath.split('/').pop() || 'Deleted asset'}
|
|
282
296
|
status="Source unavailable"
|
|
@@ -288,16 +302,29 @@ function AssetViewerBody({
|
|
|
288
302
|
const route = assetDocumentViewerRoute(spec);
|
|
289
303
|
if (route === 'online' && spec.online) {
|
|
290
304
|
return (
|
|
291
|
-
<
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
305
|
+
<AssetEditorShell
|
|
306
|
+
documentId={id}
|
|
307
|
+
active={active}
|
|
308
|
+
type="online"
|
|
309
|
+
title={spec.online.name}
|
|
310
|
+
details={
|
|
311
|
+
<OnlineAssetDetail
|
|
312
|
+
online={spec.online}
|
|
313
|
+
mode="inspector"
|
|
314
|
+
onResolved={(localPath) => resolveOnlineAssetDocument(id, localPath)}
|
|
315
|
+
/>
|
|
316
|
+
}
|
|
317
|
+
status={`${spec.online.type} · ${spec.online.source}`}
|
|
318
|
+
>
|
|
319
|
+
<OnlineAssetDetail online={spec.online} mode="preview" />
|
|
320
|
+
</AssetEditorShell>
|
|
295
321
|
);
|
|
296
322
|
}
|
|
297
323
|
const title = spec.assetPath.split('/').pop() || spec.entityId || 'Asset';
|
|
298
324
|
if (route === 'model')
|
|
299
325
|
return (
|
|
300
326
|
<ModelViewer
|
|
327
|
+
documentId={id}
|
|
301
328
|
assetPath={spec.assetPath || undefined}
|
|
302
329
|
entityId={spec.entityId}
|
|
303
330
|
active={active}
|
|
@@ -330,6 +357,8 @@ function AssetViewerBody({
|
|
|
330
357
|
}
|
|
331
358
|
return (
|
|
332
359
|
<AssetEditorShell
|
|
360
|
+
documentId={id}
|
|
361
|
+
active={active}
|
|
333
362
|
type={route}
|
|
334
363
|
title={title}
|
|
335
364
|
details={
|
|
@@ -369,7 +398,7 @@ export function AssetDocumentContent({ documentId, active }: WorkspaceDocumentCo
|
|
|
369
398
|
style={{
|
|
370
399
|
flex: 1,
|
|
371
400
|
minHeight: 0,
|
|
372
|
-
|
|
401
|
+
overflow: 'hidden',
|
|
373
402
|
pointerEvents: 'auto',
|
|
374
403
|
background: bg[1],
|
|
375
404
|
}}
|
|
@@ -22,13 +22,14 @@
|
|
|
22
22
|
import { resolveUrl } from '@engine/loader';
|
|
23
23
|
import { MaterialFileSchema } from '@engine/scene/schema/material';
|
|
24
24
|
import { useEffect, useState } from 'react';
|
|
25
|
-
import { getSelectedAsset, type SelectedAsset } from '../asset-selection';
|
|
25
|
+
import { getSelectedAsset, type SelectedAsset, setSelectedAsset } from '../asset-selection';
|
|
26
26
|
import { assetDocumentKind } from '../asset-workflow/asset-capabilities';
|
|
27
27
|
import { PROJECT_ASSET_COMMANDS } from '../asset-workflow/project-asset-commands';
|
|
28
28
|
import { Button, bg, border, radius, text } from '../design-system';
|
|
29
29
|
import { registerInspectorSections } from '../inspector-section-registry';
|
|
30
30
|
import { getModelThumbnailRenderer } from '../model-thumbnail';
|
|
31
31
|
import { validateInputMapForViewer } from './asset-viewers/InputMapViewer';
|
|
32
|
+
import { OnlineAssetDetail } from './asset-viewers/OnlineAssetDetail';
|
|
32
33
|
|
|
33
34
|
const labelStyle: React.CSSProperties = { fontSize: 11, color: text[3], marginBottom: 2 };
|
|
34
35
|
const valueStyle: React.CSSProperties = { fontSize: 11, color: text[1], wordBreak: 'break-all' };
|
|
@@ -227,6 +228,38 @@ export function AssetSelectionSection() {
|
|
|
227
228
|
const asset = getSelectedAsset();
|
|
228
229
|
if (!asset) return null;
|
|
229
230
|
const ext = asset.name.includes('.') ? (asset.name.split('.').pop() ?? '') : '';
|
|
231
|
+
if (asset.online) {
|
|
232
|
+
const online = asset.online;
|
|
233
|
+
return (
|
|
234
|
+
<div
|
|
235
|
+
data-testid="inspector-asset-selection"
|
|
236
|
+
style={{
|
|
237
|
+
pointerEvents: 'auto',
|
|
238
|
+
borderBottom: `1px solid ${border[1]}`,
|
|
239
|
+
display: 'flex',
|
|
240
|
+
flexDirection: 'column',
|
|
241
|
+
gap: 6,
|
|
242
|
+
}}
|
|
243
|
+
>
|
|
244
|
+
<OnlineAssetDetail
|
|
245
|
+
online={online}
|
|
246
|
+
mode="inspector"
|
|
247
|
+
onResolved={(localPath) => setSelectedAsset({ ...asset, path: localPath })}
|
|
248
|
+
/>
|
|
249
|
+
<div style={{ padding: '0 8px 8px' }}>
|
|
250
|
+
<Button
|
|
251
|
+
type="button"
|
|
252
|
+
style={{ width: '100%' }}
|
|
253
|
+
onClick={() =>
|
|
254
|
+
window.dispatchEvent(new CustomEvent('editor:open-online-asset', { detail: online }))
|
|
255
|
+
}
|
|
256
|
+
>
|
|
257
|
+
Open in Asset Editor
|
|
258
|
+
</Button>
|
|
259
|
+
</div>
|
|
260
|
+
</div>
|
|
261
|
+
);
|
|
262
|
+
}
|
|
230
263
|
return (
|
|
231
264
|
<div
|
|
232
265
|
data-testid="inspector-asset-selection"
|
|
@@ -50,6 +50,7 @@ import { AssetImportDetails } from '../AssetImportDetails';
|
|
|
50
50
|
import { writeProjectAsset } from '../asset-editor-persistence';
|
|
51
51
|
|
|
52
52
|
interface ModelViewerProps {
|
|
53
|
+
documentId?: string | undefined;
|
|
53
54
|
assetPath: string | undefined;
|
|
54
55
|
entityId: string | undefined;
|
|
55
56
|
materialPath?: string | undefined;
|
|
@@ -71,6 +72,7 @@ interface ModelViewerRuntime {
|
|
|
71
72
|
|
|
72
73
|
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: one viewer coordinates embedded/full chrome and both live-entity and file-backed Asset Editor sources.
|
|
73
74
|
export function ModelViewer({
|
|
75
|
+
documentId,
|
|
74
76
|
assetPath,
|
|
75
77
|
entityId,
|
|
76
78
|
materialPath,
|
|
@@ -493,6 +495,8 @@ export function ModelViewer({
|
|
|
493
495
|
|
|
494
496
|
return (
|
|
495
497
|
<AssetEditorShell
|
|
498
|
+
documentId={documentId ?? `asset-editor:${assetPath ?? entityId ?? displayName}`}
|
|
499
|
+
active={active}
|
|
496
500
|
type="model"
|
|
497
501
|
title={displayName}
|
|
498
502
|
toolbar={
|
|
@@ -663,7 +667,8 @@ export function ModelViewer({
|
|
|
663
667
|
)}
|
|
664
668
|
</div>
|
|
665
669
|
}
|
|
666
|
-
status={
|
|
670
|
+
status={`${loading ? `${loadStage}…` : error ? `${issue?.stage ?? 'rendering'} failed · ${issue?.code}` : 'Ready'} · ${inspection?.triangles.toLocaleString() ?? 0} triangles`}
|
|
671
|
+
controls={
|
|
667
672
|
inspection?.clips ? (
|
|
668
673
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
669
674
|
<Select
|
|
@@ -712,9 +717,7 @@ export function ModelViewer({
|
|
|
712
717
|
</Select>
|
|
713
718
|
</label>
|
|
714
719
|
</div>
|
|
715
|
-
) :
|
|
716
|
-
`${loading ? `${loadStage}…` : error ? `${issue?.stage ?? 'rendering'} failed · ${issue?.code}` : 'Ready'} · ${inspection?.triangles.toLocaleString() ?? 0} triangles`
|
|
717
|
-
)
|
|
720
|
+
) : undefined
|
|
718
721
|
}
|
|
719
722
|
>
|
|
720
723
|
{viewport}
|