react-three-game 0.0.56 → 0.0.57

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 (59) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.js +1 -1
  3. package/dist/shared/GameCanvas.js +1 -3
  4. package/dist/tools/assetviewer/page.js +35 -14
  5. package/dist/tools/prefabeditor/Dropdown.d.ts +15 -0
  6. package/dist/tools/prefabeditor/Dropdown.js +82 -0
  7. package/dist/tools/prefabeditor/EditorContext.d.ts +5 -0
  8. package/dist/tools/prefabeditor/EditorTree.js +138 -56
  9. package/dist/tools/prefabeditor/EditorUI.js +1 -1
  10. package/dist/tools/prefabeditor/PrefabEditor.d.ts +1 -0
  11. package/dist/tools/prefabeditor/PrefabEditor.js +13 -2
  12. package/dist/tools/prefabeditor/PrefabRoot.d.ts +1 -0
  13. package/dist/tools/prefabeditor/PrefabRoot.js +120 -34
  14. package/dist/tools/prefabeditor/components/AmbientLightComponent.js +3 -7
  15. package/dist/tools/prefabeditor/components/CameraComponent.d.ts +3 -0
  16. package/dist/tools/prefabeditor/components/CameraComponent.js +25 -0
  17. package/dist/tools/prefabeditor/components/DirectionalLightComponent.js +2 -2
  18. package/dist/tools/prefabeditor/components/EnvironmentComponent.d.ts +3 -0
  19. package/dist/tools/prefabeditor/components/EnvironmentComponent.js +15 -0
  20. package/dist/tools/prefabeditor/components/GeometryComponent.js +46 -46
  21. package/dist/tools/prefabeditor/components/Input.d.ts +51 -1
  22. package/dist/tools/prefabeditor/components/Input.js +73 -21
  23. package/dist/tools/prefabeditor/components/MaterialComponent.d.ts +8 -2
  24. package/dist/tools/prefabeditor/components/MaterialComponent.js +122 -14
  25. package/dist/tools/prefabeditor/components/ModelComponent.js +44 -3
  26. package/dist/tools/prefabeditor/components/PhysicsComponent.js +16 -81
  27. package/dist/tools/prefabeditor/components/SpotLightComponent.js +4 -12
  28. package/dist/tools/prefabeditor/components/TextComponent.js +7 -53
  29. package/dist/tools/prefabeditor/components/TransformComponent.js +18 -8
  30. package/dist/tools/prefabeditor/components/index.js +5 -1
  31. package/dist/tools/prefabeditor/styles.d.ts +5 -2
  32. package/dist/tools/prefabeditor/styles.js +7 -3
  33. package/dist/tools/prefabeditor/utils.d.ts +4 -3
  34. package/dist/tools/prefabeditor/utils.js +53 -5
  35. package/package.json +1 -1
  36. package/src/index.ts +7 -0
  37. package/src/shared/GameCanvas.tsx +0 -3
  38. package/src/tools/assetviewer/page.tsx +77 -45
  39. package/src/tools/prefabeditor/Dropdown.tsx +112 -0
  40. package/src/tools/prefabeditor/EditorContext.tsx +5 -0
  41. package/src/tools/prefabeditor/EditorTree.tsx +234 -101
  42. package/src/tools/prefabeditor/EditorUI.tsx +1 -1
  43. package/src/tools/prefabeditor/PrefabEditor.tsx +17 -4
  44. package/src/tools/prefabeditor/PrefabRoot.tsx +208 -58
  45. package/src/tools/prefabeditor/components/AmbientLightComponent.tsx +5 -11
  46. package/src/tools/prefabeditor/components/CameraComponent.tsx +80 -0
  47. package/src/tools/prefabeditor/components/DirectionalLightComponent.tsx +2 -2
  48. package/src/tools/prefabeditor/components/EnvironmentComponent.tsx +47 -0
  49. package/src/tools/prefabeditor/components/GeometryComponent.tsx +69 -63
  50. package/src/tools/prefabeditor/components/Input.tsx +220 -27
  51. package/src/tools/prefabeditor/components/MaterialComponent.tsx +178 -16
  52. package/src/tools/prefabeditor/components/ModelComponent.tsx +51 -4
  53. package/src/tools/prefabeditor/components/PhysicsComponent.tsx +44 -85
  54. package/src/tools/prefabeditor/components/SpotLightComponent.tsx +11 -17
  55. package/src/tools/prefabeditor/components/TextComponent.tsx +58 -57
  56. package/src/tools/prefabeditor/components/TransformComponent.tsx +61 -9
  57. package/src/tools/prefabeditor/components/index.ts +5 -1
  58. package/src/tools/prefabeditor/styles.ts +7 -3
  59. package/src/tools/prefabeditor/utils.ts +55 -4
@@ -21,12 +21,14 @@ const DEFAULT_PREFAB = {
21
21
  }
22
22
  }
23
23
  };
24
- const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, onPrefabChange, children }, ref) => {
24
+ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, onPrefabChange, uiPlugins, children }, ref) => {
25
25
  const [editMode, setEditMode] = useState(true);
26
26
  const [loadedPrefab, setLoadedPrefab] = useState(initialPrefab !== null && initialPrefab !== void 0 ? initialPrefab : DEFAULT_PREFAB);
27
27
  const [selectedId, setSelectedId] = useState(null);
28
28
  const [transformMode, setTransformMode] = useState("translate");
29
29
  const [snapResolution, setSnapResolution] = useState(0);
30
+ const [positionSnap, setPositionSnap] = useState(0.5);
31
+ const [rotationSnap, setRotationSnap] = useState(Math.PI / 4);
30
32
  const [history, setHistory] = useState([loadedPrefab]);
31
33
  const [historyIndex, setHistoryIndex] = useState(0);
32
34
  const throttleRef = useRef(null);
@@ -107,6 +109,10 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, onPr
107
109
  filename: `${loadedPrefab.name || 'scene'}.glb`
108
110
  });
