@principal-ai/file-city-react 0.5.42 → 0.5.44

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.
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import React from 'react';
10
10
  import type { CityData, CityBuilding, CityDistrict, HighlightLayer as BuilderHighlightLayer, LayerItem, LayerRenderStrategy } from '@principal-ai/file-city-builder';
11
+ import * as THREE from 'three';
11
12
  import type { ThreeElements } from '@react-three/fiber';
12
13
  declare module 'react' {
13
14
  namespace JSX {
@@ -17,6 +18,30 @@ declare module 'react' {
17
18
  }
18
19
  export type { CityData, CityBuilding, CityDistrict, LayerItem, LayerRenderStrategy };
19
20
  export type HighlightLayer = BuilderHighlightLayer;
21
+ /** Visual style for the `selectedPath` ring on a directory. */
22
+ export interface SelectionStyle {
23
+ /** Ring color. Defaults to the theme accent. */
24
+ color?: string;
25
+ /** Ring border width in world units. Default: 2. */
26
+ borderWidth?: number;
27
+ }
28
+ /**
29
+ * Per-frame camera callback signature. Fires once per R3F render frame from
30
+ * inside the Canvas, so projection done in the callback is in lockstep with
31
+ * the city render — useful for HTML/SVG overlays that need to track world
32
+ * positions (e.g. leader lines anchored to buildings).
33
+ *
34
+ * The callback receives the live `THREE.Camera` and the current canvas
35
+ * size in CSS pixels. To project a world point to canvas-local pixels:
36
+ *
37
+ * const v = new THREE.Vector3(x, y, z).project(camera);
38
+ * const px = (v.x * 0.5 + 0.5) * size.width;
39
+ * const py = (v.y * -0.5 + 0.5) * size.height;
40
+ */
41
+ export type OnCameraFrame = (camera: THREE.Camera, size: {
42
+ width: number;
43
+ height: number;
44
+ }) => void;
20
45
  /** What to do with non-highlighted buildings */
21
46
  export type IsolationMode = 'none' | 'transparent' | 'collapse' | 'hide';
22
47
  export interface AnimationConfig {
@@ -186,6 +211,8 @@ export interface FileCity3DProps {
186
211
  height?: number | string;
187
212
  /** Callback when a building is clicked */
188
213
  onBuildingClick?: (building: CityBuilding, event: MouseEvent) => void;
214
+ /** Callback when the hovered building changes (fires with null on hover-out) */
215
+ onBuildingHover?: (building: CityBuilding | null) => void;
189
216
  /** CSS class name */
190
217
  className?: string;
191
218
  /** Inline styles */
@@ -226,17 +253,50 @@ export interface FileCity3DProps {
226
253
  backgroundColor?: string;
227
254
  /** Text color for secondary/placeholder text */
228
255
  textColor?: string;
229
- /** Currently selected building (controlled by host) */
256
+ /**
257
+ * @deprecated Use `selectedPath` instead. When both are set, `selectedPath`
258
+ * wins. This prop will be removed in a future release.
259
+ */
230
260
  selectedBuilding?: CityBuilding | null;
261
+ /**
262
+ * Path of the selected building or directory. The component resolves the
263
+ * path against `cityData.buildings` (file selection — emphasizes the
264
+ * building and shows the InfoPanel) and `cityData.districts` (directory
265
+ * selection — draws a ring around the district). When both `selectedPath`
266
+ * and `selectedBuilding` are set, `selectedPath` wins.
267
+ */
268
+ selectedPath?: string | null;
269
+ /** Visual style for the directory selection ring drawn for `selectedPath`. */
270
+ selectionStyle?: SelectionStyle;
231
271
  /** When true, camera height adjusts based on tallest building when grown */
232
272
  adaptCameraToBuildings?: boolean;
233
273
  /** Base file type color layers (resolved with highlightLayers) */
234
274
  fileColorLayers?: HighlightLayer[];
275
+ /**
276
+ * Override the per-building color fallback. When unset (default), buildings
277
+ * not matched by a fill highlight layer are colored by file extension via
278
+ * the built-in file-type palette. Set to a CSS color (e.g. `'#475569'`) to
279
+ * render unmatched buildings in a neutral tone — useful for debug stories
280
+ * that want to isolate highlight-layer rendering.
281
+ */
282
+ defaultBuildingColor?: string;
235
283
  /**
236
284
  * Translucent slabs rendered above the city showing scope coverage as
237
285
  * elevated planes over the directories they own.
238
286
  */
239
287
  elevatedScopePanels?: ElevatedScopePanel[];
288
+ /**
289
+ * Set of panel ids that should play the "lift up and fade out" dismiss
290
+ * animation. Add an id here to start the animation; once it settles,
291
+ * `onPanelDismissed` fires so the host can drop the panel from
292
+ * `elevatedScopePanels`.
293
+ */
294
+ dismissingPanelIds?: ReadonlySet<string>;
295
+ /**
296
+ * Fires once a panel's dismiss animation has settled. The host should
297
+ * remove the id from both `dismissingPanelIds` and `elevatedScopePanels`.
298
+ */
299
+ onPanelDismissed?: (id: string) => void;
240
300
  /**
241
301
  * Configure how mouse / trackpad / touch input drives the camera.
242
302
  * Defaults match Google Maps style: left-drag pans, right-drag rotates,
@@ -246,6 +306,14 @@ export interface FileCity3DProps {
246
306
  * Memoize this object to avoid unnecessary camera re-mounts.
247
307
  */
248
308
  cameraControls?: CameraControlsConfig;
309
+ /**
310
+ * Fires once per R3F render frame with the live camera and canvas size.
311
+ * Use to project world points to canvas pixels for HTML/SVG overlays that
312
+ * need to track buildings as the camera pans / zooms / rotates.
313
+ *
314
+ * Memoize the callback to avoid re-mounting the bridge.
315
+ */
316
+ onCameraFrame?: OnCameraFrame;
249
317
  }
250
318
  /**
251
319
  * FileCity3D - 3D visualization of codebase structure
@@ -253,6 +321,6 @@ export interface FileCity3DProps {
253
321
  * Renders CityData as an interactive 3D city where buildings represent files
254
322
  * and their height corresponds to line count or file size.
255
323
  */
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;
324
+ 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, defaultBuildingColor, cameraControls, onCameraFrame, }: FileCity3DProps): import("react/jsx-runtime").JSX.Element;
257
325
  export default FileCity3D;
258
326
  //# 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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"}
@@ -430,7 +430,7 @@ function BorderHighlights({ buildings, centerOffset, highlightLayers, growProgre
430
430
  });
431
431
  if (borderEdgeData.length === 0)
432
432
  return null;
433
- return (_jsxs("instancedMesh", { ref: meshRef, args: [undefined, undefined, borderEdgeData.length], frustumCulled: false, children: [_jsx("boxGeometry", { args: [1, 1, 1] }), _jsx("meshBasicMaterial", { transparent: true, opacity: 0.9, vertexColors: true })] }));
433
+ return (_jsxs("instancedMesh", { ref: meshRef, args: [undefined, undefined, borderEdgeData.length], frustumCulled: false, children: [_jsx("boxGeometry", { args: [1, 1, 1] }), _jsx("meshBasicMaterial", { transparent: true, opacity: 0.9 })] }));
434
434
  }
435
435
  // Helper to check if a path is inside a directory
436
436
  function isPathInDirectory(path, directory) {
@@ -438,7 +438,7 @@ function isPathInDirectory(path, directory) {
438
438
  return true;
439
439
  return path === directory || path.startsWith(directory + '/');
440
440
  }
441
- function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hoveredIndex, selectedIndex, growProgress, animationConfig, heightScaling, linearScale, flatPatterns, staggerIndices, focusDirectory, highlightLayers, isolationMode, }) {
441
+ function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hoveredIndex, selectedIndex, growProgress, animationConfig, heightScaling, linearScale, flatPatterns, staggerIndices, focusDirectory, highlightLayers, isolationMode, defaultBuildingColor, }) {
442
442
  const meshRef = useRef(null);
443
443
  const startTimeRef = useRef(null);
444
444
  const tempObject = useMemo(() => new THREE.Object3D(), []);
@@ -515,7 +515,7 @@ function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hovered
515
515
  // Get all layer matches and find first fill match for building color
516
516
  const matches = getLayerMatchesForPath(building.path, highlightLayers);
517
517
  const fillMatch = matches.find(m => m.renderStrategy === 'fill');
518
- const color = fillMatch?.color ?? getColorForFile(building);
518
+ const color = fillMatch?.color ?? defaultBuildingColor ?? getColorForFile(building);
519
519
  const x = building.position.x - centerOffset.x;
520
520
  const z = building.position.z - centerOffset.z;
521
521
  const staggerIndex = staggerIndices[index] ?? index;
@@ -541,6 +541,7 @@ function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hovered
541
541
  staggerIndices,
542
542
  animationConfig.staggerDelay,
543
543
  highlightLayers,
544
+ defaultBuildingColor,
544
545
  ]);
