@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
@@ -7,8 +7,10 @@ import {
7
7
  PluginDepartmentId,
8
8
  PluginAreaClass,
9
9
  } from "../../entity/space"
10
+ import { PluginStaircasePreset } from "../create"
10
11
  import { PluginDesignQueryGeometryApi } from "./geometry"
11
12
  import { PluginDesignQuerySpacesApi } from "./spaces"
13
+ import { PluginDesignQueryReferenceLinesApi } from "./referenceLines"
12
14
 
13
15
  // ---------------------------------------------------------------------------
14
16
  // Entity type vocabulary
@@ -167,6 +169,7 @@ export type PluginDesignQueryComponentsArgs = z.infer<
167
169
  * | `spaceType` | {@linkcode PluginSpaceType} | Space-type (spaces only) |
168
170
  * | `areaClass` | {@linkcode PluginAreaClass} | Area classification (spaces only) |
169
171
  * | `departmentId` | `string` | Department (masses only) |
172
+ * | `buildingType` | `string` | Assigned building type NAME (walls / slabs / floors / roofs / ceilings only) — the Object Properties panel's Wall/Slab/Floor/Roof/Ceiling Type dropdown value, matching the names from `design.types.list(kind)` |
170
173
  * | `isLocked` | `boolean` | Lock flag |
171
174
  * | `isHidden` | `boolean` | User-hidden flag |
172
175
  * | `isSelected` | `boolean` | Current selection state |
@@ -185,6 +188,7 @@ export const PluginEntityProperties = z
185
188
  spaceType: PluginSpaceType,
186
189
  areaClass: PluginAreaClass,
187
190
  departmentId: z.string(),
191
+ buildingType: z.string(),
188
192
  isLocked: z.boolean(),
189
193
  isHidden: z.boolean(),
190
194
  isSelected: z.boolean(),
@@ -200,21 +204,82 @@ export type PluginEntityProperties = z.infer<typeof PluginEntityProperties>
200
204
  * entity's type computes them (area: floors/masses/slabs/roofs/beams/columns;
201
205
  * volume: all; length: walls/beams).
202
206
  *
207
+ * `thickness` and `height` are the Object Properties panel's dimension reads
208
+ * (the same engine source `design.update.wall` / `design.update.slab` etc.
209
+ * write through), in engine units. `thickness`: walls (the plan thickness —
210
+ * present only for straight orthogonal walls, exactly when the panel shows it)
211
+ * and slabs/floors/roofs/ceilings (the vertical extent the panel calls
212
+ * Thickness). `height`: walls only.
213
+ *
203
214
  * | Property | Type | Description |
204
215
  * |---|---|---|
205
216
  * | `area` | `number`? | Bottom-face area |
206
217
  * | `volume` | `number`? | Solid volume |
207
218
  * | `length` | `number`? | Running length (linear elements) |
219
+ * | `thickness` | `number`? | Panel Thickness — walls (straight orthogonal only) and the slab family |
220
+ * | `height` | `number`? | Panel Height — walls only |
208
221
  * | `boundingBox` | {@linkcode BBoxComponents} | World-space AABB (always present) |
209
222
  */
210
223
  export const PluginEntityMeasurements = z.object({
211
224
  area: z.number().optional(),
212
225
  volume: z.number().optional(),
213
226
  length: z.number().optional(),
227
+ thickness: z.number().optional(),
228
+ height: z.number().optional(),
214
229
  boundingBox: BBoxComponents,
215
230
  })
216
231
  export type PluginEntityMeasurements = z.infer<typeof PluginEntityMeasurements>
217
232
 
