@pascal-app/core 1.0.1 → 1.0.3

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 (71) hide show
  1. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +42 -8
  2. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +3 -0
  6. package/dist/lib/node-visibility.d.ts +12 -0
  7. package/dist/lib/node-visibility.d.ts.map +1 -0
  8. package/dist/lib/node-visibility.js +12 -0
  9. package/dist/procedural-items/query.d.ts +1 -0
  10. package/dist/procedural-items/query.d.ts.map +1 -1
  11. package/dist/procedural-items/query.js +67 -23
  12. package/dist/registry/handles.d.ts +28 -1
  13. package/dist/registry/handles.d.ts.map +1 -1
  14. package/dist/registry/index.d.ts +1 -1
  15. package/dist/registry/index.d.ts.map +1 -1
  16. package/dist/registry/subtree.d.ts.map +1 -1
  17. package/dist/registry/subtree.js +6 -0
  18. package/dist/registry/types.d.ts +12 -2
  19. package/dist/registry/types.d.ts.map +1 -1
  20. package/dist/schema/index.d.ts +1 -0
  21. package/dist/schema/index.d.ts.map +1 -1
  22. package/dist/schema/index.js +1 -0
  23. package/dist/schema/nodes/block.d.ts +2 -2
  24. package/dist/schema/nodes/block.d.ts.map +1 -1
  25. package/dist/schema/nodes/block.js +21 -8
  26. package/dist/schema/nodes/cabinet.d.ts +2 -2
  27. package/dist/schema/nodes/curtain-wall.d.ts +129 -0
  28. package/dist/schema/nodes/curtain-wall.d.ts.map +1 -0
  29. package/dist/schema/nodes/curtain-wall.js +49 -0
  30. package/dist/schema/nodes/door.d.ts +2 -2
  31. package/dist/schema/nodes/wall.d.ts +73 -0
  32. package/dist/schema/nodes/wall.d.ts.map +1 -1
  33. package/dist/schema/nodes/wall.js +3 -0
  34. package/dist/schema/types.d.ts +76 -3
  35. package/dist/schema/types.d.ts.map +1 -1
  36. package/dist/services/__fixtures__/hosting.d.ts +3 -0
  37. package/dist/services/__fixtures__/hosting.d.ts.map +1 -0
  38. package/dist/services/__fixtures__/hosting.js +9 -0
  39. package/dist/services/__fixtures__/shelf-surface-rows.d.ts +4 -0
  40. package/dist/services/__fixtures__/shelf-surface-rows.d.ts.map +1 -0
  41. package/dist/services/__fixtures__/shelf-surface-rows.js +18 -0
  42. package/dist/services/hosting.d.ts +3 -0
  43. package/dist/services/hosting.d.ts.map +1 -1
  44. package/dist/services/hosting.js +24 -7
  45. package/dist/services/index.d.ts +3 -3
  46. package/dist/services/index.d.ts.map +1 -1
  47. package/dist/services/index.js +3 -3
  48. package/dist/services/snap.d.ts +3 -0
  49. package/dist/services/snap.d.ts.map +1 -1
  50. package/dist/services/snap.js +11 -0
  51. package/dist/services/surface-hosting.d.ts +6 -2
  52. package/dist/services/surface-hosting.d.ts.map +1 -1
  53. package/dist/services/surface-hosting.js +47 -10
  54. package/dist/store/actions/node-actions.d.ts.map +1 -1
  55. package/dist/store/actions/node-actions.js +3 -96
  56. package/dist/store/use-scene.d.ts.map +1 -1
  57. package/dist/store/use-scene.js +13 -1
  58. package/dist/systems/stair/stair-opening-preview.d.ts +42 -8
  59. package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -1
  60. package/dist/systems/wall/wall-merge.d.ts +38 -0
  61. package/dist/systems/wall/wall-merge.d.ts.map +1 -0
  62. package/dist/systems/wall/wall-merge.js +240 -0
  63. package/dist/systems/wall/wall-operations.d.ts +28 -0
  64. package/dist/systems/wall/wall-operations.d.ts.map +1 -0
  65. package/dist/systems/wall/wall-operations.js +145 -0
  66. package/dist/systems/wall/wall-topology.d.ts +7 -0
  67. package/dist/systems/wall/wall-topology.d.ts.map +1 -1
  68. package/dist/systems/wall/wall-topology.js +53 -5
  69. package/dist/validation/validate-build-json.d.ts.map +1 -1
  70. package/dist/validation/validate-build-json.js +12 -0
  71. package/package.json +2 -2
