@snaptrude/plugin-core 0.9.11 → 0.9.12

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snaptrude/plugin-core",
3
- "version": "0.9.11",
3
+ "version": "0.9.12",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -0,0 +1,591 @@
1
+ import * as z from "zod"
2
+ import { PluginApiReturn } from "../../types"
3
+ import { ComponentHandle, Vec3Components } from "../../handles"
4
+
5
+ /**
6
+ * `snaptrude.design.family.*` — author NATIVE parametric families from a JSON spec.
7
+ *
8
+ * A parametric family is a reusable, parameter-driven building component: a
9
+ * curtain-wall panel, a louvre screen, a balustrade, a shading fin. Unlike the
10
+ * rest of `design.*`, you do not build one out of geometry calls — you write a
11
+ * JSON **spec** describing its parameters and its geometry rules, and the host
12
+ * compiles that into a live parametric definition the user can place, flex and
13
+ * save to their library.
14
+ *
15
+ * ## Workflow
16
+ *
17
+ * ```
18
+ * getReference() read the spec DSL once — grammar, ops, expressions, example
19
+ * prepare({ bundle }) validate + compile + show a live ghost; nothing committed
20
+ * flex({ values }) re-run at new values; returns the geometry delta
21
+ * place() arm placement — the USER clicks to drop it
22
+ * create({ name }) commit it to the project's family library
23
+ * ```
24
+ *
25
+ * ## Read getReference() first
26
+ *
27
+ * The spec DSL is a closed expression language with its own op set, coordinate
28
+ * convention (**+Y up, Snaptrude units** — the scene's own unit, 1 SU = 254 mm;
29
+ * literals may carry a unit suffix such as `600mm` or `2ft`)
30
+ * and part-naming law. It is deliberately NOT duplicated in these types:
31
+ * {@linkcode PluginDesignFamilyApi.getReference} returns the whole reference,
32
+ * generated from a family that is actually registered in the host, so the
33
+ * documentation cannot drift from what the compiler accepts.
34
+ *
35
+ * ## The repair contract
36
+ *
37
+ * {@linkcode PluginDesignFamilyApi.prepare} does **not** throw on an invalid
38
+ * spec — it returns `{ ok: false, errors }` where each error carries a JSON
39
+ * pointer into the bundle you sent (e.g. `"/root/geometry/2/body/0/w"`). Read
40
+ * `errors[0].path`, fix that node, and call `prepare` again. A failed `prepare`
41
+ * leaves the previous good draft standing, so it costs nothing but the node you
42
+ * broke. Re-authoring from scratch throws away everything already correct.
43
+ *
44
+ * Accessed via `snaptrude.design.family`.
45
+ */
46
+ export abstract class PluginDesignFamilyApi {
47
+ constructor() {}
48
+
49
+ /**
50
+ * Read the family-spec DSL reference: grammar, the op table, the expression
51
+ * whitelist, the coordinate convention, the repair contract, and a worked
52
+ * example taken from a registered family.
53
+ *
54
+ * Read this before authoring a family for the first time in a session. The
55
+ * DSL is a closed language — a guessed op or function is a rejected spec.
56
+ *
57
+ * @returns The reference text
58
+ *
59
+ * @examplePrompt How do I write a parametric family spec?
60
+ * @examplePrompt Show me the family DSL
61
+ *
62
+ * # Example
63
+ * ```ts
64
+ * const { reference } = await snaptrude.design.family.getReference()
65
+ * ```
66
+ */
67
+ public abstract getReference(): PluginApiReturn<PluginFamilyReference>
68
+
69
+ /**
70
+ * Validate, compile and preview a family spec. Nothing is committed to the
71
+ * model or the library — call {@linkcode PluginDesignFamilyApi.place} or
72
+ * {@linkcode PluginDesignFamilyApi.create} for that.
73
+ *
74
+ * Safe to call repeatedly while iterating. On success, check `partCount` and
75
+ * `bbox` against what you intended before treating the family as correct — a
76
+ * facade that built 3 parts, or a 3 m screen reporting 40 m of height, is a
77
+ * visible bug the validator cannot catch.
78
+ *
79
+ * @param args The spec bundle, as `{ bundle }` or `{ spec }`
80
+ * @returns Build report, or `{ ok: false, errors }` with JSON-pointer paths
81
+ *
82
+ * @examplePrompt Make a louvre screen with adjustable blade count and tilt
83
+ * @examplePrompt Design a curtain wall panel family with 8 bays
84
+ * @examplePrompt Create a railing family I can adjust
85
+ *
86
+ * # Example
87
+ * ```ts
88
+ * const r = await snaptrude.design.family.prepare({ bundle })
89
+ * if (!r.ok) console.error(r.errors[0].path, r.errors[0].message)
90
+ * ```
91
+ */
92
+ public abstract prepare(
93
+ args: PluginDesignFamilyPrepareArgs,
94
+ ): PluginApiReturn<PluginFamilyPrepareResult>
95
+
96
+ /**
97
+ * Re-run the prepared family's recipe at new parameter values.
98
+ *
99
+ * Values are merged over the current ones and clamped to the parameter
100
+ * sheet's min/max, so a partial `{ bays: 6 }` is fine. The returned `changed`
101
+ * buckets are the host's own diff of what the geometry did — flexing a
102
+ * driving parameter and reporting them demonstrates the family is parametric.
103
+ *
104
+ * @param args The parameter values to change
105
+ * @returns The new build plus the geometry delta
106
+ *
107
+ * @examplePrompt Change it to 6 bays
108
+ * @examplePrompt Show me what happens with 20 blades
109
+ *
110
+ * # Example
111
+ * ```ts
112
+ * const { changed } = await snaptrude.design.family.flex({ values: { bays: 6 } })
113
+ * console.log(`removed ${changed.removed}, rebuilt ${changed.rebuilt}`)
114
+ * ```
115
+ */
116
+ public abstract flex(
117
+ args: PluginDesignFamilyFlexArgs,
118
+ ): PluginApiReturn<PluginFamilyFlexResult>
119
+
120
+ /**
121
+ * The prepared family's spec bundle, or `null` when nothing is prepared.
122
+ *
123
+ * @returns The draft bundle, or `null`
124
+ *
125
+ * # Example
126
+ * ```ts
127
+ * const bundle = await snaptrude.design.family.get()
128
+ * ```
129
+ */
130
+ public abstract get(): PluginApiReturn<PluginFamilyBundle | null>
131
+
132
+ /**
133
+ * Every parametric family registered in this session — native and imported.
134
+ *
135
+ * @returns One summary per family
136
+ *
137
+ * @examplePrompt What families are available?
138
+ *
139
+ * # Example
140
+ * ```ts
141
+ * const families = await snaptrude.design.family.list()
142
+ * ```
143
+ */
144
+ public abstract list(): PluginApiReturn<PluginFamilySummary[]>
145
+
146
+ /**
147
+ * Put a family into the model — either the prepared draft or any family from
148
+ * {@linkcode PluginDesignFamilyApi.list}.
149
+ *
150
+ * Two modes, chosen by whether you pass `position`:
151
+ *
152
+ * **With `position`** the component is created there and then, undoably. Use
153
+ * this when you know where it goes — you were given coordinates, or you are
154
+ * copying an existing instance. The returned `component` handle works with
155
+ * `design.transform.move` / `.rotate` and with
156
+ * {@linkcode PluginDesignFamilyApi.setParameters}.
157
+ *
158
+ * **Without `position`** it arms placement mode with a ghost on the cursor
159
+ * and returns immediately — no geometry yet. The USER clicks to drop it, so
160
+ * tell them to. Prefer this when the location is a judgement call.
161
+ *
162
+ * To COPY a placed component, place its family again at the new position with
163
+ * the same `values` — there is no separate copy call.
164
+ *
165
+ * @param args Which family, where, and at what parameter values
166
+ * @returns What was placed; `component` is present only in positioned mode
167
+ *
168
+ * @examplePrompt Place it in the model
169
+ * @examplePrompt Put a louvre screen at the origin
170
+ * @examplePrompt Copy that table two metres to the right
171
+ *
172
+ * # Example
173
+ * ```ts
174
+ * // positioned — geometry exists when this resolves
175
+ * const { component } = await snaptrude.design.family.place({
176
+ * id: "revit:gypsum-stud-partition~90ad72",
177
+ * position: { x: 0, y: 0, z: 0 },
178
+ * rotation: 90,
179
+ * })
180
+ * await snaptrude.design.transform.move([component], { x: 2, y: 0, z: 0 })
181
+ *
182
+ * // interactive — the user clicks
183
+ * await snaptrude.design.family.place()
184
+ * ```
185
+ */
186
+ public abstract place(
187
+ args?: PluginDesignFamilyPlaceArgs,
188
+ ): PluginApiReturn<PluginFamilyPlaceResult>
189
+
190
+ /**
191
+ * Read a placed component's current parameter values and its sheet.
192
+ *
193
+ * Use before {@linkcode PluginDesignFamilyApi.setParameters} so you change
194
+ * what is actually there: parameter KEYS are the family's own, not the
195
+ * label the user says, and a Revit-imported family carries parameters that
196
+ * do not drive geometry at all.
197
+ *
198
+ * @param args The component to read
199
+ * @returns Its definition, current values, and the parameter sheet
200
+ *
201
+ * @examplePrompt What size is that table?
202
+ * @examplePrompt Show me the parameters on this partition
203
+ *
204
+ * # Example
205
+ * ```ts
206
+ * const { values, parameters } = await snaptrude.design.family.getParameters({ component })
207
+ * ```
208
+ */
209
+ public abstract getParameters(
210
+ args: PluginDesignFamilyGetParametersArgs,
211
+ ): PluginApiReturn<PluginFamilyInstanceParameters | null>
212
+
213
+ /**
214
+ * Change a placed component's parameters and rebuild its geometry in place.
215
+ *
216
+ * Partial: pass only what changes. The component keeps its position,
217
+ * rotation and identity — this is an edit, not a replace, and it is undoable.
218
+ *
219
+ * Only parameters with `drivesGeometry` actually move the mesh. Setting one
220
+ * that does not is accepted and stored (it round-trips to Revit) but nothing
221
+ * visible happens — say so rather than claiming a change the user cannot see.
222
+ *
223
+ * @param args The component and the values to change
224
+ * @returns The values now in effect
225
+ *
226
+ * @examplePrompt Make that table seat 8
227
+ * @examplePrompt Change the stud spacing to 400
228
+ *
229
+ * # Example
230
+ * ```ts
231
+ * await snaptrude.design.family.setParameters({ component, values: { seats: 8 } })
232
+ * ```
233
+ */
234
+ public abstract setParameters(
235
+ args: PluginDesignFamilySetParametersArgs,
236
+ ): PluginApiReturn<PluginFamilyInstanceParameters>
237
+
238
+ /**
239
+ * Commit the prepared bundle to the project's parametric-definition library,
240
+ * so it survives the session and appears in the Families panel.
241
+ *
242
+ * @param args Optional library name and team scope
243
+ * @returns The catalogued definition's id and version
244
+ *
245
+ * @examplePrompt Save this as a family called Vertical Louvre
246
+ * @examplePrompt Add it to my library
247
+ *
248
+ * # Example
249
+ * ```ts
250
+ * const { definitionId } = await snaptrude.design.family.create({ name: "Louvre Screen" })
251
+ * ```
252
+ */
253
+ public abstract create(
254
+ args?: PluginDesignFamilyCreateArgs,
255
+ ): PluginApiReturn<PluginFamilyCreateResult>
256
+ }
257
+
258
+ // ---------------------------------------------------------------------------
259
+ // Result types
260
+ // ---------------------------------------------------------------------------
261
+
262
+ /** The family-spec DSL reference returned by `getReference`. */
263
+ export interface PluginFamilyReference {
264
+ /** Grammar, op table, expression whitelist, repair contract, worked example. */
265
+ reference: string
266
+ }
267
+
268
+ /**
269
+ * A family spec bundle. `root` is the family; `deps` carries every spec
270
+ * reachable through an `instance` op, keyed by spec id, so a saved family is
271
+ * self-contained and reloads anywhere.
272
+ *
273
+ * The internal shape of a spec is documented by
274
+ * {@linkcode PluginDesignFamilyApi.getReference}, not by these types — it is a
275
+ * data DSL whose grammar changes independently of this API surface.
276
+ */
277
+ export interface PluginFamilyBundle {
278
+ root: Record<string, unknown>
279
+ deps?: Record<string, Record<string, unknown>>
280
+ }
281
+
282
+ /** Axis-aligned extents of a built family. Snaptrude units (1 SU = 254 mm), +Y up. */
283
+ export interface PluginFamilyBBox {
284
+ min: [number, number, number]
285
+ max: [number, number, number]
286
+ /** `[width, height, depth]` in Snaptrude units — convert with `core.units.convert` for display. */
287
+ size: [number, number, number]
288
+ }
289
+
290
+ /** One row of a family's parameter sheet. */
291
+ export interface PluginFamilyParameter {
292
+ key: string
293
+ label: string
294
+ type: "length" | "integer" | "enum" | "text" | "boolean"
295
+ /** Current value. `length`/`integer` are numbers; the rest are strings. */
296
+ value: string | number
297
+ default: string | number
298
+ /**
299
+ * Whether this parameter actually drives geometry — INFERRED by the host from
300
+ * which names the spec's expressions reference. `false` means the value is
301
+ * stored and round-tripped but moves nothing.
302
+ */
303
+ drivesGeometry: boolean
304
+ min?: number
305
+ max?: number
306
+ options?: readonly string[]
307
+ }
308
+
309
+ /**
310
+ * A validation diagnostic. `path` is a JSON pointer into the submitted bundle —
311
+ * fix that node and re-`prepare`.
312
+ */
313
+ export interface PluginFamilyDiagnostic {
314
+ /** JSON pointer, e.g. `"/root/geometry/2/body/0/w"`. */
315
+ path: string
316
+ code:
317
+ | "schema"
318
+ | "bad-expression"
319
+ | "unknown-name"
320
+ | "unknown-def"
321
+ | "cycle"
322
+ | "duplicate-name"
323
+ | "unbounded-repeat"
324
+ | "depth"
325
+ | "bad-dimension"
326
+ | "proportion"
327
+ message: string
328
+ hint?: string
329
+ }
330
+
331
+ /** What `prepare` reports. */
332
+ export interface PluginFamilyPrepareResult {
333
+ /** `false` means the spec was rejected — read `errors`, fix, re-prepare. */
334
+ ok: boolean
335
+ familyId?: string
336
+ version?: number
337
+ label?: string
338
+ /** How many parts the family built. Sanity-check against intent. */
339
+ partCount?: number
340
+ /** Extents in Snaptrude units. Sanity-check against intent. */
341
+ bbox?: PluginFamilyBBox
342
+ evalMs?: number
343
+ parameters?: PluginFamilyParameter[]
344
+ /** Computed quantities, when the spec declares a `takeoff` block. */
345
+ takeoff?: Record<string, number | string>
346
+ /** The first few part names — enough to check the naming came out right. */
347
+ sampleParts?: string[]
348
+ /** Present when `snapshot` was asked for. */
349
+ snapshot?: PluginFamilySnapshot
350
+ errors: PluginFamilyDiagnostic[]
351
+ warnings: PluginFamilyDiagnostic[]
352
+ /** Set when the bundle validated but threw while building. */
353
+ error?: string
354
+ }
355
+
356
+ /**
357
+ * The host's diff of what a flex did to the geometry. This is the evidence that
358
+ * a family is parametric.
359
+ */
360
+ export interface PluginFamilyChangeBuckets {
361
+ added: number
362
+ removed: number
363
+ moved: number
364
+ rebuilt: number
365
+ addedNames: string[]
366
+ removedNames: string[]
367
+ }
368
+
369
+ /** What `flex` reports. */
370
+ export interface PluginFamilyFlexResult {
371
+ ok: boolean
372
+ /** The RESOLVED values after merge + clamping — not necessarily what was sent. */
373
+ values?: Record<string, number | string>
374
+ partCount?: number
375
+ bbox?: PluginFamilyBBox
376
+ evalMs?: number
377
+ changed?: PluginFamilyChangeBuckets
378
+ takeoff?: Record<string, number | string>
379
+ /** Proportion warnings at the flexed values (code `proportion`): a part thinner than 0.5 mm, a part or family over 50 m, a family under 10 mm — the signature of a unit slip. */
380
+ warnings?: PluginFamilyDiagnostic[]
381
+ /** Present when `snapshot` was asked for. */
382
+ snapshot?: PluginFamilySnapshot
383
+ error?: string
384
+ }
385
+
386
+ /** One family available to place — from the session registry or the catalog. */
387
+ export interface PluginFamilySummary {
388
+ id: string
389
+ version: number
390
+ label: string
391
+ category?: string
392
+ /** Number of rows on the family's parameter sheet. */
393
+ parameters: number
394
+ /**
395
+ * Where the family came from, which decides how far you can edit it.
396
+ *
397
+ * `"native"` — authored from a JSON spec through this API. Its recipe is
398
+ * data, so every parameter genuinely re-runs the geometry.
399
+ *
400
+ * `"revit"` — imported from a Revit family. Parameters exist and are
401
+ * editable, but only those the importer could bind to geometry actually
402
+ * change the mesh; the rest are carried for round-tripping. Check
403
+ * `drivesGeometry` on the parameter before promising the user a change.
404
+ */
405
+ kind: "native" | "revit"
406
+ }
407
+
408
+ /** What `place` reports. */
409
+ export interface PluginFamilyPlaceResult {
410
+ ok: boolean
411
+ familyId?: string
412
+ version?: number
413
+ /**
414
+ * The created component — present ONLY when `position` was given. In
415
+ * interactive mode nothing exists yet, so there is nothing to hand back;
416
+ * the user has not clicked.
417
+ */
418
+ component?: ComponentHandle
419
+ error?: string
420
+ }
421
+
422
+ /** A placed component's parameter state, from `getParameters` / `setParameters`. */
423
+ export interface PluginFamilyInstanceParameters {
424
+ /** The definition this instance is pinned to. */
425
+ familyId: string
426
+ version: number
427
+ label: string
428
+ /** Current values, keyed by parameter key. */
429
+ values: Record<string, number | string>
430
+ /** The sheet: what each key means, its bounds, and whether it moves geometry. */
431
+ parameters: PluginFamilyParameter[]
432
+ /** Present when `getParameters` was asked for a `snapshot`. */
433
+ snapshot?: PluginFamilySnapshot
434
+ }
435
+
436
+ /** What `create` reports. */
437
+ export interface PluginFamilyCreateResult {
438
+ ok: boolean
439
+ definitionId?: string
440
+ version?: number
441
+ guid?: string
442
+ error?: string
443
+ }
444
+
445
+ // ---------------------------------------------------------------------------
446
+ // Argument schemas
447
+ // ---------------------------------------------------------------------------
448
+
449
+ /**
450
+ * A spec bundle as it crosses the boundary: `root` plus optional `deps`.
451
+ *
452
+ * Loose by design — the spec grammar is a data DSL validated by the host's own
453
+ * validator, which returns JSON-pointer diagnostics far more useful than a Zod
454
+ * shape error. This schema only guarantees the envelope is an object.
455
+ */
456
+ export const PluginFamilyBundleSchema = z.object({
457
+ root: z.record(z.string(), z.unknown()),
458
+ deps: z.record(z.string(), z.record(z.string(), z.unknown())).optional(),
459
+ })
460
+
461
+ /**
462
+ * Arguments for {@linkcode PluginDesignFamilyApi.prepare}.
463
+ *
464
+ * | Property | Type | Description |
465
+ * |---|---|---|
466
+ * | `bundle` | {@linkcode PluginFamilyBundle} | The family spec bundle |
467
+ * | `spec` | {@linkcode PluginFamilyBundle} | Alias for `bundle` |
468
+ * | `snapshot` | {@linkcode PluginFamilySnapshotOption} | Also return a picture of the built family |
469
+ *
470
+ * Exactly one of `bundle` / `spec` is required. A bare `FamilySpec` (no `root`
471
+ * wrapper) is also accepted by the host and wrapped for the caller.
472
+ */
473
+ /**
474
+ * Ask for a picture with the result: `true` for the default sheet (iso, front,
475
+ * right and top tiles with a dimension line), or an object to choose views and
476
+ * tile size. The picture is an isolated offscreen render of the family alone —
477
+ * the viewport is not moved — and comes back as a PNG data URL in the result's
478
+ * `snapshot.image`. Through the agent's `run_worker`, return the result and the
479
+ * image reaches the model as an image.
480
+ */
481
+ export const PluginFamilySnapshotOption = z.union([
482
+ z.boolean(),
483
+ z.object({
484
+ views: z.array(z.enum(["iso", "front", "right", "top"])).optional(),
485
+ /** Pixels per tile (square), 64–2048; default 512. */
486
+ tileSize: z.number().optional(),
487
+ }),
488
+ ])
489
+ export type PluginFamilySnapshotOption = z.infer<typeof PluginFamilySnapshotOption>
490
+
491
+ /** The picture block on prepare / flex / getParameters results when `snapshot` was asked for. */
492
+ export interface PluginFamilySnapshot {
493
+ ok: boolean
494
+ /** PNG data URL of the composed sheet. */
495
+ image?: string
496
+ views?: ("iso" | "front" | "right" | "top")[]
497
+ /** [width, height, depth] in Snaptrude units. */
498
+ size?: [number, number, number]
499
+ ms?: number
500
+ /** Set when the picture could not be taken (e.g. the legacy renderer); the call itself still succeeded. */
501
+ error?: string
502
+ }
503
+
504
+ export const PluginDesignFamilyPrepareArgs = z.object({
505
+ bundle: PluginFamilyBundleSchema.optional(),
506
+ spec: PluginFamilyBundleSchema.optional(),
507
+ snapshot: PluginFamilySnapshotOption.optional(),
508
+ })
509
+ export type PluginDesignFamilyPrepareArgs = z.infer<typeof PluginDesignFamilyPrepareArgs>
510
+
511
+ /**
512
+ * Arguments for {@linkcode PluginDesignFamilyApi.flex}.
513
+ *
514
+ * | Property | Type | Description |
515
+ * |---|---|---|
516
+ * | `values` | `Record<string, number \| string>` | Parameter values to change (partial) |
517
+ * | `snapshot` | {@linkcode PluginFamilySnapshotOption} | Also return a picture of the flexed family |
518
+ */
519
+ export const PluginDesignFamilyFlexArgs = z.object({
520
+ values: z.record(z.string(), z.union([z.number(), z.string()])),
521
+ snapshot: PluginFamilySnapshotOption.optional(),
522
+ })
523
+ export type PluginDesignFamilyFlexArgs = z.infer<typeof PluginDesignFamilyFlexArgs>
524
+
525
+ /**
526
+ * Arguments for {@linkcode PluginDesignFamilyApi.create}.
527
+ *
528
+ * | Property | Type | Description |
529
+ * |---|---|---|
530
+ * | `name` | `string` | Library name; defaults to the spec's own label |
531
+ * | `teamId` | `string` | Team library to save into; omit for personal |
532
+ */
533
+ export const PluginDesignFamilyCreateArgs = z.object({
534
+ name: z.string().optional(),
535
+ teamId: z.string().optional(),
536
+ })
537
+ export type PluginDesignFamilyCreateArgs = z.infer<typeof PluginDesignFamilyCreateArgs>
538
+
539
+ /**
540
+ * Arguments for {@linkcode PluginDesignFamilyApi.place}.
541
+ *
542
+ * | Property | Type | Description |
543
+ * |---|---|---|
544
+ * | `id` | `string` | Family to place, from {@linkcode PluginDesignFamilyApi.list}. Omit to place the prepared draft |
545
+ * | `version` | `number` | Exact version; defaults to the newest known |
546
+ * | `position` | `{ x, y, z }` | Place HERE, without user interaction. Omit to arm the cursor instead |
547
+ * | `rotation` | `number` | Yaw in degrees about +Y. Only with `position` |
548
+ * | `values` | `Record<string, number \| string>` | Parameter values for THIS instance |
549
+ * | `storey` | `number` | Storey to place on; defaults to the active one |
550
+ */
551
+ export const PluginDesignFamilyPlaceArgs = z.object({
552
+ id: z.string().optional(),
553
+ version: z.number().int().positive().optional(),
554
+ position: Vec3Components.optional(),
555
+ rotation: z.number().optional(),
556
+ values: z.record(z.string(), z.union([z.number(), z.string()])).optional(),
557
+ storey: z.number().int().optional(),
558
+ })
559
+ export type PluginDesignFamilyPlaceArgs = z.infer<typeof PluginDesignFamilyPlaceArgs>
560
+
561
+ /**
562
+ * Arguments for {@linkcode PluginDesignFamilyApi.getParameters}.
563
+ *
564
+ * | Property | Type | Description |
565
+ * |---|---|---|
566
+ * | `component` | {@linkcode ComponentHandle} | A placed parametric component |
567
+ */
568
+ export const PluginDesignFamilyGetParametersArgs = z.object({
569
+ component: ComponentHandle,
570
+ /** Also return a picture of this instance (its recipe re-run at its values, with its paint). */
571
+ snapshot: PluginFamilySnapshotOption.optional(),
572
+ })
573
+ export type PluginDesignFamilyGetParametersArgs = z.infer<
574
+ typeof PluginDesignFamilyGetParametersArgs
575
+ >
576
+
577
+ /**
578
+ * Arguments for {@linkcode PluginDesignFamilyApi.setParameters}.
579
+ *
580
+ * | Property | Type | Description |
581
+ * |---|---|---|
582
+ * | `component` | {@linkcode ComponentHandle} | A placed parametric component |
583
+ * | `values` | `Record<string, number \| string>` | Values to change (partial) |
584
+ */
585
+ export const PluginDesignFamilySetParametersArgs = z.object({
586
+ component: ComponentHandle,
587
+ values: z.record(z.string(), z.union([z.number(), z.string()])),
588
+ })
589
+ export type PluginDesignFamilySetParametersArgs = z.infer<
590
+ typeof PluginDesignFamilySetParametersArgs
591
+ >
@@ -16,6 +16,7 @@ import { PluginDesignUpdateApi } from "./update"
16
16
  import { PluginDesignVisibilityApi } from "./visibility"
