@snaptrude/plugin-core 0.9.16 → 0.9.18-voids-20261016.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.
Files changed (107) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/api-manifest.full.json +10406 -0
  3. package/api-manifest.json +827 -51
  4. package/dist/api/analysis/daylight.d.ts +5 -5
  5. package/dist/api/analysis/solar.d.ts +2 -2
  6. package/dist/api/core/camera/index.d.ts +29 -0
  7. package/dist/api/core/camera/index.d.ts.map +1 -1
  8. package/dist/api/core/geom/create/index.d.ts +7 -1
  9. package/dist/api/core/geom/create/index.d.ts.map +1 -1
  10. package/dist/api/core/geom/update/contour.d.ts +8 -2
  11. package/dist/api/core/geom/update/contour.d.ts.map +1 -1
  12. package/dist/api/core/zoom/index.d.ts +13 -0
  13. package/dist/api/core/zoom/index.d.ts.map +1 -1
  14. package/dist/api/design/bim.d.ts +209 -0
  15. package/dist/api/design/bim.d.ts.map +1 -0
  16. package/dist/api/design/boolean/index.d.ts +2 -0
  17. package/dist/api/design/boolean/index.d.ts.map +1 -1
  18. package/dist/api/design/catalog.d.ts +320 -0
  19. package/dist/api/design/catalog.d.ts.map +1 -0
  20. package/dist/api/design/create/bulk-items.d.ts +65 -25
  21. package/dist/api/design/create/bulk-items.d.ts.map +1 -1
  22. package/dist/api/design/create/index.d.ts +272 -99
  23. package/dist/api/design/create/index.d.ts.map +1 -1
  24. package/dist/api/design/create/opening-fields.d.ts +13 -12
  25. package/dist/api/design/create/opening-fields.d.ts.map +1 -1
  26. package/dist/api/design/delete/index.d.ts +1 -0
  27. package/dist/api/design/delete/index.d.ts.map +1 -1
  28. package/dist/api/design/doors/index.d.ts +18 -24
  29. package/dist/api/design/doors/index.d.ts.map +1 -1
  30. package/dist/api/design/family.d.ts +797 -27
  31. package/dist/api/design/family.d.ts.map +1 -1
  32. package/dist/api/design/index.d.ts +16 -0
  33. package/dist/api/design/index.d.ts.map +1 -1
  34. package/dist/api/design/networks.d.ts +1703 -0
  35. package/dist/api/design/networks.d.ts.map +1 -0
  36. package/dist/api/design/query/index.d.ts +42 -32
  37. package/dist/api/design/query/index.d.ts.map +1 -1
  38. package/dist/api/design/selection/index.d.ts +10 -10
  39. package/dist/api/design/types/index.d.ts +40 -24
  40. package/dist/api/design/types/index.d.ts.map +1 -1
  41. package/dist/api/design/update/index.d.ts +43 -3
  42. package/dist/api/design/update/index.d.ts.map +1 -1
  43. package/dist/api/design/windows/index.d.ts +8 -8
  44. package/dist/api/design/windows/index.d.ts.map +1 -1
  45. package/dist/api/presentation/aiInspiration.d.ts +10 -10
  46. package/dist/api/presentation/annotate.d.ts +38 -22
  47. package/dist/api/presentation/annotate.d.ts.map +1 -1
  48. package/dist/api/presentation/drawingContract.d.ts +32 -0
  49. package/dist/api/presentation/drawingContract.d.ts.map +1 -0
  50. package/dist/api/presentation/drawings.d.ts +3523 -0
  51. package/dist/api/presentation/drawings.d.ts.map +1 -0
  52. package/dist/api/presentation/export.d.ts +23 -5
  53. package/dist/api/presentation/export.d.ts.map +1 -1
  54. package/dist/api/presentation/index.d.ts +41 -12
  55. package/dist/api/presentation/index.d.ts.map +1 -1
  56. package/dist/api/presentation/placedViews.d.ts +106 -6
  57. package/dist/api/presentation/placedViews.d.ts.map +1 -1
  58. package/dist/api/presentation/sections.d.ts +461 -0
  59. package/dist/api/presentation/sections.d.ts.map +1 -0
  60. package/dist/api/presentation/shapes.d.ts +2 -2
  61. package/dist/api/presentation/sheets.d.ts +175 -19
  62. package/dist/api/presentation/sheets.d.ts.map +1 -1
  63. package/dist/api/presentation/viewPresets.d.ts +222 -0
  64. package/dist/api/presentation/viewPresets.d.ts.map +1 -0
  65. package/dist/api/presentation/views.d.ts +103 -5
  66. package/dist/api/presentation/views.d.ts.map +1 -1
  67. package/dist/api/presentation/walkthroughs.d.ts +6 -6
  68. package/dist/errors/codes.d.ts +3 -1
  69. package/dist/errors/codes.d.ts.map +1 -1
  70. package/dist/index.cjs +3749 -2328
  71. package/dist/index.cjs.map +1 -1
  72. package/dist/index.js +3616 -2328
  73. package/dist/index.js.map +1 -1
  74. package/package.json +1 -1
  75. package/scripts/generate-manifest.mjs +5 -0
  76. package/src/api/core/camera/index.ts +29 -0
  77. package/src/api/core/geom/create/index.ts +7 -1
  78. package/src/api/core/geom/update/contour.ts +8 -2
  79. package/src/api/core/zoom/index.ts +13 -0
  80. package/src/api/design/bim.ts +121 -0
  81. package/src/api/design/boolean/index.ts +2 -0
  82. package/src/api/design/catalog.ts +203 -0
  83. package/src/api/design/create/bulk-items.ts +57 -17
  84. package/src/api/design/create/index.ts +288 -120
  85. package/src/api/design/create/opening-fields.ts +15 -12
  86. package/src/api/design/delete/index.ts +1 -0
  87. package/src/api/design/doors/index.ts +58 -42
  88. package/src/api/design/family.ts +662 -32
  89. package/src/api/design/index.ts +34 -4
  90. package/src/api/design/networks.ts +607 -0
  91. package/src/api/design/query/index.ts +12 -2
  92. package/src/api/design/types/index.ts +31 -24
  93. package/src/api/design/update/index.ts +44 -3
  94. package/src/api/design/windows/index.ts +40 -22
  95. package/src/api/presentation/annotate.ts +90 -58
  96. package/src/api/presentation/drawingContract.ts +70 -0
  97. package/src/api/presentation/drawings.ts +1179 -0
  98. package/src/api/presentation/export.ts +32 -6
  99. package/src/api/presentation/index.ts +48 -14
  100. package/src/api/presentation/placedViews.ts +119 -28
  101. package/src/api/presentation/sections.ts +420 -0
  102. package/src/api/presentation/sheets.ts +181 -20
  103. package/src/api/presentation/viewPresets.ts +241 -0
  104. package/src/api/presentation/views.ts +46 -6
  105. package/src/errors/codes.ts +7 -0
  106. package/test/drawingContract.test.mjs +288 -0
  107. package/test/errors.test.mjs +1 -0
