@snaptrude/plugin-core 0.10.0 → 1.0.0
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/AGENTS.md +4 -0
- package/CHANGELOG.md +187 -3
- package/api-manifest.json +929 -25
- package/dist/api/analysis/daylight.d.ts +5 -5
- package/dist/api/analysis/solar.d.ts +2 -2
- package/dist/api/core/camera/index.d.ts +463 -0
- package/dist/api/core/camera/index.d.ts.map +1 -1
- package/dist/api/core/geom/create/index.d.ts +942 -101
- package/dist/api/core/geom/create/index.d.ts.map +1 -1
- package/dist/api/core/geom/query/brep.d.ts +59 -2
- package/dist/api/core/geom/query/brep.d.ts.map +1 -1
- package/dist/api/core/geom/query/curve.d.ts +59 -35
- package/dist/api/core/geom/query/curve.d.ts.map +1 -1
- package/dist/api/core/geom/query/edge.d.ts +2 -2
- package/dist/api/core/geom/query/face.d.ts +105 -0
- package/dist/api/core/geom/query/face.d.ts.map +1 -1
- package/dist/api/core/geom/update/contour.d.ts +10 -2
- package/dist/api/core/geom/update/contour.d.ts.map +1 -1
- package/dist/api/core/geom/update/curve.d.ts +5 -5
- package/dist/api/core/geom/update/profile.d.ts +3 -1
- package/dist/api/core/geom/update/profile.d.ts.map +1 -1
- package/dist/api/core/handles/index.d.ts +4 -2
- package/dist/api/core/handles/index.d.ts.map +1 -1
- package/dist/api/core/index.d.ts +4 -4
- package/dist/api/core/index.d.ts.map +1 -1
- package/dist/api/core/io/import/index.d.ts +3 -1
- package/dist/api/core/io/import/index.d.ts.map +1 -1
- package/dist/api/core/layers.d.ts +377 -255
- package/dist/api/core/layers.d.ts.map +1 -1
- package/dist/api/core/mode/index.d.ts +19 -7
- package/dist/api/core/mode/index.d.ts.map +1 -1
- package/dist/api/core/project/index.d.ts +68 -1
- package/dist/api/core/project/index.d.ts.map +1 -1
- package/dist/api/core/proposals/index.d.ts +4 -3
- package/dist/api/core/proposals/index.d.ts.map +1 -1
- package/dist/api/core/storeys/index.d.ts +31 -2
- package/dist/api/core/storeys/index.d.ts.map +1 -1
- package/dist/api/core/storeys/layers.d.ts +408 -0
- package/dist/api/core/storeys/layers.d.ts.map +1 -0
- package/dist/api/core/tags.d.ts +84 -3
- package/dist/api/core/tags.d.ts.map +1 -1
- package/dist/api/core/zoom/index.d.ts +13 -0
- package/dist/api/core/zoom/index.d.ts.map +1 -1
- package/dist/api/design/bim.d.ts +299 -0
- package/dist/api/design/bim.d.ts.map +1 -0
- package/dist/api/design/boolean/index.d.ts +2 -0
- package/dist/api/design/boolean/index.d.ts.map +1 -1
- package/dist/api/design/catalog.d.ts +320 -0
- package/dist/api/design/catalog.d.ts.map +1 -0
- package/dist/api/design/create/bulk-items.d.ts +269 -0
- package/dist/api/design/create/bulk-items.d.ts.map +1 -0
- package/dist/api/design/create/grids.d.ts +132 -0
- package/dist/api/design/create/grids.d.ts.map +1 -0
- package/dist/api/design/create/index.d.ts +752 -78
- package/dist/api/design/create/index.d.ts.map +1 -1
- package/dist/api/design/create/opening-fields.d.ts +38 -0
- package/dist/api/design/create/opening-fields.d.ts.map +1 -0
- package/dist/api/design/delete/index.d.ts +5 -0
- package/dist/api/design/delete/index.d.ts.map +1 -1
- package/dist/api/design/dimensions.d.ts +427 -0
- package/dist/api/design/dimensions.d.ts.map +1 -0
- package/dist/api/design/doors/index.d.ts +24 -23
- package/dist/api/design/doors/index.d.ts.map +1 -1
- package/dist/api/design/family.d.ts +1446 -0
- package/dist/api/design/family.d.ts.map +1 -0
- package/dist/api/design/furniture/index.d.ts +33 -0
- package/dist/api/design/furniture/index.d.ts.map +1 -1
- package/dist/api/design/grids.d.ts +237 -0
- package/dist/api/design/grids.d.ts.map +1 -0
- package/dist/api/design/index.d.ts +30 -0
- package/dist/api/design/index.d.ts.map +1 -1
- package/dist/api/design/networks.d.ts +1703 -0
- package/dist/api/design/networks.d.ts.map +1 -0
- package/dist/api/design/query/geometry/index.d.ts +33 -5
- package/dist/api/design/query/geometry/index.d.ts.map +1 -1
- package/dist/api/design/query/grids.d.ts +287 -0
- package/dist/api/design/query/grids.d.ts.map +1 -0
- package/dist/api/design/query/index.d.ts +143 -34
- package/dist/api/design/query/index.d.ts.map +1 -1
- package/dist/api/design/query/spaces.d.ts +5 -5
- package/dist/api/design/selection/index.d.ts +12 -12
- package/dist/api/design/types/index.d.ts +40 -24
- package/dist/api/design/types/index.d.ts.map +1 -1
- package/dist/api/design/update/index.d.ts +412 -8
- package/dist/api/design/update/index.d.ts.map +1 -1
- package/dist/api/design/visibility.d.ts +28 -0
- package/dist/api/design/visibility.d.ts.map +1 -1
- package/dist/api/design/windows/index.d.ts +8 -8
- package/dist/api/design/windows/index.d.ts.map +1 -1
- package/dist/api/entity/buildableEnvelope.d.ts +84 -2
- package/dist/api/entity/buildableEnvelope.d.ts.map +1 -1
- package/dist/api/entity/referenceLine.d.ts +13 -1
- package/dist/api/entity/referenceLine.d.ts.map +1 -1
- package/dist/api/entity/space.d.ts +12 -12
- package/dist/api/entity/story.d.ts +4 -4
- package/dist/api/presentation/aiInspiration.d.ts +24 -24
- package/dist/api/presentation/annotate.d.ts +38 -22
- package/dist/api/presentation/annotate.d.ts.map +1 -1
- package/dist/api/presentation/camera.d.ts +265 -0
- package/dist/api/presentation/camera.d.ts.map +1 -0
- package/dist/api/presentation/drawingContract.d.ts +32 -0
- package/dist/api/presentation/drawingContract.d.ts.map +1 -0
- package/dist/api/presentation/drawings.d.ts +3523 -0
- package/dist/api/presentation/drawings.d.ts.map +1 -0
- package/dist/api/presentation/export.d.ts +23 -5
- package/dist/api/presentation/export.d.ts.map +1 -1
- package/dist/api/presentation/graphics.d.ts +419 -0
- package/dist/api/presentation/graphics.d.ts.map +1 -0
- package/dist/api/presentation/index.d.ts +66 -12
- package/dist/api/presentation/index.d.ts.map +1 -1
- package/dist/api/presentation/placedViews.d.ts +116 -16
- package/dist/api/presentation/placedViews.d.ts.map +1 -1
- package/dist/api/presentation/sections.d.ts +461 -0
- package/dist/api/presentation/sections.d.ts.map +1 -0
- package/dist/api/presentation/shapes.d.ts +2 -2
- package/dist/api/presentation/sheets.d.ts +175 -19
- package/dist/api/presentation/sheets.d.ts.map +1 -1
- package/dist/api/presentation/standards.d.ts +590 -0
- package/dist/api/presentation/standards.d.ts.map +1 -0
- package/dist/api/presentation/viewPresets.d.ts +222 -0
- package/dist/api/presentation/viewPresets.d.ts.map +1 -0
- package/dist/api/presentation/views.d.ts +144 -9
- package/dist/api/presentation/views.d.ts.map +1 -1
- package/dist/api/presentation/walkthroughs.d.ts +1350 -0
- package/dist/api/presentation/walkthroughs.d.ts.map +1 -0
- package/dist/api/program/departments.d.ts +58 -2
- package/dist/api/program/departments.d.ts.map +1 -1
- package/dist/api/program/index.d.ts +16 -6
- package/dist/api/program/index.d.ts.map +1 -1
- package/dist/api/program/labels.d.ts +355 -0
- package/dist/api/program/labels.d.ts.map +1 -0
- package/dist/api/program/metadata.d.ts +328 -0
- package/dist/api/program/metadata.d.ts.map +1 -0
- package/dist/api/program/site.d.ts +4 -4
- package/dist/api/program/spreadsheet.d.ts +267 -4
- package/dist/api/program/spreadsheet.d.ts.map +1 -1
- package/dist/api/workspace/index.d.ts +49 -2
- package/dist/api/workspace/index.d.ts.map +1 -1
- package/dist/errors/codes.d.ts +3 -1
- package/dist/errors/codes.d.ts.map +1 -1
- package/dist/handles.d.ts +32 -3
- package/dist/handles.d.ts.map +1 -1
- package/dist/index.cjs +6539 -2804
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6121 -2801
- package/dist/index.js.map +1 -1
- package/dist/massParameters.d.ts +287 -0
- package/dist/massParameters.d.ts.map +1 -0
- package/dist/massParameters.ops.d.ts +138 -0
- package/dist/massParameters.ops.d.ts.map +1 -0
- package/package.json +14 -13
- package/scripts/check-dist-fresh.mjs +22 -0
- package/scripts/generate-manifest.mjs +5 -0
- package/scripts/generate-manifest.test.mjs +14 -3
- package/src/api/core/camera/index.ts +446 -1
- package/src/api/core/geom/create/index.ts +1037 -102
- package/src/api/core/geom/query/brep.ts +60 -2
- package/src/api/core/geom/query/curve.ts +26 -2
- package/src/api/core/geom/query/edge.ts +2 -2
- package/src/api/core/geom/query/face.ts +109 -4
- package/src/api/core/geom/update/contour.ts +10 -2
- package/src/api/core/geom/update/profile.ts +2 -0
- package/src/api/core/handles/index.ts +3 -2
- package/src/api/core/index.ts +4 -4
- package/src/api/core/io/import/index.ts +11 -3
- package/src/api/core/layers.ts +380 -173
- package/src/api/core/mode/index.ts +19 -7
- package/src/api/core/project/index.ts +62 -1
- package/src/api/core/proposals/index.ts +4 -3
- package/src/api/core/storeys/index.ts +34 -2
- package/src/api/core/storeys/layers.ts +339 -0
- package/src/api/core/tags.ts +76 -0
- package/src/api/core/zoom/index.ts +13 -0
- package/src/api/design/bim.ts +210 -0
- package/src/api/design/boolean/index.ts +2 -0
- package/src/api/design/catalog.ts +203 -0
- package/src/api/design/create/bulk-items.ts +294 -0
- package/src/api/design/create/grids.ts +137 -0
- package/src/api/design/create/index.ts +823 -114
- package/src/api/design/create/opening-fields.ts +40 -0
- package/src/api/design/delete/index.ts +5 -0
- package/src/api/design/dimensions.ts +453 -0
- package/src/api/design/doors/index.ts +64 -41
- package/src/api/design/family.ts +1342 -0
- package/src/api/design/furniture/index.ts +34 -0
- package/src/api/design/grids.ts +237 -0
- package/src/api/design/index.ts +49 -4
- package/src/api/design/networks.ts +607 -0
- package/src/api/design/query/geometry/index.ts +35 -4
- package/src/api/design/query/grids.ts +197 -0
- package/src/api/design/query/index.ts +105 -3
- package/src/api/design/types/index.ts +31 -24
- package/src/api/design/update/index.ts +398 -4
- package/src/api/design/visibility.ts +34 -0
- package/src/api/design/windows/index.ts +40 -22
- package/src/api/entity/buildableEnvelope.ts +77 -2
- package/src/api/entity/referenceLine.ts +25 -12
- package/src/api/presentation/annotate.ts +90 -58
- package/src/api/presentation/camera.ts +289 -0
- package/src/api/presentation/drawingContract.ts +70 -0
- package/src/api/presentation/drawings.ts +1179 -0
- package/src/api/presentation/export.ts +32 -6
- package/src/api/presentation/graphics.ts +324 -0
- package/src/api/presentation/index.ts +73 -14
- package/src/api/presentation/placedViews.ts +119 -28
- package/src/api/presentation/sections.ts +420 -0
- package/src/api/presentation/sheets.ts +181 -20
- package/src/api/presentation/standards.ts +445 -0
- package/src/api/presentation/viewPresets.ts +241 -0
- package/src/api/presentation/views.ts +81 -10
- package/src/api/presentation/walkthroughs.ts +1201 -0
- package/src/api/program/departments.ts +54 -2
- package/src/api/program/index.ts +16 -6
- package/src/api/program/labels.ts +332 -0
- package/src/api/program/metadata.ts +364 -0
- package/src/api/program/spreadsheet.ts +282 -0
- package/src/api/workspace/index.ts +51 -2
- package/src/errors/codes.ts +7 -0
- package/src/handles.ts +42 -3
- package/src/index.ts +2 -0
- package/src/massParameters.ops.ts +246 -0
- package/src/massParameters.ts +427 -0
- package/test/drawingContract.test.mjs +288 -0
- package/test/errors.test.mjs +1 -0
- package/test/massParameters.test.mjs +952 -0
- package/test/paperPensContract.test.mjs +75 -0
- package/test/presentationViewSettings.test.mjs +85 -0
|
@@ -0,0 +1,1342 @@
|
|
|
1
|
+
import * as z from "zod"
|
|
2
|
+
import { PluginApiReturn } from "../../types"
|
|
3
|
+
import {
|
|
4
|
+
BrepHandle,
|
|
5
|
+
ComponentHandle,
|
|
6
|
+
Vec3Components,
|
|
7
|
+
Vec3Handle,
|
|
8
|
+
} from "../../handles"
|
|
9
|
+
import { NetworkFamilyContract } from "./networks"
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* `snaptrude.design.family.*` — author NATIVE parametric families from a JSON spec.
|
|
13
|
+
*
|
|
14
|
+
* A parametric family is a reusable, parameter-driven building component: a
|
|
15
|
+
* curtain-wall panel, a louvre screen, a balustrade, a shading fin. Unlike the
|
|
16
|
+
* rest of `design.*`, you do not build one out of geometry calls — you write a
|
|
17
|
+
* JSON **spec** describing its parameters and its geometry rules, and the host
|
|
18
|
+
* compiles that into a live parametric definition the user can place, flex and
|
|
19
|
+
* save to their library.
|
|
20
|
+
*
|
|
21
|
+
* ## Generic component workflow
|
|
22
|
+
*
|
|
23
|
+
* ```
|
|
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
|
|
27
|
+
* flex({ values }) re-run at new values; returns the geometry delta
|
|
28
|
+
* place({ position }) put it in the model — saves the family as the first instance drops
|
|
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
|
|
31
|
+
* create({ teamId }) share with a team; a save with nothing placed also drops one instance
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* `place` saves the prepared family to the library, so it survives reload with no extra step:
|
|
35
|
+
* the open project's team library in a team project, else the user's personal library
|
|
36
|
+
* (`teamId` picks another: a team's id, or `null` for the personal library); finish a NEW generic component on
|
|
37
|
+
* `place`, not on `create` (`create` ends only an edit of a saved family, below).
|
|
38
|
+
*
|
|
39
|
+
* Architectural types use `create({ name })` to save the prepared recipe.
|
|
40
|
+
* Saving does not assign a family to a native host.
|
|
41
|
+
* Read `design.catalog` and pass the saved `{ id, version }` reference to the
|
|
42
|
+
* native `design.create` method with its contour, wall profile, or straight axis.
|
|
43
|
+
* Standalone placement does not create a native host. Selecting a Draw type
|
|
44
|
+
* does not register an API type. Native creator values use mm, while this
|
|
45
|
+
* generic family API uses SU. Read `family_authoring` for the full workflow.
|
|
46
|
+
*
|
|
47
|
+
* ## Door and window templates
|
|
48
|
+
*
|
|
49
|
+
* Start with `getTemplate({ kind: "door" | "window", subType? })`, edit the
|
|
50
|
+
* recipe's style while keeping its contract, then `prepare` → `flex` → `create`.
|
|
51
|
+
* Place the saved family with
|
|
52
|
+
* `design.create.door(familyId, hostWall, position, { version, values, hinge }, facing)`
|
|
53
|
+
* or `design.create.window` with the same positional signature. Positions,
|
|
54
|
+
* hinge and facing are vec3 handles; values use the authored parameter keys.
|
|
55
|
+
* Templates produce ParametricComponents, not separate OpeningComponents. Read
|
|
56
|
+
* and edit their instances through `getParameters` / `setParameters`.
|
|
57
|
+
* The contract maps opening dimensions to parameter keys; do not assume the
|
|
58
|
+
* recipe names its width parameter `width`. Generic family lengths use SU.
|
|
59
|
+
* Hosted door/window `create({ name })` saves without placing a standalone
|
|
60
|
+
* sample. Supply the host and position in a subsequent placement.
|
|
61
|
+
*
|
|
62
|
+
* ## Changing a saved family
|
|
63
|
+
*
|
|
64
|
+
* Open it with `get({ id })` or `get({ component })`, edit with `prepare` /
|
|
65
|
+
* `flex`, then `create()`. The app — not the agent — asks the user whether to
|
|
66
|
+
* update that family (this project's placed copies move to the new version) or
|
|
67
|
+
* save a new family. Do not ask this yourself; report the returned `decision`.
|
|
68
|
+
*
|
|
69
|
+
* ## Read getReference() first
|
|
70
|
+
*
|
|
71
|
+
* The spec DSL is a closed expression language with its own op set, coordinate
|
|
72
|
+
* convention (**+Y up, Snaptrude units** — the scene's own unit, 1 SU = 254 mm,
|
|
73
|
+
* the same unit `core.units.getBabylonType()` names and every geometry API
|
|
74
|
+
* takes; literals inside spec expressions may carry a unit suffix such as
|
|
75
|
+
* `600mm` or `2ft`)
|
|
76
|
+
* and part-naming law. It is deliberately NOT duplicated in these types:
|
|
77
|
+
* {@linkcode PluginDesignFamilyApi.getReference} returns the whole reference,
|
|
78
|
+
* generated from a family that is actually registered in the host, so the
|
|
79
|
+
* documentation cannot drift from what the compiler accepts.
|
|
80
|
+
*
|
|
81
|
+
* ## The repair contract
|
|
82
|
+
*
|
|
83
|
+
* {@linkcode PluginDesignFamilyApi.prepare} does **not** throw on an invalid
|
|
84
|
+
* spec — it returns `{ ok: false, errors }` where each error carries a JSON
|
|
85
|
+
* pointer into the bundle you sent (e.g. `"/root/geometry/2/body/0/w"`). Read
|
|
86
|
+
* `errors[0].path`, fix that node, and call `prepare` again. A failed `prepare`
|
|
87
|
+
* leaves the previous good draft standing, so it costs nothing but the node you
|
|
88
|
+
* broke. Re-authoring from scratch throws away everything already correct.
|
|
89
|
+
*
|
|
90
|
+
* ## Curved parts are exact
|
|
91
|
+
*
|
|
92
|
+
* A part whose sketch has a curved edge — any `revolve`, or an `extrude` /
|
|
93
|
+
* `sweep` / `loft` with an arc or circle in its profile (or an arc in a sweep
|
|
94
|
+
* path) — is built by the exact geometry kernel, the same one behind curved
|
|
95
|
+
* masses. Straight-edged parts keep the fast mesh builder. `getParameters` on a
|
|
96
|
+
* placed component reports which built each part in `parts[].engine`, and a
|
|
97
|
+
* kernel part carries a `brep`: read it with `core.geom.query.brep.*` and
|
|
98
|
+
* `core.geom.query.face.getSurfaceKind` — the same face-kind check as for a
|
|
99
|
+
* curved mass. If the kernel is unavailable a part falls back to the mesh
|
|
100
|
+
* builder (`engine: "mesh"`) rather than failing the family.
|
|
101
|
+
*
|
|
102
|
+
* Accessed via `snaptrude.design.family`.
|
|
103
|
+
*/
|
|
104
|
+
export abstract class PluginDesignFamilyApi {
|
|
105
|
+
constructor() {}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Read the family-spec DSL reference: grammar, the op table, the expression
|
|
109
|
+
* whitelist, the coordinate convention, the repair contract, and a worked
|
|
110
|
+
* example taken from a registered family.
|
|
111
|
+
*
|
|
112
|
+
* Read this before authoring a family for the first time in a session. The
|
|
113
|
+
* DSL is a closed language — a guessed op or function is a rejected spec.
|
|
114
|
+
*
|
|
115
|
+
* @param args Optional section, or its bare name. Omit for the core reference.
|
|
116
|
+
* Fetch architectural, networks or relations only when the family needs them.
|
|
117
|
+
* @returns The reference text
|
|
118
|
+
*
|
|
119
|
+
* @examplePrompt How do I write a parametric family spec?
|
|
120
|
+
* @examplePrompt Show me the family DSL
|
|
121
|
+
*
|
|
122
|
+
* # Example
|
|
123
|
+
* ```ts
|
|
124
|
+
* const { reference } = await snaptrude.design.family.getReference()
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
public abstract getReference(
|
|
128
|
+
args?: PluginDesignFamilyGetReferenceArgs | PluginFamilyReferenceSection,
|
|
129
|
+
): PluginApiReturn<PluginFamilyReference>
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Worked example families to learn from: real, tested bundles by discipline
|
|
133
|
+
* (architecture, structure, MEP, furniture). With no `id`, returns the
|
|
134
|
+
* catalogue — each example's id, label, discipline and the patterns it
|
|
135
|
+
* teaches — filtered by `discipline` / `tag` when given. With an `id`,
|
|
136
|
+
* returns that example's full bundle. Fetch the one closest to the task
|
|
137
|
+
* before authoring and borrow its patterns; examples are patterns to
|
|
138
|
+
* combine, not templates to copy whole.
|
|
139
|
+
*
|
|
140
|
+
* @param args - `{ id }` for one bundle, or `{ discipline?, tag? }` to list
|
|
141
|
+
* @returns The catalogue, or one example with its bundle
|
|
142
|
+
*
|
|
143
|
+
* @examplePrompt Show me an example of a structural family
|
|
144
|
+
* @examplePrompt How is a hosted door family written?
|
|
145
|
+
*
|
|
146
|
+
* # Example
|
|
147
|
+
* ```ts
|
|
148
|
+
* const list = await snaptrude.design.family.getExamples({ discipline: "mep" })
|
|
149
|
+
* const ex = await snaptrude.design.family.getExamples({ id: list[0].id })
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
public abstract getExamples(
|
|
153
|
+
args?: PluginDesignFamilyGetExamplesArgs | string,
|
|
154
|
+
): PluginApiReturn<PluginFamilyExampleSummary[] | PluginFamilyExample>
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Get a validated starter recipe for a door or window, with an opening
|
|
158
|
+
* contract, host wall-thickness binding and role-named parts. Edit profiles,
|
|
159
|
+
* panels, glazing, hardware and materials while preserving the contract.
|
|
160
|
+
* Use `prepare` / `flex` to check the recipe, then `create` to save it and
|
|
161
|
+
* `design.create.door(familyId, hostWall, position, { version, values }, facing)`
|
|
162
|
+
* (or `window`) to place it. Edit the resulting ParametricComponent through
|
|
163
|
+
* `getParameters` / `setParameters`, not a separate opening API.
|
|
164
|
+
*
|
|
165
|
+
* `subType` defaults to `singleDoor` / `fixedWindow`. An unsupported subType
|
|
166
|
+
* returns the kind's default template, a note in `guidance`, and `available`
|
|
167
|
+
* templates. Start from the closest recipe and set `root.subType` to your token.
|
|
168
|
+
*
|
|
169
|
+
* @param args The opening kind and optional subtype token
|
|
170
|
+
* @returns The bundle, parameter-key contract, subtype, guidance and available templates
|
|
171
|
+
* @example
|
|
172
|
+
* const { bundle, contract } = await snaptrude.design.family.getTemplate({
|
|
173
|
+
* kind: "door", subType: "doubleDoor",
|
|
174
|
+
* })
|
|
175
|
+
* // Flex the authored width key, not an assumed parameter name (lengths in SU).
|
|
176
|
+
* await snaptrude.design.family.prepare({ bundle })
|
|
177
|
+
* await snaptrude.design.family.flex({ values: { [contract.width]: 4 } })
|
|
178
|
+
*/
|
|
179
|
+
public abstract getTemplate(
|
|
180
|
+
args: PluginDesignFamilyGetTemplateArgs,
|
|
181
|
+
): PluginApiReturn<PluginFamilyTemplateResult>
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Validate, compile and preview a family spec. Nothing is committed to the
|
|
185
|
+
* model or the library — call {@linkcode PluginDesignFamilyApi.place} to put
|
|
186
|
+
* it in the model, which also saves it.
|
|
187
|
+
*
|
|
188
|
+
* Safe to call repeatedly while iterating. On success, check `partCount` and
|
|
189
|
+
* `bbox` against what you intended before treating the family as correct — a
|
|
190
|
+
* facade that built 3 parts, or a 3 m screen reporting 40 m of height, is a
|
|
191
|
+
* visible bug the validator cannot catch.
|
|
192
|
+
*
|
|
193
|
+
* Lengths — the spec's bare numbers, `bbox`, the sheet's `length` values — are
|
|
194
|
+
* Snaptrude units: the same 254 mm unit as the kernel's internal (`"babylon"`)
|
|
195
|
+
* unit. A unit suffix (`600mm`) is legal only inside a spec expression.
|
|
196
|
+
*
|
|
197
|
+
* @param args The spec bundle, as `{ bundle }` or `{ spec }`
|
|
198
|
+
* @returns Build report, or `{ ok: false, errors }` with JSON-pointer paths
|
|
199
|
+
*
|
|
200
|
+
* @examplePrompt Make a louvre screen with adjustable blade count and tilt
|
|
201
|
+
* @examplePrompt Design a curtain wall panel family with 8 bays
|
|
202
|
+
* @examplePrompt Create a railing family I can adjust
|
|
203
|
+
* @examplePrompt Make a pendant lamp family with a domed shade I can resize
|
|
204
|
+
*
|
|
205
|
+
* # Example
|
|
206
|
+
* ```ts
|
|
207
|
+
* const r = await snaptrude.design.family.prepare({ bundle })
|
|
208
|
+
* if (!r.ok) console.error(r.errors[0].path, r.errors[0].message)
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
public abstract prepare(
|
|
212
|
+
args: PluginDesignFamilyPrepareArgs,
|
|
213
|
+
): PluginApiReturn<PluginFamilyPrepareResult>
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Re-run the prepared family's recipe at new parameter values.
|
|
217
|
+
*
|
|
218
|
+
* Values are merged over the current ones and clamped to the parameter
|
|
219
|
+
* sheet's min/max, so a partial `{ bays: 6 }` is fine. The returned `changed`
|
|
220
|
+
* buckets are the host's own diff of what the geometry did — flexing a
|
|
221
|
+
* driving parameter and reporting them demonstrates the family is parametric.
|
|
222
|
+
*
|
|
223
|
+
* @param args The parameter values to change. Omit values (or pass an empty
|
|
224
|
+
* record) to report or photograph the current draft without rebuilding it.
|
|
225
|
+
* @returns The new build plus the geometry delta
|
|
226
|
+
*
|
|
227
|
+
* @examplePrompt Change it to 6 bays
|
|
228
|
+
* @examplePrompt Show me what happens with 20 blades
|
|
229
|
+
*
|
|
230
|
+
* # Example
|
|
231
|
+
* ```ts
|
|
232
|
+
* const { changed } = await snaptrude.design.family.flex({ values: { bays: 6 } })
|
|
233
|
+
* console.log(`removed ${changed.removed}, rebuilt ${changed.rebuilt}`)
|
|
234
|
+
* ```
|
|
235
|
+
*/
|
|
236
|
+
public abstract flex(
|
|
237
|
+
args?: PluginDesignFamilyFlexArgs,
|
|
238
|
+
): PluginApiReturn<PluginFamilyFlexResult>
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* A family's spec bundle: the prepared draft's, or — to CHANGE a saved
|
|
242
|
+
* family — that family's recipe, opened as the new draft.
|
|
243
|
+
*
|
|
244
|
+
* - No arguments: the prepared draft, or `null` when nothing is prepared.
|
|
245
|
+
* - `{ id, version? }`: open a saved family from the library (newest version
|
|
246
|
+
* when `version` is omitted).
|
|
247
|
+
* - `{ component }`: open the family (at its version) of that placed copy.
|
|
248
|
+
*
|
|
249
|
+
* Opening makes the recipe the current draft and returns `editing` — which
|
|
250
|
+
* saved family it is. Edit it with `prepare` / `flex`, then call `create`.
|
|
251
|
+
* On that save the APP shows the user its own window asking whether to
|
|
252
|
+
* update the family (every placed copy in this project moves to the new
|
|
253
|
+
* version) or save it as a new family — "new family" is the default. Never
|
|
254
|
+
* ask the user this yourself, and never promise either outcome: report what
|
|
255
|
+
* `create` returns.
|
|
256
|
+
*
|
|
257
|
+
* @param args Optional: which saved family to open
|
|
258
|
+
* @returns The bundle (plus `editing` when it edits a saved family), or `null`
|
|
259
|
+
*
|
|
260
|
+
* @examplePrompt Make the chairs I placed 50 mm wider
|
|
261
|
+
* @examplePrompt Change the Louvre Screen family so its blades are deeper
|
|
262
|
+
*
|
|
263
|
+
* # Example
|
|
264
|
+
* ```ts
|
|
265
|
+
* const draft = await snaptrude.design.family.get()
|
|
266
|
+
* const family = await snaptrude.design.family.get({ component })
|
|
267
|
+
* // family.editing → { familyId, version, name, library }
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
public abstract get(
|
|
271
|
+
args?: PluginDesignFamilyGetArgs | string,
|
|
272
|
+
): PluginApiReturn<PluginFamilyGetResult | null>
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Every available parametric family, merging the session registry and catalog.
|
|
276
|
+
*
|
|
277
|
+
* @param args Opt into compact lines for generic discovery. The default stays
|
|
278
|
+
* object-shaped so existing callers retain version, openingKind and subType.
|
|
279
|
+
* @returns Summary objects, or `id | label | category | kind` lines when compact.
|
|
280
|
+
*
|
|
281
|
+
* @examplePrompt What families are available?
|
|
282
|
+
*
|
|
283
|
+
* # Example
|
|
284
|
+
* ```ts
|
|
285
|
+
* const families = await snaptrude.design.family.list()
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
288
|
+
public abstract list(
|
|
289
|
+
args: PluginDesignFamilyListArgs & { compact: true },
|
|
290
|
+
): PluginApiReturn<string[]>
|
|
291
|
+
public abstract list(
|
|
292
|
+
args?: PluginDesignFamilyListArgs & { compact?: false },
|
|
293
|
+
): PluginApiReturn<PluginFamilySummary[]>
|
|
294
|
+
public abstract list(
|
|
295
|
+
args?: PluginDesignFamilyListArgs,
|
|
296
|
+
): PluginApiReturn<PluginFamilySummary[] | string[]>
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Read one saved catalog definition without opening a draft or placing geometry.
|
|
300
|
+
* Both id and version are required. The catalog checks access on every read.
|
|
301
|
+
* Length defaults and bounds are in mm, ready for network graph overrides;
|
|
302
|
+
* angle parameters retain degrees, identified by each parameter's unit.
|
|
303
|
+
* existing getParameters/place/setParameters continue to use Snaptrude units.
|
|
304
|
+
* Defaults include current catalog overrides, which can change without a new version.
|
|
305
|
+
* No scene, registry, history, or persistence state is changed.
|
|
306
|
+
* Missing/inaccessible pins and invalid recipes fail; no latest-version fallback.
|
|
307
|
+
* This metadata does not certify connectors or engineering compatibility.
|
|
308
|
+
*
|
|
309
|
+
* @param args Exact saved catalog id and version, from list()
|
|
310
|
+
* @returns Compact parameter metadata, not geometry or the recipe bundle
|
|
311
|
+
* @example
|
|
312
|
+
* const definition = await snaptrude.design.family.getDefinition({ id, version })
|
|
313
|
+
*/
|
|
314
|
+
public abstract getDefinition(
|
|
315
|
+
args: PluginDesignFamilyGetDefinitionArgs,
|
|
316
|
+
): PluginApiReturn<PluginFamilyDefinition>
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Put a family into the model — either the prepared draft or any family from
|
|
320
|
+
* {@linkcode PluginDesignFamilyApi.list}.
|
|
321
|
+
*
|
|
322
|
+
* Placing the prepared draft saves it to the library first, so its instances survive
|
|
323
|
+
* reload: the open project's team library in a team project, else the user's personal
|
|
324
|
+
* library. `teamId` picks another (`null` is the personal library).
|
|
325
|
+
*
|
|
326
|
+
* Two modes, chosen by whether you pass `position`:
|
|
327
|
+
*
|
|
328
|
+
* **With `position`** the component is created there and then, undoably. Use
|
|
329
|
+
* this when you know where it goes — you were given coordinates, or you are
|
|
330
|
+
* copying an existing instance. The returned `component` handle works with
|
|
331
|
+
* `design.transform.move` / `.rotate` and with
|
|
332
|
+
* {@linkcode PluginDesignFamilyApi.setParameters}.
|
|
333
|
+
*
|
|
334
|
+
* **Without `position`** it arms placement mode with a ghost on the cursor
|
|
335
|
+
* and returns immediately — no geometry yet. The USER clicks to drop it, so
|
|
336
|
+
* tell them to. Prefer this when the location is a judgement call.
|
|
337
|
+
*
|
|
338
|
+
* To COPY a placed component, place its family again at the new position with
|
|
339
|
+
* the same `values` — there is no separate copy call.
|
|
340
|
+
*
|
|
341
|
+
* HOSTED families (doors, windows — anything with an `opening`, a `host`
|
|
342
|
+
* block, or a void with `cuts: "host"`) take `host`: a wall or a placed
|
|
343
|
+
* family. The family is set into the host's face nearest `position` (the
|
|
344
|
+
* host's middle when omitted), and the host is cut — a system wall by the
|
|
345
|
+
* wall cutter, a family by its own recipe. Host-bound parameters then follow
|
|
346
|
+
* the host (a door's wall thickness follows the wall).
|
|
347
|
+
*
|
|
348
|
+
* @param args Which family, where, and at what parameter values
|
|
349
|
+
* @returns What was placed; `component` is present only in positioned mode
|
|
350
|
+
*
|
|
351
|
+
* @examplePrompt Place it in the model
|
|
352
|
+
* @examplePrompt Put a louvre screen at the origin
|
|
353
|
+
* @examplePrompt Copy that table two metres to the right
|
|
354
|
+
*
|
|
355
|
+
* # Example
|
|
356
|
+
* ```ts
|
|
357
|
+
* // positioned — geometry exists when this resolves
|
|
358
|
+
* const { component } = await snaptrude.design.family.place({
|
|
359
|
+
* id: "revit:gypsum-stud-partition~90ad72",
|
|
360
|
+
* position: { x: 0, y: 0, z: 0 },
|
|
361
|
+
* rotation: 90,
|
|
362
|
+
* })
|
|
363
|
+
* await snaptrude.design.transform.move([component], { x: 2, y: 0, z: 0 })
|
|
364
|
+
*
|
|
365
|
+
* // on the first floor — position.y is an offset above storey 2's base
|
|
366
|
+
* await snaptrude.design.family.place({ id: familyId, position: { x: 4, y: 0, z: 0 }, storey: 2 })
|
|
367
|
+
*
|
|
368
|
+
* // interactive — the user clicks
|
|
369
|
+
* await snaptrude.design.family.place()
|
|
370
|
+
*
|
|
371
|
+
* // hosted — a door in a wall, near a point along it
|
|
372
|
+
* await snaptrude.design.family.place({ id: doorId, host: wall, position: { x: 3, y: 0, z: 0 } })
|
|
373
|
+
* ```
|
|
374
|
+
*/
|
|
375
|
+
public abstract place(
|
|
376
|
+
args?: PluginDesignFamilyPlaceArgs,
|
|
377
|
+
): PluginApiReturn<PluginFamilyPlaceResult>
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Read a placed component's current parameter values and its sheet.
|
|
381
|
+
*
|
|
382
|
+
* Use before {@linkcode PluginDesignFamilyApi.setParameters} so you change
|
|
383
|
+
* what is actually there: parameter KEYS are the family's own, not the
|
|
384
|
+
* label the user says, and a Revit-imported family carries parameters that
|
|
385
|
+
* do not drive geometry at all.
|
|
386
|
+
*
|
|
387
|
+
* `length` values are Snaptrude units: the same 254 mm unit as the kernel's
|
|
388
|
+
* internal (`"babylon"`) unit — convert with `core.units.convert` to report.
|
|
389
|
+
*
|
|
390
|
+
* @param args The component to read
|
|
391
|
+
* @returns Its definition, current values, and the parameter sheet
|
|
392
|
+
*
|
|
393
|
+
* @examplePrompt What size is that table?
|
|
394
|
+
* @examplePrompt Show me the parameters on this partition
|
|
395
|
+
*
|
|
396
|
+
* # Example
|
|
397
|
+
* ```ts
|
|
398
|
+
* const { values, parameters } = await snaptrude.design.family.getParameters({ component })
|
|
399
|
+
* ```
|
|
400
|
+
*/
|
|
401
|
+
public abstract getParameters(
|
|
402
|
+
args: PluginDesignFamilyGetParametersArgs,
|
|
403
|
+
): PluginApiReturn<PluginFamilyInstanceParameters | null>
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Change a placed component's parameters and rebuild its geometry in place.
|
|
407
|
+
*
|
|
408
|
+
* Partial: pass only what changes. The component keeps its position,
|
|
409
|
+
* rotation and identity — this is an edit, not a replace, and it is undoable.
|
|
410
|
+
*
|
|
411
|
+
* Only parameters with `drivesGeometry` actually move the mesh. Setting one
|
|
412
|
+
* that does not is accepted and stored (it round-trips to Revit) but nothing
|
|
413
|
+
* visible happens — say so rather than claiming a change the user cannot see.
|
|
414
|
+
*
|
|
415
|
+
* NESTED children: `children: { "<path>": { key: value } }` sets a nested
|
|
416
|
+
* child's own values (every copy of it). A value its parent sets is sent up
|
|
417
|
+
* to what it follows when that is a plain relationship; otherwise it is
|
|
418
|
+
* refused with what to change instead (see `refused`).
|
|
419
|
+
*
|
|
420
|
+
* LINKS: `links: { key: { from, expr } }` makes this family's `key` follow
|
|
421
|
+
* another placed object — `from` is a family (it reports its parameters and
|
|
422
|
+
* outputs) or a wall (it reports thickness, length, height); `expr` is an
|
|
423
|
+
* expression over those reports. `null` removes a link. Links that would
|
|
424
|
+
* make objects follow each other in a loop are refused. Values accept unit
|
|
425
|
+
* literals ("600mm").
|
|
426
|
+
*
|
|
427
|
+
* RELATIONS: `relations: { add: [...], remove: ["r1"] }` sets how the family
|
|
428
|
+
* sits against other placed objects or cuts its host:
|
|
429
|
+
* - `attach` — `{ kind: "attach", side: "base" | "top", to: <object> | { level, face? }, offset? }`:
|
|
430
|
+
* sits on a surface, or its top reaches one (reports `reach`, `gap`, `surface`).
|
|
431
|
+
* - `join` — `{ kind: "join", end, to: <family>, toEnd, bindOther? }`: its end moves onto
|
|
432
|
+
* the other's end (reports `angle`, `absAngle`, `mitre`).
|
|
433
|
+
* - `span` — `{ kind: "span", from, to, at? }`: pinned between two objects, running along
|
|
434
|
+
* its +X (reports `distance`, `run`, `rise`, `slope`, `from.*`, `to.*`).
|
|
435
|
+
* - `path` — `{ kind: "path", from? | points, closed? }`: follows a wall's centreline, a
|
|
436
|
+
* family's run or given points (reports `length`; the recipe reads the path).
|
|
437
|
+
* - `cut` — `{ kind: "cut", to: "host" }`: applies recipe voids to the current host.
|
|
438
|
+
* Removing it closes the opening without detaching or deleting the family.
|
|
439
|
+
* Door/window placement adds it. Hosting alone does not cut. No parameter binds.
|
|
440
|
+
* Placement relations take `bind: { <param>: "<expression over its reports>" }`; bound
|
|
441
|
+
* parameters are read-only. A relation with an existing `id` replaces it.
|
|
442
|
+
*
|
|
443
|
+
* `length` values are Snaptrude units: the same 254 mm unit as the kernel's
|
|
444
|
+
* internal (`"babylon"`) unit — convert the user's number with
|
|
445
|
+
* `core.units.convert` first; a bare `400` is 400 SU, not 400 mm. Kernel part
|
|
446
|
+
* breps read before the edit are stale: re-read `getParameters`.
|
|
447
|
+
*
|
|
448
|
+
* @param args The component and the values to change
|
|
449
|
+
* @returns The values now in effect
|
|
450
|
+
*
|
|
451
|
+
* @examplePrompt Make that table seat 8
|
|
452
|
+
* @examplePrompt Change the stud spacing to 400
|
|
453
|
+
*
|
|
454
|
+
* # Example
|
|
455
|
+
* ```ts
|
|
456
|
+
* await snaptrude.design.family.setParameters({ component, values: { seats: 8 } })
|
|
457
|
+
* await snaptrude.design.family.setParameters({ component, children: { chair: { width: "380mm" } } })
|
|
458
|
+
* await snaptrude.design.family.setParameters({
|
|
459
|
+
* component: railing,
|
|
460
|
+
* links: { steps: { from: stair, expr: "steps" }, side: { from: stair, expr: "width - 40mm" } },
|
|
461
|
+
* })
|
|
462
|
+
* await snaptrude.design.family.setParameters({
|
|
463
|
+
* component: post,
|
|
464
|
+
* relations: { add: [{ kind: "attach", side: "top", to: slab, bind: { height: "reach" } }] },
|
|
465
|
+
* })
|
|
466
|
+
* ```
|
|
467
|
+
*/
|
|
468
|
+
public abstract setParameters(
|
|
469
|
+
args: PluginDesignFamilySetParametersArgs,
|
|
470
|
+
): PluginApiReturn<PluginFamilyInstanceParameters>
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Save the prepared bundle to the library: the open project's team library in a team
|
|
474
|
+
* project, else the user's personal library. `teamId` picks another team, and `null` the
|
|
475
|
+
* personal library. {@linkcode PluginDesignFamilyApi.place} already
|
|
476
|
+
* saves generic components. Hosted doors/windows save without placement;
|
|
477
|
+
* they need a wall and position through `design.create.door` / `design.create.window`.
|
|
478
|
+
* For a non-hosted family with no placed instance, saving also drops one
|
|
479
|
+
* standalone sample at the current view centre on the active storey
|
|
480
|
+
* (`placed: true`). This includes architectural families: the sample does
|
|
481
|
+
* not assign a family to a native host.
|
|
482
|
+
*
|
|
483
|
+
* When the draft edits a saved family (opened with `get({ id })` /
|
|
484
|
+
* `get({ component })`), or its name is already taken in that library, the
|
|
485
|
+
* APP asks the user in its own window whether to save a new version of that
|
|
486
|
+
* family — every placed copy in this project moves to it, and families
|
|
487
|
+
* nesting it may be offered theirs — or a new family ("new family" is the
|
|
488
|
+
* default). The agent never asks this itself and cannot answer the window.
|
|
489
|
+
* `decision` reports what the user chose; `"cancelled"` means nothing was
|
|
490
|
+
* saved (`ok: false`) and nothing will auto-save until `create` is called again.
|
|
491
|
+
*
|
|
492
|
+
* @param args Optional library name and team scope
|
|
493
|
+
* @returns The catalogued definition's id and version, and what the user chose
|
|
494
|
+
*
|
|
495
|
+
* @examplePrompt Save this as a family called Vertical Louvre
|
|
496
|
+
* @examplePrompt Add it to my library
|
|
497
|
+
*
|
|
498
|
+
* # Example
|
|
499
|
+
* ```ts
|
|
500
|
+
* const { definitionId } = await snaptrude.design.family.create({ name: "Louvre Screen" })
|
|
501
|
+
* ```
|
|
502
|
+
*/
|
|
503
|
+
public abstract create(
|
|
504
|
+
args?: PluginDesignFamilyCreateArgs,
|
|
505
|
+
): PluginApiReturn<PluginFamilyCreateResult>
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Remove a family from the library: its catalog row is archived, so it
|
|
509
|
+
* leaves the Families panel. Placed instances are untouched — in this
|
|
510
|
+
* project and every other — because archived pins keep resolving. Pass
|
|
511
|
+
* `instances: true` to ALSO delete this project's instances in one undoable
|
|
512
|
+
* command; do that only when the user asks for the instances to go.
|
|
513
|
+
*
|
|
514
|
+
* Omit `id` to target the prepared draft. `id` may be a draft id, a saved
|
|
515
|
+
* catalog id, or any family from `list()`.
|
|
516
|
+
*
|
|
517
|
+
* @param args Optional family id, and whether to delete instances too
|
|
518
|
+
* @returns Whether a library row was archived and how many instances were removed
|
|
519
|
+
*
|
|
520
|
+
* @examplePrompt Delete the Louvre Screen family from my library
|
|
521
|
+
* @examplePrompt Get rid of that bollard and every copy of it in here
|
|
522
|
+
*
|
|
523
|
+
* # Example
|
|
524
|
+
* ```ts
|
|
525
|
+
* await snaptrude.design.family.delete({ id: "revit:steel-bollard~bef047" })
|
|
526
|
+
* await snaptrude.design.family.delete({ id: "revit:steel-bollard~bef047", instances: true })
|
|
527
|
+
* ```
|
|
528
|
+
*/
|
|
529
|
+
public abstract delete(
|
|
530
|
+
args?: PluginDesignFamilyDeleteArgs | string,
|
|
531
|
+
): PluginApiReturn<PluginFamilyDeleteResult>
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/** Optional reference sections keep unrelated grammar out of the core response. */
|
|
535
|
+
export const PluginFamilyReferenceSection = z.enum([
|
|
536
|
+
"architectural",
|
|
537
|
+
"networks",
|
|
538
|
+
"relations",
|
|
539
|
+
])
|
|
540
|
+
export type PluginFamilyReferenceSection = z.infer<
|
|
541
|
+
typeof PluginFamilyReferenceSection
|
|
542
|
+
>
|
|
543
|
+
|
|
544
|
+
/** Omit section for the core grammar, workflow and example catalogue. */
|
|
545
|
+
export const PluginDesignFamilyGetReferenceArgs = z.object({
|
|
546
|
+
section: PluginFamilyReferenceSection.optional(),
|
|
547
|
+
})
|
|
548
|
+
export type PluginDesignFamilyGetReferenceArgs = z.infer<
|
|
549
|
+
typeof PluginDesignFamilyGetReferenceArgs
|
|
550
|
+
>
|
|
551
|
+
|
|
552
|
+
/** Compact output is opt-in: existing object consumers must retain their metadata. */
|
|
553
|
+
export const PluginDesignFamilyListArgs = z.object({
|
|
554
|
+
compact: z.boolean().optional(),
|
|
555
|
+
})
|
|
556
|
+
export type PluginDesignFamilyListArgs = z.infer<
|
|
557
|
+
typeof PluginDesignFamilyListArgs
|
|
558
|
+
>
|
|
559
|
+
|
|
560
|
+
// ---------------------------------------------------------------------------
|
|
561
|
+
// Result types
|
|
562
|
+
// ---------------------------------------------------------------------------
|
|
563
|
+
|
|
564
|
+
/** One worked example in the catalogue returned by `getExamples`. */
|
|
565
|
+
export interface PluginFamilyExampleSummary {
|
|
566
|
+
id: string
|
|
567
|
+
label: string
|
|
568
|
+
discipline: "architecture" | "structure" | "mep" | "furniture"
|
|
569
|
+
/** The patterns it demonstrates, e.g. "nesting", "slot", "path", "union", "trim". */
|
|
570
|
+
teaches: string[]
|
|
571
|
+
/** Size of the bundle as compact JSON, kilobytes — what fetching it costs. */
|
|
572
|
+
sizeKB: number
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/** One worked example with its full bundle, from `getExamples({ id })`. */
|
|
576
|
+
export interface PluginFamilyExample extends PluginFamilyExampleSummary {
|
|
577
|
+
bundle: PluginFamilyBundle
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/** The family-spec DSL reference returned by `getReference`. */
|
|
581
|
+
export interface PluginFamilyReference {
|
|
582
|
+
/** Grammar, op table, expression whitelist, repair contract, worked example. */
|
|
583
|
+
reference: string
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* A family spec bundle. `root` is the family; `deps` carries every spec
|
|
588
|
+
* reachable through an `instance` op, keyed by spec id, so a saved family is
|
|
589
|
+
* self-contained and reloads anywhere.
|
|
590
|
+
*
|
|
591
|
+
* The internal shape of a spec is documented by
|
|
592
|
+
* {@linkcode PluginDesignFamilyApi.getReference}, not by these types — it is a
|
|
593
|
+
* data DSL whose grammar changes independently of this API surface.
|
|
594
|
+
*/
|
|
595
|
+
export interface PluginFamilyBundle {
|
|
596
|
+
root: Record<string, unknown>
|
|
597
|
+
deps?: Record<string, Record<string, unknown>>
|
|
598
|
+
/** Connector metadata saved with the recipe; network placement evaluates these expressions. */
|
|
599
|
+
network?: NetworkFamilyContract
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/** Which saved family a draft edits. */
|
|
603
|
+
export interface PluginFamilyEditing {
|
|
604
|
+
/** Library id of the family being edited. */
|
|
605
|
+
familyId: string
|
|
606
|
+
/** The version that was opened. */
|
|
607
|
+
version: number
|
|
608
|
+
/** Its library name — an update saves under this name. */
|
|
609
|
+
name: string
|
|
610
|
+
/** "your library", or the team library's name. */
|
|
611
|
+
library: string
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/** What `get` returns: the bundle, plus `editing` when the draft edits a saved family. */
|
|
615
|
+
export interface PluginFamilyGetResult extends PluginFamilyBundle {
|
|
616
|
+
editing?: PluginFamilyEditing
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/** A family at a version, as `create` reports parents it did or did not update. */
|
|
620
|
+
export interface PluginFamilyVersionRef {
|
|
621
|
+
id: string
|
|
622
|
+
name: string
|
|
623
|
+
version: number
|
|
624
|
+
/** Set when the user chose to update it but the update failed. */
|
|
625
|
+
error?: string
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/** Axis-aligned extents of a built family. Snaptrude units (1 SU = 254 mm), +Y up. */
|
|
629
|
+
export interface PluginFamilyBBox {
|
|
630
|
+
min: [number, number, number]
|
|
631
|
+
max: [number, number, number]
|
|
632
|
+
/** `[width, height, depth]` in Snaptrude units — convert with `core.units.convert` for display. */
|
|
633
|
+
size: [number, number, number]
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/** One row of a family's parameter sheet. */
|
|
637
|
+
export interface PluginFamilyParameter {
|
|
638
|
+
key: string
|
|
639
|
+
label: string
|
|
640
|
+
type: "length" | "integer" | "enum" | "text" | "boolean"
|
|
641
|
+
/** Current value. `length` (Snaptrude units) and `integer` are numbers; the rest are strings. */
|
|
642
|
+
value: string | number
|
|
643
|
+
default: string | number
|
|
644
|
+
/**
|
|
645
|
+
* Whether this parameter actually drives geometry — INFERRED by the host from
|
|
646
|
+
* which names the spec's expressions reference. `false` means the value is
|
|
647
|
+
* stored and round-tripped but moves nothing.
|
|
648
|
+
*/
|
|
649
|
+
drivesGeometry: boolean
|
|
650
|
+
min?: number
|
|
651
|
+
max?: number
|
|
652
|
+
options?: readonly string[]
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* A validation diagnostic. `path` is a JSON pointer into the submitted bundle —
|
|
657
|
+
* fix that node and re-`prepare`.
|
|
658
|
+
*/
|
|
659
|
+
export interface PluginFamilyDiagnostic {
|
|
660
|
+
/** JSON pointer, e.g. `"/root/geometry/2/body/0/w"`. */
|
|
661
|
+
path: string
|
|
662
|
+
code:
|
|
663
|
+
| "schema"
|
|
664
|
+
| "bad-expression"
|
|
665
|
+
| "unknown-name"
|
|
666
|
+
| "unknown-def"
|
|
667
|
+
| "cycle"
|
|
668
|
+
| "duplicate-name"
|
|
669
|
+
| "unbounded-repeat"
|
|
670
|
+
| "depth"
|
|
671
|
+
| "bad-dimension"
|
|
672
|
+
| "proportion"
|
|
673
|
+
/** A part the geometry kernel could not build exactly; drawn approximately. */
|
|
674
|
+
| "kernel-fallback"
|
|
675
|
+
/** A part whose hole loop crosses its outline. */
|
|
676
|
+
| "profile-hole"
|
|
677
|
+
message: string
|
|
678
|
+
hint?: string
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* One placed part and which engine built it, from `getParameters`. `"kernel"` —
|
|
683
|
+
* a curved sketch built exactly; `"mesh"` — straight-edged, or the kernel
|
|
684
|
+
* fallback.
|
|
685
|
+
*/
|
|
686
|
+
export interface PluginFamilyPart {
|
|
687
|
+
name: string
|
|
688
|
+
engine: "kernel" | "mesh"
|
|
689
|
+
/**
|
|
690
|
+
* Kernel parts only: the part's exact solid, for `core.geom.query.brep.*` /
|
|
691
|
+
* `core.geom.query.face.getSurfaceKind`. READ-ONLY and PART-LOCAL — in the
|
|
692
|
+
* part's own frame, not where the component sits, so never pass it to a
|
|
693
|
+
* boolean or `design.create.massFromBrep` (a family changes only through
|
|
694
|
+
* `setParameters`). Valid until the next parameter change: re-read
|
|
695
|
+
* `getParameters` after `setParameters`.
|
|
696
|
+
*/
|
|
697
|
+
brep?: BrepHandle
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/** What `prepare` reports. */
|
|
701
|
+
export interface PluginFamilyPrepareResult {
|
|
702
|
+
/** `false` means the spec was rejected — read `errors`, fix, re-prepare. */
|
|
703
|
+
ok: boolean
|
|
704
|
+
familyId?: string
|
|
705
|
+
version?: number
|
|
706
|
+
label?: string
|
|
707
|
+
/** How many parts the family built. Sanity-check against intent. */
|
|
708
|
+
partCount?: number
|
|
709
|
+
/** Extents in Snaptrude units. Sanity-check against intent. */
|
|
710
|
+
bbox?: PluginFamilyBBox
|
|
711
|
+
evalMs?: number
|
|
712
|
+
parameters?: PluginFamilyParameter[]
|
|
713
|
+
/** Computed quantities, when the spec declares a `takeoff` block. */
|
|
714
|
+
takeoff?: Record<string, number | string>
|
|
715
|
+
/** The first few part names — enough to check the naming came out right. */
|
|
716
|
+
sampleParts?: string[]
|
|
717
|
+
/** Present when `snapshot` was asked for. */
|
|
718
|
+
snapshot?: PluginFamilySnapshot
|
|
719
|
+
errors: PluginFamilyDiagnostic[]
|
|
720
|
+
warnings: PluginFamilyDiagnostic[]
|
|
721
|
+
/** Set when the bundle validated but threw while building. */
|
|
722
|
+
error?: string
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
/**
|
|
726
|
+
* The host's diff of what a flex did to the geometry. This is the evidence that
|
|
727
|
+
* a family is parametric.
|
|
728
|
+
*/
|
|
729
|
+
export interface PluginFamilyChangeBuckets {
|
|
730
|
+
added: number
|
|
731
|
+
removed: number
|
|
732
|
+
moved: number
|
|
733
|
+
rebuilt: number
|
|
734
|
+
addedNames: string[]
|
|
735
|
+
removedNames: string[]
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/** What `flex` reports. */
|
|
739
|
+
export interface PluginFamilyFlexResult {
|
|
740
|
+
ok: boolean
|
|
741
|
+
/** The RESOLVED values after merge + clamping — not necessarily what was sent. */
|
|
742
|
+
values?: Record<string, number | string>
|
|
743
|
+
partCount?: number
|
|
744
|
+
bbox?: PluginFamilyBBox
|
|
745
|
+
evalMs?: number
|
|
746
|
+
changed?: PluginFamilyChangeBuckets
|
|
747
|
+
takeoff?: Record<string, number | string>
|
|
748
|
+
/** 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. */
|
|
749
|
+
warnings?: PluginFamilyDiagnostic[]
|
|
750
|
+
/** Present when `snapshot` was asked for. */
|
|
751
|
+
snapshot?: PluginFamilySnapshot
|
|
752
|
+
error?: string
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/** One family available to place — from the session registry or the catalog. */
|
|
756
|
+
export interface PluginFamilySummary {
|
|
757
|
+
id: string
|
|
758
|
+
version: number
|
|
759
|
+
label: string
|
|
760
|
+
category?: string
|
|
761
|
+
/** The category's type token (`root.subType`), e.g. singleDoor or casementDouble. */
|
|
762
|
+
subType?: string
|
|
763
|
+
/**
|
|
764
|
+
* Opening contract kind, distinct from provenance (`kind`). Place with the
|
|
765
|
+
* positional `design.create.door` / `window` API and edit via family parameters.
|
|
766
|
+
*/
|
|
767
|
+
openingKind?: "door" | "window"
|
|
768
|
+
/** Number of rows on the family's parameter sheet. */
|
|
769
|
+
parameters: number
|
|
770
|
+
/**
|
|
771
|
+
* Where the family came from, which decides how far you can edit it.
|
|
772
|
+
*
|
|
773
|
+
* `"native"` — authored from a JSON spec through this API. Its recipe is
|
|
774
|
+
* data, so every parameter genuinely re-runs the geometry.
|
|
775
|
+
*
|
|
776
|
+
* `"revit"` — imported from a Revit family. Parameters exist and are
|
|
777
|
+
* editable, but only those the importer could bind to geometry actually
|
|
778
|
+
* change the mesh; the rest are carried for round-tripping. Check
|
|
779
|
+
* `drivesGeometry` on the parameter before promising the user a change.
|
|
780
|
+
*/
|
|
781
|
+
kind: "native" | "revit"
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/** Exact catalog pin. Unsaved session definitions are not catalog inventory. */
|
|
785
|
+
export const PluginDesignFamilyGetDefinitionArgs = z.object({
|
|
786
|
+
id: z.string().min(1),
|
|
787
|
+
version: z.number().int().positive(),
|
|
788
|
+
})
|
|
789
|
+
export type PluginDesignFamilyGetDefinitionArgs = z.infer<
|
|
790
|
+
typeof PluginDesignFamilyGetDefinitionArgs
|
|
791
|
+
>
|
|
792
|
+
|
|
793
|
+
/** Saved-family metadata. Physical lengths use mm; angle entries retain degrees. */
|
|
794
|
+
export const PluginFamilyDefinitionParameter = z.object({
|
|
795
|
+
key: z.string(),
|
|
796
|
+
label: z.string(),
|
|
797
|
+
type: z.enum(["length", "integer", "enum", "text", "boolean"]),
|
|
798
|
+
/** Angles use the legacy length type. Check unit before interpreting numeric values. */
|
|
799
|
+
unit: z.enum(["mm", "degrees"]).optional(),
|
|
800
|
+
default: z.union([z.number().finite(), z.string()]),
|
|
801
|
+
min: z.number().finite().optional(),
|
|
802
|
+
max: z.number().finite().optional(),
|
|
803
|
+
options: z.array(z.string()).optional(),
|
|
804
|
+
drivesGeometry: z.boolean(),
|
|
805
|
+
readOnly: z.boolean(),
|
|
806
|
+
formulaDriven: z.boolean(),
|
|
807
|
+
/** False means shared/type: placement cannot change it for one instance. */
|
|
808
|
+
isInstance: z.boolean(),
|
|
809
|
+
})
|
|
810
|
+
export type PluginFamilyDefinitionParameter = z.infer<
|
|
811
|
+
typeof PluginFamilyDefinitionParameter
|
|
812
|
+
>
|
|
813
|
+
|
|
814
|
+
/** Read-only catalog inventory. Boolean defaults retain the family DSL's Yes/No strings. */
|
|
815
|
+
export const PluginFamilyDefinition = z.object({
|
|
816
|
+
id: z.string(),
|
|
817
|
+
version: z.number().int().positive(),
|
|
818
|
+
label: z.string(),
|
|
819
|
+
category: z.string().optional(),
|
|
820
|
+
lengthUnit: z.literal("mm"),
|
|
821
|
+
parameters: z.array(PluginFamilyDefinitionParameter),
|
|
822
|
+
/** Pinned connector metadata. Absence means this family cannot build a topology network. */
|
|
823
|
+
network: NetworkFamilyContract.optional(),
|
|
824
|
+
})
|
|
825
|
+
export type PluginFamilyDefinition = z.infer<typeof PluginFamilyDefinition>
|
|
826
|
+
|
|
827
|
+
/** What `place` reports. */
|
|
828
|
+
export interface PluginFamilyPlaceResult {
|
|
829
|
+
ok: boolean
|
|
830
|
+
familyId?: string
|
|
831
|
+
version?: number
|
|
832
|
+
/**
|
|
833
|
+
* The created component — present ONLY when `position` was given. In
|
|
834
|
+
* interactive mode nothing exists yet, so there is nothing to hand back;
|
|
835
|
+
* the user has not clicked.
|
|
836
|
+
*/
|
|
837
|
+
component?: ComponentHandle
|
|
838
|
+
error?: string
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
/** A placed component's parameter state, from `getParameters` / `setParameters`. */
|
|
842
|
+
export interface PluginFamilyInstanceParameters {
|
|
843
|
+
/** The definition this instance is pinned to. */
|
|
844
|
+
familyId: string
|
|
845
|
+
version: number
|
|
846
|
+
label: string
|
|
847
|
+
/** Current values, keyed by parameter key. */
|
|
848
|
+
values: Record<string, number | string>
|
|
849
|
+
/** The sheet: what each key means, its bounds, and whether it moves geometry. */
|
|
850
|
+
parameters: PluginFamilyParameter[]
|
|
851
|
+
/** The placed parts; kernel parts carry their exact `brep`. */
|
|
852
|
+
parts?: PluginFamilyPart[]
|
|
853
|
+
/** Present when `getParameters` was asked for a `snapshot`. */
|
|
854
|
+
snapshot?: PluginFamilySnapshot
|
|
855
|
+
/** The family's `outputs`, as last built. */
|
|
856
|
+
reports?: Record<string, number | string>
|
|
857
|
+
/** Every declared nested child, as last built. */
|
|
858
|
+
children?: PluginFamilyNestedChild[]
|
|
859
|
+
/** Parameters that follow another object (links) or the host. */
|
|
860
|
+
follows?: PluginFamilyFollow[]
|
|
861
|
+
/** Where a hosted family sits: a system wall, or a family by id. */
|
|
862
|
+
host?: ({ kind: "wall" } | { kind: "family"; id: string }) & {
|
|
863
|
+
/** True when the host no longer has room for it: it is out of the model until it fits again. */
|
|
864
|
+
parked?: boolean
|
|
865
|
+
}
|
|
866
|
+
/** Guests hosted in THIS family that no longer fit it (parked); they return when they fit. */
|
|
867
|
+
parkedGuests?: { id: string; family: string }[]
|
|
868
|
+
/** How it sits against other placed objects (attach, join, span, path), with current reports. */
|
|
869
|
+
relations?: PluginFamilyRelation[]
|
|
870
|
+
/** setParameters only: the edits refused, each with what to change instead. */
|
|
871
|
+
refused?: string[]
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/** A nested child of a placed family. */
|
|
875
|
+
export interface PluginFamilyNestedChild {
|
|
876
|
+
/** "chair", or "chair/cushion" for a child of a child — the key for `children` edits. */
|
|
877
|
+
path: string
|
|
878
|
+
label: string
|
|
879
|
+
/** The family it is now (a slot's current pick). */
|
|
880
|
+
family: string
|
|
881
|
+
familyLabel: string
|
|
882
|
+
/** How many copies the parent places. */
|
|
883
|
+
copies: number
|
|
884
|
+
slot?: { expr: string; options: string[] }
|
|
885
|
+
values: Record<string, number | string>
|
|
886
|
+
/** Values the parent sets, with the expression it sets them by. */
|
|
887
|
+
setBy: Record<string, string | undefined>
|
|
888
|
+
reports: Record<string, number | string>
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
/** A placed relation as stored, plus what it measures now. */
|
|
892
|
+
export type PluginFamilyRelation = PluginFamilyRelationInput & {
|
|
893
|
+
id: string
|
|
894
|
+
/** "Attached to", "Joins", "Spans between", "Along a path", "Cuts host". */
|
|
895
|
+
label: string
|
|
896
|
+
summary: string
|
|
897
|
+
/** False when a target is gone: the family stayed where it was. */
|
|
898
|
+
live: boolean
|
|
899
|
+
reports: Record<string, number | string>
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
/** One relation to add (`relations.add`). Targets are handles, except a cut's symbolic "host". */
|
|
903
|
+
export type PluginFamilyRelationInput = {
|
|
904
|
+
/** Replaces the relation with this id; a new one gets "r1", "r2"… */
|
|
905
|
+
id?: string
|
|
906
|
+
/** This family's parameter → an expression over the relation's reports and own parameters. */
|
|
907
|
+
bind?: Record<string, string>
|
|
908
|
+
} & (
|
|
909
|
+
| {
|
|
910
|
+
kind: "attach"
|
|
911
|
+
side: "base" | "top"
|
|
912
|
+
to: ComponentHandle | { level: number; face?: "base" | "top" }
|
|
913
|
+
offset?: number
|
|
914
|
+
}
|
|
915
|
+
| {
|
|
916
|
+
kind: "join"
|
|
917
|
+
end: "start" | "end"
|
|
918
|
+
to: ComponentHandle
|
|
919
|
+
toEnd: "start" | "end"
|
|
920
|
+
/** The other family's parameters, over the same reports. */
|
|
921
|
+
bindOther?: Record<string, string>
|
|
922
|
+
}
|
|
923
|
+
| {
|
|
924
|
+
kind: "span"
|
|
925
|
+
from: ComponentHandle
|
|
926
|
+
to: ComponentHandle
|
|
927
|
+
at?: "top" | "centre" | "base"
|
|
928
|
+
}
|
|
929
|
+
| {
|
|
930
|
+
kind: "path"
|
|
931
|
+
from?: ComponentHandle
|
|
932
|
+
points?: [number, number, number][]
|
|
933
|
+
closed?: boolean
|
|
934
|
+
}
|
|
935
|
+
| {
|
|
936
|
+
/** Apply recipe voids to the existing host without adding another host ID. */
|
|
937
|
+
kind: "cut"
|
|
938
|
+
to: "host"
|
|
939
|
+
bind?: never
|
|
940
|
+
}
|
|
941
|
+
)
|
|
942
|
+
|
|
943
|
+
/** One parameter that follows something else. */
|
|
944
|
+
export interface PluginFamilyFollow {
|
|
945
|
+
key: string
|
|
946
|
+
/** "relation": set by a placed relation — `from` is the relation's id. */
|
|
947
|
+
kind: "link" | "host" | "relation"
|
|
948
|
+
/** The source's id. */
|
|
949
|
+
from: string
|
|
950
|
+
fromLabel: string
|
|
951
|
+
/** The expression over the source's reports (for a host: the report name). */
|
|
952
|
+
expr: string
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
/** What `create` reports. */
|
|
956
|
+
export interface PluginFamilyCreateResult {
|
|
957
|
+
ok: boolean
|
|
958
|
+
definitionId?: string
|
|
959
|
+
version?: number
|
|
960
|
+
guid?: string
|
|
961
|
+
/** True when the save also dropped one instance into the model (nothing was placed yet). */
|
|
962
|
+
placed?: boolean
|
|
963
|
+
/** Host guidance for the caller, e.g. that an instance was placed at the view centre. */
|
|
964
|
+
hint?: string
|
|
965
|
+
/**
|
|
966
|
+
* What the user chose in the app's window: `"version"` (a new version of
|
|
967
|
+
* the saved family), `"new"` (a new family), `"cancelled"` (nothing saved).
|
|
968
|
+
* `"saved"` = no question was needed.
|
|
969
|
+
*/
|
|
970
|
+
decision?: "version" | "new" | "cancelled" | "saved"
|
|
971
|
+
/** The library name it was saved under. */
|
|
972
|
+
name?: string
|
|
973
|
+
/** Placed copies in this project moved to the new version (the family's and updated parents'). */
|
|
974
|
+
movedCopies?: number
|
|
975
|
+
/** What the moved copies lost because the new version no longer has it. */
|
|
976
|
+
dropped?: {
|
|
977
|
+
values: Array<{ copy: string; key: string }>
|
|
978
|
+
childValues: Array<{ copy: string; path: string }>
|
|
979
|
+
follows: Array<{ copy: string; key: string; kind: "link" | "relation" }>
|
|
980
|
+
}
|
|
981
|
+
/** Families nesting this one that the user chose to update, at their new versions. */
|
|
982
|
+
parentsUpdated?: PluginFamilyVersionRef[]
|
|
983
|
+
/** Families nesting this one that keep their version. */
|
|
984
|
+
parentsKept?: PluginFamilyVersionRef[]
|
|
985
|
+
error?: string
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
/** What `delete` reports. */
|
|
989
|
+
export interface PluginFamilyDeleteResult {
|
|
990
|
+
ok: boolean
|
|
991
|
+
familyId?: string
|
|
992
|
+
/** Instances deleted from the scene (only with `instances: true`), in one undoable command. */
|
|
993
|
+
removedInstances?: number
|
|
994
|
+
/** True when a library row was archived; false for an unsaved draft. */
|
|
995
|
+
archived?: boolean
|
|
996
|
+
error?: string
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
// ---------------------------------------------------------------------------
|
|
1000
|
+
// Argument schemas
|
|
1001
|
+
// ---------------------------------------------------------------------------
|
|
1002
|
+
|
|
1003
|
+
/**
|
|
1004
|
+
* A spec bundle as it crosses the boundary: `root` plus optional `deps`.
|
|
1005
|
+
*
|
|
1006
|
+
* Loose by design — the spec grammar is a data DSL validated by the host's own
|
|
1007
|
+
* validator, which returns JSON-pointer diagnostics far more useful than a Zod
|
|
1008
|
+
* shape error. This schema only guarantees the envelope is an object.
|
|
1009
|
+
*/
|
|
1010
|
+
export const PluginFamilyBundleSchema = z.object({
|
|
1011
|
+
root: z.record(z.string(), z.unknown()),
|
|
1012
|
+
deps: z.record(z.string(), z.record(z.string(), z.unknown())).optional(),
|
|
1013
|
+
network: NetworkFamilyContract.optional(),
|
|
1014
|
+
})
|
|
1015
|
+
|
|
1016
|
+
/**
|
|
1017
|
+
* Arguments for {@linkcode PluginDesignFamilyApi.prepare}.
|
|
1018
|
+
*
|
|
1019
|
+
* | Property | Type | Description |
|
|
1020
|
+
* |---|---|---|
|
|
1021
|
+
* | `bundle` | {@linkcode PluginFamilyBundle} | The family spec bundle |
|
|
1022
|
+
* | `spec` | {@linkcode PluginFamilyBundle} | Alias for `bundle` |
|
|
1023
|
+
* | `snapshot` | {@linkcode PluginFamilySnapshotOption} | Also return a picture of the built family |
|
|
1024
|
+
*
|
|
1025
|
+
* Exactly one of `bundle` / `spec` is required. A bare `FamilySpec` (no `root`
|
|
1026
|
+
* wrapper) is also accepted by the host and wrapped for the caller.
|
|
1027
|
+
*/
|
|
1028
|
+
/**
|
|
1029
|
+
* Ask for a picture with the result: `true` for the default sheet (iso, front,
|
|
1030
|
+
* right and top tiles with a dimension line), or an object to choose views and
|
|
1031
|
+
* tile size. The picture is an isolated offscreen render of the family alone —
|
|
1032
|
+
* the viewport is not moved — and comes back as a PNG data URL in the result's
|
|
1033
|
+
* `snapshot.image`. Through the agent's `run_worker`, return the result and the
|
|
1034
|
+
* image reaches the model as an image.
|
|
1035
|
+
*
|
|
1036
|
+
* `focus` makes it a close-up: `{ parts: ["seat", "back", "slat@*"] }` frames
|
|
1037
|
+
* the joint where those groups meet (`*` matches any run of characters;
|
|
1038
|
+
* nested names are `a/b`), or their full bounds if they do not meet. A margin
|
|
1039
|
+
* of a quarter of their size (at least 50 mm) surrounds the joint.
|
|
1040
|
+
* `{ box: { min, max } }` frames a region in family space
|
|
1041
|
+
* (Snaptrude units, +Y up). Every other part is still drawn; the dimension line
|
|
1042
|
+
* then gives the framed region and the result says `closeUp: true`. A part
|
|
1043
|
+
* name that matches nothing fails the snapshot with a list of real names.
|
|
1044
|
+
*/
|
|
1045
|
+
export const PluginFamilySnapshotFocus = z.union([
|
|
1046
|
+
z.object({ parts: z.array(z.string().min(1)).min(1) }),
|
|
1047
|
+
z.object({
|
|
1048
|
+
box: z
|
|
1049
|
+
.object({
|
|
1050
|
+
min: z.tuple([
|
|
1051
|
+
z.number().finite(),
|
|
1052
|
+
z.number().finite(),
|
|
1053
|
+
z.number().finite(),
|
|
1054
|
+
]),
|
|
1055
|
+
max: z.tuple([
|
|
1056
|
+
z.number().finite(),
|
|
1057
|
+
z.number().finite(),
|
|
1058
|
+
z.number().finite(),
|
|
1059
|
+
]),
|
|
1060
|
+
})
|
|
1061
|
+
.refine(
|
|
1062
|
+
({ min, max }) => min.every((value, axis) => value <= max[axis]),
|
|
1063
|
+
{
|
|
1064
|
+
message: "Each minimum must be at or below its maximum",
|
|
1065
|
+
},
|
|
1066
|
+
),
|
|
1067
|
+
}),
|
|
1068
|
+
])
|
|
1069
|
+
export type PluginFamilySnapshotFocus = z.infer<
|
|
1070
|
+
typeof PluginFamilySnapshotFocus
|
|
1071
|
+
>
|
|
1072
|
+
|
|
1073
|
+
/** One close-up tile; omission of view uses the sheet's first view. */
|
|
1074
|
+
export const PluginFamilySnapshotCloseUp = z.intersection(
|
|
1075
|
+
PluginFamilySnapshotFocus,
|
|
1076
|
+
z.object({ view: z.enum(["iso", "front", "right", "top"]).optional() }),
|
|
1077
|
+
)
|
|
1078
|
+
export type PluginFamilySnapshotCloseUp = z.infer<
|
|
1079
|
+
typeof PluginFamilySnapshotCloseUp
|
|
1080
|
+
>
|
|
1081
|
+
|
|
1082
|
+
export const PluginFamilySnapshotOption = z.union([
|
|
1083
|
+
z.boolean(),
|
|
1084
|
+
z.object({
|
|
1085
|
+
views: z.array(z.enum(["iso", "front", "right", "top"])).optional(),
|
|
1086
|
+
/** Pixels per tile (square), 64–2048; default 512. */
|
|
1087
|
+
tileSize: z.number().optional(),
|
|
1088
|
+
/** One focus across views, or 1–6 independent close-up tiles in one image. */
|
|
1089
|
+
focus: z
|
|
1090
|
+
.union([
|
|
1091
|
+
PluginFamilySnapshotFocus,
|
|
1092
|
+
z.array(PluginFamilySnapshotCloseUp).min(1).max(6),
|
|
1093
|
+
])
|
|
1094
|
+
.optional(),
|
|
1095
|
+
}),
|
|
1096
|
+
])
|
|
1097
|
+
export type PluginFamilySnapshotOption = z.infer<
|
|
1098
|
+
typeof PluginFamilySnapshotOption
|
|
1099
|
+
>
|
|
1100
|
+
|
|
1101
|
+
/** The picture block on prepare / flex / getParameters results when `snapshot` was asked for. */
|
|
1102
|
+
export interface PluginFamilySnapshot {
|
|
1103
|
+
ok: boolean
|
|
1104
|
+
/** PNG data URL of the composed sheet. */
|
|
1105
|
+
image?: string
|
|
1106
|
+
views?: ("iso" | "front" | "right" | "top")[]
|
|
1107
|
+
/** [width, height, depth] in SU. For a close-up list, this remains the whole family's size. */
|
|
1108
|
+
size?: [number, number, number]
|
|
1109
|
+
ms?: number
|
|
1110
|
+
/** True when the sheet is a close-up (`focus`), not the whole family. */
|
|
1111
|
+
closeUp?: boolean
|
|
1112
|
+
/** Per-tile results for a focus list. One invalid target does not discard other tiles. */
|
|
1113
|
+
closeUps?: Array<{
|
|
1114
|
+
view: "iso" | "front" | "right" | "top"
|
|
1115
|
+
label: string
|
|
1116
|
+
size?: [number, number, number]
|
|
1117
|
+
error?: string
|
|
1118
|
+
}>
|
|
1119
|
+
/** Set when the picture could not be taken (e.g. the legacy renderer); the call itself still succeeded. */
|
|
1120
|
+
error?: string
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
export const PluginDesignFamilyPrepareArgs = z.object({
|
|
1124
|
+
bundle: PluginFamilyBundleSchema.optional(),
|
|
1125
|
+
spec: PluginFamilyBundleSchema.optional(),
|
|
1126
|
+
snapshot: PluginFamilySnapshotOption.optional(),
|
|
1127
|
+
})
|
|
1128
|
+
export type PluginDesignFamilyPrepareArgs = z.infer<
|
|
1129
|
+
typeof PluginDesignFamilyPrepareArgs
|
|
1130
|
+
>
|
|
1131
|
+
|
|
1132
|
+
/**
|
|
1133
|
+
* Arguments for {@linkcode PluginDesignFamilyApi.flex}.
|
|
1134
|
+
*
|
|
1135
|
+
* | Property | Type | Description |
|
|
1136
|
+
* |---|---|---|
|
|
1137
|
+
* | `values` | `Record<string, number \| string>` | Optional sparse changes; omission does not rebuild |
|
|
1138
|
+
* | `snapshot` | {@linkcode PluginFamilySnapshotOption} | Also return a picture of the flexed family |
|
|
1139
|
+
*/
|
|
1140
|
+
export const PluginDesignFamilyFlexArgs = z.object({
|
|
1141
|
+
values: z.record(z.string(), z.union([z.number(), z.string()])).optional(),
|
|
1142
|
+
snapshot: PluginFamilySnapshotOption.optional(),
|
|
1143
|
+
})
|
|
1144
|
+
export type PluginDesignFamilyFlexArgs = z.infer<
|
|
1145
|
+
typeof PluginDesignFamilyFlexArgs
|
|
1146
|
+
>
|
|
1147
|
+
|
|
1148
|
+
/**
|
|
1149
|
+
* Arguments for {@linkcode PluginDesignFamilyApi.get}. All optional — no
|
|
1150
|
+
* arguments returns the prepared draft.
|
|
1151
|
+
*
|
|
1152
|
+
* | Property | Type | Description |
|
|
1153
|
+
* |---|---|---|
|
|
1154
|
+
* | `id` | `string` | A saved family to open for editing, from {@linkcode PluginDesignFamilyApi.list} |
|
|
1155
|
+
* | `version` | `number` | Exact version to open; defaults to the newest |
|
|
1156
|
+
* | `component` | {@linkcode ComponentHandle} | Open the family of this placed copy, at its version |
|
|
1157
|
+
*/
|
|
1158
|
+
export const PluginDesignFamilyGetArgs = z.object({
|
|
1159
|
+
id: z.string().optional(),
|
|
1160
|
+
version: z.number().int().positive().optional(),
|
|
1161
|
+
component: ComponentHandle.optional(),
|
|
1162
|
+
})
|
|
1163
|
+
export type PluginDesignFamilyGetArgs = z.infer<
|
|
1164
|
+
typeof PluginDesignFamilyGetArgs
|
|
1165
|
+
>
|
|
1166
|
+
|
|
1167
|
+
/**
|
|
1168
|
+
* Arguments for {@linkcode PluginDesignFamilyApi.getExamples}.
|
|
1169
|
+
*
|
|
1170
|
+
* | Property | Type | Description |
|
|
1171
|
+
* |---|---|---|
|
|
1172
|
+
* | `id` | `string` | One example's id — returns its bundle |
|
|
1173
|
+
* | `discipline` | `string` | List only this discipline: architecture, structure, mep, furniture |
|
|
1174
|
+
* | `tag` | `string` | List only examples teaching this pattern (e.g. "path", "union") |
|
|
1175
|
+
*/
|
|
1176
|
+
export const PluginDesignFamilyGetExamplesArgs = z.object({
|
|
1177
|
+
id: z.string().optional(),
|
|
1178
|
+
discipline: z
|
|
1179
|
+
.enum(["architecture", "structure", "mep", "furniture"])
|
|
1180
|
+
.optional(),
|
|
1181
|
+
tag: z.string().optional(),
|
|
1182
|
+
})
|
|
1183
|
+
export type PluginDesignFamilyGetExamplesArgs = z.infer<
|
|
1184
|
+
typeof PluginDesignFamilyGetExamplesArgs
|
|
1185
|
+
>
|
|
1186
|
+
|
|
1187
|
+
/**
|
|
1188
|
+
* Arguments for {@linkcode PluginDesignFamilyApi.getTemplate}.
|
|
1189
|
+
* `kind` selects the opening contract. `subType` is a nonempty type token;
|
|
1190
|
+
* omission selects singleDoor / fixedWindow. Unsupported tokens return the
|
|
1191
|
+
* default template with guidance rather than preventing custom subtype authoring.
|
|
1192
|
+
*/
|
|
1193
|
+
export const PluginDesignFamilyGetTemplateArgs = z.object({
|
|
1194
|
+
kind: z.enum(["door", "window"]),
|
|
1195
|
+
subType: z.string().min(1).optional(),
|
|
1196
|
+
})
|
|
1197
|
+
export type PluginDesignFamilyGetTemplateArgs = z.infer<
|
|
1198
|
+
typeof PluginDesignFamilyGetTemplateArgs
|
|
1199
|
+
>
|
|
1200
|
+
|
|
1201
|
+
/**
|
|
1202
|
+
* Maps opening dimensions to authored length parameter KEYS, not values.
|
|
1203
|
+
* Width and height describe the wall hole; sill measures from the wall base
|
|
1204
|
+
* to the hole bottom (windows). Wall thickness follows the host wall.
|
|
1205
|
+
* The recipe and family parameter APIs use Snaptrude units (1 SU = 254 mm).
|
|
1206
|
+
*/
|
|
1207
|
+
export interface PluginFamilyOpeningContract {
|
|
1208
|
+
kind: "door" | "window"
|
|
1209
|
+
width: string
|
|
1210
|
+
height: string
|
|
1211
|
+
sill?: string
|
|
1212
|
+
wallThickness?: string
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
/** Starter recipe metadata returned by `getTemplate`; instances are ParametricComponents. */
|
|
1216
|
+
export interface PluginFamilyTemplateResult {
|
|
1217
|
+
/** Validated recipe bundle: edit its style while preserving the contract. */
|
|
1218
|
+
bundle: PluginFamilyBundle
|
|
1219
|
+
contract: PluginFamilyOpeningContract
|
|
1220
|
+
/** Actual template subtype, including when an unsupported token used the default. */
|
|
1221
|
+
subType: string
|
|
1222
|
+
/** Authoring rules and any note about an unsupported requested subtype. */
|
|
1223
|
+
guidance: string[]
|
|
1224
|
+
/** Available starter templates for the requested kind. */
|
|
1225
|
+
available: Array<{ subType: string; label: string; teaches: string[] }>
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
/**
|
|
1229
|
+
* Arguments for {@linkcode PluginDesignFamilyApi.create}.
|
|
1230
|
+
*
|
|
1231
|
+
* | Property | Type | Description |
|
|
1232
|
+
* |---|---|---|
|
|
1233
|
+
* | `name` | `string` | Library name; defaults to the spec's own label |
|
|
1234
|
+
* | `teamId` | `string \| null` | Library to save into: a team's id, or `null` for the personal library. Omit for the open project's team library (personal in a personal project) |
|
|
1235
|
+
*/
|
|
1236
|
+
export const PluginDesignFamilyCreateArgs = z.object({
|
|
1237
|
+
name: z.string().optional(),
|
|
1238
|
+
teamId: z.string().nullable().optional(),
|
|
1239
|
+
})
|
|
1240
|
+
export type PluginDesignFamilyCreateArgs = z.infer<
|
|
1241
|
+
typeof PluginDesignFamilyCreateArgs
|
|
1242
|
+
>
|
|
1243
|
+
|
|
1244
|
+
/**
|
|
1245
|
+
* Arguments for {@linkcode PluginDesignFamilyApi.delete}.
|
|
1246
|
+
*
|
|
1247
|
+
* | Property | Type | Description |
|
|
1248
|
+
* |---|---|---|
|
|
1249
|
+
* | `id` | `string` | Family to remove; omit for the prepared draft |
|
|
1250
|
+
* | `instances` | `boolean` | Also delete this project's placed instances; default false |
|
|
1251
|
+
*/
|
|
1252
|
+
export const PluginDesignFamilyDeleteArgs = z.object({
|
|
1253
|
+
id: z.string().optional(),
|
|
1254
|
+
instances: z.boolean().optional(),
|
|
1255
|
+
})
|
|
1256
|
+
export type PluginDesignFamilyDeleteArgs = z.infer<
|
|
1257
|
+
typeof PluginDesignFamilyDeleteArgs
|
|
1258
|
+
>
|
|
1259
|
+
|
|
1260
|
+
/**
|
|
1261
|
+
* Arguments for {@linkcode PluginDesignFamilyApi.place}.
|
|
1262
|
+
*
|
|
1263
|
+
* | Property | Type | Description |
|
|
1264
|
+
* |---|---|---|
|
|
1265
|
+
* | `id` | `string` | Family to place, from {@linkcode PluginDesignFamilyApi.list}. Omit to place the prepared draft |
|
|
1266
|
+
* | `version` | `number` | Exact version; defaults to the newest known |
|
|
1267
|
+
* | `position` | `Vec3Handle` or `{ x, y, z }` | Place here in SU. Prefer a vec3 handle; coordinates remain for existing callers. Omit to arm the cursor |
|
|
1268
|
+
* | `rotation` | `number` | Yaw in degrees about +Y. Only with `position` |
|
|
1269
|
+
* | `values` | `Record<string, number \| string>` | Parameter values for THIS instance |
|
|
1270
|
+
* | `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 |
|
|
1271
|
+
* | `teamId` | `string \| null` | Library an unsaved draft saves to before it is placed: a team's id, or `null` for the personal library. Omit for the open project's team library (personal in a personal project). Ignored for a family already in a library |
|
|
1272
|
+
*/
|
|
1273
|
+
export const PluginDesignFamilyPlaceArgs = z.object({
|
|
1274
|
+
id: z.string().optional(),
|
|
1275
|
+
version: z.number().int().positive().optional(),
|
|
1276
|
+
position: z.union([Vec3Handle, Vec3Components]).optional(),
|
|
1277
|
+
rotation: z.number().optional(),
|
|
1278
|
+
values: z.record(z.string(), z.union([z.number(), z.string()])).optional(),
|
|
1279
|
+
storey: z.number().int().optional(),
|
|
1280
|
+
/** Hosted families: wall or family host. Values apply before fit checks and cutting. */
|
|
1281
|
+
host: ComponentHandle.optional(),
|
|
1282
|
+
teamId: z.string().nullable().optional(),
|
|
1283
|
+
})
|
|
1284
|
+
export type PluginDesignFamilyPlaceArgs = z.infer<
|
|
1285
|
+
typeof PluginDesignFamilyPlaceArgs
|
|
1286
|
+
>
|
|
1287
|
+
|
|
1288
|
+
/**
|
|
1289
|
+
* Arguments for {@linkcode PluginDesignFamilyApi.getParameters}.
|
|
1290
|
+
*
|
|
1291
|
+
* | Property | Type | Description |
|
|
1292
|
+
* |---|---|---|
|
|
1293
|
+
* | `component` | {@linkcode ComponentHandle} | A placed parametric component |
|
|
1294
|
+
*/
|
|
1295
|
+
export const PluginDesignFamilyGetParametersArgs = z.object({
|
|
1296
|
+
component: ComponentHandle,
|
|
1297
|
+
/** Also return a picture of this instance (its recipe re-run at its values, with its paint). */
|
|
1298
|
+
snapshot: PluginFamilySnapshotOption.optional(),
|
|
1299
|
+
})
|
|
1300
|
+
export type PluginDesignFamilyGetParametersArgs = z.infer<
|
|
1301
|
+
typeof PluginDesignFamilyGetParametersArgs
|
|
1302
|
+
>
|
|
1303
|
+
|
|
1304
|
+
/**
|
|
1305
|
+
* Arguments for {@linkcode PluginDesignFamilyApi.setParameters}.
|
|
1306
|
+
*
|
|
1307
|
+
* | Property | Type | Description |
|
|
1308
|
+
* |---|---|---|
|
|
1309
|
+
* | `component` | {@linkcode ComponentHandle} | A placed parametric component |
|
|
1310
|
+
* | `values` | `Record<string, number \| string>` | Values to change (partial) |
|
|
1311
|
+
*/
|
|
1312
|
+
export const PluginDesignFamilySetParametersArgs = z.object({
|
|
1313
|
+
component: ComponentHandle,
|
|
1314
|
+
values: z
|
|
1315
|
+
.record(z.string(), z.union([z.number(), z.string()]))
|
|
1316
|
+
.optional()
|
|
1317
|
+
.default({}),
|
|
1318
|
+
/** Nested children's own values: { "<path>": { key: value } }. */
|
|
1319
|
+
children: z
|
|
1320
|
+
.record(z.string(), z.record(z.string(), z.union([z.number(), z.string()])))
|
|
1321
|
+
.optional(),
|
|
1322
|
+
/** Links: { key: { from, expr } | null }. */
|
|
1323
|
+
links: z
|
|
1324
|
+
.record(
|
|
1325
|
+
z.string(),
|
|
1326
|
+
z.union([
|
|
1327
|
+
z.object({ from: ComponentHandle, expr: z.string() }),
|
|
1328
|
+
z.null(),
|
|
1329
|
+
]),
|
|
1330
|
+
)
|
|
1331
|
+
.optional(),
|
|
1332
|
+
/** Placed relations: { add?: [{ kind, … }], remove?: ["r1"] } — checked by the host. */
|
|
1333
|
+
relations: z
|
|
1334
|
+
.object({
|
|
1335
|
+
add: z.array(z.record(z.string(), z.unknown())).optional(),
|
|
1336
|
+
remove: z.array(z.string()).optional(),
|
|
1337
|
+
})
|
|
1338
|
+
.optional(),
|
|
1339
|
+
})
|
|
1340
|
+
export type PluginDesignFamilySetParametersArgs = z.infer<
|
|
1341
|
+
typeof PluginDesignFamilySetParametersArgs
|
|
1342
|
+
>
|