@principal-ai/file-city-react 0.5.37 → 0.5.39

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.
@@ -35,6 +35,42 @@ export interface AnimationConfig {
35
35
  }
36
36
  /** Height scaling mode for buildings */
37
37
  export type HeightScaling = 'logarithmic' | 'linear';
38
+ /**
39
+ * An opaque slab rendered above the flat city to visualize scope coverage.
40
+ * Only renders when the city is in 2D (flat) mode — in 3D the buildings show
41
+ * through normally. When opaque, the slab's depth value occludes buildings and
42
+ * icons beneath its `bounds`, so the scope reads as a single colored tile.
43
+ */
44
+ export interface ElevatedScopePanel {
45
+ /** Unique identifier (used as React key) */
46
+ id: string;
47
+ /** Hex color */
48
+ color: string;
49
+ /** 0–1 opacity. Default 1 (fully opaque). */
50
+ opacity?: number;
51
+ /** Y position (world units) above the ground when flat. Default 4. */
52
+ height?: number;
53
+ /** Slab thickness in world units (default 2) */
54
+ thickness?: number;
55
+ /** World-space bounds the slab covers */
56
+ bounds: {
57
+ minX: number;
58
+ maxX: number;
59
+ minZ: number;
60
+ maxZ: number;
61
+ };
62
+ /** Optional label rendered flat on top of the slab. */
63
+ label?: string;
64
+ /** Hex color for the label (default white). */
65
+ labelColor?: string;
66
+ /**
67
+ * Absolute label font size in world units. When omitted, falls back to a
68
+ * size derived from the panel's footprint. Always clamped to fit the tile.
69
+ */
70
+ labelSize?: number;
71
+ /** Click handler. When set, the slab becomes interactive and shows a pointer cursor. */
72
+ onClick?: () => void;
73
+ }
38
74
  /** Pattern for files that should render flat (e.g., lock files, generated files) */
39
75
  export interface FlatPattern {
40
76
  /** Glob-like pattern or regex to match file paths */
@@ -48,6 +84,32 @@ export interface RotateOptions {
48
84
  /** Animation duration in milliseconds. Default uses spring physics (~800ms feel). */
49
85
  duration?: number;
50
86
  }
87
+ export type MouseDragAction = 'pan' | 'rotate' | 'zoom' | 'none';
88
+ export type TouchOneAction = 'pan' | 'rotate' | 'none';
89
+ export type TouchTwoAction = 'pan' | 'rotate' | 'dolly-pan' | 'dolly-rotate' | 'none';
90
+ export type WheelAction = 'zoom' | 'pan';
91
+ export interface CameraControlsConfig {
92
+ /** Left mouse button drag. Default: 'pan' */
93
+ leftDrag?: MouseDragAction;
94
+ /** Right mouse button drag. Default: 'rotate' */
95
+ rightDrag?: MouseDragAction;
96
+ /** Middle mouse button drag. Default: 'zoom' */
97
+ middleDrag?: MouseDragAction;
98
+ /** Mouse wheel / two-finger trackpad scroll. Default: 'zoom'.
99
+ * When 'pan', ctrl/⌘+wheel still zooms (matches trackpad pinch). */
100
+ wheel?: WheelAction;
101
+ /** One-finger touch. Default: 'pan' */
102
+ oneFingerTouch?: TouchOneAction;
103
+ /** Two-finger touch. Default: 'dolly-pan' */
104
+ twoFingerTouch?: TouchTwoAction;
105
+ /** Pan speed multiplier. Default: 1 */
106
+ panSpeed?: number;
107
+ /** Rotate speed multiplier. Default: 1 */
108
+ rotateSpeed?: number;
109
+ /** Zoom speed multiplier. Default: 1 */
110
+ zoomSpeed?: number;
111
+ }
112
+ export declare const DEFAULT_CAMERA_CONTROLS: Required<Omit<CameraControlsConfig, 'panSpeed' | 'rotateSpeed' | 'zoomSpeed'>> & Pick<CameraControlsConfig, 'panSpeed' | 'rotateSpeed' | 'zoomSpeed'>;
51
113
  export declare function resetCamera(): void;
52
114
  export declare function moveCameraTo(x: number, z: number, size?: number): void;
53
115
  /**
@@ -164,6 +226,20 @@ export interface FileCity3DProps {
164
226
  adaptCameraToBuildings?: boolean;
165
227
  /** Base file type color layers (resolved with highlightLayers) */
166
228
  fileColorLayers?: HighlightLayer[];
229
+ /**
230
+ * Translucent slabs rendered above the city showing scope coverage as
231
+ * elevated planes over the directories they own.
232
+ */
233
+ elevatedScopePanels?: ElevatedScopePanel[];
234
+ /**
235
+ * Configure how mouse / trackpad / touch input drives the camera.
236
+ * Defaults match Google Maps style: left-drag pans, right-drag rotates,
237
+ * wheel zooms. Set `wheel: 'pan'` to make trackpad two-finger scroll pan
238
+ * (ctrl/⌘+wheel still zooms so pinch-zoom keeps working).
239
+ *
240
+ * Memoize this object to avoid unnecessary camera re-mounts.
241
+ */
242
+ cameraControls?: CameraControlsConfig;
167
243
  }
