@principal-ai/file-city-react 0.5.50 → 0.5.52

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,39 @@ 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);
453
462
  const hasActiveHighlightLayers = useMemo(() => {
454
- return highlightLayers.some(layer => layer.enabled && layer.items.length > 0);
455
- }, [highlightLayers]);
463
+ return visibilityLayers.some(layer => layer.enabled && layer.items.length > 0);
464
+ }, [visibilityLayers]);
465
+ // Directories matched by a directory-type item from a user-supplied layer
466
+ // that also contain a file-type item from any user-supplied layer. Inside
467
+ // these directories, the directory match alone isn't enough to count as
468
+ // "specifically highlighted" — file-level matches define the visible subset.
469
+ const narrowedDirectories = useMemo(() => {
470
+ const dirs = [];
471
+ const files = [];
472
+ for (const layer of visibilityLayers) {
473
+ if (!layer.enabled)
474
+ continue;
475
+ for (const item of layer.items) {
476
+ if (item.type === 'directory')
477
+ dirs.push(item.path);
478
+ else if (item.type === 'file')
479
+ files.push(item.path);
480
+ }
481
+ }
482
+ const narrowed = new Set();
483
+ for (const dir of dirs) {
484
+ for (const f of files) {
485
+ if (f === dir || f.startsWith(dir + '/')) {
486
+ narrowed.add(dir);
487
+ break;
488
+ }
489
+ }
490
+ }
491
+ return narrowed;
492
+ }, [visibilityLayers]);
456
493
  // Initialize height and dim multiplier arrays
457
494
  useEffect(() => {
458
495
  if (buildings.length > 0) {
@@ -462,38 +499,53 @@ function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hovered
462
499
  targetMultipliersRef.current = new Float32Array(buildings.length).fill(1);
463
500
  dimMultipliersRef.current = new Float32Array(buildings.length).fill(1);
464
501
  targetDimRef.current = new Float32Array(buildings.length).fill(1);
502
+ hiddenRef.current = new Uint8Array(buildings.length);
465
503
  }
466
504
  }
467
505
  }, [buildings.length]);
468
506
  // Update target multipliers when focusDirectory or highlightLayers change
469
507
  useEffect(() => {
470
- if (!targetMultipliersRef.current || !targetDimRef.current)
508
+ if (!targetMultipliersRef.current || !targetDimRef.current || !hiddenRef.current)
471
509
  return;
472
510
  buildings.forEach((building, index) => {
473
511
  let shouldCollapse = false;
474
512
  let shouldDim = false;
513
+ let shouldHide = false;
475
514
  const isInFocusDirectory = focusDirectory
476
515
  ? isPathInDirectory(building.path, focusDirectory)
477
516
  : true; // No focusDirectory means all are "in focus"
517
+ const layerMatches = hasActiveHighlightLayers
518
+ ? getLayerMatchesForPath(building.path, visibilityLayers)
519
+ : [];
478
520
  const isHighlighted = hasActiveHighlightLayers
479
- ? getHighlightForPath(building.path, highlightLayers) !== null
521
+ ? layerMatches.length > 0
480
522
  : true; // No highlights means all are "highlighted"
481
- // Determine collapse and dim behavior based on what's active:
482
- // - focusDirectory only: collapse if outside focus
483
- // - highlightLayers only (with collapse mode): collapse if not highlighted
484
- // - both: collapse if outside focus, dim if in focus but not highlighted
523
+ // A directory match doesn't count as "specifically highlighted" when
524
+ // that directory has been narrowed by file-level matches — the
525
+ // file-level matches define the visible subset within it.
526
+ const isSpecificallyHighlighted = hasActiveHighlightLayers
527
+ ? layerMatches.some(m => m.item.type === 'file' || !narrowedDirectories.has(m.item.path))
528
+ : true;
529
+ // Determine collapse/dim/hide behavior based on what's active. The
530
+ // "specifically highlighted" check applies in both collapse and hide
531
+ // modes so directory matches narrowed by file-level matches don't keep
532
+ // every sibling visible.
485
533
  if (focusDirectory && hasActiveHighlightLayers && isolationMode === 'collapse') {
486
- // Both active: collapse if outside focus, dim if in focus but not highlighted
487
534
  shouldCollapse = !isInFocusDirectory;
488
- shouldDim = isInFocusDirectory && !isHighlighted;
535
+ shouldDim = isInFocusDirectory && !isSpecificallyHighlighted;
536
+ }
537
+ else if (focusDirectory && hasActiveHighlightLayers && isolationMode === 'hide') {
538
+ shouldCollapse = !isInFocusDirectory;
539
+ shouldHide = isInFocusDirectory && !isSpecificallyHighlighted;
489
540
  }
490
541
  else if (focusDirectory) {
491
- // Focus only: collapse if outside focus directory
492
542
  shouldCollapse = !isInFocusDirectory;
493
543
  }
494
544
  else if (hasActiveHighlightLayers && isolationMode === 'collapse') {
495
- // Highlight only with collapse: collapse if not highlighted
496
- shouldCollapse = !isHighlighted;
545
+ shouldCollapse = !isSpecificallyHighlighted;
546
+ }
547
+ else if (hasActiveHighlightLayers && isolationMode === 'hide') {
548
+ shouldHide = !isSpecificallyHighlighted;
497
549
  }
498
550
  // Height: 1.0 = full, 0.05 = flat (collapsed or dimmed)
499
551
  if (shouldCollapse || shouldDim) {
@@ -505,8 +557,10 @@ function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hovered
505
557
  // Dim ref controls graying: 0 = gray out, 1 = keep color
506
558
  // Collapsed buildings go gray, dimmed buildings keep their color
507
559
  targetDimRef.current[index] = shouldCollapse ? 0 : 1;
560
+ // Hidden ref controls full invisibility (mesh + edges + icon)
561
+ hiddenRef.current[index] = shouldHide ? 1 : 0;
508
562
  });
509
- }, [focusDirectory, buildings, highlightLayers, isolationMode, hasActiveHighlightLayers]);
563
+ }, [focusDirectory, buildings, visibilityLayers, isolationMode, hasActiveHighlightLayers, narrowedDirectories]);
510
564
  // Pre-compute building data
