react-three-game 0.0.93 → 0.0.95

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 (28) hide show
  1. package/README.md +35 -77
  2. package/dist/index.d.ts +9 -6
  3. package/dist/index.js +5 -3
  4. package/dist/tools/prefabeditor/EditorTree.js +8 -12
  5. package/dist/tools/prefabeditor/EditorUI.js +4 -4
  6. package/dist/tools/prefabeditor/PrefabEditor.d.ts +12 -33
  7. package/dist/tools/prefabeditor/PrefabEditor.js +137 -161
  8. package/dist/tools/prefabeditor/PrefabRoot.d.ts +24 -12
  9. package/dist/tools/prefabeditor/PrefabRoot.js +117 -59
  10. package/dist/tools/prefabeditor/assetRuntime.d.ts +11 -17
  11. package/dist/tools/prefabeditor/assetRuntime.js +15 -15
  12. package/dist/tools/prefabeditor/components/CameraComponent.js +2 -2
  13. package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +2 -2
  14. package/dist/tools/prefabeditor/components/Input.js +5 -9
  15. package/dist/tools/prefabeditor/components/MaterialComponent.d.ts +5 -2
  16. package/dist/tools/prefabeditor/components/MaterialComponent.js +40 -24
  17. package/dist/tools/prefabeditor/components/ModelComponent.js +3 -5
  18. package/dist/tools/prefabeditor/components/PointLightComponent.js +2 -2
  19. package/dist/tools/prefabeditor/components/SoundComponent.js +2 -2
  20. package/dist/tools/prefabeditor/components/SpotLightComponent.js +2 -2
  21. package/dist/tools/prefabeditor/components/SpriteComponent.d.ts +8 -0
  22. package/dist/tools/prefabeditor/components/SpriteComponent.js +27 -0
  23. package/dist/tools/prefabeditor/components/index.js +22 -14
  24. package/dist/tools/prefabeditor/prefab.d.ts +1 -2
  25. package/dist/tools/prefabeditor/prefab.js +2 -3
  26. package/dist/tools/prefabeditor/prefabStore.d.ts +0 -6
  27. package/dist/tools/prefabeditor/prefabStore.js +1 -33
  28. package/package.json +49 -49
@@ -10,10 +10,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
11
11
  import { MapControls, TransformControls, useHelper } from "@react-three/drei";
12
12
  import GameCanvas from "../../shared/GameCanvas";
13
- import { useCallback, useEffect, useRef, useState, forwardRef, useImperativeHandle, createContext, useContext } from "react";
13
+ import { useCallback, useEffect, useMemo, useRef, useState, forwardRef, useImperativeHandle, createContext, useContext } from "react";
14
14
  import { BoxHelper } from "three";
15
15
  import { findComponentEntry } from "./types";
16
- import { PrefabRootInternal } from "./PrefabRoot";
16
+ import { PrefabEditorMode, PrefabRoot } from "./PrefabRoot";
17
17
  import EditorUI from "./EditorUI";
18
18
  import { base, toolbar } from "./styles";
19
19
  import { computeParentWorldMatrix, decompose, exportGLB as exportGLBFile, exportGLBData, focusCameraOnObject, regenerateIds } from "./utils";
@@ -37,12 +37,8 @@ function SelectionHelper({ object }) {
37
37
  useHelper(object ? objectRef : null, BoxHelper, "cyan");
38
38
  return null;
39
39
  }
40
- export var PrefabEditorMode;
41
- (function (PrefabEditorMode) {
42
- PrefabEditorMode["Edit"] = "edit";
43
- PrefabEditorMode["Play"] = "play";
44
- })(PrefabEditorMode || (PrefabEditorMode = {}));
45
40
  export const EditorContext = createContext(null);
