react-three-game 0.0.65 → 0.0.67

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 (38) hide show
  1. package/LICENSE +2 -660
  2. package/README.md +164 -91
  3. package/dist/index.d.ts +5 -3
  4. package/dist/index.js +3 -2
  5. package/dist/shared/GameCanvas.js +1 -1
  6. package/dist/tools/assetviewer/page.d.ts +13 -2
  7. package/dist/tools/assetviewer/page.js +61 -7
  8. package/dist/tools/dragdrop/index.d.ts +1 -1
  9. package/dist/tools/dragdrop/modelLoader.d.ts +2 -0
  10. package/dist/tools/prefabeditor/EditorContext.d.ts +2 -2
  11. package/dist/tools/prefabeditor/EditorTree.js +17 -3
  12. package/dist/tools/prefabeditor/EditorTreeMenus.d.ts +3 -1
  13. package/dist/tools/prefabeditor/EditorTreeMenus.js +7 -8
  14. package/dist/tools/prefabeditor/EditorUI.js +3 -7
  15. package/dist/tools/prefabeditor/GameEvents.d.ts +14 -1
  16. package/dist/tools/prefabeditor/GameEvents.js +2 -1
  17. package/dist/tools/prefabeditor/InstanceProvider.d.ts +4 -0
  18. package/dist/tools/prefabeditor/InstanceProvider.js +44 -12
  19. package/dist/tools/prefabeditor/PrefabEditor.js +77 -16
  20. package/dist/tools/prefabeditor/PrefabRoot.d.ts +9 -6
  21. package/dist/tools/prefabeditor/PrefabRoot.js +52 -126
  22. package/dist/tools/prefabeditor/components/CameraComponent.js +1 -1
  23. package/dist/tools/prefabeditor/components/ClickComponent.d.ts +3 -0
  24. package/dist/tools/prefabeditor/components/ClickComponent.js +45 -0
  25. package/dist/tools/prefabeditor/components/ComponentRegistry.js +0 -3
  26. package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +3 -3
  27. package/dist/tools/prefabeditor/components/Input.d.ts +5 -2
  28. package/dist/tools/prefabeditor/components/Input.js +71 -38
  29. package/dist/tools/prefabeditor/components/MaterialComponent.js +4 -69
  30. package/dist/tools/prefabeditor/components/ModelComponent.js +5 -80
  31. package/dist/tools/prefabeditor/components/PhysicsComponent.d.ts +2 -0
  32. package/dist/tools/prefabeditor/components/PhysicsComponent.js +77 -10
  33. package/dist/tools/prefabeditor/components/SpotLightComponent.js +9 -7
  34. package/dist/tools/prefabeditor/components/index.js +2 -0
  35. package/dist/tools/prefabeditor/types.d.ts +1 -0
  36. package/dist/tools/prefabeditor/utils.d.ts +7 -1
  37. package/dist/tools/prefabeditor/utils.js +34 -1
  38. package/package.json +1 -1
@@ -8,25 +8,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
11
- import { MapControls, TransformControls, useHelper } from "@react-three/drei";
12
- import { forwardRef, useCallback, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
13
- import { BoxHelper, Euler, Matrix4, Quaternion, SRGBColorSpace, TextureLoader, Vector3, } from "three";
14
- import { getComponent, registerComponent, getNonComposableKeys } from "./components/ComponentRegistry";
11
+ import { useHelper } from "@react-three/drei";
12
+ import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
13
+ import { BoxHelper, Euler, Matrix4, Quaternion, Vector3, } from "three";
14
+ import { getComponent, registerComponent } from "./components/ComponentRegistry";
15
15
  import components from "./components";
16
- import { loadModel } from "../dragdrop";
16
+ import { loadModel, loadTexture } from "../dragdrop";
17
17
  import { GameInstance, GameInstanceProvider, getRepeatAxesFromModelProperties, useInstanceCheck } from "./InstanceProvider";
18
- import { focusCameraOnObject, updateNode } from "./utils";
19
- import { EditorContext } from "./EditorContext";
18
+ import { decompose } from "./utils";
20
19
  components.forEach(registerComponent);
21
20
  const IDENTITY = new Matrix4();
22
- export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selectedId, onSelect, onClick, basePath = "", injectedModels = {}, injectedTextures = {} }, ref) => {
23
- var _a, _b, _c, _d;
24
- // optional editor context
25
- const editorContext = useContext(EditorContext);
26
- const transformMode = (_a = editorContext === null || editorContext === void 0 ? void 0 : editorContext.transformMode) !== null && _a !== void 0 ? _a : "translate";
27
- const snapResolution = (_b = editorContext === null || editorContext === void 0 ? void 0 : editorContext.snapResolution) !== null && _b !== void 0 ? _b : 0;
28
- const positionSnap = (_c = editorContext === null || editorContext === void 0 ? void 0 : editorContext.positionSnap) !== null && _c !== void 0 ? _c : 0.5;
29
- const rotationSnap = (_d = editorContext === null || editorContext === void 0 ? void 0 : editorContext.rotationSnap) !== null && _d !== void 0 ? _d : Math.PI / 4;
21
+ export const PrefabRoot = forwardRef(({ editMode, data, selectedId, onSelect, onClick, onSelectedObjectChange, onFocusNode, basePath = "", injectedModels = {}, injectedTextures = {} }, ref) => {
30
22
  // prefab root state
31
23
  const [models, setModels] = useState({});
32
24
  const [textures, setTextures] = useState({});
@@ -34,28 +26,21 @@ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selected
34
26
  const failedTextures = useRef(new Set());
35
27
  const objectRefs = useRef({});
36
28
  const rigidBodyRefs = useRef(new Map());
37
- const [selectedObject, setSelectedObject] = useState(null);
38
29
  const rootRef = useRef(null);
39
- const controlsRef = useRef(null);
40
30
  const availableModels = useMemo(() => (Object.assign(Object.assign({}, models), injectedModels)), [models, injectedModels]);
41
31
  const availableTextures = useMemo(() => (Object.assign(Object.assign({}, textures), injectedTextures)), [textures, injectedTextures]);
42
32
  useImperativeHandle(ref, () => ({
43
33
  root: rootRef.current,
44
34
  rigidBodyRefs: rigidBodyRefs.current,
45
- focusNode: (nodeId) => {
46
- const object = objectRefs.current[nodeId];
47
- const controls = controlsRef.current;
48
- const camera = controls === null || controls === void 0 ? void 0 : controls.object;
49
- if (!object || !controls || !camera)
50
- return;
51
- focusCameraOnObject(object, camera, controls.target, () => { var _a; return (_a = controls.update) === null || _a === void 0 ? void 0 : _a.call(controls); });
52
- }
53
- }), []);
35
+ getObject: (nodeId) => { var _a; return (_a = objectRefs.current[nodeId]) !== null && _a !== void 0 ? _a : null; },
36
+ focusNode: (nodeId) => onFocusNode === null || onFocusNode === void 0 ? void 0 : onFocusNode(nodeId),
37
+ }), [onFocusNode]);
54
38
  const registerRef = useCallback((id, obj) => {
55
39
  objectRefs.current[id] = obj;
56
- if (id === selectedId)
57
- setSelectedObject(obj);
58
- }, [selectedId]);
40
+ if (id === selectedId) {
41
+ onSelectedObjectChange === null || onSelectedObjectChange === void 0 ? void 0 : onSelectedObjectChange(obj);
42
+ }
43
+ }, [onSelectedObjectChange, selectedId]);
59
44
  const registerRigidBodyRef = useCallback((id, rb) => {
60
45
  rigidBodyRefs.current.set(id, rb);
61
46
  }, []);
