@snaptrude/plugin-core 1.0.1 → 1.0.2-plainpoint-20261102.2

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 (112) hide show
  1. package/AGENTS.md +1 -0
  2. package/CHANGELOG.md +6 -0
  3. package/api-manifest.json +1682 -118
  4. package/dist/api/core/camera/index.d.ts +16 -6
  5. package/dist/api/core/camera/index.d.ts.map +1 -1
  6. package/dist/api/core/comment/index.d.ts +7 -3
  7. package/dist/api/core/comment/index.d.ts.map +1 -1
  8. package/dist/api/core/geom/create/index.d.ts +69 -21
  9. package/dist/api/core/geom/create/index.d.ts.map +1 -1
  10. package/dist/api/core/geom/query/circle.d.ts +31 -11
  11. package/dist/api/core/geom/query/circle.d.ts.map +1 -1
  12. package/dist/api/core/geom/query/contour.d.ts +19 -7
  13. package/dist/api/core/geom/query/contour.d.ts.map +1 -1
  14. package/dist/api/core/geom/query/curve.d.ts +72 -24
  15. package/dist/api/core/geom/query/curve.d.ts.map +1 -1
  16. package/dist/api/core/geom/query/profile.d.ts +19 -7
  17. package/dist/api/core/geom/query/profile.d.ts.map +1 -1
  18. package/dist/api/core/geom/update/contour.d.ts +36 -12
  19. package/dist/api/core/geom/update/contour.d.ts.map +1 -1
  20. package/dist/api/core/geom/update/curve.d.ts +13 -5
  21. package/dist/api/core/geom/update/curve.d.ts.map +1 -1
  22. package/dist/api/core/geom/update/profile.d.ts +36 -12
  23. package/dist/api/core/geom/update/profile.d.ts.map +1 -1
  24. package/dist/api/core/io/import/index.d.ts +7 -3
  25. package/dist/api/core/io/import/index.d.ts.map +1 -1
  26. package/dist/api/core/math/quat.d.ts +2 -2
  27. package/dist/api/core/math/quat.d.ts.map +1 -1
  28. package/dist/api/core/math/vec3.d.ts +15 -15
  29. package/dist/api/core/math/vec3.d.ts.map +1 -1
  30. package/dist/api/core/storeys/index.d.ts +5 -0
  31. package/dist/api/core/storeys/index.d.ts.map +1 -1
  32. package/dist/api/design/boolean/index.d.ts +20 -4
  33. package/dist/api/design/boolean/index.d.ts.map +1 -1
  34. package/dist/api/design/create/bulk-items.d.ts +84 -16
  35. package/dist/api/design/create/bulk-items.d.ts.map +1 -1
  36. package/dist/api/design/create/index.d.ts +134 -59
  37. package/dist/api/design/create/index.d.ts.map +1 -1
  38. package/dist/api/design/create/opening-fields.d.ts +18 -6
  39. package/dist/api/design/create/opening-fields.d.ts.map +1 -1
  40. package/dist/api/design/delete/index.d.ts +14 -7
  41. package/dist/api/design/delete/index.d.ts.map +1 -1
  42. package/dist/api/design/dimensions.d.ts +12 -4
  43. package/dist/api/design/dimensions.d.ts.map +1 -1
  44. package/dist/api/design/edit/index.d.ts +7 -3
  45. package/dist/api/design/edit/index.d.ts.map +1 -1
  46. package/dist/api/design/family.d.ts +5 -1
  47. package/dist/api/design/family.d.ts.map +1 -1
  48. package/dist/api/design/index.d.ts +5 -0
  49. package/dist/api/design/index.d.ts.map +1 -1
  50. package/dist/api/design/lock.d.ts +3 -0
  51. package/dist/api/design/lock.d.ts.map +1 -1
  52. package/dist/api/design/text.d.ts +400 -0
  53. package/dist/api/design/text.d.ts.map +1 -0
  54. package/dist/api/design/transform/index.d.ts +28 -8
  55. package/dist/api/design/transform/index.d.ts.map +1 -1
  56. package/dist/api/entity/space.d.ts +30 -6
  57. package/dist/api/entity/space.d.ts.map +1 -1
  58. package/dist/api/entity/story.d.ts +4 -0
  59. package/dist/api/entity/story.d.ts.map +1 -1
  60. package/dist/api/presentation/drawings.d.ts +46 -1
  61. package/dist/api/presentation/drawings.d.ts.map +1 -1
  62. package/dist/api/presentation/graphics.d.ts +4 -0
  63. package/dist/api/presentation/graphics.d.ts.map +1 -1
  64. package/dist/api/presentation/placedViews.d.ts +29 -1
  65. package/dist/api/presentation/placedViews.d.ts.map +1 -1
  66. package/dist/api/presentation/standards.d.ts +14 -0
  67. package/dist/api/presentation/standards.d.ts.map +1 -1
  68. package/dist/api/presentation/views.d.ts +5 -1
  69. package/dist/api/presentation/views.d.ts.map +1 -1
  70. package/dist/handles.d.ts +8 -0
  71. package/dist/handles.d.ts.map +1 -1
  72. package/dist/index.cjs +2298 -2179
  73. package/dist/index.cjs.map +1 -1
  74. package/dist/index.js +2285 -2179
  75. package/dist/index.js.map +1 -1
  76. package/package.json +13 -13
  77. package/src/api/core/camera/index.ts +9 -7
  78. package/src/api/core/comment/index.ts +3 -3
  79. package/src/api/core/geom/create/index.ts +25 -25
  80. package/src/api/core/geom/query/circle.ts +11 -11
  81. package/src/api/core/geom/query/contour.ts +7 -6
  82. package/src/api/core/geom/query/curve.ts +25 -25
  83. package/src/api/core/geom/query/profile.ts +7 -6
  84. package/src/api/core/geom/update/contour.ts +12 -11
  85. package/src/api/core/geom/update/curve.ts +5 -5
  86. package/src/api/core/geom/update/profile.ts +12 -11
  87. package/src/api/core/io/import/index.ts +3 -2
  88. package/src/api/core/math/quat.ts +2 -2
  89. package/src/api/core/math/vec3.ts +16 -16
  90. package/src/api/core/storeys/index.ts +5 -0
  91. package/src/api/design/boolean/index.ts +2 -2
  92. package/src/api/design/create/bulk-items.ts +7 -2
  93. package/src/api/design/create/index.ts +34 -46
  94. package/src/api/design/create/opening-fields.ts +7 -7
  95. package/src/api/design/delete/index.ts +14 -7
  96. package/src/api/design/dimensions.ts +5 -4
  97. package/src/api/design/edit/index.ts +3 -3
  98. package/src/api/design/family.ts +2 -1
  99. package/src/api/design/index.ts +5 -0
  100. package/src/api/design/lock.ts +6 -0
  101. package/src/api/design/text.ts +394 -0
  102. package/src/api/design/transform/index.ts +15 -7
  103. package/src/api/entity/space.ts +5 -4
  104. package/src/api/entity/story.ts +4 -0
  105. package/src/api/presentation/graphics.ts +4 -1
  106. package/src/api/presentation/placedViews.ts +12 -0
  107. package/src/api/presentation/standards.ts +6 -0
  108. package/src/api/presentation/views.ts +7 -2
  109. package/src/handles.ts +11 -0
  110. package/test/designText.test.mjs +72 -0
  111. package/test/handles.test.mjs +36 -0
  112. package/test/paperPensContract.test.mjs +39 -0
package/api-manifest.json CHANGED
@@ -494,6 +494,30 @@
494
494
  "argsType": null,
495
495
  "resultType": "PluginCameraMode"
496
496
  },
497
+ {
498
+ "path": "core.camera.getNavigationSettings",
499
+ "namespace": "core.camera",
500
+ "summary": "Read the arrow-key navigation settings: whether the arrow keys walk the camera, the eye height used by `setPose`'s `standAt` and by walkthroughs that follow the floor, the walking speed, and whether walking stops at walls. Paired with {@linkcode PluginCameraApi.updateNavigationSettings}.",
501
+ "examplePrompts": [
502
+ "Is arrow-key navigation on?",
503
+ "What eye height does the camera use?\n\n# Example\n```ts\nconst { isKeyboardNavigationEnabled } =\nawait snaptrude.core.camera.getNavigationSettings()\n```"
504
+ ],
505
+ "argsType": null,
506
+ "resultType": "PluginCameraNavigationSettings"
507
+ },
508
+ {
509
+ "path": "core.camera.getPose",
510
+ "namespace": "core.camera",
511
+ "summary": "Read where the camera is and where it looks: the eye and target positions (world coordinates, internal `babylon` units), the view direction as yaw and pitch, the vertical field of view, the projection and the 2D/3D mode. Paired with {@linkcode PluginCameraApi.setPose}.",
512
+ "examplePrompts": [
513
+ "Where is the camera right now?",
514
+ "Remember this viewpoint so we can come back to it",
515
+ "Which way is the camera facing?",
516
+ "How high above the ground is the camera?\n\n# Example\n```ts\nconst pose = await snaptrude.core.camera.getPose()\nconsole.log(pose.eye, pose.yawInDegrees, pose.pitchInDegrees)\n```"
517
+ ],
518
+ "argsType": null,
519
+ "resultType": "PluginCameraPose"
520
+ },
497
521
  {
498
522
  "path": "core.camera.getProjection",
499
523
  "namespace": "core.camera",
@@ -516,9 +540,34 @@
516
540
  "Point the camera at the centre of the building from the side",
517
541
  "Set the eye to this point and aim it at that point\n\n# Example\n```ts\nconst { vec3 } = snaptrude.core.math\nawait snaptrude.core.camera.lookFrom(vec3.new(50, 30, 50), vec3.new(0, 0, 0))\n```"
518
542
  ],
519
- "argsType": "Vec3Handle",
543
+ "argsType": "Vec3Input",
520
544
  "resultType": "boolean"
521
545
  },
546
+ {
547
+ "path": "core.camera.move",
548
+ "namespace": "core.camera",
549
+ "summary": "Walk or fly the camera by a displacement in its own frame: `forward` along the level view direction, `right` to its side and `up` vertically, in internal `babylon` units. The view direction does not change. With `options.followsFloor` the camera keeps its height above the floor as it goes, climbing and descending stairs and ramps the way the arrow keys do. Stops a playing walkthrough and switches from 2D plan view to 3D first. View-state only: not write-gated, not undoable.",
550
+ "examplePrompts": [
551
+ "Walk forward 3 metres",
552
+ "Step to the right a little",
553
+ "Walk up the stairs ahead",
554
+ "Raise the camera by 2 metres\n\n# Example\n```ts\nconst metre = 1 / 0.254\nawait snaptrude.core.camera.move(\n{ forward: 3 * metre },\n{ followsFloor: true, durationInSeconds: 2 },\n)\n```"
555
+ ],
556
+ "argsType": "PluginCameraMoveDisplacement",
557
+ "resultType": "PluginCameraPose"
558
+ },
559
+ {
560
+ "path": "core.camera.rotate",
561
+ "namespace": "core.camera",
562
+ "summary": "Turn the camera in place by signed angles: `yawInDegrees` to the right (clockwise seen from above) and `pitchInDegrees` up. The eye does not move; the resulting pitch is kept within ±89°. Stops a playing walkthrough and switches from 2D plan view to 3D first. View-state only: not write-gated, not undoable.",
563
+ "examplePrompts": [
564
+ "Turn around",
565
+ "Look 45 degrees to the left",
566
+ "Tilt the camera down a bit\n\n# Example\n```ts\nawait snaptrude.core.camera.rotate(180, 0, { durationInSeconds: 1 })\n```"
567
+ ],
568
+ "argsType": "number",
569
+ "resultType": "PluginCameraPose"
570
+ },
522
571
  {
523
572
  "path": "core.camera.setMode",
524
573
  "namespace": "core.camera",
@@ -532,6 +581,19 @@
532
581
  "argsType": "PluginCameraMode",
533
582
  "resultType": "boolean"
534
583
  },
584
+ {
585
+ "path": "core.camera.setPose",
586
+ "namespace": "core.camera",
587
+ "summary": "Place the camera, optionally easing there over `options.durationInSeconds`, and resolve once the new view has been drawn, so a capture that follows sees it without a fixed wait. `pose` is one of:",
588
+ "examplePrompts": [
589
+ "Stand in the living room at eye level facing the window",
590
+ "Move the camera to (40, 12, 30) looking at the entrance",
591
+ "Fly the camera smoothly over to the courtyard",
592
+ "Put me back at the viewpoint we saved earlier\n\n# Example\n```ts\nconst { camera } = snaptrude.core\nawait camera.setPose({ standAt: { x: 20, y: 0, z: 35 }, yawInDegrees: 90 })\nawait camera.setPose(\n{ eye: { x: 60, y: 25, z: 60 }, target: { x: 0, y: 5, z: 0 } },\n{ durationInSeconds: 1.5 },\n)\n```"
593
+ ],
594
+ "argsType": "PluginCameraPoseInput",
595
+ "resultType": "PluginCameraPose"
596
+ },
535
597
  {
536
598
  "path": "core.camera.setProjection",
537
599
  "namespace": "core.camera",
@@ -559,6 +621,18 @@
559
621
  "argsType": "PluginStandardView",
560
622
  "resultType": "boolean"
561
623
  },
624
+ {
625
+ "path": "core.camera.updateNavigationSettings",
626
+ "namespace": "core.camera",
627
+ "summary": "Change arrow-key navigation settings; only the fields given change. `isKeyboardNavigationEnabled` is the user's Project Settings switch and is saved on this device; the other settings last for the session. Write-gated: this changes the user's preferences, not just the view.",
628
+ "examplePrompts": [
629
+ "Turn off arrow-key navigation",
630
+ "Use a 1.6 m eye height",
631
+ "Make walking stop at walls\n\n# Example\n```ts\nawait snaptrude.core.camera.updateNavigationSettings({\neyeHeight: 1.6 / 0.254,\nisCollisionEnabled: true,\n})\n```"
632
+ ],
633
+ "argsType": "PluginCameraNavigationSettingsPatch",
634
+ "resultType": "PluginCameraNavigationSettings"
635
+ },
562
636
  {
563
637
  "path": "core.camera.zoomExtents",
564
638
  "namespace": "core.camera",
@@ -723,7 +797,7 @@
723
797
  "Draw an arc between these two points around a centre",
724
798
  "Add a curved segment from A to B"
725
799
  ],
726
- "argsType": "Vec3Handle",
800
+ "argsType": "Vec3Input",
727
801
  "resultType": "ArcHandle"
728
802
  },
