@pascal-app/core 1.0.0-beta.3 → 1.0.0-beta.4
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/store/use-scene.js +11 -9
- package/dist/utils/heal-scene-graph.d.ts +5 -0
- package/dist/utils/heal-scene-graph.d.ts.map +1 -1
- package/dist/utils/heal-scene-graph.js +46 -1
- package/dist/utils/scene-migrations.d.ts +3 -0
- package/dist/utils/scene-migrations.d.ts.map +1 -0
- package/dist/utils/scene-migrations.js +6 -0
- package/package.json +4 -4
package/dist/store/use-scene.js
CHANGED
|
@@ -1080,22 +1080,24 @@ const useScene = create()(temporal((set, get) => ({
|
|
|
1080
1080
|
});
|
|
1081
1081
|
return; // Scene already loaded
|
|
1082
1082
|
}
|
|
1083
|
-
// Create hierarchy: Site → Building → Level
|
|
1083
|
+
// Create hierarchy: Site → Building → Level. Parent links must be
|
|
1084
|
+
// written explicitly — the schema defaults `parentId` to null, and the
|
|
1085
|
+
// scene authority rejects parent/child asymmetry that the renderer
|
|
1086
|
+
// happily traverses through `children`.
|
|
1087
|
+
const site = SiteNode.parse({});
|
|
1088
|
+
const building = BuildingNode.parse({
|
|
1089
|
+
parentId: site.id,
|
|
1090
|
+
});
|
|
1084
1091
|
const level0 = LevelNode.parse({
|
|
1092
|
+
parentId: building.id,
|
|
1085
1093
|
level: 0,
|
|
1086
1094
|
children: [],
|
|
1087
1095
|
height: 2.5,
|
|
1088
1096
|
});
|
|
1089
|
-
const building = BuildingNode.parse({
|
|
1090
|
-
children: [level0.id],
|
|
1091
|
-
});
|
|
1092
|
-
const site = SiteNode.parse({
|
|
1093
|
-
children: [building.id],
|
|
1094
|
-
});
|
|
1095
1097
|
// Define all nodes flat
|
|
1096
1098
|
const nodes = {
|
|
1097
|
-
[site.id]: site,
|
|
1098
|
-
[building.id]: building,
|
|
1099
|
+
[site.id]: { ...site, children: [building.id] },
|
|
1100
|
+
[building.id]: { ...building, children: [level0.id] },
|
|
1099
1101
|
[level0.id]: level0,
|
|
1100
1102
|
};
|
|
1101
1103
|
// Site is the root
|
|
@@ -9,6 +9,11 @@ export interface HealSceneResult {
|
|
|
9
9
|
* a different node (stale reparent leftovers), plus same-array duplicates.
|
|
10
10
|
*/
|
|
11
11
|
strippedStaleChildRefs: number;
|
|
12
|
+
/**
|
|
13
|
+
* Ids of nodes whose null `parentId` was repaired to the one parent that
|
|
14
|
+
* still claims them via `children`.
|
|
15
|
+
*/
|
|
16
|
+
repairedParentLinkNodeIds: string[];
|
|
12
17
|
}
|
|
13
18
|
/**
|
|
14
19
|
* Returns a healed copy of a `nodes` map. Pure — does not mutate `input`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"heal-scene-graph.d.ts","sourceRoot":"","sources":["../../src/utils/heal-scene-graph.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"heal-scene-graph.d.ts","sourceRoot":"","sources":["../../src/utils/heal-scene-graph.ts"],"names":[],"mappings":"AA0BA,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,kDAAkD;IAClD,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,sFAAsF;IACtF,iBAAiB,EAAE,MAAM,CAAA;IACzB;;;OAGG;IACH,sBAAsB,EAAE,MAAM,CAAA;IAC9B;;;OAGG;IACH,yBAAyB,EAAE,MAAM,EAAE,CAAA;CACpC;AAgBD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe,CAiH9E"}
|
|
@@ -13,6 +13,11 @@
|
|
|
13
13
|
// duplicate reference renders the child twice (duplicate React keys in the
|
|
14
14
|
// 2D plan, doubled hosted geometry in 3D). Same-array duplicates are
|
|
15
15
|
// collapsed too.
|
|
16
|
+
// 4. A node with a null `parentId` that exactly one parent still claims via
|
|
17
|
+
// `children` (the legacy site-child flatten and the old default-scene
|
|
18
|
+
// assembler both linked children without writing `parentId`). The editor
|
|
19
|
+
// renders the chain through `children`, but the hosted scene authority
|
|
20
|
+
// validates parent/child symmetry and rejects the whole scene.
|
|
16
21
|
//
|
|
17
22
|
// All are also prevented at the source now; this is the load-time safety net
|
|
18
23
|
// for already-saved scenes.
|
|
@@ -102,5 +107,45 @@ export function healSceneNodes(input) {
|
|
|
102
107
|
}
|
|
103
108
|
nodes[id] = node;
|
|
104
109
|
}
|
|
105
|
-
|
|
110
|
+
// Pass 3: repair null parent links. A node claimed as a child by exactly one
|
|
111
|
+
// parent must point back at it; legacy writers linked `children` without
|
|
112
|
+
// writing `parentId`. Embedded legacy site children claim by their `id` so
|
|
113
|
+
// the flattened flat-map node is repaired too.
|
|
114
|
+
const claimantsByChildId = new Map();
|
|
115
|
+
for (const [id, node] of Object.entries(nodes)) {
|
|
116
|
+
const children = node?.children;
|
|
117
|
+
if (!Array.isArray(children))
|
|
118
|
+
continue;
|
|
119
|
+
for (const child of children) {
|
|
120
|
+
const childId = typeof child === 'string'
|
|
121
|
+
? child
|
|
122
|
+
: child && typeof child === 'object' && typeof child.id === 'string'
|
|
123
|
+
? child.id
|
|
124
|
+
: null;
|
|
125
|
+
if (!childId || !(childId in nodes))
|
|
126
|
+
continue;
|
|
127
|
+
const claimants = claimantsByChildId.get(childId) ?? [];
|
|
128
|
+
claimants.push(id);
|
|
129
|
+
claimantsByChildId.set(childId, claimants);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const repairedParentLinkNodeIds = [];
|
|
133
|
+
for (const [id, node] of Object.entries(nodes)) {
|
|
134
|
+
if (!node || typeof node !== 'object')
|
|
135
|
+
continue;
|
|
136
|
+
if (node.parentId != null)
|
|
137
|
+
continue;
|
|
138
|
+
const claimants = claimantsByChildId.get(id);
|
|
139
|
+
if (claimants?.length !== 1 || claimants[0] === id)
|
|
140
|
+
continue;
|
|
141
|
+
nodes[id] = { ...node, parentId: claimants[0] };
|
|
142
|
+
repairedParentLinkNodeIds.push(id);
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
nodes,
|
|
146
|
+
droppedWallIds,
|
|
147
|
+
strippedChildRefs,
|
|
148
|
+
strippedStaleChildRefs,
|
|
149
|
+
repairedParentLinkNodeIds,
|
|
150
|
+
};
|
|
106
151
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scene-migrations.d.ts","sourceRoot":"","sources":["../../src/utils/scene-migrations.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,eAAe,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACzE,OAAO,EACL,KAAK,yBAAyB,EAC9B,8BAA8B,GAC/B,MAAM,uBAAuB,CAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Server-safe scene migrations shared by the client loader and the hosted
|
|
2
|
+
// scene authority. Everything exported here must stay pure data logic with no
|
|
3
|
+
// store, React, or Three.js imports so it can run in Server Components and
|
|
4
|
+
// API routes.
|
|
5
|
+
export { healSceneNodes } from './heal-scene-graph';
|
|
6
|
+
export { removeRetiredDrawingSheetNodes, } from './retired-scene-nodes';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pascal-app/core",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.4",
|
|
4
4
|
"description": "Core library for Pascal 3D building editor",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
"default": "./dist/utils/clone-scene-graph.js"
|
|
18
18
|
},
|
|
19
19
|
"./scene-migrations": {
|
|
20
|
-
"types": "./dist/utils/
|
|
21
|
-
"import": "./dist/utils/
|
|
22
|
-
"default": "./dist/utils/
|
|
20
|
+
"types": "./dist/utils/scene-migrations.d.ts",
|
|
21
|
+
"import": "./dist/utils/scene-migrations.js",
|
|
22
|
+
"default": "./dist/utils/scene-migrations.js"
|
|
23
23
|
},
|
|
24
24
|
"./registry": {
|
|
25
25
|
"types": "./dist/registry/index.d.ts",
|