diff --git a/apps/editor/components/editor/index.tsx b/apps/editor/components/editor/index.tsx
index 2a2b0bc7..ce675101 100644
--- a/apps/editor/components/editor/index.tsx
+++ b/apps/editor/components/editor/index.tsx
@@ -28,7 +28,7 @@ export default function Editor() {
-
+
diff --git a/apps/editor/components/ui/panels/item-panel.tsx b/apps/editor/components/ui/panels/item-panel.tsx
index f9c086be..bed39a58 100644
--- a/apps/editor/components/ui/panels/item-panel.tsx
+++ b/apps/editor/components/ui/panels/item-panel.tsx
@@ -29,7 +29,9 @@ export function ItemPanel() {
// Mark parent wall as dirty if item is attached to wall
if (node.asset.attachTo === 'wall' && node.parentId) {
- useScene.getState().dirtyNodes.add(node.parentId as AnyNode['id'])
+ requestAnimationFrame(() => {
+ useScene.getState().dirtyNodes.add(node.parentId as AnyNode['id'])
+ })
}
},
[selectedId, node, updateNode],
diff --git a/apps/editor/components/ui/primitives/number-input.tsx b/apps/editor/components/ui/primitives/number-input.tsx
index d2c6a30b..eb6659ef 100644
--- a/apps/editor/components/ui/primitives/number-input.tsx
+++ b/apps/editor/components/ui/primitives/number-input.tsx
@@ -64,8 +64,13 @@ export function NumberInput({
const deltaValue = deltaX * step
const newValue = clamp(startValueRef.current + deltaValue)
- finalValue = Number.parseFloat(newValue.toFixed(precision))
- onChange(finalValue)
+ const newFinalValue = Number.parseFloat(newValue.toFixed(precision))
+
+ // Only call onChange if value actually changed (avoid extra processing on tiny moves)
+ if (newFinalValue !== finalValue) {
+ finalValue = newFinalValue
+ onChange(finalValue)
+ }
}
const handleMouseUp = () => {
diff --git a/apps/editor/components/ui/sidebar/panels/site-panel/index.tsx b/apps/editor/components/ui/sidebar/panels/site-panel/index.tsx
index 67aa1d74..54581749 100644
--- a/apps/editor/components/ui/sidebar/panels/site-panel/index.tsx
+++ b/apps/editor/components/ui/sidebar/panels/site-panel/index.tsx
@@ -345,6 +345,7 @@ function LevelsSection() {
const nodes = useScene((state) => state.nodes);
const createNode = useScene((state) => state.createNode);
const updateNode = useScene((state) => state.updateNode);
+ const deleteNode = useScene((state) => state.deleteNode);
const selectedBuildingId = useViewer((state) => state.selection.buildingId);
const selectedLevelId = useViewer((state) => state.selection.levelId);
const setSelection = useViewer((state) => state.setSelection);
@@ -493,6 +494,15 @@ function LevelsSection() {
>
References
+ {level.level !== 0 && (
+
+ )}
diff --git a/bun.lock b/bun.lock
index 93c676af..5ed59f8c 100644
--- a/bun.lock
+++ b/bun.lock
@@ -1,6 +1,5 @@
{
"lockfileVersion": 1,
- "configVersion": 0,
"workspaces": {
"": {
"name": "editor",
@@ -63,13 +62,14 @@
},
"packages/core": {
"name": "@pascal-app/core",
- "version": "0.1.10",
+ "version": "0.1.11",
"dependencies": {
"dedent": "^1.7.1",
"idb-keyval": "^6.2.2",
"mitt": "^3.0.1",
"nanoid": "^5.1.6",
"three-bvh-csg": "^0.0.17",
+ "three-mesh-bvh": "^0.9.8",
"zod": "^4.3.5",
"zundo": "^2.3.0",
"zustand": "^5",
@@ -127,7 +127,7 @@
},
"packages/viewer": {
"name": "@pascal-app/viewer",
- "version": "0.1.10",
+ "version": "0.1.11",
"dependencies": {
"zustand": "^5",
},
diff --git a/packages/core/package.json b/packages/core/package.json
index a7b98961..9adac83f 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -33,6 +33,7 @@
"mitt": "^3.0.1",
"nanoid": "^5.1.6",
"three-bvh-csg": "^0.0.17",
+ "three-mesh-bvh": "^0.9.8",
"zod": "^4.3.5",
"zundo": "^2.3.0",
"zustand": "^5"
diff --git a/packages/core/src/systems/wall/wall-system.tsx b/packages/core/src/systems/wall/wall-system.tsx
index 8042b1da..1b7394be 100644
--- a/packages/core/src/systems/wall/wall-system.tsx
+++ b/packages/core/src/systems/wall/wall-system.tsx
@@ -21,6 +21,7 @@ const csgEvaluator = new Evaluator()
// WALL SYSTEM
// ============================================================================
+let useFrameNb = 0;
export const WallSystem = () => {
const dirtyNodes = useScene((state) => state.dirtyNodes)
const clearDirty = useScene((state) => state.clearDirty)
@@ -33,7 +34,7 @@ export const WallSystem = () => {
// Collect dirty walls and their levels
const dirtyWallsByLevel = new Map>()
-
+ useFrameNb += 1;
dirtyNodes.forEach((id) => {
const node = nodes[id]
if (!node || node.type !== 'wall') return
@@ -106,6 +107,7 @@ function updateWallGeometry(wallId: string, miterData: WallMiterData) {
const node = nodes[wallId as WallNode['id']]
if (!node || node.type !== 'wall') return
+
const mesh = sceneRegistry.nodes.get(wallId) as THREE.Mesh
if (!mesh) return