react-three-game 0.0.67 → 0.0.69

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 (35) hide show
  1. package/README.md +109 -304
  2. package/dist/index.d.ts +15 -8
  3. package/dist/index.js +11 -8
  4. package/dist/shared/GameCanvas.d.ts +1 -2
  5. package/dist/tools/prefabeditor/EditorContext.d.ts +2 -2
  6. package/dist/tools/prefabeditor/EditorTree.d.ts +6 -6
  7. package/dist/tools/prefabeditor/EditorTree.js +92 -142
  8. package/dist/tools/prefabeditor/EditorTreeMenus.d.ts +4 -11
  9. package/dist/tools/prefabeditor/EditorTreeMenus.js +16 -25
  10. package/dist/tools/prefabeditor/EditorUI.d.ts +5 -5
  11. package/dist/tools/prefabeditor/EditorUI.js +14 -11
  12. package/dist/tools/prefabeditor/GameEvents.d.ts +0 -30
  13. package/dist/tools/prefabeditor/GameEvents.js +0 -7
  14. package/dist/tools/prefabeditor/PrefabEditor.d.ts +12 -13
  15. package/dist/tools/prefabeditor/PrefabEditor.js +168 -138
  16. package/dist/tools/prefabeditor/PrefabRoot.d.ts +8 -5
  17. package/dist/tools/prefabeditor/PrefabRoot.js +141 -123
  18. package/dist/tools/prefabeditor/components/AmbientLightComponent.js +3 -3
  19. package/dist/tools/prefabeditor/components/CameraComponent.js +2 -2
  20. package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +2 -2
  21. package/dist/tools/prefabeditor/components/ModelComponent.js +0 -1
  22. package/dist/tools/prefabeditor/components/SpotLightComponent.js +2 -2
  23. package/dist/tools/prefabeditor/components/TextComponent.js +2 -3
  24. package/dist/tools/prefabeditor/components/TransformComponent.js +9 -14
  25. package/dist/tools/prefabeditor/prefabStore.d.ts +42 -0
  26. package/dist/tools/prefabeditor/prefabStore.js +347 -0
  27. package/dist/tools/prefabeditor/sceneApi.d.ts +44 -0
  28. package/dist/tools/prefabeditor/sceneApi.js +161 -0
  29. package/dist/tools/prefabeditor/styles.d.ts +2 -1
  30. package/dist/tools/prefabeditor/styles.js +2 -12
  31. package/dist/tools/prefabeditor/utils.d.ts +15 -36
  32. package/dist/tools/prefabeditor/utils.js +36 -162
  33. package/package.json +4 -3
  34. package/dist/tools/prefabeditor/EventSystem.d.ts +0 -7
  35. package/dist/tools/prefabeditor/EventSystem.js +0 -23
@@ -1,79 +1,26 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useState } from 'react';
2
+ import { memo, useCallback, useState } from 'react';
3
3
  import { getComponent } from './components/ComponentRegistry';
4
4
  import { base, colors, tree } from './styles';
5
- import { findNode, findParent, deleteNode, cloneNode, updateNodeById } from './utils';
6
5
  import { useEditorContext } from './EditorContext';
7
6
  import { Dropdown } from './Dropdown';