@@ -68,36 +53,26 @@ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selected
68
53
  };
69
54
  return () => { console.error = originalError; };
70
55
  }, []);
71
- useEffect(() => {
72
- var _a;
73
- setSelectedObject(selectedId ? (_a = objectRefs.current[selectedId]) !== null && _a !== void 0 ? _a : null : null);
74
- }, [selectedId]);
75
- const onTransformChange = () => {
76
- if (!selectedId || !onPrefabChange)
77
- return;
78
- const obj = objectRefs.current[selectedId];
79
- if (!obj)
80
- return;
81
- const parentWorld = computeParentWorldMatrix(data.root, selectedId);
82
- const local = parentWorld.clone().invert().multiply(obj.matrixWorld);
83
- const { position, rotation, scale } = decompose(local);
84
- const root = updateNode(data.root, selectedId, node => (Object.assign(Object.assign({}, node), { components: Object.assign(Object.assign({}, node.components), { transform: {
85
- type: "Transform",
86
- properties: { position, rotation, scale },
87
- } }) })));
88
- onPrefabChange(Object.assign(Object.assign({}, data), { root }));
89
- };
90
56
  useEffect(() => {
91
57
  const modelsToLoad = new Set();
92
58
  const texturesToLoad = new Set();
93
59
  walk(data.root, node => {
94
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
95
- ((_c = (_b = (_a = node.components) === null || _a === void 0 ? void 0 : _a.model) === null || _b === void 0 ? void 0 : _b.properties) === null || _c === void 0 ? void 0 : _c.filename) &&
96
- modelsToLoad.add(node.components.model.properties.filename);
97
- ((_f = (_e = (_d = node.components) === null || _d === void 0 ? void 0 : _d.material) === null || _e === void 0 ? void 0 : _e.properties) === null || _f === void 0 ? void 0 : _f.texture) &&
98
- texturesToLoad.add(node.components.material.properties.texture);
99
- ((_j = (_h = (_g = node.components) === null || _g === void 0 ? void 0 : _g.material) === null || _h === void 0 ? void 0 : _h.properties) === null || _j === void 0 ? void 0 : _j.normalMapTexture) &&
100
- texturesToLoad.add(node.components.material.properties.normalMapTexture);
60
+ var _a;
61
+ Object.values((_a = node.components) !== null && _a !== void 0 ? _a : {}).forEach(component => {
62
+ var _a, _b, _c, _d;
63
+ if (!(component === null || component === void 0 ? void 0 : component.type))
64
+ return;
65
+ if (component.type === 'Model' && ((_a = component.properties) === null || _a === void 0 ? void 0 : _a.filename)) {
66
+ modelsToLoad.add(component.properties.filename);
67
+ }
68
+ if (component.type === 'Material') {
69
+ ((_b = component.properties) === null || _b === void 0 ? void 0 : _b.texture) && texturesToLoad.add(component.properties.texture);
70
+ ((_c = component.properties) === null || _c === void 0 ? void 0 : _c.normalMapTexture) && texturesToLoad.add(component.properties.normalMapTexture);
71
+ }
72
+ if (component.type === 'SpotLight' && ((_d = component.properties) === null || _d === void 0 ? void 0 : _d.map)) {
73
+ texturesToLoad.add(component.properties.map);
74
+ }
75
+ });
101
76
  });
102
77
  modelsToLoad.forEach((file) => __awaiter(void 0, void 0, void 0, function* () {
103
78
  if (availableModels[file] || loading.current.has(file))
@@ -112,7 +87,6 @@ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selected
112
87
  setModels(m => (Object.assign(Object.assign({}, m), { [file]: model })));
113
88
  }
114
89
  }));
115
- const loader = new TextureLoader();
116
90
  texturesToLoad.forEach(file => {
117
91
  if (availableTextures[file] || loading.current.has(file) || failedTextures.current.has(file))
118
92
  return;
@@ -123,17 +97,18 @@ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selected
123
97
  : file.startsWith("/")
124
98
  ? `${basePath}${file}`
125
99
  : `${basePath}/${file}`;
126
- loader.load(path, tex => {
127
- tex.colorSpace = SRGBColorSpace;
128
- setTextures(t => (Object.assign(Object.assign({}, t), { [file]: tex })));
129
- }, undefined, (err) => {
130
- console.warn(`Failed to load texture: ${path}`, err);
100
+ void loadTexture(path).then(result => {
101
+ if (result.success && result.texture) {
102
+ setTextures(t => (Object.assign(Object.assign({}, t), { [file]: result.texture })));
103
+ return;
104
+ }
105
+ console.warn(`Failed to load texture: ${path}`, result.error);
131
106
  loading.current.delete(file);
132
107
  failedTextures.current.add(file);
133
108
  });
134
109
  });
135
110
  }, [data, availableModels, availableTextures, basePath]);
136
- return (_jsxs("group", { ref: rootRef, children: [_jsx(GameInstanceProvider, { models: availableModels, selectedId: selectedId, editMode: editMode, onSelect: editMode ? onSelect : undefined, registerRef: registerRef, children: _jsx(GameObjectRenderer, { gameObject: data.root, selectedId: selectedId, onSelect: editMode ? onSelect : undefined, onClick: onClick, registerRef: registerRef, registerRigidBodyRef: registerRigidBodyRef, loadedModels: availableModels, loadedTextures: availableTextures, editMode: editMode, parentMatrix: IDENTITY }) }), editMode && (_jsxs(_Fragment, { children: [_jsx(MapControls, { ref: controlsRef, makeDefault: true }), selectedObject && (_jsx(TransformControls, { object: selectedObject, mode: transformMode, space: "local", onObjectChange: onTransformChange, translationSnap: positionSnap > 0 ? positionSnap : undefined, rotationSnap: rotationSnap > 0 ? rotationSnap : undefined, scaleSnap: snapResolution > 0 ? snapResolution : undefined }, `transform-${transformMode}-${positionSnap}-${rotationSnap}-${snapResolution}`))] }))] }));
111
+ return (_jsx("group", { ref: rootRef, children: _jsx(GameInstanceProvider, { models: availableModels, selectedId: selectedId, editMode: editMode, onSelect: editMode ? onSelect : undefined, registerRef: registerRef, children: _jsx(GameObjectRenderer, { gameObject: data.root, selectedId: selectedId, onSelect: editMode ? onSelect : undefined, onClick: onClick, registerRef: registerRef, registerRigidBodyRef: registerRigidBodyRef, loadedModels: availableModels, loadedTextures: availableTextures, editMode: editMode, parentMatrix: IDENTITY }) }) }));
137
112
  });
