@pascal-app/viewer 0.9.1 → 0.9.2

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 (176) hide show
  1. package/dist/components/error-boundary.d.ts +6 -0
  2. package/dist/components/error-boundary.d.ts.map +1 -1
  3. package/dist/components/error-boundary.js +6 -0
  4. package/dist/components/renderers/node-renderer.d.ts +5 -1
  5. package/dist/components/renderers/node-renderer.d.ts.map +1 -1
  6. package/dist/components/renderers/node-renderer.js +5 -2
  7. package/dist/components/renderers/parametric-node-renderer.d.ts.map +1 -1
  8. package/dist/components/renderers/parametric-node-renderer.js +7 -4
  9. package/dist/components/viewer/bvh-ecctrl.d.ts +77 -0
  10. package/dist/components/viewer/bvh-ecctrl.d.ts.map +1 -0
  11. package/dist/components/viewer/bvh-ecctrl.js +552 -0
  12. package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
  13. package/dist/components/viewer/frame-limiter.js +39 -4
  14. package/dist/components/viewer/glb-interactive.d.ts +41 -0
  15. package/dist/components/viewer/glb-interactive.d.ts.map +1 -0
  16. package/dist/components/viewer/glb-interactive.js +394 -0
  17. package/dist/components/viewer/glb-reference-nodes.d.ts +29 -0
  18. package/dist/components/viewer/glb-reference-nodes.d.ts.map +1 -0
  19. package/dist/components/viewer/glb-reference-nodes.js +80 -0
  20. package/dist/components/viewer/glb-replace-instances.d.ts +18 -0
  21. package/dist/components/viewer/glb-replace-instances.d.ts.map +1 -0
  22. package/dist/components/viewer/glb-replace-instances.js +61 -0
  23. package/dist/components/viewer/glb-scene.d.ts +54 -0
  24. package/dist/components/viewer/glb-scene.d.ts.map +1 -0
  25. package/dist/components/viewer/glb-scene.js +920 -0
  26. package/dist/components/viewer/glb-walkthrough-controller.d.ts +12 -0
  27. package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -0
  28. package/dist/components/viewer/glb-walkthrough-controller.js +357 -0
  29. package/dist/components/viewer/index.d.ts +19 -2
  30. package/dist/components/viewer/index.d.ts.map +1 -1
  31. package/dist/components/viewer/index.js +104 -12
  32. package/dist/components/viewer/lights.d.ts.map +1 -1
  33. package/dist/components/viewer/lights.js +36 -5
  34. package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
  35. package/dist/components/viewer/perf-monitor.js +17 -2
  36. package/dist/components/viewer/post-processing.d.ts +7 -1
  37. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  38. package/dist/components/viewer/post-processing.js +179 -24
  39. package/dist/components/viewer/registered-systems.d.ts.map +1 -1
  40. package/dist/components/viewer/registered-systems.js +6 -4
  41. package/dist/components/viewer/scene-bvh.d.ts +2 -1
  42. package/dist/components/viewer/scene-bvh.d.ts.map +1 -1
  43. package/dist/components/viewer/scene-bvh.js +3 -0
  44. package/dist/components/viewer/scene-bvh.test.d.ts +2 -0
  45. package/dist/components/viewer/scene-bvh.test.d.ts.map +1 -0
  46. package/dist/components/viewer/scene-bvh.test.js +13 -0
  47. package/dist/components/viewer/scene-environment.d.ts +2 -0
  48. package/dist/components/viewer/scene-environment.d.ts.map +1 -0
  49. package/dist/components/viewer/scene-environment.js +79 -0
  50. package/dist/components/viewer/selection-manager.d.ts.map +1 -1
  51. package/dist/components/viewer/selection-manager.js +18 -10
  52. package/dist/components/viewer/walkthrough-controls.d.ts +1 -0
  53. package/dist/components/viewer/walkthrough-controls.d.ts.map +1 -1
  54. package/dist/components/viewer/walkthrough-controls.js +20 -0
  55. package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
  56. package/dist/hooks/use-gltf-ktx2.js +3 -22
  57. package/dist/hooks/use-node-events.d.ts +1 -1
  58. package/dist/hooks/use-node-events.d.ts.map +1 -1
  59. package/dist/hooks/use-node-events.js +22 -14
  60. package/dist/index.d.ts +19 -8
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +22 -8
  63. package/dist/lib/backdrop.d.ts +33 -0
  64. package/dist/lib/backdrop.d.ts.map +1 -0
  65. package/dist/lib/backdrop.js +92 -0
  66. package/dist/lib/box-uv.d.ts +13 -0
  67. package/dist/lib/box-uv.d.ts.map +1 -0
  68. package/dist/lib/box-uv.js +32 -0
  69. package/dist/lib/csg-utils.d.ts +3 -9
  70. package/dist/lib/csg-utils.d.ts.map +1 -1
  71. package/dist/lib/csg-utils.js +65 -5
  72. package/dist/lib/csg-utils.test.d.ts +2 -0
  73. package/dist/lib/csg-utils.test.d.ts.map +1 -0
  74. package/dist/lib/csg-utils.test.js +45 -0
  75. package/dist/lib/edge-style.d.ts +1 -0
  76. package/dist/lib/edge-style.d.ts.map +1 -1
  77. package/dist/lib/edge-style.js +26 -6
  78. package/dist/lib/ink-edges.d.ts +1 -1
  79. package/dist/lib/ink-edges.d.ts.map +1 -1
  80. package/dist/lib/ink-edges.js +13 -7
  81. package/dist/lib/ktx2-loader.d.ts +43 -0
  82. package/dist/lib/ktx2-loader.d.ts.map +1 -0
  83. package/dist/lib/ktx2-loader.js +136 -0
  84. package/dist/lib/layers.d.ts +10 -0
  85. package/dist/lib/layers.d.ts.map +1 -1
  86. package/dist/lib/layers.js +10 -0
  87. package/dist/lib/materials.d.ts +14 -1
  88. package/dist/lib/materials.d.ts.map +1 -1
  89. package/dist/lib/materials.js +103 -26
  90. package/dist/lib/scene-themes.d.ts +1 -0
  91. package/dist/lib/scene-themes.d.ts.map +1 -1
  92. package/dist/lib/scene-themes.js +25 -16
  93. package/dist/lib/shadow-only.d.ts +4 -0
  94. package/dist/lib/shadow-only.d.ts.map +1 -0
  95. package/dist/lib/shadow-only.js +36 -0
  96. package/dist/lib/tsl-compat.d.ts +12 -0
  97. package/dist/lib/tsl-compat.d.ts.map +1 -0
  98. package/dist/lib/tsl-compat.js +13 -0
  99. package/dist/store/use-viewer.d.ts +60 -18
  100. package/dist/store/use-viewer.d.ts.map +1 -1
  101. package/dist/store/use-viewer.js +175 -3
  102. package/dist/store/use-viewer.test.d.ts +2 -0
  103. package/dist/store/use-viewer.test.d.ts.map +1 -0
  104. package/dist/store/use-viewer.test.js +42 -0
  105. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
  106. package/dist/systems/ceiling/ceiling-system.js +4 -1
  107. package/dist/systems/door/door-animation.test.d.ts +2 -0
  108. package/dist/systems/door/door-animation.test.d.ts.map +1 -0
  109. package/dist/systems/door/door-animation.test.js +51 -0
  110. package/dist/systems/door/door-floor-alignment.test.d.ts +2 -0
  111. package/dist/systems/door/door-floor-alignment.test.d.ts.map +1 -0
  112. package/dist/systems/door/door-floor-alignment.test.js +50 -0
  113. package/dist/systems/door/door-system.d.ts +24 -0
  114. package/dist/systems/door/door-system.d.ts.map +1 -1
  115. package/dist/systems/door/door-system.js +494 -161
  116. package/dist/systems/fence/fence-system.d.ts +17 -0
  117. package/dist/systems/fence/fence-system.d.ts.map +1 -1
  118. package/dist/systems/fence/fence-system.js +221 -53
  119. package/dist/systems/geometry/geometry-system.d.ts +7 -0
  120. package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
  121. package/dist/systems/geometry/geometry-system.js +95 -26
  122. package/dist/systems/geometry/geometry-system.test.d.ts +2 -0
  123. package/dist/systems/geometry/geometry-system.test.d.ts.map +1 -0
  124. package/dist/systems/geometry/geometry-system.test.js +30 -0
  125. package/dist/systems/interactive/control-widget.d.ts +10 -0
  126. package/dist/systems/interactive/control-widget.d.ts.map +1 -0
  127. package/dist/systems/interactive/control-widget.js +35 -0
  128. package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
  129. package/dist/systems/interactive/interactive-system.js +55 -56
  130. package/dist/systems/item/item-system.d.ts.map +1 -1
  131. package/dist/systems/item/item-system.js +20 -6
  132. package/dist/systems/level/level-system.d.ts.map +1 -1
  133. package/dist/systems/level/level-system.js +28 -4
  134. package/dist/systems/level/level-utils.d.ts +0 -3
  135. package/dist/systems/level/level-utils.d.ts.map +1 -1
  136. package/dist/systems/level/level-utils.js +2 -41
  137. package/dist/systems/roof/roof-materials.d.ts.map +1 -1
  138. package/dist/systems/roof/roof-materials.js +29 -8
  139. package/dist/systems/roof/roof-system.d.ts +21 -8
  140. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  141. package/dist/systems/roof/roof-system.js +1079 -221
  142. package/dist/systems/slab/slab-system.d.ts +5 -3
  143. package/dist/systems/slab/slab-system.d.ts.map +1 -1
  144. package/dist/systems/slab/slab-system.js +41 -23
  145. package/dist/systems/slab/slab-system.test.js +3 -2
  146. package/dist/systems/stair/stair-system.js +6 -3
  147. package/dist/systems/wall/opening-cutout-geometry.d.ts +31 -0
  148. package/dist/systems/wall/opening-cutout-geometry.d.ts.map +1 -0
  149. package/dist/systems/wall/opening-cutout-geometry.js +167 -0
  150. package/dist/systems/wall/opening-cutout-geometry.test.d.ts +2 -0
  151. package/dist/systems/wall/opening-cutout-geometry.test.d.ts.map +1 -0
  152. package/dist/systems/wall/opening-cutout-geometry.test.js +172 -0
  153. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  154. package/dist/systems/wall/wall-cutout.js +46 -17
  155. package/dist/systems/wall/wall-materials.d.ts +11 -7
  156. package/dist/systems/wall/wall-materials.d.ts.map +1 -1
  157. package/dist/systems/wall/wall-materials.js +244 -35
  158. package/dist/systems/wall/wall-opening-cutout.test.d.ts +2 -0
  159. package/dist/systems/wall/wall-opening-cutout.test.d.ts.map +1 -0
  160. package/dist/systems/wall/wall-opening-cutout.test.js +59 -0
  161. package/dist/systems/wall/wall-support-extension.test.d.ts +2 -0
  162. package/dist/systems/wall/wall-support-extension.test.d.ts.map +1 -0
  163. package/dist/systems/wall/wall-support-extension.test.js +48 -0
  164. package/dist/systems/wall/wall-system.d.ts +2 -2
  165. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  166. package/dist/systems/wall/wall-system.js +329 -174
  167. package/dist/systems/window/window-animation-system.d.ts +12 -0
  168. package/dist/systems/window/window-animation-system.d.ts.map +1 -1
  169. package/dist/systems/window/window-animation-system.js +16 -5
  170. package/dist/systems/window/window-system.d.ts +7 -0
  171. package/dist/systems/window/window-system.d.ts.map +1 -1
  172. package/dist/systems/window/window-system.js +240 -14
  173. package/package.json +4 -4
  174. package/dist/components/viewer/ground-occluder.d.ts +0 -2
  175. package/dist/components/viewer/ground-occluder.d.ts.map +0 -1
  176. package/dist/components/viewer/ground-occluder.js +0 -67
