@snaptrude/plugin-core 1.1.0-dev.20261025.1 → 1.1.0-pbr-20261028.1

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": "1.1.0-dev.20261025.1",
3
+ "version": "1.1.0-pbr-20261028.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -48,16 +48,20 @@ export abstract class PluginDesignMaterialsApi {
48
48
 
49
49
  /**
50
50
  * List the preset material library — the catalog the app's material browser
51
- * shows (the built-in finishes plus the workspace's uploaded materials),
52
- * fetched from the material library service. Library entries are **not**
53
- * scene materials and carry no {@linkcode MaterialHandle}; each is a named
54
- * texture record. To use one, feed it to
51
+ * shows: the deployment PBR catalogue plus the server library and workspace uploads.
52
+ * Library entries are **not** scene materials and carry no {@linkcode MaterialHandle}.
53
+ * PBR names include an identity-qualified suffix `[catalogId@catalogVersion]`:
54
+ * preserve the EXACT returned name and pass `{ name: preset.name }` to create/apply.
55
+ * The host resolves all authored PBR maps, physical coverage and glass properties;
56
+ * do not strip the suffix, substitute an image-only finish, or override color/alpha.
57
+ * Scalar paints and clear glass can have an empty textureUrl and are still valid.
58
+ * For legacy image entries, feed the returned record to
55
59
  * {@linkcode PluginDesignMaterialsApi.create} /
56
60
  * {@linkcode PluginDesignMaterialsApi.apply} as a
57
61
  * {@linkcode PluginMaterialSpec}: `{ name, textureUrl, materialType: category }`.
58
62
  *
59
- * @returns The library as {@linkcode PluginPresetMaterial}`[]` (`[]` when the
60
- * library service is unreachable)
63
+ * @returns The library as {@linkcode PluginPresetMaterial}`[]`. Missing/malformed
64
+ * deployment PBR data rejects; the legacy server library may return `[]` on failure.
61
65
  *
62
66
  * @examplePrompt Show me the preset materials library
63
67
  * @examplePrompt What wood finishes are available in the material library?
@@ -167,6 +171,25 @@ export abstract class PluginDesignMaterialsApi {
167
171
  save?: boolean,
168
172
  ): PluginApiReturn<PluginMaterialResult>
169
173
 
174
+ /**
175
+ * Paint only glazing or frame slots, never the whole window/door. This preserves
176
+ * clear panes when painting metal frames. Families change with their linked copies;
177
+ * legacy instanced types change every instance of that type. BREP components are
178
+ * refused (use applyToFaces), as are locked targets or locked linked/type members.
179
+ * Slot roles come from the authored appearance, not a previously applied paint.
180
+ * Always one undo step. Catalogue selectors use the exact listPresets name.
181
+ * @param components Components whose family/type slots to paint
182
+ * @param slot Authored glazing slots or every other (frame/hardware) slot
183
+ * @param material Existing handle or inline spec, e.g. `{ name: preset.name }`
184
+ * @returns Count of targets whose family/type had at least one matching slot
185
+ * @examplePrompt Paint only the window frames with a catalogue metal finish
186
+ */
187
+ public abstract applyToSlot(
188
+ components: ComponentHandle[],
189
+ slot: "glazing" | "frame",
190
+ material: MaterialHandle | PluginMaterialSpec,
191
+ ): PluginApiReturn<PluginMaterialResult>
192
+
170
193
  /**
171
194
  * Reset one or more components back to the default material for their type,
172
195
  * removing any applied finish. The inverse of `apply`; undoable when `save`
@@ -425,6 +448,7 @@ export abstract class PluginDesignMaterialsApi {
425
448
  * | `alpha` | `number` | Opacity `0..1` |
426
449
  * | `materialType` | `string` | Material type tag (optional) |
427
450
  * | `textureUrl` | `string` | Diffuse texture URL (optional) |
451
+ * | `transmission` | `number` | Diffuse transmission `0..1` (optional; absent when none) |
428
452
  */
