@nice2dev/ui-3d 1.2.1 → 1.2.2

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 (54) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/cjs/material/NiceMaterialEditor.js +1 -0
  3. package/dist/cjs/model/ModelEditor.js +7 -1
  4. package/dist/cjs/model/ModelEditorMenuBar.js +5 -5
  5. package/dist/cjs/model/ModelEditorRightPanel.js +13 -9
  6. package/dist/cjs/model/ModelEditorSubComponents.js +9 -3
  7. package/dist/cjs/model/ModelEditorTimeline.js +3 -1
  8. package/dist/cjs/model/ModelEditorToolbar.js +1 -1
  9. package/dist/cjs/model/ModelEditorViewport.js +5 -2
  10. package/dist/cjs/model/ModelViewer.js +4 -4
  11. package/dist/cjs/model/NiceArmatureEditor.js +4 -4
  12. package/dist/cjs/model/NiceMorphTargetEditor.js +3 -1
  13. package/dist/cjs/model/NiceOctree.js +7 -4
  14. package/dist/cjs/model/NicePhysicsSimulation.js +4 -4
  15. package/dist/cjs/model/NiceProceduralGeometry.js +3 -3
  16. package/dist/cjs/model/NiceTerrainEditor.js +2 -2
  17. package/dist/cjs/model/NiceWeightPainter.js +2 -2
  18. package/dist/cjs/model/NiceXRPreview.js +6 -3
  19. package/dist/cjs/model/useModelEditor.js +92 -73
  20. package/dist/cjs/model/useModelViewer.js +13 -6
  21. package/dist/cjs/particle/NiceParticleEditor.js +10 -3
  22. package/dist/cjs/rendering/NiceCascadedShadows.js +1 -1
  23. package/dist/cjs/rendering/NiceRenderExport.js +2 -2
  24. package/dist/cjs/rendering/NiceSSAO.js +2 -1
  25. package/dist/cjs/rendering/NiceSSR.js +2 -1
  26. package/dist/cjs/rendering/NiceWebGPURenderer.js +2 -1
  27. package/dist/cjs/uv/NiceUVEditor.js +3 -3
  28. package/dist/esm/material/NiceMaterialEditor.js +1 -0
  29. package/dist/esm/model/ModelEditor.js +7 -1
  30. package/dist/esm/model/ModelEditorMenuBar.js +5 -5
  31. package/dist/esm/model/ModelEditorRightPanel.js +13 -9
  32. package/dist/esm/model/ModelEditorSubComponents.js +9 -3
  33. package/dist/esm/model/ModelEditorTimeline.js +3 -1
  34. package/dist/esm/model/ModelEditorToolbar.js +1 -1
  35. package/dist/esm/model/ModelEditorViewport.js +5 -2
  36. package/dist/esm/model/ModelViewer.js +4 -4
  37. package/dist/esm/model/NiceArmatureEditor.js +4 -4
  38. package/dist/esm/model/NiceMorphTargetEditor.js +3 -1
  39. package/dist/esm/model/NiceOctree.js +7 -4
  40. package/dist/esm/model/NicePhysicsSimulation.js +4 -4
  41. package/dist/esm/model/NiceProceduralGeometry.js +3 -3
  42. package/dist/esm/model/NiceTerrainEditor.js +2 -2
  43. package/dist/esm/model/NiceWeightPainter.js +2 -2
  44. package/dist/esm/model/NiceXRPreview.js +6 -3
  45. package/dist/esm/model/useModelEditor.js +92 -73
  46. package/dist/esm/model/useModelViewer.js +13 -6
  47. package/dist/esm/particle/NiceParticleEditor.js +10 -3
  48. package/dist/esm/rendering/NiceCascadedShadows.js +1 -1
  49. package/dist/esm/rendering/NiceRenderExport.js +2 -2
  50. package/dist/esm/rendering/NiceSSAO.js +2 -1
  51. package/dist/esm/rendering/NiceSSR.js +2 -1
  52. package/dist/esm/rendering/NiceWebGPURenderer.js +2 -1
  53. package/dist/esm/uv/NiceUVEditor.js +3 -3
  54. package/package.json +2 -2