233
+ /**
234
+ * The parametric properties of a staircase — the same editable fields the
235
+ * staircase properties panel exposes. Returned by
236
+ * {@linkcode PluginDesignQueryApi.getStaircaseParams}; the write side is
237
+ * `design.update.staircase`. Engine internals (block schema, min well size,
238
+ * square-well padding) are deliberately excluded.
239
+ *
240
+ * All dimensions are in **engine units** (the same convention as
241
+ * `design.doors.getWidth`); `steps` is a unitless count. The invariant
242
+ * `staircaseHeight = steps × riser` is governed by the two lock booleans — see
243
+ * `design.update.staircase`. Type-specific fields (`wellSize`, `landingWidth`,
244
+ * `flightStartDistAfterTurn`) are present only when the preset uses them.
245
+ *
246
+ * | Property | Type | Description |
247
+ * |---|---|---|
248
+ * | `staircaseType` | {@linkcode PluginStaircasePreset} | Base staircase type |
249
+ * | `staircasePreset` | {@linkcode PluginStaircasePreset} | Named parametric preset |
250
+ * | `staircaseHeight` | `number` | Total rise (engine units) |
251
+ * | `steps` | `number` | Step count (unitless) |
252
+ * | `riser` | `number` | Riser height (engine units) |
253
+ * | `tread` | `number` | Tread depth (engine units) |
254
+ * | `width` | `number` | Flight width (engine units) |
255
+ * | `depth` | `number` | Slab/waist thickness (engine units) |
256
+ * | `wellSize` | `number`? | Stairwell gap (dogLegged / openWell / square) |
257
+ * | `landingWidth` | `number`? | Landing depth (straight / dogLegged) |
258
+ * | `flightStartDistAfterTurn` | `number`? | Flight offset after a turn (dogLegged / lShaped / square) |
259
+ * | `baseOffset` | `number` | Vertical offset from the storey base (engine units) |
260
+ * | `isStoreyHeightUnlocked` | `boolean` | When `false`, height is tied to the storey height |
261
+ * | `isStaircaseHeightUnlocked` | `boolean` | When `false`, height recomputes from steps × riser |
262
+ * | `storey` | `number` | Storey/floor level the staircase sits on |
263
+ */
264
+ export const PluginStaircaseParams = z.object({
265
+ staircaseType: PluginStaircasePreset,
266
+ staircasePreset: PluginStaircasePreset,
267
+ staircaseHeight: z.number(),
268
+ steps: z.number(),
269
+ riser: z.number(),
270
+ tread: z.number(),
271
+ width: z.number(),
272
+ depth: z.number(),
273
+ wellSize: z.number().optional(),
274
+ landingWidth: z.number().optional(),
275
+ flightStartDistAfterTurn: z.number().optional(),
276
+ baseOffset: z.number(),
277
+ isStoreyHeightUnlocked: z.boolean(),
278
+ isStaircaseHeightUnlocked: z.boolean(),
279
+ storey: z.number(),
280
+ })
281
+ export type PluginStaircaseParams = z.infer<typeof PluginStaircaseParams>
282
+
218
283
  // ---------------------------------------------------------------------------
219
284
  // The query API
220
285
  // ---------------------------------------------------------------------------
