@snaptrude/plugin-core 0.9.17 → 0.9.18-voids-20261017.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 (102) hide show
  1. package/dist/api/analysis/daylight.d.ts +5 -5
  2. package/dist/api/analysis/solar.d.ts +2 -2
  3. package/dist/api/core/camera/index.d.ts +29 -0
  4. package/dist/api/core/camera/index.d.ts.map +1 -1
  5. package/dist/api/core/geom/create/index.d.ts +7 -1
  6. package/dist/api/core/geom/create/index.d.ts.map +1 -1
  7. package/dist/api/core/geom/update/contour.d.ts +8 -2
  8. package/dist/api/core/geom/update/contour.d.ts.map +1 -1
  9. package/dist/api/core/zoom/index.d.ts +13 -0
  10. package/dist/api/core/zoom/index.d.ts.map +1 -1
  11. package/dist/api/design/bim.d.ts +299 -0
  12. package/dist/api/design/bim.d.ts.map +1 -0
  13. package/dist/api/design/boolean/index.d.ts +2 -0
  14. package/dist/api/design/boolean/index.d.ts.map +1 -1
  15. package/dist/api/design/catalog.d.ts +320 -0
  16. package/dist/api/design/catalog.d.ts.map +1 -0
  17. package/dist/api/design/create/bulk-items.d.ts +65 -25
  18. package/dist/api/design/create/bulk-items.d.ts.map +1 -1
  19. package/dist/api/design/create/index.d.ts +272 -99
  20. package/dist/api/design/create/index.d.ts.map +1 -1
  21. package/dist/api/design/create/opening-fields.d.ts +13 -12
  22. package/dist/api/design/create/opening-fields.d.ts.map +1 -1
  23. package/dist/api/design/delete/index.d.ts +1 -0
  24. package/dist/api/design/delete/index.d.ts.map +1 -1
  25. package/dist/api/design/doors/index.d.ts +18 -24
  26. package/dist/api/design/doors/index.d.ts.map +1 -1
  27. package/dist/api/design/family.d.ts +801 -28
  28. package/dist/api/design/family.d.ts.map +1 -1
  29. package/dist/api/design/index.d.ts +16 -0
  30. package/dist/api/design/index.d.ts.map +1 -1
  31. package/dist/api/design/networks.d.ts +1703 -0
  32. package/dist/api/design/networks.d.ts.map +1 -0
  33. package/dist/api/design/query/index.d.ts +42 -32
  34. package/dist/api/design/query/index.d.ts.map +1 -1
  35. package/dist/api/design/selection/index.d.ts +10 -10
  36. package/dist/api/design/types/index.d.ts +40 -24
  37. package/dist/api/design/types/index.d.ts.map +1 -1
  38. package/dist/api/design/update/index.d.ts +43 -3
  39. package/dist/api/design/update/index.d.ts.map +1 -1
  40. package/dist/api/design/windows/index.d.ts +8 -8
  41. package/dist/api/design/windows/index.d.ts.map +1 -1
  42. package/dist/api/presentation/aiInspiration.d.ts +10 -10
  43. package/dist/api/presentation/annotate.d.ts +38 -22
  44. package/dist/api/presentation/annotate.d.ts.map +1 -1
  45. package/dist/api/presentation/drawingContract.d.ts +32 -0
  46. package/dist/api/presentation/drawingContract.d.ts.map +1 -0
  47. package/dist/api/presentation/drawings.d.ts +3523 -0
  48. package/dist/api/presentation/drawings.d.ts.map +1 -0
  49. package/dist/api/presentation/export.d.ts +23 -5
  50. package/dist/api/presentation/export.d.ts.map +1 -1
  51. package/dist/api/presentation/index.d.ts +32 -12
  52. package/dist/api/presentation/index.d.ts.map +1 -1
  53. package/dist/api/presentation/placedViews.d.ts +106 -6
  54. package/dist/api/presentation/placedViews.d.ts.map +1 -1
  55. package/dist/api/presentation/shapes.d.ts +2 -2
  56. package/dist/api/presentation/sheets.d.ts +175 -19
  57. package/dist/api/presentation/sheets.d.ts.map +1 -1
  58. package/dist/api/presentation/viewPresets.d.ts +222 -0
  59. package/dist/api/presentation/viewPresets.d.ts.map +1 -0
  60. package/dist/api/presentation/views.d.ts +103 -5
  61. package/dist/api/presentation/views.d.ts.map +1 -1
  62. package/dist/api/presentation/walkthroughs.d.ts +6 -6
  63. package/dist/errors/codes.d.ts +3 -1
  64. package/dist/errors/codes.d.ts.map +1 -1
  65. package/dist/index.cjs +3741 -2383
  66. package/dist/index.cjs.map +1 -1
  67. package/dist/index.js +3618 -2383
  68. package/dist/index.js.map +1 -1
  69. package/package.json +1 -1
  70. package/scripts/generate-manifest.mjs +5 -0
  71. package/src/api/core/camera/index.ts +29 -0
  72. package/src/api/core/geom/create/index.ts +7 -1
  73. package/src/api/core/geom/update/contour.ts +8 -2
  74. package/src/api/core/zoom/index.ts +13 -0
  75. package/src/api/design/bim.ts +210 -0
  76. package/src/api/design/boolean/index.ts +2 -0
  77. package/src/api/design/catalog.ts +203 -0
  78. package/src/api/design/create/bulk-items.ts +57 -17
  79. package/src/api/design/create/index.ts +288 -120
  80. package/src/api/design/create/opening-fields.ts +15 -12
  81. package/src/api/design/delete/index.ts +1 -0
  82. package/src/api/design/doors/index.ts +58 -42
  83. package/src/api/design/family.ts +666 -33
  84. package/src/api/design/index.ts +34 -4
  85. package/src/api/design/networks.ts +607 -0
  86. package/src/api/design/query/index.ts +12 -2
  87. package/src/api/design/types/index.ts +31 -24
  88. package/src/api/design/update/index.ts +44 -3
  89. package/src/api/design/windows/index.ts +40 -22
  90. package/src/api/presentation/annotate.ts +90 -58
  91. package/src/api/presentation/drawingContract.ts +70 -0
  92. package/src/api/presentation/drawings.ts +1179 -0
  93. package/src/api/presentation/export.ts +32 -6
  94. package/src/api/presentation/index.ts +39 -14
  95. package/src/api/presentation/placedViews.ts +119 -28
  96. package/src/api/presentation/sheets.ts +181 -20
  97. package/src/api/presentation/viewPresets.ts +241 -0
  98. package/src/api/presentation/views.ts +46 -6
  99. package/src/errors/codes.ts +7 -0
  100. package/test/drawingContract.test.mjs +288 -0
  101. package/test/errors.test.mjs +1 -0
  102. package/api-manifest.json +0 -9711
@@ -1,6 +1,12 @@
1
1
  import * as z from "zod"
2
2
  import { PluginApiReturn } from "../../types"
