@snaptrude/plugin-core 0.10.0 → 0.11.0-rc.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 (49) hide show
  1. package/CHANGELOG.md +24 -2
  2. package/api-manifest.full.json +6928 -0
  3. package/api-manifest.json +85 -4
  4. package/dist/api/core/camera/index.d.ts +16 -0
  5. package/dist/api/core/camera/index.d.ts.map +1 -1
  6. package/dist/api/core/project/index.d.ts +68 -1
  7. package/dist/api/core/project/index.d.ts.map +1 -1
  8. package/dist/api/core/storeys/index.d.ts +14 -0
  9. package/dist/api/core/storeys/index.d.ts.map +1 -1
  10. package/dist/api/design/create/index.d.ts +29 -6
  11. package/dist/api/design/create/index.d.ts.map +1 -1
  12. package/dist/api/design/delete/index.d.ts +20 -2
  13. package/dist/api/design/delete/index.d.ts.map +1 -1
  14. package/dist/api/design/furniture/index.d.ts +33 -0
  15. package/dist/api/design/furniture/index.d.ts.map +1 -1
  16. package/dist/api/design/query/index.d.ts +2 -0
  17. package/dist/api/design/query/index.d.ts.map +1 -1
  18. package/dist/api/design/query/spaces.d.ts +61 -0
  19. package/dist/api/design/query/spaces.d.ts.map +1 -1
  20. package/dist/api/design/visibility.d.ts +28 -0
  21. package/dist/api/design/visibility.d.ts.map +1 -1
  22. package/dist/api/entity/space.d.ts +3 -0
  23. package/dist/api/entity/space.d.ts.map +1 -1
  24. package/dist/api/presentation/placedViews.d.ts +35 -1
  25. package/dist/api/presentation/placedViews.d.ts.map +1 -1
  26. package/dist/api/presentation/sheets.d.ts +20 -2
  27. package/dist/api/presentation/sheets.d.ts.map +1 -1
  28. package/dist/errors/codes.d.ts +8 -3
  29. package/dist/errors/codes.d.ts.map +1 -1
  30. package/dist/index.cjs +69 -3
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.js +62 -3
  33. package/dist/index.js.map +1 -1
  34. package/package.json +13 -13
  35. package/snaptrude-plugin-core-0.11.0.tgz +0 -0
  36. package/src/api/core/camera/index.ts +17 -0
  37. package/src/api/core/project/index.ts +62 -1
  38. package/src/api/core/storeys/index.ts +15 -0
  39. package/src/api/design/create/index.ts +34 -8
  40. package/src/api/design/delete/index.ts +20 -1
  41. package/src/api/design/furniture/index.ts +34 -0
  42. package/src/api/design/query/index.ts +2 -0
  43. package/src/api/design/query/spaces.ts +68 -0
  44. package/src/api/design/visibility.ts +34 -0
  45. package/src/api/entity/space.ts +2 -0
  46. package/src/api/presentation/placedViews.ts +29 -1
  47. package/src/api/presentation/sheets.ts +19 -3
  48. package/src/errors/codes.ts +30 -2
  49. package/test/errors.test.mjs +4 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snaptrude/plugin-core",
3
- "version": "0.10.0",
3
+ "version": "0.11.0-rc.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -16,17 +16,6 @@
16
16
  "publishConfig": {
17
17
  "access": "public"
18
18
  },
19
- "scripts": {
20
- "check-types": "tsc --noEmit",
21
- "build": "tsup --clean",
22
- "dev": "tsup --watch",
23
- "clean-dist": "rm -rf dist",
24
- "test": "node --test test/*.test.mjs",
25
- "generate:manifest": "node scripts/generate-manifest.mjs",
26
- "generate:manifest:check": "node scripts/generate-manifest.mjs --check",
27
- "generate:manifest:all": "node scripts/generate-manifest.mjs --all",
28
- "generate:manifest:all:check": "node scripts/generate-manifest.mjs --all --check"
29
- },
30
19
  "devDependencies": {
31
20
  "ts-morph": "^28.0.0",
32
21
  "tsup": "^8.5.1",
@@ -37,5 +26,16 @@
37
26
  },
38
27
  "peerDependencies": {
39
28
  "zod": "^3.25.0 || ^4.0.0"
29
+ },
30
+ "scripts": {
31
+ "check-types": "tsc --noEmit",
32
+ "build": "tsup --clean",
33
+ "dev": "tsup --watch",
34
+ "clean-dist": "rm -rf dist",
35
+ "test": "node --test test/*.test.mjs",
36
+ "generate:manifest": "node scripts/generate-manifest.mjs",
37
+ "generate:manifest:check": "node scripts/generate-manifest.mjs --check",
38
+ "generate:manifest:all": "node scripts/generate-manifest.mjs --all",
39
+ "generate:manifest:all:check": "node scripts/generate-manifest.mjs --all --check"
40
40
  }
