react-three-game 0.0.100 → 0.0.102
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/README.md +2 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/plugins/crashcat/CrashcatPhysicsComponent.d.ts +3 -0
- package/dist/plugins/crashcat/CrashcatPhysicsComponent.js +352 -0
- package/dist/plugins/crashcat/CrashcatRuntime.d.ts +27 -0
- package/dist/plugins/crashcat/CrashcatRuntime.js +154 -0
- package/dist/plugins/crashcat/index.d.ts +2 -0
- package/dist/plugins/crashcat/index.js +2 -0
- package/dist/plugins/index.d.ts +1 -0
- package/dist/plugins/index.js +1 -0
- package/dist/tools/assetviewer/page.js +95 -46
- package/dist/tools/dragdrop/DragDropLoader.d.ts +3 -0
- package/dist/tools/dragdrop/DragDropLoader.js +183 -44
- package/dist/tools/dragdrop/index.d.ts +1 -1
- package/dist/tools/dragdrop/index.js +1 -1
- package/dist/tools/dragdrop/modelLoader.js +2 -0
- package/dist/tools/prefabeditor/EditorUI.js +7 -8
- package/dist/tools/prefabeditor/PrefabEditor.d.ts +3 -0
- package/dist/tools/prefabeditor/PrefabEditor.js +28 -11
- package/dist/tools/prefabeditor/PrefabRoot.d.ts +2 -0
- package/dist/tools/prefabeditor/PrefabRoot.js +51 -35
- package/dist/tools/prefabeditor/components/BufferGeometryComponent.js +20 -0
- package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +5 -0
- package/dist/tools/prefabeditor/components/ComponentRegistry.js +31 -0
- package/dist/tools/prefabeditor/components/DataComponent.js +1 -0
- package/dist/tools/prefabeditor/components/EnvironmentComponent.js +1 -0
- package/dist/tools/prefabeditor/components/GeometryComponent.js +1 -0
- package/dist/tools/prefabeditor/components/MaterialComponent.d.ts +1 -0
- package/dist/tools/prefabeditor/components/MaterialComponent.js +85 -57
- package/dist/tools/prefabeditor/components/ModelComponent.js +45 -3
- package/dist/tools/prefabeditor/components/SpriteComponent.js +1 -0
- package/dist/tools/prefabeditor/components/TransformComponent.js +1 -0
- package/dist/tools/prefabeditor/modelPrefab.d.ts +16 -0
- package/dist/tools/prefabeditor/modelPrefab.js +180 -0
- package/dist/tools/prefabeditor/prefabStore.d.ts +1 -0
- package/dist/tools/prefabeditor/prefabStore.js +75 -42
- package/package.json +27 -2
|
@@ -3,43 +3,69 @@ import { Canvas } from "@react-three/fiber";
|
|
|
3
3
|
import { OrbitControls, View, PerspectiveCamera } from "@react-three/drei";
|
|
4
4
|
import { Suspense, useEffect, useLayoutEffect, useState, useRef } from "react";
|
|
5
5
|
import { createPortal } from 'react-dom';
|
|
6
|
-
import { TextureLoader } from "three";
|
|
6
|
+
import { Mesh, TextureLoader } from "three";
|
|
7
7
|
import { loadModel } from "../dragdrop/modelLoader";
|
|
8
|
+
import { resolvePrefabAssetPath } from "../prefabeditor/PrefabEditor";
|
|
9
|
+
import { base, colors, fonts } from "../prefabeditor/styles";
|
|
8
10
|
const styles = {
|
|
9
|
-
errorIcon: { color:
|
|
11
|
+
errorIcon: { color: colors.danger, fontSize: 12 },
|
|
10
12
|
flexFillRelative: { flex: 1, position: 'relative' },
|
|
11
|
-
bottomLabel: { backgroundColor:
|
|
12
|
-
textLight: { color:
|
|
13
|
+
bottomLabel: { backgroundColor: colors.bgLight, color: colors.text, fontSize: fonts.sizeSm, padding: '1px 4px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', textAlign: 'center', borderTop: `1px solid ${colors.borderFaint}` },
|
|
14
|
+
textLight: { color: colors.text, fontFamily: fonts.family, fontSize: fonts.size },
|
|
13
15
|
iconLarge: { fontSize: 20 }
|
|
14
16
|
};
|
|
15
17
|
const assetViewerColors = {
|
|
16
|
-
panelBg:
|
|
17
|
-
controlBg:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
panelBg: colors.bg,
|
|
19
|
+
controlBg: colors.bgSurface,
|
|
20
|
+
previewBg: colors.bgLight,
|
|
21
|
+
text: colors.text,
|
|
22
|
+
border: colors.border,
|
|
23
|
+
borderFaint: colors.borderFaint,
|
|
24
|
+
accentBorder: colors.accentBorder,
|
|
25
|
+
errorBg: colors.dangerBg,
|
|
21
26
|
};
|
|
22
27
|
const assetPickerPopupBaseStyle = {
|
|
23
28
|
background: assetViewerColors.panelBg,
|
|
24
29
|
border: `1px solid ${assetViewerColors.border}`,
|
|
25
30
|
borderRadius: 0,
|
|
26
|
-
boxShadow: '
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
color: 'inherit',
|
|
31
|
-
fontSize: 10,
|
|
32
|
-
cursor: 'pointer',
|
|
33
|
-
border: `1px solid ${assetViewerColors.border}`,
|
|
34
|
-
borderRadius: 0,
|
|
31
|
+
boxShadow: 'none',
|
|
32
|
+
color: colors.text,
|
|
33
|
+
fontFamily: fonts.family,
|
|
34
|
+
fontSize: fonts.size,
|
|
35
35
|
};
|
|
36
|
+
const assetPickerButtonBaseStyle = Object.assign(Object.assign({}, base.btn), { background: assetViewerColors.controlBg, color: colors.text, fontSize: fonts.size, cursor: 'pointer' });
|
|
36
37
|
const assetPickerWideButtonStyle = Object.assign(Object.assign({}, assetPickerButtonBaseStyle), { width: '100%', padding: '6px 8px' });
|
|
38
|
+
function disposeMaterial(material) {
|
|
39
|
+
if (Array.isArray(material)) {
|
|
40
|
+
material.forEach(entry => entry.dispose());
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
material.dispose();
|
|
44
|
+
}
|
|
45
|
+
function disposeObject3D(object) {
|
|
46
|
+
object.traverse(child => {
|
|
47
|
+
var _a;
|
|
48
|
+
if (!(child instanceof Mesh))
|
|
49
|
+
return;
|
|
50
|
+
(_a = child.geometry) === null || _a === void 0 ? void 0 : _a.dispose();
|
|
51
|
+
disposeMaterial(child.material);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
37
54
|
const assetPickerSmallButtonStyle = Object.assign(Object.assign({}, assetPickerButtonBaseStyle), { padding: '4px 8px' });
|
|
38
55
|
const assetPickerEmptyPreviewStyle = {
|
|
39
|
-
backgroundColor: assetViewerColors.
|
|
56
|
+
backgroundColor: assetViewerColors.previewBg,
|
|
40
57
|
border: `1px dashed ${assetViewerColors.border}`,
|
|
41
58
|
borderRadius: 0,
|
|
42
59
|
};
|
|
60
|
+
const assetTileStyle = {
|
|
61
|
+
backgroundColor: assetViewerColors.previewBg,
|
|
62
|
+
color: assetViewerColors.text,
|
|
63
|
+
border: `1px solid ${assetViewerColors.borderFaint}`,
|
|
64
|
+
cursor: 'pointer',
|
|
65
|
+
display: 'flex',
|
|
66
|
+
flexDirection: 'column',
|
|
67
|
+
boxSizing: 'border-box',
|
|
68
|
+
};
|
|
43
69
|
function getItemsInPath(files, currentPath) {
|
|
44
70
|
// Remove the leading category folder (e.g., /textures/, /models/, /sounds/)
|
|
45
71
|
const filesWithoutCategory = files.map(file => {
|
|
@@ -64,17 +90,7 @@ function getItemsInPath(files, currentPath) {
|
|
|
64
90
|
return { folders: Array.from(folders), filesInCurrentPath };
|
|
65
91
|
}
|
|
66
92
|
function FolderTile({ name, onClick }) {
|
|
67
|
-
return (_jsxs("div", { onClick: onClick, style: {
|
|
68
|
-
maxWidth: 60,
|
|
69
|
-
aspectRatio: '1 / 1',
|
|
70
|
-
backgroundColor: assetViewerColors.controlBg,
|
|
71
|
-
color: assetViewerColors.text,
|
|
72
|
-
cursor: 'pointer',
|
|
73
|
-
display: 'flex',
|
|
74
|
-
flexDirection: 'column',
|
|
75
|
-
alignItems: 'center',
|
|
76
|
-
justifyContent: 'center'
|
|
77
|
-
}, children: [_jsx("div", { style: { fontSize: 24 }, children: "\uD83D\uDCC1" }), _jsx("div", { style: { fontSize: 10, textAlign: 'center', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', width: '100%', padding: '0 4px', marginTop: 4 }, children: name })] }));
|
|
93
|
+
return (_jsxs("div", { onClick: onClick, style: Object.assign(Object.assign({ maxWidth: 60, aspectRatio: '1 / 1' }, assetTileStyle), { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }), children: [_jsx("div", { style: { fontSize: 24 }, children: "\uD83D\uDCC1" }), _jsx("div", { style: { fontSize: 10, textAlign: 'center', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', width: '100%', padding: '0 4px', marginTop: 4 }, children: name })] }));
|
|
78
94
|
}
|
|
79
95
|
function useInView() {
|
|
80
96
|
const [isInView, setIsInView] = useState(false);
|
|
@@ -101,30 +117,53 @@ function AssetListViewer({ files, selected, onSelect, renderCard }) {
|
|
|
101
117
|
const pathParts = currentPath.split('/').filter(Boolean);
|
|
102
118
|
pathParts.pop();
|
|
103
119
|
setCurrentPath(pathParts.join('/'));
|
|
104
|
-
}, style: Object.assign(Object.assign({}, assetPickerSmallButtonStyle), { marginBottom: 4
|
|
120
|
+
}, style: Object.assign(Object.assign({}, assetPickerSmallButtonStyle), { marginBottom: 4 }), children: "\u2190 Back" })), _jsxs("div", { style: { display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 4 }, children: [folders.map((folder) => (_jsx(FolderTile, { name: folder, onClick: () => setCurrentPath(currentPath ? `${currentPath}/${folder}` : folder) }, folder))), filesInCurrentPath.map((file) => (_jsx("div", { children: renderCard(file, onSelect) }, file)))] })] }));
|
|
105
121
|
}
|
|
106
122
|
export function TextureListViewer({ files, selected, onSelect, basePath = "" }) {
|
|
107
123
|
return (_jsxs("div", { style: { position: 'relative', width: '100%', height: '100%' }, children: [_jsx("div", { style: { width: '100%', height: '100%', overflowY: 'auto', overflowX: 'hidden', paddingRight: 4 }, children: _jsx(AssetListViewer, { files: files, selected: selected, onSelect: onSelect, renderCard: (file, onSelectHandler) => (_jsx(TextureCard, { file: file, basePath: basePath, onSelect: onSelectHandler })) }) }), _jsx(SharedCanvas, {})] }));
|
|
108
124
|
}
|
|
109
125
|
function TextureCard({ file, onSelect, basePath = "" }) {
|
|
110
|
-
const [error, setError] = useState(false);
|
|
111
126
|
const [isHovered, setIsHovered] = useState(false);
|
|
127
|
+
const [error, setError] = useState(false);
|
|
112
128
|
const { ref, isInView } = useInView();
|
|
113
|
-
const fullPath = basePath
|
|
129
|
+
const fullPath = resolvePrefabAssetPath(basePath, file);
|
|
130
|
+
const fileName = file.split('/').pop();
|
|
114
131
|
if (error) {
|
|
115
|
-
return (
|
|
132
|
+
return (_jsxs("div", { ref: ref, style: Object.assign(Object.assign({ maxWidth: 60, aspectRatio: '1 / 1' }, assetTileStyle), { backgroundColor: assetViewerColors.errorBg }), onClick: () => onSelect(file), title: `Could not load ${file}`, children: [_jsx("div", { style: { flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }, children: _jsx("div", { style: styles.errorIcon, children: "\u2717" }) }), _jsx("div", { style: styles.bottomLabel, children: fileName })] }));
|
|
116
133
|
}
|
|
117
|
-
return (_jsxs("div", { ref: ref, style: { maxWidth: 60, aspectRatio: '1 / 1'
|
|
134
|
+
return (_jsxs("div", { ref: ref, style: Object.assign({ maxWidth: 60, aspectRatio: '1 / 1' }, assetTileStyle), onClick: () => onSelect(file), onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), children: [_jsx("div", { style: { flex: 1, position: 'relative' }, children: isInView ? (_jsxs(View, { style: { width: '100%', height: '100%' }, children: [_jsx(PerspectiveCamera, { makeDefault: true, position: [0, 0, 2.5], fov: 50 }), _jsx("ambientLight", { intensity: 0.8 }), _jsx("pointLight", { position: [5, 5, 5], intensity: 0.5 }), _jsx(TextureSphere, { url: fullPath, onError: () => setError(true) }), _jsx(OrbitControls, { enableZoom: false, enablePan: false, autoRotate: isHovered, autoRotateSpeed: 2 })] })) : null }), _jsx("div", { style: styles.bottomLabel, children: fileName })] }));
|
|
118
135
|
}
|
|
119
136
|
function TextureSphere({ url, onError }) {
|
|
120
137
|
const [texture, setTexture] = useState(null);
|
|
138
|
+
const textureRef = useRef(null);
|
|
139
|
+
const onErrorRef = useRef(onError);
|
|
140
|
+
onErrorRef.current = onError;
|
|
121
141
|
useEffect(() => {
|
|
142
|
+
var _a;
|
|
143
|
+
let cancelled = false;
|
|
144
|
+
(_a = textureRef.current) === null || _a === void 0 ? void 0 : _a.dispose();
|
|
145
|
+
textureRef.current = null;
|
|
122
146
|
setTexture(null);
|
|
123
147
|
const loader = new TextureLoader();
|
|
124
|
-
loader.load(url,
|
|
125
|
-
|
|
126
|
-
|
|
148
|
+
loader.load(url, loadedTexture => {
|
|
149
|
+
if (cancelled) {
|
|
150
|
+
loadedTexture.dispose();
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
textureRef.current = loadedTexture;
|
|
154
|
+
setTexture(loadedTexture);
|
|
155
|
+
}, undefined, () => {
|
|
156
|
+
var _a;
|
|
157
|
+
if (!cancelled) {
|
|
158
|
+
(_a = onErrorRef.current) === null || _a === void 0 ? void 0 : _a.call(onErrorRef);
|
|
159
|
+
}
|
|
127
160
|
});
|
|
161
|
+
return () => {
|
|
162
|
+
var _a;
|
|
163
|
+
cancelled = true;
|
|
164
|
+
(_a = textureRef.current) === null || _a === void 0 ? void 0 : _a.dispose();
|
|
165
|
+
textureRef.current = null;
|
|
166
|
+
};
|
|
128
167
|
}, [url]);
|
|
129
168
|
if (!texture)
|
|
130
169
|
return null;
|
|
@@ -136,31 +175,41 @@ export function ModelListViewer({ files, selected, onSelect, basePath = "" }) {
|
|
|
136
175
|
function ModelCard({ file, onSelect, basePath = "", size = 60, }) {
|
|
137
176
|
const [error, setError] = useState(false);
|
|
138
177
|
const { ref, isInView } = useInView();
|
|
139
|
-
const fullPath = basePath
|
|
178
|
+
const fullPath = resolvePrefabAssetPath(basePath, file);
|
|
140
179
|
if (error) {
|
|
141
|
-
return (_jsx("div", { ref: ref, style: { aspectRatio: '1 / 1', backgroundColor:
|
|
180
|
+
return (_jsx("div", { ref: ref, style: { aspectRatio: '1 / 1', backgroundColor: assetViewerColors.errorBg, cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', border: `1px solid ${colors.dangerBorder}` }, onClick: () => onSelect(file), children: _jsx("div", { style: styles.errorIcon, children: "\u2717" }) }));
|
|
142
181
|
}
|
|
143
|
-
return (_jsxs("div", { ref: ref, style: { width: size, aspectRatio: '1 / 1'
|
|
182
|
+
return (_jsxs("div", { ref: ref, style: Object.assign({ width: size, aspectRatio: '1 / 1' }, assetTileStyle), onClick: () => onSelect(file), children: [_jsx("div", { style: styles.flexFillRelative, children: isInView ? (_jsxs(View, { style: { width: '100%', height: '100%' }, children: [_jsx(PerspectiveCamera, { makeDefault: true, position: [0, 1, 3], fov: 50 }), _jsxs(Suspense, { fallback: null, children: [_jsx("ambientLight", { intensity: 1 }), _jsx("pointLight", { position: [5, 5, 5], intensity: 0.5 }), _jsx(ModelPreview, { url: fullPath, onError: () => setError(true) }), _jsx(OrbitControls, { enableZoom: false })] })] })) : null }), _jsx("div", { style: styles.bottomLabel, children: file.split('/').pop() })] }));
|
|
144
183
|
}
|
|
145
184
|
function ModelPreview({ url, onError }) {
|
|
146
185
|
const [model, setModel] = useState(null);
|
|
186
|
+
const modelRef = useRef(null);
|
|
147
187
|
const onErrorRef = useRef(onError);
|
|
148
188
|
onErrorRef.current = onError;
|
|
149
189
|
useEffect(() => {
|
|
150
190
|
let cancelled = false;
|
|
191
|
+
modelRef.current && disposeObject3D(modelRef.current);
|
|
192
|
+
modelRef.current = null;
|
|
151
193
|
setModel(null);
|
|
152
194
|
loadModel(url).then((result) => {
|
|
153
195
|
var _a;
|
|
154
|
-
if (cancelled)
|
|
196
|
+
if (cancelled) {
|
|
197
|
+
result.model && disposeObject3D(result.model);
|
|
155
198
|
return;
|
|
199
|
+
}
|
|
156
200
|
if (result.success && result.model) {
|
|
201
|
+
modelRef.current = result.model;
|
|
157
202
|
setModel(result.model);
|
|
158
203
|
}
|
|
159
204
|
else {
|
|
160
205
|
(_a = onErrorRef.current) === null || _a === void 0 ? void 0 : _a.call(onErrorRef);
|
|
161
206
|
}
|
|
162
207
|
});
|
|
163
|
-
return () => {
|
|
208
|
+
return () => {
|
|
209
|
+
cancelled = true;
|
|
210
|
+
modelRef.current && disposeObject3D(modelRef.current);
|
|
211
|
+
modelRef.current = null;
|
|
212
|
+
};
|
|
164
213
|
}, [url]);
|
|
165
214
|
if (!model)
|
|
166
215
|
return null;
|
|
@@ -171,8 +220,8 @@ export function SoundListViewer({ files, selected, onSelect, basePath = "" }) {
|
|
|
171
220
|
}
|
|
172
221
|
function SoundCard({ file, onSelect, basePath = "" }) {
|
|
173
222
|
const fileName = file.split('/').pop() || '';
|
|
174
|
-
const fullPath = basePath
|
|
175
|
-
return (_jsxs("div", { onClick: () => onSelect(file), style: { aspectRatio: '1 / 1'
|
|
223
|
+
const fullPath = resolvePrefabAssetPath(basePath, file);
|
|
224
|
+
return (_jsxs("div", { onClick: () => onSelect(file), style: Object.assign(Object.assign({ aspectRatio: '1 / 1' }, assetTileStyle), { alignItems: 'center', justifyContent: 'center' }), children: [_jsx("div", { style: styles.iconLarge, children: "\uD83D\uDD0A" }), _jsx("div", { style: { color: colors.text, fontSize: fonts.size, padding: '0 4px', marginTop: 4, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', textAlign: 'center', width: '100%' }, children: fileName })] }));
|
|
176
225
|
}
|
|
177
226
|
const PICKER_POPUP_WIDTH = 260;
|
|
178
227
|
const PICKER_POPUP_HEIGHT = 360;
|
|
@@ -225,7 +274,7 @@ export function TexturePicker({ value, onChange, basePath = "" }) {
|
|
|
225
274
|
return (_jsx(AssetPicker, { value: value, onChange: onChange, basePath: basePath, manifestFolder: "textures", rootStyle: { maxHeight: 128, overflow: 'visible', position: 'relative', display: 'flex', alignItems: 'center' }, changeButtonStyle: Object.assign(Object.assign({}, assetPickerSmallButtonStyle), { marginTop: 4 }), clearButtonStyle: Object.assign(Object.assign({}, assetPickerSmallButtonStyle), { marginTop: 4, marginLeft: 4 }), preview: _jsx(SingleTextureViewer, { file: value, basePath: basePath }), renderList: ({ files, value: selectedValue, onSelect, basePath: currentBasePath }) => (_jsx(TextureListViewer, { files: files, selected: selectedValue || undefined, onSelect: onSelect, basePath: currentBasePath })) }));
|
|
226
275
|
}
|
|
227
276
|
export function ModelPicker({ value, onChange, basePath = "", pickerKey }) {
|
|
228
|
-
return (_jsx(AssetPicker, { value: value, onChange: onChange, basePath: basePath, manifestFolder: "models", rootStyle: { maxHeight: 160, overflow: 'visible', position: 'relative', display: 'flex', gap: 8, alignItems: 'center', justifyContent: 'center' }, controlsStyle: { display: 'flex', flexDirection: 'column', gap: 6, flex: '0 0 84px', minWidth: 84, justifyContent: 'flex-end' }, changeButtonStyle: Object.assign(Object.assign({}, assetPickerWideButtonStyle), { border: `1px solid ${assetViewerColors.accentBorder}` }), clearButtonStyle: Object.assign(Object.assign({}, assetPickerWideButtonStyle), { border: `1px solid ${assetViewerColors.accentBorder}` }), popupStyle: {
|
|
277
|
+
return (_jsx(AssetPicker, { value: value, onChange: onChange, basePath: basePath, manifestFolder: "models", rootStyle: { maxHeight: 160, overflow: 'visible', position: 'relative', display: 'flex', gap: 8, alignItems: 'center', justifyContent: 'center' }, controlsStyle: { display: 'flex', flexDirection: 'column', gap: 6, flex: '0 0 84px', minWidth: 84, justifyContent: 'flex-end' }, changeButtonStyle: Object.assign(Object.assign({}, assetPickerWideButtonStyle), { border: `1px solid ${assetViewerColors.accentBorder}` }), clearButtonStyle: Object.assign(Object.assign({}, assetPickerWideButtonStyle), { border: `1px solid ${assetViewerColors.accentBorder}` }), popupStyle: { border: `1px solid ${assetViewerColors.accentBorder}` }, preview: _jsx("div", { style: { flex: '0 0 auto' }, children: _jsx(SingleModelViewer, { file: value, basePath: basePath }) }), renderList: ({ files, value: selectedValue, onSelect, basePath: currentBasePath }) => (_jsx(ModelListViewer, { files: files, selected: selectedValue ? `/${selectedValue}` : undefined, onSelect: (file) => onSelect(file.startsWith('/') ? file.slice(1) : file), basePath: currentBasePath }, pickerKey)) }));
|
|
229
278
|
}
|
|
230
279
|
export function SoundPicker({ value, onChange, basePath = "" }) {
|
|
231
280
|
return (_jsx(AssetPicker, { value: value, onChange: onChange, basePath: basePath, manifestFolder: "sound", rootStyle: { maxHeight: 76, overflow: 'visible', position: 'relative', display: 'flex', gap: 8, alignItems: 'center', justifyContent: 'center' }, controlsStyle: { display: 'flex', flexDirection: 'column', gap: 6, flex: '0 0 84px', minWidth: 84, justifyContent: 'flex-end' }, changeButtonStyle: assetPickerWideButtonStyle, clearButtonStyle: assetPickerWideButtonStyle, preview: _jsx("div", { style: { flex: '0 0 auto', minWidth: 84 }, children: value ? _jsx(SingleSoundViewer, { file: value, basePath: basePath }) : _jsx("div", { style: Object.assign({ width: 84, height: 60 }, assetPickerEmptyPreviewStyle) }) }), renderList: ({ files, value: selectedValue, onSelect, basePath: currentBasePath }) => (_jsx(SoundListViewer, { files: files, selected: selectedValue || undefined, onSelect: onSelect, basePath: currentBasePath })) }));
|
|
@@ -17,6 +17,9 @@ export interface FilePickerProps extends AssetLoadOptions, DivProps {
|
|
|
17
17
|
children?: ReactNode;
|
|
18
18
|
multiple?: boolean;
|
|
19
19
|
}
|
|
20
|
+
export declare function loadDroppedAssets(dataTransfer: DataTransfer | null, options: AssetLoadOptions): Promise<void>;
|
|
21
|
+
export declare function loadUrls(urls: string[], options: AssetLoadOptions): Promise<void>;
|
|
22
|
+
export declare function loadUrl(url: string, options: AssetLoadOptions): Promise<void>;
|
|
20
23
|
export declare function loadFiles(files: File[], { onModelLoaded, onTextureLoaded, onSoundLoaded, onUnhandledFile, onFilesLoaded, onLoadError }: AssetLoadOptions): Promise<void>;
|
|
21
24
|
export declare function DragDropLoader({ children, ...divProps }: DragDropLoaderProps): import("react/jsx-runtime").JSX.Element;
|
|
22
25
|
export declare function FilePicker({ accept, children, multiple, ...divProps }: FilePickerProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -25,56 +25,196 @@ const DEFAULT_ACCEPT = ".glb,.gltf,.fbx,.png,.jpg,.jpeg,.webp,.gif,.bmp,.svg,.mp
|
|
|
25
25
|
function getFiles(fileList) {
|
|
26
26
|
return fileList ? Array.from(fileList) : [];
|
|
27
27
|
}
|
|
28
|
-
|
|
29
|
-
return
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
28
|
+
function getStringFromItem(item) {
|
|
29
|
+
return new Promise(resolve => item.getAsString(value => resolve(value !== null && value !== void 0 ? value : "")));
|
|
30
|
+
}
|
|
31
|
+
function parseUriList(value) {
|
|
32
|
+
return value
|
|
33
|
+
.split(/\r?\n/)
|
|
34
|
+
.map(line => line.trim())
|
|
35
|
+
.filter(line => line && !line.startsWith("#"));
|
|
36
|
+
}
|
|
37
|
+
function parseDroppedUrls(value) {
|
|
38
|
+
const urls = new Set();
|
|
39
|
+
const addUrl = (candidate) => {
|
|
40
|
+
try {
|
|
41
|
+
const url = new URL(candidate.trim());
|
|
42
|
+
if (url.protocol === "http:" || url.protocol === "https:") {
|
|
43
|
+
urls.add(url.href);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (_a) {
|
|
47
|
+
// Ignore non-URL dropped text.
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
parseUriList(value).forEach(addUrl);
|
|
51
|
+
if (typeof DOMParser !== "undefined" && value.includes("<")) {
|
|
52
|
+
const document = new DOMParser().parseFromString(value, "text/html");
|
|
53
|
+
document.querySelectorAll("[href], [src]").forEach(element => {
|
|
54
|
+
var _a;
|
|
55
|
+
const rawUrl = (_a = element.getAttribute("href")) !== null && _a !== void 0 ? _a : element.getAttribute("src");
|
|
56
|
+
if (rawUrl)
|
|
57
|
+
addUrl(rawUrl);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return Array.from(urls);
|
|
61
|
+
}
|
|
62
|
+
function getDroppedUrls(dataTransfer) {
|
|
63
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
64
|
+
var _a;
|
|
65
|
+
if (!dataTransfer)
|
|
66
|
+
return [];
|
|
67
|
+
const urls = new Set();
|
|
68
|
+
const itemValues = yield Promise.all(Array.from((_a = dataTransfer.items) !== null && _a !== void 0 ? _a : [])
|
|
69
|
+
.filter(item => item.kind === "string")
|
|
70
|
+
.map(getStringFromItem));
|
|
71
|
+
itemValues.forEach(value => parseDroppedUrls(value).forEach(url => urls.add(url)));
|
|
72
|
+
const uriList = dataTransfer.getData("text/uri-list");
|
|
73
|
+
const plainText = dataTransfer.getData("text/plain");
|
|
74
|
+
const html = dataTransfer.getData("text/html");
|
|
75
|
+
[uriList, plainText, html].forEach(value => {
|
|
76
|
+
if (value)
|
|
77
|
+
parseDroppedUrls(value).forEach(url => urls.add(url));
|
|
78
|
+
});
|
|
79
|
+
return Array.from(urls);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
function getExtensionFromMimeType(mimeType) {
|
|
83
|
+
var _a;
|
|
84
|
+
const type = mimeType.split(";", 1)[0].toLowerCase();
|
|
85
|
+
const extensions = {
|
|
86
|
+
"image/jpeg": ".jpg",
|
|
87
|
+
"image/png": ".png",
|
|
88
|
+
"image/webp": ".webp",
|
|
89
|
+
"image/gif": ".gif",
|
|
90
|
+
"image/bmp": ".bmp",
|
|
91
|
+
"image/svg+xml": ".svg",
|
|
92
|
+
"model/gltf-binary": ".glb",
|
|
93
|
+
"model/gltf+json": ".gltf",
|
|
94
|
+
"audio/mpeg": ".mp3",
|
|
95
|
+
"audio/wav": ".wav",
|
|
96
|
+
"audio/ogg": ".ogg",
|
|
97
|
+
"audio/mp4": ".m4a",
|
|
98
|
+
};
|
|
99
|
+
return (_a = extensions[type]) !== null && _a !== void 0 ? _a : "";
|
|
100
|
+
}
|
|
101
|
+
function getFilenameFromUrl(url, mimeType) {
|
|
102
|
+
const parsedUrl = new URL(url);
|
|
103
|
+
const rawName = parsedUrl.pathname.split("/").filter(Boolean).pop() || "asset";
|
|
104
|
+
const decodedName = decodeURIComponent(rawName).replace(/[^\w.\-]+/g, "-");
|
|
105
|
+
const hasExtension = /\.[a-z0-9]+$/i.test(decodedName);
|
|
106
|
+
return hasExtension ? decodedName : `${decodedName}${getExtensionFromMimeType(mimeType)}`;
|
|
107
|
+
}
|
|
108
|
+
function fetchUrlAsFile(url) {
|
|
109
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
110
|
+
const response = yield fetch(url);
|
|
111
|
+
if (!response.ok) {
|
|
112
|
+
throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
|
|
113
|
+
}
|
|
114
|
+
const blob = yield response.blob();
|
|
115
|
+
return new File([blob], getFilenameFromUrl(url, blob.type), { type: blob.type });
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
export function loadDroppedAssets(dataTransfer, options) {
|
|
119
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
120
|
+
const localFiles = getFiles(dataTransfer === null || dataTransfer === void 0 ? void 0 : dataTransfer.files);
|
|
121
|
+
const remoteUrls = yield getDroppedUrls(dataTransfer);
|
|
122
|
+
const localNames = new Set(localFiles.map(file => file.name));
|
|
123
|
+
yield Promise.all(remoteUrls.map((url) => __awaiter(this, void 0, void 0, function* () {
|
|
124
|
+
var _a;
|
|
125
|
+
try {
|
|
126
|
+
const file = yield fetchUrlAsFile(url);
|
|
127
|
+
if (!localNames.has(file.name)) {
|
|
128
|
+
yield loadFile(file, options, url);
|
|
39
129
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
const fallbackName = getFilenameFromUrl(url, "");
|
|
133
|
+
const fallbackFile = new File([], fallbackName);
|
|
134
|
+
yield ((_a = options.onLoadError) === null || _a === void 0 ? void 0 : _a.call(options, error, url, fallbackFile));
|
|
135
|
+
if (!options.onLoadError) {
|
|
136
|
+
console.error("URL load error:", error);
|
|
43
137
|
}
|
|
44
|
-
|
|
138
|
+
}
|
|
139
|
+
})));
|
|
140
|
+
yield loadFiles(localFiles, options);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
export function loadUrls(urls, options) {
|
|
144
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
145
|
+
yield Promise.all(urls.map(url => loadUrl(url, options)));
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
export function loadUrl(url, options) {
|
|
149
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
150
|
+
var _a;
|
|
151
|
+
try {
|
|
152
|
+
const file = yield fetchUrlAsFile(url);
|
|
153
|
+
yield loadFile(file, options, url);
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
const fallbackName = getFilenameFromUrl(url, "");
|
|
157
|
+
const fallbackFile = new File([], fallbackName);
|
|
158
|
+
yield ((_a = options.onLoadError) === null || _a === void 0 ? void 0 : _a.call(options, error, url, fallbackFile));
|
|
159
|
+
if (!options.onLoadError) {
|
|
160
|
+
console.error("URL load error:", error);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
export function loadFiles(files_1, _a) {
|
|
166
|
+
return __awaiter(this, arguments, void 0, function* (files, { onModelLoaded, onTextureLoaded, onSoundLoaded, onUnhandledFile, onFilesLoaded, onLoadError }) {
|
|
167
|
+
yield Promise.all(files.map(file => loadFile(file, { onModelLoaded, onTextureLoaded, onSoundLoaded, onUnhandledFile, onLoadError })));
|
|
168
|
+
yield (onFilesLoaded === null || onFilesLoaded === void 0 ? void 0 : onFilesLoaded(files));
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
function loadFile(file_1, _a) {
|
|
172
|
+
return __awaiter(this, arguments, void 0, function* (file, { onModelLoaded, onTextureLoaded, onSoundLoaded, onUnhandledFile, onLoadError }, assetRef = file.name) {
|
|
173
|
+
const shouldParseModel = canParseModelFile(file);
|
|
174
|
+
const shouldParseTexture = canParseTextureFile(file);
|
|
175
|
+
const shouldParseSound = canParseSoundFile(file);
|
|
176
|
+
if (shouldParseModel) {
|
|
177
|
+
const result = yield parseModelFromFile(file);
|
|
178
|
+
if (result.success && result.model) {
|
|
179
|
+
yield (onModelLoaded === null || onModelLoaded === void 0 ? void 0 : onModelLoaded(result.model, assetRef, file));
|
|
45
180
|
return;
|
|
46
181
|
}
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
if (result.success && result.texture) {
|
|
50
|
-
yield (onTextureLoaded === null || onTextureLoaded === void 0 ? void 0 : onTextureLoaded(result.texture, file.name, file));
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
if (onLoadError) {
|
|
54
|
-
yield onLoadError(result.error, file.name, file);
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
console.error("Texture parse error:", result.error);
|
|
182
|
+
if (onLoadError) {
|
|
183
|
+
yield onLoadError(result.error, assetRef, file);
|
|
58
184
|
return;
|
|
59
185
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
yield onLoadError(result.error, file.name, file);
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
console.error("Sound parse error:", result.error);
|
|
186
|
+
console.error("Model parse error:", result.error);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (shouldParseTexture) {
|
|
190
|
+
const result = yield parseTextureFromFile(file);
|
|
191
|
+
if (result.success && result.texture) {
|
|
192
|
+
yield (onTextureLoaded === null || onTextureLoaded === void 0 ? void 0 : onTextureLoaded(result.texture, assetRef, file));
|
|
71
193
|
return;
|
|
72
194
|
}
|
|
73
|
-
if (
|
|
74
|
-
yield
|
|
195
|
+
if (onLoadError) {
|
|
196
|
+
yield onLoadError(result.error, assetRef, file);
|
|
197
|
+
return;
|
|
75
198
|
}
|
|
76
|
-
|
|
77
|
-
|
|
199
|
+
console.error("Texture parse error:", result.error);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (shouldParseSound) {
|
|
203
|
+
const result = yield parseSoundFromFile(file);
|
|
204
|
+
if (result.success && result.sound) {
|
|
205
|
+
yield (onSoundLoaded === null || onSoundLoaded === void 0 ? void 0 : onSoundLoaded(result.sound, assetRef, file));
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (onLoadError) {
|
|
209
|
+
yield onLoadError(result.error, assetRef, file);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
console.error("Sound parse error:", result.error);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (onUnhandledFile) {
|
|
216
|
+
yield onUnhandledFile(file);
|
|
217
|
+
}
|
|
78
218
|
});
|
|
79
219
|
}
|
|
80
220
|
function reportFileLoadError(error) {
|
|
@@ -94,16 +234,15 @@ export function DragDropLoader(_a) {
|
|
|
94
234
|
var { children } = _a, divProps = __rest(_a, ["children"]);
|
|
95
235
|
const loadOptions = createLoadHandlers(divProps);
|
|
96
236
|
function handleDrop(event) {
|
|
97
|
-
var _a;
|
|
98
237
|
event.preventDefault();
|
|
99
238
|
event.stopPropagation();
|
|
100
|
-
void
|
|
239
|
+
void loadDroppedAssets(event.dataTransfer, loadOptions).catch(reportFileLoadError);
|
|
101
240
|
}
|
|
102
241
|
function handleDragOver(event) {
|
|
103
242
|
event.preventDefault();
|
|
104
243
|
event.stopPropagation();
|
|
105
244
|
}
|
|
106
|
-
return (_jsx("div", Object.assign({}, divProps, { onDrop: handleDrop, onDragOver: handleDragOver, children: children })));
|
|
245
|
+
return (_jsx("div", Object.assign({ role: "application" }, divProps, { onDrop: handleDrop, onDragOver: handleDragOver, children: children })));
|
|
107
246
|
}
|
|
108
247
|
export function FilePicker(_a) {
|
|
109
248
|
var { accept = DEFAULT_ACCEPT, children, multiple = true } = _a, divProps = __rest(_a, ["accept", "children", "multiple"]);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { DragDropLoader, FilePicker, loadFiles } from "./DragDropLoader";
|
|
1
|
+
export { DragDropLoader, FilePicker, loadDroppedAssets, loadFiles, loadUrl, loadUrls } from "./DragDropLoader";
|
|
2
2
|
export type { AssetLoadOptions, DragDropLoaderProps, FilePickerProps } from "./DragDropLoader";
|
|
3
3
|
export { loadModel, loadSound, loadTexture, parseModelFromFile, parseSoundFromFile, parseTextureFromFile } from "./modelLoader";
|
|
4
4
|
export type { LoadedModel, LoadedSound, LoadedTexture, LoadedModels, LoadedSounds, LoadedTextures, ModelLoadResult, ProgressCallback, SoundLoadResult, TextureLoadResult } from "./modelLoader";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { DragDropLoader, FilePicker, loadFiles } from "./DragDropLoader";
|
|
1
|
+
export { DragDropLoader, FilePicker, loadDroppedAssets, loadFiles, loadUrl, loadUrls } from "./DragDropLoader";
|
|
2
2
|
export { loadModel, loadSound, loadTexture, parseModelFromFile, parseSoundFromFile, parseTextureFromFile } from "./modelLoader";
|
|
@@ -50,6 +50,8 @@ export function canParseModelFile(file) {
|
|
|
50
50
|
}
|
|
51
51
|
export function canParseTextureFile(file) {
|
|
52
52
|
const filename = typeof file === "string" ? file : file.name;
|
|
53
|
+
if (filename.startsWith("data:image/"))
|
|
54
|
+
return true;
|
|
53
55
|
const normalizedName = normalizeModelPath(filename);
|
|
54
56
|
return TEXTURE_FILE_EXTENSIONS.some(extension => normalizedName.endsWith(extension));
|
|
55
57
|
}
|
|
@@ -11,9 +11,8 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
13
13
|
import { useState } from 'react';
|
|
14
|
-
import { hasComponent } from "./types";
|
|
15
14
|
import EditorTree from './EditorTree';
|
|
16
|
-
import { getAllComponentDefs } from './components/ComponentRegistry';
|
|
15
|
+
import { canAddComponentToNode, getAllComponentDefs, getNextComponentKey } from './components/ComponentRegistry';
|
|
17
16
|
import { createComponentData } from './prefab';
|
|
18
17
|
import { useEditorRef } from './PrefabEditor';
|
|
19
18
|
import { base, colors, inspector, componentCard } from './styles';
|
|
@@ -34,32 +33,32 @@ function EditorUI({ selectedId, setSelectedId, getPrefab, onReplacePrefab, onImp
|
|
|
34
33
|
editor.remove(selectedId);
|
|
35
34
|
setSelectedId(null);
|
|
36
35
|
};
|
|
37
|
-
return _jsxs(_Fragment, { children: [_jsxs("div", { style: inspector.panel, children: [_jsxs("
|
|
36
|
+
return _jsxs(_Fragment, { children: [_jsxs("div", { style: inspector.panel, children: [_jsxs("button", { type: "button", style: base.header, onClick: () => setCollapsed(!collapsed), children: [_jsx("span", { children: "Inspector" }), _jsx("span", { children: collapsed ? '◀' : '▼' })] }), !collapsed && selectedNode && (_jsx(NodeInspector, { node: selectedNode, updateNode: updateNodeHandler, deleteNode: deleteNodeHandler, basePath: basePath }))] }), _jsx("div", { style: { position: 'absolute', top: 8, left: 8, zIndex: 20 }, children: _jsx(EditorTree, { selectedId: selectedId, setSelectedId: setSelectedId, getPrefab: getPrefab, onReplacePrefab: onReplacePrefab, onImportPrefab: onImportPrefab, onUndo: onUndo, onRedo: onRedo, canUndo: canUndo, canRedo: canRedo }) })] });
|
|
38
37
|
}
|
|
39
38
|
function NodeInspector({ node, updateNode, deleteNode, basePath }) {
|
|
40
39
|
var _a;
|
|
41
40
|
const ALL_COMPONENTS = getAllComponentDefs();
|
|
42
41
|
const allKeys = Object.keys(ALL_COMPONENTS);
|
|
43
|
-
const available = allKeys.filter(k =>
|
|
42
|
+
const available = allKeys.filter(k => canAddComponentToNode(node, ALL_COMPONENTS[k], ALL_COMPONENTS));
|
|
44
43
|
const [preferredAddType, setAddType] = useState(available[0] || "");
|
|
45
44
|
const addType = available.includes(preferredAddType) ? preferredAddType : (available[0] || "");
|
|
46
|
-
return _jsxs("div", { style: inspector.content, children: [_jsxs("div", { style: base.section, children: [_jsxs("div", { style: { display: "flex", marginBottom: 4, alignItems: 'center', gap: 4 }, children: [_jsx("div", { style: { fontSize: 10, color: colors.textDim, wordBreak: 'break-all', background: colors.bgLight, padding: '2px 4px', flex: 1, fontFamily: 'monospace', minHeight: 18, boxSizing: 'border-box' }, children: node.id }), _jsx("button", { style: Object.assign(Object.assign(Object.assign({}, base.btn), base.btnDanger), { minWidth: 22, padding: '2px 4px' }), title: "Delete Node", onClick: deleteNode, children: "\u2715" })] }), _jsx("input", { style: base.input, value: (_a = node.name) !== null && _a !== void 0 ? _a : "", placeholder: 'Node name', onChange: e => updateNode(n => (Object.assign(Object.assign({}, n), { name: e.target.value }))) })] }), _jsxs("div", { style: base.section, children: [_jsx("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 4 }, children: _jsx("div", { style: base.label, children: "Components" }) }), node.components && Object.entries(node.components).map(([key, comp]) => {
|
|
45
|
+
return _jsxs("div", { style: inspector.content, children: [_jsxs("div", { style: base.section, children: [_jsxs("div", { style: { display: "flex", marginBottom: 4, alignItems: 'center', gap: 4 }, children: [_jsx("div", { style: { fontSize: 10, color: colors.textDim, wordBreak: 'break-all', background: colors.bgLight, padding: '2px 4px', flex: 1, fontFamily: 'monospace', minHeight: 18, boxSizing: 'border-box' }, children: node.id }), _jsx("button", { style: Object.assign(Object.assign(Object.assign({}, base.btn), base.btnDanger), { minWidth: 22, padding: '2px 4px' }), type: "button", title: "Delete Node", onClick: deleteNode, children: "\u2715" })] }), _jsx("input", { style: base.input, value: (_a = node.name) !== null && _a !== void 0 ? _a : "", placeholder: 'Node name', onChange: e => updateNode(n => (Object.assign(Object.assign({}, n), { name: e.target.value }))) })] }), _jsxs("div", { style: base.section, children: [_jsx("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 4 }, children: _jsx("div", { style: base.label, children: "Components" }) }), node.components && Object.entries(node.components).map(([key, comp]) => {
|
|
47
46
|
if (!comp)
|
|
48
47
|
return null;
|
|
49
48
|
const def = ALL_COMPONENTS[comp.type];
|
|
50
49
|
if (!def)
|
|
51
50
|
return _jsxs("div", { style: { color: colors.danger, fontSize: 11 }, children: ["Unknown: ", comp.type] }, key);
|
|
52
|
-
return (_jsxs("div", { style: componentCard.container, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 3 }, children: [_jsx("div", { style: { fontSize: 11, fontWeight: 500 }, children: key }), _jsx("button", { style: Object.assign(Object.assign({}, base.btn), { padding: '2px 4px', minWidth: 20 }), title: "Remove Component", onClick: () => updateNode(n => {
|
|
51
|
+
return (_jsxs("div", { style: componentCard.container, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 3 }, children: [_jsx("div", { style: { fontSize: 11, fontWeight: 500 }, children: key }), _jsx("button", { type: "button", style: Object.assign(Object.assign({}, base.btn), { padding: '2px 4px', minWidth: 20 }), title: "Remove Component", onClick: () => updateNode(n => {
|
|
53
52
|
var _a;
|
|
54
53
|
const _b = (_a = n.components) !== null && _a !== void 0 ? _a : {}, _c = key, _ = _b[_c], rest = __rest(_b, [typeof _c === "symbol" ? _c : _c + ""]);
|
|
55
54
|
return Object.assign(Object.assign({}, n), { components: rest });
|
|
56
55
|
}), children: "\u2715" })] }), def.Editor && (_jsx(def.Editor, { component: comp, node: node, onUpdate: (newProps) => updateNode(n => (Object.assign(Object.assign({}, n), { components: Object.assign(Object.assign({}, n.components), { [key]: Object.assign(Object.assign({}, comp), { properties: Object.assign(Object.assign({}, comp.properties), newProps) }) }) }))), basePath: basePath }))] }, key));
|
|
57
|
-
})] }), available.length > 0 && (_jsx("div", { children: _jsxs("div", { style: base.row, children: [_jsx("select", { style: Object.assign(Object.assign({}, base.input), { flex: 1, background: colors.bgInput, border: `1px solid ${colors.border}`, minHeight: 22 }), value: addType, onChange: e => setAddType(e.target.value), children: available.map(k => _jsx("option", { value: k, children: k }, k)) }), _jsx("button", { style: base.btn, disabled: !addType, onClick: () => {
|
|
56
|
+
})] }), available.length > 0 && (_jsx("div", { children: _jsxs("div", { style: base.row, children: [_jsx("select", { style: Object.assign(Object.assign({}, base.input), { flex: 1, background: colors.bgInput, border: `1px solid ${colors.border}`, minHeight: 22 }), value: addType, onChange: e => setAddType(e.target.value), children: available.map(k => _jsx("option", { value: k, children: k }, k)) }), _jsx("button", { type: "button", style: base.btn, disabled: !addType, onClick: () => {
|
|
58
57
|
if (!addType)
|
|
59
58
|
return;
|
|
60
59
|
const def = ALL_COMPONENTS[addType];
|
|
61
60
|
if (def) {
|
|
62
|
-
updateNode(n => (Object.assign(Object.assign({}, n), { components: Object.assign(Object.assign({}, n.components), { [
|
|
61
|
+
updateNode(n => (Object.assign(Object.assign({}, n), { components: Object.assign(Object.assign({}, n.components), { [getNextComponentKey(n, def.name)]: createComponentData(def.name) }) })));
|
|
63
62
|
}
|
|
64
63
|
}, title: "Add Component", children: "+" })] }) }))] });
|
|
65
64
|
}
|
|
@@ -2,6 +2,9 @@ import GameCanvas from "../../shared/GameCanvas";
|
|
|
2
2
|
import type { Prefab } from "./types";
|
|
3
3
|
import { PrefabEditorMode, type Scene } from "./PrefabRoot";
|
|
4
4
|
import type { ExportGLBOptions } from "./utils";
|
|
5
|
+
export declare function isAbsoluteAssetPath(path: string): boolean;
|
|
6
|
+
export declare function resolvePrefabAssetPath(basePath: string, file: string): string;
|
|
7
|
+
export declare function getPrefabAssetRef(assetRef: string, folder: "models" | "textures" | "sound"): string;
|
|
5
8
|
export interface PrefabEditorRef extends Scene {
|
|
6
9
|
save: () => Prefab;
|
|
7
10
|
load: (prefab: Prefab, options?: {
|