3
- import { BrepHandle, ComponentHandle, Vec3Components } from "../../handles"
3
+ import {
4
+ BrepHandle,
5
+ ComponentHandle,
6
+ Vec3Components,
7
+ Vec3Handle,
8
+ } from "../../handles"
9
+ import { NetworkFamilyContract } from "./networks"
4
10
 
5
11
  /**
6
12
  * `snaptrude.design.family.*` — author NATIVE parametric families from a JSON spec.
@@ -12,19 +18,52 @@ import { BrepHandle, ComponentHandle, Vec3Components } from "../../handles"
12
18
  * compiles that into a live parametric definition the user can place, flex and
13
19
  * save to their library.
14
20
  *
15
- * ## Workflow
21
+ * ## Generic component workflow
16
22
  *
17
23
  * ```
18
- * getReference() read the spec DSL once — grammar, ops, expressions, example
19
- * prepare({ bundle }) validate + compile + show a live ghost; nothing committed
24
+ * getReference() read the spec DSL once — grammar, ops, expressions, examples
25
+ * getExamples() worked example families by discipline; getExamples({ id }) for one
26
+ * prepare({ bundle }) validate + compile; nothing committed, nothing in the viewport
20
27
  * flex({ values }) re-run at new values; returns the geometry delta
21
28
  * place({ position }) put it in the model — saves the family as the first instance drops
22
29
  * place() save the family, then arm placement — the USER clicks to drop it
30
+ * create() save an EDIT of a saved family — the app asks the user
23
31
  * create({ teamId }) share with a team; a save with nothing placed also drops one instance
24
32
  * ```
25
33
  *
26
34
  * `place` saves the prepared family to the user's personal library, so it
27
- * survives reload with no extra step; finish on `place`, not on `create`.
35
+ * survives reload with no extra step; finish a NEW generic component on
36
+ * `place`, not on `create` (`create` ends only an edit of a saved family, below).
37
+ *
38
+ * Architectural types use `create({ name })` to save the prepared recipe.
39
+ * Saving does not assign a family to a native host.
40
+ * Read `design.catalog` and pass the saved `{ id, version }` reference to the
41
+ * native `design.create` method with its contour, wall profile, or straight axis.
42
+ * Standalone placement does not create a native host. Selecting a Draw type
43
+ * does not register an API type. Native creator values use mm, while this
44
+ * generic family API uses SU. Read `family_authoring` for the full workflow.
45
+ *
46
+ * ## Door and window templates
47
+ *
48
+ * Start with `getTemplate({ kind: "door" | "window", subType? })`, edit the
49
+ * recipe's style while keeping its contract, then `prepare` → `flex` → `create`.
50
+ * Place the saved family with
51
+ * `design.create.door(familyId, hostWall, position, { version, values, hinge }, facing)`
52
+ * or `design.create.window` with the same positional signature. Positions,
53
+ * hinge and facing are vec3 handles; values use the authored parameter keys.
54
+ * Templates produce ParametricComponents, not separate OpeningComponents. Read
55
+ * and edit their instances through `getParameters` / `setParameters`.
56
+ * The contract maps opening dimensions to parameter keys; do not assume the
57
+ * recipe names its width parameter `width`. Generic family lengths use SU.
58
+ * Hosted door/window `create({ name })` saves without placing a standalone
59
+ * sample. Supply the host and position in a subsequent placement.
60
+ *
61
+ * ## Changing a saved family
62
+ *
63
+ * Open it with `get({ id })` or `get({ component })`, edit with `prepare` /
64
+ * `flex`, then `create()`. The app — not the agent — asks the user whether to
65
+ * update that family (this project's placed copies move to the new version) or
66
+ * save a new family. Do not ask this yourself; report the returned `decision`.
28
67
  *
29
68
  * ## Read getReference() first
30
69
  *
@@ -72,6 +111,8 @@ export abstract class PluginDesignFamilyApi {
72
111
  * Read this before authoring a family for the first time in a session. The
73
112
  * DSL is a closed language — a guessed op or function is a rejected spec.
74
113
  *
114
+ * @param args Optional section, or its bare name. Omit for the core reference.
115
+ * Fetch architectural, networks or relations only when the family needs them.
75
116
  * @returns The reference text
76
117
  *
77
118
  * @examplePrompt How do I write a parametric family spec?
@@ -82,7 +123,61 @@ export abstract class PluginDesignFamilyApi {
82
123
  * const { reference } = await snaptrude.design.family.getReference()
83
124
  * ```
84
125
  */
85
- public abstract getReference(): PluginApiReturn<PluginFamilyReference>
126
+ public abstract getReference(
127
+ args?: PluginDesignFamilyGetReferenceArgs | PluginFamilyReferenceSection,
128
+ ): PluginApiReturn<PluginFamilyReference>
129
+
130
+ /**
131
+ * Worked example families to learn from: real, tested bundles by discipline
132
+ * (architecture, structure, MEP, furniture). With no `id`, returns the
133
+ * catalogue — each example's id, label, discipline and the patterns it
134
+ * teaches — filtered by `discipline` / `tag` when given. With an `id`,
135
+ * returns that example's full bundle. Fetch the one closest to the task
136
+ * before authoring and borrow its patterns; examples are patterns to
137
+ * combine, not templates to copy whole.
138
+ *
139
+ * @param args - `{ id }` for one bundle, or `{ discipline?, tag? }` to list
140
+ * @returns The catalogue, or one example with its bundle
141
+ *
142
+ * @examplePrompt Show me an example of a structural family
143
+ * @examplePrompt How is a hosted door family written?
144
+ *
145
+ * # Example
146
+ * ```ts
147
+ * const list = await snaptrude.design.family.getExamples({ discipline: "mep" })
148
+ * const ex = await snaptrude.design.family.getExamples({ id: list[0].id })
149
+ * ```
150
+ */
151
+ public abstract getExamples(
152
+ args?: PluginDesignFamilyGetExamplesArgs | string,
153
+ ): PluginApiReturn<PluginFamilyExampleSummary[] | PluginFamilyExample>
154
+
155
+ /**
156
+ * Get a validated starter recipe for a door or window, with an opening
157
+ * contract, host wall-thickness binding and role-named parts. Edit profiles,
158
+ * panels, glazing, hardware and materials while preserving the contract.
159
+ * Use `prepare` / `flex` to check the recipe, then `create` to save it and
160
+ * `design.create.door(familyId, hostWall, position, { version, values }, facing)`
161
+ * (or `window`) to place it. Edit the resulting ParametricComponent through
162
+ * `getParameters` / `setParameters`, not a separate opening API.
163
+ *
164
+ * `subType` defaults to `singleDoor` / `fixedWindow`. An unsupported subType
165
+ * returns the kind's default template, a note in `guidance`, and `available`
166
+ * templates. Start from the closest recipe and set `root.subType` to your token.
167
+ *
168
+ * @param args The opening kind and optional subtype token
169
+ * @returns The bundle, parameter-key contract, subtype, guidance and available templates
170
+ * @example
171
+ * const { bundle, contract } = await snaptrude.design.family.getTemplate({
172
+ * kind: "door", subType: "doubleDoor",
173
+ * })
174
+ * // Flex the authored width key, not an assumed parameter name (lengths in SU).
175
+ * await snaptrude.design.family.prepare({ bundle })
176
+ * await snaptrude.design.family.flex({ values: { [contract.width]: 4 } })
177
+ */
178
+ public abstract getTemplate(
179
+ args: PluginDesignFamilyGetTemplateArgs,
180
+ ): PluginApiReturn<PluginFamilyTemplateResult>
86
181
 