41
- }
41
+ }
Binary file
@@ -62,6 +62,23 @@ export abstract class PluginCameraApi {
62
62
  view: PluginStandardView,
63
63
  ): PluginApiReturn<boolean>
64
64
 
65
+ /**
66
+ * Get the current camera mode. Paired with {@linkcode PluginCameraApi.setMode}.
67
+ *
68
+ * @returns `"2d"` when the editor is in plan view, `"3d"` otherwise.
69
+ *
70
+ * @examplePrompt Am I in 2D or 3D?
71
+ * @examplePrompt What view mode is the editor in?
72
+ *
73
+ * # Example
74
+ * ```ts
75
+ * if ((await snaptrude.core.camera.getMode()) === "2d") {
76
+ * await snaptrude.core.camera.setMode("3d")
77
+ * }
78
+ * ```
79
+ */
80
+ public abstract getMode(): PluginApiReturn<PluginCameraMode>
81
+
65
82
  /**
66
83
  * Toggle the modelling mode between `2d` (plan) and `3d`. Mirrors the canvas
67
84
  * 2D/3D toggle: `3d` enters the isometric perspective view, `2d` drops to the
@@ -1,15 +1,47 @@
1
1
  import * as z from "zod"
2
2
  import { PluginApiReturn } from "../../../types"
3
+ import { PUnitType } from "../units"
3
4
 
4
5
  /**
5
6
  * Project-level settings and info.
6
7
  *
7
- * Accessed via `snaptrude.core.project`. Currently exposes
8
+ * Accessed via `snaptrude.core.project`. Exposes
9
+ * {@linkcode PluginProjectApi.getInfo} (project identity) and
8
10
  * {@linkcode PluginProjectApi.settings} (snap + grid controls).
9
11
  */
10
12
  export abstract class PluginProjectApi {
11
13
  constructor() {}
12
14
 
15
+ /**
16
+ * Read identity and headline facts about the currently open project — id,
17
+ * display name, unit type, storey count, active storey, and site location
18
+ * when the project is geo-located.
19
+ *
20
+ * `activeStorey` and `storeyCount` are both scoped to the ACTIVE BUILDING, so
21
+ * they always describe the same building. In a multi-building project
22
+ * `storeyCount` is therefore NOT the total across every building — use
23
+ * `core.storeys.list()`, which spans all buildings, for that.
24
+ *
25
+ * @returns A {@linkcode PluginProjectInfo}. `name` is `null` only when the
26
+ * project genuinely has no title; `location` is `null` only when the project
27
+ * is not geo-located. Neither is used to signal a failure.
28
+ * @throws PRECONDITION_FAILED when no project is open.
29
+ * @throws OPERATION_FAILED when the project lookup or the site-location read
30
+ * fails. A failed read is never reported as `null`.
31
+ *
32
+ * @examplePrompt What is this project called?
33
+ * @examplePrompt Where is this project located?
34
+ * @examplePrompt Give me a summary of this project
35
+ * @examplePrompt How many storeys does this project have?
36
+ *
37
+ * # Example
38
+ * ```ts
39
+ * const info = await snaptrude.core.project.getInfo()
40
+ * console.log(info.name, info.units, info.storeyCount)
41
+ * ```
42
+ */
43
+ public abstract getInfo(): PluginApiReturn<PluginProjectInfo>
44
+
13
45
  /** Project settings — snaps and grid. See {@linkcode PluginProjectSettingsApi}. */
14
46
  public abstract settings: PluginProjectSettingsApi
15
47
  }
@@ -416,3 +448,32 @@ export const PluginToleranceArgs = z.object({
416
448
  })
417
449
 
418
450
  export type PluginToleranceArgs = z.infer<typeof PluginToleranceArgs>
