@principal-ai/file-city-react 0.5.50 → 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,
@@ -1129,20 +1206,17 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1129
1206
  // Ensure camera FOV is correct (defaults to 75 before prop applies)
1130
1207
  const perspCam = camera;
1131
1208
  if (perspCam.fov !== 50) {
1132
- console.log('[Frame 1] Correcting FOV from', perspCam.fov, 'to 50');
1133
1209
  perspCam.fov = 50;
1134
1210
  perspCam.updateProjectionMatrix();
1135
1211
  }
1136
1212
  // Calculate initial 2D position with correct aspect ratio
1137
1213
  const initialPos = getInitial2DPosition();
1138
- console.log('[Frame 1] Setting camera to initial 2D position:', initialPos);
1139
1214
  camera.position.set(initialPos.x, initialPos.y, initialPos.z);
1140
1215
  // Wait for controls to be ready, then set target and sync spring
1141
1216
  if (controlsRef.current) {
1142
1217
  controlsRef.current.target.set(initialPos.targetX, initialPos.targetY, initialPos.targetZ);
1143
1218
  controlsRef.current.update();
1144
1219
  // Sync spring to match camera position (use immediate to avoid animation)
1145
- console.log('[api.start#frame1-immediate]', initialPos);
1146
1220
  api.start({
1147
1221
  camX: initialPos.x,
1148
1222
  camY: initialPos.y,
@@ -1164,35 +1238,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1164
1238
  // Wait for controls and initialization to complete
1165
1239
  if (!controlsRef.current || !hasAppliedInitial.current)
1166
1240
  return;
1167
- // [debug] log every frame: which branch is active + spring vs camera
1168
- // position. Throttled to every 250ms. Catches both spring-driven motion
1169
- // AND external mutation (when no branch is active but position changes).
1170
- {
1171
- const w = globalThis;
1172
- w.__fileCityFrameLog ?? (w.__fileCityFrameLog = { last: 0, lastY: NaN });
1173
- const log = w.__fileCityFrameLog;
1174
- const now = performance.now();
1175
- const yChanged = Math.abs(camera.position.y - log.lastY) > 0.5 ||
1176
- Number.isNaN(log.lastY);
1177
- if (now - log.last > 250 && yChanged) {
1178
- log.last = now;
1179
- log.lastY = camera.position.y;
1180
- const branch = isOrbitingRef.current
1181
- ? 'orbit'
1182
- : isTiltingRef.current
1183
- ? 'tilt'
1184
- : isAnimatingRef.current
1185
- ? 'animating'
1186
- : 'idle';
1187
- // eslint-disable-next-line no-console
1188
- console.log(`[useFrame#${branch}]`, {
1189
- springY: camY.get(),
1190
- posY: camera.position.y,
1191
- springZ: camZ.get(),
1192
- posZ: camera.position.z,
1193
- });
1194
- }
1195
- }
1196
1241
  // Handle orbit animation (horizontal rotation along arc)
1197
1242
  if (isOrbitingRef.current && orbitParamsRef.current) {
1198
1243
  const { centerX, centerZ, distance, height } = orbitParamsRef.current;
@@ -1250,7 +1295,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1250
1295
  const resetToInitial = useCallback(() => {
1251
1296
  const targetHeight = citySize * 1.1;
1252
1297
  const targetZ = citySize * 1.3;
1253
- console.log('[api.start#resetToInitial]', { targetHeight, targetZ });
1254
1298
  api.start({
1255
1299
  camX: 0,
1256
1300
  camY: targetHeight,
@@ -1264,7 +1308,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1264
1308
  const effectiveSize = size ?? citySize * 0.3;
1265
1309
  const distance = Math.max(effectiveSize * 2, 50);
1266
1310
  const height = Math.max(effectiveSize * 1.5, 40);
1267
- console.log('[api.start#moveTo]', { x, z, height, distance });
1268
1311
  api.start({
1269
1312
  camX: x,
1270
1313
  camY: height,
@@ -1294,7 +1337,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1294
1337
  });
1295
1338
  }
1296
1339
  const config = options?.duration ? { duration: options.duration } : undefined;
1297
- console.log('[api.start#setFlatView]', { x, z, height, options });
1298
1340
  api.start({
1299
1341
  camX: x,
1300
1342
  camY: height,
@@ -1322,7 +1364,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1322
1364
  const animConfig = options?.duration
1323
1365
  ? { duration: options.duration, easing: (t) => t }
1324
1366
  : { tension: 60, friction: 20 };
1325
- console.log('[api.start#setTarget]', { x, y, z, newCamX, newCamY, newCamZ });
1326
1367
  api.start({
1327
1368
  camX: newCamX,
1328
1369
  camY: newCamY,
@@ -1770,7 +1811,7 @@ function SelectionRing({ district, centerOffset, color, borderWidth, growProgres
1770
1811
  const barH = 0.5;
1771
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 })] })] }));
1772
1813
  }
1773
- 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, }) {
1774
1815
  const centerOffset = useMemo(() => ({
1775
1816
  x: (cityData.bounds.minX + cityData.bounds.maxX) / 2,
1776
1817
  z: (cityData.bounds.minZ + cityData.bounds.maxZ) / 2,
@@ -1782,7 +1823,7 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
1782
1823
  return 0;
1783
1824
  return Math.max(...cityData.buildings.map(b => b.dimensions[1]), 0);
1784
1825
  }, [adaptCameraToBuildings, cityData.buildings]);
1785
- const activeHighlights = useMemo(() => hasActiveHighlights(highlightLayers), [highlightLayers]);
1826
+ const activeHighlights = useMemo(() => hasActiveHighlights(visibilityLayers), [visibilityLayers]);
1786
1827
  // Helper to check if a path is inside a directory
1787
1828
  const isPathInDirectory = useCallback((path, directory) => {
1788
1829
  if (!directory)
@@ -1974,7 +2015,7 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
1974
2015
  // Focus color takes priority, then highlight layer color
1975
2016
  const districtColor = (isFocused && buildingFocusColor) ? buildingFocusColor : highlightLayerColor;
1976
2017
  return (_jsx(DistrictFloor, { district: district, centerOffset: centerOffset, opacity: 1, highlightColor: districtColor, growProgress: growProgress }, district.path));
1977
- }), _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 &&
1978
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 }))] }));
1979
2020
  }
