@pascal-app/core 0.7.0 → 0.9.1
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.
- package/dist/events/bus.d.ts +47 -18
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/events/bus.js +1 -1
- package/dist/hooks/scene-registry/scene-registry.d.ts +6 -22
- package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
- package/dist/hooks/scene-registry/scene-registry.js +33 -26
- package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts +14 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts.map +1 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.js +51 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts +2 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts.map +1 -0
- package/dist/hooks/spatial-grid/floor-placed-elevation.test.js +273 -0
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +13 -2
- package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-manager.js +95 -9
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +28 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
- package/dist/hooks/spatial-grid/spatial-grid-sync.js +91 -18
- package/dist/index.d.ts +22 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -2
- package/dist/lib/polygon-geometry.d.ts.map +1 -1
- package/dist/lib/polygon-relations.d.ts +10 -0
- package/dist/lib/polygon-relations.d.ts.map +1 -0
- package/dist/lib/polygon-relations.js +73 -0
- package/dist/lib/space-detection-pause.test.d.ts +2 -0
- package/dist/lib/space-detection-pause.test.d.ts.map +1 -0
- package/dist/lib/space-detection-pause.test.js +42 -0
- package/dist/lib/space-detection.d.ts +30 -1
- package/dist/lib/space-detection.d.ts.map +1 -1
- package/dist/lib/space-detection.js +222 -41
- package/dist/lib/space-detection.test.d.ts +2 -0
- package/dist/lib/space-detection.test.d.ts.map +1 -0
- package/dist/lib/space-detection.test.js +77 -0
- package/dist/material-library.d.ts +1 -1
- package/dist/material-library.d.ts.map +1 -1
- package/dist/material-library.js +1957 -141
- package/dist/registry/__bench__/relations-resolver.bench.d.ts +15 -0
- package/dist/registry/__bench__/relations-resolver.bench.d.ts.map +1 -0
- package/dist/registry/__bench__/relations-resolver.bench.js +170 -0
- package/dist/registry/handles.d.ts +321 -0
- package/dist/registry/handles.d.ts.map +1 -0
- package/dist/registry/handles.js +24 -0
- package/dist/registry/index.d.ts +7 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +4 -0
- package/dist/registry/registry.d.ts +93 -0
- package/dist/registry/registry.d.ts.map +1 -0
- package/dist/registry/registry.js +201 -0
- package/dist/registry/registry.test.d.ts +2 -0
- package/dist/registry/registry.test.d.ts.map +1 -0
- package/dist/registry/registry.test.js +183 -0
- package/dist/registry/relations-resolver.d.ts +42 -0
- package/dist/registry/relations-resolver.d.ts.map +1 -0
- package/dist/registry/relations-resolver.js +77 -0
- package/dist/registry/relations-resolver.test.d.ts +2 -0
- package/dist/registry/relations-resolver.test.d.ts.map +1 -0
- package/dist/registry/relations-resolver.test.js +183 -0
- package/dist/registry/scene-api.d.ts +44 -0
- package/dist/registry/scene-api.d.ts.map +1 -0
- package/dist/registry/scene-api.js +111 -0
- package/dist/registry/scene-api.test.d.ts +2 -0
- package/dist/registry/scene-api.test.d.ts.map +1 -0
- package/dist/registry/scene-api.test.js +183 -0
- package/dist/registry/subtree.d.ts +67 -0
- package/dist/registry/subtree.d.ts.map +1 -0
- package/dist/registry/subtree.js +119 -0
- package/dist/registry/subtree.test.d.ts +2 -0
- package/dist/registry/subtree.test.d.ts.map +1 -0
- package/dist/registry/subtree.test.js +133 -0
- package/dist/registry/types.d.ts +1420 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/registry/types.js +1 -0
- package/dist/schema/asset-url.test.js +0 -4
- package/dist/schema/index.d.ts +17 -2
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +17 -3
- package/dist/schema/material.d.ts +12 -2
- package/dist/schema/material.d.ts.map +1 -1
- package/dist/schema/material.js +10 -0
- package/dist/schema/nodes/box-vent.d.ts +74 -0
- package/dist/schema/nodes/box-vent.d.ts.map +1 -0
- package/dist/schema/nodes/box-vent.js +63 -0
- package/dist/schema/nodes/building.d.ts +1 -1
- package/dist/schema/nodes/building.d.ts.map +1 -1
- package/dist/schema/nodes/building.js +3 -2
- package/dist/schema/nodes/ceiling.d.ts +3 -1
- package/dist/schema/nodes/ceiling.d.ts.map +1 -1
- package/dist/schema/nodes/ceiling.js +1 -1
- package/dist/schema/nodes/chimney.d.ts +158 -0
- package/dist/schema/nodes/chimney.d.ts.map +1 -0
- package/dist/schema/nodes/chimney.js +67 -0
- package/dist/schema/nodes/column.d.ts +598 -6
- package/dist/schema/nodes/column.d.ts.map +1 -1
- package/dist/schema/nodes/column.js +586 -6
- package/dist/schema/nodes/cupola.d.ts +66 -0
- package/dist/schema/nodes/cupola.d.ts.map +1 -0
- package/dist/schema/nodes/cupola.js +28 -0
- package/dist/schema/nodes/door.d.ts +1 -1
- package/dist/schema/nodes/dormer.d.ts +228 -0
- package/dist/schema/nodes/dormer.d.ts.map +1 -0
- package/dist/schema/nodes/dormer.js +119 -0
- package/dist/schema/nodes/downspout.d.ts +75 -0
- package/dist/schema/nodes/downspout.d.ts.map +1 -0
- package/dist/schema/nodes/downspout.js +63 -0
- package/dist/schema/nodes/elevator.d.ts +105 -0
- package/dist/schema/nodes/elevator.d.ts.map +1 -0
- package/dist/schema/nodes/elevator.js +54 -0
- package/dist/schema/nodes/eyebrow-vent.d.ts +68 -0
- package/dist/schema/nodes/eyebrow-vent.d.ts.map +1 -0
- package/dist/schema/nodes/eyebrow-vent.js +40 -0
- package/dist/schema/nodes/fence.d.ts +3 -2
- package/dist/schema/nodes/fence.d.ts.map +1 -1
- package/dist/schema/nodes/fence.js +4 -2
- package/dist/schema/nodes/gutter.d.ts +84 -0
- package/dist/schema/nodes/gutter.d.ts.map +1 -0
- package/dist/schema/nodes/gutter.js +76 -0
- package/dist/schema/nodes/item.d.ts +16 -0
- package/dist/schema/nodes/item.d.ts.map +1 -1
- package/dist/schema/nodes/item.js +21 -0
- package/dist/schema/nodes/level.d.ts +1 -1
- package/dist/schema/nodes/level.d.ts.map +1 -1
- package/dist/schema/nodes/level.js +2 -0
- package/dist/schema/nodes/ridge-vent.d.ts +67 -0
- package/dist/schema/nodes/ridge-vent.d.ts.map +1 -0
- package/dist/schema/nodes/ridge-vent.js +30 -0
- package/dist/schema/nodes/roof-segment.d.ts +198 -4
- package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
- package/dist/schema/nodes/roof-segment.js +254 -3
- package/dist/schema/nodes/roof.d.ts +6 -6
- package/dist/schema/nodes/roof.d.ts.map +1 -1
- package/dist/schema/nodes/roof.js +9 -21
- package/dist/schema/nodes/shelf.d.ts +105 -0
- package/dist/schema/nodes/shelf.d.ts.map +1 -0
- package/dist/schema/nodes/shelf.js +84 -0
- package/dist/schema/nodes/site.d.ts +1 -116
- package/dist/schema/nodes/site.d.ts.map +1 -1
- package/dist/schema/nodes/site.js +2 -6
- package/dist/schema/nodes/skylight.d.ts +279 -0
- package/dist/schema/nodes/skylight.d.ts.map +1 -0
- package/dist/schema/nodes/skylight.js +152 -0
- package/dist/schema/nodes/slab.d.ts +3 -1
- package/dist/schema/nodes/slab.d.ts.map +1 -1
- package/dist/schema/nodes/slab.js +1 -1
- package/dist/schema/nodes/solar-panel.d.ts +117 -0
- package/dist/schema/nodes/solar-panel.d.ts.map +1 -0
- package/dist/schema/nodes/solar-panel.js +47 -0
- package/dist/schema/nodes/stair-segment.d.ts +1 -1
- package/dist/schema/nodes/stair.d.ts +6 -6
- package/dist/schema/nodes/stair.d.ts.map +1 -1
- package/dist/schema/nodes/stair.js +9 -7
- package/dist/schema/nodes/surface-hole-metadata.d.ts +2 -0
- package/dist/schema/nodes/surface-hole-metadata.d.ts.map +1 -1
- package/dist/schema/nodes/surface-hole-metadata.js +3 -1
- package/dist/schema/nodes/turbine-vent.d.ts +68 -0
- package/dist/schema/nodes/turbine-vent.d.ts.map +1 -0
- package/dist/schema/nodes/turbine-vent.js +41 -0
- package/dist/schema/nodes/wall.d.ts +3 -3
- package/dist/schema/nodes/window.d.ts +1 -1
- package/dist/schema/types.d.ts +1448 -236
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +26 -0
- package/dist/services/alignment-anchors.d.ts +100 -0
- package/dist/services/alignment-anchors.d.ts.map +1 -0
- package/dist/services/alignment-anchors.js +283 -0
- package/dist/services/alignment-anchors.test.d.ts +2 -0
- package/dist/services/alignment-anchors.test.d.ts.map +1 -0
- package/dist/services/alignment-anchors.test.js +383 -0
- package/dist/services/alignment.d.ts +126 -0
- package/dist/services/alignment.d.ts.map +1 -0
- package/dist/services/alignment.js +169 -0
- package/dist/services/alignment.test.d.ts +2 -0
- package/dist/services/alignment.test.d.ts.map +1 -0
- package/dist/services/alignment.test.js +92 -0
- package/dist/services/drag-session.d.ts +47 -0
- package/dist/services/drag-session.d.ts.map +1 -0
- package/dist/services/drag-session.js +100 -0
- package/dist/services/drag-session.test.d.ts +2 -0
- package/dist/services/drag-session.test.d.ts.map +1 -0
- package/dist/services/drag-session.test.js +219 -0
- package/dist/services/hosting.d.ts +80 -0
- package/dist/services/hosting.d.ts.map +1 -0
- package/dist/services/hosting.js +122 -0
- package/dist/services/hosting.test.d.ts +2 -0
- package/dist/services/hosting.test.d.ts.map +1 -0
- package/dist/services/hosting.test.js +199 -0
- package/dist/services/index.d.ts +7 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +6 -0
- package/dist/services/movement.d.ts +48 -0
- package/dist/services/movement.d.ts.map +1 -0
- package/dist/services/movement.js +65 -0
- package/dist/services/movement.test.d.ts +2 -0
- package/dist/services/movement.test.d.ts.map +1 -0
- package/dist/services/movement.test.js +133 -0
- package/dist/services/single-undo-dance.test.d.ts +2 -0
- package/dist/services/single-undo-dance.test.d.ts.map +1 -0
- package/dist/services/single-undo-dance.test.js +210 -0
- package/dist/services/snap.d.ts +72 -0
- package/dist/services/snap.d.ts.map +1 -0
- package/dist/services/snap.js +120 -0
- package/dist/services/snap.test.d.ts +2 -0
- package/dist/services/snap.test.d.ts.map +1 -0
- package/dist/services/snap.test.js +95 -0
- package/dist/solar-panel-presets.d.ts +17 -0
- package/dist/solar-panel-presets.d.ts.map +1 -0
- package/dist/solar-panel-presets.js +43 -0
- package/dist/store/actions/node-actions.d.ts +14 -2
- package/dist/store/actions/node-actions.d.ts.map +1 -1
- package/dist/store/actions/node-actions.js +499 -20
- package/dist/store/actions/node-mutation-sanitize.test.d.ts +2 -0
- package/dist/store/actions/node-mutation-sanitize.test.d.ts.map +1 -0
- package/dist/store/actions/node-mutation-sanitize.test.js +147 -0
- package/dist/store/actions/reparent.test.d.ts +2 -0
- package/dist/store/actions/reparent.test.d.ts.map +1 -0
- package/dist/store/actions/reparent.test.js +212 -0
- package/dist/store/use-interactive.d.ts +39 -0
- package/dist/store/use-interactive.d.ts.map +1 -1
- package/dist/store/use-interactive.js +75 -0
- package/dist/store/use-live-node-overrides.d.ts +22 -0
- package/dist/store/use-live-node-overrides.d.ts.map +1 -0
- package/dist/store/use-live-node-overrides.js +59 -0
- package/dist/store/use-scene-elevator-migration.test.d.ts +2 -0
- package/dist/store/use-scene-elevator-migration.test.d.ts.map +1 -0
- package/dist/store/use-scene-elevator-migration.test.js +109 -0
- package/dist/store/use-scene.d.ts +11 -0
- package/dist/store/use-scene.d.ts.map +1 -1
- package/dist/store/use-scene.js +207 -11
- package/dist/systems/elevator/elevator-dispatch.d.ts +12 -0
- package/dist/systems/elevator/elevator-dispatch.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-dispatch.js +57 -0
- package/dist/systems/elevator/elevator-geometry.d.ts +15 -0
- package/dist/systems/elevator/elevator-geometry.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-geometry.js +50 -0
- package/dist/systems/elevator/elevator-opening-sync.d.ts +6 -0
- package/dist/systems/elevator/elevator-opening-sync.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-opening-sync.js +218 -0
- package/dist/systems/elevator/elevator-opening-sync.test.d.ts +2 -0
- package/dist/systems/elevator/elevator-opening-sync.test.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-opening-sync.test.js +146 -0
- package/dist/systems/elevator/elevator-opening-system.d.ts +2 -0
- package/dist/systems/elevator/elevator-opening-system.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-opening-system.js +48 -0
- package/dist/systems/elevator/elevator-runtime-system.d.ts +2 -0
- package/dist/systems/elevator/elevator-runtime-system.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-runtime-system.js +8 -0
- package/dist/systems/elevator/elevator-runtime.d.ts +18 -0
- package/dist/systems/elevator/elevator-runtime.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-runtime.js +225 -0
- package/dist/systems/elevator/elevator-runtime.test.d.ts +2 -0
- package/dist/systems/elevator/elevator-runtime.test.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-runtime.test.js +64 -0
- package/dist/systems/elevator/elevator-service.d.ts +19 -0
- package/dist/systems/elevator/elevator-service.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-service.js +111 -0
- package/dist/systems/stair/stair-footprint.d.ts +44 -0
- package/dist/systems/stair/stair-footprint.d.ts.map +1 -0
- package/dist/systems/stair/stair-footprint.js +168 -0
- package/dist/systems/stair/stair-opening-preview.d.ts +1777 -0
- package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -0
- package/dist/systems/stair/stair-opening-preview.js +93 -0
- package/dist/systems/stair/stair-opening-preview.test.d.ts +2 -0
- package/dist/systems/stair/stair-opening-preview.test.d.ts.map +1 -0
- package/dist/systems/stair/stair-opening-preview.test.js +80 -0
- package/dist/systems/stair/stair-opening-sync.d.ts.map +1 -1
- package/dist/systems/stair/stair-opening-sync.js +183 -185
- package/dist/systems/stair/stair-opening-sync.test.js +385 -4
- package/dist/systems/stair/stair-opening-system.d.ts +2 -0
- package/dist/systems/stair/stair-opening-system.d.ts.map +1 -0
- package/dist/systems/stair/stair-opening-system.js +104 -0
- package/dist/systems/wall/wall-curve.d.ts +1 -1
- package/dist/systems/wall/wall-curve.d.ts.map +1 -1
- package/dist/systems/wall/wall-curve.js +1 -1
- package/dist/systems/wall/wall-mitering.d.ts.map +1 -1
- package/dist/systems/wall/wall-mitering.js +2 -6
- package/dist/systems/wall/wall-move.d.ts +48 -0
- package/dist/systems/wall/wall-move.d.ts.map +1 -0
- package/dist/systems/wall/wall-move.js +193 -0
- package/dist/utils/clone-scene-graph.d.ts +6 -4
- package/dist/utils/clone-scene-graph.d.ts.map +1 -1
- package/dist/utils/clone-scene-graph.js +6 -4
- package/dist/utils/clone-scene-graph.test.d.ts +2 -0
- package/dist/utils/clone-scene-graph.test.d.ts.map +1 -0
- package/dist/utils/clone-scene-graph.test.js +59 -0
- package/dist/validation/validate-build-json.d.ts +43 -0
- package/dist/validation/validate-build-json.d.ts.map +1 -0
- package/dist/validation/validate-build-json.js +227 -0
- package/package.json +11 -3
- package/dist/materials.d.ts +0 -10
- package/dist/materials.d.ts.map +0 -1
- package/dist/materials.js +0 -22
- package/dist/systems/ceiling/ceiling-system.d.ts +0 -8
- package/dist/systems/ceiling/ceiling-system.d.ts.map +0 -1
- package/dist/systems/ceiling/ceiling-system.js +0 -92
- package/dist/systems/door/door-system.d.ts +0 -2
- package/dist/systems/door/door-system.d.ts.map +0 -1
- package/dist/systems/door/door-system.js +0 -195
- package/dist/systems/fence/fence-system.d.ts +0 -2
- package/dist/systems/fence/fence-system.d.ts.map +0 -1
- package/dist/systems/fence/fence-system.js +0 -187
- package/dist/systems/item/item-system.d.ts +0 -2
- package/dist/systems/item/item-system.d.ts.map +0 -1
- package/dist/systems/item/item-system.js +0 -48
- package/dist/systems/roof/roof-system.d.ts +0 -16
- package/dist/systems/roof/roof-system.d.ts.map +0 -1
- package/dist/systems/roof/roof-system.js +0 -797
- package/dist/systems/slab/slab-system.d.ts +0 -8
- package/dist/systems/slab/slab-system.d.ts.map +0 -1
- package/dist/systems/slab/slab-system.js +0 -214
- package/dist/systems/stair/stair-system.d.ts +0 -2
- package/dist/systems/stair/stair-system.d.ts.map +0 -1
- package/dist/systems/stair/stair-system.js +0 -776
- package/dist/systems/wall/wall-system.d.ts +0 -12
- package/dist/systems/wall/wall-system.d.ts.map +0 -1
- package/dist/systems/wall/wall-system.js +0 -455
- package/dist/systems/window/window-system.d.ts +0 -2
- package/dist/systems/window/window-system.d.ts.map +0 -1
- package/dist/systems/window/window-system.js +0 -131
|
@@ -1,4 +1,346 @@
|
|
|
1
|
-
import { getEffectiveWallSurfaceMaterial, getWallSurfaceMaterialSignature, } from '../../schema';
|
|
1
|
+
import { AnyNode as AnyNodeSchema, getEffectiveWallSurfaceMaterial, getWallSurfaceMaterialSignature, } from '../../schema';
|
|
2
|
+
const NUMBER_FORMAT_BOUNDS = {
|
|
3
|
+
safeint: [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER],
|
|
4
|
+
int32: [-2147483648, 2147483647],
|
|
5
|
+
uint32: [0, 4294967295],
|
|
6
|
+
float32: [-3.4028234663852886e38, 3.4028234663852886e38],
|
|
7
|
+
};
|
|
8
|
+
const INTEGER_NUMBER_FORMATS = new Set(['safeint', 'int32', 'uint32']);
|
|
9
|
+
function getSchemaDef(schema) {
|
|
10
|
+
return schema?._zod?.def ?? schema?.def;
|
|
11
|
+
}
|
|
12
|
+
function getSchemaDefault(schema) {
|
|
13
|
+
const def = getSchemaDef(schema);
|
|
14
|
+
if (!(def?.type === 'default' || def?.type === 'prefault'))
|
|
15
|
+
return undefined;
|
|
16
|
+
return def.defaultValue;
|
|
17
|
+
}
|
|
18
|
+
function unwrapSchema(schema) {
|
|
19
|
+
let current = schema ?? null;
|
|
20
|
+
while (current) {
|
|
21
|
+
const def = getSchemaDef(current);
|
|
22
|
+
if (!(def?.type === 'default' ||
|
|
23
|
+
def?.type === 'prefault' ||
|
|
24
|
+
def?.type === 'optional' ||
|
|
25
|
+
def?.type === 'nullable' ||
|
|
26
|
+
def?.type === 'catch' ||
|
|
27
|
+
def?.type === 'readonly' ||
|
|
28
|
+
def?.type === 'nonoptional')) {
|
|
29
|
+
return current;
|
|
30
|
+
}
|
|
31
|
+
current = def.innerType ?? null;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
function getObjectShape(schema) {
|
|
36
|
+
const unwrapped = unwrapSchema(schema);
|
|
37
|
+
const def = getSchemaDef(unwrapped);
|
|
38
|
+
if (def?.type !== 'object')
|
|
39
|
+
return null;
|
|
40
|
+
return unwrapped?.shape ?? def.shape ?? null;
|
|
41
|
+
}
|
|
42
|
+
function schemaAllowsValue(schema, value) {
|
|
43
|
+
const unwrapped = unwrapSchema(schema);
|
|
44
|
+
if (!unwrapped)
|
|
45
|
+
return false;
|
|
46
|
+
const def = getSchemaDef(unwrapped);
|
|
47
|
+
if (unwrapped._zod?.values?.has(value))
|
|
48
|
+
return true;
|
|
49
|
+
if (Array.isArray(def?.values) && def.values.includes(value))
|
|
50
|
+
return true;
|
|
51
|
+
if (def?.entries && Object.values(def.entries).includes(value))
|
|
52
|
+
return true;
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
function getNodeSchemaForType(type) {
|
|
56
|
+
const schema = AnyNodeSchema;
|
|
57
|
+
const options = getSchemaDef(schema)?.options;
|
|
58
|
+
if (!options)
|
|
59
|
+
return null;
|
|
60
|
+
for (const option of options) {
|
|
61
|
+
const shape = getObjectShape(option);
|
|
62
|
+
if (shape?.type && schemaAllowsValue(shape.type, type)) {
|
|
63
|
+
return option;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
function applyLowerLimit(current, candidate) {
|
|
69
|
+
if (!current)
|
|
70
|
+
return candidate;
|
|
71
|
+
if (candidate.value > current.value)
|
|
72
|
+
return candidate;
|
|
73
|
+
if (candidate.value === current.value && !candidate.inclusive)
|
|
74
|
+
return candidate;
|
|
75
|
+
return current;
|
|
76
|
+
}
|
|
77
|
+
function applyUpperLimit(current, candidate) {
|
|
78
|
+
if (!current)
|
|
79
|
+
return candidate;
|
|
80
|
+
if (candidate.value < current.value)
|
|
81
|
+
return candidate;
|
|
82
|
+
if (candidate.value === current.value && !candidate.inclusive)
|
|
83
|
+
return candidate;
|
|
84
|
+
return current;
|
|
85
|
+
}
|
|
86
|
+
function getNumberConstraints(schema) {
|
|
87
|
+
const unwrapped = unwrapSchema(schema) ?? schema;
|
|
88
|
+
const def = getSchemaDef(unwrapped);
|
|
89
|
+
const constraints = { integer: false };
|
|
90
|
+
const minValue = unwrapped.minValue;
|
|
91
|
+
if (typeof minValue === 'number') {
|
|
92
|
+
constraints.min = applyLowerLimit(constraints.min, { value: minValue, inclusive: true });
|
|
93
|
+
}
|
|
94
|
+
const maxValue = unwrapped.maxValue;
|
|
95
|
+
if (typeof maxValue === 'number') {
|
|
96
|
+
constraints.max = applyUpperLimit(constraints.max, { value: maxValue, inclusive: true });
|
|
97
|
+
}
|
|
98
|
+
for (const check of def?.checks ?? []) {
|
|
99
|
+
const checkDef = check._zod?.def;
|
|
100
|
+
if (!checkDef)
|
|
101
|
+
continue;
|
|
102
|
+
if (checkDef.check === 'greater_than' && typeof checkDef.value === 'number') {
|
|
103
|
+
constraints.min = applyLowerLimit(constraints.min, {
|
|
104
|
+
value: checkDef.value,
|
|
105
|
+
inclusive: checkDef.inclusive !== false,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
else if (checkDef.check === 'less_than' && typeof checkDef.value === 'number') {
|
|
109
|
+
constraints.max = applyUpperLimit(constraints.max, {
|
|
110
|
+
value: checkDef.value,
|
|
111
|
+
inclusive: checkDef.inclusive !== false,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
else if (checkDef.check === 'number_format' && checkDef.format) {
|
|
115
|
+
constraints.integer ||= INTEGER_NUMBER_FORMATS.has(checkDef.format);
|
|
116
|
+
const bounds = NUMBER_FORMAT_BOUNDS[checkDef.format];
|
|
117
|
+
if (bounds) {
|
|
118
|
+
constraints.min = applyLowerLimit(constraints.min, {
|
|
119
|
+
value: bounds[0],
|
|
120
|
+
inclusive: true,
|
|
121
|
+
});
|
|
122
|
+
constraints.max = applyUpperLimit(constraints.max, {
|
|
123
|
+
value: bounds[1],
|
|
124
|
+
inclusive: true,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const bag = unwrapped._zod?.bag;
|
|
130
|
+
if (typeof bag?.minimum === 'number') {
|
|
131
|
+
constraints.min = applyLowerLimit(constraints.min, { value: bag.minimum, inclusive: true });
|
|
132
|
+
}
|
|
133
|
+
if (typeof bag?.exclusiveMinimum === 'number') {
|
|
134
|
+
constraints.min = applyLowerLimit(constraints.min, {
|
|
135
|
+
value: bag.exclusiveMinimum,
|
|
136
|
+
inclusive: false,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
if (typeof bag?.maximum === 'number') {
|
|
140
|
+
constraints.max = applyUpperLimit(constraints.max, { value: bag.maximum, inclusive: true });
|
|
141
|
+
}
|
|
142
|
+
if (typeof bag?.exclusiveMaximum === 'number') {
|
|
143
|
+
constraints.max = applyUpperLimit(constraints.max, {
|
|
144
|
+
value: bag.exclusiveMaximum,
|
|
145
|
+
inclusive: false,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return constraints;
|
|
149
|
+
}
|
|
150
|
+
function nextAbove(value) {
|
|
151
|
+
return value === 0 ? Number.EPSILON : value + Math.abs(value) * Number.EPSILON;
|
|
152
|
+
}
|
|
153
|
+
function nextBelow(value) {
|
|
154
|
+
return value === 0 ? -Number.EPSILON : value - Math.abs(value) * Number.EPSILON;
|
|
155
|
+
}
|
|
156
|
+
function clampNumber(value, constraints) {
|
|
157
|
+
let next = value;
|
|
158
|
+
let rounded = false;
|
|
159
|
+
let clamped = false;
|
|
160
|
+
if (constraints.integer && !Number.isInteger(next)) {
|
|
161
|
+
next = Math.round(next);
|
|
162
|
+
rounded = true;
|
|
163
|
+
}
|
|
164
|
+
if (constraints.min) {
|
|
165
|
+
const min = constraints.min.inclusive ? constraints.min.value : nextAbove(constraints.min.value);
|
|
166
|
+
if (next < min) {
|
|
167
|
+
next = min;
|
|
168
|
+
clamped = true;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (constraints.max) {
|
|
172
|
+
const max = constraints.max.inclusive ? constraints.max.value : nextBelow(constraints.max.value);
|
|
173
|
+
if (next > max) {
|
|
174
|
+
next = max;
|
|
175
|
+
clamped = true;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
value: next,
|
|
180
|
+
action: clamped ? 'clamped' : rounded ? 'rounded' : undefined,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function getFiniteFallbackNumber(fallback, constraints) {
|
|
184
|
+
if (typeof fallback !== 'number' || !Number.isFinite(fallback))
|
|
185
|
+
return undefined;
|
|
186
|
+
return clampNumber(fallback, constraints).value;
|
|
187
|
+
}
|
|
188
|
+
function sanitizeNumber(schema, value, fallback, path) {
|
|
189
|
+
const constraints = schema ? getNumberConstraints(schema) : { integer: false };
|
|
190
|
+
if (!Number.isFinite(value)) {
|
|
191
|
+
const replacement = getFiniteFallbackNumber(fallback, constraints);
|
|
192
|
+
if (replacement === undefined) {
|
|
193
|
+
return {
|
|
194
|
+
value,
|
|
195
|
+
omit: true,
|
|
196
|
+
issues: [{ path, from: value, action: 'dropped' }],
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
value: replacement,
|
|
201
|
+
issues: [{ path, from: value, to: replacement, action: 'dropped' }],
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
const clamped = clampNumber(value, constraints);
|
|
205
|
+
if (!Object.is(clamped.value, value)) {
|
|
206
|
+
return {
|
|
207
|
+
value: clamped.value,
|
|
208
|
+
issues: [
|
|
209
|
+
{
|
|
210
|
+
path,
|
|
211
|
+
from: value,
|
|
212
|
+
to: clamped.value,
|
|
213
|
+
action: clamped.action ?? 'clamped',
|
|
214
|
+
},
|
|
215
|
+
],
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
return { value, issues: [] };
|
|
219
|
+
}
|
|
220
|
+
function sanitizeNumericValue(schema, value, fallback, path) {
|
|
221
|
+
const defaultFallback = schema ? getSchemaDefault(schema) : undefined;
|
|
222
|
+
const effectiveFallback = fallback === undefined ? defaultFallback : fallback;
|
|
223
|
+
const unwrapped = unwrapSchema(schema);
|
|
224
|
+
const def = getSchemaDef(unwrapped);
|
|
225
|
+
if (def?.type === 'number') {
|
|
226
|
+
if (typeof value !== 'number')
|
|
227
|
+
return { value, issues: [] };
|
|
228
|
+
return sanitizeNumber(unwrapped, value, effectiveFallback, path);
|
|
229
|
+
}
|
|
230
|
+
if (typeof value === 'number') {
|
|
231
|
+
return sanitizeNumber(null, value, effectiveFallback, path);
|
|
232
|
+
}
|
|
233
|
+
if (def?.type === 'tuple' && Array.isArray(value)) {
|
|
234
|
+
const fallbackItems = Array.isArray(effectiveFallback) ? effectiveFallback : [];
|
|
235
|
+
const next = [...value];
|
|
236
|
+
const issues = [];
|
|
237
|
+
for (let index = 0; index < next.length; index += 1) {
|
|
238
|
+
const itemSchema = def.items?.[index] ?? def.rest ?? null;
|
|
239
|
+
const child = sanitizeNumericValue(itemSchema, next[index], fallbackItems[index], [
|
|
240
|
+
...path,
|
|
241
|
+
index,
|
|
242
|
+
]);
|
|
243
|
+
issues.push(...child.issues);
|
|
244
|
+
if (child.omit) {
|
|
245
|
+
return { value, omit: true, issues };
|
|
246
|
+
}
|
|
247
|
+
next[index] = child.value;
|
|
248
|
+
}
|
|
249
|
+
return { value: issues.length > 0 ? next : value, issues };
|
|
250
|
+
}
|
|
251
|
+
if (def?.type === 'array' && Array.isArray(value)) {
|
|
252
|
+
const fallbackItems = Array.isArray(effectiveFallback) ? effectiveFallback : [];
|
|
253
|
+
const next = [];
|
|
254
|
+
const issues = [];
|
|
255
|
+
let omitted = false;
|
|
256
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
257
|
+
const child = sanitizeNumericValue(def.element ?? null, value[index], fallbackItems[index], [
|
|
258
|
+
...path,
|
|
259
|
+
index,
|
|
260
|
+
]);
|
|
261
|
+
issues.push(...child.issues);
|
|
262
|
+
if (child.omit) {
|
|
263
|
+
omitted = true;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
next.push(child.value);
|
|
267
|
+
}
|
|
268
|
+
return { value: issues.length > 0 || omitted ? next : value, issues };
|
|
269
|
+
}
|
|
270
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
271
|
+
const shape = def?.type === 'object' ? (unwrapped?.shape ?? def.shape ?? {}) : {};
|
|
272
|
+
const fallbackObject = effectiveFallback &&
|
|
273
|
+
typeof effectiveFallback === 'object' &&
|
|
274
|
+
!Array.isArray(effectiveFallback)
|
|
275
|
+
? effectiveFallback
|
|
276
|
+
: {};
|
|
277
|
+
const input = value;
|
|
278
|
+
const next = { ...input };
|
|
279
|
+
const issues = [];
|
|
280
|
+
for (const key of Object.keys(input)) {
|
|
281
|
+
const child = sanitizeNumericValue(shape[key] ?? null, input[key], fallbackObject[key], [
|
|
282
|
+
...path,
|
|
283
|
+
key,
|
|
284
|
+
]);
|
|
285
|
+
issues.push(...child.issues);
|
|
286
|
+
if (child.omit) {
|
|
287
|
+
delete next[key];
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
next[key] = child.value;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return { value: issues.length > 0 ? next : value, issues };
|
|
294
|
+
}
|
|
295
|
+
return { value, issues: [] };
|
|
296
|
+
}
|
|
297
|
+
function formatNumericValue(value) {
|
|
298
|
+
if (Number.isNaN(value))
|
|
299
|
+
return 'NaN';
|
|
300
|
+
if (value === Infinity)
|
|
301
|
+
return 'Infinity';
|
|
302
|
+
if (value === -Infinity)
|
|
303
|
+
return '-Infinity';
|
|
304
|
+
return String(value);
|
|
305
|
+
}
|
|
306
|
+
function numericSanitizeIssuesToMessage(issues) {
|
|
307
|
+
return issues
|
|
308
|
+
.map((issue) => {
|
|
309
|
+
const path = issue.path.map(String).join('.') || '<root>';
|
|
310
|
+
const to = issue.to === undefined ? '' : ` -> ${formatNumericValue(issue.to)}`;
|
|
311
|
+
return `${path}: ${formatNumericValue(issue.from)} ${issue.action}${to}`;
|
|
312
|
+
})
|
|
313
|
+
.join('; ');
|
|
314
|
+
}
|
|
315
|
+
function warnSanitizedNodeMutation(mutation, nodeId, issues) {
|
|
316
|
+
console.warn(`[Scene] Sanitized invalid numeric node ${mutation}`, nodeId, numericSanitizeIssuesToMessage(issues));
|
|
317
|
+
}
|
|
318
|
+
function parseCreatedNode(node, parentId) {
|
|
319
|
+
const candidate = { ...node, parentId };
|
|
320
|
+
const parsed = AnyNodeSchema.safeParse(candidate);
|
|
321
|
+
if (parsed.success)
|
|
322
|
+
return parsed.data;
|
|
323
|
+
const schema = getNodeSchemaForType(candidate.type);
|
|
324
|
+
const sanitized = sanitizeNumericValue(schema, candidate, undefined, []);
|
|
325
|
+
if (sanitized.issues.length === 0) {
|
|
326
|
+
return candidate;
|
|
327
|
+
}
|
|
328
|
+
warnSanitizedNodeMutation('create', node.id, sanitized.issues);
|
|
329
|
+
return sanitized.value;
|
|
330
|
+
}
|
|
331
|
+
function parseUpdatedNode(currentNode, data) {
|
|
332
|
+
const candidate = { ...currentNode, ...data };
|
|
333
|
+
const parsed = AnyNodeSchema.safeParse(candidate);
|
|
334
|
+
if (parsed.success)
|
|
335
|
+
return parsed.data;
|
|
336
|
+
const schema = getNodeSchemaForType(candidate.type);
|
|
337
|
+
const sanitized = sanitizeNumericValue(schema, data, currentNode, []);
|
|
338
|
+
if (sanitized.issues.length === 0) {
|
|
339
|
+
return candidate;
|
|
340
|
+
}
|
|
341
|
+
warnSanitizedNodeMutation('update', currentNode.id, sanitized.issues);
|
|
342
|
+
return { ...currentNode, ...sanitized.value };
|
|
343
|
+
}
|
|
2
344
|
// Track pending RAF for updateNodesAction to prevent multiple queued callbacks
|
|
3
345
|
let pendingRafId = null;
|
|
4
346
|
let pendingUpdates = new Set();
|
|
@@ -72,7 +414,7 @@ function buildMergedWallAttachmentUpdates(primary, secondary, mergedWallId, merg
|
|
|
72
414
|
const wallChildren = [...(primary.children ?? []), ...(secondary.children ?? [])];
|
|
73
415
|
for (const childId of wallChildren) {
|
|
74
416
|
const child = nodes[childId];
|
|
75
|
-
if (!child
|
|
417
|
+
if (!(child && 'position' in child && Array.isArray(child.position))) {
|
|
76
418
|
continue;
|
|
77
419
|
}
|
|
78
420
|
const sourceWall = child.parentId === secondary.id ? secondary : primary;
|
|
@@ -123,10 +465,10 @@ function buildWallMergePlans(nodes, idsToDelete) {
|
|
|
123
465
|
return a.id.localeCompare(b.id);
|
|
124
466
|
});
|
|
125
467
|
const [primary, secondary] = sortedCandidates;
|
|
126
|
-
if (!primary
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
468
|
+
if (!(primary &&
|
|
469
|
+
secondary &&
|
|
470
|
+
areWallStylesCompatible(primary, secondary) &&
|
|
471
|
+
areWallsCollinearAcrossPoint(primary, secondary, junction))) {
|
|
130
472
|
continue;
|
|
131
473
|
}
|
|
132
474
|
const { start, end } = resolveMergedWallEndpoints(primary, secondary, junction);
|
|
@@ -154,21 +496,25 @@ export const createNodesAction = (set, get, ops) => {
|
|
|
154
496
|
const nextRootIds = [...state.rootNodeIds];
|
|
155
497
|
for (const { node, parentId } of ops) {
|
|
156
498
|
const effectiveParentId = parentId ?? node.parentId ?? null;
|
|
157
|
-
|
|
158
|
-
const newNode = {
|
|
159
|
-
...node,
|
|
160
|
-
parentId: effectiveParentId,
|
|
161
|
-
};
|
|
499
|
+
const newNode = parseCreatedNode(node, effectiveParentId);
|
|
162
500
|
nextNodes[newNode.id] = newNode;
|
|
163
|
-
// 2. Update the Parent's children list
|
|
501
|
+
// 2. Update the Parent's children list. We append to ANY container
|
|
502
|
+
// parent (kind has `children` in its schema) — if the field is
|
|
503
|
+
// present but undefined (e.g. an old saved scene from before the
|
|
504
|
+
// kind gained children), we initialise to `[]` first so the
|
|
505
|
+
// reparenting goes through. Without this, hosting items on an
|
|
506
|
+
// old shelf (v1, before `children` was added) silently no-ops:
|
|
507
|
+
// the item is reparented to the shelf but the shelf's children
|
|
508
|
+
// array is never updated, so `ParametricNodeRenderer` doesn't
|
|
509
|
+
// mount it and the item "disappears".
|
|
164
510
|
if (effectiveParentId && nextNodes[effectiveParentId]) {
|
|
165
511
|
const parent = nextNodes[effectiveParentId];
|
|
166
|
-
|
|
167
|
-
|
|
512
|
+
if ('children' in parent) {
|
|
513
|
+
const existing = parent.children;
|
|
514
|
+
const children = Array.isArray(existing) ? existing : [];
|
|
168
515
|
nextNodes[effectiveParentId] = {
|
|
169
516
|
...parent,
|
|
170
|
-
|
|
171
|
-
children: Array.from(new Set([...parent.children, newNode.id])), // We don't verify child types here
|
|
517
|
+
children: Array.from(new Set([...children, newNode.id])),
|
|
172
518
|
};
|
|
173
519
|
}
|
|
174
520
|
}
|
|
@@ -186,8 +532,129 @@ export const createNodesAction = (set, get, ops) => {
|
|
|
186
532
|
get().markDirty(node.id);
|
|
187
533
|
if (parentId)
|
|
188
534
|
get().markDirty(parentId);
|
|
535
|
+
else if (node.parentId)
|
|
536
|
+
get().markDirty(node.parentId);
|
|
189
537
|
});
|
|
190
538
|
};
|
|
539
|
+
export const applyNodeChangesAction = (set, get, changes) => {
|
|
540
|
+
if (get().readOnly)
|
|
541
|
+
return;
|
|
542
|
+
const createOps = changes.create ?? [];
|
|
543
|
+
const updateOps = changes.update ?? [];
|
|
544
|
+
const deleteOps = changes.delete ?? [];
|
|
545
|
+
const nodesToMarkDirty = new Set();
|
|
546
|
+
const parentsToMarkDirty = new Set();
|
|
547
|
+
set((state) => {
|
|
548
|
+
const nextNodes = { ...state.nodes };
|
|
549
|
+
const nextCollections = { ...state.collections };
|
|
550
|
+
const nextRootIds = [...state.rootNodeIds];
|
|
551
|
+
let resolvedRootIds = nextRootIds;
|
|
552
|
+
for (const { id, data } of updateOps) {
|
|
553
|
+
const currentNode = nextNodes[id];
|
|
554
|
+
if (!currentNode)
|
|
555
|
+
continue;
|
|
556
|
+
const updatedNode = parseUpdatedNode(currentNode, data);
|
|
557
|
+
if (data.parentId !== undefined && data.parentId !== currentNode.parentId) {
|
|
558
|
+
const oldParentId = currentNode.parentId;
|
|
559
|
+
if (oldParentId && nextNodes[oldParentId]) {
|
|
560
|
+
const oldParent = nextNodes[oldParentId];
|
|
561
|
+
nextNodes[oldParent.id] = {
|
|
562
|
+
...oldParent,
|
|
563
|
+
children: oldParent.children.filter((childId) => childId !== id),
|
|
564
|
+
};
|
|
565
|
+
parentsToMarkDirty.add(oldParent.id);
|
|
566
|
+
}
|
|
567
|
+
const newParentId = data.parentId;
|
|
568
|
+
if (newParentId && nextNodes[newParentId]) {
|
|
569
|
+
const newParent = nextNodes[newParentId];
|
|
570
|
+
nextNodes[newParent.id] = {
|
|
571
|
+
...newParent,
|
|
572
|
+
children: Array.from(new Set([...newParent.children, id])),
|
|
573
|
+
};
|
|
574
|
+
parentsToMarkDirty.add(newParent.id);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
nextNodes[id] = updatedNode;
|
|
578
|
+
nodesToMarkDirty.add(id);
|
|
579
|
+
}
|
|
580
|
+
for (const { node, parentId } of createOps) {
|
|
581
|
+
const effectiveParentId = parentId ?? node.parentId ?? null;
|
|
582
|
+
const newNode = parseCreatedNode(node, effectiveParentId);
|
|
583
|
+
nextNodes[newNode.id] = newNode;
|
|
584
|
+
nodesToMarkDirty.add(newNode.id);
|
|
585
|
+
if (effectiveParentId && nextNodes[effectiveParentId]) {
|
|
586
|
+
const parent = nextNodes[effectiveParentId];
|
|
587
|
+
if ('children' in parent && Array.isArray(parent.children)) {
|
|
588
|
+
nextNodes[effectiveParentId] = {
|
|
589
|
+
...parent,
|
|
590
|
+
children: Array.from(new Set([...parent.children, newNode.id])),
|
|
591
|
+
};
|
|
592
|
+
parentsToMarkDirty.add(effectiveParentId);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
else if (!effectiveParentId && !nextRootIds.includes(newNode.id)) {
|
|
596
|
+
nextRootIds.push(newNode.id);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
const allIdsToDelete = new Set();
|
|
600
|
+
const collectDelete = (id) => {
|
|
601
|
+
if (allIdsToDelete.has(id))
|
|
602
|
+
return;
|
|
603
|
+
allIdsToDelete.add(id);
|
|
604
|
+
const node = nextNodes[id];
|
|
605
|
+
if (node && 'children' in node && Array.isArray(node.children)) {
|
|
606
|
+
for (const childId of node.children) {
|
|
607
|
+
collectDelete(childId);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
for (const id of deleteOps) {
|
|
612
|
+
collectDelete(id);
|
|
613
|
+
}
|
|
614
|
+
for (const id of allIdsToDelete) {
|
|
615
|
+
const node = nextNodes[id];
|
|
616
|
+
if (!node)
|
|
617
|
+
continue;
|
|
618
|
+
const parentId = node.parentId;
|
|
619
|
+
if (parentId && nextNodes[parentId] && !allIdsToDelete.has(parentId)) {
|
|
620
|
+
const parent = nextNodes[parentId];
|
|
621
|
+
if (parent.children) {
|
|
622
|
+
nextNodes[parent.id] = {
|
|
623
|
+
...parent,
|
|
624
|
+
children: parent.children.filter((childId) => childId !== id),
|
|
625
|
+
};
|
|
626
|
+
parentsToMarkDirty.add(parent.id);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
resolvedRootIds = resolvedRootIds.filter((rootId) => rootId !== id);
|
|
630
|
+
if ('collectionIds' in node && node.collectionIds) {
|
|
631
|
+
for (const collectionId of node.collectionIds) {
|
|
632
|
+
const collection = nextCollections[collectionId];
|
|
633
|
+
if (collection) {
|
|
634
|
+
nextCollections[collectionId] = {
|
|
635
|
+
...collection,
|
|
636
|
+
nodeIds: collection.nodeIds.filter((nodeId) => nodeId !== id),
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
delete nextNodes[id];
|
|
642
|
+
}
|
|
643
|
+
return { nodes: nextNodes, rootNodeIds: resolvedRootIds, collections: nextCollections };
|
|
644
|
+
});
|
|
645
|
+
for (const id of nodesToMarkDirty) {
|
|
646
|
+
get().markDirty(id);
|
|
647
|
+
}
|
|
648
|
+
for (const id of parentsToMarkDirty) {
|
|
649
|
+
get().markDirty(id);
|
|
650
|
+
const parent = get().nodes[id];
|
|
651
|
+
if (parent && 'children' in parent && Array.isArray(parent.children)) {
|
|
652
|
+
for (const childId of parent.children) {
|
|
653
|
+
get().markDirty(childId);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
};
|
|
191
658
|
export const updateNodesAction = (set, get, updates) => {
|
|
192
659
|
if (get().readOnly)
|
|
193
660
|
return;
|
|
@@ -198,31 +665,43 @@ export const updateNodesAction = (set, get, updates) => {
|
|
|
198
665
|
const currentNode = nextNodes[id];
|
|
199
666
|
if (!currentNode)
|
|
200
667
|
continue;
|
|
668
|
+
const updatedNode = parseUpdatedNode(currentNode, data);
|
|
201
669
|
// Handle Reparenting Logic
|
|
202
670
|
if (data.parentId !== undefined && data.parentId !== currentNode.parentId) {
|
|
203
671
|
// 1. Remove from old parent
|
|
204
672
|
const oldParentId = currentNode.parentId;
|
|
205
673
|
if (oldParentId && nextNodes[oldParentId]) {
|
|
206
674
|
const oldParent = nextNodes[oldParentId];
|
|
675
|
+
const oldChildren = Array.isArray(oldParent.children)
|
|
676
|
+
? oldParent.children
|
|
677
|
+
: [];
|
|
207
678
|
nextNodes[oldParent.id] = {
|
|
208
679
|
...oldParent,
|
|
209
|
-
children:
|
|
680
|
+
children: oldChildren.filter((childId) => childId !== id),
|
|
210
681
|
};
|
|
211
682
|
parentsToUpdate.add(oldParent.id);
|
|
212
683
|
}
|
|
213
|
-
// 2. Add to new parent
|
|
684
|
+
// 2. Add to new parent. Defensive against parents that don't yet
|
|
685
|
+
// carry a `children` array — older saved scenes can predate the
|
|
686
|
+
// schema field on a particular kind (shelf v1 → v2 added one),
|
|
687
|
+
// and a spread of `undefined` here throws and aborts the entire
|
|
688
|
+
// `set` callback. Initialising to `[]` matches what the schema's
|
|
689
|
+
// default would have produced.
|
|
214
690
|
const newParentId = data.parentId;
|
|
215
691
|
if (newParentId && nextNodes[newParentId]) {
|
|
216
692
|
const newParent = nextNodes[newParentId];
|
|
693
|
+
const newChildren = Array.isArray(newParent.children)
|
|
694
|
+
? newParent.children
|
|
695
|
+
: [];
|
|
217
696
|
nextNodes[newParent.id] = {
|
|
218
697
|
...newParent,
|
|
219
|
-
children: Array.from(new Set([...
|
|
698
|
+
children: Array.from(new Set([...newChildren, id])),
|
|
220
699
|
};
|
|
221
700
|
parentsToUpdate.add(newParent.id);
|
|
222
701
|
}
|
|
223
702
|
}
|
|
224
703
|
// Apply the update
|
|
225
|
-
nextNodes[id] =
|
|
704
|
+
nextNodes[id] = updatedNode;
|
|
226
705
|
}
|
|
227
706
|
return { nodes: nextNodes };
|
|
228
707
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-mutation-sanitize.test.d.ts","sourceRoot":"","sources":["../../../src/store/actions/node-mutation-sanitize.test.ts"],"names":[],"mappings":""}
|