8
- import { FileMenu, MenuTriggerButton, TreeContextMenu, TreeNodeMenu } from './EditorTreeMenus';
9
- function moveNode(root, draggedId, targetId, position) {
10
- const draggedNode = findNode(root, draggedId);
11
- const oldParent = findParent(root, draggedId);
12
- if (!draggedNode || !oldParent || findNode(draggedNode, targetId)) {
13
- return root;
14
- }
15
- if (position === 'before') {
16
- const targetParent = findParent(root, targetId);
17
- if (!(targetParent === null || targetParent === void 0 ? void 0 : targetParent.children))
18
- return root;
19
- if (targetParent.id === oldParent.id) {
20
- const siblings = targetParent.children.filter(child => child.id !== draggedId);
21
- const targetIndex = siblings.findIndex(child => child.id === targetId);
22
- if (targetIndex === -1)
23
- return root;
24
- siblings.splice(targetIndex, 0, draggedNode);
25
- return updateNodeById(root, targetParent.id, parent => (Object.assign(Object.assign({}, parent), { children: siblings })));
26
- }
27
- const rootWithoutDragged = updateNodeById(root, oldParent.id, parent => {
28
- var _a;
29
- return (Object.assign(Object.assign({}, parent), { children: ((_a = parent.children) !== null && _a !== void 0 ? _a : []).filter(child => child.id !== draggedId) }));
30
- });
31
- return updateNodeById(rootWithoutDragged, targetParent.id, parent => {
32
- var _a;
33
- const children = [...((_a = parent.children) !== null && _a !== void 0 ? _a : [])];
34
- const targetIndex = children.findIndex(child => child.id === targetId);
35
- if (targetIndex === -1)
36
- return parent;
37
- children.splice(targetIndex, 0, draggedNode);
38
- return Object.assign(Object.assign({}, parent), { children });
39
- });
40
- }
41
- const rootWithoutDragged = updateNodeById(root, oldParent.id, parent => {
42
- var _a;
43
- return (Object.assign(Object.assign({}, parent), { children: ((_a = parent.children) !== null && _a !== void 0 ? _a : []).filter(child => child.id !== draggedId) }));
44
- });
45
- return updateNodeById(rootWithoutDragged, targetId, target => {
46
- var _a;
47
- return (Object.assign(Object.assign({}, target), { children: [...((_a = target.children) !== null && _a !== void 0 ? _a : []), draggedNode] }));
48
- });
49
- }
50
- function duplicateNodeBelow(root, nodeId) {
51
- const node = findNode(root, nodeId);
52
- const parent = findParent(root, nodeId);
53
- if (!node || !parent)
54
- return null;
55
- const duplicate = cloneNode(node);
56
- const nextRoot = updateNodeById(root, parent.id, currentParent => (Object.assign(Object.assign({}, currentParent), { children: (() => {
57
- var _a;
58
- const children = [...((_a = currentParent.children) !== null && _a !== void 0 ? _a : [])];
59
- const index = children.findIndex(child => child.id === nodeId);
60
- if (index === -1)
61
- return [...children, duplicate];
62
- children.splice(index + 1, 0, duplicate);
63
- return children;
64
- })() })));
65
- return { root: nextRoot, duplicatedId: duplicate.id };
66
- }
67
- export default function EditorTree({ prefabData, setPrefabData, selectedId, setSelectedId, onUndo, onRedo, canUndo, canRedo }) {
7
+ import { FileMenu, TreeContextMenu, TreeNodeMenu } from './EditorTreeMenus';
8
+ import { usePrefabChildIds, usePrefabNode, usePrefabRootId, usePrefabStore, usePrefabStoreApi } from './prefabStore';
9
+ export default function EditorTree({ selectedId, setSelectedId, getPrefab, onReplacePrefab, onImportPrefab, onUndo, onRedo, canUndo, canRedo }) {
68
10
  const { onFocusNode } = useEditorContext();
11
+ const rootId = usePrefabRootId();
12
+ const store = usePrefabStoreApi();
13
+ const addChild = usePrefabStore(state => state.addChild);
14
+ const duplicateNode = usePrefabStore(state => state.duplicateNode);
15
+ const deleteNode = usePrefabStore(state => state.deleteNode);
16
+ const toggleNodeFlag = usePrefabStore(state => state.toggleNodeFlag);
17
+ const moveNode = usePrefabStore(state => state.moveNode);
69
18
  const [draggedId, setDraggedId] = useState(null);
70
19
  const [dropTarget, setDropTarget] = useState(null);
71
20
  const [collapsedIds, setCollapsedIds] = useState(new Set());
72
21
  const [collapsed, setCollapsed] = useState(false);
73
22
  const [searchQuery, setSearchQuery] = useState('');
74
23
  const [contextMenu, setContextMenu] = useState(null);
75
- if (!prefabData || !setPrefabData)
76
- return null;
77
24
  const toggleCollapse = (e, id) => {
78
25
  e.stopPropagation();
79
26
  setCollapsedIds(prev => {
@@ -94,43 +41,32 @@ export default function EditorTree({ prefabData, setPrefabData, selectedId, setS
94
41
  }
95
42
  }
96
43
  };
97
- setPrefabData(prev => (Object.assign(Object.assign({}, prev), { root: updateNodeById(prev.root, parentId, parent => {
98
- var _a;
99
- return (Object.assign(Object.assign({}, parent), { children: [...((_a = parent.children) !== null && _a !== void 0 ? _a : []), newNode] }));
100
- }) })));
44
+ addChild(parentId, newNode);
101
45
  setSelectedId(newNode.id);
102
46
  };
103
47
  const handleDuplicate = (nodeId) => {
104
- if (nodeId === prefabData.root.id)
48
+ if (nodeId === rootId)
105
49
  return;
106
- setPrefabData(prev => {
107
- const result = duplicateNodeBelow(prev.root, nodeId);
108
- if (!result)
109
- return prev;
110
- setSelectedId(result.duplicatedId);
111
- return Object.assign(Object.assign({}, prev), { root: result.root });
112
- });
50
+ const duplicatedId = duplicateNode(nodeId);
51
+ if (duplicatedId)
52
+ setSelectedId(duplicatedId);
113
53
  };
114
54
  const handleDelete = (nodeId) => {
115
- if (nodeId === prefabData.root.id)
55
+ if (nodeId === rootId)
116
56
  return;
117
- setPrefabData(prev => (Object.assign(Object.assign({}, prev), { root: deleteNode(prev.root, nodeId) })));
57
+ deleteNode(nodeId);
118
58
  if (selectedId === nodeId)
119
59
  setSelectedId(null);
120
60
  };
121
- const toggleNodeFlag = (nodeId, key) => {
122
- setPrefabData(prev => (Object.assign(Object.assign({}, prev), { root: updateNodeById(prev.root, nodeId, node => (Object.assign(Object.assign({}, node), { [key]: !node[key] }))) })));
123
- };
124
61
  const handleToggleDisabled = (nodeId) => {
125
62
  toggleNodeFlag(nodeId, 'disabled');
126
63
  };
127
64
  const handleToggleLocked = (nodeId) => {
128
65
  var _a;
129
- const willLock = !((_a = findNode(prefabData.root, nodeId)) === null || _a === void 0 ? void 0 : _a.locked);
66
+ const willLock = !((_a = store.getState().nodesById[nodeId]) === null || _a === void 0 ? void 0 : _a.locked);
130
67
  toggleNodeFlag(nodeId, 'locked');
131
- if (willLock && selectedId === nodeId) {
68
+ if (willLock && selectedId === nodeId)
132
69
  setSelectedId(null);
133
- }
134
70
  };
135
71
  const closeContextMenu = () => setContextMenu(null);
136
72
  const openContextMenu = (nodeId, x, y) => {
@@ -143,10 +79,10 @@ export default function EditorTree({ prefabData, setPrefabData, selectedId, setS
143
79
  };
144
80
  const renderTreeNodeMenu = (nodeId, isRoot, onClose) => {
145
81
  var _a;
146
- return (_jsx(TreeNodeMenu, { isRoot: isRoot, nodeId: nodeId, locked: (_a = findNode(prefabData.root, nodeId)) === null || _a === void 0 ? void 0 : _a.locked, onAddChild: handleAddChild, onFocus: handleFocus, onToggleLock: isRoot ? undefined : handleToggleLocked, onDuplicate: isRoot ? undefined : handleDuplicate, onDelete: isRoot ? undefined : handleDelete, onClose: onClose }));
82
+ return (_jsx(TreeNodeMenu, { isRoot: isRoot, nodeId: nodeId, locked: (_a = store.getState().nodesById[nodeId]) === null || _a === void 0 ? void 0 : _a.locked, onAddChild: handleAddChild, onFocus: handleFocus, onToggleLock: isRoot ? undefined : handleToggleLocked, onDuplicate: isRoot ? undefined : handleDuplicate, onDelete: isRoot ? undefined : handleDelete, onClose: onClose }));
147
83
  };
148
84
  const handleDragStart = (e, id) => {
149
- if (id === prefabData.root.id)
85
+ if (id === rootId)
150
86
  return e.preventDefault();
151
87
  e.dataTransfer.effectAllowed = "move";
152
88
  setDraggedId(id);
@@ -160,9 +96,6 @@ export default function EditorTree({ prefabData, setPrefabData, selectedId, setS
160
96
  const handleDragOver = (e, targetId, isRoot) => {
161
97
  if (!draggedId || draggedId === targetId)
162
98
  return;
163
- const draggedNode = findNode(prefabData.root, draggedId);
164
- if (draggedNode && findNode(draggedNode, targetId))
165
- return;
166
99
  e.preventDefault();
167
100
  setDropTarget({ id: targetId, position: getDropPosition(e, isRoot) });
168
101
  };
@@ -176,75 +109,92 @@ export default function EditorTree({ prefabData, setPrefabData, selectedId, setS
176
109
  if (!draggedId || draggedId === targetId)
177
110
  return;
178
111
  e.preventDefault();
179
- const dropPosition = getDropPosition(e, isRoot);
180
- setPrefabData(prev => {
181
- const root = moveNode(prev.root, draggedId, targetId, dropPosition);
182
- return root === prev.root ? prev : Object.assign(Object.assign({}, prev), { root });
183
- });
112
+ moveNode(draggedId, targetId, getDropPosition(e, isRoot));
184
113
  setDraggedId(null);
185
114
  setDropTarget(null);
186
115
  };
187
- const matchesSearch = (node, query) => {
188
- var _a, _b, _c;
189
- if (!query)
190
- return true;
191
- const lowerQuery = query.toLowerCase();
192
- const nodeName = ((_a = node.name) !== null && _a !== void 0 ? _a : node.id).toLowerCase();
193
- if (nodeName.includes(lowerQuery))
194
- return true;
195
- return (_c = (_b = node.children) === null || _b === void 0 ? void 0 : _b.some(child => matchesSearch(child, query))) !== null && _c !== void 0 ? _c : false;
196
- };
197
- const renderNode = (node, depth = 0) => {
198
- var _a;
199
- if (!node)
200
- return null;
201
- if (!matchesSearch(node, searchQuery))
202
- return null;
203
- const isSelected = node.id === selectedId;
204
- const isCollapsed = collapsedIds.has(node.id);
205
- const hasChildren = node.children && node.children.length > 0;
206
- const isRoot = node.id === prefabData.root.id;
207
- const isDropTarget = (dropTarget === null || dropTarget === void 0 ? void 0 : dropTarget.id) === node.id;
208
- const showDropBefore = isDropTarget && (dropTarget === null || dropTarget === void 0 ? void 0 : dropTarget.position) === 'before';
209
- const showDropInside = isDropTarget && (dropTarget === null || dropTarget === void 0 ? void 0 : dropTarget.position) === 'inside';
210
- return (_jsxs("div", { children: [_jsxs("div", { style: Object.assign(Object.assign(Object.assign({}, tree.row), (isSelected ? tree.selected : {})), { paddingLeft: `${depth * 12 + 6}px`, opacity: node.disabled ? 0.4 : 1, display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderTop: showDropBefore ? `2px solid ${colors.accent}` : undefined, boxShadow: showDropInside ? `inset 0 0 0 1px ${colors.accentBorder}` : undefined }), draggable: !isRoot, onClick: (e) => { e.stopPropagation(); setSelectedId(node.id); }, onContextMenu: (e) => {
211
- e.preventDefault();
212
- e.stopPropagation();
213
- openContextMenu(node.id, e.clientX, e.clientY);
214
- }, onDragStart: (e) => handleDragStart(e, node.id), onDragEnd: () => { setDraggedId(null); setDropTarget(null); }, onDragOver: (e) => handleDragOver(e, node.id, isRoot), onDragLeave: (e) => handleDragLeave(e, node.id), onDrop: (e) => handleDrop(e, node.id, isRoot), children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', flex: 1, minWidth: 0 }, children: [_jsx("span", { style: {
215
- width: 12,
216
- opacity: 0.6,
217
- marginRight: 4,
218
- cursor: 'pointer',
219
- visibility: hasChildren ? 'visible' : 'hidden'
220
- }, onClick: (e) => hasChildren && toggleCollapse(e, node.id), children: isCollapsed ? '▶' : '▼' }), !isRoot && _jsx("span", { style: { marginRight: 4, opacity: 0.4 }, children: "\u22EE\u22EE" }), _jsx("span", { style: { overflow: 'hidden', textOverflow: 'ellipsis' }, children: (_a = node.name) !== null && _a !== void 0 ? _a : node.id }), node.locked && _jsx("span", { style: { marginLeft: 6, opacity: 0.6 }, children: "\uD83D\uDD12" })] }), !isRoot && (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Dropdown, { placement: "bottom-end", trigger: ({ ref, toggle }) => (_jsx(MenuTriggerButton, { buttonRef: ref, onToggle: toggle, title: "Node Actions", style: {
221
- background: 'none',
222
- border: 'none',
223
- cursor: 'pointer',
224
- padding: '0 4px',
225
- fontSize: 14,
226
- opacity: 0.7,
227
- color: 'inherit',
228
- }, children: "\u22EF" })), children: (close) => renderTreeNodeMenu(node.id, false, close) }), _jsx("button", { style: {
116
+ const visibleIds = usePrefabStore(useCallback(state => searchQuery ? buildVisibleIds(state, rootId, searchQuery) : null, [rootId, searchQuery]));
117
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { style: Object.assign(Object.assign({}, tree.panel), { width: collapsed ? 'auto' : 224 }), children: [_jsxs("div", { style: base.header, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 6, cursor: 'pointer' }, onClick: () => setCollapsed(!collapsed), children: [_jsx("span", { children: collapsed ? '▶' : '▼' }), _jsx("span", { children: "Scene" })] }), !collapsed && (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4 }, children: [_jsx("button", { style: Object.assign(Object.assign({}, base.btn), { padding: '2px 6px', fontSize: 10, opacity: canUndo ? 1 : 0.4 }), onClick: (e) => { e.stopPropagation(); onUndo === null || onUndo === void 0 ? void 0 : onUndo(); }, disabled: !canUndo, title: "Undo", children: "\u21B6" }), _jsx("button", { style: Object.assign(Object.assign({}, base.btn), { padding: '2px 6px', fontSize: 10, opacity: canRedo ? 1 : 0.4 }), onClick: (e) => { e.stopPropagation(); onRedo === null || onRedo === void 0 ? void 0 : onRedo(); }, disabled: !canRedo, title: "Redo", children: "\u21B7" }), _jsx(Dropdown, { placement: "bottom-end", trigger: ({ ref, toggle }) => (_jsx("button", { ref: ref, title: "Menu", style: Object.assign(Object.assign({}, base.btn), { padding: '2px 6px', fontSize: 10 }), onClick: (e) => {
118
+ e.stopPropagation();
119
+ toggle();
120
+ }, children: "\u22EE" })), children: (close) => (_jsx(FileMenu, { getPrefab: getPrefab, onReplacePrefab: onReplacePrefab, onImportPrefab: onImportPrefab, onClose: close })) })] }))] }), !collapsed && (_jsxs(_Fragment, { children: [_jsx("div", { style: { padding: '4px 4px', borderBottom: `1px solid ${colors.borderLight}` }, children: _jsx("input", { type: "text", placeholder: "Search nodes...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), onClick: (e) => e.stopPropagation(), style: Object.assign(Object.assign({}, base.input), { padding: '4px 8px' }) }) }), _jsx("div", { style: tree.scroll, children: _jsx(TreeNode, { nodeId: rootId, depth: 0, rootId: rootId, visibleIds: visibleIds, collapsedIds: collapsedIds, dropTarget: dropTarget, selectedNodeId: selectedId, onToggleCollapse: toggleCollapse, onOpenContextMenu: openContextMenu, onDragStart: handleDragStart, onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, onDragEnd: () => { setDraggedId(null); setDropTarget(null); }, renderTreeNodeMenu: renderTreeNodeMenu, onToggleDisabled: handleToggleDisabled, setSelectedId: setSelectedId }) })] }))] }), _jsx(TreeContextMenu, { contextMenu: contextMenu, onClose: closeContextMenu, children: (nodeId, close) => renderTreeNodeMenu(nodeId, nodeId === rootId, close) })] }));
121
+ }
122
+ const TreeNode = memo(function TreeNode({ nodeId, depth, rootId, visibleIds, collapsedIds, dropTarget, selectedNodeId, onToggleCollapse, onOpenContextMenu, onDragStart, onDragOver, onDragLeave, onDrop, onDragEnd, renderTreeNodeMenu, onToggleDisabled, setSelectedId, }) {
123
+ var _a;
124
+ const node = usePrefabNode(nodeId);
125
+ const childIds = usePrefabChildIds(nodeId);
126
+ const isSelected = selectedNodeId === nodeId;
127
+ if (!node || (visibleIds && !visibleIds.has(nodeId)))
128
+ return null;
129
+ const isCollapsed = collapsedIds.has(nodeId);
130
+ const hasChildren = childIds.length > 0;
131
+ const isRoot = nodeId === rootId;
132
+ const isDropTarget = (dropTarget === null || dropTarget === void 0 ? void 0 : dropTarget.id) === nodeId;
133
+ const showDropBefore = isDropTarget && (dropTarget === null || dropTarget === void 0 ? void 0 : dropTarget.position) === 'before';
134
+ const showDropInside = isDropTarget && (dropTarget === null || dropTarget === void 0 ? void 0 : dropTarget.position) === 'inside';
135
+ return (_jsxs("div", { children: [_jsxs("div", { style: Object.assign(Object.assign(Object.assign({}, tree.row), (isSelected ? tree.selected : {})), { paddingLeft: `${depth * 12 + 6}px`, opacity: node.disabled ? 0.4 : 1, display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderTop: showDropBefore ? `2px solid ${colors.accent}` : undefined, boxShadow: showDropInside ? `inset 0 0 0 1px ${colors.accentBorder}` : undefined }), draggable: !isRoot, onClick: (e) => { e.stopPropagation(); setSelectedId(nodeId); }, onContextMenu: (e) => {
136
+ e.preventDefault();
137
+ e.stopPropagation();
138
+ onOpenContextMenu(nodeId, e.clientX, e.clientY);
139
+ }, onDragStart: (e) => onDragStart(e, nodeId), onDragEnd: onDragEnd, onDragOver: (e) => onDragOver(e, nodeId, isRoot), onDragLeave: (e) => onDragLeave(e, nodeId), onDrop: (e) => onDrop(e, nodeId, isRoot), children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', flex: 1, minWidth: 0 }, children: [_jsx("span", { style: {
140
+ width: 12,
141
+ opacity: 0.6,
142
+ marginRight: 4,
143
+ cursor: 'pointer',
144
+ visibility: hasChildren ? 'visible' : 'hidden'
145
+ }, onClick: (e) => hasChildren && onToggleCollapse(e, nodeId), children: isCollapsed ? '▶' : '▼' }), !isRoot && _jsx("span", { style: { marginRight: 4, opacity: 0.4 }, children: "\u22EE\u22EE" }), _jsx("span", { style: { overflow: 'hidden', textOverflow: 'ellipsis' }, children: (_a = node.name) !== null && _a !== void 0 ? _a : node.id }), node.locked && _jsx("span", { style: { marginLeft: 6, opacity: 0.6 }, children: "\uD83D\uDD12" })] }), !isRoot && (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Dropdown, { placement: "bottom-end", trigger: ({ ref, toggle }) => (_jsx("button", { ref: ref, title: "Node Actions", style: {
229
146
  background: 'none',
230
147
  border: 'none',
231
148
  cursor: 'pointer',
232
149
  padding: '0 4px',
233
150
  fontSize: 14,
234
- opacity: node.disabled ? 0.5 : 0.7,
151
+ opacity: 0.7,
235
152
  color: 'inherit',
236
153
  }, onClick: (e) => {
237
154
  e.stopPropagation();
238
- handleToggleDisabled(node.id);
239
- }, title: node.disabled ? 'Enable' : 'Disable', children: node.disabled ? '◎' : '◉' })] })), isRoot && (_jsx(Dropdown, { placement: "bottom-end", trigger: ({ ref, toggle }) => (_jsx(MenuTriggerButton, { buttonRef: ref, onToggle: toggle, title: "Scene Actions", style: {
155
+ toggle();
156
+ }, children: "\u22EF" })), children: (close) => renderTreeNodeMenu(nodeId, false, close) }), _jsx("button", { style: {
240
157
  background: 'none',
241
158
  border: 'none',
242
159
  cursor: 'pointer',
243
160
  padding: '0 4px',
244
161
  fontSize: 14,
245
- opacity: 0.7,
162
+ opacity: node.disabled ? 0.5 : 0.7,
246
163
  color: 'inherit',
247
- }, children: "\u22EF" })), children: (close) => renderTreeNodeMenu(node.id, true, close) }))] }), !isCollapsed && node.children && node.children.map(child => renderNode(child, depth + 1))] }, node.id));
164
+ }, onClick: (e) => {
165
+ e.stopPropagation();
166
+ onToggleDisabled(nodeId);
167
+ }, title: node.disabled ? 'Enable' : 'Disable', children: node.disabled ? '◎' : '◉' })] })), isRoot && (_jsx(Dropdown, { placement: "bottom-end", trigger: ({ ref, toggle }) => (_jsx("button", { ref: ref, title: "Scene Actions", style: {
168
+ background: 'none',
169
+ border: 'none',
170
+ cursor: 'pointer',
171
+ padding: '0 4px',
172
+ fontSize: 14,
173
+ opacity: 0.7,
174
+ color: 'inherit',
175
+ }, onClick: (e) => {
176
+ e.stopPropagation();
177
+ toggle();
178
+ }, children: "\u22EF" })), children: (close) => renderTreeNodeMenu(nodeId, true, close) }))] }), !isCollapsed && childIds.map(childId => (_jsx(TreeNode, { nodeId: childId, depth: depth + 1, rootId: rootId, visibleIds: visibleIds, collapsedIds: collapsedIds, dropTarget: dropTarget, selectedNodeId: selectedNodeId, onToggleCollapse: onToggleCollapse, onOpenContextMenu: onOpenContextMenu, onDragStart: onDragStart, onDragOver: onDragOver, onDragLeave: onDragLeave, onDrop: onDrop, onDragEnd: onDragEnd, renderTreeNodeMenu: renderTreeNodeMenu, onToggleDisabled: onToggleDisabled, setSelectedId: setSelectedId }, childId)))] }));
179
+ });
180
+ function buildVisibleIds(state, rootId, query) {
181
+ if (!query)
182
+ return null;
183
+ const visibleIds = new Set();
184
+ const lowerQuery = query.toLowerCase();
185
+ const visit = (nodeId) => {
186
+ var _a, _b;
187
+ const node = state.nodesById[nodeId];
188
+ if (!node)
189
+ return false;
190
+ const selfMatches = ((_a = node.name) !== null && _a !== void 0 ? _a : node.id).toLowerCase().includes(lowerQuery);
191
+ const childMatches = ((_b = state.childIdsById[nodeId]) !== null && _b !== void 0 ? _b : []).some(visit);
192
+ if (selfMatches || childMatches) {
193
+ visibleIds.add(nodeId);
194
+ return true;
195
+ }
196
+ return false;
248
197
  };
249
- return (_jsxs(_Fragment, { children: [_jsxs("div", { style: Object.assign(Object.assign({}, tree.panel), { width: collapsed ? 'auto' : 224 }), children: [_jsxs("div", { style: base.header, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 6, cursor: 'pointer' }, onClick: () => setCollapsed(!collapsed), children: [_jsx("span", { children: collapsed ? '▶' : '▼' }), _jsx("span", { children: "Scene" })] }), !collapsed && (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4 }, children: [_jsx("button", { style: Object.assign(Object.assign({}, base.btn), { padding: '2px 6px', fontSize: 10, opacity: canUndo ? 1 : 0.4 }), onClick: (e) => { e.stopPropagation(); onUndo === null || onUndo === void 0 ? void 0 : onUndo(); }, disabled: !canUndo, title: "Undo", children: "\u21B6" }), _jsx("button", { style: Object.assign(Object.assign({}, base.btn), { padding: '2px 6px', fontSize: 10, opacity: canRedo ? 1 : 0.4 }), onClick: (e) => { e.stopPropagation(); onRedo === null || onRedo === void 0 ? void 0 : onRedo(); }, disabled: !canRedo, title: "Redo", children: "\u21B7" }), _jsx(Dropdown, { placement: "bottom-end", trigger: ({ ref, toggle }) => (_jsx(MenuTriggerButton, { buttonRef: ref, onToggle: toggle, title: "Menu", style: Object.assign(Object.assign({}, base.btn), { padding: '2px 6px', fontSize: 10 }), children: "\u22EE" })), children: (close) => (_jsx(FileMenu, { prefabData: prefabData, setPrefabData: setPrefabData, onClose: close })) })] }))] }), !collapsed && (_jsxs(_Fragment, { children: [_jsx("div", { style: { padding: '4px 4px', borderBottom: `1px solid ${colors.borderLight}` }, children: _jsx("input", { type: "text", placeholder: "Search nodes...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), onClick: (e) => e.stopPropagation(), style: Object.assign(Object.assign({}, base.input), { padding: '4px 8px' }) }) }), _jsx("div", { className: "tree-scroll", style: tree.scroll, children: renderNode(prefabData.root) })] }))] }), _jsx(TreeContextMenu, { contextMenu: contextMenu, onClose: closeContextMenu, children: (nodeId, close) => renderTreeNodeMenu(nodeId, nodeId === prefabData.root.id, close) })] }));
198
+ visit(rootId);
199
+ return visibleIds;
250
200
  }
@@ -1,17 +1,9 @@
1
- import { Dispatch, SetStateAction } from 'react';
2
1
  import { Prefab } from './types';
3
2
  export type TreeContextMenuState = {
4
3
  nodeId: string;
5
4
  x: number;
6
5
  y: number;
7
6
  } | null;
8
- export declare function MenuTriggerButton({ buttonRef, onToggle, title, style, children, }: {
9
- buttonRef: React.RefObject<HTMLButtonElement | null>;
10
- onToggle: () => void;
11
- title: string;
12
- style: React.CSSProperties;
13
- children: React.ReactNode;
14
- }): import("react/jsx-runtime").JSX.Element;
15
7
  export declare function TreeNodeMenu({ isRoot, nodeId, locked, onAddChild, onFocus, onToggleLock, onDuplicate, onDelete, onClose, }: {
16
8
  isRoot: boolean;
17
9
  nodeId: string;
@@ -28,8 +20,9 @@ export declare function TreeContextMenu({ contextMenu, onClose, children, }: {
28
20
  onClose: () => void;
29
21
  children: (nodeId: string, onClose: () => void) => React.ReactNode;
30
22
  }): import("react").ReactPortal | null;
31
- export declare function FileMenu({ prefabData, setPrefabData, onClose }: {
32
- prefabData: Prefab;
33
- setPrefabData: Dispatch<SetStateAction<Prefab>>;
23
+ export declare function FileMenu({ getPrefab, onReplacePrefab, onImportPrefab, onClose }: {
24
+ getPrefab: () => Prefab;
25
+ onReplacePrefab: (prefab: Prefab) => void;
26
+ onImportPrefab: (prefab: Prefab) => void;
34
27
  onClose: () => void;
35
28
  }): import("react/jsx-runtime").JSX.Element;
@@ -13,15 +13,15 @@ import { createPortal } from 'react-dom';
13
13
  import { menu } from './styles';
14
14
  import { useEditorContext } from './EditorContext';
15
15
  import { getComponent } from './components/ComponentRegistry';
16
- import { loadJson, saveJson, regenerateIds, updateNodeById } from './utils';
16
+ import { loadJson, saveJson } from './utils';
17
17
  function createEmptyPrefab() {
18
18
  var _a;
19
19
  return {
20
20
  id: crypto.randomUUID(),
21
- name: 'New Scene',
21
+ name: 'New Prefab',
22
22
  root: {
23
23
  id: crypto.randomUUID(),
24
- name: 'Scene',
24
+ name: 'Root',
25
25
  components: {
26
26
  transform: {
27
27
  type: 'Transform',
@@ -47,12 +47,6 @@ function MenuSubmenu({ label, children, }) {
47
47
  zIndex: 1,
48
48
  }, children: _jsx(MenuPanel, { children: children }) }))] }));
49
49
  }
50
- export function MenuTriggerButton({ buttonRef, onToggle, title, style, children, }) {
51
- return (_jsx("button", { ref: buttonRef, style: style, onClick: (e) => {
52
- e.stopPropagation();
53
- onToggle();
54
- }, title: title, children: children }));
55
- }
56
50
  export function TreeNodeMenu({ isRoot, nodeId, locked, onAddChild, onFocus, onToggleLock, onDuplicate, onDelete, onClose, }) {
57
51
  return (_jsxs(MenuPanel, { children: [_jsx(MenuItemButton, { onClick: () => { onAddChild(nodeId); onClose(); }, children: "Add Child" }), _jsx(MenuItemButton, { onClick: () => { onFocus(nodeId); onClose(); }, children: "Focus Camera" }), !isRoot && onToggleLock && (_jsx(MenuItemButton, { onClick: () => { onToggleLock(nodeId); onClose(); }, children: locked ? 'Unlock' : 'Lock' })), !isRoot && onDuplicate && (_jsx(MenuItemButton, { onClick: () => { onDuplicate(nodeId); onClose(); }, children: "Duplicate" })), !isRoot && onDelete && (_jsx(MenuItemButton, { danger: true, onClick: () => { onDelete(nodeId); onClose(); }, children: "Delete" }))] }));
58
52
  }
@@ -104,32 +98,29 @@ export function TreeContextMenu({ contextMenu, onClose, children, }) {
104
98
  zIndex: 1000,
105
99
  }, onMouseLeave: onClose, onContextMenu: (e) => e.preventDefault(), children: children(contextMenu.nodeId, onClose) }), document.body);
106
100
  }
107
- export function FileMenu({ prefabData, setPrefabData, onClose }) {
101
+ export function FileMenu({ getPrefab, onReplacePrefab, onImportPrefab, onClose }) {
108
102
  const { onScreenshot, onExportGLB } = useEditorContext();
109
- const handleNewScene = () => {
110
- setPrefabData(createEmptyPrefab());
103
+ const handleNew = () => {
104
+ onReplacePrefab(createEmptyPrefab());
111
105
  onClose();
112
106
  };
113
- const handleNewSceneFromPrefab = () => __awaiter(this, void 0, void 0, function* () {
114
- const loadedPrefab = yield loadJson();
115
- if (!loadedPrefab)
107
+ const handleOpen = () => __awaiter(this, void 0, void 0, function* () {
108
+ const loaded = yield loadJson();
109
+ if (!loaded)
116
110
  return;
117
- setPrefabData(loadedPrefab);
111
+ onReplacePrefab(loaded);
118
112
  onClose();
119
113
  });
120
114
  const handleSave = () => {
121
- saveJson(prefabData, 'prefab');
115
+ saveJson(getPrefab(), 'prefab');
122
116
  onClose();
123
117
  };
124
- const handleLoadIntoScene = () => __awaiter(this, void 0, void 0, function* () {
125
- const loadedPrefab = yield loadJson();
126
- if (!loadedPrefab)
118
+ const handleImport = () => __awaiter(this, void 0, void 0, function* () {
119
+ const loaded = yield loadJson();
120
+ if (!loaded)
127
121
  return;
128
- setPrefabData(prev => (Object.assign(Object.assign({}, prev), { root: updateNodeById(prev.root, prev.root.id, root => {
129
- var _a;
130
- return (Object.assign(Object.assign({}, root), { children: [...((_a = root.children) !== null && _a !== void 0 ? _a : []), regenerateIds(loadedPrefab.root)] }));
131
- }) })));
122
+ onImportPrefab(loaded);
132
123
  onClose();
133
124
  });
134
- return (_jsxs(MenuPanel, { style: { overflow: 'visible' }, children: [_jsxs(MenuSubmenu, { label: "File", children: [_jsx(MenuItemButton, { onClick: handleNewScene, children: "New Scene" }), _jsx(MenuItemButton, { onClick: handleNewSceneFromPrefab, children: "New Scene from Prefab" }), _jsx(MenuItemButton, { onClick: handleLoadIntoScene, children: "Load Prefab into Scene" }), _jsx(MenuItemButton, { onClick: handleSave, children: "Save Prefab" })] }), _jsxs(MenuSubmenu, { label: "Export", children: [_jsx(MenuItemButton, { onClick: () => { onExportGLB === null || onExportGLB === void 0 ? void 0 : onExportGLB(); onClose(); }, children: "GLB" }), _jsx(MenuItemButton, { onClick: () => { onScreenshot === null || onScreenshot === void 0 ? void 0 : onScreenshot(); onClose(); }, children: "PNG" })] })] }));
125
+ return (_jsxs(MenuPanel, { style: { overflow: 'visible' }, children: [_jsxs(MenuSubmenu, { label: "File", children: [_jsx(MenuItemButton, { onClick: handleNew, children: "New Prefab" }), _jsx(MenuItemButton, { onClick: handleOpen, children: "Open Prefab" }), _jsx(MenuItemButton, { onClick: handleImport, children: "Import Prefab" }), _jsx(MenuItemButton, { onClick: handleSave, children: "Save Prefab" })] }), _jsxs(MenuSubmenu, { label: "Export", children: [_jsx(MenuItemButton, { onClick: () => { onExportGLB === null || onExportGLB === void 0 ? void 0 : onExportGLB(); onClose(); }, children: "GLB" }), _jsx(MenuItemButton, { onClick: () => { onScreenshot === null || onScreenshot === void 0 ? void 0 : onScreenshot(); onClose(); }, children: "PNG" })] })] }));
135
126
  }
@@ -1,10 +1,10 @@
1
- import { Dispatch, SetStateAction } from 'react';
2
1
  import { Prefab } from "./types";
3
- declare function EditorUI({ prefabData, setPrefabData, selectedId, setSelectedId, basePath, onUndo, onRedo, canUndo, canRedo }: {
4
- prefabData?: Prefab;
5
- setPrefabData?: Dispatch<SetStateAction<Prefab>>;
2
+ declare function EditorUI({ selectedId, setSelectedId, getPrefab, onReplacePrefab, onImportPrefab, basePath, onUndo, onRedo, canUndo, canRedo }: {
6
3
  selectedId: string | null;
7
- setSelectedId: Dispatch<SetStateAction<string | null>>;
4
+ setSelectedId: (id: string | null) => void;
5
+ getPrefab: () => Prefab;
6
+ onReplacePrefab: (prefab: Prefab) => void;
7
+ onImportPrefab: (prefab: Prefab) => void;
8
8
  basePath?: string;
9
9
  onUndo?: () => void;
10
10
  onRedo?: () => void;
@@ -13,23 +13,26 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
13
13
  import { useState } from 'react';
14
14
  import EditorTree from './EditorTree';
15
15
  import { getAllComponents } from './components/ComponentRegistry';
16
- import { base, colors, inspector, scrollbarCSS, componentCard } from './styles';
17
- import { findNode, updateNode, deleteNode } from './utils';
18
- function EditorUI({ prefabData, setPrefabData, selectedId, setSelectedId, basePath, onUndo, onRedo, canUndo, canRedo }) {
16
+ import { base, colors, inspector, componentCard } from './styles';
17
+ import { usePrefabStore } from './prefabStore';
18
+ function EditorUI({ selectedId, setSelectedId, getPrefab, onReplacePrefab, onImportPrefab, basePath, onUndo, onRedo, canUndo, canRedo }) {
19
19
  const [collapsed, setCollapsed] = useState(false);
20
- const updateNodeHandler = (updater) => {
21
- if (!prefabData || !setPrefabData || !selectedId)
20
+ const rootId = usePrefabStore(state => state.rootId);
21
+ const selectedNode = usePrefabStore(state => { var _a; return selectedId ? (_a = state.nodesById[selectedId]) !== null && _a !== void 0 ? _a : null : null; });
22
+ const updateNode = usePrefabStore(state => state.updateNode);
23
+ const deleteNode = usePrefabStore(state => state.deleteNode);
24
+ const updateNodeHandler = (update) => {
25
+ if (!selectedId)
22
26
  return;
23
- setPrefabData(prev => (Object.assign(Object.assign({}, prev), { root: updateNode(prev.root, selectedId, updater) })));
27
+ updateNode(selectedId, update);
24
28
  };
25
29
  const deleteNodeHandler = () => {
26
- if (!prefabData || !setPrefabData || !selectedId || selectedId === prefabData.root.id)
30
+ if (!selectedId || selectedId === rootId)
27
31
  return;
28
- setPrefabData(prev => (Object.assign(Object.assign({}, prev), { root: deleteNode(prev.root, selectedId) })));
32
+ deleteNode(selectedId);
29
33
  setSelectedId(null);
30
34
  };
31
- const selectedNode = selectedId && prefabData ? findNode(prefabData.root, selectedId) : null;
32
- return _jsxs(_Fragment, { children: [_jsx("style", { children: scrollbarCSS }), _jsxs("div", { style: inspector.panel, children: [_jsxs("div", { 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, { prefabData: prefabData, setPrefabData: setPrefabData, selectedId: selectedId, setSelectedId: setSelectedId, onUndo: onUndo, onRedo: onRedo, canUndo: canUndo, canRedo: canRedo }) })] });
35
+ return _jsxs(_Fragment, { children: [_jsxs("div", { style: inspector.panel, children: [_jsxs("div", { 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 }) })] });
33
36
  }
34
37
  function NodeInspector({ node, updateNode, deleteNode, basePath }) {
35
38
  var _a;
@@ -38,7 +41,7 @@ function NodeInspector({ node, updateNode, deleteNode, basePath }) {
38
41
  const available = allKeys.filter(k => { var _a; return !((_a = node.components) === null || _a === void 0 ? void 0 : _a[k.toLowerCase()]); });
39
42
  const [preferredAddType, setAddType] = useState(available[0] || "");
40
43
  const addType = available.includes(preferredAddType) ? preferredAddType : (available[0] || "");
41
- return _jsxs("div", { style: inspector.content, className: "prefab-scroll", children: [_jsxs("div", { style: base.section, children: [_jsxs("div", { style: { display: "flex", marginBottom: 8, alignItems: 'center', gap: 8 }, children: [_jsx("div", { style: { fontSize: 10, color: colors.textDim, wordBreak: 'break-all', border: `1px solid ${colors.border}`, padding: '2px 6px', borderRadius: 3, flex: 1, fontFamily: 'monospace' }, children: node.id }), _jsx("button", { style: Object.assign(Object.assign({}, base.btn), base.btnDanger), title: "Delete Node", onClick: deleteNode, children: "\u274C" })] }), _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: 8 }, children: _jsx("div", { style: base.label, children: "Components" }) }), node.components && Object.entries(node.components).map(([key, comp]) => {
44
+ return _jsxs("div", { style: inspector.content, children: [_jsxs("div", { style: base.section, children: [_jsxs("div", { style: { display: "flex", marginBottom: 8, alignItems: 'center', gap: 8 }, children: [_jsx("div", { style: { fontSize: 10, color: colors.textDim, wordBreak: 'break-all', border: `1px solid ${colors.border}`, padding: '2px 6px', borderRadius: 3, flex: 1, fontFamily: 'monospace' }, children: node.id }), _jsx("button", { style: Object.assign(Object.assign({}, base.btn), base.btnDanger), title: "Delete Node", onClick: deleteNode, children: "\u274C" })] }), _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: 8 }, children: _jsx("div", { style: base.label, children: "Components" }) }), node.components && Object.entries(node.components).map(([key, comp]) => {
42
45
  if (!comp)
43
46
  return null;
44
47
  const def = ALL_COMPONENTS[comp.type];
@@ -106,34 +106,4 @@ export declare function useGameEvent<T extends string>(type: T, handler: EventHa
106
106
  * Entity IDs are stored in RigidBody userData.
107
107
  */
108
108
  export declare function getEntityIdFromRigidBody(rigidBody: RapierRigidBody | null | undefined): string | null;
109
- /** @deprecated Use gameEvents instead */
110
- export declare const entityEvents: {
111
- /**
112
- * Emit an event to all subscribers
113
- */
114
- emit<T extends string>(type: T, payload: GameEventPayload<T>): void;
115
- /**
116
- * Subscribe to an event type
117
- * @returns Unsubscribe function
118
- */
119
- on<T extends string>(type: T, handler: EventHandler<GameEventPayload<T>>): () => void;
120
- /**
121
- * Unsubscribe from an event type
122
- */
123
- off<T extends string>(type: T, handler: EventHandler<GameEventPayload<T>>): void;
124
- /**
125
- * Remove all subscribers (useful for cleanup/reset)
126
- */
127
- clear(): void;
128
- /**
129
- * Check if an event type has any subscribers
130
- */
131
- hasListeners(type: string): boolean;
132
- };
133
- /** @deprecated Use useGameEvent instead */
134
- export declare const useEntityEvent: typeof useGameEvent;
135
- /** @deprecated Use GameEventType instead */
136
- export type EntityEventType = PhysicsEventType;
137
- /** @deprecated Use PhysicsEventPayload instead */
138
- export type EntityEventPayload = PhysicsEventPayload;
139
109
  export {};
@@ -111,10 +111,3 @@ export function getEntityIdFromRigidBody(rigidBody) {
111
111
  const userData = rigidBody.userData;
112
112
  return (_a = userData === null || userData === void 0 ? void 0 : userData.entityId) !== null && _a !== void 0 ? _a : null;
113
113
  }
114
- // ============================================================================
115
- // Backward Compatibility Aliases
116
- // ============================================================================
117
- /** @deprecated Use gameEvents instead */
118
- export const entityEvents = gameEvents;
119
- /** @deprecated Use useGameEvent instead */
120
- export const useEntityEvent = useGameEvent;
@@ -3,28 +3,27 @@ import { Object3D, Texture } from "three";
3
3
  import { GameObject, Prefab } from "./types";
4
4
  import { PrefabRootRef } from "./PrefabRoot";
5
5
  import type { ExportGLBOptions } from "./utils";
6
- export interface PrefabEditorAssetOptions {
7
- name?: string;
8
- parentId?: string;
9
- select?: boolean;
10
- }
6
+ import { type Scene, type SpawnOptions } from "./sceneApi";
11
7
  export interface PrefabEditorRef {
12
8
  screenshot: () => void;
13
- exportGLB: (options?: ExportGLBOptions) => Promise<ArrayBuffer | object | undefined>;
9
+ exportGLB: (options?: ExportGLBOptions) => Promise<ArrayBuffer | undefined>;
14
10
  exportGLBData: () => Promise<ArrayBuffer | undefined>;
15
11
  clearSelection: () => Promise<void>;
16
- prefab: Prefab;
17
- setPrefab: (prefab: Prefab) => void;
18
- replacePrefab: (prefab: Prefab) => void;
19
- addModel: (path: string, model: Object3D, options?: PrefabEditorAssetOptions) => GameObject;
20
- addTexture: (path: string, texture: Texture, options?: PrefabEditorAssetOptions) => GameObject;
21
- rootRef: React.RefObject<PrefabRootRef | null>;
12
+ save: () => Prefab;
13
+ scene: Scene;
14
+ load: (prefab: Prefab, options?: {
15
+ resetHistory?: boolean;
16
+ notifyChange?: boolean;
17
+ }) => void;
18
+ addModel: (path: string, model: Object3D, options?: SpawnOptions) => GameObject;
19
+ addTexture: (path: string, texture: Texture, options?: SpawnOptions) => GameObject;
20
+ viewRef: React.RefObject<PrefabRootRef | null>;
22
21
  }
23
22
  export interface PrefabEditorProps {
24
23
  basePath?: string;
25
24
  initialPrefab?: Prefab;
26
25
  physics?: boolean;
27
- onPrefabChange?: (prefab: Prefab) => void;
26
+ onChange?: (prefab: Prefab) => void;
28
27
  showUI?: boolean;
29
28
  enableWindowDrop?: boolean;
30
29
  canvasProps?: Omit<React.ComponentProps<typeof GameCanvas>, 'children' | 'canvasRef'>;