138
113
  export function GameObjectRenderer(props) {
139
114
  var _a, _b, _c;
@@ -162,12 +137,14 @@ function isPhysicsProps(v) {
162
137
  return (v === null || v === void 0 ? void 0 : v.type) === "fixed" || (v === null || v === void 0 ? void 0 : v.type) === "dynamic" || (v === null || v === void 0 ? void 0 : v.type) === "kinematicPosition" || (v === null || v === void 0 ? void 0 : v.type) === "kinematicVelocity";
163
138
  }
164
139
  function InstancedNode({ gameObject, parentMatrix = IDENTITY, editMode, registerRef, selectedId: _selectedId, onSelect, onClick }) {
165
- var _a, _b, _c, _d, _e, _f, _g;
140
+ var _a, _b, _c, _d, _e, _f, _g, _h;
166
141
  const localTransform = getNodeTransformProps(gameObject);
167
- const physicsProps = isPhysicsProps((_b = (_a = gameObject.components) === null || _a === void 0 ? void 0 : _a.physics) === null || _b === void 0 ? void 0 : _b.properties)
168
- ? (_d = (_c = gameObject.components) === null || _c === void 0 ? void 0 : _c.physics) === null || _d === void 0 ? void 0 : _d.properties
142
+ const isLocked = Boolean(gameObject.locked);
143
+ const clickable = Object.values((_a = gameObject.components) !== null && _a !== void 0 ? _a : {}).some(component => (component === null || component === void 0 ? void 0 : component.type) === 'Click');
144
+ const physicsProps = isPhysicsProps((_c = (_b = gameObject.components) === null || _b === void 0 ? void 0 : _b.physics) === null || _c === void 0 ? void 0 : _c.properties)
145
+ ? (_e = (_d = gameObject.components) === null || _d === void 0 ? void 0 : _d.physics) === null || _e === void 0 ? void 0 : _e.properties
169
146
  : undefined;
170
- const modelUrl = (_g = (_f = (_e = gameObject.components) === null || _e === void 0 ? void 0 : _e.model) === null || _f === void 0 ? void 0 : _f.properties) === null || _g === void 0 ? void 0 : _g.filename;
147
+ const modelUrl = (_h = (_g = (_f = gameObject.components) === null || _f === void 0 ? void 0 : _f.model) === null || _g === void 0 ? void 0 : _g.properties) === null || _h === void 0 ? void 0 : _h.filename;
171
148
  const instances = useMemo(() => buildRepeatedInstances(gameObject, parentMatrix, modelUrl, physicsProps), [gameObject, modelUrl, parentMatrix, physicsProps]);
172
149
  const groupRef = useRef(null);
173
150
  const clickValid = useRef(false);
@@ -178,22 +155,23 @@ function InstancedNode({ gameObject, parentMatrix = IDENTITY, editMode, register
178
155
  }
179
156
  }, [gameObject.id, registerRef, editMode]);
180
157
  if (editMode) {
181
- return (_jsxs(_Fragment, { children: [_jsx("group", { ref: groupRef, position: localTransform.position, rotation: localTransform.rotation, scale: localTransform.scale, onPointerDown: (e) => { e.stopPropagation(); clickValid.current = true; }, onPointerMove: () => { clickValid.current = false; }, onPointerUp: (e) => {
158
+ return (_jsxs(_Fragment, { children: [_jsx("group", { ref: groupRef, position: localTransform.position, rotation: localTransform.rotation, scale: localTransform.scale, onPointerDown: isLocked ? undefined : (e) => { e.stopPropagation(); clickValid.current = true; }, onPointerMove: isLocked ? undefined : () => { clickValid.current = false; }, onPointerUp: isLocked ? undefined : (e) => {
182
159
  if (clickValid.current) {
183
160
  e.stopPropagation();
184
161
  onSelect === null || onSelect === void 0 ? void 0 : onSelect(gameObject.id);
185
162
  onClick === null || onClick === void 0 ? void 0 : onClick(e, gameObject);
186
163
  }
187
164
  clickValid.current = false;
188
- }, children: _jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) }) }), instances.map(instance => (_jsx(GameInstance, { id: instance.id, sourceId: gameObject.id, modelUrl: instance.modelUrl, position: instance.position, rotation: instance.rotation, scale: instance.scale, physics: instance.physics }, instance.id)))] }));
165
+ }, children: _jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) }) }), instances.map(instance => (_jsx(GameInstance, { id: instance.id, sourceId: gameObject.id, clickable: clickable, modelUrl: instance.modelUrl, position: instance.position, rotation: instance.rotation, scale: instance.scale, locked: isLocked, physics: instance.physics }, instance.id)))] }));
189
166
  }