511
565
  const buildingData = useMemo(() => {
512
566
  return buildings.map((building, index) => {
@@ -588,6 +642,14 @@ function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hovered
588
642
  const collapseSpeed = 0.08;
589
643
  buildingData.forEach((data, instanceIndex) => {
590
644
  const { width, depth, fullHeight, x, z, staggerDelayMs } = data;
645
+ const isHidden = hiddenRef.current?.[instanceIndex] === 1;
646
+ if (isHidden) {
647
+ tempObject.position.set(x, baseOffset, z);
648
+ tempObject.scale.set(0, 0, 0);
649
+ tempObject.updateMatrix();
650
+ meshRef.current.setMatrixAt(instanceIndex, tempObject.matrix);
651
+ return;
652
+ }
591
653
  // Animate height multiplier towards target
592
654
  const currentMultiplier = heightMultipliersRef.current[instanceIndex];
593
655
  const targetMultiplier = targetMultipliersRef.current[instanceIndex];
@@ -671,7 +733,7 @@ function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hovered
671
733
  z: d.z,
672
734
  staggerDelayMs: d.staggerDelayMs,
673
735
  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 })] }));
736
+ })), 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
737
  }
676
738
  function AnimatedIcon({ x, z, targetHeight, iconSize, texture, opacity, growProgress, }) {
677
739
  const meshRef = useRef(null);
@@ -699,7 +761,33 @@ function AnimatedIcon({ x, z, targetHeight, iconSize, texture, opacity, growProg
699
761
  });
700
762
  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
763
  }