1980
2021
  /**
@@ -2024,6 +2065,9 @@ export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingC
2024
2065
  const focusDirectory = resolved.focusDirectory;
2025
2066
  const focusColor = resolved.focusColor;
2026
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]);
2027
2071
  // `selectedPath` wins over the deprecated `selectedBuilding` when both are
2028
2072
  // set. A path resolves to either a building (file selection) or a district
2029
2073
  // (directory selection) — never both.
@@ -2107,6 +2151,6 @@ export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingC
2107
2151
  height: '100%',
2108
2152
  opacity: cameraReady ? 1 : 0,
2109
2153
  transition: 'opacity 0.1s ease-in',
2110
- }, 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) }))] }));
2111
2155
  }
2112
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.50",
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,40 +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 every frame: which branch is active + spring vs camera
1906
- // position. Throttled to every 250ms. Catches both spring-driven motion
1907
- // AND external mutation (when no branch is active but position changes).
1908
- {
1909
- const w = (
1910
- globalThis as unknown as {
1911
- __fileCityFrameLog?: { last: number; lastY: number };
1912
- }
1913
- );
1914
- w.__fileCityFrameLog ??= { last: 0, lastY: NaN };
1915
- const log = w.__fileCityFrameLog;
1916
- const now = performance.now();
1917
- const yChanged =
1918
- Math.abs(camera.position.y - log.lastY) > 0.5 ||
1919
- Number.isNaN(log.lastY);
1920
- if (now - log.last > 250 && yChanged) {
1921
- log.last = now;
1922
- log.lastY = camera.position.y;
1923
- const branch = isOrbitingRef.current
1924
- ? 'orbit'
1925
- : isTiltingRef.current
1926
- ? 'tilt'
1927
- : isAnimatingRef.current
1928
- ? 'animating'
1929
- : 'idle';
1930
- // eslint-disable-next-line no-console
1931
- console.log(`[useFrame#${branch}]`, {
1932
- springY: camY.get(),
1933
- posY: camera.position.y,
1934
- springZ: camZ.get(),
1935
- posZ: camera.position.z,
1936
- });
1937
- }
1938
- }
1939
1982
  // Handle orbit animation (horizontal rotation along arc)
1940
1983
  if (isOrbitingRef.current && orbitParamsRef.current) {
1941
1984
  const { centerX, centerZ, distance, height } = orbitParamsRef.current;
@@ -2002,7 +2045,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
2002
2045
  const targetHeight = citySize * 1.1;
2003
2046
  const targetZ = citySize * 1.3;
2004
2047
 
2005
- console.log('[api.start#resetToInitial]', { targetHeight, targetZ });
2006
2048
  api.start({
2007
2049
  camX: 0,
2008
2050
  camY: targetHeight,
@@ -2018,7 +2060,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
2018
2060
  const distance = Math.max(effectiveSize * 2, 50);
2019
2061
  const height = Math.max(effectiveSize * 1.5, 40);
2020
2062
 
2021
- console.log('[api.start#moveTo]', { x, z, height, distance });
2022
2063
  api.start({
2023
2064
  camX: x,
2024
2065
  camY: height,
@@ -2050,7 +2091,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
2050
2091
  });
2051
2092
  }
2052
2093
  const config = options?.duration ? { duration: options.duration } : undefined;
2053
- console.log('[api.start#setFlatView]', { x, z, height, options });
2054
2094
  api.start({
2055
2095
  camX: x,
2056
2096
  camY: height,
@@ -2085,7 +2125,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
2085
2125
  ? { duration: options.duration, easing: (t: number) => t }
2086
2126
  : { tension: 60, friction: 20 };
2087
2127
 
2088
- console.log('[api.start#setTarget]', { x, y, z, newCamX, newCamY, newCamZ });
2089
2128
  api.start({
2090
2129
  camX: newCamX,
2091
2130
  camY: newCamY,
@@ -2826,6 +2865,8 @@ interface CitySceneProps {
2826
2865
  growProgress: number;
2827
2866
  animationConfig: AnimationConfig;
2828
2867
  highlightLayers: HighlightLayer[];
2868
+ /** User-supplied highlight layers (no file-color layers) for visibility logic. */
2869
+ visibilityLayers: HighlightLayer[];
2829
2870
  isolationMode: IsolationMode;