@@ -4,6 +4,7 @@ import {
4
4
  BrepHandle,
5
5
  ComponentHandle,
6
6
  ContourHandle,
7
+ LineHandle,
7
8
  CurveHandle,
8
9
  ProfileHandle,
9
10
  Vec3Handle,
@@ -25,10 +26,19 @@ import type {
25
26
  PluginCreateDoorItem,
26
27
  PluginCreateFloorItem,
27
28
  PluginCreateFurnitureItem,
29
+ PluginCreateVoidItem,
28
30
  PluginCreateWallRunItem,
29
31
  PluginCreateWindowItem,
30
32
  } from "./bulk-items"
31
33
  import { PluginWallTopProfile } from "./bulk-items"
34
+ import { PluginWallType } from "../types"
35
+ import {
36
+ PluginFamilyPlacement,
37
+ PluginFamilyFloorOptions,
38
+ PluginFamilySlabOptions,
39
+ PluginFamilyCeilingOptions,
40
+ PluginFamilyValues,
41
+ } from "../catalog"
32
42
 
33
43
  /**
34
44
  * `design.create.*` — author new scene-committed BIM entities.
@@ -43,9 +53,17 @@ import { PluginWallTopProfile } from "./bulk-items"
43
53
  * Footprint objects take a {@linkcode ContourHandle} (outer profile + holes);
44
54
  * build one with `snaptrude.core.geom.create.contourFromProfile(s)`.
45
55
  *
46
- * `door`/`window` place a catalog item into a host wall (the world point is
56
+ * Family overloads take `{ type, values? }` from `design.catalog`. The family supplies
57
+ * construction; the contour or axis supplies instance geometry. Geometry uses SU,
58
+ * while family length overrides use mm and angles use degrees. Omitted parameters
59
+ * retain family defaults; shared/type, formula and geometry-bound overrides are refused.
60
+ * Placement never publishes a definition. Unsupported references or geometry fail before mutation.
61
+ *
62
+ * `door`/`window` place a parametric family into a host wall (the world point is
47
63
  * projected onto the wall face to locate the opening; the wall is re-cut in one
48
- * undo entry) and throw if the point projects outside the wall. `smartLayout` is
64
+ * undo entry) and throw if the point projects outside the wall. Voids
65
+ * (`void`/`voids`) are the third hosted child: holes through slabs, floors,
66
+ * roofs and ceilings. `smartLayout` is
49
67
  * the one creator returning a grouped {@linkcode PluginSmartLayoutResult} rather
50
68
  * than a handle — it clones an in-scene template into one or more target spaces,
51
69
  * merging every target into a single undo batch (see its JSDoc).
@@ -198,6 +216,9 @@ export abstract class PluginDesignCreateApi {
198
216
  * Create a **slab** by extruding a footprint contour by `thickness`
199
217
  * (default direction: down).
200
218
  *
219
+ * To cut a hole in an EXISTING one, use `design.create.void`; a contour hole
220
+ * here is baked into the footprint.
221
+ *
201
222
  * @param contour - Footprint (outer + holes)
202
223
  * @param thickness - Slab thickness (> 0)
203
224
  * @param direction - Extrusion direction (default `"down"`)
@@ -219,20 +240,32 @@ export abstract class PluginDesignCreateApi {
219
240
  * ```
220
241
  */
221
242
  public abstract slab(
222
- contour: ContourHandle,
223
- thickness: number,
224
- direction?: "up" | "down",
225
- slabType?: PluginSlabType,
243
+ ...args:
244
+ | [
245
+ contour: ContourHandle,
246
+ thickness: number,
247
+ direction?: "up" | "down",
248
+ slabType?: PluginSlabType,
249
+ ]
250
+ | [contour: ContourHandle, options: PluginFamilySlabOptions]
226
251
  ): PluginApiReturn<ComponentHandle>
227
252
 
228
253
  /**
229
254
  * Create a **floor** by extruding a footprint contour by `thickness`
230
255
  * (extruded upward).
231
256
  *
257
+ * The native family overload takes `{ type, values?, position? }`. Contour
258
+ * geometry uses SU; family values use mm. The family supplies layer thickness.
259
+ * PG2 can split or merge the footprint, so both overloads return an array.
260
+ * A merge into an existing floor can return no newly created handles.
261
+ *
262
+ * To cut a hole in an EXISTING one, use `design.create.void`; a contour hole
263
+ * here is baked into the footprint.
264
+ *
232
265
  * @param contour - Footprint (outer + holes)
233
266
  * @param thickness - Floor thickness (> 0)
234
267
  * @param position - Offset from origin
235
- * @returns the {@linkcode ComponentHandle} of the created floor
268
+ * @returns Newly created {@linkcode ComponentHandle}s; an empty array after a merge
236
269
  * @throws if the contour is invalid or the thickness is not positive
237
270
  *
238
271
  * @examplePrompt Create a floor from this room outline
@@ -247,15 +280,15 @@ export abstract class PluginDesignCreateApi {
247
280
  * const floor = await snaptrude.design.create.floor(contour, 0.1)
248
281
  * ```
249
282
  *
250
- * @performance For MORE THAN ONE floor, call `design.create.floors(items[])` — one
251
- * host round-trip and ONE undo entry for the whole batch. Looping this single-floor
252
- * creator is N round-trips and N undo entries.
283
+ * @performance For dimensional floors, `design.create.floors(items[])` uses one
284
+ * host round-trip and one undo entry. That bulk method does not accept native
285
+ * family options; use this method for a family-backed floor.
253
286
  */
254
287
  public abstract floor(
255
- contour: ContourHandle,
256
- thickness: number,
257
- position?: Vec3Handle,
258
- ): PluginApiReturn<ComponentHandle>
288
+ ...args:
289
+ | [contour: ContourHandle, thickness: number, position?: Vec3Handle]
290
+ | [contour: ContourHandle, options: PluginFamilyFloorOptions]
291
+ ): PluginApiReturn<ComponentHandle[]>
259
292
 