@@ -244,22 +244,22 @@ const NiceArmatureEditor = ({ skeleton, skinnedMesh: _skinnedMesh, scene, camera
244
244
  } }), jsx("button", { type: "button", disabled: !newBoneName.trim(), onClick: () => {
245
245
  addBone(selectedBoneId, newBoneName.trim());
246
246
  setNewBoneName('');
247
- }, children: "+ Add" }), jsx("button", { type: "button", onClick: () => removeBone(selectedBoneId), "aria-label": t('armatureEditor.removeBone', 'Remove bone'), children: jsx(NiceIcon, { name: "trash", size: 14 }) })] })), selectedBone && (jsxs("div", { style: {
247
+ }, children: t('armatureEditor.add', '+ Add') }), jsx("button", { type: "button", onClick: () => removeBone(selectedBoneId), "aria-label": t('armatureEditor.removeBone', 'Remove bone'), children: jsx(NiceIcon, { name: "trash", size: 14 }) })] })), selectedBone && (jsxs("div", { style: {
248
248
  display: 'grid',
249
249
  gap: 4,
250
250
  fontSize: 12,
251
251
  border: '1px solid var(--nice-border, #e2e8f0)',
252
252
  borderRadius: 6,
253
253
  padding: 8,
254
- }, children: [jsx("strong", { children: selectedBone.name }), jsxs("label", { children: ["Rotation X: ", (selectedBone.localRotation.x * (180 / Math.PI)).toFixed(1), "\u00B0"] }), jsx("input", { type: "range", min: -180, max: 180, value: selectedBone.localRotation.x * (180 / Math.PI), onChange: (e) => {
254
+ }, children: [jsx("strong", { children: selectedBone.name }), jsx("label", { children: t('armatureEditor.rotationX', 'Rotation X: {x}°').replace('{x}', String((selectedBone.localRotation.x * (180 / Math.PI)).toFixed(1))) }), jsx("input", { type: "range", min: -180, max: 180, value: selectedBone.localRotation.x * (180 / Math.PI), onChange: (e) => {
255
255
  const rot = selectedBone.localRotation.clone();
256
256
  rot.x = parseFloat(e.target.value) * (Math.PI / 180);
257
257
  setBoneRotation(selectedBone.id, rot);
258
- } }), jsxs("label", { children: ["Rotation Y: ", (selectedBone.localRotation.y * (180 / Math.PI)).toFixed(1), "\u00B0"] }), jsx("input", { type: "range", min: -180, max: 180, value: selectedBone.localRotation.y * (180 / Math.PI), onChange: (e) => {
258
+ } }), jsx("label", { children: t('armatureEditor.rotationY', 'Rotation Y: {y}°').replace('{y}', String((selectedBone.localRotation.y * (180 / Math.PI)).toFixed(1))) }), jsx("input", { type: "range", min: -180, max: 180, value: selectedBone.localRotation.y * (180 / Math.PI), onChange: (e) => {
259
259
  const rot = selectedBone.localRotation.clone();
260
260
  rot.y = parseFloat(e.target.value) * (Math.PI / 180);
261
261
  setBoneRotation(selectedBone.id, rot);
262
- } }), jsxs("label", { children: ["Rotation Z: ", (selectedBone.localRotation.z * (180 / Math.PI)).toFixed(1), "\u00B0"] }), jsx("input", { type: "range", min: -180, max: 180, value: selectedBone.localRotation.z * (180 / Math.PI), onChange: (e) => {
262
+ } }), jsx("label", { children: t('armatureEditor.rotationZ', 'Rotation Z: {z}°').replace('{z}', String((selectedBone.localRotation.z * (180 / Math.PI)).toFixed(1))) }), jsx("input", { type: "range", min: -180, max: 180, value: selectedBone.localRotation.z * (180 / Math.PI), onChange: (e) => {
263
263
  const rot = selectedBone.localRotation.clone();
264
264
  rot.z = parseFloat(e.target.value) * (Math.PI / 180);
265
265
  setBoneRotation(selectedBone.id, rot);
@@ -212,7 +212,9 @@ const NiceMorphTargetEditor = ({ mesh, onInfluenceChange, className = '', tutori
212
212
  padding: '4px 8px',
213
213
  border: '1px solid var(--nice-border, #e2e8f0)',
214
214
  borderRadius: 4,
215
- } }), jsx("button", { type: "button", onClick: handleCreate, children: "+ Add" })] }), animations.length > 0 && (jsxs("div", { style: { marginTop: 8 }, children: [jsx("strong", { style: { fontSize: 12 }, children: t('morphTargetEditor.animations', 'Animations') }), animations.map((anim) => (jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4, marginTop: 4 }, children: [jsx("span", { style: { flex: 1 }, children: anim.name }), jsx("button", { type: "button", onClick: () => (isAnimating ? stopAnimation() : startAnimation(anim)), style: { fontSize: 11 }, "aria-label": isAnimating ? 'Stop' : 'Play', children: jsx(NiceIcon, { name: isAnimating ? 'stop' : 'play', size: 14 }) }), jsx("button", { type: "button", onClick: () => removeAnimation(anim.name), style: { fontSize: 11 }, "aria-label": t('morphTargetEditor.removeAnimation', 'Remove animation'), children: jsx(NiceIcon, { name: "trash", size: 14 }) })] }, anim.name)))] }))] }));
215
+ } }), jsx("button", { type: "button", onClick: handleCreate, children: t('morphTargetEditor.add', '+ Add') })] }), animations.length > 0 && (jsxs("div", { style: { marginTop: 8 }, children: [jsx("strong", { style: { fontSize: 12 }, children: t('morphTargetEditor.animations', 'Animations') }), animations.map((anim) => (jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4, marginTop: 4 }, children: [jsx("span", { style: { flex: 1 }, children: anim.name }), jsx("button", { type: "button", onClick: () => (isAnimating ? stopAnimation() : startAnimation(anim)), style: { fontSize: 11 }, "aria-label": isAnimating
216
+ ? t('morphTargetEditor.stop', 'Stop')
217
+ : t('morphTargetEditor.play', 'Play'), children: jsx(NiceIcon, { name: isAnimating ? 'stop' : 'play', size: 14 }) }), jsx("button", { type: "button", onClick: () => removeAnimation(anim.name), style: { fontSize: 11 }, "aria-label": t('morphTargetEditor.removeAnimation', 'Remove animation'), children: jsx(NiceIcon, { name: "trash", size: 14 }) })] }, anim.name)))] }))] }));
216
218
  };
217
219
 
218
220
  export { NiceMorphTargetEditor, useMorphTargetEditor };
@@ -327,19 +327,22 @@ function useOctree(scene, configOverride) {
327
327
  const NiceOctree = ({ scene, config, showDebug: initialShowDebug, className = '', }) => {
328
328
  const { t } = useNiceTranslation();
329
329
  const octree = useOctree(scene, config);
330
+ const { setShowDebug } = octree;
330
331
  useEffect(() => {
331
332
  if (initialShowDebug !== undefined) {
332
- octree.setShowDebug(initialShowDebug);
333
+ setShowDebug(initialShowDebug);
333
334
  }
334
- }, [initialShowDebug]);
335
- return (jsxs("div", { className: `nice-octree ${className}`, style: { display: 'grid', gap: 8, fontSize: 13 }, children: [jsxs("strong", { children: [jsx(NiceIcon, { name: "tree", size: 14 }), " Spatial Index (Octree)"] }), jsxs("button", { type: "button", onClick: octree.build, style: {
335
+ }, [initialShowDebug, setShowDebug]);
336
+ return (jsxs("div", { className: `nice-octree ${className}`, style: { display: 'grid', gap: 8, fontSize: 13 }, children: [jsxs("strong", { children: [jsx(NiceIcon, { name: "tree", size: 14 }), ' ', t('octree.spatialIndexOctree', 'Spatial Index (Octree)')] }), jsxs("button", { type: "button", onClick: octree.build, style: {
336
337
  background: 'var(--nice-color-accent, #6366f1)',
337
338
  color: 'var(--nice-text-on-primary, #fff)',
338
339
  border: 'none',
339
340
  borderRadius: 4,
340
341
  padding: '6px 12px',
341
342
  cursor: 'pointer',
342
- }, children: [jsx(NiceIcon, { name: "zap", size: 14 }), " ", t('octree.buildOctree', 'Build Octree')] }), jsxs("div", { style: { fontSize: 11, opacity: 0.7 }, children: ["Nodes: ", octree.nodeCount, " | Objects: ", octree.objectCount] }), jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 6 }, children: [jsx("input", { type: "checkbox", checked: octree.showDebug, onChange: (e) => octree.setShowDebug(e.target.checked) }), t('octree.showDebugWireframes', 'Show debug wireframes')] })] }));
343
+ }, children: [jsx(NiceIcon, { name: "zap", size: 14 }), " ", t('octree.buildOctree', 'Build Octree')] }), jsx("div", { style: { fontSize: 11, opacity: 0.7 }, children: t('octree.nodesObjects', 'Nodes: {nodeCount} · Objects: {objectCount}')
344
+ .replace('{nodeCount}', String(octree.nodeCount))
345
+ .replace('{objectCount}', String(octree.objectCount)) }), jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 6 }, children: [jsx("input", { type: "checkbox", checked: octree.showDebug, onChange: (e) => octree.setShowDebug(e.target.checked) }), t('octree.showDebugWireframes', 'Show debug wireframes')] })] }));
343
346
  };