168
244
  /**
169
245
  * FileCity3D - 3D visualization of codebase structure
@@ -171,6 +247,6 @@ export interface FileCity3DProps {
171
247
  * Renders CityData as an interactive 3D city where buildings represent files
172
248
  * and their height corresponds to line count or file size.
173
249
  */
174
- export declare function FileCity3D({ cityData, width, height, onBuildingClick, className, style, animation, isGrown: externalIsGrown, onGrowChange, showControls, highlightLayers: externalHighlightLayers, isolationMode: externalIsolationMode, dimOpacity: _dimOpacity, isLoading, loadingMessage, emptyMessage, heightScaling, linearScale, flatPatterns, focusDirectory: externalFocusDirectory, focusColor: externalFocusColor, onDirectorySelect: _onDirectorySelect, backgroundColor, textColor, selectedBuilding, adaptCameraToBuildings, fileColorLayers, }: FileCity3DProps): import("react/jsx-runtime").JSX.Element;
250
+ export declare function FileCity3D({ cityData, width, height, onBuildingClick, className, style, animation, isGrown: externalIsGrown, onGrowChange, showControls, elevatedScopePanels, highlightLayers: externalHighlightLayers, isolationMode: externalIsolationMode, dimOpacity: _dimOpacity, isLoading, loadingMessage, emptyMessage, heightScaling, linearScale, flatPatterns, focusDirectory: externalFocusDirectory, focusColor: externalFocusColor, onDirectorySelect: _onDirectorySelect, backgroundColor, textColor, selectedBuilding, adaptCameraToBuildings, fileColorLayers, cameraControls, }: FileCity3DProps): import("react/jsx-runtime").JSX.Element;
175
251
  export default FileCity3D;
176
252
  //# sourceMappingURL=FileCity3D.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FileCity3D.d.ts","sourceRoot":"","sources":["../../../src/components/FileCity3D/FileCity3D.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"FileCity3D.d.ts","sourceRoot":"","sources":["../../../src/components/FileCity3D/FileCity3D.tsx"],"names":[],"mappings":"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"}
@@ -10,7 +10,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
10
10
  import React, { useMemo, useRef, useState, useEffect, useCallback } from 'react';
11
11
  import { Canvas, useFrame, useThree } from '@react-three/fiber';
12
12
  import { useSpring } from '@react-spring/three';
13
- import { OrbitControls, PerspectiveCamera, Text } from '@react-three/drei';
13
+ import { MapControls, PerspectiveCamera, Text } from '@react-three/drei';
14
14
  import { getFileConfig } from '@principal-ai/file-city-builder';
15
15
  import * as THREE from 'three';
16
16
  import { resolveVisualizationIntent } from '../../utils/visualizationResolution';
@@ -777,6 +777,38 @@ function DistrictFloor({ district, centerOffset, highlightColor, growProgress })
777
777
  const textZ = flatZ + (grownZ - flatZ) * growProgress;
778
778
  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 })] }));
779
779
  }
780
+ export const DEFAULT_CAMERA_CONTROLS = {
781
+ leftDrag: 'pan',
782
+ rightDrag: 'rotate',
783
+ middleDrag: 'zoom',
784
+ wheel: 'pan',
785
+ oneFingerTouch: 'pan',
786
+ twoFingerTouch: 'dolly-pan',
787
+ };
788
+ function mouseAction(action) {
789
+ switch (action) {
790
+ case 'pan': return THREE.MOUSE.PAN;
791
+ case 'rotate': return THREE.MOUSE.ROTATE;
792
+ case 'zoom': return THREE.MOUSE.DOLLY;
793
+ case 'none': return undefined;
794
+ }
795
+ }
796
+ function touchOneAction(action) {
797
+ switch (action) {
798
+ case 'pan': return THREE.TOUCH.PAN;
799
+ case 'rotate': return THREE.TOUCH.ROTATE;
800
+ case 'none': return undefined;
801
+ }
802
+ }
803
+ function touchTwoAction(action) {
804
+ switch (action) {
805
+ case 'pan': return THREE.TOUCH.PAN;
806
+ case 'rotate': return THREE.TOUCH.ROTATE;
807
+ case 'dolly-pan': return THREE.TOUCH.DOLLY_PAN;
808
+ case 'dolly-rotate': return THREE.TOUCH.DOLLY_ROTATE;
809
+ case 'none': return undefined;
810
+ }
811
+ }
780
812
  let cameraApi = null;