2830
2871
  heightScaling: HeightScaling;
2831
2872
  linearScale: number;
@@ -2851,6 +2892,7 @@ function CityScene({
2851
2892
  growProgress,
2852
2893
  animationConfig,
2853
2894
  highlightLayers,
2895
+ visibilityLayers,
2854
2896
  isolationMode,
2855
2897
  heightScaling,
2856
2898
  linearScale,
@@ -2884,7 +2926,7 @@ function CityScene({
2884
2926
  return Math.max(...cityData.buildings.map(b => b.dimensions[1]), 0);
2885
2927
  }, [adaptCameraToBuildings, cityData.buildings]);
2886
2928
 
2887
- const activeHighlights = useMemo(() => hasActiveHighlights(highlightLayers), [highlightLayers]);
2929
+ const activeHighlights = useMemo(() => hasActiveHighlights(visibilityLayers), [visibilityLayers]);
2888
2930
 
2889
2931
  // Helper to check if a path is inside a directory
2890
2932
  const isPathInDirectory = useCallback((path: string, directory: string) => {
@@ -3168,6 +3210,7 @@ function CityScene({
3168
3210
  staggerIndices={staggerIndices}
3169
3211
  focusDirectory={buildingFocusDirectory}
3170
3212
  highlightLayers={highlightLayers}
3213
+ visibilityLayers={visibilityLayers}
3171
3214
  isolationMode={isolationMode}
3172
3215
  defaultBuildingColor={defaultBuildingColor}
3173
3216
  />
@@ -3180,6 +3223,7 @@ function CityScene({
3180
3223
  linearScale={linearScale}
3181
3224
  flatPatterns={flatPatterns}
3182
3225
  highlightLayers={highlightLayers}
3226
+ visibilityLayers={visibilityLayers}
3183
3227
  isolationMode={isolationMode}
3184
3228
  hasActiveHighlights={activeHighlights}
3185
3229
  />
@@ -3428,6 +3472,13 @@ export function FileCity3D({
3428
3472
  const focusColor = resolved.focusColor;
3429
3473
  const isolationMode = resolved.isolationMode as IsolationMode;
3430
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
+
3431
3482
  // `selectedPath` wins over the deprecated `selectedBuilding` when both are
3432
3483
  // set. A path resolves to either a building (file selection) or a district
3433
3484
  // (directory selection) — never both.
@@ -3553,6 +3604,7 @@ export function FileCity3D({
3553
3604
  growProgress={growProgress}
3554
3605
  animationConfig={animationConfig}
3555
3606
  highlightLayers={highlightLayers}
3607
+ visibilityLayers={visibilityLayers}
3556
3608
  isolationMode={isolationMode}
3557
3609
  heightScaling={heightScaling}
3558
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
  />