@snaptrude/plugin-core 0.9.13 → 0.9.15

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 (101) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/api-manifest.full.json +8930 -0
  3. package/api-manifest.json +1002 -55
  4. package/dist/api/core/geom/create/index.d.ts +935 -100
  5. package/dist/api/core/geom/create/index.d.ts.map +1 -1
  6. package/dist/api/core/geom/query/brep.d.ts +59 -2
  7. package/dist/api/core/geom/query/brep.d.ts.map +1 -1
  8. package/dist/api/core/geom/query/curve.d.ts +59 -35
  9. package/dist/api/core/geom/query/curve.d.ts.map +1 -1
  10. package/dist/api/core/geom/query/edge.d.ts +2 -2
  11. package/dist/api/core/geom/query/face.d.ts +105 -0
  12. package/dist/api/core/geom/query/face.d.ts.map +1 -1
  13. package/dist/api/core/geom/update/contour.d.ts +2 -0
  14. package/dist/api/core/geom/update/contour.d.ts.map +1 -1
  15. package/dist/api/core/geom/update/curve.d.ts +5 -5
  16. package/dist/api/core/geom/update/profile.d.ts +3 -1
  17. package/dist/api/core/geom/update/profile.d.ts.map +1 -1
  18. package/dist/api/core/handles/index.d.ts +4 -2
  19. package/dist/api/core/handles/index.d.ts.map +1 -1
  20. package/dist/api/core/index.d.ts +4 -4
  21. package/dist/api/core/index.d.ts.map +1 -1
  22. package/dist/api/core/layers.d.ts +377 -255
  23. package/dist/api/core/layers.d.ts.map +1 -1
  24. package/dist/api/core/proposals/index.d.ts +4 -3
  25. package/dist/api/core/proposals/index.d.ts.map +1 -1
  26. package/dist/api/core/storeys/index.d.ts +12 -0
  27. package/dist/api/core/storeys/index.d.ts.map +1 -1
  28. package/dist/api/core/storeys/layers.d.ts +408 -0
  29. package/dist/api/core/storeys/layers.d.ts.map +1 -0
  30. package/dist/api/core/tags.d.ts +3 -3
  31. package/dist/api/design/create/bulk-items.d.ts +44 -0
  32. package/dist/api/design/create/bulk-items.d.ts.map +1 -1
  33. package/dist/api/design/create/grids.d.ts +1 -1
  34. package/dist/api/design/create/index.d.ts +31 -3
  35. package/dist/api/design/create/index.d.ts.map +1 -1
  36. package/dist/api/design/family.d.ts +67 -10
  37. package/dist/api/design/family.d.ts.map +1 -1
  38. package/dist/api/design/query/geometry/index.d.ts +33 -5
  39. package/dist/api/design/query/geometry/index.d.ts.map +1 -1
  40. package/dist/api/design/query/grids.d.ts +3 -3
  41. package/dist/api/design/query/index.d.ts +47 -0
  42. package/dist/api/design/query/index.d.ts.map +1 -1
  43. package/dist/api/design/query/spaces.d.ts +5 -5
  44. package/dist/api/design/selection/index.d.ts +2 -2
  45. package/dist/api/design/update/index.d.ts +172 -0
  46. package/dist/api/design/update/index.d.ts.map +1 -1
  47. package/dist/api/entity/referenceLine.d.ts +1 -1
  48. package/dist/api/entity/space.d.ts +4 -4
  49. package/dist/api/entity/story.d.ts +4 -4
  50. package/dist/api/presentation/aiInspiration.d.ts +13 -13
  51. package/dist/api/presentation/camera.d.ts +265 -0
  52. package/dist/api/presentation/camera.d.ts.map +1 -0
  53. package/dist/api/presentation/index.d.ts +5 -0
  54. package/dist/api/presentation/index.d.ts.map +1 -1
  55. package/dist/api/presentation/placedViews.d.ts +1 -1
  56. package/dist/api/presentation/views.d.ts +3 -1
  57. package/dist/api/presentation/views.d.ts.map +1 -1
  58. package/dist/api/program/site.d.ts +4 -4
  59. package/dist/api/program/spreadsheet.d.ts +4 -4
  60. package/dist/handles.d.ts +13 -3
  61. package/dist/handles.d.ts.map +1 -1
  62. package/dist/index.cjs +3638 -2958
  63. package/dist/index.cjs.map +1 -1
  64. package/dist/index.d.ts +2 -0
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +3558 -2955
  67. package/dist/index.js.map +1 -1
  68. package/dist/massParameters.d.ts +287 -0
  69. package/dist/massParameters.d.ts.map +1 -0
  70. package/dist/massParameters.ops.d.ts +138 -0
  71. package/dist/massParameters.ops.d.ts.map +1 -0
  72. package/package.json +2 -1
  73. package/scripts/check-dist-fresh.mjs +22 -0
  74. package/scripts/generate-manifest.test.mjs +14 -3
  75. package/src/api/core/geom/create/index.ts +1030 -101
  76. package/src/api/core/geom/query/brep.ts +60 -2
  77. package/src/api/core/geom/query/curve.ts +26 -2
  78. package/src/api/core/geom/query/edge.ts +2 -2
  79. package/src/api/core/geom/query/face.ts +109 -4
  80. package/src/api/core/geom/update/contour.ts +2 -0
  81. package/src/api/core/geom/update/profile.ts +2 -0
  82. package/src/api/core/handles/index.ts +3 -2
  83. package/src/api/core/index.ts +4 -4
  84. package/src/api/core/layers.ts +380 -173
  85. package/src/api/core/proposals/index.ts +4 -3
  86. package/src/api/core/storeys/index.ts +14 -0
  87. package/src/api/core/storeys/layers.ts +339 -0
  88. package/src/api/design/create/bulk-items.ts +68 -0
  89. package/src/api/design/create/index.ts +30 -4
  90. package/src/api/design/family.ts +68 -10
  91. package/src/api/design/query/geometry/index.ts +35 -4
  92. package/src/api/design/query/index.ts +50 -0
  93. package/src/api/design/update/index.ts +186 -1
  94. package/src/api/presentation/camera.ts +289 -0
  95. package/src/api/presentation/index.ts +5 -0
  96. package/src/api/presentation/views.ts +3 -1
  97. package/src/handles.ts +18 -3
  98. package/src/index.ts +2 -0
  99. package/src/massParameters.ops.ts +246 -0
  100. package/src/massParameters.ts +427 -0
  101. package/test/massParameters.test.mjs +952 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,56 @@