781
813
  export function resetCamera() {
782
814
  cameraApi?.reset();
@@ -853,10 +885,12 @@ export function getCameraAngle() {
853
885
  export function getCameraTilt() {
854
886
  return cameraApi?.getCurrentTilt() ?? null;
855
887
  }
856
- const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, focusTarget, maxBuildingHeight = 0, onCameraReady, }) {
888
+ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, focusTarget, maxBuildingHeight = 0, cameraControls, onCameraReady, }) {
857
889
  // Use selector to only subscribe to camera, not the entire R3F state
858
890
  // This prevents re-renders on pointer movement
859
891
  const camera = useThree((state) => state.camera);
892
+ const gl = useThree((state) => state.gl);
893
+ const controlsConfig = useMemo(() => ({ ...DEFAULT_CAMERA_CONTROLS, ...cameraControls }), [cameraControls]);
860
894
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
861
895
  const controlsRef = useRef(null);
862
896
  const isAnimatingRef = useRef(false);
@@ -1069,7 +1103,7 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1069
1103
  const currentTilt = tiltAngle.get();
1070
1104
  // Convert tilt angle to polar angle (0° tilt = looking down, 90° tilt = level)
1071
1105
  // Clamp to avoid extreme angles
1072
- const clampedTilt = Math.max(5, Math.min(85, currentTilt));
1106
+ const clampedTilt = Math.max(0, Math.min(85, currentTilt));
1073
1107
  const polarRadians = (clampedTilt * Math.PI) / 180;
1074
1108
  const azimuthRadians = (azimuthAngle * Math.PI) / 180;
1075
1109
  // Spherical to Cartesian conversion
@@ -1331,13 +1365,73 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1331
1365
  cameraApi = null;
1332
1366
  };
1333
1367
  }, [resetToInitial, moveTo, setTarget, rotateTo, rotateBy, tiltTo, tiltBy, getCurrentPosition, getCurrentTarget, getCurrentAngle, getCurrentTilt]);
1334
- return (_jsxs(_Fragment, { children: [_jsx(PerspectiveCamera, { makeDefault: true, fov: 50, near: 1, far: citySize * 10 }), _jsx(OrbitControls, { ref: controlsRef, enableDamping: true, dampingFactor: 0.05, minDistance: 10, maxDistance: citySize * 3, maxPolarAngle: Math.PI / 2.1 })] }));
1368
+ // Custom wheel handler for wheel === 'pan'. We disable MapControls' built-in
1369
+ // zoom (otherwise it competes with our handler) and handle both axes here:
1370
+ // ctrl/⌘+wheel = zoom (matches trackpad pinch), plain wheel = pan along the
1371
+ // camera-relative ground plane.
1372
+ useEffect(() => {
1373
+ if (controlsConfig.wheel !== 'pan')
1374
+ return;
1375
+ const canvas = gl.domElement;
1376
+ const right = new THREE.Vector3();
1377
+ const forward = new THREE.Vector3();
1378
+ const offset = new THREE.Vector3();
1379
+ const direction = new THREE.Vector3();
1380
+ const panSpeed = controlsConfig.panSpeed ?? 1;
1381
+ const zoomSpeed = controlsConfig.zoomSpeed ?? 1;
1382
+ const onWheel = (e) => {
1383
+ const controls = controlsRef.current;
1384
+ if (!controls)
1385
+ return;
1386
+ e.preventDefault();
1387
+ const target = controls.target;
1388
+ if (e.ctrlKey || e.metaKey) {
1389
+ direction.subVectors(camera.position, target);
1390
+ const distance = direction.length();
1391
+ const scale = Math.exp(e.deltaY * 0.01 * zoomSpeed);
1392
+ const minD = controls.minDistance ?? 0;
1393
+ const maxD = controls.maxDistance ?? Infinity;
1394
+ const newDistance = Math.min(Math.max(distance * scale, minD), maxD);
1395
+ direction.normalize().multiplyScalar(newDistance);
1396
+ camera.position.copy(target).add(direction);
1397
+ controls.update();
1398
+ return;
1399
+ }
1400
+ const distance = camera.position.distanceTo(target);
1401
+ const factor = distance * 0.0015 * panSpeed;
1402
+ camera.getWorldDirection(forward);
1403
+ forward.y = 0;
1404
+ if (forward.lengthSq() < 1e-6)
1405
+ forward.set(0, 0, -1);
1406
+ forward.normalize();
1407
+ right.crossVectors(forward, camera.up).normalize();
1408
+ offset.set(0, 0, 0);
1409
+ offset.addScaledVector(right, e.deltaX * factor);
1410
+ offset.addScaledVector(forward, -e.deltaY * factor);
1411
+ camera.position.add(offset);
1412
+ target.add(offset);
1413
+ controls.update();
1414
+ };
1415
+ canvas.addEventListener('wheel', onWheel, { passive: false });
1416
+ return () => canvas.removeEventListener('wheel', onWheel);
1417
+ }, [camera, gl, controlsConfig.wheel, controlsConfig.panSpeed, controlsConfig.zoomSpeed]);
1418
+ const mouseButtons = useMemo(() => ({
1419
+ LEFT: mouseAction(controlsConfig.leftDrag),
1420
+ MIDDLE: mouseAction(controlsConfig.middleDrag),
1421
+ RIGHT: mouseAction(controlsConfig.rightDrag),
1422
+ }), [controlsConfig.leftDrag, controlsConfig.middleDrag, controlsConfig.rightDrag]);
1423
+ const touches = useMemo(() => ({
1424
+ ONE: touchOneAction(controlsConfig.oneFingerTouch),
1425
+ TWO: touchTwoAction(controlsConfig.twoFingerTouch),
1426
+ }), [controlsConfig.oneFingerTouch, controlsConfig.twoFingerTouch]);
1427
+ return (_jsxs(_Fragment, { children: [_jsx(PerspectiveCamera, { makeDefault: true, fov: 50, near: 1, far: citySize * 10 }), _jsx(MapControls, { ref: controlsRef, enableDamping: true, dampingFactor: 0.05, minDistance: 10, maxDistance: citySize * 3, maxPolarAngle: Math.PI / 2.1, mouseButtons: mouseButtons, touches: touches, enableZoom: controlsConfig.wheel !== 'pan', panSpeed: controlsConfig.panSpeed ?? 1, rotateSpeed: controlsConfig.rotateSpeed ?? 1, zoomSpeed: controlsConfig.zoomSpeed ?? 1 })] }));
1335
1428
  }, (prevProps, nextProps) => {
1336
- // Custom comparison: only re-render if isFlat, citySize, or maxBuildingHeight changes
1429
+ // Custom comparison: only re-render if isFlat, citySize, maxBuildingHeight, or cameraControls changes
1337
1430
  // Skip re-render when only focusTarget changes (handled internally by useEffect on isFlat)
1338
1431
  return (prevProps.isFlat === nextProps.isFlat &&
1339
1432
  prevProps.citySize === nextProps.citySize &&
1340
- prevProps.maxBuildingHeight === nextProps.maxBuildingHeight);
1433
+ prevProps.maxBuildingHeight === nextProps.maxBuildingHeight &&
1434
+ prevProps.cameraControls === nextProps.cameraControls);
1341
1435
  });
