@snaptrude/plugin-core 0.9.7 → 0.9.9

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 (39) hide show
  1. package/AGENTS.md +4 -0
  2. package/CHANGELOG.md +11 -0
  3. package/api-manifest.json +222 -4
  4. package/dist/api/core/tags.d.ts +81 -0
  5. package/dist/api/core/tags.d.ts.map +1 -1
  6. package/dist/api/design/create/bulk-items.d.ts +14 -6
  7. package/dist/api/design/create/bulk-items.d.ts.map +1 -1
  8. package/dist/api/design/create/index.d.ts +41 -18
  9. package/dist/api/design/create/index.d.ts.map +1 -1
  10. package/dist/api/design/create/opening-fields.d.ts +10 -2
  11. package/dist/api/design/create/opening-fields.d.ts.map +1 -1
  12. package/dist/api/design/doors/index.d.ts +20 -13
  13. package/dist/api/design/doors/index.d.ts.map +1 -1
  14. package/dist/api/program/departments.d.ts +58 -2
  15. package/dist/api/program/departments.d.ts.map +1 -1
  16. package/dist/api/program/index.d.ts +16 -6
  17. package/dist/api/program/index.d.ts.map +1 -1
  18. package/dist/api/program/labels.d.ts +355 -0
  19. package/dist/api/program/labels.d.ts.map +1 -0
  20. package/dist/api/program/metadata.d.ts +328 -0
  21. package/dist/api/program/metadata.d.ts.map +1 -0
  22. package/dist/api/program/spreadsheet.d.ts +263 -0
  23. package/dist/api/program/spreadsheet.d.ts.map +1 -1
  24. package/dist/index.cjs +1084 -833
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.js +1039 -833
  27. package/dist/index.js.map +1 -1
  28. package/package.json +13 -13
  29. package/src/api/core/tags.ts +76 -0
  30. package/src/api/design/create/bulk-items.ts +10 -6
  31. package/src/api/design/create/index.ts +43 -24
  32. package/src/api/design/create/opening-fields.ts +10 -2
  33. package/src/api/design/doors/index.ts +20 -13
  34. package/src/api/program/departments.ts +54 -2
  35. package/src/api/program/index.ts +16 -6
  36. package/src/api/program/labels.ts +332 -0
  37. package/src/api/program/metadata.ts +364 -0
  38. package/src/api/program/spreadsheet.ts +282 -0
  39. package/api-manifest.full.json +0 -8442
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.9",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -16,17 +16,6 @@
16
16
  "publishConfig": {
17
17
  "access": "public"
18
18
  },
19
- "devDependencies": {
20
- "ts-morph": "^28.0.0",
21
- "tsup": "^8.5.1",
22
- "typescript": "^5.5.4"
23
- },
24
- "dependencies": {
25
- "zod": "^4.3.6"
26
- },
27
- "peerDependencies": {
28
- "zod": "^3.25.0 || ^4.0.0"
29
- },
30
19
  "scripts": {
31
20
  "check-types": "tsc --noEmit",
32
21
  "build": "tsup --clean",
@@ -37,5 +26,16 @@
37
26
  "generate:manifest:check": "node scripts/generate-manifest.mjs --check",
38
27
  "generate:manifest:all": "node scripts/generate-manifest.mjs --all",
39
28
  "generate:manifest:all:check": "node scripts/generate-manifest.mjs --all --check"
29
+ },
30
+ "devDependencies": {
31
+ "ts-morph": "^28.0.0",
32
+ "tsup": "^8.5.1",
33
+ "typescript": "^5.5.4"
34
+ },
35
+ "dependencies": {
36
+ "zod": "^4.3.6"
37
+ },
38
+ "peerDependencies": {
39
+ "zod": "^3.25.0 || ^4.0.0"
40
40
  }
41
- }
41
+ }
@@ -163,6 +163,40 @@ export abstract class PluginCoreTagsApi {
163
163
  componentId: ComponentHandle,
164
164
  ): PluginApiReturn<PluginCoreTagsGetTagsForComponentResult>
165
165
 
