@snaptrude/plugin-core 0.9.7 → 0.9.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@snaptrude/plugin-core",
3
- "version": "0.9.7",
3
+ "version": "0.9.8",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -77,15 +77,17 @@ export type PluginCreateFloorItems = z.infer<typeof PluginCreateFloorItems>
77
77
  * One door to place via {@linkcode PluginDesignCreateApi.doors} — the same
78
78
  * fields as a `"door"` {@linkcode PluginDesignCreateOpeningOptions}, so the
79
79
  * size overrides singular {@linkcode PluginDesignCreateApi.door} lacks are
80
- * available per item. `facing` is a WORLD POINT on the side of the wall the
81
- * door opens into, not a direction. Rejects unknown fields.
80
+ * available per item. `facing` and `hinge` are WORLD POINTS, not directions:
81
+ * `facing` on the side of the wall the door swings open to, `hinge` near the
82
+ * jamb it is hinged on. Rejects unknown fields.
82
83
  *
83
84
  * | Property | Type | Description |
84
85
  * |---|---|---|
85
86
  * | `catalogId` | `string` | Library id: team `_id` or general `fullName` |
86
87
  * | `hostWall` | {@linkcode ComponentHandle} | The wall to host the door |
87
88
  * | `position` | {@linkcode Vec3Handle} | World point projected onto the wall |