1
1
  # @snaptrude/plugin-core
2
2
 
3
+ ## 0.9.15
4
+
5
+ ### Patch Changes
6
+
7
+ - 4bfe118: `core.layers`: the layers in the Layers panel in the left sidebar. `list()`, `get(layerId)` and `getActive()` read the active design option's layers (default first, with `objectCount`); `create(name)` adds a layer to the active option (undoable, not activated); `rename(layerId, name)` renames (not undoable, default layer refused); `update(layerId, { locked?, hidden? })` locks and hides per-user (undoable; other option keys are rejected); `setActive(layerId)` sets where new objects land; `delete(layerId, { deleteObjects: true } | { targetLayerId })` deletes with an explicit disposition (server-validated; refused for the default, last, locked or shared layer; treat as permanent); `assign(componentIds, layerId)` moves objects onto a layer (undoable, locked objects skipped). Layer ids are the ids `core.proposals` records list as `bucketIds` / `defaultBucketId` (the proposals docs now point there).
8
+
9
+ MOVED (compatible at runtime): the former `core.layers` (each storey's sketch surfaces and imported underlays, ids `ly_*`) is now `core.storeys.layers`, with the same methods (`list`, `get`, `getActive`, `setActive`, `update`) and signatures. Plugins that still call the old shape on `core.layers` (`list(storeyId)`, `getActive(storeyId)`, `get` / `setActive` / `update(layerId, hidden)` with a `ly_*` id) keep working: the host routes those calls to `core.storeys.layers`. They are deprecated; new code should call `snaptrude.core.storeys.layers.*` directly. Only the TypeScript names change: `PluginCoreLayersApi` -> `PluginCoreStoreysLayersApi`, `PluginCoreLayer` -> `PluginCoreStoreyLayer`, `PluginCoreLayerType` -> `PluginCoreStoreyLayerType`, and `PluginCoreLayers*Args` / `PluginCoreLayers*Result` -> `PluginCoreStoreysLayers*Args` / `PluginCoreStoreysLayers*Result`; the names `PluginCoreLayersApi`, `PluginCoreLayer` and `PluginCoreLayers*` now describe the Layers panel.
10
+
11
+ - 80c1a2b: `presentation.camera`: the Present-mode canvas camera. `capture()` screenshots what the Present canvas currently shows (the visible area at the current pan and zoom, as a base64 data URL with its pixel size); `get()` reads pan, zoom, the visible page rectangle and the sheet on screen; `set({ x?, y?, zoom? })` pans and zooms; `zoomToFit()` frames every sheet; `zoomTo(shapeId)` frames one sheet or placed shape. View-state only (not write-gated, not undoable); requires Present mode to be open.
12
+ - 0e3df96: Raked and gabled walls: `design.create.walls` takes a trailing `topProfiles` argument and `design.create.wallRuns` items a `topProfiles` field — one optional `PluginWallTopProfile` per centerline curve (`[{ along, height }, …]`, `along` 0→1, at least two points). The wall is built as a real Wall with the given top edge (two points = single slope, three = gable), mitred at run corners, in the same single undo entry; it is non-parametric (the parametric wall graph does not rebuild it), like a wall whose top edge was dragged by hand.
13
+ - d84ea44: `design.update.setObjectType(components, objectType)`: convert masses into real walls, roofs, slabs or floors (`objectType` uses the `design.query` `type` vocabulary: `"wall" | "roof" | "slab" | "floor"`) — the properties panel's Object Type dropdown, headless, keeping the geometry exactly as built (no flattening to a default thickness) and keeping the component's handle. Instance families convert together. One undoable operation.
14
+
15
+ ## 0.9.14
16
+
17
+ ### Patch Changes
18
+
19
+ - 22c919b: Curved-geometry contracts — spline curves, primitive solids, curved-surface reads, and the shared mass-parameter record.
20
+ - **`spline` handle kind** — `SplineHandle` joins `CurveHandle` (`line | arc | spline`), so every `core.geom.query.curve` member and `profileFromCurves` take a spline, and joins `ArenaKind`, so `core.handles.release`, `releaseAll("spline")` and `endScope`'s `retain` list accept one. Closed spline loops never exist as a single handle: a closed run is authored directly as a profile.
21
+ - **`core.geom.create.splineFromPoints`** — an open NURBS curve passing exactly THROUGH 3..256 ordered points (interpolated, not control points).
22
+ - **`core.geom.create.profileFromSplinePoints`** — the closed counterpart: a smooth closed profile through the points, ready for `contourFromProfile` → extrude/loft/sweep. No mixed straight/spline input in v1 — compose those with `profileFromCurves`.
23
+ - **Primitive solids** — `brepFromSphere`, `brepFromCylinder`, `brepFromCone` (optional `topRadius`, default 0 = apex) and `brepFromTorus`. Positional plain `{x, y, z}` args matching the `brepFromRevolution` family; `base` is the bottom-cap centre for cylinder/cone, `centre` for sphere/torus. Each result carries an exact analytic surface and a creation recipe.
24
+ - **`core.geom.create.brepFromSweep`** — `options.transition` gains `"round"` beside `"miter"` and `{ bevel }`: corners carried around a tangent circular arc, so the corner patch is a real curved surface.
25
+ - **`core.geom.create.brepFromRevolution`** — JSDoc rewritten to the implemented gate: segments may run at any angle to the axis (an incline revolves into a cone, an arc into a sphere or torus section). Two rules, checked in this order: the axis must lie in the profile's plane or run parallel to it (a parallel axis offset from the plane is judged by its projection into the plane, as 0.9.13 built it; a skew axis is `PRECONDITION_FAILED`), then the whole profile must stay on one side of the axis — touching it is legal (dome poles, cylinder inner edges), crossing it is not (`VALIDATION`).
26
+ - **`core.geom.query.brep.getEdgeCurve`** — a fourth `"spline"` variant (`degree`, `controlPoints`, expanded `knots`, endpoints, `length`), so reading back a spline edge no longer throws.
27
+ - **`core.geom.query.brep.getVolume`** — exact enclosed volume integrated from the surfaces, free of tessellation error.
28
+ - **`core.geom.query.face.getSurfaceKind`** — `"plane" | "cylinder" | "cone" | "sphere" | "torus" | "spline" | null`: the one read that tells a real curved face from a faceted approximation of one.
29
+ - **`core.geom.query.curve`** — class JSDoc gains the per-member spline support table; unsupported members refuse with `PRECONDITION_FAILED` rather than answering wrongly.
30
+ - **`design.query.getParameters`** — read a mass's creation-parameter record (`kind` + values, world coordinates), or `null` for geometry with no honest recipe.
31
+ - **`massParameters`** (new module, exported from the package root) — `MassParametersRecord` / `ContourRecord` / `ProfileRecord` / `CurveRecord` / `XYZ` types and Zod schemas, shared verbatim by plugin-core, the host and the agent runtime. Pure JSON, one `{x, y, z}` point shape, array bounds as contract (through-points ≤ 256, spline poles ≤ 512, sweep path ≤ 512, loft sections ≤ 32, profile curves ≤ 256, contour holes ≤ 64).
32
+
33
+ Editing what was made, and rounding it:
34
+ - **`design.update.parameters(component, patch)`** — the write pair of `design.query.getParameters`: patch a parametric mass's creation recipe and the solid rebuilds in place, keeping its id, storey, materials, position and linked copies. `patch` is a partial of the record's `values` (keys you omit are unchanged; each key you send replaces that value whole — a scalar, a point, a list or a record (`contour`, `profile`, `sections`, `path`, `options`, `operations`) alike — so send the complete value you read) or the whole record read back and edited; `kind` and `version` are immutable; the reply is the complete post-edit record in world coordinates. `PRECONDITION_FAILED` when the component is not a mass (a family instance: use `design.family.setParameters`), when the mass carries no live recipe, when it was scaled (undo the scale and call again), when it or a linked copy is locked (unlock them — the recipe is intact, and `getParameters` cannot see the lock), when a recorded operation's selectors no longer resolve, or when the edit is the first twist or taper, or the straightening, of an extrusion that carries a shell, fillet or chamfer (the faces renumber: send the twist keys with `operations: []` first, then re-apply), `VALIDATION` for argument errors, `OPERATION_FAILED` for a kernel refusal that leaves the model unchanged.
35
+ - **`core.geom.create.splineFromControlPoints(controlPoints, { degree? })`** — an open NURBS curve DEFINED BY 3..512 control points (pulled toward, not through, the interior ones), degree 2 or 3, clamped uniform knots computed host-side. It is the write pair of a `splinePoles` curve record, so a curve read back off a mass can have its poles moved and be rebuilt.
36
+ - **`brepFromFillet` / `brepFromChamfer` / `brepFromShell` / `brepFromOffset` accept CURVED solids.** The straight-edges-only and no-shared-vertex rules are gone: edges may be straight, circular or spline, and adjacent edges (corner blends) are allowed. The JSDoc carries the rim-selection recipe — `brep.listEdges` → `edge.listFaces` → `face.getSurfaceKind` on both faces (`listEdgesBetween` takes two VERTEX indices and is not a face-pair lookup) — and the new refusals: `VALIDATION` where there is no crease (a seam, or two patches of one smooth surface), and — for `brepFromFillet` alone, the one operation with a per-edge kernel verdict — `PRECONDITION_FAILED` naming the edge the kernel could not blend. An over-size chamfer, shell or offset is `OPERATION_FAILED` with a hint asking for a smaller distance/thickness and no edge or face named. Selection caps are RECORD caps, not call caps: the wire schemas accept any selection size, exactly as 0.9.x did — a fillet/chamfer of more than 64 edges or a shell opening more than 32 faces still builds, but the result carries no parameter record (the recipe-less fallback).
37
+ - **`MassParametersRecord` gains an optional `operations` list** (`version` stays 1) — a fillet, chamfer, shell or offset of a recorded solid keeps the record, with `kind` still the base constructor and the operation appended. `OperationRecord`, `EdgeSelector`, `FaceSelector`, `TopologyCounts`, `SurfaceKind`, `CurveKind`, `ParameterPatchValue` and `MassParametersPatch` join the module, with caps `MAX_OPERATIONS` 8, `MAX_OPERATION_EDGES` 64, `MAX_OPERATION_FACES` 32. Selectors are TOPOLOGICAL (edge index + its face pair + curve kind; face index + surface kind + edge count) so they survive a scalar edit of the base and trip on a reorder.
38
+ - **`design.query.geometry.getBrep`** — additive `options.frame` (`"local"` default, unchanged 0.9.x behaviour; `"world"` places the solid where it sits in the scene, for booleans between scene masses and for fillet/shell/offset/chamfer results committed back beside their source).
39
+
40
+ Freeform surfaces, sweeps, patches and variable blends:
41
+ - **`core.geom.create.brepFromSweepAlongCurve(profile, path, options?)`** — sweeps a cross-section along a SMOOTH path of 1..64 curves (splines, arcs and lines chained end to end), where `brepFromSweep` takes a polyline. `options.orientation` picks how the profile turns: `"upright"` (default) keeps its up aligned with world up, `"frenet"` rolls it with the curvature, `"fixed"` carries the authored frame along unchanged — which is a pure translation, and so is also how a footprint is bent along an arc. `options.rail`, a second guide curve, carries the profile along a second path; a closed rail, or a rail together with `orientation`, is `VALIDATION`, and a railed sweep records nothing (`getParameters` answers `null`). Otherwise it records as the one new kind `sweepAlongCurve` (`{ profile, path: CurveRecord[], options? }`, `version` still 1), so the orientation stays editable through `design.update.parameters`; the shipped `sweep` kind and its `XYZ[]` path are untouched.
42
+ - **`core.geom.create.brepFromPatch(boundary, thickness, options?)`** — stretches a surface across a closed loop of 3..16 boundary items and thickens it into a SOLID in one call: a roof over four curves, a canopy between two rails, a blend across a gap. A boundary item is a curve or an `OwnedEdge` (`{ edge, brep, face? }` — an edge handle cannot name its own solid, and one patch may meet two of them). `options.continuity: "tangent"` leaves the named face smoothly and then REQUIRES `face` on every item, because a solid edge lies between two faces and only the caller knows which one to continue; `interiorPoints` (≤ 64) pull the surface through them, and `side` picks which way the solid grows. There is no sheet handle and no surface object: only solids reach the canvas. The result carries no recipe — `design.query.getParameters` is `null` and the mass label is its provenance.
43
+ - **`core.geom.create.brepsFromSplitByFace(brep, toolFace, toolBrep)`** — the curved twin of `brepsFromSplit`, which cuts with a plane: cuts a solid with a FACE of another solid and returns the pieces largest first. Both breps must be in the same frame — read scene masses with `getBrep(x, { frame: "world" })`. A cut that does not pass all the way through is a `VALIDATION`, not a silent single piece.
44
+ - **`core.geom.create.curvesFromProjection(curve, face, faceBrep)`** — drops a curve onto a face along its normal and returns the runs that land on it, one curve per connected run, or `[]` when the curve misses the face; `VALIDATION` when the curve's midpoint lies on the face's surface (no drop direction).
45
+ - **`core.geom.query.face.getArea(face, brep)` / `.getCentroid(face, brep)`** — exact area (internal units squared) and centre of area of one face, integrated off its surface rather than its facets. These are the two reads that tell freeform faces apart, where every face answers `"spline"` and a kind census returns nothing. Both take the parent brep, owner last, exactly like `listVertexPositions(face, brep)`.
46
+ - **`brepFromLoft`** gains `options.smoothing` — `"ruled"` (default) is today's straight-sided loft byte for byte; `"smooth"` lofts every section in ONE pass, so the sides curve continuously through the intermediates. `LoftOptions` carries it into the record.
47
+ - **`brepFromExtrusion`** gains a trailing `options?: { twistInDegrees?, topScale?, levels? }`, and the shipped `extrusion` record gains the same three optional values (`version` still 1) — a twisted or tapered tower stays the SAME kind, so "twist this tower" is an edit rather than a delete-and-rebuild. `twistInDegrees` is an angle and `topScale` a ratio: neither is ever converted by a unit factor. `levels` is a count (each step may turn at most 22.5°), resolved at create time and written into the record, so an existing mass regenerates from the record and never from a heuristic that may change under it. All three absent is the straight extrusion, byte for byte.
48
+ - **`brepFromFillet`** gains a trailing `options?: { endRadius?, radii? }` — a VARIABLE blend. `endRadius` interpolates linearly from `radius` along the whole tangent chain the one edge you pass belongs to (an open chain: on a closed rim a law whose two ends differ is refused, for `endRadius` and `radii` alike). `radii` is the sampled form instead: 2..32 rows of `{ position, radius }` with `position` the normalised ARC LENGTH along that contour from the passed edge's start — a ratio, never converted — strictly increasing. Either form takes exactly ONE edge, they are mutually exclusive, and both round-trip on the fillet `OperationRecord`.
49
+ - **Face and edge selectors gain `at?`** — where the face or edge sits inside its solid, its centroid (an edge's midpoint) over the solid's bounding-box extent, a ratio per axis. It is written only when index, kind and counts cannot tell two of them apart, which on a freeform solid is the common case, and it is verified — never searched — on replay, so a record written without it resolves byte-identically.
50
+ - **`ParameterPatchValue`** widens with `SweepAlongCurveRecordOptions` and `CurveRecord[]` (the `sweepAlongCurve` path). It only ever widens; every value it took before it still takes.
51
+ - **`massParameters.ops` — a second record module.** `EdgeSelector`, `FaceSelector`, `TopologyCounts`, `OperationRecord`, `ParameterPatchValue` and the operation caps (plus the new `MAX_FILLET_LAW_SAMPLES` 32) move into `src/massParameters.ops.ts` and are re-exported from `massParameters` and from the package root — no import path changes. The split keeps each module small enough for an agent to read whole. New caps beside the shipped ones: `MAX_PATH_CURVES` 64, `MAX_TWIST_LEVELS` 64, and — on the creator, because a patch has no record — `MAX_PATCH_BOUNDARY_CURVES` 16 and `MAX_PATCH_INTERIOR_POINTS` 64.
52
+ - **`design.family.getParameters`** gains an optional `parts: PluginFamilyPart[]` — each placed part's `name`, the `engine` that built it (`"kernel"` for a curved sketch built exactly, `"mesh"` otherwise) and, on kernel parts, a read-only part-local `brep` for the face-kind census. `design.query.geometry.getBrep` keeps returning `null` for a family.
53
+
3
54
  ## 0.9.13
4
55
 
5
56
  ### Patch Changes
@@ -51,6 +102,16 @@
51
102
  - `core.tags.listForComponents(componentIds?)` — every space's tags + colors in one round-trip.
52
103
  - `program.departments` — `targetCount` on the department record and `setTargetCount(departmentId, targetCount)`, the count twin of `setTargetArea` (the engine stores both on the department, like on labels).
53
104
 
105
+ ## 0.9.8
106
+
107
+ ### Patch Changes
108
+
109
+ - 7fd374e: Add `hinge` to `design.create.door` / `window` (in `options`), `doors[]` / `windows[]` items and `opening`: a world point near the jamb the leaf is hinged on; the placed item is reflected along the wall when the catalog item's authored hinge side would land on the other jamb — one undo entry, no extra command.
110
+
111
+ **BEHAVIOUR FIX — `facing`.** The host placed the swing on the side OPPOSITE the `facing` point (the interactive tool's cursor convention) while the contract said "the room it opens into". The host now matches the contract: the leaf swings open toward `facing`. Callers that passed a point on the room side get the documented result; nothing in the contract changed shape.
112
+
113
+ `design.doors.getSwingDirection` is now derived from the placed mesh's orientation (hand as seen standing on the swing side facing the door); it previously read a scaling sign that placement never sets and reported `"left"` for every door. `mirror` axis docs now say what each axis does to a door (`'x'` swing side, `'z'` hinged jamb, `'y'` upside down); behaviour unchanged.
114
+
54
115
  ## 0.9.7
55
116
 
56
117
  ### Patch Changes