17
17
  import { PluginDesignDimensionsApi } from "./dimensions"
18
18
  import { PluginDesignTypesApi } from "./types"
19
+ import { PluginDesignFamilyApi } from "./family"
19
20
  import { PluginDesignGridsApi } from "./grids"
20
21
  import { PluginDesignChangeResult } from "./lock"
21
22
 
@@ -34,6 +35,7 @@ import { PluginDesignChangeResult } from "./lock"
34
35
  * - {@linkcode PluginDesignApi.visibility} — hide / isolate / reveal entities
35
36
  * - {@linkcode PluginDesignApi.dimensions} — dimension lines (Measuring Tape): create / list / delete / hide
36
37
  * - {@linkcode PluginDesignApi.types} — read-only building type / assembly reference
38
+ * - {@linkcode PluginDesignApi.family} — author parametric families from a JSON spec
37
39
  * - {@linkcode PluginDesignApi.lock} / {@linkcode PluginDesignApi.unlock} / {@linkcode PluginDesignApi.isLocked} / {@linkcode PluginDesignApi.listLocked} — lock state (top-level design verbs, §2A.1)
38
40
  * - {@linkcode PluginDesignApi.lockArea} / {@linkcode PluginDesignApi.unlockArea} / {@linkcode PluginDesignApi.isAreaLocked} / {@linkcode PluginDesignApi.listAreaLocked} — footprint-area lock for Room/Department spaces
