@principal-ai/file-city-react 0.5.49 → 0.5.51

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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"}
@@ -203,7 +203,7 @@ function getHighlightForPath(path, layers) {
203
203
  function hasActiveHighlights(layers) {
204
204
  return layers.some(layer => layer.enabled && layer.items.length > 0);
205
205
  }
206
- function BuildingEdges({ buildings, growProgress, minHeight, baseOffset, springDuration, heightMultipliersRef, }) {
206
+ function BuildingEdges({ buildings, growProgress, minHeight, baseOffset, springDuration, heightMultipliersRef, hiddenRef, }) {
207
207
  const meshRef = useRef(null);
208
208
  const startTimeRef = useRef(null);
209
209
  const tempObject = useMemo(() => new THREE.Object3D(), []);
@@ -234,6 +234,14 @@ function BuildingEdges({ buildings, growProgress, minHeight, baseOffset, springD
234
234
  const animStartTime = startTimeRef.current ?? currentTime;
235
235
  edgeData.forEach((edge, idx) => {
236
236
  const { x, z, fullHeight, staggerDelayMs, buildingIndex } = edge;
237
+ const isHidden = hiddenRef?.current?.[buildingIndex] === 1;
238
+ if (isHidden) {
239
+ tempObject.position.set(x, baseOffset, z);
240
+ tempObject.scale.set(0, 0, 0);
241
+ tempObject.updateMatrix();
242
+ meshRef.current.setMatrixAt(idx, tempObject.matrix);
243
+ return;
244
+ }
237
245
  // Get height multiplier from shared ref (for collapse animation)
238
246
  const heightMultiplier = heightMultipliersRef.current?.[buildingIndex] ?? 1;
239
247
  // Calculate per-building animation progress
@@ -438,7 +446,7 @@ function isPathInDirectory(path, directory) {
438
446
  return true;
439
447
  return path === directory || path.startsWith(directory + '/');
440
448
  }
441
- function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hoveredIndex, selectedIndex, growProgress, animationConfig, heightScaling, linearScale, flatPatterns, staggerIndices, focusDirectory, highlightLayers, isolationMode, defaultBuildingColor, }) {
449
+ function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hoveredIndex, selectedIndex, growProgress, animationConfig, heightScaling, linearScale, flatPatterns, staggerIndices, focusDirectory, highlightLayers, visibilityLayers, isolationMode, defaultBuildingColor, }) {
442
450
  const meshRef = useRef(null);
443
451
  const startTimeRef = useRef(null);
444
452
  const tempObject = useMemo(() => new THREE.Object3D(), []);
@@ -449,10 +457,42 @@ function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hovered
449
457
  // Track dim state for buildings in focus but not highlighted (0 = dimmed, 1 = full)
450
458
  const dimMultipliersRef = useRef(null);
451
459
  const targetDimRef = useRef(null);
452
- // Check if highlight layers have any active items
460
+ // Track which buildings should be hidden entirely (1 = hidden, 0 = visible)
461
+ const hiddenRef = useRef(null);
462
+ // Check if user-supplied highlight layers have any active items. File-color
463
+ // layers don't count — they're decorative and shouldn't trigger isolation.
453
464
  const hasActiveHighlightLayers = useMemo(() => {
454
- return highlightLayers.some(layer => layer.enabled && layer.items.length > 0);
455
- }, [highlightLayers]);
465
+ return visibilityLayers.some(layer => layer.enabled && layer.items.length > 0);
466
+ }, [visibilityLayers]);
467
+ // Directories matched by a directory-type item that also contain a file-type
468
+ // item from any user-supplied layer. Inside these directories, a directory-
469
+ // only match isn't enough to count as "specifically highlighted" — file-level
470
+ // matches narrow the visible set, so unmatched siblings get hidden in 'hide'
471
+ // isolation mode.
472
+ const narrowedDirectories = useMemo(() => {
473
+ const dirs = [];
474
+ const files = [];
475
+ for (const layer of visibilityLayers) {
476
+ if (!layer.enabled)
477
+ continue;
478
+ for (const item of layer.items) {
479
+ if (item.type === 'directory')
480
+ dirs.push(item.path);
481
+ else if (item.type === 'file')
482
+ files.push(item.path);
483
+ }
484
+ }
485
+ const narrowed = new Set();
486
+ for (const dir of dirs) {
487
+ for (const f of files) {
488
+ if (f === dir || f.startsWith(dir + '/')) {
489
+ narrowed.add(dir);
490
+ break;
491
+ }
492
+ }
493
+ }
494
+ return narrowed;
495
+ }, [visibilityLayers]);
456
496
  // Initialize height and dim multiplier arrays
457
497
  useEffect(() => {
458
498
  if (buildings.length > 0) {
@@ -462,39 +502,55 @@ function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hovered
462
502
  targetMultipliersRef.current = new Float32Array(buildings.length).fill(1);
463
503
  dimMultipliersRef.current = new Float32Array(buildings.length).fill(1);
464
504
  targetDimRef.current = new Float32Array(buildings.length).fill(1);
505
+ hiddenRef.current = new Uint8Array(buildings.length);
465
506
  }
466
507
  }
467
508
  }, [buildings.length]);
468
509
  // Update target multipliers when focusDirectory or highlightLayers change
469
510
  useEffect(() => {
470
- if (!targetMultipliersRef.current || !targetDimRef.current)
511
+ if (!targetMultipliersRef.current || !targetDimRef.current || !hiddenRef.current)
471
512
  return;
472
513
  buildings.forEach((building, index) => {
473
514
  let shouldCollapse = false;
474
515
  let shouldDim = false;
516
+ let shouldHide = false;
475
517
  const isInFocusDirectory = focusDirectory
476
518
  ? isPathInDirectory(building.path, focusDirectory)
477
519
  : true; // No focusDirectory means all are "in focus"
520
+ const layerMatches = hasActiveHighlightLayers
521
+ ? getLayerMatchesForPath(building.path, visibilityLayers)
522
+ : [];
478
523
  const isHighlighted = hasActiveHighlightLayers
479
- ? getHighlightForPath(building.path, highlightLayers) !== null
524
+ ? layerMatches.length > 0
480
525
  : true; // No highlights means all are "highlighted"
481
- // Determine collapse and dim behavior based on what's active:
526
+ // For 'hide' mode, a directory-only match doesn't count as highlighted
527
+ // when that directory has been narrowed by file-level matches in some
528
+ // layer — the file-level matches define the visible subset.
529
+ const isSpecificallyHighlighted = hasActiveHighlightLayers
530
+ ? layerMatches.some(m => m.item.type === 'file' || !narrowedDirectories.has(m.item.path))
531
+ : true;
532
+ // Determine collapse/dim/hide behavior based on what's active:
482
533
  // - focusDirectory only: collapse if outside focus
483
534
  // - highlightLayers only (with collapse mode): collapse if not highlighted
484
- // - both: collapse if outside focus, dim if in focus but not highlighted
535
+ // - highlightLayers only (with hide mode): hide if not specifically highlighted
536
+ // - both: collapse if outside focus, dim/hide if in focus but not highlighted
485
537
  if (focusDirectory && hasActiveHighlightLayers && isolationMode === 'collapse') {
486
- // Both active: collapse if outside focus, dim if in focus but not highlighted
487
538
  shouldCollapse = !isInFocusDirectory;
488
539
  shouldDim = isInFocusDirectory && !isHighlighted;
489
540
  }
541
+ else if (focusDirectory && hasActiveHighlightLayers && isolationMode === 'hide') {
542
+ shouldCollapse = !isInFocusDirectory;
543
+ shouldHide = isInFocusDirectory && !isSpecificallyHighlighted;
544
+ }
490
545
  else if (focusDirectory) {
491
- // Focus only: collapse if outside focus directory
492
546
  shouldCollapse = !isInFocusDirectory;
493
547
  }
494
548
  else if (hasActiveHighlightLayers && isolationMode === 'collapse') {
495
- // Highlight only with collapse: collapse if not highlighted
496
549
  shouldCollapse = !isHighlighted;
497
550
  }
551
+ else if (hasActiveHighlightLayers && isolationMode === 'hide') {
552
+ shouldHide = !isSpecificallyHighlighted;
553
+ }
498
554
  // Height: 1.0 = full, 0.05 = flat (collapsed or dimmed)
499
555
  if (shouldCollapse || shouldDim) {
500
556
  targetMultipliersRef.current[index] = 0.05;
@@ -505,8 +561,10 @@ function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hovered
505
561
  // Dim ref controls graying: 0 = gray out, 1 = keep color
506
562
  // Collapsed buildings go gray, dimmed buildings keep their color
507
563
  targetDimRef.current[index] = shouldCollapse ? 0 : 1;
564
+ // Hidden ref controls full invisibility (mesh + edges + icon)
565
+ hiddenRef.current[index] = shouldHide ? 1 : 0;
508
566
  });
509
- }, [focusDirectory, buildings, highlightLayers, isolationMode, hasActiveHighlightLayers]);
567
+ }, [focusDirectory, buildings, visibilityLayers, isolationMode, hasActiveHighlightLayers, narrowedDirectories]);
510
568
  // Pre-compute building data
511
569
  const buildingData = useMemo(() => {
512
570
  return buildings.map((building, index) => {
@@ -588,6 +646,14 @@ function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hovered
588
646
  const collapseSpeed = 0.08;
589
647
  buildingData.forEach((data, instanceIndex) => {
590
648
  const { width, depth, fullHeight, x, z, staggerDelayMs } = data;
649
+ const isHidden = hiddenRef.current?.[instanceIndex] === 1;
650
+ if (isHidden) {
651
+ tempObject.position.set(x, baseOffset, z);
652
+ tempObject.scale.set(0, 0, 0);
653
+ tempObject.updateMatrix();
654
+ meshRef.current.setMatrixAt(instanceIndex, tempObject.matrix);
655
+ return;
656
+ }
591
657
  // Animate height multiplier towards target
592
658
  const currentMultiplier = heightMultipliersRef.current[instanceIndex];
593
659
  const targetMultiplier = targetMultipliersRef.current[instanceIndex];
@@ -671,7 +737,7 @@ function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hovered
671
737
  z: d.z,
672
738
  staggerDelayMs: d.staggerDelayMs,
673
739
  buildingIndex: d.index,
674
- })), growProgress: growProgress, minHeight: minHeight, baseOffset: baseOffset, springDuration: springDuration, heightMultipliersRef: heightMultipliersRef }), _jsx(BorderHighlights, { buildings: buildings, centerOffset: centerOffset, highlightLayers: highlightLayers, growProgress: growProgress, minHeight: minHeight, baseOffset: baseOffset, springDuration: springDuration, heightMultipliersRef: heightMultipliersRef, heightScaling: heightScaling, linearScale: linearScale, flatPatterns: flatPatterns, staggerIndices: staggerIndices, animationConfig: animationConfig })] }));
740
+ })), growProgress: growProgress, minHeight: minHeight, baseOffset: baseOffset, springDuration: springDuration, heightMultipliersRef: heightMultipliersRef, hiddenRef: hiddenRef }), _jsx(BorderHighlights, { buildings: buildings, centerOffset: centerOffset, highlightLayers: highlightLayers, growProgress: growProgress, minHeight: minHeight, baseOffset: baseOffset, springDuration: springDuration, heightMultipliersRef: heightMultipliersRef, heightScaling: heightScaling, linearScale: linearScale, flatPatterns: flatPatterns, staggerIndices: staggerIndices, animationConfig: animationConfig })] }));
675
741
  }