1342
1436
  function InfoPanel({ building }) {
1343
1437
  if (!building)
@@ -1347,7 +1441,7 @@ function InfoPanel({ building }) {
1347
1441
  return (_jsxs("div", { style: {
1348
1442
  position: 'absolute',
1349
1443
  bottom: 16,
1350
- left: 16,
1444
+ left: 60,
1351
1445
  background: 'rgba(15, 23, 42, 0.9)',
1352
1446
  border: '1px solid #334155',
1353
1447
  borderRadius: 8,
@@ -1365,7 +1459,7 @@ function InfoPanel({ building }) {
1365
1459
  gap: 12,
1366
1460
  }, children: [building.lineCount !== undefined && (_jsxs("span", { children: [building.lineCount.toLocaleString(), " lines"] })), building.size !== undefined && _jsxs("span", { children: [(building.size / 1024).toFixed(1), " KB"] })] })] }));
1367
1461
  }
1368
- function ControlsOverlay({ isFlat, onToggle, onResetCamera }) {
1462
+ function ControlsOverlay({ isFlat, onToggle, onResetCamera, onLookDown }) {
1369
1463
  const buttonStyle = {
1370
1464
  background: 'rgba(15, 23, 42, 0.9)',
1371
1465
  border: '1px solid #334155',
@@ -1391,9 +1485,14 @@ function ControlsOverlay({ isFlat, onToggle, onResetCamera }) {
1391
1485
  position: 'absolute',
1392
1486
  top: 8,
1393
1487
  right: 8,
1394
- }, title: "Reset View", children: "\u21BB" })] }));
1488
+ }, title: "Reset View", children: "\u21BB" }), _jsx("button", { onClick: onLookDown, style: {
1489
+ ...buttonStyle,
1490
+ position: 'absolute',
1491
+ bottom: 8,
1492
+ left: 8,
1493
+ }, title: "Look down", children: "\u2B07" })] }));
1395
1494
  }
