@snaptrude/plugin-core 0.0.0-dev-20260911064951 → 0.0.0-dev-20260921132444

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 (55) hide show
  1. package/AGENTS.md +4 -0
  2. package/CHANGELOG.md +35 -1
  3. package/dist/api/core/mode/index.d.ts +19 -7
  4. package/dist/api/core/mode/index.d.ts.map +1 -1
  5. package/dist/api/core/tags.d.ts +81 -0
  6. package/dist/api/core/tags.d.ts.map +1 -1
  7. package/dist/api/design/create/grids.d.ts +132 -0
  8. package/dist/api/design/create/grids.d.ts.map +1 -0
  9. package/dist/api/design/create/index.d.ts +109 -1
  10. package/dist/api/design/create/index.d.ts.map +1 -1
  11. package/dist/api/design/family.d.ts +73 -5
  12. package/dist/api/design/family.d.ts.map +1 -1
  13. package/dist/api/design/grids.d.ts +237 -0
  14. package/dist/api/design/grids.d.ts.map +1 -0
  15. package/dist/api/design/index.d.ts +4 -0
  16. package/dist/api/design/index.d.ts.map +1 -1
  17. package/dist/api/design/query/grids.d.ts +287 -0
  18. package/dist/api/design/query/grids.d.ts.map +1 -0
  19. package/dist/api/design/query/index.d.ts +29 -0
  20. package/dist/api/design/query/index.d.ts.map +1 -1
  21. package/dist/api/design/update/index.d.ts +74 -0
  22. package/dist/api/design/update/index.d.ts.map +1 -1
  23. package/dist/api/program/departments.d.ts +58 -2
  24. package/dist/api/program/departments.d.ts.map +1 -1
  25. package/dist/api/program/index.d.ts +16 -6
  26. package/dist/api/program/index.d.ts.map +1 -1
  27. package/dist/api/program/labels.d.ts +355 -0
  28. package/dist/api/program/labels.d.ts.map +1 -0
  29. package/dist/api/program/metadata.d.ts +328 -0
  30. package/dist/api/program/metadata.d.ts.map +1 -0
  31. package/dist/api/program/spreadsheet.d.ts +263 -0
  32. package/dist/api/program/spreadsheet.d.ts.map +1 -1
  33. package/dist/api/workspace/index.d.ts +49 -2
  34. package/dist/api/workspace/index.d.ts.map +1 -1
  35. package/dist/index.cjs +2365 -1908
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.js +2294 -1908
  38. package/dist/index.js.map +1 -1
  39. package/package.json +1 -1
  40. package/src/api/core/mode/index.ts +19 -7
  41. package/src/api/core/tags.ts +76 -0
  42. package/src/api/design/create/grids.ts +137 -0
  43. package/src/api/design/create/index.ts +131 -0
  44. package/src/api/design/family.ts +51 -4
  45. package/src/api/design/grids.ts +237 -0
  46. package/src/api/design/index.ts +5 -0
  47. package/src/api/design/query/grids.ts +197 -0
  48. package/src/api/design/query/index.ts +27 -0
  49. package/src/api/design/update/index.ts +53 -0
  50. package/src/api/program/departments.ts +54 -2
  51. package/src/api/program/index.ts +16 -6
  52. package/src/api/program/labels.ts +332 -0
  53. package/src/api/program/metadata.ts +364 -0
  54. package/src/api/program/spreadsheet.ts +282 -0
  55. package/src/api/workspace/index.ts +51 -2