260
293
  /**
261
294
  * Create **many floors** in one undoable operation (bulk plural of
@@ -301,6 +334,9 @@ export abstract class PluginDesignCreateApi {
301
334
  * Create a **roof** by extruding a footprint contour by `thickness`. Created
302
335
  * flat (extruded downward); pitch/slope is a separate post-creation edit.
303
336
  *
337
+ * To cut a hole in an EXISTING one, use `design.create.void`; a contour hole
338
+ * here is baked into the footprint.
339
+ *
304
340
  * @param contour - Footprint (outer + holes)
305
341
  * @param thickness - Roof-slab thickness (> 0)
306
342
  * @returns the {@linkcode ComponentHandle} of the created roof
@@ -320,13 +356,16 @@ export abstract class PluginDesignCreateApi {
320
356
  */
321
357
  public abstract roof(
322
358
  contour: ContourHandle,
323
- thickness: number,
359
+ thickness: number | PluginFamilyPlacement,
324
360
  ): PluginApiReturn<ComponentHandle>
325
361
 
326
362
  /**
327
363
  * Create a **ceiling** by extruding a footprint contour by `thickness`, lifted
328
364
  * `heightFromFloor` above the floor.
329
365
  *
366
+ * To cut a hole in an EXISTING one, use `design.create.void`; a contour hole
367
+ * here is baked into the footprint.
368
+ *
330
369
  * @param contour - Footprint (outer + holes)
331
370
  * @param thickness - Ceiling thickness (> 0)
332
371
  * @param heightFromFloor - Lift above the floor (default 0)
@@ -346,9 +385,9 @@ export abstract class PluginDesignCreateApi {
346
385
  * ```
347
386
  */
348
387
  public abstract ceiling(
349
- contour: ContourHandle,
350
- thickness: number,
351
- heightFromFloor?: number,
388
+ ...args:
389
+ | [contour: ContourHandle, thickness: number, heightFromFloor?: number]
390
+ | [contour: ContourHandle, options: PluginFamilyCeilingOptions]
352
391
  ): PluginApiReturn<ComponentHandle>
353
392
 
354
393
  /**
@@ -379,10 +418,14 @@ export abstract class PluginDesignCreateApi {
379
418
  * ```
380
419
  */
381
420
  public abstract column(
382
- position: Vec3Handle,
383
- crossSection: ContourHandle,
384
- height: number,
385
- directionUp?: Vec3Handle,
421
+ ...args:
422
+ | [
423
+ position: Vec3Handle,
424
+ crossSection: ContourHandle,
425
+ height: number,
426
+ directionUp?: Vec3Handle,
427
+ ]
428
+ | [axis: LineHandle, options: PluginFamilyPlacement]
386
429
  ): PluginApiReturn<ComponentHandle>
387
430
 
388
431
  /**
@@ -426,9 +469,9 @@ export abstract class PluginDesignCreateApi {
426
469
  * ```
427
470
  */
428
471
  public abstract beam(
429
- section: ContourHandle,
430
- length: number,
431
- direction?: Vec3Handle,
472
+ ...args:
473
+ | [section: ContourHandle, length: number, direction?: Vec3Handle]
474
+ | [axis: LineHandle, options: PluginFamilyPlacement]
432
475
  ): PluginApiReturn<ComponentHandle>
433
476
 
434
477
  /**
@@ -439,12 +482,12 @@ export abstract class PluginDesignCreateApi {
439
482
  * @param height - Wall height (default engine default)
440
483
  * @param thickness - Wall thickness (default: the `wallType`'s total layer
441
484
  * thickness when `wallType` is given, else the engine default)
442
- * @param wallType - Wall type to build with — the same searchable list the
443
- * Draw tab's Wall Type dropdown offers. Pass a name from
444
- * `design.types.list("wall")` (the summary's `label`, or its `"wall:…"` id).
445
- * Drives the wall's construction layers and default material, and — when
446
- * `thickness` is omitted — its total layer thickness. Default: the engine's
447
- * generic wall.
485
+ * @param wallType - Legacy label/`wall:` id, or the exact `nativeFamily`
486
+ * `{ id, version }` pin from `design.types.list("wall")`. Native pins are
487
+ * authorized on every use; selecting a family in Draw is not registration.
488
+ * Native pins support straight horizontal centerlines and flat tops only.
489
+ * Omit thickness for native pins; a conflicting explicit thickness rejects.
490
+ * Default: the engine's generic wall.
448
491
  * @param storey - Target storey (integer: `1` ground, `2` first floor, `-1`
449
492
  * basement — same convention as `design.create.space`). The walls are
450
493
  * assigned to it and lifted to its base elevation, all inside the same
@@ -503,9 +546,10 @@ export abstract class PluginDesignCreateApi {
503
546
  profile: ProfileHandle,
504
547
  height?: number,
505
548
  thickness?: number,
506
- wallType?: string,
549
+ wallType?: PluginWallType,
507
550
  storey?: number,
508
551
  topProfiles?: (PluginWallTopProfile | null)[],
552
+ values?: PluginFamilyValues,
509
553
  ): PluginApiReturn<ComponentHandle[]>
510
554
 
511
555
  /**
@@ -895,30 +939,23 @@ export abstract class PluginDesignCreateApi {
895
939
  ): PluginApiReturn<ComponentHandle[]>
896
940
 
897
941
  /**
898
- * Place a **door** from the catalog into a host wall.
942
+ * Place a parametric **door** family into a host wall.
899
943
  *
900
- * Identified by a **catalog (library) id** — the team object's stable `_id`
901
- * or the general library's `fullName`. Discover ids with
944
+ * Identified by a saved **family definition id**. Discover ids with
902
945
  * {@linkcode PluginDesignDoorsApi.listCatalog}. The world `position` is
903
946
  * projected onto the host wall's face to locate the opening; the wall is
904
947
  * re-cut and the door hosted in a single undo entry. Placement is
905
- * asynchronous (the source mesh is fetched if not cached).
948
+ * asynchronous. Values and host bindings are evaluated before the first cut.
906
949
  *
907
- * @param catalogId - Library id: team `_id` or general `fullName`
950
+ * @param familyId - Hosted door definition ID; legacy mesh catalog IDs are not accepted
908
951
  * @param hostWall - The wall to host the door
909
952
  * @param position - World point projected onto the wall to locate the opening
910
- * @param options - Optional placement options: `label` — instance name;
911
- * `hinge` — world point near the jamb the door is hinged on (the door is
912
- * reflected along the wall so its hinged jamb is the one nearer this
913
- * point; default the catalog item's authored hinge side)
914
- * @param facing - World point on the side of the wall the door swings open
915
- * to (the room it opens into — where the plan symbol draws the swing arc).
916
- * Any point clearly on that side works (e.g. the room's center). Default:
917
- * the engine picks a side (nondeterministic when `position` sits on the
918
- * wall centerline).
953
+ * @param options - `version` pins the definition. `values` sets authored parameters
954
+ * (numeric lengths in SU, or unit strings). `label` names the instance.
955
+ * `hinge` selects the declared `hand` enum: local -X is left, +X is right.
956
+ * @param facing - World point selecting local +Z; omitted uses the picked face
919
957
  * @returns the {@linkcode ComponentHandle} of the placed door
920
- * @throws if the catalog id is unknown, the host is not a wall, the source
921
- * mesh fails to load, or the projected point falls **outside** the host wall
958
+ * @throws if the family/category/parameters are invalid, or placement or cutting fails
922
959
  *
923
960
  * @examplePrompt Add a door to this wall here
924
961
  * @examplePrompt Place a sliding door on the selected wall
@@ -961,26 +998,28 @@ export abstract class PluginDesignCreateApi {
961
998
  * that single call. Looping this single-door creator is N round-trips.
962
999
  */