1396
- function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding, selectedBuilding, growProgress, animationConfig, highlightLayers, isolationMode, heightScaling, linearScale, flatPatterns, focusDirectory, focusColor, adaptCameraToBuildings = false, onCameraReady, }) {
1495
+ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding, selectedBuilding, growProgress, animationConfig, highlightLayers, isolationMode, heightScaling, linearScale, flatPatterns, focusDirectory, focusColor, adaptCameraToBuildings = false, elevatedScopePanels, cameraControls, onCameraReady, }) {
1397
1496
  const centerOffset = useMemo(() => ({
1398
1497
  x: (cityData.bounds.minX + cityData.bounds.maxX) / 2,
1399
1498
  z: (cityData.bounds.minZ + cityData.bounds.maxZ) / 2,
@@ -1557,7 +1656,7 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
1557
1656
  return null;
1558
1657
  return cityData.buildings.findIndex(b => b.path === selectedBuilding.path);
1559
1658
  }, [selectedBuilding, cityData.buildings]);
1560
- return (_jsxs(_Fragment, { children: [_jsx(AnimatedCamera, { citySize: citySize, isFlat: growProgress === 0, focusTarget: focusTarget, maxBuildingHeight: maxBuildingHeight, onCameraReady: onCameraReady }), _jsx("ambientLight", { intensity: 1.2 }), _jsx("hemisphereLight", { args: ['#ddeeff', '#667788', 0.8], position: [0, citySize, 0] }), _jsx("directionalLight", { position: [citySize, citySize * 1.5, citySize * 0.5], intensity: 2, castShadow: true, "shadow-mapSize": [2048, 2048] }), _jsx("directionalLight", { position: [-citySize * 0.5, citySize * 0.8, -citySize * 0.5], intensity: 1 }), _jsx("directionalLight", { position: [citySize * 0.3, citySize, citySize], intensity: 0.6 }), cityData.districts.map(district => {
1659
+ return (_jsxs(_Fragment, { children: [_jsx(AnimatedCamera, { citySize: citySize, isFlat: growProgress === 0, focusTarget: focusTarget, maxBuildingHeight: maxBuildingHeight, cameraControls: cameraControls, onCameraReady: onCameraReady }), _jsx("ambientLight", { intensity: 1.2 }), _jsx("hemisphereLight", { args: ['#ddeeff', '#667788', 0.8], position: [0, citySize, 0] }), _jsx("directionalLight", { position: [citySize, citySize * 1.5, citySize * 0.5], intensity: 2, castShadow: true, "shadow-mapSize": [2048, 2048] }), _jsx("directionalLight", { position: [-citySize * 0.5, citySize * 0.8, -citySize * 0.5], intensity: 1 }), _jsx("directionalLight", { position: [citySize * 0.3, citySize, citySize], intensity: 0.6 }), cityData.districts.map(district => {
1561
1660
  // Check if district matches focusDirectory
1562
1661
  const isFocused = buildingFocusDirectory
1563
1662
  ? district.path === buildingFocusDirectory
@@ -1580,7 +1679,43 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
1580
1679
  // Focus color takes priority, then highlight layer color
1581
1680
  const districtColor = (isFocused && buildingFocusColor) ? buildingFocusColor : highlightLayerColor;
1582
1681
  return (_jsx(DistrictFloor, { district: district, centerOffset: centerOffset, opacity: 1, highlightColor: districtColor, growProgress: growProgress }, district.path));
1583
- }), _jsx(InstancedBuildings, { buildings: cityData.buildings, centerOffset: centerOffset, onHover: onBuildingHover, onClick: onBuildingClick, hoveredIndex: hoveredIndex, selectedIndex: selectedIndex, growProgress: growProgress, animationConfig: animationConfig, heightScaling: heightScaling, linearScale: linearScale, flatPatterns: flatPatterns, staggerIndices: staggerIndices, focusDirectory: buildingFocusDirectory, highlightLayers: highlightLayers, isolationMode: isolationMode }), _jsx(BuildingIcons, { buildings: cityData.buildings, centerOffset: centerOffset, growProgress: growProgress, heightScaling: heightScaling, linearScale: linearScale, flatPatterns: flatPatterns, highlightLayers: highlightLayers, isolationMode: isolationMode, hasActiveHighlights: activeHighlights })] }));
1682
+ }), _jsx(InstancedBuildings, { buildings: cityData.buildings, centerOffset: centerOffset, onHover: onBuildingHover, onClick: onBuildingClick, hoveredIndex: hoveredIndex, selectedIndex: selectedIndex, growProgress: growProgress, animationConfig: animationConfig, heightScaling: heightScaling, linearScale: linearScale, flatPatterns: flatPatterns, staggerIndices: staggerIndices, focusDirectory: buildingFocusDirectory, highlightLayers: highlightLayers, isolationMode: isolationMode }), _jsx(BuildingIcons, { buildings: cityData.buildings, centerOffset: centerOffset, growProgress: growProgress, heightScaling: heightScaling, linearScale: linearScale, flatPatterns: flatPatterns, highlightLayers: highlightLayers, isolationMode: isolationMode, hasActiveHighlights: activeHighlights }), growProgress === 0 &&
1683
+ elevatedScopePanels?.map(panel => {
1684
+ const cx = (panel.bounds.minX + panel.bounds.maxX) / 2 - centerOffset.x;
1685
+ const cz = (panel.bounds.minZ + panel.bounds.maxZ) / 2 - centerOffset.z;
1686
+ const w = Math.max(1, panel.bounds.maxX - panel.bounds.minX);
1687
+ const d = Math.max(1, panel.bounds.maxZ - panel.bounds.minZ);
1688
+ const t = panel.thickness ?? 2;
1689
+ const y = (panel.height ?? 4) + t / 2;
1690
+ const opacity = panel.opacity ?? 1;
1691
+ const isOpaque = opacity >= 1;
1692
+ const topY = y + t / 2;
1693
+ // Size text to the panel: roughly fit longest reasonable label,
1694
+ // clamped so tiny tiles still render legibly and huge ones don't
1695
+ // get absurd. Callers may override via panel.labelSize, but we
1696
+ // still cap to the tile footprint so the label fits.
1697
+ const tileMax = Math.min(w, d) / 2;
1698
+ const requested = panel.labelSize ?? Math.min(w, d) / 6;
1699
+ const labelSize = Math.max(4, Math.min(tileMax, requested));
1700
+ const handleClick = panel.onClick
1701
+ ? (e) => {
1702
+ e.stopPropagation();
1703
+ panel.onClick();
1704
+ }
1705
+ : undefined;
1706
+ const handlePointerOver = panel.onClick
1707
+ ? (e) => {
1708
+ e.stopPropagation();
1709
+ document.body.style.cursor = 'pointer';
1710
+ }
1711
+ : undefined;
1712
+ const handlePointerOut = panel.onClick
1713
+ ? () => {
1714
+ document.body.style.cursor = '';
1715
+ }
1716
+ : undefined;
1717
+ return (_jsxs("group", { children: [_jsxs("mesh", { position: [cx, y, cz], renderOrder: 10, onClick: handleClick, onPointerOver: handlePointerOver, onPointerOut: handlePointerOut, children: [_jsx("boxGeometry", { args: [w, t, d] }), _jsx("meshBasicMaterial", { color: panel.color, transparent: !isOpaque, opacity: opacity, depthWrite: isOpaque })] }), panel.label && (_jsxs(Text, { position: [cx, topY + 0.05, cz], rotation: [-Math.PI / 2, 0, 0], fontSize: labelSize, color: panel.labelColor ?? '#ffffff', anchorX: "center", anchorY: "middle", maxWidth: w * 0.9, textAlign: "center", renderOrder: 11, frustumCulled: false, children: [panel.label, _jsx("meshBasicMaterial", { attach: "material", color: panel.labelColor ?? '#ffffff', depthWrite: false, depthTest: false })] }))] }, panel.id));
1718
+ })] }));
1584
1719
  }
1585
1720
  /**
1586
1721
  * FileCity3D - 3D visualization of codebase structure
@@ -1588,7 +1723,7 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
1588
1723
  * Renders CityData as an interactive 3D city where buildings represent files
1589
1724
  * and their height corresponds to line count or file size.
1590
1725
  */
1591
- export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingClick, className, style, animation, isGrown: externalIsGrown, onGrowChange, showControls = false, highlightLayers: externalHighlightLayers, isolationMode: externalIsolationMode, dimOpacity: _dimOpacity = 0.15, isLoading = false, loadingMessage = 'Loading file city...', emptyMessage = 'No file tree data available', heightScaling = 'linear', linearScale = 1, flatPatterns = DEFAULT_FLAT_PATTERNS, focusDirectory: externalFocusDirectory, focusColor: externalFocusColor, onDirectorySelect: _onDirectorySelect, backgroundColor = '#0f172a', textColor = '#94a3b8', selectedBuilding = null, adaptCameraToBuildings = false, fileColorLayers, }) {
1726
+ export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingClick, className, style, animation, isGrown: externalIsGrown, onGrowChange, showControls = false, elevatedScopePanels, highlightLayers: externalHighlightLayers, isolationMode: externalIsolationMode, dimOpacity: _dimOpacity = 0.15, isLoading = false, loadingMessage = 'Loading file city...', emptyMessage = 'No file tree data available', heightScaling = 'linear', linearScale = 1, flatPatterns = DEFAULT_FLAT_PATTERNS, focusDirectory: externalFocusDirectory, focusColor: externalFocusColor, onDirectorySelect: _onDirectorySelect, backgroundColor = '#0f172a', textColor = '#94a3b8', selectedBuilding = null, adaptCameraToBuildings = false, fileColorLayers, cameraControls, }) {
1592
1727
  const [hoveredBuilding, setHoveredBuilding] = useState(null);
1593
1728
  const [internalIsGrown, setInternalIsGrown] = useState(false);
1594
1729
  const [cameraReady, setCameraReady] = useState(false);
@@ -1695,6 +1830,6 @@ export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingC
1695
1830
  height: '100%',
1696
1831
  opacity: cameraReady ? 1 : 0,
1697
1832
  transition: 'opacity 0.1s ease-in',
1698
- }, children: _jsx(CityScene, { cityData: cityData, onBuildingHover: setHoveredBuilding, onBuildingClick: onBuildingClick, hoveredBuilding: hoveredBuilding, selectedBuilding: selectedBuilding, growProgress: growProgress, animationConfig: animationConfig, highlightLayers: highlightLayers, isolationMode: isolationMode, heightScaling: heightScaling, linearScale: linearScale, flatPatterns: flatPatterns, focusDirectory: focusDirectory, focusColor: focusColor, adaptCameraToBuildings: adaptCameraToBuildings, onCameraReady: () => setCameraReady(true) }) }), _jsx(InfoPanel, { building: selectedBuilding }), showControls && (_jsx(ControlsOverlay, { isFlat: !isGrown, onToggle: handleToggle, onResetCamera: resetCamera }))] }));
1833
+ }, children: _jsx(CityScene, { cityData: cityData, onBuildingHover: setHoveredBuilding, onBuildingClick: onBuildingClick, hoveredBuilding: hoveredBuilding, selectedBuilding: selectedBuilding, growProgress: growProgress, animationConfig: animationConfig, highlightLayers: highlightLayers, isolationMode: isolationMode, heightScaling: heightScaling, linearScale: linearScale, flatPatterns: flatPatterns, focusDirectory: focusDirectory, focusColor: focusColor, adaptCameraToBuildings: adaptCameraToBuildings, elevatedScopePanels: elevatedScopePanels, cameraControls: cameraControls, onCameraReady: () => setCameraReady(true) }) }), _jsx(InfoPanel, { building: selectedBuilding }), showControls && (_jsx(ControlsOverlay, { isFlat: !isGrown, onToggle: handleToggle, onResetCamera: resetCamera, onLookDown: () => tiltCameraTo(0) }))] }));
1699
1834
  }