190
- return (_jsx(_Fragment, { children: instances.map(instance => (_jsx(GameInstance, { id: instance.id, sourceId: gameObject.id, modelUrl: instance.modelUrl, position: instance.position, rotation: instance.rotation, scale: instance.scale, physics: instance.physics }, instance.id))) }));
167
+ return (_jsx(_Fragment, { children: instances.map(instance => (_jsx(GameInstance, { id: instance.id, sourceId: gameObject.id, clickable: clickable, modelUrl: instance.modelUrl, position: instance.position, rotation: instance.rotation, scale: instance.scale, locked: isLocked, physics: instance.physics }, instance.id))) }));
191
168
  }
192
169
  function StandardNode({ gameObject, selectedId, onSelect, onClick, registerRef, registerRigidBodyRef, loadedModels, loadedTextures, editMode, parentMatrix = IDENTITY, }) {
193
170
  var _a, _b, _c, _d, _e;
194
171
  const groupRef = useRef(null);
195
172
  const helperRef = useRef(null);
196
173
  const clickValid = useRef(false);
174
+ const isLocked = Boolean(gameObject.locked);
197
175
  const isSelected = selectedId === gameObject.id;
198
176
  const stillInstanced = useInstanceCheck(gameObject.id);
199
177
  useHelper(editMode && isSelected ? helperRef : null, BoxHelper, "cyan");
@@ -235,26 +213,22 @@ function StandardNode({ gameObject, selectedId, onSelect, onClick, registerRef,
235
213
  loadedTextures,
236
214
  editMode,
237
215
  });
238
- const inner = (_jsx("group", { onPointerDown: editMode ? onDown : undefined, onPointerMove: editMode ? () => (clickValid.current = false) : undefined, onPointerUp: editMode ? onUp : undefined, children: renderCompositionNode(gameObject, renderCtx, parentMatrix, childNodes) }));
216
+ const inner = (_jsx("group", { onPointerDown: editMode && !isLocked ? onDown : undefined, onPointerMove: editMode && !isLocked ? () => (clickValid.current = false) : undefined, onPointerUp: editMode && !isLocked ? onUp : undefined, children: renderCompositionNode(gameObject, renderCtx, parentMatrix, childNodes) }));
239
217
  if (editMode) {
240
218
  return (_jsxs(_Fragment, { children: [_jsx("group", { ref: groupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: _jsx("mesh", { visible: false, children: _jsx("boxGeometry", { args: [0.01, 0.01, 0.01] }) }) }), _jsx("group", { ref: helperRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner }), hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View) ? (_jsx(physicsDef.View, { properties: physics.properties, position: transform.position, rotation: transform.rotation, scale: transform.scale, editMode: editMode, nodeId: gameObject.id, registerRigidBodyRef: registerRigidBodyRef, children: inner }, physicsKey)) : null] }));
241
219
  }
242
220
  if (hasPhysics && (physicsDef === null || physicsDef === void 0 ? void 0 : physicsDef.View)) {
243
221
  return (_jsx(physicsDef.View, { properties: physics.properties, position: transform.position, rotation: transform.rotation, scale: transform.scale, editMode: editMode, nodeId: gameObject.id, registerRigidBodyRef: registerRigidBodyRef, children: inner }, physicsKey));
244
222
  }
245
- return (_jsx("group", { ref: groupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, onPointerDown: onDown, onPointerMove: () => (clickValid.current = false), onPointerUp: onUp, children: inner }));
246
- }
247
- const CHILD_HOST_COMPONENT_TYPES = new Set(["Environment"]);
248
- function isChildHostType(type) {
249
- return CHILD_HOST_COMPONENT_TYPES.has(type);
223
+ return (_jsx("group", { ref: groupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, children: inner }));
250
224
  }
251
225
  function getChildHostComponents(gameObject) {
252
226
  var _a;
253
227
  return Object.entries((_a = gameObject.components) !== null && _a !== void 0 ? _a : {}).flatMap(([key, comp]) => {
254
- if (!(comp === null || comp === void 0 ? void 0 : comp.type) || !isChildHostType(comp.type))
228
+ if (!(comp === null || comp === void 0 ? void 0 : comp.type))
255
229
  return [];
256
230
  const def = getComponent(comp.type);
257
- if (!(def === null || def === void 0 ? void 0 : def.View))
231
+ if (!(def === null || def === void 0 ? void 0 : def.View) || def.nonComposable)
258
232
  return [];
259
233
  return { key, View: def.View, properties: comp.properties };
260
234
  });
@@ -327,16 +301,6 @@ function buildRepeatedInstances(gameObject, parentMatrix, modelUrl, physics) {
327
301
  }
328
302
  return instances;
329
303
  }
330
- function decompose(m) {
331
- const p = new Vector3(), q = new Quaternion(), s = new Vector3();
332
- m.decompose(p, q, s);
333
- const e = new Euler().setFromQuaternion(q);
334
- return {
335
- position: [p.x, p.y, p.z],
336
- rotation: [e.x, e.y, e.z],
337
- scale: [s.x, s.y, s.z],
338
- };
339
- }
340
304
  function getNodeTransformProps(node) {
341
305
  var _a, _b, _c, _d, _e;
342
306
  const t = (_b = (_a = node === null || node === void 0 ? void 0 : node.components) === null || _a === void 0 ? void 0 : _a.transform) === null || _b === void 0 ? void 0 : _b.properties;
@@ -346,20 +310,6 @@ function getNodeTransformProps(node) {
346
310
  scale: (_e = t === null || t === void 0 ? void 0 : t.scale) !== null && _e !== void 0 ? _e : [1, 1, 1],
347
311
  };
348
312
  }