963
1000
  public abstract door(
964
- catalogId: string,
1001
+ familyId: string,
965
1002
  hostWall: ComponentHandle,
966
1003
  position: Vec3Handle,
967
- options?: { label?: string; hinge?: Vec3Handle },
1004
+ options?: {
1005
+ label?: string
1006
+ hinge?: Vec3Handle
1007
+ version?: number
1008
+ values?: Record<string, number | string>
1009
+ },
968
1010
  facing?: Vec3Handle,
969
1011
  ): PluginApiReturn<ComponentHandle>
970
1012
 
971
1013
  /**
972
1014
  * Place **many doors** into host walls in one undoable operation (bulk plural
973
- * of {@linkcode door}). Each item carries its own catalog id, host wall,
1015
+ * of {@linkcode door}). Each item carries its own family id, host wall,
974
1016
  * world position, and optional `facing`, `label`, `width` and `height` — the
975
1017
  * same fields as a `"door"` {@linkcode PluginDesignCreateOpeningOptions}.
976
1018
  * Validate-all-or-throw; one command.
977
1019
  *
978
- * As with the singular creator, placing an opening **re-cuts its host wall**:
979
- * the wall you passed as `hostWall` stops resolving once the call returns.
980
- * Several items MAY name the same original wall handle in one call — the host
981
- * chains the re-cuts internally — but after the call recover the surviving
982
- * wall with `design.query.getHost(opening)`, never by reusing the handle you
983
- * passed in.
1020
+ * The wall keeps its identity. Several items may use the same host.
1021
+ * Width and height set named recipe parameters, not mesh scale. The recipe's
1022
+ * evaluated hole can include frame allowances. A failed item rolls back the batch.
984
1023
  *
985
1024
  * @param items - One {@linkcode PluginCreateDoorItem} per door to place
986
1025
  * (≥1, ≤1000)
@@ -988,10 +1027,9 @@ export abstract class PluginDesignCreateApi {
988
1027
  * @throws `VALIDATION` if the items fail the schema (empty array, more than
989
1028
  * 1000 items, unknown fields, a non-positive `width`/`height`);
990
1029
  * `HANDLE_INVALID` if a `position`, `facing` or `hinge` handle is unknown
991
- * or released; `PRECONDITION_FAILED` if a catalog id is unknown, a `hostWall`
992
- * is not a wall / is locked / is not in the active proposal, the projected
993
- * point falls outside its host wall, or a door tool is active;
994
- * `OPERATION_FAILED` if a source mesh fails to load or placement fails
1030
+ * or released; `PRECONDITION_FAILED` if the family cannot be resolved or saved,
1031
+ * or a host is locked / is not in the active proposal;
1032
+ * `OPERATION_FAILED` if placement, containment, overlap checks or cutting fail
995
1033
  * (nothing is created — `details.itemIndex` is the failing item);
996
1034
  * `METHOD_NOT_PERMITTED` if the plugin may not write.
997
1035
  *
@@ -1002,7 +1040,7 @@ export abstract class PluginDesignCreateApi {
1002
1040
  * @examplePrompt Add all the doors for this floor plan together
1003
1041
  *
1004
1042
  * @performance Bulk creator — the whole batch is ONE host round-trip and ONE undo
1005
- * entry, and each distinct catalog source loads once. Always prefer this over
1043
+ * entry. Always prefer this over
1006
1044
  * calling `design.create.door` in a loop: build the full `items[]` array first
1007
1045
  * (all host walls and points up front), then make one call.
1008
1046
  *
@@ -1012,10 +1050,10 @@ export abstract class PluginDesignCreateApi {
1012
1050
  * const [wall] = await snaptrude.design.query.listWalls({ isSelected: true })
1013
1051
  * const [entry] = await snaptrude.design.doors.listCatalog()
1014
1052
  * const [front, side] = await snaptrude.design.create.doors([
1015
- * { catalogId: entry.id, hostWall: wall, position: await v.new(2, 0, 5), label: "D-01" },
1016
- * { catalogId: entry.id, hostWall: wall, position: await v.new(6, 0, 5), width: 1.2 },
1053
+ * { familyId: entry.id, hostWall: wall, position: await v.new(2, 0, 5), label: "D-01" },
1054
+ * { familyId: entry.id, hostWall: wall, position: await v.new(6, 0, 5), values: { width: "1200mm" } },
1017
1055
  * ])
1018
- * // `wall` was re-cut twice and no longer resolves — ask the opening for its host:
1056
+ * // The host still resolves after both cuts:
1019
1057
  * const currentWall = await snaptrude.design.query.getHost(front)
1020
1058
  * ```
1021
1059
  */