166
+ /**
167
+ * List the tags carried by many components in one call.
168
+ *
169
+ * The plural of {@linkcode PluginCoreTagsApi.listForComponent}: pass
170
+ * `componentIds` to read specific components (unknown ids are skipped), or
171
+ * omit it to read **every** taggable component (Mass/Floor space) in the
172
+ * project. Each entry carries the component id and its tags — one per tagged
173
+ * category, with the tag's `color` — so a plugin can color every space by a
174
+ * chosen category's tag in a single round-trip.
175
+ *
176
+ * @param componentIds - Optional `Component.id`s to read; omit for all spaces.
177
+ * @returns A {@linkcode PluginCoreTagsListForComponentsResult} with a
178
+ * `components` array, one entry per component (in input order when
179
+ * `componentIds` is given).
180
+ *
181
+ * @performance One round-trip for any number of components — use this instead of a `listForComponent` loop.
182
+ *
183
+ * @examplePrompt Get the tags of every space with their colors
184
+ * @examplePrompt List the tags on all rooms so I can color them by Occupancy
185
+ * @examplePrompt Which tag does each selected space carry?
186
+ *
187
+ * # Example
188
+ * ```ts
189
+ * const { components } = await snaptrude.core.tags.listForComponents()
190
+ * for (const c of components) {
191
+ * const occupancy = c.tags.find((t) => t.categoryName === "Occupancy")
192
+ * if (occupancy) paint(c.componentId, occupancy.color)
193
+ * }
194
+ * ```
195
+ */
196
+ public abstract listForComponents(
197
+ componentIds?: ComponentHandle[],
198
+ ): PluginApiReturn<PluginCoreTagsListForComponentsResult>
199
+
166
200
  /**
167
201
  * List the components carrying a given tag or category.
168
202
  *
@@ -588,6 +622,48 @@ export type PluginCoreTagsGetTagsForComponentResult = z.infer<
588
622
  typeof PluginCoreTagsGetTagsForComponentResult
589
623
  >
590
624
 
625
+ /**
626
+ * Arguments for {@linkcode PluginCoreTagsApi.listForComponents}.
627
+ *
628
+ * | Property | Type | Description |
629
+ * |---|---|---|
630
+ * | `componentIds` | `ComponentHandle[]?` | Components to read; omit for every taggable component |
631
+ */
632
+ export const PluginCoreTagsListForComponentsArgs = z.object({
633
+ componentIds: z.array(ComponentHandle).optional(),
634
+ })
635
+ export type PluginCoreTagsListForComponentsArgs = z.infer<
636
+ typeof PluginCoreTagsListForComponentsArgs
637
+ >
638
+
639
+ /**
640
+ * One component's tags, for {@linkcode PluginCoreTagsApi.listForComponents}.
641
+ *
642
+ * | Property | Type | Description |
643
+ * |---|---|---|
644
+ * | `componentId` | `ComponentHandle` | The component's id |
645
+ * | `tags` | {@linkcode PluginCoreComponentTag}`[]` | Its tags, one per tagged category (empty when none) |
646
+ */
647
+ export const PluginCoreComponentTags = z.object({
648
+ componentId: ComponentHandle,
649
+ tags: z.array(PluginCoreComponentTag),
650
+ })
651
+ export type PluginCoreComponentTags = z.infer<typeof PluginCoreComponentTags>
652
+
653
+ /**
654
+ * Result of {@linkcode PluginCoreTagsApi.listForComponents}.
655
+ *
656
+ * | Property | Type | Description |
657
+ * |---|---|---|
658
+ * | `components` | {@linkcode PluginCoreComponentTags}`[]` | One entry per component |
659
+ */
660
+ export const PluginCoreTagsListForComponentsResult = z.object({
661
+ components: z.array(PluginCoreComponentTags),
662
+ })
663
+ export type PluginCoreTagsListForComponentsResult = z.infer<
664
+ typeof PluginCoreTagsListForComponentsResult
665
+ >
666
+
591
667
  /**
592
668
  * Arguments for {@linkcode PluginCoreTagsApi.listComponents}. Provide exactly one
593
669
  * of `tagId`, `categoryId`. `untagged` is valid only with `categoryId`.
@@ -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,
@@ -7,7 +7,7 @@ import { ComponentHandle } from "../../handles"
7
7
  * Program departments — read the department groupings of the active program.
8
8
  *
9
9
  * In program mode a **department** is a named, colored grouping of spaces with an
10
- * optional area **target**. This is the program-planning view of departments
10
+ * optional gross area **target** and a unit **count** target. This is the program-planning view of departments
11
11
  * (their identity, color, and target area); the underlying space geometry is read
12
12
  * and edited through the geometry namespaces, not here.
13
13
  *
@@ -203,6 +203,33 @@ export abstract class PluginProgramDepartmentsApi {
203
203
  units?: PluginAreaUnit,
204
204
  ): PluginApiReturn<PluginProgramDepartmentsSetTargetAreaResult>
205
205
 
206
+ /**
207
+ * Set a department's target unit count.
208
+ *
209
+ * The number of units (program blocks) the department is planned for — the
210
+ * count twin of {@linkcode PluginProgramDepartmentsApi.setTargetArea}, stored
211
+ * beside the gross area target on the department. Read it back from
212
+ * {@linkcode PluginProgramDepartmentsApi.get} (`targetCount`).
213
+ *
214
+ * @param departmentId - The id of the department whose count to set.
215
+ * @param targetCount - The target number of units (a non-negative integer).
216
+ * @returns The updated {@linkcode PluginProgramDepartment} with its `units`.
217
+ * @throws If no department has the given id.
218
+ *
219
+ * @examplePrompt Set the Bedrooms department unit count to 12
220
+ * @examplePrompt The program needs 4 clinic blocks — update the department count
221
+ * @examplePrompt Update the target count for department dep_123
222
+ *
223
+ * # Example
224
+ * ```ts
225
+ * await snaptrude.program.departments.setTargetCount("dep_123", 12)
226
+ * ```
227
+ */
228
+ public abstract setTargetCount(
229
+ departmentId: string,
230
+ targetCount: number,
231
+ ): PluginApiReturn<PluginProgramDepartmentsSetTargetCountResult>
232
+
206
233
  /**
207
234
  * Delete a department.
208
235
  *
@@ -239,13 +266,15 @@ export abstract class PluginProgramDepartmentsApi {
239
266
  * | `id` | `string` | Unique department id |
240
267
  * | `name` | `string` | Display name |
241
268
  * | `color` | `string` | CSS hex color string (e.g. `"#b5e1dc"`) |
242
- * | `targetArea` | `number \| null` | Target area in the response's `units`, or `null` if no target is set |
269
+ * | `targetArea` | `number \| null` | Gross target area in the response's `units`, or `null` if no target is set |
270
+ * | `targetCount` | `number` | Target number of units / program blocks (`0` when unset) |
243
271
  */