@@ -1,9 +1,9 @@
1
- import { isProceduralItem } from '../procedural-items/query.js';
1
+ import { attachmentBounds, attachmentRegionsOverlap, isProceduralItem, } from '../procedural-items/query.js';
2
2
  import { evaluateRecipe } from '../procedural-items/recipe.js';
3
- import { boxCorners, frame, transformPoint } from '../procedural-items/spatial.js';
3
+ import { boundsOf, boxCorners, frame, transformPoint } from '../procedural-items/spatial.js';
4
4
  import { nodeRegistry } from '../registry/registry.js';
5
5
  import { getScaledDimensions, isLowProfileItemSurface } from '../schema/nodes/item.js';
6
- import { canHostOnTop } from './hosting.js';
6
+ import { canHostOnTop, wouldCreateHostingCycle } from './hosting.js';
7
7
  import { shelfRowBoardDimensions } from './shelf-board.js';
8
8
  import { surfaceRegionContainsFootprint, surfaceRegionContainsPoint } from './surface-region.js';
9
9
  export const NON_PHYSICAL_HOST_KINDS = [
@@ -132,7 +132,29 @@ const adapters = new Map([
132
132
  ]);
133
133
  export function getSurfaceProvider(host) {
134
134
  const declaration = nodeRegistry.get(host.type)?.capabilities.surfaces;
135
- return declaration?.hosting ?? adapters.get(host.type) ?? hitDerivedSurfaceProvider;
135
+ return declaration?.hosting || adapters.get(host.type) || hitDerivedSurfaceProvider;
136
+ }
137
+ export function rendersHostedChildren(def) {
138
+ return def.renderer
139
+ ? def.renderer.kind === 'parametric' && def.rendersChildren !== false
140
+ : !!def.geometry;
141
+ }
142
+ export function canHostSurfaceChild(host, childKind, childId) {
143
+ const def = nodeRegistry.get(host.type);
144
+ if (!def ||
145
+ NON_PHYSICAL_HOST_KINDS.includes(host.type) ||
146
+ def.capabilities.surfaces?.hosting === false ||
147
+ nodeRegistry.get(childKind)?.capabilities.surfacePlacement === 'floor-only' ||
148
+ !rendersHostedChildren(def))
149
+ return false;
150
+ const generated = childId
151
+ ? undefined
152
+ : nodeRegistry.get(childKind)?.schema.shape.id?.safeParse(undefined);
153
+ const id = childId ?? (generated?.success ? generated.data : undefined);
154
+ if (typeof id !== 'string')
155
+ return false;
156
+ const parsed = def.schema.shape.children?.safeParse([id]);
157
+ return parsed?.success === true && Array.isArray(parsed.data) && parsed.data.includes(id);
136
158
  }
137
159
  function hostRegion(host, ctx) {
138
160
  const capabilities = nodeRegistry.get(host.type)?.capabilities;
@@ -159,9 +181,10 @@ function hostRegion(host, ctx) {
159
181
  /** Hit and child rotation are host-local; bounds and dimensions are scaled child-local values. */
160
182
  export function resolveSurfacePlacement(args) {
161
183
  const { host, hit, childKind, childFootprint } = args;
162
- // Slabs support level children via supportSlabId. Defer to floor placement
163
- // without a refusal, which would block the paired grid event's floor drop.
164
- if (host.type === 'slab')
184
+ if (args.childId && wouldCreateHostingCycle(args.childId, host, args.scene))
185
+ return null;
186
+ // Defer without a refusal so the paired grid event can place on floor support.
187
+ if (!canHostOnTop(host) || !canHostSurfaceChild(host, childKind, args.childId))
165
188
  return null;
166
189
  const reject = (reason) => {
167
190
  args.onReject?.(reason);
@@ -169,8 +192,6 @@ export function resolveSurfacePlacement(args) {
169
192
  };
170
193
  if (!hit.point.every(Number.isFinite))
171
194
  return reject('invalid-hit');
172
- if (NON_PHYSICAL_HOST_KINDS.includes(host.type) || !canHostOnTop(host))
173
- return reject('host-not-eligible');
174
195
  const ctx = { scene: args.scene };
175
196
  const provider = getSurfaceProvider(host);
176
197
  const poseFor = (surface) => {
@@ -219,7 +240,7 @@ export function resolveSurfacePlacement(args) {
219
240
  surfaceLocal: surface.id === null ? null : { position: localPosition, rotationY: rotation[1], rotation },
220
241
  };
221
242
  };
222
- let surface = provider.resolveHit(host, hit, ctx);
243
+ let surface = args.surface ?? provider.resolveHit(host, hit, ctx);
223
244
  if (!surface && hit.normalWorldY >= UPWARD_SURFACE_NORMAL_MIN_Y) {
224
245
  // Grab offsets and snapping can leave the pointer over a hole while the child's centre is supported.
225
246
  surface = nearestSurface((provider.surfaces?.(host, ctx) ?? []).filter((candidate) => {
@@ -264,5 +285,21 @@ export function resolveSurfacePlacement(args) {
264
285
  if (!surfaceRegionContainsFootprint(hostRegion(host, ctx), pose.position, childFootprint.size, childFootprint.rotation ?? childFootprint.rotationY, childFootprint.localBounds))
265
286
  return reject('footprint-exceeds-host');
266
287
  }
288
+ if (isProceduralItem(host) && pose.surfaceId !== null && pose.surfaceLocal) {
289
+ const bounds = childFootprint.localBounds ?? {
290
+ min: [-childFootprint.size[0] / 2, 0, -childFootprint.size[2] / 2],
291
+ max: [childFootprint.size[0] / 2, childFootprint.size[1], childFootprint.size[2] / 2],
292
+ };
293
+ const proposal = boundsOf(boxCorners([...bounds.min], [...bounds.max]).map((point) => transformPoint(frame([...pose.surfaceLocal.position], [...pose.surfaceLocal.rotation]), point)));
294
+ for (const id of host.children) {
295
+ if (id === args.childId || host.attachments[id] !== pose.surfaceId)
296
+ continue;
297
+ const child = args.scene.get(id);
298
+ if (child &&
299
+ (isProceduralItem(child) || child.type === 'item') &&
300
+ attachmentRegionsOverlap(attachmentBounds(child), proposal))
301
+ return reject('surface-occupied');
302
+ }
303
+ }
267
304
  return pose;
268
305
  }
@@ -1 +1 @@
1
- {"version":3,"file":"node-actions.d.ts","sourceRoot":"","sources":["../../../src/store/actions/node-actions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"node-actions.d.ts","sourceRoot":"","sources":["../../../src/store/actions/node-actions.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,8 @@
1
1
  import { nodeRegistry } from '../../registry/registry.js';
2
2
  import { validateNodeRelations } from '../../registry/validate-relations.js';
3
- import { AnyNode as AnyNodeSchema, createDefaultGuttersForSegment, createDefaultRidgeVentsForSegment, DownspoutNode as DownspoutNodeSchema, defaultDownspoutMetadata, generateId, getDefaultGutterSide, getEffectiveWallSurfaceMaterial, getWallSurfaceMaterialSignature, isAutoGutterEnabled, isAutoRidgeVentEnabled, isDefaultDownspoutNode, isDefaultGutterNode, isDefaultRidgeVentNode, parseNode, planAutomaticDownspouts, resolveAutomaticDownspoutLength, } from '../../schema/index.js';
3
+ import { AnyNode as AnyNodeSchema, createDefaultGuttersForSegment, createDefaultRidgeVentsForSegment, DownspoutNode as DownspoutNodeSchema, defaultDownspoutMetadata, generateId, getDefaultGutterSide, isAutoGutterEnabled, isAutoRidgeVentEnabled, isDefaultDownspoutNode, isDefaultGutterNode, isDefaultRidgeVentNode, parseNode, planAutomaticDownspouts, resolveAutomaticDownspoutLength, } from '../../schema/index.js';
4
4
  import { constrainWallCurveOffsetToAvoidIntersections } from '../../systems/wall/wall-curve.js';
5
+ import { areWallStylesCompatible, areWallsCollinearAcrossPoint, buildMergedWallAttachmentUpdates, getWallEndpointAtPoint, resolveMergedWallEndpoints, } from '../../systems/wall/wall-merge.js';
5
6
  import { activeSceneCommitNodeIds, addActiveSceneCommitNodeIds, runWithSceneCommitNodeIds, } from '../history-control.js';
6
7
  const DEFAULT_RIDGE_VENT_REFRESH_FIELDS = new Set([
7
8
  'roofType',
@@ -750,100 +751,6 @@ function refreshDefaultGuttersForRoofIds(nextNodes, roofIds, dirtyIds, deletedId
750
751
  // Track pending RAF for updateNodesAction to prevent multiple queued callbacks
751
752
  let pendingRafId = null;
752
753
  let pendingUpdates = new Set();
753
- function pointsEqual(a, b, tolerance = 1e-6) {
754
- const dx = a[0] - b[0];
755
- const dz = a[1] - b[1];
756
- return dx * dx + dz * dz <= tolerance * tolerance;
757
- }
758
- function wallLength(wall) {
759
- return Math.hypot(wall.end[0] - wall.start[0], wall.end[1] - wall.start[1]);
760
- }
761
- function getWallEndpointAtPoint(wall, point) {
762
- if (pointsEqual(wall.start, point))
763
- return 'start';
764
- if (pointsEqual(wall.end, point))
765
- return 'end';
766
- return null;
767
- }
768
- function getWallFreeEndpoint(wall, sharedPoint) {
769
- return pointsEqual(wall.start, sharedPoint) ? wall.end : wall.start;
770
- }
771
- function areWallStylesCompatible(a, b) {
772
- const aInterior = getWallSurfaceMaterialSignature(getEffectiveWallSurfaceMaterial(a, 'interior'));
773
- const bInterior = getWallSurfaceMaterialSignature(getEffectiveWallSurfaceMaterial(b, 'interior'));
774
- const aExterior = getWallSurfaceMaterialSignature(getEffectiveWallSurfaceMaterial(a, 'exterior'));
775
- const bExterior = getWallSurfaceMaterialSignature(getEffectiveWallSurfaceMaterial(b, 'exterior'));
776
- return ((a.parentId ?? null) === (b.parentId ?? null) &&
777
- Math.abs((a.curveOffset ?? 0) - (b.curveOffset ?? 0)) <= 1e-6 &&
778
- Math.abs((a.thickness ?? 0.2) - (b.thickness ?? 0.2)) <= 1e-6 &&
779
- // Absent height means plane-bound (follows the storey), which must never
780
- // merge with an explicit height — even one that currently matches the plane.
781
- (a.height == null) === (b.height == null) &&
782
- Math.abs((a.height ?? 0) - (b.height ?? 0)) <= 1e-6 &&
783
- aInterior === bInterior &&
784
- aExterior === bExterior &&
785
- a.frontSide === b.frontSide &&
786
- a.backSide === b.backSide &&
787
- a.visible === b.visible);
788
- }
789
- function areWallsCollinearAcrossPoint(a, b, sharedPoint) {
790
- const freeA = getWallFreeEndpoint(a, sharedPoint);
791
- const freeB = getWallFreeEndpoint(b, sharedPoint);
792
- const ax = freeA[0] - sharedPoint[0];
793
- const az = freeA[1] - sharedPoint[1];
794
- const bx = freeB[0] - sharedPoint[0];
795
- const bz = freeB[1] - sharedPoint[1];
796
- const lenA = Math.hypot(ax, az);
797
- const lenB = Math.hypot(bx, bz);
798
- if (lenA < 1e-6 || lenB < 1e-6)
799
- return false;
800
- const cross = (ax * bz - az * bx) / (lenA * lenB);
801
- const dot = (ax * bx + az * bz) / (lenA * lenB);
802
- return Math.abs(cross) <= 1e-4 && dot < -0.999;
803
- }
804
- function resolveMergedWallEndpoints(primary, secondary, sharedPoint) {
805
- const primaryEndpoint = getWallEndpointAtPoint(primary, sharedPoint);
806
- const secondaryEndpoint = getWallEndpointAtPoint(secondary, sharedPoint);
807
- if (primaryEndpoint === 'end' && secondaryEndpoint === 'start') {
808
- return { start: primary.start, end: secondary.end };
809
- }
810
- if (primaryEndpoint === 'start' && secondaryEndpoint === 'end') {
811
- return { start: secondary.start, end: primary.end };
812
- }
813
- if (primaryEndpoint === 'start' && secondaryEndpoint === 'start') {
814
- return { start: primary.end, end: secondary.end };
815
- }
816
- return { start: primary.start, end: secondary.start };
817
- }
818
- function buildMergedWallAttachmentUpdates(primary, secondary, mergedWallId, mergedStart, mergedEnd, nodes) {
819
- const mergedLength = Math.max(Math.hypot(mergedEnd[0] - mergedStart[0], mergedEnd[1] - mergedStart[1]), 1e-6);
820
- const tangentX = (mergedEnd[0] - mergedStart[0]) / mergedLength;
821
- const tangentZ = (mergedEnd[1] - mergedStart[1]) / mergedLength;
822
- const updates = [];
823
- const wallChildren = [...(primary.children ?? []), ...(secondary.children ?? [])];
824
- for (const childId of wallChildren) {
825
- const child = nodes[childId];
826
- if (!(child && 'position' in child && Array.isArray(child.position))) {
827
- continue;
828
- }
829
- const sourceWall = child.parentId === secondary.id ? secondary : primary;
830
- const sourceLength = Math.max(wallLength(sourceWall), 1e-6);
831
- const localX = typeof child.position[0] === 'number' ? child.position[0] : 0;
832
- const worldX = sourceWall.start[0] + ((sourceWall.end[0] - sourceWall.start[0]) * localX) / sourceLength;
833
- const worldZ = sourceWall.start[1] + ((sourceWall.end[1] - sourceWall.start[1]) * localX) / sourceLength;
834
- const nextLocalX = Math.max(0, Math.min(mergedLength, (worldX - mergedStart[0]) * tangentX + (worldZ - mergedStart[1]) * tangentZ));
835
- updates.push({
836
- id: childId,
837
- data: {
838
- parentId: mergedWallId,
839
- wallId: mergedWallId,
840
- position: [nextLocalX, child.position[1], child.position[2]],
841
- ...('wallT' in child ? { wallT: nextLocalX / mergedLength } : {}),
842
- },
843
- });
844
- }
845
- return updates;
846
- }
847
754
  function buildWallMergePlans(nodes, idsToDelete) {
848
755
  const deletedWalls = idsToDelete
849
756
  .map((id) => nodes[id])
@@ -861,7 +768,7 @@ function buildWallMergePlans(nodes, idsToDelete) {
861
768
  return false;
862
769
  if ((node.parentId ?? null) !== (deletedWall.parentId ?? null))
863
770
  return false;
864
- return pointsEqual(node.start, junction) || pointsEqual(node.end, junction);
771
+ return getWallEndpointAtPoint(node, junction) !== null;
865
772
  });
866
773
  if (candidates.length !== 2) {
867
774
  continue;
@@ -1 +1 @@
1
- {"version":3,"file":"use-scene.d.ts","sourceRoot":"","sources":["../../src/store/use-scene.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-scene.d.ts","sourceRoot":"","sources":["../../src/store/use-scene.ts"],"names":[],"mappings":"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"}
@@ -991,9 +991,21 @@ function sceneHistorySnapshotFromState(state) {
991
991
  }
992
992
  }
993
993
  const historyNodes = {};
994
- for (const [id, node] of Object.entries(nodes)) {
994
+ for (const [id, sourceNode] of Object.entries(nodes)) {
995
995
  if (transientNodeIds.has(id))
996
996
  continue;
997
+ let node = sourceNode;
998
+ if (node.type === 'procedural-item') {
999
+ let attachments;
1000
+ for (const childId of transientNodeIds) {
1001
+ if (!Object.hasOwn(node.attachments, childId))
1002
+ continue;
1003
+ attachments ??= { ...node.attachments };
1004
+ delete attachments[childId];
1005
+ }
1006
+ if (attachments)
1007
+ node = { ...node, attachments };
1008
+ }
997
1009
  if (!('children' in node && Array.isArray(node.children))) {
998
1010
  historyNodes[id] = node;
999
1011
  continue;
@@ -490,6 +490,40 @@ export declare function getNodesWithLiveStairOpeningInputs(nodes: Record<string,
490
490
  } | undefined;
491
491
  materialPreset?: string | undefined;
492
492
  height?: number | undefined;
493
+ wallType?: "standard" | "curtain" | undefined;
494
+ curtainWall?: {
495
+ construction: "stick" | "unitized";
496
+ framing: "capped" | "vertical-caps" | "horizontal-caps" | "structural-glazing";
497
+ columns: {
498
+ layout: "count" | "maximum-spacing" | "fixed-spacing";
499
+ count: number;
500
+ spacing: number;
501
+ alignment: "start" | "center" | "end";
502
+ };
503
+ rows: {
504
+ layout: "count" | "maximum-spacing" | "fixed-spacing";
505
+ count: number;
506
+ spacing: number;
507
+ alignment: "start" | "center" | "end";
508
+ };
509
+ mullionWidth: number;
510
+ transomWidth: number;
511
+ perimeterWidth: number;
512
+ jointWidth: number;
513
+ glassThickness: number;
514
+ panelType: "glass" | "solid" | "empty";
515
+ spandrel: "top" | "none" | "bottom";
516
+ frameColor: string;
517
+ glassColor: string;
518
+ solidColor: string;
519
+ glassOpacity: number;
520
+ glassRoughness: number;
521
+ panels: {
522
+ column: number;
523
+ row: number;
524
+ type: "glass" | "solid" | "empty";
525
+ }[];
526
+ } | undefined;
493
527
  thickness?: number | undefined;
494
528
  fillToTerrain?: boolean | undefined;
495
529
  interiorMaterial?: {
@@ -572,10 +606,10 @@ export declare function getNodesWithLiveStairOpeningInputs(nodes: Record<string,
572
606
  baseHeight: number;
573
607
  style: "slat" | "rail" | "privacy" | "horizontal";
574
608
  baseStyle: "floating" | "grounded";
575
- thickness: number;
576
- postSpacing: number;
577
609
  start: [number, number];
578
610
  end: [number, number];
611
+ thickness: number;
612
+ postSpacing: number;
579
613
  postSize: number;
580
614
  topRailHeight: number;
581
615
  groundClearance: number;
@@ -644,7 +678,7 @@ export declare function getNodesWithLiveStairOpeningInputs(nodes: Record<string,
644
678
  frontStyle: "slab" | "shaker" | "raised-arch";
645
679
  panelReady: boolean;
646
680
  handleStyle: "none" | "bar" | "cutout" | "hole" | "knob";
647
- handlePosition: "top" | "auto" | "center";
681
+ handlePosition: "top" | "center" | "auto";
648
682
  frontOverlay: "full" | "inset";
649
683
  withBottomPanel: boolean;
650
684
  showPlinth: boolean;
@@ -790,7 +824,7 @@ export declare function getNodesWithLiveStairOpeningInputs(nodes: Record<string,
790
824
  frontStyle: "slab" | "shaker" | "raised-arch";
791
825
  panelReady: boolean;
792
826
  handleStyle: "none" | "bar" | "cutout" | "hole" | "knob";
793
- handlePosition: "top" | "auto" | "center";
827
+ handlePosition: "top" | "center" | "auto";
794
828
  frontOverlay: "full" | "inset";
795
829
  withBottomPanel: boolean;
796
830
  showPlinth: boolean;
@@ -1489,8 +1523,8 @@ export declare function getNodesWithLiveStairOpeningInputs(nodes: Record<string,
1489
1523
  height: number;
1490
1524
  style: "wall-shelf" | "bookshelf" | "open-rack" | "cubby";
1491
1525
  rows: number;
1492
- thickness: number;
1493
1526
  columns: number;
1527
+ thickness: number;
1494
1528
  withBack: boolean;
1495
1529
  withSides: boolean;
1496
1530
  withBottom: boolean;
@@ -1954,7 +1988,7 @@ export declare function getNodesWithLiveStairOpeningInputs(nodes: Record<string,
1954
1988
  swingDirection: "inward" | "outward";
1955
1989
  swingAngle: number;
1956
1990
  segments: {
1957
- type: "glass" | "panel" | "empty";
1991
+ type: "glass" | "empty" | "panel";
1958
1992
  heightRatio: number;
1959
1993
  columnRatios: number[];
1960
1994
  dividerThickness: number;
@@ -2378,9 +2412,9 @@ export declare function getNodesWithLiveStairOpeningInputs(nodes: Record<string,
2378
2412
  metadata: Record<string, unknown>;
2379
2413
  rotation: number;
2380
2414
  rows: number;
2415
+ columns: number;
2381
2416
  frameThickness: number;
2382
2417
  frameDepth: number;
2383
- columns: number;
2384
2418
  panelWidth: number;
2385
2419
  panelHeight: number;
2386
2420
  gapX: number;
@@ -2446,6 +2480,7 @@ export declare function getNodesWithLiveStairOpeningInputs(nodes: Record<string,
2446
2480
  rotation: number;
2447
2481
  width: number;
2448
2482
  height: number;
2483
+ glassThickness: number;
2449
2484
  operationState: number;
2450
2485
  slideDirection: "x" | "z";
2451
2486
  frameThickness: number;
@@ -2453,7 +2488,6 @@ export declare function getNodesWithLiveStairOpeningInputs(nodes: Record<string,
2453
2488
  curbHeight: number;
2454
2489
  cutoutOffset: number;
2455
2490
  skylightType: "flat" | "sliding" | "opening" | "walk-on" | "lantern";
2456
- glassThickness: number;
2457
2491
  lanternHeight: number;
2458
2492
  lanternTopScale: number;
2459
2493
  openingAngle: number;
@@ -1 +1 @@
1
- {"version":3,"file":"stair-opening-preview.d.ts","sourceRoot":"","sources":["../../../src/systems/stair/stair-opening-preview.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC7E,OAA6B,EAAE,KAAK,iBAAiB,EAAE,MAAM,qCAAqC,CAAA;AAClG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAA;AAGpE,KAAK,oBAAoB,GAAG;IAC1B,EAAE,EAAE,SAAS,CAAA;IACb,IAAI,EAAE,OAAO,CAAC,QAAQ,GAAG,WAAW,CAAC,CAAA;CACtC,CAAA;AAoBD,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,EAClD,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACrD,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,WAYvC;AAED,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,EAClD,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACrD,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwBvC;AAED,wBAAgB,qCAAqC;;mBAUlC,oBAAoB,EAAE;;EAiCxC"}
1
+ {"version":3,"file":"stair-opening-preview.d.ts","sourceRoot":"","sources":["../../../src/systems/stair/stair-opening-preview.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC7E,OAA6B,EAAE,KAAK,iBAAiB,EAAE,MAAM,qCAAqC,CAAA;AAClG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAA;AAGpE,KAAK,oBAAoB,GAAG;IAC1B,EAAE,EAAE,SAAS,CAAA;IACb,IAAI,EAAE,OAAO,CAAC,QAAQ,GAAG,WAAW,CAAC,CAAA;CACtC,CAAA;AAoBD,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,EAClD,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACrD,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,WAYvC;AAED,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,EAClD,aAAa,EAAE,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACrD,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwBvC;AAED,wBAAgB,qCAAqC;;mBAUlC,oBAAoB,EAAE;;EAiCxC"}
@@ -0,0 +1,38 @@
1
+ import { type AnyNode, type AnyNodeId, type WallNode } from '../../schema/index.js';
2
+ import type { WallTopologyChanges } from './wall-topology.js';
3
+ export type WallAttachmentUpdate = {
4
+ id: AnyNodeId;
5
+ data: Partial<AnyNode>;
6
+ };
7
+ export declare function getWallEndpointAtPoint(wall: Pick<WallNode, 'start' | 'end'>, point: [number, number]): 'start' | 'end' | null;
8
+ /**
9
+ * The first thing two walls disagree on (for the merge's explanation), or null.
10
+ * The delete heal is strict: room sides must match, and a wall following the
11
+ * storey never joins one with an explicit height. An explicit merge compares
12
+ * what is visible instead — `heightOf` resolves each wall's actual height — and
13
+ * leaves the sides to room detection, which reclassifies the merged wall.
14
+ */
15
+ export declare function wallStyleMismatch(a: WallNode, b: WallNode, options: {
16
+ sides: boolean;
17
+ heightOf?: (wall: WallNode) => number;
18
+ }): string | null;
19
+ export declare function areWallStylesCompatible(a: WallNode, b: WallNode): boolean;
20
+ export declare function areWallsCollinearAcrossPoint(a: WallNode, b: WallNode, sharedPoint: [number, number]): boolean;
21
+ export declare function resolveMergedWallEndpoints(primary: WallNode, secondary: WallNode, sharedPoint: [number, number]): {
22
+ start: [number, number];
23
+ end: [number, number];
24
+ };
25
+ export declare function buildMergedWallAttachmentUpdates(primary: WallNode, secondary: WallNode, mergedWallId: AnyNodeId, mergedStart: [number, number], mergedEnd: [number, number], nodes: Record<AnyNodeId, AnyNode>): WallAttachmentUpdate[];
26
+ /**
27
+ * Merges a straight run of adjoining walls into one — the inverse of a split.
28
+ * Every joint must hold exactly these walls (a T or a cross stays split), and
29
+ * neighbours must continue in line and look alike (`wallStyleMismatch`: same
30
+ * thickness, visible height and finish). The wall with the most attachments
31
+ * keeps its id and height mode; openings and wall items keep their world
32
+ * position on it.
33
+ */
34
+ export declare function planWallMerge(nodes: Record<AnyNodeId, AnyNode>, wallIds: readonly AnyNodeId[]): {
35
+ changes: WallTopologyChanges;
36
+ wallId: WallNode['id'];
37
+ };
38
+ //# sourceMappingURL=wall-merge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wall-merge.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-merge.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,SAAS,EAGd,KAAK,QAAQ,EACd,MAAM,cAAc,CAAA;AACrB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAK1D,MAAM,MAAM,oBAAoB,GAAG;IAAE,EAAE,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,CAAA;AAY5E,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,GAAG,KAAK,CAAC,EACrC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GACtB,OAAO,GAAG,KAAK,GAAG,IAAI,CAIxB;AAMD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,CAAC,EAAE,QAAQ,EACX,CAAC,EAAE,QAAQ,EACX,OAAO,EAAE;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,MAAM,CAAA;CAAE,GACjE,MAAM,GAAG,IAAI,CAuBf;AAED,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,WAE/D;AAED,wBAAgB,4BAA4B,CAC1C,CAAC,EAAE,QAAQ,EACX,CAAC,EAAE,QAAQ,EACX,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,WAgB9B;AAED,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,QAAQ,EACjB,SAAS,EAAE,QAAQ,EACnB,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5B;IAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAkBpD;AAyBD,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,QAAQ,EACjB,SAAS,EAAE,QAAQ,EACnB,YAAY,EAAE,SAAS,EACvB,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAC7B,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3B,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,GAChC,oBAAoB,EAAE,CAyDxB;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,OAAO,EAAE,SAAS,SAAS,EAAE,GAC5B;IAAE,OAAO,EAAE,mBAAmB,CAAC;IAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAA;CAAE,CA4F1D"}