@@ -1024,29 +1062,21 @@ export abstract class PluginDesignCreateApi {
1024
1062
  ): PluginApiReturn<ComponentHandle[]>
1025
1063
 
1026
1064
  /**
1027
- * Place a **window** from the catalog into a host wall.
1065
+ * Place a parametric **window** family into a host wall.
1028
1066
  *
1029
- * Identified by a **catalog (library) id** — the team object's stable `_id`
1030
- * or the general library's `fullName`. Discover ids with
1067
+ * Identified by a saved **family definition id**. Discover ids with
1031
1068
  * {@linkcode PluginDesignWindowsApi.listCatalog}. The world `position` is
1032
1069
  * projected onto the host wall's face to locate the opening; the wall is
1033
1070
  * re-cut and the window hosted in a single undo entry. Placement is
1034
- * asynchronous (the source mesh is fetched if not cached).
1071
+ * asynchronous. The sill parameter determines elevation; position Y is ignored.
1035
1072
  *
1036
- * @param catalogId - Library id: team `_id` or general `fullName`
1073
+ * @param familyId - Hosted window definition ID; legacy mesh catalog IDs are not accepted
1037
1074
  * @param hostWall - The wall to host the window
1038
1075
  * @param position - World point projected onto the wall to locate the opening
1039
- * @param options - Optional placement options: `label` — instance name;
1040
- * `hinge` — world point near the jamb the window is hinged on (same rule
1041
- * as {@linkcode PluginDesignCreateApi.door}; default the catalog item's
1042
- * authored side)
1043
- * @param facing - World point on the side of the wall the window opens to
1044
- * (matters for asymmetric windows, e.g. casement swing) — same convention
1045
- * as {@linkcode PluginDesignCreateApi.door}. Default: the engine picks a
1046
- * side (nondeterministic when `position` sits on the centerline).
1076
+ * @param options - `version`, `values`, `label`, and parameter-driven `hinge`, as for door
1077
+ * @param facing - World point selecting local +Z; omitted uses the picked face
1047
1078
  * @returns the {@linkcode ComponentHandle} of the placed window
1048
- * @throws if the catalog id is unknown, the host is not a wall, the source
1049
- * mesh fails to load, or the projected point falls **outside** the host wall
1079
+ * @throws if the family/category/parameters are invalid, or placement or cutting fails
1050
1080
  *
1051
1081
  * @examplePrompt Add a window to this wall here
1052
1082
  * @examplePrompt Place a casement window on the selected wall
@@ -1072,28 +1102,29 @@ export abstract class PluginDesignCreateApi {
1072
1102
  * inside that single call. Looping this single-window creator is N round-trips.
1073
1103
  */
1074
1104
  public abstract window(
1075
- catalogId: string,
1105
+ familyId: string,
1076
1106
  hostWall: ComponentHandle,
1077
1107
  position: Vec3Handle,
1078
- options?: { label?: string; hinge?: Vec3Handle },
1108
+ options?: {
1109
+ label?: string
1110
+ hinge?: Vec3Handle
1111
+ version?: number
1112
+ values?: Record<string, number | string>
1113
+ },
1079
1114
  facing?: Vec3Handle,
1080
1115
  ): PluginApiReturn<ComponentHandle>
1081
1116
 