@@ -237,6 +302,8 @@ export abstract class PluginDesignQueryApi {
237
302
  public abstract geometry: PluginDesignQueryGeometryApi
238
303
  /** Space-specific reads (footprint, …). See {@linkcode PluginDesignQuerySpacesApi}. */
239
304
  public abstract spaces: PluginDesignQuerySpacesApi
305
+ /** Reference-line-specific reads. See {@linkcode PluginDesignQueryReferenceLinesApi}. */
306
+ public abstract referenceLines: PluginDesignQueryReferenceLinesApi
240
307
 
241
308
  constructor() {}
242
309
 
@@ -704,9 +771,7 @@ export abstract class PluginDesignQueryApi {
704
771
  * }
705
772
  * ```
706
773
  */
707
- public abstract exists(
708
- id: ComponentHandle,
709
- ): PluginApiReturn<boolean>
774
+ public abstract exists(id: ComponentHandle): PluginApiReturn<boolean>
710
775
 
711
776
  /**
712
777
  * Get the kind of BIM entity a handle refers to — wall, door, space, slab,
@@ -760,9 +825,7 @@ export abstract class PluginDesignQueryApi {
760
825
  * }
761
826
  * ```
762
827
  */
763
- public abstract getLabel(
764
- component: ComponentHandle,
765
- ): PluginApiReturn<string>
828
+ public abstract getLabel(component: ComponentHandle): PluginApiReturn<string>
766
829
 
767
830
  /**
768
831
  * Get a serializable, persistable reference to an entity. Under the all-handle
@@ -846,13 +909,16 @@ export abstract class PluginDesignQueryApi {
846
909
  * Read an entity's common properties as a {@linkcode PluginEntityProperties}
847
910
  * record (only applicable fields are present). For spaces/masses this includes
848
911
  * `adjacency` — the per-entity adjacency read (bulk form:
849
- * `program.adjacency.getMatrix`).
912
+ * `program.adjacency.getMatrix`). For walls and the slab family (slab / floor
913
+ * / roof / ceiling) this includes `buildingType` — the assigned building type
914
+ * NAME the panel's type dropdown shows, matching `design.types.list(kind)`.
850
915
  *
851
916
  * @param component - the entity's {@linkcode ComponentHandle}
852
917
  * @returns the property record
853
918
  *
854
919
  * @examplePrompt Show me all the details of the selected object
855
920
  * @examplePrompt What storey is this wall on?
921
+ * @examplePrompt Which wall type is assigned to this wall?
856
922
  * @examplePrompt Which department does this room belong to?
857
923
  * @examplePrompt Is this element locked or hidden?
858
924
  * @examplePrompt Get the adjacencies of this space
@@ -882,11 +948,20 @@ export abstract class PluginDesignQueryApi {
882
948
  * Measure an entity — read its area, volume, and/or running length (whichever
883
949
  * its type supports) together with its world-space bounding box in one call.
884
950
  *
951
+ * For walls and the slab family this also carries the Object Properties
952
+ * panel's dimension reads in engine units: `thickness` (walls — straight
953
+ * orthogonal only, exactly when the panel shows it; slabs / floors / roofs /
954
+ * ceilings — the vertical extent the panel calls Thickness) and `height`
955
+ * (walls). These are the read side of `design.update.wall` /
956
+ * `design.update.slab` / `.floor` / `.roof` / `.ceiling`.
957
+ *
885
958
  * @param component - the entity's {@linkcode ComponentHandle}
886
959
  * @returns a {@linkcode PluginEntityMeasurements}, or `null` if the entity is gone
887
960
  *
888
961
  * @examplePrompt What's the area of this room?
889
962
  * @examplePrompt How long is the selected wall?
963
+ * @examplePrompt How thick is this wall?
964
+ * @examplePrompt Get the thickness of the slab on level 2
890
965
  * @examplePrompt Get the volume of this mass
891
966
  * @examplePrompt Give me the dimensions of the selected element
892
967
  * @examplePrompt What's the floor area of the kitchen?
@@ -896,12 +971,47 @@ export abstract class PluginDesignQueryApi {
896
971
  * const [space] = await snaptrude.design.query.listSpaces()
897
972
  * const m = await snaptrude.design.query.measure(space)
898
973
  * console.log(m?.area, m?.volume)
974
+ *
975
+ * // wall dimensions — the panel's Thickness / Height, in engine units
976
+ * const [wall] = await snaptrude.design.query.listWalls()
977
+ * const wm = await snaptrude.design.query.measure(wall)
978
+ * console.log(wm?.thickness, wm?.height)
899
979
  * ```
900
980
  */
901
981
  public abstract measure(
902
982
  component: ComponentHandle,
903
983
  ): PluginApiReturn<PluginEntityMeasurements | null>
904
984
 
985
+ /**
986
+ * Read a staircase's parametric properties — its preset, dimensions, step
987
+ * count, base offset, and height-lock state — as a
988
+ * {@linkcode PluginStaircaseParams} record. The paired write is
989
+ * `design.update.staircase`.
990
+ *
991
+ * All dimensions are in **engine units** (the same convention as
992
+ * `design.doors.getWidth`); `steps` is a unitless count. A read — never
993
+ * throws: returns `null` for a handle that is not a staircase or no longer
994
+ * resolves.
995
+ *
996
+ * @param staircase - the staircase's {@linkcode ComponentHandle}
997
+ * @returns the {@linkcode PluginStaircaseParams} record, or `null`
998
+ *
999
+ * @examplePrompt What's the riser height of this staircase?
1000
+ * @examplePrompt How many steps does the selected stair have?
1001
+ * @examplePrompt Show me the tread and width of this staircase
1002
+ * @examplePrompt Read all the parameters of the stairs on level 1
1003
+ *
1004
+ * # Example
1005
+ * ```ts
1006
+ * const [stair] = await snaptrude.design.query.listStaircases({ isSelected: true })
1007
+ * const params = stair ? await snaptrude.design.query.getStaircaseParams(stair) : null
1008
+ * if (params) console.log(params.steps, params.riser, params.tread)
1009
+ * ```
1010
+ */
1011
+ public abstract getStaircaseParams(
1012
+ staircase: ComponentHandle,
1013
+ ): PluginApiReturn<PluginStaircaseParams | null>
1014
+
905
1015
  /**
906
1016
  * Get the **union** axis-aligned bounding box enclosing a set of entities.
907
1017
  *
@@ -931,7 +1041,107 @@ export abstract class PluginDesignQueryApi {
931
1041
  public abstract getBoundingBox(
932
1042
  components: ComponentHandle[],
933
1043
  ): PluginApiReturn<BBoxComponents | null>
1044
+
1045
+ /**
1046
+ * Compute the plan **outline of what is built on a storey** — the union of
1047
+ * the storey's wall (by default) footprints as polygons-with-holes. This is
1048
+ * the footprint-from-the-built-model query: by the time slabs are needed the
1049
+ * walls exist, and the engine's own solids (snapped, joined and mitred at
1050
+ * creation) are the cleanest wall network available — no CAD re-tracing.
1051
+ *
1052
+ * Semantics: the union of wall solids is the wall *material* — a ring-shaped
1053
+ * polygon. The **outer ring is the OUTSIDE wall face** (the slab boundary);
1054
+ * every enclosed region — rooms AND courtyards — appears as a hole. When the
1055
+ * largest hole is ~zero the walls don't enclose anything: the footprint
1056
+ * reports `enclosed: false` (an open wall network), never a garbage outline.
1057
+ * Detached buildings come back as separate footprints.
1058
+ *
1059
+ * A read — nothing is created. Feed a footprint's `outline` to
1060
+ * `core.geom.create.profileFromLinePoints` → `design.create.slab` to build
1061
+ * the floor/roof plate. Coordinates are world-space plan (x, z) in Snaptrude
1062
+ * internal units; `areaSq` values are in squared internal units.
1063
+ *
1064
+ * @param options - storey / included kinds / debris filter; see
1065
+ * {@linkcode PluginStoreyOutlineOptions}.
1066
+ * @returns the storey's footprints (possibly several for detached buildings)
1067
+ * plus pieces discarded by `minArea` (`footprints: []` when nothing matches).
1068
+ *
1069
+ * @examplePrompt What is the building footprint on the ground floor?
1070
+ * @examplePrompt Create a floor slab covering the whole storey
1071
+ * @examplePrompt Get the outline of the walls on storey 1
1072
+ * @examplePrompt How much area do the ground-floor walls enclose?
1073
+ *
1074
+ * # Example
1075
+ * ```ts
1076
+ * const r = await snaptrude.design.query.storeyOutline({ storey: 1 })
1077
+ * const main = r.footprints.find((f) => f.enclosed)
1078
+ * if (main) {
1079
+ * const profile = await snaptrude.core.geom.create.profileFromLinePoints(
1080
+ * main.outline.map((p) => ({ x: p.x, y: 0, z: p.z })),
1081
+ * )
1082
+ * // → design.create.slab with the profile's contour
1083
+ * }
1084
+ * ```
1085
+ */
1086
+ public abstract storeyOutline(
1087
+ options?: PluginStoreyOutlineOptions,
1088
+ ): PluginApiReturn<PluginStoreyOutlineResult>
934
1089
  }