545
546
  const minHeight = 0.3;
546
547
  const baseOffset = 0.2;
@@ -886,6 +887,18 @@ export function getCameraAngle() {
886
887
  export function getCameraTilt() {
887
888
  return cameraApi?.getCurrentTilt() ?? null;
888
889
  }
890
+ /**
891
+ * Bridge for piping the live camera + canvas size out of the R3F Canvas on
892
+ * every frame. Mounted as a child of `<Canvas>` so it has access to the R3F
893
+ * render loop; runs zero work if no callback is provided.
894
+ */
895
+ function CameraFrameBridge({ onCameraFrame }) {
896
+ const { camera, size } = useThree();
897
+ useFrame(() => {
898
+ onCameraFrame?.(camera, { width: size.width, height: size.height });
899
+ });
900
+ return null;
901
+ }
889
902
  const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, focusTarget, maxBuildingHeight = 0, cameraControls, onCameraReady, }) {
890
903
  // Use selector to only subscribe to camera, not the entire R3F state
891
904
  // This prevents re-renders on pointer movement
@@ -1554,7 +1567,132 @@ function ControlsOverlay({ isFlat, onToggle, onResetCamera, onLookDown }) {
1554
1567
  left: 56,
1555
1568
  }, title: "Reset View", children: "\u21BB" })] }));