451
+
452
+ /**
453
+ * Identity and headline facts about the currently open project.
454
+ *
455
+ * `location` is a read-only projection of {@linkcode PluginProgramSiteApi.getLocation}
456
+ * — `program.site.*` remains the full site surface (context, weather, polygons).
457
+ * It is mirrored here because "what and where is this project" is one question.
458
+ *
459
+ * | Property | Type | Description |
460
+ * |---|---|---|
461
+ * | `projectId` | `string` | The open project's id (floorkey) |
462
+ * | `name` | `string \| null` | Display name; `null` only when the project has no title (a failed lookup throws) |
463
+ * | `units` | {@linkcode PUnitType} | The project's unit type |
464
+ * | `activeStorey` | `number` | The active storey value, in the active building |
465
+ * | `storeyCount` | `number` | How many storeys the ACTIVE BUILDING has (not the project-wide total — `core.storeys.list()` spans all buildings) |
466
+ * | `location` | `{ latitude, longitude } \| null` | Site location; `null` only when not geo-located (a failed read throws) |
467
+ */
468
+ export const PluginProjectInfo = z.object({
469
+ projectId: z.string(),
470
+ name: z.string().nullable(),
471
+ units: PUnitType,
472
+ activeStorey: z.number(),
473
+ storeyCount: z.number(),
474
+ location: z
475
+ .object({ latitude: z.number(), longitude: z.number() })
476
+ .nullable(),
477
+ })
478
+
479
+ export type PluginProjectInfo = z.infer<typeof PluginProjectInfo>
@@ -160,6 +160,21 @@ export abstract class PluginCoreStoreysApi {
160
160
  options?: { name?: string },
161
161
  ): PluginApiReturn<PluginStoryUpdateResult>
162
162
 
163
+ /**
164
+ * Get the active storey's value. Paired with {@linkcode PluginCoreStoreysApi.setActive}.
165
+ *
166
+ * @returns The active storey value, in the same numbering `list` and `get` use.
167
+ *
168
+ * @examplePrompt Which storey am I on?
169
+ * @examplePrompt What is the current storey?
170
+ *
171
+ * # Example
172
+ * ```ts
173
+ * const storey = await snaptrude.core.storeys.getActive()
174
+ * ```
175
+ */
176
+ public abstract getActive(): PluginApiReturn<number>
177
+
163
178
  /**
164
179
  * Make a storey the active storey — the same as clicking it in the storey/layer
165
180
  * panel. Subsequent draws and creates target this storey, and in 2D the
@@ -217,11 +217,26 @@ export abstract class PluginDesignCreateApi {
217
217
  * Create a **floor** by extruding a footprint contour by `thickness`
218
218
  * (extruded upward).
219
219
  *
220
+ * The parametric engine owns floor topology and may resolve the request
221
+ * differently than asked: drawing against existing floors can **split** the
222
+ * footprint into several floors, or **merge** it into a pre-existing
223
+ * neighbour (that floor's region grows; nothing new is created). The call
224
+ * honors the engine's resolution and returns **every floor that covers the
225
+ * requested footprint after the operation** — the newly created floor(s),
226
+ * and/or the pre-existing floor that absorbed it. The array is never empty
227
+ * on success; a lone independently-created floor comes back as `[floor]`.
228
+ *
229
+ * **Breaking change in 0.11.0** — previously returned a single
230
+ * {@linkcode ComponentHandle}; callers must switch to the array (the first
231
+ * element preserves the old single-floor behavior for simple creates).
232
+ *
220
233
  * @param contour - Footprint (outer + holes)
221
234
  * @param thickness - Floor thickness (> 0)
222
235
  * @param position - Offset from origin
223
- * @returns the {@linkcode ComponentHandle} of the created floor
224
- * @throws if the contour is invalid or the thickness is not positive
236
+ * @returns the {@linkcode ComponentHandle}s of every floor covering the
237
+ * requested footprint after parametric resolution (created and/or absorbing)
238
+ * @throws if the contour is invalid, the thickness is not positive, or the
239
+ * engine rejected the operation (nothing covers the footprint)
225
240
  *
226
241
  * @examplePrompt Create a floor from this room outline
227
242
  * @examplePrompt Add a 100mm thick floor over the footprint
@@ -232,14 +247,14 @@ export abstract class PluginDesignCreateApi {
232
247
  * ```ts
233
248
  * const rect = await snaptrude.core.geom.create.profileRect(5, 4)
234
249
  * const contour = await snaptrude.core.geom.create.contourFromProfile(rect)
235
- * const floor = await snaptrude.design.create.floor(contour, 0.1)
250
+ * const [floor, ...siblings] = await snaptrude.design.create.floor(contour, 0.1)
236
251
  * ```
237
252
  */
238
253
  public abstract floor(
239
254
  contour: ContourHandle,
240
255
  thickness: number,
241
256
  position?: Vec3Handle,
242
- ): PluginApiReturn<ComponentHandle>
257
+ ): PluginApiReturn<ComponentHandle[]>
243
258
 