109
111
  };
112
+ const handleFocusNode = (nodeId) => {
113
+ var _a;
114
+ (_a = prefabRootRef.current) === null || _a === void 0 ? void 0 : _a.focusNode(nodeId);
115
+ };
110
116
  useEffect(() => {
111
117
  const canvas = document.querySelector('canvas');
112
118
  if (canvas)
@@ -180,9 +186,14 @@ const PrefabEditor = forwardRef(({ basePath, initialPrefab, physics = true, onPr
180
186
  setTransformMode,
181
187
  snapResolution,
182
188
  setSnapResolution,
189
+ positionSnap,
190
+ setPositionSnap,
191
+ rotationSnap,
192
+ setRotationSnap,
193
+ onFocusNode: handleFocusNode,
183
194
  onScreenshot: handleScreenshot,
184
195
  onExportGLB: handleExportGLB
185
- }, children: [_jsx(GameCanvas, { children: physics ? (_jsx(Physics, { debug: editMode, paused: editMode, children: content })) : content }), _jsx("div", { style: toolbar.panel, children: _jsx("button", { style: base.btn, onClick: () => setEditMode(!editMode), children: editMode ? "▶" : "⏸" }) }), editMode && _jsx(EditorUI, { prefabData: loadedPrefab, setPrefabData: updatePrefab, selectedId: selectedId, setSelectedId: setSelectedId, basePath: basePath, onUndo: undo, onRedo: redo, canUndo: historyIndex > 0, canRedo: historyIndex < history.length - 1 })] });
196
+ }, children: [_jsx(GameCanvas, { camera: { position: [0, 5, 15] }, children: physics ? (_jsx(Physics, { debug: editMode, paused: editMode, children: content })) : content }), _jsxs("div", { style: toolbar.panel, children: [_jsx("button", { style: base.btn, onClick: () => setEditMode(!editMode), children: editMode ? "▶" : "⏸" }), uiPlugins] }), _jsx(EditorUI, { prefabData: loadedPrefab, setPrefabData: updatePrefab, selectedId: selectedId, setSelectedId: setSelectedId, basePath: basePath, onUndo: undo, onRedo: redo, canUndo: historyIndex > 0, canRedo: historyIndex < history.length - 1 })] });
186
197
  });
187
198
  PrefabEditor.displayName = "PrefabEditor";
188
199
  export default PrefabEditor;
@@ -6,6 +6,7 @@ export interface PrefabRootRef {
6
6
  rigidBodyRefs: Map<string, any>;
7
7
  injectModel: (filename: string, model: Object3D) => void;
8
8
  injectTexture: (filename: string, file: File) => void;
9
+ focusNode: (nodeId: string) => void;
9
10
  }
10
11
  export declare const PrefabRoot: import("react").ForwardRefExoticComponent<{
11
12
  editMode?: boolean;
@@ -15,24 +15,28 @@ import { getComponent, registerComponent, getNonComposableKeys } from "./compone
15
15
  import components from "./components";
16
16
  import { loadModel } from "../dragdrop/modelLoader";
17
17
  import { GameInstance, GameInstanceProvider, useInstanceCheck } from "./InstanceProvider";
18
- import { updateNode } from "./utils";
18
+ import { focusCameraOnObject, updateNode } from "./utils";
19
19
  import { EditorContext } from "./EditorContext";
20
20
  components.forEach(registerComponent);
21
21
  const IDENTITY = new Matrix4();
22
22
  export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selectedId, onSelect, onClick, basePath = "" }, ref) => {
23
- var _a, _b;
23
+ var _a, _b, _c, _d;
24
24
  // optional editor context
25
25
  const editorContext = useContext(EditorContext);
26
26
  const transformMode = (_a = editorContext === null || editorContext === void 0 ? void 0 : editorContext.transformMode) !== null && _a !== void 0 ? _a : "translate";
27
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;
28
30
  // prefab root state
29
31
  const [models, setModels] = useState({});
30
32
  const [textures, setTextures] = useState({});
31
33
  const loading = useRef(new Set());
34
+ const failedTextures = useRef(new Set());
32
35
  const objectRefs = useRef({});
33
36
  const rigidBodyRefs = useRef(new Map());
34
37
  const [selectedObject, setSelectedObject] = useState(null);
35
38
  const rootRef = useRef(null);
39
+ const controlsRef = useRef(null);
36
40
  const injectModel = useCallback((filename, model) => {
37
41
  setModels(m => (Object.assign(Object.assign({}, m), { [filename]: model })));
38
42
  }, []);
@@ -50,7 +54,15 @@ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selected
50
54
  root: rootRef.current,
51
55
  rigidBodyRefs: rigidBodyRefs.current,
52
56
  injectModel,
53
- injectTexture
57
+ injectTexture,
58
+ focusNode: (nodeId) => {
59
+ const object = objectRefs.current[nodeId];
60
+ const controls = controlsRef.current;
61
+ const camera = controls === null || controls === void 0 ? void 0 : controls.object;
62
+ if (!object || !controls || !camera)
63
+ return;
64
+ focusCameraOnObject(object, camera, controls.target, () => { var _a; return (_a = controls.update) === null || _a === void 0 ? void 0 : _a.call(controls); });
65
+ }
54
66
  }), [injectModel, injectTexture]);