1082
1117
  /**
1083
1118
  * Place **many windows** into host walls in one undoable operation (bulk
1084
- * plural of {@linkcode window}). Each item carries its own catalog id, host
1119
+ * plural of {@linkcode window}). Each item carries its own family id, host
1085
1120
  * wall, world position, and optional `facing`, `label`, `width`, `height` and
1086
1121
  * `sillHeight` — the same fields as a `"window"`
1087
1122
  * {@linkcode PluginDesignCreateOpeningOptions}. `sillHeight` is measured from
1088
1123
  * the host wall base to the BOTTOM of the window. Validate-all-or-throw; one
1089
1124
  * command.
1090
1125
  *
1091
- * As with the singular creator, placing an opening **re-cuts its host wall**:
1092
- * the wall you passed as `hostWall` stops resolving once the call returns.
1093
- * Several items MAY name the same original wall handle in one call — the host
1094
- * chains the re-cuts internally — but after the call recover the surviving
1095
- * wall with `design.query.getHost(opening)`, never by reusing the handle you
1096
- * passed in.
1126
+ * Hosts keep their identity. Width, height and sillHeight set the declared
1127
+ * width, height and sill parameters before the first cut. No mesh scaling occurs.
1097
1128
  *
1098
1129
  * @param items - One {@linkcode PluginCreateWindowItem} per window to place
1099
1130
  * (≥1, ≤1000)
@@ -1101,10 +1132,9 @@ export abstract class PluginDesignCreateApi {
1101
1132
  * @throws `VALIDATION` if the items fail the schema (empty array, more than
1102
1133
  * 1000 items, unknown fields, a non-positive `width`/`height`, a negative
1103
1134
  * `sillHeight`); `HANDLE_INVALID` if a `position`, `facing` or `hinge`
1104
- * handle is unknown or released; `PRECONDITION_FAILED` if a catalog id is unknown, a
1105
- * `hostWall` is not a wall / is locked / is not in the active proposal, the
1106
- * projected point falls outside its host wall, or a window tool is active;
1107
- * `OPERATION_FAILED` if a source mesh fails to load or placement fails
1135
+ * handle is unknown or released; `PRECONDITION_FAILED` if the family cannot be
1136
+ * resolved or saved, or a host is locked / is not in the active proposal;
1137
+ * `OPERATION_FAILED` if placement, containment, overlap checks or cutting fail
1108
1138
  * (nothing is created — `details.itemIndex` is the failing item);
1109
1139
  * `METHOD_NOT_PERMITTED` if the plugin may not write.
1110
1140
  *
@@ -1115,7 +1145,7 @@ export abstract class PluginDesignCreateApi {
1115
1145
  * @examplePrompt Add all the windows with a 0.9m sill height together
1116
1146
  *
1117
1147
  * @performance Bulk creator — the whole batch is ONE host round-trip and ONE undo
1118
- * entry, and each distinct catalog source loads once. Always prefer this over
1148
+ * entry. Always prefer this over
1119
1149
  * calling `design.create.window` in a loop: build the full `items[]` array first
1120
1150
  * (all host walls and points up front), then make one call.
1121
1151
  *
@@ -1125,8 +1155,8 @@ export abstract class PluginDesignCreateApi {
1125
1155
  * const [wall] = await snaptrude.design.query.listWalls({ isSelected: true })
1126
1156
  * const [casement] = await snaptrude.design.windows.listCatalog()
1127
1157
  * const placed = await snaptrude.design.create.windows([
1128
- * { catalogId: casement.id, hostWall: wall, position: await v.new(2, 0, 5), sillHeight: 0.9 },
1129
- * { catalogId: casement.id, hostWall: wall, position: await v.new(5, 0, 5), sillHeight: 0.9 },
1158
+ * { familyId: casement.id, hostWall: wall, position: await v.new(2, 0, 5), values: { sill: "900mm" } },
1159
+ * { familyId: casement.id, hostWall: wall, position: await v.new(5, 0, 5), values: { sill: "900mm" } },
1130
1160
  * ])
1131
1161
  * const currentWall = await snaptrude.design.query.getHost(placed[0])
1132
1162
  * ```
@@ -1136,17 +1166,160 @@ export abstract class PluginDesignCreateApi {
1136
1166
  ): PluginApiReturn<ComponentHandle[]>
1137
1167
 
1138
1168
  /**
1139
- * Place a catalog **door or window** into a host wall with optional size overrides.
1169
+ * Cut a **void** — a hole, opening or shaft — through an existing FLAT slab,
1170
+ * floor, roof or ceiling. The void is a selectable child object of its host
1171
+ * (the "Void" of the Draw Void tool). It always cuts the full host thickness.
1172
+ * It can be moved with `design.transform.move` (a move that would leave the
1173
+ * host is shortened without a report) and removed with
1174
+ * `design.delete.entities`; to resize, delete it and cut a new one
1175
+ * (`design.update.space` refuses a void).
1176
+ *
1177
+ * Give the outline **in plan** (all points at one height): its height is
1178
+ * ignored, and it is dropped straight onto the host's top face, so one outline
1179
+ * works on every storey. Keep it at least 25.4 mm (0.1 SU) inside the host
1180
+ * edge and away from other voids; merge overlapping holes into one outline.
1181
+ * Straight segments and arcs only (no splines).
1182
+ *
1183
+ * **A room has a floor finish AND a structural slab that overlap in plan; a
1184
+ * void in one does not cut the other, so a through-opening needs one void in
1185
+ * each.**
1186
+ *
1187
+ * `getEntityType` reports a void as `"mass"`, and `listMasses` /
1188
+ * `listByType("mass")` include voids: a mass whose `design.query.getHost` is a
1189
+ * slab, floor, roof or ceiling is a void. After its host is copied, a void can
1190
+ * report `null` and be missing from those lists; `listChildren(host)` still
1191
+ * finds it.
1192
+ *
1193
+ * Call it as `snaptrude.design.create.void(...)`; `void` is a reserved word and
1194
+ * cannot be destructured. Voids in walls, on sloped, generated or multi-plane
1195
+ * roofs, and on instanced hosts are not available yet; for a door or window
1196
+ * use `design.create.door` / `window`. To make a NEW slab that already has a
1197
+ * fixed hole, pass a contour with holes to `design.create.slab`; that hole is
1198
+ * part of the footprint, not an object. For a contour's outer loop, pass
1199
+ * `core.geom.query.contour.getOuterProfile(contour)`.
1200
+ *
1201
+ * # Example
1202
+ * ```ts
1203
+ * const m = 1000 / 254 // metres → Snaptrude units (SU)
1204
+ * const [slab] = await snaptrude.design.query.listByType("slab", { isSelected: true })
1205
+ * const center = await snaptrude.core.math.vec3.new(10 * m, 0, 5 * m) // plan point; y is ignored
1206
+ * const outline = await snaptrude.core.geom.create.profileRect(2 * m, 2 * m, center) // 2 m x 2 m
1207
+ * const hole = await snaptrude.design.create.void(slab, outline)
1208
+ * ```
1209
+ *
1210
+ * A round hole is two arcs (a circle handle is not a profile):
1211
+ * ```ts
1212
+ * const m = 1000 / 254
1213
+ * const { vec3 } = snaptrude.core.math
1214
+ * const g = snaptrude.core.geom.create
1215
+ * const r = 0.5 * m // 1 m diameter
1216
+ * const centre = await vec3.new(10 * m, 0, 5 * m)
1217
+ * const axis = await vec3.new(0, -1, 0)
1218
+ * const east = await vec3.new(10 * m + r, 0, 5 * m)
1219
+ * const west = await vec3.new(10 * m - r, 0, 5 * m)
1220
+ * const round = await g.profileFromCurves([await g.arc(east, west, centre, axis), await g.arc(west, east, centre, axis)])
1221
+ * await snaptrude.design.create.void(slab, round) // never destructure `void`
1222
+ * ```
1223
+ *
1224
+ * @param host - The slab, floor, roof or ceiling to cut through
1225
+ * @param outline - Closed, flat plan outline (world SU); its height is ignored
1226
+ * @returns the {@linkcode ComponentHandle} of the new void
1227
+ * @throws `VALIDATION` if the outline is open, crosses itself, is not flat or
1228
+ * has a spline, or the arguments are the wrong handle kinds or swapped;
1229
+ * `HANDLE_INVALID` if a handle is unknown or released or the host was
1230
+ * deleted; `HANDLE_STALE` if the host was rebuilt (re-query it);
1231
+ * `PRECONDITION_FAILED` if the
1232
+ * host is a wall or not a slab/floor/roof/ceiling, is sloped, instanced, in
1233
+ * another building, locked, outside the active proposal, a generated roof or
1234
+ * not a single plate, or there is no active structure (`engineCode`
1235
+ * `NO_ACTIVE_STRUCTURE`); `OPERATION_FAILED` with `engineCode`
1236
+ * `VOID_DOES_NOT_FIT` when the outline crosses the host edge, overlaps a void
1237
+ * or hole, is closer than 25.4 mm to either, or the fit could not be checked,
1238
+ * or another `engineCode` when the cut failed (nothing is created, except
1239
+ * `PUBLISH_FAILED` with a hint that the voids are in the scene: undo, then
1240
+ * retry); `METHOD_NOT_PERMITTED` if the plugin may not write.
1241
+ *
1242
+ * @examplePrompt Cut a hole in this slab
1243
+ * @examplePrompt Add a 1.2m by 2.4m stair opening in the first-floor slab
1244
+ * @examplePrompt Make a 600 x 600 service penetration in the floor for the riser
1245
+ * @examplePrompt Put a skylight opening through the roof
1246
+ * @examplePrompt Cut a round 1m duct hole in the slab
1247
+ * @examplePrompt Make a cut-out in the ceiling for the light well
1248
+ * @examplePrompt Add a double-height atrium void in the level 2 floor
1249
+ *
1250
+ * @performance Single-void creator. For MORE THAN ONE void, call
1251
+ * `design.create.voids(items[])`: one host round-trip, ONE undo entry, one cut
1252
+ * per host.
1253
+ */
1254
+ public abstract void(
1255
+ host: ComponentHandle,
1256
+ outline: ProfileHandle,
1257
+ ): PluginApiReturn<ComponentHandle>
1258
+
1259
+ /**
1260
+ * Cut **many voids** (holes, openings, shafts) through slabs, floors, roofs
1261
+ * and ceilings in one undoable operation (bulk plural of {@linkcode void}).
1262
+ * Items may share a host or use different hosts — one shaft outline on every
1263
+ * storey's slab AND floor is one call. Validate-all-or-throw: one item whose
1264
+ * host does not contain the outline rejects the whole batch, so pass only the
1265
+ * hosts that contain it; `details.itemIndex` names the failing item (for two
1266
+ * overlapping items of the batch, the earlier one). At most 50 voids per host
1267
+ * and 200 in all per call. Same outline rules as {@linkcode void}: plan outline, 25.4 mm
1268
+ * (0.1 SU) clearance, floor AND slab are separate hosts. Call it as
1269
+ * `snaptrude.design.create.voids(...)`.
1270
+ *
1271
+ * # Example
1272
+ * ```ts
1273
+ * const m = 1000 / 254
1274
+ * // The user selects the slabs AND floors the shaft passes through; every one must contain the outline.
1275
+ * const hosts = [
1276
+ * ...(await snaptrude.design.query.listByType("slab", { isSelected: true })),
1277
+ * ...(await snaptrude.design.query.listByType("floor", { isSelected: true })),
1278
+ * ]
1279
+ * const centre = await snaptrude.core.math.vec3.new(12 * m, 0, -6 * m)
1280
+ * const shaft = await snaptrude.core.geom.create.profileRect(2 * m, 2 * m, centre)
1281
+ * const holes = await snaptrude.design.create.voids(hosts.map((host) => ({ host, outline: shaft })))
1282
+ * ```
1283
+ *
1284
+ * @param items - One {@linkcode PluginCreateVoidItem} per void (≥1, ≤200; ≤50 per host)
1285
+ * @returns the new voids as {@linkcode ComponentHandle}`[]`, in input order
1286
+ * @throws `VALIDATION` if the items fail the schema (empty array, more than
1287
+ * 1000 items, unknown fields), an outline is invalid (as for
1288
+ * {@linkcode void}), more than 50 items share one host, or there are more
1289
+ * than 200 items; plus every code {@linkcode void} throws, each with
1290
+ * `details.itemIndex`. Nothing is created on any failure, except
1291
+ * `PUBLISH_FAILED` (as for {@linkcode void}).
1292
+ *
1293
+ * @examplePrompt Cut a lift shaft through every floor slab of the core
1294
+ * @examplePrompt Cut a 2m x 2m shaft through all the slabs at this location
1295
+ * @examplePrompt Make stair openings in all the floors above the staircases
1296
+ * @examplePrompt Make holes in the roof for these three skylights
1297
+ * @examplePrompt Cut openings in each storey's slab matching this outline
1298
+ * @examplePrompt Punch the same riser penetration through every floor at once
1299
+ *
1300
+ * @performance ONE host round-trip, ONE undo entry, ONE cut per host. Never call
1301
+ * `void` in a loop. Runs synchronously on the main thread: 50 voids on one
1302
+ * host take about 1 s, and the time grows with the number of hosts (caps:
1303
+ * 50 per host, 200 per call).
1304
+ */
1305
+ public abstract voids(
1306
+ items: PluginCreateVoidItem[],
1307
+ ): PluginApiReturn<ComponentHandle[]>
1308
+
1309
+ /**
1310
+ * Place a parametric **door or window** into a host wall with parameter overrides.
1140
1311
  *
1141
1312
  * The world `position` is projected onto the host wall to locate the opening.
1142
1313
  * All supplied dimensions use Snaptrude engine units. Window `sillHeight` is
1143
1314
  * measured from the host wall base to the bottom of the window, not to
1144
1315
  * its center. Placement is asynchronous and committed as one undoable creation.
1145
1316
  *
1146
- * @param options - Opening kind, catalog and host references, placement, and
1317
+ * Wall only. For a hole in a slab, floor or roof use `design.create.void`.
1318
+ *
1319
+ * @param options - Opening kind, family and host references, placement, and
1147
1320
  * optional facing (swing side), hinge (hinged jamb), label, and dimensions
1148
1321
  * @returns the {@linkcode ComponentHandle} of the placed door or window
1149
- * @throws if the options are invalid, the catalog id is unknown, the host is
1322
+ * @throws if the options are invalid, the family id is unknown, the host is
1150
1323
  * not a wall, loading fails, or the opening cannot be placed on the host
1151
1324
  *
1152
1325
  * @examplePrompt Add a 1m wide door to this wall here
@@ -1160,7 +1333,7 @@ export abstract class PluginDesignCreateApi {
1160
1333
  * ```ts
1161
1334
  * const opening = await snaptrude.design.create.opening({
1162
1335
  * kind: "window",
1163
- * catalogId: windowType.id,
1336
+ * familyId: windowType.id,
1164
1337
  * hostWall: wall,
1165
1338
  * position: await snaptrude.core.math.vec3.new(3, 0, 5),
1166
1339
  * width: 1.2,
@@ -1660,7 +1833,7 @@ export type PluginDesignCreateBeamArgs = z.infer<
1660
1833
  * | `profile` | {@linkcode ProfileHandle} | Ordered curve chain (wall centerlines) |
1661
1834
  * | `height` | `number`? | Wall height (default engine default) |
1662
1835
  * | `thickness` | `number`? | Wall thickness (default: `wallType`'s total layer thickness when given, else engine default) |
1663
- * | `wallType` | `string`? | Wall type name from `design.types.list("wall")` — layers/material/thickness defaults (default generic) |
1836
+ * | `wallType` | {@linkcode PluginWallType}? | Legacy name/id or exact native `{ id, version }` pin from `design.types.list("wall")` (default generic) |
1664
1837
  * | `storey` | `number`? | Target storey (integer, must exist) — walls assigned + lifted to its base in the same undo entry (default: geometry-derived) |
1665
1838
  * | `topProfiles` | ({@linkcode PluginWallTopProfile} \| `null`)[]? | Top edge per curve (same order); `null`/missing = flat at `height`. Straight curves only |
1666
1839
  */