87
182
  /**
88
183
  * Validate, compile and preview a family spec. Nothing is committed to the
@@ -124,7 +219,8 @@ export abstract class PluginDesignFamilyApi {
124
219
  * buckets are the host's own diff of what the geometry did — flexing a
125
220
  * driving parameter and reporting them demonstrates the family is parametric.
126
221
  *
127
- * @param args The parameter values to change
222
+ * @param args The parameter values to change. Omit values (or pass an empty
223
+ * record) to report or photograph the current draft without rebuilding it.
128
224
  * @returns The new build plus the geometry delta
129
225
  *
130
226
  * @examplePrompt Change it to 6 bays
@@ -137,25 +233,49 @@ export abstract class PluginDesignFamilyApi {
137
233
  * ```
138
234
  */
139
235
  public abstract flex(
140
- args: PluginDesignFamilyFlexArgs,
236
+ args?: PluginDesignFamilyFlexArgs,
141
237
  ): PluginApiReturn<PluginFamilyFlexResult>
142
238
 
143
239
  /**
144
- * The prepared family's spec bundle, or `null` when nothing is prepared.
240
+ * A family's spec bundle: the prepared draft's, or — to CHANGE a saved
241
+ * family — that family's recipe, opened as the new draft.
242
+ *
243
+ * - No arguments: the prepared draft, or `null` when nothing is prepared.
244
+ * - `{ id, version? }`: open a saved family from the library (newest version
245
+ * when `version` is omitted).
246
+ * - `{ component }`: open the family (at its version) of that placed copy.
247
+ *
248
+ * Opening makes the recipe the current draft and returns `editing` — which
249
+ * saved family it is. Edit it with `prepare` / `flex`, then call `create`.
250
+ * On that save the APP shows the user its own window asking whether to
251
+ * update the family (every placed copy in this project moves to the new
252
+ * version) or save it as a new family — "new family" is the default. Never
253
+ * ask the user this yourself, and never promise either outcome: report what
254
+ * `create` returns.
145
255
  *
146
- * @returns The draft bundle, or `null`
256
+ * @param args Optional: which saved family to open
257
+ * @returns The bundle (plus `editing` when it edits a saved family), or `null`
258
+ *
259
+ * @examplePrompt Make the chairs I placed 50 mm wider
260
+ * @examplePrompt Change the Louvre Screen family so its blades are deeper
147
261
  *
148
262
  * # Example
149
263
  * ```ts
150
- * const bundle = await snaptrude.design.family.get()
264
+ * const draft = await snaptrude.design.family.get()
265
+ * const family = await snaptrude.design.family.get({ component })
266
+ * // family.editing → { familyId, version, name, library }
151
267
  * ```
152
268
  */
153
- public abstract get(): PluginApiReturn<PluginFamilyBundle | null>
269
+ public abstract get(
270
+ args?: PluginDesignFamilyGetArgs | string,
271
+ ): PluginApiReturn<PluginFamilyGetResult | null>
154
272
 
155
273
  /**
156
- * Every parametric family registered in this session — native and imported.
274
+ * Every available parametric family, merging the session registry and catalog.
157
275
  *
158
- * @returns One summary per family
276
+ * @param args Opt into compact lines for generic discovery. The default stays
277
+ * object-shaped so existing callers retain version, openingKind and subType.
278
+ * @returns Summary objects, or `id | label | category | kind` lines when compact.
159
279
  *
160
280
  * @examplePrompt What families are available?
161
281
  *
@@ -164,7 +284,35 @@ export abstract class PluginDesignFamilyApi {
164
284
  * const families = await snaptrude.design.family.list()
165
285
  * ```
166
286
  */
167
- public abstract list(): PluginApiReturn<PluginFamilySummary[]>
287
+ public abstract list(
288
+ args: PluginDesignFamilyListArgs & { compact: true },
289
+ ): PluginApiReturn<string[]>
290
+ public abstract list(
291
+ args?: PluginDesignFamilyListArgs & { compact?: false },
292
+ ): PluginApiReturn<PluginFamilySummary[]>
293
+ public abstract list(
294
+ args?: PluginDesignFamilyListArgs,
295
+ ): PluginApiReturn<PluginFamilySummary[] | string[]>
296
+
297
+ /**
298
+ * Read one saved catalog definition without opening a draft or placing geometry.
299
+ * Both id and version are required. The catalog checks access on every read.
300
+ * Length defaults and bounds are in mm, ready for network graph overrides;
301
+ * angle parameters retain degrees, identified by each parameter's unit.
302
+ * existing getParameters/place/setParameters continue to use Snaptrude units.
303
+ * Defaults include current catalog overrides, which can change without a new version.
304
+ * No scene, registry, history, or persistence state is changed.
305
+ * Missing/inaccessible pins and invalid recipes fail; no latest-version fallback.
306
+ * This metadata does not certify connectors or engineering compatibility.
307
+ *
308
+ * @param args Exact saved catalog id and version, from list()
309
+ * @returns Compact parameter metadata, not geometry or the recipe bundle
310
+ * @example
311
+ * const definition = await snaptrude.design.family.getDefinition({ id, version })
312
+ */
313
+ public abstract getDefinition(
314
+ args: PluginDesignFamilyGetDefinitionArgs,
315
+ ): PluginApiReturn<PluginFamilyDefinition>
168
316
 
169
317
  /**
170
318
  * Put a family into the model — either the prepared draft or any family from
@@ -188,6 +336,13 @@ export abstract class PluginDesignFamilyApi {
188
336
  * To COPY a placed component, place its family again at the new position with
189
337
  * the same `values` — there is no separate copy call.
190
338
  *
339
+ * HOSTED families (doors, windows — anything with an `opening`, a `host`
340
+ * block, or a void with `cuts: "host"`) take `host`: a wall or a placed
341
+ * family. The family is set into the host's face nearest `position` (the
342
+ * host's middle when omitted), and the host is cut — a system wall by the
343
+ * wall cutter, a family by its own recipe. Host-bound parameters then follow
344
+ * the host (a door's wall thickness follows the wall).
345
+ *
191
346
  * @param args Which family, where, and at what parameter values
192
347
  * @returns What was placed; `component` is present only in positioned mode
193
348
  *
@@ -205,8 +360,14 @@ export abstract class PluginDesignFamilyApi {
205
360
  * })
206
361
  * await snaptrude.design.transform.move([component], { x: 2, y: 0, z: 0 })
207
362
  *
363
+ * // on the first floor — position.y is an offset above storey 2's base
364
+ * await snaptrude.design.family.place({ id: familyId, position: { x: 4, y: 0, z: 0 }, storey: 2 })
365
+ *
208
366
  * // interactive — the user clicks
209
367
  * await snaptrude.design.family.place()
368
+ *
369
+ * // hosted — a door in a wall, near a point along it
370
+ * await snaptrude.design.family.place({ id: doorId, host: wall, position: { x: 3, y: 0, z: 0 } })
210
371
  * ```
211
372
  */