244
272
  export const PluginProgramDepartment = z.object({
245
273
  id: z.string(),
246
274
  name: z.string(),
247
275
  color: z.string(),
248
276
  targetArea: z.number().nullable(),
277
+ targetCount: z.number(),
249
278
  })
250
279
  export type PluginProgramDepartment = z.infer<typeof PluginProgramDepartment>
251
280
 
@@ -434,6 +463,29 @@ export type PluginProgramDepartmentsSetTargetAreaResult = z.infer<
434
463
  typeof PluginProgramDepartmentsSetTargetAreaResult
435
464
  >
436
465
 
466
+ /**
467
+ * Arguments for {@linkcode PluginProgramDepartmentsApi.setTargetCount}.
468
+ *
469
+ * | Property | Type | Description |
470
+ * |---|---|---|
471
+ * | `departmentId` | `string` | The id of the department whose count to set |
472
+ * | `targetCount` | `number` | Target number of units (non-negative integer) |
473
+ */
474
+ export const PluginProgramDepartmentsSetTargetCountArgs = z.object({
475
+ departmentId: z.string(),
476
+ targetCount: z.number().int().nonnegative(),
477
+ })
478
+ export type PluginProgramDepartmentsSetTargetCountArgs = z.infer<
479
+ typeof PluginProgramDepartmentsSetTargetCountArgs
480
+ >
481
+
482
+ /** Result of {@linkcode PluginProgramDepartmentsApi.setTargetCount} — the updated record with `units`. */
483
+ export const PluginProgramDepartmentsSetTargetCountResult =
484
+ PluginProgramDepartmentRecord
485
+ export type PluginProgramDepartmentsSetTargetCountResult = z.infer<
486
+ typeof PluginProgramDepartmentsSetTargetCountResult
487
+ >
488
+
437
489
  /**
438
490
  * Arguments for {@linkcode PluginProgramDepartmentsApi.delete}.
439
491
  *
@@ -7,22 +7,26 @@ import { PluginProgramSiteApi } from "./site"
7
7
  import { PluginProgramCoresApi } from "./cores"
8
8
  import { PluginProgramClassificationApi } from "./classification"
9
9
  import { PluginProgramAreasApi } from "./areas"
10
+ import { PluginProgramMetadataApi } from "./metadata"
11
+ import { PluginProgramLabelsApi } from "./labels"
10
12
 
11
13
  /**
12
14
  * Program-mode planning APIs.
13
15
  *
14
16
  * The program is the space-planning layer: department groupings with area targets,
15
- * the metrics that track allocated area against those targets, and the reports
16
- * that render program/takeoff data to spreadsheet sheets. Reads describe the
17
- * *plan* (intent + targets); the geometry that realizes the plan is authored
18
- * through the geometry namespaces.
17
+ * the labels (program rows) inside them, the metrics that track allocated area
18
+ * against those targets, and the reports that render program/takeoff data to
19
+ * spreadsheet sheets. Reads describe the *plan* (intent + targets); the geometry
20
+ * that realizes the plan is authored through the geometry namespaces.
19
21
  *
20
22
  * - {@linkcode PluginProgramApi.departments} — Read & edit program departments (groupings + targets)
23
+ * - {@linkcode PluginProgramApi.labels} — Read & edit program labels (program rows: per-type area/count targets)
24
+ * - {@linkcode PluginProgramApi.metadata} — Read & edit program metadata (custom columns) on spaces
21
25
  * - {@linkcode PluginProgramApi.adjacency} — Read & compute the department/space adjacency matrix
22
26
  * - {@linkcode PluginProgramApi.layout} — Arrange/pack spaces in the envelope
23
27
  * - {@linkcode PluginProgramApi.metrics} — Read the area-program summary
24
28
  * - {@linkcode PluginProgramApi.areas} — FAR / built-up-area rollup and groupings
25
- * - {@linkcode PluginProgramApi.spreadsheet} — Render program data to sheets and read it back
29
+ * - {@linkcode PluginProgramApi.spreadsheet} — Render program data to sheets, custom grouped sheets, read back
26
30
  * - {@linkcode PluginProgramApi.site} — Read site/plot context and read/update Site Analysis
27
31
  * - {@linkcode PluginProgramApi.cores} — Read & predicate vertical-circulation cores (reads only)
28
32
  * - {@linkcode PluginProgramApi.classification} — Classification catalog + display tags
@@ -32,6 +36,10 @@ import { PluginProgramAreasApi } from "./areas"
32
36
  export abstract class PluginProgramApi {
33
37
  /** Program departments — groupings, area targets, and space assignment. See {@linkcode PluginProgramDepartmentsApi}. */