@@ -1668,9 +1841,10 @@ export const PluginDesignCreateWallsArgs = z.object({
1668
1841
  profile: ProfileHandle,
1669
1842
  height: z.number().optional(),
1670
1843
  thickness: z.number().optional(),
1671
- wallType: z.string().min(1).optional(),
1844
+ wallType: PluginWallType.optional(),
1672
1845
  storey: z.number().int().optional(),
1673
1846
  topProfiles: z.array(PluginWallTopProfile.nullable()).optional(),
1847
+ values: PluginFamilyValues.optional(),
1674
1848
  })
1675
1849
  export type PluginDesignCreateWallsArgs = z.infer<
1676
1850
  typeof PluginDesignCreateWallsArgs
@@ -1743,26 +1917,23 @@ export type PluginDesignCreateFurnitureArgs = z.infer<
1743
1917
  *
1744
1918
  * | Property | Type | Description |
1745
1919
  * |---|---|---|
1746
- * | `catalogId` | `string` | Library id: team `_id` or general `fullName` |
1920
+ * | `familyId` | `string` | Hosted door family definition ID |
1921
+ * | `version` | `number`? | Exact version; newest known by default |
1922
+ * | `values` | `Record<string, number \| string>`? | Parameters; numeric lengths use SU |
1747
1923
  * | `hostWall` | {@linkcode ComponentHandle} | The wall to host the door |