1700
1835
  export default FileCity3D;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * FileCity3D - 3D visualization component
3
3
  */
4
- export { FileCity3D, resetCamera, getCameraAngle, getCameraTarget, getCameraTilt, rotateCameraTo, rotateCameraBy, tiltCameraTo, tiltCameraBy, moveCameraTo, setCameraTarget, DEFAULT_FLAT_PATTERNS, } from './FileCity3D';
5
- export type { FileCity3DProps, AnimationConfig, HighlightLayer, LayerItem, LayerRenderStrategy, IsolationMode, HeightScaling, FlatPattern, CityData, CityBuilding, CityDistrict, } from './FileCity3D';
4
+ export { FileCity3D, resetCamera, getCameraAngle, getCameraTarget, getCameraTilt, rotateCameraTo, rotateCameraBy, tiltCameraTo, tiltCameraBy, moveCameraTo, setCameraTarget, DEFAULT_FLAT_PATTERNS, DEFAULT_CAMERA_CONTROLS, } from './FileCity3D';
5
+ export type { FileCity3DProps, AnimationConfig, HighlightLayer, LayerItem, LayerRenderStrategy, IsolationMode, HeightScaling, FlatPattern, ElevatedScopePanel, CityData, CityBuilding, CityDistrict, CameraControlsConfig, MouseDragAction, TouchOneAction, TouchTwoAction, WheelAction, } from './FileCity3D';
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/FileCity3D/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,UAAU,EACV,WAAW,EACX,cAAc,EACd,eAAe,EACf,aAAa,EACb,cAAc,EACd,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,qBAAqB,GACtB,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,eAAe,EACf,eAAe,EACf,cAAc,EACd,SAAS,EACT,mBAAmB,EACnB,aAAa,EACb,aAAa,EACb,WAAW,EACX,QAAQ,EACR,YAAY,EACZ,YAAY,GACb,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/FileCity3D/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,UAAU,EACV,WAAW,EACX,cAAc,EACd,eAAe,EACf,aAAa,EACb,cAAc,EACd,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,eAAe,EACf,eAAe,EACf,cAAc,EACd,SAAS,EACT,mBAAmB,EACnB,aAAa,EACb,aAAa,EACb,WAAW,EACX,kBAAkB,EAClB,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,oBAAoB,EACpB,eAAe,EACf,cAAc,EACd,cAAc,EACd,WAAW,GACZ,MAAM,cAAc,CAAC"}
@@ -1,4 +1,4 @@
1
1
  /**
2
2
  * FileCity3D - 3D visualization component
3
3
  */