429
453
  export const PluginMaterialInfo = z.object({
430
454
  name: z.string(),
@@ -433,6 +457,7 @@ export const PluginMaterialInfo = z.object({
433
457
  alpha: z.number(),
434
458
  materialType: z.string().optional(),
435
459
  textureUrl: z.string().optional(),
460
+ transmission: z.number().optional(),
436
461
  })
437
462
  export type PluginMaterialInfo = z.infer<typeof PluginMaterialInfo>
438
463
 
@@ -441,11 +466,12 @@ export type PluginMaterialInfo = z.infer<typeof PluginMaterialInfo>
441
466
  *
442
467
  * | Property | Type | Description |
443
468
  * |---|---|---|
444
- * | `name` | `string` | Name (optional; generated if omitted) |
469
+ * | `name` | `string` | Name (optional; generated if omitted); an exact identity-qualified listPresets name selects the full authored PBR finish |
445
470
  * | `color` | `string` | Diffuse color `#rrggbb` (optional) |
446
471
  * | `textureUrl` | `string` | Diffuse texture URL (optional; wins over color) |
447
472
  * | `alpha` | `number` | Opacity `0..1` (optional) |
448
473
  * | `materialType` | `string` | Material type tag (optional) |
474
+ * | `transmission` | `number` | Diffuse transmission `0..1` (optional; stored on the material's PBR data). Not combinable with a catalog (listPresets) name |
449
475
  */
450
476
  export const PluginMaterialSpec = z.object({
451
477
  name: z.string().optional(),
@@ -453,6 +479,7 @@ export const PluginMaterialSpec = z.object({
453
479
  textureUrl: z.string().optional(),
454
480
  alpha: z.number().optional(),
455
481
  materialType: z.string().optional(),
482
+ transmission: z.number().min(0).max(1).optional(),
456
483
  })
457
484
  export type PluginMaterialSpec = z.infer<typeof PluginMaterialSpec>
458
485
 
@@ -544,6 +571,16 @@ export type PluginDesignMaterialsApplyArgs = z.infer<
544
571
  typeof PluginDesignMaterialsApplyArgs
545
572
  >
546
573
 
574
+ /** Arguments for the existing host slot-paint contract. */
575
+ export const PluginDesignMaterialsApplyToSlotArgs = z.object({
576
+ components: z.array(ComponentHandle),
577
+ slot: z.enum(["glazing", "frame"]),
578
+ material: z.union([MaterialHandle, PluginMaterialSpec]),
579
+ })
580
+ export type PluginDesignMaterialsApplyToSlotArgs = z.infer<
581
+ typeof PluginDesignMaterialsApplyToSlotArgs
582
+ >
583
+
547
584
  /** Arguments for {@linkcode PluginDesignMaterialsApi.reset}. */
548
585
  export const PluginDesignMaterialsResetArgs = z.object({
549
586
  components: z.array(ComponentHandle),
@@ -1,6 +1,5 @@
1
1
  import * as z from "zod"
2
2
  import type { PluginApiReturn } from "../../types"
3
- import { Vec3Handle } from "../../handles"
4
3
 
5
4
  const id = z.string().regex(/^[A-Za-z][A-Za-z0-9_-]{0,79}$/)
6
5
  const vector = z.tuple([
@@ -413,47 +412,6 @@ export const NetworkCreateArgs = NetworkValidateArgs.extend({
413
412
  requestId: z.string().uuid(),
414
413
  })
415
414
  export const NetworkGetArgs = z.object({ graphId: z.string().uuid() }).strict()
416
- /** Displacement components are world millimetres, Y up, not Snaptrude units. */
417
- export const NetworkMoveEdgeArgs = NetworkGetArgs.extend({
418
- expectedRevision: z.number().int().positive(),
419
- edgeId: id,
420
- displacement: Vec3Handle,
421
- requestId: z.string().uuid(),
422
- }).strict()
423
- export type NetworkMoveEdgeArgs = z.input<typeof NetworkMoveEdgeArgs>
424
-
425
- export const NetworkMoveEdgeResult = z.object({
426
- graphId: z.string().uuid(),
427
- revision: z.number().int().positive(),
428
- affectedComponentIds: z.array(z.string().min(1)),
429
- alreadyApplied: z.boolean(),
430
- /** Queue acceptance does not acknowledge durable storage. */
431
- saveStatus: z.enum(["queued", "existing"]),
432
- }).strict()
433
- export type NetworkMoveEdgeResult = z.infer<typeof NetworkMoveEdgeResult>
434
-
435
- /** Immutable receipts survive undo. Retrying a move never executes it again. */
436
- export const NetworkEditReceipt = z.object({
437
- requestId: z.string().uuid(),
438
- fingerprint: z.string().max(1000),
439
- revision: z.number().int().positive(),
440
- affectedComponentIds: z.array(z.string().min(1)).max(1000),
441
- }).strict()
442
- export type NetworkEditReceipt = z.infer<typeof NetworkEditReceipt>
443
-
444
- /** Each intentional apply/undo/redo has its own identity. Retries reuse it. */
445
- export const NetworkTransition = z.object({
446
- id: z.string().uuid(),
447
- expectedRevision: z.number().int().positive(),
448
- forward: z.boolean(),
449
- }).strict()
450
- export type NetworkTransition = z.infer<typeof NetworkTransition>
451
-
452
- export const NetworkTransitionReceipt = NetworkTransition.extend({
453
- requestId: z.string().uuid(),
454
- revision: z.number().int().positive(),
455
- }).strict()
456
- export type NetworkTransitionReceipt = z.infer<typeof NetworkTransitionReceipt>
457
415
  export const NetworkContextArgs = z
458
416
  .object({
459
417
  offset: z.number().int().nonnegative().default(0),
@@ -516,12 +474,10 @@ export type NetworkComponentBinding = z.infer<typeof NetworkComponentBinding>
516
474
  export interface SavedNetworkGraph {
517
475
  id: string
518
476
  domain: NetworkDomain
519
- revision: number
477
+ revision: 1
520
478
  requestId: string
521
479
  document: NetworkGraphDocument
522
480
  components: NetworkComponentBinding[]
523
- edits?: NetworkEditReceipt[]
524
- transitions?: NetworkTransitionReceipt[]
525
481
  }
526
482
  export interface NetworkCreateResult {
527
483
  graphId: string
@@ -646,22 +602,6 @@ export abstract class PluginDomainNetworkApi {
646
602
  /** Electrical equipment, physical routes and logical circuits, constructed from agent-authored intent. */
647
603
  export abstract class PluginElectricalApi extends PluginDomainNetworkApi {}
648
604
  /** Plumbing equipment and routed pipe networks, constructed from agent-authored intent. */
649
- export abstract class PluginPlumbingApi extends PluginDomainNetworkApi {
650
- /** Offset one straight pipe perpendicular to its axis between two rigid 90-degree elbows.
651
- * Resize adjacent pipes through instance length parameters. Out-of-plane offsets add
652
- * one pinned elbow and straight pipe per affected end, preserving fixed outer ports
653
- * and existing component identities. Rigid fittings only rotate and translate.
654
- * Reject axial sliding, moving tees, constrained fittings,
655
- * scene drift, stale revisions, and infeasible family geometry before mutation.
656
- * @param args Graph/edge identity, current revision, displacement handle in world mm,
657
- * and a unique retry identity. Identical retries return the original receipt after undo too.
658
- * @returns Affected component IDs and revision in one undoable, queued transaction.
659
- * @example
660
- * const displacement = await snaptrude.core.math.vec3.new(0, 300, 0)
661
- * await snaptrude.design.plumbing.moveEdge({ graphId, edgeId, expectedRevision,
662
- * displacement, requestId: crypto.randomUUID() })
663
- */
664
- abstract moveEdge(args: NetworkMoveEdgeArgs): PluginApiReturn<NetworkMoveEdgeResult>
665
- }
605
+ export abstract class PluginPlumbingApi extends PluginDomainNetworkApi {}
666
606
  /** Family-backed air-side HVAC networks. The agent authors engineering intent; the host constructs it. */
667
607
  export abstract class PluginMechanicalApi extends PluginDomainNetworkApi {}
@@ -1,8 +1,9 @@
1
1
  import * as z from "zod"
2
2
  import { PluginApiReturn } from "../../../types"
3
- import { ComponentHandle } from "../../../handles"
3
+ import { ComponentHandle, MaterialHandle } from "../../../handles"
4
4
  import { PluginDesignChangeResult } from "../lock"
5
5
  import { PluginObjectCatalogGroup, PluginObjectCatalogItem } from "../doors"
6
+ import { PluginMaterialSpec } from "../materials"
6
7
 
7
8
  /**
8
9
  * `snaptrude.design.windows` — window-specific reads. Creation lives at
@@ -22,6 +23,33 @@ import { PluginObjectCatalogGroup, PluginObjectCatalogItem } from "../doors"
22
23
  export abstract class PluginDesignWindowsApi {
23
24
  constructor() {}
24
25
 
26
+ /**
27
+ * Paint only the window's authored glazing, preserving its frame. Families change
28
+ * with linked copies; legacy catalogue types change every instance of that type.
29
+ * Undoable; refuses locked linked/type members or out-of-proposal targets.
30
+ * @param window Window whose glazing to paint
31
+ * @param material Handle or spec; preserve the exact listPresets name for PBR glass
32
+ * @returns Components affected, including linked/type instances
33
+ * @examplePrompt Use clear glass for this window without changing its frame
34
+ */
35
+ public abstract setGlazing(
36
+ window: ComponentHandle,
37
+ material: MaterialHandle | PluginMaterialSpec,
38
+ ): PluginApiReturn<PluginDesignChangeResult>
39
+
40
+ /**
41
+ * Paint only non-glazing slots (frames, sashes, hardware), never the clear panes.
42
+ * Scope, locking and undo match setGlazing and design.materials.applyToSlot.
43
+ * @param window Window whose frame to paint
44
+ * @param material Handle or spec, e.g. `{ name: preset.name }` from listPresets
45
+ * @returns Components affected, including linked/type instances
46
+ * @examplePrompt Use brushed metal for this window's frame without changing the glass
47
+ */
48
+ public abstract setFrame(
49
+ window: ComponentHandle,
50
+ material: MaterialHandle | PluginMaterialSpec,
51
+ ): PluginApiReturn<PluginDesignChangeResult>
52
+
25
53
  /**
26
54
  * Get the type name of a window — the label that identifies which kind of
27
55
  * window the instance is, as shown in its properties.
@@ -310,6 +338,15 @@ export type PluginDesignWindowSetHeightArgs = z.infer<
310
338
  typeof PluginDesignWindowSetHeightArgs
311
339
  >
312
340
 
341
+ /** Arguments for setGlazing / setFrame; both preserve the other authored slot kind. */
342
+ export const PluginDesignWindowSetSlotArgs = z.object({
343
+ window: ComponentHandle,
344
+ material: z.union([MaterialHandle, PluginMaterialSpec]),
345
+ })
346
+ export type PluginDesignWindowSetSlotArgs = z.infer<
347
+ typeof PluginDesignWindowSetSlotArgs
348
+ >
349
+
313
350
  // ---------------------------------------------------------------------------
314
351
  // catalog (shared DTOs defined in ../doors, re-exported here for symmetry)
315
352
  // ---------------------------------------------------------------------------