244
259
  /**
245
260
  * Create a **roof** by extruding a footprint contour by `thickness`. Created
@@ -395,7 +410,13 @@ export abstract class PluginDesignCreateApi {
395
410
  * single undo entry. The storey must already exist
396
411
  * (`core.storeys.create` first). Default: geometry-derived — a profile
397
412
  * drawn at y = 0 lands on storey 1; the ACTIVE storey is NOT consulted.
398
- * @returns a {@linkcode ComponentHandle}`[]` (one per wall), in profile order
413
+ * @returns the actually-created wall {@linkcode ComponentHandle}s. The
414
+ * parametric engine owns wall topology — curves touching existing walls
415
+ * may merge into or split against them, so the returned count can differ
416
+ * from the input curve count. Profile-ordered 1:1 matches come first,
417
+ * then the remaining merged/split products in creation order. Re-query
418
+ * (e.g. `design.query.spaces.listGeneratedBim`) rather than assuming a
419
+ * wall per input curve.
399
420
  * @throws if the profile is empty, `wallType` names no wall type in the
400
421
  * project, `storey` does not exist, or wall creation fails
401
422
  *
@@ -537,7 +558,9 @@ export abstract class PluginDesignCreateApi {
537
558
  * the item's height yourself.
538
559
  * @param options - Optional placement options: `label` — instance name
539
560
  * (default auto `${name}Ins${n}`); `createNewSourceMesh` — emit a
540
- * source-mesh creation command (default `true`)
561
+ * source-mesh creation command (default `true`). `position.y` is always
562
+ * grounded: the placed instance's bounding-box bottom rests at it (the
563
+ * surface-flush grounding described above)
541
564
  * @param angleInDegrees - Optional signed rotation about the vertical axis, in
542
565
  * degrees (same convention as {@linkcode PluginDesignTransformApi.rotate}).
543
566
  * Applied at creation time so it is part of the placement's single undo entry.
@@ -571,7 +594,10 @@ export abstract class PluginDesignCreateApi {
571
594
  public abstract furniture(
572
595
  catalogId: string,
573
596
  position: Vec3Handle,
574
- options?: { label?: string; createNewSourceMesh?: boolean },
597
+ options?: {
598
+ label?: string
599
+ createNewSourceMesh?: boolean
600
+ },
575
601
  angleInDegrees?: number,
576
602
  ): PluginApiReturn<ComponentHandle>
577
603
 
@@ -1219,7 +1245,7 @@ export const PluginDesignCreateWallsArgs = z.object({
1219
1245
  export type PluginDesignCreateWallsArgs = z.infer<
1220
1246
  typeof PluginDesignCreateWallsArgs
1221
1247
  >
1222
- // TRANSPORT: positional signature shipped — storey is the trailing 5th arg:
1248
+ // TRANSPORT: positional signature shipped:
1223
1249
  // walls(profile: ProfileHandle, height?: number, thickness?: number, wallType?: string, storey?: number)
1224
1250
 
1225
1251
  // ---------------------------------------------------------------------------
@@ -20,6 +20,22 @@ export const PluginDesignDeleteEntitiesArgs = z.object({
20
20
  })
21
21
  export type PluginDesignDeleteEntitiesArgs = z.infer<typeof PluginDesignDeleteEntitiesArgs>
22
22
 
23
+ /**
24
+ * Result of {@linkcode PluginDesignDeleteApi.entities} — the shared
25
+ * change-result shape plus the delete cascade. Deliberately a delete-specific
26
+ * extension of {@linkcode PluginDesignChangeResult} (which other change
27
+ * mutations keep unchanged).
28
+ *
29
+ * | Property | Type | Description |
30
+ * |---|---|---|
31
+ * | `affected` | {@linkcode ComponentHandle}`[]` | The entities that were deleted (the resolved input set) |
32
+ * | `cascaded` | {@linkcode ComponentHandle}`[]`? | Components PG2 removed as a consequence — derived walls/floors/slabs of deleted spaces; returned so callers can invalidate cached handles |
33
+ */
34
+ export const PluginDesignDeleteResult = PluginDesignChangeResult.extend({
35
+ cascaded: z.array(ComponentHandle).optional(),
36
+ })
37
+ export type PluginDesignDeleteResult = z.infer<typeof PluginDesignDeleteResult>
38
+
23
39
  /**
24
40
  * `snaptrude.design.delete.*` — **hard removal** of scene BIM entities.
25
41
  *
@@ -43,6 +59,9 @@ export abstract class PluginDesignDeleteApi {
43
59
  * @returns The entities that were deleted, as {@linkcode ComponentHandle}`[]`
44
60
  * (echoed host-side from the resolved+deleted set — the engine returns no ids).
45
61
  * Deleted handles are **stale** afterwards: re-resolving one throws `HANDLE_INVALID`.
62
+ * When the parametric engine removes further components as a consequence
63
+ * (derived walls/floors/slabs of deleted spaces), those are reported in the
64
+ * optional `cascaded` array — invalidate any cached handles to them too.
46
65
  * @throws If any supplied handle is unknown/forged (`HANDLE_INVALID`) — the whole
47
66
  * call rejects before anything is deleted (all-or-nothing resolution).
48
67
  *
@@ -66,5 +85,5 @@ export abstract class PluginDesignDeleteApi {
66
85
  */
