@snaptrude/plugin-core 0.0.0-dev-20260708130115 → 0.0.0-dev-20260827135706

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 (261) hide show
  1. package/CHANGELOG.md +152 -1
  2. package/api-manifest.json +3620 -276
  3. package/dist/api/analysis/daylight.d.ts +603 -0
  4. package/dist/api/analysis/daylight.d.ts.map +1 -0
  5. package/dist/api/analysis/heatmaps.d.ts +658 -0
  6. package/dist/api/analysis/heatmaps.d.ts.map +1 -0
  7. package/dist/api/analysis/illuminance.d.ts +145 -0
  8. package/dist/api/analysis/illuminance.d.ts.map +1 -0
  9. package/dist/api/analysis/index.d.ts +59 -0
  10. package/dist/api/analysis/index.d.ts.map +1 -0
  11. package/dist/api/analysis/shadows.d.ts +165 -0
  12. package/dist/api/analysis/shadows.d.ts.map +1 -0
  13. package/dist/api/analysis/solar.d.ts +249 -0
  14. package/dist/api/analysis/solar.d.ts.map +1 -0
  15. package/dist/api/analysis/sunlightHours.d.ts +208 -0
  16. package/dist/api/analysis/sunlightHours.d.ts.map +1 -0
  17. package/dist/api/analysis/sunpath.d.ts +80 -0
  18. package/dist/api/analysis/sunpath.d.ts.map +1 -0
  19. package/dist/api/analysis/weather.d.ts +193 -0
  20. package/dist/api/analysis/weather.d.ts.map +1 -0
  21. package/dist/api/core/camera/index.d.ts +261 -0
  22. package/dist/api/core/camera/index.d.ts.map +1 -0
  23. package/dist/api/core/comment/index.d.ts +105 -2
  24. package/dist/api/core/comment/index.d.ts.map +1 -1
  25. package/dist/api/core/geom/create/index.d.ts +840 -14
  26. package/dist/api/core/geom/create/index.d.ts.map +1 -1
  27. package/dist/api/core/geom/delete/index.d.ts +8 -2
  28. package/dist/api/core/geom/delete/index.d.ts.map +1 -1
  29. package/dist/api/core/geom/query/arc.d.ts +5 -5
  30. package/dist/api/core/geom/query/brep.d.ts +130 -18
  31. package/dist/api/core/geom/query/brep.d.ts.map +1 -1
  32. package/dist/api/core/geom/query/circle.d.ts +18 -18
  33. package/dist/api/core/geom/query/contour.d.ts +20 -20
  34. package/dist/api/core/geom/query/curve.d.ts +49 -49
  35. package/dist/api/core/geom/query/edge.d.ts +5 -5
  36. package/dist/api/core/geom/query/face.d.ts +16 -16
  37. package/dist/api/core/geom/query/halfedge.d.ts +8 -8
  38. package/dist/api/core/geom/query/profile.d.ts +19 -19
  39. package/dist/api/core/geom/query/vertex.d.ts +8 -8
  40. package/dist/api/core/geom/update/contour.d.ts +14 -14
  41. package/dist/api/core/geom/update/curve.d.ts +7 -7
  42. package/dist/api/core/geom/update/profile.d.ts +16 -16
  43. package/dist/api/core/handles/index.d.ts +210 -0
  44. package/dist/api/core/handles/index.d.ts.map +1 -0
  45. package/dist/api/core/index.d.ts +34 -0
  46. package/dist/api/core/index.d.ts.map +1 -1
  47. package/dist/api/core/io/export/index.d.ts +134 -0
  48. package/dist/api/core/io/export/index.d.ts.map +1 -0
  49. package/dist/api/core/io/import/index.d.ts +453 -0
  50. package/dist/api/core/io/import/index.d.ts.map +1 -0
  51. package/dist/api/core/io/index.d.ts +40 -0
  52. package/dist/api/core/io/index.d.ts.map +1 -0
  53. package/dist/api/core/io/job/index.d.ts +139 -0
  54. package/dist/api/core/io/job/index.d.ts.map +1 -0
  55. package/dist/api/core/io/query/index.d.ts +74 -0
  56. package/dist/api/core/io/query/index.d.ts.map +1 -0
  57. package/dist/api/core/io/terrain/index.d.ts +341 -0
  58. package/dist/api/core/io/terrain/index.d.ts.map +1 -0
  59. package/dist/api/core/io/underlay/index.d.ts +805 -0
  60. package/dist/api/core/io/underlay/index.d.ts.map +1 -0
  61. package/dist/api/core/layers.d.ts +7 -7
  62. package/dist/api/core/mode/index.d.ts +99 -0
  63. package/dist/api/core/mode/index.d.ts.map +1 -0
  64. package/dist/api/core/project/index.d.ts +68 -1
  65. package/dist/api/core/project/index.d.ts.map +1 -1
  66. package/dist/api/core/proposals/index.d.ts +531 -0
  67. package/dist/api/core/proposals/index.d.ts.map +1 -0
  68. package/dist/api/core/storeys/index.d.ts +265 -0
  69. package/dist/api/core/storeys/index.d.ts.map +1 -0
  70. package/dist/api/core/tags.d.ts +24 -0
  71. package/dist/api/core/tags.d.ts.map +1 -1
  72. package/dist/api/core/user.d.ts +44 -0
  73. package/dist/api/core/user.d.ts.map +1 -0
  74. package/dist/api/core/zoom/index.d.ts +4 -0
  75. package/dist/api/core/zoom/index.d.ts.map +1 -1
  76. package/dist/api/design/boolean/index.d.ts +4 -4
  77. package/dist/api/design/create/index.d.ts +568 -45
  78. package/dist/api/design/create/index.d.ts.map +1 -1
  79. package/dist/api/design/delete/index.d.ts +3 -0
  80. package/dist/api/design/delete/index.d.ts.map +1 -1
  81. package/dist/api/design/doors/index.d.ts +195 -0
  82. package/dist/api/design/doors/index.d.ts.map +1 -1
  83. package/dist/api/design/edit/index.d.ts +1 -1
  84. package/dist/api/design/erase/index.d.ts +2 -2
  85. package/dist/api/design/family.d.ts +349 -0
  86. package/dist/api/design/family.d.ts.map +1 -0
  87. package/dist/api/design/furniture/index.d.ts +181 -8
  88. package/dist/api/design/furniture/index.d.ts.map +1 -1
  89. package/dist/api/design/index.d.ts +98 -0
  90. package/dist/api/design/index.d.ts.map +1 -1
  91. package/dist/api/design/lock.d.ts +26 -0
  92. package/dist/api/design/lock.d.ts.map +1 -1
  93. package/dist/api/design/materials/index.d.ts +270 -16
  94. package/dist/api/design/materials/index.d.ts.map +1 -1
  95. package/dist/api/design/query/geometry/index.d.ts +112 -0
  96. package/dist/api/design/query/geometry/index.d.ts.map +1 -1
  97. package/dist/api/design/query/index.d.ts +282 -11
  98. package/dist/api/design/query/index.d.ts.map +1 -1
  99. package/dist/api/design/query/referenceLines.d.ts +45 -0
  100. package/dist/api/design/query/referenceLines.d.ts.map +1 -0
  101. package/dist/api/design/query/spaces.d.ts +181 -8
  102. package/dist/api/design/query/spaces.d.ts.map +1 -1
  103. package/dist/api/design/selection/index.d.ts +144 -0
  104. package/dist/api/design/selection/index.d.ts.map +1 -1
  105. package/dist/api/design/transform/index.d.ts +172 -10
  106. package/dist/api/design/transform/index.d.ts.map +1 -1
  107. package/dist/api/design/types/index.d.ts +181 -0
  108. package/dist/api/design/types/index.d.ts.map +1 -0
  109. package/dist/api/design/update/index.d.ts +553 -2
  110. package/dist/api/design/update/index.d.ts.map +1 -1
  111. package/dist/api/design/visibility.d.ts +126 -0
  112. package/dist/api/design/visibility.d.ts.map +1 -0
  113. package/dist/api/design/windows/index.d.ts +113 -2
  114. package/dist/api/design/windows/index.d.ts.map +1 -1
  115. package/dist/api/entity/buildableEnvelope.d.ts +4 -0
  116. package/dist/api/entity/buildableEnvelope.d.ts.map +1 -1
  117. package/dist/api/entity/referenceLine.d.ts +10 -2
  118. package/dist/api/entity/referenceLine.d.ts.map +1 -1
  119. package/dist/api/entity/space.d.ts +21 -21
  120. package/dist/api/entity/story.d.ts +242 -15
  121. package/dist/api/entity/story.d.ts.map +1 -1
  122. package/dist/api/index.d.ts +10 -0
  123. package/dist/api/index.d.ts.map +1 -1
  124. package/dist/api/presentation/aiInspiration.d.ts +25 -25
  125. package/dist/api/presentation/annotate.d.ts +467 -0
  126. package/dist/api/presentation/annotate.d.ts.map +1 -0
  127. package/dist/api/presentation/diagrams.d.ts +111 -8
  128. package/dist/api/presentation/diagrams.d.ts.map +1 -1
  129. package/dist/api/presentation/export.d.ts +108 -0
  130. package/dist/api/presentation/export.d.ts.map +1 -0
  131. package/dist/api/presentation/import.d.ts +55 -4
  132. package/dist/api/presentation/import.d.ts.map +1 -1
  133. package/dist/api/presentation/index.d.ts +58 -1
  134. package/dist/api/presentation/index.d.ts.map +1 -1
  135. package/dist/api/presentation/placedViews.d.ts +1139 -0
  136. package/dist/api/presentation/placedViews.d.ts.map +1 -0
  137. package/dist/api/presentation/shapes.d.ts +481 -0
  138. package/dist/api/presentation/shapes.d.ts.map +1 -0
  139. package/dist/api/presentation/sheets.d.ts +452 -13
  140. package/dist/api/presentation/sheets.d.ts.map +1 -1
  141. package/dist/api/presentation/slideshow.d.ts +125 -0
  142. package/dist/api/presentation/slideshow.d.ts.map +1 -0
  143. package/dist/api/presentation/tables.d.ts +81 -0
  144. package/dist/api/presentation/tables.d.ts.map +1 -0
  145. package/dist/api/presentation/views.d.ts +367 -7
  146. package/dist/api/presentation/views.d.ts.map +1 -1
  147. package/dist/api/program/areas.d.ts +102 -12
  148. package/dist/api/program/areas.d.ts.map +1 -1
  149. package/dist/api/program/cores.d.ts +3 -99
  150. package/dist/api/program/cores.d.ts.map +1 -1
  151. package/dist/api/program/index.d.ts +7 -15
  152. package/dist/api/program/index.d.ts.map +1 -1
  153. package/dist/api/program/layout.d.ts +346 -11
  154. package/dist/api/program/layout.d.ts.map +1 -1
  155. package/dist/api/program/site.d.ts +469 -13
  156. package/dist/api/program/site.d.ts.map +1 -1
  157. package/dist/api/program/spreadsheet.d.ts +365 -41
  158. package/dist/api/program/spreadsheet.d.ts.map +1 -1
  159. package/dist/api/workspace/index.d.ts +505 -0
  160. package/dist/api/workspace/index.d.ts.map +1 -0
  161. package/dist/errors/codes.d.ts +34 -0
  162. package/dist/errors/codes.d.ts.map +1 -0
  163. package/dist/errors/envelope.d.ts +56 -0
  164. package/dist/errors/envelope.d.ts.map +1 -0
  165. package/dist/errors/index.d.ts +6 -0
  166. package/dist/errors/index.d.ts.map +1 -0
  167. package/dist/errors/plugin-error.d.ts +69 -0
  168. package/dist/errors/plugin-error.d.ts.map +1 -0
  169. package/dist/handles.d.ts +97 -25
  170. package/dist/handles.d.ts.map +1 -1
  171. package/dist/host-utils.d.ts +4 -0
  172. package/dist/host-utils.d.ts.map +1 -1
  173. package/dist/index.cjs +4582 -1422
  174. package/dist/index.cjs.map +1 -1
  175. package/dist/index.d.ts +1 -0
  176. package/dist/index.d.ts.map +1 -1
  177. package/dist/index.js +4171 -1411
  178. package/dist/index.js.map +1 -1
  179. package/package.json +4 -2
  180. package/scripts/generate-manifest.mjs +45 -0
  181. package/scripts/generate-manifest.test.mjs +103 -4
  182. package/src/api/analysis/daylight.ts +470 -0
  183. package/src/api/analysis/heatmaps.ts +683 -0
  184. package/src/api/analysis/illuminance.ts +155 -0
  185. package/src/api/analysis/index.ts +61 -0
  186. package/src/api/analysis/shadows.ts +183 -0
  187. package/src/api/analysis/solar.ts +237 -0
  188. package/src/api/analysis/sunlightHours.ts +211 -0
  189. package/src/api/analysis/sunpath.ts +83 -0
  190. package/src/api/analysis/weather.ts +179 -0
  191. package/src/api/core/camera/index.ts +268 -0
  192. package/src/api/core/comment/index.ts +120 -2
  193. package/src/api/core/geom/create/index.ts +912 -1
  194. package/src/api/core/geom/delete/index.ts +6 -0
  195. package/src/api/core/geom/query/brep.ts +119 -0
  196. package/src/api/core/handles/index.ts +233 -0
  197. package/src/api/core/index.ts +34 -0
  198. package/src/api/core/io/export/index.ts +126 -0
  199. package/src/api/core/io/import/index.ts +496 -0
  200. package/src/api/core/io/index.ts +42 -0
  201. package/src/api/core/io/job/index.ts +140 -0
  202. package/src/api/core/io/query/index.ts +71 -0
  203. package/src/api/core/io/terrain/index.ts +360 -0
  204. package/src/api/core/io/underlay/index.ts +705 -0
  205. package/src/api/core/mode/index.ts +96 -0
  206. package/src/api/core/project/index.ts +62 -1
  207. package/src/api/core/proposals/index.ts +569 -0
  208. package/src/api/core/storeys/index.ts +294 -0
  209. package/src/api/core/tags.ts +27 -0
  210. package/src/api/core/user.ts +46 -0
  211. package/src/api/core/zoom/index.ts +4 -0
  212. package/src/api/design/create/index.ts +670 -30
  213. package/src/api/design/delete/index.ts +3 -0
  214. package/src/api/design/doors/index.ts +208 -0
  215. package/src/api/design/erase/index.ts +1 -1
  216. package/src/api/design/family.ts +388 -0
  217. package/src/api/design/furniture/index.ts +197 -8
  218. package/src/api/design/index.ts +102 -0
  219. package/src/api/design/lock.ts +27 -0
  220. package/src/api/design/materials/index.ts +334 -27
  221. package/src/api/design/query/geometry/index.ts +125 -3
  222. package/src/api/design/query/index.ts +217 -7
  223. package/src/api/design/query/referenceLines.ts +52 -0
  224. package/src/api/design/query/spaces.ts +143 -0
  225. package/src/api/design/selection/index.ts +129 -0
  226. package/src/api/design/transform/index.ts +170 -9
  227. package/src/api/design/types/index.ts +156 -0
  228. package/src/api/design/update/index.ts +631 -3
  229. package/src/api/design/visibility.ts +143 -0
  230. package/src/api/design/windows/index.ts +128 -2
  231. package/src/api/entity/buildableEnvelope.ts +4 -0
  232. package/src/api/entity/referenceLine.ts +8 -0
  233. package/src/api/entity/story.ts +259 -15
  234. package/src/api/index.ts +10 -0
  235. package/src/api/presentation/annotate.ts +385 -0
  236. package/src/api/presentation/diagrams.ts +118 -8
  237. package/src/api/presentation/export.ts +108 -0
  238. package/src/api/presentation/import.ts +51 -4
  239. package/src/api/presentation/index.ts +66 -1
  240. package/src/api/presentation/placedViews.ts +1120 -0
  241. package/src/api/presentation/shapes.ts +274 -0
  242. package/src/api/presentation/sheets.ts +400 -13
  243. package/src/api/presentation/slideshow.ts +134 -0
  244. package/src/api/presentation/tables.ts +84 -0
  245. package/src/api/presentation/views.ts +376 -8
  246. package/src/api/program/areas.ts +88 -15
  247. package/src/api/program/cores.ts +3 -91
  248. package/src/api/program/index.ts +7 -15
  249. package/src/api/program/layout.ts +365 -11
  250. package/src/api/program/site.ts +435 -13
  251. package/src/api/program/spreadsheet.ts +376 -35
  252. package/src/api/workspace/index.ts +563 -0
  253. package/src/errors/codes.ts +136 -0
  254. package/src/errors/envelope.ts +75 -0
  255. package/src/errors/index.ts +21 -0
  256. package/src/errors/plugin-error.ts +134 -0
  257. package/src/handles.ts +123 -13
  258. package/src/host-utils.ts +4 -0
  259. package/src/index.ts +1 -0
  260. package/test/errors.test.mjs +184 -0
  261. package/tsconfig.json +7 -2