34
38
  public abstract departments: PluginProgramDepartmentsApi
39
+ /** Program labels — the program rows with per-type area/count targets. See {@linkcode PluginProgramLabelsApi}. */
40
+ public abstract labels: PluginProgramLabelsApi
41
+ /** Program metadata — custom columns and their per-space values. See {@linkcode PluginProgramMetadataApi}. */
42
+ public abstract metadata: PluginProgramMetadataApi
35
43
  /** Adjacency matrix — read & compute proximity relationships. See {@linkcode PluginProgramAdjacencyApi}. */
36
44
  public abstract adjacency: PluginProgramAdjacencyApi
37
45
  /** Arrange/pack spaces in the envelope (async job family). See {@linkcode PluginProgramLayoutApi}. */
@@ -40,7 +48,7 @@ export abstract class PluginProgramApi {
40
48
  public abstract metrics: PluginProgramMetricsApi
41
49
  /** FAR / built-up-area rollup and groupings. See {@linkcode PluginProgramAreasApi}. */
42
50
  public abstract areas: PluginProgramAreasApi
43
- /** Spreadsheet reports — render tables to sheets, export, list, read back. See {@linkcode PluginProgramSpreadsheetApi}. */
51
+ /** Spreadsheet reports — render tables to sheets, custom grouped sheets, export, read back. See {@linkcode PluginProgramSpreadsheetApi}. */
44
52
  public abstract spreadsheet: PluginProgramSpreadsheetApi
45
53
  /** Site/plot context and persisted Site Analysis. See {@linkcode PluginProgramSiteApi}. */
46
54
  public abstract site: PluginProgramSiteApi
@@ -61,3 +69,5 @@ export * from "./site"
61
69
  export * from "./cores"
62
70
  export * from "./classification"
63
71
  export * from "./areas"
72
+ export * from "./metadata"
73
+ export * from "./labels"