67
86
  public abstract entities(
68
87
  components: ComponentHandle[],
69
- ): PluginApiReturn<PluginDesignChangeResult>
88
+ ): PluginApiReturn<PluginDesignDeleteResult>
70
89
  }
@@ -2,6 +2,7 @@ import * as z from "zod"
2
2
  import { PluginApiReturn } from "../../../types"
3
3
  import { ComponentHandle } from "../../../handles"
4
4
  import { PluginDesignChangeResult } from "../lock"
5
+ import { PluginObjectCatalogGroup } from "../doors"
5
6
 
6
7
  /**
7
8
  * `snaptrude.design.furniture` — the placeable furniture **catalog** (a library of
@@ -92,9 +93,42 @@ export abstract class PluginDesignFurnitureApi {
92
93
  * const items = await snaptrude.design.furniture.listCatalog(undefined, first)
93
94
  * console.log(first, "→", items.length, "items")
94
95
  * ```
96
+ *
97
+ * @deprecated Use `design.furniture.listCatalogGroups` — the same name
98
+ * `design.doors` and `design.windows` use. Still supported.
95
99
  */
96
100
  public abstract listCategories(): PluginApiReturn<string[]>
97
101
 
102
+ /**
103
+ * List the furniture catalog's groups. Matches
104
+ * {@linkcode PluginDesignDoorsApi.listCatalogGroups} and
105
+ * {@linkcode PluginDesignWindowsApi.listCatalogGroups}, so all three catalog
106
+ * surfaces are named alike.
107
+ *
108
+ * `source` is `"default"` for the built-in picker groups and `"team"` for the
109
+ * sub-types of THIS project's team library — exactly the split
110
+ * {@linkcode PluginDesignFurnitureApi.listCatalog}'s `source` filter uses, so
111
+ * every `"team"` group is guaranteed to match at least one team item here.
112
+ * For furniture the group token and its label are the same string (the
113
+ * category), so `dbType === label`; `design.doors`/`design.windows` have a
114
+ * distinct engine `dbType`.
115
+ *
116
+ * @returns The catalog groups as {@linkcode PluginObjectCatalogGroup}`[]`
117
+ * (`[]` when empty). Pass a group's `dbType` (=== `label`) to
118
+ * {@linkcode PluginDesignFurnitureApi.listCatalog}'s `category` filter to
119
+ * list its items.
120
+ *
121
+ * @examplePrompt What furniture groups are available?
122
+ * @examplePrompt List the furniture categories in this project
123
+ *
124
+ * # Example
125
+ * ```ts
126
+ * const groups = await snaptrude.design.furniture.listCatalogGroups()
127
+ * for (const g of groups) console.log(g.dbType, g.label, g.source)
128
+ * ```
129
+ */
130
+ public abstract listCatalogGroups(): PluginApiReturn<PluginObjectCatalogGroup[]>
131
+
98
132
  /**
99
133
  * List the placeable furniture catalog (team + general libraries),
100
134
  * optionally filtered by library `source` and/or `category`.
@@ -173,6 +173,7 @@ export type PluginDesignQueryComponentsArgs = z.infer<
173
173
  * | `isLocked` | `boolean` | Lock flag |
174
174
  * | `isHidden` | `boolean` | User-hidden flag |
175
175
  * | `isSelected` | `boolean` | Current selection state |
176
+ * | `isVisible` | `boolean` | Whether the entity currently has a visible representation on canvas (user hide flag AND LOD/mode gating both considered); read-only |
176
177
  * | `boundingBox` | {@linkcode BBoxComponents} | World-space AABB |
177
178
  * | `adjacency` | `{ spaceId: string; value: number }[]` | Adjacency relationships (spaces/masses only). `value`: `2` = Direct (required), `1` = Indirect or physical contact, `-1` = Restricted; `0` entries are filtered out. `[]` when adjacency has never been computed — see `program.adjacency.compute`. |
178
179
  */
@@ -192,6 +193,7 @@ export const PluginEntityProperties = z
192
193
  isLocked: z.boolean(),
193
194
  isHidden: z.boolean(),
194
195
  isSelected: z.boolean(),
196
+ isVisible: z.boolean(),
195
197
  boundingBox: BBoxComponents,
196
198
  adjacency: z.array(z.object({ spaceId: z.string(), value: z.number() })),
197
199
  })