349
- function computeParentWorldMatrix(root, targetId) {
350
- let result = null;
351
- const visit = (node, parent) => {
352
- var _a;
353
- if (node.id === targetId) {
354
- result = parent.clone();
355
- return;
356
- }
357
- const world = parent.clone().multiply(compose(node));
358
- (_a = node.children) === null || _a === void 0 ? void 0 : _a.forEach(c => !result && visit(c, world));
359
- };
360
- visit(root, IDENTITY);
361
- return result !== null && result !== void 0 ? result : IDENTITY;
362
- }
363
313
  function renderCompositionSubtree(gameObject, ctx, parentMatrix = IDENTITY) {
364
314
  if (!gameObject || gameObject.disabled)
365
315
  return null;
@@ -374,9 +324,7 @@ function renderHostedChildren(gameObject, ctx, parentMatrix) {
374
324
  }
375
325
  function renderCompositionNode(gameObject, ctx, parentMatrix, childNodes) {
376
326
  const ownContent = renderNodeOwnContent(gameObject, ctx, parentMatrix);
377
- const siblingContent = renderNodeSiblingComponents(gameObject, ctx, parentMatrix);
378
- const subtree = _jsxs(_Fragment, { children: [ownContent, siblingContent, childNodes] });
379
- return wrapWithChildHosts(gameObject, ctx, parentMatrix, subtree);
327
+ return wrapWithChildHosts(gameObject, ctx, parentMatrix, _jsxs(_Fragment, { children: [ownContent, childNodes] }));
380
328
  }
381
329
  function renderNodeOwnContent(gameObject, ctx, parentMatrix) {
382
330
  var _a, _b, _c, _d;
@@ -412,28 +360,6 @@ function renderNodeOwnContent(gameObject, ctx, parentMatrix) {
412
360
  }
413
361
  return core;
414
362
  }
415
- function renderNodeSiblingComponents(gameObject, ctx, parentMatrix) {
416
- var _a;
417
- const contextProps = {
418
- loadedModels: ctx.loadedModels,
419
- loadedTextures: ctx.loadedTextures,
420
- editMode: ctx.editMode,
421
- isSelected: ctx.selectedId === gameObject.id,
422
- nodeId: gameObject.id,
423
- parentMatrix,
424
- registerRef: ctx.registerRef,
425
- };
426
- return Object.entries((_a = gameObject.components) !== null && _a !== void 0 ? _a : {})
427
- .filter(([key]) => !getNonComposableKeys().includes(key))
428
- .flatMap(([key, comp]) => {
429
- if (!(comp === null || comp === void 0 ? void 0 : comp.type) || isChildHostType(comp.type))
430
- return [];
431
- const def = getComponent(comp.type);
432
- if (!(def === null || def === void 0 ? void 0 : def.View))
433
- return [];
434
- return _jsx(def.View, Object.assign({ properties: comp.properties }, contextProps), key);
435
- });
436
- }
437
363
  function wrapWithChildHosts(gameObject, ctx, parentMatrix, subtree) {
438
364
  const contextProps = {
439
365
  loadedModels: ctx.loadedModels,
@@ -34,7 +34,7 @@ function CameraComponentView({ properties, editMode, isSelected }) {
34
34
  cameraHelper.update();
35
35
  }
36
36
  });
37
- return (_jsxs(_Fragment, { children: [_jsx(PerspectiveCamera, { ref: (instance) => setCamera(instance), makeDefault: !editMode, fov: fov, near: near, zoom: zoom, far: far }), editMode && isSelected && cameraHelper && (_jsx("primitive", { object: cameraHelper })), editMode && !isSelected ? (_jsxs("mesh", { children: [_jsx("boxGeometry", { args: [0.34, 0.22, 0.18] }), _jsx("meshBasicMaterial", { color: "#22d3ee", wireframe: true })] })) : null] }));
37
+ return (_jsxs(_Fragment, { children: [_jsx(PerspectiveCamera, { ref: (instance) => setCamera(instance), makeDefault: !editMode, fov: fov, near: near, zoom: zoom, far: far }), editMode && isSelected && cameraHelper && (_jsx("primitive", { object: cameraHelper })), editMode ? (_jsxs("group", { children: [_jsxs("mesh", { children: [_jsx("boxGeometry", { args: [0.3, 0.3, 0.5] }), _jsx("meshBasicMaterial", { color: '#22d3ee', wireframe: true })] }), _jsxs("mesh", { position: [0, 0, -0.25], rotation: [Math.PI / 2, 0, 0], children: [_jsx("coneGeometry", { args: [0.08, 0.16, 16] }), _jsx("meshBasicMaterial", { color: '#22d3ee', wireframe: true })] })] })) : null] }));
38
38
  }