344
347
 
345
348
  export { NiceOctree, useOctree };
@@ -275,20 +275,20 @@ const NicePhysicsSimulation = ({ scene, selectedObject, onSimulationStep, classN
275
275
  borderRadius: 4,
276
276
  padding: '5px 8px',
277
277
  cursor: 'pointer',
278
- }, children: isRunning ? (jsxs(Fragment, { children: [jsx(NiceIcon, { name: "pause", size: 14 }), " ", t('physicsSimulation.pause', 'Pause')] })) : (jsxs(Fragment, { children: [jsx(NiceIcon, { name: "play", size: 14 }), " ", t('physicsSimulation.play', 'Play')] })) }), jsxs("button", { type: "button", onClick: step, disabled: isRunning, style: { padding: '5px 8px', cursor: 'pointer' }, children: [jsx(NiceIcon, { name: "skip-forward", size: 14 }), " ", t('physicsSimulation.step', 'Step')] }), jsxs("button", { type: "button", onClick: reset, style: { padding: '5px 8px', cursor: 'pointer' }, children: [jsx(NiceIcon, { name: "refresh", size: 14 }), " ", t('physicsSimulation.reset', 'Reset')] })] }), jsxs("label", { children: ["Gravity Y: ", worldConfig.gravity.y.toFixed(1), jsx("input", { type: "range", min: -20, max: 0, step: 0.1, value: worldConfig.gravity.y, onChange: (e) => setWorldConfig({ gravity: new THREE.Vector3(0, parseFloat(e.target.value), 0) }), style: { width: '100%' } })] }), selectedObject && (jsxs("fieldset", { style: {
278
+ }, children: isRunning ? (jsxs(Fragment, { children: [jsx(NiceIcon, { name: "pause", size: 14 }), " ", t('physicsSimulation.pause', 'Pause')] })) : (jsxs(Fragment, { children: [jsx(NiceIcon, { name: "play", size: 14 }), " ", t('physicsSimulation.play', 'Play')] })) }), jsxs("button", { type: "button", onClick: step, disabled: isRunning, style: { padding: '5px 8px', cursor: 'pointer' }, children: [jsx(NiceIcon, { name: "skip-forward", size: 14 }), " ", t('physicsSimulation.step', 'Step')] }), jsxs("button", { type: "button", onClick: reset, style: { padding: '5px 8px', cursor: 'pointer' }, children: [jsx(NiceIcon, { name: "refresh", size: 14 }), " ", t('physicsSimulation.reset', 'Reset')] })] }), jsxs("label", { children: [t('physicsSimulation.gravityY', 'Gravity Y: {y}').replace('{y}', String(worldConfig.gravity.y.toFixed(1))), jsx("input", { type: "range", min: -20, max: 0, step: 0.1, value: worldConfig.gravity.y, onChange: (e) => setWorldConfig({ gravity: new THREE.Vector3(0, parseFloat(e.target.value), 0) }), style: { width: '100%' } })] }), selectedObject && (jsxs("fieldset", { style: {
279
279
  border: '1px solid var(--nice-border, #e2e8f0)',
280
280
  borderRadius: 6,
281
281
  padding: 6,
282
282
  display: 'grid',
283
283
  gap: 4,
284
- }, children: [jsxs("legend", { style: { fontSize: 11 }, children: [selectedObject.name || 'Object', " \u2014 Physics"] }), !selectedBody ? (jsx("button", { type: "button", onClick: () => addBody(selectedObject), style: {
284
+ }, children: [jsx("legend", { style: { fontSize: 11 }, children: t('physicsSimulation.objectPhysics', '{name} — Physics').replace('{name}', selectedObject.name || t('physicsSimulation.object', 'Object')) }), !selectedBody ? (jsx("button", { type: "button", onClick: () => addBody(selectedObject), style: {
285
285
  background: 'var(--nice-color-accent, #6366f1)',
286
286
  color: 'var(--nice-text-on-primary, #fff)',
287
287
  border: 'none',
288
288
  borderRadius: 4,
289
289
  padding: '5px 8px',
290
290
  cursor: 'pointer',
291
- }, children: "+ Add Rigid Body" })) : (jsxs(Fragment, { children: [jsxs("label", { children: ["Type:", jsxs("select", { value: selectedBody.bodyType, onChange: (e) => updateBody(selectedBody.id, { bodyType: e.target.value }), style: { marginLeft: 8, width: '100%' }, children: [jsx("option", { value: "dynamic", children: t('physicsSimulation.dynamic', 'Dynamic') }), jsx("option", { value: "static", children: t('physicsSimulation.static', 'Static') }), jsx("option", { value: "kinematic", children: t('physicsSimulation.kinematic', 'Kinematic') })] })] }), jsxs("label", { children: ["Shape:", jsxs("select", { value: selectedBody.colliderShape, onChange: (e) => updateBody(selectedBody.id, { colliderShape: e.target.value }), style: { marginLeft: 8, width: '100%' }, children: [jsx("option", { value: "box", children: "Box" }), jsx("option", { value: "sphere", children: t('physicsSimulation.sphere', 'Sphere') }), jsx("option", { value: "capsule", children: t('physicsSimulation.capsule', 'Capsule') }), jsx("option", { value: "convexHull", children: t('physicsSimulation.convexHull', 'Convex Hull') }), jsx("option", { value: "trimesh", children: t('physicsSimulation.trimesh', 'Trimesh') })] })] }), jsxs("label", { children: ["Mass: ", selectedBody.mass.toFixed(1), jsx("input", { type: "range", min: 0.1, max: 100, step: 0.1, value: selectedBody.mass, onChange: (e) => updateBody(selectedBody.id, { mass: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: ["Restitution: ", selectedBody.restitution.toFixed(2), jsx("input", { type: "range", min: 0, max: 1, step: 0.01, value: selectedBody.restitution, onChange: (e) => updateBody(selectedBody.id, { restitution: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: ["Friction: ", selectedBody.friction.toFixed(2), jsx("input", { type: "range", min: 0, max: 1, step: 0.01, value: selectedBody.friction, onChange: (e) => updateBody(selectedBody.id, { friction: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 6 }, children: [jsx("input", { type: "checkbox", checked: selectedBody.isSensor, onChange: (e) => updateBody(selectedBody.id, { isSensor: e.target.checked }) }), "Sensor (trigger only)"] }), jsx("button", { type: "button", onClick: () => removeBody(selectedBody.id), style: {
291
+ }, children: t('physicsSimulation.addRigidBody', '+ Add Rigid Body') })) : (jsxs(Fragment, { children: [jsxs("label", { children: [t('physicsSimulation.type', 'Type:'), jsxs("select", { value: selectedBody.bodyType, onChange: (e) => updateBody(selectedBody.id, { bodyType: e.target.value }), style: { marginLeft: 8, width: '100%' }, children: [jsx("option", { value: "dynamic", children: t('physicsSimulation.dynamic', 'Dynamic') }), jsx("option", { value: "static", children: t('physicsSimulation.static', 'Static') }), jsx("option", { value: "kinematic", children: t('physicsSimulation.kinematic', 'Kinematic') })] })] }), jsxs("label", { children: [t('physicsSimulation.shape', 'Shape:'), jsxs("select", { value: selectedBody.colliderShape, onChange: (e) => updateBody(selectedBody.id, { colliderShape: e.target.value }), style: { marginLeft: 8, width: '100%' }, children: [jsx("option", { value: "box", children: t('physicsSimulation.box', 'Box') }), jsx("option", { value: "sphere", children: t('physicsSimulation.sphere', 'Sphere') }), jsx("option", { value: "capsule", children: t('physicsSimulation.capsule', 'Capsule') }), jsx("option", { value: "convexHull", children: t('physicsSimulation.convexHull', 'Convex Hull') }), jsx("option", { value: "trimesh", children: t('physicsSimulation.trimesh', 'Trimesh') })] })] }), jsxs("label", { children: [t('physicsSimulation.mass', 'Mass: {mass}').replace('{mass}', String(selectedBody.mass.toFixed(1))), jsx("input", { type: "range", min: 0.1, max: 100, step: 0.1, value: selectedBody.mass, onChange: (e) => updateBody(selectedBody.id, { mass: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('physicsSimulation.restitution', 'Restitution: {restitution}').replace('{restitution}', String(selectedBody.restitution.toFixed(2))), jsx("input", { type: "range", min: 0, max: 1, step: 0.01, value: selectedBody.restitution, onChange: (e) => updateBody(selectedBody.id, { restitution: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('physicsSimulation.friction', 'Friction: {friction}').replace('{friction}', String(selectedBody.friction.toFixed(2))), jsx("input", { type: "range", min: 0, max: 1, step: 0.01, value: selectedBody.friction, onChange: (e) => updateBody(selectedBody.id, { friction: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 6 }, children: [jsx("input", { type: "checkbox", checked: selectedBody.isSensor, onChange: (e) => updateBody(selectedBody.id, { isSensor: e.target.checked }) }), t('physicsSimulation.sensorTriggerOnly', 'Sensor (trigger only)')] }), jsx("button", { type: "button", onClick: () => removeBody(selectedBody.id), style: {
292
292
  background: 'var(--nice-danger, #ef4444)',
293
293
  color: 'var(--nice-text-on-primary, #fff)',
294
294
  border: 'none',
@@ -296,7 +296,7 @@ const NicePhysicsSimulation = ({ scene, selectedObject, onSimulationStep, classN
296
296
  padding: '4px 8px',
297
297
  cursor: 'pointer',
298
298
  fontSize: 11,
299
- }, children: t('physicsSimulation.removeRigidBody', 'Remove Rigid Body') })] }))] })), jsxs("div", { style: { fontSize: 11, opacity: 0.7 }, children: ["Total bodies: ", bodies.length] })] }));
299
+ }, children: t('physicsSimulation.removeRigidBody', 'Remove Rigid Body') })] }))] })), jsx("div", { style: { fontSize: 11, opacity: 0.7 }, children: t('physicsSimulation.totalBodies', 'Total bodies: {bodies}').replace('{bodies}', String(bodies.length)) })] }));
300
300
  };
301
301
 
302
302
  export { NicePhysicsSimulation, usePhysicsSimulation };
@@ -295,13 +295,13 @@ function useProceduralGeometry(scene, onGenerated) {
295
295
  const NiceProceduralGeometry = ({ scene, onGenerated, className = '', }) => {
296
296
  const { t } = useNiceTranslation();
297
297
  const { type, setType, treeParams, setTreeParams, rockParams, setRockParams, buildingParams, setBuildingParams, gearParams, setGearParams, generate, } = useProceduralGeometry(scene, onGenerated);
298
- return (jsxs("div", { className: `nice-procedural-geometry ${className}`, style: { display: 'grid', gap: 8, fontSize: 13 }, children: [jsx("strong", { children: t('proceduralGeometry.proceduralGeometry', 'Procedural Geometry') }), jsxs("select", { value: type, onChange: (e) => setType(e.target.value), style: { width: '100%', padding: '4px 8px' }, children: [jsx("option", { value: "tree", children: t('proceduralGeometry.tree', 'Tree') }), jsx("option", { value: "rock", children: t('proceduralGeometry.rock', 'Rock') }), jsx("option", { value: "building", children: t('proceduralGeometry.building', 'Building') }), jsx("option", { value: "pipe", children: t('proceduralGeometry.pipe', 'Pipe') }), jsx("option", { value: "gear", children: t('proceduralGeometry.gear', 'Gear') })] }), type === 'tree' && (jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxs("label", { children: ["Height: ", treeParams.trunkHeight.toFixed(1), jsx("input", { type: "range", min: 1, max: 15, step: 0.5, value: treeParams.trunkHeight, onChange: (e) => setTreeParams({ ...treeParams, trunkHeight: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: ["Branches: ", treeParams.branches, jsx("input", { type: "range", min: 1, max: 6, value: treeParams.branches, onChange: (e) => setTreeParams({ ...treeParams, branches: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxs("label", { children: ["Depth: ", treeParams.depth, jsx("input", { type: "range", min: 1, max: 5, value: treeParams.depth, onChange: (e) => setTreeParams({ ...treeParams, depth: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxs("label", { children: ["Angle: ", treeParams.branchAngle, "\u00B0", jsx("input", { type: "range", min: 10, max: 80, value: treeParams.branchAngle, onChange: (e) => setTreeParams({ ...treeParams, branchAngle: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxs("label", { children: ["Seed: ", treeParams.seed, jsx("input", { type: "range", min: 1, max: 999, value: treeParams.seed, onChange: (e) => setTreeParams({ ...treeParams, seed: parseInt(e.target.value, 10) }), style: { width: '100%' } })] })] })), type === 'rock' && (jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxs("label", { children: ["Radius: ", rockParams.radius.toFixed(1), jsx("input", { type: "range", min: 0.2, max: 5, step: 0.1, value: rockParams.radius, onChange: (e) => setRockParams({ ...rockParams, radius: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: ["Detail: ", rockParams.detail, jsx("input", { type: "range", min: 1, max: 5, value: rockParams.detail, onChange: (e) => setRockParams({ ...rockParams, detail: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxs("label", { children: ["Noise: ", rockParams.noiseAmount.toFixed(2), jsx("input", { type: "range", min: 0, max: 0.5, step: 0.01, value: rockParams.noiseAmount, onChange: (e) => setRockParams({ ...rockParams, noiseAmount: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: ["Seed: ", rockParams.seed, jsx("input", { type: "range", min: 1, max: 999, value: rockParams.seed, onChange: (e) => setRockParams({ ...rockParams, seed: parseInt(e.target.value, 10) }), style: { width: '100%' } })] })] })), type === 'building' && (jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxs("label", { children: ["Width: ", buildingParams.width, jsx("input", { type: "range", min: 4, max: 20, step: 1, value: buildingParams.width, onChange: (e) => setBuildingParams({ ...buildingParams, width: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxs("label", { children: ["Floors: ", buildingParams.floors, jsx("input", { type: "range", min: 1, max: 20, value: buildingParams.floors, onChange: (e) => setBuildingParams({ ...buildingParams, floors: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxs("label", { children: ["Windows/Floor: ", buildingParams.windowsPerFloor, jsx("input", { type: "range", min: 1, max: 10, value: buildingParams.windowsPerFloor, onChange: (e) => setBuildingParams({
298
+ return (jsxs("div", { className: `nice-procedural-geometry ${className}`, style: { display: 'grid', gap: 8, fontSize: 13 }, children: [jsx("strong", { children: t('proceduralGeometry.proceduralGeometry', 'Procedural Geometry') }), jsxs("select", { value: type, onChange: (e) => setType(e.target.value), style: { width: '100%', padding: '4px 8px' }, children: [jsx("option", { value: "tree", children: t('proceduralGeometry.tree', 'Tree') }), jsx("option", { value: "rock", children: t('proceduralGeometry.rock', 'Rock') }), jsx("option", { value: "building", children: t('proceduralGeometry.building', 'Building') }), jsx("option", { value: "pipe", children: t('proceduralGeometry.pipe', 'Pipe') }), jsx("option", { value: "gear", children: t('proceduralGeometry.gear', 'Gear') })] }), type === 'tree' && (jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxs("label", { children: [t('proceduralGeometry.height', 'Height: {trunkHeight}').replace('{trunkHeight}', String(treeParams.trunkHeight.toFixed(1))), jsx("input", { type: "range", min: 1, max: 15, step: 0.5, value: treeParams.trunkHeight, onChange: (e) => setTreeParams({ ...treeParams, trunkHeight: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.branches', 'Branches: {branches}').replace('{branches}', String(treeParams.branches)), jsx("input", { type: "range", min: 1, max: 6, value: treeParams.branches, onChange: (e) => setTreeParams({ ...treeParams, branches: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.depth', 'Depth: {depth}').replace('{depth}', String(treeParams.depth)), jsx("input", { type: "range", min: 1, max: 5, value: treeParams.depth, onChange: (e) => setTreeParams({ ...treeParams, depth: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.angle', 'Angle: {branchAngle}°').replace('{branchAngle}', String(treeParams.branchAngle)), jsx("input", { type: "range", min: 10, max: 80, value: treeParams.branchAngle, onChange: (e) => setTreeParams({ ...treeParams, branchAngle: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.seed', 'Seed: {seed}').replace('{seed}', String(treeParams.seed)), jsx("input", { type: "range", min: 1, max: 999, value: treeParams.seed, onChange: (e) => setTreeParams({ ...treeParams, seed: parseInt(e.target.value, 10) }), style: { width: '100%' } })] })] })), type === 'rock' && (jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxs("label", { children: [t('proceduralGeometry.radius', 'Radius: {radius}').replace('{radius}', String(rockParams.radius.toFixed(1))), jsx("input", { type: "range", min: 0.2, max: 5, step: 0.1, value: rockParams.radius, onChange: (e) => setRockParams({ ...rockParams, radius: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.detail', 'Detail: {detail}').replace('{detail}', String(rockParams.detail)), jsx("input", { type: "range", min: 1, max: 5, value: rockParams.detail, onChange: (e) => setRockParams({ ...rockParams, detail: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.noise', 'Noise: {noiseAmount}').replace('{noiseAmount}', String(rockParams.noiseAmount.toFixed(2))), jsx("input", { type: "range", min: 0, max: 0.5, step: 0.01, value: rockParams.noiseAmount, onChange: (e) => setRockParams({ ...rockParams, noiseAmount: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.seed', 'Seed: {seed}').replace('{seed}', String(rockParams.seed)), jsx("input", { type: "range", min: 1, max: 999, value: rockParams.seed, onChange: (e) => setRockParams({ ...rockParams, seed: parseInt(e.target.value, 10) }), style: { width: '100%' } })] })] })), type === 'building' && (jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxs("label", { children: [t('proceduralGeometry.width', 'Width: {width}').replace('{width}', String(buildingParams.width)), jsx("input", { type: "range", min: 4, max: 20, step: 1, value: buildingParams.width, onChange: (e) => setBuildingParams({ ...buildingParams, width: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.floors', 'Floors: {floors}').replace('{floors}', String(buildingParams.floors)), jsx("input", { type: "range", min: 1, max: 20, value: buildingParams.floors, onChange: (e) => setBuildingParams({ ...buildingParams, floors: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.windowsFloor', 'Windows/Floor: {windowsPerFloor}').replace('{windowsPerFloor}', String(buildingParams.windowsPerFloor)), jsx("input", { type: "range", min: 1, max: 10, value: buildingParams.windowsPerFloor, onChange: (e) => setBuildingParams({
299
299
  ...buildingParams,
300
300
  windowsPerFloor: parseInt(e.target.value, 10),
301
- }), style: { width: '100%' } })] }), jsxs("label", { children: ["Roof:", jsxs("select", { value: buildingParams.roofStyle, onChange: (e) => setBuildingParams({
301
+ }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.roof', 'Roof:'), jsxs("select", { value: buildingParams.roofStyle, onChange: (e) => setBuildingParams({
302
302
  ...buildingParams,
303
303
  roofStyle: e.target.value,
304
- }), style: { marginLeft: 8 }, children: [jsx("option", { value: "flat", children: t('proceduralGeometry.flat', 'Flat') }), jsx("option", { value: "gable", children: t('proceduralGeometry.gable', 'Gable') }), jsx("option", { value: "hip", children: "Hip" })] })] })] })), type === 'gear' && (jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxs("label", { children: ["Outer Radius: ", gearParams.outerRadius.toFixed(1), jsx("input", { type: "range", min: 0.5, max: 5, step: 0.1, value: gearParams.outerRadius, onChange: (e) => setGearParams({ ...gearParams, outerRadius: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: ["Teeth: ", gearParams.teeth, jsx("input", { type: "range", min: 4, max: 30, value: gearParams.teeth, onChange: (e) => setGearParams({ ...gearParams, teeth: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxs("label", { children: ["Tooth Depth: ", gearParams.toothDepth.toFixed(2), jsx("input", { type: "range", min: 0.05, max: 1, step: 0.05, value: gearParams.toothDepth, onChange: (e) => setGearParams({ ...gearParams, toothDepth: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: ["Thickness: ", gearParams.thickness.toFixed(2), jsx("input", { type: "range", min: 0.1, max: 2, step: 0.1, value: gearParams.thickness, onChange: (e) => setGearParams({ ...gearParams, thickness: parseFloat(e.target.value) }), style: { width: '100%' } })] })] })), jsxs("button", { type: "button", onClick: generate, style: {
304
+ }), style: { marginLeft: 8 }, children: [jsx("option", { value: "flat", children: t('proceduralGeometry.flat', 'Flat') }), jsx("option", { value: "gable", children: t('proceduralGeometry.gable', 'Gable') }), jsx("option", { value: "hip", children: t('proceduralGeometry.hip', 'Hip') })] })] })] })), type === 'gear' && (jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxs("label", { children: [t('proceduralGeometry.outerRadius', 'Outer Radius: {outerRadius}').replace('{outerRadius}', String(gearParams.outerRadius.toFixed(1))), jsx("input", { type: "range", min: 0.5, max: 5, step: 0.1, value: gearParams.outerRadius, onChange: (e) => setGearParams({ ...gearParams, outerRadius: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.teeth', 'Teeth: {teeth}').replace('{teeth}', String(gearParams.teeth)), jsx("input", { type: "range", min: 4, max: 30, value: gearParams.teeth, onChange: (e) => setGearParams({ ...gearParams, teeth: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.toothDepth', 'Tooth Depth: {toothDepth}').replace('{toothDepth}', String(gearParams.toothDepth.toFixed(2))), jsx("input", { type: "range", min: 0.05, max: 1, step: 0.05, value: gearParams.toothDepth, onChange: (e) => setGearParams({ ...gearParams, toothDepth: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.thickness', 'Thickness: {thickness}').replace('{thickness}', String(gearParams.thickness.toFixed(2))), jsx("input", { type: "range", min: 0.1, max: 2, step: 0.1, value: gearParams.thickness, onChange: (e) => setGearParams({ ...gearParams, thickness: parseFloat(e.target.value) }), style: { width: '100%' } })] })] })), jsxs("button", { type: "button", onClick: generate, style: {
305
305
  background: 'var(--nice-color-accent, #6366f1)',
306
306
  color: 'var(--nice-text-on-primary, #fff)',
307
307
  border: 'none',
@@ -346,7 +346,7 @@ const NiceTerrainEditor = ({ scene, camera, canvas, config: configProp, onTerrai
346
346
  border: '1px solid var(--nice-border, #e2e8f0)',
347
347
  background: brush.mode === m ? 'var(--nice-color-accent, #6366f1)' : 'transparent',
348
348
  color: brush.mode === m ? 'var(--nice-color-white, #fff)' : 'inherit',
349
- }, children: m }, m)))] }), jsxs("label", { children: ["Radius: ", brush.radius.toFixed(1), jsx("input", { type: "range", min: 0.5, max: 20, step: 0.5, value: brush.radius, onChange: (e) => setBrush({ radius: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: ["Strength: ", brush.strength.toFixed(2), jsx("input", { type: "range", min: 0.01, max: 1, step: 0.01, value: brush.strength, onChange: (e) => setBrush({ strength: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsx("button", { type: "button", onClick: isPainting ? stopPainting : startPainting, style: {
349
+ }, children: m }, m)))] }), jsxs("label", { children: [t('terrainEditor.radius', 'Radius: {radius}').replace('{radius}', String(brush.radius.toFixed(1))), jsx("input", { type: "range", min: 0.5, max: 20, step: 0.5, value: brush.radius, onChange: (e) => setBrush({ radius: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('terrainEditor.strength', 'Strength: {strength}').replace('{strength}', String(brush.strength.toFixed(2))), jsx("input", { type: "range", min: 0.01, max: 1, step: 0.01, value: brush.strength, onChange: (e) => setBrush({ strength: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsx("button", { type: "button", onClick: isPainting ? stopPainting : startPainting, style: {
350
350
  background: isPainting
351
351
  ? 'var(--nice-danger, #ef4444)'
352
352
  : 'var(--nice-color-accent, #6366f1)',
@@ -361,7 +361,7 @@ const NiceTerrainEditor = ({ scene, camera, canvas, config: configProp, onTerrai
361
361
  padding: 6,
362
362
  display: 'grid',
363
363
  gap: 4,
364
- }, children: [jsx("legend", { style: { fontSize: 11 }, children: t('terrainEditor.procedural', 'Procedural') }), jsxs("label", { children: ["Octaves: ", noiseOctaves, jsx("input", { type: "range", min: 1, max: 10, value: noiseOctaves, onChange: (e) => setNoiseOctaves(parseInt(e.target.value, 10)), style: { width: '100%' } })] }), jsxs("label", { children: ["Persistence: ", noisePersistence.toFixed(2), jsx("input", { type: "range", min: 0.1, max: 1, step: 0.05, value: noisePersistence, onChange: (e) => setNoisePersistence(parseFloat(e.target.value)), style: { width: '100%' } })] }), jsxs("div", { style: { display: 'flex', gap: 4 }, children: [jsxs("button", { type: "button", onClick: () => generateNoise(noiseOctaves, noisePersistence), style: { flex: 1 }, children: [jsx(NiceIcon, { name: "mountain", size: 14 }), ' ', t('terrainEditor.generateNoise', 'Generate Noise')] }), jsxs("button", { type: "button", onClick: generateFlat, style: { flex: 1 }, children: [jsx(NiceIcon, { name: "square", size: 14 }), " ", t('terrainEditor.flatten', 'Flatten')] })] })] }), brush.mode === 'paint' && (jsxs("fieldset", { style: {
364
+ }, children: [jsx("legend", { style: { fontSize: 11 }, children: t('terrainEditor.procedural', 'Procedural') }), jsxs("label", { children: [t('terrainEditor.octaves', 'Octaves: {noiseOctaves}').replace('{noiseOctaves}', String(noiseOctaves)), jsx("input", { type: "range", min: 1, max: 10, value: noiseOctaves, onChange: (e) => setNoiseOctaves(parseInt(e.target.value, 10)), style: { width: '100%' } })] }), jsxs("label", { children: [t('terrainEditor.persistence', 'Persistence: {noisePersistence}').replace('{noisePersistence}', String(noisePersistence.toFixed(2))), jsx("input", { type: "range", min: 0.1, max: 1, step: 0.05, value: noisePersistence, onChange: (e) => setNoisePersistence(parseFloat(e.target.value)), style: { width: '100%' } })] }), jsxs("div", { style: { display: 'flex', gap: 4 }, children: [jsxs("button", { type: "button", onClick: () => generateNoise(noiseOctaves, noisePersistence), style: { flex: 1 }, children: [jsx(NiceIcon, { name: "mountain", size: 14 }), ' ', t('terrainEditor.generateNoise', 'Generate Noise')] }), jsxs("button", { type: "button", onClick: generateFlat, style: { flex: 1 }, children: [jsx(NiceIcon, { name: "square", size: 14 }), " ", t('terrainEditor.flatten', 'Flatten')] })] })] }), brush.mode === 'paint' && (jsxs("fieldset", { style: {
365
365
  border: '1px solid var(--nice-border, #e2e8f0)',
366
366
  borderRadius: 6,
367
367
  padding: 6,
@@ -231,7 +231,7 @@ const NiceWeightPainter = ({ skinnedMesh, activeBoneIndex: initialBoneIndex, sce
231
231
  useEffect(() => {
232
232
  setActiveBoneIndex(initialBoneIndex);
233
233
  }, [initialBoneIndex, setActiveBoneIndex]);
234
- return (jsxs("div", { className: `nice-weight-painter ${className}`, style: { display: 'grid', gap: 8, fontSize: 13 }, children: [jsxs("label", { children: ["Bone:", jsx("select", { value: activeBoneIndex, onChange: (e) => setActiveBoneIndex(parseInt(e.target.value, 10)), style: { marginLeft: 8, width: '100%' }, children: boneNames.map((name, i) => (jsx("option", { value: i, children: name }, i))) })] }), jsxs("fieldset", { style: {
234
+ return (jsxs("div", { className: `nice-weight-painter ${className}`, style: { display: 'grid', gap: 8, fontSize: 13 }, children: [jsxs("label", { children: [t('weightPainter.bone', 'Bone:'), jsx("select", { value: activeBoneIndex, onChange: (e) => setActiveBoneIndex(parseInt(e.target.value, 10)), style: { marginLeft: 8, width: '100%' }, children: boneNames.map((name, i) => (jsx("option", { value: i, children: name }, i))) })] }), jsxs("fieldset", { style: {
235
235
  display: 'flex',
236
236
  gap: 4,
237
237
  border: '1px solid var(--nice-border, #e2e8f0)',
@@ -246,7 +246,7 @@ const NiceWeightPainter = ({ skinnedMesh, activeBoneIndex: initialBoneIndex, sce
246
246
  borderRadius: 4,
247
247
  cursor: 'pointer',
248
248
  fontSize: 11,
249
- }, children: mode }, mode)))] }), jsxs("label", { children: ["Radius: ", brush.radius.toFixed(2), jsx("input", { type: "range", min: 0.05, max: 2, step: 0.05, value: brush.radius, onChange: (e) => setBrush({ radius: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: ["Strength: ", brush.strength.toFixed(2), jsx("input", { type: "range", min: 0.01, max: 1, step: 0.01, value: brush.strength, onChange: (e) => setBrush({ strength: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("div", { style: { display: 'flex', gap: 4 }, children: [jsx("button", { type: "button", onClick: isPainting ? stopPainting : startPainting, style: {
249
+ }, children: mode }, mode)))] }), jsxs("label", { children: [t('weightPainter.radius', 'Radius: {radius}').replace('{radius}', String(brush.radius.toFixed(2))), jsx("input", { type: "range", min: 0.05, max: 2, step: 0.05, value: brush.radius, onChange: (e) => setBrush({ radius: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('weightPainter.strength', 'Strength: {strength}').replace('{strength}', String(brush.strength.toFixed(2))), jsx("input", { type: "range", min: 0.01, max: 1, step: 0.01, value: brush.strength, onChange: (e) => setBrush({ strength: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("div", { style: { display: 'flex', gap: 4 }, children: [jsx("button", { type: "button", onClick: isPainting ? stopPainting : startPainting, style: {
250
250
  flex: 1,
251
251
  background: isPainting
252
252
  ? 'var(--nice-danger, #ef4444)'
@@ -275,7 +275,7 @@ const NiceXRPreview = ({ renderer, scene, camera, mode: initialMode, onSessionSt
275
275
  background: mode === 'vr' ? 'var(--nice-color-accent, #6366f1)' : 'transparent',
276
276
  color: mode === 'vr' ? 'var(--nice-color-white, #fff)' : 'inherit',
277
277
  border: '1px solid var(--nice-border, #e2e8f0)',
278
- }, children: [jsx(NiceIcon, { name: "eye", size: 14 }), " VR"] }), jsxs("button", { type: "button", onClick: () => setMode('ar'), style: {
278
+ }, children: [jsx(NiceIcon, { name: "eye", size: 14 }), " ", t('xrPreview.vr', 'VR')] }), jsxs("button", { type: "button", onClick: () => setMode('ar'), style: {
279
279
  flex: 1,
280
280
  padding: '5px 8px',
281
281
  borderRadius: 4,
@@ -283,7 +283,7 @@ const NiceXRPreview = ({ renderer, scene, camera, mode: initialMode, onSessionSt
283
283
  background: mode === 'ar' ? 'var(--nice-color-accent, #6366f1)' : 'transparent',
284
284
  color: mode === 'ar' ? 'var(--nice-color-white, #fff)' : 'inherit',
285
285
  border: '1px solid var(--nice-border, #e2e8f0)',
286
- }, children: [jsx(NiceIcon, { name: "window", size: 14 }), " AR"] })] }), jsx("button", { type: "button", onClick: isSessionActive ? endSession : startSession, style: {
286
+ }, children: [jsx(NiceIcon, { name: "window", size: 14 }), " ", t('xrPreview.ar', 'AR')] })] }), jsx("button", { type: "button", onClick: isSessionActive ? endSession : startSession, style: {
287
287
  background: isSessionActive
288
288
  ? 'var(--nice-danger, #ef4444)'
289
289
  : 'var(--nice-success, #22c55e)',
@@ -292,7 +292,10 @@ const NiceXRPreview = ({ renderer, scene, camera, mode: initialMode, onSessionSt
292
292
  borderRadius: 4,
293
293
  padding: '6px 12px',
294
294
  cursor: 'pointer',
295
- }, children: isSessionActive ? (jsxs(Fragment, { children: [jsx(NiceIcon, { name: "stop", size: 14 }), " ", t('xrPreview.exitXr', 'Exit XR')] })) : (jsxs(Fragment, { children: [jsx(NiceIcon, { name: "play", size: 14 }), " ", t('xrPreview.enterXr', 'Enter XR')] })) }), mode === 'vr' && (jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 6 }, children: [jsx("input", { type: "checkbox", checked: teleportEnabled, onChange: (e) => setTeleportEnabled(e.target.checked) }), t('xrPreview.teleportation', 'Teleportation')] })), isSessionActive && (jsxs("div", { style: { fontSize: 11, opacity: 0.7 }, children: ["Controllers: ", controllers.length, jsx("br", {}), mode === 'ar' && hitTestResult && (jsxs(Fragment, { children: ["Hit: (", hitTestResult.x.toFixed(2), ", ", hitTestResult.y.toFixed(2), ",", ' ', hitTestResult.z.toFixed(2), ")"] }))] }))] }))] }));
295
+ }, children: isSessionActive ? (jsxs(Fragment, { children: [jsx(NiceIcon, { name: "stop", size: 14 }), " ", t('xrPreview.exitXr', 'Exit XR')] })) : (jsxs(Fragment, { children: [jsx(NiceIcon, { name: "play", size: 14 }), " ", t('xrPreview.enterXr', 'Enter XR')] })) }), mode === 'vr' && (jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 6 }, children: [jsx("input", { type: "checkbox", checked: teleportEnabled, onChange: (e) => setTeleportEnabled(e.target.checked) }), t('xrPreview.teleportation', 'Teleportation')] })), isSessionActive && (jsxs("div", { style: { fontSize: 11, opacity: 0.7 }, children: [t('xrPreview.controllers', 'Controllers: {controllers}').replace('{controllers}', String(controllers.length)), jsx("br", {}), mode === 'ar' && hitTestResult && (jsx(Fragment, { children: t('xrPreview.hit', 'Hit: ({x}, {y}, {z})')
296
+ .replace('{x}', String(hitTestResult.x.toFixed(2)))
297
+ .replace('{y}', String(hitTestResult.y.toFixed(2)))
298
+ .replace('{z}', String(hitTestResult.z.toFixed(2))) }))] }))] }))] }));
296
299
  };
297
300
 
298
301
  export { NiceXRPreview, useXRPreview };