935
1090
 
1091
+
1092
+ /** A point in the world-space plan (x, z) projection, Snaptrude internal units. */
1093
+ export const PluginOutlinePlanPoint = z.object({ x: z.number(), z: z.number() })
1094
+ export type PluginOutlinePlanPoint = z.infer<typeof PluginOutlinePlanPoint>
1095
+
1096
+ /**
1097
+ * Options for {@link PluginDesignQueryApi.storeyOutline}.
1098
+ *
1099
+ * | Property | Type | Description |
1100
+ * |---|---|---|
1101
+ * | `storey` | `number`? | Only components on this storey (default: all storeys) |
1102
+ * | `include` | {@link PluginEntityType}`[]`? | Component kinds to union (default `["wall"]`) |
1103
+ * | `minArea` | `number`? | Drop union pieces below this plan area, squared internal units (default `1`) |
1104
+ */
1105
+ export const PluginStoreyOutlineOptions = z.object({
1106
+ storey: z.number().int().optional(),
1107
+ include: z.array(PluginEntityType).nonempty().default(["wall"]),
1108
+ minArea: z.number().nonnegative().default(1),
1109
+ })
1110
+ export type PluginStoreyOutlineOptions = z.input<
1111
+ typeof PluginStoreyOutlineOptions
1112
+ >
1113
+
1114
+ /**
1115
+ * One connected footprint from {@link PluginDesignQueryApi.storeyOutline}.
1116
+ * `outline` traces the OUTSIDE wall face; `holes` are the enclosed regions
1117
+ * (rooms and courtyards). `enclosed` is `false` when the wall network does not
1118
+ * close around any region (open C-shape) — don't slab an unenclosed outline
1119
+ * without checking it.
1120
+ */
1121
+ export const PluginStoreyFootprint = z.object({
1122
+ outline: z.array(PluginOutlinePlanPoint),
1123
+ holes: z.array(
1124
+ z.object({
1125
+ ring: z.array(PluginOutlinePlanPoint),
1126
+ areaSq: z.number().nonnegative(),
1127
+ }),
1128
+ ),
1129
+ areaSq: z.number().nonnegative(),
1130
+ enclosed: z.boolean(),
1131
+ })
1132
+ export type PluginStoreyFootprint = z.infer<typeof PluginStoreyFootprint>
1133
+
1134
+ /** Result of {@link PluginDesignQueryApi.storeyOutline}. */
1135
+ export const PluginStoreyOutlineResult = z.object({
1136
+ footprints: z.array(PluginStoreyFootprint),
1137
+ discarded: z.array(
1138
+ z.object({ areaSq: z.number().nonnegative(), reason: z.string() }),
1139
+ ),
1140
+ })
1141
+ export type PluginStoreyOutlineResult = z.infer<
1142
+ typeof PluginStoreyOutlineResult
1143
+ >
1144
+
936
1145
  export * from "./geometry"