676
742
  function AnimatedIcon({ x, z, targetHeight, iconSize, texture, opacity, growProgress, }) {
677
743
  const meshRef = useRef(null);
@@ -699,7 +765,33 @@ function AnimatedIcon({ x, z, targetHeight, iconSize, texture, opacity, growProg
699
765
  });
700
766
  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 })] }));
701
767
  }
702
- function BuildingIcons({ buildings, centerOffset, growProgress, heightScaling, linearScale, flatPatterns, highlightLayers, isolationMode, hasActiveHighlights, }) {
768
+ function BuildingIcons({ buildings, centerOffset, growProgress, heightScaling, linearScale, flatPatterns, highlightLayers, visibilityLayers, isolationMode, hasActiveHighlights, }) {
769
+ // Same narrowing rule as InstancedBuildings, scoped to user highlight layers
770
+ // only (file-color layers don't narrow visibility).
771
+ const narrowedDirectories = useMemo(() => {
772
+ const dirs = [];
773
+ const files = [];
774
+ for (const layer of visibilityLayers) {
775
+ if (!layer.enabled)
776
+ continue;
777
+ for (const item of layer.items) {
778
+ if (item.type === 'directory')
779
+ dirs.push(item.path);
780
+ else if (item.type === 'file')
781
+ files.push(item.path);
782
+ }
783
+ }
784
+ const narrowed = new Set();
785
+ for (const dir of dirs) {
786
+ for (const f of files) {
787
+ if (f === dir || f.startsWith(dir + '/')) {
788
+ narrowed.add(dir);
789
+ break;
790
+ }
791
+ }
792
+ }
793
+ return narrowed;
794
+ }, [visibilityLayers]);
703
795
  // Pre-compute buildings with icons
704
796
  const buildingsWithIcons = useMemo(() => {
705
797
  return buildings
@@ -707,10 +799,11 @@ function BuildingIcons({ buildings, centerOffset, growProgress, heightScaling, l
707
799
  const config = getConfigForFile(building);
708
800
  if (!config.icon)
709
801
  return null;
710
- const highlight = getHighlightForPath(building.path, highlightLayers);
711
- const isHighlighted = highlight !== null;
802
+ const matches = getLayerMatchesForPath(building.path, visibilityLayers);
803
+ const isHighlighted = matches.length > 0;
804
+ const isSpecificallyHighlighted = matches.some(m => m.item.type === 'file' || !narrowedDirectories.has(m.item.path));
712
805
  const shouldDim = hasActiveHighlights && !isHighlighted;
713
- const shouldHide = shouldDim && isolationMode === 'hide';
806
+ const shouldHide = hasActiveHighlights && isolationMode === 'hide' && !isSpecificallyHighlighted;
714
807
  const shouldCollapse = shouldDim && isolationMode === 'collapse';
715
808
  // Hide icons for buildings that are hidden or collapsed
716
809
  if (shouldHide || shouldCollapse)
@@ -732,12 +825,13 @@ function BuildingIcons({ buildings, centerOffset, growProgress, heightScaling, l
732
825
  }, [
733
826
  buildings,
734
827
  centerOffset,
735
- highlightLayers,
828
+ visibilityLayers,
736
829
  isolationMode,
737
830
  hasActiveHighlights,
738
831
  heightScaling,
739
832
  linearScale,
740
833
  flatPatterns,
834
+ narrowedDirectories,
741
835
  ]);
742
836
  // Icons are now always rendered (flat or grown)
743
837
  return (_jsx(_Fragment, { children: buildingsWithIcons.map(({ building, config, x, z, targetHeight, shouldDim }) => {
@@ -777,7 +871,7 @@ function DistrictFloor({ district, centerOffset, highlightColor, growProgress })
777
871
  const flatZ = depth / 2 - 6; // Near bottom of district when flat, with padding
778
872
  const grownZ = depth / 2 + 2; // Just outside edge when grown
779
873
  const textZ = flatZ + (grownZ - flatZ) * growProgress;
780
- return (_jsxs("group", { position: [centerX, 0, centerZ], children: [_jsxs("lineSegments", { rotation: [-Math.PI / 2, 0, 0], position: [0, floorY, 0], renderOrder: -1, children: [_jsx("edgesGeometry", { args: [new THREE.PlaneGeometry(width, depth)], attach: "geometry" }), _jsx("lineBasicMaterial", { color: borderColor, linewidth: lineWidth, depthWrite: false })] }), highlightColor && (_jsxs("mesh", { rotation: [-Math.PI / 2, 0, 0], position: [0, floorY - 0.1, 0], renderOrder: -2, children: [_jsx("planeGeometry", { args: [width, depth] }), _jsx("meshBasicMaterial", { color: highlightColor, transparent: true, opacity: 0.15, depthWrite: false })] })), _jsx(Text, { position: [0, textY, textZ], rotation: [textRotationX, 0, 0], fontSize: Math.max(6, Math.min(12, width / 3)), color: labelColor, anchorX: "center", anchorY: "middle", outlineWidth: 0.15, outlineColor: "#0f172a", children: dirName })] }));
874
+ return (_jsxs("group", { position: [centerX, 0, centerZ], children: [_jsxs("lineSegments", { rotation: [-Math.PI / 2, 0, 0], position: [0, floorY, 0], renderOrder: -1, children: [_jsx("edgesGeometry", { args: [new THREE.PlaneGeometry(width, depth)], attach: "geometry" }), _jsx("lineBasicMaterial", { color: borderColor, linewidth: lineWidth, depthWrite: false })] }), _jsx(Text, { position: [0, textY, textZ], rotation: [textRotationX, 0, 0], fontSize: Math.max(6, Math.min(12, width / 3)), color: labelColor, anchorX: "center", anchorY: "middle", outlineWidth: 0.15, outlineColor: "#0f172a", children: dirName })] }));
781
875
  }
782
876
  export const DEFAULT_CAMERA_CONTROLS = {
783
877
  leftDrag: 'pan',
@@ -957,7 +1051,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
957
1051
  // Calculate initial position with default aspect ratio
958
1052
  // This will be corrected in Frame 1 if aspect is different
959
1053
  const initialHeight = calculateFlatCameraHeight(1);
960
- console.log('[Spring init] Initializing with 2D position, height:', initialHeight);
961
1054
  return {
962
1055
  camX: 0,
963
1056
  camY: initialHeight,
@@ -967,17 +1060,11 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
967
1060
  lookZ: 0,
968
1061
  config: { tension: 60, friction: 20 },
969
1062
  onStart: () => {
970
- // Only allow animations after initial setup is complete
971
1063
  if (hasAppliedInitial.current) {
972
- console.log('[Spring onStart] Animation starting - camY:', camY.get());
973
1064
  isAnimatingRef.current = true;
974
1065
  }
975
- else {
976
- console.log('[Spring onStart] Blocked - initialization not complete');
977
- }
978
1066
  },
979
1067
  onRest: () => {
980
- console.log('[Spring onRest] Animation finished');
981
1068
  isAnimatingRef.current = false;
982
1069
  },
983
1070
  };
@@ -1012,19 +1099,11 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1012
1099
  // When isFlat changes from true to false, animate to 3D view
1013
1100
  // Component always starts in 2D, so we only animate the 2D→3D transition
1014
1101
  useEffect(() => {
1015
- console.log('[useEffect] isFlat:', isFlat, 'prevIsFlat:', prevIsFlatRef.current, 'hasAppliedInitial:', hasAppliedInitial.current);
1016
- // Skip until camera is initialized
1017
- if (!hasAppliedInitial.current) {
1018
- console.log('[useEffect] Skipping - not initialized yet');
1102
+ if (!hasAppliedInitial.current)
1019
1103
  return;
1020
- }
1021
- // Only animate if isFlat changed from true to false (2D → 3D transition)
1022
1104
  const isFlatChanged = prevIsFlatRef.current !== isFlat;
1023
- if (!isFlatChanged) {
1024
- console.log('[useEffect] No isFlat change - skipping');
1105
+ if (!isFlatChanged)
1025
1106
  return;
1026
- }
1027
- console.log('[useEffect] isFlat changed from', prevIsFlatRef.current, 'to', isFlat, '- animating transition');
1028
1107
  prevIsFlatRef.current = isFlat;
1029
1108
  // Calculate target position for 3D view
1030
1109
  const newPos = isFlat
@@ -1046,7 +1125,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1046
1125
  targetY: 0,
1047
1126
  targetZ: 0,
1048
1127
  };
1049
- console.log('[api.start#isFlat-toggle]', newPos);
1050
1128
  api.start({
1051
1129
  camX: newPos.x,
1052
1130
  camY: newPos.y,
@@ -1109,7 +1187,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1109
1187
  targetZ: 0,
1110
1188
  };
1111
1189
  }
1112
- console.log('[api.start#focus-target]', { focusTarget, isFlat, newPos });
1113
1190
  api.start({
1114
1191
  camX: newPos.x,
1115
1192
  camY: newPos.y,
@@ -1122,7 +1199,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1122
1199
  }, [focusTarget, isFlat]);
1123
1200
  // Update camera each frame
1124
1201
  useFrame(() => {
1125
- var _a;
1126
1202
  frameCount.current++;
1127
1203
  // On Frame 1: Set camera to initial 2D position and mark as ready
1128
1204
  // Component always starts in 2D mode, so we just need to set the correct position once
@@ -1130,20 +1206,17 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1130
1206
  // Ensure camera FOV is correct (defaults to 75 before prop applies)
1131
1207
  const perspCam = camera;
1132
1208
  if (perspCam.fov !== 50) {
1133
- console.log('[Frame 1] Correcting FOV from', perspCam.fov, 'to 50');
1134
1209
  perspCam.fov = 50;
1135
1210
  perspCam.updateProjectionMatrix();
1136
1211
  }
1137
1212
  // Calculate initial 2D position with correct aspect ratio
1138
1213
  const initialPos = getInitial2DPosition();
1139
- console.log('[Frame 1] Setting camera to initial 2D position:', initialPos);
1140
1214
  camera.position.set(initialPos.x, initialPos.y, initialPos.z);
1141
1215
  // Wait for controls to be ready, then set target and sync spring
1142
1216
  if (controlsRef.current) {
1143
1217
  controlsRef.current.target.set(initialPos.targetX, initialPos.targetY, initialPos.targetZ);
1144
1218
  controlsRef.current.update();
1145
1219
  // Sync spring to match camera position (use immediate to avoid animation)
1146
- console.log('[api.start#frame1-immediate]', initialPos);
1147
1220
  api.start({
1148
1221
  camX: initialPos.x,
1149
1222
  camY: initialPos.y,
@@ -1165,28 +1238,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1165
1238
  // Wait for controls and initialization to complete
1166
1239
  if (!controlsRef.current || !hasAppliedInitial.current)
1167
1240
  return;
1168
- // [debug] log which branch runs each frame so consumers can identify
1169
- // unexpected camera mutation when no api.start fired.
1170
- if (isOrbitingRef.current ||
1171
- isTiltingRef.current ||
1172
- isAnimatingRef.current) {
1173
- const branch = isOrbitingRef.current
1174
- ? 'orbit'
1175
- : isTiltingRef.current
1176
- ? 'tilt'
1177
- : 'animating';
1178
- const w = (_a = globalThis).__fileCityFrameLog ?? (_a.__fileCityFrameLog = new Map());
1179
- const last = w.get(branch) ?? 0;
1180
- const now = performance.now();
1181
- if (now - last > 200) {
1182
- w.set(branch, now);
1183
- // eslint-disable-next-line no-console
1184
- console.log(`[useFrame#${branch}]`, {
1185
- camY: camY.get(),
1186
- posY: camera.position.y,
1187
- });
1188
- }
1189
- }
1190
1241
  // Handle orbit animation (horizontal rotation along arc)
1191
1242
  if (isOrbitingRef.current && orbitParamsRef.current) {
1192
1243
  const { centerX, centerZ, distance, height } = orbitParamsRef.current;
@@ -1244,7 +1295,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1244
1295
  const resetToInitial = useCallback(() => {
1245
1296
  const targetHeight = citySize * 1.1;
1246
1297
  const targetZ = citySize * 1.3;
1247
- console.log('[api.start#resetToInitial]', { targetHeight, targetZ });
1248
1298
  api.start({
1249
1299
  camX: 0,
1250
1300
  camY: targetHeight,
@@ -1258,7 +1308,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1258
1308
  const effectiveSize = size ?? citySize * 0.3;
1259
1309
  const distance = Math.max(effectiveSize * 2, 50);
1260
1310
  const height = Math.max(effectiveSize * 1.5, 40);
1261
- console.log('[api.start#moveTo]', { x, z, height, distance });
1262
1311
  api.start({
1263
1312
  camX: x,
1264
1313
  camY: height,
@@ -1288,7 +1337,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1288
1337
  });
1289
1338
  }
1290
1339
  const config = options?.duration ? { duration: options.duration } : undefined;
1291
- console.log('[api.start#setFlatView]', { x, z, height, options });
1292
1340
  api.start({
1293
1341
  camX: x,
1294
1342
  camY: height,
@@ -1316,7 +1364,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1316
1364
  const animConfig = options?.duration
1317
1365
  ? { duration: options.duration, easing: (t) => t }
1318
1366
  : { tension: 60, friction: 20 };
1319
- console.log('[api.start#setTarget]', { x, y, z, newCamX, newCamY, newCamZ });
1320
1367
  api.start({
1321
1368
  camX: newCamX,
1322
1369
  camY: newCamY,
@@ -1764,7 +1811,7 @@ function SelectionRing({ district, centerOffset, color, borderWidth, growProgres
1764
1811
  const barH = 0.5;
1765
1812
  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 })] })] }));
1766
1813
  }
1767
- 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, }) {
1814
+ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding, selectedBuilding, selectedDistrict, selectionStyle, growProgress, animationConfig, highlightLayers, visibilityLayers, isolationMode, heightScaling, linearScale, flatPatterns, focusDirectory, focusColor, adaptCameraToBuildings = false, elevatedScopePanels, dismissingPanelIds, onPanelDismissed, cameraControls, defaultBuildingColor, onCameraReady, }) {
1768
1815
  const centerOffset = useMemo(() => ({
1769
1816
  x: (cityData.bounds.minX + cityData.bounds.maxX) / 2,
1770
1817
  z: (cityData.bounds.minZ + cityData.bounds.maxZ) / 2,
@@ -1776,7 +1823,7 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
1776
1823
  return 0;
1777
1824
  return Math.max(...cityData.buildings.map(b => b.dimensions[1]), 0);
1778
1825
  }, [adaptCameraToBuildings, cityData.buildings]);
1779
- const activeHighlights = useMemo(() => hasActiveHighlights(highlightLayers), [highlightLayers]);
1826
+ const activeHighlights = useMemo(() => hasActiveHighlights(visibilityLayers), [visibilityLayers]);
1780
1827
  // Helper to check if a path is inside a directory
1781
1828
  const isPathInDirectory = useCallback((path, directory) => {
1782
1829
  if (!directory)
@@ -1968,7 +2015,7 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
1968
2015
  // Focus color takes priority, then highlight layer color
1969
2016
  const districtColor = (isFocused && buildingFocusColor) ? buildingFocusColor : highlightLayerColor;
1970
2017
  return (_jsx(DistrictFloor, { district: district, centerOffset: centerOffset, opacity: 1, highlightColor: districtColor, growProgress: growProgress }, district.path));
1971
- }), _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 &&
2018
+ }), _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, visibilityLayers: visibilityLayers, isolationMode: isolationMode, defaultBuildingColor: defaultBuildingColor }), _jsx(BuildingIcons, { buildings: cityData.buildings, centerOffset: centerOffset, growProgress: growProgress, heightScaling: heightScaling, linearScale: linearScale, flatPatterns: flatPatterns, highlightLayers: highlightLayers, visibilityLayers: visibilityLayers, isolationMode: isolationMode, hasActiveHighlights: activeHighlights }), growProgress === 0 &&
1972
2019
  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 }))] }));
1973
2020
  }
1974
2021
  /**
@@ -2018,6 +2065,9 @@ export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingC
2018
2065
  const focusDirectory = resolved.focusDirectory;
2019
2066
  const focusColor = resolved.focusColor;
2020
2067
  const isolationMode = resolved.isolationMode;
2068
+ // User-supplied highlight layers only — used for visibility decisions so
2069
+ // file-color layers don't keep every building visible in 'hide' mode.
2070
+ const visibilityLayers = useMemo(() => (externalHighlightLayers ?? []), [externalHighlightLayers]);
2021
2071
  // `selectedPath` wins over the deprecated `selectedBuilding` when both are
2022
2072
  // set. A path resolves to either a building (file selection) or a district
2023
2073
  // (directory selection) — never both.
@@ -2101,6 +2151,6 @@ export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingC
2101
2151
  height: '100%',
2102
2152
  opacity: cameraReady ? 1 : 0,
2103
2153
  transition: 'opacity 0.1s ease-in',
2104
- }, 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) }))] }));
2154
+ }, 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, visibilityLayers: visibilityLayers, 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) }))] }));
2105
2155
  }
2106
2156
  export default FileCity3D;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@principal-ai/file-city-react",
3
- "version": "0.5.49",
3
+ "version": "0.5.51",
4
4
  "type": "module",
5
5
  "description": "React components for File City visualization",
6
6
  "main": "dist/index.js",
@@ -409,6 +409,7 @@ interface BuildingEdgesProps {
409
409
  baseOffset: number;
410
410
  springDuration: number;
411
411
  heightMultipliersRef: React.MutableRefObject<Float32Array | null>;
412
+ hiddenRef?: React.MutableRefObject<Uint8Array | null>;
412
413
  }
413
414
 
414
415
  function BuildingEdges({
@@ -418,6 +419,7 @@ function BuildingEdges({
418
419
  baseOffset,
419
420
  springDuration,
420
421
  heightMultipliersRef,
422
+ hiddenRef,
421
423
  }: BuildingEdgesProps) {
422
424
  const meshRef = useRef<THREE.InstancedMesh>(null);
423
425
  const startTimeRef = useRef<number | null>(null);
@@ -456,6 +458,16 @@ function BuildingEdges({
456
458
  edgeData.forEach((edge, idx) => {
457
459
  const { x, z, fullHeight, staggerDelayMs, buildingIndex } = edge;
458
460
 
461
+ const isHidden = hiddenRef?.current?.[buildingIndex] === 1;
462
+
463
+ if (isHidden) {
464
+ tempObject.position.set(x, baseOffset, z);
465
+ tempObject.scale.set(0, 0, 0);
466
+ tempObject.updateMatrix();
467
+ meshRef.current!.setMatrixAt(idx, tempObject.matrix);
468
+ return;
469
+ }
470
+
459
471
  // Get height multiplier from shared ref (for collapse animation)
460
472
  const heightMultiplier = heightMultipliersRef.current?.[buildingIndex] ?? 1;
461
473
 
@@ -759,7 +771,10 @@ interface InstancedBuildingsProps {
759
771
  flatPatterns: FlatPattern[];
760
772
  staggerIndices: number[];
761
773
  focusDirectory: string | null;
774
+ /** Combined layers (user highlights + filtered file-color layers) — used for fill colors. */
762
775
  highlightLayers: HighlightLayer[];
776
+ /** User-supplied highlight layers only (no file-color layers) — used for visibility decisions. */
777
+ visibilityLayers: HighlightLayer[];
763
778
  isolationMode: IsolationMode;
764
779
  defaultBuildingColor?: string;
765
780
  }
@@ -785,6 +800,7 @@ function InstancedBuildings({
785
800
  staggerIndices,
786
801
  focusDirectory,
787
802
  highlightLayers,
803
+ visibilityLayers,
788
804
  isolationMode,
789
805
  defaultBuildingColor,
790
806
  }: InstancedBuildingsProps) {
@@ -799,11 +815,41 @@ function InstancedBuildings({
799
815
  // Track dim state for buildings in focus but not highlighted (0 = dimmed, 1 = full)
800
816
  const dimMultipliersRef = useRef<Float32Array | null>(null);
801
817
  const targetDimRef = useRef<Float32Array | null>(null);
818
+ // Track which buildings should be hidden entirely (1 = hidden, 0 = visible)
819
+ const hiddenRef = useRef<Uint8Array | null>(null);
802
820
 
803
- // Check if highlight layers have any active items
821
+ // Check if user-supplied highlight layers have any active items. File-color
822
+ // layers don't count — they're decorative and shouldn't trigger isolation.
804
823
  const hasActiveHighlightLayers = useMemo(() => {
805
- return highlightLayers.some(layer => layer.enabled && layer.items.length > 0);
806
- }, [highlightLayers]);
824
+ return visibilityLayers.some(layer => layer.enabled && layer.items.length > 0);
825
+ }, [visibilityLayers]);
826
+
827
+ // Directories matched by a directory-type item that also contain a file-type
828
+ // item from any user-supplied layer. Inside these directories, a directory-
829
+ // only match isn't enough to count as "specifically highlighted" — file-level
830
+ // matches narrow the visible set, so unmatched siblings get hidden in 'hide'
831
+ // isolation mode.
832
+ const narrowedDirectories = useMemo(() => {
833
+ const dirs: string[] = [];
834
+ const files: string[] = [];
835
+ for (const layer of visibilityLayers) {
836
+ if (!layer.enabled) continue;
837
+ for (const item of layer.items) {
838
+ if (item.type === 'directory') dirs.push(item.path);
839
+ else if (item.type === 'file') files.push(item.path);
840
+ }
841
+ }
842
+ const narrowed = new Set<string>();
843
+ for (const dir of dirs) {
844
+ for (const f of files) {
845
+ if (f === dir || f.startsWith(dir + '/')) {
846
+ narrowed.add(dir);
847
+ break;
848
+ }
849
+ }
850
+ }
851
+ return narrowed;
852
+ }, [visibilityLayers]);
807
853
 
808
854
  // Initialize height and dim multiplier arrays
809
855
  useEffect(() => {
@@ -816,40 +862,57 @@ function InstancedBuildings({
816
862
  targetMultipliersRef.current = new Float32Array(buildings.length).fill(1);
817
863
  dimMultipliersRef.current = new Float32Array(buildings.length).fill(1);
818
864
  targetDimRef.current = new Float32Array(buildings.length).fill(1);
865
+ hiddenRef.current = new Uint8Array(buildings.length);
819
866
  }
820
867
  }
821
868
  }, [buildings.length]);
822
869
 
823
870
  // Update target multipliers when focusDirectory or highlightLayers change
824
871
  useEffect(() => {
825
- if (!targetMultipliersRef.current || !targetDimRef.current) return;
872
+ if (!targetMultipliersRef.current || !targetDimRef.current || !hiddenRef.current) return;
826
873
 
827
874
  buildings.forEach((building, index) => {
828
875
  let shouldCollapse = false;
829
876
  let shouldDim = false;
877
+ let shouldHide = false;
830
878
 
831
879
  const isInFocusDirectory = focusDirectory
832
880
  ? isPathInDirectory(building.path, focusDirectory)
833
881
  : true; // No focusDirectory means all are "in focus"
834
882
 
883
+ const layerMatches = hasActiveHighlightLayers
884
+ ? getLayerMatchesForPath(building.path, visibilityLayers)
885
+ : [];
835
886
  const isHighlighted = hasActiveHighlightLayers
836
- ? getHighlightForPath(building.path, highlightLayers) !== null
887
+ ? layerMatches.length > 0
837
888
  : true; // No highlights means all are "highlighted"
838
889
 
839
- // Determine collapse and dim behavior based on what's active:
890
+ // For 'hide' mode, a directory-only match doesn't count as highlighted
891
+ // when that directory has been narrowed by file-level matches in some
892
+ // layer — the file-level matches define the visible subset.
893
+ const isSpecificallyHighlighted = hasActiveHighlightLayers
894
+ ? layerMatches.some(m =>
895
+ m.item.type === 'file' || !narrowedDirectories.has(m.item.path),
896
+ )
897
+ : true;
898
+
899
+ // Determine collapse/dim/hide behavior based on what's active:
840
900
  // - focusDirectory only: collapse if outside focus
841
901
  // - highlightLayers only (with collapse mode): collapse if not highlighted
842
- // - both: collapse if outside focus, dim if in focus but not highlighted
902
+ // - highlightLayers only (with hide mode): hide if not specifically highlighted
903
+ // - both: collapse if outside focus, dim/hide if in focus but not highlighted
843
904
  if (focusDirectory && hasActiveHighlightLayers && isolationMode === 'collapse') {
844
- // Both active: collapse if outside focus, dim if in focus but not highlighted
845
905
  shouldCollapse = !isInFocusDirectory;
846
906
  shouldDim = isInFocusDirectory && !isHighlighted;
907
+ } else if (focusDirectory && hasActiveHighlightLayers && isolationMode === 'hide') {
908
+ shouldCollapse = !isInFocusDirectory;
909
+ shouldHide = isInFocusDirectory && !isSpecificallyHighlighted;
847
910
  } else if (focusDirectory) {
848
- // Focus only: collapse if outside focus directory
849
911
  shouldCollapse = !isInFocusDirectory;
850
912
  } else if (hasActiveHighlightLayers && isolationMode === 'collapse') {
851
- // Highlight only with collapse: collapse if not highlighted
852
913
  shouldCollapse = !isHighlighted;
914
+ } else if (hasActiveHighlightLayers && isolationMode === 'hide') {
915
+ shouldHide = !isSpecificallyHighlighted;
853
916
  }
854
917
 
855
918
  // Height: 1.0 = full, 0.05 = flat (collapsed or dimmed)
@@ -861,8 +924,10 @@ function InstancedBuildings({
861
924
  // Dim ref controls graying: 0 = gray out, 1 = keep color
862
925
  // Collapsed buildings go gray, dimmed buildings keep their color
863
926
  targetDimRef.current![index] = shouldCollapse ? 0 : 1;
927
+ // Hidden ref controls full invisibility (mesh + edges + icon)
928
+ hiddenRef.current![index] = shouldHide ? 1 : 0;
864
929
  });
865
- }, [focusDirectory, buildings, highlightLayers, isolationMode, hasActiveHighlightLayers]);
930
+ }, [focusDirectory, buildings, visibilityLayers, isolationMode, hasActiveHighlightLayers, narrowedDirectories]);
866
931
 
867
932
  // Pre-compute building data
868
933
  const buildingData = useMemo(() => {
@@ -962,6 +1027,16 @@ function InstancedBuildings({
962
1027
  buildingData.forEach((data, instanceIndex) => {
963
1028
  const { width, depth, fullHeight, x, z, staggerDelayMs } = data;
964
1029
 
1030
+ const isHidden = hiddenRef.current?.[instanceIndex] === 1;
1031
+
1032
+ if (isHidden) {
1033
+ tempObject.position.set(x, baseOffset, z);
1034
+ tempObject.scale.set(0, 0, 0);
1035
+ tempObject.updateMatrix();
1036
+ meshRef.current!.setMatrixAt(instanceIndex, tempObject.matrix);
1037
+ return;
1038
+ }
1039
+
965
1040
  // Animate height multiplier towards target
966
1041
  const currentMultiplier = heightMultipliersRef.current![instanceIndex];
967
1042
  const targetMultiplier = targetMultipliersRef.current![instanceIndex];
@@ -1089,6 +1164,7 @@ function InstancedBuildings({
1089
1164
  baseOffset={baseOffset}
1090
1165
  springDuration={springDuration}
1091
1166
  heightMultipliersRef={heightMultipliersRef}
1167
+ hiddenRef={hiddenRef}
1092
1168
  />
1093
1169
 
1094
1170
  {/* Border highlights (colored, layer-driven) */}
@@ -1123,6 +1199,8 @@ interface BuildingIconsProps {
1123
1199
  linearScale: number;
1124
1200
  flatPatterns: FlatPattern[];
1125
1201
  highlightLayers: HighlightLayer[];
1202
+ /** User-supplied highlight layers only (excludes file-color layers). */
1203
+ visibilityLayers: HighlightLayer[];
1126
1204
  isolationMode: IsolationMode;
1127
1205
  hasActiveHighlights: boolean;
1128
1206
  }
@@ -1205,9 +1283,34 @@ function BuildingIcons({
1205
1283
  linearScale,
1206
1284
  flatPatterns,
1207
1285
  highlightLayers,
1286
+ visibilityLayers,
1208
1287
  isolationMode,
1209
1288
  hasActiveHighlights,
1210
1289
  }: BuildingIconsProps) {
1290
+ // Same narrowing rule as InstancedBuildings, scoped to user highlight layers
1291
+ // only (file-color layers don't narrow visibility).
1292
+ const narrowedDirectories = useMemo(() => {
1293
+ const dirs: string[] = [];
1294
+ const files: string[] = [];
1295
+ for (const layer of visibilityLayers) {
1296
+ if (!layer.enabled) continue;
1297
+ for (const item of layer.items) {
1298
+ if (item.type === 'directory') dirs.push(item.path);
1299
+ else if (item.type === 'file') files.push(item.path);
1300
+ }
1301
+ }
1302
+ const narrowed = new Set<string>();
1303
+ for (const dir of dirs) {
1304
+ for (const f of files) {
1305
+ if (f === dir || f.startsWith(dir + '/')) {
1306
+ narrowed.add(dir);
1307
+ break;
1308
+ }
1309
+ }
1310
+ }
1311
+ return narrowed;
1312
+ }, [visibilityLayers]);
1313
+
1211
1314
  // Pre-compute buildings with icons
1212
1315
  const buildingsWithIcons = useMemo(() => {
1213
1316
  return buildings
@@ -1215,10 +1318,14 @@ function BuildingIcons({
1215
1318
  const config = getConfigForFile(building);
1216
1319
  if (!config.icon) return null;
1217
1320
 
1218
- const highlight = getHighlightForPath(building.path, highlightLayers);
1219
- const isHighlighted = highlight !== null;
1321
+ const matches = getLayerMatchesForPath(building.path, visibilityLayers);
1322
+ const isHighlighted = matches.length > 0;
1323
+ const isSpecificallyHighlighted = matches.some(
1324
+ m => m.item.type === 'file' || !narrowedDirectories.has(m.item.path),
1325
+ );
1220
1326
  const shouldDim = hasActiveHighlights && !isHighlighted;
1221
- const shouldHide = shouldDim && isolationMode === 'hide';
1327
+ const shouldHide =
1328
+ hasActiveHighlights && isolationMode === 'hide' && !isSpecificallyHighlighted;
1222
1329
  const shouldCollapse = shouldDim && isolationMode === 'collapse';
1223
1330
 
1224
1331
  // Hide icons for buildings that are hidden or collapsed
@@ -1250,12 +1357,13 @@ function BuildingIcons({
1250
1357
  }, [
1251
1358
  buildings,
1252
1359
  centerOffset,
1253
- highlightLayers,
1360
+ visibilityLayers,
1254
1361
  isolationMode,
1255
1362
  hasActiveHighlights,
1256
1363
  heightScaling,
1257
1364
  linearScale,
1258
1365
  flatPatterns,
1366
+ narrowedDirectories,
1259
1367
  ]);
1260
1368
 
1261
1369
  // Icons are now always rendered (flat or grown)
@@ -1340,14 +1448,6 @@ function DistrictFloor({ district, centerOffset, highlightColor, growProgress }:
1340
1448
  <lineBasicMaterial color={borderColor} linewidth={lineWidth} depthWrite={false} />
1341
1449
  </lineSegments>
1342
1450
 
1343
- {/* Highlighted floor fill when focused */}
1344
- {highlightColor && (
1345
- <mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, floorY - 0.1, 0]} renderOrder={-2}>
1346
- <planeGeometry args={[width, depth]} />
1347
- <meshBasicMaterial color={highlightColor} transparent opacity={0.15} depthWrite={false} />
1348
- </mesh>
1349
- )}
1350
-
1351
1451
  {/* Always show directory name label */}
1352
1452
  <Text
1353
1453
  position={[0, textY, textZ]}
@@ -1658,7 +1758,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
1658
1758
  // This will be corrected in Frame 1 if aspect is different
1659
1759
  const initialHeight = calculateFlatCameraHeight(1);
1660
1760
 
1661
- console.log('[Spring init] Initializing with 2D position, height:', initialHeight);
1662
1761
  return {
1663
1762
  camX: 0,
1664
1763
  camY: initialHeight,
@@ -1668,16 +1767,11 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
1668
1767
  lookZ: 0,
1669
1768
  config: { tension: 60, friction: 20 },
1670
1769
  onStart: () => {
1671
- // Only allow animations after initial setup is complete
1672
1770
  if (hasAppliedInitial.current) {
1673
- console.log('[Spring onStart] Animation starting - camY:', camY.get());
1674
1771
  isAnimatingRef.current = true;
1675
- } else {
1676
- console.log('[Spring onStart] Blocked - initialization not complete');
1677
1772
  }
1678
1773
  },
1679
1774
  onRest: () => {
1680
- console.log('[Spring onRest] Animation finished');
1681
1775
  isAnimatingRef.current = false;
1682
1776
  },
1683
1777
  };
@@ -1728,23 +1822,11 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
1728
1822
  // When isFlat changes from true to false, animate to 3D view
1729
1823
  // Component always starts in 2D, so we only animate the 2D→3D transition
1730
1824
  useEffect(() => {
1731
- console.log('[useEffect] isFlat:', isFlat, 'prevIsFlat:', prevIsFlatRef.current, 'hasAppliedInitial:', hasAppliedInitial.current);
1732
-
1733
- // Skip until camera is initialized
1734
- if (!hasAppliedInitial.current) {
1735
- console.log('[useEffect] Skipping - not initialized yet');
1736
- return;
1737
- }
1825
+ if (!hasAppliedInitial.current) return;
1738
1826
 
1739
- // Only animate if isFlat changed from true to false (2D → 3D transition)
1740
1827
  const isFlatChanged = prevIsFlatRef.current !== isFlat;
1828
+ if (!isFlatChanged) return;
1741
1829
 
1742
- if (!isFlatChanged) {
1743
- console.log('[useEffect] No isFlat change - skipping');
1744
- return;
1745
- }
1746
-
1747
- console.log('[useEffect] isFlat changed from', prevIsFlatRef.current, 'to', isFlat, '- animating transition');
1748
1830
  prevIsFlatRef.current = isFlat;
1749
1831
 
1750
1832
  // Calculate target position for 3D view
@@ -1768,7 +1850,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
1768
1850
  targetZ: 0,
1769
1851
  };
1770
1852
 
1771
- console.log('[api.start#isFlat-toggle]', newPos);
1772
1853
  api.start({
1773
1854
  camX: newPos.x,
1774
1855
  camY: newPos.y,
@@ -1838,7 +1919,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
1838
1919
  };
1839
1920
  }
1840
1921
 
1841
- console.log('[api.start#focus-target]', { focusTarget, isFlat, newPos });
1842
1922
  api.start({
1843
1923
  camX: newPos.x,
1844
1924
  camY: newPos.y,
@@ -1860,14 +1940,12 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
1860
1940
  // Ensure camera FOV is correct (defaults to 75 before prop applies)
1861
1941
  const perspCam = camera as THREE.PerspectiveCamera;
1862
1942
  if (perspCam.fov !== 50) {
1863
- console.log('[Frame 1] Correcting FOV from', perspCam.fov, 'to 50');
1864
1943
  perspCam.fov = 50;
1865
1944
  perspCam.updateProjectionMatrix();
1866
1945
  }
1867
1946
 
1868
1947
  // Calculate initial 2D position with correct aspect ratio
1869
1948
  const initialPos = getInitial2DPosition();
1870
- console.log('[Frame 1] Setting camera to initial 2D position:', initialPos);
1871
1949
 
1872
1950
  camera.position.set(initialPos.x, initialPos.y, initialPos.z);
1873
1951
 
@@ -1877,7 +1955,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
1877
1955
  controlsRef.current.update();
1878
1956
 
1879
1957
  // Sync spring to match camera position (use immediate to avoid animation)
1880
- console.log('[api.start#frame1-immediate]', initialPos);
1881
1958
  api.start({
1882
1959
  camX: initialPos.x,
1883
1960
  camY: initialPos.y,
@@ -1902,32 +1979,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
1902
1979
  // Wait for controls and initialization to complete
1903
1980
  if (!controlsRef.current || !hasAppliedInitial.current) return;
1904
1981
 
1905
- // [debug] log which branch runs each frame so consumers can identify
1906
- // unexpected camera mutation when no api.start fired.
1907
- if (
1908
- isOrbitingRef.current ||
1909
- isTiltingRef.current ||
1910
- isAnimatingRef.current
1911
- ) {
1912
- const branch = isOrbitingRef.current
1913
- ? 'orbit'
1914
- : isTiltingRef.current
1915
- ? 'tilt'
1916
- : 'animating';
1917
- const w = (
1918
- globalThis as unknown as { __fileCityFrameLog?: Map<string, number> }
1919
- ).__fileCityFrameLog ??= new Map<string, number>();
1920
- const last = w.get(branch) ?? 0;
1921
- const now = performance.now();
1922
- if (now - last > 200) {
1923
- w.set(branch, now);
1924
- // eslint-disable-next-line no-console
1925
- console.log(`[useFrame#${branch}]`, {
1926
- camY: camY.get(),
1927
- posY: camera.position.y,
1928
- });
1929
- }
1930
- }
1931
1982
  // Handle orbit animation (horizontal rotation along arc)
1932
1983
  if (isOrbitingRef.current && orbitParamsRef.current) {
1933
1984
  const { centerX, centerZ, distance, height } = orbitParamsRef.current;
@@ -1994,7 +2045,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
1994
2045
  const targetHeight = citySize * 1.1;
1995
2046
  const targetZ = citySize * 1.3;
1996
2047
 
1997
- console.log('[api.start#resetToInitial]', { targetHeight, targetZ });
1998
2048
  api.start({
1999
2049
  camX: 0,
2000
2050
  camY: targetHeight,
@@ -2010,7 +2060,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
2010
2060
  const distance = Math.max(effectiveSize * 2, 50);
2011
2061
  const height = Math.max(effectiveSize * 1.5, 40);
2012
2062
 
2013
- console.log('[api.start#moveTo]', { x, z, height, distance });
2014
2063
  api.start({
2015
2064
  camX: x,
2016
2065
  camY: height,
@@ -2042,7 +2091,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
2042
2091
  });
2043
2092
  }
2044
2093
  const config = options?.duration ? { duration: options.duration } : undefined;
2045
- console.log('[api.start#setFlatView]', { x, z, height, options });
2046
2094
  api.start({
2047
2095
  camX: x,
2048
2096
  camY: height,
@@ -2077,7 +2125,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
2077
2125
  ? { duration: options.duration, easing: (t: number) => t }
2078
2126
  : { tension: 60, friction: 20 };
2079
2127
 
2080
- console.log('[api.start#setTarget]', { x, y, z, newCamX, newCamY, newCamZ });
2081
2128
  api.start({
2082
2129
  camX: newCamX,
2083
2130
  camY: newCamY,
@@ -2818,6 +2865,8 @@ interface CitySceneProps {
2818
2865
  growProgress: number;
2819
2866
  animationConfig: AnimationConfig;
2820
2867
  highlightLayers: HighlightLayer[];
2868
+ /** User-supplied highlight layers (no file-color layers) for visibility logic. */
2869
+ visibilityLayers: HighlightLayer[];
2821
2870
  isolationMode: IsolationMode;
2822
2871
  heightScaling: HeightScaling;
2823
2872
  linearScale: number;
@@ -2843,6 +2892,7 @@ function CityScene({
2843
2892
  growProgress,
2844
2893
  animationConfig,
2845
2894
  highlightLayers,
2895
+ visibilityLayers,
2846
2896
  isolationMode,
2847
2897
  heightScaling,
2848
2898
  linearScale,
@@ -2876,7 +2926,7 @@ function CityScene({
2876
2926
  return Math.max(...cityData.buildings.map(b => b.dimensions[1]), 0);
2877
2927
  }, [adaptCameraToBuildings, cityData.buildings]);
2878
2928
 
2879
- const activeHighlights = useMemo(() => hasActiveHighlights(highlightLayers), [highlightLayers]);
2929
+ const activeHighlights = useMemo(() => hasActiveHighlights(visibilityLayers), [visibilityLayers]);
2880
2930
 
2881
2931
  // Helper to check if a path is inside a directory
2882
2932
  const isPathInDirectory = useCallback((path: string, directory: string) => {
@@ -3160,6 +3210,7 @@ function CityScene({
3160
3210
  staggerIndices={staggerIndices}
3161
3211
  focusDirectory={buildingFocusDirectory}
3162
3212
  highlightLayers={highlightLayers}
3213
+ visibilityLayers={visibilityLayers}
3163
3214
  isolationMode={isolationMode}
3164
3215
  defaultBuildingColor={defaultBuildingColor}
3165
3216
  />
@@ -3172,6 +3223,7 @@ function CityScene({
3172
3223
  linearScale={linearScale}
3173
3224
  flatPatterns={flatPatterns}
3174
3225
  highlightLayers={highlightLayers}
3226
+ visibilityLayers={visibilityLayers}
3175
3227
  isolationMode={isolationMode}
3176
3228
  hasActiveHighlights={activeHighlights}
3177
3229
  />
@@ -3420,6 +3472,13 @@ export function FileCity3D({
3420
3472
  const focusColor = resolved.focusColor;
3421
3473
  const isolationMode = resolved.isolationMode as IsolationMode;
3422
3474
 
3475
+ // User-supplied highlight layers only — used for visibility decisions so
3476
+ // file-color layers don't keep every building visible in 'hide' mode.
3477
+ const visibilityLayers = useMemo(
3478
+ () => (externalHighlightLayers ?? []) as HighlightLayer[],
3479
+ [externalHighlightLayers],
3480
+ );
3481
+
3423
3482
  // `selectedPath` wins over the deprecated `selectedBuilding` when both are
3424
3483
  // set. A path resolves to either a building (file selection) or a district
3425
3484
  // (directory selection) — never both.
@@ -3545,6 +3604,7 @@ export function FileCity3D({
3545
3604
  growProgress={growProgress}
3546
3605
  animationConfig={animationConfig}
3547
3606
  highlightLayers={highlightLayers}
3607
+ visibilityLayers={visibilityLayers}
3548
3608
  isolationMode={isolationMode}
3549
3609
  heightScaling={heightScaling}
3550
3610
  linearScale={linearScale}
@@ -392,11 +392,41 @@ const testScenarios: TestScenario[] = [
392
392
  },
393
393
  ],
394
394
  },
395
+ {
396
+ id: 'S10-dir-plus-files-inside',
397
+ name: 'S10: Directory + Files Inside',
398
+ description:
399
+ 'Directory layer on api + file layer for specific routes inside it — directory-only matches inside the dir should be hidden in hide mode',
400
+ focusDirectory: null,
401
+ highlightLayers: [
402
+ {
403
+ id: 'api-dir-layer',
404
+ name: 'API Routes (dir)',
405
+ enabled: true,
406
+ priority: 0,
407
+ color: '#3b82f6',
408
+ items: [{ path: 'auth-server/src/app/api', type: 'directory' as const }],
409
+ },
410
+ {
411
+ id: 'workos-files-layer',
412
+ name: 'WorkOS Routes (files)',
413
+ enabled: true,
414
+ priority: 1,
415
+ color: '#ec4899',
416
+ items: [
417
+ { path: 'auth-server/src/app/api/auth/workos/callback/route.ts', type: 'file' as const },
418
+ { path: 'auth-server/src/app/api/auth/workos/verify/route.ts', type: 'file' as const },
419
+ { path: 'auth-server/src/app/api/auth/workos/token/route.ts', type: 'file' as const },
420
+ ],
421
+ },
422
+ ],
423
+ },
395
424
  ];
396
425
 
397
426
  export const ScenarioComparison: StoryObj = {
398
427
  render: function RenderScenarioComparison() {
399
428
  const [currentScenarioIndex, setCurrentScenarioIndex] = useState(0);
429
+ const [isGrown, setIsGrown] = useState(true);
400
430
  const scenario = testScenarios[currentScenarioIndex];
401
431
  const cityData = authServerCityData as CityData;
402
432
 
@@ -521,9 +551,11 @@ export const ScenarioComparison: StoryObj = {
521
551
  fileColorLayers={fileColorLayers}
522
552
  focusDirectory={scenario.focusDirectory}
523
553
  focusColor={scenario.focusColor}
554
+ isolationMode="hide"
524
555
  width="100%"
525
556
  height="100%"
526
- isGrown={true}
557
+ isGrown={isGrown}
558
+ onGrowChange={setIsGrown}
527
559
  showControls={true}
528
560
  backgroundColor="#0f1419"
529
561
  />