@snaptrude/plugin-core 0.0.0-bimsvg-20260928124803 → 0.0.0-bimsvg-20260929092811

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +36 -3
  2. package/api-manifest.json +322 -30
  3. package/dist/api/core/geom/create/index.d.ts +935 -100
  4. package/dist/api/core/geom/create/index.d.ts.map +1 -1
  5. package/dist/api/core/geom/query/brep.d.ts +59 -2
  6. package/dist/api/core/geom/query/brep.d.ts.map +1 -1
  7. package/dist/api/core/geom/query/curve.d.ts +59 -35
  8. package/dist/api/core/geom/query/curve.d.ts.map +1 -1
  9. package/dist/api/core/geom/query/edge.d.ts +2 -2
  10. package/dist/api/core/geom/query/face.d.ts +105 -0
  11. package/dist/api/core/geom/query/face.d.ts.map +1 -1
  12. package/dist/api/core/geom/update/contour.d.ts +2 -0
  13. package/dist/api/core/geom/update/contour.d.ts.map +1 -1
  14. package/dist/api/core/geom/update/curve.d.ts +5 -5
  15. package/dist/api/core/geom/update/profile.d.ts +3 -1
  16. package/dist/api/core/geom/update/profile.d.ts.map +1 -1
  17. package/dist/api/core/handles/index.d.ts +4 -2
  18. package/dist/api/core/handles/index.d.ts.map +1 -1
  19. package/dist/api/design/create/grids.d.ts +1 -1
  20. package/dist/api/design/family.d.ts +67 -10
  21. package/dist/api/design/family.d.ts.map +1 -1
  22. package/dist/api/design/query/geometry/index.d.ts +33 -5
  23. package/dist/api/design/query/geometry/index.d.ts.map +1 -1
  24. package/dist/api/design/query/grids.d.ts +3 -3
  25. package/dist/api/design/query/index.d.ts +47 -0
  26. package/dist/api/design/query/index.d.ts.map +1 -1
  27. package/dist/api/design/query/spaces.d.ts +5 -5
  28. package/dist/api/design/update/index.d.ts +99 -0
  29. package/dist/api/design/update/index.d.ts.map +1 -1
  30. package/dist/api/entity/referenceLine.d.ts +1 -1
  31. package/dist/api/entity/space.d.ts +4 -4
  32. package/dist/api/entity/story.d.ts +4 -4
  33. package/dist/api/program/site.d.ts +4 -4
  34. package/dist/api/program/spreadsheet.d.ts +4 -4
  35. package/dist/handles.d.ts +13 -3
  36. package/dist/handles.d.ts.map +1 -1
  37. package/dist/index.cjs +3594 -3123
  38. package/dist/index.cjs.map +1 -1
  39. package/dist/index.d.ts +2 -0
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +3547 -3123
  42. package/dist/index.js.map +1 -1
  43. package/dist/massParameters.d.ts +287 -0
  44. package/dist/massParameters.d.ts.map +1 -0
  45. package/dist/massParameters.ops.d.ts +138 -0
  46. package/dist/massParameters.ops.d.ts.map +1 -0
  47. package/package.json +14 -13
  48. package/scripts/check-dist-fresh.mjs +22 -0
  49. package/src/api/core/geom/create/index.ts +1030 -101
  50. package/src/api/core/geom/query/brep.ts +60 -2
  51. package/src/api/core/geom/query/curve.ts +26 -2
  52. package/src/api/core/geom/query/edge.ts +2 -2
  53. package/src/api/core/geom/query/face.ts +109 -4
  54. package/src/api/core/geom/update/contour.ts +2 -0
  55. package/src/api/core/geom/update/profile.ts +2 -0
  56. package/src/api/core/handles/index.ts +3 -2
  57. package/src/api/design/family.ts +68 -10
  58. package/src/api/design/query/geometry/index.ts +35 -4
  59. package/src/api/design/query/index.ts +50 -0
  60. package/src/api/design/update/index.ts +106 -0
  61. package/src/handles.ts +18 -3
  62. package/src/index.ts +2 -0
  63. package/src/massParameters.ops.ts +246 -0
  64. package/src/massParameters.ts +427 -0
  65. package/test/massParameters.test.mjs +952 -0
package/CHANGELOG.md CHANGED
@@ -1,10 +1,43 @@
1
1
  # @snaptrude/plugin-core
2
2
 
3
- ## 0.0.0-bimsvg-20260928124803
3
+ ## 0.9.14
4
4
 
5
- ### Minor Changes
5
+ ### Patch Changes
6
6
 