937
1146
  export * from "./spaces"
1147
+ export * from "./referenceLines"
@@ -0,0 +1,52 @@
1
+ import * as z from "zod"
2
+ import { PluginApiReturn } from "../../../types"
3
+ import {
4
+ PluginReferenceLineGetProperty,
5
+ PluginReferenceLineGetResult,
6
+ } from "../../entity/referenceLine"
7
+
8
+ /**
9
+ * `design.query.referenceLines.*` — reference-line-specific reads.
10
+ *
11
+ * A **reference line** is a 2D guide line (or arc) in the scene, typically used
12
+ * for grid lines and alignment guides. Enumerate reference lines with
13
+ * `design.query.listReferenceLines`; read an individual line's properties here.
14
+ * This is the canonical home of the read formerly at `entity.referenceLine.get`
15
+ * (now deprecated).
16
+ *
17
+ * Accessed via `snaptrude.design.query.referenceLines`.
18
+ */
19
+ export abstract class PluginDesignQueryReferenceLinesApi {
20
+ constructor() {}
21
+
22
+ /**
23
+ * Get properties of a reference line by its ID.
24
+ *
25
+ * Only the properties listed in `properties` are returned — unlisted
26
+ * properties will be `undefined` in the result.
27
+ *
28
+ * @param referenceLineId - The unique reference line ID
29
+ * @param properties - Array of property names to retrieve. See
30
+ * {@linkcode PluginReferenceLineGetProperty}.
31
+ * @returns A partial {@linkcode PluginReferenceLineGetResult} containing only the
32
+ * requested properties
33
+ * @throws If the reference line does not exist
34
+ *
35
+ * @examplePrompt Get the curve geometry of this reference line
36
+ * @examplePrompt Where does this grid line run?
37
+ * @examplePrompt Read the geometry of a guide line by its id
38
+ * @examplePrompt Look up the curve of the selected reference line
39
+ *
40
+ * # Example
41
+ * ```ts
42
+ * const [refLine] = await snaptrude.design.query.listReferenceLines()
43
+ * const result = await snaptrude.design.query.referenceLines.get(refLine, ["curve"])
44
+ * // result.curve is an opaque CurveHandle; read its coordinates via
45
+ * // `snaptrude.core.geom.curve`.
46
+ * ```
47
+ */
48
+ public abstract get(
49
+ referenceLineId: string,
50
+ properties: z.infer<typeof PluginReferenceLineGetProperty>[],
51
+ ): PluginApiReturn<PluginReferenceLineGetResult>
52
+ }
@@ -75,6 +75,55 @@ export abstract class PluginDesignQuerySpacesApi {
75
75
  public abstract getFootprint(
76
76
  space: ComponentHandle,
77
77
  ): PluginApiReturn<ContourHandle | null>
78
+
79
+ /**
80
+ * Get the **enclosure** of a space — the floor and ceiling that cap it, the
81
+ * bounding walls around it (with each wall's door/window openings), and the
82
+ * neighbouring spaces it shares a boundary with.
83
+ *
84
+ * **Geometric v1 — best effort.** The engine has no persisted space→surface
85
+ * topology, so this read is derived purely from *plan geometry*: the space
86
+ * footprint is matched against the plan footprints/baselines of the slabs and
87
+ * walls on the same storey. Consequences a caller must account for:
88
+ *
89
+ * - `faceIndices` is **always `[]`**. The triangle→B-rep-face provenance that
90
+ * would populate it is a separate engine capability that does not exist yet;
91
+ * read a surface's full geometry via
92
+ * {@linkcode PluginDesignQueryGeometryApi.getBrep} on its `component` instead.
93
+ * - After heavy edits (move / stretch / boolean) or on imported models the plan
94
+ * match can miss or mis-attribute a surface — treat the result as advisory,
95
+ * not authoritative.
96
+ * - `adjacentSpaces` comes from the same adjacency data as
97
+ * {@linkcode PluginProgramAdjacencyApi.getMatrix} (so the two agree on which
98
+ * spaces neighbour). `sharedEdgeLength` is `0` because that data exposes no
99
+ * edge length, and `sharedSurface` is the common boundary wall only when one
100
+ * is found geometrically (else `null`).
101
+ *
102
+ * Read-only; never mutates the model.
103
+ *
104
+ * @param space - The space (room mass) to read
105
+ * @returns the {@linkcode PluginSpaceEnclosure}, or `null` when `space` is not a
106
+ * space (room mass) or has no extractable plan footprint
107
+ *
108
+ * @examplePrompt What walls, floor and ceiling enclose this room?
109
+ * @examplePrompt Which of this room's walls are external?
110
+ * @examplePrompt List the doors and windows around the selected space
111
+ * @examplePrompt Which spaces are adjacent to this room, and through which wall?
112
+ *
113
+ * # Example
114
+ * ```ts
115
+ * const [space] = await snaptrude.design.query.listSpaces()
116
+ * const enclosure = await snaptrude.design.query.spaces.getEnclosure(space)
117
+ * if (enclosure) {
118
+ * const walls = enclosure.surfaces.filter((s) => s.role === "wall")
119
+ * const external = walls.filter((s) => s.isExternal)
120
+ * console.log(`${walls.length} walls, ${external.length} external`)
121
+ * }
122
+ * ```
123
+ */
124
+ public abstract getEnclosure(
125
+ space: ComponentHandle,
126
+ ): PluginApiReturn<PluginSpaceEnclosure | null>
78
127
  }