@@ -0,0 +1,10 @@
1
+ import type { Control, ControlValue } from '@pascal-app/core';
2
+ /** One interactive control (toggle / slider / temperature) rendered inside the
3
+ * item controls overlay. Shared by the parametric `InteractiveSystem` and the
4
+ * baked-GLB `GlbInteractive` overlay so both look and behave identically. */
5
+ export declare const ControlWidget: ({ control, value, onChange, }: {
6
+ control: Control;
7
+ value: ControlValue;
8
+ onChange: (v: ControlValue) => void;
9
+ }) => import("react").JSX.Element | null;
10
+ //# sourceMappingURL=control-widget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control-widget.d.ts","sourceRoot":"","sources":["../../../src/systems/interactive/control-widget.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAE7D;;8EAE8E;AAC9E,eAAO,MAAM,aAAa,GAAI,+BAI3B;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,YAAY,CAAA;IACnB,QAAQ,EAAE,CAAC,CAAC,EAAE,YAAY,KAAK,IAAI,CAAA;CACpC,uCAuEA,CAAA"}
@@ -0,0 +1,35 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /** One interactive control (toggle / slider / temperature) rendered inside the
4
+ * item controls overlay. Shared by the parametric `InteractiveSystem` and the
5
+ * baked-GLB `GlbInteractive` overlay so both look and behave identically. */
6
+ export const ControlWidget = ({ control, value, onChange, }) => {
7
+ const labelStyle = {
8
+ color: 'white',
9
+ fontSize: 11,
10
+ fontFamily: 'monospace',
11
+ display: 'flex',
12
+ flexDirection: 'column',
13
+ gap: 2,
14
+ };
15
+ if (control.kind === 'toggle') {
16
+ return (_jsx("button", { onClick: () => onChange(!value), style: {
17
+ background: value ? '#4ade80' : '#374151',
18
+ color: 'white',
19
+ border: 'none',
20
+ borderRadius: 4,
21
+ padding: '4px 8px',
22
+ cursor: 'pointer',
23
+ fontSize: 12,
24
+ fontFamily: 'monospace',
25
+ transition: 'background 0.2s',
26
+ }, children: control.label ?? (value ? 'On' : 'Off') }));
27
+ }
28
+ if (control.kind === 'slider') {
29
+ return (_jsxs("label", { style: labelStyle, children: [_jsxs("span", { children: [control.label, ": ", value, control.unit ? ` ${control.unit}` : ''] }), _jsx("input", { max: control.max, min: control.min, onChange: (e) => onChange(Number(e.target.value)), onPointerDown: (e) => e.stopPropagation(), step: control.step, type: "range", value: value })] }));
30
+ }
31
+ if (control.kind === 'temperature') {
32
+ return (_jsxs("label", { style: labelStyle, children: [_jsxs("span", { children: [control.label, ": ", value, "\u00B0", control.unit] }), _jsx("input", { max: control.max, min: control.min, onChange: (e) => onChange(Number(e.target.value)), onPointerDown: (e) => e.stopPropagation(), step: 1, type: "range", value: value })] }));
33
+ }
34
+ return null;
35
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"interactive-system.d.ts","sourceRoot":"","sources":["../../../src/systems/interactive/interactive-system.tsx"],"names":[],"mappings":"AAwBA,eAAO,MAAM,iBAAiB,mCAgB7B,CAAA"}
1
+ {"version":3,"file":"interactive-system.d.ts","sourceRoot":"","sources":["../../../src/systems/interactive/interactive-system.tsx"],"names":[],"mappings":"AAiCA,eAAO,MAAM,iBAAiB,mCAsB7B,CAAA"}
@@ -1,22 +1,41 @@
1
1
  'use client';