4
- export { FileCity3D, resetCamera, getCameraAngle, getCameraTarget, getCameraTilt, rotateCameraTo, rotateCameraBy, tiltCameraTo, tiltCameraBy, moveCameraTo, setCameraTarget, DEFAULT_FLAT_PATTERNS, } from './FileCity3D';
4
+ export { FileCity3D, resetCamera, getCameraAngle, getCameraTarget, getCameraTilt, rotateCameraTo, rotateCameraBy, tiltCameraTo, tiltCameraBy, moveCameraTo, setCameraTarget, DEFAULT_FLAT_PATTERNS, DEFAULT_CAMERA_CONTROLS, } from './FileCity3D';
package/dist/index.d.ts CHANGED
@@ -14,7 +14,7 @@ export type { FileTree } from '@principal-ai/file-city-builder';
14
14
  export { CityViewWithReactFlow, type CityViewWithReactFlowProps, } from './components/CityViewWithReactFlow';
15
15
  export { ThemeProvider, useTheme } from '@principal-ade/industry-theme';
16
16
  export { FileCity3D, resetCamera, DEFAULT_FLAT_PATTERNS } from './components/FileCity3D';
17
- export type { FileCity3DProps, AnimationConfig, HighlightLayer as FileCity3DHighlightLayer, IsolationMode, HeightScaling, FlatPattern, } from './components/FileCity3D';
17
+ export type { FileCity3DProps, AnimationConfig, HighlightLayer as FileCity3DHighlightLayer, IsolationMode, HeightScaling, FlatPattern, ElevatedScopePanel, } from './components/FileCity3D';
18
18
  export type { HighlightLayer as FileCity3DHL } from './components/FileCity3D';