@@ -0,0 +1,237 @@
1
+ import * as z from "zod"
2
+ import { PluginApiReturn } from "../../types"
3
+ import { ComponentHandle } from "../../handles"
4
+ import { PluginGridStyleOptions } from "./create/grids"
5
+
6
+ /**
7
+ * Tag policy for structural line removal (mirrors the engine
8
+ * `GridLineTagPolicy` values verbatim): `"rewrite"` renumbers each affected
9
+ * wing's tag sequence after removal; `"preserve"` keeps the surviving lines'
10
+ * tags unchanged.
11
+ */
12
+ export const PluginGridLineTagPolicy = z.enum(["rewrite", "preserve"])
13
+ export type PluginGridLineTagPolicy = z.infer<typeof PluginGridLineTagPolicy>
14
+
15
+ /** A positional grid-line address — indices into `design.query.grids.get`'s `wings` arrays. */
16
+ export const PluginGridLineAddress = z.object({
17
+ wing: z.number().int().nonnegative(),
18
+ line: z.number().int().nonnegative(),
19
+ })
20
+ export type PluginGridLineAddress = z.infer<typeof PluginGridLineAddress>
21
+
22
+ /**
23
+ * One per-wing entry of {@linkcode PluginDesignGridsApi.updateWings} —
24
+ * addressed by wing index. `spanMin`/`spanMax` move the family's length
25
+ * handles (Snaptrude units).
26
+ */
27
+ export const PluginGridWingEntry = z.object({
28
+ wing: z.number().int().nonnegative(),
29
+ spacing: z.number().positive().finite().optional(),
30
+ count: z.number().int().min(1).optional(),
31
+ spanMin: z.number().finite().optional(),
32
+ spanMax: z.number().finite().optional(),
33
+ })
34
+ export type PluginGridWingEntry = z.infer<typeof PluginGridWingEntry>
35
+
36
+ /** Arguments for {@linkcode PluginDesignGridsApi.updateWings}. */
37
+ export const PluginDesignGridsUpdateWingsArgs = z.object({
38
+ grid: ComponentHandle,
39
+ wings: z.array(PluginGridWingEntry).min(1),
40
+ })
41
+ export type PluginDesignGridsUpdateWingsArgs = z.infer<
42
+ typeof PluginDesignGridsUpdateWingsArgs
43
+ >
44
+
45
+ /**
46
+ * One per-line entry of {@linkcode PluginDesignGridsApi.updateLines} —
47
+ * addressed positionally by `{ wing, line }` indices (invalidated by
48
+ * structural edits; re-query after mutating). `distanceToPrevious` /
49
+ * `distanceToNext` set the typed distance to the adjacent line on that side
50
+ * and are mutually exclusive.
51
+ *
52
+ * `visible` is **hide-only** (`false`), mirroring the product: per-line show
53
+ * does not exist — showing the GROUP (`design.visibility.show`) restores all
54
+ * of its lines, exactly like the native Show All.
55
+ */
56
+ export const PluginGridLineEntry = z
57
+ .object({
58
+ wing: z.number().int().nonnegative(),
59
+ line: z.number().int().nonnegative(),
60
+ tag: z.string().min(1).optional(),
61
+ distanceToPrevious: z.number().positive().finite().optional(),
62
+ distanceToNext: z.number().positive().finite().optional(),
63
+ props: PluginGridStyleOptions.optional(),
64
+ visible: z.literal(false).optional(),
65
+ })
66
+ .superRefine((entry, ctx) => {
67
+ if (entry.distanceToPrevious !== undefined && entry.distanceToNext !== undefined) {
68
+ ctx.addIssue({
69
+ code: z.ZodIssueCode.custom,
70
+ message:
71
+ "distanceToPrevious and distanceToNext are mutually exclusive on one line entry",
72
+ })
73
+ }
74
+ })
75
+ export type PluginGridLineEntry = z.infer<typeof PluginGridLineEntry>
76
+
77
+ /** Arguments for {@linkcode PluginDesignGridsApi.updateLines}. */
78
+ export const PluginDesignGridsUpdateLinesArgs = z.object({
79
+ grid: ComponentHandle,
80
+ lines: z.array(PluginGridLineEntry).min(1),
81
+ })
82
+ export type PluginDesignGridsUpdateLinesArgs = z.infer<
83
+ typeof PluginDesignGridsUpdateLinesArgs
84
+ >
85
+
86
+ /** Arguments for {@linkcode PluginDesignGridsApi.addLine}. */
87
+ export const PluginDesignGridsAddLineArgs = z.object({
88
+ grid: ComponentHandle,
89
+ wing: z.number().int().nonnegative(),
90
+ position: z.number().finite(),
91
+ })
92
+ export type PluginDesignGridsAddLineArgs = z.infer<typeof PluginDesignGridsAddLineArgs>
93
+
94
+ /** Arguments for {@linkcode PluginDesignGridsApi.removeLines}. */
95
+ export const PluginDesignGridsRemoveLinesArgs = z.object({
96
+ grid: ComponentHandle,
97
+ lines: z.array(PluginGridLineAddress).min(1),
98
+ tagPolicy: PluginGridLineTagPolicy.optional(),
99
+ })
100
+ export type PluginDesignGridsRemoveLinesArgs = z.infer<
101
+ typeof PluginDesignGridsRemoveLinesArgs
102
+ >
103
+
104
+ /**
105
+ * `design.grids.*` — structural edits on a grid's line structure (the
106
+ * entity-family home for grid-specific operations, like `design.doors`).
107
+ *
108
+ * Lines are addressed positionally as `{ wing, line }` index pairs into the
109
+ * `wings` arrays returned by `design.query.grids.get`. Adding, removing, or
110
+ * re-counting lines re-indexes the wing — re-query after every structural edit.
111
+ *
112
+ * Group-level edits (default styling, angle, storey visibility) live on
113
+ * `design.update.grid`; creation lives on `design.create.grid*`.
114
+ *
115
+ * Accessed via `snaptrude.design.grids`.
116
+ */
117
+ export abstract class PluginDesignGridsApi {
118
+ constructor() {}
119
+
120
+ /**
121
+ * Update one or more **wings** of a grid with sparse per-wing entries —
122
+ * `spacing` (uniform re-space), `count` (grow/truncate + tag renumber),
123
+ * `spanMin`/`spanMax` (family length handles). Every change routes through
124
+ * the exact setters the native panel uses — each changed field commits its
125
+ * own undoable command, identical to editing that field in the panel.
126
+ *
127
+ * A `count` change re-indexes the wing's lines — positional `{ wing, line }`
128
+ * addresses held from before the call are stale afterwards; re-query.
129
+ *
130
+ * @param grid - The grid group
131
+ * @param wings - Sparse {@linkcode PluginGridWingEntry} entries (≥ 1)
132
+ * @returns `true` once applied
133
+ *
134
+ * @examplePrompt Change this grid's spacing to 8 metres
135
+ * @examplePrompt Make this grid 10 by 12 lines
136
+ *
137
+ * # Example
138
+ * ```ts
139
+ * await snaptrude.design.grids.updateWings(grid, [{ wing: 0, spacing: 8, count: 10 }])
140
+ * ```
141
+ */
142
+ public abstract updateWings(
143
+ grid: ComponentHandle,
144
+ wings: PluginGridWingEntry[],
145
+ ): PluginApiReturn<boolean>
146
+
147
+ /**
148
+ * Update one or more **lines** of a grid with sparse per-line entries —
149
+ * `tag`, `distanceToPrevious`/`distanceToNext` (typed distance to the
150
+ * neighbour on that side; mutually exclusive), `props` (per-line style
151
+ * overrides), and `visible: false` (hide). Every change routes through the
152
+ * exact setters the native panel uses — each changed field commits its own
153
+ * undoable command, identical to editing that field in the panel.
154
+ *
155
+ * Visibility mirrors the product exactly: `visible` accepts only `false` —
156
+ * per-line show does not exist. Hiding lines that would leave NO visible
157
+ * line hides the whole GROUP instead (the native last-line rule; those line
158
+ * flags stay untouched). To bring hidden lines back, show the group
159
+ * (`design.visibility.show([grid])`) — it restores ALL of its lines, like
160
+ * the native Show All.
161
+ *
162
+ * @param grid - The grid group
163
+ * @param lines - Sparse {@linkcode PluginGridLineEntry} entries (≥ 1)
164
+ * @returns `true` once applied
165
+ *
166
+ * @examplePrompt Rename grid line 2 to "B"
167
+ * @examplePrompt Move this grid line 4 metres from its neighbour
168
+ * @examplePrompt Hide grid line 3 on this grid
169
+ *
170
+ * # Example
171
+ * ```ts
172
+ * await snaptrude.design.grids.updateLines(grid, [
173
+ * { wing: 0, line: 1, tag: "B", distanceToPrevious: 4 },
174
+ * ])
175
+ * ```
176
+ */
177
+ public abstract updateLines(
178
+ grid: ComponentHandle,
179
+ lines: PluginGridLineEntry[],
180
+ ): PluginApiReturn<boolean>
181
+
182
+ /**
183
+ * Insert a parallel grid line into a wing at an explicit position along the
184
+ * wing's spacing axis (Snaptrude units, wing-local — line 0's position can
185
+ * be read from its curve). Sibling lines do not shift; the wing's tag
186
+ * sequence is renumbered. Undoable.
187
+ *
188
+ * @param grid - The grid group
189
+ * @param wing - Wing index
190
+ * @param position - Position along the wing's spacing axis (Snaptrude units)
191
+ * @returns `true` once inserted
192
+ * @throws `VALIDATION` on a bad wing index or non-finite position
193
+ *
194
+ * @examplePrompt Add a grid line between lines 2 and 3
195
+ * @examplePrompt Insert an extra grid line 4 metres from line A
196
+ *
197
+ * # Example
198
+ * ```ts
199
+ * await snaptrude.design.grids.addLine(grid, 0, 12.5)
200
+ * ```
201
+ */
202
+ public abstract addLine(
203
+ grid: ComponentHandle,
204
+ wing: number,
205
+ position: number,
206
+ ): PluginApiReturn<boolean>
207
+
208
+ /**
209
+ * Remove grid lines by positional address, as ONE undoable step. With
210
+ * `tagPolicy: "rewrite"` (default) each affected wing renumbers its tag
211
+ * sequence; `"preserve"` keeps surviving tags unchanged. A wing emptied of
212
+ * lines is dropped from the group.
213
+ *
214
+ * Removing EVERY line of the group is rejected — delete the group instead
215
+ * (`design.delete.entities`).
216
+ *
217
+ * @param grid - The grid group
218
+ * @param lines - Positional addresses to remove (see `design.query.grids.get`)
219
+ * @param tagPolicy - `"rewrite"` (default) or `"preserve"`
220
+ * @returns `true` once removed
221
+ * @throws `VALIDATION` on out-of-range addresses; `OPERATION_FAILED` when the
222
+ * removal would delete the group's last line
223
+ *
224
+ * @examplePrompt Delete grid line B
225
+ * @examplePrompt Remove the last two lines of this grid without renumbering
226
+ *
227
+ * # Example
228
+ * ```ts
229
+ * await snaptrude.design.grids.removeLines(grid, [{ wing: 0, line: 2 }], "preserve")
230
+ * ```
231
+ */
232
+ public abstract removeLines(
233
+ grid: ComponentHandle,
234
+ lines: PluginGridLineAddress[],
235
+ tagPolicy?: PluginGridLineTagPolicy,
236
+ ): PluginApiReturn<boolean>
237
+ }
@@ -17,6 +17,7 @@ import { PluginDesignVisibilityApi } from "./visibility"
17
17
  import { PluginDesignDimensionsApi } from "./dimensions"