1556
1569
  }
1557
- function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding, selectedBuilding, growProgress, animationConfig, highlightLayers, isolationMode, heightScaling, linearScale, flatPatterns, focusDirectory, focusColor, adaptCameraToBuildings = false, elevatedScopePanels, cameraControls, onCameraReady, }) {
1570
+ // Distance (world units) the panel lifts upward when dismissed. Reads as
1571
+ // "toward the camera" because the camera looks down in flat mode.
1572
+ const PANEL_DISMISS_LIFT = 60;
1573
+ // Dismiss animation duration in seconds.
1574
+ const PANEL_DISMISS_DURATION = 0.7;
1575
+ function ElevatedScopePanelMesh({ panel, centerOffset, dismissing, onDismissed, }) {
1576
+ const cx = (panel.bounds.minX + panel.bounds.maxX) / 2 - centerOffset.x;
1577
+ const cz = (panel.bounds.minZ + panel.bounds.maxZ) / 2 - centerOffset.z;
1578
+ const w = Math.max(1, panel.bounds.maxX - panel.bounds.minX);
1579
+ const d = Math.max(1, panel.bounds.maxZ - panel.bounds.minZ);
1580
+ const t = panel.thickness ?? 2;
1581
+ const y = (panel.height ?? 4) + t / 2;
1582
+ const baseOpacity = panel.opacity ?? 1;
1583
+ const isOpaqueStatic = baseOpacity >= 1;
1584
+ const topY = y + t / 2;
1585
+ const tileMax = Math.min(w, d) / 2;
1586
+ const requested = panel.labelSize ?? Math.min(w, d) / 6;
1587
+ const labelSize = Math.max(4, Math.min(tileMax, requested));
1588
+ // Drive the dismiss animation directly via useFrame + Three.js refs
1589
+ // rather than react-spring. React re-renders (e.g., from FileCity3D's
1590
+ // hover state on mouse-move) were disturbing the spring-driven
1591
+ // animation; mutating the Three.js objects directly keeps the
1592
+ // animation isolated from React's render cycle.
1593
+ const groupRef = useRef(null);
1594
+ const slabMaterialRef = useRef(null);
1595
+ const labelMaterialRefs = useRef([]);
1596
+ const startTimeRef = useRef(null);
1597
+ const finishedRef = useRef(false);
1598
+ const [fullyDismissed, setFullyDismissed] = useState(false);
1599
+ useFrame(({ clock }) => {
1600
+ if (!dismissing || finishedRef.current)
1601
+ return;
1602
+ if (startTimeRef.current === null) {
1603
+ startTimeRef.current = clock.elapsedTime;
1604
+ }
1605
+ const elapsed = clock.elapsedTime - startTimeRef.current;
1606
+ const t = Math.min(elapsed / PANEL_DISMISS_DURATION, 1);
1607
+ // ease-out cubic
1608
+ const eased = 1 - Math.pow(1 - t, 3);
1609
+ if (groupRef.current) {
1610
+ groupRef.current.position.y = eased * PANEL_DISMISS_LIFT;
1611
+ }
1612
+ const opacityNow = baseOpacity * (1 - eased);
1613
+ if (slabMaterialRef.current) {
1614
+ slabMaterialRef.current.opacity = opacityNow;
1615
+ }
1616
+ for (const mat of labelMaterialRefs.current) {
1617
+ if (mat)
1618
+ mat.opacity = opacityNow;
1619
+ }
1620
+ if (t >= 1) {
1621
+ finishedRef.current = true;
1622
+ setFullyDismissed(true);
1623
+ onDismissed?.(panel.id);
1624
+ }
1625
+ });
1626
+ if (fullyDismissed)
1627
+ return null;
1628
+ // Always transparent on the slab so animated opacity actually blends.
1629
+ // (Three.js skips alpha blending entirely when transparent=false, which
1630
+ // makes the fade invisible until the prop flips mid-animation.)
1631
+ // depthWrite stays true while the panel is at full opacity so it still
1632
+ // occludes buildings beneath; we drop it once dismissal starts so the
1633
+ // fading panel doesn't punch a hole in the scene.
1634
+ const slabDepthWrite = isOpaqueStatic && !dismissing;
1635
+ const interactive = Boolean(panel.onClick || panel.onDoubleClick);
1636
+ const handleClick = panel.onClick
1637
+ ? (e) => {
1638
+ e.stopPropagation();
1639
+ panel.onClick(e.nativeEvent);
1640
+ }
1641
+ : undefined;
1642
+ const handleDoubleClick = panel.onDoubleClick
1643
+ ? (e) => {
1644
+ e.stopPropagation();
1645
+ panel.onDoubleClick(e.nativeEvent);
1646
+ }
1647
+ : undefined;
1648
+ const handlePointerOver = interactive
1649
+ ? (e) => {
1650
+ e.stopPropagation();
1651
+ document.body.style.cursor = 'pointer';
1652
+ }
1653
+ : undefined;
1654
+ const handlePointerOut = interactive
1655
+ ? () => {
1656
+ document.body.style.cursor = '';
1657
+ }
1658
+ : undefined;
1659
+ const labelColor = panel.labelColor ?? '#ffffff';
1660
+ const displayLabelColor = panel.displayLabelColor ?? panel.labelColor ?? '#ffffff';
1661
+ // Reset the array each render — the ref callbacks below will repopulate
1662
+ // it. This avoids stale entries if labels come and go.
1663
+ labelMaterialRefs.current = [];
1664
+ const captureLabelMat = (mat) => {
1665
+ if (mat)
1666
+ labelMaterialRefs.current.push(mat);
1667
+ };
1668
+ 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: [
1669
+ Math.min(w * 0.9, panel.displayLabel.length * labelSize * 0.55),
1670
+ labelSize * 0.06,
1671
+ ] }), _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 })] }))] }));
1672
+ }
1673
+ // Lifted just above the default umbrella topY (height 4 + thickness 2 → 6)
1674
+ // so the ring isn't occluded by an `ElevatedScopePanel` covering the same
1675
+ // district when the city is flat.
1676
+ const SELECTION_RING_FLAT_Y = 7;
1677
+ function SelectionRing({ district, centerOffset, color, borderWidth, growProgress, }) {
1678
+ const { worldBounds } = district;
1679
+ const inflate = 1;
1680
+ const minX = worldBounds.minX - inflate;
1681
+ const maxX = worldBounds.maxX + inflate;
1682
+ const minZ = worldBounds.minZ - inflate;
1683
+ const maxZ = worldBounds.maxZ + inflate;
1684
+ const cx = (minX + maxX) / 2 - centerOffset.x;
1685
+ const cz = (minZ + maxZ) / 2 - centerOffset.z;
1686
+ const w = maxX - minX;
1687
+ const d = maxZ - minZ;
1688
+ const pathDepth = district.path.split('/').length;
1689
+ const groundY = -5 - pathDepth * 0.1 + 0.2;
1690
+ const y = SELECTION_RING_FLAT_Y + (groundY - SELECTION_RING_FLAT_Y) * growProgress;
1691
+ const t = Math.max(0.5, borderWidth);
1692
+ const barH = 0.5;
1693
+ 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 })] })] }));
1694
+ }
1695
+ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding, selectedBuilding, selectedDistrict, selectionStyle, growProgress, animationConfig, highlightLayers, isolationMode, heightScaling, linearScale, flatPatterns, focusDirectory, focusColor, adaptCameraToBuildings = false, elevatedScopePanels, dismissingPanelIds, onPanelDismissed, cameraControls, defaultBuildingColor, onCameraReady, }) {
1558
1696
  const centerOffset = useMemo(() => ({
1559
1697
  x: (cityData.bounds.minX + cityData.bounds.maxX) / 2,
1560
1698
  z: (cityData.bounds.minZ + cityData.bounds.maxZ) / 2,
@@ -1758,53 +1896,8 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
1758
1896
  // Focus color takes priority, then highlight layer color
1759
1897
  const districtColor = (isFocused && buildingFocusColor) ? buildingFocusColor : highlightLayerColor;
1760
1898
  return (_jsx(DistrictFloor, { district: district, centerOffset: centerOffset, opacity: 1, highlightColor: districtColor, growProgress: growProgress }, district.path));
1761
- }), _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
- })] }));
1899
+ }), _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, defaultBuildingColor: defaultBuildingColor }), _jsx(BuildingIcons, { buildings: cityData.buildings, centerOffset: centerOffset, growProgress: growProgress, heightScaling: heightScaling, linearScale: linearScale, flatPatterns: flatPatterns, highlightLayers: highlightLayers, isolationMode: isolationMode, hasActiveHighlights: activeHighlights }), growProgress === 0 &&
1900
+ 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
1901
  }