212
373
  public abstract place(
@@ -249,6 +410,34 @@ export abstract class PluginDesignFamilyApi {
249
410
  * that does not is accepted and stored (it round-trips to Revit) but nothing
250
411
  * visible happens — say so rather than claiming a change the user cannot see.
251
412
  *
413
+ * NESTED children: `children: { "<path>": { key: value } }` sets a nested
414
+ * child's own values (every copy of it). A value its parent sets is sent up
415
+ * to what it follows when that is a plain relationship; otherwise it is
416
+ * refused with what to change instead (see `refused`).
417
+ *
418
+ * LINKS: `links: { key: { from, expr } }` makes this family's `key` follow
419
+ * another placed object — `from` is a family (it reports its parameters and
420
+ * outputs) or a wall (it reports thickness, length, height); `expr` is an
421
+ * expression over those reports. `null` removes a link. Links that would
422
+ * make objects follow each other in a loop are refused. Values accept unit
423
+ * literals ("600mm").
424
+ *
425
+ * RELATIONS: `relations: { add: [...], remove: ["r1"] }` sets how the family
426
+ * sits against other placed objects or cuts its host:
427
+ * - `attach` — `{ kind: "attach", side: "base" | "top", to: <object> | { level, face? }, offset? }`:
428
+ * sits on a surface, or its top reaches one (reports `reach`, `gap`, `surface`).
429
+ * - `join` — `{ kind: "join", end, to: <family>, toEnd, bindOther? }`: its end moves onto
430
+ * the other's end (reports `angle`, `absAngle`, `mitre`).
431
+ * - `span` — `{ kind: "span", from, to, at? }`: pinned between two objects, running along
432
+ * its +X (reports `distance`, `run`, `rise`, `slope`, `from.*`, `to.*`).
433
+ * - `path` — `{ kind: "path", from? | points, closed? }`: follows a wall's centreline, a
434
+ * family's run or given points (reports `length`; the recipe reads the path).
435
+ * - `cut` — `{ kind: "cut", to: "host" }`: applies recipe voids to the current host.
436
+ * Removing it closes the opening without detaching or deleting the family.
437
+ * Door/window placement adds it. Hosting alone does not cut. No parameter binds.
438
+ * Placement relations take `bind: { <param>: "<expression over its reports>" }`; bound
439
+ * parameters are read-only. A relation with an existing `id` replaces it.
440
+ *
252
441
  * `length` values are Snaptrude units: the same 254 mm unit as the kernel's
253
442
  * internal (`"babylon"`) unit — convert the user's number with
254
443
  * `core.units.convert` first; a bare `400` is 400 SU, not 400 mm. Kernel part
@@ -263,6 +452,15 @@ export abstract class PluginDesignFamilyApi {
263
452
  * # Example
264
453
  * ```ts
265
454
  * await snaptrude.design.family.setParameters({ component, values: { seats: 8 } })
455
+ * await snaptrude.design.family.setParameters({ component, children: { chair: { width: "380mm" } } })
456
+ * await snaptrude.design.family.setParameters({
457
+ * component: railing,
458
+ * links: { steps: { from: stair, expr: "steps" }, side: { from: stair, expr: "width - 40mm" } },
459
+ * })
460
+ * await snaptrude.design.family.setParameters({
461
+ * component: post,
462
+ * relations: { add: [{ kind: "attach", side: "top", to: slab, bind: { height: "reach" } }] },
463
+ * })
266
464
  * ```
267
465
  */
268
466
  public abstract setParameters(
@@ -272,12 +470,24 @@ export abstract class PluginDesignFamilyApi {
272
470
  /**
273
471
  * Save the prepared bundle to the library — the user's personal library
274
472
  * unless `teamId` is given. {@linkcode PluginDesignFamilyApi.place} already
275
- * saves, so `create` is for sharing with a team. When nothing of the family is
276
- * in the model yet, one instance is also dropped at the centre of the current
277
- * view on the active storey (`placed: true`), so a save is never invisible.
473
+ * saves generic components. Hosted doors/windows save without placement;
474
+ * they need a wall and position through `design.create.door` / `design.create.window`.
475
+ * For a non-hosted family with no placed instance, saving also drops one
476
+ * standalone sample at the current view centre on the active storey
477
+ * (`placed: true`). This includes architectural families: the sample does
478
+ * not assign a family to a native host.
479
+ *
480
+ * When the draft edits a saved family (opened with `get({ id })` /
481
+ * `get({ component })`), or its name is already taken in that library, the
482
+ * APP asks the user in its own window whether to save a new version of that
483
+ * family — every placed copy in this project moves to it, and families
484
+ * nesting it may be offered theirs — or a new family ("new family" is the
485
+ * default). The agent never asks this itself and cannot answer the window.
486
+ * `decision` reports what the user chose; `"cancelled"` means nothing was
487
+ * saved (`ok: false`) and nothing will auto-save until `create` is called again.
278
488
  *
279
489
  * @param args Optional library name and team scope
280
- * @returns The catalogued definition's id and version
490
+ * @returns The catalogued definition's id and version, and what the user chose
281
491
  *
282
492
  * @examplePrompt Save this as a family called Vertical Louvre
283
493
  * @examplePrompt Add it to my library
@@ -314,14 +524,56 @@ export abstract class PluginDesignFamilyApi {
314
524
  * ```
315
525
  */
316
526
  public abstract delete(
317
- args?: PluginDesignFamilyDeleteArgs,
527
+ args?: PluginDesignFamilyDeleteArgs | string,
318
528
  ): PluginApiReturn<PluginFamilyDeleteResult>
319
529
  }
320
530
 
531
+ /** Optional reference sections keep unrelated grammar out of the core response. */
532
+ export const PluginFamilyReferenceSection = z.enum([
533
+ "architectural",
534
+ "networks",
535
+ "relations",
536
+ ])
537
+ export type PluginFamilyReferenceSection = z.infer<
538
+ typeof PluginFamilyReferenceSection
539
+ >
540
+
541
+ /** Omit section for the core grammar, workflow and example catalogue. */
542
+ export const PluginDesignFamilyGetReferenceArgs = z.object({
543
+ section: PluginFamilyReferenceSection.optional(),
544
+ })
545
+ export type PluginDesignFamilyGetReferenceArgs = z.infer<
546
+ typeof PluginDesignFamilyGetReferenceArgs
547
+ >
548
+
549
+ /** Compact output is opt-in: existing object consumers must retain their metadata. */
550
+ export const PluginDesignFamilyListArgs = z.object({
551
+ compact: z.boolean().optional(),
552
+ })
553
+ export type PluginDesignFamilyListArgs = z.infer<
554
+ typeof PluginDesignFamilyListArgs
555
+ >
556
+
321
557
  // ---------------------------------------------------------------------------
322
558
  // Result types
323
559
  // ---------------------------------------------------------------------------
324
560
 
561
+ /** One worked example in the catalogue returned by `getExamples`. */
562
+ export interface PluginFamilyExampleSummary {
563
+ id: string
564
+ label: string
565
+ discipline: "architecture" | "structure" | "mep" | "furniture"
566
+ /** The patterns it demonstrates, e.g. "nesting", "slot", "path", "union", "trim". */
567
+ teaches: string[]
568
+ /** Size of the bundle as compact JSON, kilobytes — what fetching it costs. */
569
+ sizeKB: number
570
+ }
571
+
572
+ /** One worked example with its full bundle, from `getExamples({ id })`. */
573
+ export interface PluginFamilyExample extends PluginFamilyExampleSummary {
574
+ bundle: PluginFamilyBundle
575
+ }
576
+
325
577
  /** The family-spec DSL reference returned by `getReference`. */
326
578
  export interface PluginFamilyReference {
327
579
  /** Grammar, op table, expression whitelist, repair contract, worked example. */
@@ -340,6 +592,34 @@ export interface PluginFamilyReference {
340
592
  export interface PluginFamilyBundle {
341
593
  root: Record<string, unknown>
342
594
  deps?: Record<string, Record<string, unknown>>
595
+ /** Connector metadata saved with the recipe; network placement evaluates these expressions. */
596
+ network?: NetworkFamilyContract
597
+ }
598
+
599
+ /** Which saved family a draft edits. */
600
+ export interface PluginFamilyEditing {
601
+ /** Library id of the family being edited. */
602
+ familyId: string
603
+ /** The version that was opened. */
604
+ version: number
605
+ /** Its library name — an update saves under this name. */
606
+ name: string
607
+ /** "your library", or the team library's name. */
608
+ library: string
609
+ }
610
+
611
+ /** What `get` returns: the bundle, plus `editing` when the draft edits a saved family. */
612
+ export interface PluginFamilyGetResult extends PluginFamilyBundle {
613
+ editing?: PluginFamilyEditing
614
+ }
615
+
616
+ /** A family at a version, as `create` reports parents it did or did not update. */
617
+ export interface PluginFamilyVersionRef {
618
+ id: string
619
+ name: string
620
+ version: number
621
+ /** Set when the user chose to update it but the update failed. */
622
+ error?: string
343
623
  }
344
624
 
345
625
  /** Axis-aligned extents of a built family. Snaptrude units (1 SU = 254 mm), +Y up. */
@@ -387,6 +667,10 @@ export interface PluginFamilyDiagnostic {
387
667
  | "depth"
388
668
  | "bad-dimension"
389
669
  | "proportion"
670
+ /** A part the geometry kernel could not build exactly; drawn approximately. */
671
+ | "kernel-fallback"
672
+ /** A part whose hole loop crosses its outline. */
673
+ | "profile-hole"
390
674
  message: string
391
675
  hint?: string
392
676
  }
@@ -471,6 +755,13 @@ export interface PluginFamilySummary {
471
755
  version: number
472
756
  label: string
473
757
  category?: string
758
+ /** The category's type token (`root.subType`), e.g. singleDoor or casementDouble. */
759
+ subType?: string
760
+ /**
761
+ * Opening contract kind, distinct from provenance (`kind`). Place with the
762
+ * positional `design.create.door` / `window` API and edit via family parameters.
763
+ */
764
+ openingKind?: "door" | "window"
474
765
  /** Number of rows on the family's parameter sheet. */
475
766
  parameters: number
476
767
  /**
@@ -487,6 +778,49 @@ export interface PluginFamilySummary {
487
778
  kind: "native" | "revit"
488
779
  }
489
780
 
781
+ /** Exact catalog pin. Unsaved session definitions are not catalog inventory. */
782
+ export const PluginDesignFamilyGetDefinitionArgs = z.object({
783
+ id: z.string().min(1),
784
+ version: z.number().int().positive(),
785
+ })
786
+ export type PluginDesignFamilyGetDefinitionArgs = z.infer<
787
+ typeof PluginDesignFamilyGetDefinitionArgs
788
+ >
789
+
790
+ /** Saved-family metadata. Physical lengths use mm; angle entries retain degrees. */
791
+ export const PluginFamilyDefinitionParameter = z.object({
792
+ key: z.string(),
793
+ label: z.string(),
794
+ type: z.enum(["length", "integer", "enum", "text", "boolean"]),
795
+ /** Angles use the legacy length type. Check unit before interpreting numeric values. */
796
+ unit: z.enum(["mm", "degrees"]).optional(),
797
+ default: z.union([z.number().finite(), z.string()]),
798
+ min: z.number().finite().optional(),
799
+ max: z.number().finite().optional(),
800
+ options: z.array(z.string()).optional(),
801
+ drivesGeometry: z.boolean(),
802
+ readOnly: z.boolean(),
803
+ formulaDriven: z.boolean(),
804
+ /** False means shared/type: placement cannot change it for one instance. */
805
+ isInstance: z.boolean(),
806
+ })
807
+ export type PluginFamilyDefinitionParameter = z.infer<
808
+ typeof PluginFamilyDefinitionParameter
809
+ >
810
+
811
+ /** Read-only catalog inventory. Boolean defaults retain the family DSL's Yes/No strings. */
812
+ export const PluginFamilyDefinition = z.object({
813
+ id: z.string(),
814
+ version: z.number().int().positive(),
815
+ label: z.string(),
816
+ category: z.string().optional(),
817
+ lengthUnit: z.literal("mm"),
818
+ parameters: z.array(PluginFamilyDefinitionParameter),
819
+ /** Pinned connector metadata. Absence means this family cannot build a topology network. */
820
+ network: NetworkFamilyContract.optional(),
821
+ })
822
+ export type PluginFamilyDefinition = z.infer<typeof PluginFamilyDefinition>
823
+
490
824
  /** What `place` reports. */
491
825
  export interface PluginFamilyPlaceResult {
492
826
  ok: boolean
@@ -515,6 +849,104 @@ export interface PluginFamilyInstanceParameters {
515
849
  parts?: PluginFamilyPart[]
516
850
  /** Present when `getParameters` was asked for a `snapshot`. */
517
851
  snapshot?: PluginFamilySnapshot
852
+ /** The family's `outputs`, as last built. */
853
+ reports?: Record<string, number | string>
854
+ /** Every declared nested child, as last built. */
855
+ children?: PluginFamilyNestedChild[]
856
+ /** Parameters that follow another object (links) or the host. */
857
+ follows?: PluginFamilyFollow[]
858
+ /** Where a hosted family sits: a system wall, or a family by id. */
859
+ host?: ({ kind: "wall" } | { kind: "family"; id: string }) & {
860
+ /** True when the host no longer has room for it: it is out of the model until it fits again. */
861
+ parked?: boolean
862
+ }
863
+ /** Guests hosted in THIS family that no longer fit it (parked); they return when they fit. */
864
+ parkedGuests?: { id: string; family: string }[]
865
+ /** How it sits against other placed objects (attach, join, span, path), with current reports. */
866
+ relations?: PluginFamilyRelation[]
867
+ /** setParameters only: the edits refused, each with what to change instead. */
868
+ refused?: string[]
869
+ }
870
+
871
+ /** A nested child of a placed family. */
872
+ export interface PluginFamilyNestedChild {
873
+ /** "chair", or "chair/cushion" for a child of a child — the key for `children` edits. */
874
+ path: string
875
+ label: string
876
+ /** The family it is now (a slot's current pick). */
877
+ family: string
878
+ familyLabel: string
879
+ /** How many copies the parent places. */
880
+ copies: number
881
+ slot?: { expr: string; options: string[] }
882
+ values: Record<string, number | string>
883
+ /** Values the parent sets, with the expression it sets them by. */
884
+ setBy: Record<string, string | undefined>
885
+ reports: Record<string, number | string>
886
+ }
887
+
888
+ /** A placed relation as stored, plus what it measures now. */
889
+ export type PluginFamilyRelation = PluginFamilyRelationInput & {
890
+ id: string
891
+ /** "Attached to", "Joins", "Spans between", "Along a path", "Cuts host". */
892
+ label: string
893
+ summary: string
894
+ /** False when a target is gone: the family stayed where it was. */
895
+ live: boolean
896
+ reports: Record<string, number | string>
897
+ }
898
+
899
+ /** One relation to add (`relations.add`). Targets are handles, except a cut's symbolic "host". */
900
+ export type PluginFamilyRelationInput = {
901
+ /** Replaces the relation with this id; a new one gets "r1", "r2"… */
902
+ id?: string
903
+ /** This family's parameter → an expression over the relation's reports and own parameters. */
904
+ bind?: Record<string, string>
905
+ } & (
906
+ | {
907
+ kind: "attach"
908
+ side: "base" | "top"
909
+ to: ComponentHandle | { level: number; face?: "base" | "top" }
910
+ offset?: number
911
+ }
912
+ | {
913
+ kind: "join"
914
+ end: "start" | "end"
915
+ to: ComponentHandle
916
+ toEnd: "start" | "end"
917
+ /** The other family's parameters, over the same reports. */
918
+ bindOther?: Record<string, string>
919
+ }
920
+ | {
921
+ kind: "span"
922
+ from: ComponentHandle
923
+ to: ComponentHandle
924
+ at?: "top" | "centre" | "base"
925
+ }
926
+ | {
927
+ kind: "path"
928
+ from?: ComponentHandle
929
+ points?: [number, number, number][]
930
+ closed?: boolean
931
+ }
932
+ | {
933
+ /** Apply recipe voids to the existing host without adding another host ID. */
934
+ kind: "cut"
935
+ to: "host"
936
+ bind?: never
937
+ }
938
+ )
939
+
940
+ /** One parameter that follows something else. */
941
+ export interface PluginFamilyFollow {
942
+ key: string
943
+ /** "relation": set by a placed relation — `from` is the relation's id. */
944
+ kind: "link" | "host" | "relation"
945
+ /** The source's id. */
946
+ from: string
947
+ fromLabel: string
948
+ /** The expression over the source's reports (for a host: the report name). */
949
+ expr: string
518
950
  }
519
951
 
520
952
  /** What `create` reports. */
@@ -527,6 +959,26 @@ export interface PluginFamilyCreateResult {
527
959
  placed?: boolean
528
960
  /** Host guidance for the caller, e.g. that an instance was placed at the view centre. */
529
961
  hint?: string
962
+ /**
963
+ * What the user chose in the app's window: `"version"` (a new version of
964
+ * the saved family), `"new"` (a new family), `"cancelled"` (nothing saved).
965
+ * `"saved"` = no question was needed.
966
+ */
967
+ decision?: "version" | "new" | "cancelled" | "saved"
968
+ /** The library name it was saved under. */
969
+ name?: string
970
+ /** Placed copies in this project moved to the new version (the family's and updated parents'). */
971
+ movedCopies?: number
972
+ /** What the moved copies lost because the new version no longer has it. */
973
+ dropped?: {
974
+ values: Array<{ copy: string; key: string }>
975
+ childValues: Array<{ copy: string; path: string }>
976
+ follows: Array<{ copy: string; key: string; kind: "link" | "relation" }>
977
+ }
978
+ /** Families nesting this one that the user chose to update, at their new versions. */
979
+ parentsUpdated?: PluginFamilyVersionRef[]
980
+ /** Families nesting this one that keep their version. */
981
+ parentsKept?: PluginFamilyVersionRef[]
530
982
  error?: string
531
983
  }
532
984
 
@@ -555,6 +1007,7 @@ export interface PluginFamilyDeleteResult {
555
1007
  export const PluginFamilyBundleSchema = z.object({
556
1008
  root: z.record(z.string(), z.unknown()),
557
1009
  deps: z.record(z.string(), z.record(z.string(), z.unknown())).optional(),
1010
+ network: NetworkFamilyContract.optional(),
558
1011
  })
559
1012
 
560
1013
  /**
@@ -576,16 +1029,71 @@ export const PluginFamilyBundleSchema = z.object({
576
1029
  * the viewport is not moved — and comes back as a PNG data URL in the result's
577
1030
  * `snapshot.image`. Through the agent's `run_worker`, return the result and the
578
1031
  * image reaches the model as an image.
1032
+ *
1033
+ * `focus` makes it a close-up: `{ parts: ["seat", "back", "slat@*"] }` frames
1034
+ * the joint where those groups meet (`*` matches any run of characters;
1035
+ * nested names are `a/b`), or their full bounds if they do not meet. A margin
1036
+ * of a quarter of their size (at least 50 mm) surrounds the joint.
1037
+ * `{ box: { min, max } }` frames a region in family space
1038
+ * (Snaptrude units, +Y up). Every other part is still drawn; the dimension line
1039
+ * then gives the framed region and the result says `closeUp: true`. A part
1040
+ * name that matches nothing fails the snapshot with a list of real names.
579
1041
  */
1042
+ export const PluginFamilySnapshotFocus = z.union([
1043
+ z.object({ parts: z.array(z.string().min(1)).min(1) }),
1044
+ z.object({
1045
+ box: z
1046
+ .object({
1047
+ min: z.tuple([
1048
+ z.number().finite(),
1049
+ z.number().finite(),
1050
+ z.number().finite(),
1051
+ ]),
1052
+ max: z.tuple([
1053
+ z.number().finite(),
1054
+ z.number().finite(),
1055
+ z.number().finite(),
1056
+ ]),
1057
+ })
1058
+ .refine(
1059
+ ({ min, max }) => min.every((value, axis) => value <= max[axis]),
1060
+ {
1061
+ message: "Each minimum must be at or below its maximum",
1062
+ },
1063
+ ),
1064
+ }),
1065
+ ])
1066
+ export type PluginFamilySnapshotFocus = z.infer<
1067
+ typeof PluginFamilySnapshotFocus
1068
+ >
1069
+
1070
+ /** One close-up tile; omission of view uses the sheet's first view. */
1071
+ export const PluginFamilySnapshotCloseUp = z.intersection(
1072
+ PluginFamilySnapshotFocus,
1073
+ z.object({ view: z.enum(["iso", "front", "right", "top"]).optional() }),
1074
+ )
1075
+ export type PluginFamilySnapshotCloseUp = z.infer<
1076
+ typeof PluginFamilySnapshotCloseUp
1077
+ >
1078
+
580
1079
  export const PluginFamilySnapshotOption = z.union([
581
1080
  z.boolean(),
582
1081
  z.object({
583
1082
  views: z.array(z.enum(["iso", "front", "right", "top"])).optional(),
584
1083
  /** Pixels per tile (square), 64–2048; default 512. */
585
1084
  tileSize: z.number().optional(),
1085
+ /** One focus across views, or 1–6 independent close-up tiles in one image. */
1086
+ focus: z
1087
+ .union([
1088
+ PluginFamilySnapshotFocus,
1089
+ z.array(PluginFamilySnapshotCloseUp).min(1).max(6),
1090
+ ])
1091
+ .optional(),
586
1092
  }),
587
1093
  ])
588
- export type PluginFamilySnapshotOption = z.infer<typeof PluginFamilySnapshotOption>
1094
+ export type PluginFamilySnapshotOption = z.infer<
1095
+ typeof PluginFamilySnapshotOption
1096
+ >
589
1097
 
590
1098
  /** The picture block on prepare / flex / getParameters results when `snapshot` was asked for. */
591
1099
  export interface PluginFamilySnapshot {
@@ -593,9 +1101,18 @@ export interface PluginFamilySnapshot {
593
1101
  /** PNG data URL of the composed sheet. */
594
1102
  image?: string
595
1103
  views?: ("iso" | "front" | "right" | "top")[]
596
- /** [width, height, depth] in Snaptrude units. */
1104
+ /** [width, height, depth] in SU. For a close-up list, this remains the whole family's size. */
597
1105
  size?: [number, number, number]
598
1106
  ms?: number
1107
+ /** True when the sheet is a close-up (`focus`), not the whole family. */
1108
+ closeUp?: boolean
1109
+ /** Per-tile results for a focus list. One invalid target does not discard other tiles. */
1110
+ closeUps?: Array<{
1111
+ view: "iso" | "front" | "right" | "top"
1112
+ label: string
1113
+ size?: [number, number, number]
1114
+ error?: string
1115
+ }>
599
1116
  /** Set when the picture could not be taken (e.g. the legacy renderer); the call itself still succeeded. */
600
1117
  error?: string
601
1118
  }
@@ -605,21 +1122,105 @@ export const PluginDesignFamilyPrepareArgs = z.object({
605
1122
  spec: PluginFamilyBundleSchema.optional(),
606
1123
  snapshot: PluginFamilySnapshotOption.optional(),
607
1124
  })
608
- export type PluginDesignFamilyPrepareArgs = z.infer<typeof PluginDesignFamilyPrepareArgs>
1125
+ export type PluginDesignFamilyPrepareArgs = z.infer<
1126
+ typeof PluginDesignFamilyPrepareArgs
1127
+ >
609
1128
 
610
1129
  /**
611
1130
  * Arguments for {@linkcode PluginDesignFamilyApi.flex}.
612
1131
  *
613
1132
  * | Property | Type | Description |
614
1133
  * |---|---|---|
615
- * | `values` | `Record<string, number \| string>` | Parameter values to change (partial) |
1134
+ * | `values` | `Record<string, number \| string>` | Optional sparse changes; omission does not rebuild |
616
1135
  * | `snapshot` | {@linkcode PluginFamilySnapshotOption} | Also return a picture of the flexed family |
617
1136
  */
618
1137
  export const PluginDesignFamilyFlexArgs = z.object({
619
- values: z.record(z.string(), z.union([z.number(), z.string()])),
1138
+ values: z.record(z.string(), z.union([z.number(), z.string()])).optional(),
620
1139
  snapshot: PluginFamilySnapshotOption.optional(),
621
1140
  })
622
- export type PluginDesignFamilyFlexArgs = z.infer<typeof PluginDesignFamilyFlexArgs>
1141
+ export type PluginDesignFamilyFlexArgs = z.infer<
1142
+ typeof PluginDesignFamilyFlexArgs
1143
+ >
1144
+
1145
+ /**
1146
+ * Arguments for {@linkcode PluginDesignFamilyApi.get}. All optional — no
1147
+ * arguments returns the prepared draft.
1148
+ *
1149
+ * | Property | Type | Description |
1150
+ * |---|---|---|
1151
+ * | `id` | `string` | A saved family to open for editing, from {@linkcode PluginDesignFamilyApi.list} |
1152
+ * | `version` | `number` | Exact version to open; defaults to the newest |
1153
+ * | `component` | {@linkcode ComponentHandle} | Open the family of this placed copy, at its version |
1154
+ */
1155
+ export const PluginDesignFamilyGetArgs = z.object({
1156
+ id: z.string().optional(),
1157
+ version: z.number().int().positive().optional(),
1158
+ component: ComponentHandle.optional(),
1159
+ })
1160
+ export type PluginDesignFamilyGetArgs = z.infer<
1161
+ typeof PluginDesignFamilyGetArgs
1162
+ >
1163
+
1164
+ /**
1165
+ * Arguments for {@linkcode PluginDesignFamilyApi.getExamples}.
1166
+ *
1167
+ * | Property | Type | Description |
1168
+ * |---|---|---|
1169
+ * | `id` | `string` | One example's id — returns its bundle |
1170
+ * | `discipline` | `string` | List only this discipline: architecture, structure, mep, furniture |
1171
+ * | `tag` | `string` | List only examples teaching this pattern (e.g. "path", "union") |
1172
+ */
1173
+ export const PluginDesignFamilyGetExamplesArgs = z.object({
1174
+ id: z.string().optional(),
1175
+ discipline: z
1176
+ .enum(["architecture", "structure", "mep", "furniture"])
1177
+ .optional(),
1178
+ tag: z.string().optional(),
1179
+ })
1180
+ export type PluginDesignFamilyGetExamplesArgs = z.infer<
1181
+ typeof PluginDesignFamilyGetExamplesArgs
1182
+ >
1183
+
1184
+ /**
1185
+ * Arguments for {@linkcode PluginDesignFamilyApi.getTemplate}.
1186
+ * `kind` selects the opening contract. `subType` is a nonempty type token;
1187
+ * omission selects singleDoor / fixedWindow. Unsupported tokens return the
1188
+ * default template with guidance rather than preventing custom subtype authoring.
1189
+ */
1190
+ export const PluginDesignFamilyGetTemplateArgs = z.object({
1191
+ kind: z.enum(["door", "window"]),
1192
+ subType: z.string().min(1).optional(),
1193
+ })
1194
+ export type PluginDesignFamilyGetTemplateArgs = z.infer<
1195
+ typeof PluginDesignFamilyGetTemplateArgs
1196
+ >
1197
+
1198
+ /**
1199
+ * Maps opening dimensions to authored length parameter KEYS, not values.
1200
+ * Width and height describe the wall hole; sill measures from the wall base
1201
+ * to the hole bottom (windows). Wall thickness follows the host wall.
1202
+ * The recipe and family parameter APIs use Snaptrude units (1 SU = 254 mm).
1203
+ */
1204
+ export interface PluginFamilyOpeningContract {
1205
+ kind: "door" | "window"
1206
+ width: string
1207
+ height: string
1208
+ sill?: string
1209
+ wallThickness?: string
1210
+ }
1211
+
1212
+ /** Starter recipe metadata returned by `getTemplate`; instances are ParametricComponents. */
1213
+ export interface PluginFamilyTemplateResult {
1214
+ /** Validated recipe bundle: edit its style while preserving the contract. */
1215
+ bundle: PluginFamilyBundle
1216
+ contract: PluginFamilyOpeningContract
1217
+ /** Actual template subtype, including when an unsupported token used the default. */
1218
+ subType: string
1219
+ /** Authoring rules and any note about an unsupported requested subtype. */
1220
+ guidance: string[]
1221
+ /** Available starter templates for the requested kind. */
1222
+ available: Array<{ subType: string; label: string; teaches: string[] }>
1223
+ }
623
1224
 
624
1225
  /**
625
1226
  * Arguments for {@linkcode PluginDesignFamilyApi.create}.
@@ -633,7 +1234,9 @@ export const PluginDesignFamilyCreateArgs = z.object({
633
1234
  name: z.string().optional(),
634
1235
  teamId: z.string().optional(),
635
1236
  })
636
- export type PluginDesignFamilyCreateArgs = z.infer<typeof PluginDesignFamilyCreateArgs>
1237
+ export type PluginDesignFamilyCreateArgs = z.infer<
1238
+ typeof PluginDesignFamilyCreateArgs
1239
+ >
637
1240
 
638
1241
  /**
639
1242
  * Arguments for {@linkcode PluginDesignFamilyApi.delete}.
@@ -647,7 +1250,9 @@ export const PluginDesignFamilyDeleteArgs = z.object({
647
1250
  id: z.string().optional(),
648
1251
  instances: z.boolean().optional(),
649
1252
  })
650
- export type PluginDesignFamilyDeleteArgs = z.infer<typeof PluginDesignFamilyDeleteArgs>
1253
+ export type PluginDesignFamilyDeleteArgs = z.infer<
1254
+ typeof PluginDesignFamilyDeleteArgs
1255
+ >
651
1256
 
652
1257
  /**
653
1258
  * Arguments for {@linkcode PluginDesignFamilyApi.place}.
@@ -656,20 +1261,24 @@ export type PluginDesignFamilyDeleteArgs = z.infer<typeof PluginDesignFamilyDele
656
1261
  * |---|---|---|
657
1262
  * | `id` | `string` | Family to place, from {@linkcode PluginDesignFamilyApi.list}. Omit to place the prepared draft |
658
1263
  * | `version` | `number` | Exact version; defaults to the newest known |
659
- * | `position` | `{ x, y, z }` | Place HERE, without user interaction. Omit to arm the cursor instead |
1264
+ * | `position` | `Vec3Handle` or `{ x, y, z }` | Place here in SU. Prefer a vec3 handle; coordinates remain for existing callers. Omit to arm the cursor |
660
1265
  * | `rotation` | `number` | Yaw in degrees about +Y. Only with `position` |
661
1266
  * | `values` | `Record<string, number \| string>` | Parameter values for THIS instance |
662
- * | `storey` | `number` | Storey to place on; defaults to the active one |
1267
+ * | `storey` | `number` | Target storey (integer: 1 ground, 2 first floor, -1 basement; must already exist). The family is lifted to that storey's base and `position.y` is an offset above it, like `design.create.walls`. Without it, `position` is absolute and the storey is derived from where the family's geometry sits. Ignored with `host`: a hosted family follows its host |
663
1268
  */
664
1269
  export const PluginDesignFamilyPlaceArgs = z.object({
665
1270
  id: z.string().optional(),
666
1271
  version: z.number().int().positive().optional(),
667
- position: Vec3Components.optional(),
1272
+ position: z.union([Vec3Handle, Vec3Components]).optional(),
668
1273
  rotation: z.number().optional(),
669
1274
  values: z.record(z.string(), z.union([z.number(), z.string()])).optional(),
670
1275
  storey: z.number().int().optional(),
1276
+ /** Hosted families: wall or family host. Values apply before fit checks and cutting. */
1277
+ host: ComponentHandle.optional(),
671
1278
  })
672
- export type PluginDesignFamilyPlaceArgs = z.infer<typeof PluginDesignFamilyPlaceArgs>
1279
+ export type PluginDesignFamilyPlaceArgs = z.infer<
1280
+ typeof PluginDesignFamilyPlaceArgs
1281
+ >
673
1282
 
674
1283
  /**
675
1284
  * Arguments for {@linkcode PluginDesignFamilyApi.getParameters}.
@@ -697,7 +1306,31 @@ export type PluginDesignFamilyGetParametersArgs = z.infer<
697
1306
  */
698
1307
  export const PluginDesignFamilySetParametersArgs = z.object({
699
1308
  component: ComponentHandle,
700
- values: z.record(z.string(), z.union([z.number(), z.string()])),
1309
+ values: z
1310
+ .record(z.string(), z.union([z.number(), z.string()]))
1311
+ .optional()
1312
+ .default({}),
1313
+ /** Nested children's own values: { "<path>": { key: value } }. */
1314
+ children: z
1315
+ .record(z.string(), z.record(z.string(), z.union([z.number(), z.string()])))
1316
+ .optional(),
1317
+ /** Links: { key: { from, expr } | null }. */
1318
+ links: z
1319
+ .record(
1320
+ z.string(),
1321
+ z.union([
1322
+ z.object({ from: ComponentHandle, expr: z.string() }),
1323
+ z.null(),
1324
+ ]),
1325
+ )
1326
+ .optional(),
1327
+ /** Placed relations: { add?: [{ kind, … }], remove?: ["r1"] } — checked by the host. */
1328
+ relations: z
1329
+ .object({
1330
+ add: z.array(z.record(z.string(), z.unknown())).optional(),
1331
+ remove: z.array(z.string()).optional(),
1332
+ })
1333
+ .optional(),
701
1334
  })
702
1335
  export type PluginDesignFamilySetParametersArgs = z.infer<
703
1336
  typeof PluginDesignFamilySetParametersArgs