@@ -124,6 +124,41 @@ export abstract class PluginDesignQuerySpacesApi {
124
124
  public abstract getEnclosure(
125
125
  space: ComponentHandle,
126
126
  ): PluginApiReturn<PluginSpaceEnclosure | null>
127
+
128
+ /**
129
+ * List the space's **engine-generated BIM components** — the walls, floors
130
+ * and slabs the parametric engine derived from this space, grouped by kind.
131
+ *
132
+ * Unlike {@linkcode getEnclosure} (a geometric best-effort match), this read
133
+ * reports the engine's own derivation records, so every returned handle is a
134
+ * component the engine created *for this space*.
135
+ *
136
+ * The derived set is NOT stable across writes: a merge/split/rebuild of the
137
+ * space graph retires members and creates successors — re-query after any
138
+ * write that touches the space. This is the churn-recovery anchor: when a
139
+ * cached derived handle goes stale, call this again to pick up the current
140
+ * set.
141
+ *
142
+ * @param space - The space (room mass) to read
143
+ * @returns the generated component handles, grouped by kind
144
+ * ({@linkcode PluginSpaceGeneratedBim}); a group is `[]` when the engine
145
+ * derived nothing of that kind
146
+ * @throws If the space does not exist or the component is not a space/mass
147
+ *
148
+ * @examplePrompt Which walls did this room generate?
149
+ * @examplePrompt List the BIM components derived from the selected space
150
+ * @examplePrompt Get the floors and slabs that belong to this room
151
+ *
152
+ * # Example
153
+ * ```ts
154
+ * const [space] = await snaptrude.design.query.listSpaces()
155
+ * const bim = await snaptrude.design.query.spaces.listGeneratedBim(space)
156
+ * console.log(`${bim.walls.length} walls, ${bim.floors.length} floors`)
157
+ * ```
158
+ */
159
+ public abstract listGeneratedBim(
160
+ space: ComponentHandle,
161
+ ): PluginApiReturn<PluginSpaceGeneratedBim>
127
162
  }
128
163
 
129
164
  /**
@@ -273,3 +308,36 @@ export const PluginSpaceEnclosure = z.object({
273
308
  adjacentSpaces: z.array(PluginAdjacentSpace),
274
309
  })
275
310
  export type PluginSpaceEnclosure = z.infer<typeof PluginSpaceEnclosure>
311
+
312
+ /**
313
+ * Arguments for {@linkcode PluginDesignQuerySpacesApi.listGeneratedBim}.
314
+ *
315
+ * | Property | Type | Description |
316
+ * |---|---|---|
317
+ * | `space` | {@linkcode ComponentHandle} | The space (room mass) to read |
318
+ */
319
+ export const PluginDesignQuerySpacesListGeneratedBimArgs = z.object({
320
+ space: ComponentHandle,
321
+ })
322
+ export type PluginDesignQuerySpacesListGeneratedBimArgs = z.infer<
323
+ typeof PluginDesignQuerySpacesListGeneratedBimArgs
324
+ >
325
+
326
+ /**
327
+ * Result of {@linkcode PluginDesignQuerySpacesApi.listGeneratedBim} — the
328
+ * space's engine-generated BIM component handles, grouped by kind. The set
329
+ * changes whenever a write merges/splits/rebuilds the space graph — re-query
330
+ * after writes rather than caching it.
331
+ *
332
+ * | Property | Type | Description |
333
+ * |---|---|---|
334
+ * | `walls` | {@linkcode ComponentHandle}`[]` | Walls the engine derived from the space |
335
+ * | `floors` | {@linkcode ComponentHandle}`[]` | Floors the engine derived from the space |
336
+ * | `slabs` | {@linkcode ComponentHandle}`[]` | Slabs the engine derived from the space |
337
+ */
338
+ export const PluginSpaceGeneratedBim = z.object({
339
+ walls: z.array(ComponentHandle),
340
+ floors: z.array(ComponentHandle),
341
+ slabs: z.array(ComponentHandle),
342
+ })
343
+ export type PluginSpaceGeneratedBim = z.infer<typeof PluginSpaceGeneratedBim>
@@ -39,6 +39,26 @@ export abstract class PluginDesignVisibilityApi {
39
39
  components: ComponentHandle[],
40
40
  ): PluginApiReturn<PluginDesignChangeResult>
41
41
 