39
39
  const CameraComponent = {
40
40
  name: 'Camera',
@@ -0,0 +1,3 @@
1
+ import { Component } from './ComponentRegistry';
2
+ declare const ClickComponent: Component;
3
+ export default ClickComponent;
@@ -0,0 +1,45 @@
1
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useRef } from 'react';
3
+ import { gameEvents } from '../GameEvents';
4
+ import { FieldGroup } from './Input';
5
+ function ClickComponentEditor() {
6
+ return (_jsx(FieldGroup, { children: _jsx("div", { style: { fontSize: 12, opacity: 0.8 }, children: "Emits a click game event in play mode when this entity is clicked." }) }));
7
+ }
8
+ function ClickComponentView({ children, editMode, nodeId }) {
9
+ const clickValid = useRef(false);
10
+ const emitClick = (event) => {
11
+ if (!nodeId)
12
+ return;
13
+ gameEvents.emit('click', {
14
+ sourceEntityId: nodeId,
15
+ point: [event.point.x, event.point.y, event.point.z],
16
+ button: event.button,
17
+ altKey: event.nativeEvent.altKey,
18
+ ctrlKey: event.nativeEvent.ctrlKey,
19
+ metaKey: event.nativeEvent.metaKey,
20
+ shiftKey: event.nativeEvent.shiftKey,
21
+ });
22
+ };
23
+ if (editMode) {
24
+ return _jsx(_Fragment, { children: children });
25
+ }
26
+ return (_jsx("group", { onPointerDown: (event) => {
27
+ event.stopPropagation();
28
+ clickValid.current = true;
29
+ }, onPointerMove: () => {
30
+ clickValid.current = false;
31
+ }, onPointerUp: (event) => {
32
+ if (!clickValid.current)
33
+ return;
34
+ event.stopPropagation();
35
+ emitClick(event);
36
+ clickValid.current = false;
37
+ }, children: children }));
38
+ }
39
+ const ClickComponent = {
40
+ name: 'Click',
41
+ Editor: ClickComponentEditor,
42
+ View: ClickComponentView,
43
+ defaultProperties: {},
44
+ };
45
+ export default ClickComponent;
@@ -1,8 +1,5 @@
1
1
  const REGISTRY = {};
2
2
  export function registerComponent(component) {
3
- if (REGISTRY[component.name]) {
4
- throw new Error(`Component with name ${component.name} already registered.`);
5
- }
6
3
  REGISTRY[component.name] = component;
7
4
  }
8
5
  export function getComponent(name) {
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { useRef, useEffect, useMemo, useState } from "react";
3
3
  import { useFrame } from "@react-three/fiber";
4
4
  import { CameraHelper, Vector3 } from "three";
5
- import { FieldRenderer, Input } from "./Input";
5
+ import { FieldRenderer, NumberInput } from "./Input";
6
6
  const smallLabel = { display: 'block', fontSize: '8px', color: 'rgba(34, 211, 238, 0.5)', marginBottom: 2 };
7
7
  const directionalLightDefaults = {
8
8
  color: '#ffffff',
@@ -28,7 +28,7 @@ const directionalLightFields = [
28
28
  label: 'Shadow Camera',
29
29
  render: ({ values, onChangeMultiple }) => {
30
30
  var _a, _b, _c, _d, _e, _f;
31
- return (_jsxs("div", { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 4 }, children: [_jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Near" }), _jsx(Input, { step: 0.1, value: (_a = values.shadowCameraNear) !== null && _a !== void 0 ? _a : 0.1, onChange: v => onChangeMultiple({ shadowCameraNear: v }) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Far" }), _jsx(Input, { step: 1, value: (_b = values.shadowCameraFar) !== null && _b !== void 0 ? _b : 100, onChange: v => onChangeMultiple({ shadowCameraFar: v }) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Top" }), _jsx(Input, { step: 1, value: (_c = values.shadowCameraTop) !== null && _c !== void 0 ? _c : 30, onChange: v => onChangeMultiple({ shadowCameraTop: v }) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Bottom" }), _jsx(Input, { step: 1, value: (_d = values.shadowCameraBottom) !== null && _d !== void 0 ? _d : -30, onChange: v => onChangeMultiple({ shadowCameraBottom: v }) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Left" }), _jsx(Input, { step: 1, value: (_e = values.shadowCameraLeft) !== null && _e !== void 0 ? _e : -30, onChange: v => onChangeMultiple({ shadowCameraLeft: v }) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Right" }), _jsx(Input, { step: 1, value: (_f = values.shadowCameraRight) !== null && _f !== void 0 ? _f : 30, onChange: v => onChangeMultiple({ shadowCameraRight: v }) })] })] }));
31
+ return (_jsxs("div", { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 4 }, children: [_jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Near" }), _jsx(NumberInput, { step: 0.1, value: (_a = values.shadowCameraNear) !== null && _a !== void 0 ? _a : 0.1, onChange: v => onChangeMultiple({ shadowCameraNear: v }) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Far" }), _jsx(NumberInput, { step: 1, value: (_b = values.shadowCameraFar) !== null && _b !== void 0 ? _b : 100, onChange: v => onChangeMultiple({ shadowCameraFar: v }) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Top" }), _jsx(NumberInput, { step: 1, value: (_c = values.shadowCameraTop) !== null && _c !== void 0 ? _c : 30, onChange: v => onChangeMultiple({ shadowCameraTop: v }) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Bottom" }), _jsx(NumberInput, { step: 1, value: (_d = values.shadowCameraBottom) !== null && _d !== void 0 ? _d : -30, onChange: v => onChangeMultiple({ shadowCameraBottom: v }) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Left" }), _jsx(NumberInput, { step: 1, value: (_e = values.shadowCameraLeft) !== null && _e !== void 0 ? _e : -30, onChange: v => onChangeMultiple({ shadowCameraLeft: v }) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Right" }), _jsx(NumberInput, { step: 1, value: (_f = values.shadowCameraRight) !== null && _f !== void 0 ? _f : 30, onChange: v => onChangeMultiple({ shadowCameraRight: v }) })] })] }));
32
32
  },
33
33
  },
34
34
  {
@@ -37,7 +37,7 @@ const directionalLightFields = [
37
37
  label: 'Target Offset',
38
38
  render: ({ value, onChange }) => {
39
39
  const offset = value !== null && value !== void 0 ? value : [0, -5, 0];
40
- return (_jsxs("div", { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 4 }, children: [_jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "X" }), _jsx(Input, { step: 0.5, value: offset[0], onChange: v => onChange([v, offset[1], offset[2]]) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Y" }), _jsx(Input, { step: 0.5, value: offset[1], onChange: v => onChange([offset[0], v, offset[2]]) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Z" }), _jsx(Input, { step: 0.5, value: offset[2], onChange: v => onChange([offset[0], offset[1], v]) })] })] }));
40
+ return (_jsxs("div", { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 4 }, children: [_jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "X" }), _jsx(NumberInput, { step: 0.5, value: offset[0], onChange: v => onChange([v, offset[1], offset[2]]) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Y" }), _jsx(NumberInput, { step: 0.5, value: offset[1], onChange: v => onChange([offset[0], v, offset[2]]) })] }), _jsxs("div", { children: [_jsx("label", { style: smallLabel, children: "Z" }), _jsx(NumberInput, { step: 0.5, value: offset[2], onChange: v => onChange([offset[0], offset[1], v]) })] })] }));
41
41
  },
42
42
  },
43
43
  ];