41
+ export const EditorRefContext = createContext(null);
46
42
  export function useEditorContext() {
47
43
  const context = useContext(EditorContext);
48
44
  if (!context) {
@@ -50,6 +46,13 @@ export function useEditorContext() {
50
46
  }
51
47
  return context;
52
48
  }
49
+ export function useEditorRef() {
50
+ const editorRef = useContext(EditorRefContext);
51
+ if (!editorRef) {
52
+ throw new Error("useEditorRef must be used within PrefabEditor");
53
+ }
54
+ return editorRef;
55
+ }
53
56
  const MAX_HISTORY_LENGTH = 50;
54
57
  const HISTORY_DEBOUNCE_MS = 500;
55
58
  const DEFAULT_PREFAB = {
@@ -68,35 +71,68 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, mode: initialMode =
68
71
  const [prefabStore] = useState(() => createPrefabStore(startingPrefab));
69
72
  const [history, setHistory] = useState([startingPrefab]);
70
73
  const [historyIndex, setHistoryIndex] = useState(0);
71
- const changeOriginRef = useRef(null);
72
74
  const historyIndexRef = useRef(0);
75
+ const historyTimeoutRef = useRef(null);
73
76
  const prefabRootRef = useRef(null);
74
77
  const canvasRef = useRef(null);
75
78
  const controlsRef = useRef(null);
76
79
  const transformControlsRef = useRef(null);
77
80
  const onChangeRef = useRef(onChange);
78
- const sceneChangeListenersRef = useRef(new Set());
79
81
  const isEditMode = mode === PrefabEditorMode.Edit;
80
82
  const getPrefab = useCallback(() => denormalizePrefab(prefabStore.getState()), [prefabStore]);
81
83
  const getNode = useCallback((nodeId) => { var _a; return (_a = prefabStore.getState().nodesById[nodeId]) !== null && _a !== void 0 ? _a : null; }, [prefabStore]);
82
- const getSceneRootObject = useCallback(() => { var _a, _b; return (_b = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.root) !== null && _b !== void 0 ? _b : null; }, []);
83
- const getNodeObject = useCallback((nodeId) => { var _a, _b; return (_b = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.getNodeObject(nodeId)) !== null && _b !== void 0 ? _b : null; }, []);
84
- const getNodeHandle = useCallback((nodeId, kind) => { var _a, _b; return (_b = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.getNodeHandle(nodeId, kind)) !== null && _b !== void 0 ? _b : null; }, []);
85
- const updateNode = useCallback((nodeId, update) => {
86
- prefabStore.getState().updateNode(nodeId, update);
87
- }, [prefabStore]);
88
- const updateNodes = useCallback((updates) => {
89
- prefabStore.getState().updateNodes(updates);
90
- }, [prefabStore]);
91
- const deleteNode = useCallback((nodeId) => {
92
- prefabStore.getState().deleteNode(nodeId);
93
- }, [prefabStore]);
94
- const duplicateNode = useCallback((nodeId) => {
95
- return prefabStore.getState().duplicateNode(nodeId);
96
- }, [prefabStore]);
97
- const moveNode = useCallback((draggedId, targetId, position) => {
98
- prefabStore.getState().moveNode(draggedId, targetId, position);
99
- }, [prefabStore]);
84
+ const getRoot = useCallback(() => { var _a, _b; return (_b = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.root) !== null && _b !== void 0 ? _b : null; }, []);
85
+ const getObject = useCallback((nodeId) => { var _a, _b; return (_b = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.getObject(nodeId)) !== null && _b !== void 0 ? _b : null; }, []);
86
+ const getHandle = useCallback((nodeId, kind) => { var _a, _b; return (_b = (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.getHandle(nodeId, kind)) !== null && _b !== void 0 ? _b : null; }, []);
87
+ const scheduleHistory = useCallback((nextPrefab) => {
88
+ if (historyTimeoutRef.current) {
89
+ clearTimeout(historyTimeoutRef.current);
90
+ historyTimeoutRef.current = null;
91
+ }
92
+ historyTimeoutRef.current = setTimeout(() => {
93
+ const currentHistoryIndex = historyIndexRef.current;
94
+ setHistory(prev => {
95
+ const nextHistory = [...prev.slice(0, currentHistoryIndex + 1), nextPrefab];
96
+ return nextHistory.length > MAX_HISTORY_LENGTH ? nextHistory.slice(1) : nextHistory;
97
+ });
98
+ const nextHistoryIndex = Math.min(currentHistoryIndex + 1, MAX_HISTORY_LENGTH - 1);
99
+ historyIndexRef.current = nextHistoryIndex;
100
+ setHistoryIndex(nextHistoryIndex);
101
+ historyTimeoutRef.current = null;
102
+ }, HISTORY_DEBOUNCE_MS);
103
+ }, []);
104
+ const mutate = useCallback((run, pushHistory = isEditMode) => {
105
+ var _a;
106
+ const before = prefabStore.getState();
107
+ const result = run(before);
108
+ const after = prefabStore.getState();
109
+ if (after === before)
110
+ return result;
111
+ const prefab = denormalizePrefab(after);
112
+ (_a = onChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onChangeRef, prefab);
113
+ if (pushHistory)
114
+ scheduleHistory(prefab);
115
+ return result;
116
+ }, [isEditMode, prefabStore, scheduleHistory]);
117
+ const update = useCallback((id, fn) => {
118
+ mutate(s => s.updateNode(id, fn));
119
+ }, [mutate]);
120
+ const remove = useCallback((id) => {
121
+ mutate(s => s.deleteNode(id));
122
+ }, [mutate]);
123
+ const duplicate = useCallback((id) => {
124
+ return mutate(s => s.duplicateNode(id));
125
+ }, [mutate]);
126
+ const move = useCallback((draggedId, targetId, position) => {
127
+ mutate(s => s.moveNode(draggedId, targetId, position));
128
+ }, [mutate]);
129
+ const add = useCallback((node, parentId) => {
130
+ mutate(s => s.addChild(parentId !== null && parentId !== void 0 ? parentId : s.rootId, node));
131
+ return node;
132
+ }, [mutate]);
133
+ const replace = useCallback((prefab) => {
134
+ mutate(s => s.replacePrefab(prefab), false);
135
+ }, [mutate]);
100
136
  onChangeRef.current = onChange;
101
137
  const setSelection = useCallback((nodeId) => {
102
138
  const nextNode = nodeId ? prefabStore.getState().nodesById[nodeId] : null;
@@ -122,10 +158,14 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, mode: initialMode =
122
158
  updateMode(initialMode);
123
159
  }, [initialMode, updateMode]);
124
160
  const loadPrefab = useCallback((prefab, options) => {
125
- var _a;
126
- changeOriginRef.current = (options === null || options === void 0 ? void 0 : options.notifyChange) === false ? "replace-silent" : "replace";
161
+ var _a, _b;
127
162
  (_a = transformControlsRef.current) === null || _a === void 0 ? void 0 : _a.detach();
163
+ const before = prefabStore.getState();
128
164
  prefabStore.getState().replacePrefab(prefab);
165
+ const after = prefabStore.getState();
166
+ if (after !== before && (options === null || options === void 0 ? void 0 : options.notifyChange) !== false) {
167
+ (_b = onChangeRef.current) === null || _b === void 0 ? void 0 : _b.call(onChangeRef, prefab);
168
+ }
129
169
  if (options === null || options === void 0 ? void 0 : options.resetHistory) {
130
170
  setSelectedId(null);
131
171
  setHistory([prefab]);
@@ -141,47 +181,12 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, mode: initialMode =
141
181
  loadPrefab(initialPrefab, { resetHistory: true, notifyChange: false });
142
182
  }, [initialPrefab, loadPrefab]);
143
183
  useEffect(() => {
144
- let historyTimeout = null;
145
- let lastRevision = prefabStore.getState().revision;
146
- const unsubscribe = prefabStore.subscribe((state) => {
147
- var _a;
148
- if (state.revision === lastRevision) {
149
- return;
150
- }
151
- lastRevision = state.revision;
152
- sceneChangeListenersRef.current.forEach(listener => listener(state.revision));
153
- const nextPrefab = denormalizePrefab(state);
154
- const changeOrigin = changeOriginRef.current;
155
- if (changeOrigin !== "replace-silent") {
156
- (_a = onChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onChangeRef, nextPrefab);
157
- }
158
- if (historyTimeout) {
159
- clearTimeout(historyTimeout);
160
- historyTimeout = null;
161
- }
162
- if (changeOrigin || !isEditMode) {
163
- changeOriginRef.current = null;
164
- return;
165
- }
166
- historyTimeout = setTimeout(() => {
167
- const currentHistoryIndex = historyIndexRef.current;
168
- setHistory(prev => {
169
- const nextHistory = [...prev.slice(0, currentHistoryIndex + 1), nextPrefab];
170
- return nextHistory.length > MAX_HISTORY_LENGTH ? nextHistory.slice(1) : nextHistory;
171
- });
172
- const nextHistoryIndex = Math.min(currentHistoryIndex + 1, MAX_HISTORY_LENGTH - 1);
173
- historyIndexRef.current = nextHistoryIndex;
174
- setHistoryIndex(nextHistoryIndex);
175
- historyTimeout = null;
176
- }, HISTORY_DEBOUNCE_MS);
177
- });
178
184
  return () => {
179
- if (historyTimeout) {
180
- clearTimeout(historyTimeout);
185
+ if (historyTimeoutRef.current) {
186
+ clearTimeout(historyTimeoutRef.current);
181
187
  }
182
- unsubscribe();
183
188
  };
184
- }, [isEditMode, prefabStore]);
189
+ }, []);
185
190
  useEffect(() => {
186
191
  if (!selectedId)
187
192
  return;
@@ -192,42 +197,19 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, mode: initialMode =
192
197
  });
193
198
  return () => unsubscribe();
194
199
  }, [prefabStore, selectedId]);
195
- const selectedObject = selectedId ? getNodeObject(selectedId) : null;
200
+ const selectedObject = selectedId ? getObject(selectedId) : null;
196
201
  const transformObject = isEditMode && selectedObject
197
- && isObjectAttachedToRoot(getSceneRootObject(), selectedObject)
202
+ && isObjectAttachedToRoot(getRoot(), selectedObject)
198
203
  ? selectedObject
199
204
  : null;
200
- const addNode = useCallback((node, options) => {
201
- var _a;
202
- const { addChild, rootId } = prefabStore.getState();
203
- addChild((_a = options === null || options === void 0 ? void 0 : options.parentId) !== null && _a !== void 0 ? _a : rootId, node);
204
- if ((options === null || options === void 0 ? void 0 : options.select) !== false) {
205
- setSelection(node.id);
206
- }
207
- return node;
208
- }, [prefabStore, setSelection]);
209
205
  const importPrefab = useCallback((prefab) => {
210
- addNode(regenerateIds(prefab.root), { select: false });
211
- }, [addNode]);
212
- const addModel = useCallback((path, model, options) => {
213
- var _a;
214
- const node = createModelNode(path, options === null || options === void 0 ? void 0 : options.name);
215
- addNode(node, options);
216
- (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.addModel(path, model);
217
- return node;
218
- }, [addNode]);
219
- const addTexture = useCallback((path, texture, options) => {
220
- var _a;
221
- const node = createImageNode(path, options === null || options === void 0 ? void 0 : options.name);
222
- addNode(node, options);
223
- (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.addTexture(path, texture);
224
- return node;
225
- }, [addNode]);
206
+ add(regenerateIds(prefab.root));
207
+ }, [add]);
226
208
  const applyHistory = (index) => {
227
- var _a;
228
- changeOriginRef.current = "history";
209
+ var _a, _b;
229
210
  (_a = transformControlsRef.current) === null || _a === void 0 ? void 0 : _a.detach();
230
211
  prefabStore.getState().replacePrefab(history[index]);
212
+ (_b = onChangeRef.current) === null || _b === void 0 ? void 0 : _b.call(onChangeRef, history[index]);
231
213
  historyIndexRef.current = index;
232
214
  setHistoryIndex(index);
233
215
  setSelectedId(prev => prev && prefabStore.getState().nodesById[prev] ? prev : null);
@@ -275,36 +257,36 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, mode: initialMode =
275
257
  }), [setSelection]);