@@ -52,6 +52,9 @@ export abstract class PluginDesignDeleteApi {
52
52
  * @examplePrompt Delete all the furniture I picked
53
53
  * @examplePrompt Remove the door and window I just created
54
54
  *
55
+ * @performance Array delete — pass every target in one call (one host round-trip). There is
56
+ * no per-item delete; collect the whole set and never loop this per entity.
57
+ *
55
58
  * # Example
56
59
  * ```ts
57
60
  * // Hard-delete every furniture item on storey 2 (a single undo entry)
@@ -12,6 +12,13 @@ import { PluginDesignChangeResult } from "../lock"
12
12
  * mesh reflection state. `mirror` reflects the door across an axis (undoable, one
13
13
  * command); `setType` is intentionally absent (the engine has no in-place re-type —
14
14
  * it would require delete+recreate).
15
+ *
16
+ * The **catalog** reads (`listCatalogGroups`/`listCatalog`/`getCatalogItem`/`exists`)
17
+ * browse the placeable door library — plain value records keyed by a library id
18
+ * (team `_id` or general `fullName`), NOT handles — and feed `design.create.door`.
19
+ * They are network-backed but carry no `Async` suffix (§6.6) and never throw
20
+ * (`[]`/`null`). The {@linkcode PluginObjectCatalogGroup} / {@linkcode PluginObjectCatalogItem}
21
+ * DTOs are shared with `design.windows`.
15
22
  */
16
23
  export abstract class PluginDesignDoorsApi {
17
24
  constructor() {}
@@ -98,6 +105,30 @@ export abstract class PluginDesignDoorsApi {
98
105
  */
99
106
  public abstract getHeight(door: ComponentHandle): PluginApiReturn<number | null>
100
107
 
108
+ /**
109
+ * Get a door's full dimensions in one call — width along the wall, vertical
110
+ * height, and thickness through the wall — measured in the door's local
111
+ * oriented space. Parity with {@linkcode PluginDesignWindowsApi.getDimensions}.
112
+ *
113
+ * @param door The door to query
114
+ * @returns The dimensions, or `null`
115
+ *
116
+ * @examplePrompt What are the dimensions of this door?
117
+ * @examplePrompt How big is the selected door?
118
+ * @examplePrompt Give me the width, height and depth of this door
119
+ * @examplePrompt Measure the entrance door including its thickness through the wall
120
+ *
121
+ * # Example
122
+ * ```ts
123
+ * const [door] = await snaptrude.design.query.listDoors({ isSelected: true })
124
+ * const dims = await snaptrude.design.doors.getDimensions(door)
125
+ * if (dims) console.log(dims.width, dims.height, dims.thickness)
126
+ * ```
127
+ */
128
+ public abstract getDimensions(
129
+ door: ComponentHandle
130
+ ): PluginApiReturn<PluginDoorDimensions | null>
131
+
101
132
  /**
102
133
  * Get the floor a door is hosted on — the storey/level component that
103
134
  * supports the door.
@@ -224,6 +255,90 @@ export abstract class PluginDesignDoorsApi {
224
255
  doors: ComponentHandle[],
225
256
  height: number
226
257
  ): PluginApiReturn<PluginDesignChangeResult>
258
+
259
+ /**
260
+ * List the door catalog **groups** — the placement families (Swing, Sliding,
261
+ * Folding, Pivot, …), built-in groups merged with any team-custom types.
262
+ *
263
+ * @returns The groups as {@linkcode PluginObjectCatalogGroup}`[]` (`[]` when empty)
264
+ *
265
+ * @examplePrompt What kinds of doors can I place?
266
+ * @examplePrompt List the door groups in the catalog
267
+ * @examplePrompt Show me the door families available in this project
268
+ * @examplePrompt Which door types does this project support?
269
+ *
270
+ * # Example
271
+ * ```ts
272
+ * const groups = await snaptrude.design.doors.listCatalogGroups()
273
+ * for (const g of groups) console.log(g.dbType, g.label, g.source)
274
+ * ```
275
+ */
276
+ public abstract listCatalogGroups(): PluginApiReturn<PluginObjectCatalogGroup[]>
277
+
278
+ /**
279
+ * List the placeable door catalog (team + general libraries), optionally
280
+ * restricted to one group by its `dbType` token.
281
+ *
282
+ * @param group Restrict to one group's `dbType` (optional; e.g. `"singleDoor"`)
283
+ * @returns The catalog as {@linkcode PluginObjectCatalogItem}`[]` (`[]` when empty)
284
+ *
285
+ * @examplePrompt What doors can I place in this project?
286
+ * @examplePrompt List every door in the library
287
+ * @examplePrompt Show me the sliding doors I can add
288
+ * @examplePrompt Browse the door catalog
289
+ *
290
+ * # Example
291
+ * ```ts
292
+ * const [group] = await snaptrude.design.doors.listCatalogGroups()
293
+ * const items = await snaptrude.design.doors.listCatalog(group?.dbType)
294
+ * for (const item of items) console.log(item.id, item.name, item.thumbnailUrl)
295
+ * ```
296
+ */
297
+ public abstract listCatalog(
298
+ group?: string
299
+ ): PluginApiReturn<PluginObjectCatalogItem[]>
300
+
301
+ /**
302
+ * Get a single door catalog item by its library id.
303
+ *
304
+ * @param id The catalog library id
305
+ * @returns The item, or `null` if no catalog item matches
306
+ *
307
+ * @examplePrompt Get the details of this door from the library
308
+ * @examplePrompt Look up the catalog entry for this door id
309
+ * @examplePrompt What is the cost of this door item?
310
+ * @examplePrompt Show me the family name of this door in the catalog
311
+ *
312
+ * # Example
313
+ * ```ts
314
+ * const [entry] = await snaptrude.design.doors.listCatalog()
315
+ * const item = await snaptrude.design.doors.getCatalogItem(entry.id)
316
+ * if (item) console.log(item.name, item.source, item.dbType)
317
+ * ```
318
+ */
319
+ public abstract getCatalogItem(
320
+ id: string
321
+ ): PluginApiReturn<PluginObjectCatalogItem | null>
322
+
323
+ /**
324
+ * Test whether a door catalog item exists for the given library id.
325
+ *
326
+ * @param id The catalog library id
327
+ * @returns `true` if it exists, otherwise `false`
328
+ *
329
+ * @examplePrompt Is this door still in the library?
330
+ * @examplePrompt Check whether a door catalog item with this id exists
331
+ * @examplePrompt Does my team library have this door?
332
+ * @examplePrompt Verify the door is available before placing it
333
+ *
334
+ * # Example
335
+ * ```ts
336
+ * const [entry] = await snaptrude.design.doors.listCatalog()
337
+ * const stillAvailable = await snaptrude.design.doors.exists(entry.id)
338
+ * console.log(stillAvailable ? "safe to place" : "removed from library")
339
+ * ```
340
+ */
341
+ public abstract exists(id: string): PluginApiReturn<boolean>
227
342
  }
228
343
 
229
344
  /**
@@ -238,6 +353,22 @@ export const PluginDesignDoorArgs = z.object({
238
353
  })
239
354
  export type PluginDesignDoorArgs = z.infer<typeof PluginDesignDoorArgs>
240
355
 
356
+ /**
357
+ * A door's dimensions (engine units), symmetric with {@linkcode PluginWindowDimensions}.
358
+ *
359
+ * | Property | Type | Description |
360
+ * |---|---|---|
361
+ * | `width` | `number` | Along the wall |
362
+ * | `height` | `number` | Vertical |
363
+ * | `thickness` | `number` | Through the wall (engine `length` axis) |
364
+ */
365
+ export const PluginDoorDimensions = z.object({
366
+ width: z.number(),
367
+ height: z.number(),
368
+ thickness: z.number(),
369
+ })
370
+ export type PluginDoorDimensions = z.infer<typeof PluginDoorDimensions>
371
+
241
372
  /** Mirror axis — tokens mirror the engine `FlipDirection` verbatim (§6.1). */
242
373
  export const PluginMirrorAxis = z.enum(["x", "y", "z"])
243
374
  export type PluginMirrorAxis = z.infer<typeof PluginMirrorAxis>
@@ -283,3 +414,80 @@ export const PluginDesignDoorSetHeightArgs = z.object({
283
414
  height: z.number(),
284
415
  })
285
416
  export type PluginDesignDoorSetHeightArgs = z.infer<typeof PluginDesignDoorSetHeightArgs>
417
+
418
+ // ---------------------------------------------------------------------------
419
+ // catalog (shared by doors + windows)
420
+ // ---------------------------------------------------------------------------
421
+
422
+ /**
423
+ * A door/window catalog **group** — a placement family (e.g. Swing, Sliding,
424
+ * Casement). Shared by `design.doors` and `design.windows`.
425
+ *
426
+ * | Property | Type | Description |
427
+ * |---|---|---|
428
+ * | `dbType` | `string` | Stable engine token (e.g. `singleDoor`, `casementDouble`) |
429
+ * | `label` | `string` | Human-readable group label (e.g. `"Swing - 1 Shutter"`) |
430
+ * | `source` | `'default' \| 'team'` | Built-in group or a team-custom type |
431
+ */
432
+ export const PluginObjectCatalogGroup = z.object({
433
+ dbType: z.string(),
434
+ label: z.string(),
435
+ source: z.enum(["default", "team"]),
436
+ })
437
+ export type PluginObjectCatalogGroup = z.infer<typeof PluginObjectCatalogGroup>
438
+
439
+ /**
440
+ * A placeable door/window catalog **item** (value record — not a scene
441
+ * entity/handle). Shared by `design.doors` and `design.windows`; symmetric with
442
+ * {@linkcode PluginFurnitureCatalogItem}. The raw mesh (`.babylon`) URL is never
443
+ * exposed — only `thumbnailUrl`.
444
+ *
445
+ * | Property | Type | Description |
446
+ * |---|---|---|
447
+ * | `id` | `string` | Library id (team `_id` or general `fullName`) — pass to `design.create.door`/`window` |
448
+ * | `name` | `string` | Display name |
449
+ * | `source` | `'team' \| 'general'` | Which library it came from |
450
+ * | `dbType` | `string` | The group token it belongs to (e.g. `singleDoor`) |
451
+ * | `subType` | `string`? | Engine sub-type (optional) |
452
+ * | `thumbnailUrl` | `string`? | Preview thumbnail URL (optional) |
453
+ * | `cost` | `number`? | Cost (optional; team items) |
454
+ * | `familyName` | `string`? | Family name (optional) |
455
+ */
456
+ export const PluginObjectCatalogItem = z.object({
457
+ id: z.string(),
458
+ name: z.string(),
459
+ source: z.enum(["team", "general"]),
460
+ dbType: z.string(),
461
+ subType: z.string().optional(),
462
+ thumbnailUrl: z.string().optional(),
463
+ cost: z.number().optional(),
464
+ familyName: z.string().optional(),
465
+ })
466
+ export type PluginObjectCatalogItem = z.infer<typeof PluginObjectCatalogItem>
467
+
468
+ /**
469
+ * Arguments for {@linkcode PluginDesignDoorsApi.listCatalog}.
470
+ *
471
+ * | Property | Type | Description |
472
+ * |---|---|---|
473
+ * | `group` | `string`? | Restrict to one group's `dbType` token (optional) |
474
+ */
475
+ export const PluginDesignDoorListCatalogArgs = z.object({
476
+ group: z.string().optional(),
477
+ })
478
+ export type PluginDesignDoorListCatalogArgs = z.infer<typeof PluginDesignDoorListCatalogArgs>
479
+
480
+ /**
481
+ * Arguments for {@linkcode PluginDesignDoorsApi.getCatalogItem} / {@linkcode PluginDesignDoorsApi.exists}.
482
+ *
483
+ * | Property | Type | Description |
484
+ * |---|---|---|
485
+ * | `id` | `string` | The catalog library id |
486
+ */
487
+ export const PluginDesignDoorGetCatalogItemArgs = z.object({
488
+ id: z.string(),
489
+ })
490
+ export type PluginDesignDoorGetCatalogItemArgs = z.infer<typeof PluginDesignDoorGetCatalogItemArgs>
491
+
492
+ export const PluginDesignDoorExistsArgs = PluginDesignDoorGetCatalogItemArgs
493
+ export type PluginDesignDoorExistsArgs = z.infer<typeof PluginDesignDoorExistsArgs>
@@ -35,7 +35,7 @@ export const ErasableEdgeRef = z.object({
35
35
  strategy: EraseStrategy,
36
36
  /** Bottom-graph edge weight: `1` = external boundary edge, `>=2` = internal edge shared between components. */
37
37
  edgeWeight: z.number(),
38
- /** The components this edge is incident to — one for external (DirectDelete), two for internal (DirectUnion). */
38
+ /** The components this edge is incident to — one for external (DirectDelete); two or more for internal (DirectUnion; 3+ at T-junctions where several masses share the edge). */
39
39
  components: z.array(ComponentHandle),
40
40
  /** A representative point on the edge (its head endpoint). Diagnostic only. */
41
41
  snapPoint: Vec3Handle,
@@ -0,0 +1,388 @@
1
+ import * as z from "zod"
2
+ import { PluginApiReturn } from "../../types"
3
+
4
+ /**
5
+ * `snaptrude.design.family.*` — author NATIVE parametric families from a JSON spec.
6
+ *
7
+ * A parametric family is a reusable, parameter-driven building component: a
8
+ * curtain-wall panel, a louvre screen, a balustrade, a shading fin. Unlike the
9
+ * rest of `design.*`, you do not build one out of geometry calls — you write a
10
+ * JSON **spec** describing its parameters and its geometry rules, and the host
11
+ * compiles that into a live parametric definition the user can place, flex and
12
+ * save to their library.
13
+ *
14
+ * ## Workflow
15
+ *
16
+ * ```
17
+ * getReference() read the spec DSL once — grammar, ops, expressions, example
18
+ * prepare({ bundle }) validate + compile + show a live ghost; nothing committed
19
+ * flex({ values }) re-run at new values; returns the geometry delta
20
+ * place() arm placement — the USER clicks to drop it
21
+ * create({ name }) commit it to the project's family library
22
+ * ```
23
+ *
24
+ * ## Read getReference() first
25
+ *
26
+ * The spec DSL is a closed expression language with its own op set, coordinate
27
+ * convention (**+Y up, integer millimetres** — not Snaptrude's babylon units)
28
+ * and part-naming law. It is deliberately NOT duplicated in these types:
29
+ * {@linkcode PluginDesignFamilyApi.getReference} returns the whole reference,
30
+ * generated from a family that is actually registered in the host, so the
31
+ * documentation cannot drift from what the compiler accepts.
32
+ *
33
+ * ## The repair contract
34
+ *
35
+ * {@linkcode PluginDesignFamilyApi.prepare} does **not** throw on an invalid
36
+ * spec — it returns `{ ok: false, errors }` where each error carries a JSON
37
+ * pointer into the bundle you sent (e.g. `"/root/geometry/2/body/0/w"`). Read
38
+ * `errors[0].path`, fix that node, and call `prepare` again. A failed `prepare`
39
+ * leaves the previous good draft standing, so it costs nothing but the node you
40
+ * broke. Re-authoring from scratch throws away everything already correct.
41
+ *
42
+ * Accessed via `snaptrude.design.family`.
43
+ */
44
+ export abstract class PluginDesignFamilyApi {
45
+ constructor() {}
46
+
47
+ /**
48
+ * Read the family-spec DSL reference: grammar, the op table, the expression
49
+ * whitelist, the coordinate convention, the repair contract, and a worked
50
+ * example taken from a registered family.
51
+ *
52
+ * Read this before authoring a family for the first time in a session. The
53
+ * DSL is a closed language — a guessed op or function is a rejected spec.
54
+ *
55
+ * @returns The reference text
56
+ *
57
+ * @examplePrompt How do I write a parametric family spec?
58
+ * @examplePrompt Show me the family DSL
59
+ *
60
+ * # Example
61
+ * ```ts
62
+ * const { reference } = await snaptrude.design.family.getReference()
63
+ * ```
64
+ */
65
+ public abstract getReference(): PluginApiReturn<PluginFamilyReference>
66
+
67
+ /**
68
+ * Validate, compile and preview a family spec. Nothing is committed to the
69
+ * model or the library — call {@linkcode PluginDesignFamilyApi.place} or
70
+ * {@linkcode PluginDesignFamilyApi.create} for that.
71
+ *
72
+ * Safe to call repeatedly while iterating. On success, check `partCount` and
73
+ * `bbox` against what you intended before treating the family as correct — a
74
+ * facade that built 3 parts, or a 3 m screen reporting 40 m of height, is a
75
+ * visible bug the validator cannot catch.
76
+ *
77
+ * @param args The spec bundle, as `{ bundle }` or `{ spec }`
78
+ * @returns Build report, or `{ ok: false, errors }` with JSON-pointer paths
79
+ *
80
+ * @examplePrompt Make a louvre screen with adjustable blade count and tilt
81
+ * @examplePrompt Design a curtain wall panel family with 8 bays
82
+ * @examplePrompt Create a railing family I can adjust
83
+ *
84
+ * # Example
85
+ * ```ts
86
+ * const r = await snaptrude.design.family.prepare({ bundle })
87
+ * if (!r.ok) console.error(r.errors[0].path, r.errors[0].message)
88
+ * ```
89
+ */
90
+ public abstract prepare(
91
+ args: PluginDesignFamilyPrepareArgs,
92
+ ): PluginApiReturn<PluginFamilyPrepareResult>
93
+
94
+ /**
95
+ * Re-run the prepared family's recipe at new parameter values.
96
+ *
97
+ * Values are merged over the current ones and clamped to the parameter
98
+ * sheet's min/max, so a partial `{ bays: 6 }` is fine. The returned `changed`
99
+ * buckets are the host's own diff of what the geometry did — flexing a
100
+ * driving parameter and reporting them demonstrates the family is parametric.
101
+ *
102
+ * @param args The parameter values to change
103
+ * @returns The new build plus the geometry delta
104
+ *
105
+ * @examplePrompt Change it to 6 bays
106
+ * @examplePrompt Show me what happens with 20 blades
107
+ *
108
+ * # Example
109
+ * ```ts
110
+ * const { changed } = await snaptrude.design.family.flex({ values: { bays: 6 } })
111
+ * console.log(`removed ${changed.removed}, rebuilt ${changed.rebuilt}`)
112
+ * ```
113
+ */
114
+ public abstract flex(
115
+ args: PluginDesignFamilyFlexArgs,
116
+ ): PluginApiReturn<PluginFamilyFlexResult>
117
+
118
+ /**
119
+ * The prepared family's spec bundle, or `null` when nothing is prepared.
120
+ *
121
+ * @returns The draft bundle, or `null`
122
+ *
123
+ * # Example
124
+ * ```ts
125
+ * const bundle = await snaptrude.design.family.get()
126
+ * ```
127
+ */
128
+ public abstract get(): PluginApiReturn<PluginFamilyBundle | null>
129
+
130
+ /**
131
+ * Every parametric family registered in this session — native and imported.
132
+ *
133
+ * @returns One summary per family
134
+ *
135
+ * @examplePrompt What families are available?
136
+ *
137
+ * # Example
138
+ * ```ts
139
+ * const families = await snaptrude.design.family.list()
140
+ * ```
141
+ */
142
+ public abstract list(): PluginApiReturn<PluginFamilySummary[]>
143
+
144
+ /**
145
+ * Arm placement with the prepared family.
146
+ *
147
+ * This does **not** create geometry. It puts the editor into placement mode
148
+ * with a ghost on the cursor; the user clicks in the canvas to drop it, the
149
+ * same as for any other family. Tell the user to click.
150
+ *
151
+ * @returns Which family was armed
152
+ *
153
+ * @examplePrompt Place it in the model
154
+ * @examplePrompt Let me put this on the facade
155
+ *
156
+ * # Example
157
+ * ```ts
158
+ * await snaptrude.design.family.place()
159
+ * ```
160
+ */
161
+ public abstract place(): PluginApiReturn<PluginFamilyPlaceResult>
162
+
163
+ /**
164
+ * Commit the prepared bundle to the project's parametric-definition library,
165
+ * so it survives the session and appears in the Families panel.
166
+ *
167
+ * @param args Optional library name and team scope
168
+ * @returns The catalogued definition's id and version
169
+ *
170
+ * @examplePrompt Save this as a family called Vertical Louvre
171
+ * @examplePrompt Add it to my library
172
+ *
173
+ * # Example
174
+ * ```ts
175
+ * const { definitionId } = await snaptrude.design.family.create({ name: "Louvre Screen" })
176
+ * ```
177
+ */
178
+ public abstract create(
179
+ args?: PluginDesignFamilyCreateArgs,
180
+ ): PluginApiReturn<PluginFamilyCreateResult>
181
+ }
182
+
183
+ // ---------------------------------------------------------------------------
184
+ // Result types
185
+ // ---------------------------------------------------------------------------
186
+
187
+ /** The family-spec DSL reference returned by `getReference`. */
188
+ export interface PluginFamilyReference {
189
+ /** Grammar, op table, expression whitelist, repair contract, worked example. */
190
+ reference: string
191
+ }
192
+
193
+ /**
194
+ * A family spec bundle. `root` is the family; `deps` carries every spec
195
+ * reachable through an `instance` op, keyed by spec id, so a saved family is
196
+ * self-contained and reloads anywhere.
197
+ *
198
+ * The internal shape of a spec is documented by
199
+ * {@linkcode PluginDesignFamilyApi.getReference}, not by these types — it is a
200
+ * data DSL whose grammar changes independently of this API surface.
201
+ */
202
+ export interface PluginFamilyBundle {
203
+ root: Record<string, unknown>
204
+ deps?: Record<string, Record<string, unknown>>
205
+ }
206
+
207
+ /** Axis-aligned extents of a built family. Integer millimetres, +Y up. */
208
+ export interface PluginFamilyBBox {
209
+ min: [number, number, number]
210
+ max: [number, number, number]
211
+ /** `[width, height, depth]` in millimetres. */
212
+ size: [number, number, number]
213
+ }
214
+
215
+ /** One row of a family's parameter sheet. */
216
+ export interface PluginFamilyParameter {
217
+ key: string
218
+ label: string
219
+ type: "length" | "integer" | "enum" | "text" | "boolean"
220
+ /** Current value. `length`/`integer` are numbers; the rest are strings. */
221
+ value: string | number
222
+ default: string | number
223
+ /**
224
+ * Whether this parameter actually drives geometry — INFERRED by the host from
225
+ * which names the spec's expressions reference. `false` means the value is
226
+ * stored and round-tripped but moves nothing.
227
+ */
228
+ drivesGeometry: boolean
229
+ min?: number
230
+ max?: number
231
+ options?: readonly string[]
232
+ }
233
+
234
+ /**
235
+ * A validation diagnostic. `path` is a JSON pointer into the submitted bundle —
236
+ * fix that node and re-`prepare`.
237
+ */
238
+ export interface PluginFamilyDiagnostic {
239
+ /** JSON pointer, e.g. `"/root/geometry/2/body/0/w"`. */
240
+ path: string
241
+ code:
242
+ | "schema"
243
+ | "bad-expression"
244
+ | "unknown-name"
245
+ | "unknown-def"
246
+ | "cycle"
247
+ | "duplicate-name"
248
+ | "unbounded-repeat"
249
+ | "depth"
250
+ | "bad-dimension"
251
+ message: string
252
+ hint?: string
253
+ }
254
+
255
+ /** What `prepare` reports. */
256
+ export interface PluginFamilyPrepareResult {
257
+ /** `false` means the spec was rejected — read `errors`, fix, re-prepare. */
258
+ ok: boolean
259
+ familyId?: string
260
+ version?: number
261
+ label?: string
262
+ /** How many parts the family built. Sanity-check against intent. */
263
+ partCount?: number
264
+ /** Extents in millimetres. Sanity-check against intent. */
265
+ bbox?: PluginFamilyBBox
266
+ evalMs?: number
267
+ parameters?: PluginFamilyParameter[]
268
+ /** Computed quantities, when the spec declares a `takeoff` block. */
269
+ takeoff?: Record<string, number | string>
270
+ /** The first few part names — enough to check the naming came out right. */
271
+ sampleParts?: string[]
272
+ errors: PluginFamilyDiagnostic[]
273
+ warnings: PluginFamilyDiagnostic[]
274
+ /** Set when the bundle validated but threw while building. */
275
+ error?: string
276
+ }
277
+
278
+ /**
279
+ * The host's diff of what a flex did to the geometry. This is the evidence that
280
+ * a family is parametric.
281
+ */
282
+ export interface PluginFamilyChangeBuckets {
283
+ added: number
284
+ removed: number
285
+ moved: number
286
+ rebuilt: number
287
+ addedNames: string[]
288
+ removedNames: string[]
289
+ }
290
+
291
+ /** What `flex` reports. */
292
+ export interface PluginFamilyFlexResult {
293
+ ok: boolean
294
+ /** The RESOLVED values after merge + clamping — not necessarily what was sent. */
295
+ values?: Record<string, number | string>
296
+ partCount?: number
297
+ bbox?: PluginFamilyBBox
298
+ evalMs?: number
299
+ changed?: PluginFamilyChangeBuckets
300
+ takeoff?: Record<string, number | string>
301
+ error?: string
302
+ }
303
+
304
+ /** One registered parametric family. */
305
+ export interface PluginFamilySummary {
306
+ id: string
307
+ version: number
308
+ label: string
309
+ category?: string
310
+ /** Number of rows on the family's parameter sheet. */
311
+ parameters: number
312
+ }
313
+
314
+ /** What `place` reports. */
315
+ export interface PluginFamilyPlaceResult {
316
+ ok: boolean
317
+ familyId?: string
318
+ version?: number
319
+ error?: string
320
+ }
321
+
322
+ /** What `create` reports. */
323
+ export interface PluginFamilyCreateResult {
324
+ ok: boolean
325
+ definitionId?: string
326
+ version?: number
327
+ guid?: string
328
+ error?: string
329
+ }
330
+
331
+ // ---------------------------------------------------------------------------
332
+ // Argument schemas
333
+ // ---------------------------------------------------------------------------
334
+
335
+ /**
336
+ * A spec bundle as it crosses the boundary: `root` plus optional `deps`.
337
+ *
338
+ * Loose by design — the spec grammar is a data DSL validated by the host's own
339
+ * validator, which returns JSON-pointer diagnostics far more useful than a Zod
340
+ * shape error. This schema only guarantees the envelope is an object.
341
+ */
342
+ export const PluginFamilyBundleSchema = z.object({
343
+ root: z.record(z.string(), z.unknown()),
344
+ deps: z.record(z.string(), z.record(z.string(), z.unknown())).optional(),
345
+ })
346
+
347
+ /**
348
+ * Arguments for {@linkcode PluginDesignFamilyApi.prepare}.
349
+ *
350
+ * | Property | Type | Description |
351
+ * |---|---|---|
352
+ * | `bundle` | {@linkcode PluginFamilyBundle} | The family spec bundle |
353
+ * | `spec` | {@linkcode PluginFamilyBundle} | Alias for `bundle` |
354
+ *
355
+ * Exactly one of `bundle` / `spec` is required. A bare `FamilySpec` (no `root`
356
+ * wrapper) is also accepted by the host and wrapped for the caller.
357
+ */
358
+ export const PluginDesignFamilyPrepareArgs = z.object({
359
+ bundle: PluginFamilyBundleSchema.optional(),
360
+ spec: PluginFamilyBundleSchema.optional(),
361
+ })
362
+ export type PluginDesignFamilyPrepareArgs = z.infer<typeof PluginDesignFamilyPrepareArgs>
363
+
364
+ /**
365
+ * Arguments for {@linkcode PluginDesignFamilyApi.flex}.
366
+ *
367
+ * | Property | Type | Description |
368
+ * |---|---|---|
369
+ * | `values` | `Record<string, number \| string>` | Parameter values to change (partial) |
370
+ */
371
+ export const PluginDesignFamilyFlexArgs = z.object({
372
+ values: z.record(z.string(), z.union([z.number(), z.string()])),
373
+ })
374
+ export type PluginDesignFamilyFlexArgs = z.infer<typeof PluginDesignFamilyFlexArgs>
375
+
376
+ /**
377
+ * Arguments for {@linkcode PluginDesignFamilyApi.create}.
378
+ *
379
+ * | Property | Type | Description |
380
+ * |---|---|---|
381
+ * | `name` | `string` | Library name; defaults to the spec's own label |
382
+ * | `teamId` | `string` | Team library to save into; omit for personal |
383
+ */
384
+ export const PluginDesignFamilyCreateArgs = z.object({
385
+ name: z.string().optional(),
386
+ teamId: z.string().optional(),
387
+ })
388
+ export type PluginDesignFamilyCreateArgs = z.infer<typeof PluginDesignFamilyCreateArgs>