729
803
  {
@@ -817,7 +891,7 @@
817
891
  {
818
892
  "path": "core.geom.create.brepFromLoft",
819
893
  "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.",
894
+ "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
895
  "examplePrompts": [
822
896
  "Make a tapered tower from these two outlines",
823
897
  "Loft between a large base and a smaller top",
@@ -863,7 +937,7 @@
863
937
  "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
938
  "examplePrompts": [
865
939
  "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```"
940
+ "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
941
  ],
868
942
  "argsType": "(CurveHandle | OwnedEdge)[]",
869
943
  "resultType": "BrepHandle"
@@ -898,7 +972,7 @@
898
972
  {
899
973
  "path": "core.geom.create.brepFromSphere",
900
974
  "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`.",
975
+ "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
976
  "examplePrompts": [
903
977
  "Create a sphere of radius 5 at the origin",
904
978
  "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```"
@@ -913,7 +987,7 @@
913
987
  "examplePrompts": [
914
988
  "Subtract the courtyard from the building mass",
915
989
  "Cut this solid out of that one",
916
- "Punch a hole through the slab with this box\n\n# Example\n```ts\n// buildingBrep minus courtyardBrep — order matters\nconst carved = await snaptrude.core.geom.create.brepFromSubtraction(buildingBrep, courtyardBrep)\n```"
990
+ "Subtract this box solid from that solid\n\n# Example\n```ts\n// buildingBrep minus courtyardBrep — order matters\nconst carved = await snaptrude.core.geom.create.brepFromSubtraction(buildingBrep, courtyardBrep)\n```"
917
991
  ],
918
992
  "argsType": "BrepHandle",
919
993
  "resultType": "BrepHandle"
@@ -921,7 +995,7 @@
921
995
  {
922
996
  "path": "core.geom.create.brepFromSweep",
923
997
  "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.",
998
+ "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
999
  "examplePrompts": [
926
1000
  "Sweep this profile along the corridor path",
927
1001
  "Extrude the railing section along this route",
@@ -938,7 +1012,7 @@
938
1012
  {
939
1013
  "path": "core.geom.create.brepFromSweepAlongCurve",
940
1014
  "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).",
1015
+ "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
1016
  "examplePrompts": [
943
1017
  "Sweep a 600x400 duct along this curved route and keep it upright",
944
1018
  "Run this handrail along the curved stair edge",
@@ -1000,7 +1074,7 @@
1000
1074
  "Make a circle of radius 2 at this point",
1001
1075
  "Draw a circular outline 5m across"
1002
1076
  ],
1003
- "argsType": "Vec3Handle",
1077
+ "argsType": "Vec3Input",
1004
1078
  "resultType": "CircleHandle"
1005
1079
  },
1006
1080
  {
@@ -1038,7 +1112,7 @@
1038
1112
  "Draw a line from point A to point B",
1039
1113
  "Draw a straight edge between these two points"
1040
1114
  ],
1041
- "argsType": "Vec3Handle",
1115
+ "argsType": "Vec3Input",
1042
1116
  "resultType": "LineHandle"
1043
1117
  },
1044
1118
  {
@@ -1059,7 +1133,7 @@
1059
1133
  "Draw a closed polygon through these corner points",
1060
1134
  "Create an L-shaped outline from a list of points"
1061
1135
  ],
1062
- "argsType": "Vec3Handle[]",
1136
+ "argsType": "Vec3Input[]",
1063
1137
  "resultType": "ProfileHandle"
1064
1138
  },
1065
1139
  {
@@ -1071,13 +1145,13 @@
1071
1145
  "Organic curved outline through these corners",
1072
1146
  "Freeform podium profile\n\n# Example\n```ts\nconst profile = await snaptrude.core.geom.create.profileFromSplinePoints([p0, p1, p2, p3, p4])\nconst contour = await snaptrude.core.geom.create.contourFromProfile(profile)\nconst brep = await snaptrude.core.geom.create.brepFromExtrusion(contour, { x: 0, y: 1, z: 0 }, 3)\nconst podium = await snaptrude.design.create.massFromBrep(brep, \"Podium\")\n```"
1073
1147
  ],
1074
- "argsType": "Vec3Handle[]",
1148
+ "argsType": "Vec3Input[]",
1075
1149
  "resultType": "ProfileHandle"
1076
1150
  },
1077
1151
  {
1078
1152
  "path": "core.geom.create.profileRect",
1079
1153
  "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.",
1154
+ "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
1155
  "examplePrompts": [
1082
1156
  "Draw a 4m by 3m rectangle",
1083
1157
  "Make a rectangular outline for a room footprint"
@@ -1090,10 +1164,9 @@
1090
1164
  "namespace": "core.geom.create",
1091
1165
  "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
1166
  "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```"
1167
+ "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
1168
  ],
1096
- "argsType": "Vec3Handle[]",
1169
+ "argsType": "Vec3Input[]",
1097
1170
  "resultType": "SplineHandle"
1098
1171
  },
1099
1172
  {
@@ -1105,7 +1178,7 @@
1105
1178
  "Make a wavy edge that passes through A, B, C and D",
1106
1179
  "Freeform curved wall path through these clicks\n\n# Example\n```ts\nconst spline = await snaptrude.core.geom.create.splineFromPoints([p0, p1, p2, p3])\n// Mixed profile: smooth top, straight closing base\nconst base = await snaptrude.core.geom.create.line(p3, p0)\nconst profile = await snaptrude.core.geom.create.profileFromCurves([spline, base])\n```"
1107
1180
  ],
1108
- "argsType": "Vec3Handle[]",
1181
+ "argsType": "Vec3Input[]",
1109
1182
  "resultType": "SplineHandle"
1110
1183
  },
1111
1184
  {
@@ -1189,7 +1262,7 @@
1189
1262
  {
1190
1263
  "path": "core.geom.query.brep.getCentroid",
1191
1264
  "namespace": "core.geom.query.brep",
1192
- "summary": "Get the centroid (mean of all vertex positions) of a brep.",
1265
+ "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
1266
  "examplePrompts": [
1194
1267
  "Find the centre point of this shape",
1195
1268
  "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 +1273,7 @@
1200
1273
  {
1201
1274
  "path": "core.geom.query.brep.getDistance",
1202
1275
  "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.",
1276
+ "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
1277
  "examplePrompts": [
1205
1278
  "What is the clearance between these two masses?",
1206
1279
  "How far apart are the tower and the neighbouring building?",
@@ -2349,7 +2422,7 @@
2349
2422
  "summary": "Add a hole to a contour by appending an inner profile. Immutable — returns a NEW contour and leaves the input unchanged.",
2350
2423
  "examplePrompts": [
2351
2424
  "Cut a hole in this shape",
2352
- "Punch an opening through this floor outline\n\n# Example\n```ts\nconst outer = await snaptrude.core.geom.create.profileRect(10, 8)\nconst contour = await snaptrude.core.geom.create.contourFromProfile(outer)\nconst hole = await snaptrude.core.geom.create.profileRect(2, 2)\nconst withHole = await snaptrude.core.geom.update.contour.addHole(contour, hole)\nconst count = await snaptrude.core.geom.query.contour.getHoleCount(withHole) // 1\n```"
2425
+ "Punch an opening through this outline\n\n# Example\n```ts\nconst outer = await snaptrude.core.geom.create.profileRect(10, 8)\nconst contour = await snaptrude.core.geom.create.contourFromProfile(outer)\nconst hole = await snaptrude.core.geom.create.profileRect(2, 2)\nconst withHole = await snaptrude.core.geom.update.contour.addHole(contour, hole)\nconst count = await snaptrude.core.geom.query.contour.getHoleCount(withHole) // 1\n```"
2353
2426
  ],
2354
2427
  "argsType": "ContourHandle",
2355
2428
  "resultType": "ContourHandle"
@@ -2393,7 +2466,7 @@
2393
2466
  "summary": "Remove a hole from a contour by inner-profile index. Immutable — returns a NEW contour and leaves the input unchanged.",
2394
2467
  "examplePrompts": [
2395
2468
  "Remove the hole from this shape",
2396
- "Fill in the opening in this floor outline\n\n# Example\n```ts\nconst outer = await snaptrude.core.geom.create.profileRect(10, 8)\nconst hole = await snaptrude.core.geom.create.profileRect(2, 2)\nconst contour = await snaptrude.core.geom.create.contourFromProfiles(outer, [hole])\nconst solid = await snaptrude.core.geom.update.contour.removeHole(contour, 0)\n```"
2469
+ "Fill in the opening in this outline\n\n# Example\n```ts\nconst outer = await snaptrude.core.geom.create.profileRect(10, 8)\nconst hole = await snaptrude.core.geom.create.profileRect(2, 2)\nconst contour = await snaptrude.core.geom.create.contourFromProfiles(outer, [hole])\nconst solid = await snaptrude.core.geom.update.contour.removeHole(contour, 0)\n```"
2397
2470
  ],
2398
2471
  "argsType": "ContourHandle",
2399
2472
  "resultType": "ContourHandle"
@@ -3250,14 +3323,55 @@
3250
3323
  "argsType": "UnderlayHandle",
3251
3324
  "resultType": "{ scaleFactor: number }"
3252
3325
  },
3326
+ {
3327
+ "path": "core.layers.assign",
3328
+ "namespace": "core.layers",
3329
+ "summary": "Move objects onto a layer (picking a layer in the object properties panel).",
3330
+ "examplePrompts": [
3331
+ "Move the selected objects to the BIM layer",
3332
+ "Move the selected objects to the Furniture layer",
3333
+ "Assign the generated walls to the Structure layer",
3334
+ "Put these masses on layer 3f2a…\n\n# Example\n```ts\nconst { assigned, skipped } = await snaptrude.core.layers.assign(ids, layerId)\n```"
3335
+ ],
3336
+ "argsType": "ComponentHandle[]",
3337
+ "resultType": "PluginCoreLayersAssignResult"
3338
+ },
3339
+ {
3340
+ "path": "core.layers.create",
3341
+ "namespace": "core.layers",
3342
+ "summary": "Create a new layer in the Layers panel (the panel's \"Add new layer\" button followed by naming it).",
3343
+ "examplePrompts": [
3344
+ "Create a new layer called Furniture",
3345
+ "Add a layer called Landscape",
3346
+ "Add a layer named Landscape to the Layers panel",
3347
+ "Make a new layer for the facade elements",
3348
+ "Create layers named Structure and Services\n\n# Example\n```ts\nconst layer = await snaptrude.core.layers.create(\"Furniture\")\nawait snaptrude.core.layers.setActive(layer.id)\n```"
3349
+ ],
3350
+ "argsType": "string",
3351
+ "resultType": "PluginCoreLayersCreateResult"
3352
+ },
3353
+ {
3354
+ "path": "core.layers.delete",
3355
+ "namespace": "core.layers",
3356
+ "summary": "Delete a layer (the panel row's \"Delete layer\").",
3357
+ "examplePrompts": [
3358
+ "Delete the Furniture layer and everything on it",
3359
+ "Delete the Furniture layer and move its objects to Default",
3360
+ "Remove layer 3f2a… and move its objects to the default layer",
3361
+ "Merge the Services layer into Structure and delete it",
3362
+ "Delete every empty layer\n\n# Example\n```ts\nawait snaptrude.core.layers.delete(layerId, { deleteObjects: true })\nconst { layers } = await snaptrude.core.layers.list()\nconst def = layers.find((l) => l.isDefault)!\nawait snaptrude.core.layers.delete(otherId, { targetLayerId: def.id })\n```"
3363
+ ],
3364
+ "argsType": "string",
3365
+ "resultType": "PluginCoreLayersDeleteResult"
3366
+ },
3253
3367
  {
3254
3368
  "path": "core.layers.get",
3255
3369
  "namespace": "core.layers",
3256
- "summary": "Get a single drawing layer by id.",
3370
+ "summary": "Get one Layers-panel layer by id, including layers owned by other design options.",
3257
3371
  "examplePrompts": [
3258
- "Get the drawing layer with id ly_5",
3259
- "Is layer ly_5 hidden or locked?",
3260
- "Look up a single drawing layer by its id\n\n# Example\n```ts\nconst layer = await snaptrude.core.layers.get(\"ly_5\")\nif (layer) console.log(layer.name, layer.hidden, layer.locked)\n```"
3372
+ "Get the Layers-panel layer with id 3f2a…",
3373
+ "Is the Furniture layer locked or hidden?",
3374
+ "How many objects are on layer 3f2a…?\n\n# Example\n```ts\nconst layer = await snaptrude.core.layers.get(layerId)\nif (layer) console.log(layer.name, layer.objectCount)\n```"
3261
3375
  ],
3262
3376
  "argsType": "string",
3263
3377
  "resultType": "PluginCoreLayersGetResult"
@@ -3265,35 +3379,51 @@
3265
3379
  {
3266
3380
  "path": "core.layers.getActive",
3267
3381
  "namespace": "core.layers",
3268
- "summary": "Get the active drawing layer of a storey.",
3382
+ "summary": "Get the active Layers-panel layer: the one new objects land on.",
3269
3383
  "examplePrompts": [
3270
- "Which drawing layer is active on this storey?",
3271
- "Get the current layer on storey st_2",
3272
- "What layer am I drawing on?\n\n# Example\n```ts\nconst active = await snaptrude.core.layers.getActive(\"st_2\")\nif (active) console.log(active.name)\n```"
3384
+ "Which layer is active?",
3385
+ "Which layer in the Layers panel is active?",
3386
+ "What layer will new objects go on?",
3387
+ "Which layer are new walls and masses being added to?\n\n# Example\n```ts\nconst active = await snaptrude.core.layers.getActive()\nif (active) console.log(active.name)\n```"
3273
3388
  ],
3274
- "argsType": "string",
3389
+ "argsType": null,
3275
3390
  "resultType": "PluginCoreLayersGetActiveResult"
3276
3391
  },
3277
3392
  {
3278
3393
  "path": "core.layers.list",
3279
3394
  "namespace": "core.layers",
3280
- "summary": "List the drawing layers of a storey.",
3395
+ "summary": "List the layers in the Layers panel: the active design option's layers, default layer first, then in creation order.",
3281
3396
  "examplePrompts": [
3282
- "List the drawing layers on this storey",
3283
- "Show all the PDF underlays on storey st_2",
3284
- "What CAD layers are on the ground floor?\n\n# Example\n```ts\nconst { layers } = await snaptrude.core.layers.list(\"st_2\")\nfor (const l of layers) console.log(l.name, l.layerType, l.hidden)\n```"
3397
+ "List all the layers in the Layers panel",
3398
+ "Show me the layers in the left sidebar",
3399
+ "What layers does this project have?",
3400
+ "Show every layer with whether it is locked or hidden",
3401
+ "Which layer is the default layer?",
3402
+ "Which layers are empty?\n\n# Example\n```ts\nconst { layers } = await snaptrude.core.layers.list()\nfor (const l of layers) console.log(l.name, l.objectCount, l.locked, l.hidden)\n```"
3285
3403
  ],
3286
- "argsType": "string",
3404
+ "argsType": null,
3287
3405
  "resultType": "PluginCoreLayersListResult"
3288
3406
  },
3407
+ {
3408
+ "path": "core.layers.rename",
3409
+ "namespace": "core.layers",
3410
+ "summary": "Rename a layer (the panel row's \"Rename layer\").",
3411
+ "examplePrompts": [
3412
+ "Rename the Furniture layer to Loose furniture",
3413
+ "Rename layer 3f2a… to Existing building",
3414
+ "Change the name of the Landscape layer\n\n# Example\n```ts\nawait snaptrude.core.layers.rename(layerId, \"Loose furniture\")\n```"
3415
+ ],
3416
+ "argsType": "string",
3417
+ "resultType": "PluginCoreLayersRenameResult"
3418
+ },
3289
3419
  {
3290
3420
  "path": "core.layers.setActive",
3291
3421
  "namespace": "core.layers",
3292
- "summary": "Make a drawing layer the active layer of its storey.",
3422
+ "summary": "Make a layer the active layer (clicking its row in the Layers panel).",
3293
3423
  "examplePrompts": [
3294
- "Draw on layer ly_5",
3295
- "Make this the active layer",
3296
- "Switch the active layer to the CAD underlay\n\n# Example\n```ts\nawait snaptrude.core.layers.setActive(\"ly_5\")\n```"
3424
+ "Put new objects on the Furniture layer",
3425
+ "Make the Structure layer the active layer in the Layers panel",
3426
+ "Switch the active layer to Landscape\n\n# Example\n```ts\nawait snaptrude.core.layers.setActive(layerId)\n```"
3297
3427
  ],
3298
3428
  "argsType": "string",
3299
3429
  "resultType": "PluginCoreLayersSetActiveResult"
@@ -3301,11 +3431,12 @@
3301
3431
  {
3302
3432
  "path": "core.layers.update",
3303
3433
  "namespace": "core.layers",
3304
- "summary": "Update a drawing layer's visibility.",
3434
+ "summary": "Lock/unlock or show/hide a layer (the panel row's padlock and eye).",
3305
3435
  "examplePrompts": [
3306
- "Hide the PDF underlay layer",
3307
- "Show the CAD layer again",
3308
- "Hide layer ly_5\n\n# Example\n```ts\nconst layer = await snaptrude.core.layers.update(\"ly_5\", true)\n```"
3436
+ "Lock the Default layer",
3437
+ "Lock the Structure layer",
3438
+ "Hide the Landscape layer in the Layers panel",
3439
+ "Unlock layer 3f2a… and show it again\n\n# Example\n```ts\nawait snaptrude.core.layers.update(layerId, { locked: true, hidden: false })\n```"
3309
3440
  ],
3310
3441
  "argsType": "string",
3311
3442
  "resultType": "PluginCoreLayersUpdateResult"
@@ -3355,7 +3486,7 @@
3355
3486
  "namespace": "core.math.quat",
3356
3487
  "summary": "Build a quaternion from a rotation axis and an angle in radians.",
3357
3488
  "examplePrompts": [],
3358
- "argsType": "Vec3Handle",
3489
+ "argsType": "Vec3Input",
3359
3490
  "resultType": "QuatHandle"
3360
3491
  },
3361
3492
  {
@@ -3435,7 +3566,7 @@
3435
3566
  "namespace": "core.math.vec3",
3436
3567
  "summary": "Add two vectors component-wise (`a + b`).",
3437
3568
  "examplePrompts": [],
3438
- "argsType": "Vec3Handle",
3569
+ "argsType": "Vec3Input",
3439
3570
  "resultType": "Vec3Handle"
3440
3571
  },
3441
3572
  {
@@ -3443,7 +3574,7 @@
3443
3574
  "namespace": "core.math.vec3",
3444
3575
  "summary": "Read the primitive `{ x, y, z }` components of a vector handle.",
3445
3576
  "examplePrompts": [],
3446
- "argsType": "Vec3Handle",
3577
+ "argsType": "Vec3Input",
3447
3578
  "resultType": "Vec3Components"
3448
3579
  },
3449
3580
  {
@@ -3451,7 +3582,7 @@
3451
3582
  "namespace": "core.math.vec3",
3452
3583
  "summary": "Compute the cross product `a × b` — the vector perpendicular to both.",
3453
3584
  "examplePrompts": [],
3454
- "argsType": "Vec3Handle",
3585
+ "argsType": "Vec3Input",
3455
3586
  "resultType": "Vec3Handle"
3456
3587
  },
3457
3588
  {
@@ -3462,7 +3593,7 @@
3462
3593
  "What is the distance between these two points?",
3463
3594
  "Measure how far apart these two points are\n\n# Example\n```ts\nconst a = await snaptrude.core.math.vec3.new(0, 0, 0)\nconst b = await snaptrude.core.math.vec3.new(3, 4, 0)\nconst dist = await snaptrude.core.math.vec3.distance(a, b) // 5\n```"
3464
3595
  ],
3465
- "argsType": "Vec3Handle",
3596
+ "argsType": "Vec3Input",
3466
3597
  "resultType": "number"
3467
3598
  },
3468
3599
  {
@@ -3470,7 +3601,7 @@
3470
3601
  "namespace": "core.math.vec3",
3471
3602
  "summary": "Compute the dot product of two vectors.",
3472
3603
  "examplePrompts": [],
3473
- "argsType": "Vec3Handle",
3604
+ "argsType": "Vec3Input",
3474
3605
  "resultType": "number"
3475
3606
  },
3476
3607
  {
@@ -3478,7 +3609,7 @@
3478
3609
  "namespace": "core.math.vec3",
3479
3610
  "summary": "Test whether two vectors have exactly equal components.",
3480
3611
  "examplePrompts": [],
3481
- "argsType": "Vec3Handle",
3612
+ "argsType": "Vec3Input",
3482
3613
  "resultType": "boolean"
3483
3614
  },
3484
3615
  {
@@ -3486,7 +3617,7 @@
3486
3617
  "namespace": "core.math.vec3",
3487
3618
  "summary": "Test whether two vectors are equal within a tolerance `epsilon` (default `1e-6`).",
3488
3619
  "examplePrompts": [],
3489
- "argsType": "Vec3Handle",
3620
+ "argsType": "Vec3Input",
3490
3621
  "resultType": "boolean"
3491
3622
  },
3492
3623
  {
@@ -3494,7 +3625,7 @@
3494
3625
  "namespace": "core.math.vec3",
3495
3626
  "summary": "Compute the Euclidean length (magnitude) of a vector.",
3496
3627
  "examplePrompts": [],
3497
- "argsType": "Vec3Handle",
3628
+ "argsType": "Vec3Input",
3498
3629
  "resultType": "number"
3499
3630
  },
3500
3631
  {
@@ -3502,7 +3633,7 @@
3502
3633
  "namespace": "core.math.vec3",
3503
3634
  "summary": "Compute the squared Euclidean length of a vector (avoids the sqrt).",
3504
3635
  "examplePrompts": [],
3505
- "argsType": "Vec3Handle",
3636
+ "argsType": "Vec3Input",
3506
3637
  "resultType": "number"
3507
3638
  },
3508
3639
  {
@@ -3510,7 +3641,7 @@
3510
3641
  "namespace": "core.math.vec3",
3511
3642
  "summary": "Linearly interpolate between two vectors: `a + (b - a) * t`.",
3512
3643
  "examplePrompts": [],
3513
- "argsType": "Vec3Handle",
3644
+ "argsType": "Vec3Input",
3514
3645
  "resultType": "Vec3Handle"
3515
3646
  },
3516
3647
  {
@@ -3518,7 +3649,7 @@
3518
3649
  "namespace": "core.math.vec3",
3519
3650
  "summary": "Negate a vector, reversing its direction.",
3520
3651
  "examplePrompts": [],
3521
- "argsType": "Vec3Handle",
3652
+ "argsType": "Vec3Input",
3522
3653
  "resultType": "Vec3Handle"
3523
3654
  },
3524
3655
  {
@@ -3534,7 +3665,7 @@
3534
3665
  "namespace": "core.math.vec3",
3535
3666
  "summary": "Return the unit vector in the same direction (the zero vector if length is 0).",
3536
3667
  "examplePrompts": [],
3537
- "argsType": "Vec3Handle",
3668
+ "argsType": "Vec3Input",
3538
3669
  "resultType": "Vec3Handle"
3539
3670
  },
3540
3671
  {
@@ -3542,7 +3673,7 @@
3542
3673
  "namespace": "core.math.vec3",
3543
3674
  "summary": "Scale `v` by a scalar.",
3544
3675
  "examplePrompts": [],
3545
- "argsType": "Vec3Handle",
3676
+ "argsType": "Vec3Input",
3546
3677
  "resultType": "Vec3Handle"
3547
3678
  },
3548
3679
  {
@@ -3550,7 +3681,7 @@
3550
3681
  "namespace": "core.math.vec3",
3551
3682
  "summary": "Subtract vector `b` from `a` component-wise (`a - b`).",
3552
3683
  "examplePrompts": [],
3553
- "argsType": "Vec3Handle",
3684
+ "argsType": "Vec3Input",
3554
3685
  "resultType": "Vec3Handle"
3555
3686
  },
3556
3687
  {
@@ -4035,6 +4166,66 @@
4035
4166
  "argsType": null,
4036
4167
  "resultType": "number"
4037
4168
  },
4169
+ {
4170
+ "path": "core.storeys.layers.get",
4171
+ "namespace": "core.storeys.layers",
4172
+ "summary": "Get a single storey layer by id.",
4173
+ "examplePrompts": [
4174
+ "Get the storey drawing layer with id ly_5",
4175
+ "Is storey layer ly_5 hidden?",
4176
+ "Look up a storey underlay layer by id\n\n# Example\n```ts\nconst layer = await snaptrude.core.storeys.layers.get(\"ly_5\")\nif (layer) console.log(layer.name, layer.hidden, layer.locked)\n```"
4177
+ ],
4178
+ "argsType": "string",
4179
+ "resultType": "PluginCoreStoreysLayersGetResult"
4180
+ },
4181
+ {
4182
+ "path": "core.storeys.layers.getActive",
4183
+ "namespace": "core.storeys.layers",
4184
+ "summary": "Get the active storey layer of a storey.",
4185
+ "examplePrompts": [
4186
+ "Which sketch surface (Wall/Rough) is active on this storey?",
4187
+ "Get the active storey drawing layer on st_2",
4188
+ "Which storey layer is new sketch geometry drawn onto?\n\n# Example\n```ts\nconst active = await snaptrude.core.storeys.layers.getActive(\"st_2\")\nif (active) console.log(active.name)\n```"
4189
+ ],
4190
+ "argsType": "string",
4191
+ "resultType": "PluginCoreStoreysLayersGetActiveResult"
4192
+ },
4193
+ {
4194
+ "path": "core.storeys.layers.list",
4195
+ "namespace": "core.storeys.layers",
4196
+ "summary": "List the storey layers of a storey.",
4197
+ "examplePrompts": [
4198
+ "List the sketch surfaces and underlays on this storey",
4199
+ "Show all the PDF underlays on storey st_2",
4200
+ "What CAD layers are on the ground floor?\n\n# Example\n```ts\nconst { layers } = await snaptrude.core.storeys.layers.list(\"st_2\")\nfor (const l of layers) console.log(l.name, l.layerType, l.hidden)\n```"
4201
+ ],
4202
+ "argsType": "string",
4203
+ "resultType": "PluginCoreStoreysLayersListResult"
4204
+ },
4205
+ {
4206
+ "path": "core.storeys.layers.setActive",
4207
+ "namespace": "core.storeys.layers",
4208
+ "summary": "Make a storey layer the active layer of its storey.",
4209
+ "examplePrompts": [
4210
+ "Switch this storey's sketch surface to the Rough layer",
4211
+ "Make storey layer ly_5 the active sketch surface",
4212
+ "Switch the active storey layer to the CAD underlay\n\n# Example\n```ts\nawait snaptrude.core.storeys.layers.setActive(\"ly_5\")\n```"
4213
+ ],
4214
+ "argsType": "string",
4215
+ "resultType": "PluginCoreStoreysLayersSetActiveResult"
4216
+ },
4217
+ {
4218
+ "path": "core.storeys.layers.update",
4219
+ "namespace": "core.storeys.layers",
4220
+ "summary": "Update a storey layer's visibility.",
4221
+ "examplePrompts": [
4222
+ "Hide the PDF underlay layer",
4223
+ "Show the CAD underlay again",
4224
+ "Hide storey layer ly_5\n\n# Example\n```ts\nconst layer = await snaptrude.core.storeys.layers.update(\"ly_5\", true)\n```"
4225
+ ],
4226
+ "argsType": "string",
4227
+ "resultType": "PluginCoreStoreysLayersUpdateResult"
4228
+ },
4038
4229
  {
4039
4230
  "path": "core.storeys.list",
4040
4231
  "namespace": "core.storeys",
@@ -4051,7 +4242,7 @@
4051
4242
  {
4052
4243
  "path": "core.storeys.setActive",
4053
4244
  "namespace": "core.storeys",
4054
- "summary": "Make a storey the active storey — the same as clicking it in the storey/layer panel. Subsequent draws and creates target this storey, and in 2D the viewport switches to it. This is a view/navigation change: it is **not** undoable and commits nothing to the model.",
4245
+ "summary": "Make a storey the active storey — the same as clicking it in the storey/layer panel. Subsequent draws and creates target this storey, and in 2D the viewport switches to that storey's plan (and its cut range), unless the active view is already a plan on it. This is a view/navigation change: it is **not** undoable and commits nothing to the model.",
4055
4246
  "examplePrompts": [
4056
4247
  "Switch to the second floor",
4057
4248
  "Make the ground storey active",
@@ -4375,6 +4566,46 @@
4375
4566
  "resultType": "boolean",
4376
4567
  "deprecated": "core.camera.zoomSelection"
4377
4568
  },
4569
+ {
4570
+ "path": "design.bim.create",
4571
+ "namespace": "design.bim",
4572
+ "summary": "Execute native Sketch-to-BIM. Revalidates scope, catalogue access and construction before mutation. Uses native progress/cancel controls and worker cancellation. Floors/roofs retain project defaults.",
4573
+ "examplePrompts": [
4574
+ "Convert these sketch rooms to BIM with wall layers chosen for each room boundary"
4575
+ ],
4576
+ "argsType": "string",
4577
+ "resultType": "PluginBimResult"
4578
+ },
4579
+ {
4580
+ "path": "design.bim.getContext",
4581
+ "namespace": "design.bim",
4582
+ "summary": "Read room adjacency and native conversion eligibility in the active proposal.",
4583
+ "examplePrompts": [
4584
+ "Inspect the rooms on each side of these sketch wall boundaries"
4585
+ ],
4586
+ "argsType": "PluginBimContextArgs",
4587
+ "resultType": "PluginBimContext"
4588
+ },
4589
+ {
4590
+ "path": "design.bim.prepare",
4591
+ "namespace": "design.bim",
4592
+ "summary": "Stage a complete wall-construction assignment. A new prepare replaces this plugin's prior plan.",
4593
+ "examplePrompts": [
4594
+ "Validate wall layer assignments before converting sketch rooms to BIM"
4595
+ ],
4596
+ "argsType": "PluginBimPrepareArgs",
4597
+ "resultType": "PluginBimPreparation"
4598
+ },
4599
+ {
4600
+ "path": "design.bim.update",
4601
+ "namespace": "design.bim",
4602
+ "summary": "Add a partition graph with explicit connectivity and chosen wall families in one undo step. Validation is part of this call. PG2 splits intersections and generates floors with native lineage. Does not move/delete existing edges, infer face snapping, or change catalogue definitions.",
4603
+ "examplePrompts": [
4604
+ "Add bathroom partitions attached to these existing graph boundaries"
4605
+ ],
4606
+ "argsType": "string",
4607
+ "resultType": "PluginBimUpdateResult"
4608
+ },
4378
4609
  {
4379
4610
  "path": "design.boolean.intersect",
4380
4611
  "namespace": "design.boolean",
@@ -4416,6 +4647,38 @@
4416
4647
  "argsType": "ComponentHandle[]",
4417
4648
  "resultType": "BooleanOutcome"
4418
4649
  },
4650
+ {
4651
+ "path": "design.catalog.get",
4652
+ "namespace": "design.catalog",
4653
+ "summary": "",
4654
+ "examplePrompts": [],
4655
+ "argsType": "PluginCatalogReference",
4656
+ "resultType": "PluginCatalogEntry | null"
4657
+ },
4658
+ {
4659
+ "path": "design.catalog.list",
4660
+ "namespace": "design.catalog",
4661
+ "summary": "",
4662
+ "examplePrompts": [],
4663
+ "argsType": "PluginCatalogKind",
4664
+ "resultType": "PluginCatalogEntry[]"
4665
+ },
4666
+ {
4667
+ "path": "design.catalog.listCategories",
4668
+ "namespace": "design.catalog",
4669
+ "summary": "Discover categories from accessible saved families and current project constructions. Omit parent for roots; pass a slash-separated returned path for its direct children. This metadata read does not fetch recipes, include unsaved drafts or legacy mesh inventories, or establish capabilities. Use search then get before placement. Names retain their case.",
4670
+ "examplePrompts": [],
4671
+ "argsType": "PluginCatalogListCategoriesArgs",
4672
+ "resultType": "PluginCatalogCategory[]"
4673
+ },
4674
+ {
4675
+ "path": "design.catalog.search",
4676
+ "namespace": "design.catalog",
4677
+ "summary": "Search accessible saved-family and project-construction metadata without downloading recipes. Categories are data, not an SDK enum. Legacy mesh inventories and unsaved drafts are excluded. Inspect selected references with get before placement; a search match does not prove validity or supported behavior. Pagination reads live data, not a snapshot. Failures reject.",
4678
+ "examplePrompts": [],
4679
+ "argsType": "PluginCatalogSearchArgs",
4680
+ "resultType": "PluginCatalogSearchResult"
4681
+ },
4419
4682
  {
4420
4683
  "path": "design.create.beam",
4421
4684
  "namespace": "design.create",
@@ -4426,7 +4689,7 @@
4426
4689
  "Run a 300x600 beam across the room",
4427
4690
  "Extrude this cross-section into a 5m long beam\n\n# Example\n```ts\nconst rect = await snaptrude.core.geom.create.profileRect(0.3, 0.6)\nconst section = await snaptrude.core.geom.create.contourFromProfile(rect)\nconst alongX = await snaptrude.core.math.vec3.new(1, 0, 0)\nconst beam = await snaptrude.design.create.beam(section, 6, alongX)\n```"
4428
4691
  ],
4429
- "argsType": "ContourHandle",
4692
+ "argsType": "| [section: ContourHandle, length: number, direction?: Vec3Input]\n | [axis: LineHandle, options: PluginFamilyPlacement]",
4430
4693
  "resultType": "ComponentHandle"
4431
4694
  },
4432
4695
  {
@@ -4442,6 +4705,17 @@
4442
4705
  "argsType": "PluginBuildableEnvelopePolygonVertex[]",
4443
4706
  "resultType": "PluginBuildableEnvelopeCreateResult"
4444
4707
  },
4708
+ {
4709
+ "path": "design.create.buildableEnvelopeFromSite",
4710
+ "namespace": "design.create",
4711
+ "summary": "Create a default buildable envelope for an existing site — the same as the right panel's \"Create Buildable Envelope\" button. Front, side and rear distances are seeded from Site Analysis or the Program sheet, with a 10 ft fallback, and the envelope is linked to the site. Call it only after the user agrees (for example when `design.update.siteEdge` throws `NO_BUILDABLE_ENVELOPE`).",
4712
+ "examplePrompts": [
4713
+ "Create a buildable envelope for this site with default setbacks",
4714
+ "Add the zoning envelope to my site\n\n# Example\n```ts\nconst [{ siteId }] = (await snaptrude.program.site.listEdges()).sites\nconst { buildableEnvelopeId } =\nawait snaptrude.design.create.buildableEnvelopeFromSite(siteId)\n```"
4715
+ ],
4716
+ "argsType": "string",
4717
+ "resultType": "PluginDesignCreateBuildableEnvelopeFromSiteResult"
4718
+ },
4445
4719
  {
4446
4720
  "path": "design.create.ceiling",
4447
4721
  "namespace": "design.create",
@@ -4452,7 +4726,7 @@
4452
4726
  "Put a dropped ceiling in the living room at 3 metres",
4453
4727
  "Add a 50mm thick ceiling lifted 2800mm from floor level\n\n# Example\n```ts\nconst rect = await snaptrude.core.geom.create.profileRect(5, 4)\nconst contour = await snaptrude.core.geom.create.contourFromProfile(rect)\nconst ceiling = await snaptrude.design.create.ceiling(contour, 0.05, 2.8)\n```"
4454
4728
  ],
4455
- "argsType": "ContourHandle",
4729
+ "argsType": "| [contour: ContourHandle, thickness: number, heightFromFloor?: number]\n | [contour: ContourHandle, options: PluginFamilyCeilingOptions]",
4456
4730
  "resultType": "ComponentHandle"
4457
4731
  },
4458
4732
  {
@@ -4465,7 +4739,7 @@
4465
4739
  "Create a 450mm square concrete column 4 metres high",
4466
4740
  "Stand a column up from this base point\n\n# Example\n```ts\nconst rect = await snaptrude.core.geom.create.profileRect(0.45, 0.45)\nconst crossSection = await snaptrude.core.geom.create.contourFromProfile(rect)\nconst base = await snaptrude.core.math.vec3.new(4, 0, 4)\nconst column = await snaptrude.design.create.column(base, crossSection, 3)\n```"
4467
4741
  ],
4468
- "argsType": "Vec3Handle",
4742
+ "argsType": "| [\n position: Vec3Input,\n crossSection: ContourHandle,\n height: number,\n directionUp?: Vec3Input,\n ]\n | [axis: LineHandle, options: PluginFamilyPlacement]",
4469
4743
  "resultType": "ComponentHandle"
4470
4744
  },
4471
4745
  {
@@ -4482,13 +4756,12 @@
4482
4756
  {
4483
4757
  "path": "design.create.copy",
4484
4758
  "namespace": "design.create",
4485
- "summary": "**Copy** existing entities, offsetting each copy by `displacement * i` (i = 1…count), source positions preserved. Migrated from the removed `tools.copy`. In `\"instance\"` mode copies stay in the source's instance family where possible; `\"unique\"` makes independent geometry. The created copies become the active selection. Undoable.",
4759
+ "summary": "**Copy** existing entities, offsetting each copy by `displacement * i` (i = 1…count), source positions preserved. Migrated from the removed `tools.copy`. The created copies become the active selection. Undoable.",
4486
4760
  "examplePrompts": [
4487
4761
  "Copy this room 3 times spaced 6m apart",
4488
4762
  "Duplicate the selected furniture along the X axis",
4489
4763
  "Make 5 copies of this column at 4 metre intervals",
4490
- "Array these walls with a 3m offset",
4491
- "Clone the desk as a unique copy, not an instance"
4764
+ "Array these walls with a 3m offset"
4492
4765
  ],
4493
4766
  "argsType": "ComponentHandle[]",
4494
4767
  "resultType": "ComponentHandle[]",
@@ -4497,7 +4770,7 @@
4497
4770
  {
4498
4771
  "path": "design.create.door",
4499
4772
  "namespace": "design.create",
4500
- "summary": "Place a **door** from the catalog into a host wall.",
4773
+ "summary": "Place a parametric **door** family into a host wall.",
4501
4774
  "examplePrompts": [
4502
4775
  "Add a door to this wall here",
4503
4776
  "Place a sliding door on the selected wall",
@@ -4514,17 +4787,17 @@
4514
4787
  {
4515
4788
  "path": "design.create.doors",
4516
4789
  "namespace": "design.create",
4517
- "summary": "Place **many doors** into host walls in one undoable operation (bulk plural of {@linkcode door}). Each item carries its own catalog id, host wall, world position, and optional `facing`, `label`, `width` and `height` — the same fields as a `\"door\"` {@linkcode PluginDesignCreateOpeningOptions}. Validate-all-or-throw; one command.",
4790
+ "summary": "Place **many doors** into host walls in one undoable operation (bulk plural of {@linkcode door}). Each item carries its own family id, host wall, world position, and optional `facing`, `label`, `width` and `height` — the same fields as a `\"door\"` {@linkcode PluginDesignCreateOpeningOptions}. Validate-all-or-throw; one command.",
4518
4791
  "examplePrompts": [
4519
4792
  "Add doors to all of these walls at once",
4520
4793
  "Place a door in every room in one operation",
4521
4794
  "Bulk add the entrance doors from this schedule",
4522
4795
  "Put three doors on this wall in a single undo step",
4523
- "Add all the doors for this floor plan together"
4796
+ "Add all the doors for this floor plan together\n\nA width/height (top-level, or under the family's own keys in `values`) on a family that\ntakes its size from its Type while Custom Sizes is off is never dropped: the Type within\n25 mm is chosen, else Custom Sizes is turned on and that size is built, leaving the Type\nlabel stale. `design.doors.checkPlacement` reports which as `sizing`; `customSizes: \"No\"`\nwith a size no Type matches is refused."
4524
4797
  ],
4525
4798
  "argsType": "PluginCreateDoorItem[]",
4526
4799
  "resultType": "ComponentHandle[]",
4527
- "performance": "Bulk creator — the whole batch is ONE host round-trip and ONE undo entry, and each distinct catalog source loads once. Always prefer this over calling `design.create.door` in a loop: build the full `items[]` array first (all host walls and points up front), then make one call. # Example ```ts const v = snaptrude.core.math.vec3 const [wall] = await snaptrude.design.query.listWalls({ isSelected: true }) const [entry] = await snaptrude.design.doors.listCatalog() const [front, side] = await snaptrude.design.create.doors([ { catalogId: entry.id, hostWall: wall, position: await v.new(2, 0, 5), label: \"D-01\" }, { catalogId: entry.id, hostWall: wall, position: await v.new(6, 0, 5), width: 1.2 }, ]) // `wall` was re-cut twice and no longer resolves — ask the opening for its host: const currentWall = await snaptrude.design.query.getHost(front) ```"
4800
+ "performance": "Bulk creator — the whole batch is ONE host round-trip and ONE undo entry. Always prefer this over calling `design.create.door` in a loop: build the full `items[]` array first (all host walls and points up front), then make one call. # Example ```ts const v = snaptrude.core.math.vec3 const [wall] = await snaptrude.design.query.listWalls({ isSelected: true }) const [entry] = await snaptrude.design.doors.listCatalog() const [front, side] = await snaptrude.design.create.doors([ { familyId: entry.id, hostWall: wall, position: await v.new(2, 0, 5), label: \"D-01\" }, { familyId: entry.id, hostWall: wall, position: await v.new(6, 0, 5), values: { width: \"1200mm\" } }, ]) // The host still resolves after both cuts: const currentWall = await snaptrude.design.query.getHost(front) ```"
4528
4801
  },
4529
4802
  {
4530
4803
  "path": "design.create.floor",
@@ -4536,9 +4809,9 @@
4536
4809
  "Lay flooring across the selected outline",
4537
4810
  "Put a floor at this position with 150mm thickness\n\n# Example\n```ts\nconst rect = await snaptrude.core.geom.create.profileRect(5, 4)\nconst contour = await snaptrude.core.geom.create.contourFromProfile(rect)\nconst floor = await snaptrude.design.create.floor(contour, 0.1)\n```"
4538
4811
  ],
4539
- "argsType": "ContourHandle",
4540
- "resultType": "ComponentHandle",
4541
- "performance": "For MORE THAN ONE floor, call `design.create.floors(items[])` — one host round-trip and ONE undo entry for the whole batch. Looping this single-floor creator is N round-trips and N undo entries."
4812
+ "argsType": "| [contour: ContourHandle, thickness: number, position?: Vec3Input]\n | [contour: ContourHandle, options: PluginFamilyFloorOptions]",
4813
+ "resultType": "ComponentHandle[]",
4814
+ "performance": "For dimensional floors, `design.create.floors(items[])` uses one host round-trip and one undo entry. That bulk method does not accept native family options; use this method for a family-backed floor."
4542
4815
  },
4543
4816
  {
4544
4817
  "path": "design.create.floors",
@@ -4650,14 +4923,14 @@
4650
4923
  {
4651
4924
  "path": "design.create.opening",
4652
4925
  "namespace": "design.create",
4653
- "summary": "Place a catalog **door or window** into a host wall with optional size overrides.",
4926
+ "summary": "Place a parametric **door or window** into a host wall with parameter overrides.",
4654
4927
  "examplePrompts": [
4655
4928
  "Add a 1m wide door to this wall here",
4656
4929
  "Place a window with a 0.9m sill height on the selected wall"
4657
4930
  ],
4658
4931
  "argsType": "PluginDesignCreateOpeningOptions",
4659
4932
  "resultType": "ComponentHandle",
4660
- "performance": "Single-opening creator — use it for one hosted opening. For MORE THAN ONE, call `design.create.doors(items[])` / `design.create.windows(items[])`: the same per-item fields, one host round-trip, ONE undo entry. # Example ```ts const opening = await snaptrude.design.create.opening({ kind: \"window\", catalogId: windowType.id, hostWall: wall, position: await snaptrude.core.math.vec3.new(3, 0, 5), width: 1.2, height: 1.5, sillHeight: 0.9, }) ```"
4933
+ "performance": "Single-opening creator — use it for one hosted opening. For MORE THAN ONE, call `design.create.doors(items[])` / `design.create.windows(items[])`: the same per-item fields, one host round-trip, ONE undo entry. # Example ```ts const opening = await snaptrude.design.create.opening({ kind: \"window\", familyId: windowType.id, hostWall: wall, position: await snaptrude.core.math.vec3.new(3, 0, 5), width: 1.2, height: 1.5, sillHeight: 0.9, }) ```"
4661
4934
  },
4662
4935
  {
4663
4936
  "path": "design.create.referenceLines",
@@ -4697,7 +4970,7 @@
4697
4970
  "Make a basement slab that extrudes downward",
4698
4971
  "Give this room outline a 6 inch slab\n\n# Example\n```ts\nconst rect = await snaptrude.core.geom.create.profileRect(12, 10)\nconst contour = await snaptrude.core.geom.create.contourFromProfile(rect)\nconst slab = await snaptrude.design.create.slab(contour, 0.15, \"down\", \"Plinth\")\n```"
4699
4972
  ],
4700
- "argsType": "ContourHandle",
4973
+ "argsType": "| [\n contour: ContourHandle,\n thickness: number,\n direction?: \"up\" | \"down\",\n slabType?: PluginSlabType,\n ]\n | [contour: ContourHandle, options: PluginFamilySlabOptions]",
4701
4974
  "resultType": "ComponentHandle"
4702
4975
  },
4703
4976
  {
@@ -4759,10 +5032,43 @@
4759
5032
  "argsType": "PluginStaircasePreset",
4760
5033
  "resultType": "ComponentHandle"
4761
5034
  },
5035
+ {
5036
+ "path": "design.create.void",
5037
+ "namespace": "design.create",
5038
+ "summary": "Cut a **void** — a hole, opening or shaft — through an existing FLAT slab, floor, roof or ceiling. The void is a selectable child object of its host (the \"Void\" of the Draw Void tool). It always cuts the full host thickness. It can be moved with `design.transform.move` (a move that would leave the host is shortened without a report) and removed with `design.delete.entities`; to resize, delete it and cut a new one (`design.update.space` refuses a void).",
5039
+ "examplePrompts": [
5040
+ "Cut a hole in this slab",
5041
+ "Add a 1.2m by 2.4m stair opening in the first-floor slab",
5042
+ "Make a 600 x 600 service penetration in the floor for the riser",
5043
+ "Put a skylight opening through the roof",
5044
+ "Cut a round 1m duct hole in the slab",
5045
+ "Make a cut-out in the ceiling for the light well",
5046
+ "Add a double-height atrium void in the level 2 floor"
5047
+ ],
5048
+ "argsType": "ComponentHandle",
5049
+ "resultType": "ComponentHandle",
5050
+ "performance": "Single-void creator. For MORE THAN ONE void, call `design.create.voids(items[])`: one host round-trip, ONE undo entry, one cut per host."
5051
+ },
5052
+ {
5053
+ "path": "design.create.voids",
5054
+ "namespace": "design.create",
5055
+ "summary": "Cut **many voids** (holes, openings, shafts) through slabs, floors, roofs and ceilings in one undoable operation (bulk plural of {@linkcode void}). Items may share a host or use different hosts — one shaft outline on every storey's slab AND floor is one call. Validate-all-or-throw: one item whose host does not contain the outline rejects the whole batch, so pass only the hosts that contain it; `details.itemIndex` names the failing item (for two overlapping items of the batch, the earlier one). At most 50 voids per host and 200 in all per call. Same outline rules as {@linkcode void}: plan outline, 25.4 mm (0.1 SU) clearance, floor AND slab are separate hosts. Call it as `snaptrude.design.create.voids(...)`.",
5056
+ "examplePrompts": [
5057
+ "Cut a lift shaft through every floor slab of the core",
5058
+ "Cut a 2m x 2m shaft through all the slabs at this location",
5059
+ "Make stair openings in all the floors above the staircases",
5060
+ "Make holes in the roof for these three skylights",
5061
+ "Cut openings in each storey's slab matching this outline",
5062
+ "Punch the same riser penetration through every floor at once"
5063
+ ],
5064
+ "argsType": "PluginCreateVoidItem[]",
5065
+ "resultType": "ComponentHandle[]",
5066
+ "performance": "ONE host round-trip, ONE undo entry, ONE cut per host. Never call `void` in a loop. Runs synchronously on the main thread: 50 voids on one host take about 1 s, and the time grows with the number of hosts (caps: 50 per host, 200 per call)."
5067
+ },
4762
5068
  {
4763
5069
  "path": "design.create.wallRuns",
4764
5070
  "namespace": "design.create",
4765
- "summary": "Create **many wall runs** in one undoable operation (bulk plural of {@linkcode walls}). Each item is one full run — a wall per curve in that item's profile chain, mitred at shared endpoints. Junctions resolve within a run, not between runs. Each item carries its own dimensions, `wallType` and `storey`, so a single call can build a whole floor plate or several storeys at once. Validate-all-or-throw; one command.",
5071
+ "summary": "Create **many wall runs** in one undoable operation (bulk plural of {@linkcode walls}). Each item is one full run — a wall per curve in that item's profile chain, mitred at shared endpoints. Junctions resolve within a run, not between runs. Each item carries its own dimensions, `wallType` and `storey`, so a single call can build a whole floor plate or several storeys at once. Walls have one height: a non-null `topProfiles` entry is refused with `PRECONDITION_FAILED` (`details.itemIndex` names the item) and nothing is created. Validate-all-or-throw; one command.",
4766
5072
  "examplePrompts": [
4767
5073
  "Draw all the walls of this floor plan at once",
4768
5074
  "Build every room's perimeter in one operation",
@@ -4794,7 +5100,7 @@
4794
5100
  {
4795
5101
  "path": "design.create.window",
4796
5102
  "namespace": "design.create",
4797
- "summary": "Place a **window** from the catalog into a host wall.",
5103
+ "summary": "Place a parametric **window** family into a host wall.",
4798
5104
  "examplePrompts": [
4799
5105
  "Add a window to this wall here",
4800
5106
  "Place a casement window on the selected wall",
@@ -4810,17 +5116,17 @@
4810
5116
  {
4811
5117
  "path": "design.create.windows",
4812
5118
  "namespace": "design.create",
4813
- "summary": "Place **many windows** into host walls in one undoable operation (bulk plural of {@linkcode window}). Each item carries its own catalog id, host wall, world position, and optional `facing`, `label`, `width`, `height` and `sillHeight` — the same fields as a `\"window\"` {@linkcode PluginDesignCreateOpeningOptions}. `sillHeight` is measured from the host wall base to the BOTTOM of the window. Validate-all-or-throw; one command.",
5119
+ "summary": "Place **many windows** into host walls in one undoable operation (bulk plural of {@linkcode window}). Each item carries its own family id, host wall, world position, and optional `facing`, `label`, `width`, `height` and `sillHeight` — the same fields as a `\"window\"` {@linkcode PluginDesignCreateOpeningOptions}. `sillHeight` is measured from the host wall base to the BOTTOM of the window. Validate-all-or-throw; one command.",
4814
5120
  "examplePrompts": [
4815
5121
  "Add windows along this whole facade at once",
4816
5122
  "Place a window in every bedroom in one operation",
4817
5123
  "Bulk add the windows from this schedule",
4818
5124
  "Put four windows on this wall in a single undo step",
4819
- "Add all the windows with a 0.9m sill height together"
5125
+ "Add all the windows with a 0.9m sill height together\n\nA width/height (top-level, or under the family's own keys in `values`) on a family that\ntakes its size from its Type while Custom Sizes is off is never dropped: the Type within\n25 mm is chosen, else Custom Sizes is turned on and that size is built, leaving the Type\nlabel stale. `design.windows.checkPlacement` reports which as `sizing`; `customSizes: \"No\"`\nwith a size no Type matches is refused."
4820
5126
  ],
4821
5127
  "argsType": "PluginCreateWindowItem[]",
4822
5128
  "resultType": "ComponentHandle[]",
4823
- "performance": "Bulk creator — the whole batch is ONE host round-trip and ONE undo entry, and each distinct catalog source loads once. Always prefer this over calling `design.create.window` in a loop: build the full `items[]` array first (all host walls and points up front), then make one call. # Example ```ts const v = snaptrude.core.math.vec3 const [wall] = await snaptrude.design.query.listWalls({ isSelected: true }) const [casement] = await snaptrude.design.windows.listCatalog() const placed = await snaptrude.design.create.windows([ { catalogId: casement.id, hostWall: wall, position: await v.new(2, 0, 5), sillHeight: 0.9 }, { catalogId: casement.id, hostWall: wall, position: await v.new(5, 0, 5), sillHeight: 0.9 }, ]) const currentWall = await snaptrude.design.query.getHost(placed[0]) ```"
5129
+ "performance": "Bulk creator — the whole batch is ONE host round-trip and ONE undo entry. Always prefer this over calling `design.create.window` in a loop: build the full `items[]` array first (all host walls and points up front), then make one call. # Example ```ts const v = snaptrude.core.math.vec3 const [wall] = await snaptrude.design.query.listWalls({ isSelected: true }) const [casement] = await snaptrude.design.windows.listCatalog() const placed = await snaptrude.design.create.windows([ { familyId: casement.id, hostWall: wall, position: await v.new(2, 0, 5), values: { sill: \"900mm\" } }, { familyId: casement.id, hostWall: wall, position: await v.new(5, 0, 5), values: { sill: \"900mm\" } }, ]) const currentWall = await snaptrude.design.query.getHost(placed[0]) ```"
4824
5130
  },
4825
5131
  {
4826
5132
  "path": "design.delete.entities",
@@ -4831,7 +5137,8 @@
4831
5137
  "Remove these walls from the model",
4832
5138
  "Get rid of this mass entirely",
4833
5139
  "Delete all the furniture I picked",
4834
- "Remove the door and window I just created"
5140
+ "Remove the door and window I just created",
5141
+ "Remove the void I cut in this slab"
4835
5142
  ],
4836
5143
  "argsType": "ComponentHandle[]",
4837
5144
  "resultType": "PluginDesignChangeResult",
@@ -4846,7 +5153,7 @@
4846
5153
  "Add a dimension line along this wall",
4847
5154
  "Measure the width of this room and label it on the plan\n\n# Example\n```ts\nconst { design, core } = snaptrude\nconst v = core.math.vec3\nconst curve = core.geom.query.curve\nfor (const wall of await design.query.listWalls({ storeys: [2] })) {\nconst cl = await design.query.geometry.getCenterline(wall)\nif (!cl) continue\nconst a = await curve.getStartPoint(cl)\nconst b = await curve.getEndPoint(cl)\nawait design.dimensions.create(\nawait v.new(a.x, a.y, a.z),\nawait v.new(b.x, b.y, b.z),\nwall,\n)\n}\n```"
4848
5155
  ],
4849
- "argsType": "Vec3Handle",
5156
+ "argsType": "Vec3Input",
4850
5157
  "resultType": "DimensionHandle"
4851
5158
  },
4852
5159
  {
@@ -4913,6 +5220,16 @@
4913
5220
  "resultType": "PluginDimensionsChangeResult",
4914
5221
  "performance": "Array API — one host round-trip and one undo entry for the whole set. # Example ```ts const dims = snaptrude.design.dimensions const hidden = await dims.list({ isHidden: true }) await dims.show(hidden.map((d) => d.id)) ```"
4915
5222
  },
5223
+ {
5224
+ "path": "design.doors.checkPlacement",
5225
+ "namespace": "design.doors",
5226
+ "summary": "Trial the native host fit and geometry rebuild, then restore the model without publishing commands. The first result uses the creation options; subsequent results use candidateWidths in input order. Widths are authored parameters, not physical envelopes or a guaranteed maximum width. No catalog save, history entry or model persistence occurs. Rerun after model edits. A failed fit leaves cut unverified. Family-host cuts are also unverified: their rebuild cannot supply a reversible, authoritative trial result. Material clearance checks evaluated parts against physical neighbours across hosts. The intended host uses native fit/cut checks. Creation repeats clearance immediately before placement; this report grants no later approval. Editing permission is required; locked hosts and an active opening tool reject. It does not say which room the door opens into: read `opensInto` from `design.validation.access()` after placement (see {@linkcode getSwingDirection}). A requested width/height on a family sized by its Type (Custom Sizes off) picks the Type within 25 mm, else turns Custom Sizes on; each result's `sizing` says which. Creation (`design.create.door(s)`) applies the same rule, so the size you pass is the size built.",
5227
+ "examplePrompts": [
5228
+ "Check which door widths fit this host without placing a door"
5229
+ ],
5230
+ "argsType": "PluginOpeningPlacementArgs",
5231
+ "resultType": "PluginOpeningPlacementResult"
5232
+ },
4916
5233
  {
4917
5234
  "path": "design.doors.exists",
4918
5235
  "namespace": "design.doors",
@@ -4942,7 +5259,7 @@
4942
5259
  {
4943
5260
  "path": "design.doors.getDimensions",
4944
5261
  "namespace": "design.doors",
4945
- "summary": "Get a door's full dimensions in one call — width along the wall, vertical height, and thickness through the wall — measured in the door's local oriented space. Parity with {@linkcode PluginDesignWindowsApi.getDimensions}.",
5262
+ "summary": "Get evaluated opening width/height and host thickness, in Snaptrude units. The opening can include allowances beyond the width/height input parameters.",
4946
5263
  "examplePrompts": [
4947
5264
  "What are the dimensions of this door?",
4948
5265
  "How big is the selected door?",
@@ -4981,7 +5298,7 @@
4981
5298
  {
4982
5299
  "path": "design.doors.getSupportFloor",
4983
5300
  "namespace": "design.doors",
4984
- "summary": "Get the floor a door is hosted on — the storey/level component that supports the door.",
5301
+ "summary": "Returns null for recipe doors. Room floors own doorway infill; there is no separate support-floor entity.",
4985
5302
  "examplePrompts": [
4986
5303
  "Which floor is this door on?",
4987
5304
  "What storey does the selected door belong to?",
@@ -4994,12 +5311,13 @@
4994
5311
  {
4995
5312
  "path": "design.doors.getSwingDirection",
4996
5313
  "namespace": "design.doors",
4997
- "summary": "Get a door's swing (hinge) handedness. Stand on the side the door swings open to, facing the door: `'left'` when the hinged jamb is on your left, `'right'` when it is on your right. Derived from the placed mesh's orientation (the same reflection `mirror` and the `hinge` placement option apply), not a persisted field.",
5314
+ "summary": "Read the saved hand parameter. left means local -X; right means local +X. Returns null when the recipe does not declare that control.",
4998
5315
  "examplePrompts": [
4999
5316
  "Which way does this door swing?",
5000
5317
  "Is the selected door left-handed or right-handed?",
5001
5318
  "Check the hinge side of this door",
5002
- "Does the entrance door open to the left or the right?\n\n# Example\n```ts\nconst [door] = await snaptrude.design.query.listDoors({ isSelected: true })\nconst swing = await snaptrude.design.doors.getSwingDirection(door)\nif (swing) console.log(\"opens as a\", swing, \"hand door\")\n```"
5319
+ "Does the entrance door open to the left or the right?",
5320
+ "Which room does this door open into?\n\n# Example\n```ts\nconst [door] = await snaptrude.design.query.listDoors({ isSelected: true })\nconst swing = await snaptrude.design.doors.getSwingDirection(door)\nif (swing) console.log(\"opens as a\", swing, \"hand door\")\n// Which room it opens into\nconst { topology } = await snaptrude.design.validation.access()\nconst side = topology.rooms.flatMap((r) => r.doors).find((d) => d.door === door)?.opensInto\n```"
5003
5321
  ],
5004
5322
  "argsType": "ComponentHandle",
5005
5323
  "resultType": "\"left\" | \"right\" | null"
@@ -5059,7 +5377,7 @@
5059
5377
  {
5060
5378
  "path": "design.doors.mirror",
5061
5379
  "namespace": "design.doors",
5062
- "summary": "Mirror a door about its own axes. `'x'` (default) flips the swing side — the door opens to the other side of the wall; `'z'` swaps the hinged jamb (left-hand ↔ right-hand, same swing side); `'y'` turns it upside down. Undoable as a single command.",
5380
+ "summary": "Change authored controls in one undo step: x (default) toggles swing front/back; z toggles hand left/right. The recipe must declare these enums. Axis y rejects. No mesh reflection occurs. Recipes use outputs.swingSide (1 or -1) for the adjoining room's floor ownership. A door between two room floors requires this output; a missing or invalid report rejects the cut. The same side decides `opensInto` in `design.validation.access()`; read it there to confirm which room the door now opens into.",
5063
5381
  "examplePrompts": [
5064
5382
  "Flip this door's swing",
5065
5383
  "Mirror the selected door",
@@ -5110,6 +5428,46 @@
5110
5428
  "argsType": "ComponentHandle",
5111
5429
  "resultType": "OffsetSplitOutcome"
5112
5430
  },
5431
+ {
5432
+ "path": "design.electrical.context",
5433
+ "namespace": "design.electrical",
5434
+ "summary": "Read the active proposal's architectural context before authoring. Keep one revision across pages.",
5435
+ "examplePrompts": [],
5436
+ "argsType": "z.input<typeof NetworkContextArgs>",
5437
+ "resultType": "NetworkContextResult"
5438
+ },
5439
+ {
5440
+ "path": "design.electrical.create",
5441
+ "namespace": "design.electrical",
5442
+ "summary": "Construct each node on its authored storey, with one undo command and queued project persistence. Networks can span storeys. Reuse requestId on retry; components returns the durable node mapping. Coordinates are world millimetres, Y-up; rotations are degrees. No engineering rules are inferred. Throws for stale architecture, invalid references/geometry or conflicting retries.",
5443
+ "examplePrompts": [],
5444
+ "argsType": "z.input<typeof NetworkCreateArgs>",
5445
+ "resultType": "NetworkCreateResult"
5446
+ },
5447
+ {
5448
+ "path": "design.electrical.get",
5449
+ "namespace": "design.electrical",
5450
+ "summary": "Read one saved graph in the active proposal; null on miss. IDs are durable, not worker handles.",
5451
+ "examplePrompts": [],
5452
+ "argsType": "z.input<typeof NetworkGetArgs>",
5453
+ "resultType": "SavedNetworkGraph | null"
5454
+ },
5455
+ {
5456
+ "path": "design.electrical.query",
5457
+ "namespace": "design.electrical",
5458
+ "summary": "Read bounded authored topology in graph revision. Crossings do not connect. No clash/compliance claims.",
5459
+ "examplePrompts": [],
5460
+ "argsType": "z.input<typeof NetworkQueryArgs>",
5461
+ "resultType": "NetworkQueryResult"
5462
+ },
5463
+ {
5464
+ "path": "design.electrical.validate",
5465
+ "namespace": "design.electrical",
5466
+ "summary": "Validate authored JSON, pinned families and recipe evaluation without placing or saving anything. This does not build kernel meshes or certify engineering/code compliance.",
5467
+ "examplePrompts": [],
5468
+ "argsType": "z.input<typeof NetworkValidateArgs>",
5469
+ "resultType": "NetworkValidation"
5470
+ },
5113
5471
  {
5114
5472
  "path": "design.erase.edge",
5115
5473
  "namespace": "design.erase",
@@ -5140,7 +5498,7 @@
5140
5498
  {
5141
5499
  "path": "design.family.create",
5142
5500
  "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.",
5501
+ "summary": "Save the prepared bundle to the library: the open project's team library in a team project, else the user's personal library. `teamId` picks another team, and `null` the personal library. {@linkcode PluginDesignFamilyApi.place} already saves generic components. Hosted doors/windows save without placement; they need a wall and position through `design.create.door` / `design.create.window`. For a non-hosted family with no placed instance, saving also drops one standalone sample at the current view centre on the active storey (`placed: true`). This includes architectural families: the sample does not assign a family to a native host.",
5144
5502
  "examplePrompts": [
5145
5503
  "Save this as a family called Vertical Louvre",
5146
5504
  "Add it to my library\n\n# Example\n```ts\nconst { definitionId } = await snaptrude.design.family.create({ name: \"Louvre Screen\" })\n```"
@@ -5156,9 +5514,17 @@
5156
5514
  "Delete the Louvre Screen family from my library",
5157
5515
  "Get rid of that bollard and every copy of it in here\n\n# Example\n```ts\nawait snaptrude.design.family.delete({ id: \"revit:steel-bollard~bef047\" })\nawait snaptrude.design.family.delete({ id: \"revit:steel-bollard~bef047\", instances: true })\n```"
5158
5516
  ],
5159
- "argsType": "PluginDesignFamilyDeleteArgs",
5517
+ "argsType": "PluginDesignFamilyDeleteArgs | string",
5160
5518
  "resultType": "PluginFamilyDeleteResult"
5161
5519
  },
5520
+ {
5521
+ "path": "design.family.duplicate",
5522
+ "namespace": "design.family",
5523
+ "summary": "Assign one placed recipe family or parametric door/window to a new project family. Does not copy geometry, publish a library or alter native system families. Subsequent authored parameter edits no longer affect the original family. Undo restores the original family identity and name together. This is project-only. If the user requests both an independent project family and a library asset, call duplicate first, then saveToLibrary with the same component. If they request a library save later, call saveToLibrary alone; do not duplicate again.",
5524
+ "examplePrompts": [],
5525
+ "argsType": "PluginDesignFamilyDuplicateArgs",
5526
+ "resultType": "PluginFamilyDuplicateResult"
5527
+ },
5162
5528
  {
5163
5529
  "path": "design.family.flex",
5164
5530
  "namespace": "design.family",
@@ -5178,9 +5544,28 @@
5178
5544
  "Make the chairs I placed 50 mm wider",
5179
5545
  "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
5546
  ],
5181
- "argsType": "PluginDesignFamilyGetArgs",
5547
+ "argsType": "PluginDesignFamilyGetArgs | string",
5182
5548
  "resultType": "PluginFamilyGetResult | null"
5183
5549
  },
5550
+ {
5551
+ "path": "design.family.getDefinition",
5552
+ "namespace": "design.family",
5553
+ "summary": "Read one saved catalog definition without opening a draft or placing geometry. Both id and version are required. The catalog checks access on every read. Length defaults and bounds are in mm, ready for network graph overrides; angle parameters retain degrees, identified by each parameter's unit. existing getParameters/place/setParameters continue to use Snaptrude units. Defaults include current catalog overrides, which can change without a new version. No scene, registry, history, or persistence state is changed. Missing/inaccessible pins and invalid recipes fail; no latest-version fallback. This metadata does not certify connectors or engineering compatibility.",
5554
+ "examplePrompts": [],
5555
+ "argsType": "PluginDesignFamilyGetDefinitionArgs",
5556
+ "resultType": "PluginFamilyDefinition"
5557
+ },
5558
+ {
5559
+ "path": "design.family.getExamples",
5560
+ "namespace": "design.family",
5561
+ "summary": "Worked example families to learn from: real, tested bundles by discipline (architecture, structure, MEP, furniture). With no `id`, returns the catalogue — each example's id, label, discipline and the patterns it teaches — filtered by `discipline` / `tag` when given. With an `id`, returns that example's full bundle. Fetch the one closest to the task before authoring and borrow its patterns; examples are patterns to combine, not templates to copy whole.",
5562
+ "examplePrompts": [
5563
+ "Show me an example of a structural family",
5564
+ "How is a hosted door family written?\n\n# Example\n```ts\nconst list = await snaptrude.design.family.getExamples({ discipline: \"mep\" })\nconst ex = await snaptrude.design.family.getExamples({ id: list[0].id })\n```"
5565
+ ],
5566
+ "argsType": "PluginDesignFamilyGetExamplesArgs | string",
5567
+ "resultType": "PluginFamilyExampleSummary[] | PluginFamilyExample"
5568
+ },
5184
5569
  {
5185
5570
  "path": "design.family.getParameters",
5186
5571
  "namespace": "design.family",
@@ -5200,19 +5585,43 @@
5200
5585
  "How do I write a parametric family spec?",
5201
5586
  "Show me the family DSL\n\n# Example\n```ts\nconst { reference } = await snaptrude.design.family.getReference()\n```"
5202
5587
  ],
5203
- "argsType": null,
5588
+ "argsType": "PluginDesignFamilyGetReferenceArgs | PluginFamilyReferenceSection",
5204
5589
  "resultType": "PluginFamilyReference"
5205
5590
  },
5591
+ {
5592
+ "path": "design.family.getTemplate",
5593
+ "namespace": "design.family",
5594
+ "summary": "Get a validated starter recipe for a door or window, with an opening contract, host wall-thickness binding and role-named parts. Edit profiles, panels, glazing, hardware and materials while preserving the contract. Use `prepare` / `flex` to check the recipe, then `create` to save it and `design.create.door(familyId, hostWall, position, { version, values }, facing)` (or `window`) to place it. Edit the resulting ParametricComponent through `getParameters` / `setParameters`, not a separate opening API.",
5595
+ "examplePrompts": [],
5596
+ "argsType": "PluginDesignFamilyGetTemplateArgs",
5597
+ "resultType": "PluginFamilyTemplateResult"
5598
+ },
5206
5599
  {
5207
5600
  "path": "design.family.list",
5208
5601
  "namespace": "design.family",
5209
- "summary": "Every parametric family registered in this session — native and imported.",
5602
+ "summary": "Every available parametric family, merging the session registry and catalog.",
5210
5603
  "examplePrompts": [
5211
5604
  "What families are available?\n\n# Example\n```ts\nconst families = await snaptrude.design.family.list()\n```"
5212
5605
  ],
5213
- "argsType": null,
5606
+ "argsType": "PluginDesignFamilyListArgs & { compact: true }",
5607
+ "resultType": "string[]"
5608
+ },
5609
+ {
5610
+ "path": "design.family.list",
5611
+ "namespace": "design.family",
5612
+ "summary": "",
5613
+ "examplePrompts": [],
5614
+ "argsType": "PluginDesignFamilyListArgs & { compact?: false }",
5214
5615
  "resultType": "PluginFamilySummary[]"
5215
5616
  },
5617
+ {
5618
+ "path": "design.family.list",
5619
+ "namespace": "design.family",
5620
+ "summary": "",
5621
+ "examplePrompts": [],
5622
+ "argsType": "PluginDesignFamilyListArgs",
5623
+ "resultType": "PluginFamilySummary[] | string[]"
5624
+ },
5216
5625
  {
5217
5626
  "path": "design.family.place",
5218
5627
  "namespace": "design.family",
@@ -5220,7 +5629,7 @@
5220
5629
  "examplePrompts": [
5221
5630
  "Place it in the model",
5222
5631
  "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```"
5632
+ "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// on the first floor — position.y is an offset above storey 2's base\nawait snaptrude.design.family.place({ id: familyId, position: { x: 4, y: 0, z: 0 }, storey: 2 })\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```"
5224
5633
  ],
5225
5634
  "argsType": "PluginDesignFamilyPlaceArgs",
5226
5635
  "resultType": "PluginFamilyPlaceResult"
@@ -5228,19 +5637,28 @@
5228
5637
  {
5229
5638
  "path": "design.family.prepare",
5230
5639
  "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.",
5640
+ "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
5641
  "examplePrompts": [
5233
5642
  "Make a louvre screen with adjustable blade count and tilt",
5234
5643
  "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```"
5644
+ "Create a railing family I can adjust",
5645
+ "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
5646
  ],
5237
5647
  "argsType": "PluginDesignFamilyPrepareArgs",
5238
5648
  "resultType": "PluginFamilyPrepareResult"
5239
5649
  },
5650
+ {
5651
+ "path": "design.family.saveToLibrary",
5652
+ "namespace": "design.family",
5653
+ "summary": "Save a placed recipe family or parametric door/window's current authored configuration as a separate library asset. Does not place geometry, reassign any model component, overwrite the original library asset or publish publicly.",
5654
+ "examplePrompts": [],
5655
+ "argsType": "PluginDesignFamilySaveToLibraryArgs",
5656
+ "resultType": "PluginFamilySaveToLibraryResult"
5657
+ },
5240
5658
  {
5241
5659
  "path": "design.family.setParameters",
5242
5660
  "namespace": "design.family",
5243
- "summary": "Change a placed component's parameters and rebuild its geometry in place.",
5661
+ "summary": "Change authored parameters across the selected component's project family. Use `duplicate({ component, name })` first for an independent project variant. This never publishes or modifies a library. Only changed authored values propagate; placement, hosting, links and relations remain local. A locked family member or host refuses a shared edit. Mixed requests containing links, relations and parameters are not one atomic transaction.",
5244
5662
  "examplePrompts": [
5245
5663
  "Make that table seat 8",
5246
5664
  "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```"
@@ -5654,6 +6072,86 @@
5654
6072
  "argsType": "ComponentHandle",
5655
6073
  "resultType": "PluginMaterialResult"
5656
6074
  },
6075
+ {
6076
+ "path": "design.mechanical.context",
6077
+ "namespace": "design.mechanical",
6078
+ "summary": "Read the active proposal's architectural context before authoring. Keep one revision across pages.",
6079
+ "examplePrompts": [],
6080
+ "argsType": "z.input<typeof NetworkContextArgs>",
6081
+ "resultType": "NetworkContextResult"
6082
+ },
6083
+ {
6084
+ "path": "design.mechanical.create",
6085
+ "namespace": "design.mechanical",
6086
+ "summary": "Construct each node on its authored storey, with one undo command and queued project persistence. Networks can span storeys. Reuse requestId on retry; components returns the durable node mapping. Coordinates are world millimetres, Y-up; rotations are degrees. No engineering rules are inferred. Throws for stale architecture, invalid references/geometry or conflicting retries.",
6087
+ "examplePrompts": [],
6088
+ "argsType": "z.input<typeof NetworkCreateArgs>",
6089
+ "resultType": "NetworkCreateResult"
6090
+ },
6091
+ {
6092
+ "path": "design.mechanical.get",
6093
+ "namespace": "design.mechanical",
6094
+ "summary": "Read one saved graph in the active proposal; null on miss. IDs are durable, not worker handles.",
6095
+ "examplePrompts": [],
6096
+ "argsType": "z.input<typeof NetworkGetArgs>",
6097
+ "resultType": "SavedNetworkGraph | null"
6098
+ },
6099
+ {
6100
+ "path": "design.mechanical.query",
6101
+ "namespace": "design.mechanical",
6102
+ "summary": "Read bounded authored topology in graph revision. Crossings do not connect. No clash/compliance claims.",
6103
+ "examplePrompts": [],
6104
+ "argsType": "z.input<typeof NetworkQueryArgs>",
6105
+ "resultType": "NetworkQueryResult"
6106
+ },
6107
+ {
6108
+ "path": "design.mechanical.validate",
6109
+ "namespace": "design.mechanical",
6110
+ "summary": "Validate authored JSON, pinned families and recipe evaluation without placing or saving anything. This does not build kernel meshes or certify engineering/code compliance.",
6111
+ "examplePrompts": [],
6112
+ "argsType": "z.input<typeof NetworkValidateArgs>",
6113
+ "resultType": "NetworkValidation"
6114
+ },
6115
+ {
6116
+ "path": "design.plumbing.context",
6117
+ "namespace": "design.plumbing",
6118
+ "summary": "Read the active proposal's architectural context before authoring. Keep one revision across pages.",
6119
+ "examplePrompts": [],
6120
+ "argsType": "z.input<typeof NetworkContextArgs>",
6121
+ "resultType": "NetworkContextResult"
6122
+ },
6123
+ {
6124
+ "path": "design.plumbing.create",
6125
+ "namespace": "design.plumbing",
6126
+ "summary": "Construct each node on its authored storey, with one undo command and queued project persistence. Networks can span storeys. Reuse requestId on retry; components returns the durable node mapping. Coordinates are world millimetres, Y-up; rotations are degrees. No engineering rules are inferred. Throws for stale architecture, invalid references/geometry or conflicting retries.",
6127
+ "examplePrompts": [],
6128
+ "argsType": "z.input<typeof NetworkCreateArgs>",
6129
+ "resultType": "NetworkCreateResult"
6130
+ },
6131
+ {
6132
+ "path": "design.plumbing.get",
6133
+ "namespace": "design.plumbing",
6134
+ "summary": "Read one saved graph in the active proposal; null on miss. IDs are durable, not worker handles.",
6135
+ "examplePrompts": [],
6136
+ "argsType": "z.input<typeof NetworkGetArgs>",
6137
+ "resultType": "SavedNetworkGraph | null"
6138
+ },
6139
+ {
6140
+ "path": "design.plumbing.query",
6141
+ "namespace": "design.plumbing",
6142
+ "summary": "Read bounded authored topology in graph revision. Crossings do not connect. No clash/compliance claims.",
6143
+ "examplePrompts": [],
6144
+ "argsType": "z.input<typeof NetworkQueryArgs>",
6145
+ "resultType": "NetworkQueryResult"
6146
+ },
6147
+ {
6148
+ "path": "design.plumbing.validate",
6149
+ "namespace": "design.plumbing",
6150
+ "summary": "Validate authored JSON, pinned families and recipe evaluation without placing or saving anything. This does not build kernel meshes or certify engineering/code compliance.",
6151
+ "examplePrompts": [],
6152
+ "argsType": "z.input<typeof NetworkValidateArgs>",
6153
+ "resultType": "NetworkValidation"
6154
+ },
5657
6155
  {
5658
6156
  "path": "design.query.exists",
5659
6157
  "namespace": "design.query",
@@ -5712,7 +6210,7 @@
5712
6210
  {
5713
6211
  "path": "design.query.geometry.getTriangulatedMeshes",
5714
6212
  "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.",
6213
+ "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
6214
  "examplePrompts": [
5717
6215
  "Export the selected masses as a triangle mesh",
5718
6216
  "Get the raw vertices and triangles of this wall in world coordinates",
@@ -5776,12 +6274,13 @@
5776
6274
  {
5777
6275
  "path": "design.query.getHost",
5778
6276
  "namespace": "design.query",
5779
- "summary": "Get an entity's host/parent entity (e.g. the wall a door is cut into).",
6277
+ "summary": "Get an entity's host/parent entity (e.g. the wall a door is cut into, or the slab, floor, roof or ceiling a void is cut through).",
5780
6278
  "examplePrompts": [
5781
6279
  "Which wall is this door in?",
5782
6280
  "Find the parent of the selected window",
5783
6281
  "What is this element attached to?",
5784
- "Which wall hosts this window?\n\n# Example\n```ts\n// From a door up to its host wall, then measure the wall\nconst [door] = await snaptrude.design.query.listDoors({ isSelected: true })\nconst hostWall = door ? await snaptrude.design.query.getHost(door) : null\nif (hostWall) {\nconst m = await snaptrude.design.query.measure(hostWall)\nconsole.log(\"wall length:\", m?.length)\n}\n```"
6282
+ "Which wall hosts this window?",
6283
+ "Which slab is this shaft in?\n\n# Example\n```ts\n// From a door up to its host wall, then measure the wall\nconst [door] = await snaptrude.design.query.listDoors({ isSelected: true })\nconst hostWall = door ? await snaptrude.design.query.getHost(door) : null\nif (hostWall) {\nconst m = await snaptrude.design.query.measure(hostWall)\nconsole.log(\"wall length:\", m?.length)\n}\n```"
5785
6284
  ],
5786
6285
  "argsType": "ComponentHandle",
5787
6286
  "resultType": "ComponentHandle | null"
@@ -5895,12 +6394,13 @@
5895
6394
  {
5896
6395
  "path": "design.query.listChildren",
5897
6396
  "namespace": "design.query",
5898
- "summary": "List an entity's direct child entities — for example the doors and windows hosted in a wall.",
6397
+ "summary": "List an entity's direct child entities — for example the doors and windows hosted in a wall, or the voids (holes, shafts, openings) cut in a slab, floor, roof or ceiling by `design.create.void`.",
5899
6398
  "examplePrompts": [
5900
6399
  "What's inside this wall — list its doors and windows",
5901
6400
  "Show the child elements of the selected object",
5902
6401
  "Which openings belong to this wall?",
5903
- "List everything nested under this mass\n\n# Example\n```ts\n// Count the openings hosted in a wall, split by kind\nconst children = await snaptrude.design.query.listChildren(wall)\nlet doors = 0\nlet windows = 0\nfor (const child of children) {\nconst kind = await snaptrude.design.query.getEntityType(child)\nif (kind === \"door\") doors++\nif (kind === \"window\") windows++\n}\n```"
6402
+ "List everything nested under this mass",
6403
+ "List all holes in this slab\n\n# Example\n```ts\n// Count the openings hosted in a wall, split by kind\nconst children = await snaptrude.design.query.listChildren(wall)\nlet doors = 0\nlet windows = 0\nfor (const child of children) {\nconst kind = await snaptrude.design.query.getEntityType(child)\nif (kind === \"door\") doors++\nif (kind === \"window\") windows++\n}\n```"
5904
6404
  ],
5905
6405
  "argsType": "ComponentHandle",
5906
6406
  "resultType": "ComponentHandle[]"
@@ -6076,6 +6576,14 @@
6076
6576
  "argsType": "PluginEntityFilter",
6077
6577
  "resultType": "ComponentHandle[]"
6078
6578
  },
6579
+ {
6580
+ "path": "design.query.listVoids",
6581
+ "namespace": "design.query",
6582
+ "summary": "List Void components, excluding masses and spaces. Voids cut their hosts. Use family queries for authored parametric families with a cut relationship.",
6583
+ "examplePrompts": [],
6584
+ "argsType": "PluginEntityFilter",
6585
+ "resultType": "ComponentHandle[]"
6586
+ },
6079
6587
  {
6080
6588
  "path": "design.query.listWalls",
6081
6589
  "namespace": "design.query",
@@ -6266,6 +6774,66 @@
6266
6774
  "argsType": "PluginSelectionFilter",
6267
6775
  "resultType": "PluginDesignChangeResult"
6268
6776
  },
6777
+ {
6778
+ "path": "design.text.create",
6779
+ "namespace": "design.text",
6780
+ "summary": "Place a text, or an annotation when `options.arrowhead` is given.",
6781
+ "examplePrompts": [
6782
+ "Label the living room \"LIVING\" on the plan",
6783
+ "Add an annotation pointing at this column saying \"RCC 300x300\"",
6784
+ "Write \"NORTH ENTRY\" in red bold text at the entrance on level 2\n\n# Example\n```ts\nconst { design, core } = snaptrude\nconst v = core.math.vec3\n// A plain label on storey 1.\nawait design.text.create(await v.new(10, 0, 20), \"LIVING\", { storey: 1 })\n// An annotation whose leader points at a wall corner.\nawait design.text.create(await v.new(30, 0, 40), \"Shear wall\", {\narrowhead: await v.new(22, 0, 32),\ncolor: \"#c62828\",\nfontStyle: \"Bold\",\n})\n```"
6785
+ ],
6786
+ "argsType": "Vec3Handle",
6787
+ "resultType": "ComponentHandle"
6788
+ },
6789
+ {
6790
+ "path": "design.text.get",
6791
+ "namespace": "design.text",
6792
+ "summary": "Read one text by handle.",
6793
+ "examplePrompts": [
6794
+ "What does this text say?",
6795
+ "Read the annotation I just placed\n\n# Example\n```ts\nconst t = await snaptrude.design.text.get(handle)\nif (t?.leader) console.log(t.content, \"points at\", t.leader.arrowhead)\n```"
6796
+ ],
6797
+ "argsType": "ComponentHandle",
6798
+ "resultType": "PluginText | null"
6799
+ },
6800
+ {
6801
+ "path": "design.text.list",
6802
+ "namespace": "design.text",
6803
+ "summary": "List the texts and annotations of every building in the active design option as full records (each carries its `buildingId`).",
6804
+ "examplePrompts": [
6805
+ "List all the text labels on level 1",
6806
+ "Which annotations are in this model?",
6807
+ "Find the text that says \"LOBBY\""
6808
+ ],
6809
+ "argsType": "{\n storeys?: number[]\n kind?: PluginTextKind\n isHidden?: boolean\n }",
6810
+ "resultType": "PluginText[]",
6811
+ "performance": "Array read — one host round-trip returns every record. Filter in one call rather than calling `get` per text. # Example ```ts const texts = await snaptrude.design.text.list({ storeys: [1] }) const lobby = texts.find((t) => t.content === \"LOBBY\") ```"
6812
+ },
6813
+ {
6814
+ "path": "design.text.setLeader",
6815
+ "namespace": "design.text",
6816
+ "summary": "Point a text's leader at a new place — the same edit as dragging an annotation's arrowhead with the Edit tool. Passing `null` removes the leader (the annotation becomes a plain text); giving a plain text an arrowhead turns it into an annotation. The text itself does not move. To move the text while its arrowhead stays put, move it with `design.transform.move`, then call this with the old `leader.arrowhead`.",
6817
+ "examplePrompts": [
6818
+ "Point this annotation at the door instead",
6819
+ "Remove the leader from this label\n\n# Example\n```ts\nconst { design, core } = snaptrude\nawait design.text.setLeader(handle, await core.math.vec3.new(12, 0, 8))\n```"
6820
+ ],
6821
+ "argsType": "ComponentHandle",
6822
+ "resultType": "PluginText"
6823
+ },
6824
+ {
6825
+ "path": "design.text.update",
6826
+ "namespace": "design.text",
6827
+ "summary": "Change the words, colour or font of one or more texts — the same edit as the Properties panel. Fields left out keep each text's own value. A font style the (new) family does not have falls back to `\"Regular\"`, as in the panel. One undo entry for the whole batch.",
6828
+ "examplePrompts": [
6829
+ "Make all the room labels bold",
6830
+ "Change this annotation to read \"Fire exit\"",
6831
+ "Use Roboto in blue for every text on level 2"
6832
+ ],
6833
+ "argsType": "ComponentHandle[]",
6834
+ "resultType": "PluginTextChangeResult",
6835
+ "performance": "Array API — pass the whole set in one call (one host round-trip, one undo entry). Never loop this per text. # Example ```ts const text = snaptrude.design.text const level2 = await text.list({ storeys: [2] }) await text.update(level2.map((t) => t.id), { fontFamily: \"Roboto\", color: \"#1565c0\" }) ```"
6836
+ },
6269
6837
  {
6270
6838
  "path": "design.transform.align",
6271
6839
  "namespace": "design.transform",
@@ -6329,14 +6897,14 @@
6329
6897
  {
6330
6898
  "path": "design.types.get",
6331
6899
  "namespace": "design.types",
6332
- "summary": "Get one building type by its `\"<kind>:<name>\"` id — its labelled layer stack (each layer's role, thickness and material) plus the summed total thickness.",
6900
+ "summary": "Get one building type by its legacy id or exact native wall pin — its labelled layer stack (each layer's role, thickness and material) plus the summed total thickness.",
6333
6901
  "examplePrompts": [
6334
6902
  "What layers make up this wall type?",
6335
6903
  "Show the construction of the exposed-concrete wall",
6336
6904
  "What's the total thickness of this slab type?",
6337
- "Which materials are in this roof assembly?\n\n# Example\n```ts\nconst [first] = await snaptrude.design.types.list(\"wall\")\nconst type = await snaptrude.design.types.get(first.id)\nif (type) for (const layer of type.layers) console.log(layer.name, layer.thickness)\n```"
6905
+ "Which materials are in this roof assembly?\n\n# Example\n```ts\nconst [first] = await snaptrude.design.types.list(\"wall\")\nconst type = await snaptrude.design.types.get(first.nativeFamily ?? first.id)\nif (type) for (const layer of type.layers) console.log(layer.name, layer.thickness)\n```"
6338
6906
  ],
6339
- "argsType": "string",
6907
+ "argsType": "PluginWallType",
6340
6908
  "resultType": "PluginBuildingType | null"
6341
6909
  },
6342
6910
  {
@@ -6378,6 +6946,14 @@
6378
6946
  "argsType": "ComponentHandle[]",
6379
6947
  "resultType": "PluginDesignChangeResult"
6380
6948
  },
6949
+ {
6950
+ "path": "design.update.beam",
6951
+ "namespace": "design.update",
6952
+ "summary": "Replace a native beam family while preserving its authored axis. Values use mm/degrees.",
6953
+ "examplePrompts": [],
6954
+ "argsType": "ComponentHandle",
6955
+ "resultType": "PluginDesignChangeResult"
6956
+ },
6381
6957
  {
6382
6958
  "path": "design.update.buildableEnvelope",
6383
6959
  "namespace": "design.update",
@@ -6403,6 +6979,14 @@
6403
6979
  "argsType": "ComponentHandle",
6404
6980
  "resultType": "PluginDesignChangeResult"
6405
6981
  },
6982
+ {
6983
+ "path": "design.update.column",
6984
+ "namespace": "design.update",
6985
+ "summary": "Replace a native column family while preserving its authored axis. Values use mm/degrees.",
6986
+ "examplePrompts": [],
6987
+ "argsType": "ComponentHandle",
6988
+ "resultType": "PluginDesignChangeResult"
6989
+ },
6406
6990
  {
6407
6991
  "path": "design.update.floor",
6408
6992
  "namespace": "design.update",
@@ -6440,6 +7024,14 @@
6440
7024
  "argsType": "ComponentHandle",
6441
7025
  "resultType": "MassParametersRecord"
6442
7026
  },
7027
+ {
7028
+ "path": "design.update.referenceLine",
7029
+ "namespace": "design.update",
7030
+ "summary": "Apply a project style through the native reference-line property command. One saved undo step; no selection or tool preference changes. Saves the legacy thickness/mode mirror too. Requires Paper Pens and an editable line in the current proposal. A no-op creates no history entry.",
7031
+ "examplePrompts": [],
7032
+ "argsType": "string",
7033
+ "resultType": "PluginDesignUpdateReferenceLineResult"
7034
+ },
6443
7035
  {
6444
7036
  "path": "design.update.roof",
6445
7037
  "namespace": "design.update",
@@ -6492,6 +7084,21 @@
6492
7084
  "argsType": "ComponentHandle",
6493
7085
  "resultType": "boolean"
6494
7086
  },
7087
+ {
7088
+ "path": "design.update.setObjectType",
7089
+ "namespace": "design.update",
7090
+ "summary": "Change the **object type** of one or more masses — turn a mass into a real wall, roof, slab or floor: the properties panel's Object Type dropdown, headless. The geometry is kept exactly as it is (a wedge stays a wedge; nothing is flattened to a default thickness), so a sloped mass built with `design.create.massFromBrep` becomes a sloped roof or a raked wall. The component keeps its handle, label and storey; it gains the target type's construction layers and properties, and the type's default material when the mass still wore the default. One undoable operation for the whole list; undo turns them back into masses.",
7091
+ "examplePrompts": [
7092
+ "Convert this mass into a wall",
7093
+ "Turn the sloped mass into a roof",
7094
+ "Make these masses real slabs",
7095
+ "Change the object type of the selection to wall",
7096
+ "Convert the wedge I built into a raked wall",
7097
+ "Turn this mass into a floor\n\n# Example\n```ts\nconst brep = await snaptrude.core.geom.create.brepFromFaces(wedgeFaces)\nconst wedge = await snaptrude.design.create.massFromBrep(brep, \"Gable end\")\nconst { affected } = await snaptrude.design.update.setObjectType([wedge], \"wall\")\nconsole.log(await snaptrude.design.query.getEntityType(affected[0])) // \"wall\"\n```"
7098
+ ],
7099
+ "argsType": "ComponentHandle[]",
7100
+ "resultType": "PluginDesignChangeResult"
7101
+ },
6495
7102
  {
6496
7103
  "path": "design.update.setStorey",
6497
7104
  "namespace": "design.update",
@@ -6505,6 +7112,18 @@
6505
7112
  "argsType": "ComponentHandle[]",
6506
7113
  "resultType": "PluginDesignChangeResult"
6507
7114
  },
7115
+ {
7116
+ "path": "design.update.siteEdge",
7117
+ "namespace": "design.update",
7118
+ "summary": "Change one site edge's type (front / side / rear) and/or give that edge its own setback distance, the same as editing a setback pill on the canvas. One undo step; the pills and Site Analysis sheet refresh.",
7119
+ "examplePrompts": [
7120
+ "Make edge 2 the front",
7121
+ "Set just the north edge setback to 8 m",
7122
+ "Remove the custom setback on edge 3\n\n# Example\n```ts\nconst [{ siteId }] = (await snaptrude.program.site.listEdges()).sites\nawait snaptrude.design.update.siteEdge(siteId, 2, { role: \"front\" })\nawait snaptrude.design.update.siteEdge(siteId, 3, { setback: 8, lengthUnit: \"m\" })\nawait snaptrude.design.update.siteEdge(siteId, 3, { setback: null })\n```"
7123
+ ],
7124
+ "argsType": "string",
7125
+ "resultType": "PluginDesignUpdateSiteEdgeResult"
7126
+ },
6508
7127
  {
6509
7128
  "path": "design.update.slab",
6510
7129
  "namespace": "design.update",
@@ -6626,6 +7245,16 @@
6626
7245
  "argsType": null,
6627
7246
  "resultType": "PluginDesignChangeResult"
6628
7247
  },
7248
+ {
7249
+ "path": "design.windows.checkPlacement",
7250
+ "namespace": "design.windows",
7251
+ "summary": "Trial the native host fit and geometry rebuild, then restore the model without publishing commands. The first result uses the creation options; subsequent results use candidateWidths in input order. Widths are authored parameters, not physical envelopes or a guaranteed maximum width. No catalog save, history entry or model persistence occurs. Rerun after model edits. A failed fit leaves cut unverified. Family-host cuts are also unverified: their rebuild cannot supply a reversible, authoritative trial result. Material clearance checks evaluated parts against physical neighbours across hosts. The intended host uses native fit/cut checks. Creation repeats clearance immediately before placement; this report grants no later approval. Editing permission is required; locked hosts and an active opening tool reject. A requested width/height on a family sized by its Type (Custom Sizes off) picks the Type within 25 mm, else turns Custom Sizes on; each result's `sizing` says which. Creation (`design.create.window(s)`) applies the same rule, so the size you pass is the size built.",
7252
+ "examplePrompts": [
7253
+ "Check whether this window fits and its host can be cut"
7254
+ ],
7255
+ "argsType": "PluginOpeningPlacementArgs",
7256
+ "resultType": "PluginOpeningPlacementResult"
7257
+ },
6629
7258
  {
6630
7259
  "path": "design.windows.exists",
6631
7260
  "namespace": "design.windows",
@@ -6655,7 +7284,7 @@
6655
7284
  {
6656
7285
  "path": "design.windows.getDimensions",
6657
7286
  "namespace": "design.windows",
6658
- "summary": "Get a window's full dimensions in one call — width along the wall, vertical height, and thickness through the wall — measured in the window's local oriented space.",
7287
+ "summary": "Get evaluated opening width/height and host thickness in Snaptrude units. The opening can differ from the width/height input parameters.",
6659
7288
  "examplePrompts": [
6660
7289
  "What are the dimensions of this window?",
6661
7290
  "How big is the selected window?",
@@ -7161,7 +7790,7 @@
7161
7790
  "Add a red arrow from the title to the plan view",
7162
7791
  "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
7792
  ],
7164
- "argsType": "string",
7793
+ "argsType": "string | undefined",
7165
7794
  "resultType": "PluginPresentationAnnotateResult"
7166
7795
  },
7167
7796
  {
@@ -7173,7 +7802,7 @@
7173
7802
  "Put a yellow note on the cover sheet",
7174
7803
  "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
7804
  ],
7176
- "argsType": "string",
7805
+ "argsType": "string | undefined",
7177
7806
  "resultType": "PluginPresentationAnnotateResult"
7178
7807
  },
7179
7808
  {
@@ -7185,7 +7814,7 @@
7185
7814
  "Add a red rectangle highlight to the sheet",
7186
7815
  "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
7816
  ],
7188
- "argsType": "string",
7817
+ "argsType": "string | undefined",
7189
7818
  "resultType": "PluginPresentationAnnotateResult"
7190
7819
  },
7191
7820
  {
@@ -7198,9 +7827,73 @@
7198
7827
  "Caption this view with \"Proposed layout\"",
7199
7828
  "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
7829
  ],
7201
- "argsType": "string",
7830
+ "argsType": "string | undefined",
7202
7831
  "resultType": "PluginPresentationAnnotateTextResult"
7203
7832
  },
7833
+ {
7834
+ "path": "presentation.camera.capture",
7835
+ "namespace": "presentation.camera",
7836
+ "summary": "Screenshot what the Present canvas currently shows.",
7837
+ "examplePrompts": [
7838
+ "Take a screenshot of the Present canvas",
7839
+ "Capture what I am looking at in Present mode",
7840
+ "Screenshot the presentation canvas as a JPEG",
7841
+ "Grab a high-res image of the current sheet view\n\n# Example\n```ts\nconst { image, width, height } = await snaptrude.presentation.camera.capture({\nformat: \"png\",\npixelRatio: 2,\n})\n```"
7842
+ ],
7843
+ "argsType": "{\n format?: PluginPresentationImageFormat\n pixelRatio?: number\n }",
7844
+ "resultType": "PluginPresentationCameraCaptureResult"
7845
+ },
7846
+ {
7847
+ "path": "presentation.camera.get",
7848
+ "namespace": "presentation.camera",
7849
+ "summary": "Read the Present camera: pan, zoom, the visible page-space rectangle and the sheet under the centre of the view.",
7850
+ "examplePrompts": [
7851
+ "Where is the Present canvas camera?",
7852
+ "What zoom level is Present mode at?",
7853
+ "Which sheet is on screen right now?",
7854
+ "Read the visible area of the presentation canvas\n\n# Example\n```ts\nconst { zoom, sheetId } = await snaptrude.presentation.camera.get()\n```"
7855
+ ],
7856
+ "argsType": null,
7857
+ "resultType": "PluginPresentationCameraState"
7858
+ },
7859
+ {
7860
+ "path": "presentation.camera.set",
7861
+ "namespace": "presentation.camera",
7862
+ "summary": "Pan and/or zoom the Present canvas. Fields left out keep their current value, so `{ zoom: 2 }` changes only the zoom (the page point at the viewport's top-left corner stays put) and `{ x, y }` pans at the current zoom. `zoom` is clamped to the editor's range (1% to 1000%); the requested `x`/`y` are always honoured. Immediate (no animation). View-state only — not undoable.",
7863
+ "examplePrompts": [
7864
+ "Zoom the Present canvas to 50%",
7865
+ "Pan the presentation canvas to the right",
7866
+ "Restore the Present camera I saved earlier",
7867
+ "Set the Present zoom to 200% without moving\n\n# Example\n```ts\nconst saved = await snaptrude.presentation.camera.get()\nawait snaptrude.presentation.camera.set({ zoom: 0.5 })\n// …later, restore the exact framing\nawait snaptrude.presentation.camera.set({ x: saved.x, y: saved.y, zoom: saved.zoom })\n```"
7868
+ ],
7869
+ "argsType": "{\n x?: number\n y?: number\n zoom?: number\n }",
7870
+ "resultType": "PluginPresentationCameraState"
7871
+ },
7872
+ {
7873
+ "path": "presentation.camera.zoomTo",
7874
+ "namespace": "presentation.camera",
7875
+ "summary": "Frame one shape on the Present canvas: a sheet, a placed view, a keyed shape or an annotation — any id the other `presentation` namespaces hand out. Pair with {@linkcode PluginPresentationCameraApi.capture} to screenshot a single sheet or placed view as it appears on screen. Immediate, unlike `sheets.setActive`, which animates its move over half a second. View-state only — not undoable.",
7876
+ "examplePrompts": [
7877
+ "Zoom in on sheet 2 in Present mode",
7878
+ "Frame this placed view on the Present canvas",
7879
+ "Go to the cover sheet and screenshot it",
7880
+ "Centre the presentation view on that table\n\n# Example\n```ts\nconst { sheets } = await snaptrude.presentation.sheets.list()\nawait snaptrude.presentation.camera.zoomTo(sheets[1].id)\nconst { image } = await snaptrude.presentation.camera.capture()\n```"
7881
+ ],
7882
+ "argsType": "string",
7883
+ "resultType": "PluginPresentationCameraState"
7884
+ },
7885
+ {
7886
+ "path": "presentation.camera.zoomToFit",
7887
+ "namespace": "presentation.camera",
7888
+ "summary": "Frame everything on the Present canvas — every sheet and loose shape — the Present-mode \"zoom to fit\". View-state only — not undoable.",
7889
+ "examplePrompts": [
7890
+ "Zoom out to see all the sheets",
7891
+ "Fit the whole presentation in the Present view",
7892
+ "Zoom to fit in Present mode\n\n# Example\n```ts\nawait snaptrude.presentation.camera.zoomToFit()\n```"
7893
+ ],
7894
+ "argsType": null,
7895
+ "resultType": "PluginPresentationCameraState"
7896
+ },
7204
7897
  {
7205
7898
  "path": "presentation.diagrams.generateProgram",
7206
7899
  "namespace": "presentation.diagrams",
@@ -7237,20 +7930,413 @@
7237
7930
  "argsType": "string",
7238
7931
  "resultType": "PluginPresentationDiagramsPlaceResult"
7239
7932
  },
7933
+ {
7934
+ "path": "presentation.drawings.applyPreset",
7935
+ "namespace": "presentation.drawings",
7936
+ "summary": "Bind a native kernel plan to a view preset and clear its declared overrides, so it takes the preset's styles. Local title, proposal and building text is kept. One Present undo step. Presets apply only to drawings whose `getCapabilities(drawingId).presets` is `true`.",
7937
+ "examplePrompts": [
7938
+ "Apply the presentation plan preset to this drawing",
7939
+ "Use the Planning Issue style on every plan drawing",
7940
+ "Make this drawing look like the other plans\n\n# Example\n```ts\nconst presets = await snaptrude.presentation.viewPresets.list()\nconst preset = presets.find((each) => each.name === \"Planning Issue\")\nif (preset) await snaptrude.presentation.drawings.applyPreset(drawing.id, preset.id)\n```"
7941
+ ],
7942
+ "argsType": "string",
7943
+ "resultType": "PluginDrawing"
7944
+ },
7945
+ {
7946
+ "path": "presentation.drawings.copy",
7947
+ "namespace": "presentation.drawings",
7948
+ "summary": "Duplicate a drawing with its labels and annotations (native duplication, offset slightly from the original). One Present undo step. The copy keeps the original's source view, scale, crop, styles and preset binding.",
7949
+ "examplePrompts": [
7950
+ "Duplicate this drawing",
7951
+ "Make a copy of the ground floor plan drawing",
7952
+ "Copy this drawing so I can try a different style\n\n# Example\n```ts\nconst copy = await snaptrude.presentation.drawings.copy(drawing.id)\nawait snaptrude.presentation.drawings.rename(copy.id, \"Ground Floor (option B)\")\n```"
7953
+ ],
7954
+ "argsType": "string",
7955
+ "resultType": "PluginDrawing"
7956
+ },
7957
+ {
7958
+ "path": "presentation.drawings.create",
7959
+ "namespace": "presentation.drawings",
7960
+ "summary": "Generate a drawing from a saved view. Creates the drawing beside its source view on the canvas, never on a sheet; to put a drawing on a sheet use `presentation.sheets.place(sheetId, viewId)` (fits it to the sheet) or `presentation.sheets.placeDrawing(sheetId, drawingId)` (moves this drawing onto the sheet, scaled down to fit). Omit `position` unless the user asked for a specific place: the host puts the new drawing next to its source view, clear of other drawings. An explicit `position` (page coordinates, the drawing's centre) is kept and can land on top of an existing drawing. The user's selection is left as it was. Changes the drawing, not the Present camera. The Present view may follow the drawing as a courtesy to a user watching (never zooming in, not while the camera is locked); to look at it, call `presentation.camera.zoomTo(drawing.id)` then `presentation.camera.capture()`. `scale` must be a standard architectural scale denominator (100 means 1:100) in the project's unit system; any other value is rejected. Metric: 10, 20, 50, 100, 150, 200, 250, 500, 1000. Imperial detail: 12, 16, 24, 32, 48, 64, 96, 128, 192, 384, 768, 1536 (1\" = 1' to 1/128\" = 1'; 48 is 1/4\" = 1', 96 is 1/8\" = 1'). Imperial site: 120, 240, 360, 480, 600, 1200, 2400, 3600, 4800 (1\" = 10' to 1\" = 400'). Pick the LARGEST standard scale (smallest denominator) at which the view still fits its frame or sheet and its room labels stay readable: for a building floor plan that is usually 1:50 to 1:200 (1/4\" = 1' to 1/16\" = 1'). Never use a site-sized scale (1:500 and up, 1/64\" = 1' (768) and smaller, 1\" = 20' (240) and up) for a single building plan unless the user asked for it. `scale` applies to plans, site plans and elevations; leave it out for a 3D view. After creating, read `labels.getLayout(drawingId)`: when labels are unplaced and carry a `suggestedScale`, move to that larger scale with `setScale`. To apply a preset, create first and then call `applyPreset`.",
7961
+ "examplePrompts": [
7962
+ "Create a drawing from the ground floor view",
7963
+ "Make a 1:100 plan drawing of level 2",
7964
+ "Draw the north elevation at 1/8\" = 1'",
7965
+ "Put the site plan view on the Present canvas as a drawing\n\n# Example\n```ts\nconst { views } = await snaptrude.presentation.views.list()\nconst plan = views.find((view) => view.kind === \"plan\")!\nconst drawing = await snaptrude.presentation.drawings.create(plan.id, { scale: 100 })\nawait snaptrude.presentation.camera.zoomTo(drawing.id)\nconst { image } = await snaptrude.presentation.camera.capture()\n// To put it on a sheet: await snaptrude.presentation.sheets.placeDrawing(sheet.id, drawing.id)\n```"
7966
+ ],
7967
+ "argsType": "string",
7968
+ "resultType": "PluginDrawing"
7969
+ },
7970
+ {
7971
+ "path": "presentation.drawings.delete",
7972
+ "namespace": "presentation.drawings",
7973
+ "summary": "Delete a drawing with its labels and annotations, as one Present undo step. The source saved view is not deleted.",
7974
+ "examplePrompts": [
7975
+ "Delete this drawing",
7976
+ "Remove the old elevation drawing from the canvas",
7977
+ "Get rid of the duplicate plan drawing\n\n# Example\n```ts\nawait snaptrude.presentation.drawings.delete(drawing.id)\n```"
7978
+ ],
7979
+ "argsType": "string",
7980
+ "resultType": "void"
7981
+ },
7982
+ {
7983
+ "path": "presentation.drawings.export",
7984
+ "namespace": "presentation.drawings",
7985
+ "summary": "Export one drawing, with its labels and annotations, without a sheet. PDF size follows the drawing's canvas bounds; SVG and PDF keep vector content, raster fallbacks stay raster. `resolution` applies to raster formats. It does not wait for the canvas to paint, so it works while the user is on another browser tab. `download: true` starts a browser download and returns metadata only (never send large base64 to an agent); otherwise `files` carry `dataBase64`. `downloadStarted` means initiated, not proof of an OS-saved file. Layout warnings are returned. `fileName` is a base name WITHOUT an extension (\"Floor Pattern View\", never \"Floor Pattern View.pdf\"): the host appends .pdf/.svg/.png/.jpg for the format, and a name ending in .pdf, .svg, .png, .jpg or .jpeg, a dot or a space is rejected.",
7986
+ "examplePrompts": [
7987
+ "Export this drawing as a PDF",
7988
+ "Download the ground floor plan drawing as an SVG",
7989
+ "Save this drawing as a high-resolution PNG\n\n# Example\n```ts\nawait snaptrude.presentation.drawings.getStatus(drawing.id, { waitMs: 10_000 })\nconst result = await snaptrude.presentation.drawings.export(drawing.id, \"pdf\", {\nfileName: \"Ground Floor Plan\",\ndownload: true,\n})\n```"
7990
+ ],
7991
+ "argsType": "string",
7992
+ "resultType": "PluginDrawingExportResult"
7993
+ },
7994
+ {
7995
+ "path": "presentation.drawings.get",
7996
+ "namespace": "presentation.drawings",
7997
+ "summary": "Read one drawing by its stable id.",
7998
+ "examplePrompts": [
7999
+ "Does the ground floor drawing still exist?",
8000
+ "What preset is this drawing using?",
8001
+ "Read the scale and crop of this drawing\n\n# Example\n```ts\nconst current = await snaptrude.presentation.drawings.get(drawing.id)\nif (current) console.log(current.scale, current.presetId)\n```"
8002
+ ],
8003
+ "argsType": "string",
8004
+ "resultType": "PluginDrawing | null"
8005
+ },
8006
+ {
8007
+ "path": "presentation.drawings.getCapabilities",
8008
+ "namespace": "presentation.drawings",
8009
+ "summary": "Read what a drawing supports: its actual renderer, whether it takes presets (native kernel plans only), label layout and an architectural scale, the standard scales for this project, and its export formats. Read this before preset, scale, label-layout, style or export writes instead of inferring support from the source view.",
8010
+ "examplePrompts": [
8011
+ "What editing capabilities does this drawing support?",
8012
+ "Can I apply a preset to this drawing?",
8013
+ "Which scales can this drawing use?\n\n# Example\n```ts\nconst capabilities = await snaptrude.presentation.drawings.getCapabilities(drawing.id)\nif (capabilities.presets) {\nawait snaptrude.presentation.drawings.applyPreset(drawing.id, preset.id)\n}\n```"
8014
+ ],
8015
+ "argsType": "string",
8016
+ "resultType": "PluginDrawingCapabilities"
8017
+ },
8018
+ {
8019
+ "path": "presentation.drawings.getStatus",
8020
+ "namespace": "presentation.drawings",
8021
+ "summary": "Read a drawing's generation, label-layout and asset-upload state, optionally waiting up to `waitMs` (0 by default, at most 30,000) for it to become export-ready and uploaded. A timeout is not an error: the current state is returned. Read this before export or before closing Present mode.",
8022
+ "examplePrompts": [
8023
+ "Is this drawing ready to export?",
8024
+ "Wait for the new drawing to finish generating",
8025
+ "Has the drawing been uploaded yet?\n\n# Example\n```ts\nconst status = await snaptrude.presentation.drawings.getStatus(drawing.id, {\nwaitMs: 10_000,\n})\nif (!status.exportReady) console.warn(status.warnings)\n```"
8026
+ ],
8027
+ "argsType": "string",
8028
+ "resultType": "PluginDrawingStatus"
8029
+ },
8030
+ {
8031
+ "path": "presentation.drawings.getStyles",
8032
+ "namespace": "presentation.drawings",
8033
+ "summary": "Read a drawing's effective style categories (preset plus its own overrides) and the controls each category accepts. Always read this before {@linkcode PluginPresentationDrawingsApi.updateStyles}: accepted categories and controls depend on the drawing, and an `absent` category rejects writes. Do not echo a whole `settings` record as a patch (legacy reads may include the read-only `disable` flag and `-1` for unset opacity).",
8034
+ "examplePrompts": [
8035
+ "Show the editable styles for this drawing",
8036
+ "What line weight are the walls drawn with?",
8037
+ "Which categories can I restyle on this plan?\n\n# Example\n```ts\nconst styles = await snaptrude.presentation.drawings.getStyles(drawing.id)\nconst editable = styles.categories.filter((row) => !row.absent)\n```"
8038
+ ],
8039
+ "argsType": "string",
8040
+ "resultType": "PluginDrawingStyles"
8041
+ },
8042
+ {
8043
+ "path": "presentation.drawings.labels.getLayout",
8044
+ "namespace": "presentation.drawings.labels",
8045
+ "summary": "Read the label-layout diagnostics of a drawing: labels that could not be placed cleanly (each with a `suggestedScale` at which it would fit), pinned labels, merged duplicates and dropped rows. Reading never hides a label: an unplaced label is still shown at a best-effort position.",
8046
+ "examplePrompts": [
8047
+ "Check this drawing for overlapping labels",
8048
+ "Do all the room names fit on the ground floor drawing?",
8049
+ "Which scale would make the labels on this plan readable?\n\n# Example\n```ts\nconst layout = await snaptrude.presentation.drawings.labels.getLayout(drawing.id)\nconst suggested = layout.unplaced.find((row) => row.suggestedScale)?.suggestedScale\nif (suggested) await snaptrude.presentation.drawings.setScale(drawing.id, suggested)\n```"
8050
+ ],
8051
+ "argsType": "string",
8052
+ "resultType": "PluginDrawingLabelLayout"
8053
+ },
8054
+ {
8055
+ "path": "presentation.drawings.labels.list",
8056
+ "namespace": "presentation.drawings.labels",
8057
+ "summary": "List every text label on a drawing, including labels the user or the layout has hidden. An agent's own annotations under the drawing are not labels and are not listed.",
8058
+ "examplePrompts": [
8059
+ "List all labels in this drawing",
8060
+ "Which room labels are hidden on the ground floor drawing?",
8061
+ "Show me the text of every label on this plan\n\n# Example\n```ts\nconst labels = await snaptrude.presentation.drawings.labels.list(drawing.id)\nconst hidden = labels.filter((label) => label.hidden)\n```"
8062
+ ],
8063
+ "argsType": "string",
8064
+ "resultType": "PluginPlacedViewLabel[]"
8065
+ },
8066
+ {
8067
+ "path": "presentation.drawings.labels.move",
8068
+ "namespace": "presentation.drawings.labels",
8069
+ "summary": "Move a label within its drawing. A room label keeps the new position as a native pin at the drawing's current architectural scale; other labels move as plain text shapes. One Present undo step.",
8070
+ "examplePrompts": [
8071
+ "Move the Kitchen label up a little",
8072
+ "Put the living room name in the middle of the room",
8073
+ "Nudge this label to the left\n\n# Example\n```ts\nconst [label] = await snaptrude.presentation.drawings.labels.list(drawing.id)\nawait snaptrude.presentation.drawings.labels.move(label.labelId, {\nx: label.position.x,\ny: label.position.y - 20,\n})\n```"
8074
+ ],
8075
+ "argsType": "string",
8076
+ "resultType": "PluginPlacedViewLabel"
8077
+ },
8078
+ {
8079
+ "path": "presentation.drawings.labels.place",
8080
+ "namespace": "presentation.drawings.labels",
8081
+ "summary": "Pin a room label that could not be placed cleanly at the best position the native layout solver finds, at the current architectural scale. May wait up to ten seconds for a reloaded drawing's fit data. One Present undo step.",
8082
+ "examplePrompts": [
8083
+ "Place the labels that do not fit",
8084
+ "Pin the Kitchen label where it fits best",
8085
+ "Fix the overlapping room names on this drawing\n\n# Example\n```ts\nconst { unplaced } = await snaptrude.presentation.drawings.labels.getLayout(drawing.id)\nfor (const row of unplaced) {\nawait snaptrude.presentation.drawings.labels.place(row.labelId)\n}\n```"
8086
+ ],
8087
+ "argsType": "string",
8088
+ "resultType": "boolean"
8089
+ },
8090
+ {
8091
+ "path": "presentation.drawings.labels.reset",
8092
+ "namespace": "presentation.drawings.labels",
8093
+ "summary": "Clear room-label pins (from `move`, `place` or the user) so the native layout places those labels again: at the current architectural scale by default, or at every scale with `{ scope: \"all\" }`. One Present undo step.",
8094
+ "examplePrompts": [
8095
+ "Reset the label positions on this drawing",
8096
+ "Undo all my label moves at every scale",
8097
+ "Let the room labels lay themselves out again\n\n# Example\n```ts\nconst cleared = await snaptrude.presentation.drawings.labels.reset(drawing.id, {\nscope: \"all\",\n})\n```"
8098
+ ],
8099
+ "argsType": "string",
8100
+ "resultType": "number"
8101
+ },
8102
+ {
8103
+ "path": "presentation.drawings.labels.setHidden",
8104
+ "namespace": "presentation.drawings.labels",
8105
+ "summary": "Hide or show a label. This is the user's own visibility, independent of labels the layout or a crop hides. One Present undo step.",
8106
+ "examplePrompts": [
8107
+ "Hide the Store label on this drawing",
8108
+ "Show the corridor label again",
8109
+ "Turn off the label for the plant room\n\n# Example\n```ts\nconst labels = await snaptrude.presentation.drawings.labels.list(drawing.id)\nconst store = labels.find((label) => label.text.startsWith(\"Store\"))\nif (store) await snaptrude.presentation.drawings.labels.setHidden(store.labelId, true)\n```"
8110
+ ],
8111
+ "argsType": "string",
8112
+ "resultType": "PluginPlacedViewLabel"
8113
+ },
8114
+ {
8115
+ "path": "presentation.drawings.list",
8116
+ "namespace": "presentation.drawings",
8117
+ "summary": "List the drawings on the current Present page, loose or on sheets.",
8118
+ "examplePrompts": [
8119
+ "List all drawings in this presentation",
8120
+ "Which drawings are not on a sheet yet?",
8121
+ "What scale is each plan drawing at?\n\n# Example\n```ts\nconst drawings = await snaptrude.presentation.drawings.list()\nconst loose = drawings.filter((drawing) => !drawing.sheetId)\n```"
8122
+ ],
8123
+ "argsType": null,
8124
+ "resultType": "PluginDrawing[]"
8125
+ },
8126
+ {
8127
+ "path": "presentation.drawings.listFonts",
8128
+ "namespace": "presentation.drawings",
8129
+ "summary": "List the font families drawing text can use, from the native catalog (`\"DEFAULT\"` first). Use these names in a style patch's `fontFamily` instead of inventing one.",
8130
+ "examplePrompts": [
8131
+ "Which fonts can I use for drawing labels?",
8132
+ "Is Inter available as a drawing font?",
8133
+ "List the fonts for the title block\n\n# Example\n```ts\nconst fonts = await snaptrude.presentation.drawings.listFonts()\nconst family = fonts.includes(\"Inter\") ? \"Inter\" : \"DEFAULT\"\n```"
8134
+ ],
8135
+ "argsType": null,
8136
+ "resultType": "string[]"
8137
+ },
8138
+ {
8139
+ "path": "presentation.drawings.listPatterns",
8140
+ "namespace": "presentation.drawings",
8141
+ "summary": "List the hatch patterns the native pattern catalog offers. Use these ids in a style patch's `pattern.id` instead of inventing one.",
8142
+ "examplePrompts": [
8143
+ "What hatch patterns can I use?",
8144
+ "List the brick and concrete patterns",
8145
+ "Which fill patterns are available for rooms?\n\n# Example\n```ts\nconst patterns = await snaptrude.presentation.drawings.listPatterns()\nconst brick = patterns.find((pattern) => pattern.label.toLowerCase().includes(\"brick\"))\n```"
8146
+ ],
8147
+ "argsType": null,
8148
+ "resultType": "{ id: string; label: string; group: string }[]"
8149
+ },
8150
+ {
8151
+ "path": "presentation.drawings.rename",
8152
+ "namespace": "presentation.drawings",
8153
+ "summary": "Set the drawing's own title text. The source saved view keeps its name. One Present undo step.",
8154
+ "examplePrompts": [
8155
+ "Rename this drawing to Ground Floor Plan",
8156
+ "Change the title of the elevation drawing",
8157
+ "Call this drawing Level 2 General Arrangement\n\n# Example\n```ts\nawait snaptrude.presentation.drawings.rename(drawing.id, \"Ground Floor Plan\")\n```"
8158
+ ],
8159
+ "argsType": "string",
8160
+ "resultType": "PluginDrawing"
8161
+ },
8162
+ {
8163
+ "path": "presentation.drawings.resetOverrides",
8164
+ "namespace": "presentation.drawings",
8165
+ "summary": "Drop a kernel plan's own style overrides so it matches its bound preset again. Local title, proposal and building text is kept. One Present undo step.",
8166
+ "examplePrompts": [
8167
+ "Reset this drawing to its preset styles",
8168
+ "Undo my style changes on the ground floor plan",
8169
+ "Clear the overrides on this drawing\n\n# Example\n```ts\nawait snaptrude.presentation.drawings.resetOverrides(drawing.id)\n```"
8170
+ ],
8171
+ "argsType": "string",
8172
+ "resultType": "PluginDrawing"
8173
+ },
8174
+ {
8175
+ "path": "presentation.drawings.setCrop",
8176
+ "namespace": "presentation.drawings",
8177
+ "summary": "Crop a drawing to a window of itself, or pass `null` to remove the crop. Crop corners are 0–1 fractions of the uncropped drawing and must satisfy `topLeft < bottomRight` on both axes. One Present undo step. A loose drawing that grows over a neighbour it did not cover before (for example when the crop is removed) moves to the nearest free spot, which `placedViews.setCrop` does not do; an overlap the user made stays, and a drawing on a sheet never moves. Changes the drawing, not the Present camera. The Present view may follow the drawing as a courtesy to a user watching (never zooming in, not while the camera is locked); to look at it, call `presentation.camera.zoomTo(drawing.id)` then `presentation.camera.capture()`.",
8178
+ "examplePrompts": [
8179
+ "Crop this drawing to the left half",
8180
+ "Remove the crop from the site plan drawing",
8181
+ "Show only the top-right corner of this plan\n\n# Example\n```ts\nawait snaptrude.presentation.drawings.setCrop(drawing.id, {\ntopLeft: { x: 0, y: 0 },\nbottomRight: { x: 0.5, y: 1 },\n})\nawait snaptrude.presentation.drawings.setCrop(drawing.id, null)\n```"
8182
+ ],
8183
+ "argsType": "string",
8184
+ "resultType": "PluginDrawing"
8185
+ },
8186
+ {
8187
+ "path": "presentation.drawings.setScale",
8188
+ "namespace": "presentation.drawings",
8189
+ "summary": "Set a drawing's architectural scale; perspective/isometric drawings reject this. `scale` must be a standard architectural scale denominator (100 means 1:100) in the project's unit system; any other value is rejected. Metric: 10, 20, 50, 100, 150, 200, 250, 500, 1000. Imperial detail: 12, 16, 24, 32, 48, 64, 96, 128, 192, 384, 768, 1536 (1\" = 1' to 1/128\" = 1'; 48 is 1/4\" = 1', 96 is 1/8\" = 1'). Imperial site: 120, 240, 360, 480, 600, 1200, 2400, 3600, 4800 (1\" = 10' to 1\" = 400'). Pick the LARGEST standard scale (smallest denominator) at which the view still fits its frame or sheet and its room labels stay readable: for a building floor plan that is usually 1:50 to 1:200 (1/4\" = 1' to 1/16\" = 1'). Never use a site-sized scale (1:500 and up, 1/64\" = 1' (768) and smaller, 1\" = 20' (240) and up) for a single building plan unless the user asked for it. A loose drawing that grows over a neighbour it did not cover before moves to the nearest free spot, which `placedViews.setScale` does not do; an overlap the user made stays, and a drawing on a sheet never moves. Changes the drawing, not the Present camera. The Present view may follow the drawing as a courtesy to a user watching (never zooming in, not while the camera is locked); to look at it, call `presentation.camera.zoomTo(drawing.id)` then `presentation.camera.capture()`.",
8190
+ "examplePrompts": [
8191
+ "Set this drawing to 1:50",
8192
+ "Change the plan drawing scale to 1/4\" = 1'",
8193
+ "Make the ground floor drawing bigger so the labels fit\n\n# Example\n```ts\nconst layout = await snaptrude.presentation.drawings.labels.getLayout(drawing.id)\nconst suggested = layout.unplaced.find((row) => row.suggestedScale)?.suggestedScale\nawait snaptrude.presentation.drawings.setScale(drawing.id, suggested ?? 50)\n```"
8194
+ ],
8195
+ "argsType": "string",
8196
+ "resultType": "PluginDrawing"
8197
+ },
8198
+ {
8199
+ "path": "presentation.drawings.updateContent",
8200
+ "namespace": "presentation.drawings",
8201
+ "summary": "Regenerate one to 100 drawings from their source views, after the model or the view changed. Waits for the update to finish.",
8202
+ "examplePrompts": [
8203
+ "Update this drawing with my latest model changes",
8204
+ "Refresh all the drawings",
8205
+ "Regenerate the ground floor plan drawing\n\n# Example\n```ts\nconst drawings = await snaptrude.presentation.drawings.list()\nconst results = await snaptrude.presentation.drawings.updateContent(\ndrawings.map((drawing) => drawing.id),\n)\n```"
8206
+ ],
8207
+ "argsType": "string[]",
8208
+ "resultType": "{ drawingId: string; updated: boolean }[]"
8209
+ },
8210
+ {
8211
+ "path": "presentation.drawings.updateStyles",
8212
+ "namespace": "presentation.drawings",
8213
+ "summary": "Merge a sparse style patch into one category of a drawing, as one Present undo step. Nested objects merge rather than replace the row. Line and outline weights are paper millimetres (0–2.11); opacity is 0–100; colours are `#rrggbb`. On a kernel plan the patch becomes the drawing's own overrides of its preset; other drawings take the legacy placed-view category patch. An absent category, an unsupported field or an invalid value rejects the whole write.",
8214
+ "examplePrompts": [
8215
+ "Make the walls on this drawing thicker",
8216
+ "Fill the rooms at 60% opacity with a larger hatch",
8217
+ "Turn off the door tags on this plan\n\n# Example\n```ts\nconst styles = await snaptrude.presentation.drawings.getStyles(drawing.id)\nconst space = styles.categories.find((row) => row.category === \"Space\")\nif (space && !space.absent && space.controls.some((control) => control.key === \"opacity\")) {\nawait snaptrude.presentation.drawings.updateStyles(drawing.id, \"Space\", {\nopacity: 60,\npattern: { scale: 1.5 },\n})\n}\n```"
8218
+ ],
8219
+ "argsType": "string",
8220
+ "resultType": "PluginDrawingStyles"
8221
+ },
7240
8222
  {
7241
8223
  "path": "presentation.export",
7242
8224
  "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.",
8225
+ "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. `fileName` is a base name WITHOUT an extension (\"Floor Pattern View\", never \"Floor Pattern View.pdf\"): the host appends the extension for the format, and a name ending in .pdf, .svg, .png, .jpg or .jpeg, a dot or a space is rejected.",
7244
8226
  "examplePrompts": [
7245
8227
  "Export the presentation as a PDF",
7246
8228
  "Download all sheets as a single PDF",
7247
8229
  "Export sheet 1 as a PNG",
7248
8230
  "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```"
8231
+ "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
8232
  ],
7251
8233
  "argsType": "PluginPresentationExportFormat",
7252
8234
  "resultType": "PluginPresentationExportResult"
7253
8235
  },
8236
+ {
8237
+ "path": "presentation.export",
8238
+ "namespace": "presentation",
8239
+ "summary": "",
8240
+ "examplePrompts": [],
8241
+ "argsType": "PluginPresentationExportFormat",
8242
+ "resultType": "PluginPresentationExportDownloadResult"
8243
+ },
8244
+ {
8245
+ "path": "presentation.export",
8246
+ "namespace": "presentation",
8247
+ "summary": "",
8248
+ "examplePrompts": [],
8249
+ "argsType": "PluginPresentationExportFormat",
8250
+ "resultType": "PluginPresentationExportResult | PluginPresentationExportDownloadResult"
8251
+ },
8252
+ {
8253
+ "path": "presentation.graphics.disableThinLines",
8254
+ "namespace": "presentation.graphics",
8255
+ "summary": "Disable the viewer's Thin Lines preference. No model save, undo or peer change.",
8256
+ "examplePrompts": [],
8257
+ "argsType": null,
8258
+ "resultType": "boolean"
8259
+ },
8260
+ {
8261
+ "path": "presentation.graphics.enableThinLines",
8262
+ "namespace": "presentation.graphics",
8263
+ "summary": "Enable the viewer's Thin Lines preference. No model save, undo or peer change. capture_view and exported images deliberately suspend it and use real weights.",
8264
+ "examplePrompts": [],
8265
+ "argsType": null,
8266
+ "resultType": "boolean"
8267
+ },
8268
+ {
8269
+ "path": "presentation.graphics.get",
8270
+ "namespace": "presentation.graphics",
8271
+ "summary": "Read a view's scale denominator, detail and category overrides.",
8272
+ "examplePrompts": [
8273
+ "What scale is this plan drawn at?"
8274
+ ],
8275
+ "argsType": "PluginGraphicsViewId",
8276
+ "resultType": "PluginViewGraphics | null"
8277
+ },
8278
+ {
8279
+ "path": "presentation.graphics.isEnabled",
8280
+ "namespace": "presentation.graphics",
8281
+ "summary": "Whether Paper Pens is enabled on this viewer. Never changes the feature flag.",
8282
+ "examplePrompts": [],
8283
+ "argsType": null,
8284
+ "resultType": "boolean"
8285
+ },
8286
+ {
8287
+ "path": "presentation.graphics.isThinLinesEnabled",
8288
+ "namespace": "presentation.graphics",
8289
+ "summary": "Read the remembered Thin Lines preference. A disabled feature or capture suspension can make the renderer ignore a true preference temporarily.",
8290
+ "examplePrompts": [],
8291
+ "argsType": null,
8292
+ "resultType": "boolean"
8293
+ },
8294
+ {
8295
+ "path": "presentation.graphics.listScales",
8296
+ "namespace": "presentation.graphics",
8297
+ "summary": "List the chip's scale choices for the project units, in the same order. Custom denominators need not be listed: any integer 1–24000 is valid.",
8298
+ "examplePrompts": [],
8299
+ "argsType": "PluginGraphicsViewId",
8300
+ "resultType": "PluginGraphicsScaleOption[]"
8301
+ },
8302
+ {
8303
+ "path": "presentation.graphics.reset",
8304
+ "namespace": "presentation.graphics",
8305
+ "summary": "Clear one category or all category overrides, as one saved undoable step. The view's scale and detail level are preserved.",
8306
+ "examplePrompts": [],
8307
+ "argsType": "PluginGraphicsResetOptions",
8308
+ "resultType": "PluginViewGraphics"
8309
+ },
8310
+ {
8311
+ "path": "presentation.graphics.setDetailLevel",
8312
+ "namespace": "presentation.graphics",
8313
+ "summary": "Set the active plan's Coarse/Fine drawing detail through the native setter. Fine draws wall layers; this does not enable the project's Detailed View take-off setting. Saved for a saved view, not a model undo step; unsaved current views are transient.",
8314
+ "examplePrompts": [
8315
+ "Show this plan in Fine detail"
8316
+ ],
8317
+ "argsType": "PluginGraphicsDetailLevel",
8318
+ "resultType": "PluginViewGraphics"
8319
+ },
8320
+ {
8321
+ "path": "presentation.graphics.setScale",
8322
+ "namespace": "presentation.graphics",
8323
+ "summary": "Set a paper view's denominator through the same path as the scale chip. A saved view persists this change; it is not a model undo step. Without a saved current view, only viewer state changes. Does not switch the camera. An inactive target refuses NOT_ACTIVE_VIEW.",
8324
+ "examplePrompts": [
8325
+ "Set this plan to 1:50"
8326
+ ],
8327
+ "argsType": "number",
8328
+ "resultType": "PluginViewGraphics"
8329
+ },
8330
+ {
8331
+ "path": "presentation.graphics.update",
8332
+ "namespace": "presentation.graphics",
8333
+ "summary": "Change only supplied category fields, as one saved and undoable step. Does not edit project standards. Requires a saved view.",
8334
+ "examplePrompts": [
8335
+ "Halftone the furniture in this view"
8336
+ ],
8337
+ "argsType": "PluginGraphicsOverrides",
8338
+ "resultType": "PluginViewGraphics"
8339
+ },
7254
8340
  {
7255
8341
  "path": "presentation.import.image",
7256
8342
  "namespace": "presentation.import",
@@ -7516,7 +8602,7 @@
7516
8602
  {
7517
8603
  "path": "presentation.placedViews.setScale",
7518
8604
  "namespace": "presentation.placedViews",
7519
- "summary": "Set a placed view's architectural scale.",
8605
+ "summary": "Set a placed view's architectural scale. `scale` must be a standard architectural scale denominator (100 means 1:100) in the project's unit system; any other value is rejected. Metric: 10, 20, 50, 100, 150, 200, 250, 500, 1000. Imperial detail: 12, 16, 24, 32, 48, 64, 96, 128, 192, 384, 768, 1536 (1\" = 1' to 1/128\" = 1'; 48 is 1/4\" = 1', 96 is 1/8\" = 1'). Imperial site: 120, 240, 360, 480, 600, 1200, 2400, 3600, 4800 (1\" = 10' to 1\" = 400'). Pick the LARGEST standard scale (smallest denominator) at which the view still fits its frame or sheet and its room labels stay readable: for a building floor plan that is usually 1:50 to 1:200 (1/4\" = 1' to 1/16\" = 1'). Never use a site-sized scale (1:500 and up, 1/64\" = 1' (768) and smaller, 1\" = 20' (240) and up) for a single building plan unless the user asked for it.",
7520
8606
  "examplePrompts": [
7521
8607
  "Set the plan to 1:100",
7522
8608
  "Change the placed floor plan to 1:50 scale",
@@ -7551,6 +8637,99 @@
7551
8637
  "argsType": "string",
7552
8638
  "resultType": "PluginPlacedViewCategoryStyles"
7553
8639
  },
8640
+ {
8641
+ "path": "presentation.sections.create",
8642
+ "namespace": "presentation.sections",
8643
+ "summary": "Draw a section line from `start` to `end` on the plan. The section looks along the line's direction turned a quarter turn — a line drawn toward +x looks toward +z; flip it with `update(id, { isFlipped: true })`. Its slice defaults to the building's full height and its depth to the far side of the model. Write-gated; one undo step.",
8644
+ "examplePrompts": [
8645
+ "Cut a section through the middle of the building, east to west",
8646
+ "Add a section across the stair core",
8647
+ "Draw a longitudinal section and open it\n\n# Example\n```ts\nconst section = await snaptrude.presentation.sections.create(\n{ x: -40, z: 10 },\n{ x: 40, z: 10 },\n{ name: \"Longitudinal\", isActive: true },\n)\n```"
8648
+ ],
8649
+ "argsType": "PluginSectionPoint",
8650
+ "resultType": "PluginSection"
8651
+ },
8652
+ {
8653
+ "path": "presentation.sections.delete",
8654
+ "namespace": "presentation.sections",
8655
+ "summary": "Delete a section. Closes its view first if it is open. Write-gated; one undo step.",
8656
+ "examplePrompts": [
8657
+ "Delete Section B\n\n# Example\n```ts\nawait snaptrude.presentation.sections.delete(sectionId)\n```"
8658
+ ],
8659
+ "argsType": "string",
8660
+ "resultType": "boolean"
8661
+ },
8662
+ {
8663
+ "path": "presentation.sections.get",
8664
+ "namespace": "presentation.sections",
8665
+ "summary": "Get one section by id.",
8666
+ "examplePrompts": [
8667
+ "Where does Section A run?",
8668
+ "How deep is this section?\n\n# Example\n```ts\nconst section = await snaptrude.presentation.sections.get(sectionId)\nconsole.log(section?.start, section?.end)\n```"
8669
+ ],
8670
+ "argsType": "string",
8671
+ "resultType": "PluginSection | null"
8672
+ },
8673
+ {
8674
+ "path": "presentation.sections.getActive",
8675
+ "namespace": "presentation.sections",
8676
+ "summary": "Get the section whose view is open.",
8677
+ "examplePrompts": [
8678
+ "Which section am I looking at?\n\n# Example\n```ts\nconst open = await snaptrude.presentation.sections.getActive()\n```"
8679
+ ],
8680
+ "argsType": null,
8681
+ "resultType": "PluginSection | null"
8682
+ },
8683
+ {
8684
+ "path": "presentation.sections.getCut",
8685
+ "namespace": "presentation.sections",
8686
+ "summary": "What a section cuts: every element its cutting plane passes through within the section's extents, with the cut's area, outline length and extent on the plane, largest first. Computed from the model, not from the drawing, so it needs no open view and works however the section is shown; furniture is left out, as in the drawing, and so is anything hidden in the model. Areas are exact for closed solids. Repeated calls reuse the cuts of elements that have not changed. Read-only.",
8687
+ "examplePrompts": [
8688
+ "Which walls does Section A cut?",
8689
+ "How thick is the slab where the section crosses it?",
8690
+ "Does this section go through the stair?",
8691
+ "What is the floor-to-floor height in this section?\n\n# Example\n```ts\nconst cut = await snaptrude.presentation.sections.getCut(sectionId)\nconst walls = cut.elements.filter((element) => element.type === \"wall\")\nconst thickest = walls[0]\nconsole.log(thickest.extent.uMax - thickest.extent.uMin)\n```"
8692
+ ],
8693
+ "argsType": "string",
8694
+ "resultType": "PluginSectionCut"
8695
+ },
8696
+ {
8697
+ "path": "presentation.sections.list",
8698
+ "namespace": "presentation.sections",
8699
+ "summary": "List the project's sections, in Views panel order.",
8700
+ "examplePrompts": [
8701
+ "Which sections are there?",
8702
+ "List the section lines in this project",
8703
+ "Is there a section through the stair?\n\n# Example\n```ts\nconst sections = await snaptrude.presentation.sections.list()\n```"
8704
+ ],
8705
+ "argsType": null,
8706
+ "resultType": "PluginSection[]"
8707
+ },
8708
+ {
8709
+ "path": "presentation.sections.setActive",
8710
+ "namespace": "presentation.sections",
8711
+ "summary": "Open a section's view, or close the open one with `null`. Opening frames the section's saved camera, or a straight-on view when it has none. Closing returns to 3D; with no section view open, `null` does nothing. Not an edit: nothing is added to the undo history.",
8712
+ "examplePrompts": [
8713
+ "Open Section A",
8714
+ "Show me the section through the atrium",
8715
+ "Close the section view\n\n# Example\n```ts\nawait snaptrude.presentation.sections.setActive(section.sectionId)\nawait snaptrude.presentation.sections.setActive(null)\n```"
8716
+ ],
8717
+ "argsType": "string | null",
8718
+ "resultType": "PluginSection | null"
8719
+ },
8720
+ {
8721
+ "path": "presentation.sections.update",
8722
+ "namespace": "presentation.sections",
8723
+ "summary": "Change a section. Only the given fields change; the whole patch is one undo step. Flipping without a `depth` carries the depth to the far side of the model in the new direction, as `create` does. Write-gated.",
8724
+ "examplePrompts": [
8725
+ "Make Section A look the other way",
8726
+ "Only show the ground floor in this section",
8727
+ "Rename the section to \"Through atrium\"",
8728
+ "Move the section line 5 m north\n\n# Example\n```ts\nawait snaptrude.presentation.sections.update(sectionId, {\nisFlipped: true,\ndepth: 60,\n})\n```"
8729
+ ],
8730
+ "argsType": "string",
8731
+ "resultType": "PluginSection"
8732
+ },
7554
8733
  {
7555
8734
  "path": "presentation.shapes.list",
7556
8735
  "namespace": "presentation.shapes",
@@ -7653,17 +8832,31 @@
7653
8832
  {
7654
8833
  "path": "presentation.sheets.place",
7655
8834
  "namespace": "presentation.sheets",
7656
- "summary": "Place a saved view onto a sheet.",
8835
+ "summary": "Place a saved view onto a sheet. This is the call that puts a view on a sheet (`presentation.drawings.create` makes a drawing beside its source view, not on a sheet). When `scale` is omitted the view is fitted to the target sheet: a 2D or site-plan view is placed at the largest standard scale at which it fits inside the target sheet's margin, and never at a larger scale than the normal view drop would pick; a 3D view has no scale, so it is shrunk evenly to fit instead. This is the same fit as `placeDrawing`. Omit `position` unless the user asked for a specific place. Without a `position` the drawing goes to the sheet centre when that is free, otherwise to the nearest free spot to the centre inside the margin, beside the drawings already there; when there is no free spot it is centred anyway and the result has `overlaps: true`. With a `position` it is placed as given, and the result has `overlaps: true` when it covers another drawing, but it is not moved. In both cases only other drawings count (placed views and their title blocks and labels): text, images and other shapes on the sheet are ignored. Each call fits one view to the whole sheet, so a second fitted view often has no free spot: when several views should share a sheet, pass a `scale` small enough for all of them; otherwise use `sheets.create` per view. When the result's `overlaps` is true, move the drawing or put it on its own sheet. An explicit `scale` is always kept, even when the drawing (title block included) does not fit inside the sheet's margin at that scale; the result then has `overflows: true`. When it is true, pass a smaller scale (larger denominator), omit `scale` to fit, or use a larger sheet. `scale` must be a standard architectural scale denominator (100 means 1:100) in the project's unit system; any other value is rejected. Metric: 10, 20, 50, 100, 150, 200, 250, 500, 1000. Imperial detail: 12, 16, 24, 32, 48, 64, 96, 128, 192, 384, 768, 1536 (1\" = 1' to 1/128\" = 1'; 48 is 1/4\" = 1', 96 is 1/8\" = 1'). Imperial site: 120, 240, 360, 480, 600, 1200, 2400, 3600, 4800 (1\" = 10' to 1\" = 400'). Pick the LARGEST standard scale (smallest denominator) at which the view still fits its frame or sheet and its room labels stay readable: for a building floor plan that is usually 1:50 to 1:200 (1/4\" = 1' to 1/16\" = 1'). Never use a site-sized scale (1:500 and up, 1/64\" = 1' (768) and smaller, 1\" = 20' (240) and up) for a single building plan unless the user asked for it.",
7657
8836
  "examplePrompts": [
7658
8837
  "Place the floor plan on Sheet 1",
7659
8838
  "Add this 3D view to the cover sheet",
7660
8839
  "Drop the site plan onto the layout sheet at 100, 200",
7661
8840
  "Put the Ground Floor view on sheet_1 at 1:100 scale",
7662
- "Place the plan on the cover sheet at quarter-inch scale\n\n# Example\n```ts\nconst { shapeId } = await snaptrude.presentation.sheets.place(\"sheet_1\", \"view_123\", {\nscale: 100, // 1:100 (metric project)\n})\n```"
8841
+ "Place the plan on the cover sheet at quarter-inch scale",
8842
+ "Add a floor plan drawing of each storey to this sheet\n\n# Example\n```ts\nconst { shapeId } = await snaptrude.presentation.sheets.place(\"sheet_1\", \"view_123\", {\nscale: 100, // 1:100 (metric project)\n})\n```"
7663
8843
  ],
7664
8844
  "argsType": "string",
7665
8845
  "resultType": "PluginPresentationSheetsPlaceResult"
7666
8846
  },
8847
+ {
8848
+ "path": "presentation.sheets.placeDrawing",
8849
+ "namespace": "presentation.sheets",
8850
+ "summary": "Move an existing drawing onto a sheet without copying or regenerating it, scaled down to fit the sheet when it is too big. `presentation.drawings.create` leaves a drawing beside its source view, not on a sheet; this is the call that puts it on one. To place a saved view on a sheet in one call, use `sheets.place(sheetId, viewId)`, which fits it the same way. By default (`fit` true or omitted) a drawing larger than the sheet's drawable area (the sheet inside its margin) is moved to the LARGEST standard architectural scale at which it fits; a drawing that already fits keeps its scale, and a drawing is never scaled up. A 3D drawing has no architectural scale, so it is shrunk uniformly to fit instead. With `fit: false` the scale is kept and a drawing that does not fit is rejected with an error naming the drawing, the sheet and the scale, and saying to call `placeDrawing` without `fit: false` to fit it, or use a larger sheet. When even the smallest standard scale does not fit, the call is rejected with an error suggesting a larger sheet size. Without a `position` the drawing goes to the sheet centre when that is free, otherwise to the nearest free spot to the centre inside the margin, beside the drawings already there; when there is no free spot it is centred anyway and the result has `overlaps: true`. With a `position` it is placed as given, and the result has `overlaps: true` when it covers another drawing, but it is not moved. In both cases only other drawings count (placed views and their title blocks and labels): text, images and other shapes on the sheet are ignored. Each call fits one drawing to the whole sheet, so a second fitted drawing often has no free spot: when several drawings should share a sheet, give them scales small enough for all of them; otherwise use `sheets.create` per drawing. The position is sheet-local. The drawing's stable id remains unchanged; `shapeId` therefore identifies the moved drawing. The result reports the architectural scale the drawing has on the sheet and whether this call changed it; a host that reports it also says whether the drawing `overlaps` another drawing on the sheet (move it or put it on its own sheet when true).",
8851
+ "examplePrompts": [
8852
+ "Move this drawing onto the cover sheet",
8853
+ "Place drawing_1 at the center of sheet_2",
8854
+ "Put the level 2 plan drawing on the Level 2 Plan sheet",
8855
+ "Add the floor plan drawings to this sheet\n\n# Example\n```ts\nconst { scale, rescaled } = await snaptrude.presentation.sheets.placeDrawing(\nsheet.id,\ndrawing.id,\n)\n```"
8856
+ ],
8857
+ "argsType": "string",
8858
+ "resultType": "PluginPresentationSheetsPlaceDrawingResult"
8859
+ },
7667
8860
  {
7668
8861
  "path": "presentation.sheets.rename",
7669
8862
  "namespace": "presentation.sheets",
@@ -7774,6 +8967,62 @@
7774
8967
  "argsType": null,
7775
8968
  "resultType": "PluginPresentationSlideshowState"
7776
8969
  },
8970
+ {
8971
+ "path": "presentation.standards.export",
8972
+ "namespace": "presentation.standards",
8973
+ "summary": "Serialize the native standard-file format. No browser download or save. The file includes its stock/version and stores widths in paper mm.",
8974
+ "examplePrompts": [
8975
+ "Export the line standard"
8976
+ ],
8977
+ "argsType": null,
8978
+ "resultType": "string"
8979
+ },
8980
+ {
8981
+ "path": "presentation.standards.get",
8982
+ "namespace": "presentation.standards",
8983
+ "summary": "Read the stored sparse standard; omitted fields inherit stock values.",
8984
+ "examplePrompts": [],
8985
+ "argsType": null,
8986
+ "resultType": "PluginGraphicStandard"
8987
+ },
8988
+ {
8989
+ "path": "presentation.standards.getResolved",
8990
+ "namespace": "presentation.standards",
8991
+ "summary": "Read the actual native resolution, including applicable view overrides. No camera, view, preference or project data changes.",
8992
+ "examplePrompts": [
8993
+ "How thick are the wall cut lines at 1:50?"
8994
+ ],
8995
+ "argsType": "PluginGraphicStandardResolveOptions",
8996
+ "resultType": "PluginResolvedGraphicStandard | null"
8997
+ },
8998
+ {
8999
+ "path": "presentation.standards.import",
9000
+ "namespace": "presentation.standards",
9001
+ "summary": "Import native standard-file JSON exactly as the dialog does, replacing the project standard in one saved undo step. Never edits team preferences.",
9002
+ "examplePrompts": [
9003
+ "Match the line weights in this standard file"
9004
+ ],
9005
+ "argsType": "string",
9006
+ "resultType": "PluginGraphicStandard"
9007
+ },
9008
+ {
9009
+ "path": "presentation.standards.reset",
9010
+ "namespace": "presentation.standards",
9011
+ "summary": "Reset an editable tab, or all tabs, through the native reset functions. One saved undo step; stock retention/pruning follows the native commit.",
9012
+ "examplePrompts": [],
9013
+ "argsType": "PluginGraphicStandardTab",
9014
+ "resultType": "PluginGraphicStandard"
9015
+ },
9016
+ {
9017
+ "path": "presentation.standards.update",
9018
+ "namespace": "presentation.standards",
9019
+ "summary": "Apply only supplied fields as one saved, undoable native commit. Validate the whole patch first; invalid fields never leave a partial edit behind.",
9020
+ "examplePrompts": [
9021
+ "Make wall cut lines heavier throughout the project"
9022
+ ],
9023
+ "argsType": "PluginGraphicStandardPatch",
9024
+ "resultType": "PluginGraphicStandard"
9025
+ },
7777
9026
  {
7778
9027
  "path": "presentation.tables.place",
7779
9028
  "namespace": "presentation.tables",
@@ -7787,6 +9036,90 @@
7787
9036
  "argsType": "string",
7788
9037
  "resultType": "PluginCanvasShape"
7789
9038
  },
9039
+ {
9040
+ "path": "presentation.viewPresets.copy",
9041
+ "namespace": "presentation.viewPresets",
9042
+ "summary": "Duplicate a preset under a native-generated unique name. Drawing bindings do not change. Not undoable (catalog write). Built-ins can be copied.",
9043
+ "examplePrompts": [
9044
+ "Duplicate this preset",
9045
+ "Make an editable copy of the built-in preset",
9046
+ "Copy the Planning Issue preset so I can change it\n\n# Example\n```ts\nconst copy = await snaptrude.presentation.viewPresets.copy(preset.id)\nawait snaptrude.presentation.viewPresets.rename(copy.id, \"Planning Issue B\")\n```"
9047
+ ],
9048
+ "argsType": "string",
9049
+ "resultType": "PluginViewPreset"
9050
+ },
9051
+ {
9052
+ "path": "presentation.viewPresets.create",
9053
+ "namespace": "presentation.viewPresets",
9054
+ "summary": "Save a kernel-plan drawing's effective styles as a new custom preset and bind that drawing to it. Not undoable (catalog write).",
9055
+ "examplePrompts": [
9056
+ "Save this drawing's styles as a new preset",
9057
+ "Make a preset called Planning Issue from this plan",
9058
+ "Turn my restyled drawing into a reusable style\n\n# Example\n```ts\nconst capabilities = await snaptrude.presentation.drawings.getCapabilities(drawing.id)\nif (capabilities.presets) {\nconst preset = await snaptrude.presentation.viewPresets.create(\"Planning Issue\", {\nfromDrawingId: drawing.id,\n})\n}\n```"
9059
+ ],
9060
+ "argsType": "string",
9061
+ "resultType": "PluginViewPreset"
9062
+ },
9063
+ {
9064
+ "path": "presentation.viewPresets.delete",
9065
+ "namespace": "presentation.viewPresets",
9066
+ "summary": "Delete a custom preset and rebind its drawings to another existing preset. Built-ins cannot be deleted. Not undoable (catalog write).",
9067
+ "examplePrompts": [
9068
+ "Delete this preset",
9069
+ "Remove the old style preset and switch its drawings to the default",
9070
+ "Get rid of the Planning Issue B preset\n\n# Example\n```ts\nconst presets = await snaptrude.presentation.viewPresets.list()\nconst replacement = presets.find((each) => each.builtIn)!\nawait snaptrude.presentation.viewPresets.delete(customPreset.id, {\nreplacementId: replacement.id,\n})\n```"
9071
+ ],
9072
+ "argsType": "string",
9073
+ "resultType": "void"
9074
+ },
9075
+ {
9076
+ "path": "presentation.viewPresets.get",
9077
+ "namespace": "presentation.viewPresets",
9078
+ "summary": "Read one preset by id.",
9079
+ "examplePrompts": [
9080
+ "Which preset is this drawing using?",
9081
+ "Is this preset a built-in one?",
9082
+ "Read the preset bound to the ground floor plan\n\n# Example\n```ts\nconst drawing = await snaptrude.presentation.drawings.get(drawingId)\nconst preset = drawing?.presetId\n? await snaptrude.presentation.viewPresets.get(drawing.presetId)\n: null\n```"
9083
+ ],
9084
+ "argsType": "string",
9085
+ "resultType": "PluginViewPreset | null"
9086
+ },
9087
+ {
9088
+ "path": "presentation.viewPresets.list",
9089
+ "namespace": "presentation.viewPresets",
9090
+ "summary": "List the preset catalog: built-ins first, then custom presets by name.",
9091
+ "examplePrompts": [
9092
+ "List the available drawing style presets",
9093
+ "Which custom presets does this presentation have?",
9094
+ "Is there a Planning Issue preset?\n\n# Example\n```ts\nconst presets = await snaptrude.presentation.viewPresets.list()\nconst custom = presets.filter((preset) => !preset.builtIn)\n```"
9095
+ ],
9096
+ "argsType": null,
9097
+ "resultType": "PluginViewPreset[]"
9098
+ },
9099
+ {
9100
+ "path": "presentation.viewPresets.rename",
9101
+ "namespace": "presentation.viewPresets",
9102
+ "summary": "Rename a custom preset. Names must stay unique; built-ins cannot be renamed. Not undoable (catalog write).",
9103
+ "examplePrompts": [
9104
+ "Rename this preset to Client Review",
9105
+ "Call the copied preset Planning Issue B",
9106
+ "Change the name of my custom drawing style\n\n# Example\n```ts\nawait snaptrude.presentation.viewPresets.rename(preset.id, \"Client Review\")\n```"
9107
+ ],
9108
+ "argsType": "string",
9109
+ "resultType": "PluginViewPreset"
9110
+ },
9111
+ {
9112
+ "path": "presentation.viewPresets.update",
9113
+ "namespace": "presentation.viewPresets",
9114
+ "summary": "Replace a custom preset's values with the effective styles of a drawing already bound to it, then refresh every bound drawing. Built-ins cannot be updated. Not undoable (catalog write).",
9115
+ "examplePrompts": [
9116
+ "Update this preset from the current drawing styles",
9117
+ "Push my style changes to every drawing using this preset",
9118
+ "Save these overrides into the Planning Issue preset\n\n# Example\n```ts\nawait snaptrude.presentation.viewPresets.update(drawing.presetId!, {\nfromDrawingId: drawing.id,\n})\n```"
9119
+ ],
9120
+ "argsType": "string",
9121
+ "resultType": "PluginViewPreset"
9122
+ },
7790
9123
  {
7791
9124
  "path": "presentation.views.capture",
7792
9125
  "namespace": "presentation.views",
@@ -7858,7 +9191,9 @@
7858
9191
  "What color mode is the Ground Floor view using?",
7859
9192
  "Get the display settings for this view",
7860
9193
  "Does the site plan view show axes and labels?",
7861
- "Read the background color of the active view\n\n# Example\n```ts\nconst settings = await snaptrude.presentation.views.getSettings(\"view_123\")\nif (settings) console.log(settings.colorMode, settings.backgroundColor)\n```"
9194
+ "Read the background color of the active view",
9195
+ "What is the cut height of this plan?",
9196
+ "How deep does this plan look below the cut?\n\n# Example\n```ts\nconst settings = await snaptrude.presentation.views.getSettings(\"view_123\")\nif (settings) console.log(settings.colorMode, settings.backgroundColor)\n```"
7862
9197
  ],
7863
9198
  "argsType": "string",
7864
9199
  "resultType": "PluginPresentationViewsGetSettingsResult"
@@ -7869,7 +9204,7 @@
7869
9204
  "summary": "List the saved views in the active project.",
7870
9205
  "examplePrompts": [
7871
9206
  "List all the saved views",
7872
- "What 2D and 3D views are in this project?",
9207
+ "What plan, 3D, and elevation views are in this project?",
7873
9208
  "Which view is currently active?",
7874
9209
  "Get the names of every presentation view",
7875
9210
  "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```"
@@ -7912,11 +9247,240 @@
7912
9247
  "Set the background of the active view to white",
7913
9248
  "Show area labels on this view",
7914
9249
  "Enable hidden-line mode for the section view",
7915
- "Make the 3D view orthographic instead of perspective\n\n# Example\n```ts\nawait snaptrude.presentation.views.updateSettings(\"view_123\", {\ncolorMode: \"monochrome\",\nshowAxis: false,\n})\n// switch a 3D view to an isometric (orthographic) projection\nawait snaptrude.presentation.views.updateSettings(\"view_123\", {\nviewMode: \"isometric\",\n})\n```"
9250
+ "Make the 3D view orthographic instead of perspective",
9251
+ "Raise the cut plane of the Ground Floor plan to 1.8 m",
9252
+ "Show the basement below in this plan",
9253
+ "Hide the overhead objects in this plan\n\n# Example\n```ts\nawait snaptrude.presentation.views.updateSettings(\"view_123\", {\ncolorMode: \"monochrome\",\nshowAxis: false,\n})\n// switch a 3D view to an isometric (orthographic) projection\nawait snaptrude.presentation.views.updateSettings(\"view_123\", {\nviewMode: \"isometric\",\n})\n// set a plan's range: cut 1.2 m up, show the basement below, no overhead\nawait snaptrude.presentation.views.updateSettings(\"plan_1\", {\ncutPlaneHeight: 4.72,\nviewDepthStorey: -1,\nshowOverheadObjects: false,\n})\n```"
7916
9254
  ],
7917
9255
  "argsType": "string",
7918
9256
  "resultType": "PluginPresentationViewsUpdateSettingsResult"
7919
9257
  },
9258
+ {
9259
+ "path": "presentation.walkthroughs.cancelJob",
9260
+ "namespace": "presentation.walkthroughs",
9261
+ "summary": "Cancel an export job. The recording stops and nothing is placed; a job that has already finished is returned unchanged. Write-gated.",
9262
+ "examplePrompts": [
9263
+ "Cancel the video export\n\n# Example\n```ts\nawait snaptrude.presentation.walkthroughs.cancelJob(jobId)\n```"
9264
+ ],
9265
+ "argsType": "string",
9266
+ "resultType": "PluginWalkthroughExportJob"
9267
+ },
9268
+ {
9269
+ "path": "presentation.walkthroughs.capture",
9270
+ "namespace": "presentation.walkthroughs",
9271
+ "summary": "Capture frames of a walkthrough as one contact-sheet image: a grid of views at evenly spaced moments (or at `options.timesInSeconds`), read left to right and top to bottom. The camera goes back to where it was afterwards. Switches from 2D plan view to 3D first.",
9272
+ "examplePrompts": [
9273
+ "Show me what the walkthrough looks like",
9274
+ "Capture six frames of the fly-over\n\n# Example\n```ts\nconst { image } = await snaptrude.presentation.walkthroughs.capture(walkthroughId, {\ncount: 6,\n})\n```"
9275
+ ],
9276
+ "argsType": "string",
9277
+ "resultType": "PluginWalkthroughCaptureResult"
9278
+ },
9279
+ {
9280
+ "path": "presentation.walkthroughs.create",
9281
+ "namespace": "presentation.walkthroughs",
9282
+ "summary": "Create a walkthrough from full keyframes: where the camera stands at each and how it looks there (ahead along the path, at a target, or in a fixed direction). A keyframe that follows the floor stands at the walkthrough's eye height above the floor, stair, terrain or ground at or below its `eye`, and the path between two such keyframes climbs stairs and ramps. The new walkthrough opens in the walkthrough dock. Write-gated; one undo step.",
9283
+ "examplePrompts": [
9284
+ "Make a walkthrough through these three viewpoints",
9285
+ "Create a camera path that looks at the atrium the whole way\n\n# Example\n```ts\nconst walkthrough = await snaptrude.presentation.walkthroughs.create(\n[\n{ eye: { x: 0, y: 30, z: 80 }, look: { mode: \"target\", target: { x: 0, y: 10, z: 0 } } },\n{ eye: { x: 60, y: 30, z: 0 }, look: { mode: \"target\", target: { x: 0, y: 10, z: 0 } } },\n],\n{ name: \"Approach\", durationInSeconds: 10 },\n)\n```"
9286
+ ],
9287
+ "argsType": "PluginWalkthroughKeyframeInput[]",
9288
+ "resultType": "PluginWalkthrough"
9289
+ },
9290
+ {
9291
+ "path": "presentation.walkthroughs.createFromPoints",
9292
+ "namespace": "presentation.walkthroughs",
9293
+ "summary": "Create a walkthrough that walks through points on the floor, the way the Walkthrough tool draws a route: the camera stands at eye height above the floor, stair, terrain or ground at each point, climbs stairs and ramps between them, and looks ahead along the path, or at `options.lookAt` the whole way. Write-gated; one undo step.",
9294
+ "examplePrompts": [
9295
+ "Walk from the entrance through the lobby to the stairs",
9296
+ "Make a walkthrough that visits every room on this floor",
9297
+ "Create a loop around the courtyard\n\n# Example\n```ts\nawait snaptrude.presentation.walkthroughs.createFromPoints(\n[{ x: 0, y: 0, z: 0 }, { x: 40, y: 0, z: 0 }, { x: 40, y: 0, z: 30 }],\n{ name: \"Ground floor tour\" },\n)\n```"
9298
+ ],
9299
+ "argsType": "Vec3Components[]",
9300
+ "resultType": "PluginWalkthrough"
9301
+ },
9302
+ {
9303
+ "path": "presentation.walkthroughs.createFromPreset",
9304
+ "namespace": "presentation.walkthroughs",
9305
+ "summary": "Create a walkthrough from a camera move around a subject, as the Walkthrough button's menu does: `orbit` circles it facing its middle, `flyOver` passes above it looking ahead and down, `pushIn` and `pullBack` move toward and away from it, and `rise` cranes up from eye level. The subject is `options.subject`, else the selection, else the visible model without its terrain; the move is sized to fit it in view and starts on the side the camera is on. While the walkthrough is still a preset, {@linkcode PluginPresentationWalkthroughsApi.update} can change its distance, height and direction; editing a keyframe makes it ordinary. Write-gated; one undo step.",
9306
+ "examplePrompts": [
9307
+ "Orbit around the selected building",
9308
+ "Make a drone fly-over of the site",
9309
+ "Push in slowly toward the entrance",
9310
+ "Crane up to reveal the whole campus\n\n# Example\n```ts\nawait snaptrude.presentation.walkthroughs.createFromPreset(\"orbit\", {\nsubject: [towerId],\nheightInDegrees: 35,\ndurationInSeconds: 20,\n})\n```"
9311
+ ],
9312
+ "argsType": "PluginWalkthroughPresetKind",
9313
+ "resultType": "PluginWalkthrough"
9314
+ },
9315
+ {
9316
+ "path": "presentation.walkthroughs.createKeyframe",
9317
+ "namespace": "presentation.walkthroughs",
9318
+ "summary": "Add a keyframe to a walkthrough at `index` (0 to the keyframe count; default: the end). A preset walkthrough becomes ordinary. Write-gated; one undo step.",
9319
+ "examplePrompts": [
9320
+ "Add a stop in the kitchen after the second point\n\n# Example\n```ts\nawait snaptrude.presentation.walkthroughs.createKeyframe(\nwalkthroughId,\n{ eye: { x: 12, y: 0, z: 30 }, followsFloor: true, holdInSeconds: 2 },\n2,\n)\n```"
9321
+ ],
9322
+ "argsType": "string",
9323
+ "resultType": "PluginWalkthrough"
9324
+ },
9325
+ {
9326
+ "path": "presentation.walkthroughs.delete",
9327
+ "namespace": "presentation.walkthroughs",
9328
+ "summary": "Delete a walkthrough. Write-gated; one undo step.",
9329
+ "examplePrompts": [
9330
+ "Delete the old walkthrough\n\n# Example\n```ts\nawait snaptrude.presentation.walkthroughs.delete(walkthroughId)\n```"
9331
+ ],
9332
+ "argsType": "string",
9333
+ "resultType": "boolean"
9334
+ },
9335
+ {
9336
+ "path": "presentation.walkthroughs.deleteKeyframe",
9337
+ "namespace": "presentation.walkthroughs",
9338
+ "summary": "Remove a keyframe; the path then runs straight from the keyframe before it to the one after. Removing the last keyframe deletes the walkthrough. A preset walkthrough becomes ordinary. Write-gated; one undo step.",
9339
+ "examplePrompts": [
9340
+ "Remove the third stop from the walkthrough\n\n# Example\n```ts\nawait snaptrude.presentation.walkthroughs.deleteKeyframe(walkthroughId, keyframeId)\n```"
9341
+ ],
9342
+ "argsType": "string",
9343
+ "resultType": "PluginWalkthrough | null"
9344
+ },
9345
+ {
9346
+ "path": "presentation.walkthroughs.export",
9347
+ "namespace": "presentation.walkthroughs",
9348
+ "summary": "Record a walkthrough as a video and place it on the Present canvas, as an asynchronous job: this resolves at once with the queued job, and {@linkcode PluginPresentationWalkthroughsApi.getJob} reports it until it completes, fails or is cancelled. Recording plays the walkthrough in real time at the chosen size, so it takes about as long as the walkthrough plus the upload; the editor switches to Present mode to place the video, which keeps playing muted in a loop there. Only one export runs at a time. Write-gated.",
9349
+ "examplePrompts": [
9350
+ "Export the walkthrough as a video",
9351
+ "Put a 1080p video of the orbit on a sheet\n\n# Example\n```ts\nconst walkthroughs = snaptrude.presentation.walkthroughs\nlet job = await walkthroughs.export(walkthroughId, { quality: \"1080p\" })\nwhile (job.status === \"queued\" || job.status === \"running\") {\nawait new Promise((resolve) => setTimeout(resolve, 2000))\njob = (await walkthroughs.getJob(job.jobId))!\n}\nconsole.log(job.result?.shapeId)\n```"
9352
+ ],
9353
+ "argsType": "string",
9354
+ "resultType": "PluginWalkthroughExportJob"
9355
+ },
9356
+ {
9357
+ "path": "presentation.walkthroughs.get",
9358
+ "namespace": "presentation.walkthroughs",
9359
+ "summary": "Get one walkthrough by id.",
9360
+ "examplePrompts": [
9361
+ "Show me the keyframes of the orbit walkthrough",
9362
+ "How long does this walkthrough take?\n\n# Example\n```ts\nconst walkthrough = await snaptrude.presentation.walkthroughs.get(walkthroughId)\nconsole.log(walkthrough?.durationInSeconds)\n```"
9363
+ ],
9364
+ "argsType": "string",
9365
+ "resultType": "PluginWalkthrough | null"
9366
+ },
9367
+ {
9368
+ "path": "presentation.walkthroughs.getJob",
9369
+ "namespace": "presentation.walkthroughs",
9370
+ "summary": "Get an export job by id.",
9371
+ "examplePrompts": [
9372
+ "Is the video export done yet?\n\n# Example\n```ts\nconst job = await snaptrude.presentation.walkthroughs.getJob(jobId)\n```"
9373
+ ],
9374
+ "argsType": "string",
9375
+ "resultType": "PluginWalkthroughExportJob | null"
9376
+ },
9377
+ {
9378
+ "path": "presentation.walkthroughs.getState",
9379
+ "namespace": "presentation.walkthroughs",
9380
+ "summary": "Read the playback state: which walkthrough is playing or paused, and how far through it.",
9381
+ "examplePrompts": [
9382
+ "Is the walkthrough still playing?\n\n# Example\n```ts\nconst { status, timeInSeconds } = await snaptrude.presentation.walkthroughs.getState()\n```"
9383
+ ],
9384
+ "argsType": null,
9385
+ "resultType": "PluginWalkthroughPlaybackState"
9386
+ },
9387
+ {
9388
+ "path": "presentation.walkthroughs.list",
9389
+ "namespace": "presentation.walkthroughs",
9390
+ "summary": "List the open project's walkthroughs, oldest first.",
9391
+ "examplePrompts": [
9392
+ "Which walkthroughs are there?",
9393
+ "List the camera paths in this project\n\n# Example\n```ts\nconst { walkthroughs } = await snaptrude.presentation.walkthroughs.list()\n```"
9394
+ ],
9395
+ "argsType": null,
9396
+ "resultType": "PluginWalkthroughsListResult"
9397
+ },
9398
+ {
9399
+ "path": "presentation.walkthroughs.listIssues",
9400
+ "namespace": "presentation.walkthroughs",
9401
+ "summary": "Find places where a walkthrough's camera passes through the model: through anything walking would stop at, such as a wall, glazing, a column, a slab or a roof, which would show the inside of the geometry on screen. Doors, furniture and room masses don't count, as walking passes through them. Each issue gives the first moment and point of one pass. The camera itself does not move.",
9402
+ "examplePrompts": [
9403
+ "Does the walkthrough go through any walls?",
9404
+ "Check the camera path for clashes and fix them\n\n# Example\n```ts\nconst { issues } = await snaptrude.presentation.walkthroughs.listIssues(walkthroughId)\nfor (const issue of issues) console.log(issue.timeInSeconds, issue.componentId)\n```"
9405
+ ],
9406
+ "argsType": "string",
9407
+ "resultType": "PluginWalkthroughListIssuesResult"
9408
+ },
9409
+ {
9410
+ "path": "presentation.walkthroughs.listJobs",
9411
+ "namespace": "presentation.walkthroughs",
9412
+ "summary": "List export jobs, newest first: running ones and those that finished in the last 30 minutes.",
9413
+ "examplePrompts": [
9414
+ "Which video exports are running?\n\n# Example\n```ts\nconst { jobs } = await snaptrude.presentation.walkthroughs.listJobs()\n```"
9415
+ ],
9416
+ "argsType": null,
9417
+ "resultType": "PluginWalkthroughListJobsResult"
9418
+ },
9419
+ {
9420
+ "path": "presentation.walkthroughs.reorderKeyframe",
9421
+ "namespace": "presentation.walkthroughs",
9422
+ "summary": "Move a keyframe to position `index` in the walkthrough's order (the `presentation.sheets.reorder` convention). A preset walkthrough becomes ordinary. Write-gated; one undo step.",
9423
+ "examplePrompts": [
9424
+ "Visit the terrace before the living room\n\n# Example\n```ts\nawait snaptrude.presentation.walkthroughs.reorderKeyframe(walkthroughId, keyframeId, 0)\n```"
9425
+ ],
9426
+ "argsType": "string",
9427
+ "resultType": "PluginWalkthrough"
9428
+ },
9429
+ {
9430
+ "path": "presentation.walkthroughs.sample",
9431
+ "namespace": "presentation.walkthroughs",
9432
+ "summary": "Sample where the camera is and which way it looks at given moments of a walkthrough's playback, without moving the camera.",
9433
+ "examplePrompts": [
9434
+ "Where is the camera halfway through the walkthrough?",
9435
+ "Check how high the camera flies over the roof\n\n# Example\n```ts\nconst walkthrough = await snaptrude.presentation.walkthroughs.get(walkthroughId)\nconst { samples } = await snaptrude.presentation.walkthroughs.sample(walkthroughId, [\n0,\nwalkthrough!.durationInSeconds / 2,\n])\n```"
9436
+ ],
9437
+ "argsType": "string",
9438
+ "resultType": "PluginWalkthroughSampleResult"
9439
+ },
9440
+ {
9441
+ "path": "presentation.walkthroughs.start",
9442
+ "namespace": "presentation.walkthroughs",
9443
+ "summary": "Play a walkthrough in the viewport, from the start or `options.fromTimeInSeconds`. Arrow-key navigation pauses while it plays; {@linkcode PluginPresentationWalkthroughsApi.stop} puts the camera back where it was. Switches from 2D plan view to 3D first. Resolves once playback has started; a walkthrough that is already playing keeps playing.",
9444
+ "examplePrompts": [
9445
+ "Play the walkthrough",
9446
+ "Show me the orbit\n\n# Example\n```ts\nawait snaptrude.presentation.walkthroughs.start(walkthroughId)\n```"
9447
+ ],
9448
+ "argsType": "string",
9449
+ "resultType": "PluginWalkthroughPlaybackState"
9450
+ },
9451
+ {
9452
+ "path": "presentation.walkthroughs.stop",
9453
+ "namespace": "presentation.walkthroughs",
9454
+ "summary": "Stop playback and put the camera back where it was before it started. A no-op when nothing is playing.",
9455
+ "examplePrompts": [
9456
+ "Stop the walkthrough\n\n# Example\n```ts\nawait snaptrude.presentation.walkthroughs.stop()\n```"
9457
+ ],
9458
+ "argsType": null,
9459
+ "resultType": "PluginWalkthroughPlaybackState"
9460
+ },
9461
+ {
9462
+ "path": "presentation.walkthroughs.update",
9463
+ "namespace": "presentation.walkthroughs",
9464
+ "summary": "Change a walkthrough: only the fields given change. Speed and duration are two ways to set one thing, so give at most one of them. `preset` changes a preset walkthrough's camera move and makes its keyframes again, keeping its duration; changing `isLoop` of a preset walkthrough makes it ordinary. Write-gated; one undo step.",
9465
+ "examplePrompts": [
9466
+ "Slow the walkthrough down to 30 seconds",
9467
+ "Rename it to \"Evening approach\"",
9468
+ "Make the orbit go the other way round and higher up\n\n# Example\n```ts\nawait snaptrude.presentation.walkthroughs.update(walkthroughId, {\ndurationInSeconds: 30,\nisRepeating: true,\n})\n```"
9469
+ ],
9470
+ "argsType": "string",
9471
+ "resultType": "PluginWalkthrough"
9472
+ },
9473
+ {
9474
+ "path": "presentation.walkthroughs.updateKeyframe",
9475
+ "namespace": "presentation.walkthroughs",
9476
+ "summary": "Change a keyframe: only the fields given change. A preset walkthrough becomes ordinary. Write-gated; one undo step.",
9477
+ "examplePrompts": [
9478
+ "Pause for three seconds at the second point",
9479
+ "Make the last point look at the fireplace\n\n# Example\n```ts\nawait snaptrude.presentation.walkthroughs.updateKeyframe(walkthroughId, keyframeId, {\nlook: { mode: \"target\", target: { x: 5, y: 4, z: 10 } },\n})\n```"
9480
+ ],
9481
+ "argsType": "string",
9482
+ "resultType": "PluginWalkthrough"
9483
+ },
7920
9484
  {
7921
9485
  "path": "program.adjacency.compute",
7922
9486
  "namespace": "program.adjacency",