19
19
  export { resolveVisualizationIntent } from './utils/visualizationResolution';
20
20
  export type { VisualizationIntent, ResolvedVisualizationState, } from './utils/visualizationResolution';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,8BAA8B,EAC9B,KAAK,mCAAmC,GACzC,MAAM,6CAA6C,CAAC;AAGrD,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,UAAU,GACX,MAAM,sCAAsC,CAAC;AAG9C,OAAO,EAAE,KAAK,mBAAmB,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGvF,OAAO,EACL,gCAAgC,EAChC,6BAA6B,EAC7B,oCAAoC,GACrC,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EACL,8BAA8B,EAC9B,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,kCAAkC,CAAC;AAE1C,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,kCAAkC,CAAC;AAG1C,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAGzF,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAG7F,YAAY,EACV,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,sBAAsB,EACtB,QAAQ,EACR,UAAU,GACX,MAAM,iCAAiC,CAAC;AAGzC,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAG1E,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,YAAY,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAG7F,YAAY,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAGhE,OAAO,EACL,qBAAqB,EACrB,KAAK,0BAA0B,GAChC,MAAM,oCAAoC,CAAC;AAG5C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAGxE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AACzF,YAAY,EACV,eAAe,EACf,eAAe,EACf,cAAc,IAAI,wBAAwB,EAC1C,aAAa,EACb,aAAa,EACb,WAAW,GACZ,MAAM,yBAAyB,CAAC;AAIjC,YAAY,EAAE,cAAc,IAAI,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAI9E,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,YAAY,EACV,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,iCAAiC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,8BAA8B,EAC9B,KAAK,mCAAmC,GACzC,MAAM,6CAA6C,CAAC;AAGrD,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,UAAU,GACX,MAAM,sCAAsC,CAAC;AAG9C,OAAO,EAAE,KAAK,mBAAmB,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAGvF,OAAO,EACL,gCAAgC,EAChC,6BAA6B,EAC7B,oCAAoC,GACrC,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EACL,8BAA8B,EAC9B,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,kCAAkC,CAAC;AAE1C,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,kCAAkC,CAAC;AAG1C,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAGzF,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAG7F,YAAY,EACV,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,sBAAsB,EACtB,QAAQ,EACR,UAAU,GACX,MAAM,iCAAiC,CAAC;AAGzC,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAG1E,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,YAAY,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAG7F,YAAY,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAGhE,OAAO,EACL,qBAAqB,EACrB,KAAK,0BAA0B,GAChC,MAAM,oCAAoC,CAAC;AAG5C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAGxE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AACzF,YAAY,EACV,eAAe,EACf,eAAe,EACf,cAAc,IAAI,wBAAwB,EAC1C,aAAa,EACb,aAAa,EACb,WAAW,EACX,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AAIjC,YAAY,EAAE,cAAc,IAAI,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAI9E,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,YAAY,EACV,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,iCAAiC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@principal-ai/file-city-react",
3
- "version": "0.5.37",
3
+ "version": "0.5.39",
4
4
  "type": "module",
5
5
  "description": "React components for File City visualization",
6
6
  "main": "dist/index.js",
@@ -26,6 +26,7 @@
26
26
  "three": ">=0.170.0"
27
27
  },
28
28
  "devDependencies": {
29
+ "@pierre/trees": "^1.0.0-beta.3",
29
30
  "@principal-ade/industry-theme": "^0.1.19",
30
31
  "@principal-ai/file-city-builder": "^0.4.5",
31
32
  "@react-spring/three": "^10.0.3",