2
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { pointInPolygon, sceneRegistry, useInteractive, useScene, } from '@pascal-app/core';
4
4
  import { Html } from '@react-three/drei';
5
5
  import { createPortal, useFrame } from '@react-three/fiber';
6
- import { useState } from 'react';
6
+ import { useEffect, useState } from 'react';
7
7
  import { Vector3 } from 'three';
8
8
  import { useShallow } from 'zustand/react/shallow';
9
9
  import useViewer from '../../store/use-viewer';
10
+ import { ControlWidget } from './control-widget';
10
11
  const _tempVec = new Vector3();
11
- // ---- Parent: one overlay per interactive item ----
12
+ // ---- Parent: one overlay per interactive item inside the selected zone ----
13
+ //
14
+ // The <Html> overlays only exist while a zone is selected and the item sits
15
+ // inside it. Mounting them unconditionally is not an option: each drei <Html>
16
+ // repositions and re-sorts its DOM element on every camera-move frame, and
17
+ // with `occlude` it also raycasts the entire scene per overlay per frame. On
18
+ // large scenes (hundreds of interactive items) that starves the frame budget
19
+ // and the display/z-index churn makes the whole DOM UI flicker.
20
+ //
21
+ // The child components stay rendered (returning null) so an overlay can fade
22
+ // out before its <Html> unmounts.
12
23
  export const InteractiveSystem = () => {
24
+ const zoneId = useViewer((s) => s.selection.zoneId);
25
+ const zonePolygon = useScene((s) => {
26
+ if (!zoneId)
27
+ return null;
28
+ const z = s.nodes[zoneId];
29
+ return z?.polygon ?? null;
30
+ });
13
31
  const interactiveNodeIds = useScene(useShallow((state) => Object.values(state.nodes)
14
32
  .filter((n) => n.type === 'item' && n.asset.interactive != null)
15
33
  .map((n) => n.id)));
16
- return (_jsx(_Fragment, { children: interactiveNodeIds.map((id) => (_jsx(ItemControlsOverlay, { nodeId: id }, id))) }));
34
+ return (_jsx(_Fragment, { children: interactiveNodeIds.map((id) => (_jsx(ItemControlsOverlay, { nodeId: id, zonePolygon: zonePolygon }, id))) }));
17
35
  };