88
- * | `facing` | {@linkcode Vec3Handle}? | World point on the side the door faces (default engine-chosen) |
89
+ * | `facing` | {@linkcode Vec3Handle}? | World point on the side the door swings open to (default engine-chosen) |
90
+ * | `hinge` | {@linkcode Vec3Handle}? | World point near the jamb the door is hinged on (default the catalog item's authored side) |
89
91
  * | `label` | `string`? | Instance name (optional) |
90
92
  * | `width` | `number`? | Width override (Snaptrude units, > 0; default the catalog item's) |
91
93
  * | `height` | `number`? | Height override (Snaptrude units, > 0; default the catalog item's) |
@@ -106,8 +108,9 @@ export type PluginCreateDoorItems = z.infer<typeof PluginCreateDoorItems>
106
108
  * One window to place via {@linkcode PluginDesignCreateApi.windows} — the same
107
109
  * fields as a `"window"` {@linkcode PluginDesignCreateOpeningOptions}.
108
110
  * `sillHeight` is a nonnegative Snaptrude-unit distance from the host wall's
109
- * base to the BOTTOM of the window, not to its center. `facing` is a WORLD
110
- * POINT on the desired side of the wall, not a direction. Rejects unknown
111
+ * base to the BOTTOM of the window, not to its center. `facing` and `hinge`
112
+ * are WORLD POINTS, not directions: `facing` on the side of the wall the
113
+ * window opens to, `hinge` near the jamb it is hinged on. Rejects unknown
111
114
  * fields.
112
115
  *
113
116
  * | Property | Type | Description |
@@ -115,7 +118,8 @@ export type PluginCreateDoorItems = z.infer<typeof PluginCreateDoorItems>
115
118
  * | `catalogId` | `string` | Library id: team `_id` or general `fullName` |
116
119
  * | `hostWall` | {@linkcode ComponentHandle} | The wall to host the window |
117
120
  * | `position` | {@linkcode Vec3Handle} | World point projected onto the wall |
118
- * | `facing` | {@linkcode Vec3Handle}? | World point on the side the window faces (default engine-chosen) |
121
+ * | `facing` | {@linkcode Vec3Handle}? | World point on the side the window opens to (default engine-chosen) |
122
+ * | `hinge` | {@linkcode Vec3Handle}? | World point near the jamb the window is hinged on (default the catalog item's authored side) |
119
123
  * | `label` | `string`? | Instance name (optional) |
120
124
  * | `width` | `number`? | Width override (Snaptrude units, > 0; default the catalog item's) |
121
125
  * | `height` | `number`? | Height override (Snaptrude units, > 0; default the catalog item's) |
@@ -753,12 +753,15 @@ export abstract class PluginDesignCreateApi {
753
753
  * @param catalogId - Library id: team `_id` or general `fullName`
754
754
  * @param hostWall - The wall to host the door
755
755
  * @param position - World point projected onto the wall to locate the opening
756
- * @param options - Optional placement options: `label` — instance name
757
- * @param facing - World point selecting which side of the wall the door faces
758
- * (the room it opens into) the same convention as approaching the wall
759
- * from that side with the cursor in the interactive tool. Any point clearly
760
- * on that side works (e.g. the room's center). Default: the engine picks a
761
- * side (nondeterministic when `position` sits on the wall centerline).
756
+ * @param options - Optional placement options: `label` — instance name;
757
+ * `hinge` world point near the jamb the door is hinged on (the door is
758
+ * reflected along the wall so its hinged jamb is the one nearer this
759
+ * point; default the catalog item's authored hinge side)
760
+ * @param facing - World point on the side of the wall the door swings open
761
+ * to (the room it opens into — where the plan symbol draws the swing arc).
762
+ * Any point clearly on that side works (e.g. the room's center). Default:
763
+ * the engine picks a side (nondeterministic when `position` sits on the
764
+ * wall centerline).
762
765
  * @returns the {@linkcode ComponentHandle} of the placed door
763
766
  * @throws if the catalog id is unknown, the host is not a wall, the source
764
767
  * mesh fails to load, or the projected point falls **outside** the host wall
@@ -769,6 +772,7 @@ export abstract class PluginDesignCreateApi {
769
772
  * @examplePrompt Insert the entrance door into this wall
770
773
  * @examplePrompt Add a door to the wall and call it Entry-01
771
774
  * @examplePrompt Add a door that opens into the living room
775
+ * @examplePrompt Put a door here hinged on the north jamb
772
776
  *
773
777
  * # Example
774
778
  * ```ts
@@ -788,6 +792,14 @@ export abstract class PluginDesignCreateApi {
788
792
  * undefined,
789
793
  * await snaptrude.core.math.vec3.new(3, 0, 9),
790
794
  * )
795
+ * // …and hinged on the jamb nearest a point (here the +x end of the opening):
796
+ * const hingedRight = await snaptrude.design.create.door(
797
+ * entry.id,
798
+ * wall,
799
+ * await snaptrude.core.math.vec3.new(3, 0, 5),
800
+ * { hinge: await snaptrude.core.math.vec3.new(3.5, 0, 5) },
801
+ * await snaptrude.core.math.vec3.new(3, 0, 9),
802
+ * )
791
803
  * ```
792
804
  *
793
805
  * @performance For MORE THAN ONE door, call `design.create.doors(items[])` — one host
@@ -798,7 +810,7 @@ export abstract class PluginDesignCreateApi {
798
810
  catalogId: string,
799
811
  hostWall: ComponentHandle,
800
812
  position: Vec3Handle,
801
- options?: { label?: string },
813
+ options?: { label?: string; hinge?: Vec3Handle },
802
814
  facing?: Vec3Handle,
803
815
  ): PluginApiReturn<ComponentHandle>
804
816
 
@@ -821,8 +833,8 @@ export abstract class PluginDesignCreateApi {
821
833
  * @returns the placed doors as {@linkcode ComponentHandle}`[]`, in input order
822
834
  * @throws `VALIDATION` if the items fail the schema (empty array, more than
823
835
  * 1000 items, unknown fields, a non-positive `width`/`height`);
824
- * `HANDLE_INVALID` if a `position` or `facing` handle is unknown or
825
- * released; `PRECONDITION_FAILED` if a catalog id is unknown, a `hostWall`
836
+ * `HANDLE_INVALID` if a `position`, `facing` or `hinge` handle is unknown
837
+ * or released; `PRECONDITION_FAILED` if a catalog id is unknown, a `hostWall`
826
838
  * is not a wall / is locked / is not in the active proposal, the projected
827
839
  * point falls outside its host wall, or a door tool is active;
828
840
  * `OPERATION_FAILED` if a source mesh fails to load or placement fails
@@ -870,11 +882,14 @@ export abstract class PluginDesignCreateApi {
870
882
  * @param catalogId - Library id: team `_id` or general `fullName`
871
883
  * @param hostWall - The wall to host the window
872
884
  * @param position - World point projected onto the wall to locate the opening
873
- * @param options - Optional placement options: `label` — instance name
874
- * @param facing - World point selecting which side of the wall the window
875
- * faces (matters for asymmetric windows, e.g. casement swing) same
876
- * convention as {@linkcode PluginDesignCreateApi.door}. Default: the engine
877
- * picks a side (nondeterministic when `position` sits on the centerline).
885
+ * @param options - Optional placement options: `label` — instance name;
886
+ * `hinge` world point near the jamb the window is hinged on (same rule
887
+ * as {@linkcode PluginDesignCreateApi.door}; default the catalog item's
888
+ * authored side)
889
+ * @param facing - World point on the side of the wall the window opens to
890
+ * (matters for asymmetric windows, e.g. casement swing) — same convention
891
+ * as {@linkcode PluginDesignCreateApi.door}. Default: the engine picks a
892
+ * side (nondeterministic when `position` sits on the centerline).
878
893
  * @returns the {@linkcode ComponentHandle} of the placed window
879
894
  * @throws if the catalog id is unknown, the host is not a wall, the source
880
895
  * mesh fails to load, or the projected point falls **outside** the host wall
@@ -906,7 +921,7 @@ export abstract class PluginDesignCreateApi {
906
921
  catalogId: string,
907
922
  hostWall: ComponentHandle,
908
923
  position: Vec3Handle,
909
- options?: { label?: string },
924
+ options?: { label?: string; hinge?: Vec3Handle },
910
925
  facing?: Vec3Handle,
911
926
  ): PluginApiReturn<ComponentHandle>
912
927
 
@@ -931,8 +946,8 @@ export abstract class PluginDesignCreateApi {
931
946
  * @returns the placed windows as {@linkcode ComponentHandle}`[]`, in input order
932
947
  * @throws `VALIDATION` if the items fail the schema (empty array, more than
933
948
  * 1000 items, unknown fields, a non-positive `width`/`height`, a negative
934
- * `sillHeight`); `HANDLE_INVALID` if a `position` or `facing` handle is
935
- * unknown or released; `PRECONDITION_FAILED` if a catalog id is unknown, a
949
+ * `sillHeight`); `HANDLE_INVALID` if a `position`, `facing` or `hinge`
950
+ * handle is unknown or released; `PRECONDITION_FAILED` if a catalog id is unknown, a
936
951
  * `hostWall` is not a wall / is locked / is not in the active proposal, the
937
952
  * projected point falls outside its host wall, or a window tool is active;
938
953
  * `OPERATION_FAILED` if a source mesh fails to load or placement fails
@@ -975,7 +990,7 @@ export abstract class PluginDesignCreateApi {
975
990
  * its center. Placement is asynchronous and committed as one undoable creation.
976
991
  *
977
992
  * @param options - Opening kind, catalog and host references, placement, and
978
- * optional facing, label, and dimensions
993
+ * optional facing (swing side), hinge (hinged jamb), label, and dimensions
979
994
  * @returns the {@linkcode ComponentHandle} of the placed door or window
980
995
  * @throws if the options are invalid, the catalog id is unknown, the host is
981
996
  * not a wall, loading fails, or the opening cannot be placed on the host
@@ -1576,7 +1591,8 @@ export type PluginDesignCreateFurnitureArgs = z.infer<
1576
1591
  * | `hostWall` | {@linkcode ComponentHandle} | The wall to host the door |
1577
1592
  * | `position` | {@linkcode Vec3Handle} | World point projected onto the wall |
1578
1593
  * | `label` | `string`? | Instance name (optional) |
1579
- * | `facing` | {@linkcode Vec3Handle}? | World point on the side of the wall the door faces (default engine-chosen) |
1594
+ * | `facing` | {@linkcode Vec3Handle}? | World point on the side of the wall the door swings open to (default engine-chosen) |
1595
+ * | `hinge` | {@linkcode Vec3Handle}? | World point near the jamb the door is hinged on (default the catalog item's authored side) |
1580
1596
  */
1581
1597
  export const PluginDesignCreateDoorArgs = z.object({
1582
1598
  catalogId: z.string().min(1),
@@ -1584,12 +1600,13 @@ export const PluginDesignCreateDoorArgs = z.object({
1584
1600
  position: Vec3Handle,
1585
1601
  label: z.string().optional(),
1586
1602
  facing: Vec3Handle.optional(),
1603
+ hinge: Vec3Handle.optional(),
1587
1604
  })
1588
1605
  export type PluginDesignCreateDoorArgs = z.infer<
1589
1606
  typeof PluginDesignCreateDoorArgs
1590
1607
  >
1591
- // TRANSPORT: positional signature shipped — facing is the trailing 5th arg:
1592
- // door(catalogId: string, hostWall: ComponentHandle, position: Vec3Handle, options?: { label? }, facing?: Vec3Handle)
1608
+ // TRANSPORT: positional signature shipped — facing is the trailing 5th arg, hinge rides in options:
1609
+ // door(catalogId: string, hostWall: ComponentHandle, position: Vec3Handle, options?: { label?, hinge? }, facing?: Vec3Handle)
1593
1610
 
1594
1611
  // ---------------------------------------------------------------------------
1595
1612
  // window
@@ -1604,7 +1621,8 @@ export type PluginDesignCreateDoorArgs = z.infer<
1604
1621
  * | `hostWall` | {@linkcode ComponentHandle} | The wall to host the window |
1605
1622
  * | `position` | {@linkcode Vec3Handle} | World point projected onto the wall |
1606
1623
  * | `label` | `string`? | Instance name (optional) |
1607
- * | `facing` | {@linkcode Vec3Handle}? | World point on the side of the wall the window faces (default engine-chosen) |
1624
+ * | `facing` | {@linkcode Vec3Handle}? | World point on the side of the wall the window opens to (default engine-chosen) |
1625
+ * | `hinge` | {@linkcode Vec3Handle}? | World point near the jamb the window is hinged on (default the catalog item's authored side) |
1608
1626
  */
1609
1627
  export const PluginDesignCreateWindowArgs = z.object({
1610
1628
  catalogId: z.string().min(1),
@@ -1612,12 +1630,13 @@ export const PluginDesignCreateWindowArgs = z.object({
1612
1630
  position: Vec3Handle,
1613
1631
  label: z.string().optional(),
1614
1632
  facing: Vec3Handle.optional(),
1633
+ hinge: Vec3Handle.optional(),
1615
1634
  })
1616
1635
  export type PluginDesignCreateWindowArgs = z.infer<
1617
1636
  typeof PluginDesignCreateWindowArgs
1618
1637
  >
1619
- // TRANSPORT: positional signature shipped — facing is the trailing 5th arg:
1620
- // window(catalogId: string, hostWall: ComponentHandle, position: Vec3Handle, options?: { label? }, facing?: Vec3Handle)
1638
+ // TRANSPORT: positional signature shipped — facing is the trailing 5th arg, hinge rides in options:
1639
+ // window(catalogId: string, hostWall: ComponentHandle, position: Vec3Handle, options?: { label?, hinge? }, facing?: Vec3Handle)
1621
1640
 
1622
1641
  // ---------------------------------------------------------------------------
1623
1642
  // opening
@@ -6,14 +6,21 @@ import { ComponentHandle, Vec3Handle } from "../../../handles"
6
6
  * {@linkcode PluginDesignCreateOpeningOptions} and by the bulk item schemas in
7
7
  * `bulk-items.ts`, so singular and plural openings validate identically.
8
8
  *
9
- * `facing` is a WORLD POINT on the desired side of the wall, not a direction.
9
+ * `facing` and `hinge` are WORLD POINTS, not directions: `facing` sits on the
10
+ * side of the wall the leaf swings open to (where the plan symbol draws the
11
+ * swing arc); `hinge` sits near the jamb the leaf is hinged on — the item is
12
+ * reflected along the wall so its hinged jamb is the one nearer that point.
13
+ * Either may be omitted (engine-chosen side / the catalog item's authored
14
+ * hinge side). Neither has a visible effect on symmetric items such as
15
+ * sliders or fixed windows.
10
16
  *
11
17
  * | Property | Type | Description |
12
18
  * |---|---|---|
13
19
  * | `catalogId` | `string` | Library id: team `_id` or general `fullName` |
14
20
  * | `hostWall` | {@linkcode ComponentHandle} | The wall to host the opening |
15
21
  * | `position` | {@linkcode Vec3Handle} | World point projected onto the wall |
16
- * | `facing` | {@linkcode Vec3Handle}? | World point on the side of the wall the opening faces (default engine-chosen) |
22
+ * | `facing` | {@linkcode Vec3Handle}? | World point on the side of the wall the opening swings open to (default engine-chosen) |
23
+ * | `hinge` | {@linkcode Vec3Handle}? | World point near the jamb the opening is hinged on (default the catalog item's authored side) |
17
24
  * | `label` | `string`? | Instance name (optional) |
18
25
  * | `width` | `number`? | Width override (Snaptrude units, > 0; default the catalog item's) |
19
26
  * | `height` | `number`? | Height override (Snaptrude units, > 0; default the catalog item's) |
@@ -23,6 +30,7 @@ export const PluginOpeningBaseOptions = {
23
30
  hostWall: ComponentHandle,
24
31
  position: Vec3Handle,
25
32
  facing: Vec3Handle.optional(),
33
+ hinge: Vec3Handle.optional(),
26
34
  label: z.string().optional(),
27
35
  width: z.number().finite().positive().optional(),
28
36
  height: z.number().finite().positive().optional(),
@@ -8,9 +8,11 @@ import { PluginDesignChangeResult } from "../lock"
8
8
  * `design.create.door`; generic reads (`listDoors`/`getHost`/`getProperties`) live at
9
9
  * `design.query.*`. Door targets are {@linkcode ComponentHandle}s.
10
10
  *
11
- * `getSwingDirection` is a DERIVED read (no persisted field) — computed from the door
12
- * mesh reflection state. `mirror` reflects the door across an axis (undoable, one
13
- * command); `setType` is intentionally absent (the engine has no in-place re-type
11
+ * `getSwingDirection` is a DERIVED read (no persisted field) — computed from the placed
12
+ * mesh's orientation: stand on the side the door swings open to, facing it; `'left'`
13
+ * when the hinged jamb is on your left. `mirror` reflects the door about one of its own
14
+ * axes (undoable, one command): `'x'` flips the swing side, `'z'` swaps the hinged jamb;
15
+ * `setType` is intentionally absent (the engine has no in-place re-type —
14
16
  * it would require delete+recreate).
15
17
  *
16
18
  * The **catalog** reads (`listCatalogGroups`/`listCatalog`/`getCatalogItem`/`exists`)
@@ -154,9 +156,11 @@ export abstract class PluginDesignDoorsApi {
154
156
  public abstract getSupportFloor(door: ComponentHandle): PluginApiReturn<ComponentHandle | null>
155
157
 
156
158
  /**
157
- * Get a door's swing (hinge) handedness whether it opens as a left-hand or
158
- * right-hand door. Derived from the door mesh's reflection state, not a
159
- * persisted field.
159
+ * Get a door's swing (hinge) handedness. Stand on the side the door swings
160
+ * open to, facing the door: `'left'` when the hinged jamb is on your left,
161
+ * `'right'` when it is on your right. Derived from the placed mesh's
162
+ * orientation (the same reflection `mirror` and the `hinge` placement option
163
+ * apply), not a persisted field.
160
164
  *
161
165
  * @param door The door to query
162
166
  * @returns `'left'` / `'right'`, or `null` if indeterminate
@@ -178,11 +182,13 @@ export abstract class PluginDesignDoorsApi {
178
182
  ): PluginApiReturn<"left" | "right" | null>
179
183
 
180
184
  /**
181
- * Mirror a door across an axis, flipping its swing so it opens from the
182
- * other side. Undoable as a single command.
185
+ * Mirror a door about its own axes. `'x'` (default) flips the swing side
186
+ * the door opens to the other side of the wall; `'z'` swaps the hinged jamb
187
+ * (left-hand ↔ right-hand, same swing side); `'y'` turns it upside down.
188
+ * Undoable as a single command.
183
189
  *
184
190
  * @param door The door to mirror
185
- * @param axis Reflection axis (optional; default `'x'` — the swing-flip axis)
191
+ * @param axis Reflection axis (optional; default `'x'` — the swing-flip axis; `'z'` — the hinge-jamb axis)
186
192
  * @returns The affected door(s)
187
193
  *
188
194
  * @examplePrompt Flip this door's swing
@@ -193,10 +199,11 @@ export abstract class PluginDesignDoorsApi {
193
199
  *
194
200
  * # Example
195
201
  * ```ts
196
- * // Flip the selected door's swing — axis defaults to "x" (the swing-flip
197
- * // axis); pass "y" or "z" to reflect across a different axis
202
+ * // Flip the selected door's swing side — axis defaults to "x"
198
203
  * const [door] = await snaptrude.design.query.listDoors({ isSelected: true })
199
204
  * const { affected } = await snaptrude.design.doors.mirror(door)
205
+ * // …or swap the hinged jamb instead (keeps the swing side)
206
+ * await snaptrude.design.doors.mirror(door, "z")
200
207
  * ```
201
208
  */
202
209
  public abstract mirror(
@@ -369,7 +376,7 @@ export const PluginDoorDimensions = z.object({
369
376
  })
370
377
  export type PluginDoorDimensions = z.infer<typeof PluginDoorDimensions>
371
378
 
372
- /** Mirror axis — tokens mirror the engine `FlipDirection` verbatim (§6.1). */
379
+ /** Mirror axis — tokens mirror the engine `FlipDirection` verbatim: `'x'` flips the swing side, `'z'` swaps the hinged jamb, `'y'` turns the door upside down. */
373
380
  export const PluginMirrorAxis = z.enum(["x", "y", "z"])
374
381
  export type PluginMirrorAxis = z.infer<typeof PluginMirrorAxis>
375
382
 
@@ -379,7 +386,7 @@ export type PluginMirrorAxis = z.infer<typeof PluginMirrorAxis>
379
386
  * | Property | Type | Description |
380
387
  * |---|---|---|
381
388
  * | `door` | {@linkcode ComponentHandle} | The door to mirror |
382
- * | `axis` | {@linkcode PluginMirrorAxis} | Reflection axis (optional; default `'x'` — the swing-flip axis) |
389
+ * | `axis` | {@linkcode PluginMirrorAxis} | Reflection axis (optional; default `'x'` — the swing-flip axis; `'z'` — the hinge-jamb axis) |
383
390
  */
384
391
  export const PluginDesignDoorMirrorArgs = z.object({
385
392
  door: ComponentHandle,