79
128
 
80
129
  /**
@@ -130,3 +179,97 @@ export const PluginDesignQuerySpacesGetResult = PluginSpaceGetResult.omit({
130
179
  export type PluginDesignQuerySpacesGetResult = z.infer<
131
180
  typeof PluginDesignQuerySpacesGetResult
132
181
  >
182
+
183
+ /**
184
+ * Arguments for {@linkcode PluginDesignQuerySpacesApi.getEnclosure}.
185
+ *
186
+ * | Property | Type | Description |
187
+ * |---|---|---|
188
+ * | `space` | {@linkcode ComponentHandle} | The space (room mass) to read |
189
+ */
190
+ export const PluginDesignQuerySpacesGetEnclosureArgs = z.object({
191
+ space: ComponentHandle,
192
+ })
193
+ export type PluginDesignQuerySpacesGetEnclosureArgs = z.infer<
194
+ typeof PluginDesignQuerySpacesGetEnclosureArgs
195
+ >
196
+
197
+ /**
198
+ * The role a bounding surface plays in a {@linkcode PluginSpaceEnclosure}:
199
+ * `"floor"` (slab below), `"ceiling"` (slab/roof above), or `"wall"`.
200
+ */
201
+ export const PluginEnclosureRole = z.enum(["floor", "ceiling", "wall"])
202
+ export type PluginEnclosureRole = z.infer<typeof PluginEnclosureRole>
203
+
204
+ /** The kind of opening hosted in a bounding wall. */
205
+ export const PluginEnclosureOpeningKind = z.enum(["door", "window", "void"])
206
+ export type PluginEnclosureOpeningKind = z.infer<
207
+ typeof PluginEnclosureOpeningKind
208
+ >
209
+
210
+ /**
211
+ * One opening (door / window / void) in a bounding wall.
212
+ *
213
+ * | Property | Type | Description |
214
+ * |---|---|---|
215
+ * | `kind` | {@linkcode PluginEnclosureOpeningKind} | `"door"`, `"window"`, or `"void"` |
216
+ * | `component` | {@linkcode ComponentHandle} | The opening entity |
217
+ */
218
+ export const PluginEnclosureOpening = z.object({
219
+ kind: PluginEnclosureOpeningKind,
220
+ component: ComponentHandle,
221
+ })
222
+ export type PluginEnclosureOpening = z.infer<typeof PluginEnclosureOpening>
223
+
224
+ /**
225
+ * One bounding surface of a space's enclosure.
226
+ *
227
+ * | Property | Type | Description |
228
+ * |---|---|---|
229
+ * | `role` | {@linkcode PluginEnclosureRole} | `"floor"`, `"ceiling"`, or `"wall"` |
230
+ * | `component` | {@linkcode ComponentHandle} | The wall / slab / roof entity |
231
+ * | `faceIndices` | `number[]` | B-rep face indices facing the space. **Always `[]` in geometric v1** (no triangle→face provenance yet) |
232
+ * | `isExternal` | `boolean` | No space on the other side — for a wall, no other room bounds it; for a floor/ceiling, no room caps the storey below/above |
233
+ * | `openings` | {@linkcode PluginEnclosureOpening}`[]` | Doors/windows/voids in the surface (walls only; `[]` for floor/ceiling in v1) |
234
+ */
235
+ export const PluginEnclosureSurface = z.object({
236
+ role: PluginEnclosureRole,
237
+ component: ComponentHandle,
238
+ faceIndices: z.array(z.number()),
239
+ isExternal: z.boolean(),
240
+ openings: z.array(PluginEnclosureOpening),
241
+ })
242
+ export type PluginEnclosureSurface = z.infer<typeof PluginEnclosureSurface>
243
+
244
+ /**
245
+ * A space that neighbours the queried space.
246
+ *
247
+ * | Property | Type | Description |
248
+ * |---|---|---|
249
+ * | `space` | {@linkcode ComponentHandle} | The neighbouring space |
250
+ * | `sharedSurface` | {@linkcode ComponentHandle}` \| null` | The common boundary wall when one is found geometrically, else `null` (e.g. an open-plan or non-physical adjacency) |
251
+ * | `sharedEdgeLength` | `number` | Plan-unit length of the shared boundary — **always `0` in geometric v1** (the adjacency data exposes no edge length) |
252
+ */
253
+ export const PluginAdjacentSpace = z.object({
254
+ space: ComponentHandle,
255
+ sharedSurface: ComponentHandle.nullable(),
256
+ sharedEdgeLength: z.number(),
257
+ })
258
+ export type PluginAdjacentSpace = z.infer<typeof PluginAdjacentSpace>
259
+
260
+ /**
261
+ * Result of {@linkcode PluginDesignQuerySpacesApi.getEnclosure} — the geometric
262
+ * v1 enclosure of a space.
263
+ *
264
+ * | Property | Type | Description |
265
+ * |---|---|---|
266
+ * | `space` | {@linkcode ComponentHandle} | The queried space |
267
+ * | `surfaces` | {@linkcode PluginEnclosureSurface}`[]` | Floor + ceiling + bounding walls, as a flat list (any of the three may be absent when no plan match is found) |
268
+ * | `adjacentSpaces` | {@linkcode PluginAdjacentSpace}`[]` | Neighbouring spaces from the adjacency data (`[]` when adjacency was never computed) |
269
+ */
270
+ export const PluginSpaceEnclosure = z.object({
271
+ space: ComponentHandle,
272
+ surfaces: z.array(PluginEnclosureSurface),
273
+ adjacentSpaces: z.array(PluginAdjacentSpace),
274
+ })
275
+ export type PluginSpaceEnclosure = z.infer<typeof PluginSpaceEnclosure>
@@ -3,6 +3,91 @@ import { PluginApiReturn } from "../../../types"
3
3
  import { ComponentHandle } from "../../../handles"