42
+ /**
43
+ * Reveal specific hidden components — the inverse of
44
+ * {@linkcode PluginDesignVisibilityApi.hide}. Use
45
+ * {@linkcode PluginDesignVisibilityApi.showAll} to reveal everything.
46
+ *
47
+ * @param components - The components to reveal.
48
+ * @returns A {@linkcode PluginDesignChangeResult} echoing the components shown.
49
+ *
50
+ * @examplePrompt Show these walls again
51
+ * @examplePrompt Unhide the selected furniture
52
+ *
53
+ * # Example
54
+ * ```ts
55
+ * await snaptrude.design.visibility.show([wall])
56
+ * ```
57
+ */
58
+ public abstract show(
59
+ components: ComponentHandle[],
60
+ ): PluginApiReturn<PluginDesignChangeResult>
61
+
42
62
  /**
43
63
  * Isolate entities — hide everything else so only the given entities remain
44
64
  * visible (the "Isolate" / solo action). Undoable. Reverse it with
@@ -94,6 +114,20 @@ export const PluginDesignVisibilityHideArgs = z.object({
94
114
  })
95
115
  export type PluginDesignVisibilityHideArgs = z.infer<typeof PluginDesignVisibilityHideArgs>
96
116
 
117
+ /**
118
+ * Arguments for {@linkcode PluginDesignVisibilityApi.show}.
119
+ *
120
+ * | Property | Type | Description |
121
+ * |---|---|---|
122
+ * | `components` | {@linkcode ComponentHandle}`[]` | Entities to reveal |
123
+ */
124
+ export const PluginDesignVisibilityShowArgs = z.object({
125
+ components: z.array(ComponentHandle),
126
+ })
127
+ export type PluginDesignVisibilityShowArgs = z.infer<
128
+ typeof PluginDesignVisibilityShowArgs
129
+ >
130
+
97
131
  /**
98
132
  * Arguments for {@linkcode PluginDesignVisibilityApi.isolate}.
99
133
  *
@@ -434,6 +434,7 @@ export type PluginSpaceUpdateArgs = z.infer<typeof PluginSpaceUpdateArgs>
434
434
  * | `massType` | `string?` | Updated mass type (if changed) |
435
435
  * | `spaceType` | `string?` | Updated space type (if changed) |
436
436
  * | `departmentId` | `string \| null?` | Updated department ID (if changed) |
437
+ * | `rebuilt` | `boolean?` | `true` when a geometry update rebuilt the space's derived BIM — hosted children were re-created and the previous derived handles are retired (re-query them) |
437
438
  */
