@principal-ai/file-city-react 0.5.42 → 0.5.43
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/dist/components/FileCity3D/FileCity3D.d.ts +36 -2
- package/dist/components/FileCity3D/FileCity3D.d.ts.map +1 -1
- package/dist/components/FileCity3D/FileCity3D.js +146 -49
- package/dist/components/FileCity3D/index.d.ts +1 -1
- package/dist/components/FileCity3D/index.d.ts.map +1 -1
- package/dist/components/FileCityExplorer/FileCityExplorer.d.ts.map +1 -1
- package/dist/components/FileCityExplorer/FileCityExplorer.js +1 -31
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/FileCity3D/FileCity3D.tsx +376 -135
- package/src/components/FileCity3D/index.ts +1 -0
- package/src/components/FileCityExplorer/FileCityExplorer.tsx +2 -30
- package/src/index.ts +1 -0
- package/src/stories/ElevatedScopePanels.stories.tsx +72 -27
- package/src/stories/FileCityExplorer.stories.tsx +2 -29
- package/src/stories/LeaderLineSnippetOverlay.stories.tsx +725 -137
|
@@ -17,6 +17,13 @@ declare module 'react' {
|
|
|
17
17
|
}
|
|
18
18
|
export type { CityData, CityBuilding, CityDistrict, LayerItem, LayerRenderStrategy };
|
|
19
19
|
export type HighlightLayer = BuilderHighlightLayer;
|
|
20
|
+
/** Visual style for the `selectedPath` ring on a directory. */
|
|
21
|
+
export interface SelectionStyle {
|
|
22
|
+
/** Ring color. Defaults to the theme accent. */
|
|
23
|
+
color?: string;
|
|
24
|
+
/** Ring border width in world units. Default: 2. */
|
|
25
|
+
borderWidth?: number;
|
|
26
|
+
}
|
|
20
27
|
/** What to do with non-highlighted buildings */
|
|
21
28
|
export type IsolationMode = 'none' | 'transparent' | 'collapse' | 'hide';
|
|
22
29
|
export interface AnimationConfig {
|
|
@@ -186,6 +193,8 @@ export interface FileCity3DProps {
|
|
|
186
193
|
height?: number | string;
|
|
187
194
|
/** Callback when a building is clicked */
|
|
188
195
|
onBuildingClick?: (building: CityBuilding, event: MouseEvent) => void;
|
|
196
|
+
/** Callback when the hovered building changes (fires with null on hover-out) */
|
|
197
|
+
onBuildingHover?: (building: CityBuilding | null) => void;
|
|
189
198
|
/** CSS class name */
|
|
190
199
|
className?: string;
|
|
191
200
|
/** Inline styles */
|
|
@@ -226,8 +235,21 @@ export interface FileCity3DProps {
|
|
|
226
235
|
backgroundColor?: string;
|
|
227
236
|
/** Text color for secondary/placeholder text */
|
|
228
237
|
textColor?: string;
|
|
229
|
-
/**
|
|
238
|
+
/**
|
|
239
|
+
* @deprecated Use `selectedPath` instead. When both are set, `selectedPath`
|
|
240
|
+
* wins. This prop will be removed in a future release.
|
|
241
|
+
*/
|
|
230
242
|
selectedBuilding?: CityBuilding | null;
|
|
243
|
+
/**
|
|
244
|
+
* Path of the selected building or directory. The component resolves the
|
|
245
|
+
* path against `cityData.buildings` (file selection — emphasizes the
|
|
246
|
+
* building and shows the InfoPanel) and `cityData.districts` (directory
|
|
247
|
+
* selection — draws a ring around the district). When both `selectedPath`
|
|
248
|
+
* and `selectedBuilding` are set, `selectedPath` wins.
|
|
249
|
+
*/
|
|
250
|
+
selectedPath?: string | null;
|
|
251
|
+
/** Visual style for the directory selection ring drawn for `selectedPath`. */
|
|
252
|
+
selectionStyle?: SelectionStyle;
|
|
231
253
|
/** When true, camera height adjusts based on tallest building when grown */
|
|
232
254
|
adaptCameraToBuildings?: boolean;
|
|
233
255
|
/** Base file type color layers (resolved with highlightLayers) */
|
|
@@ -237,6 +259,18 @@ export interface FileCity3DProps {
|
|
|
237
259
|
* elevated planes over the directories they own.
|
|
238
260
|
*/
|
|
239
261
|
elevatedScopePanels?: ElevatedScopePanel[];
|
|
262
|
+
/**
|
|
263
|
+
* Set of panel ids that should play the "lift up and fade out" dismiss
|
|
264
|
+
* animation. Add an id here to start the animation; once it settles,
|
|
265
|
+
* `onPanelDismissed` fires so the host can drop the panel from
|
|
266
|
+
* `elevatedScopePanels`.
|
|
267
|
+
*/
|
|
268
|
+
dismissingPanelIds?: ReadonlySet<string>;
|
|
269
|
+
/**
|
|
270
|
+
* Fires once a panel's dismiss animation has settled. The host should
|
|
271
|
+
* remove the id from both `dismissingPanelIds` and `elevatedScopePanels`.
|
|
272
|
+
*/
|
|
273
|
+
onPanelDismissed?: (id: string) => void;
|
|
240
274
|
/**
|
|
241
275
|
* Configure how mouse / trackpad / touch input drives the camera.
|
|
242
276
|
* Defaults match Google Maps style: left-drag pans, right-drag rotates,
|
|
@@ -253,6 +287,6 @@ export interface FileCity3DProps {
|
|
|
253
287
|
* Renders CityData as an interactive 3D city where buildings represent files
|
|
254
288
|
* and their height corresponds to line count or file size.
|
|
255
289
|
*/
|
|
256
|
-
export declare function FileCity3D({ cityData, width, height, onBuildingClick, className, style, animation, isGrown: externalIsGrown, onGrowChange, showControls, elevatedScopePanels, highlightLayers: externalHighlightLayers, isolationMode: externalIsolationMode, dimOpacity: _dimOpacity, isLoading, loadingMessage, emptyMessage, heightScaling, linearScale, flatPatterns, focusDirectory: externalFocusDirectory, focusColor: externalFocusColor, onDirectorySelect: _onDirectorySelect, backgroundColor, textColor, selectedBuilding, adaptCameraToBuildings, fileColorLayers, cameraControls, }: FileCity3DProps): import("react/jsx-runtime").JSX.Element;
|
|
290
|
+
export declare function FileCity3D({ cityData, width, height, onBuildingClick, onBuildingHover, className, style, animation, isGrown: externalIsGrown, onGrowChange, showControls, elevatedScopePanels, dismissingPanelIds, onPanelDismissed, highlightLayers: externalHighlightLayers, isolationMode: externalIsolationMode, dimOpacity: _dimOpacity, isLoading, loadingMessage, emptyMessage, heightScaling, linearScale, flatPatterns, focusDirectory: externalFocusDirectory, focusColor: externalFocusColor, onDirectorySelect: _onDirectorySelect, backgroundColor, textColor, selectedBuilding, selectedPath, selectionStyle, adaptCameraToBuildings, fileColorLayers, cameraControls, }: FileCity3DProps): import("react/jsx-runtime").JSX.Element;
|
|
257
291
|
export default FileCity3D;
|
|
258
292
|
//# sourceMappingURL=FileCity3D.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileCity3D.d.ts","sourceRoot":"","sources":["../../../src/components/FileCity3D/FileCity3D.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"FileCity3D.d.ts","sourceRoot":"","sources":["../../../src/components/FileCity3D/FileCity3D.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1554,7 +1554,132 @@ function ControlsOverlay({ isFlat, onToggle, onResetCamera, onLookDown }) {
|
|
|
1554
1554
|
left: 56,
|
|
1555
1555
|
}, title: "Reset View", children: "\u21BB" })] }));
|
|
1556
1556
|
}
|
|
1557
|
-
|
|
1557
|
+
// Distance (world units) the panel lifts upward when dismissed. Reads as
|
|
1558
|
+
// "toward the camera" because the camera looks down in flat mode.
|
|
1559
|
+
const PANEL_DISMISS_LIFT = 60;
|
|
1560
|
+
// Dismiss animation duration in seconds.
|
|
1561
|
+
const PANEL_DISMISS_DURATION = 0.7;
|
|
1562
|
+
function ElevatedScopePanelMesh({ panel, centerOffset, dismissing, onDismissed, }) {
|
|
1563
|
+
const cx = (panel.bounds.minX + panel.bounds.maxX) / 2 - centerOffset.x;
|
|
1564
|
+
const cz = (panel.bounds.minZ + panel.bounds.maxZ) / 2 - centerOffset.z;
|
|
1565
|
+
const w = Math.max(1, panel.bounds.maxX - panel.bounds.minX);
|
|
1566
|
+
const d = Math.max(1, panel.bounds.maxZ - panel.bounds.minZ);
|
|
1567
|
+
const t = panel.thickness ?? 2;
|
|
1568
|
+
const y = (panel.height ?? 4) + t / 2;
|
|
1569
|
+
const baseOpacity = panel.opacity ?? 1;
|
|
1570
|
+
const isOpaqueStatic = baseOpacity >= 1;
|
|
1571
|
+
const topY = y + t / 2;
|
|
1572
|
+
const tileMax = Math.min(w, d) / 2;
|
|
1573
|
+
const requested = panel.labelSize ?? Math.min(w, d) / 6;
|
|
1574
|
+
const labelSize = Math.max(4, Math.min(tileMax, requested));
|
|
1575
|
+
// Drive the dismiss animation directly via useFrame + Three.js refs
|
|
1576
|
+
// rather than react-spring. React re-renders (e.g., from FileCity3D's
|
|
1577
|
+
// hover state on mouse-move) were disturbing the spring-driven
|
|
1578
|
+
// animation; mutating the Three.js objects directly keeps the
|
|
1579
|
+
// animation isolated from React's render cycle.
|
|
1580
|
+
const groupRef = useRef(null);
|
|
1581
|
+
const slabMaterialRef = useRef(null);
|
|
1582
|
+
const labelMaterialRefs = useRef([]);
|
|
1583
|
+
const startTimeRef = useRef(null);
|
|
1584
|
+
const finishedRef = useRef(false);
|
|
1585
|
+
const [fullyDismissed, setFullyDismissed] = useState(false);
|
|
1586
|
+
useFrame(({ clock }) => {
|
|
1587
|
+
if (!dismissing || finishedRef.current)
|
|
1588
|
+
return;
|
|
1589
|
+
if (startTimeRef.current === null) {
|
|
1590
|
+
startTimeRef.current = clock.elapsedTime;
|
|
1591
|
+
}
|
|
1592
|
+
const elapsed = clock.elapsedTime - startTimeRef.current;
|
|
1593
|
+
const t = Math.min(elapsed / PANEL_DISMISS_DURATION, 1);
|
|
1594
|
+
// ease-out cubic
|
|
1595
|
+
const eased = 1 - Math.pow(1 - t, 3);
|
|
1596
|
+
if (groupRef.current) {
|
|
1597
|
+
groupRef.current.position.y = eased * PANEL_DISMISS_LIFT;
|
|
1598
|
+
}
|
|
1599
|
+
const opacityNow = baseOpacity * (1 - eased);
|
|
1600
|
+
if (slabMaterialRef.current) {
|
|
1601
|
+
slabMaterialRef.current.opacity = opacityNow;
|
|
1602
|
+
}
|
|
1603
|
+
for (const mat of labelMaterialRefs.current) {
|
|
1604
|
+
if (mat)
|
|
1605
|
+
mat.opacity = opacityNow;
|
|
1606
|
+
}
|
|
1607
|
+
if (t >= 1) {
|
|
1608
|
+
finishedRef.current = true;
|
|
1609
|
+
setFullyDismissed(true);
|
|
1610
|
+
onDismissed?.(panel.id);
|
|
1611
|
+
}
|
|
1612
|
+
});
|
|
1613
|
+
if (fullyDismissed)
|
|
1614
|
+
return null;
|
|
1615
|
+
// Always transparent on the slab so animated opacity actually blends.
|
|
1616
|
+
// (Three.js skips alpha blending entirely when transparent=false, which
|
|
1617
|
+
// makes the fade invisible until the prop flips mid-animation.)
|
|
1618
|
+
// depthWrite stays true while the panel is at full opacity so it still
|
|
1619
|
+
// occludes buildings beneath; we drop it once dismissal starts so the
|
|
1620
|
+
// fading panel doesn't punch a hole in the scene.
|
|
1621
|
+
const slabDepthWrite = isOpaqueStatic && !dismissing;
|
|
1622
|
+
const interactive = Boolean(panel.onClick || panel.onDoubleClick);
|
|
1623
|
+
const handleClick = panel.onClick
|
|
1624
|
+
? (e) => {
|
|
1625
|
+
e.stopPropagation();
|
|
1626
|
+
panel.onClick(e.nativeEvent);
|
|
1627
|
+
}
|
|
1628
|
+
: undefined;
|
|
1629
|
+
const handleDoubleClick = panel.onDoubleClick
|
|
1630
|
+
? (e) => {
|
|
1631
|
+
e.stopPropagation();
|
|
1632
|
+
panel.onDoubleClick(e.nativeEvent);
|
|
1633
|
+
}
|
|
1634
|
+
: undefined;
|
|
1635
|
+
const handlePointerOver = interactive
|
|
1636
|
+
? (e) => {
|
|
1637
|
+
e.stopPropagation();
|
|
1638
|
+
document.body.style.cursor = 'pointer';
|
|
1639
|
+
}
|
|
1640
|
+
: undefined;
|
|
1641
|
+
const handlePointerOut = interactive
|
|
1642
|
+
? () => {
|
|
1643
|
+
document.body.style.cursor = '';
|
|
1644
|
+
}
|
|
1645
|
+
: undefined;
|
|
1646
|
+
const labelColor = panel.labelColor ?? '#ffffff';
|
|
1647
|
+
const displayLabelColor = panel.displayLabelColor ?? panel.labelColor ?? '#ffffff';
|
|
1648
|
+
// Reset the array each render — the ref callbacks below will repopulate
|
|
1649
|
+
// it. This avoids stale entries if labels come and go.
|
|
1650
|
+
labelMaterialRefs.current = [];
|
|
1651
|
+
const captureLabelMat = (mat) => {
|
|
1652
|
+
if (mat)
|
|
1653
|
+
labelMaterialRefs.current.push(mat);
|
|
1654
|
+
};
|
|
1655
|
+
return (_jsxs("group", { ref: groupRef, children: [_jsxs("mesh", { position: [cx, y, cz], renderOrder: 10, onClick: dismissing ? undefined : handleClick, onDoubleClick: dismissing ? undefined : handleDoubleClick, onPointerOver: dismissing ? undefined : handlePointerOver, onPointerOut: dismissing ? undefined : handlePointerOut, children: [_jsx("boxGeometry", { args: [w, t, d] }), _jsx("meshBasicMaterial", { ref: slabMaterialRef, color: panel.color, transparent: true, opacity: baseOpacity, depthWrite: slabDepthWrite })] }), panel.displayLabel && (_jsxs(_Fragment, { children: [_jsxs(Text, { position: [cx, topY + 0.05, cz - labelSize * 0.6], rotation: [-Math.PI / 2, 0, 0], fontSize: labelSize, color: displayLabelColor, anchorX: "center", anchorY: "middle", maxWidth: w * 0.9, textAlign: "center", renderOrder: 11, frustumCulled: false, children: [panel.displayLabel, _jsx("meshBasicMaterial", { ref: captureLabelMat, attach: "material", color: displayLabelColor, depthWrite: false, depthTest: false, transparent: true, opacity: baseOpacity })] }), _jsxs("mesh", { position: [cx, topY + 0.06, cz - labelSize * 0.05], rotation: [-Math.PI / 2, 0, 0], renderOrder: 11, children: [_jsx("planeGeometry", { args: [
|
|
1656
|
+
Math.min(w * 0.9, panel.displayLabel.length * labelSize * 0.55),
|
|
1657
|
+
labelSize * 0.06,
|
|
1658
|
+
] }), _jsx("meshBasicMaterial", { ref: captureLabelMat, color: displayLabelColor, depthWrite: false, depthTest: false, transparent: true, opacity: baseOpacity })] })] })), panel.label && (_jsxs(Text, { position: [cx, topY + 0.05, cz + (panel.displayLabel ? labelSize * 0.6 : 0)], rotation: [-Math.PI / 2, 0, 0], fontSize: labelSize, color: labelColor, anchorX: "center", anchorY: "middle", maxWidth: w * 0.9, textAlign: "center", renderOrder: 11, frustumCulled: false, children: [panel.label, _jsx("meshBasicMaterial", { ref: captureLabelMat, attach: "material", color: labelColor, depthWrite: false, depthTest: false, transparent: true, opacity: baseOpacity })] }))] }));
|
|
1659
|
+
}
|
|
1660
|
+
// Lifted just above the default umbrella topY (height 4 + thickness 2 → 6)
|
|
1661
|
+
// so the ring isn't occluded by an `ElevatedScopePanel` covering the same
|
|
1662
|
+
// district when the city is flat.
|
|
1663
|
+
const SELECTION_RING_FLAT_Y = 7;
|
|
1664
|
+
function SelectionRing({ district, centerOffset, color, borderWidth, growProgress, }) {
|
|
1665
|
+
const { worldBounds } = district;
|
|
1666
|
+
const inflate = 1;
|
|
1667
|
+
const minX = worldBounds.minX - inflate;
|
|
1668
|
+
const maxX = worldBounds.maxX + inflate;
|
|
1669
|
+
const minZ = worldBounds.minZ - inflate;
|
|
1670
|
+
const maxZ = worldBounds.maxZ + inflate;
|
|
1671
|
+
const cx = (minX + maxX) / 2 - centerOffset.x;
|
|
1672
|
+
const cz = (minZ + maxZ) / 2 - centerOffset.z;
|
|
1673
|
+
const w = maxX - minX;
|
|
1674
|
+
const d = maxZ - minZ;
|
|
1675
|
+
const pathDepth = district.path.split('/').length;
|
|
1676
|
+
const groundY = -5 - pathDepth * 0.1 + 0.2;
|
|
1677
|
+
const y = SELECTION_RING_FLAT_Y + (groundY - SELECTION_RING_FLAT_Y) * growProgress;
|
|
1678
|
+
const t = Math.max(0.5, borderWidth);
|
|
1679
|
+
const barH = 0.5;
|
|
1680
|
+
return (_jsxs("group", { position: [cx, y, cz], children: [_jsxs("mesh", { position: [0, 0, -d / 2], renderOrder: 20, children: [_jsx("boxGeometry", { args: [w + t, barH, t] }), _jsx("meshBasicMaterial", { color: color, transparent: true, opacity: 0.95 })] }), _jsxs("mesh", { position: [0, 0, d / 2], renderOrder: 20, children: [_jsx("boxGeometry", { args: [w + t, barH, t] }), _jsx("meshBasicMaterial", { color: color, transparent: true, opacity: 0.95 })] }), _jsxs("mesh", { position: [-w / 2, 0, 0], renderOrder: 20, children: [_jsx("boxGeometry", { args: [t, barH, d + t] }), _jsx("meshBasicMaterial", { color: color, transparent: true, opacity: 0.95 })] }), _jsxs("mesh", { position: [w / 2, 0, 0], renderOrder: 20, children: [_jsx("boxGeometry", { args: [t, barH, d + t] }), _jsx("meshBasicMaterial", { color: color, transparent: true, opacity: 0.95 })] })] }));
|
|
1681
|
+
}
|
|
1682
|
+
function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding, selectedBuilding, selectedDistrict, selectionStyle, growProgress, animationConfig, highlightLayers, isolationMode, heightScaling, linearScale, flatPatterns, focusDirectory, focusColor, adaptCameraToBuildings = false, elevatedScopePanels, dismissingPanelIds, onPanelDismissed, cameraControls, onCameraReady, }) {
|
|
1558
1683
|
const centerOffset = useMemo(() => ({
|
|
1559
1684
|
x: (cityData.bounds.minX + cityData.bounds.maxX) / 2,
|
|
1560
1685
|
z: (cityData.bounds.minZ + cityData.bounds.maxZ) / 2,
|
|
@@ -1759,52 +1884,7 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
|
|
|
1759
1884
|
const districtColor = (isFocused && buildingFocusColor) ? buildingFocusColor : highlightLayerColor;
|
|
1760
1885
|
return (_jsx(DistrictFloor, { district: district, centerOffset: centerOffset, opacity: 1, highlightColor: districtColor, growProgress: growProgress }, district.path));
|
|
1761
1886
|
}), _jsx(InstancedBuildings, { buildings: cityData.buildings, centerOffset: centerOffset, onHover: onBuildingHover, onClick: onBuildingClick, hoveredIndex: hoveredIndex, selectedIndex: selectedIndex, growProgress: growProgress, animationConfig: animationConfig, heightScaling: heightScaling, linearScale: linearScale, flatPatterns: flatPatterns, staggerIndices: staggerIndices, focusDirectory: buildingFocusDirectory, highlightLayers: highlightLayers, isolationMode: isolationMode }), _jsx(BuildingIcons, { buildings: cityData.buildings, centerOffset: centerOffset, growProgress: growProgress, heightScaling: heightScaling, linearScale: linearScale, flatPatterns: flatPatterns, highlightLayers: highlightLayers, isolationMode: isolationMode, hasActiveHighlights: activeHighlights }), growProgress === 0 &&
|
|
1762
|
-
elevatedScopePanels?.map(panel => {
|
|
1763
|
-
const cx = (panel.bounds.minX + panel.bounds.maxX) / 2 - centerOffset.x;
|
|
1764
|
-
const cz = (panel.bounds.minZ + panel.bounds.maxZ) / 2 - centerOffset.z;
|
|
1765
|
-
const w = Math.max(1, panel.bounds.maxX - panel.bounds.minX);
|
|
1766
|
-
const d = Math.max(1, panel.bounds.maxZ - panel.bounds.minZ);
|
|
1767
|
-
const t = panel.thickness ?? 2;
|
|
1768
|
-
const y = (panel.height ?? 4) + t / 2;
|
|
1769
|
-
const opacity = panel.opacity ?? 1;
|
|
1770
|
-
const isOpaque = opacity >= 1;
|
|
1771
|
-
const topY = y + t / 2;
|
|
1772
|
-
// Size text to the panel: roughly fit longest reasonable label,
|
|
1773
|
-
// clamped so tiny tiles still render legibly and huge ones don't
|
|
1774
|
-
// get absurd. Callers may override via panel.labelSize, but we
|
|
1775
|
-
// still cap to the tile footprint so the label fits.
|
|
1776
|
-
const tileMax = Math.min(w, d) / 2;
|
|
1777
|
-
const requested = panel.labelSize ?? Math.min(w, d) / 6;
|
|
1778
|
-
const labelSize = Math.max(4, Math.min(tileMax, requested));
|
|
1779
|
-
const interactive = Boolean(panel.onClick || panel.onDoubleClick);
|
|
1780
|
-
const handleClick = panel.onClick
|
|
1781
|
-
? (e) => {
|
|
1782
|
-
e.stopPropagation();
|
|
1783
|
-
panel.onClick(e.nativeEvent);
|
|
1784
|
-
}
|
|
1785
|
-
: undefined;
|
|
1786
|
-
const handleDoubleClick = panel.onDoubleClick
|
|
1787
|
-
? (e) => {
|
|
1788
|
-
e.stopPropagation();
|
|
1789
|
-
panel.onDoubleClick(e.nativeEvent);
|
|
1790
|
-
}
|
|
1791
|
-
: undefined;
|
|
1792
|
-
const handlePointerOver = interactive
|
|
1793
|
-
? (e) => {
|
|
1794
|
-
e.stopPropagation();
|
|
1795
|
-
document.body.style.cursor = 'pointer';
|
|
1796
|
-
}
|
|
1797
|
-
: undefined;
|
|
1798
|
-
const handlePointerOut = interactive
|
|
1799
|
-
? () => {
|
|
1800
|
-
document.body.style.cursor = '';
|
|
1801
|
-
}
|
|
1802
|
-
: undefined;
|
|
1803
|
-
return (_jsxs("group", { children: [_jsxs("mesh", { position: [cx, y, cz], renderOrder: 10, onClick: handleClick, onDoubleClick: handleDoubleClick, onPointerOver: handlePointerOver, onPointerOut: handlePointerOut, children: [_jsx("boxGeometry", { args: [w, t, d] }), _jsx("meshBasicMaterial", { color: panel.color, transparent: !isOpaque, opacity: opacity, depthWrite: isOpaque })] }), panel.displayLabel && (_jsxs(_Fragment, { children: [_jsxs(Text, { position: [cx, topY + 0.05, cz - labelSize * 0.6], rotation: [-Math.PI / 2, 0, 0], fontSize: labelSize, color: panel.displayLabelColor ?? panel.labelColor ?? '#ffffff', anchorX: "center", anchorY: "middle", maxWidth: w * 0.9, textAlign: "center", renderOrder: 11, frustumCulled: false, children: [panel.displayLabel, _jsx("meshBasicMaterial", { attach: "material", color: panel.displayLabelColor ?? panel.labelColor ?? '#ffffff', depthWrite: false, depthTest: false })] }), _jsxs("mesh", { position: [cx, topY + 0.06, cz - labelSize * 0.05], rotation: [-Math.PI / 2, 0, 0], renderOrder: 11, children: [_jsx("planeGeometry", { args: [
|
|
1804
|
-
Math.min(w * 0.9, panel.displayLabel.length * labelSize * 0.55),
|
|
1805
|
-
labelSize * 0.06,
|
|
1806
|
-
] }), _jsx("meshBasicMaterial", { color: panel.displayLabelColor ?? panel.labelColor ?? '#ffffff', depthWrite: false, depthTest: false, transparent: true })] })] })), panel.label && (_jsxs(Text, { position: [cx, topY + 0.05, cz + (panel.displayLabel ? labelSize * 0.6 : 0)], rotation: [-Math.PI / 2, 0, 0], fontSize: labelSize, color: panel.labelColor ?? '#ffffff', anchorX: "center", anchorY: "middle", maxWidth: w * 0.9, textAlign: "center", renderOrder: 11, frustumCulled: false, children: [panel.label, _jsx("meshBasicMaterial", { attach: "material", color: panel.labelColor ?? '#ffffff', depthWrite: false, depthTest: false })] }))] }, panel.id));
|
|
1807
|
-
})] }));
|
|
1887
|
+
elevatedScopePanels?.map(panel => (_jsx(ElevatedScopePanelMesh, { panel: panel, centerOffset: centerOffset, dismissing: dismissingPanelIds?.has(panel.id) ?? false, onDismissed: onPanelDismissed }, panel.id))), selectedDistrict && (_jsx(SelectionRing, { district: selectedDistrict, centerOffset: centerOffset, color: selectionStyle?.color ?? '#facc15', borderWidth: selectionStyle?.borderWidth ?? 2, growProgress: growProgress }))] }));
|
|
1808
1888
|
}
|
|
1809
1889
|
/**
|
|
1810
1890
|
* FileCity3D - 3D visualization of codebase structure
|
|
@@ -1812,10 +1892,14 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
|
|
|
1812
1892
|
* Renders CityData as an interactive 3D city where buildings represent files
|
|
1813
1893
|
* and their height corresponds to line count or file size.
|
|
1814
1894
|
*/
|
|
1815
|
-
export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingClick, className, style, animation, isGrown: externalIsGrown, onGrowChange, showControls = false, elevatedScopePanels, highlightLayers: externalHighlightLayers, isolationMode: externalIsolationMode, dimOpacity: _dimOpacity = 0.15, isLoading = false, loadingMessage = 'Loading file city...', emptyMessage = 'No file tree data available', heightScaling = 'linear', linearScale = 1, flatPatterns = DEFAULT_FLAT_PATTERNS, focusDirectory: externalFocusDirectory, focusColor: externalFocusColor, onDirectorySelect: _onDirectorySelect, backgroundColor = '#0f172a', textColor = '#94a3b8', selectedBuilding = null, adaptCameraToBuildings = false, fileColorLayers, cameraControls, }) {
|
|
1895
|
+
export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingClick, onBuildingHover, className, style, animation, isGrown: externalIsGrown, onGrowChange, showControls = false, elevatedScopePanels, dismissingPanelIds, onPanelDismissed, highlightLayers: externalHighlightLayers, isolationMode: externalIsolationMode, dimOpacity: _dimOpacity = 0.15, isLoading = false, loadingMessage = 'Loading file city...', emptyMessage = 'No file tree data available', heightScaling = 'linear', linearScale = 1, flatPatterns = DEFAULT_FLAT_PATTERNS, focusDirectory: externalFocusDirectory, focusColor: externalFocusColor, onDirectorySelect: _onDirectorySelect, backgroundColor = '#0f172a', textColor = '#94a3b8', selectedBuilding = null, selectedPath = null, selectionStyle, adaptCameraToBuildings = false, fileColorLayers, cameraControls, }) {
|
|
1816
1896
|
const [hoveredBuilding, setHoveredBuilding] = useState(null);
|
|
1817
1897
|
const [internalIsGrown, setInternalIsGrown] = useState(false);
|
|
1818
1898
|
const [cameraReady, setCameraReady] = useState(false);
|
|
1899
|
+
const handleBuildingHover = useCallback((building) => {
|
|
1900
|
+
setHoveredBuilding(building);
|
|
1901
|
+
onBuildingHover?.(building);
|
|
1902
|
+
}, [onBuildingHover]);
|
|
1819
1903
|
const animationConfig = useMemo(() => ({ ...DEFAULT_ANIMATION, ...animation }), [animation]);
|
|
1820
1904
|
// ============================================================================
|
|
1821
1905
|
// Visualization Resolution
|
|
@@ -1849,6 +1933,19 @@ export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingC
|
|
|
1849
1933
|
const focusDirectory = resolved.focusDirectory;
|
|
1850
1934
|
const focusColor = resolved.focusColor;
|
|
1851
1935
|
const isolationMode = resolved.isolationMode;
|
|
1936
|
+
// `selectedPath` wins over the deprecated `selectedBuilding` when both are
|
|
1937
|
+
// set. A path resolves to either a building (file selection) or a district
|
|
1938
|
+
// (directory selection) — never both.
|
|
1939
|
+
const resolvedSelection = useMemo(() => {
|
|
1940
|
+
if (selectedPath != null) {
|
|
1941
|
+
const building = cityData.buildings.find(b => b.path === selectedPath) ?? null;
|
|
1942
|
+
if (building)
|
|
1943
|
+
return { building, district: null };
|
|
1944
|
+
const district = cityData.districts.find(d => d.path === selectedPath) ?? null;
|
|
1945
|
+
return { building: null, district };
|
|
1946
|
+
}
|
|
1947
|
+
return { building: selectedBuilding ?? null, district: null };
|
|
1948
|
+
}, [selectedPath, selectedBuilding, cityData.buildings, cityData.districts]);
|
|
1852
1949
|
const isGrown = externalIsGrown !== undefined ? externalIsGrown : internalIsGrown;
|
|
1853
1950
|
const setIsGrown = (value) => {
|
|
1854
1951
|
setInternalIsGrown(value);
|
|
@@ -1919,6 +2016,6 @@ export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingC
|
|
|
1919
2016
|
height: '100%',
|
|
1920
2017
|
opacity: cameraReady ? 1 : 0,
|
|
1921
2018
|
transition: 'opacity 0.1s ease-in',
|
|
1922
|
-
}, children: _jsx(CityScene, { cityData: cityData, onBuildingHover:
|
|
2019
|
+
}, children: _jsx(CityScene, { cityData: cityData, onBuildingHover: handleBuildingHover, onBuildingClick: onBuildingClick, hoveredBuilding: hoveredBuilding, selectedBuilding: resolvedSelection.building, selectedDistrict: resolvedSelection.district, selectionStyle: selectionStyle, growProgress: growProgress, animationConfig: animationConfig, highlightLayers: highlightLayers, isolationMode: isolationMode, heightScaling: heightScaling, linearScale: linearScale, flatPatterns: flatPatterns, focusDirectory: focusDirectory, focusColor: focusColor, adaptCameraToBuildings: adaptCameraToBuildings, elevatedScopePanels: elevatedScopePanels, dismissingPanelIds: dismissingPanelIds, onPanelDismissed: onPanelDismissed, cameraControls: cameraControls, onCameraReady: () => setCameraReady(true) }) }), _jsx(InfoPanel, { building: resolvedSelection.building }), showControls && (_jsx(ControlsOverlay, { isFlat: !isGrown, onToggle: handleToggle, onResetCamera: resetCamera, onLookDown: () => tiltCameraTo(0) }))] }));
|
|
1923
2020
|
}
|
|
1924
2021
|
export default FileCity3D;
|
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
* FileCity3D - 3D visualization component
|
|
3
3
|
*/
|
|
4
4
|
export { FileCity3D, resetCamera, getCameraAngle, getCameraTarget, getCameraTilt, rotateCameraTo, rotateCameraBy, tiltCameraTo, tiltCameraBy, moveCameraTo, setCameraTarget, DEFAULT_FLAT_PATTERNS, DEFAULT_CAMERA_CONTROLS, } from './FileCity3D';
|
|
5
|
-
export type { FileCity3DProps, AnimationConfig, HighlightLayer, LayerItem, LayerRenderStrategy, IsolationMode, HeightScaling, FlatPattern, ElevatedScopePanel, CityData, CityBuilding, CityDistrict, CameraControlsConfig, MouseDragAction, TouchOneAction, TouchTwoAction, WheelAction, } from './FileCity3D';
|
|
5
|
+
export type { FileCity3DProps, AnimationConfig, HighlightLayer, LayerItem, LayerRenderStrategy, IsolationMode, HeightScaling, FlatPattern, ElevatedScopePanel, SelectionStyle, CityData, CityBuilding, CityDistrict, CameraControlsConfig, MouseDragAction, TouchOneAction, TouchTwoAction, WheelAction, } from './FileCity3D';
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/FileCity3D/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,UAAU,EACV,WAAW,EACX,cAAc,EACd,eAAe,EACf,aAAa,EACb,cAAc,EACd,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,eAAe,EACf,eAAe,EACf,cAAc,EACd,SAAS,EACT,mBAAmB,EACnB,aAAa,EACb,aAAa,EACb,WAAW,EACX,kBAAkB,EAClB,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,oBAAoB,EACpB,eAAe,EACf,cAAc,EACd,cAAc,EACd,WAAW,GACZ,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/FileCity3D/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,UAAU,EACV,WAAW,EACX,cAAc,EACd,eAAe,EACf,aAAa,EACb,cAAc,EACd,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,eAAe,EACf,eAAe,EACf,cAAc,EACd,SAAS,EACT,mBAAmB,EACnB,aAAa,EACb,aAAa,EACb,WAAW,EACX,kBAAkB,EAClB,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,oBAAoB,EACpB,eAAe,EACf,cAAc,EACd,cAAc,EACd,WAAW,GACZ,MAAM,cAAc,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileCityExplorer.d.ts","sourceRoot":"","sources":["../../../src/components/FileCityExplorer/FileCityExplorer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAU1B,OAAO,EAEL,KAAK,QAAQ,EAGd,MAAM,eAAe,CAAC;AAOvB,OAAO,KAAK,EAAa,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAuB7D,MAAM,WAAW,qBAAqB;IACpC,4CAA4C;IAC5C,QAAQ,EAAE,QAAQ,CAAC;IACnB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,aAAa,CAAC,EAAE,KAAK,EAAE,CAAC;IACxB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;IAC7B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,
|
|
1
|
+
{"version":3,"file":"FileCityExplorer.d.ts","sourceRoot":"","sources":["../../../src/components/FileCityExplorer/FileCityExplorer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAU1B,OAAO,EAEL,KAAK,QAAQ,EAGd,MAAM,eAAe,CAAC;AAOvB,OAAO,KAAK,EAAa,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAuB7D,MAAM,WAAW,qBAAqB;IACpC,4CAA4C;IAC5C,QAAQ,EAAE,QAAQ,CAAC;IACnB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,aAAa,CAAC,EAAE,KAAK,EAAE,CAAC;IACxB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;IAC7B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA60C5D,CAAC"}
|
|
@@ -467,46 +467,16 @@ export const FileCityExplorer = ({ cityData, packageRoot, initialScopes, initial
|
|
|
467
467
|
const displayLabel = folderPath ? areaNameByCityPath.get(folderPath) : undefined;
|
|
468
468
|
return displayLabel ? { ...panel, displayLabel } : panel;
|
|
469
469
|
});
|
|
470
|
-
// Selection indicator: render a thin, slightly-larger panel underneath
|
|
471
|
-
// the selected folder's umbrella so an accent ring peeks out around its
|
|
472
|
-
// edges. Inserted *before* the umbrella in the list so the umbrella
|
|
473
|
-
// draws on top — only the inflated rim shows. If the folder is expanded
|
|
474
|
-
// (no umbrella in the panel list) findIndex returns -1 and no ring is
|
|
475
|
-
// drawn, which is exactly what we want.
|
|
476
|
-
if (selectedPanelFolder) {
|
|
477
|
-
const idx = panels.findIndex(p => p.id === `folder::${selectedPanelFolder}`);
|
|
478
|
-
if (idx >= 0) {
|
|
479
|
-
const target = panels[idx];
|
|
480
|
-
const inflate = 4;
|
|
481
|
-
const border = {
|
|
482
|
-
id: `folder-border::${selectedPanelFolder}`,
|
|
483
|
-
color: theme.colors.warning,
|
|
484
|
-
height: (target.height ?? 4) - 2,
|
|
485
|
-
thickness: 1,
|
|
486
|
-
bounds: {
|
|
487
|
-
minX: target.bounds.minX - inflate,
|
|
488
|
-
maxX: target.bounds.maxX + inflate,
|
|
489
|
-
minZ: target.bounds.minZ - inflate,
|
|
490
|
-
maxZ: target.bounds.maxZ + inflate,
|
|
491
|
-
},
|
|
492
|
-
};
|
|
493
|
-
const next = [...panels];
|
|
494
|
-
next.splice(idx, 0, border);
|
|
495
|
-
return next;
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
470
|
return panels.length > 0 ? panels : undefined;
|
|
499
471
|
}, [
|
|
500
472
|
activeTab,
|
|
501
473
|
cityData,
|
|
502
|
-
selectedPanelFolder,
|
|
503
474
|
treeModel,
|
|
504
475
|
folderTreeExpansion,
|
|
505
476
|
setFocusDirectoryIfUnpinned,
|
|
506
477
|
areaNameByCityPath,
|
|
507
478
|
folderIndex,
|
|
508
479
|
packageRootClamp,
|
|
509
|
-
theme,
|
|
510
480
|
]);
|
|
511
481
|
// Cmd-click on a building → surface the chain of expanded ancestor folders
|
|
512
482
|
// (their umbrellas are currently hidden because they're expanded). Each
|
|
@@ -689,7 +659,7 @@ export const FileCityExplorer = ({ cityData, packageRoot, initialScopes, initial
|
|
|
689
659
|
left: 0,
|
|
690
660
|
right: 0,
|
|
691
661
|
bottom: 0,
|
|
692
|
-
}, children: _jsx(FileCity3D, { cityData: cityData, height: "100%", width: "100%", heightScaling: "linear", linearScale: 0.5, backgroundColor: theme.colors.background, textColor: theme.colors.textMuted, focusDirectory: focusDirectory, highlightLayers: cityHighlightLayers, elevatedScopePanels: cityElevatedPanels ?? folderElevatedPanels, onBuildingClick: handleBuildingClick, animation: {
|
|
662
|
+
}, children: _jsx(FileCity3D, { cityData: cityData, height: "100%", width: "100%", heightScaling: "linear", linearScale: 0.5, backgroundColor: theme.colors.background, textColor: theme.colors.textMuted, focusDirectory: focusDirectory, highlightLayers: cityHighlightLayers, elevatedScopePanels: cityElevatedPanels ?? folderElevatedPanels, selectedPath: activeTab === 'files' ? selectedPanelFolder : null, selectionStyle: { color: theme.colors.warning }, onBuildingClick: handleBuildingClick, animation: {
|
|
693
663
|
startFlat: true,
|
|
694
664
|
autoStartDelay: null,
|
|
695
665
|
staggerDelay: 5,
|
package/dist/index.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export type { FileTree } from '@principal-ai/file-city-builder';
|
|
|
14
14
|
export { CityViewWithReactFlow, type CityViewWithReactFlowProps, } from './components/CityViewWithReactFlow';
|
|
15
15
|
export { ThemeProvider, useTheme } from '@principal-ade/industry-theme';
|
|
16
16
|
export { FileCity3D, resetCamera, DEFAULT_FLAT_PATTERNS } from './components/FileCity3D';
|
|
17
|
-
export type { FileCity3DProps, AnimationConfig, HighlightLayer as FileCity3DHighlightLayer, IsolationMode, HeightScaling, FlatPattern, ElevatedScopePanel, } from './components/FileCity3D';
|
|
17
|
+
export type { FileCity3DProps, AnimationConfig, HighlightLayer as FileCity3DHighlightLayer, IsolationMode, HeightScaling, FlatPattern, ElevatedScopePanel, SelectionStyle, } from './components/FileCity3D';
|
|
18
18
|
export type { HighlightLayer as FileCity3DHL } from './components/FileCity3D';
|
|
19
19
|
export { buildFolderElevatedPanels, buildFolderIndex, hashFolderColor, } from './utils/folderElevatedPanels';
|
|
20
20
|
export type { BuildFolderElevatedPanelsOptions } from './utils/folderElevatedPanels';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,8BAA8B,EAC9B,KAAK,mCAAmC,GACzC,MAAM,6CAA6C,CAAC;AAGrD,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,UAAU,GACX,MAAM,sCAAsC,CAAC;AAG9C,OAAO,EAAE,KAAK,mBAAmB,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGvF,OAAO,EACL,gCAAgC,EAChC,6BAA6B,EAC7B,oCAAoC,GACrC,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EACL,8BAA8B,EAC9B,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,kCAAkC,CAAC;AAE1C,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,kCAAkC,CAAC;AAG1C,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAGzF,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAG7F,YAAY,EACV,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,sBAAsB,EACtB,QAAQ,EACR,UAAU,GACX,MAAM,iCAAiC,CAAC;AAGzC,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAG1E,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,YAAY,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAG7F,YAAY,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAGhE,OAAO,EACL,qBAAqB,EACrB,KAAK,0BAA0B,GAChC,MAAM,oCAAoC,CAAC;AAG5C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAGxE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AACzF,YAAY,EACV,eAAe,EACf,eAAe,EACf,cAAc,IAAI,wBAAwB,EAC1C,aAAa,EACb,aAAa,EACb,WAAW,EACX,kBAAkB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,8BAA8B,EAC9B,KAAK,mCAAmC,GACzC,MAAM,6CAA6C,CAAC;AAGrD,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,UAAU,GACX,MAAM,sCAAsC,CAAC;AAG9C,OAAO,EAAE,KAAK,mBAAmB,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGvF,OAAO,EACL,gCAAgC,EAChC,6BAA6B,EAC7B,oCAAoC,GACrC,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EACL,8BAA8B,EAC9B,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,kCAAkC,CAAC;AAE1C,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,kCAAkC,CAAC;AAG1C,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAGzF,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAG7F,YAAY,EACV,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,sBAAsB,EACtB,QAAQ,EACR,UAAU,GACX,MAAM,iCAAiC,CAAC;AAGzC,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAG1E,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,YAAY,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAG7F,YAAY,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAGhE,OAAO,EACL,qBAAqB,EACrB,KAAK,0BAA0B,GAChC,MAAM,oCAAoC,CAAC;AAG5C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAGxE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AACzF,YAAY,EACV,eAAe,EACf,eAAe,EACf,cAAc,IAAI,wBAAwB,EAC1C,aAAa,EACb,aAAa,EACb,WAAW,EACX,kBAAkB,EAClB,cAAc,GACf,MAAM,yBAAyB,CAAC;AAIjC,YAAY,EAAE,cAAc,IAAI,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAG9E,OAAO,EACL,yBAAyB,EACzB,gBAAgB,EAChB,eAAe,GAChB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EAAE,gCAAgC,EAAE,MAAM,8BAA8B,CAAC;AAIrF,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,YAAY,EACV,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,iCAAiC,CAAC"}
|