@@ -52,12 +52,15 @@ interface InputProps {
52
52
  min?: number;
53
53
  max?: number;
54
54
  style?: React.CSSProperties;
55
- label?: string;
56
55
  }
57
- export declare function Input({ value, onChange, step, min, max, style, label }: InputProps): import("react/jsx-runtime").JSX.Element;
56
+ export declare function NumberInput({ value, onChange, step, min, max, style }: InputProps): import("react/jsx-runtime").JSX.Element;
58
57
  export declare function Label({ children }: {
59
58
  children: React.ReactNode;
60
59
  }): import("react/jsx-runtime").JSX.Element;
60
+ export declare function FieldRow({ label, children, }: {
61
+ label: string;
62
+ children: React.ReactNode;
63
+ }): import("react/jsx-runtime").JSX.Element;
61
64
  export declare function Vector3Input({ label, value, onChange, snap, labelExtra }: {
62
65
  label: string;
63
66
  value: [number, number, number];
@@ -49,23 +49,59 @@ function getStepPrecision(step) {
49
49
  const decimal = stepString.split('.')[1];
50
50
  return (_a = decimal === null || decimal === void 0 ? void 0 : decimal.length) !== null && _a !== void 0 ? _a : 0;
51
51
  }
52
- export function Input({ value, onChange, step, min, max, style, label }) {
52
+ function clampNumber(value, min, max) {
53
+ if (min !== undefined && value < min)
54
+ return min;
55
+ if (max !== undefined && value > max)
56
+ return max;
57
+ return value;
58
+ }
59
+ function normalizeNumber(value, step, min, max) {
60
+ const clampedValue = clampNumber(value, min, max);
61
+ const normalizedStep = getNumericStep(step, 0);
62
+ if (!Number.isFinite(normalizedStep) || normalizedStep <= 0)
63
+ return clampedValue;
64
+ const precision = getStepPrecision(normalizedStep);
65
+ const stepBase = min !== null && min !== void 0 ? min : 0;
66
+ const steppedValue = stepBase + Math.round((clampedValue - stepBase) / normalizedStep) * normalizedStep;
67
+ return Number(steppedValue.toFixed(precision));
68
+ }
69
+ function isIncompleteNumber(value) {
70
+ return value === '' || value === '-' || value === '.' || value === '-.';
71
+ }
72
+ export function NumberInput({ value, onChange, step, min, max, style }) {
53
73
  const [draft, setDraft] = useState(() => value.toString());
74
+ const [isFocused, setIsFocused] = useState(false);
54
75
  useEffect(() => {
55
- setDraft(value.toString());
56
- }, [value]);
76
+ if (!isFocused) {
77
+ setDraft(value.toString());
78
+ }
79
+ }, [value, isFocused]);
57
80
  const handleChange = (e) => {
58
81
  const inputValue = e.target.value;
59
82
  setDraft(inputValue);
60
- const num = parseFloat(inputValue);
83
+ if (isIncompleteNumber(inputValue))
84
+ return;
85
+ const num = Number(inputValue);
61
86
  if (Number.isFinite(num)) {
62
- onChange(num);
87
+ onChange(clampNumber(num, min, max));
63
88
  }
64
89
  };
65
90
  const handleBlur = () => {
66
- const num = parseFloat(draft);
91
+ setIsFocused(false);
92
+ if (isIncompleteNumber(draft)) {
93
+ setDraft(value.toString());
94
+ return;
95
+ }
96
+ const num = Number(draft);
67
97
  if (!Number.isFinite(num)) {
68
98
  setDraft(value.toString());
99
+ return;
100
+ }
101
+ const normalized = normalizeNumber(num, step, min, max);
102
+ setDraft(normalized.toString());
103
+ if (normalized !== value) {
104
+ onChange(normalized);
69
105
  }
70
106
  };
71
107
  const dragState = useRef(null);
@@ -88,15 +124,10 @@ export function Input({ value, onChange, step, min, max, style, label }) {
88
124
  scrubStep /= 10;
89
125
  if (e.altKey)
90
126
  scrubStep *= 10;
91
- const precision = getStepPrecision(scrubStep);
92
127
  const deltaSteps = Math.round(dx / 8);
93
- let nextValue = startValue + deltaSteps * scrubStep;
94
- // Apply min/max constraints
95
- if (min !== undefined && nextValue < min)
96
- nextValue = min;
97
- if (max !== undefined && nextValue > max)
98
- nextValue = max;
99
- setDraft(nextValue.toFixed(precision));
128
+ const rawValue = startValue + deltaSteps * scrubStep;
129
+ const nextValue = normalizeNumber(rawValue, scrubStep, min, max);
130
+ setDraft(nextValue.toString());
100
131
  onChange(nextValue);
101
132
  };
102
133
  const endScrub = (e) => {
@@ -106,26 +137,23 @@ export function Input({ value, onChange, step, min, max, style, label }) {
106
137
  document.body.style.cursor = "";
107
138
  e.currentTarget.releasePointerCapture(e.pointerId);
108
139
  };
109
- if (label) {
110
- return (_jsxs("div", { style: {
111
- display: 'flex',
112
- alignItems: 'center',
113
- justifyContent: 'space-between',
114
- }, children: [_jsx("span", { style: Object.assign(Object.assign({}, styles.label), { marginBottom: 0, userSelect: 'none', flex: '0 0 auto', minWidth: 20 }), children: label }), _jsx("input", { type: "text", value: draft, onChange: handleChange, onBlur: handleBlur, onKeyDown: e => {
115
- if (e.key === 'Enter') {
116
- e.target.blur();
117
- }
118
- }, step: step, min: min, max: max, style: Object.assign(Object.assign(Object.assign({}, styles.input), { cursor: 'ew-resize' }), style), onPointerDown: startScrub, onPointerMove: onScrubMove, onPointerUp: endScrub })] }));
119
- }
120
- return (_jsx("input", { type: "text", value: draft, onChange: handleChange, onBlur: handleBlur, onKeyDown: e => {
140
+ return (_jsx("input", { type: "number", inputMode: "decimal", value: draft, onChange: handleChange, onFocus: () => setIsFocused(true), onBlur: handleBlur, onKeyDown: e => {
121
141
  if (e.key === 'Enter') {
122
142
  e.target.blur();
123
143
  }
124
- }, step: step, min: min, max: max, style: Object.assign(Object.assign(Object.assign({}, styles.input), { cursor: 'ew-resize' }), style), onPointerDown: startScrub, onPointerMove: onScrubMove, onPointerUp: endScrub }));
144
+ }, step: step !== null && step !== void 0 ? step : 'any', min: min, max: max, style: Object.assign(Object.assign(Object.assign({}, styles.input), { cursor: 'ew-resize' }), style), onPointerDown: startScrub, onPointerMove: onScrubMove, onPointerUp: endScrub }));
125
145
  }
126
146
  export function Label({ children }) {
127
147
  return _jsx("label", { style: styles.label, children: children });
128
148
  }
149
+ export function FieldRow({ label, children, }) {
150
+ return (_jsxs("div", { style: {
151
+ display: 'flex',
152
+ alignItems: 'center',
153
+ justifyContent: 'space-between',
154
+ gap: 8,
155
+ }, children: [_jsx("span", { style: Object.assign(Object.assign({}, styles.label), { marginBottom: 0, userSelect: 'none', flex: '0 0 auto', minWidth: 20 }), children: label }), children] }));
156
+ }
129
157
  export function Vector3Input({ label, value, onChange, snap, labelExtra }) {
130
158
  const snapValue = (num) => {
131
159
  if (!snap)
@@ -133,18 +161,23 @@ export function Vector3Input({ label, value, onChange, snap, labelExtra }) {
133
161
  return Math.round(num / snap) * snap;
134
162
  };
135
163
  const [draft, setDraft] = useState(() => value.map(v => v.toString()));
136
- // Sync external changes (gizmo, undo, etc.)
137
164
  useEffect(() => {
138
165
  setDraft(value.map(v => v.toString()));
139
- }, [value[0], value[1], value[2]]);
166
+ }, [value]);
140
167
  const dragState = useRef(null);
141
168
  const commit = (index) => {
142
- const num = parseFloat(draft[index]);
143
- if (Number.isFinite(num)) {
144
- const next = [...value];
145
- next[index] = snapValue(num);
146
- onChange(next);
169
+ if (isIncompleteNumber(draft[index])) {
170
+ setDraft(value.map(v => v.toString()));
171
+ return;
147
172
  }
173
+ const num = Number(draft[index]);
174
+ if (!Number.isFinite(num)) {
175
+ setDraft(value.map(v => v.toString()));
176
+ return;
177
+ }
178
+ const next = [...value];
179
+ next[index] = snapValue(num);
180
+ onChange(next);
148
181
  };
149
182
  const startScrub = (e, index) => {
150
183
  dragState.current = {
@@ -171,7 +204,7 @@ export function Vector3Input({ label, value, onChange, snap, labelExtra }) {
171
204
  next[index] = nextValue;
172
205
  setDraft(d => {
173
206
  const copy = [...d];
174
- copy[index] = nextValue.toFixed(3);
207
+ copy[index] = nextValue.toString();
175
208
  return copy;
176
209
  });
177
210
  onChange(next);
@@ -216,7 +249,7 @@ export function Vector3Input({ label, value, onChange, snap, labelExtra }) {
216
249
  width: '100%',
217
250
  minWidth: 0,
218
251
  cursor: 'inherit',
219
- }, type: "text", value: draft[index], onChange: e => {
252
+ }, type: "number", inputMode: "decimal", step: snap !== null && snap !== void 0 ? snap : 'any', value: draft[index], onChange: e => {
220
253
  const next = [...draft];
221
254
  next[index] = e.target.value;
222
255
  setDraft(next);
@@ -263,7 +296,7 @@ export function FieldGroup({ children }) {
263
296
  }
264
297
  export function NumberField({ name, label, values, onChange, fallback = 0, step, min, max, style, }) {
265
298
  var _a;
266
- return (_jsx(Input, { label: label, value: (_a = values[name]) !== null && _a !== void 0 ? _a : fallback, onChange: bindFieldChange(name, onChange), step: step, min: min, max: max, style: style }));
299
+ return (_jsx(FieldRow, { label: label, children: _jsx(NumberInput, { value: (_a = values[name]) !== null && _a !== void 0 ? _a : fallback, onChange: bindFieldChange(name, onChange), step: step, min: min, max: max, style: style }) }));
267
300
  }
268
301
  export function StringField({ name, label, values, onChange, fallback = '', placeholder, }) {
269
302
  var _a;
@@ -296,7 +329,7 @@ export function FieldRenderer({ fields, values, onChange }) {
296
329
  case 'vector3':
297
330
  return (_jsx(Vector3Input, { label: field.label, value: value !== null && value !== void 0 ? value : [0, 0, 0], onChange: v => updateField(field.name, v), snap: field.snap }, field.name));
298
331
  case 'number':
299
- return (_jsx(Input, { label: field.label, value: value !== null && value !== void 0 ? value : 0, onChange: v => updateField(field.name, v), min: field.min, max: field.max, step: field.step }, field.name));
332
+ return (_jsx(FieldRow, { label: field.label, children: _jsx(NumberInput, { value: value !== null && value !== void 0 ? value : 0, onChange: v => updateField(field.name, v), min: field.min, max: field.max, step: field.step }) }, field.name));
300
333
  case 'string':
301
334
  return (_jsx(StringInput, { label: field.label, value: value !== null && value !== void 0 ? value : '', onChange: v => updateField(field.name, v), placeholder: field.placeholder }, field.name));
302
335
  case 'color':