438
439
  export const PluginSpaceUpdateResult = z.object({
439
440
  spaceId: z.string(),
@@ -442,6 +443,7 @@ export const PluginSpaceUpdateResult = z.object({
442
443
  spaceType: z.string().optional(),
443
444
  areaClass: PluginAreaClass.optional(),
444
445
  departmentId: z.string().nullable().optional(),
446
+ rebuilt: z.boolean().optional(),
445
447
  })
446
448
 
447
449
  export type PluginSpaceUpdateResult = z.infer<typeof PluginSpaceUpdateResult>
@@ -1,6 +1,6 @@
1
1
  import * as z from "zod"
2
2
  import { PluginApiReturn } from "../../types"
3
- import { PluginSheetPosition } from "./sheets"
3
+ import { PluginSheetPosition, PluginViewLod } from "./sheets"
4
4
 
5
5
  /**
6
6
  * Placed views — the view shapes already laid out on the Present canvas.
@@ -613,6 +613,23 @@ export abstract class PluginPresentationPlacedViewsApi {
613
613
  shapeId: string,
614
614
  enabled: boolean,
615
615
  ): PluginApiReturn<void>
616
+
617
+ /**
618
+ * Set the level of detail a placed view renders at — `"design"` (the
619
+ * Design-mode massing representation) or `"bim"` (the detailed BIM
620
+ * representation). Views only. Undoable. Requires Present mode to be open.
621
+ *
622
+ * @param shapeId - The placed view shape.
623
+ * @param lod - Target level of detail.
624
+ * @throws If Present mode is not open or `shapeId` is not a placed view.
625
+ *
626
+ * @examplePrompt Switch this placed view to the BIM level of detail
627
+ * @examplePrompt Show the massing version of the placed plan
628
+ */
629
+ public abstract setLod(
630
+ shapeId: string,
631
+ lod: PluginViewLod,
632
+ ): PluginApiReturn<void>
616
633
  }
617
634
 
618
635
  /** A point in crop space — fractions (0–1) of the uncropped view. */
@@ -657,6 +674,8 @@ export type PluginPlacedViewCrop = z.infer<typeof PluginPlacedViewCrop>
657
674
  * | `rotation` | `number` | Rotation in radians, `[0, 2π)` (nonzero only for 3D views — 2D views cannot rotate; set with `setRotation`) |
658
675
  * | `crop` | {@linkcode PluginPlacedViewCrop}` \| null` | Crop window (`null` when uncropped) |
659
676
  * | `isUnlinked` | `boolean` | Whether the source proposal was removed (an unlinked view no longer refreshes) |
677
+ * | `lod` | {@linkcode PluginViewLod}? | Level of detail the view renders at (`"design"` massing / `"bim"` detail); absent on views that predate LOD variants (set with `setLod`) |
678
+ * | `inactiveBuildingMask` | `boolean`? | Whether inactive proposal buildings are masked (the `setMask` toggle); absent when never set (masked by default) |
660
679
  */
661
680
  export const PluginPlacedView = z.object({
662
681
  shapeId: z.string(),
@@ -670,6 +689,8 @@ export const PluginPlacedView = z.object({
670
689
  rotation: z.number(),
671
690
  crop: PluginPlacedViewCrop.nullable(),
672
691
  isUnlinked: z.boolean(),
692
+ lod: PluginViewLod.optional(),
693
+ inactiveBuildingMask: z.boolean().optional(),
673
694
  })
674
695
  export type PluginPlacedView = z.infer<typeof PluginPlacedView>
675
696
 
@@ -1118,3 +1139,10 @@ export const PluginPlacedViewsSetMaskArgs = z.object({
1118
1139
  enabled: z.boolean(),
1119
1140
  })
1120
1141
  export type PluginPlacedViewsSetMaskArgs = z.infer<typeof PluginPlacedViewsSetMaskArgs>
1142
+
1143
+ /** Arguments for {@link PluginPresentationPlacedViewsApi.setLod}. */
1144
+ export const PluginPlacedViewsSetLodArgs = z.object({
1145
+ shapeId: z.string().min(1),
1146
+ lod: PluginViewLod,
1147
+ })
1148
+ export type PluginPlacedViewsSetLodArgs = z.infer<typeof PluginPlacedViewsSetLodArgs>
@@ -172,9 +172,10 @@ export abstract class PluginPresentationSheetsApi {
172
172
  * @param sheetId - The id of the sheet to place onto.
173
173
  * @param viewId - The id of the saved view to place.
174
174
  * @param options - Optional `position` ({@linkcode PluginSheetPosition}) —
175
- * where to place it (sheet centre when omitted) — and `scale` — a standard
175
+ * where to place it (sheet centre when omitted) — `scale` — a standard
176
176
  * scale value for the project's unit system (2D/site-plan views only;
177
- * auto-fit when omitted).
177
+ * auto-fit when omitted) — and `lod` ({@linkcode PluginViewLod}) — the LOD
178
+ * of the placed view (default: the product's Design default).
178
179
  * @returns A {@linkcode PluginPresentationSheetsPlaceResult} with the created
179
180
  * `shapeId`.
180
181
  * @throws If Present mode is not open, the sheet/view id is invalid, `scale`
@@ -197,7 +198,11 @@ export abstract class PluginPresentationSheetsApi {
197
198
  public abstract place(
198
199
  sheetId: string,
199
200
  viewId: string,
200
- options?: { position?: PluginSheetPosition; scale?: number },
201
+ options?: {
202
+ position?: PluginSheetPosition
203
+ scale?: number
204
+ lod?: PluginViewLod
205
+ },
201
206
  ): PluginApiReturn<PluginPresentationSheetsPlaceResult>
202
207
 
203
208
  /**
@@ -408,6 +413,15 @@ export const PluginSheetPosition = z.object({
408
413
  })
409
414
  export type PluginSheetPosition = z.infer<typeof PluginSheetPosition>
410
415
 
416
+ /**
417
+ * The level of detail a placed view renders at: `"design"` (the Design-mode
418
+ * massing representation) or `"bim"` (the detailed BIM representation).
419
+ * Shared by `sheets.place` and `placedViews.*` (defined here because
420
+ * `placedViews` already imports from this module).
421
+ */
422
+ export const PluginViewLod = z.enum(["design", "bim"])
423
+ export type PluginViewLod = z.infer<typeof PluginViewLod>
424
+
411
425
  /** Result of {@linkcode PluginPresentationSheetsApi.list}. */
412
426
  export const PluginPresentationSheetsListResult = z.object({
413
427
  sheets: z.array(PluginPresentationSheet),
@@ -469,12 +483,14 @@ export type PluginPresentationSheetsSetMarginArgs = z.infer<
469
483
  * | `viewId` | `string` | The saved view to place |
470
484
  * | `position` | {@linkcode PluginSheetPosition}` \| undefined` | Where to place it (sheet centre when omitted) |
471
485
  * | `scale` | `number \| undefined` | Standard scale value for the project's unit system (2D/site-plan views only; auto-fit to the closest standard scale when omitted) |
486
+ * | `lod` | {@linkcode PluginViewLod}` \| undefined` | LOD of the placed view (default: the product's Design default) |
472
487
  */
473
488
  export const PluginPresentationSheetsPlaceArgs = z.object({
474
489
  sheetId: z.string(),
475
490
  viewId: z.string(),
476
491
  position: PluginSheetPosition.optional(),
477
492
  scale: z.number().positive().optional(),
493
+ lod: PluginViewLod.optional(),
478
494
  })
479
495
  export type PluginPresentationSheetsPlaceArgs = z.infer<
480
496
  typeof PluginPresentationSheetsPlaceArgs