1809
1902
  /**
1810
1903
  * FileCity3D - 3D visualization of codebase structure
@@ -1812,10 +1905,14 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
1812
1905
  * Renders CityData as an interactive 3D city where buildings represent files
1813
1906
  * and their height corresponds to line count or file size.
1814
1907
  */
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, }) {
1908
+ 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, defaultBuildingColor, cameraControls, onCameraFrame, }) {
1816
1909
  const [hoveredBuilding, setHoveredBuilding] = useState(null);
1817
1910
  const [internalIsGrown, setInternalIsGrown] = useState(false);
1818
1911
  const [cameraReady, setCameraReady] = useState(false);
1912
+ const handleBuildingHover = useCallback((building) => {
1913
+ setHoveredBuilding(building);
1914
+ onBuildingHover?.(building);
1915
+ }, [onBuildingHover]);
1819
1916
  const animationConfig = useMemo(() => ({ ...DEFAULT_ANIMATION, ...animation }), [animation]);
1820
1917
  // ============================================================================
1821
1918
  // Visualization Resolution
@@ -1849,6 +1946,19 @@ export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingC
1849
1946
  const focusDirectory = resolved.focusDirectory;
1850
1947
  const focusColor = resolved.focusColor;
1851
1948
  const isolationMode = resolved.isolationMode;
1949
+ // `selectedPath` wins over the deprecated `selectedBuilding` when both are
1950
+ // set. A path resolves to either a building (file selection) or a district
1951
+ // (directory selection) — never both.
1952
+ const resolvedSelection = useMemo(() => {
1953
+ if (selectedPath != null) {
1954
+ const building = cityData.buildings.find(b => b.path === selectedPath) ?? null;
1955
+ if (building)
1956
+ return { building, district: null };
1957
+ const district = cityData.districts.find(d => d.path === selectedPath) ?? null;
1958
+ return { building: null, district };
1959
+ }
1960
+ return { building: selectedBuilding ?? null, district: null };
1961
+ }, [selectedPath, selectedBuilding, cityData.buildings, cityData.districts]);
1852
1962
  const isGrown = externalIsGrown !== undefined ? externalIsGrown : internalIsGrown;
1853
1963
  const setIsGrown = (value) => {
1854
1964
  setInternalIsGrown(value);
@@ -1910,7 +2020,7 @@ export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingC
1910
2020
  background: backgroundColor,
1911
2021
  overflow: 'hidden',
1912
2022
  ...style,
1913
- }, children: [_jsx(Canvas, { shadows: true, flat // Disables tone mapping for true colors
2023
+ }, children: [_jsxs(Canvas, { shadows: true, flat // Disables tone mapping for true colors
1914
2024
  : true, style: {
1915
2025
  position: 'absolute',
1916
2026
  top: 0,
@@ -1919,6 +2029,6 @@ export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingC
1919
2029
  height: '100%',
1920
2030
  opacity: cameraReady ? 1 : 0,
1921
2031
  transition: 'opacity 0.1s ease-in',
1922
- }, children: _jsx(CityScene, { cityData: cityData, onBuildingHover: setHoveredBuilding, onBuildingClick: onBuildingClick, hoveredBuilding: hoveredBuilding, selectedBuilding: selectedBuilding, growProgress: growProgress, animationConfig: animationConfig, highlightLayers: highlightLayers, isolationMode: isolationMode, heightScaling: heightScaling, linearScale: linearScale, flatPatterns: flatPatterns, focusDirectory: focusDirectory, focusColor: focusColor, adaptCameraToBuildings: adaptCameraToBuildings, elevatedScopePanels: elevatedScopePanels, cameraControls: cameraControls, onCameraReady: () => setCameraReady(true) }) }), _jsx(InfoPanel, { building: selectedBuilding }), showControls && (_jsx(ControlsOverlay, { isFlat: !isGrown, onToggle: handleToggle, onResetCamera: resetCamera, onLookDown: () => tiltCameraTo(0) }))] }));
2032
+ }, 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, defaultBuildingColor: defaultBuildingColor, onCameraReady: () => setCameraReady(true) }), onCameraFrame && _jsx(CameraFrameBridge, { onCameraFrame: onCameraFrame })] }), _jsx(InfoPanel, { building: resolvedSelection.building }), showControls && (_jsx(ControlsOverlay, { isFlat: !isGrown, onToggle: handleToggle, onResetCamera: resetCamera, onLookDown: () => tiltCameraTo(0) }))] }));
1923
2033
  }
1924
2034
  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, OnCameraFrame, } 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,EACX,aAAa,GACd,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,CAy2C5D,CAAC"}
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';
@@ -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,GACnB,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"}
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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@principal-ai/file-city-react",
3
- "version": "0.5.42",
3
+ "version": "0.5.44",
4
4
  "type": "module",
5
5
  "description": "React components for File City visualization",
6
6
  "main": "dist/index.js",