4
4
  import { PluginDesignChangeResult } from "../lock"
5
5
 
6
+ /**
7
+ * The entity kinds {@linkcode PluginDesignSelectionApi.setByFilter} can select —
8
+ * the union of the editor's "Filter selection" menus (Design tab + BIM tab).
9
+ * Tokens mirror {@linkcode PluginEntityType} where a kind exists on the query
10
+ * surface; the rest are selection-only kinds the query surface cannot reach
11
+ * (layer-backed and annotation objects).
12
+ *
13
+ * | Value | Entity (menu label) |
14
+ * |---|---|
15
+ * | `"wall"` | Wall |
16
+ * | `"slab"` | Floor slab |
17
+ * | `"floor"` | Flooring |
18
+ * | `"door"` | Door |
19
+ * | `"window"` | Window |
20
+ * | `"beam"` | Beam |
21
+ * | `"column"` | Column |
22
+ * | `"ceiling"` | Ceiling |
23
+ * | `"roof"` | Roof |
24
+ * | `"staircase"` | Staircase |
25
+ * | `"furniture"` | Furniture |
26
+ * | `"pergola"` | Pergola |
27
+ * | `"sunshade"` | Sunshade |
28
+ * | `"referenceLine"` | Reference line |
29
+ * | `"space"` | Space (room mass) |
30
+ * | `"site"` | Site |
31
+ * | `"terrain"` | Terrain |
32
+ * | `"cad"` | CAD import |
33
+ * | `"pdf"` | PDF import |
34
+ * | `"image"` | Image import |
35
+ * | `"dimensionLine"` | Dimension line |
36
+ * | `"model3d"` | Imported 3D model |
37
+ * | `"neighborhoodBuilding"` | Neighborhood building |
38
+ * | `"programBlock"` | Program block (department mass) |
39
+ */
40
+ export const PluginSelectionEntityType = z.enum([
41
+ "wall",
42
+ "slab",
43
+ "floor",
44
+ "door",
45
+ "window",
46
+ "beam",
47
+ "column",
48
+ "ceiling",
49
+ "roof",
50
+ "staircase",
51
+ "furniture",
52
+ "pergola",
53
+ "sunshade",
54
+ "referenceLine",
55
+ "space",
56
+ "site",
57
+ "terrain",
58
+ "cad",
59
+ "pdf",
60
+ "image",
61
+ "dimensionLine",
62
+ "model3d",
63
+ "neighborhoodBuilding",
64
+ "programBlock",
65
+ ])
66
+ export type PluginSelectionEntityType = z.infer<typeof PluginSelectionEntityType>
67
+
68
+ /**
69
+ * The filter for {@linkcode PluginDesignSelectionApi.setByFilter}. At least one
70
+ * field must be non-empty (to empty the selection use
71
+ * {@linkcode PluginDesignSelectionApi.clear}). Fields **AND**-combine; values
72
+ * within a field **OR**-combine — `{ storeys: [2], types: ["furniture"] }`
73
+ * selects only the furniture on storey 2.
74
+ *
75
+ * | Field | Type | Selects entities where |
76
+ * |---|---|---|
77
+ * | `storeys` | `number[]` | storey/floor level ∈ `storeys` (negative = basement) |
78
+ * | `types` | {@linkcode PluginSelectionEntityType}`[]` | entity kind ∈ `types` |
79
+ */
80
+ export const PluginSelectionFilter = z
81
+ .object({
82
+ storeys: z.array(z.number()).optional(),
83
+ types: z.array(PluginSelectionEntityType).optional(),
84
+ })
85
+ .refine((f) => (f.storeys?.length ?? 0) + (f.types?.length ?? 0) > 0, {
86
+ message:
87
+ "PluginSelectionFilter requires at least one non-empty field (storeys or types); use design.selection.clear() to deselect everything",
88
+ })
89
+ export type PluginSelectionFilter = z.infer<typeof PluginSelectionFilter>
90
+
6
91
  /**
7
92
  * `snaptrude.design.selection` — read and mutate the current scene selection.
8
93
  *
@@ -123,6 +208,50 @@ export abstract class PluginDesignSelectionApi {
123
208
  * ```
124
209
  */
