@snaptrude/plugin-core 0.6.0 → 0.7.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/CHANGELOG.md +29 -0
- package/api-manifest.json +206 -704
- package/dist/api/core/index.d.ts +5 -0
- package/dist/api/core/index.d.ts.map +1 -1
- package/dist/api/core/proposals/index.d.ts +475 -0
- package/dist/api/core/proposals/index.d.ts.map +1 -0
- package/dist/api/design/create/index.d.ts +209 -0
- package/dist/api/design/create/index.d.ts.map +1 -1
- package/dist/api/design/doors/index.d.ts +159 -0
- package/dist/api/design/doors/index.d.ts.map +1 -1
- package/dist/api/design/furniture/index.d.ts +34 -5
- package/dist/api/design/furniture/index.d.ts.map +1 -1
- package/dist/api/design/index.d.ts +83 -0
- package/dist/api/design/index.d.ts.map +1 -1
- package/dist/api/design/lock.d.ts +26 -0
- package/dist/api/design/lock.d.ts.map +1 -1
- package/dist/api/design/materials/index.d.ts +159 -2
- package/dist/api/design/materials/index.d.ts.map +1 -1
- package/dist/api/design/query/index.d.ts +115 -5
- package/dist/api/design/query/index.d.ts.map +1 -1
- package/dist/api/design/selection/index.d.ts +2 -2
- package/dist/api/design/transform/index.d.ts +83 -2
- package/dist/api/design/transform/index.d.ts.map +1 -1
- package/dist/api/design/update/index.d.ts +168 -0
- package/dist/api/design/update/index.d.ts.map +1 -1
- package/dist/api/design/windows/index.d.ts +113 -2
- package/dist/api/design/windows/index.d.ts.map +1 -1
- package/dist/api/entity/story.d.ts +101 -7
- package/dist/api/entity/story.d.ts.map +1 -1
- package/dist/api/presentation/aiInspiration.d.ts +1967 -7
- package/dist/api/presentation/aiInspiration.d.ts.map +1 -1
- package/dist/api/presentation/import.d.ts +49 -4
- package/dist/api/presentation/import.d.ts.map +1 -1
- package/dist/api/presentation/index.d.ts +2 -3
- package/dist/api/presentation/index.d.ts.map +1 -1
- package/dist/api/presentation/views.d.ts +210 -5
- package/dist/api/presentation/views.d.ts.map +1 -1
- package/dist/api/program/areas.d.ts +42 -12
- package/dist/api/program/areas.d.ts.map +1 -1
- package/dist/api/program/index.d.ts +3 -11
- package/dist/api/program/index.d.ts.map +1 -1
- package/dist/api/program/layout.d.ts +186 -11
- package/dist/api/program/layout.d.ts.map +1 -1
- package/dist/errors/codes.d.ts +34 -0
- package/dist/errors/codes.d.ts.map +1 -0
- package/dist/errors/envelope.d.ts +56 -0
- package/dist/errors/envelope.d.ts.map +1 -0
- package/dist/errors/index.d.ts +6 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/plugin-error.d.ts +69 -0
- package/dist/errors/plugin-error.d.ts.map +1 -0
- package/dist/host-utils.d.ts +4 -0
- package/dist/host-utils.d.ts.map +1 -1
- package/dist/index.cjs +1993 -1033
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1874 -1030
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/scripts/generate-manifest.test.mjs +77 -0
- package/src/api/core/index.ts +5 -0
- package/src/api/core/proposals/index.ts +509 -0
- package/src/api/design/create/index.ts +249 -0
- package/src/api/design/doors/index.ts +168 -0
- package/src/api/design/furniture/index.ts +36 -5
- package/src/api/design/index.ts +87 -0
- package/src/api/design/lock.ts +27 -0
- package/src/api/design/materials/index.ts +182 -2
- package/src/api/design/query/index.ts +81 -0
- package/src/api/design/transform/index.ts +74 -2
- package/src/api/design/update/index.ts +167 -0
- package/src/api/design/windows/index.ts +128 -2
- package/src/api/entity/story.ts +105 -7
- package/src/api/presentation/aiInspiration.ts +761 -7
- package/src/api/presentation/import.ts +45 -4
- package/src/api/presentation/index.ts +2 -3
- package/src/api/presentation/views.ts +221 -5
- package/src/api/program/areas.ts +34 -12
- package/src/api/program/index.ts +3 -11
- package/src/api/program/layout.ts +195 -11
- package/src/errors/codes.ts +136 -0
- package/src/errors/envelope.ts +75 -0
- package/src/errors/index.ts +21 -0
- package/src/errors/plugin-error.ts +134 -0
- package/src/host-utils.ts +4 -0
- package/src/index.ts +1 -0
- package/test/errors.test.mjs +184 -0
- package/api-manifest.full.json +0 -5600
|
@@ -24,6 +24,13 @@ import {
|
|
|
24
24
|
* Footprint objects take a {@linkcode ContourHandle} (outer profile + holes);
|
|
25
25
|
* build one with `snaptrude.core.geom.create.contourFromProfile(s)`.
|
|
26
26
|
*
|
|
27
|
+
* `door`/`window` place a catalog item into a host wall (the world point is
|
|
28
|
+
* projected onto the wall face to locate the opening; the wall is re-cut in one
|
|
29
|
+
* undo entry) and throw if the point projects outside the wall. `smartLayout` is
|
|
30
|
+
* the one creator returning a grouped {@linkcode PluginSmartLayoutResult} rather
|
|
31
|
+
* than a handle — it clones an in-scene template into one or more target spaces,
|
|
32
|
+
* merging every target into a single undo batch (see its JSDoc).
|
|
33
|
+
*
|
|
27
34
|
* Accessed via `snaptrude.design.create`.
|
|
28
35
|
*/
|
|
29
36
|
export abstract class PluginDesignCreateApi {
|
|
@@ -433,6 +440,142 @@ export abstract class PluginDesignCreateApi {
|
|
|
433
440
|
options?: { label?: string; createNewSourceMesh?: boolean },
|
|
434
441
|
): PluginApiReturn<ComponentHandle>
|
|
435
442
|
|
|
443
|
+
/**
|
|
444
|
+
* Place a **door** from the catalog into a host wall.
|
|
445
|
+
*
|
|
446
|
+
* Identified by a **catalog (library) id** — the team object's stable `_id`
|
|
447
|
+
* or the general library's `fullName`. Discover ids with
|
|
448
|
+
* {@linkcode PluginDesignDoorsApi.listCatalog}. The world `position` is
|
|
449
|
+
* projected onto the host wall's face to locate the opening; the wall is
|
|
450
|
+
* re-cut and the door hosted in a single undo entry. Placement is
|
|
451
|
+
* asynchronous (the source mesh is fetched if not cached).
|
|
452
|
+
*
|
|
453
|
+
* @param catalogId - Library id: team `_id` or general `fullName`
|
|
454
|
+
* @param hostWall - The wall to host the door
|
|
455
|
+
* @param position - World point projected onto the wall to locate the opening
|
|
456
|
+
* @param options - Optional placement options: `label` — instance name
|
|
457
|
+
* @returns the {@linkcode ComponentHandle} of the placed door
|
|
458
|
+
* @throws if the catalog id is unknown, the host is not a wall, the source
|
|
459
|
+
* mesh fails to load, or the projected point falls **outside** the host wall
|
|
460
|
+
*
|
|
461
|
+
* @examplePrompt Add a door to this wall here
|
|
462
|
+
* @examplePrompt Place a sliding door on the selected wall
|
|
463
|
+
* @examplePrompt Put a door from the catalog at this point on the wall
|
|
464
|
+
* @examplePrompt Insert the entrance door into this wall
|
|
465
|
+
* @examplePrompt Add a door to the wall and call it Entry-01
|
|
466
|
+
*
|
|
467
|
+
* # Example
|
|
468
|
+
* ```ts
|
|
469
|
+
* const [wall] = await snaptrude.design.query.listWalls({ isSelected: true })
|
|
470
|
+
* const [entry] = await snaptrude.design.doors.listCatalog()
|
|
471
|
+
* const door = await snaptrude.design.create.door(
|
|
472
|
+
* entry.id,
|
|
473
|
+
* wall,
|
|
474
|
+
* await snaptrude.core.math.vec3.new(3, 0, 5),
|
|
475
|
+
* { label: "Entry-01" },
|
|
476
|
+
* )
|
|
477
|
+
* ```
|
|
478
|
+
*/
|
|
479
|
+
public abstract door(
|
|
480
|
+
catalogId: string,
|
|
481
|
+
hostWall: ComponentHandle,
|
|
482
|
+
position: Vec3Handle,
|
|
483
|
+
options?: { label?: string },
|
|
484
|
+
): PluginApiReturn<ComponentHandle>
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Place a **window** from the catalog into a host wall.
|
|
488
|
+
*
|
|
489
|
+
* Identified by a **catalog (library) id** — the team object's stable `_id`
|
|
490
|
+
* or the general library's `fullName`. Discover ids with
|
|
491
|
+
* {@linkcode PluginDesignWindowsApi.listCatalog}. The world `position` is
|
|
492
|
+
* projected onto the host wall's face to locate the opening; the wall is
|
|
493
|
+
* re-cut and the window hosted in a single undo entry. Placement is
|
|
494
|
+
* asynchronous (the source mesh is fetched if not cached).
|
|
495
|
+
*
|
|
496
|
+
* @param catalogId - Library id: team `_id` or general `fullName`
|
|
497
|
+
* @param hostWall - The wall to host the window
|
|
498
|
+
* @param position - World point projected onto the wall to locate the opening
|
|
499
|
+
* @param options - Optional placement options: `label` — instance name
|
|
500
|
+
* @returns the {@linkcode ComponentHandle} of the placed window
|
|
501
|
+
* @throws if the catalog id is unknown, the host is not a wall, the source
|
|
502
|
+
* mesh fails to load, or the projected point falls **outside** the host wall
|
|
503
|
+
*
|
|
504
|
+
* @examplePrompt Add a window to this wall here
|
|
505
|
+
* @examplePrompt Place a casement window on the selected wall
|
|
506
|
+
* @examplePrompt Put a window from the catalog at this point on the wall
|
|
507
|
+
* @examplePrompt Insert a window into the living room wall
|
|
508
|
+
* @examplePrompt Add a window to the wall and name it Win-01
|
|
509
|
+
*
|
|
510
|
+
* # Example
|
|
511
|
+
* ```ts
|
|
512
|
+
* const [wall] = await snaptrude.design.query.listWalls({ isSelected: true })
|
|
513
|
+
* const [entry] = await snaptrude.design.windows.listCatalog()
|
|
514
|
+
* const window = await snaptrude.design.create.window(
|
|
515
|
+
* entry.id,
|
|
516
|
+
* wall,
|
|
517
|
+
* await snaptrude.core.math.vec3.new(3, 0, 5),
|
|
518
|
+
* { label: "Win-01" },
|
|
519
|
+
* )
|
|
520
|
+
* ```
|
|
521
|
+
*/
|
|
522
|
+
public abstract window(
|
|
523
|
+
catalogId: string,
|
|
524
|
+
hostWall: ComponentHandle,
|
|
525
|
+
position: Vec3Handle,
|
|
526
|
+
options?: { label?: string },
|
|
527
|
+
): PluginApiReturn<ComponentHandle>
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Place a **smart layout** — clone an in-scene template cluster (walls,
|
|
531
|
+
* furniture, doors, windows, floors) into one or more target ROOM/DEPARTMENT
|
|
532
|
+
* spaces, adapting orientation and fit to each target.
|
|
533
|
+
*
|
|
534
|
+
* Supply the template as **exactly one** of `templateGroup` (a group **id**
|
|
535
|
+
* from `snaptrude.core.groups.list` — groups are string-id entities, not
|
|
536
|
+
* component handles) or
|
|
537
|
+
* `templateComponents` (an explicit component set); every target in `targets`
|
|
538
|
+
* (≥1) receives its own placement. By default the target spaces are hidden
|
|
539
|
+
* after placement (`hideTargets`, mirroring the product) — pass `false` to keep
|
|
540
|
+
* them visible.
|
|
541
|
+
*
|
|
542
|
+
* The whole operation — every wall/opening/furniture/floor across every target,
|
|
543
|
+
* plus the target-hide — is committed as a **single undo step**. Because all
|
|
544
|
+
* targets merge into one creation batch, the created entities are returned
|
|
545
|
+
* **flat, grouped by kind** ({@linkcode PluginSmartLayoutResult}), NOT attributed
|
|
546
|
+
* per target (per-target attribution would break the single-undo guarantee).
|
|
547
|
+
* `skippedWalls` is the interior-optimizer's best-candidate count of walls it
|
|
548
|
+
* could not place. Synchronous and heavy — many targets can jank the main thread.
|
|
549
|
+
*
|
|
550
|
+
* @param options - {@linkcode PluginSmartLayoutArgs}: template source, targets, and `hideTargets`
|
|
551
|
+
* @returns a {@linkcode PluginSmartLayoutResult} — created entities grouped by kind + `skippedWalls`
|
|
552
|
+
* @throws if smart layout is not enabled, the template group is invalid, there
|
|
553
|
+
* is no template / no targets / no valid mappings, or component creation fails
|
|
554
|
+
*
|
|
555
|
+
* @examplePrompt Copy this room's layout into the selected rooms
|
|
556
|
+
* @examplePrompt Apply this furniture arrangement to the other offices
|
|
557
|
+
* @examplePrompt Replicate this template into these two spaces
|
|
558
|
+
* @examplePrompt Place this desk cluster into every meeting room
|
|
559
|
+
* @examplePrompt Use this room as a template for the rooms I selected but keep them visible
|
|
560
|
+
*
|
|
561
|
+
* # Example
|
|
562
|
+
* ```ts
|
|
563
|
+
* const [templateRoom, ...targets] = await snaptrude.design.query.listSpaces({ isSelected: true })
|
|
564
|
+
* const result = await snaptrude.design.create.smartLayout({
|
|
565
|
+
* templateComponents: [templateRoom],
|
|
566
|
+
* targets,
|
|
567
|
+
* })
|
|
568
|
+
* console.log("placed walls:", result.created.walls.length, "skipped:", result.skippedWalls)
|
|
569
|
+
*
|
|
570
|
+
* // …or clone a saved group by its id (from core.groups.list)
|
|
571
|
+
* const { groups } = await snaptrude.core.groups.list()
|
|
572
|
+
* await snaptrude.design.create.smartLayout({ templateGroup: groups[0].id, targets })
|
|
573
|
+
* ```
|
|
574
|
+
*/
|
|
575
|
+
public abstract smartLayout(
|
|
576
|
+
options: PluginSmartLayoutArgs,
|
|
577
|
+
): PluginApiReturn<PluginSmartLayoutResult>
|
|
578
|
+
|
|
436
579
|
/**
|
|
437
580
|
* **Copy** existing entities, offsetting each copy by `displacement * i` (i = 1…count),
|
|
438
581
|
* source positions preserved. Migrated from the removed `tools.copy`. In `"instance"`
|
|
@@ -804,6 +947,112 @@ export type PluginDesignCreateFurnitureArgs = z.infer<
|
|
|
804
947
|
// TRANSPORT: positional signature shipped —
|
|
805
948
|
// furniture(catalogId: string, position: Vec3Handle, options?: { label?, createNewSourceMesh? })
|
|
806
949
|
|
|
950
|
+
// ---------------------------------------------------------------------------
|
|
951
|
+
// door
|
|
952
|
+
// ---------------------------------------------------------------------------
|
|
953
|
+
|
|
954
|
+
/**
|
|
955
|
+
* Arguments for {@linkcode PluginDesignCreateApi.door}.
|
|
956
|
+
*
|
|
957
|
+
* | Property | Type | Description |
|
|
958
|
+
* |---|---|---|
|
|
959
|
+
* | `catalogId` | `string` | Library id: team `_id` or general `fullName` |
|
|
960
|
+
* | `hostWall` | {@linkcode ComponentHandle} | The wall to host the door |
|
|
961
|
+
* | `position` | {@linkcode Vec3Handle} | World point projected onto the wall |
|
|
962
|
+
* | `label` | `string`? | Instance name (optional) |
|
|
963
|
+
*/
|
|
964
|
+
export const PluginDesignCreateDoorArgs = z.object({
|
|
965
|
+
catalogId: z.string().min(1),
|
|
966
|
+
hostWall: ComponentHandle,
|
|
967
|
+
position: Vec3Handle,
|
|
968
|
+
label: z.string().optional(),
|
|
969
|
+
})
|
|
970
|
+
export type PluginDesignCreateDoorArgs = z.infer<typeof PluginDesignCreateDoorArgs>
|
|
971
|
+
// TRANSPORT: positional signature shipped —
|
|
972
|
+
// door(catalogId: string, hostWall: ComponentHandle, position: Vec3Handle, options?: { label? })
|
|
973
|
+
|
|
974
|
+
// ---------------------------------------------------------------------------
|
|
975
|
+
// window
|
|
976
|
+
// ---------------------------------------------------------------------------
|
|
977
|
+
|
|
978
|
+
/**
|
|
979
|
+
* Arguments for {@linkcode PluginDesignCreateApi.window}.
|
|
980
|
+
*
|
|
981
|
+
* | Property | Type | Description |
|
|
982
|
+
* |---|---|---|
|
|
983
|
+
* | `catalogId` | `string` | Library id: team `_id` or general `fullName` |
|
|
984
|
+
* | `hostWall` | {@linkcode ComponentHandle} | The wall to host the window |
|
|
985
|
+
* | `position` | {@linkcode Vec3Handle} | World point projected onto the wall |
|
|
986
|
+
* | `label` | `string`? | Instance name (optional) |
|
|
987
|
+
*/
|
|
988
|
+
export const PluginDesignCreateWindowArgs = z.object({
|
|
989
|
+
catalogId: z.string().min(1),
|
|
990
|
+
hostWall: ComponentHandle,
|
|
991
|
+
position: Vec3Handle,
|
|
992
|
+
label: z.string().optional(),
|
|
993
|
+
})
|
|
994
|
+
export type PluginDesignCreateWindowArgs = z.infer<typeof PluginDesignCreateWindowArgs>
|
|
995
|
+
// TRANSPORT: positional signature shipped —
|
|
996
|
+
// window(catalogId: string, hostWall: ComponentHandle, position: Vec3Handle, options?: { label? })
|
|
997
|
+
|
|
998
|
+
// ---------------------------------------------------------------------------
|
|
999
|
+
// smartLayout
|
|
1000
|
+
// ---------------------------------------------------------------------------
|
|
1001
|
+
|
|
1002
|
+
/**
|
|
1003
|
+
* Arguments for {@linkcode PluginDesignCreateApi.smartLayout}. Provide **exactly
|
|
1004
|
+
* one** template source: `templateGroup` or a non-empty `templateComponents`.
|
|
1005
|
+
*
|
|
1006
|
+
* | Property | Type | Description |
|
|
1007
|
+
* |---|---|---|
|
|
1008
|
+
* | `templateGroup` | `string`? | Id of the group to clone as the template (a group id from `snaptrude.core.groups.list`, NOT a component handle) |
|
|
1009
|
+
* | `templateComponents` | {@linkcode ComponentHandle}`[]`? | Explicit component set to clone as the template |
|
|
1010
|
+
* | `targets` | {@linkcode ComponentHandle}`[]` | ROOM/DEPARTMENT spaces to place into (≥1) |
|
|
1011
|
+
* | `hideTargets` | `boolean`? | Hide the target spaces after placement (default `true`) |
|
|
1012
|
+
*/
|
|
1013
|
+
export const PluginSmartLayoutArgs = z
|
|
1014
|
+
.object({
|
|
1015
|
+
templateGroup: z.string().min(1).optional(),
|
|
1016
|
+
templateComponents: z.array(ComponentHandle).optional(),
|
|
1017
|
+
targets: z.array(ComponentHandle).min(1),
|
|
1018
|
+
hideTargets: z.boolean().optional(),
|
|
1019
|
+
})
|
|
1020
|
+
.refine(
|
|
1021
|
+
(v) =>
|
|
1022
|
+
(v.templateGroup != null) !== ((v.templateComponents?.length ?? 0) > 0),
|
|
1023
|
+
{
|
|
1024
|
+
message:
|
|
1025
|
+
"Provide exactly one template source: templateGroup or a non-empty templateComponents",
|
|
1026
|
+
},
|
|
1027
|
+
)
|
|
1028
|
+
export type PluginSmartLayoutArgs = z.infer<typeof PluginSmartLayoutArgs>
|
|
1029
|
+
|
|
1030
|
+
/**
|
|
1031
|
+
* Result of {@linkcode PluginDesignCreateApi.smartLayout} — the entities the run
|
|
1032
|
+
* created, grouped by kind (flat across all targets; see the method JSDoc for why
|
|
1033
|
+
* this is not attributed per target).
|
|
1034
|
+
*
|
|
1035
|
+
* | Property | Type | Description |
|
|
1036
|
+
* |---|---|---|
|
|
1037
|
+
* | `created.walls` | {@linkcode ComponentHandle}`[]` | Created walls |
|
|
1038
|
+
* | `created.furniture` | {@linkcode ComponentHandle}`[]` | Created furniture |
|
|
1039
|
+
* | `created.doors` | {@linkcode ComponentHandle}`[]` | Created doors |
|
|
1040
|
+
* | `created.windows` | {@linkcode ComponentHandle}`[]` | Created windows |
|
|
1041
|
+
* | `created.floors` | {@linkcode ComponentHandle}`[]` | Created floors |
|
|
1042
|
+
* | `skippedWalls` | `number` | Walls the interior optimizer could not place |
|
|
1043
|
+
*/
|
|
1044
|
+
export const PluginSmartLayoutResult = z.object({
|
|
1045
|
+
created: z.object({
|
|
1046
|
+
walls: z.array(ComponentHandle),
|
|
1047
|
+
furniture: z.array(ComponentHandle),
|
|
1048
|
+
doors: z.array(ComponentHandle),
|
|
1049
|
+
windows: z.array(ComponentHandle),
|
|
1050
|
+
floors: z.array(ComponentHandle),
|
|
1051
|
+
}),
|
|
1052
|
+
skippedWalls: z.number(),
|
|
1053
|
+
})
|
|
1054
|
+
export type PluginSmartLayoutResult = z.infer<typeof PluginSmartLayoutResult>
|
|
1055
|
+
|
|
807
1056
|
// ---------------------------------------------------------------------------
|
|
808
1057
|
// referenceLines
|
|
809
1058
|
// ---------------------------------------------------------------------------
|
|
@@ -12,6 +12,13 @@ import { PluginDesignChangeResult } from "../lock"
|
|
|
12
12
|
* mesh reflection state. `mirror` reflects the door across an axis (undoable, one
|
|
13
13
|
* command); `setType` is intentionally absent (the engine has no in-place re-type —
|
|
14
14
|
* it would require delete+recreate).
|
|
15
|
+
*
|
|
16
|
+
* The **catalog** reads (`listCatalogGroups`/`listCatalog`/`getCatalogItem`/`exists`)
|
|
17
|
+
* browse the placeable door library — plain value records keyed by a library id
|
|
18
|
+
* (team `_id` or general `fullName`), NOT handles — and feed `design.create.door`.
|
|
19
|
+
* They are network-backed but carry no `Async` suffix (§6.6) and never throw
|
|
20
|
+
* (`[]`/`null`). The {@linkcode PluginObjectCatalogGroup} / {@linkcode PluginObjectCatalogItem}
|
|
21
|
+
* DTOs are shared with `design.windows`.
|
|
15
22
|
*/
|
|
16
23
|
export abstract class PluginDesignDoorsApi {
|
|
17
24
|
constructor() {}
|
|
@@ -224,6 +231,90 @@ export abstract class PluginDesignDoorsApi {
|
|
|
224
231
|
doors: ComponentHandle[],
|
|
225
232
|
height: number
|
|
226
233
|
): PluginApiReturn<PluginDesignChangeResult>
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* List the door catalog **groups** — the placement families (Swing, Sliding,
|
|
237
|
+
* Folding, Pivot, …), built-in groups merged with any team-custom types.
|
|
238
|
+
*
|
|
239
|
+
* @returns The groups as {@linkcode PluginObjectCatalogGroup}`[]` (`[]` when empty)
|
|
240
|
+
*
|
|
241
|
+
* @examplePrompt What kinds of doors can I place?
|
|
242
|
+
* @examplePrompt List the door groups in the catalog
|
|
243
|
+
* @examplePrompt Show me the door families available in this project
|
|
244
|
+
* @examplePrompt Which door types does this project support?
|
|
245
|
+
*
|
|
246
|
+
* # Example
|
|
247
|
+
* ```ts
|
|
248
|
+
* const groups = await snaptrude.design.doors.listCatalogGroups()
|
|
249
|
+
* for (const g of groups) console.log(g.dbType, g.label, g.source)
|
|
250
|
+
* ```
|
|
251
|
+
*/
|
|
252
|
+
public abstract listCatalogGroups(): PluginApiReturn<PluginObjectCatalogGroup[]>
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* List the placeable door catalog (team + general libraries), optionally
|
|
256
|
+
* restricted to one group by its `dbType` token.
|
|
257
|
+
*
|
|
258
|
+
* @param group Restrict to one group's `dbType` (optional; e.g. `"singleDoor"`)
|
|
259
|
+
* @returns The catalog as {@linkcode PluginObjectCatalogItem}`[]` (`[]` when empty)
|
|
260
|
+
*
|
|
261
|
+
* @examplePrompt What doors can I place in this project?
|
|
262
|
+
* @examplePrompt List every door in the library
|
|
263
|
+
* @examplePrompt Show me the sliding doors I can add
|
|
264
|
+
* @examplePrompt Browse the door catalog
|
|
265
|
+
*
|
|
266
|
+
* # Example
|
|
267
|
+
* ```ts
|
|
268
|
+
* const [group] = await snaptrude.design.doors.listCatalogGroups()
|
|
269
|
+
* const items = await snaptrude.design.doors.listCatalog(group?.dbType)
|
|
270
|
+
* for (const item of items) console.log(item.id, item.name, item.thumbnailUrl)
|
|
271
|
+
* ```
|
|
272
|
+
*/
|
|
273
|
+
public abstract listCatalog(
|
|
274
|
+
group?: string
|
|
275
|
+
): PluginApiReturn<PluginObjectCatalogItem[]>
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Get a single door catalog item by its library id.
|
|
279
|
+
*
|
|
280
|
+
* @param id The catalog library id
|
|
281
|
+
* @returns The item, or `null` if no catalog item matches
|
|
282
|
+
*
|
|
283
|
+
* @examplePrompt Get the details of this door from the library
|
|
284
|
+
* @examplePrompt Look up the catalog entry for this door id
|
|
285
|
+
* @examplePrompt What is the cost of this door item?
|
|
286
|
+
* @examplePrompt Show me the family name of this door in the catalog
|
|
287
|
+
*
|
|
288
|
+
* # Example
|
|
289
|
+
* ```ts
|
|
290
|
+
* const [entry] = await snaptrude.design.doors.listCatalog()
|
|
291
|
+
* const item = await snaptrude.design.doors.getCatalogItem(entry.id)
|
|
292
|
+
* if (item) console.log(item.name, item.source, item.dbType)
|
|
293
|
+
* ```
|
|
294
|
+
*/
|
|
295
|
+
public abstract getCatalogItem(
|
|
296
|
+
id: string
|
|
297
|
+
): PluginApiReturn<PluginObjectCatalogItem | null>
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Test whether a door catalog item exists for the given library id.
|
|
301
|
+
*
|
|
302
|
+
* @param id The catalog library id
|
|
303
|
+
* @returns `true` if it exists, otherwise `false`
|
|
304
|
+
*
|
|
305
|
+
* @examplePrompt Is this door still in the library?
|
|
306
|
+
* @examplePrompt Check whether a door catalog item with this id exists
|
|
307
|
+
* @examplePrompt Does my team library have this door?
|
|
308
|
+
* @examplePrompt Verify the door is available before placing it
|
|
309
|
+
*
|
|
310
|
+
* # Example
|
|
311
|
+
* ```ts
|
|
312
|
+
* const [entry] = await snaptrude.design.doors.listCatalog()
|
|
313
|
+
* const stillAvailable = await snaptrude.design.doors.exists(entry.id)
|
|
314
|
+
* console.log(stillAvailable ? "safe to place" : "removed from library")
|
|
315
|
+
* ```
|
|
316
|
+
*/
|
|
317
|
+
public abstract exists(id: string): PluginApiReturn<boolean>
|
|
227
318
|
}
|
|
228
319
|
|
|
229
320
|
/**
|
|
@@ -283,3 +374,80 @@ export const PluginDesignDoorSetHeightArgs = z.object({
|
|
|
283
374
|
height: z.number(),
|
|
284
375
|
})
|
|
285
376
|
export type PluginDesignDoorSetHeightArgs = z.infer<typeof PluginDesignDoorSetHeightArgs>
|
|
377
|
+
|
|
378
|
+
// ---------------------------------------------------------------------------
|
|
379
|
+
// catalog (shared by doors + windows)
|
|
380
|
+
// ---------------------------------------------------------------------------
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* A door/window catalog **group** — a placement family (e.g. Swing, Sliding,
|
|
384
|
+
* Casement). Shared by `design.doors` and `design.windows`.
|
|
385
|
+
*
|
|
386
|
+
* | Property | Type | Description |
|
|
387
|
+
* |---|---|---|
|
|
388
|
+
* | `dbType` | `string` | Stable engine token (e.g. `singleDoor`, `casementDouble`) |
|
|
389
|
+
* | `label` | `string` | Human-readable group label (e.g. `"Swing - 1 Shutter"`) |
|
|
390
|
+
* | `source` | `'default' \| 'team'` | Built-in group or a team-custom type |
|
|
391
|
+
*/
|
|
392
|
+
export const PluginObjectCatalogGroup = z.object({
|
|
393
|
+
dbType: z.string(),
|
|
394
|
+
label: z.string(),
|
|
395
|
+
source: z.enum(["default", "team"]),
|
|
396
|
+
})
|
|
397
|
+
export type PluginObjectCatalogGroup = z.infer<typeof PluginObjectCatalogGroup>
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* A placeable door/window catalog **item** (value record — not a scene
|
|
401
|
+
* entity/handle). Shared by `design.doors` and `design.windows`; symmetric with
|
|
402
|
+
* {@linkcode PluginFurnitureCatalogItem}. The raw mesh (`.babylon`) URL is never
|
|
403
|
+
* exposed — only `thumbnailUrl`.
|
|
404
|
+
*
|
|
405
|
+
* | Property | Type | Description |
|
|
406
|
+
* |---|---|---|
|
|
407
|
+
* | `id` | `string` | Library id (team `_id` or general `fullName`) — pass to `design.create.door`/`window` |
|
|
408
|
+
* | `name` | `string` | Display name |
|
|
409
|
+
* | `source` | `'team' \| 'general'` | Which library it came from |
|
|
410
|
+
* | `dbType` | `string` | The group token it belongs to (e.g. `singleDoor`) |
|
|
411
|
+
* | `subType` | `string`? | Engine sub-type (optional) |
|
|
412
|
+
* | `thumbnailUrl` | `string`? | Preview thumbnail URL (optional) |
|
|
413
|
+
* | `cost` | `number`? | Cost (optional; team items) |
|
|
414
|
+
* | `familyName` | `string`? | Family name (optional) |
|
|
415
|
+
*/
|
|
416
|
+
export const PluginObjectCatalogItem = z.object({
|
|
417
|
+
id: z.string(),
|
|
418
|
+
name: z.string(),
|
|
419
|
+
source: z.enum(["team", "general"]),
|
|
420
|
+
dbType: z.string(),
|
|
421
|
+
subType: z.string().optional(),
|
|
422
|
+
thumbnailUrl: z.string().optional(),
|
|
423
|
+
cost: z.number().optional(),
|
|
424
|
+
familyName: z.string().optional(),
|
|
425
|
+
})
|
|
426
|
+
export type PluginObjectCatalogItem = z.infer<typeof PluginObjectCatalogItem>
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Arguments for {@linkcode PluginDesignDoorsApi.listCatalog}.
|
|
430
|
+
*
|
|
431
|
+
* | Property | Type | Description |
|
|
432
|
+
* |---|---|---|
|
|
433
|
+
* | `group` | `string`? | Restrict to one group's `dbType` token (optional) |
|
|
434
|
+
*/
|
|
435
|
+
export const PluginDesignDoorListCatalogArgs = z.object({
|
|
436
|
+
group: z.string().optional(),
|
|
437
|
+
})
|
|
438
|
+
export type PluginDesignDoorListCatalogArgs = z.infer<typeof PluginDesignDoorListCatalogArgs>
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Arguments for {@linkcode PluginDesignDoorsApi.getCatalogItem} / {@linkcode PluginDesignDoorsApi.exists}.
|
|
442
|
+
*
|
|
443
|
+
* | Property | Type | Description |
|
|
444
|
+
* |---|---|---|
|
|
445
|
+
* | `id` | `string` | The catalog library id |
|
|
446
|
+
*/
|
|
447
|
+
export const PluginDesignDoorGetCatalogItemArgs = z.object({
|
|
448
|
+
id: z.string(),
|
|
449
|
+
})
|
|
450
|
+
export type PluginDesignDoorGetCatalogItemArgs = z.infer<typeof PluginDesignDoorGetCatalogItemArgs>
|
|
451
|
+
|
|
452
|
+
export const PluginDesignDoorExistsArgs = PluginDesignDoorGetCatalogItemArgs
|
|
453
|
+
export type PluginDesignDoorExistsArgs = z.infer<typeof PluginDesignDoorExistsArgs>
|
|
@@ -15,24 +15,49 @@ export abstract class PluginDesignFurnitureApi {
|
|
|
15
15
|
constructor() {}
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* List the
|
|
18
|
+
* List the categories (Living, Bedroom, Kitchen, …) that organize the
|
|
19
|
+
* furniture catalog — the picker taxonomy, merged from built-in categories,
|
|
20
|
+
* team types, and backend user types. Pass a value to
|
|
21
|
+
* {@linkcode PluginDesignFurnitureApi.listCatalog}'s `category` filter.
|
|
22
|
+
* @returns The category names (`[]` when empty)
|
|
23
|
+
*
|
|
24
|
+
* @examplePrompt What furniture categories are available?
|
|
25
|
+
* @examplePrompt List the furniture categories
|
|
26
|
+
* @examplePrompt Show me the furniture groups like living and bedroom
|
|
27
|
+
* @examplePrompt Which furniture categories can I filter by?
|
|
28
|
+
*
|
|
29
|
+
* # Example
|
|
30
|
+
* ```ts
|
|
31
|
+
* const categories = await snaptrude.design.furniture.listCategories()
|
|
32
|
+
* const [first] = categories
|
|
33
|
+
* const items = await snaptrude.design.furniture.listCatalog(undefined, first)
|
|
34
|
+
* console.log(first, "→", items.length, "items")
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
public abstract listCategories(): PluginApiReturn<string[]>
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* List the placeable furniture catalog (team + general libraries),
|
|
41
|
+
* optionally filtered by library `source` and/or `category`.
|
|
19
42
|
* @param source Restrict to one library (optional)
|
|
43
|
+
* @param category Restrict to one category (optional; see {@linkcode PluginDesignFurnitureApi.listCategories})
|
|
20
44
|
* @returns The catalog as {@linkcode PluginFurnitureCatalogItem}`[]` (`[]` when empty)
|
|
21
45
|
*
|
|
22
46
|
* @examplePrompt What furniture can I place in this project?
|
|
23
47
|
* @examplePrompt Show me all the furniture available in the library
|
|
24
48
|
* @examplePrompt List the chairs and tables I can add to the model
|
|
25
49
|
* @examplePrompt Which furniture items are in my team's library?
|
|
26
|
-
* @examplePrompt
|
|
50
|
+
* @examplePrompt Show me the bedroom furniture I can place
|
|
27
51
|
*
|
|
28
52
|
* # Example
|
|
29
53
|
* ```ts
|
|
30
|
-
* const
|
|
31
|
-
* for (const item of
|
|
54
|
+
* const bedroom = await snaptrude.design.furniture.listCatalog("team", "bedroom")
|
|
55
|
+
* for (const item of bedroom) console.log(item.id, item.name, item.thumbnailUrl)
|
|
32
56
|
* ```
|
|
33
57
|
*/
|
|
34
58
|
public abstract listCatalog(
|
|
35
|
-
source?: "team" | "general"
|
|
59
|
+
source?: "team" | "general",
|
|
60
|
+
category?: string
|
|
36
61
|
): PluginApiReturn<PluginFurnitureCatalogItem[]>
|
|
37
62
|
|
|
38
63
|
/**
|
|
@@ -85,6 +110,8 @@ export abstract class PluginDesignFurnitureApi {
|
|
|
85
110
|
* | `name` | `string` | Display name |
|
|
86
111
|
* | `source` | `'team' \| 'general'` | Which library it came from |
|
|
87
112
|
* | `subType` | `string` | Sub-type (optional) |
|
|
113
|
+
* | `category` | `string` | Category the item belongs to (optional) |
|
|
114
|
+
* | `thumbnailUrl` | `string` | Preview thumbnail URL (optional; never the mesh URL) |
|
|
88
115
|
* | `cost` | `number` | Cost (optional; team items) |
|
|
89
116
|
* | `familyName` | `string` | Family name (optional) |
|
|
90
117
|
*/
|
|
@@ -93,6 +120,8 @@ export const PluginFurnitureCatalogItem = z.object({
|
|
|
93
120
|
name: z.string(),
|
|
94
121
|
source: z.enum(["team", "general"]),
|
|
95
122
|
subType: z.string().optional(),
|
|
123
|
+
category: z.string().optional(),
|
|
124
|
+
thumbnailUrl: z.string().optional(),
|
|
96
125
|
cost: z.number().optional(),
|
|
97
126
|
familyName: z.string().optional(),
|
|
98
127
|
})
|
|
@@ -104,9 +133,11 @@ export type PluginFurnitureCatalogItem = z.infer<typeof PluginFurnitureCatalogIt
|
|
|
104
133
|
* | Property | Type | Description |
|
|
105
134
|
* |---|---|---|
|
|
106
135
|
* | `source` | `'team' \| 'general'` | Restrict to one library (optional) |
|
|
136
|
+
* | `category` | `string` | Restrict to one category (optional) |
|
|
107
137
|
*/
|
|
108
138
|
export const PluginDesignFurnitureListCatalogArgs = z.object({
|
|
109
139
|
source: z.enum(["team", "general"]).optional(),
|
|
140
|
+
category: z.string().optional(),
|
|
110
141
|
})
|
|
111
142
|
export type PluginDesignFurnitureListCatalogArgs = z.infer<
|
|
112
143
|
typeof PluginDesignFurnitureListCatalogArgs
|
package/src/api/design/index.ts
CHANGED
|
@@ -28,6 +28,7 @@ import { PluginDesignChangeResult } from "./lock"
|
|
|
28
28
|
* - {@linkcode PluginDesignApi.erase} — plan-level adjacency-edge erase (NOT hard delete)
|
|
29
29
|
* - {@linkcode PluginDesignApi.delete} — hard entity removal
|
|
30
30
|
* - {@linkcode PluginDesignApi.lock} / {@linkcode PluginDesignApi.unlock} / {@linkcode PluginDesignApi.isLocked} / {@linkcode PluginDesignApi.listLocked} — lock state (top-level design verbs, §2A.1)
|
|
31
|
+
* - {@linkcode PluginDesignApi.lockArea} / {@linkcode PluginDesignApi.unlockArea} / {@linkcode PluginDesignApi.isAreaLocked} / {@linkcode PluginDesignApi.listAreaLocked} — footprint-area lock for Room/Department spaces
|
|
31
32
|
*
|
|
32
33
|
* Transform and edit operation namespaces land here in later waves.
|
|
33
34
|
*/
|
|
@@ -142,6 +143,92 @@ export abstract class PluginDesignApi {
|
|
|
142
143
|
*/
|
|
143
144
|
public abstract listLocked(): PluginApiReturn<ComponentHandle[]>
|
|
144
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Lock the **footprint area** of one or more Room / Department mass spaces —
|
|
148
|
+
* geometry edits that would change the bottom-face area are then refused or
|
|
149
|
+
* auto-resized to preserve it. Distinct from {@linkcode PluginDesignApi.lock}
|
|
150
|
+
* (which freezes the whole object): this locks only the area. Cascades to the
|
|
151
|
+
* space's instances and its source mass. Undoable.
|
|
152
|
+
*
|
|
153
|
+
* Only Room / Department masses are area-lockable; any other handle throws.
|
|
154
|
+
* Requires the `area_lock` feature to be enabled — when it is off this throws.
|
|
155
|
+
*
|
|
156
|
+
* @param spaces Room / Department mass spaces to area-lock
|
|
157
|
+
* @returns The affected spaces (post-cascade, echoing the input handles)
|
|
158
|
+
*
|
|
159
|
+
* @examplePrompt Lock the floor area of the selected rooms
|
|
160
|
+
* @examplePrompt Freeze the footprint area of this department so its size can't drift
|
|
161
|
+
* @examplePrompt Area-lock every room on this storey
|
|
162
|
+
* @examplePrompt Pin the area of this space while I edit its walls
|
|
163
|
+
*
|
|
164
|
+
* # Example
|
|
165
|
+
* ```ts
|
|
166
|
+
* const rooms = await snaptrude.design.selection.get()
|
|
167
|
+
* const { affected } = await snaptrude.design.lockArea(rooms)
|
|
168
|
+
* console.log("area-locked", affected.length, "spaces")
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
public abstract lockArea(spaces: ComponentHandle[]): PluginApiReturn<PluginDesignChangeResult>
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Unlock the footprint area of previously area-locked spaces so their area can
|
|
175
|
+
* change again. Paired with {@linkcode PluginDesignApi.lockArea}; cascades to
|
|
176
|
+
* instances + source; undoable. Requires the `area_lock` feature (throws if off).
|
|
177
|
+
*
|
|
178
|
+
* @param spaces Room / Department mass spaces to area-unlock
|
|
179
|
+
* @returns The affected spaces (post-cascade)
|
|
180
|
+
*
|
|
181
|
+
* @examplePrompt Unlock the area of the selected rooms
|
|
182
|
+
* @examplePrompt Remove the footprint-area lock from this department
|
|
183
|
+
* @examplePrompt Let me resize this space again by unlocking its area
|
|
184
|
+
* @examplePrompt Area-unlock everything I locked earlier
|
|
185
|
+
*
|
|
186
|
+
* # Example
|
|
187
|
+
* ```ts
|
|
188
|
+
* const locked = await snaptrude.design.listAreaLocked()
|
|
189
|
+
* const { affected } = await snaptrude.design.unlockArea(locked)
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
public abstract unlockArea(spaces: ComponentHandle[]): PluginApiReturn<PluginDesignChangeResult>
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Test whether a space's footprint area is locked. A read — never throws;
|
|
196
|
+
* returns `false` for non-space handles and when the `area_lock` feature is off.
|
|
197
|
+
*
|
|
198
|
+
* @param space The space to test
|
|
199
|
+
* @returns `true` if the space's area is locked, otherwise `false`
|
|
200
|
+
*
|
|
201
|
+
* @examplePrompt Is the area of this room locked?
|
|
202
|
+
* @examplePrompt Check whether this department's footprint is pinned
|
|
203
|
+
* @examplePrompt Tell me if the selected space has its area locked
|
|
204
|
+
*
|
|
205
|
+
* # Example
|
|
206
|
+
* ```ts
|
|
207
|
+
* const [room] = await snaptrude.design.selection.get()
|
|
208
|
+
* const locked = await snaptrude.design.isAreaLocked(room)
|
|
209
|
+
* console.log(locked ? "area is locked" : "area is free")
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
212
|
+
public abstract isAreaLocked(space: ComponentHandle): PluginApiReturn<boolean>
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* List the spaces whose footprint area is locked. A read — never throws; returns
|
|
216
|
+
* `[]` when the `area_lock` feature is off.
|
|
217
|
+
*
|
|
218
|
+
* @returns The area-locked spaces as {@linkcode ComponentHandle}`[]`
|
|
219
|
+
*
|
|
220
|
+
* @examplePrompt Which spaces have their area locked?
|
|
221
|
+
* @examplePrompt List every room with a locked footprint
|
|
222
|
+
* @examplePrompt Show me all the area-locked departments
|
|
223
|
+
*
|
|
224
|
+
* # Example
|
|
225
|
+
* ```ts
|
|
226
|
+
* const locked = await snaptrude.design.listAreaLocked()
|
|
227
|
+
* console.log(locked.length, "area-locked spaces")
|
|
228
|
+
* ```
|
|
229
|
+
*/
|
|
230
|
+
public abstract listAreaLocked(): PluginApiReturn<ComponentHandle[]>
|
|
231
|
+
|
|
145
232
|
constructor() {}
|
|
146
233
|
}
|
|
147
234
|
|
package/src/api/design/lock.ts
CHANGED
|
@@ -40,3 +40,30 @@ export const PluginDesignIsLockedArgs = z.object({
|
|
|
40
40
|
includeInherited: z.boolean().optional(),
|
|
41
41
|
})
|
|
42
42
|
export type PluginDesignIsLockedArgs = z.infer<typeof PluginDesignIsLockedArgs>
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Arguments for {@linkcode PluginDesignApi.lockArea} / {@linkcode PluginDesignApi.unlockArea}.
|
|
46
|
+
*
|
|
47
|
+
* | Property | Type | Description |
|
|
48
|
+
* |---|---|---|
|
|
49
|
+
* | `spaces` | {@linkcode ComponentHandle}`[]` | Room / Department mass spaces to area-lock / unlock |
|
|
50
|
+
*/
|
|
51
|
+
export const PluginDesignLockAreaArgs = z.object({
|
|
52
|
+
spaces: z.array(ComponentHandle),
|
|
53
|
+
})
|
|
54
|
+
export type PluginDesignLockAreaArgs = z.infer<typeof PluginDesignLockAreaArgs>
|
|
55
|
+
|
|
56
|
+
export const PluginDesignUnlockAreaArgs = PluginDesignLockAreaArgs
|
|
57
|
+
export type PluginDesignUnlockAreaArgs = z.infer<typeof PluginDesignUnlockAreaArgs>
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Arguments for {@linkcode PluginDesignApi.isAreaLocked}.
|
|
61
|
+
*
|
|
62
|
+
* | Property | Type | Description |
|
|
63
|
+
* |---|---|---|
|
|
64
|
+
* | `space` | {@linkcode ComponentHandle} | The space to test |
|
|
65
|
+
*/
|
|
66
|
+
export const PluginDesignIsAreaLockedArgs = z.object({
|
|
67
|
+
space: ComponentHandle,
|
|
68
|
+
})
|
|
69
|
+
export type PluginDesignIsAreaLockedArgs = z.infer<typeof PluginDesignIsAreaLockedArgs>
|