39
41
  *
@@ -72,6 +74,8 @@ export abstract class PluginDesignApi {
72
74
  public abstract dimensions: PluginDesignDimensionsApi
73
75
  /** Read-only building type / assembly reference. See {@linkcode PluginDesignTypesApi}. */
74
76
  public abstract types: PluginDesignTypesApi
77
+ /** Author parametric families from a JSON spec. See {@linkcode PluginDesignFamilyApi}. */
78
+ public abstract family: PluginDesignFamilyApi
75
79
 
76
80
  /** Structural edits on a grid's line structure. See {@linkcode PluginDesignGridsApi}. */
77
81
  public abstract grids: PluginDesignGridsApi
@@ -266,4 +270,5 @@ export * from "./update"
266
270
  export * from "./visibility"
267
271
  export * from "./dimensions"
268
272
  export * from "./types"
273
+ export * from "./family"
269
274
  export * from "./grids"
@@ -42,6 +42,7 @@ import { PluginDesignQueryGridsApi } from "./grids"
42
42
  * | `"curtainWall"` | Parametric curtain wall |
43
43
  * | `"mullion"` | Curtain-wall mullion |
44
44
  * | `"panel"` | Curtain-wall panel |
45
+ * | `"parametricComponent"` | A placed parametric family instance (native or Revit-imported). Its geometry is rebuilt from `design.family` parameters, so read and drive it through `design.family.getParameters` / `setParameters` rather than editing its meshes. |
45
46
  * | `"grid"` | Structural grid group |
46
47
  */
47
48
  export const PluginEntityType = z.enum([
@@ -62,6 +63,7 @@ export const PluginEntityType = z.enum([
62
63
  "curtainWall",
63
64
  "mullion",
64
65
  "panel",
66
+ "parametricComponent",
65
67
  "grid",
66
68
  ])
67
69
  export type PluginEntityType = z.infer<typeof PluginEntityType>