18
18
  import { PluginDesignTypesApi } from "./types"
19
19
  import { PluginDesignFamilyApi } from "./family"
20
+ import { PluginDesignGridsApi } from "./grids"
20
21
  import { PluginDesignChangeResult } from "./lock"
21
22
 
22
23
  /**
@@ -76,6 +77,9 @@ export abstract class PluginDesignApi {
76
77
  /** Author parametric families from a JSON spec. See {@linkcode PluginDesignFamilyApi}. */
77
78
  public abstract family: PluginDesignFamilyApi
78
79
 
80
+ /** Structural edits on a grid's line structure. See {@linkcode PluginDesignGridsApi}. */
81
+ public abstract grids: PluginDesignGridsApi
82
+
79
83
  /**
80
84
  * Lock entities so they cannot be edited. Undoable. Cascades to void/door/window
81
85
  * children, stacked-wall companions, and terrain neighborhood; skips
@@ -267,3 +271,4 @@ export * from "./visibility"
267
271
  export * from "./dimensions"
268
272
  export * from "./types"
269
273
  export * from "./family"
274
+ export * from "./grids"
@@ -0,0 +1,197 @@
1
+ import * as z from "zod"
2
+ import { PluginApiReturn } from "../../../types"
3
+ import { CurveHandle, BBoxComponents } from "../../../handles"
4
+ import { PluginReferenceLineStyle } from "../create"
5
+
6
+ // ---------------------------------------------------------------------------
7
+ // Records
8
+ // ---------------------------------------------------------------------------
9
+
10
+ /**
11
+ * Visual properties of grid lines — the group's defaults and any per-line
12
+ * overrides share this shape (values mirror the engine's `GridLineProps`).
13
+ *
14
+ * | Property | Type | Description |
15
+ * |---|---|---|
16
+ * | `style` | {@linkcode PluginReferenceLineStyle} | Line style (`"SOLID"` \| `"DASHED"` \| `"DOT_DASH"`) |
17
+ * | `weight` | `number` | Line weight |
18
+ * | `color` | `string` | Line color as a `#rrggbb` hex string |
19
+ * | `showStart` | `boolean` | Whether the start-end tag bubble is shown |
20
+ * | `showEnd` | `boolean` | Whether the end-end tag bubble is shown |
21
+ */
22
+ export const PluginGridLineProps = z.object({
23
+ style: PluginReferenceLineStyle,
24
+ weight: z.number(),
25
+ color: z.string(),
26
+ showStart: z.boolean(),
27
+ showEnd: z.boolean(),
28
+ })
29
+ export type PluginGridLineProps = z.infer<typeof PluginGridLineProps>
30
+
31
+ /**
32
+ * One grid line within a wing.
33
+ *
34
+ * | Property | Type | Description |
35
+ * |---|---|---|
36
+ * | `curve` | {@linkcode CurveHandle} | The line's geometry in **world space** (a line or arc; read coordinates via `core.geom.curve`) |
37
+ * | `tag` | `string` | The line's grid tag (e.g. `"A"`, `"3"`) |
38
+ * | `visible` | `boolean` | Persisted per-line visibility |
39
+ * | `props` | {@linkcode PluginGridLineProps} | **Effective** visual properties (group defaults merged with this line's overrides) |
40
+ * | `overrides` | `Partial<`{@linkcode PluginGridLineProps}`>`? | This line's raw overrides, when any exist |
41
+ */
42
+ export const PluginGridLineRecord = z.object({
43
+ curve: CurveHandle,
44
+ tag: z.string(),
45
+ visible: z.boolean(),
46
+ props: PluginGridLineProps,
47
+ overrides: PluginGridLineProps.partial().optional(),
48
+ })
49
+ export type PluginGridLineRecord = z.infer<typeof PluginGridLineRecord>
50
+
51
+ /**
52
+ * One wing of a grid — a family of parallel lines sharing a local frame.
53
+ * `spacing` and `span` are measured along the wing's local axes in
54
+ * Snaptrude units; line addresses used by mutating APIs are
55
+ * `{ wing, line }` index pairs into these arrays.
56
+ *
57
+ * | Property | Type | Description |
58
+ * |---|---|---|
59
+ * | `spacing` | `number` | Nominal spacing between adjacent lines (Snaptrude units) |
60
+ * | `tagStart` | `string` | First tag of the wing's deterministic tag sequence |
61
+ * | `span` | `{ min: number; max: number }`? | Explicit family length along the wing's line axis, when baked |
62
+ * | `lines` | {@linkcode PluginGridLineRecord}`[]` | The wing's lines, in wing order (index = the `line` address) |
63
+ */
64
+ export const PluginGridWingRecord = z.object({
65
+ spacing: z.number(),
66
+ tagStart: z.string(),
67
+ span: z.object({ min: z.number(), max: z.number() }).optional(),
68
+ lines: z.array(PluginGridLineRecord),
69
+ })
70
+ export type PluginGridWingRecord = z.infer<typeof PluginGridWingRecord>
71
+
72
+ /**
73
+ * Per-storey 2D-plan visibility of a grid.
74
+ *
75
+ * | Property | Type | Description |
76
+ * |---|---|---|
77
+ * | `activeStorey` | `number` | The storey the grid belongs to |
78
+ * | `disabledOnStoreys` | `number[]` | Storeys on whose 2D plan the grid is hidden |
79
+ */
80
+ export const PluginGridStoreyVisibility = z.object({
81
+ activeStorey: z.number(),
82
+ disabledOnStoreys: z.array(z.number()),
83
+ })
84
+ export type PluginGridStoreyVisibility = z.infer<typeof PluginGridStoreyVisibility>
85
+
86
+ // ---------------------------------------------------------------------------
87
+ // get — args & result
88
+ // ---------------------------------------------------------------------------
89
+
90
+ /**
91
+ * Property names readable via {@linkcode PluginDesignQueryGridsApi.get}.
92
+ *
93
+ * | Value | Description |
94
+ * |---|---|
95
+ * | `"angle"` | World rotation about +Y, in **degrees** |
96
+ * | `"props"` | Group-level default line properties |
97
+ * | `"storeyVisibility"` | Active storey + storeys where the grid is hidden in 2D |
98
+ * | `"wings"` | Full wing/line structure with world-space curve handles |
99
+ * | `"bounds"` | World-space axis-aligned bounding box of the grid geometry |
100
+ */
101
+ export const PluginGridGetProperty = z.enum([
102
+ "angle",
103
+ "props",
104
+ "storeyVisibility",
105
+ "wings",
106
+ "bounds",
107
+ ])
108
+ export type PluginGridGetProperty = z.infer<typeof PluginGridGetProperty>
109
+
110
+ /**
111
+ * Arguments for {@linkcode PluginDesignQueryGridsApi.get}.
112
+ *
113
+ * | Property | Type | Description |
114
+ * |---|---|---|
115
+ * | `gridId` | `string` | The grid group ID to query |
116
+ * | `properties` | {@linkcode PluginGridGetProperty}`[]` | Properties to retrieve |
117
+ */
118
+ export const PluginGridGetArgs = z.object({
119
+ gridId: z.string(),
120
+ properties: z.array(PluginGridGetProperty),
121
+ })
122
+ export type PluginGridGetArgs = z.infer<typeof PluginGridGetArgs>
123
+
124
+ /**
125
+ * Result of {@linkcode PluginDesignQueryGridsApi.get} — only the requested
126
+ * properties are present.
127
+ */
128
+ export const PluginGridGetResult = z
129
+ .object({
130
+ angle: z.number(),
131
+ props: PluginGridLineProps,
132
+ storeyVisibility: PluginGridStoreyVisibility,
133
+ wings: z.array(PluginGridWingRecord),
134
+ bounds: BBoxComponents,
135
+ })
136
+ .partial()
137
+ export type PluginGridGetResult = z.infer<typeof PluginGridGetResult>
138
+
139
+ // ---------------------------------------------------------------------------
140
+ // Abstract API
141
+ // ---------------------------------------------------------------------------
142
+
143
+ /**
144
+ * `design.query.grids.*` — structural-grid-specific reads.
145
+ *
146
+ * A **grid** (grid group) is a structural/documentation grid: one or more
147
+ * *wings* (families of parallel lines with a shared local frame), each holding
148
+ * tagged grid *lines* (straight lines, or a lone arc). Enumerate grids with
149
+ * `design.query.listGrids`; read an individual grid's structure here.
150
+ *
151
+ * Lines have no stable identity of their own — mutating APIs address them
152
+ * positionally as `{ wing, line }` index pairs into the `wings` arrays
153
+ * returned by {@linkcode get}. Structural edits (adding/removing lines,
154
+ * count changes) re-index lines; re-query after mutating.
155
+ *
156
+ * Accessed via `snaptrude.design.query.grids`.
157
+ */
158
+ export abstract class PluginDesignQueryGridsApi {
159
+ constructor() {}
160
+
161
+ /**
162
+ * Get properties of a grid by its ID.
163
+ *
164
+ * Only the properties listed in `properties` are returned — unlisted
165
+ * properties will be `undefined` in the result. `wings` is the heavy
166
+ * read (it registers a world-space {@linkcode CurveHandle} per line);
167
+ * request it only when the geometry is needed.
168
+ *
169
+ * Units: `angle` is in **degrees** (positive = CCW about +Y); `spacing`,
170
+ * `span`, `bounds`, and curve coordinates are in **Snaptrude units**,
171
+ * world space.
172
+ *
173
+ * @param gridId - The unique grid group ID
174
+ * @param properties - Array of property names to retrieve. See
175
+ * {@linkcode PluginGridGetProperty}.
176
+ * @returns A partial {@linkcode PluginGridGetResult} containing only the
177
+ * requested properties
178
+ * @throws If the grid does not exist
179
+ *
180
+ * @examplePrompt What is the spacing of this grid?
181
+ * @examplePrompt Read the grid line tags on this structural grid
182
+ * @examplePrompt Get the geometry of every line in this grid
183
+ * @examplePrompt Which storeys is this grid hidden on?
184
+ *
185
+ * # Example
186
+ * ```ts
187
+ * const [grid] = await snaptrude.design.query.listGrids()
188
+ * const { wings } = await snaptrude.design.query.grids.get(grid, ["wings"])
189
+ * // wings[0].lines[0].curve is an opaque CurveHandle in world space;
190
+ * // read its coordinates via `snaptrude.core.geom.curve`.
191
+ * ```
192
+ */
193
+ public abstract get(
194
+ gridId: string,
195
+ properties: z.infer<typeof PluginGridGetProperty>[],
196
+ ): PluginApiReturn<PluginGridGetResult>
197
+ }
@@ -11,6 +11,7 @@ import { PluginStaircasePreset } from "../create"
11
11
  import { PluginDesignQueryGeometryApi } from "./geometry"