276
258
  const handleExportGLB = useCallback((...args_1) => __awaiter(void 0, [...args_1], void 0, function* (options = {}) {
277
259
  yield clearSelection();
278
- const rootObject = getSceneRootObject();
260
+ const rootObject = getRoot();
279
261
  if (!rootObject)
280
262
  return;
281
263
  return exportGLBFile(rootObject, Object.assign({ filename: `${prefabStore.getState().prefabName || 'prefab'}.glb` }, options));
282
- }), [clearSelection, getSceneRootObject, prefabStore]);
264
+ }), [clearSelection, getRoot, prefabStore]);
283
265
  const handleExportGLBData = useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
284
266
  yield clearSelection();
285
- const rootObject = getSceneRootObject();
267
+ const rootObject = getRoot();
286
268
  if (!rootObject)
287
269
  return;
288
270
  return exportGLBData(rootObject);
289
- }), [clearSelection, getSceneRootObject]);
271
+ }), [clearSelection, getRoot]);
290
272
  const handleFocusNode = useCallback((nodeId) => {
291
- const object = getNodeObject(nodeId);
273
+ const object = getObject(nodeId);
292
274
  const controls = controlsRef.current;
293
275
  const camera = controls === null || controls === void 0 ? void 0 : controls.object;
294
276
  if (!object || !controls || !camera)
295
277
  return;
296
278
  focusCameraOnObject(object, camera, controls.target, () => { var _a; return (_a = controls.update) === null || _a === void 0 ? void 0 : _a.call(controls); });
297
- }, [getNodeObject]);
279
+ }, [getObject]);
298
280
  const handleTransformChange = () => {
299
281
  if (!selectedId)
300
282
  return;
301
- const object = getNodeObject(selectedId);
283
+ const object = getObject(selectedId);
302
284
  if (!object)
303
285
  return;
304
286
  const parentWorld = computeParentWorldMatrix(prefabStore.getState(), selectedId);
305
287
  const local = parentWorld.clone().invert().multiply(object.matrixWorld);
306
288
  const { position, rotation, scale } = decompose(local);
307
- prefabStore.getState().updateNode(selectedId, node => {
289
+ update(selectedId, node => {
308
290
  var _a;
309
291
  const entry = findComponentEntry(node, "Transform");
310
292
  const key = (_a = entry === null || entry === void 0 ? void 0 : entry[0]) !== null && _a !== void 0 ? _a : "transform";
@@ -327,16 +309,17 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, mode: initialMode =
327
309
  e.preventDefault();
328
310
  e.stopPropagation();
329
311
  const files = ((_a = e.dataTransfer) === null || _a === void 0 ? void 0 : _a.files) ? Array.from(e.dataTransfer.files) : [];
312
+ const scene = prefabRootRef.current;
330
313
  void loadFiles(files, {
331
314
  onModelLoaded: (model, filename) => {
332
- addModel(`models/${filename}`, model, {
333
- name: filename.replace(/\.[^.]+$/, '')
334
- });
315
+ const path = `models/${filename}`;
316
+ scene === null || scene === void 0 ? void 0 : scene.addModel(path, model);
317
+ add(createModelNode(path, filename.replace(/\.[^.]+$/, '')));
335
318
  },
336
319
  onTextureLoaded: (texture, filename) => {
337
- addTexture(`textures/${filename}`, texture, {
338
- name: filename.replace(/\.[^.]+$/, '')
339
- });
320
+ const path = `textures/${filename}`;
321
+ scene === null || scene === void 0 ? void 0 : scene.addTexture(path, texture);
322
+ add(createImageNode(path, filename.replace(/\.[^.]+$/, '')));
340
323
  },
341
324
  onLoadError: error => {
342
325
  console.error('Drop asset error:', error);
@@ -349,65 +332,58 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, mode: initialMode =
349
332
  window.removeEventListener('dragover', handleDragOver);
350
333
  window.removeEventListener('drop', handleDrop);
351
334
  };
352
- }, [addModel, addTexture, isEditMode, enableWindowDrop]);
353
- useImperativeHandle(ref, () => ({
335
+ }, [add, isEditMode, enableWindowDrop]);
336
+ const sceneValue = useMemo(() => ({
354
337
  get root() {
355
- return getSceneRootObject();
356
- },
357
- getNode,
358
- getNodeObject,
359
- getNodeHandle,
360
- onSceneChange: (listener) => {
361
- sceneChangeListenersRef.current.add(listener);
362
- return () => {
363
- sceneChangeListenersRef.current.delete(listener);
364
- };
338
+ return getRoot();
365
339
  },
366
- screenshot: handleScreenshot,
367
- exportGLB: handleExportGLB,
368
- exportGLBData: handleExportGLBData,
369
- clearSelection,
370
- save: getPrefab,
371
- load: loadPrefab,
372
- updateNode,
373
- updateNodes,
374
- deleteNode,
375
- duplicateNode,
376
- moveNode,
377
- addNode,
378
- addModel,
379
- addTexture
380
- }), [addModel, addNode, addTexture, clearSelection, deleteNode, duplicateNode, getNode, getNodeHandle, getNodeObject, getPrefab, getSceneRootObject, handleExportGLB, handleExportGLBData, handleScreenshot, loadPrefab, moveNode, updateNode, updateNodes]);
381
- const content = (_jsxs(_Fragment, { children: [isEditMode ? _jsx("gridHelper", { args: [10, 10], position: [0, -1, 0] }) : null, _jsx(PrefabRootInternal, { ref: prefabRootRef, store: prefabStore, editMode: isEditMode, selectedId: selectedId, onSelect: setSelection, basePath: basePath }), children] }));
340
+ mode,
341
+ get: getNode,
342
+ getObject,
343
+ getHandle,
344
+ add,
345
+ update,
346
+ remove,
347
+ duplicate,
348
+ move,
349
+ replace,
350
+ addModel: (path, model) => { var _a; return (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.addModel(path, model); },
351
+ addTexture: (path, texture) => { var _a; return (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.addTexture(path, texture); },
352
+ addSound: (path, sound) => { var _a; return (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.addSound(path, sound); },
353
+ }), [add, duplicate, getHandle, getNode, getObject, getRoot, mode, move, remove, replace, update]);
354
+ const editorRefValue = useMemo(() => (Object.assign(Object.assign({}, sceneValue), { save: getPrefab, load: loadPrefab, undo,
355
+ redo, screenshot: handleScreenshot, exportGLB: handleExportGLB, exportGLBData: handleExportGLBData, clearSelection })), [clearSelection, getPrefab, handleExportGLB, handleExportGLBData, handleScreenshot, loadPrefab, redo, sceneValue, undo]);
356
+ useImperativeHandle(ref, () => editorRefValue, [editorRefValue]);
357
+ const content = (_jsxs(_Fragment, { children: [isEditMode ? _jsx("gridHelper", { args: [10, 10], position: [0, -1, 0] }) : null, _jsx(PrefabRoot, { ref: prefabRootRef, store: prefabStore, editMode: isEditMode, selectedId: selectedId, onSelect: setSelection, basePath: basePath, children: children })] }));
382
358
  const handleCanvasCreated = useCallback((state) => {
383
359
  var _a;
384
360
  canvasRef.current = state.gl.domElement;
385
361
  (_a = canvasProps === null || canvasProps === void 0 ? void 0 : canvasProps.onCreated) === null || _a === void 0 ? void 0 : _a.call(canvasProps, state);
386
362
  }, [canvasProps]);
387
- return _jsx(PrefabStoreProvider, { store: prefabStore, children: _jsxs(EditorContext.Provider, { value: {
388
- mode,
389
- setMode: updateMode,
390
- transformMode,
391
- setTransformMode,
392
- scaleSnap,
393
- setScaleSnap,
394
- positionSnap,
395
- setPositionSnap,
396
- rotationSnap,
397
- setRotationSnap,
398
- onFocusNode: isEditMode ? handleFocusNode : undefined,
399
- onScreenshot: handleScreenshot,
400
- onExportGLB: handleExportGLB
401
- }, children: [_jsxs(GameCanvas, Object.assign({ camera: { position: [0, 5, 15] } }, canvasProps, { onCreated: handleCanvasCreated, onPointerMissed: isEditMode
402
- ? (event) => {
403
- var _a, _b, _c, _d;
404
- const button = (_c = (_a = event.button) !== null && _a !== void 0 ? _a : (_b = event.sourceEvent) === null || _b === void 0 ? void 0 : _b.button) !== null && _c !== void 0 ? _c : 0;
405
- if (button === 0 && selectedId) {
406
- setSelection(null);
363
+ return _jsx(PrefabStoreProvider, { store: prefabStore, children: _jsx(EditorRefContext.Provider, { value: editorRefValue, children: _jsxs(EditorContext.Provider, { value: {
364
+ mode,
365
+ setMode: updateMode,
366
+ transformMode,
367
+ setTransformMode,
368
+ scaleSnap,
369
+ setScaleSnap,
370
+ positionSnap,
371
+ setPositionSnap,
372
+ rotationSnap,
373
+ setRotationSnap,
374
+ onFocusNode: isEditMode ? handleFocusNode : undefined,
375
+ onScreenshot: handleScreenshot,
376
+ onExportGLB: handleExportGLB
377
+ }, children: [_jsxs(GameCanvas, Object.assign({ camera: { position: [0, 5, 15] } }, canvasProps, { onCreated: handleCanvasCreated, onPointerMissed: isEditMode
378
+ ? (event) => {
379
+ var _a, _b, _c, _d;
380
+ const button = (_c = (_a = event.button) !== null && _a !== void 0 ? _a : (_b = event.sourceEvent) === null || _b === void 0 ? void 0 : _b.button) !== null && _c !== void 0 ? _c : 0;
381
+ if (button === 0 && selectedId) {
382
+ setSelection(null);
383
+ }
384
+ (_d = canvasProps === null || canvasProps === void 0 ? void 0 : canvasProps.onPointerMissed) === null || _d === void 0 ? void 0 : _d.call(canvasProps, event);
407
385
  }
408
- (_d = canvasProps === null || canvasProps === void 0 ? void 0 : canvasProps.onPointerMissed) === null || _d === void 0 ? void 0 : _d.call(canvasProps, event);
409
- }
410
- : canvasProps === null || canvasProps === void 0 ? void 0 : canvasProps.onPointerMissed, children: [content, isEditMode ? _jsx(SelectionHelper, { object: transformObject }) : null, isEditMode && (_jsxs(_Fragment, { children: [_jsx(MapControls, { ref: controlsRef, enableDamping: false, makeDefault: true }), transformObject && (_jsx(TransformControls, { ref: transformControlsRef, object: transformObject, mode: transformMode, space: "local", onObjectChange: handleTransformChange, translationSnap: positionSnap > 0 ? positionSnap : undefined, rotationSnap: rotationSnap > 0 ? rotationSnap : undefined, scaleSnap: scaleSnap > 0 ? scaleSnap : undefined }, `transform-${selectedId}-${transformMode}-${positionSnap}-${rotationSnap}-${scaleSnap}`))] }))] })), showUI && (_jsxs(_Fragment, { children: [_jsxs("div", { style: toolbar.panel, children: [_jsx("button", { style: base.btn, onClick: toggleMode, children: isEditMode ? "▶" : "⏸" }), uiPlugins] }), isEditMode && (_jsx(EditorUI, { selectedId: selectedId, setSelectedId: setSelection, getPrefab: getPrefab, onReplacePrefab: (prefab) => loadPrefab(prefab, { resetHistory: true }), onImportPrefab: importPrefab, basePath: basePath, onUndo: undo, onRedo: redo, canUndo: historyIndex > 0, canRedo: historyIndex < history.length - 1 }))] }))] }) });
386
+ : canvasProps === null || canvasProps === void 0 ? void 0 : canvasProps.onPointerMissed, children: [content, isEditMode ? _jsx(SelectionHelper, { object: transformObject }) : null, isEditMode && (_jsxs(_Fragment, { children: [_jsx(MapControls, { ref: controlsRef, enableDamping: false, makeDefault: true }), transformObject && (_jsx(TransformControls, { ref: transformControlsRef, object: transformObject, mode: transformMode, space: "local", onObjectChange: handleTransformChange, translationSnap: positionSnap > 0 ? positionSnap : undefined, rotationSnap: rotationSnap > 0 ? rotationSnap : undefined, scaleSnap: scaleSnap > 0 ? scaleSnap : undefined }, `transform-${selectedId}-${transformMode}-${positionSnap}-${rotationSnap}-${scaleSnap}`))] }))] })), showUI && (_jsxs(_Fragment, { children: [_jsxs("div", { style: toolbar.panel, children: [_jsx("button", { style: base.btn, onClick: toggleMode, children: isEditMode ? "▶" : "⏸" }), uiPlugins] }), isEditMode && (_jsx(EditorUI, { selectedId: selectedId, setSelectedId: setSelection, getPrefab: getPrefab, onReplacePrefab: (prefab) => loadPrefab(prefab, { resetHistory: true }), onImportPrefab: importPrefab, basePath: basePath, onUndo: undo, onRedo: redo, canUndo: historyIndex > 0, canRedo: historyIndex < history.length - 1 }))] }))] }) }) });
411
387
  });
412
388
  PrefabEditor.displayName = "PrefabEditor";
413
389
  export default PrefabEditor;
@@ -1,31 +1,43 @@
1
- import { type ForwardRefExoticComponent, type RefAttributes } from "react";
2
1
  import { Matrix4, Object3D, Texture } from "three";
3
2
  import { ThreeEvent } from "@react-three/fiber";
4
3
  import { GameObject as GameObjectType, Prefab } from "./types";
5
4
  import { LoadedModels } from "../dragdrop";
6
5
  import { PrefabStoreApi } from "./prefabStore";
7
- export interface PrefabRootRef {
6
+ export declare enum PrefabEditorMode {
7
+ Edit = "edit",
8
+ Play = "play"
9
+ }
10
+ export type PrefabNode = Omit<GameObjectType, "children">;
11
+ export interface Scene {
8
12
  root: Object3D | null;
9
- getNodeObject: (nodeId: string) => Object3D | null;
10
- getNodeHandle: <T = unknown>(nodeId: string, kind: string) => T | null;
11
- addModel: (path: string, model: Object3D) => void;
12
- addTexture: (path: string, texture: Texture) => void;
13
- addSound: (path: string, sound: AudioBuffer) => void;
13
+ mode: PrefabEditorMode;
14
+ get(id: string): GameObjectType | null;
15
+ getObject(id: string): Object3D | null;
16
+ getHandle<T = unknown>(id: string, kind: string): T | null;
17
+ add(node: GameObjectType, parentId?: string): GameObjectType;
18
+ update(id: string, fn: (node: PrefabNode) => PrefabNode): void;
19
+ remove(id: string): void;
20
+ duplicate(id: string): string | null;
21
+ move(draggedId: string, targetId: string, position: "before" | "inside"): void;
22
+ replace(prefab: Prefab): void;
23
+ addModel(path: string, model: Object3D): void;
24
+ addTexture(path: string, texture: Texture): void;
25
+ addSound(path: string, sound: AudioBuffer): void;
14
26
  }
27
+ export declare const SceneContext: import("react").Context<Scene | null>;
28
+ export declare function useScene(): Scene;
15
29
  export interface PrefabRootProps {
16
30
  editMode?: boolean;
17
31
  data?: Prefab;
32
+ store?: PrefabStoreApi;
18
33
  selectedId?: string | null;
19
34
  onSelect?: (id: string | null) => void;
20
35
  onClick?: (event: ThreeEvent<PointerEvent>, node: GameObjectType) => void;
21
36
  onEditNodeClick?: (event: ThreeEvent<PointerEvent>, node: GameObjectType) => void;
22
37
  basePath?: string;
38
+ children?: React.ReactNode;
23
39
  }
24
- interface PrefabRootInternalProps extends PrefabRootProps {
25
- store?: PrefabStoreApi;
26
- }
27
- export declare const PrefabRootInternal: ForwardRefExoticComponent<PrefabRootInternalProps & RefAttributes<PrefabRootRef>>;
28
- export declare const PrefabRoot: ForwardRefExoticComponent<PrefabRootProps & RefAttributes<PrefabRootRef>>;
40
+ export declare const PrefabRoot: import("react").ForwardRefExoticComponent<PrefabRootProps & import("react").RefAttributes<Scene>>;
29
41
  export declare function GameObjectRenderer(props: RendererProps): import("react/jsx-runtime").JSX.Element | null;
30
42
  interface RendererProps {
31
43
  nodeId: string;