7
- - 95fbff4: Add first-class drawing contracts, reusable view presets, drawing placement on sheets, and drawing-parented annotations.
7
+ - 22c919b: Curved-geometry contracts — spline curves, primitive solids, curved-surface reads, and the shared mass-parameter record.
8
+ - **`spline` handle kind** — `SplineHandle` joins `CurveHandle` (`line | arc | spline`), so every `core.geom.query.curve` member and `profileFromCurves` take a spline, and joins `ArenaKind`, so `core.handles.release`, `releaseAll("spline")` and `endScope`'s `retain` list accept one. Closed spline loops never exist as a single handle: a closed run is authored directly as a profile.
9
+ - **`core.geom.create.splineFromPoints`** — an open NURBS curve passing exactly THROUGH 3..256 ordered points (interpolated, not control points).
10
+ - **`core.geom.create.profileFromSplinePoints`** — the closed counterpart: a smooth closed profile through the points, ready for `contourFromProfile` → extrude/loft/sweep. No mixed straight/spline input in v1 — compose those with `profileFromCurves`.
11
+ - **Primitive solids** — `brepFromSphere`, `brepFromCylinder`, `brepFromCone` (optional `topRadius`, default 0 = apex) and `brepFromTorus`. Positional plain `{x, y, z}` args matching the `brepFromRevolution` family; `base` is the bottom-cap centre for cylinder/cone, `centre` for sphere/torus. Each result carries an exact analytic surface and a creation recipe.
12
+ - **`core.geom.create.brepFromSweep`** — `options.transition` gains `"round"` beside `"miter"` and `{ bevel }`: corners carried around a tangent circular arc, so the corner patch is a real curved surface.
13
+ - **`core.geom.create.brepFromRevolution`** — JSDoc rewritten to the implemented gate: segments may run at any angle to the axis (an incline revolves into a cone, an arc into a sphere or torus section). Two rules, checked in this order: the axis must lie in the profile's plane or run parallel to it (a parallel axis offset from the plane is judged by its projection into the plane, as 0.9.13 built it; a skew axis is `PRECONDITION_FAILED`), then the whole profile must stay on one side of the axis — touching it is legal (dome poles, cylinder inner edges), crossing it is not (`VALIDATION`).
14
+ - **`core.geom.query.brep.getEdgeCurve`** — a fourth `"spline"` variant (`degree`, `controlPoints`, expanded `knots`, endpoints, `length`), so reading back a spline edge no longer throws.
15
+ - **`core.geom.query.brep.getVolume`** — exact enclosed volume integrated from the surfaces, free of tessellation error.
16
+ - **`core.geom.query.face.getSurfaceKind`** — `"plane" | "cylinder" | "cone" | "sphere" | "torus" | "spline" | null`: the one read that tells a real curved face from a faceted approximation of one.
17
+ - **`core.geom.query.curve`** — class JSDoc gains the per-member spline support table; unsupported members refuse with `PRECONDITION_FAILED` rather than answering wrongly.
18
+ - **`design.query.getParameters`** — read a mass's creation-parameter record (`kind` + values, world coordinates), or `null` for geometry with no honest recipe.
19
+ - **`massParameters`** (new module, exported from the package root) — `MassParametersRecord` / `ContourRecord` / `ProfileRecord` / `CurveRecord` / `XYZ` types and Zod schemas, shared verbatim by plugin-core, the host and the agent runtime. Pure JSON, one `{x, y, z}` point shape, array bounds as contract (through-points ≤ 256, spline poles ≤ 512, sweep path ≤ 512, loft sections ≤ 32, profile curves ≤ 256, contour holes ≤ 64).
20
+
21
+ Editing what was made, and rounding it:
22
+ - **`design.update.parameters(component, patch)`** — the write pair of `design.query.getParameters`: patch a parametric mass's creation recipe and the solid rebuilds in place, keeping its id, storey, materials, position and linked copies. `patch` is a partial of the record's `values` (keys you omit are unchanged; each key you send replaces that value whole — a scalar, a point, a list or a record (`contour`, `profile`, `sections`, `path`, `options`, `operations`) alike — so send the complete value you read) or the whole record read back and edited; `kind` and `version` are immutable; the reply is the complete post-edit record in world coordinates. `PRECONDITION_FAILED` when the component is not a mass (a family instance: use `design.family.setParameters`), when the mass carries no live recipe, when it was scaled (undo the scale and call again), when it or a linked copy is locked (unlock them — the recipe is intact, and `getParameters` cannot see the lock), when a recorded operation's selectors no longer resolve, or when the edit is the first twist or taper, or the straightening, of an extrusion that carries a shell, fillet or chamfer (the faces renumber: send the twist keys with `operations: []` first, then re-apply), `VALIDATION` for argument errors, `OPERATION_FAILED` for a kernel refusal that leaves the model unchanged.
23
+ - **`core.geom.create.splineFromControlPoints(controlPoints, { degree? })`** — an open NURBS curve DEFINED BY 3..512 control points (pulled toward, not through, the interior ones), degree 2 or 3, clamped uniform knots computed host-side. It is the write pair of a `splinePoles` curve record, so a curve read back off a mass can have its poles moved and be rebuilt.
24
+ - **`brepFromFillet` / `brepFromChamfer` / `brepFromShell` / `brepFromOffset` accept CURVED solids.** The straight-edges-only and no-shared-vertex rules are gone: edges may be straight, circular or spline, and adjacent edges (corner blends) are allowed. The JSDoc carries the rim-selection recipe — `brep.listEdges` → `edge.listFaces` → `face.getSurfaceKind` on both faces (`listEdgesBetween` takes two VERTEX indices and is not a face-pair lookup) — and the new refusals: `VALIDATION` where there is no crease (a seam, or two patches of one smooth surface), and — for `brepFromFillet` alone, the one operation with a per-edge kernel verdict — `PRECONDITION_FAILED` naming the edge the kernel could not blend. An over-size chamfer, shell or offset is `OPERATION_FAILED` with a hint asking for a smaller distance/thickness and no edge or face named. Selection caps are RECORD caps, not call caps: the wire schemas accept any selection size, exactly as 0.9.x did — a fillet/chamfer of more than 64 edges or a shell opening more than 32 faces still builds, but the result carries no parameter record (the recipe-less fallback).
25
+ - **`MassParametersRecord` gains an optional `operations` list** (`version` stays 1) — a fillet, chamfer, shell or offset of a recorded solid keeps the record, with `kind` still the base constructor and the operation appended. `OperationRecord`, `EdgeSelector`, `FaceSelector`, `TopologyCounts`, `SurfaceKind`, `CurveKind`, `ParameterPatchValue` and `MassParametersPatch` join the module, with caps `MAX_OPERATIONS` 8, `MAX_OPERATION_EDGES` 64, `MAX_OPERATION_FACES` 32. Selectors are TOPOLOGICAL (edge index + its face pair + curve kind; face index + surface kind + edge count) so they survive a scalar edit of the base and trip on a reorder.
26
+ - **`design.query.geometry.getBrep`** — additive `options.frame` (`"local"` default, unchanged 0.9.x behaviour; `"world"` places the solid where it sits in the scene, for booleans between scene masses and for fillet/shell/offset/chamfer results committed back beside their source).
27
+
28
+ Freeform surfaces, sweeps, patches and variable blends:
29
+ - **`core.geom.create.brepFromSweepAlongCurve(profile, path, options?)`** — sweeps a cross-section along a SMOOTH path of 1..64 curves (splines, arcs and lines chained end to end), where `brepFromSweep` takes a polyline. `options.orientation` picks how the profile turns: `"upright"` (default) keeps its up aligned with world up, `"frenet"` rolls it with the curvature, `"fixed"` carries the authored frame along unchanged — which is a pure translation, and so is also how a footprint is bent along an arc. `options.rail`, a second guide curve, carries the profile along a second path; a closed rail, or a rail together with `orientation`, is `VALIDATION`, and a railed sweep records nothing (`getParameters` answers `null`). Otherwise it records as the one new kind `sweepAlongCurve` (`{ profile, path: CurveRecord[], options? }`, `version` still 1), so the orientation stays editable through `design.update.parameters`; the shipped `sweep` kind and its `XYZ[]` path are untouched.
30
+ - **`core.geom.create.brepFromPatch(boundary, thickness, options?)`** — stretches a surface across a closed loop of 3..16 boundary items and thickens it into a SOLID in one call: a roof over four curves, a canopy between two rails, a blend across a gap. A boundary item is a curve or an `OwnedEdge` (`{ edge, brep, face? }` — an edge handle cannot name its own solid, and one patch may meet two of them). `options.continuity: "tangent"` leaves the named face smoothly and then REQUIRES `face` on every item, because a solid edge lies between two faces and only the caller knows which one to continue; `interiorPoints` (≤ 64) pull the surface through them, and `side` picks which way the solid grows. There is no sheet handle and no surface object: only solids reach the canvas. The result carries no recipe — `design.query.getParameters` is `null` and the mass label is its provenance.
31
+ - **`core.geom.create.brepsFromSplitByFace(brep, toolFace, toolBrep)`** — the curved twin of `brepsFromSplit`, which cuts with a plane: cuts a solid with a FACE of another solid and returns the pieces largest first. Both breps must be in the same frame — read scene masses with `getBrep(x, { frame: "world" })`. A cut that does not pass all the way through is a `VALIDATION`, not a silent single piece.
32
+ - **`core.geom.create.curvesFromProjection(curve, face, faceBrep)`** — drops a curve onto a face along its normal and returns the runs that land on it, one curve per connected run, or `[]` when the curve misses the face; `VALIDATION` when the curve's midpoint lies on the face's surface (no drop direction).
33
+ - **`core.geom.query.face.getArea(face, brep)` / `.getCentroid(face, brep)`** — exact area (internal units squared) and centre of area of one face, integrated off its surface rather than its facets. These are the two reads that tell freeform faces apart, where every face answers `"spline"` and a kind census returns nothing. Both take the parent brep, owner last, exactly like `listVertexPositions(face, brep)`.
34
+ - **`brepFromLoft`** gains `options.smoothing` — `"ruled"` (default) is today's straight-sided loft byte for byte; `"smooth"` lofts every section in ONE pass, so the sides curve continuously through the intermediates. `LoftOptions` carries it into the record.
35
+ - **`brepFromExtrusion`** gains a trailing `options?: { twistInDegrees?, topScale?, levels? }`, and the shipped `extrusion` record gains the same three optional values (`version` still 1) — a twisted or tapered tower stays the SAME kind, so "twist this tower" is an edit rather than a delete-and-rebuild. `twistInDegrees` is an angle and `topScale` a ratio: neither is ever converted by a unit factor. `levels` is a count (each step may turn at most 22.5°), resolved at create time and written into the record, so an existing mass regenerates from the record and never from a heuristic that may change under it. All three absent is the straight extrusion, byte for byte.
36
+ - **`brepFromFillet`** gains a trailing `options?: { endRadius?, radii? }` — a VARIABLE blend. `endRadius` interpolates linearly from `radius` along the whole tangent chain the one edge you pass belongs to (an open chain: on a closed rim a law whose two ends differ is refused, for `endRadius` and `radii` alike). `radii` is the sampled form instead: 2..32 rows of `{ position, radius }` with `position` the normalised ARC LENGTH along that contour from the passed edge's start — a ratio, never converted — strictly increasing. Either form takes exactly ONE edge, they are mutually exclusive, and both round-trip on the fillet `OperationRecord`.
37
+ - **Face and edge selectors gain `at?`** — where the face or edge sits inside its solid, its centroid (an edge's midpoint) over the solid's bounding-box extent, a ratio per axis. It is written only when index, kind and counts cannot tell two of them apart, which on a freeform solid is the common case, and it is verified — never searched — on replay, so a record written without it resolves byte-identically.
38
+ - **`ParameterPatchValue`** widens with `SweepAlongCurveRecordOptions` and `CurveRecord[]` (the `sweepAlongCurve` path). It only ever widens; every value it took before it still takes.
39
+ - **`massParameters.ops` — a second record module.** `EdgeSelector`, `FaceSelector`, `TopologyCounts`, `OperationRecord`, `ParameterPatchValue` and the operation caps (plus the new `MAX_FILLET_LAW_SAMPLES` 32) move into `src/massParameters.ops.ts` and are re-exported from `massParameters` and from the package root — no import path changes. The split keeps each module small enough for an agent to read whole. New caps beside the shipped ones: `MAX_PATH_CURVES` 64, `MAX_TWIST_LEVELS` 64, and — on the creator, because a patch has no record — `MAX_PATCH_BOUNDARY_CURVES` 16 and `MAX_PATCH_INTERIOR_POINTS` 64.
40
+ - **`design.family.getParameters`** gains an optional `parts: PluginFamilyPart[]` — each placed part's `name`, the `engine` that built it (`"kernel"` for a curved sketch built exactly, `"mesh"` otherwise) and, on kernel parts, a read-only part-local `brep` for the face-kind census. `design.query.geometry.getBrep` keeps returning `null` for a family.
8
41
 
9
42
  ## 0.9.13
10
43
 
package/api-manifest.json CHANGED
@@ -817,7 +817,7 @@
817
817
  {
818
818
  "path": "core.geom.create.brepFromLoft",
819
819
  "namespace": "core.geom.create",
820
- "summary": "Create a closed solid **B-rep** by lofting between a bottom and a top contour, optionally through intermediate cross-sections. Host API call — returns a {@linkcode BrepHandle}. Sections with different edge counts are auto-matched: vertices are deterministically inserted on the smaller sections at the larger section's perimeter positions, so the same input always yields the same solid. For straight-edge (all-line) sections the seam is auto-aligned too — winding and start-vertex correspondence are chosen to avoid a twist, so outlines authored from any starting corner loft cleanly; arc-bearing sections keep the authored correspondence unchanged. A pair whose side faces would be non-planar under every correspondence still builds for hole-free straight-edged pairs — planar side quads stay quads and warped ones split into planar triangles (the triangulated lane); arc-bearing or holed twisted pairs are rejected, as is auto-matching that would pair an arc with a straight edge. Matching hole counts are allowed in the two-section form. With intermediates the loft is a chain of ruled segments folded into one solid: sections must be planar and hole-free, ordered bottom → intermediates → top. The contours are copied — the input handles are never mutated.",
820
+ "summary": "Create a closed solid **B-rep** by lofting between a bottom and a top contour, optionally through intermediate cross-sections. Host API call — returns a {@linkcode BrepHandle}. Sections with different edge counts are auto-matched: vertices are deterministically inserted on the smaller sections at the larger section's perimeter positions, so the same input always yields the same solid. For straight-edge (all-line) sections the seam is auto-aligned too — winding and start-vertex correspondence are chosen to avoid a twist, so outlines authored from any starting corner loft cleanly; arc-bearing sections keep the authored correspondence unchanged. A pair whose side faces would be non-planar under every correspondence still builds for hole-free straight-edged pairs — planar side quads stay quads and warped ones split into planar triangles (the triangulated fallback); arc-bearing or holed twisted pairs are rejected, as is auto-matching that would pair an arc with a straight edge. Matching hole counts are allowed in the two-section form. With intermediates the loft is a chain of ruled segments folded into one solid: sections must be planar and hole-free, ordered bottom → intermediates → top. The contours are copied — the input handles are never mutated.",
821
821
  "examplePrompts": [
822
822
  "Make a tapered tower from these two outlines",
823
823
  "Loft between a large base and a smaller top",
@@ -863,7 +863,7 @@
863
863
  "summary": "Stretches a surface across a closed loop of boundary curves and thickens it into a SOLID in one call — a roof over four curves, a canopy between two rails, a blend across a gap. `thickness` is in internal length units and is required: only solids reach the canvas, and there is no separate surface object to keep. Pass edges as `{ edge, brep, face }` with `continuity: \"tangent\"` to leave the named face smoothly. The result carries NO recipe (`design.query.getParameters` returns `null`) — label it when you commit it, because the label is all the provenance there is.",
864
864
  "examplePrompts": [
865
865
  "Patch a roof over these four boundary curves, 250 mm thick",
866
- "Span a canopy between these two curved rails, 300 mm thick\n\n# Example\n```ts\n// A roof over four curves\nconst roof = await snaptrude.core.geom.create.brepFromPatch(\n[curveA, curveB, curveC, curveD],\n0.25,\n)\nconst mass = await snaptrude.design.create.massFromBrep(roof, \"Roof patch\")\n```\n\n```ts\n// A canopy between two towers' rims, leaving every wall it meets smoothly:\n// with `continuity: \"tangent\"` EVERY item is an edge naming its face.\nconst canopy = await snaptrude.core.geom.create.brepFromPatch(\n[\n{ edge: rimA, brep: towerA, face: wallA },\n{ edge: sideA, brep: towerA, face: endA },\n{ edge: rimB, brep: towerB, face: wallB },\n{ edge: sideB, brep: towerB, face: endB },\n],\n0.3,\n{ continuity: \"tangent\" },\n)\n```"
866
+ "Span a canopy between these two curved rails, 300 mm thick\n\n# Example\n```ts\n// A roof over four curves\nconst roof = await snaptrude.core.geom.create.brepFromPatch(\n[curveA, curveB, curveC, curveD],\n0.984, // 250 mm\n)\nconst mass = await snaptrude.design.create.massFromBrep(roof, \"Roof patch\")\n```\n\n```ts\n// A canopy between two towers' rims, leaving every wall it meets smoothly:\n// with `continuity: \"tangent\"` EVERY item is an edge naming its face.\nconst canopy = await snaptrude.core.geom.create.brepFromPatch(\n[\n{ edge: rimA, brep: towerA, face: wallA },\n{ edge: sideA, brep: towerA, face: endA },\n{ edge: rimB, brep: towerB, face: wallB },\n{ edge: sideB, brep: towerB, face: endB },\n],\n1.181, // 300 mm\n{ continuity: \"tangent\" },\n)\n```"
867
867
  ],
868
868
  "argsType": "(CurveHandle | OwnedEdge)[]",
869
869
  "resultType": "BrepHandle"
@@ -898,7 +898,7 @@
898
898
  {
899
899
  "path": "core.geom.create.brepFromSphere",
900
900
  "namespace": "core.geom.create",
901
- "summary": "Create a solid **sphere** B-rep from a centre and radius. Host API call — returns a {@linkcode BrepHandle}; commit it to the scene with `design.create.massFromBrep`. The result carries an exact spherical surface (not a faceted approximation) and a creation recipe — read the committed mass's parameters back with `design.query.getParameters` and edit them with `design.update.parameters`.",
901
+ "summary": "Create a solid **sphere** B-rep (boundary representation: the solid's exact faces, edges and vertices) from a centre and radius. Host API call — returns a {@linkcode BrepHandle}; commit it to the scene with `design.create.massFromBrep`. The result carries an exact spherical surface (not a faceted approximation) and a creation recipe — read the committed mass's parameters back with `design.query.getParameters` and edit them with `design.update.parameters`.",
902
902
  "examplePrompts": [
903
903
  "Create a sphere of radius 5 at the origin",
904
904
  "Add a 3m ball on top of the tower\n\n# Example\n```ts\nconst brep = await snaptrude.core.geom.create.brepFromSphere({ x: 0, y: 5, z: 0 }, 5)\nconst mass = await snaptrude.design.create.massFromBrep(brep, \"Sphere\")\n```"
@@ -921,7 +921,7 @@
921
921
  {
922
922
  "path": "core.geom.create.brepFromSweep",
923
923
  "namespace": "core.geom.create",
924
- "summary": "Sweeps a planar profile along a polyline path, with mitred corners at each bend. Host API call — returns a new {@linkcode BrepHandle}; the input contour is read-only. The profile must be hole-free and must not lie in a plane containing the first path segment's direction. Repeating the first point as the last closes the path into a ring solid (a picture frame): closed paths must be flat and straight-segmented and take a straight-edged (arc-free) profile — the ring's cross-section is the authored profile projected along the first leg onto the first mitre plane, so a profile not perpendicular to the first leg appears sheared. An optional scale law tapers the sweep: `startScale`/`endScale` interpolate linearly by arc length, or `scales` gives one factor per path point (scaling is about the profile centroid). A uniform scale (all factors equal) works on any path today's sweep accepts; a varying scale takes straight (collinear) open paths with straight-edged profiles — tapering across a bend or along an arc edge would create faces Snaptrude cannot represent and throws (sweep each straight run separately and union the results instead). An optional `transition` picks the corner treatment: `\"miter\"` (default) is today's sharp mitred bend; `\"round\"` carries the section around each turning corner on a tangent circular arc, so the corner patch is a real curved (cylindrical) surface rather than a flat cut (open paths only — `\"round\"` on a closed ring path is refused); `{ bevel: b }` chamfers each turning corner by cutting `b` back along both adjacent legs (works on open and closed paths — each leg must be long enough for its cuts). On a corner-less path `transition` has no effect, and the varying-scale lane takes straight paths, so it never sees a corner.",
924
+ "summary": "Sweeps a planar profile along a polyline path, with mitred corners at each bend. Host API call — returns a new {@linkcode BrepHandle}; the input contour is read-only. The profile must be hole-free and must not lie in a plane containing the first path segment's direction. Repeating the first point as the last closes the path into a ring solid (a picture frame): closed paths must be flat and straight-segmented and take a straight-edged (arc-free) profile — the ring's cross-section is the authored profile projected along the first leg onto the first mitre plane, so a profile not perpendicular to the first leg appears sheared. An optional scale law tapers the sweep: `startScale`/`endScale` interpolate linearly by arc length, or `scales` gives one factor per path point (scaling is about the profile centroid). A uniform scale (all factors equal) works on any path today's sweep accepts; a varying scale takes straight (collinear) open paths with straight-edged profiles — tapering across a bend or along an arc edge would create faces Snaptrude cannot represent and throws (sweep each straight run separately and union the results instead). An optional `transition` picks the corner treatment: `\"miter\"` (default) is a sharp mitred bend; `\"round\"` carries the section around each turning corner on a tangent circular arc, so the corner patch is a real curved surface (geometrically cylindrical; `getSurfaceKind` reports it as `\"spline\"`) rather than a flat cut (open paths only — `\"round\"` on a closed ring path is refused); `{ bevel: b }` chamfers each turning corner by cutting `b` back along both adjacent legs (works on open and closed paths — each leg must be long enough for its cuts). On a corner-less path `transition` has no effect, and a varying scale only accepts straight paths, so it never meets a corner.",
925
925
  "examplePrompts": [
926
926
  "Sweep this profile along the corridor path",
927
927
  "Extrude the railing section along this route",
@@ -938,7 +938,7 @@
938
938
  {
939
939
  "path": "core.geom.create.brepFromSweepAlongCurve",
940
940
  "namespace": "core.geom.create",
941
- "summary": "Sweeps a cross-section along a SMOOTH path — splines, arcs and lines chained end to end — with control of how the profile turns. {@linkcode PluginGeomCreateApi.brepFromSweep} takes a polyline path; this takes curves. Host API call — returns a new {@linkcode BrepHandle}; the inputs are read-only. Records as `sweepAlongCurve`, so the orientation stays editable through `design.update.parameters` (agent-editable; the panel shows it read-only — a string is not a scalar row).",
941
+ "summary": "Sweeps a cross-section along a SMOOTH path — splines, arcs and lines chained end to end — with control of how the profile turns. {@linkcode PluginGeomCreateApi.brepFromSweep} takes a polyline path; this takes curves. Host API call — returns a new {@linkcode BrepHandle}; the inputs are read-only. Records as `sweepAlongCurve`, so the orientation stays editable through `design.update.parameters`. The properties panel shows it read-only, because it is a text value, not a number. A railed sweep records nothing (`getParameters` answers `null`).",
942
942
  "examplePrompts": [
943
943
  "Sweep a 600x400 duct along this curved route and keep it upright",
944
944
  "Run this handrail along the curved stair edge",
@@ -1077,7 +1077,7 @@
1077
1077
  {
1078
1078
  "path": "core.geom.create.profileRect",
1079
1079
  "namespace": "core.geom.create",
1080
- "summary": "Create an axis-aligned **rectangle** profile (XZ plane) of `width` × `depth`, centred at `center` (defaults to the origin). Host API call — returns a handle.",
1080
+ "summary": "Create an axis-aligned **rectangle** profile in the XZ plane, `width` × `depth`, centred at `center` (defaults to the origin). Host API call — returns a handle.",
1081
1081
  "examplePrompts": [
1082
1082
  "Draw a 4m by 3m rectangle",
1083
1083
  "Make a rectangular outline for a room footprint"
@@ -1090,8 +1090,7 @@
1090
1090
  "namespace": "core.geom.create",
1091
1091
  "summary": "Create an open NURBS curve DEFINED BY control points — the curve is pulled TOWARD the interior points, not through them; it starts at the first point and ends at the last. Knots are computed for you (clamped, uniform) — there is no knot-vector argument. For a curve that must pass THROUGH given points use {@linkcode PluginGeomCreateApi.splineFromPoints}.",
1092
1092
  "examplePrompts": [
1093
- "Draw a smooth curve controlled by these four points",
1094
- "Rebuild this edge from its control polygon\n\n# Example\n```ts\nconst curve = await snaptrude.core.geom.create.splineFromControlPoints([p0, p1, p2, p3])\n// Quadratic instead of the default cubic\nconst tighter = await snaptrude.core.geom.create.splineFromControlPoints(\n[p0, p1, p2, p3],\n{ degree: 2 },\n)\n```"
1093
+ "Draw a smooth curve controlled by these four points\n\n# Example\n```ts\nconst curve = await snaptrude.core.geom.create.splineFromControlPoints([p0, p1, p2, p3])\n// Quadratic instead of the default cubic\nconst tighter = await snaptrude.core.geom.create.splineFromControlPoints(\n[p0, p1, p2, p3],\n{ degree: 2 },\n)\n```"
1095
1094
  ],
1096
1095
  "argsType": "Vec3Handle[]",
1097
1096
  "resultType": "SplineHandle"
@@ -1189,7 +1188,7 @@
1189
1188
  {
1190
1189
  "path": "core.geom.query.brep.getCentroid",
1191
1190
  "namespace": "core.geom.query.brep",
1192
- "summary": "Get the centroid (mean of all vertex positions) of a brep.",
1191
+ "summary": "Get the centroid of a brep: the mean of its vertex positions for planar and cylinder-sided solids; for a solid with sphere, cone, torus or spline geometry, its exact centre of mass (integrated over the surfaces; the vertex mean again if the kernel cannot integrate it, e.g. an open brep).",
1193
1192
  "examplePrompts": [
1194
1193
  "Find the centre point of this shape",
1195
1194
  "What is the centroid of the selected object?\n\n# Example\n```ts\nconst [mass] = await snaptrude.design.query.listMasses()\nconst brep = await snaptrude.design.query.geometry.getBrep(mass)\nif (brep) {\nconst centroid = await snaptrude.core.geom.query.brep.getCentroid(brep)\nconsole.log(centroid.x, centroid.y, centroid.z)\n}\n```"
@@ -1200,7 +1199,7 @@
1200
1199
  {
1201
1200
  "path": "core.geom.query.brep.getDistance",
1202
1201
  "namespace": "core.geom.query.brep",
1203
- "summary": "Measures the minimum distance between two solids, with the closest witness point on each. Touching or overlapping solids report a distance of 0 with contact points.",
1202
+ "summary": "Measures the minimum distance between two solids, with the closest point on each. Touching or overlapping solids report a distance of 0 with contact points.",
1204
1203
  "examplePrompts": [
1205
1204
  "What is the clearance between these two masses?",
1206
1205
  "How far apart are the tower and the neighbouring building?",
@@ -5140,7 +5139,7 @@
5140
5139
  {
5141
5140
  "path": "design.family.create",
5142
5141
  "namespace": "design.family",
5143
- "summary": "Commit the prepared bundle to the project's parametric-definition library, so it survives the session and appears in the Families panel.",
5142
+ "summary": "Save the prepared bundle to the library — the user's personal library unless `teamId` is given. {@linkcode PluginDesignFamilyApi.place} already saves, so `create` is for sharing with a team. When nothing of the family is in the model yet, one instance is also dropped at the centre of the current view on the active storey (`placed: true`), so a save is never invisible.",
5144
5143
  "examplePrompts": [
5145
5144
  "Save this as a family called Vertical Louvre",
5146
5145
  "Add it to my library\n\n# Example\n```ts\nconst { definitionId } = await snaptrude.design.family.create({ name: \"Louvre Screen\" })\n```"
@@ -5173,13 +5172,10 @@
5173
5172
  {
5174
5173
  "path": "design.family.get",
5175
5174
  "namespace": "design.family",
5176
- "summary": "A family's spec bundle: the prepared draft's, or — to CHANGE a saved family — that family's recipe, opened as the new draft.",
5177
- "examplePrompts": [
5178
- "Make the chairs I placed 50 mm wider",
5179
- "Change the Louvre Screen family so its blades are deeper\n\n# Example\n```ts\nconst draft = await snaptrude.design.family.get()\nconst family = await snaptrude.design.family.get({ component })\n// family.editing → { familyId, version, name, library }\n```"
5180
- ],
5181
- "argsType": "PluginDesignFamilyGetArgs",
5182
- "resultType": "PluginFamilyGetResult | null"
5175
+ "summary": "The prepared family's spec bundle, or `null` when nothing is prepared.",
5176
+ "examplePrompts": [],
5177
+ "argsType": null,
5178
+ "resultType": "PluginFamilyBundle | null"
5183
5179
  },
5184
5180
  {
5185
5181
  "path": "design.family.getParameters",
@@ -5220,7 +5216,7 @@
5220
5216
  "examplePrompts": [
5221
5217
  "Place it in the model",
5222
5218
  "Put a louvre screen at the origin",
5223
- "Copy that table two metres to the right\n\n# Example\n```ts\n// positioned — geometry exists when this resolves\nconst { component } = await snaptrude.design.family.place({\nid: \"revit:gypsum-stud-partition~90ad72\",\nposition: { x: 0, y: 0, z: 0 },\nrotation: 90,\n})\nawait snaptrude.design.transform.move([component], { x: 2, y: 0, z: 0 })\n\n// interactive — the user clicks\nawait snaptrude.design.family.place()\n\n// hosted — a door in a wall, near a point along it\nawait snaptrude.design.family.place({ id: doorId, host: wall, position: { x: 3, y: 0, z: 0 } })\n```"
5219
+ "Copy that table two metres to the right\n\n# Example\n```ts\n// positioned — geometry exists when this resolves\nconst { component } = await snaptrude.design.family.place({\nid: \"revit:gypsum-stud-partition~90ad72\",\nposition: { x: 0, y: 0, z: 0 },\nrotation: 90,\n})\nawait snaptrude.design.transform.move([component], { x: 2, y: 0, z: 0 })\n\n// interactive — the user clicks\nawait snaptrude.design.family.place()\n```"
5224
5220
  ],
5225
5221
  "argsType": "PluginDesignFamilyPlaceArgs",
5226
5222
  "resultType": "PluginFamilyPlaceResult"
@@ -5228,11 +5224,12 @@
5228
5224
  {
5229
5225
  "path": "design.family.prepare",
5230
5226
  "namespace": "design.family",
5231
- "summary": "Validate, compile and preview a family spec. Nothing is committed to the model or the library — call {@linkcode PluginDesignFamilyApi.place} or {@linkcode PluginDesignFamilyApi.create} for that.",
5227
+ "summary": "Validate, compile and preview a family spec. Nothing is committed to the model or the library — call {@linkcode PluginDesignFamilyApi.place} to put it in the model, which also saves it.",
5232
5228
  "examplePrompts": [
5233
5229
  "Make a louvre screen with adjustable blade count and tilt",
5234
5230
  "Design a curtain wall panel family with 8 bays",
5235
- "Create a railing family I can adjust\n\n# Example\n```ts\nconst r = await snaptrude.design.family.prepare({ bundle })\nif (!r.ok) console.error(r.errors[0].path, r.errors[0].message)\n```"
5231
+ "Create a railing family I can adjust",
5232
+ "Make a pendant lamp family with a domed shade I can resize\n\n# Example\n```ts\nconst r = await snaptrude.design.family.prepare({ bundle })\nif (!r.ok) console.error(r.errors[0].path, r.errors[0].message)\n```"
5236
5233
  ],
5237
5234
  "argsType": "PluginDesignFamilyPrepareArgs",
5238
5235
  "resultType": "PluginFamilyPrepareResult"
@@ -5243,7 +5240,7 @@
5243
5240
  "summary": "Change a placed component's parameters and rebuild its geometry in place.",
5244
5241
  "examplePrompts": [
5245
5242
  "Make that table seat 8",
5246
- "Change the stud spacing to 400\n\n# Example\n```ts\nawait snaptrude.design.family.setParameters({ component, values: { seats: 8 } })\nawait snaptrude.design.family.setParameters({ component, children: { chair: { width: \"380mm\" } } })\nawait snaptrude.design.family.setParameters({\ncomponent: railing,\nlinks: { steps: { from: stair, expr: \"steps\" }, side: { from: stair, expr: \"width - 40mm\" } },\n})\nawait snaptrude.design.family.setParameters({\ncomponent: post,\nrelations: { add: [{ kind: \"attach\", side: \"top\", to: slab, bind: { height: \"reach\" } }] },\n})\n```"
5243
+ "Change the stud spacing to 400\n\n# Example\n```ts\nawait snaptrude.design.family.setParameters({ component, values: { seats: 8 } })\n```"
5247
5244
  ],
5248
5245
  "argsType": "PluginDesignFamilySetParametersArgs",
5249
5246
  "resultType": "PluginFamilyInstanceParameters"
@@ -5712,7 +5709,7 @@
5712
5709
  {
5713
5710
  "path": "design.query.geometry.getTriangulatedMeshes",
5714
5711
  "namespace": "design.query.geometry",
5715
- "summary": "Get the **triangulated render meshes** of scene components as plain, serializable arrays — flat world-space vertex `positions` (`[x0, y0, z0, x1, …]`) and triangle `indices` (three per triangle), one record per requested component.",
5712
+ "summary": "Get the **triangulated render meshes** of scene components as plain, serializable arrays — flat world-space vertex `positions` (`[x0, y0, z0, x1, …]`) and triangle `indices` (three per triangle), one record per requested component. The result is `{ meshes }`, not a bare array — destructure it before iterating.",
5716
5713
  "examplePrompts": [
5717
5714
  "Export the selected masses as a triangle mesh",
5718
5715
  "Get the raw vertices and triangles of this wall in world coordinates",
@@ -7161,7 +7158,7 @@
7161
7158
  "Add a red arrow from the title to the plan view",
7162
7159
  "Point an arrow at the top-left view on the cover sheet\n\n# Example\n```ts\nconst { shapeId } = await snaptrude.presentation.annotate.arrow(\n\"sheet_1\",\n{ x: 100, y: 200 },\n{ x: 300, y: 250 },\n{ color: \"red\" },\n)\n```"
7163
7160
  ],
7164
- "argsType": "string",
7161
+ "argsType": "string | undefined",
7165
7162
  "resultType": "PluginPresentationAnnotateResult"
7166
7163
  },
7167
7164
  {
@@ -7173,7 +7170,7 @@
7173
7170
  "Put a yellow note on the cover sheet",
7174
7171
  "Leave a note next to the ground floor plan\n\n# Example\n```ts\nconst { shapeId } = await snaptrude.presentation.annotate.note(\n\"sheet_1\",\n\"Review this wall\",\n{ position: { x: 60, y: 120 }, color: \"yellow\" },\n)\n```"
7175
7172
  ],
7176
- "argsType": "string",
7173
+ "argsType": "string | undefined",
7177
7174
  "resultType": "PluginPresentationAnnotateResult"
7178
7175
  },
7179
7176
  {
@@ -7185,7 +7182,7 @@
7185
7182
  "Add a red rectangle highlight to the sheet",
7186
7183
  "Draw an ellipse around the entrance on the cover sheet\n\n# Example\n```ts\nconst { shapeId } = await snaptrude.presentation.annotate.shape(\n\"sheet_1\",\n\"cloud\",\n{ x: 80, y: 80, w: 240, h: 160 },\n{ color: \"red\" },\n)\n```"
7187
7184
  ],
7188
- "argsType": "string",
7185
+ "argsType": "string | undefined",
7189
7186
  "resultType": "PluginPresentationAnnotateResult"
7190
7187
  },
7191
7188
  {
@@ -7198,7 +7195,7 @@
7198
7195
  "Caption this view with \"Proposed layout\"",
7199
7196
  "Put a red note on the sheet\n\n# Example\n```ts\nconst { shapeId } = await snaptrude.presentation.annotate.text(\n\"sheet_1\",\n\"Ground Floor\",\n{ position: { x: 40, y: 40 }, size: \"xl\", color: \"blue\" },\n)\n```"
7200
7197
  ],
7201
- "argsType": "string",
7198
+ "argsType": "string | undefined",
7202
7199
  "resultType": "PluginPresentationAnnotateTextResult"
7203
7200
  },
7204
7201
  {
@@ -7237,20 +7234,242 @@
7237
7234
  "argsType": "string",
7238
7235
  "resultType": "PluginPresentationDiagramsPlaceResult"
7239
7236
  },
7237
+ {
7238
+ "path": "presentation.drawings.applyPreset",
7239
+ "namespace": "presentation.drawings",
7240
+ "summary": "Bind a kernel plan to a preset and clear declared overrides; local title text is retained.",
7241
+ "examplePrompts": [
7242
+ "Apply the presentation plan preset to this drawing"
7243
+ ],
7244
+ "argsType": "string",
7245
+ "resultType": "PluginDrawing"
7246
+ },
7247
+ {
7248
+ "path": "presentation.drawings.copy",
7249
+ "namespace": "presentation.drawings",
7250
+ "summary": "Copy a drawing and its annotations with native duplication; returns the new drawing.",
7251
+ "examplePrompts": [],
7252
+ "argsType": "string",
7253
+ "resultType": "PluginDrawing"
7254
+ },
7255
+ {
7256
+ "path": "presentation.drawings.create",
7257
+ "namespace": "presentation.drawings",
7258
+ "summary": "Generate a saved source view on the canvas. Options use page coordinates and a standard scale denominator.",
7259
+ "examplePrompts": [
7260
+ "Create a drawing from the ground floor view"
7261
+ ],
7262
+ "argsType": "string",
7263
+ "resultType": "PluginDrawing"
7264
+ },
7265
+ {
7266
+ "path": "presentation.drawings.delete",
7267
+ "namespace": "presentation.drawings",
7268
+ "summary": "Delete a drawing and its children as one Present undo step.",
7269
+ "examplePrompts": [],
7270
+ "argsType": "string",
7271
+ "resultType": "void"
7272
+ },
7273
+ {
7274
+ "path": "presentation.drawings.export",
7275
+ "namespace": "presentation.drawings",
7276
+ "summary": "Export the drawing including labels/annotations, no sheet required. PDF size follows its canvas bounds. download:true starts a browser download and returns metadata only (never send large base64 to an agent). Otherwise files contain dataBase64. downloadStarted means initiated, not proof of an OS-saved file. SVG/PDF preserve vector content; raster fallbacks remain raster. Layout warnings are returned.",
7277
+ "examplePrompts": [],
7278
+ "argsType": "string",
7279
+ "resultType": "PluginDrawingExportResult"
7280
+ },
7281
+ {
7282
+ "path": "presentation.drawings.get",
7283
+ "namespace": "presentation.drawings",
7284
+ "summary": "Read a drawing by its stable id; null if it no longer exists.",
7285
+ "examplePrompts": [],
7286
+ "argsType": "string",
7287
+ "resultType": "PluginDrawing | null"
7288
+ },
7289
+ {
7290
+ "path": "presentation.drawings.getCapabilities",
7291
+ "namespace": "presentation.drawings",
7292
+ "summary": "Read actual renderer and supported operations; presets are kernel-plan-only.",
7293
+ "examplePrompts": [
7294
+ "What editing capabilities does this drawing support?"
7295
+ ],
7296
+ "argsType": "string",
7297
+ "resultType": "PluginDrawingCapabilities"
7298
+ },
7299
+ {
7300
+ "path": "presentation.drawings.getStatus",
7301
+ "namespace": "presentation.drawings",
7302
+ "summary": "Wait at most waitMs (0 by default) for export readiness and upload; returns current status on timeout.",
7303
+ "examplePrompts": [],
7304
+ "argsType": "string",
7305
+ "resultType": "PluginDrawingStatus"
7306
+ },
7307
+ {
7308
+ "path": "presentation.drawings.getStyles",
7309
+ "namespace": "presentation.drawings",
7310
+ "summary": "Read effective categories/controls before editing; absent categories reject writes.",
7311
+ "examplePrompts": [
7312
+ "Show the editable styles for this drawing"
7313
+ ],
7314
+ "argsType": "string",
7315
+ "resultType": "PluginDrawingStyles"
7316
+ },
7317
+ {
7318
+ "path": "presentation.drawings.labels.getLayout",
7319
+ "namespace": "presentation.drawings.labels",
7320
+ "summary": "Read collision diagnostics, suggested scales and pin counts without hiding failed labels.",
7321
+ "examplePrompts": [
7322
+ "Check this drawing for overlapping labels"
7323
+ ],
7324
+ "argsType": "string",
7325
+ "resultType": "PluginDrawingLabelLayout"
7326
+ },
7327
+ {
7328
+ "path": "presentation.drawings.labels.list",
7329
+ "namespace": "presentation.drawings.labels",
7330
+ "summary": "List every text label on a drawing, including hidden labels.",
7331
+ "examplePrompts": [
7332
+ "List all labels in this drawing"
7333
+ ],
7334
+ "argsType": "string",
7335
+ "resultType": "PluginPlacedViewLabel[]"
7336
+ },
7337
+ {
7338
+ "path": "presentation.drawings.labels.move",
7339
+ "namespace": "presentation.drawings.labels",
7340
+ "summary": "Move a label; room labels retain the chosen position at the current architectural scale.",
7341
+ "examplePrompts": [],
7342
+ "argsType": "string",
7343
+ "resultType": "PluginPlacedViewLabel"
7344
+ },
7345
+ {
7346
+ "path": "presentation.drawings.labels.place",
7347
+ "namespace": "presentation.drawings.labels",
7348
+ "summary": "Pin an unclean room label at its best available position. May wait up to ten seconds for fitting.",
7349
+ "examplePrompts": [],
7350
+ "argsType": "string",
7351
+ "resultType": "boolean"
7352
+ },
7353
+ {
7354
+ "path": "presentation.drawings.labels.reset",
7355
+ "namespace": "presentation.drawings.labels",
7356
+ "summary": "Clear pins at this scale (default) or every scale. Returns the number cleared.",
7357
+ "examplePrompts": [],
7358
+ "argsType": "string",
7359
+ "resultType": "number"
7360
+ },
7361
+ {
7362
+ "path": "presentation.drawings.labels.setHidden",
7363
+ "namespace": "presentation.drawings.labels",
7364
+ "summary": "Set user visibility, independently of layout/crop visibility.",
7365
+ "examplePrompts": [],
7366
+ "argsType": "string",
7367
+ "resultType": "PluginPlacedViewLabel"
7368
+ },
7369
+ {
7370
+ "path": "presentation.drawings.list",
7371
+ "namespace": "presentation.drawings",
7372
+ "summary": "List drawing deliverables on the current Present page.",
7373
+ "examplePrompts": [
7374
+ "List all drawings in this presentation"
7375
+ ],
7376
+ "argsType": null,
7377
+ "resultType": "PluginDrawing[]"
7378
+ },
7379
+ {
7380
+ "path": "presentation.drawings.listFonts",
7381
+ "namespace": "presentation.drawings",
7382
+ "summary": "List available font families from the native catalog (including DEFAULT).",
7383
+ "examplePrompts": [],
7384
+ "argsType": null,
7385
+ "resultType": "string[]"
7386
+ },
7387
+ {
7388
+ "path": "presentation.drawings.listPatterns",
7389
+ "namespace": "presentation.drawings",
7390
+ "summary": "List pattern ids accepted by the native hatch catalog.",
7391
+ "examplePrompts": [],
7392
+ "argsType": null,
7393
+ "resultType": "{ id: string; label: string; group: string }[]"
7394
+ },
7395
+ {
7396
+ "path": "presentation.drawings.rename",
7397
+ "namespace": "presentation.drawings",
7398
+ "summary": "Rename the drawing title without renaming its source saved view.",
7399
+ "examplePrompts": [],
7400
+ "argsType": "string",
7401
+ "resultType": "PluginDrawing"
7402
+ },
7403
+ {
7404
+ "path": "presentation.drawings.resetOverrides",
7405
+ "namespace": "presentation.drawings",
7406
+ "summary": "Reset declared overrides to the bound preset, preserving local title/proposal/building text.",
7407
+ "examplePrompts": [],
7408
+ "argsType": "string",
7409
+ "resultType": "PluginDrawing"
7410
+ },
7411
+ {
7412
+ "path": "presentation.drawings.setCrop",
7413
+ "namespace": "presentation.drawings",
7414
+ "summary": "Set normalized crop corners (0–1), or null to remove the crop.",
7415
+ "examplePrompts": [],
7416
+ "argsType": "string",
7417
+ "resultType": "PluginDrawing"
7418
+ },
7419
+ {
7420
+ "path": "presentation.drawings.setScale",
7421
+ "namespace": "presentation.drawings",
7422
+ "summary": "Set a standard architectural scale; perspective/isometric drawings reject this.",
7423
+ "examplePrompts": [],
7424
+ "argsType": "string",
7425
+ "resultType": "PluginDrawing"
7426
+ },
7427
+ {
7428
+ "path": "presentation.drawings.updateContent",
7429
+ "namespace": "presentation.drawings",
7430
+ "summary": "Regenerate specified drawings. Reports actual changed assets, not merely submitted ids.",
7431
+ "examplePrompts": [],
7432
+ "argsType": "string[]",
7433
+ "resultType": "{ drawingId: string; updated: boolean }[]"
7434
+ },
7435
+ {
7436
+ "path": "presentation.drawings.updateStyles",
7437
+ "namespace": "presentation.drawings",
7438
+ "summary": "Merge a typed category patch as one undo step. Invalid/unsupported fields reject the whole call.",
7439
+ "examplePrompts": [],
7440
+ "argsType": "string",
7441
+ "resultType": "PluginDrawingStyles"
7442
+ },
7240
7443
  {
7241
7444
  "path": "presentation.export",
7242
7445
  "namespace": "presentation",
7243
- "summary": "Export the presentation's layout sheets to a downloadable file (PDF / PNG / JPG) and return the encoded bytes as base64. Wraps the Present-mode Export dialog pipeline headlessly. Requires Present mode to be open.",
7446
+ "summary": "Export the presentation's layout sheets to a downloadable file (PDF / PNG / JPG). Return base64 by default; `download: true` initiates browser downloads and returns metadata without base64. Requires Present mode to be open.",
7244
7447
  "examplePrompts": [
7245
7448
  "Export the presentation as a PDF",
7246
7449
  "Download all sheets as a single PDF",
7247
7450
  "Export sheet 1 as a PNG",
7248
7451
  "Save the presentation sheets as JPG images",
7249
- "Export these sheets to PDF at high resolution\n\n# Example\n```ts\nconst { files } = await snaptrude.presentation.export(\"pdf\")\n```"
7452
+ "Export these sheets to PDF at high resolution\n\n# Example\n```ts\nconst { files } = await snaptrude.presentation.export(\"pdf\")\nconst download = await snaptrude.presentation.export(\"pdf\", { download: true })\n```"
7250
7453
  ],
7251
7454
  "argsType": "PluginPresentationExportFormat",
7252
7455
  "resultType": "PluginPresentationExportResult"
7253
7456
  },
7457
+ {
7458
+ "path": "presentation.export",
7459
+ "namespace": "presentation",
7460
+ "summary": "",
7461
+ "examplePrompts": [],
7462
+ "argsType": "PluginPresentationExportFormat",
7463
+ "resultType": "PluginPresentationExportDownloadResult"
7464
+ },
7465
+ {
7466
+ "path": "presentation.export",
7467
+ "namespace": "presentation",
7468
+ "summary": "",
7469
+ "examplePrompts": [],
7470
+ "argsType": "PluginPresentationExportFormat",
7471
+ "resultType": "PluginPresentationExportResult | PluginPresentationExportDownloadResult"
7472
+ },
7254
7473
  {
7255
7474
  "path": "presentation.import.image",
7256
7475
  "namespace": "presentation.import",
@@ -7664,6 +7883,17 @@
7664
7883
  "argsType": "string",
7665
7884
  "resultType": "PluginPresentationSheetsPlaceResult"
7666
7885
  },
7886
+ {
7887
+ "path": "presentation.sheets.placeDrawing",
7888
+ "namespace": "presentation.sheets",
7889
+ "summary": "Move an existing drawing onto a sheet without copying or regenerating it.",
7890
+ "examplePrompts": [
7891
+ "Move this drawing onto the cover sheet",
7892
+ "Place drawing_1 at the center of sheet_2"
7893
+ ],
7894
+ "argsType": "string",
7895
+ "resultType": "PluginPresentationSheetsPlaceResult"
7896
+ },
7667
7897
  {
7668
7898
  "path": "presentation.sheets.rename",
7669
7899
  "namespace": "presentation.sheets",
@@ -7787,6 +8017,68 @@
7787
8017
  "argsType": "string",
7788
8018
  "resultType": "PluginCanvasShape"
7789
8019
  },
8020
+ {
8021
+ "path": "presentation.viewPresets.copy",
8022
+ "namespace": "presentation.viewPresets",
8023
+ "summary": "Copy a preset with a native unique name, without changing drawing bindings.",
8024
+ "examplePrompts": [],
8025
+ "argsType": "string",
8026
+ "resultType": "PluginViewPreset"
8027
+ },
8028
+ {
8029
+ "path": "presentation.viewPresets.create",
8030
+ "namespace": "presentation.viewPresets",
8031
+ "summary": "Capture a drawing's effective styles and bind it to the new preset. Names must be unique.",
8032
+ "examplePrompts": [
8033
+ "Save this drawing's styles as a new preset"
8034
+ ],
8035
+ "argsType": "string",
8036
+ "resultType": "PluginViewPreset"
8037
+ },
8038
+ {
8039
+ "path": "presentation.viewPresets.delete",
8040
+ "namespace": "presentation.viewPresets",
8041
+ "summary": "Delete a custom preset, rebinding its drawings to a different existing replacement.",
8042
+ "examplePrompts": [],
8043
+ "argsType": "string",
8044
+ "resultType": "void"
8045
+ },
8046
+ {
8047
+ "path": "presentation.viewPresets.get",
8048
+ "namespace": "presentation.viewPresets",
8049
+ "summary": "Read a preset, or null when missing.",
8050
+ "examplePrompts": [],
8051
+ "argsType": "string",
8052
+ "resultType": "PluginViewPreset | null"
8053
+ },
8054
+ {
8055
+ "path": "presentation.viewPresets.list",
8056
+ "namespace": "presentation.viewPresets",
8057
+ "summary": "List built-ins first, then custom presets by name.",
8058
+ "examplePrompts": [
8059
+ "List the available drawing style presets"
8060
+ ],
8061
+ "argsType": null,
8062
+ "resultType": "PluginViewPreset[]"
8063
+ },
8064
+ {
8065
+ "path": "presentation.viewPresets.rename",
8066
+ "namespace": "presentation.viewPresets",
8067
+ "summary": "Rename a custom preset. Built-ins reject this operation.",
8068
+ "examplePrompts": [],
8069
+ "argsType": "string",
8070
+ "resultType": "PluginViewPreset"
8071
+ },
8072
+ {
8073
+ "path": "presentation.viewPresets.update",
8074
+ "namespace": "presentation.viewPresets",
8075
+ "summary": "Update a custom preset from a drawing bound to it; refresh all bound drawings.",
8076
+ "examplePrompts": [
8077
+ "Update this preset from the current drawing styles"
8078
+ ],
8079
+ "argsType": "string",
8080
+ "resultType": "PluginViewPreset"
8081
+ },
7790
8082
  {
7791
8083
  "path": "presentation.views.capture",
7792
8084
  "namespace": "presentation.views",
@@ -7869,7 +8161,7 @@
7869
8161
  "summary": "List the saved views in the active project.",
7870
8162
  "examplePrompts": [
7871
8163
  "List all the saved views",
7872
- "What 2D and 3D views are in this project?",
8164
+ "What plan, 3D, and elevation views are in this project?",
7873
8165
  "Which view is currently active?",
7874
8166
  "Get the names of every presentation view",
7875
8167
  "Show me the 3D views I can put on a sheet\n\n# Example\n```ts\nconst { views } = await snaptrude.presentation.views.list()\nconst active = views.find((v) => v.isActive)\nconst plans = views.filter((v) => v.type === \"2d\")\n```"