702
- function BuildingIcons({ buildings, centerOffset, growProgress, heightScaling, linearScale, flatPatterns, highlightLayers, isolationMode, hasActiveHighlights, }) {
764
+ function BuildingIcons({ buildings, centerOffset, growProgress, heightScaling, linearScale, flatPatterns, highlightLayers, visibilityLayers, isolationMode, hasActiveHighlights, }) {
765
+ // Same narrowing rule as InstancedBuildings, scoped to user highlight layers
766
+ // only (file-color layers don't narrow visibility).
767
+ const narrowedDirectories = useMemo(() => {
768
+ const dirs = [];
769
+ const files = [];
770
+ for (const layer of visibilityLayers) {
771
+ if (!layer.enabled)
772
+ continue;
773
+ for (const item of layer.items) {
774
+ if (item.type === 'directory')
775
+ dirs.push(item.path);
776
+ else if (item.type === 'file')
777
+ files.push(item.path);
778
+ }
779
+ }
780
+ const narrowed = new Set();
781
+ for (const dir of dirs) {
782
+ for (const f of files) {
783
+ if (f === dir || f.startsWith(dir + '/')) {
784
+ narrowed.add(dir);
785
+ break;
786
+ }
787
+ }
788
+ }
789
+ return narrowed;
790
+ }, [visibilityLayers]);
703
791
  // Pre-compute buildings with icons
704
792
  const buildingsWithIcons = useMemo(() => {
705
793
  return buildings
@@ -707,10 +795,11 @@ function BuildingIcons({ buildings, centerOffset, growProgress, heightScaling, l
707
795
  const config = getConfigForFile(building);
708
796
  if (!config.icon)
709
797
  return null;
710
- const highlight = getHighlightForPath(building.path, highlightLayers);
711
- const isHighlighted = highlight !== null;
712
- const shouldDim = hasActiveHighlights && !isHighlighted;
713
- const shouldHide = shouldDim && isolationMode === 'hide';
798
+ const matches = getLayerMatchesForPath(building.path, visibilityLayers);
799
+ const isHighlighted = matches.length > 0;
800
+ const isSpecificallyHighlighted = matches.some(m => m.item.type === 'file' || !narrowedDirectories.has(m.item.path));
801
+ const shouldDim = hasActiveHighlights && !isSpecificallyHighlighted;
802
+ const shouldHide = hasActiveHighlights && isolationMode === 'hide' && !isSpecificallyHighlighted;
714
803
  const shouldCollapse = shouldDim && isolationMode === 'collapse';
715
804
  // Hide icons for buildings that are hidden or collapsed
716
805
  if (shouldHide || shouldCollapse)
@@ -732,12 +821,13 @@ function BuildingIcons({ buildings, centerOffset, growProgress, heightScaling, l
732
821
  }, [
733
822
  buildings,
734
823
  centerOffset,
735
- highlightLayers,
824
+ visibilityLayers,
736
825
  isolationMode,
737
826
  hasActiveHighlights,
738
827
  heightScaling,
739
828
  linearScale,
740
829
  flatPatterns,
830
+ narrowedDirectories,
741
831
  ]);
742
832
  // Icons are now always rendered (flat or grown)
743
833
  return (_jsx(_Fragment, { children: buildingsWithIcons.map(({ building, config, x, z, targetHeight, shouldDim }) => {
@@ -777,7 +867,7 @@ function DistrictFloor({ district, centerOffset, highlightColor, growProgress })
777
867
  const flatZ = depth / 2 - 6; // Near bottom of district when flat, with padding
778
868
  const grownZ = depth / 2 + 2; // Just outside edge when grown
779
869
  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 })] }));
870
+ 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
871
  }
782
872
  export const DEFAULT_CAMERA_CONTROLS = {
783
873
  leftDrag: 'pan',
@@ -957,7 +1047,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
957
1047
  // Calculate initial position with default aspect ratio
958
1048
  // This will be corrected in Frame 1 if aspect is different
959
1049
  const initialHeight = calculateFlatCameraHeight(1);
960
- console.log('[Spring init] Initializing with 2D position, height:', initialHeight);
961
1050
  return {
962
1051
  camX: 0,
963
1052
  camY: initialHeight,
@@ -967,17 +1056,11 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
967
1056
  lookZ: 0,
968
1057
  config: { tension: 60, friction: 20 },
969
1058
  onStart: () => {
970
- // Only allow animations after initial setup is complete
971
1059
  if (hasAppliedInitial.current) {
972
- console.log('[Spring onStart] Animation starting - camY:', camY.get());
973
1060
  isAnimatingRef.current = true;
974
1061
  }
975
- else {
976
- console.log('[Spring onStart] Blocked - initialization not complete');
977
- }
978
1062
  },
979
1063
  onRest: () => {
980
- console.log('[Spring onRest] Animation finished');
981
1064
  isAnimatingRef.current = false;
982
1065
  },
983
1066
  };
@@ -1012,19 +1095,11 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1012
1095
  // When isFlat changes from true to false, animate to 3D view
1013
1096
  // Component always starts in 2D, so we only animate the 2D→3D transition
1014
1097
  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');
1098
+ if (!hasAppliedInitial.current)
1019
1099
  return;
1020
- }
1021
- // Only animate if isFlat changed from true to false (2D → 3D transition)
1022
1100
  const isFlatChanged = prevIsFlatRef.current !== isFlat;
1023
- if (!isFlatChanged) {
1024
- console.log('[useEffect] No isFlat change - skipping');
1101
+ if (!isFlatChanged)
1025
1102
  return;
1026
- }
1027
- console.log('[useEffect] isFlat changed from', prevIsFlatRef.current, 'to', isFlat, '- animating transition');
1028
1103
  prevIsFlatRef.current = isFlat;
1029
1104
  // Calculate target position for 3D view
1030
1105
  const newPos = isFlat
@@ -1046,7 +1121,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1046
1121
  targetY: 0,
1047
1122
  targetZ: 0,
1048
1123
  };
1049
- console.log('[api.start#isFlat-toggle]', newPos);
1050
1124
  api.start({
1051
1125
  camX: newPos.x,
1052
1126
  camY: newPos.y,
@@ -1109,7 +1183,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1109
1183
  targetZ: 0,
1110
1184
  };
1111
1185
  }
1112
- console.log('[api.start#focus-target]', { focusTarget, isFlat, newPos });
1113
1186
  api.start({
1114
1187
  camX: newPos.x,
1115
1188
  camY: newPos.y,
@@ -1129,20 +1202,17 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1129
1202
  // Ensure camera FOV is correct (defaults to 75 before prop applies)
1130
1203
  const perspCam = camera;
1131
1204
  if (perspCam.fov !== 50) {
1132
- console.log('[Frame 1] Correcting FOV from', perspCam.fov, 'to 50');
1133
1205
  perspCam.fov = 50;
1134
1206
  perspCam.updateProjectionMatrix();
1135
1207
  }
1136
1208
  // Calculate initial 2D position with correct aspect ratio
1137
1209
  const initialPos = getInitial2DPosition();
1138
- console.log('[Frame 1] Setting camera to initial 2D position:', initialPos);
1139
1210
  camera.position.set(initialPos.x, initialPos.y, initialPos.z);
1140
1211
  // Wait for controls to be ready, then set target and sync spring
1141
1212
  if (controlsRef.current) {
1142
1213
  controlsRef.current.target.set(initialPos.targetX, initialPos.targetY, initialPos.targetZ);
1143
1214
  controlsRef.current.update();
1144
1215
  // Sync spring to match camera position (use immediate to avoid animation)
1145
- console.log('[api.start#frame1-immediate]', initialPos);
1146
1216
  api.start({
1147
1217
  camX: initialPos.x,
1148
1218
  camY: initialPos.y,
@@ -1164,35 +1234,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1164
1234
  // Wait for controls and initialization to complete
1165
1235
  if (!controlsRef.current || !hasAppliedInitial.current)
1166
1236
  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
1237
  // Handle orbit animation (horizontal rotation along arc)
1197
1238
  if (isOrbitingRef.current && orbitParamsRef.current) {
1198
1239
  const { centerX, centerZ, distance, height } = orbitParamsRef.current;
@@ -1250,7 +1291,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1250
1291
  const resetToInitial = useCallback(() => {
1251
1292
  const targetHeight = citySize * 1.1;
1252
1293
  const targetZ = citySize * 1.3;
1253
- console.log('[api.start#resetToInitial]', { targetHeight, targetZ });
1254
1294
  api.start({
1255
1295
  camX: 0,
1256
1296
  camY: targetHeight,
@@ -1264,7 +1304,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1264
1304
  const effectiveSize = size ?? citySize * 0.3;
1265
1305
  const distance = Math.max(effectiveSize * 2, 50);
1266
1306
  const height = Math.max(effectiveSize * 1.5, 40);
1267
- console.log('[api.start#moveTo]', { x, z, height, distance });
1268
1307
  api.start({
1269
1308
  camX: x,
1270
1309
  camY: height,
@@ -1294,7 +1333,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1294
1333
  });
1295
1334
  }
1296
1335
  const config = options?.duration ? { duration: options.duration } : undefined;
1297
- console.log('[api.start#setFlatView]', { x, z, height, options });
1298
1336
  api.start({
1299
1337
  camX: x,
1300
1338
  camY: height,
@@ -1322,7 +1360,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1322
1360
  const animConfig = options?.duration
1323
1361
  ? { duration: options.duration, easing: (t) => t }
1324
1362
  : { tension: 60, friction: 20 };
1325
- console.log('[api.start#setTarget]', { x, y, z, newCamX, newCamY, newCamZ });
1326
1363
  api.start({
1327
1364
  camX: newCamX,
1328
1365
  camY: newCamY,
@@ -1770,7 +1807,7 @@ function SelectionRing({ district, centerOffset, color, borderWidth, growProgres
1770
1807
  const barH = 0.5;
1771
1808
  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
1809
  }
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, }) {
1810
+ 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
1811
  const centerOffset = useMemo(() => ({
1775
1812
  x: (cityData.bounds.minX + cityData.bounds.maxX) / 2,
1776
1813
  z: (cityData.bounds.minZ + cityData.bounds.maxZ) / 2,
@@ -1782,7 +1819,7 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
1782
1819
  return 0;
1783
1820
  return Math.max(...cityData.buildings.map(b => b.dimensions[1]), 0);
1784
1821
  }, [adaptCameraToBuildings, cityData.buildings]);
1785
- const activeHighlights = useMemo(() => hasActiveHighlights(highlightLayers), [highlightLayers]);
1822
+ const activeHighlights = useMemo(() => hasActiveHighlights(visibilityLayers), [visibilityLayers]);
1786
1823
  // Helper to check if a path is inside a directory
1787
1824
  const isPathInDirectory = useCallback((path, directory) => {
1788
1825
  if (!directory)
@@ -1974,7 +2011,7 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
1974
2011
  // Focus color takes priority, then highlight layer color
1975
2012
  const districtColor = (isFocused && buildingFocusColor) ? buildingFocusColor : highlightLayerColor;
1976
2013
  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 &&
2014
+ }), _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
2015
  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
2016
  }
1980
2017
  /**
@@ -2024,6 +2061,9 @@ export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingC
2024
2061
  const focusDirectory = resolved.focusDirectory;
2025
2062
  const focusColor = resolved.focusColor;
2026
2063
  const isolationMode = resolved.isolationMode;
2064
+ // User-supplied highlight layers only — used for visibility decisions so
2065
+ // file-color layers don't keep every building visible in 'hide' mode.
2066
+ const visibilityLayers = useMemo(() => (externalHighlightLayers ?? []), [externalHighlightLayers]);
2027
2067
  // `selectedPath` wins over the deprecated `selectedBuilding` when both are
2028
2068
  // set. A path resolves to either a building (file selection) or a district
2029
2069
  // (directory selection) — never both.
@@ -2107,6 +2147,6 @@ export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingC
2107
2147
  height: '100%',
2108
2148
  opacity: cameraReady ? 1 : 0,
2109
2149
  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) }))] }));
2150
+ }, 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
2151
  }
2112
2152
  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.52",
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,38 @@ 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
804
821
  const hasActiveHighlightLayers = useMemo(() => {
805
- return highlightLayers.some(layer => layer.enabled && layer.items.length > 0);
806
- }, [highlightLayers]);
822
+ return visibilityLayers.some(layer => layer.enabled && layer.items.length > 0);
823
+ }, [visibilityLayers]);
824
+
825
+ // Directories matched by a directory-type item from a user-supplied layer
826
+ // that also contain a file-type item from any user-supplied layer. Inside
827
+ // these directories, the directory match alone isn't enough to count as
828
+ // "specifically highlighted" — file-level matches define the visible subset.
829
+ const narrowedDirectories = useMemo(() => {
830
+ const dirs: string[] = [];
831
+ const files: string[] = [];
832
+ for (const layer of visibilityLayers) {
833
+ if (!layer.enabled) continue;
834
+ for (const item of layer.items) {
835
+ if (item.type === 'directory') dirs.push(item.path);
836
+ else if (item.type === 'file') files.push(item.path);
837
+ }
838
+ }
839
+ const narrowed = new Set<string>();
840
+ for (const dir of dirs) {
841
+ for (const f of files) {
842
+ if (f === dir || f.startsWith(dir + '/')) {
843
+ narrowed.add(dir);
844
+ break;
845
+ }
846
+ }
847
+ }
848
+ return narrowed;
849
+ }, [visibilityLayers]);
807
850
 
808
851
  // Initialize height and dim multiplier arrays
809
852
  useEffect(() => {
@@ -816,40 +859,56 @@ function InstancedBuildings({
816
859
  targetMultipliersRef.current = new Float32Array(buildings.length).fill(1);
817
860
  dimMultipliersRef.current = new Float32Array(buildings.length).fill(1);
818
861
  targetDimRef.current = new Float32Array(buildings.length).fill(1);
862
+ hiddenRef.current = new Uint8Array(buildings.length);
819
863
  }
820
864
  }
821
865
  }, [buildings.length]);
822
866
 
823
867
  // Update target multipliers when focusDirectory or highlightLayers change
824
868
  useEffect(() => {
825
- if (!targetMultipliersRef.current || !targetDimRef.current) return;
869
+ if (!targetMultipliersRef.current || !targetDimRef.current || !hiddenRef.current) return;
826
870
 
827
871
  buildings.forEach((building, index) => {
828
872
  let shouldCollapse = false;
829
873
  let shouldDim = false;
874
+ let shouldHide = false;
830
875
 
831
876
  const isInFocusDirectory = focusDirectory
832
877
  ? isPathInDirectory(building.path, focusDirectory)
833
878
  : true; // No focusDirectory means all are "in focus"
834
879
 
880
+ const layerMatches = hasActiveHighlightLayers
881
+ ? getLayerMatchesForPath(building.path, visibilityLayers)
882
+ : [];
835
883
  const isHighlighted = hasActiveHighlightLayers
836
- ? getHighlightForPath(building.path, highlightLayers) !== null
884
+ ? layerMatches.length > 0
837
885
  : true; // No highlights means all are "highlighted"
838
886
 
839
- // Determine collapse and dim behavior based on what's active:
840
- // - focusDirectory only: collapse if outside focus
841
- // - highlightLayers only (with collapse mode): collapse if not highlighted
842
- // - both: collapse if outside focus, dim if in focus but not highlighted
887
+ // A directory match doesn't count as "specifically highlighted" when
888
+ // that directory has been narrowed by file-level matches — the
889
+ // file-level matches define the visible subset within it.
890
+ const isSpecificallyHighlighted = hasActiveHighlightLayers
891
+ ? layerMatches.some(m =>
892
+ m.item.type === 'file' || !narrowedDirectories.has(m.item.path),
893
+ )
894
+ : true;
895
+
896
+ // Determine collapse/dim/hide behavior based on what's active. The
897
+ // "specifically highlighted" check applies in both collapse and hide
898
+ // modes so directory matches narrowed by file-level matches don't keep
899
+ // every sibling visible.
843
900
  if (focusDirectory && hasActiveHighlightLayers && isolationMode === 'collapse') {
844
- // Both active: collapse if outside focus, dim if in focus but not highlighted
845
901
  shouldCollapse = !isInFocusDirectory;
846
- shouldDim = isInFocusDirectory && !isHighlighted;
902
+ shouldDim = isInFocusDirectory && !isSpecificallyHighlighted;
903
+ } else if (focusDirectory && hasActiveHighlightLayers && isolationMode === 'hide') {
904
+ shouldCollapse = !isInFocusDirectory;
905
+ shouldHide = isInFocusDirectory && !isSpecificallyHighlighted;
847
906
  } else if (focusDirectory) {
848
- // Focus only: collapse if outside focus directory
849
907
  shouldCollapse = !isInFocusDirectory;
850
908
  } else if (hasActiveHighlightLayers && isolationMode === 'collapse') {
851
- // Highlight only with collapse: collapse if not highlighted
852
- shouldCollapse = !isHighlighted;
909
+ shouldCollapse = !isSpecificallyHighlighted;
910
+ } else if (hasActiveHighlightLayers && isolationMode === 'hide') {
911
+ shouldHide = !isSpecificallyHighlighted;
853
912
  }
854
913
 
855
914
  // Height: 1.0 = full, 0.05 = flat (collapsed or dimmed)
@@ -861,8 +920,10 @@ function InstancedBuildings({
861
920
  // Dim ref controls graying: 0 = gray out, 1 = keep color
862
921
  // Collapsed buildings go gray, dimmed buildings keep their color
863
922
  targetDimRef.current![index] = shouldCollapse ? 0 : 1;
923
+ // Hidden ref controls full invisibility (mesh + edges + icon)
924
+ hiddenRef.current![index] = shouldHide ? 1 : 0;
864
925
  });
865
- }, [focusDirectory, buildings, highlightLayers, isolationMode, hasActiveHighlightLayers]);
926
+ }, [focusDirectory, buildings, visibilityLayers, isolationMode, hasActiveHighlightLayers, narrowedDirectories]);
866
927
 
867
928
  // Pre-compute building data
868
929
  const buildingData = useMemo(() => {
@@ -962,6 +1023,16 @@ function InstancedBuildings({
962
1023
  buildingData.forEach((data, instanceIndex) => {
963
1024
  const { width, depth, fullHeight, x, z, staggerDelayMs } = data;
964
1025
 
1026
+ const isHidden = hiddenRef.current?.[instanceIndex] === 1;
1027
+
1028
+ if (isHidden) {
1029
+ tempObject.position.set(x, baseOffset, z);
1030
+ tempObject.scale.set(0, 0, 0);
1031
+ tempObject.updateMatrix();
1032
+ meshRef.current!.setMatrixAt(instanceIndex, tempObject.matrix);
1033
+ return;
1034
+ }
1035
+
965
1036
  // Animate height multiplier towards target
966
1037
  const currentMultiplier = heightMultipliersRef.current![instanceIndex];
967
1038
  const targetMultiplier = targetMultipliersRef.current![instanceIndex];
@@ -1089,6 +1160,7 @@ function InstancedBuildings({
1089
1160
  baseOffset={baseOffset}
1090
1161
  springDuration={springDuration}
1091
1162
  heightMultipliersRef={heightMultipliersRef}
1163
+ hiddenRef={hiddenRef}
1092
1164
  />
1093
1165
 
1094
1166
  {/* Border highlights (colored, layer-driven) */}
@@ -1123,6 +1195,8 @@ interface BuildingIconsProps {
1123
1195
  linearScale: number;
1124
1196
  flatPatterns: FlatPattern[];
1125
1197
  highlightLayers: HighlightLayer[];
1198
+ /** User-supplied highlight layers only (excludes file-color layers). */
1199
+ visibilityLayers: HighlightLayer[];
1126
1200
  isolationMode: IsolationMode;
1127
1201
  hasActiveHighlights: boolean;
1128
1202
  }
@@ -1205,9 +1279,34 @@ function BuildingIcons({
1205
1279
  linearScale,
1206
1280
  flatPatterns,
1207
1281
  highlightLayers,
1282
+ visibilityLayers,
1208
1283
  isolationMode,
1209
1284
  hasActiveHighlights,
1210
1285
  }: BuildingIconsProps) {
1286
+ // Same narrowing rule as InstancedBuildings, scoped to user highlight layers
1287
+ // only (file-color layers don't narrow visibility).
1288
+ const narrowedDirectories = useMemo(() => {
1289
+ const dirs: string[] = [];
1290
+ const files: string[] = [];
1291
+ for (const layer of visibilityLayers) {
1292
+ if (!layer.enabled) continue;
1293
+ for (const item of layer.items) {
1294
+ if (item.type === 'directory') dirs.push(item.path);
1295
+ else if (item.type === 'file') files.push(item.path);
1296
+ }
1297
+ }
1298
+ const narrowed = new Set<string>();
1299
+ for (const dir of dirs) {
1300
+ for (const f of files) {
1301
+ if (f === dir || f.startsWith(dir + '/')) {
1302
+ narrowed.add(dir);
1303
+ break;
1304
+ }
1305
+ }
1306
+ }
1307
+ return narrowed;
1308
+ }, [visibilityLayers]);
1309
+
1211
1310
  // Pre-compute buildings with icons
1212
1311
  const buildingsWithIcons = useMemo(() => {
1213
1312
  return buildings
@@ -1215,10 +1314,14 @@ function BuildingIcons({
1215
1314
  const config = getConfigForFile(building);
1216
1315
  if (!config.icon) return null;
1217
1316
 
1218
- const highlight = getHighlightForPath(building.path, highlightLayers);
1219
- const isHighlighted = highlight !== null;
1220
- const shouldDim = hasActiveHighlights && !isHighlighted;
1221
- const shouldHide = shouldDim && isolationMode === 'hide';
1317
+ const matches = getLayerMatchesForPath(building.path, visibilityLayers);
1318
+ const isHighlighted = matches.length > 0;
1319
+ const isSpecificallyHighlighted = matches.some(
1320
+ m => m.item.type === 'file' || !narrowedDirectories.has(m.item.path),
1321
+ );
1322
+ const shouldDim = hasActiveHighlights && !isSpecificallyHighlighted;
1323
+ const shouldHide =
1324
+ hasActiveHighlights && isolationMode === 'hide' && !isSpecificallyHighlighted;
1222
1325
  const shouldCollapse = shouldDim && isolationMode === 'collapse';
1223
1326
 
1224
1327
  // Hide icons for buildings that are hidden or collapsed
@@ -1250,12 +1353,13 @@ function BuildingIcons({
1250
1353
  }, [
1251
1354
  buildings,
1252
1355
  centerOffset,
1253
- highlightLayers,
1356
+ visibilityLayers,
1254
1357
  isolationMode,
1255
1358
  hasActiveHighlights,
1256
1359
  heightScaling,
1257
1360
  linearScale,
1258
1361
  flatPatterns,
1362
+ narrowedDirectories,
1259
1363
  ]);
1260
1364
 
1261
1365
  // Icons are now always rendered (flat or grown)
@@ -1340,14 +1444,6 @@ function DistrictFloor({ district, centerOffset, highlightColor, growProgress }:
1340
1444
  <lineBasicMaterial color={borderColor} linewidth={lineWidth} depthWrite={false} />
1341
1445
  </lineSegments>
1342
1446
 
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
1447
  {/* Always show directory name label */}
1352
1448
  <Text
1353
1449
  position={[0, textY, textZ]}
@@ -1658,7 +1754,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
1658
1754
  // This will be corrected in Frame 1 if aspect is different
1659
1755
  const initialHeight = calculateFlatCameraHeight(1);
1660
1756
 
1661
- console.log('[Spring init] Initializing with 2D position, height:', initialHeight);
1662
1757
  return {
1663
1758
  camX: 0,
1664
1759
  camY: initialHeight,
@@ -1668,16 +1763,11 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
1668
1763
  lookZ: 0,
1669
1764
  config: { tension: 60, friction: 20 },
1670
1765
  onStart: () => {
1671
- // Only allow animations after initial setup is complete
1672
1766
  if (hasAppliedInitial.current) {
1673
- console.log('[Spring onStart] Animation starting - camY:', camY.get());
1674
1767
  isAnimatingRef.current = true;
1675
- } else {
1676
- console.log('[Spring onStart] Blocked - initialization not complete');
1677
1768
  }
1678
1769
  },
1679
1770
  onRest: () => {
1680
- console.log('[Spring onRest] Animation finished');
1681
1771
  isAnimatingRef.current = false;
1682
1772
  },
1683
1773
  };
@@ -1728,23 +1818,11 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
1728
1818
  // When isFlat changes from true to false, animate to 3D view
1729
1819
  // Component always starts in 2D, so we only animate the 2D→3D transition
1730
1820
  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
- }
1821
+ if (!hasAppliedInitial.current) return;
1738
1822
 
1739
- // Only animate if isFlat changed from true to false (2D → 3D transition)
1740
1823
  const isFlatChanged = prevIsFlatRef.current !== isFlat;
1824
+ if (!isFlatChanged) return;
1741
1825
 
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
1826
  prevIsFlatRef.current = isFlat;
1749
1827
 
1750
1828
  // Calculate target position for 3D view
@@ -1768,7 +1846,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
1768
1846
  targetZ: 0,
1769
1847
  };
1770
1848
 
1771
- console.log('[api.start#isFlat-toggle]', newPos);
1772
1849
  api.start({
1773
1850
  camX: newPos.x,
1774
1851
  camY: newPos.y,
@@ -1838,7 +1915,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
1838
1915
  };
1839
1916
  }
1840
1917
 
1841
- console.log('[api.start#focus-target]', { focusTarget, isFlat, newPos });
1842
1918
  api.start({
1843
1919
  camX: newPos.x,
1844
1920
  camY: newPos.y,
@@ -1860,14 +1936,12 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
1860
1936
  // Ensure camera FOV is correct (defaults to 75 before prop applies)
1861
1937
  const perspCam = camera as THREE.PerspectiveCamera;
1862
1938
  if (perspCam.fov !== 50) {
1863
- console.log('[Frame 1] Correcting FOV from', perspCam.fov, 'to 50');
1864
1939
  perspCam.fov = 50;
1865
1940
  perspCam.updateProjectionMatrix();
1866
1941
  }
1867
1942
 
1868
1943
  // Calculate initial 2D position with correct aspect ratio
1869
1944
  const initialPos = getInitial2DPosition();
1870
- console.log('[Frame 1] Setting camera to initial 2D position:', initialPos);
1871
1945
 
1872
1946
  camera.position.set(initialPos.x, initialPos.y, initialPos.z);
1873
1947
 
@@ -1877,7 +1951,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
1877
1951
  controlsRef.current.update();
1878
1952
 
1879
1953
  // Sync spring to match camera position (use immediate to avoid animation)
1880
- console.log('[api.start#frame1-immediate]', initialPos);
1881
1954
  api.start({
1882
1955
  camX: initialPos.x,
1883
1956
  camY: initialPos.y,
@@ -1902,40 +1975,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
1902
1975
  // Wait for controls and initialization to complete
1903
1976
  if (!controlsRef.current || !hasAppliedInitial.current) return;
1904
1977
 
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
1978
  // Handle orbit animation (horizontal rotation along arc)
1940
1979
  if (isOrbitingRef.current && orbitParamsRef.current) {
1941
1980
  const { centerX, centerZ, distance, height } = orbitParamsRef.current;
@@ -2002,7 +2041,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
2002
2041
  const targetHeight = citySize * 1.1;
2003
2042
  const targetZ = citySize * 1.3;
2004
2043
 
2005
- console.log('[api.start#resetToInitial]', { targetHeight, targetZ });
2006
2044
  api.start({
2007
2045
  camX: 0,
2008
2046
  camY: targetHeight,
@@ -2018,7 +2056,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
2018
2056
  const distance = Math.max(effectiveSize * 2, 50);
2019
2057
  const height = Math.max(effectiveSize * 1.5, 40);
2020
2058
 
2021
- console.log('[api.start#moveTo]', { x, z, height, distance });
2022
2059
  api.start({
2023
2060
  camX: x,
2024
2061
  camY: height,
@@ -2050,7 +2087,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
2050
2087
  });
2051
2088
  }
2052
2089
  const config = options?.duration ? { duration: options.duration } : undefined;
2053
- console.log('[api.start#setFlatView]', { x, z, height, options });
2054
2090
  api.start({
2055
2091
  camX: x,
2056
2092
  camY: height,
@@ -2085,7 +2121,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({
2085
2121
  ? { duration: options.duration, easing: (t: number) => t }
2086
2122
  : { tension: 60, friction: 20 };
2087
2123
 
2088
- console.log('[api.start#setTarget]', { x, y, z, newCamX, newCamY, newCamZ });
2089
2124
  api.start({
2090
2125
  camX: newCamX,
2091
2126
  camY: newCamY,
@@ -2826,6 +2861,8 @@ interface CitySceneProps {
2826
2861
  growProgress: number;
2827
2862
  animationConfig: AnimationConfig;
2828
2863
  highlightLayers: HighlightLayer[];
2864
+ /** User-supplied highlight layers (no file-color layers) for visibility logic. */
2865
+ visibilityLayers: HighlightLayer[];
2829
2866
  isolationMode: IsolationMode;
2830
2867
  heightScaling: HeightScaling;
2831
2868
  linearScale: number;
@@ -2851,6 +2888,7 @@ function CityScene({
2851
2888
  growProgress,
2852
2889
  animationConfig,
2853
2890
  highlightLayers,
2891
+ visibilityLayers,
2854
2892
  isolationMode,
2855
2893
  heightScaling,
2856
2894
  linearScale,
@@ -2884,7 +2922,7 @@ function CityScene({
2884
2922
  return Math.max(...cityData.buildings.map(b => b.dimensions[1]), 0);
2885
2923
  }, [adaptCameraToBuildings, cityData.buildings]);
2886
2924
 
2887
- const activeHighlights = useMemo(() => hasActiveHighlights(highlightLayers), [highlightLayers]);
2925
+ const activeHighlights = useMemo(() => hasActiveHighlights(visibilityLayers), [visibilityLayers]);
2888
2926
 
2889
2927
  // Helper to check if a path is inside a directory
2890
2928
  const isPathInDirectory = useCallback((path: string, directory: string) => {
@@ -3168,6 +3206,7 @@ function CityScene({
3168
3206
  staggerIndices={staggerIndices}
3169
3207
  focusDirectory={buildingFocusDirectory}
3170
3208
  highlightLayers={highlightLayers}
3209
+ visibilityLayers={visibilityLayers}
3171
3210
  isolationMode={isolationMode}
3172
3211
  defaultBuildingColor={defaultBuildingColor}
3173
3212
  />
@@ -3180,6 +3219,7 @@ function CityScene({
3180
3219
  linearScale={linearScale}
3181
3220
  flatPatterns={flatPatterns}
3182
3221
  highlightLayers={highlightLayers}
3222
+ visibilityLayers={visibilityLayers}
3183
3223
  isolationMode={isolationMode}
3184
3224
  hasActiveHighlights={activeHighlights}
3185
3225
  />
@@ -3428,6 +3468,13 @@ export function FileCity3D({
3428
3468
  const focusColor = resolved.focusColor;
3429
3469
  const isolationMode = resolved.isolationMode as IsolationMode;
3430
3470
 
3471
+ // User-supplied highlight layers only — used for visibility decisions so
3472
+ // file-color layers don't keep every building visible in 'hide' mode.
3473
+ const visibilityLayers = useMemo(
3474
+ () => (externalHighlightLayers ?? []) as HighlightLayer[],
3475
+ [externalHighlightLayers],
3476
+ );
3477
+
3431
3478
  // `selectedPath` wins over the deprecated `selectedBuilding` when both are
3432
3479
  // set. A path resolves to either a building (file selection) or a district
3433
3480
  // (directory selection) — never both.
@@ -3553,6 +3600,7 @@ export function FileCity3D({
3553
3600
  growProgress={growProgress}
3554
3601
  animationConfig={animationConfig}
3555
3602
  highlightLayers={highlightLayers}
3603
+ visibilityLayers={visibilityLayers}
3556
3604
  isolationMode={isolationMode}
3557
3605
  heightScaling={heightScaling}
3558
3606
  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
  />