125
210
  public abstract clear(): PluginApiReturn<PluginDesignChangeResult>
211
+
212
+ /**
213
+ * Replace the selection with every visible entity of the active building that
214
+ * matches the filter — the programmatic equivalent of the editor's
215
+ * **Story selection** (`storeys`) and **Filter selection** (`types`) menus
216
+ * under the select tool, including their combination (fields AND, values
217
+ * within a field OR). Reaches selection-only kinds the query surface cannot
218
+ * (sites, terrain, CAD/PDF/image imports, dimension lines, 3D models,
219
+ * neighborhood buildings) and selects whole groups the way the editor does.
220
+ *
221
+ * This is a one-shot bulk select, not a persistent mode: the user's next
222
+ * canvas click resets the underlying filter state (the selection it made
223
+ * stays until changed). Results honor the ambient view — active building,
224
+ * hidden objects/buckets, and in 2D the active storey.
225
+ *
226
+ * @param filter Which entities to select — {@linkcode PluginSelectionFilter};
227
+ * at least one field must be non-empty
228
+ * @returns The resulting selection snapshot
229
+ *
230
+ * @examplePrompt Select everything on storey 2
231
+ * @examplePrompt Select all the walls and doors
232
+ * @examplePrompt Story selection: select the ground floor and first floor
233
+ * @examplePrompt Filter selection: select all furniture in the model
234
+ * @examplePrompt Select only the furniture on the second storey
235
+ * @examplePrompt Select all the spaces so I can recolor them
236
+ *
237
+ * # Example
238
+ * ```ts
239
+ * // "Story selection": everything on storeys 1 and 2
240
+ * await snaptrude.design.selection.setByFilter({ storeys: [1, 2] })
241
+ *
242
+ * // "Filter selection": all walls and doors
243
+ * await snaptrude.design.selection.setByFilter({ types: ["wall", "door"] })
244
+ *
245
+ * // Combined: only the furniture on storey 2
246
+ * const { affected } = await snaptrude.design.selection.setByFilter({
247
+ * storeys: [2],
248
+ * types: ["furniture"],
249
+ * })
250
+ * ```
251
+ */
252
+ public abstract setByFilter(
253
+ filter: PluginSelectionFilter
254
+ ): PluginApiReturn<PluginDesignChangeResult>
126
255
  }
127
256
 
128
257
  /**