12
12
  import { PluginDesignQuerySpacesApi } from "./spaces"
13
13
  import { PluginDesignQueryReferenceLinesApi } from "./referenceLines"
14
+ import { PluginDesignQueryGridsApi } from "./grids"
14
15
 
15
16
  // ---------------------------------------------------------------------------
16
17
  // Entity type vocabulary
@@ -42,6 +43,7 @@ import { PluginDesignQueryReferenceLinesApi } from "./referenceLines"
42
43
  * | `"mullion"` | Curtain-wall mullion |
43
44
  * | `"panel"` | Curtain-wall panel |
44
45
  * | `"parametricComponent"` | A placed parametric family instance (native or Revit-imported). Its geometry is rebuilt from `design.family` parameters, so read and drive it through `design.family.getParameters` / `setParameters` rather than editing its meshes. |
46
+ * | `"grid"` | Structural grid group |
45
47
  */
46
48
  export const PluginEntityType = z.enum([
47
49
  "wall",
@@ -62,6 +64,7 @@ export const PluginEntityType = z.enum([
62
64
  "mullion",
63
65
  "panel",
64
66
  "parametricComponent",
67
+ "grid",
65
68
  ])
66
69
  export type PluginEntityType = z.infer<typeof PluginEntityType>
67
70
 
@@ -306,6 +309,8 @@ export abstract class PluginDesignQueryApi {
306
309
  public abstract spaces: PluginDesignQuerySpacesApi
307
310
  /** Reference-line-specific reads. See {@linkcode PluginDesignQueryReferenceLinesApi}. */
308
311
  public abstract referenceLines: PluginDesignQueryReferenceLinesApi
312
+ /** Structural-grid-specific reads. See {@linkcode PluginDesignQueryGridsApi}. */
313
+ public abstract grids: PluginDesignQueryGridsApi
309
314
 
310
315
  constructor() {}
311
316
 
@@ -723,6 +728,27 @@ export abstract class PluginDesignQueryApi {
723
728
  filter?: PluginEntityFilter,
724
729
  ): PluginApiReturn<ComponentHandle[]>
725
730
 
731
+ /**
732
+ * List all the structural grids (grid groups) in the project, optionally
733
+ * filtered by storey, building, selection state, or region.
734
+ *
735
+ * @param filter - an optional {@linkcode PluginEntityFilter}
736
+ * @returns grid-group {@linkcode ComponentHandle}`[]`
737
+ *
738
+ * @examplePrompt List all the structural grids in the model
739
+ * @examplePrompt How many grids are on the ground floor?
740
+ * @examplePrompt Show the grids on this storey
741
+ * @examplePrompt Which grids are currently selected?
742
+ *
743
+ * # Example
744
+ * ```ts
745
+ * const groundFloorGrids = await snaptrude.design.query.listGrids({ storeys: [1] })
746
+ * ```
747
+ */
748
+ public abstract listGrids(
749
+ filter?: PluginEntityFilter,
750
+ ): PluginApiReturn<ComponentHandle[]>
751
+
726
752
  // ── single-entity access & identity ─────────────────────────────────────────
727
753
 
728
754
  /**
@@ -1147,3 +1173,4 @@ export type PluginStoreyOutlineResult = z.infer<
1147
1173
  export * from "./geometry"
1148
1174
  export * from "./spaces"
1149
1175
  export * from "./referenceLines"
1176
+ export * from "./grids"
@@ -15,6 +15,7 @@ import {
15
15
  PluginBuildableEnvelopeVerticalCap,
16
16
  PluginBuildableEnvelopeUpdateResult,
17
17
  } from "../../entity/buildableEnvelope"
18
+ import { PluginGridStyleOptions } from "../create/grids"
18
19
 
19
20
  /**
20
21
  * Sparse property updates for a space. Only provided fields change.
@@ -238,6 +239,27 @@ export type PluginDesignSetStoreyArgs = z.infer<
238
239
  typeof PluginDesignSetStoreyArgs
239
240
  >
240
241
 
242
+ /**
243
+ * The sparse GROUP-LEVEL patch accepted by {@linkcode PluginDesignUpdateApi.grid}.
244
+ * Only provided fields change. Wing- and line-level updates live on the grids
245
+ * entity family: `design.grids.updateWings` / `design.grids.updateLines`.
246
+ */
247
+ export const PluginGridPatch = z.object({
248
+ angle: z.number().finite().optional(),
249
+ props: PluginGridStyleOptions.optional(),
250
+ storeyVisibility: z
251
+ .array(z.object({ storey: z.number().int(), visible: z.boolean() }))
252
+ .optional(),
253
+ })
254
+ export type PluginGridPatch = z.infer<typeof PluginGridPatch>
255
+
256
+ /** Arguments for {@linkcode PluginDesignUpdateApi.grid}. */
257
+ export const PluginDesignUpdateGridArgs = z.object({
258
+ grid: ComponentHandle,
259
+ patch: PluginGridPatch,
260
+ })
261
+ export type PluginDesignUpdateGridArgs = z.infer<typeof PluginDesignUpdateGridArgs>
262
+
241
263
  /**
242
264
  * `snaptrude.design.update` — edit existing scene entities (properties + geometry).
243
265
  *
@@ -290,6 +312,37 @@ export abstract class PluginDesignUpdateApi {
290
312
  label: string,
291
313
  ): PluginApiReturn<boolean>
292
314
 
315
+ /**
316
+ * Update a structural grid's **group-level** fields with a sparse patch —
317
+ * default line styling, absolute angle, and per-storey 2D visibility. Only
318
+ * provided fields change. Wing- and line-level updates live on the grids
319
+ * entity family: {@linkcode PluginDesignGridsApi.updateWings} /
320
+ * {@linkcode PluginDesignGridsApi.updateLines}.
321
+ *
322
+ * Every change routes through the exact setters the native panel uses —
323
+ * each changed field commits its own undoable command, identical to editing
324
+ * that field in the panel. `angle` is an absolute target in degrees (CCW
325
+ * about +Y).
326
+ *
327
+ * @param grid - The grid group to update
328
+ * @param patch - The sparse {@linkcode PluginGridPatch}
329
+ * @returns `true` once applied
330
+ *
331
+ * @examplePrompt Rotate the grid to 45 degrees
332
+ * @examplePrompt Make this grid's lines dashed and grey
333
+ * @examplePrompt Hide this grid on storey 3's plan
334
+ *
335
+ * # Example
336
+ * ```ts
337
+ * const [grid] = await snaptrude.design.query.listGrids()
338
+ * await snaptrude.design.update.grid(grid, { angle: 45, props: { style: "DASHED" } })
339
+ * ```
340
+ */
341
+ public abstract grid(
342
+ grid: ComponentHandle,
343
+ patch: PluginGridPatch,
344
+ ): PluginApiReturn<boolean>
345
+
293
346
  /**
294
347
  * Update a single space (room) — change its properties (room label, space
295
348
  * type, mass type, area class, department) and/or replace its geometry by
@@ -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
  *