55
67
  const registerRef = useCallback((id, obj) => {
56
68
  objectRefs.current[id] = obj;
@@ -92,11 +104,13 @@ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selected
92
104
  const modelsToLoad = new Set();
93
105
  const texturesToLoad = new Set();
94
106
  walk(data.root, node => {
95
- var _a, _b, _c, _d, _e, _f;
107
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
96
108
  ((_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) &&
97
109
  modelsToLoad.add(node.components.model.properties.filename);
98
110
  ((_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) &&
99
111
  texturesToLoad.add(node.components.material.properties.texture);
112
+ ((_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) &&
113
+ texturesToLoad.add(node.components.material.properties.normalMapTexture);
100
114
  });
101
115
  modelsToLoad.forEach((file) => __awaiter(void 0, void 0, void 0, function* () {
102
116
  if (models[file] || loading.current.has(file))
@@ -111,7 +125,7 @@ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selected
111
125
  }));
112
126
  const loader = new TextureLoader();
113
127
  texturesToLoad.forEach(file => {
114
- if (textures[file] || loading.current.has(file))
128
+ if (textures[file] || loading.current.has(file) || failedTextures.current.has(file))
115
129
  return;
116
130
  loading.current.add(file);
117
131
  // Handle full URLs (http/https) or regular paths
@@ -124,12 +138,13 @@ export const PrefabRoot = forwardRef(({ editMode, data, onPrefabChange, selected
124
138
  tex.colorSpace = SRGBColorSpace;
125
139
  setTextures(t => (Object.assign(Object.assign({}, t), { [file]: tex })));
126
140
  }, undefined, (err) => {
127
- console.error(`Failed to load texture: ${path}`, err);
141
+ console.warn(`Failed to load texture: ${path}`, err);
128
142
  loading.current.delete(file);
143
+ failedTextures.current.add(file);
129
144
  });
130
145
  });
131
146
  }, [data, models, textures]);
132
- return (_jsxs("group", { ref: rootRef, children: [_jsx(GameInstanceProvider, { models: models, 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: models, loadedTextures: textures, editMode: editMode, parentMatrix: IDENTITY }) }), editMode && (_jsxs(_Fragment, { children: [_jsx(MapControls, { makeDefault: true }), selectedObject && (_jsx(TransformControls, { object: selectedObject, mode: transformMode, space: "local", onObjectChange: onTransformChange, translationSnap: snapResolution > 0 ? snapResolution : undefined, rotationSnap: snapResolution > 0 ? snapResolution : undefined, scaleSnap: snapResolution > 0 ? snapResolution : undefined }, `transform-${snapResolution}`))] }))] }));
147
+ return (_jsxs("group", { ref: rootRef, children: [_jsx(GameInstanceProvider, { models: models, 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: models, loadedTextures: textures, 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}`))] }))] }));
133
148
  });
134
149
  export function GameObjectRenderer(props) {
135
150
  var _a, _b, _c;
@@ -187,7 +202,7 @@ function InstancedNode({ gameObject, parentMatrix = IDENTITY, editMode, register
187
202
  return (_jsx(GameInstance, { id: gameObject.id, modelUrl: (_k = (_j = (_h = gameObject.components) === null || _h === void 0 ? void 0 : _h.model) === null || _j === void 0 ? void 0 : _j.properties) === null || _k === void 0 ? void 0 : _k.filename, position: worldPosition, rotation: worldRotation, scale: worldScale, physics: physicsProps }));
188
203
  }
189
204
  function StandardNode({ gameObject, selectedId, onSelect, onClick, registerRef, registerRigidBodyRef, loadedModels, loadedTextures, editMode, parentMatrix = IDENTITY, }) {
190
- var _a, _b, _c, _d, _e, _f;
205
+ var _a, _b, _c, _d, _e;
191
206
  const groupRef = useRef(null);
192
207
  const helperRef = useRef(null);
193
208
  const clickValid = useRef(false);
@@ -219,7 +234,20 @@ function StandardNode({ gameObject, selectedId, onSelect, onClick, registerRef,
219
234
  const physicsDef = hasPhysics ? getComponent("Physics") : null;
220
235
  const isInstanced = (_e = (_d = (_c = gameObject.components) === null || _c === void 0 ? void 0 : _c.model) === null || _d === void 0 ? void 0 : _d.properties) === null || _e === void 0 ? void 0 : _e.instanced;
221
236
  const physicsKey = `physics_${gameObject.id}_${isInstanced ? 'instanced' : 'standard'}`;
222
- const inner = (_jsxs("group", { onPointerDown: editMode ? onDown : undefined, onPointerMove: editMode ? () => (clickValid.current = false) : undefined, onPointerUp: editMode ? onUp : undefined, children: [renderCoreNode(gameObject, { loadedModels, loadedTextures, editMode, registerRef }, parentMatrix), (_f = gameObject.children) === null || _f === void 0 ? void 0 : _f.map(child => (_jsx(GameObjectRenderer, { gameObject: child, selectedId: selectedId, onSelect: onSelect, onClick: onClick, registerRef: registerRef, registerRigidBodyRef: registerRigidBodyRef, loadedModels: loadedModels, loadedTextures: loadedTextures, editMode: editMode, parentMatrix: world }, child.id)))] }));
237
+ const renderCtx = createRenderContext(loadedModels, loadedTextures, editMode, selectedId, registerRef);
238
+ const childNodes = getChildHostComponents(gameObject).length > 0
239
+ ? renderHostedChildren(gameObject, renderCtx, world)
240
+ : renderSceneChildren(gameObject, world, {
241
+ selectedId,
242
+ onSelect,
243
+ onClick,
244
+ registerRef,
245
+ registerRigidBodyRef,
246
+ loadedModels,
247
+ loadedTextures,
248
+ editMode,
249
+ });
250
+ const inner = (_jsx("group", { onPointerDown: editMode ? onDown : undefined, onPointerMove: editMode ? () => (clickValid.current = false) : undefined, onPointerUp: editMode ? onUp : undefined, children: renderCompositionNode(gameObject, renderCtx, parentMatrix, childNodes) }));
223
251
  if (editMode) {
224
252
  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] }));
225
253
  }
@@ -228,6 +256,28 @@ function StandardNode({ gameObject, selectedId, onSelect, onClick, registerRef,
228
256
  }
229
257
  return (_jsx("group", { ref: groupRef, position: transform.position, rotation: transform.rotation, scale: transform.scale, onPointerDown: onDown, onPointerMove: () => (clickValid.current = false), onPointerUp: onUp, children: inner }));
230
258
  }
259
+ const CHILD_HOST_COMPONENT_TYPES = new Set(["Environment"]);
260
+ function isChildHostType(type) {
261
+ return CHILD_HOST_COMPONENT_TYPES.has(type);
262
+ }
263
+ function getChildHostComponents(gameObject) {
264
+ var _a;
265
+ return Object.entries((_a = gameObject.components) !== null && _a !== void 0 ? _a : {}).flatMap(([key, comp]) => {
266
+ if (!(comp === null || comp === void 0 ? void 0 : comp.type) || !isChildHostType(comp.type))
267
+ return [];
268
+ const def = getComponent(comp.type);
269
+ if (!(def === null || def === void 0 ? void 0 : def.View))
270
+ return [];
271
+ return { key, View: def.View, properties: comp.properties };
272
+ });
273
+ }
274
+ function createRenderContext(loadedModels, loadedTextures, editMode, selectedId, registerRef) {
275
+ return { loadedModels, loadedTextures, editMode, selectedId, registerRef };
276
+ }
277
+ function renderSceneChildren(gameObject, parentMatrix, props) {
278
+ var _a;
279
+ return (_a = gameObject.children) === null || _a === void 0 ? void 0 : _a.map(child => _jsx(GameObjectRenderer, { gameObject: child, selectedId: props.selectedId, onSelect: props.onSelect, onClick: props.onClick, registerRef: props.registerRef, registerRigidBodyRef: props.registerRigidBodyRef, loadedModels: props.loadedModels, loadedTextures: props.loadedTextures, editMode: props.editMode, parentMatrix: parentMatrix }, child.id));
280
+ }
231
281
  function walk(node, fn) {
232
282
  var _a;
233
283
  fn(node);
@@ -270,7 +320,25 @@ function computeParentWorldMatrix(root, targetId) {
270
320
  visit(root, IDENTITY);
271
321
  return result !== null && result !== void 0 ? result : IDENTITY;
272
322
  }
273
- function renderCoreNode(gameObject, ctx, parentMatrix) {
323
+ function renderCompositionSubtree(gameObject, ctx, parentMatrix = IDENTITY) {
324
+ if (!gameObject || gameObject.disabled)
325
+ return null;
326
+ const transform = getNodeTransformProps(gameObject);
327
+ const world = parentMatrix.clone().multiply(compose(gameObject));
328
+ const childNodes = renderHostedChildren(gameObject, ctx, world);
329
+ return (_jsx("group", { position: transform.position, rotation: transform.rotation, scale: transform.scale, children: renderCompositionNode(gameObject, ctx, parentMatrix, childNodes) }, gameObject.id));
330
+ }
331
+ function renderHostedChildren(gameObject, ctx, parentMatrix) {
332
+ var _a;
333
+ return (_a = gameObject.children) === null || _a === void 0 ? void 0 : _a.map(child => renderCompositionSubtree(child, ctx, parentMatrix));
334
+ }
335
+ function renderCompositionNode(gameObject, ctx, parentMatrix, childNodes) {
336
+ const ownContent = renderNodeOwnContent(gameObject, ctx, parentMatrix);
337
+ const siblingContent = renderNodeSiblingComponents(gameObject, ctx, parentMatrix);
338
+ const subtree = _jsxs(_Fragment, { children: [ownContent, siblingContent, childNodes] });
339
+ return wrapWithChildHosts(gameObject, ctx, parentMatrix, subtree);
340
+ }
341
+ function renderNodeOwnContent(gameObject, ctx, parentMatrix) {
274
342
  var _a, _b, _c, _d;
275
343
  const geometry = (_a = gameObject.components) === null || _a === void 0 ? void 0 : _a.geometry;
276
344
  const material = (_b = gameObject.components) === null || _b === void 0 ? void 0 : _b.material;
@@ -284,41 +352,59 @@ function renderCoreNode(gameObject, ctx, parentMatrix) {
284
352
  loadedModels: ctx.loadedModels,
285
353
  loadedTextures: ctx.loadedTextures,
286
354
  editMode: ctx.editMode,
355
+ isSelected: ctx.selectedId === gameObject.id,
356
+ nodeId: gameObject.id,
287
357
  parentMatrix,
288
358
  registerRef: ctx.registerRef,
289
359
  };
290
- const wrappers = [];
291
- const leaves = [];
292
- if (gameObject.components) {
293
- Object.entries(gameObject.components)
294
- .filter(([k]) => !getNonComposableKeys().includes(k))
295
- .forEach(([key, comp]) => {
296
- if (!(comp === null || comp === void 0 ? void 0 : comp.type))
297
- return;
298
- const def = getComponent(comp.type);
299
- if (!(def === null || def === void 0 ? void 0 : def.View))
300
- return;
301
- if (def.View.toString().includes("children")) {
302
- wrappers.push({ key, View: def.View, properties: comp.properties });
303
- }
304
- else {
305
- leaves.push(_jsx(def.View, Object.assign({ properties: comp.properties }, contextProps), key));
306
- }
307
- });
308
- }
309
360
  let core;
310
361
  if (model && (modelDef === null || modelDef === void 0 ? void 0 : modelDef.View)) {
311
- core = (_jsxs(modelDef.View, Object.assign({ properties: model.properties }, contextProps, { children: [material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, Object.assign({ properties: material.properties }, contextProps), "material")), leaves] })));
362
+ core = (_jsx(modelDef.View, Object.assign({ properties: model.properties }, contextProps, { children: material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, Object.assign({ properties: material.properties }, contextProps), "material")) })));
312
363
  }
313
364
  else if (geometry && (geometryDef === null || geometryDef === void 0 ? void 0 : geometryDef.View)) {
314
- core = (_jsxs("mesh", { castShadow: true, receiveShadow: true, children: [_jsx(geometryDef.View, Object.assign({ properties: geometry.properties }, contextProps)), material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, Object.assign({ properties: material.properties }, contextProps), "material")), leaves] }));
365
+ core = (_jsxs("mesh", { castShadow: true, receiveShadow: true, children: [_jsx(geometryDef.View, Object.assign({ properties: geometry.properties }, contextProps)), material && (materialDef === null || materialDef === void 0 ? void 0 : materialDef.View) && (_jsx(materialDef.View, Object.assign({ properties: material.properties }, contextProps), "material"))] }));
315
366
  }
316
367
  else if (text && (textDef === null || textDef === void 0 ? void 0 : textDef.View)) {
317
- core = (_jsxs(_Fragment, { children: [_jsx(textDef.View, Object.assign({ properties: text.properties }, contextProps)), leaves] }));
368
+ core = (_jsx(_Fragment, { children: _jsx(textDef.View, Object.assign({ properties: text.properties }, contextProps)) }));
318
369
  }
319
370
  else {
320
- core = _jsx(_Fragment, { children: leaves });
371
+ core = null;
321
372
  }
322
- return wrappers.reduce((acc, { key, View, properties }) => (_jsx(View, Object.assign({ properties: properties }, contextProps, { children: acc }), key)), core);
373
+ return core;
374
+ }
375
+ function renderNodeSiblingComponents(gameObject, ctx, parentMatrix) {
376
+ var _a;
377
+ const contextProps = {
378
+ loadedModels: ctx.loadedModels,
379
+ loadedTextures: ctx.loadedTextures,
380
+ editMode: ctx.editMode,
381
+ isSelected: ctx.selectedId === gameObject.id,
382
+ nodeId: gameObject.id,
383
+ parentMatrix,
384
+ registerRef: ctx.registerRef,
385
+ };
386
+ return Object.entries((_a = gameObject.components) !== null && _a !== void 0 ? _a : {})
387
+ .filter(([key]) => !getNonComposableKeys().includes(key))
388
+ .flatMap(([key, comp]) => {
389
+ if (!(comp === null || comp === void 0 ? void 0 : comp.type) || isChildHostType(comp.type))
390
+ return [];
391
+ const def = getComponent(comp.type);
392
+ if (!(def === null || def === void 0 ? void 0 : def.View))
393
+ return [];
394
+ return _jsx(def.View, Object.assign({ properties: comp.properties }, contextProps), key);
395
+ });
396
+ }
397
+ function wrapWithChildHosts(gameObject, ctx, parentMatrix, subtree) {
398
+ const contextProps = {
399
+ loadedModels: ctx.loadedModels,
400
+ loadedTextures: ctx.loadedTextures,
401
+ editMode: ctx.editMode,
402
+ isSelected: ctx.selectedId === gameObject.id,
403
+ nodeId: gameObject.id,
404
+ parentMatrix,
405
+ registerRef: ctx.registerRef,
406
+ };
407
+ const childHosts = getChildHostComponents(gameObject);
408
+ return childHosts.reduce((acc, { key, View, properties }) => (_jsx(View, Object.assign({ properties: properties }, contextProps, { children: acc }), key)), subtree);
323
409
  }
324
410
  export default PrefabRoot;
@@ -1,11 +1,7 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { FieldRenderer } from "./Input";
3
- const ambientLightFields = [
4
- { name: 'color', type: 'color', label: 'Color' },
5
- { name: 'intensity', type: 'number', label: 'Intensity', step: 0.1, min: 0 },
6
- ];
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ColorField, FieldGroup, NumberField } from "./Input";
7
3
  function AmbientLightComponentEditor({ component, onUpdate, }) {
8
- return (_jsx(FieldRenderer, { fields: ambientLightFields, values: component.properties, onChange: onUpdate }));
4
+ return (_jsxs(FieldGroup, { children: [_jsx(ColorField, { name: "color", label: "Color", values: component.properties, onChange: onUpdate }), _jsx(NumberField, { name: "intensity", label: "Intensity", values: component.properties, onChange: onUpdate, min: 0, step: 0.1, fallback: 1 })] }));
9
5
  }
10
6
  function AmbientLightComponentView({ properties }) {
11
7
  const { color = '#ffffff', intensity = 1 } = properties;
@@ -0,0 +1,3 @@
1
+ import { Component } from './ComponentRegistry';
2
+ declare const CameraComponent: Component;
3
+ export default CameraComponent;
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { PerspectiveCamera, useHelper } from '@react-three/drei';
3
+ import { useRef } from 'react';
4
+ import { CameraHelper } from 'three';
5
+ import { FieldGroup, NumberField } from './Input';
6
+ function CameraComponentEditor({ component, onUpdate }) {
7
+ return (_jsxs(FieldGroup, { children: [_jsx(NumberField, { name: "fov", label: "FOV", values: component.properties, onChange: onUpdate, fallback: 50, min: 1, max: 179, step: 1 }), _jsx(NumberField, { name: "near", label: "Near", values: component.properties, onChange: onUpdate, fallback: 0.1, min: 0.001, step: 0.1 }), _jsx(NumberField, { name: "zoom", label: "Zoom", values: component.properties, onChange: onUpdate, fallback: 1, min: 0.01, step: 0.1 }), _jsx(NumberField, { name: "far", label: "Far", values: component.properties, onChange: onUpdate, fallback: 1000, min: 0.1, step: 1 })] }));
8
+ }
9
+ function CameraComponentView({ properties, editMode, isSelected }) {
10
+ var _a, _b, _c, _d;
11
+ const fov = (_a = properties === null || properties === void 0 ? void 0 : properties.fov) !== null && _a !== void 0 ? _a : 50;
12
+ const near = (_b = properties === null || properties === void 0 ? void 0 : properties.near) !== null && _b !== void 0 ? _b : 0.1;
13
+ const zoom = (_c = properties === null || properties === void 0 ? void 0 : properties.zoom) !== null && _c !== void 0 ? _c : 1;
14
+ const far = (_d = properties === null || properties === void 0 ? void 0 : properties.far) !== null && _d !== void 0 ? _d : 1000;
15
+ const cameraRef = useRef(null);
16
+ useHelper(editMode && isSelected ? cameraRef : null, CameraHelper);
17
+ return (_jsxs(_Fragment, { children: [_jsx(PerspectiveCamera, { ref: cameraRef, makeDefault: !editMode, fov: fov, near: near, zoom: zoom, far: far }), editMode && !isSelected ? (_jsxs("mesh", { children: [_jsx("boxGeometry", { args: [0.34, 0.22, 0.18] }), _jsx("meshBasicMaterial", { color: "#22d3ee", wireframe: true })] })) : null] }));
18
+ }
19
+ const CameraComponent = {
20
+ name: 'Camera',
21
+ Editor: CameraComponentEditor,
22
+ View: CameraComponentView,
23
+ defaultProperties: {},
24
+ };
25
+ export default CameraComponent;
@@ -31,7 +31,7 @@ const directionalLightFields = [
31
31
  function DirectionalLightComponentEditor({ component, onUpdate }) {
32
32
  return (_jsx(FieldRenderer, { fields: directionalLightFields, values: component.properties, onChange: onUpdate }));
33
33
  }
34
- function DirectionalLightView({ properties, editMode }) {
34
+ function DirectionalLightView({ properties, editMode, isSelected }) {
35
35
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
36
36
  const color = (_a = properties.color) !== null && _a !== void 0 ? _a : '#ffffff';
37
37
  const intensity = (_b = properties.intensity) !== null && _b !== void 0 ? _b : 1.0;
@@ -61,7 +61,7 @@ function DirectionalLightView({ properties, editMode }) {
61
61
  // Target is positioned relative to light's world position
62
62
  targetRef.current.position.set(lightWorldPos.x + targetOffset[0], lightWorldPos.y + targetOffset[1], lightWorldPos.z + targetOffset[2]);
63
63
  });
64
- return (_jsxs(_Fragment, { children: [_jsx("directionalLight", { ref: directionalLightRef, color: color, intensity: intensity, castShadow: castShadow, "shadow-mapSize-width": shadowMapSize, "shadow-mapSize-height": shadowMapSize, "shadow-camera-near": shadowCameraNear, "shadow-camera-far": shadowCameraFar, "shadow-camera-top": shadowCameraTop, "shadow-camera-bottom": shadowCameraBottom, "shadow-camera-left": shadowCameraLeft, "shadow-camera-right": shadowCameraRight, "shadow-bias": -0.001, "shadow-normalBias": 0.02 }), _jsx("object3D", { ref: targetRef }), editMode && (_jsxs(_Fragment, { children: [_jsxs("mesh", { children: [_jsx("sphereGeometry", { args: [0.3, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true })] }), _jsxs("mesh", { position: targetOffset, children: [_jsx("sphereGeometry", { args: [0.2, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true, opacity: 0.5, transparent: true })] }), _jsxs("line", { children: [_jsx("bufferGeometry", { onUpdate: (geo) => {
64
+ return (_jsxs(_Fragment, { children: [_jsx("directionalLight", { ref: directionalLightRef, color: color, intensity: intensity, castShadow: castShadow, "shadow-mapSize-width": shadowMapSize, "shadow-mapSize-height": shadowMapSize, "shadow-camera-near": shadowCameraNear, "shadow-camera-far": shadowCameraFar, "shadow-camera-top": shadowCameraTop, "shadow-camera-bottom": shadowCameraBottom, "shadow-camera-left": shadowCameraLeft, "shadow-camera-right": shadowCameraRight, "shadow-bias": -0.001, "shadow-normalBias": 0.02 }), _jsx("object3D", { ref: targetRef }), editMode && isSelected && (_jsxs(_Fragment, { children: [_jsxs("mesh", { children: [_jsx("sphereGeometry", { args: [0.3, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true })] }), _jsxs("mesh", { position: targetOffset, children: [_jsx("sphereGeometry", { args: [0.2, 8, 6] }), _jsx("meshBasicMaterial", { color: color, wireframe: true, opacity: 0.5, transparent: true })] }), _jsxs("line", { children: [_jsx("bufferGeometry", { onUpdate: (geo) => {
65
65
  const points = [
66
66
  new Vector3(0, 0, 0),
67
67
  new Vector3(targetOffset[0], targetOffset[1], targetOffset[2])
@@ -0,0 +1,3 @@
1
+ import { Component } from './ComponentRegistry';
2
+ declare const EnvironmentComponent: Component;
3
+ export default EnvironmentComponent;
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Environment } from '@react-three/drei';
3
+ import { FieldGroup, NumberField } from './Input';
4
+ function EnvironmentView({ properties, children, editMode, loadedTextures, loadedModels, }) {
5
+ const { intensity = 1, resolution = 256 } = properties;
6
+ const assetRevision = `${Object.keys(loadedTextures !== null && loadedTextures !== void 0 ? loadedTextures : {}).sort().join('|')}::${Object.keys(loadedModels !== null && loadedModels !== void 0 ? loadedModels : {}).sort().join('|')}`;
7
+ return (_jsx(Environment, { background: true, environmentIntensity: intensity, resolution: resolution, frames: editMode ? undefined : 1, children: children }, assetRevision));
8
+ }
9
+ const EnvironmentComponent = {
10
+ name: 'Environment',
11
+ Editor: ({ component, onUpdate }) => (_jsxs(FieldGroup, { children: [_jsx(NumberField, { name: "intensity", label: "Intensity", values: component.properties, onChange: onUpdate, min: 0, step: 0.1, fallback: 1 }), _jsx(NumberField, { name: "resolution", label: "Resolution", values: component.properties, onChange: onUpdate, min: 64, step: 64, fallback: 256 })] })),
12
+ View: EnvironmentView,
13
+ defaultProperties: {},
14
+ };
15
+ export default EnvironmentComponent;
@@ -1,67 +1,67 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { FieldRenderer, Input } from "./Input";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { FieldGroup, NumberField, SelectField } from "./Input";
3
3
  const GEOMETRY_ARGS = {
4
4
  box: {
5
- labels: ["Width", "Height", "Depth"],
6
- defaults: [1, 1, 1],
5
+ fields: [
6
+ { name: 'width', label: 'Width', defaultValue: 1, min: 0.01, step: 0.1 },
7
+ { name: 'height', label: 'Height', defaultValue: 1, min: 0.01, step: 0.1 },
8
+ { name: 'depth', label: 'Depth', defaultValue: 1, min: 0.01, step: 0.1 },
9
+ ],
7
10
  },
8
11
  sphere: {
9
- labels: ["Radius", "Width Segments", "Height Segments"],
10
- defaults: [1, 32, 16],
12
+ fields: [
13
+ { name: 'radius', label: 'Radius', defaultValue: 1, min: 0.01, step: 0.1 },
14
+ { name: 'widthSegments', label: 'Width Segments', defaultValue: 32, min: 3, step: 1 },
15
+ { name: 'heightSegments', label: 'Height Segments', defaultValue: 16, min: 2, step: 1 },
16
+ ],
11
17
  },
12
18
  plane: {
13
- labels: ["Width", "Height"],
14
- defaults: [1, 1],
19
+ fields: [
20
+ { name: 'width', label: 'Width', defaultValue: 1, min: 0.01, step: 0.1 },
21
+ { name: 'height', label: 'Height', defaultValue: 1, min: 0.01, step: 0.1 },
22
+ ],
15
23
  },
16
24
  cylinder: {
17
- labels: ["Radius Top", "Radius Bottom", "Height", "Radial Segments"],
18
- defaults: [1, 1, 1, 32],
25
+ fields: [
26
+ { name: 'radiusTop', label: 'Radius Top', defaultValue: 1, min: 0.01, step: 0.1 },
27
+ { name: 'radiusBottom', label: 'Radius Bottom', defaultValue: 1, min: 0.01, step: 0.1 },
28
+ { name: 'height', label: 'Height', defaultValue: 1, min: 0.01, step: 0.1 },
29
+ { name: 'radialSegments', label: 'Radial Segments', defaultValue: 32, min: 3, step: 1 },
30
+ ],
19
31
  },
20
32
  };
33
+ function getDefaultArgs(geometryType) {
34
+ var _a, _b;
35
+ return ((_b = (_a = GEOMETRY_ARGS[geometryType]) === null || _a === void 0 ? void 0 : _a.fields) !== null && _b !== void 0 ? _b : []).map(field => field.defaultValue);
36
+ }
21
37
  function GeometryComponentEditor({ component, onUpdate, }) {
22
- const { geometryType, args = [] } = component.properties;
23
- const schema = GEOMETRY_ARGS[geometryType];
24
- const fields = [
25
- {
26
- name: 'geometryType',
27
- type: 'select',
28
- label: 'Type',
29
- options: [
30
- { value: 'box', label: 'Box' },
31
- { value: 'sphere', label: 'Sphere' },
32
- { value: 'plane', label: 'Plane' },
33
- { value: 'cylinder', label: 'Cylinder' },
34
- ],
35
- },
36
- {
37
- name: 'args',
38
- type: 'custom',
39
- label: '',
40
- render: ({ values, onChangeMultiple }) => {
41
- const currentType = values.geometryType;
42
- const currentSchema = GEOMETRY_ARGS[currentType];
43
- const currentArgs = values.args || currentSchema.defaults;
44
- return (_jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: currentSchema.labels.map((label, i) => {
45
- var _a;
46
- return (_jsx(Input, { label: label, value: (_a = currentArgs[i]) !== null && _a !== void 0 ? _a : currentSchema.defaults[i], step: 0.1, min: 0.01, onChange: value => {
47
- const next = [...currentArgs];
48
- next[i] = value;
49
- onChangeMultiple({ args: next });
50
- } }, label));
51
- }) }));
52
- },
53
- },
54
- ];
38
+ var _a, _b, _c;
39
+ const geometryType = (_a = component.properties.geometryType) !== null && _a !== void 0 ? _a : 'box';
40
+ const schema = (_b = GEOMETRY_ARGS[geometryType]) !== null && _b !== void 0 ? _b : GEOMETRY_ARGS.box;
41
+ const args = (_c = component.properties.args) !== null && _c !== void 0 ? _c : getDefaultArgs(geometryType);
55
42
  // Handle geometry type change to reset args
56
43
  const handleChange = (newValues) => {
57
44
  if ('geometryType' in newValues && newValues.geometryType !== geometryType) {
58
- onUpdate({ geometryType: newValues.geometryType, args: GEOMETRY_ARGS[newValues.geometryType].defaults });
45
+ onUpdate({ geometryType: newValues.geometryType, args: getDefaultArgs(newValues.geometryType) });
59
46
  }
60
47
  else {
61
48
  onUpdate(newValues);
62
49
  }
63
50
  };
64
- return (_jsx(FieldRenderer, { fields: fields, values: component.properties, onChange: handleChange }));
51
+ const updateArg = (index, value) => {
52
+ const next = [...args];
53
+ next[index] = value;
54
+ onUpdate({ args: next });
55
+ };
56
+ return (_jsxs(FieldGroup, { children: [_jsx(SelectField, { name: "geometryType", label: "Type", values: component.properties, onChange: handleChange, options: [
57
+ { value: 'box', label: 'Box' },
58
+ { value: 'sphere', label: 'Sphere' },
59
+ { value: 'plane', label: 'Plane' },
60
+ { value: 'cylinder', label: 'Cylinder' },
61
+ ] }), schema.fields.map((field, index) => {
62
+ var _a;
63
+ return (_jsx(NumberField, { name: field.name, label: field.label, values: { [field.name]: (_a = args[index]) !== null && _a !== void 0 ? _a : field.defaultValue }, onChange: (next) => updateArg(index, next[field.name]), fallback: field.defaultValue, min: field.min, step: field.step }, field.name));
64
+ })] }));
65
65
  }
66
66
  // View for Geometry component
67
67
  function GeometryComponentView({ properties, children }) {
@@ -87,7 +87,7 @@ const GeometryComponent = {
87
87
  nonComposable: true,
88
88
  defaultProperties: {
89
89
  geometryType: 'box',
90
- args: GEOMETRY_ARGS.box.defaults,
90
+ args: getDefaultArgs('box'),
91
91
  }
92
92
  };
93
93
  export default GeometryComponent;
@@ -58,11 +58,12 @@ export declare function Input({ value, onChange, step, min, max, style, label }:
58
58
  export declare function Label({ children }: {
59
59
  children: React.ReactNode;
60
60
  }): import("react/jsx-runtime").JSX.Element;
61
- export declare function Vector3Input({ label, value, onChange, snap }: {
61
+ export declare function Vector3Input({ label, value, onChange, snap, labelExtra }: {
62
62
  label: string;
63
63
  value: [number, number, number];
64
64
  onChange: (v: [number, number, number]) => void;
65
65
  snap?: number;
66
+ labelExtra?: React.ReactNode;
66
67
  }): import("react/jsx-runtime").JSX.Element;
67
68
  export declare function ColorInput({ label, value, onChange }: {
68
69
  label?: string;
@@ -89,6 +90,55 @@ export declare function SelectInput({ label, value, onChange, options }: {
89
90
  label: string;
90
91
  }[];
91
92
  }): import("react/jsx-runtime").JSX.Element;
93
+ interface BoundFieldProps {
94
+ name: string;
95
+ values: Record<string, any>;
96
+ onChange: (values: Record<string, any>) => void;
97
+ }
98
+ interface BoundNumberFieldProps extends BoundFieldProps {
99
+ label: string;
100
+ fallback?: number;
101
+ step?: string | number;
102
+ min?: number;
103
+ max?: number;
104
+ style?: React.CSSProperties;
105
+ }
106
+ interface BoundStringFieldProps extends BoundFieldProps {
107
+ label: string;
108
+ fallback?: string;
109
+ placeholder?: string;
110
+ }
111
+ interface BoundColorFieldProps extends BoundFieldProps {
112
+ label: string;
113
+ fallback?: string;
114
+ }
115
+ interface BoundBooleanFieldProps extends BoundFieldProps {
116
+ label: string;
117
+ fallback?: boolean;
118
+ }
119
+ interface BoundSelectFieldProps extends BoundFieldProps {
120
+ label: string;
121
+ fallback?: string;
122
+ options: {
123
+ value: string;
124
+ label: string;
125
+ }[];
126
+ }
127
+ interface BoundVector3FieldProps extends BoundFieldProps {
128
+ label: string;
129
+ fallback?: [number, number, number];
130
+ snap?: number;
131
+ labelExtra?: React.ReactNode;
132
+ }
133
+ export declare function FieldGroup({ children }: {
134
+ children: React.ReactNode;
135
+ }): import("react/jsx-runtime").JSX.Element;
136
+ export declare function NumberField({ name, label, values, onChange, fallback, step, min, max, style, }: BoundNumberFieldProps): import("react/jsx-runtime").JSX.Element;
137
+ export declare function StringField({ name, label, values, onChange, fallback, placeholder, }: BoundStringFieldProps): import("react/jsx-runtime").JSX.Element;
138
+ export declare function ColorField({ name, label, values, onChange, fallback, }: BoundColorFieldProps): import("react/jsx-runtime").JSX.Element;
139
+ export declare function BooleanField({ name, label, values, onChange, fallback, }: BoundBooleanFieldProps): import("react/jsx-runtime").JSX.Element;
140
+ export declare function SelectField({ name, label, values, onChange, fallback, options, }: BoundSelectFieldProps): import("react/jsx-runtime").JSX.Element;
141
+ export declare function Vector3Field({ name, label, values, onChange, fallback, snap, labelExtra, }: BoundVector3FieldProps): import("react/jsx-runtime").JSX.Element;
92
142
  interface FieldRendererProps {
93
143
  fields: FieldDefinition[];
94
144
  values: Record<string, any>;