1748
1924
  * | `position` | {@linkcode Vec3Handle} | World point projected onto the wall |
1749
1925
  * | `label` | `string`? | Instance name (optional) |
1750
1926
  * | `facing` | {@linkcode Vec3Handle}? | World point on the side of the wall the door swings open to (default engine-chosen) |
1751
- * | `hinge` | {@linkcode Vec3Handle}? | World point near the jamb the door is hinged on (default the catalog item's authored side) |
1927
+ * | `hinge` | {@linkcode Vec3Handle}? | World point near the hinged jamb; requires hand control |
1752
1928
  */
1753
1929
  export const PluginDesignCreateDoorArgs = z.object({
1754
- catalogId: z.string().min(1),
1755
- hostWall: ComponentHandle,
1756
- position: Vec3Handle,
1757
- label: z.string().optional(),
1758
- facing: Vec3Handle.optional(),
1759
- hinge: Vec3Handle.optional(),
1930
+ ...PluginOpeningBaseOptions,
1760
1931
  })
1761
1932
  export type PluginDesignCreateDoorArgs = z.infer<
1762
1933
  typeof PluginDesignCreateDoorArgs
1763
1934
  >
1764
1935
  // TRANSPORT: positional signature shipped — facing is the trailing 5th arg, hinge rides in options:
1765
- // door(catalogId: string, hostWall: ComponentHandle, position: Vec3Handle, options?: { label?, hinge? }, facing?: Vec3Handle)
1936
+ // door(familyId, hostWall, position, options?: { label?, hinge?, version?, values? }, facing?)
1766
1937
 
1767
1938
  // ---------------------------------------------------------------------------
1768
1939
  // window
@@ -1773,26 +1944,23 @@ export type PluginDesignCreateDoorArgs = z.infer<
1773
1944
  *
1774
1945
  * | Property | Type | Description |
1775
1946
  * |---|---|---|
1776
- * | `catalogId` | `string` | Library id: team `_id` or general `fullName` |
1947
+ * | `familyId` | `string` | Hosted window family definition ID |
1948
+ * | `version` | `number`? | Exact version; newest known by default |
1949
+ * | `values` | `Record<string, number \| string>`? | Parameters; numeric lengths use SU |
1777
1950
  * | `hostWall` | {@linkcode ComponentHandle} | The wall to host the window |
1778
1951
  * | `position` | {@linkcode Vec3Handle} | World point projected onto the wall |
1779
1952
  * | `label` | `string`? | Instance name (optional) |
1780
1953
  * | `facing` | {@linkcode Vec3Handle}? | World point on the side of the wall the window opens to (default engine-chosen) |
1781
- * | `hinge` | {@linkcode Vec3Handle}? | World point near the jamb the window is hinged on (default the catalog item's authored side) |
1954
+ * | `hinge` | {@linkcode Vec3Handle}? | World point near the hinged jamb; requires hand control |
1782
1955
  */
1783
1956
  export const PluginDesignCreateWindowArgs = z.object({
1784
- catalogId: z.string().min(1),
1785
- hostWall: ComponentHandle,
1786
- position: Vec3Handle,
1787
- label: z.string().optional(),
1788
- facing: Vec3Handle.optional(),
1789
- hinge: Vec3Handle.optional(),
1957
+ ...PluginOpeningBaseOptions,
1790
1958
  })
1791
1959
  export type PluginDesignCreateWindowArgs = z.infer<
1792
1960
  typeof PluginDesignCreateWindowArgs
1793
1961
  >
1794
1962
  // TRANSPORT: positional signature shipped — facing is the trailing 5th arg, hinge rides in options:
1795
- // window(catalogId: string, hostWall: ComponentHandle, position: Vec3Handle, options?: { label?, hinge? }, facing?: Vec3Handle)
1963
+ // window(familyId, hostWall, position, options?: { label?, hinge?, version?, values? }, facing?)
1796
1964
 
1797
1965
  // ---------------------------------------------------------------------------
1798
1966
  // opening