@principal-ai/file-city-react 0.5.25 → 0.5.27

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.
@@ -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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"}
@@ -472,55 +472,37 @@ function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hovered
472
472
  buildingIndex: d.index,
473
473
  })), growProgress: growProgress, minHeight: minHeight, baseOffset: baseOffset, springDuration: springDuration, heightMultipliersRef: heightMultipliersRef })] }));
474
474
  }
475
- function AnimatedIcon({ x, z, targetHeight, iconSize, texture, opacity, growProgress, staggerDelayMs, springDuration, }) {
475
+ function AnimatedIcon({ x, z, targetHeight, iconSize, texture, opacity, growProgress, }) {
476
476
  const meshRef = useRef(null);
477
- const startTimeRef = useRef(null);
478
477
  const materialRef = useRef(null);
479
- useFrame(({ clock }) => {
478
+ useFrame(() => {
480
479
  if (!meshRef.current)
481
480
  return;
482
- if (startTimeRef.current === null && growProgress > 0) {
483
- startTimeRef.current = clock.elapsedTime * 1000;
484
- }
485
- const currentTime = clock.elapsedTime * 1000;
486
- const animStartTime = startTimeRef.current ?? currentTime;
487
- // Calculate per-icon animation progress
488
- const elapsed = currentTime - animStartTime - staggerDelayMs;
489
- let animProgress = growProgress;
490
- if (growProgress > 0 && elapsed >= 0) {
491
- const t = Math.min(elapsed / springDuration, 1);
492
- const eased = 1 - Math.pow(1 - t, 3);
493
- animProgress = eased * growProgress;
494
- }
495
- else if (growProgress > 0 && elapsed < 0) {
496
- animProgress = 0;
497
- }
481
+ // Icons track the global growProgress directly (no stagger)
482
+ // This keeps them in sync with the building heights
498
483
  const minHeight = 0.3;
499
484
  const baseOffset = 0.2;
500
- const height = animProgress * targetHeight + minHeight;
485
+ const height = growProgress * targetHeight + minHeight;
501
486
  const buildingTop = height + baseOffset;
502
- // When flat (animProgress=0): icon lies flat at ground level
503
- // When grown (animProgress=1): icon lies flat on building roof
504
- const flatY = minHeight + baseOffset + 0.1;
505
- const grownY = buildingTop + 0.1;
506
- const yPosition = flatY + (grownY - flatY) * animProgress;
487
+ // When flat (growProgress=0): icon lies flat at ground level
488
+ // When grown (growProgress=1): icon lies flat above building roof
489
+ const flatY = minHeight + baseOffset + 0.5;
490
+ const grownY = buildingTop + 0.5;
491
+ const yPosition = flatY + (grownY - flatY) * growProgress;
507
492
  meshRef.current.position.y = yPosition;
508
493
  // Keep icon flat (facing up) at all times
509
494
  meshRef.current.rotation.x = -Math.PI / 2;
510
495
  if (materialRef.current) {
511
- // Show icons even when flat, fade out only slightly
512
- const minOpacity = 0.8;
513
- const effectiveOpacity = minOpacity + (1 - minOpacity) * animProgress;
514
- materialRef.current.opacity = opacity * effectiveOpacity;
496
+ materialRef.current.opacity = opacity;
515
497
  }
516
498
  });
517
499
  return (_jsxs("mesh", { ref: meshRef, position: [x, 0, z], scale: [iconSize, iconSize, 1], raycast: () => null, children: [_jsx("planeGeometry", { args: [1, 1] }), _jsx("meshBasicMaterial", { ref: materialRef, map: texture, transparent: true, opacity: 0.8, depthTest: true, depthWrite: false, side: THREE.DoubleSide })] }));
518
500
  }
519
- function BuildingIcons({ buildings, centerOffset, growProgress, heightScaling, linearScale, flatPatterns, highlightLayers, isolationMode, hasActiveHighlights, staggerIndices, springDuration, staggerDelay, }) {
501
+ function BuildingIcons({ buildings, centerOffset, growProgress, heightScaling, linearScale, flatPatterns, highlightLayers, isolationMode, hasActiveHighlights, }) {
520
502
  // Pre-compute buildings with icons
521
503
  const buildingsWithIcons = useMemo(() => {
522
504
  return buildings
523
- .map((building, index) => {
505
+ .map((building) => {
524
506
  const config = getConfigForFile(building);
525
507
  if (!config.icon)
526
508
  return null;
@@ -529,14 +511,13 @@ function BuildingIcons({ buildings, centerOffset, growProgress, heightScaling, l
529
511
  const shouldDim = hasActiveHighlights && !isHighlighted;
530
512
  const shouldHide = shouldDim && isolationMode === 'hide';
531
513
  const shouldCollapse = shouldDim && isolationMode === 'collapse';
532
- if (shouldHide)
514
+ // Hide icons for buildings that are hidden or collapsed
515
+ if (shouldHide || shouldCollapse)
533
516
  return null;
534
517
  const fullHeight = calculateBuildingHeight(building, heightScaling, linearScale, flatPatterns);
535
- const targetHeight = shouldCollapse ? 0.5 : fullHeight;
518
+ const targetHeight = fullHeight;
536
519
  const x = building.position.x - centerOffset.x;
537
520
  const z = building.position.z - centerOffset.z;
538
- const staggerIndex = staggerIndices[index] ?? index;
539
- const staggerDelayMs = staggerDelay * staggerIndex;
540
521
  return {
541
522
  building,
542
523
  config,
@@ -544,7 +525,6 @@ function BuildingIcons({ buildings, centerOffset, growProgress, heightScaling, l
544
525
  z,
545
526
  targetHeight,
546
527
  shouldDim,
547
- staggerDelayMs,
548
528
  };
549
529
  })
550
530
  .filter(Boolean);
@@ -557,11 +537,9 @@ function BuildingIcons({ buildings, centerOffset, growProgress, heightScaling, l
557
537
  heightScaling,
558
538
  linearScale,
559
539
  flatPatterns,
560
- staggerIndices,
561
- staggerDelay,
562
540
  ]);
563
541
  // Icons are now always rendered (flat or grown)
564
- return (_jsx(_Fragment, { children: buildingsWithIcons.map(({ building, config, x, z, targetHeight, shouldDim, staggerDelayMs }) => {
542
+ return (_jsx(_Fragment, { children: buildingsWithIcons.map(({ building, config, x, z, targetHeight, shouldDim }) => {
565
543
  const icon = config.icon;
566
544
  const texture = getIconTexture(icon.name, icon.color || '#ffffff');
567
545
  if (!texture)
@@ -571,7 +549,7 @@ function BuildingIcons({ buildings, centerOffset, growProgress, heightScaling, l
571
549
  const minDimension = Math.min(width, depth);
572
550
  const iconSize = minDimension * (icon.size || 0.6) * 1.7;
573
551
  const opacity = shouldDim && isolationMode === 'transparent' ? 0.3 : 1;
574
- return (_jsx(AnimatedIcon, { x: x, z: z, targetHeight: targetHeight, iconSize: iconSize, texture: texture, opacity: opacity, growProgress: growProgress, staggerDelayMs: staggerDelayMs, springDuration: springDuration }, building.path));
552
+ return (_jsx(AnimatedIcon, { x: x, z: z, targetHeight: targetHeight, iconSize: iconSize, texture: texture, opacity: opacity, growProgress: growProgress }, building.path));
575
553
  }) }));
576
554
  }
577
555
  function DistrictFloor({ district, centerOffset, highlightColor, growProgress }) {
@@ -684,6 +662,7 @@ function AnimatedCamera({ citySize, isFlat, focusTarget, maxBuildingHeight = 0 }
684
662
  const isOrbitingRef = useRef(false);
685
663
  const hasAppliedInitial = useRef(false);
686
664
  const frameCount = useRef(0);
665
+ const prevIsFlatRef = useRef(isFlat); // Track previous isFlat to detect actual state changes
687
666
  // Calculate camera height to fit city in viewport (for top-down view)
688
667
  // Formula: height = citySize / (2 * tan(fov/2) * min(1, aspect))
689
668
  // Padding factor adds space around the city to match 2D component
@@ -785,11 +764,30 @@ function AnimatedCamera({ citySize, isFlat, focusTarget, maxBuildingHeight = 0 }
785
764
  const orbitParamsRef = useRef(null);
786
765
  // Track tilt parameters during vertical rotation
787
766
  const tiltParamsRef = useRef(null);
788
- // When targetPos changes after initial, animate to new position
767
+ // When isFlat changes after initial setup, animate to new position
768
+ // We track isFlat explicitly rather than targetPos to avoid spurious animations
769
+ // from aspect ratio changes or other recalculations
789
770
  useEffect(() => {
790
771
  // Skip the first render - we handle that directly in useFrame
791
772
  if (!hasAppliedInitial.current)
792
773
  return;
774
+ // Only animate if isFlat actually changed (flat <-> grown transition)
775
+ const isFlatChanged = prevIsFlatRef.current !== isFlat;
776
+ prevIsFlatRef.current = isFlat;
777
+ if (!isFlatChanged) {
778
+ // isFlat didn't change, just update position directly without animation
779
+ // This handles things like focusTarget changes within the same flat/grown state
780
+ api.set({
781
+ camX: targetPos.x,
782
+ camY: targetPos.y,
783
+ camZ: targetPos.z,
784
+ lookX: targetPos.targetX,
785
+ lookY: targetPos.targetY,
786
+ lookZ: targetPos.targetZ,
787
+ });
788
+ return;
789
+ }
790
+ // isFlat changed - animate the transition
793
791
  api.start({
794
792
  camX: targetPos.x,
795
793
  camY: targetPos.y,
@@ -801,7 +799,7 @@ function AnimatedCamera({ citySize, isFlat, focusTarget, maxBuildingHeight = 0 }
801
799
  isAnimatingRef.current = false;
802
800
  },
803
801
  });
804
- }, [targetPos, api]);
802
+ }, [targetPos, api, isFlat]);
805
803
  // Update camera each frame
806
804
  useFrame(() => {
807
805
  frameCount.current++;
@@ -1350,7 +1348,7 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
1350
1348
  // Focus color takes priority, then highlight layer color
1351
1349
  const districtColor = (isFocused && buildingFocusColor) ? buildingFocusColor : highlightLayerColor;
1352
1350
  return (_jsx(DistrictFloor, { district: district, centerOffset: centerOffset, opacity: 1, highlightColor: districtColor, growProgress: growProgress }, district.path));
1353
- }), _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, staggerIndices: staggerIndices, springDuration: springDuration, staggerDelay: animationConfig.staggerDelay || 15 })] }));
1351
+ }), _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 })] }));
1354
1352
  }
1355
1353
  /**
1356
1354
  * FileCity3D - 3D visualization of codebase structure
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@principal-ai/file-city-react",
3
- "version": "0.5.25",
3
+ "version": "0.5.27",
4
4
  "type": "module",
5
5
  "description": "React components for File City visualization",
6
6
  "main": "dist/index.js",
@@ -769,9 +769,6 @@ interface BuildingIconsProps {
769
769
  highlightLayers: HighlightLayer[];
770
770
  isolationMode: IsolationMode;
771
771
  hasActiveHighlights: boolean;
772
- staggerIndices: number[];
773
- springDuration: number;
774
- staggerDelay: number;
775
772
  }
776
773
 
777
774
  // Individual animated icon component
@@ -783,8 +780,6 @@ interface AnimatedIconProps {
783
780
  texture: THREE.Texture;
784
781
  opacity: number;
785
782
  growProgress: number;
786
- staggerDelayMs: number;
787
- springDuration: number;
788
783
  }
789
784
 
790
785
  function AnimatedIcon({
@@ -795,45 +790,25 @@ function AnimatedIcon({
795
790
  texture,
796
791
  opacity,
797
792
  growProgress,
798
- staggerDelayMs,
799
- springDuration,
800
793
  }: AnimatedIconProps) {
801
794
  const meshRef = useRef<THREE.Mesh>(null);
802
- const startTimeRef = useRef<number | null>(null);
803
795
  const materialRef = useRef<THREE.MeshBasicMaterial>(null);
804
796
 
805
- useFrame(({ clock }) => {
797
+ useFrame(() => {
806
798
  if (!meshRef.current) return;
807
799
 
808
- if (startTimeRef.current === null && growProgress > 0) {
809
- startTimeRef.current = clock.elapsedTime * 1000;
810
- }
811
-
812
- const currentTime = clock.elapsedTime * 1000;
813
- const animStartTime = startTimeRef.current ?? currentTime;
814
-
815
- // Calculate per-icon animation progress
816
- const elapsed = currentTime - animStartTime - staggerDelayMs;
817
- let animProgress = growProgress;
818
-
819
- if (growProgress > 0 && elapsed >= 0) {
820
- const t = Math.min(elapsed / springDuration, 1);
821
- const eased = 1 - Math.pow(1 - t, 3);
822
- animProgress = eased * growProgress;
823
- } else if (growProgress > 0 && elapsed < 0) {
824
- animProgress = 0;
825
- }
826
-
800
+ // Icons track the global growProgress directly (no stagger)
801
+ // This keeps them in sync with the building heights
827
802
  const minHeight = 0.3;
828
803
  const baseOffset = 0.2;
829
- const height = animProgress * targetHeight + minHeight;
804
+ const height = growProgress * targetHeight + minHeight;
830
805
  const buildingTop = height + baseOffset;
831
806
 
832
- // When flat (animProgress=0): icon lies flat at ground level
833
- // When grown (animProgress=1): icon lies flat on building roof
834
- const flatY = minHeight + baseOffset + 0.1;
835
- const grownY = buildingTop + 0.1;
836
- const yPosition = flatY + (grownY - flatY) * animProgress;
807
+ // When flat (growProgress=0): icon lies flat at ground level
808
+ // When grown (growProgress=1): icon lies flat above building roof
809
+ const flatY = minHeight + baseOffset + 0.5;
810
+ const grownY = buildingTop + 0.5;
811
+ const yPosition = flatY + (grownY - flatY) * growProgress;
837
812
 
838
813
  meshRef.current.position.y = yPosition;
839
814
 
@@ -841,10 +816,7 @@ function AnimatedIcon({
841
816
  meshRef.current.rotation.x = -Math.PI / 2;
842
817
 
843
818
  if (materialRef.current) {
844
- // Show icons even when flat, fade out only slightly
845
- const minOpacity = 0.8;
846
- const effectiveOpacity = minOpacity + (1 - minOpacity) * animProgress;
847
- materialRef.current.opacity = opacity * effectiveOpacity;
819
+ materialRef.current.opacity = opacity;
848
820
  }
849
821
  });
850
822
 
@@ -879,14 +851,11 @@ function BuildingIcons({
879
851
  highlightLayers,
880
852
  isolationMode,
881
853
  hasActiveHighlights,
882
- staggerIndices,
883
- springDuration,
884
- staggerDelay,
885
854
  }: BuildingIconsProps) {
886
855
  // Pre-compute buildings with icons
887
856
  const buildingsWithIcons = useMemo(() => {
888
857
  return buildings
889
- .map((building, index) => {
858
+ .map((building) => {
890
859
  const config = getConfigForFile(building);
891
860
  if (!config.icon) return null;
892
861
 
@@ -896,17 +865,15 @@ function BuildingIcons({
896
865
  const shouldHide = shouldDim && isolationMode === 'hide';
897
866
  const shouldCollapse = shouldDim && isolationMode === 'collapse';
898
867
 
899
- if (shouldHide) return null;
868
+ // Hide icons for buildings that are hidden or collapsed
869
+ if (shouldHide || shouldCollapse) return null;
900
870
 
901
871
  const fullHeight = calculateBuildingHeight(building, heightScaling, linearScale, flatPatterns);
902
- const targetHeight = shouldCollapse ? 0.5 : fullHeight;
872
+ const targetHeight = fullHeight;
903
873
 
904
874
  const x = building.position.x - centerOffset.x;
905
875
  const z = building.position.z - centerOffset.z;
906
876
 
907
- const staggerIndex = staggerIndices[index] ?? index;
908
- const staggerDelayMs = staggerDelay * staggerIndex;
909
-
910
877
  return {
911
878
  building,
912
879
  config,
@@ -914,7 +881,6 @@ function BuildingIcons({
914
881
  z,
915
882
  targetHeight,
916
883
  shouldDim,
917
- staggerDelayMs,
918
884
  };
919
885
  })
920
886
  .filter(Boolean) as Array<{
@@ -924,7 +890,6 @@ function BuildingIcons({
924
890
  z: number;
925
891
  targetHeight: number;
926
892
  shouldDim: boolean;
927
- staggerDelayMs: number;
928
893
  }>;
929
894
  }, [
930
895
  buildings,
@@ -935,15 +900,13 @@ function BuildingIcons({
935
900
  heightScaling,
936
901
  linearScale,
937
902
  flatPatterns,
938
- staggerIndices,
939
- staggerDelay,
940
903
  ]);
941
904
 
942
905
  // Icons are now always rendered (flat or grown)
943
906
  return (
944
907
  <>
945
908
  {buildingsWithIcons.map(
946
- ({ building, config, x, z, targetHeight, shouldDim, staggerDelayMs }) => {
909
+ ({ building, config, x, z, targetHeight, shouldDim }) => {
947
910
  const icon = config.icon!;
948
911
  const texture = getIconTexture(icon.name, icon.color || '#ffffff');
949
912
  if (!texture) return null;
@@ -965,8 +928,6 @@ function BuildingIcons({
965
928
  texture={texture}
966
929
  opacity={opacity}
967
930
  growProgress={growProgress}
968
- staggerDelayMs={staggerDelayMs}
969
- springDuration={springDuration}
970
931
  />
971
932
  );
972
933
  },
@@ -1185,6 +1146,7 @@ function AnimatedCamera({ citySize, isFlat, focusTarget, maxBuildingHeight = 0 }
1185
1146
  const isOrbitingRef = useRef(false);
1186
1147
  const hasAppliedInitial = useRef(false);
1187
1148
  const frameCount = useRef(0);
1149
+ const prevIsFlatRef = useRef(isFlat); // Track previous isFlat to detect actual state changes
1188
1150
 
1189
1151
  // Calculate camera height to fit city in viewport (for top-down view)
1190
1152
  // Formula: height = citySize / (2 * tan(fov/2) * min(1, aspect))
@@ -1307,11 +1269,32 @@ function AnimatedCamera({ citySize, isFlat, focusTarget, maxBuildingHeight = 0 }
1307
1269
  azimuthAngle: number; // horizontal angle to maintain
1308
1270
  } | null>(null);
1309
1271
 
1310
- // When targetPos changes after initial, animate to new position
1272
+ // When isFlat changes after initial setup, animate to new position
1273
+ // We track isFlat explicitly rather than targetPos to avoid spurious animations
1274
+ // from aspect ratio changes or other recalculations
1311
1275
  useEffect(() => {
1312
1276
  // Skip the first render - we handle that directly in useFrame
1313
1277
  if (!hasAppliedInitial.current) return;
1314
1278
 
1279
+ // Only animate if isFlat actually changed (flat <-> grown transition)
1280
+ const isFlatChanged = prevIsFlatRef.current !== isFlat;
1281
+ prevIsFlatRef.current = isFlat;
1282
+
1283
+ if (!isFlatChanged) {
1284
+ // isFlat didn't change, just update position directly without animation
1285
+ // This handles things like focusTarget changes within the same flat/grown state
1286
+ api.set({
1287
+ camX: targetPos.x,
1288
+ camY: targetPos.y,
1289
+ camZ: targetPos.z,
1290
+ lookX: targetPos.targetX,
1291
+ lookY: targetPos.targetY,
1292
+ lookZ: targetPos.targetZ,
1293
+ });
1294
+ return;
1295
+ }
1296
+
1297
+ // isFlat changed - animate the transition
1315
1298
  api.start({
1316
1299
  camX: targetPos.x,
1317
1300
  camY: targetPos.y,
@@ -1323,7 +1306,7 @@ function AnimatedCamera({ citySize, isFlat, focusTarget, maxBuildingHeight = 0 }
1323
1306
  isAnimatingRef.current = false;
1324
1307
  },
1325
1308
  });
1326
- }, [targetPos, api]);
1309
+ }, [targetPos, api, isFlat]);
1327
1310
 
1328
1311
  // Update camera each frame
1329
1312
  useFrame(() => {
@@ -2135,9 +2118,6 @@ function CityScene({
2135
2118
  highlightLayers={highlightLayers}
2136
2119
  isolationMode={isolationMode}
2137
2120
  hasActiveHighlights={activeHighlights}
2138
- staggerIndices={staggerIndices}
2139
- springDuration={springDuration}
2140
- staggerDelay={animationConfig.staggerDelay || 15}
2141
2121
  />
2142
2122
  </>
2143
2123
  );