18
36
  // ---- Child: polls sceneRegistry then portals controls into the item group ----
19
- const ItemControlsOverlay = ({ nodeId }) => {
37
+ const FADE_MS = 300;
38
+ const ItemControlsOverlay = ({ nodeId, zonePolygon, }) => {
20
39
  const node = useScene((state) => state.nodes[nodeId]);
21
40
  const [itemObj, setItemObj] = useState(null);
22
41
  useFrame(() => {
@@ -28,26 +47,37 @@ const ItemControlsOverlay = ({ nodeId }) => {
28
47
  });
29
48
  const controlValues = useInteractive(useShallow((state) => state.items[nodeId]?.controlValues));
30
49
  const setControlValue = useInteractive((state) => state.setControlValue);
31
- const zoneId = useViewer((s) => s.selection.zoneId);
32
- const zonePolygon = useScene((s) => {
33
- if (!zoneId)
34
- return null;
35
- const z = s.nodes[zoneId];
36
- return z?.polygon ?? null;
37
- });
38
- if (!(itemObj && controlValues && node?.asset.interactive))
50
+ let visible = false;
51
+ if (itemObj && zonePolygon?.length) {
52
+ itemObj.getWorldPosition(_tempVec);
53
+ visible = pointInPolygon(_tempVec.x, _tempVec.z, zonePolygon);
54
+ }
55
+ // Fade in on mount and fade out before unmounting the <Html>.
56
+ const [mounted, setMounted] = useState(false);
57
+ const [shown, setShown] = useState(false);
58
+ useEffect(() => {
59
+ if (visible) {
60
+ setMounted(true);
61
+ // Double rAF: the overlay has to paint once at opacity 0 before the
62
+ // opacity-1 style lands, otherwise the fade-in transition is skipped.
63
+ let raf2 = 0;
64
+ const raf1 = requestAnimationFrame(() => {
65
+ raf2 = requestAnimationFrame(() => setShown(true));
66
+ });
67
+ return () => {
68
+ cancelAnimationFrame(raf1);
69
+ cancelAnimationFrame(raf2);
70
+ };
71
+ }
72
+ setShown(false);
73
+ const timeout = setTimeout(() => setMounted(false), FADE_MS);
74
+ return () => clearTimeout(timeout);
75
+ }, [visible]);
76
+ if (!(mounted && itemObj && controlValues && node?.asset.interactive))
39
77
  return null;
40
78
  const { controls } = node.asset.interactive;
41
79
  const [, height] = node.asset.dimensions;
42
- let opacity = 0;
43
- let pointerEvents = 'none';
44
- if (zoneId && zonePolygon?.length) {
45
- itemObj.getWorldPosition(_tempVec);
46
- const inside = pointInPolygon(_tempVec.x, _tempVec.z, zonePolygon);
47
- opacity = inside ? 1 : 0.1;
48
- pointerEvents = inside ? 'auto' : 'none';
49
- }
50
- return createPortal(_jsx(Html, { center: true, distanceFactor: 8, occlude: true, position: [0, height + 0.3, 0], zIndexRange: [20, 0], children: _jsx("div", { style: {
80
+ return createPortal(_jsx(Html, { center: true, distanceFactor: 8, eps: -1, position: [0, height + 0.3, 0], zIndexRange: [20, 0], children: _jsx("div", { style: {
51
81
  display: 'flex',
52
82
  flexDirection: 'column',
53
83
  gap: 6,
@@ -56,40 +86,9 @@ const ItemControlsOverlay = ({ nodeId }) => {
56
86
  borderRadius: 8,
57
87
  padding: '8px 12px',
58
88
  minWidth: 120,
59
- pointerEvents,
89
+ pointerEvents: visible ? 'auto' : 'none',
60
90
  userSelect: 'none',
61
- opacity,
62
- transition: 'opacity 0.3s ease',
91
+ opacity: shown ? 1 : 0,
92
+ transition: `opacity ${FADE_MS}ms ease`,
63
93
  }, children: controls.map((control, i) => (_jsx(ControlWidget, { control: control, onChange: (v) => setControlValue(nodeId, i, v), value: controlValues[i] ?? false }, i))) }) }), itemObj);
64
94
  };
65
- // ---- Control widgets ----
66
- const ControlWidget = ({ control, value, onChange, }) => {
67
- const labelStyle = {
68
- color: 'white',
69
- fontSize: 11,
70
- fontFamily: 'monospace',
71
- display: 'flex',
72
- flexDirection: 'column',
73
- gap: 2,
74
- };
75
- if (control.kind === 'toggle') {
76
- return (_jsx("button", { onClick: () => onChange(!value), style: {
77
- background: value ? '#4ade80' : '#374151',
78
- color: 'white',
79
- border: 'none',
80
- borderRadius: 4,
81
- padding: '4px 8px',
82
- cursor: 'pointer',
83
- fontSize: 12,
84
- fontFamily: 'monospace',
85
- transition: 'background 0.2s',
86
- }, children: control.label ?? (value ? 'On' : 'Off') }));
87
- }
88
- if (control.kind === 'slider') {
89
- return (_jsxs("label", { style: labelStyle, children: [_jsxs("span", { children: [control.label, ": ", value, control.unit ? ` ${control.unit}` : ''] }), _jsx("input", { max: control.max, min: control.min, onChange: (e) => onChange(Number(e.target.value)), onPointerDown: (e) => e.stopPropagation(), step: control.step, type: "range", value: value })] }));
90
- }
91
- if (control.kind === 'temperature') {
92
- return (_jsxs("label", { style: labelStyle, children: [_jsxs("span", { children: [control.label, ": ", value, "\u00B0", control.unit] }), _jsx("input", { max: control.max, min: control.min, onChange: (e) => onChange(Number(e.target.value)), onPointerDown: (e) => e.stopPropagation(), step: 1, type: "range", value: value })] }));
93
- }
94
- return null;
95
- };
@@ -1 +1 @@
1
- {"version":3,"file":"item-system.d.ts","sourceRoot":"","sources":["../../../src/systems/item/item-system.tsx"],"names":[],"mappings":"AAcA;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,YA+BtB,CAAA"}
1
+ {"version":3,"file":"item-system.d.ts","sourceRoot":"","sources":["../../../src/systems/item/item-system.tsx"],"names":[],"mappings":"AAcA;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,YA8CtB,CAAA"}
@@ -19,21 +19,35 @@ export const ItemSystem = () => {
19
19
  const nodes = useScene.getState().nodes;
20
20
  dirtyNodes.forEach((id) => {
21
21
  const node = nodes[id];
22
- if (!node || node.type !== 'item')
22
+ if (node?.type !== 'item')
23
23
  return;
24
24
  const item = node;
25
25
  const mesh = sceneRegistry.nodes.get(id);
26
26
  if (!mesh)
27
27
  return;
28
28
  if (item.asset.attachTo === 'wall-side') {
29
- // Wall-attached item: offset Z by half the parent wall's thickness
30
- const parentWall = item.parentId ? nodes[item.parentId] : undefined;
31
- if (parentWall && parentWall.type === 'wall') {
32
- const wallThickness = parentWall.thickness ?? 0.1;
29
+ // Wall-attached item: offset Z by half the host wall's thickness.
30
+ // Roof-segment wall faces share the convention — the face frame's
31
+ // z = 0 is the wall mid-plane, so the same push lands the item on
32
+ // the outer surface.
33
+ const parent = item.parentId ? nodes[item.parentId] : undefined;
34
+ const thickness = parent?.type === 'wall'
35
+ ? (parent.thickness ?? 0.1)
36
+ : parent?.type === 'roof-segment'
37
+ ? (parent.wallThickness ?? 0.1)
38
+ : undefined;
39
+ if (thickness !== undefined) {
33
40
  const side = item.side === 'front' ? 1 : -1;
34
- mesh.position.z = (wallThickness / 2) * side;
41
+ mesh.position.z = (thickness / 2) * side;
35
42
  }
36
43
  }
44
+ // Hold the mark until the model settles (loaded, terminally failed, or
45
+ // never expected — see ItemRenderer.markSettled). Registration alone
46
+ // isn't "built": clearing then would let scene-ready fire while GLBs are
47
+ // still downloading and a bake would export loading placeholders.
48
+ const settled = mesh.userData.itemModelSettled;
49
+ if (!settled)
50
+ return;
37
51
  clearDirty(id);
38
52
  });
39
53
  }, 2);
@@ -1 +1 @@
1
- {"version":3,"file":"level-system.d.ts","sourceRoot":"","sources":["../../../src/systems/level/level-system.tsx"],"names":[],"mappings":"AAQA,eAAO,MAAM,WAAW,YAsCvB,CAAA"}
1
+ {"version":3,"file":"level-system.d.ts","sourceRoot":"","sources":["../../../src/systems/level/level-system.tsx"],"names":[],"mappings":"AAeA,eAAO,MAAM,WAAW,YA6DvB,CAAA"}
@@ -1,9 +1,14 @@
1
- import { sceneRegistry, useScene } from '@pascal-app/core';
1
+ import { getLevelHeight, sceneRegistry, useScene } from '@pascal-app/core';
2
2
  import { useFrame } from '@react-three/fiber';
3
3
  import { lerp } from 'three/src/math/MathUtils.js';
4
+ import { applyShadowOnly, clearShadowOnly } from '../../lib/shadow-only';
4
5
  import useViewer from '../../store/use-viewer';
5
- import { getLevelHeight } from './level-utils';
6
6
  const EXPLODED_GAP = 5;
7
+ // Levels currently in shadow-caster-only mode (solo hides them from the color
8
+ // passes but keeps their sun shadows). Tracked so we can restore layer masks
9
+ // exactly once on transition; apply re-runs every frame so meshes rebuilt
10
+ // while hidden (theme/texture changes) get re-hidden.
11
+ const shadowOnlyLevels = new WeakSet();
7
12
  export const LevelSystem = () => {
8
13
  useFrame((_, delta) => {
9
14
  const nodes = useScene.getState().nodes;
@@ -19,6 +24,9 @@ export const LevelSystem = () => {
19
24
  });
20
25
  entries.sort((a, b) => a.index - b.index);
21
26
  // Walk sorted levels, accumulating base Y offsets
27
+ const selectedIndex = selectedLevel
28
+ ? entries.find((e) => e.levelId === selectedLevel)?.index
29
+ : undefined;
22
30
  let cumulativeY = 0;
23
31
  for (const { levelId, index, obj } of entries) {
24
32
  const level = nodes[levelId];
@@ -26,8 +34,24 @@ export const LevelSystem = () => {
26
34
  const explodedExtra = levelMode === 'exploded' ? index * EXPLODED_GAP : 0;
27
35
  const targetY = baseY + explodedExtra;
28
36
  obj.position.y = lerp(obj.position.y, targetY, delta * 12); // Smoothly animate to new Y position
29
- obj.visible = levelMode !== 'solo' || level?.id === selectedLevel || !selectedLevel;
30
- cumulativeY += getLevelHeight(levelId, nodes);
37
+ // Solo: hidden levels ABOVE the soloed one stay in the shadow map
38
+ // (shadow-caster-only) so the sun still shadows the soloed floor through
39
+ // them; levels below can't block the sun, so they plain-hide.
40
+ const hidden = levelMode === 'solo' && Boolean(selectedLevel) && level?.id !== selectedLevel;
41
+ const castsWhileHidden = hidden && selectedIndex !== undefined && index > selectedIndex;
42
+ if (castsWhileHidden) {
43
+ applyShadowOnly(obj);
44
+ shadowOnlyLevels.add(obj);
45
+ obj.visible = true;
46
+ }
47
+ else {
48
+ if (shadowOnlyLevels.has(obj)) {
49
+ clearShadowOnly(obj);
50
+ shadowOnlyLevels.delete(obj);
51
+ }
52
+ obj.visible = !hidden;
53
+ }
54
+ cumulativeY += getLevelHeight(levelId, nodes, (wallId) => sceneRegistry.nodes.get(wallId)?.position.y);
31
55
  }
32
56
  }, 5); // Using a lower priority so it runs after transforms from other systems have settled
33
57
  return null;
@@ -1,6 +1,3 @@
1
- import { useScene } from '@pascal-app/core';
2
- export declare const DEFAULT_LEVEL_HEIGHT = 2.5;
3
- export declare function getLevelHeight(levelId: string, nodes: ReturnType<typeof useScene.getState>['nodes']): number;
4
1
  /**
5
2
  * Instantly snaps all level Objects3D to their true stacked Y positions
6
3
  * (ignores levelMode — always uses stacked, no exploded gap).
@@ -1 +1 @@
1
- {"version":3,"file":"level-utils.d.ts","sourceRoot":"","sources":["../../../src/systems/level/level-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,QAAQ,EAET,MAAM,kBAAkB,CAAA;AAEzB,eAAO,MAAM,oBAAoB,MAAM,CAAA;AAQvC,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,GACnD,MAAM,CA8BR;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,IAAI,CAyCtD"}
1
+ {"version":3,"file":"level-utils.d.ts","sourceRoot":"","sources":["../../../src/systems/level/level-utils.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,IAAI,CA6CtD"}
@@ -1,43 +1,4 @@
1
- import { sceneRegistry, useScene, } from '@pascal-app/core';
2
- export const DEFAULT_LEVEL_HEIGHT = 2.5;
3
- // Cache: levelId → computed height. Invalidated when the nodes reference changes.
4
- // Zustand produces a new `nodes` object on every mutation, so reference equality
5
- // is a zero-cost way to detect stale data without any subscription overhead.
6
- const heightCache = new Map();
7
- let lastNodesRef = null;
8
- export function getLevelHeight(levelId, nodes) {
9
- if (nodes !== lastNodesRef) {
10
- heightCache.clear();
11
- lastNodesRef = nodes;
12
- }
13
- if (heightCache.has(levelId))
14
- return heightCache.get(levelId);
15
- const level = nodes[levelId];
16
- if (!level)
17
- return DEFAULT_LEVEL_HEIGHT;
18
- let maxTop = 0;
19
- for (const childId of level.children) {
20
- const child = nodes[childId];
21
- if (!child)
22
- continue;
23
- if (child.type === 'ceiling') {
24
- const ch = child.height ?? DEFAULT_LEVEL_HEIGHT;
25
- if (ch > maxTop)
26
- maxTop = ch;
27
- }
28
- else if (child.type === 'wall') {
29
- let meshY = sceneRegistry.nodes.get(childId)?.position.y ?? 0;
30
- if (meshY < 0)
31
- meshY = 0;
32
- const top = meshY + (child.height ?? DEFAULT_LEVEL_HEIGHT);
33
- if (top > maxTop)
34
- maxTop = top;
35
- }
36
- }
37
- const height = maxTop > 0 ? maxTop : DEFAULT_LEVEL_HEIGHT;
38
- heightCache.set(levelId, height);
39
- return height;
40
- }
1
+ import { getLevelHeight, sceneRegistry, useScene } from '@pascal-app/core';
41
2
  /**
42
3
  * Instantly snaps all level Objects3D to their true stacked Y positions
43
4
  * (ignores levelMode — always uses stacked, no exploded gap).
@@ -68,7 +29,7 @@ export function snapLevelsToTruePositions() {
68
29
  for (const { levelId, obj } of entries) {
69
30
  obj.position.y = cumulativeY;
70
31
  obj.visible = true;
71
- cumulativeY += getLevelHeight(levelId, nodes);
32
+ cumulativeY += getLevelHeight(levelId, nodes, (wallId) => sceneRegistry.nodes.get(wallId)?.position.y);
72
33
  }
73
34
  return () => {
74
35
  for (const { levelId, obj } of entries) {
@@ -1 +1 @@
1
- {"version":3,"file":"roof-materials.d.ts","sourceRoot":"","sources":["../../../src/systems/roof/roof-materials.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAA;AACnC,OAAO,EACL,KAAK,WAAW,EAIhB,KAAK,aAAa,EACnB,MAAM,qBAAqB,CAAA;AAE5B,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;AA6BhG,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,QAAQ,EACd,OAAO,GAAE,aAA0B,EACnC,QAAQ,UAAO,EACf,WAAW,GAAE,WAAoB,EACjC,UAAU,CAAC,EAAE,MAAM,GAClB,iBAAiB,GAAG,IAAI,CA0D1B"}
1
+ {"version":3,"file":"roof-materials.d.ts","sourceRoot":"","sources":["../../../src/systems/roof/roof-materials.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAA;AACnC,OAAO,EACL,KAAK,WAAW,EAIhB,KAAK,aAAa,EAEnB,MAAM,qBAAqB,CAAA;AAa5B,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;AA6BhG,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,QAAQ,EACd,OAAO,GAAE,aAA0B,EACnC,QAAQ,UAAO,EACf,WAAW,GAAE,WAAoB,EACjC,UAAU,CAAC,EAAE,MAAM,GAClB,iBAAiB,GAAG,IAAI,CAsE1B"}
@@ -1,5 +1,15 @@
1
1
  import { getEffectiveRoofSurfaceMaterial, } from '@pascal-app/core';
2
- import { createMaterial, createMaterialFromPresetRef, createSurfaceRoleMaterial, } from '../../lib/materials';
2
+ import { createMaterial, createMaterialFromPresetRef, createSurfaceRoleMaterial, resolveSlotDefaultMaterial, } from '../../lib/materials';
3
+ // Declared catalog defaults for an unpainted roof, per the 4-slot layout
4
+ // (0 wall/trim · 1 deck · 2 interior soffit · 3 shingle top). The wall/trim
5
+ // band mirrors the wall kind's default (WALL_SLOT_DEFAULT = concrete-drywall)
6
+ // so a roof reads as continuous with the walls below it.
7
+ const ROOF_DEFAULT_REFS = [
8
+ 'library:concrete-drywall',
9
+ 'library:preset-softwhite',
10
+ 'library:preset-softwhite',
11
+ 'library:roof-terracottatiles',
12
+ ];
3
13
  const roofMaterialArrayCache = new Map();
4
14
  function getSurfaceMaterialSignature(spec) {
5
15
  return JSON.stringify({
@@ -43,27 +53,38 @@ export function getRoofMaterialArray(node, shading = 'rendered', textures = true
43
53
  ceilingMaterial,
44
54
  roofMaterial,
45
55
  ];
56
+ // Textures-off (monochrome) is the guaranteed escape hatch: themed role
57
+ // colours, no catalog finishes.
46
58
  if (!textures) {
47
59
  roofMaterialArrayCache.set(cacheKey, roleArray);
48
60
  return roleArray;
49
61
  }
62
+ // Textures-on default appearance: catalog finishes per slot (terracotta
63
+ // shingle, soft-white deck/soffit, wall-coloured trim). Used both when the
64
+ // roof is unpainted and to fill any individual unpainted slot below.
65
+ const defaultArray = [
66
+ resolveSlotDefaultMaterial(ROOF_DEFAULT_REFS[0], shading),
67
+ resolveSlotDefaultMaterial(ROOF_DEFAULT_REFS[1], shading),
68
+ resolveSlotDefaultMaterial(ROOF_DEFAULT_REFS[2], shading),
69
+ resolveSlotDefaultMaterial(ROOF_DEFAULT_REFS[3], shading),
70
+ ];
50
71
  const topMaterial = createResolvedMaterial(top.material, top.materialPreset, shading);
51
72
  const edgeMaterial = createResolvedMaterial(edge.material, edge.materialPreset, shading);
52
73
  const wallMaterial = createResolvedMaterial(wall.material, wall.materialPreset, shading);
53
74
  if (!(topMaterial || edgeMaterial || wallMaterial)) {
54
- roofMaterialArrayCache.set(cacheKey, roleArray);
55
- return roleArray;
75
+ roofMaterialArrayCache.set(cacheKey, defaultArray);
76
+ return defaultArray;
56
77
  }
57
- // Each slot resolves to its own role only, then the themed default — never
78
+ // Each slot resolves to its own role only, then the declared default — never
58
79
  // another role. Cross-role fallback here used to splatter a single painted
59
80
  // surface (e.g. the edge) across the shingle and soffit slots. The legacy
60
81
  // catch-all still fills every role because `getEffectiveRoofSurfaceMaterial`
61
82
  // returns it for top/edge/wall alike.
62
83
  const materialArray = [
63
- edgeMaterial ?? roofMaterial,
64
- wallMaterial ?? ceilingMaterial,
65
- wallMaterial ?? ceilingMaterial,
66
- topMaterial ?? roofMaterial,
84
+ edgeMaterial ?? defaultArray[0],
85
+ wallMaterial ?? defaultArray[1],
86
+ wallMaterial ?? defaultArray[2],
87
+ topMaterial ?? defaultArray[3],
67
88
  ];
68
89
  roofMaterialArrayCache.set(cacheKey, materialArray);
69
90
  return materialArray;
@@ -1,4 +1,4 @@
1
- import { type RoofSegmentNode } from '@pascal-app/core';
1
+ import { type AnyNode, type RoofSegmentNode } from '@pascal-app/core';
2
2
  import * as THREE from 'three';
3
3
  import { Brush } from 'three-bvh-csg';
4
4
  export declare const RoofSystem: () => null;
@@ -17,18 +17,30 @@ export declare const roofCsgDummyMats: [
17
17
  THREE.MeshBasicMaterial
18
18
  ];
19
19
  export declare const ROOF_MATERIAL_SLOT_COUNT = 4;
20
+ type RoofSegmentBrushSet = {
21
+ deckSlab: Brush;
22
+ shinSlab: Brush;
23
+ wallBrush: Brush;
24
+ innerBrush: Brush;
25
+ rakeBoards: THREE.BufferGeometry | null;
26
+ };
20
27
  export declare function mapRoofGroupMaterialIndex(groupMaterialIndex: number | undefined, csgMaterials: THREE.Material[], matToIndex: Map<THREE.Material, number>): number;
28
+ /**
29
+ * Subtract a segment's trim cuts from an arbitrary segment-local geometry,
30
+ * returning the clipped result. The input geometry is consumed (disposed) on
31
+ * each successful CSG pass — callers that need to keep the original must pass a
32
+ * clone. Returns the input untouched when the segment has no trim. Used
33
+ * internally for rake-board / end-slope attachments and exported so roof
34
+ * accessories (chimney, vents, skylight, …) can clip their own meshes by the
35
+ * same trim, in the same segment-local frame.
36
+ */
37
+ export declare function clipGeometryBySegmentTrim(geometry: THREE.BufferGeometry | null, segment: RoofSegmentNode): THREE.BufferGeometry | null;
21
38
  /**
22
39
  * Generate complete hollow-shell geometry for a roof segment.
23
40
  * Ports the prototype's CSG approach using three-bvh-csg.
24
41
  */
25
- export declare function getRoofSegmentBrushes(node: RoofSegmentNode): {
26
- deckSlab: Brush;
27
- shinSlab: Brush;
28
- wallBrush: Brush;
29
- innerBrush: Brush;
30
- } | null;
31
- export declare function generateRoofSegmentGeometry(node: RoofSegmentNode): THREE.BufferGeometry;
42
+ export declare function getRoofSegmentBrushes(node: RoofSegmentNode): RoofSegmentBrushSet | null;
43
+ export declare function generateRoofSegmentGeometry(node: RoofSegmentNode, nodes?: Record<string, AnyNode>): THREE.BufferGeometry;
32
44
  export declare function remapRoofShellFaces(geometry: THREE.BufferGeometry, node: RoofSegmentNode): void;
33
45
  export type SurfaceFrame = {
34
46
  point: THREE.Vector3;
@@ -40,4 +52,5 @@ export type SurfaceFrame = {
40
52
  * to the true roof surface even on multi-slope roofs (gambrel/mansard/dutch).
41
53
  */
42
54
  export declare function getRoofOuterSurfaceFrameAtPoint(segment: RoofSegmentNode, lx: number, lz: number): SurfaceFrame;
55
+ export {};
43
56
  //# sourceMappingURL=roof-system.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"roof-system.d.ts","sourceRoot":"","sources":["../../../src/systems/roof/roof-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,eAAe,EAKrB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAY,KAAK,EAA0B,MAAM,eAAe,CAAA;AA0DvE,eAAO,MAAM,UAAU,YAmItB,CAAA;AA8PD;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE;IAC7B,KAAK,CAAC,iBAAiB;IACvB,KAAK,CAAC,iBAAiB;IACvB,KAAK,CAAC,iBAAiB;IACvB,KAAK,CAAC,iBAAiB;CAMxB,CAAA;AAID,eAAO,MAAM,wBAAwB,IAAI,CAAA;AAEzC,wBAAgB,yBAAyB,CACvC,kBAAkB,EAAE,MAAM,GAAG,SAAS,EACtC,YAAY,EAAE,KAAK,CAAC,QAAQ,EAAE,EAC9B,UAAU,EAAE,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,GACtC,MAAM,CAuBR;AAaD;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,eAAe,GACpB;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,KAAK,CAAA;CAAE,GAAG,IAAI,CAmQlF;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,eAAe,GAAG,KAAK,CAAC,cAAc,CAmDvF;AAcD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,KAAK,CAAC,cAAc,EAAE,IAAI,EAAE,eAAe,QAyExF;AAwWD,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,KAAK,CAAC,OAAO,CAAA;IACpB,MAAM,EAAE,KAAK,CAAC,OAAO,CAAA;CACtB,CAAA;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,eAAe,EACxB,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,GACT,YAAY,CA6Kd"}
1
+ {"version":3,"file":"roof-system.d.ts","sourceRoot":"","sources":["../../../src/systems/roof/roof-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAcZ,KAAK,eAAe,EAKrB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAY,KAAK,EAA0B,MAAM,eAAe,CAAA;AAwIvE,eAAO,MAAM,UAAU,YAuItB,CAAA;AAmXD;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE;IAC7B,KAAK,CAAC,iBAAiB;IACvB,KAAK,CAAC,iBAAiB;IACvB,KAAK,CAAC,iBAAiB;IACvB,KAAK,CAAC,iBAAiB;CAMxB,CAAA;AAID,eAAO,MAAM,wBAAwB,IAAI,CAAA;AAEzC,KAAK,mBAAmB,GAAG;IACzB,QAAQ,EAAE,KAAK,CAAA;IACf,QAAQ,EAAE,KAAK,CAAA;IACf,SAAS,EAAE,KAAK,CAAA;IAChB,UAAU,EAAE,KAAK,CAAA;IACjB,UAAU,EAAE,KAAK,CAAC,cAAc,GAAG,IAAI,CAAA;CACxC,CAAA;AA0FD,wBAAgB,yBAAyB,CACvC,kBAAkB,EAAE,MAAM,GAAG,SAAS,EACtC,YAAY,EAAE,KAAK,CAAC,QAAQ,EAAE,EAC9B,UAAU,EAAE,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,GACtC,MAAM,CAuBR;AAgUD;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,KAAK,CAAC,cAAc,GAAG,IAAI,EACrC,OAAO,EAAE,eAAe,GACvB,KAAK,CAAC,cAAc,GAAG,IAAI,CAyB7B;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,eAAe,GAAG,mBAAmB,GAAG,IAAI,CA+SvF;AAED,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,eAAe,EACrB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,KAAK,CAAC,cAAc,CAsFtB;AAsLD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,KAAK,CAAC,cAAc,EAAE,IAAI,EAAE,eAAe,QA2ExF;AAijBD,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,KAAK,CAAC,OAAO,CAAA;IACpB,MAAM,EAAE,KAAK,CAAC,OAAO,CAAA;CACtB,CAAA;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,eAAe,EACxB,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,GACT,YAAY,CAqLd"}