@react-arch/react 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 React Arch
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # @react-arch/react
2
+
3
+ Declarative React components for describing buildings, plus the reconciler that
4
+ turns your component tree into the canonical `@react-arch/core` model.
5
+
6
+ This is the layer that makes **code the source of truth** (see ADR 0002). It is
7
+ a real [`react-reconciler`](https://npmjs.com/package/react-reconciler) host
8
+ renderer, so anything React can do — composition, hooks, props, `.map()` — works
9
+ when authoring buildings.
10
+
11
+ ## Components
12
+
13
+ `Building`, `Floor`, `Room`, `Wall`, `Door`, `Window`, `Opening`, `Furniture`
14
+ (`Fixture`), `Material`, `Group`, plus reserved `Slab` / `Roof` / `Stairs`.
15
+
16
+ ```tsx
17
+ import { Building, Floor, Room, Door, Window } from "@react-arch/react";
18
+
19
+ export function House() {
20
+ return (
21
+ <Building name="Modern House" units="metric">
22
+ <Floor id="ground" name="Ground Floor" elevation={0} height={2.8}>
23
+ <Room id="living" name="Living Room" x={0} y={0} width={5} depth={4}>
24
+ <Door wall="south" offset={1} width={0.9} />
25
+ <Window wall="west" offset={2} width={2.2} height={1.4} />
26
+ </Room>
27
+ </Floor>
28
+ </Building>
29
+ );
30
+ }
31
+ ```
32
+
33
+ A rectangular `<Room>` (`x`, `y`, `width`, `depth`) generates its four perimeter
34
+ walls; `<Door>`/`<Window>` attach to a named side (`north`/`south`/`east`/`west`)
35
+ at an `offset` along that wall. Coordinates are metres, X→right, Y→down.
36
+
37
+ ## Rendering to the model
38
+
39
+ ```ts
40
+ import { renderToDocument } from "@react-arch/react";
41
+ const doc = renderToDocument(<House />); // → BuildingDocument
42
+ ```
43
+
44
+ ## Registration (Remotion-style)
45
+
46
+ ```tsx
47
+ import { Composition } from "@react-arch/react";
48
+
49
+ export function Root() {
50
+ return (
51
+ <>
52
+ <Composition id="house" name="Modern House" component={House} />
53
+ </>
54
+ );
55
+ }
56
+ ```
57
+
58
+ The Studio mounts `Root`, reads the registry (`getCompositions`), and renders
59
+ the selected composition to a model with `renderComposition`.
@@ -0,0 +1,178 @@
1
+ import { ComponentType, ReactElement, ReactNode } from "react";
2
+ import { Building as BuildingModel, BuildingDocument, BuildingDocument as BuildingDocument$1, Floor as FloorModel, Opening as OpeningModel, Room as RoomModel, Wall as WallModel } from "@react-arch/core";
3
+
4
+ //#region src/tags.d.ts
5
+ /**
6
+ * Internal host element tags. Users never type these — the public components
7
+ * in `components.tsx` create them. The reconciler builds an instance tree of
8
+ * these tags, which `convert.ts` turns into a BuildingDocument.
9
+ */
10
+ declare const TAG: {
11
+ readonly building: "ra-building";
12
+ readonly floor: "ra-floor";
13
+ readonly room: "ra-room";
14
+ readonly wall: "ra-wall";
15
+ readonly door: "ra-door";
16
+ readonly window: "ra-window";
17
+ readonly opening: "ra-opening";
18
+ readonly furniture: "ra-furniture";
19
+ readonly material: "ra-material";
20
+ readonly group: "ra-group";
21
+ readonly slab: "ra-slab";
22
+ readonly roof: "ra-roof";
23
+ readonly stairs: "ra-stairs";
24
+ readonly text: "ra-text";
25
+ };
26
+ /** Side of a rectangular room a door/window attaches to. */
27
+ type RoomSide = "north" | "south" | "east" | "west";
28
+ //#endregion
29
+ //#region src/components.d.ts
30
+ type WithChildren<T> = T & {
31
+ children?: ReactNode;
32
+ };
33
+ interface BuildingProps {
34
+ id?: string;
35
+ name?: string;
36
+ units?: "metric" | "imperial";
37
+ }
38
+ declare function Building(props: WithChildren<BuildingProps>): import("react").DOMElement<Record<string, unknown>, Element>;
39
+ interface FloorProps {
40
+ id?: string;
41
+ name?: string;
42
+ elevation?: number;
43
+ height?: number;
44
+ }
45
+ declare function Floor(props: WithChildren<FloorProps>): import("react").DOMElement<Record<string, unknown>, Element>;
46
+ interface RoomProps {
47
+ id?: string;
48
+ name?: string;
49
+ /** Top-left corner X (metres, X increases right). */
50
+ x?: number;
51
+ /** Top-left corner Y (metres, Y increases down). */
52
+ y?: number;
53
+ /** Extent along X. */
54
+ width?: number;
55
+ /** Extent along Y. */
56
+ depth?: number;
57
+ height?: number;
58
+ wallThickness?: number;
59
+ floorMaterial?: string;
60
+ ceilingMaterial?: string;
61
+ usage?: string;
62
+ }
63
+ declare function Room(props: WithChildren<RoomProps>): import("react").DOMElement<Record<string, unknown>, Element>;
64
+ interface WallProps {
65
+ id?: string;
66
+ from: [number, number];
67
+ to: [number, number];
68
+ thickness?: number;
69
+ height?: number;
70
+ materialId?: string;
71
+ }
72
+ declare function Wall(props: WallProps): import("react").DOMElement<Record<string, unknown>, Element>;
73
+ interface OpeningCommonProps {
74
+ id?: string;
75
+ /** Which side of the parent room this attaches to. */
76
+ wall?: RoomSide;
77
+ /** Distance along the wall centerline from its start corner. */
78
+ offset?: number;
79
+ width?: number;
80
+ height?: number;
81
+ sillHeight?: number;
82
+ }
83
+ declare function Door(props: OpeningCommonProps): import("react").DOMElement<Record<string, unknown>, Element>;
84
+ declare function Window(props: OpeningCommonProps): import("react").DOMElement<Record<string, unknown>, Element>;
85
+ declare function Opening(props: OpeningCommonProps): import("react").DOMElement<Record<string, unknown>, Element>;
86
+ interface FurnitureProps {
87
+ id?: string;
88
+ type: string;
89
+ x?: number;
90
+ y?: number;
91
+ z?: number;
92
+ rotation?: number;
93
+ scaleX?: number;
94
+ scaleY?: number;
95
+ scaleZ?: number;
96
+ assetId?: string;
97
+ }
98
+ declare function Furniture(props: FurnitureProps): import("react").DOMElement<Record<string, unknown>, Element>;
99
+ /** Alias used inside reusable room modules. */
100
+ declare const Fixture: typeof Furniture;
101
+ interface MaterialProps {
102
+ id: string;
103
+ name?: string;
104
+ category?: string;
105
+ baseColor: string;
106
+ roughness?: number;
107
+ metalness?: number;
108
+ opacity?: number;
109
+ textureUrl?: string;
110
+ }
111
+ declare function Material(props: MaterialProps): import("react").DOMElement<Record<string, unknown>, Element>;
112
+ declare function Group(props: WithChildren<{
113
+ id?: string;
114
+ }>): import("react").DOMElement<Record<string, unknown>, Element>;
115
+ declare function Slab(props: WithChildren<{
116
+ id?: string;
117
+ }>): import("react").DOMElement<Record<string, unknown>, Element>;
118
+ declare function Roof(props: WithChildren<{
119
+ id?: string;
120
+ type?: string;
121
+ }>): import("react").DOMElement<Record<string, unknown>, Element>;
122
+ declare function Stairs(props: WithChildren<{
123
+ id?: string;
124
+ }>): import("react").DOMElement<Record<string, unknown>, Element>;
125
+ //#endregion
126
+ //#region src/registry.d.ts
127
+ interface BuildingComposition {
128
+ id: string;
129
+ name: string;
130
+ component: ComponentType<Record<string, unknown>>;
131
+ defaultProps?: Record<string, unknown>;
132
+ }
133
+ /** Increments whenever the registry changes (incl. hot-reload re-registration). */
134
+ declare function getCompositionsVersion(): number;
135
+ declare function __registerComposition(c: BuildingComposition): void;
136
+ declare function __unregisterComposition(id: string): void;
137
+ declare function getCompositions(): BuildingComposition[];
138
+ declare function subscribeCompositions(fn: () => void): () => void;
139
+ /**
140
+ * Register the root component (mirrors Remotion's registerRoot). The Studio
141
+ * mounts this so its <Composition> children register themselves.
142
+ */
143
+ declare function registerRoot(component: ComponentType): void;
144
+ declare function getRoot(): ComponentType | null;
145
+ /**
146
+ * Declares a building composition. Renders nothing visible; on mount it
147
+ * registers itself into the module registry the Studio reads.
148
+ */
149
+ declare function Composition(props: BuildingComposition): null;
150
+ /** Render any React Arch element tree to the canonical model. */
151
+ declare function renderToDocument(element: ReactElement, fallbackName?: string): BuildingDocument$1;
152
+ /** Render a registered composition (component + its default props) to a model. */
153
+ declare function renderComposition(comp: BuildingComposition): BuildingDocument$1;
154
+ //#endregion
155
+ //#region src/renderer.d.ts
156
+ /** A node in the intermediate instance tree built by the reconciler. */
157
+ interface Instance {
158
+ tag: string;
159
+ props: Record<string, unknown>;
160
+ children: Instance[];
161
+ }
162
+ /**
163
+ * Render a React element tree through the React Arch reconciler and return the
164
+ * resulting intermediate instance tree. Synchronous (legacy root) so the tree
165
+ * is fully built when this returns.
166
+ */
167
+ declare function renderToInstances(element: ReactElement): Instance[];
168
+ //#endregion
169
+ //#region src/convert.d.ts
170
+ /**
171
+ * Convert a rendered instance tree into the canonical BuildingDocument.
172
+ * `fallbackName` is used when the <Building> has no `name` prop (e.g. the
173
+ * registered composition name).
174
+ */
175
+ declare function convert(instances: Instance[], fallbackName?: string): BuildingDocument$1;
176
+ //#endregion
177
+ export { Building, BuildingComposition, type BuildingDocument, type BuildingModel, BuildingProps, Composition, Door, Fixture, Floor, type FloorModel, FloorProps, Furniture, FurnitureProps, Group, type Instance, Material, MaterialProps, Opening, OpeningCommonProps, type OpeningModel, Roof, Room, type RoomModel, RoomProps, type RoomSide, Slab, Stairs, TAG, Wall, type WallModel, WallProps, Window, __registerComposition, __unregisterComposition, convert, getCompositions, getCompositionsVersion, getRoot, registerRoot, renderComposition, renderToDocument, renderToInstances, subscribeCompositions };
178
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/tags.ts","../src/components.tsx","../src/registry.ts","../src/renderer.ts","../src/convert.ts"],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,508 @@
1
+ import { createElement, useEffect } from "react";
2
+ import Reconciler from "react-reconciler";
3
+ import { createId, round } from "@react-arch/shared";
4
+ import { normalize, sub } from "@react-arch/geometry";
5
+ import { DEFAULT_MATERIALS, MODEL_VERSION } from "@react-arch/core";
6
+ //#region src/tags.ts
7
+ /**
8
+ * Internal host element tags. Users never type these — the public components
9
+ * in `components.tsx` create them. The reconciler builds an instance tree of
10
+ * these tags, which `convert.ts` turns into a BuildingDocument.
11
+ */
12
+ const TAG = {
13
+ building: "ra-building",
14
+ floor: "ra-floor",
15
+ room: "ra-room",
16
+ wall: "ra-wall",
17
+ door: "ra-door",
18
+ window: "ra-window",
19
+ opening: "ra-opening",
20
+ furniture: "ra-furniture",
21
+ material: "ra-material",
22
+ group: "ra-group",
23
+ slab: "ra-slab",
24
+ roof: "ra-roof",
25
+ stairs: "ra-stairs",
26
+ text: "ra-text"
27
+ };
28
+ //#endregion
29
+ //#region src/components.tsx
30
+ const h = (tag, props) => createElement(tag, props);
31
+ function Building(props) {
32
+ return h(TAG.building, props);
33
+ }
34
+ function Floor(props) {
35
+ return h(TAG.floor, props);
36
+ }
37
+ function Room(props) {
38
+ return h(TAG.room, props);
39
+ }
40
+ function Wall(props) {
41
+ return h(TAG.wall, props);
42
+ }
43
+ function Door(props) {
44
+ return h(TAG.door, props);
45
+ }
46
+ function Window(props) {
47
+ return h(TAG.window, props);
48
+ }
49
+ function Opening(props) {
50
+ return h(TAG.opening, props);
51
+ }
52
+ function Furniture(props) {
53
+ return h(TAG.furniture, props);
54
+ }
55
+ /** Alias used inside reusable room modules. */
56
+ const Fixture = Furniture;
57
+ function Material(props) {
58
+ return h(TAG.material, props);
59
+ }
60
+ function Group(props) {
61
+ return h(TAG.group, props);
62
+ }
63
+ function Slab(props) {
64
+ return h(TAG.slab, props);
65
+ }
66
+ function Roof(props) {
67
+ return h(TAG.roof, props);
68
+ }
69
+ function Stairs(props) {
70
+ return h(TAG.stairs, props);
71
+ }
72
+ //#endregion
73
+ //#region src/renderer.ts
74
+ function stripChildren(props) {
75
+ const { children, ...rest } = props;
76
+ return rest;
77
+ }
78
+ const reconciler = Reconciler({
79
+ supportsMutation: true,
80
+ supportsPersistence: false,
81
+ supportsHydration: false,
82
+ isPrimaryRenderer: false,
83
+ noTimeout: -1,
84
+ now: () => 0,
85
+ getRootHostContext: () => ({}),
86
+ getChildHostContext: (parent) => parent,
87
+ getPublicInstance: (instance) => instance,
88
+ prepareForCommit: () => null,
89
+ resetAfterCommit: () => {},
90
+ preparePortalMount: () => {},
91
+ createInstance(tag, props) {
92
+ return {
93
+ tag,
94
+ props: stripChildren(props),
95
+ children: []
96
+ };
97
+ },
98
+ createTextInstance(text) {
99
+ return {
100
+ tag: "ra-text",
101
+ props: { text },
102
+ children: []
103
+ };
104
+ },
105
+ appendInitialChild(parent, child) {
106
+ parent.children.push(child);
107
+ },
108
+ appendChild(parent, child) {
109
+ parent.children.push(child);
110
+ },
111
+ appendChildToContainer(container, child) {
112
+ container.children.push(child);
113
+ },
114
+ insertBefore(parent, child, before) {
115
+ const i = parent.children.indexOf(before);
116
+ parent.children.splice(i, 0, child);
117
+ },
118
+ insertInContainerBefore(container, child, before) {
119
+ const i = container.children.indexOf(before);
120
+ container.children.splice(i, 0, child);
121
+ },
122
+ removeChild(parent, child) {
123
+ parent.children = parent.children.filter((c) => c !== child);
124
+ },
125
+ removeChildFromContainer(container, child) {
126
+ container.children = container.children.filter((c) => c !== child);
127
+ },
128
+ finalizeInitialChildren: () => false,
129
+ shouldSetTextContent: () => false,
130
+ clearContainer(container) {
131
+ container.children = [];
132
+ },
133
+ prepareUpdate: () => true,
134
+ commitUpdate(instance, _payload, _tag, _old, newProps) {
135
+ instance.props = stripChildren(newProps);
136
+ },
137
+ commitTextUpdate(instance, _old, text) {
138
+ instance.props = { text };
139
+ },
140
+ getCurrentEventPriority: () => 16,
141
+ detachDeletedInstance: () => {},
142
+ prepareScopeUpdate: () => {},
143
+ getInstanceFromScope: () => null,
144
+ getInstanceFromNode: () => null,
145
+ beforeActiveInstanceBlur: () => {},
146
+ afterActiveInstanceBlur: () => {},
147
+ scheduleTimeout: setTimeout,
148
+ cancelTimeout: clearTimeout
149
+ });
150
+ const LegacyRoot = 0;
151
+ /**
152
+ * Render a React element tree through the React Arch reconciler and return the
153
+ * resulting intermediate instance tree. Synchronous (legacy root) so the tree
154
+ * is fully built when this returns.
155
+ */
156
+ function renderToInstances(element) {
157
+ const container = { children: [] };
158
+ const root = reconciler.createContainer(container, LegacyRoot, null, false, null, "react-arch", (error) => {
159
+ console.error("[react-arch] render error", error);
160
+ }, null);
161
+ reconciler.updateContainer(element, root, null, null);
162
+ return container.children;
163
+ }
164
+ //#endregion
165
+ //#region src/convert.ts
166
+ function num(props, key, fallback) {
167
+ const v = props[key];
168
+ return typeof v === "number" && Number.isFinite(v) ? v : fallback;
169
+ }
170
+ function str(props, key) {
171
+ const v = props[key];
172
+ return typeof v === "string" ? v : void 0;
173
+ }
174
+ function vec2(v) {
175
+ if (Array.isArray(v) && v.length === 2 && typeof v[0] === "number" && typeof v[1] === "number") return [v[0], v[1]];
176
+ }
177
+ /** Expand transparent <Group> nodes and drop text nodes. */
178
+ function flatten(nodes) {
179
+ const out = [];
180
+ for (const n of nodes) {
181
+ if (n.tag === TAG.text) continue;
182
+ if (n.tag === TAG.group) out.push(...flatten(n.children));
183
+ else out.push(n);
184
+ }
185
+ return out;
186
+ }
187
+ function toMaterial(node) {
188
+ const p = node.props;
189
+ return {
190
+ id: str(p, "id") ?? createId("mat"),
191
+ name: str(p, "name") ?? "Material",
192
+ category: str(p, "category") ?? "custom",
193
+ baseColor: str(p, "baseColor") ?? "#cccccc",
194
+ roughness: typeof p.roughness === "number" ? p.roughness : void 0,
195
+ metalness: typeof p.metalness === "number" ? p.metalness : void 0,
196
+ opacity: typeof p.opacity === "number" ? p.opacity : void 0,
197
+ textureUrl: str(p, "textureUrl")
198
+ };
199
+ }
200
+ const OPENING_DEFAULTS = {
201
+ door: {
202
+ width: .9,
203
+ height: 2.1,
204
+ sill: 0
205
+ },
206
+ window: {
207
+ width: 1.2,
208
+ height: 1.4,
209
+ sill: .9
210
+ },
211
+ opening: {
212
+ width: 1,
213
+ height: 2.1,
214
+ sill: 0
215
+ }
216
+ };
217
+ function toObject(node, floorId) {
218
+ const p = node.props;
219
+ return {
220
+ id: str(p, "id") ?? createId("object"),
221
+ floorId,
222
+ type: str(p, "type") ?? "object",
223
+ position: [
224
+ num(p, "x", 0),
225
+ num(p, "y", 0),
226
+ num(p, "z", 0)
227
+ ],
228
+ rotation: [
229
+ 0,
230
+ 0,
231
+ num(p, "rotation", 0)
232
+ ],
233
+ scale: [
234
+ num(p, "scaleX", 1),
235
+ num(p, "scaleY", 1),
236
+ num(p, "scaleZ", 1)
237
+ ],
238
+ assetId: str(p, "assetId")
239
+ };
240
+ }
241
+ function convertFloor(node, buildingId, materials) {
242
+ const p = node.props;
243
+ const floor = {
244
+ id: str(p, "id") ?? createId("floor"),
245
+ buildingId,
246
+ name: str(p, "name") ?? "Floor",
247
+ elevation: num(p, "elevation", 0),
248
+ height: num(p, "height", 2.8),
249
+ visible: true,
250
+ locked: false,
251
+ walls: [],
252
+ rooms: [],
253
+ openings: [],
254
+ objects: []
255
+ };
256
+ const makeWall = (start, end, thickness, materialId) => {
257
+ const w = {
258
+ id: createId("wall"),
259
+ floorId: floor.id,
260
+ start,
261
+ end,
262
+ thickness,
263
+ height: floor.height,
264
+ materialId
265
+ };
266
+ floor.walls.push(w);
267
+ return w;
268
+ };
269
+ for (const child of flatten(node.children)) switch (child.tag) {
270
+ case TAG.material:
271
+ materials.push(toMaterial(child));
272
+ break;
273
+ case TAG.wall: {
274
+ const w = makeWall(vec2(child.props.from) ?? [0, 0], vec2(child.props.to) ?? [0, 0], num(child.props, "thickness", .2), str(child.props, "materialId"));
275
+ if (str(child.props, "id")) w.id = str(child.props, "id");
276
+ if (typeof child.props.height === "number") w.height = child.props.height;
277
+ break;
278
+ }
279
+ case TAG.room: {
280
+ const x = num(child.props, "x", 0);
281
+ const y = num(child.props, "y", 0);
282
+ const w = num(child.props, "width", 4);
283
+ const d = num(child.props, "depth", 4);
284
+ const thickness = num(child.props, "wallThickness", .2);
285
+ const polygon = [
286
+ [x, y],
287
+ [x + w, y],
288
+ [x + w, y + d],
289
+ [x, y + d]
290
+ ];
291
+ const room = {
292
+ id: str(child.props, "id") ?? createId("room"),
293
+ floorId: floor.id,
294
+ name: str(child.props, "name") ?? "Room",
295
+ polygon,
296
+ floorMaterialId: str(child.props, "floorMaterial"),
297
+ ceilingMaterialId: str(child.props, "ceilingMaterial"),
298
+ usageType: str(child.props, "usage"),
299
+ height: typeof child.props.height === "number" ? child.props.height : void 0
300
+ };
301
+ floor.rooms.push(room);
302
+ const sides = {
303
+ north: makeWall([x, y], [x + w, y], thickness),
304
+ south: makeWall([x, y + d], [x + w, y + d], thickness),
305
+ west: makeWall([x, y], [x, y + d], thickness),
306
+ east: makeWall([x + w, y], [x + w, y + d], thickness)
307
+ };
308
+ room.boundaryWallIds = Object.values(sides).map((s) => s.id);
309
+ for (const o of flatten(child.children)) {
310
+ if (o.tag === TAG.furniture) {
311
+ floor.objects.push(toObject(o, floor.id));
312
+ continue;
313
+ }
314
+ if (o.tag !== TAG.door && o.tag !== TAG.window && o.tag !== TAG.opening) continue;
315
+ const type = o.tag === TAG.door ? "door" : o.tag === TAG.window ? "window" : "opening";
316
+ const def = OPENING_DEFAULTS[type];
317
+ const side = str(o.props, "wall") ?? "south";
318
+ const wall = sides[side] ?? sides.south;
319
+ const wallLen = side === "north" || side === "south" ? w : d;
320
+ const opening = {
321
+ id: str(o.props, "id") ?? createId("opening"),
322
+ floorId: floor.id,
323
+ wallId: wall.id,
324
+ type,
325
+ offset: num(o.props, "offset", wallLen / 2),
326
+ width: num(o.props, "width", def.width),
327
+ height: num(o.props, "height", def.height),
328
+ sillHeight: num(o.props, "sillHeight", def.sill)
329
+ };
330
+ floor.openings.push(opening);
331
+ }
332
+ break;
333
+ }
334
+ case TAG.furniture:
335
+ floor.objects.push(toObject(child, floor.id));
336
+ break;
337
+ default: break;
338
+ }
339
+ mergeCoincidentWalls(floor);
340
+ return floor;
341
+ }
342
+ /**
343
+ * Two adjacent rooms each generate their own perimeter walls, producing a pair
344
+ * of coincident walls on their shared edge. Without merging, a door declared on
345
+ * one room only cuts that room's wall and the neighbour's wall stays solid
346
+ * behind it — the opening never passes through (and the walls z-fight in 3D).
347
+ *
348
+ * This collapses coincident, equal-thickness walls into one survivor and
349
+ * re-points openings (re-projecting their offset) and room boundaries onto it,
350
+ * so an interior door becomes a real passage between the rooms.
351
+ */
352
+ function mergeCoincidentWalls(floor) {
353
+ const key = (w) => {
354
+ const a = [round(w.start[0]), round(w.start[1])];
355
+ const b = [round(w.end[0]), round(w.end[1])];
356
+ const [p, q] = a[0] < b[0] || a[0] === b[0] && a[1] <= b[1] ? [a, b] : [b, a];
357
+ return `${p[0]},${p[1]}|${q[0]},${q[1]}|${round(w.thickness)}`;
358
+ };
359
+ const groups = /* @__PURE__ */ new Map();
360
+ for (const w of floor.walls) {
361
+ const k = key(w);
362
+ const g = groups.get(k);
363
+ if (g) g.push(w);
364
+ else groups.set(k, [w]);
365
+ }
366
+ const survivors = [];
367
+ const survivorOf = /* @__PURE__ */ new Map();
368
+ for (const group of groups.values()) {
369
+ const survivor = group[0];
370
+ survivors.push(survivor);
371
+ for (const w of group) survivorOf.set(w.id, survivor);
372
+ }
373
+ if (survivors.length === floor.walls.length) return;
374
+ const wallById = new Map(floor.walls.map((w) => [w.id, w]));
375
+ for (const o of floor.openings) {
376
+ const survivor = survivorOf.get(o.wallId);
377
+ if (!survivor || survivor.id === o.wallId) continue;
378
+ const orig = wallById.get(o.wallId);
379
+ if (orig) {
380
+ const od = normalize(sub(orig.end, orig.start));
381
+ const cx = orig.start[0] + od[0] * o.offset;
382
+ const cy = orig.start[1] + od[1] * o.offset;
383
+ const sd = normalize(sub(survivor.end, survivor.start));
384
+ o.offset = (cx - survivor.start[0]) * sd[0] + (cy - survivor.start[1]) * sd[1];
385
+ }
386
+ o.wallId = survivor.id;
387
+ }
388
+ for (const r of floor.rooms) if (r.boundaryWallIds) r.boundaryWallIds = Array.from(new Set(r.boundaryWallIds.map((id) => survivorOf.get(id)?.id ?? id)));
389
+ floor.walls = survivors;
390
+ }
391
+ /**
392
+ * Convert a rendered instance tree into the canonical BuildingDocument.
393
+ * `fallbackName` is used when the <Building> has no `name` prop (e.g. the
394
+ * registered composition name).
395
+ */
396
+ function convert(instances, fallbackName = "Untitled") {
397
+ const tops = flatten(instances);
398
+ const materials = [...DEFAULT_MATERIALS];
399
+ for (const n of tops) if (n.tag === TAG.material) materials.push(toMaterial(n));
400
+ const buildingNodes = tops.filter((n) => n.tag === TAG.building);
401
+ const buildings = [];
402
+ let units = "metric";
403
+ let docName = fallbackName;
404
+ const sources = buildingNodes.length > 0 ? buildingNodes : [{
405
+ tag: TAG.building,
406
+ props: {},
407
+ children: tops
408
+ }];
409
+ for (const bn of sources) {
410
+ const buildingId = str(bn.props, "id") ?? createId("bld");
411
+ const name = str(bn.props, "name") ?? fallbackName;
412
+ if (str(bn.props, "units") === "imperial") units = "imperial";
413
+ docName = name;
414
+ const floors = flatten(bn.children).filter((c) => c.tag === TAG.floor).map((fn) => convertFloor(fn, buildingId, materials));
415
+ floors.sort((a, b) => a.elevation - b.elevation);
416
+ buildings.push({
417
+ id: buildingId,
418
+ name,
419
+ floors
420
+ });
421
+ }
422
+ return {
423
+ id: createId("doc"),
424
+ version: MODEL_VERSION,
425
+ name: docName,
426
+ units,
427
+ buildings,
428
+ materials,
429
+ assets: [],
430
+ metadata: { source: "react" }
431
+ };
432
+ }
433
+ //#endregion
434
+ //#region src/registry.ts
435
+ const compositions = /* @__PURE__ */ new Map();
436
+ const listeners = /* @__PURE__ */ new Set();
437
+ let rootComponent = null;
438
+ let snapshot = [];
439
+ let version = 0;
440
+ function emit() {
441
+ snapshot = [...compositions.values()];
442
+ version += 1;
443
+ for (const fn of listeners) fn();
444
+ }
445
+ /** Increments whenever the registry changes (incl. hot-reload re-registration). */
446
+ function getCompositionsVersion() {
447
+ return version;
448
+ }
449
+ function __registerComposition(c) {
450
+ compositions.set(c.id, c);
451
+ emit();
452
+ }
453
+ function __unregisterComposition(id) {
454
+ compositions.delete(id);
455
+ emit();
456
+ }
457
+ function getCompositions() {
458
+ return snapshot;
459
+ }
460
+ function subscribeCompositions(fn) {
461
+ listeners.add(fn);
462
+ return () => listeners.delete(fn);
463
+ }
464
+ /**
465
+ * Register the root component (mirrors Remotion's registerRoot). The Studio
466
+ * mounts this so its <Composition> children register themselves.
467
+ */
468
+ function registerRoot(component) {
469
+ rootComponent = component;
470
+ emit();
471
+ }
472
+ function getRoot() {
473
+ return rootComponent;
474
+ }
475
+ /**
476
+ * Declares a building composition. Renders nothing visible; on mount it
477
+ * registers itself into the module registry the Studio reads.
478
+ */
479
+ function Composition(props) {
480
+ const { id, name, component, defaultProps } = props;
481
+ useEffect(() => {
482
+ __registerComposition({
483
+ id,
484
+ name,
485
+ component,
486
+ defaultProps
487
+ });
488
+ return () => __unregisterComposition(id);
489
+ }, [
490
+ id,
491
+ name,
492
+ component,
493
+ defaultProps
494
+ ]);
495
+ return null;
496
+ }
497
+ /** Render any React Arch element tree to the canonical model. */
498
+ function renderToDocument(element, fallbackName) {
499
+ return convert(renderToInstances(element), fallbackName);
500
+ }
501
+ /** Render a registered composition (component + its default props) to a model. */
502
+ function renderComposition(comp) {
503
+ return renderToDocument(createElement(comp.component, comp.defaultProps ?? {}), comp.name);
504
+ }
505
+ //#endregion
506
+ export { Building, Composition, Door, Fixture, Floor, Furniture, Group, Material, Opening, Roof, Room, Slab, Stairs, TAG, Wall, Window, __registerComposition, __unregisterComposition, convert, getCompositions, getCompositionsVersion, getRoot, registerRoot, renderComposition, renderToDocument, renderToInstances, subscribeCompositions };
507
+
508
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/tags.ts","../src/components.tsx","../src/renderer.ts","../src/convert.ts","../src/registry.ts"],"sourcesContent":["/**\n * Internal host element tags. Users never type these — the public components\n * in `components.tsx` create them. The reconciler builds an instance tree of\n * these tags, which `convert.ts` turns into a BuildingDocument.\n */\nexport const TAG = {\n building: \"ra-building\",\n floor: \"ra-floor\",\n room: \"ra-room\",\n wall: \"ra-wall\",\n door: \"ra-door\",\n window: \"ra-window\",\n opening: \"ra-opening\",\n furniture: \"ra-furniture\",\n material: \"ra-material\",\n group: \"ra-group\",\n slab: \"ra-slab\",\n roof: \"ra-roof\",\n stairs: \"ra-stairs\",\n text: \"ra-text\",\n} as const;\n\nexport type Tag = (typeof TAG)[keyof typeof TAG];\n\n/** Side of a rectangular room a door/window attaches to. */\nexport type RoomSide = \"north\" | \"south\" | \"east\" | \"west\";\n","import { createElement, type ReactNode } from \"react\";\nimport { TAG, type RoomSide } from \"./tags.js\";\n\ntype WithChildren<T> = T & { children?: ReactNode };\nconst h = (tag: string, props: object) =>\n createElement(tag, props as Record<string, unknown>);\n\nexport interface BuildingProps {\n id?: string;\n name?: string;\n units?: \"metric\" | \"imperial\";\n}\nexport function Building(props: WithChildren<BuildingProps>) {\n return h(TAG.building, props);\n}\n\nexport interface FloorProps {\n id?: string;\n name?: string;\n elevation?: number;\n height?: number;\n}\nexport function Floor(props: WithChildren<FloorProps>) {\n return h(TAG.floor, props);\n}\n\nexport interface RoomProps {\n id?: string;\n name?: string;\n /** Top-left corner X (metres, X increases right). */\n x?: number;\n /** Top-left corner Y (metres, Y increases down). */\n y?: number;\n /** Extent along X. */\n width?: number;\n /** Extent along Y. */\n depth?: number;\n height?: number;\n wallThickness?: number;\n floorMaterial?: string;\n ceilingMaterial?: string;\n usage?: string;\n}\nexport function Room(props: WithChildren<RoomProps>) {\n return h(TAG.room, props);\n}\n\nexport interface WallProps {\n id?: string;\n from: [number, number];\n to: [number, number];\n thickness?: number;\n height?: number;\n materialId?: string;\n}\nexport function Wall(props: WallProps) {\n return h(TAG.wall, props);\n}\n\nexport interface OpeningCommonProps {\n id?: string;\n /** Which side of the parent room this attaches to. */\n wall?: RoomSide;\n /** Distance along the wall centerline from its start corner. */\n offset?: number;\n width?: number;\n height?: number;\n sillHeight?: number;\n}\nexport function Door(props: OpeningCommonProps) {\n return h(TAG.door, props);\n}\nexport function Window(props: OpeningCommonProps) {\n return h(TAG.window, props);\n}\nexport function Opening(props: OpeningCommonProps) {\n return h(TAG.opening, props);\n}\n\nexport interface FurnitureProps {\n id?: string;\n type: string;\n x?: number;\n y?: number;\n z?: number;\n rotation?: number;\n scaleX?: number;\n scaleY?: number;\n scaleZ?: number;\n assetId?: string;\n}\nexport function Furniture(props: FurnitureProps) {\n return h(TAG.furniture, props);\n}\n/** Alias used inside reusable room modules. */\nexport const Fixture = Furniture;\n\nexport interface MaterialProps {\n id: string;\n name?: string;\n category?: string;\n baseColor: string;\n roughness?: number;\n metalness?: number;\n opacity?: number;\n textureUrl?: string;\n}\nexport function Material(props: MaterialProps) {\n return h(TAG.material, props);\n}\n\nexport function Group(props: WithChildren<{ id?: string }>) {\n return h(TAG.group, props);\n}\n\n// Reserved primitives — accepted in the tree, modelled minimally for the MVP.\nexport function Slab(props: WithChildren<{ id?: string }>) {\n return h(TAG.slab, props);\n}\nexport function Roof(props: WithChildren<{ id?: string; type?: string }>) {\n return h(TAG.roof, props);\n}\nexport function Stairs(props: WithChildren<{ id?: string }>) {\n return h(TAG.stairs, props);\n}\n","import Reconciler from \"react-reconciler\";\nimport type { ReactElement } from \"react\";\n\n/** A node in the intermediate instance tree built by the reconciler. */\nexport interface Instance {\n tag: string;\n props: Record<string, unknown>;\n children: Instance[];\n}\n\nexport interface Container {\n children: Instance[];\n}\n\nfunction stripChildren(props: Record<string, unknown>): Record<string, unknown> {\n const { children, ...rest } = props;\n void children;\n return rest;\n}\n\n// react-reconciler's types are notoriously strict; the host config is plain.\nconst hostConfig: any = {\n supportsMutation: true,\n supportsPersistence: false,\n supportsHydration: false,\n isPrimaryRenderer: false,\n noTimeout: -1,\n now: () => 0,\n\n getRootHostContext: () => ({}),\n getChildHostContext: (parent: unknown) => parent,\n getPublicInstance: (instance: Instance) => instance,\n\n prepareForCommit: () => null,\n resetAfterCommit: () => {},\n preparePortalMount: () => {},\n\n createInstance(tag: string, props: Record<string, unknown>): Instance {\n return { tag, props: stripChildren(props), children: [] };\n },\n\n createTextInstance(text: string): Instance {\n return { tag: \"ra-text\", props: { text }, children: [] };\n },\n\n appendInitialChild(parent: Instance, child: Instance) {\n parent.children.push(child);\n },\n appendChild(parent: Instance, child: Instance) {\n parent.children.push(child);\n },\n appendChildToContainer(container: Container, child: Instance) {\n container.children.push(child);\n },\n\n insertBefore(parent: Instance, child: Instance, before: Instance) {\n const i = parent.children.indexOf(before);\n parent.children.splice(i, 0, child);\n },\n insertInContainerBefore(container: Container, child: Instance, before: Instance) {\n const i = container.children.indexOf(before);\n container.children.splice(i, 0, child);\n },\n\n removeChild(parent: Instance, child: Instance) {\n parent.children = parent.children.filter((c) => c !== child);\n },\n removeChildFromContainer(container: Container, child: Instance) {\n container.children = container.children.filter((c) => c !== child);\n },\n\n finalizeInitialChildren: () => false,\n shouldSetTextContent: () => false,\n clearContainer(container: Container) {\n container.children = [];\n },\n\n prepareUpdate: () => true,\n commitUpdate(instance: Instance, _payload: unknown, _tag: string, _old: unknown, newProps: Record<string, unknown>) {\n instance.props = stripChildren(newProps);\n },\n commitTextUpdate(instance: Instance, _old: string, text: string) {\n instance.props = { text };\n },\n\n getCurrentEventPriority: () => 16,\n detachDeletedInstance: () => {},\n prepareScopeUpdate: () => {},\n getInstanceFromScope: () => null,\n getInstanceFromNode: () => null,\n beforeActiveInstanceBlur: () => {},\n afterActiveInstanceBlur: () => {},\n scheduleTimeout: setTimeout,\n cancelTimeout: clearTimeout,\n};\n\nconst reconciler = Reconciler(hostConfig);\n\nconst LegacyRoot = 0;\n\n/**\n * Render a React element tree through the React Arch reconciler and return the\n * resulting intermediate instance tree. Synchronous (legacy root) so the tree\n * is fully built when this returns.\n */\nexport function renderToInstances(element: ReactElement): Instance[] {\n const container: Container = { children: [] };\n const root = reconciler.createContainer(\n container,\n LegacyRoot,\n null,\n false,\n null,\n \"react-arch\",\n (error: unknown) => {\n // Surface render errors loudly; the studio shows them as diagnostics.\n console.error(\"[react-arch] render error\", error);\n },\n null,\n );\n reconciler.updateContainer(element, root, null, null);\n return container.children;\n}\n","import { createId, round, type Units, type Vec2 } from \"@react-arch/shared\";\nimport { normalize, sub } from \"@react-arch/geometry\";\nimport {\n DEFAULT_MATERIALS,\n MODEL_VERSION,\n type Building,\n type BuildingDocument,\n type BuildingObject,\n type Floor,\n type Material,\n type Opening,\n type Room,\n type Wall,\n} from \"@react-arch/core\";\nimport { TAG, type RoomSide } from \"./tags.js\";\nimport type { Instance } from \"./renderer.js\";\n\nfunction num(props: Record<string, unknown>, key: string, fallback: number): number {\n const v = props[key];\n return typeof v === \"number\" && Number.isFinite(v) ? v : fallback;\n}\nfunction str(props: Record<string, unknown>, key: string): string | undefined {\n const v = props[key];\n return typeof v === \"string\" ? v : undefined;\n}\nfunction vec2(v: unknown): Vec2 | undefined {\n if (Array.isArray(v) && v.length === 2 && typeof v[0] === \"number\" && typeof v[1] === \"number\") {\n return [v[0], v[1]];\n }\n return undefined;\n}\n\n/** Expand transparent <Group> nodes and drop text nodes. */\nfunction flatten(nodes: Instance[]): Instance[] {\n const out: Instance[] = [];\n for (const n of nodes) {\n if (n.tag === TAG.text) continue;\n if (n.tag === TAG.group) out.push(...flatten(n.children));\n else out.push(n);\n }\n return out;\n}\n\nfunction toMaterial(node: Instance): Material {\n const p = node.props;\n return {\n id: str(p, \"id\") ?? createId(\"mat\"),\n name: str(p, \"name\") ?? \"Material\",\n category: (str(p, \"category\") as Material[\"category\"]) ?? \"custom\",\n baseColor: str(p, \"baseColor\") ?? \"#cccccc\",\n roughness: typeof p.roughness === \"number\" ? p.roughness : undefined,\n metalness: typeof p.metalness === \"number\" ? p.metalness : undefined,\n opacity: typeof p.opacity === \"number\" ? p.opacity : undefined,\n textureUrl: str(p, \"textureUrl\"),\n };\n}\n\ninterface OpeningDefaults {\n width: number;\n height: number;\n sill: number;\n}\nconst OPENING_DEFAULTS: Record<string, OpeningDefaults> = {\n door: { width: 0.9, height: 2.1, sill: 0 },\n window: { width: 1.2, height: 1.4, sill: 0.9 },\n opening: { width: 1.0, height: 2.1, sill: 0 },\n};\n\nfunction toObject(node: Instance, floorId: string): BuildingObject {\n const p = node.props;\n return {\n id: str(p, \"id\") ?? createId(\"object\"),\n floorId,\n type: str(p, \"type\") ?? \"object\",\n position: [num(p, \"x\", 0), num(p, \"y\", 0), num(p, \"z\", 0)],\n rotation: [0, 0, num(p, \"rotation\", 0)],\n scale: [num(p, \"scaleX\", 1), num(p, \"scaleY\", 1), num(p, \"scaleZ\", 1)],\n assetId: str(p, \"assetId\"),\n };\n}\n\nfunction convertFloor(node: Instance, buildingId: string, materials: Material[]): Floor {\n const p = node.props;\n const floor: Floor = {\n id: str(p, \"id\") ?? createId(\"floor\"),\n buildingId,\n name: str(p, \"name\") ?? \"Floor\",\n elevation: num(p, \"elevation\", 0),\n height: num(p, \"height\", 2.8),\n visible: true,\n locked: false,\n walls: [],\n rooms: [],\n openings: [],\n objects: [],\n };\n\n const makeWall = (start: Vec2, end: Vec2, thickness: number, materialId?: string): Wall => {\n const w: Wall = {\n id: createId(\"wall\"),\n floorId: floor.id,\n start,\n end,\n thickness,\n height: floor.height,\n materialId,\n };\n floor.walls.push(w);\n return w;\n };\n\n for (const child of flatten(node.children)) {\n switch (child.tag) {\n case TAG.material:\n materials.push(toMaterial(child));\n break;\n\n case TAG.wall: {\n const from = vec2(child.props.from) ?? [0, 0];\n const to = vec2(child.props.to) ?? [0, 0];\n const w = makeWall(from, to, num(child.props, \"thickness\", 0.2), str(child.props, \"materialId\"));\n if (str(child.props, \"id\")) w.id = str(child.props, \"id\")!;\n if (typeof child.props.height === \"number\") w.height = child.props.height;\n break;\n }\n\n case TAG.room: {\n const x = num(child.props, \"x\", 0);\n const y = num(child.props, \"y\", 0);\n const w = num(child.props, \"width\", 4);\n const d = num(child.props, \"depth\", 4);\n const thickness = num(child.props, \"wallThickness\", 0.2);\n const polygon: Vec2[] = [\n [x, y],\n [x + w, y],\n [x + w, y + d],\n [x, y + d],\n ];\n const room: Room = {\n id: str(child.props, \"id\") ?? createId(\"room\"),\n floorId: floor.id,\n name: str(child.props, \"name\") ?? \"Room\",\n polygon,\n floorMaterialId: str(child.props, \"floorMaterial\"),\n ceilingMaterialId: str(child.props, \"ceilingMaterial\"),\n usageType: str(child.props, \"usage\"),\n height: typeof child.props.height === \"number\" ? child.props.height : undefined,\n };\n floor.rooms.push(room);\n\n // Generate perimeter walls. Offsets increase in a natural reading\n // direction per side (L→R for horizontal, T→B for vertical).\n const sides: Record<RoomSide, Wall> = {\n north: makeWall([x, y], [x + w, y], thickness),\n south: makeWall([x, y + d], [x + w, y + d], thickness),\n west: makeWall([x, y], [x, y + d], thickness),\n east: makeWall([x + w, y], [x + w, y + d], thickness),\n };\n room.boundaryWallIds = Object.values(sides).map((s) => s.id);\n\n for (const o of flatten(child.children)) {\n // Furniture/fixtures are commonly declared inside a room.\n if (o.tag === TAG.furniture) {\n floor.objects.push(toObject(o, floor.id));\n continue;\n }\n if (o.tag !== TAG.door && o.tag !== TAG.window && o.tag !== TAG.opening) continue;\n const type = o.tag === TAG.door ? \"door\" : o.tag === TAG.window ? \"window\" : \"opening\";\n const def = OPENING_DEFAULTS[type]!;\n const side = (str(o.props, \"wall\") as RoomSide) ?? \"south\";\n const wall = sides[side] ?? sides.south;\n const wallLen = side === \"north\" || side === \"south\" ? w : d;\n const opening: Opening = {\n id: str(o.props, \"id\") ?? createId(\"opening\"),\n floorId: floor.id,\n wallId: wall.id,\n type,\n offset: num(o.props, \"offset\", wallLen / 2),\n width: num(o.props, \"width\", def.width),\n height: num(o.props, \"height\", def.height),\n sillHeight: num(o.props, \"sillHeight\", def.sill),\n };\n floor.openings.push(opening);\n }\n break;\n }\n\n case TAG.furniture:\n floor.objects.push(toObject(child, floor.id));\n break;\n\n default:\n // slab / roof / stairs are reserved; ignored for the MVP model.\n break;\n }\n }\n\n mergeCoincidentWalls(floor);\n return floor;\n}\n\n/**\n * Two adjacent rooms each generate their own perimeter walls, producing a pair\n * of coincident walls on their shared edge. Without merging, a door declared on\n * one room only cuts that room's wall and the neighbour's wall stays solid\n * behind it — the opening never passes through (and the walls z-fight in 3D).\n *\n * This collapses coincident, equal-thickness walls into one survivor and\n * re-points openings (re-projecting their offset) and room boundaries onto it,\n * so an interior door becomes a real passage between the rooms.\n */\nfunction mergeCoincidentWalls(floor: Floor): void {\n const key = (w: Wall): string => {\n const a: Vec2 = [round(w.start[0]), round(w.start[1])];\n const b: Vec2 = [round(w.end[0]), round(w.end[1])];\n // Order-insensitive so reversed walls share a key.\n const [p, q] = a[0] < b[0] || (a[0] === b[0] && a[1] <= b[1]) ? [a, b] : [b, a];\n return `${p[0]},${p[1]}|${q[0]},${q[1]}|${round(w.thickness)}`;\n };\n\n const groups = new Map<string, Wall[]>();\n for (const w of floor.walls) {\n const k = key(w);\n const g = groups.get(k);\n if (g) g.push(w);\n else groups.set(k, [w]);\n }\n\n const survivors: Wall[] = [];\n const survivorOf = new Map<string, Wall>(); // any wall id -> survivor wall\n for (const group of groups.values()) {\n const survivor = group[0]!;\n survivors.push(survivor);\n for (const w of group) survivorOf.set(w.id, survivor);\n }\n\n if (survivors.length === floor.walls.length) return; // nothing coincident\n\n const wallById = new Map(floor.walls.map((w) => [w.id, w]));\n for (const o of floor.openings) {\n const survivor = survivorOf.get(o.wallId);\n if (!survivor || survivor.id === o.wallId) continue;\n const orig = wallById.get(o.wallId);\n if (orig) {\n // World centre of the opening on its original wall.\n const od = normalize(sub(orig.end, orig.start));\n const cx = orig.start[0] + od[0] * o.offset;\n const cy = orig.start[1] + od[1] * o.offset;\n // Offset of that point along the survivor (handles reversed direction).\n const sd = normalize(sub(survivor.end, survivor.start));\n o.offset = (cx - survivor.start[0]) * sd[0] + (cy - survivor.start[1]) * sd[1];\n }\n o.wallId = survivor.id;\n }\n\n for (const r of floor.rooms) {\n if (r.boundaryWallIds) {\n r.boundaryWallIds = Array.from(\n new Set(r.boundaryWallIds.map((id) => survivorOf.get(id)?.id ?? id)),\n );\n }\n }\n\n floor.walls = survivors;\n}\n\n/**\n * Convert a rendered instance tree into the canonical BuildingDocument.\n * `fallbackName` is used when the <Building> has no `name` prop (e.g. the\n * registered composition name).\n */\nexport function convert(instances: Instance[], fallbackName = \"Untitled\"): BuildingDocument {\n const tops = flatten(instances);\n const materials: Material[] = [...DEFAULT_MATERIALS];\n for (const n of tops) if (n.tag === TAG.material) materials.push(toMaterial(n));\n\n const buildingNodes = tops.filter((n) => n.tag === TAG.building);\n const buildings: Building[] = [];\n let units: Units = \"metric\";\n let docName = fallbackName;\n\n const sources = buildingNodes.length > 0 ? buildingNodes : [{ tag: TAG.building, props: {}, children: tops }];\n\n for (const bn of sources) {\n const buildingId = str(bn.props, \"id\") ?? createId(\"bld\");\n const name = str(bn.props, \"name\") ?? fallbackName;\n if (str(bn.props, \"units\") === \"imperial\") units = \"imperial\";\n docName = name;\n const floors = flatten(bn.children)\n .filter((c) => c.tag === TAG.floor)\n .map((fn) => convertFloor(fn, buildingId, materials));\n // Keep floors ordered by elevation for stable stacking.\n floors.sort((a, b) => a.elevation - b.elevation);\n buildings.push({ id: buildingId, name, floors });\n }\n\n return {\n id: createId(\"doc\"),\n version: MODEL_VERSION,\n name: docName,\n units,\n buildings,\n materials,\n assets: [],\n metadata: { source: \"react\" },\n };\n}\n","import { createElement, useEffect, type ComponentType, type ReactElement } from \"react\";\nimport type { BuildingDocument } from \"@react-arch/core\";\nimport { renderToInstances } from \"./renderer.js\";\nimport { convert } from \"./convert.js\";\n\nexport interface BuildingComposition {\n id: string;\n name: string;\n component: ComponentType<Record<string, unknown>>;\n defaultProps?: Record<string, unknown>;\n}\n\n// --- module-level registry (Remotion-style) -------------------------------\n\nconst compositions = new Map<string, BuildingComposition>();\nconst listeners = new Set<() => void>();\nlet rootComponent: ComponentType | null = null;\n// Cached, referentially-stable snapshot for useSyncExternalStore consumers.\nlet snapshot: BuildingComposition[] = [];\nlet version = 0;\n\nfunction emit(): void {\n snapshot = [...compositions.values()];\n version += 1;\n for (const fn of listeners) fn();\n}\n\n/** Increments whenever the registry changes (incl. hot-reload re-registration). */\nexport function getCompositionsVersion(): number {\n return version;\n}\n\nexport function __registerComposition(c: BuildingComposition): void {\n compositions.set(c.id, c);\n emit();\n}\nexport function __unregisterComposition(id: string): void {\n compositions.delete(id);\n emit();\n}\n\nexport function getCompositions(): BuildingComposition[] {\n return snapshot;\n}\nexport function subscribeCompositions(fn: () => void): () => void {\n listeners.add(fn);\n return () => listeners.delete(fn);\n}\n\n/**\n * Register the root component (mirrors Remotion's registerRoot). The Studio\n * mounts this so its <Composition> children register themselves.\n */\nexport function registerRoot(component: ComponentType): void {\n rootComponent = component;\n emit();\n}\nexport function getRoot(): ComponentType | null {\n return rootComponent;\n}\n\n/**\n * Declares a building composition. Renders nothing visible; on mount it\n * registers itself into the module registry the Studio reads.\n */\nexport function Composition(props: BuildingComposition): null {\n const { id, name, component, defaultProps } = props;\n useEffect(() => {\n __registerComposition({ id, name, component, defaultProps });\n return () => __unregisterComposition(id);\n }, [id, name, component, defaultProps]);\n return null;\n}\n\n// --- rendering -------------------------------------------------------------\n\n/** Render any React Arch element tree to the canonical model. */\nexport function renderToDocument(element: ReactElement, fallbackName?: string): BuildingDocument {\n return convert(renderToInstances(element), fallbackName);\n}\n\n/** Render a registered composition (component + its default props) to a model. */\nexport function renderComposition(comp: BuildingComposition): BuildingDocument {\n const element = createElement(comp.component, comp.defaultProps ?? {});\n return renderToDocument(element, comp.name);\n}\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@react-arch/react",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "dependencies": {
15
+ "react-reconciler": "^0.29.2",
16
+ "@react-arch/core": "0.1.0",
17
+ "@react-arch/geometry": "0.1.0",
18
+ "@react-arch/shared": "0.1.0"
19
+ },
20
+ "peerDependencies": {
21
+ "react": "^18.3.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/react": "^18.3.12",
25
+ "@types/react-reconciler": "^0.28.9",
26
+ "react": "^18.3.1",
27
+ "typescript": "^5.7.2",
28
+ "vitest": "^2.1.8"
29
+ },
30
+ "files": [
31
+ "dist"
32
+ ],
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "https://github.com/react-arch/react-arch.git",
39
+ "directory": "packages/react"
40
+ },
41
+ "license": "MIT",
42
+ "scripts": {
43
+ "typecheck": "tsc --noEmit",
44
+ "test": "vitest run --passWithNoTests",
45
+ "lint": "oxlint src",
46
+ "build": "tsdown"
47
+ }
48
+ }