@principal-ai/file-city-react 0.5.42 → 0.5.44
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.
- package/dist/components/FileCity3D/FileCity3D.d.ts +70 -2
- package/dist/components/FileCity3D/FileCity3D.d.ts.map +1 -1
- package/dist/components/FileCity3D/FileCity3D.js +164 -54
- package/dist/components/FileCity3D/index.d.ts +1 -1
- package/dist/components/FileCity3D/index.d.ts.map +1 -1
- package/dist/components/FileCityExplorer/FileCityExplorer.d.ts.map +1 -1
- package/dist/components/FileCityExplorer/FileCityExplorer.js +1 -31
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/FileCity3D/FileCity3D.tsx +437 -137
- package/src/components/FileCity3D/index.ts +2 -0
- package/src/components/FileCityExplorer/FileCityExplorer.tsx +2 -30
- package/src/index.ts +1 -0
- package/src/stories/ElevatedScopePanels.stories.tsx +72 -27
- package/src/stories/FileCityExplorer.stories.tsx +2 -29
- package/src/stories/HighlightLayersFlatDebug.stories.tsx +319 -0
- package/src/stories/LeaderLineSnippetOverlay.stories.tsx +725 -137
- package/src/stories/LeaderLineSnippetOverlay3D.stories.tsx +1060 -0
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import React from 'react';
|
|
10
10
|
import type { CityData, CityBuilding, CityDistrict, HighlightLayer as BuilderHighlightLayer, LayerItem, LayerRenderStrategy } from '@principal-ai/file-city-builder';
|
|
11
|
+
import * as THREE from 'three';
|
|
11
12
|
import type { ThreeElements } from '@react-three/fiber';
|
|
12
13
|
declare module 'react' {
|
|
13
14
|
namespace JSX {
|
|
@@ -17,6 +18,30 @@ declare module 'react' {
|
|
|
17
18
|
}
|
|
18
19
|
export type { CityData, CityBuilding, CityDistrict, LayerItem, LayerRenderStrategy };
|
|
19
20
|
export type HighlightLayer = BuilderHighlightLayer;
|
|
21
|
+
/** Visual style for the `selectedPath` ring on a directory. */
|
|
22
|
+
export interface SelectionStyle {
|
|
23
|
+
/** Ring color. Defaults to the theme accent. */
|
|
24
|
+
color?: string;
|
|
25
|
+
/** Ring border width in world units. Default: 2. */
|
|
26
|
+
borderWidth?: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Per-frame camera callback signature. Fires once per R3F render frame from
|
|
30
|
+
* inside the Canvas, so projection done in the callback is in lockstep with
|
|
31
|
+
* the city render — useful for HTML/SVG overlays that need to track world
|
|
32
|
+
* positions (e.g. leader lines anchored to buildings).
|
|
33
|
+
*
|
|
34
|
+
* The callback receives the live `THREE.Camera` and the current canvas
|
|
35
|
+
* size in CSS pixels. To project a world point to canvas-local pixels:
|
|
36
|
+
*
|
|
37
|
+
* const v = new THREE.Vector3(x, y, z).project(camera);
|
|
38
|
+
* const px = (v.x * 0.5 + 0.5) * size.width;
|
|
39
|
+
* const py = (v.y * -0.5 + 0.5) * size.height;
|
|
40
|
+
*/
|
|
41
|
+
export type OnCameraFrame = (camera: THREE.Camera, size: {
|
|
42
|
+
width: number;
|
|
43
|
+
height: number;
|
|
44
|
+
}) => void;
|
|
20
45
|
/** What to do with non-highlighted buildings */
|
|
21
46
|
export type IsolationMode = 'none' | 'transparent' | 'collapse' | 'hide';
|
|
22
47
|
export interface AnimationConfig {
|
|
@@ -186,6 +211,8 @@ export interface FileCity3DProps {
|
|
|
186
211
|
height?: number | string;
|
|
187
212
|
/** Callback when a building is clicked */
|
|
188
213
|
onBuildingClick?: (building: CityBuilding, event: MouseEvent) => void;
|
|
214
|
+
/** Callback when the hovered building changes (fires with null on hover-out) */
|
|
215
|
+
onBuildingHover?: (building: CityBuilding | null) => void;
|
|
189
216
|
/** CSS class name */
|
|
190
217
|
className?: string;
|
|
191
218
|
/** Inline styles */
|
|
@@ -226,17 +253,50 @@ export interface FileCity3DProps {
|
|
|
226
253
|
backgroundColor?: string;
|
|
227
254
|
/** Text color for secondary/placeholder text */
|
|
228
255
|
textColor?: string;
|
|
229
|
-
/**
|
|
256
|
+
/**
|
|
257
|
+
* @deprecated Use `selectedPath` instead. When both are set, `selectedPath`
|
|
258
|
+
* wins. This prop will be removed in a future release.
|
|
259
|
+
*/
|
|
230
260
|
selectedBuilding?: CityBuilding | null;
|
|
261
|
+
/**
|
|
262
|
+
* Path of the selected building or directory. The component resolves the
|
|
263
|
+
* path against `cityData.buildings` (file selection — emphasizes the
|
|
264
|
+
* building and shows the InfoPanel) and `cityData.districts` (directory
|
|
265
|
+
* selection — draws a ring around the district). When both `selectedPath`
|
|
266
|
+
* and `selectedBuilding` are set, `selectedPath` wins.
|
|
267
|
+
*/
|
|
268
|
+
selectedPath?: string | null;
|
|
269
|
+
/** Visual style for the directory selection ring drawn for `selectedPath`. */
|
|
270
|
+
selectionStyle?: SelectionStyle;
|
|
231
271
|
/** When true, camera height adjusts based on tallest building when grown */
|
|
232
272
|
adaptCameraToBuildings?: boolean;
|
|
233
273
|
/** Base file type color layers (resolved with highlightLayers) */
|
|
234
274
|
fileColorLayers?: HighlightLayer[];
|
|
275
|
+
/**
|
|
276
|
+
* Override the per-building color fallback. When unset (default), buildings
|
|
277
|
+
* not matched by a fill highlight layer are colored by file extension via
|
|
278
|
+
* the built-in file-type palette. Set to a CSS color (e.g. `'#475569'`) to
|
|
279
|
+
* render unmatched buildings in a neutral tone — useful for debug stories
|
|
280
|
+
* that want to isolate highlight-layer rendering.
|
|
281
|
+
*/
|
|
282
|
+
defaultBuildingColor?: string;
|
|
235
283
|
/**
|
|
236
284
|
* Translucent slabs rendered above the city showing scope coverage as
|
|
237
285
|
* elevated planes over the directories they own.
|
|
238
286
|
*/
|
|
239
287
|
elevatedScopePanels?: ElevatedScopePanel[];
|
|
288
|
+
/**
|
|
289
|
+
* Set of panel ids that should play the "lift up and fade out" dismiss
|
|
290
|
+
* animation. Add an id here to start the animation; once it settles,
|
|
291
|
+
* `onPanelDismissed` fires so the host can drop the panel from
|
|
292
|
+
* `elevatedScopePanels`.
|
|
293
|
+
*/
|
|
294
|
+
dismissingPanelIds?: ReadonlySet<string>;
|
|
295
|
+
/**
|
|
296
|
+
* Fires once a panel's dismiss animation has settled. The host should
|
|
297
|
+
* remove the id from both `dismissingPanelIds` and `elevatedScopePanels`.
|
|
298
|
+
*/
|
|
299
|
+
onPanelDismissed?: (id: string) => void;
|
|
240
300
|
/**
|
|
241
301
|
* Configure how mouse / trackpad / touch input drives the camera.
|
|
242
302
|
* Defaults match Google Maps style: left-drag pans, right-drag rotates,
|
|
@@ -246,6 +306,14 @@ export interface FileCity3DProps {
|
|
|
246
306
|
* Memoize this object to avoid unnecessary camera re-mounts.
|
|
247
307
|
*/
|
|
248
308
|
cameraControls?: CameraControlsConfig;
|
|
309
|
+
/**
|
|
310
|
+
* Fires once per R3F render frame with the live camera and canvas size.
|
|
311
|
+
* Use to project world points to canvas pixels for HTML/SVG overlays that
|
|
312
|
+
* need to track buildings as the camera pans / zooms / rotates.
|
|
313
|
+
*
|
|
314
|
+
* Memoize the callback to avoid re-mounting the bridge.
|
|
315
|
+
*/
|
|
316
|
+
onCameraFrame?: OnCameraFrame;
|
|
249
317
|
}
|
|
250
318
|
/**
|
|
251
319
|
* FileCity3D - 3D visualization of codebase structure
|
|
@@ -253,6 +321,6 @@ export interface FileCity3DProps {
|
|
|
253
321
|
* Renders CityData as an interactive 3D city where buildings represent files
|
|
254
322
|
* and their height corresponds to line count or file size.
|
|
255
323
|
*/
|
|
256
|
-
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;
|
|
324
|
+
export declare function FileCity3D({ cityData, width, height, onBuildingClick, onBuildingHover, className, style, animation, isGrown: externalIsGrown, onGrowChange, showControls, elevatedScopePanels, dismissingPanelIds, onPanelDismissed, highlightLayers: externalHighlightLayers, isolationMode: externalIsolationMode, dimOpacity: _dimOpacity, isLoading, loadingMessage, emptyMessage, heightScaling, linearScale, flatPatterns, focusDirectory: externalFocusDirectory, focusColor: externalFocusColor, onDirectorySelect: _onDirectorySelect, backgroundColor, textColor, selectedBuilding, selectedPath, selectionStyle, adaptCameraToBuildings, fileColorLayers, defaultBuildingColor, cameraControls, onCameraFrame, }: FileCity3DProps): import("react/jsx-runtime").JSX.Element;
|
|
257
325
|
export default FileCity3D;
|
|
258
326
|
//# sourceMappingURL=FileCity3D.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileCity3D.d.ts","sourceRoot":"","sources":["../../../src/components/FileCity3D/FileCity3D.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAA4D,MAAM,OAAO,CAAC;AAOjF,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EACZ,YAAY,EAEZ,cAAc,IAAI,qBAAqB,EACvC,SAAS,EACT,mBAAmB,EACpB,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"FileCity3D.d.ts","sourceRoot":"","sources":["../../../src/components/FileCity3D/FileCity3D.tsx"],"names":[],"mappings":"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"}
|
|
@@ -430,7 +430,7 @@ function BorderHighlights({ buildings, centerOffset, highlightLayers, growProgre
|
|
|
430
430
|
});
|
|
431
431
|
if (borderEdgeData.length === 0)
|
|
432
432
|
return null;
|
|
433
|
-
return (_jsxs("instancedMesh", { ref: meshRef, args: [undefined, undefined, borderEdgeData.length], frustumCulled: false, children: [_jsx("boxGeometry", { args: [1, 1, 1] }), _jsx("meshBasicMaterial", { transparent: true, opacity: 0.9
|
|
433
|
+
return (_jsxs("instancedMesh", { ref: meshRef, args: [undefined, undefined, borderEdgeData.length], frustumCulled: false, children: [_jsx("boxGeometry", { args: [1, 1, 1] }), _jsx("meshBasicMaterial", { transparent: true, opacity: 0.9 })] }));
|
|
434
434
|
}
|
|
435
435
|
// Helper to check if a path is inside a directory
|
|
436
436
|
function isPathInDirectory(path, directory) {
|
|
@@ -438,7 +438,7 @@ function isPathInDirectory(path, directory) {
|
|
|
438
438
|
return true;
|
|
439
439
|
return path === directory || path.startsWith(directory + '/');
|
|
440
440
|
}
|
|
441
|
-
function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hoveredIndex, selectedIndex, growProgress, animationConfig, heightScaling, linearScale, flatPatterns, staggerIndices, focusDirectory, highlightLayers, isolationMode, }) {
|
|
441
|
+
function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hoveredIndex, selectedIndex, growProgress, animationConfig, heightScaling, linearScale, flatPatterns, staggerIndices, focusDirectory, highlightLayers, isolationMode, defaultBuildingColor, }) {
|
|
442
442
|
const meshRef = useRef(null);
|
|
443
443
|
const startTimeRef = useRef(null);
|
|
444
444
|
const tempObject = useMemo(() => new THREE.Object3D(), []);
|
|
@@ -515,7 +515,7 @@ function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hovered
|
|
|
515
515
|
// Get all layer matches and find first fill match for building color
|
|
516
516
|
const matches = getLayerMatchesForPath(building.path, highlightLayers);
|
|
517
517
|
const fillMatch = matches.find(m => m.renderStrategy === 'fill');
|
|
518
|
-
const color = fillMatch?.color ?? getColorForFile(building);
|
|
518
|
+
const color = fillMatch?.color ?? defaultBuildingColor ?? getColorForFile(building);
|
|
519
519
|
const x = building.position.x - centerOffset.x;
|
|
520
520
|
const z = building.position.z - centerOffset.z;
|
|
521
521
|
const staggerIndex = staggerIndices[index] ?? index;
|
|
@@ -541,6 +541,7 @@ function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hovered
|
|
|
541
541
|
staggerIndices,
|
|
542
542
|
animationConfig.staggerDelay,
|
|
543
543
|
highlightLayers,
|
|
544
|
+
defaultBuildingColor,
|
|
544
545
|
]);
|
|
545
546
|
const minHeight = 0.3;
|
|
546
547
|
const baseOffset = 0.2;
|
|
@@ -886,6 +887,18 @@ export function getCameraAngle() {
|
|
|
886
887
|
export function getCameraTilt() {
|
|
887
888
|
return cameraApi?.getCurrentTilt() ?? null;
|
|
888
889
|
}
|
|
890
|
+
/**
|
|
891
|
+
* Bridge for piping the live camera + canvas size out of the R3F Canvas on
|
|
892
|
+
* every frame. Mounted as a child of `<Canvas>` so it has access to the R3F
|
|
893
|
+
* render loop; runs zero work if no callback is provided.
|
|
894
|
+
*/
|
|
895
|
+
function CameraFrameBridge({ onCameraFrame }) {
|
|
896
|
+
const { camera, size } = useThree();
|
|
897
|
+
useFrame(() => {
|
|
898
|
+
onCameraFrame?.(camera, { width: size.width, height: size.height });
|
|
899
|
+
});
|
|
900
|
+
return null;
|
|
901
|
+
}
|
|
889
902
|
const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, focusTarget, maxBuildingHeight = 0, cameraControls, onCameraReady, }) {
|
|
890
903
|
// Use selector to only subscribe to camera, not the entire R3F state
|
|
891
904
|
// This prevents re-renders on pointer movement
|
|
@@ -1554,7 +1567,132 @@ function ControlsOverlay({ isFlat, onToggle, onResetCamera, onLookDown }) {
|
|
|
1554
1567
|
left: 56,
|
|
1555
1568
|
}, title: "Reset View", children: "\u21BB" })] }));
|
|
1556
1569
|
}
|
|
1557
|
-
|
|
1570
|
+
// Distance (world units) the panel lifts upward when dismissed. Reads as
|
|
1571
|
+
// "toward the camera" because the camera looks down in flat mode.
|
|
1572
|
+
const PANEL_DISMISS_LIFT = 60;
|
|
1573
|
+
// Dismiss animation duration in seconds.
|
|
1574
|
+
const PANEL_DISMISS_DURATION = 0.7;
|
|
1575
|
+
function ElevatedScopePanelMesh({ panel, centerOffset, dismissing, onDismissed, }) {
|
|
1576
|
+
const cx = (panel.bounds.minX + panel.bounds.maxX) / 2 - centerOffset.x;
|
|
1577
|
+
const cz = (panel.bounds.minZ + panel.bounds.maxZ) / 2 - centerOffset.z;
|
|
1578
|
+
const w = Math.max(1, panel.bounds.maxX - panel.bounds.minX);
|
|
1579
|
+
const d = Math.max(1, panel.bounds.maxZ - panel.bounds.minZ);
|
|
1580
|
+
const t = panel.thickness ?? 2;
|
|
1581
|
+
const y = (panel.height ?? 4) + t / 2;
|
|
1582
|
+
const baseOpacity = panel.opacity ?? 1;
|
|
1583
|
+
const isOpaqueStatic = baseOpacity >= 1;
|
|
1584
|
+
const topY = y + t / 2;
|
|
1585
|
+
const tileMax = Math.min(w, d) / 2;
|
|
1586
|
+
const requested = panel.labelSize ?? Math.min(w, d) / 6;
|
|
1587
|
+
const labelSize = Math.max(4, Math.min(tileMax, requested));
|
|
1588
|
+
// Drive the dismiss animation directly via useFrame + Three.js refs
|
|
1589
|
+
// rather than react-spring. React re-renders (e.g., from FileCity3D's
|
|
1590
|
+
// hover state on mouse-move) were disturbing the spring-driven
|
|
1591
|
+
// animation; mutating the Three.js objects directly keeps the
|
|
1592
|
+
// animation isolated from React's render cycle.
|
|
1593
|
+
const groupRef = useRef(null);
|
|
1594
|
+
const slabMaterialRef = useRef(null);
|
|
1595
|
+
const labelMaterialRefs = useRef([]);
|
|
1596
|
+
const startTimeRef = useRef(null);
|
|
1597
|
+
const finishedRef = useRef(false);
|
|
1598
|
+
const [fullyDismissed, setFullyDismissed] = useState(false);
|
|
1599
|
+
useFrame(({ clock }) => {
|
|
1600
|
+
if (!dismissing || finishedRef.current)
|
|
1601
|
+
return;
|
|
1602
|
+
if (startTimeRef.current === null) {
|
|
1603
|
+
startTimeRef.current = clock.elapsedTime;
|
|
1604
|
+
}
|
|
1605
|
+
const elapsed = clock.elapsedTime - startTimeRef.current;
|
|
1606
|
+
const t = Math.min(elapsed / PANEL_DISMISS_DURATION, 1);
|
|
1607
|
+
// ease-out cubic
|
|
1608
|
+
const eased = 1 - Math.pow(1 - t, 3);
|
|
1609
|
+
if (groupRef.current) {
|
|
1610
|
+
groupRef.current.position.y = eased * PANEL_DISMISS_LIFT;
|
|
1611
|
+
}
|
|
1612
|
+
const opacityNow = baseOpacity * (1 - eased);
|
|
1613
|
+
if (slabMaterialRef.current) {
|
|
1614
|
+
slabMaterialRef.current.opacity = opacityNow;
|
|
1615
|
+
}
|
|
1616
|
+
for (const mat of labelMaterialRefs.current) {
|
|
1617
|
+
if (mat)
|
|
1618
|
+
mat.opacity = opacityNow;
|
|
1619
|
+
}
|
|
1620
|
+
if (t >= 1) {
|
|
1621
|
+
finishedRef.current = true;
|
|
1622
|
+
setFullyDismissed(true);
|
|
1623
|
+
onDismissed?.(panel.id);
|
|
1624
|
+
}
|
|
1625
|
+
});
|
|
1626
|
+
if (fullyDismissed)
|
|
1627
|
+
return null;
|
|
1628
|
+
// Always transparent on the slab so animated opacity actually blends.
|
|
1629
|
+
// (Three.js skips alpha blending entirely when transparent=false, which
|
|
1630
|
+
// makes the fade invisible until the prop flips mid-animation.)
|
|
1631
|
+
// depthWrite stays true while the panel is at full opacity so it still
|
|
1632
|
+
// occludes buildings beneath; we drop it once dismissal starts so the
|
|
1633
|
+
// fading panel doesn't punch a hole in the scene.
|
|
1634
|
+
const slabDepthWrite = isOpaqueStatic && !dismissing;
|
|
1635
|
+
const interactive = Boolean(panel.onClick || panel.onDoubleClick);
|
|
1636
|
+
const handleClick = panel.onClick
|
|
1637
|
+
? (e) => {
|
|
1638
|
+
e.stopPropagation();
|
|
1639
|
+
panel.onClick(e.nativeEvent);
|
|
1640
|
+
}
|
|
1641
|
+
: undefined;
|
|
1642
|
+
const handleDoubleClick = panel.onDoubleClick
|
|
1643
|
+
? (e) => {
|
|
1644
|
+
e.stopPropagation();
|
|
1645
|
+
panel.onDoubleClick(e.nativeEvent);
|
|
1646
|
+
}
|
|
1647
|
+
: undefined;
|
|
1648
|
+
const handlePointerOver = interactive
|
|
1649
|
+
? (e) => {
|
|
1650
|
+
e.stopPropagation();
|
|
1651
|
+
document.body.style.cursor = 'pointer';
|
|
1652
|
+
}
|
|
1653
|
+
: undefined;
|
|
1654
|
+
const handlePointerOut = interactive
|
|
1655
|
+
? () => {
|
|
1656
|
+
document.body.style.cursor = '';
|
|
1657
|
+
}
|
|
1658
|
+
: undefined;
|
|
1659
|
+
const labelColor = panel.labelColor ?? '#ffffff';
|
|
1660
|
+
const displayLabelColor = panel.displayLabelColor ?? panel.labelColor ?? '#ffffff';
|
|
1661
|
+
// Reset the array each render — the ref callbacks below will repopulate
|
|
1662
|
+
// it. This avoids stale entries if labels come and go.
|
|
1663
|
+
labelMaterialRefs.current = [];
|
|
1664
|
+
const captureLabelMat = (mat) => {
|
|
1665
|
+
if (mat)
|
|
1666
|
+
labelMaterialRefs.current.push(mat);
|
|
1667
|
+
};
|
|
1668
|
+
return (_jsxs("group", { ref: groupRef, children: [_jsxs("mesh", { position: [cx, y, cz], renderOrder: 10, onClick: dismissing ? undefined : handleClick, onDoubleClick: dismissing ? undefined : handleDoubleClick, onPointerOver: dismissing ? undefined : handlePointerOver, onPointerOut: dismissing ? undefined : handlePointerOut, children: [_jsx("boxGeometry", { args: [w, t, d] }), _jsx("meshBasicMaterial", { ref: slabMaterialRef, color: panel.color, transparent: true, opacity: baseOpacity, depthWrite: slabDepthWrite })] }), panel.displayLabel && (_jsxs(_Fragment, { children: [_jsxs(Text, { position: [cx, topY + 0.05, cz - labelSize * 0.6], rotation: [-Math.PI / 2, 0, 0], fontSize: labelSize, color: displayLabelColor, anchorX: "center", anchorY: "middle", maxWidth: w * 0.9, textAlign: "center", renderOrder: 11, frustumCulled: false, children: [panel.displayLabel, _jsx("meshBasicMaterial", { ref: captureLabelMat, attach: "material", color: displayLabelColor, depthWrite: false, depthTest: false, transparent: true, opacity: baseOpacity })] }), _jsxs("mesh", { position: [cx, topY + 0.06, cz - labelSize * 0.05], rotation: [-Math.PI / 2, 0, 0], renderOrder: 11, children: [_jsx("planeGeometry", { args: [
|
|
1669
|
+
Math.min(w * 0.9, panel.displayLabel.length * labelSize * 0.55),
|
|
1670
|
+
labelSize * 0.06,
|
|
1671
|
+
] }), _jsx("meshBasicMaterial", { ref: captureLabelMat, color: displayLabelColor, depthWrite: false, depthTest: false, transparent: true, opacity: baseOpacity })] })] })), panel.label && (_jsxs(Text, { position: [cx, topY + 0.05, cz + (panel.displayLabel ? labelSize * 0.6 : 0)], rotation: [-Math.PI / 2, 0, 0], fontSize: labelSize, color: labelColor, anchorX: "center", anchorY: "middle", maxWidth: w * 0.9, textAlign: "center", renderOrder: 11, frustumCulled: false, children: [panel.label, _jsx("meshBasicMaterial", { ref: captureLabelMat, attach: "material", color: labelColor, depthWrite: false, depthTest: false, transparent: true, opacity: baseOpacity })] }))] }));
|
|
1672
|
+
}
|
|
1673
|
+
// Lifted just above the default umbrella topY (height 4 + thickness 2 → 6)
|
|
1674
|
+
// so the ring isn't occluded by an `ElevatedScopePanel` covering the same
|
|
1675
|
+
// district when the city is flat.
|
|
1676
|
+
const SELECTION_RING_FLAT_Y = 7;
|
|
1677
|
+
function SelectionRing({ district, centerOffset, color, borderWidth, growProgress, }) {
|
|
1678
|
+
const { worldBounds } = district;
|
|
1679
|
+
const inflate = 1;
|
|
1680
|
+
const minX = worldBounds.minX - inflate;
|
|
1681
|
+
const maxX = worldBounds.maxX + inflate;
|
|
1682
|
+
const minZ = worldBounds.minZ - inflate;
|
|
1683
|
+
const maxZ = worldBounds.maxZ + inflate;
|
|
1684
|
+
const cx = (minX + maxX) / 2 - centerOffset.x;
|
|
1685
|
+
const cz = (minZ + maxZ) / 2 - centerOffset.z;
|
|
1686
|
+
const w = maxX - minX;
|
|
1687
|
+
const d = maxZ - minZ;
|
|
1688
|
+
const pathDepth = district.path.split('/').length;
|
|
1689
|
+
const groundY = -5 - pathDepth * 0.1 + 0.2;
|
|
1690
|
+
const y = SELECTION_RING_FLAT_Y + (groundY - SELECTION_RING_FLAT_Y) * growProgress;
|
|
1691
|
+
const t = Math.max(0.5, borderWidth);
|
|
1692
|
+
const barH = 0.5;
|
|
1693
|
+
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 })] })] }));
|
|
1694
|
+
}
|
|
1695
|
+
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, }) {
|
|
1558
1696
|
const centerOffset = useMemo(() => ({
|
|
1559
1697
|
x: (cityData.bounds.minX + cityData.bounds.maxX) / 2,
|
|
1560
1698
|
z: (cityData.bounds.minZ + cityData.bounds.maxZ) / 2,
|
|
@@ -1758,53 +1896,8 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
|
|
|
1758
1896
|
// Focus color takes priority, then highlight layer color
|
|
1759
1897
|
const districtColor = (isFocused && buildingFocusColor) ? buildingFocusColor : highlightLayerColor;
|
|
1760
1898
|
return (_jsx(DistrictFloor, { district: district, centerOffset: centerOffset, opacity: 1, highlightColor: districtColor, growProgress: growProgress }, district.path));
|
|
1761
|
-
}), _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 &&
|
|
1762
|
-
elevatedScopePanels?.map(panel => {
|
|
1763
|
-
const cx = (panel.bounds.minX + panel.bounds.maxX) / 2 - centerOffset.x;
|
|
1764
|
-
const cz = (panel.bounds.minZ + panel.bounds.maxZ) / 2 - centerOffset.z;
|
|
1765
|
-
const w = Math.max(1, panel.bounds.maxX - panel.bounds.minX);
|
|
1766
|
-
const d = Math.max(1, panel.bounds.maxZ - panel.bounds.minZ);
|
|
1767
|
-
const t = panel.thickness ?? 2;
|
|
1768
|
-
const y = (panel.height ?? 4) + t / 2;
|
|
1769
|
-
const opacity = panel.opacity ?? 1;
|
|
1770
|
-
const isOpaque = opacity >= 1;
|
|
1771
|
-
const topY = y + t / 2;
|
|
1772
|
-
// Size text to the panel: roughly fit longest reasonable label,
|
|
1773
|
-
// clamped so tiny tiles still render legibly and huge ones don't
|
|
1774
|
-
// get absurd. Callers may override via panel.labelSize, but we
|
|
1775
|
-
// still cap to the tile footprint so the label fits.
|
|
1776
|
-
const tileMax = Math.min(w, d) / 2;
|
|
1777
|
-
const requested = panel.labelSize ?? Math.min(w, d) / 6;
|
|
1778
|
-
const labelSize = Math.max(4, Math.min(tileMax, requested));
|
|
1779
|
-
const interactive = Boolean(panel.onClick || panel.onDoubleClick);
|
|
1780
|
-
const handleClick = panel.onClick
|
|
1781
|
-
? (e) => {
|
|
1782
|
-
e.stopPropagation();
|
|
1783
|
-
panel.onClick(e.nativeEvent);
|
|
1784
|
-
}
|
|
1785
|
-
: undefined;
|
|
1786
|
-
const handleDoubleClick = panel.onDoubleClick
|
|
1787
|
-
? (e) => {
|
|
1788
|
-
e.stopPropagation();
|
|
1789
|
-
panel.onDoubleClick(e.nativeEvent);
|
|
1790
|
-
}
|
|
1791
|
-
: undefined;
|
|
1792
|
-
const handlePointerOver = interactive
|
|
1793
|
-
? (e) => {
|
|
1794
|
-
e.stopPropagation();
|
|
1795
|
-
document.body.style.cursor = 'pointer';
|
|
1796
|
-
}
|
|
1797
|
-
: undefined;
|
|
1798
|
-
const handlePointerOut = interactive
|
|
1799
|
-
? () => {
|
|
1800
|
-
document.body.style.cursor = '';
|
|
1801
|
-
}
|
|
1802
|
-
: undefined;
|
|
1803
|
-
return (_jsxs("group", { children: [_jsxs("mesh", { position: [cx, y, cz], renderOrder: 10, onClick: handleClick, onDoubleClick: handleDoubleClick, onPointerOver: handlePointerOver, onPointerOut: handlePointerOut, children: [_jsx("boxGeometry", { args: [w, t, d] }), _jsx("meshBasicMaterial", { color: panel.color, transparent: !isOpaque, opacity: opacity, depthWrite: isOpaque })] }), panel.displayLabel && (_jsxs(_Fragment, { children: [_jsxs(Text, { position: [cx, topY + 0.05, cz - labelSize * 0.6], rotation: [-Math.PI / 2, 0, 0], fontSize: labelSize, color: panel.displayLabelColor ?? panel.labelColor ?? '#ffffff', anchorX: "center", anchorY: "middle", maxWidth: w * 0.9, textAlign: "center", renderOrder: 11, frustumCulled: false, children: [panel.displayLabel, _jsx("meshBasicMaterial", { attach: "material", color: panel.displayLabelColor ?? panel.labelColor ?? '#ffffff', depthWrite: false, depthTest: false })] }), _jsxs("mesh", { position: [cx, topY + 0.06, cz - labelSize * 0.05], rotation: [-Math.PI / 2, 0, 0], renderOrder: 11, children: [_jsx("planeGeometry", { args: [
|
|
1804
|
-
Math.min(w * 0.9, panel.displayLabel.length * labelSize * 0.55),
|
|
1805
|
-
labelSize * 0.06,
|
|
1806
|
-
] }), _jsx("meshBasicMaterial", { color: panel.displayLabelColor ?? panel.labelColor ?? '#ffffff', depthWrite: false, depthTest: false, transparent: true })] })] })), panel.label && (_jsxs(Text, { position: [cx, topY + 0.05, cz + (panel.displayLabel ? labelSize * 0.6 : 0)], 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));
|
|
1807
|
-
})] }));
|
|
1899
|
+
}), _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 &&
|
|
1900
|
+
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 }))] }));
|
|
1808
1901
|
}
|
|
1809
1902
|
/**
|
|
1810
1903
|
* FileCity3D - 3D visualization of codebase structure
|
|
@@ -1812,10 +1905,14 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
|
|
|
1812
1905
|
* Renders CityData as an interactive 3D city where buildings represent files
|
|
1813
1906
|
* and their height corresponds to line count or file size.
|
|
1814
1907
|
*/
|
|
1815
|
-
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, }) {
|
|
1908
|
+
export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingClick, onBuildingHover, className, style, animation, isGrown: externalIsGrown, onGrowChange, showControls = false, elevatedScopePanels, dismissingPanelIds, onPanelDismissed, 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, selectedPath = null, selectionStyle, adaptCameraToBuildings = false, fileColorLayers, defaultBuildingColor, cameraControls, onCameraFrame, }) {
|
|
1816
1909
|
const [hoveredBuilding, setHoveredBuilding] = useState(null);
|
|
1817
1910
|
const [internalIsGrown, setInternalIsGrown] = useState(false);
|
|
1818
1911
|
const [cameraReady, setCameraReady] = useState(false);
|
|
1912
|
+
const handleBuildingHover = useCallback((building) => {
|
|
1913
|
+
setHoveredBuilding(building);
|
|
1914
|
+
onBuildingHover?.(building);
|
|
1915
|
+
}, [onBuildingHover]);
|
|
1819
1916
|
const animationConfig = useMemo(() => ({ ...DEFAULT_ANIMATION, ...animation }), [animation]);
|
|
1820
1917
|
// ============================================================================
|
|
1821
1918
|
// Visualization Resolution
|
|
@@ -1849,6 +1946,19 @@ export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingC
|
|
|
1849
1946
|
const focusDirectory = resolved.focusDirectory;
|
|
1850
1947
|
const focusColor = resolved.focusColor;
|
|
1851
1948
|
const isolationMode = resolved.isolationMode;
|
|
1949
|
+
// `selectedPath` wins over the deprecated `selectedBuilding` when both are
|
|
1950
|
+
// set. A path resolves to either a building (file selection) or a district
|
|
1951
|
+
// (directory selection) — never both.
|
|
1952
|
+
const resolvedSelection = useMemo(() => {
|
|
1953
|
+
if (selectedPath != null) {
|
|
1954
|
+
const building = cityData.buildings.find(b => b.path === selectedPath) ?? null;
|
|
1955
|
+
if (building)
|
|
1956
|
+
return { building, district: null };
|
|
1957
|
+
const district = cityData.districts.find(d => d.path === selectedPath) ?? null;
|
|
1958
|
+
return { building: null, district };
|
|
1959
|
+
}
|
|
1960
|
+
return { building: selectedBuilding ?? null, district: null };
|
|
1961
|
+
}, [selectedPath, selectedBuilding, cityData.buildings, cityData.districts]);
|
|
1852
1962
|
const isGrown = externalIsGrown !== undefined ? externalIsGrown : internalIsGrown;
|
|
1853
1963
|
const setIsGrown = (value) => {
|
|
1854
1964
|
setInternalIsGrown(value);
|
|
@@ -1910,7 +2020,7 @@ export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingC
|
|
|
1910
2020
|
background: backgroundColor,
|
|
1911
2021
|
overflow: 'hidden',
|
|
1912
2022
|
...style,
|
|
1913
|
-
}, children: [
|
|
2023
|
+
}, children: [_jsxs(Canvas, { shadows: true, flat // Disables tone mapping for true colors
|
|
1914
2024
|
: true, style: {
|
|
1915
2025
|
position: 'absolute',
|
|
1916
2026
|
top: 0,
|
|
@@ -1919,6 +2029,6 @@ export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingC
|
|
|
1919
2029
|
height: '100%',
|
|
1920
2030
|
opacity: cameraReady ? 1 : 0,
|
|
1921
2031
|
transition: 'opacity 0.1s ease-in',
|
|
1922
|
-
}, children: _jsx(CityScene, { cityData: cityData, onBuildingHover:
|
|
2032
|
+
}, 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) }))] }));
|
|
1923
2033
|
}
|
|
1924
2034
|
export default FileCity3D;
|
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
* FileCity3D - 3D visualization component
|
|
3
3
|
*/
|
|
4
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';
|
|
5
|
+
export type { FileCity3DProps, AnimationConfig, HighlightLayer, LayerItem, LayerRenderStrategy, IsolationMode, HeightScaling, FlatPattern, ElevatedScopePanel, SelectionStyle, CityData, CityBuilding, CityDistrict, CameraControlsConfig, MouseDragAction, TouchOneAction, TouchTwoAction, WheelAction, OnCameraFrame, } 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,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,
|
|
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,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,oBAAoB,EACpB,eAAe,EACf,cAAc,EACd,cAAc,EACd,WAAW,EACX,aAAa,GACd,MAAM,cAAc,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileCityExplorer.d.ts","sourceRoot":"","sources":["../../../src/components/FileCityExplorer/FileCityExplorer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAU1B,OAAO,EAEL,KAAK,QAAQ,EAGd,MAAM,eAAe,CAAC;AAOvB,OAAO,KAAK,EAAa,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAuB7D,MAAM,WAAW,qBAAqB;IACpC,4CAA4C;IAC5C,QAAQ,EAAE,QAAQ,CAAC;IACnB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,aAAa,CAAC,EAAE,KAAK,EAAE,CAAC;IACxB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;IAC7B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,
|
|
1
|
+
{"version":3,"file":"FileCityExplorer.d.ts","sourceRoot":"","sources":["../../../src/components/FileCityExplorer/FileCityExplorer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAU1B,OAAO,EAEL,KAAK,QAAQ,EAGd,MAAM,eAAe,CAAC;AAOvB,OAAO,KAAK,EAAa,WAAW,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAuB7D,MAAM,WAAW,qBAAqB;IACpC,4CAA4C;IAC5C,QAAQ,EAAE,QAAQ,CAAC;IACnB;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,aAAa,CAAC,EAAE,KAAK,EAAE,CAAC;IACxB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;IAC7B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA60C5D,CAAC"}
|
|
@@ -467,46 +467,16 @@ export const FileCityExplorer = ({ cityData, packageRoot, initialScopes, initial
|
|
|
467
467
|
const displayLabel = folderPath ? areaNameByCityPath.get(folderPath) : undefined;
|
|
468
468
|
return displayLabel ? { ...panel, displayLabel } : panel;
|
|
469
469
|
});
|
|
470
|
-
// Selection indicator: render a thin, slightly-larger panel underneath
|
|
471
|
-
// the selected folder's umbrella so an accent ring peeks out around its
|
|
472
|
-
// edges. Inserted *before* the umbrella in the list so the umbrella
|
|
473
|
-
// draws on top — only the inflated rim shows. If the folder is expanded
|
|
474
|
-
// (no umbrella in the panel list) findIndex returns -1 and no ring is
|
|
475
|
-
// drawn, which is exactly what we want.
|
|
476
|
-
if (selectedPanelFolder) {
|
|
477
|
-
const idx = panels.findIndex(p => p.id === `folder::${selectedPanelFolder}`);
|
|
478
|
-
if (idx >= 0) {
|
|
479
|
-
const target = panels[idx];
|
|
480
|
-
const inflate = 4;
|
|
481
|
-
const border = {
|
|
482
|
-
id: `folder-border::${selectedPanelFolder}`,
|
|
483
|
-
color: theme.colors.warning,
|
|
484
|
-
height: (target.height ?? 4) - 2,
|
|
485
|
-
thickness: 1,
|
|
486
|
-
bounds: {
|
|
487
|
-
minX: target.bounds.minX - inflate,
|
|
488
|
-
maxX: target.bounds.maxX + inflate,
|
|
489
|
-
minZ: target.bounds.minZ - inflate,
|
|
490
|
-
maxZ: target.bounds.maxZ + inflate,
|
|
491
|
-
},
|
|
492
|
-
};
|
|
493
|
-
const next = [...panels];
|
|
494
|
-
next.splice(idx, 0, border);
|
|
495
|
-
return next;
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
470
|
return panels.length > 0 ? panels : undefined;
|
|
499
471
|
}, [
|
|
500
472
|
activeTab,
|
|
501
473
|
cityData,
|
|
502
|
-
selectedPanelFolder,
|
|
503
474
|
treeModel,
|
|
504
475
|
folderTreeExpansion,
|
|
505
476
|
setFocusDirectoryIfUnpinned,
|
|
506
477
|
areaNameByCityPath,
|
|
507
478
|
folderIndex,
|
|
508
479
|
packageRootClamp,
|
|
509
|
-
theme,
|
|
510
480
|
]);
|
|
511
481
|
// Cmd-click on a building → surface the chain of expanded ancestor folders
|
|
512
482
|
// (their umbrellas are currently hidden because they're expanded). Each
|
|
@@ -689,7 +659,7 @@ export const FileCityExplorer = ({ cityData, packageRoot, initialScopes, initial
|
|
|
689
659
|
left: 0,
|
|
690
660
|
right: 0,
|
|
691
661
|
bottom: 0,
|
|
692
|
-
}, children: _jsx(FileCity3D, { cityData: cityData, height: "100%", width: "100%", heightScaling: "linear", linearScale: 0.5, backgroundColor: theme.colors.background, textColor: theme.colors.textMuted, focusDirectory: focusDirectory, highlightLayers: cityHighlightLayers, elevatedScopePanels: cityElevatedPanels ?? folderElevatedPanels, onBuildingClick: handleBuildingClick, animation: {
|
|
662
|
+
}, children: _jsx(FileCity3D, { cityData: cityData, height: "100%", width: "100%", heightScaling: "linear", linearScale: 0.5, backgroundColor: theme.colors.background, textColor: theme.colors.textMuted, focusDirectory: focusDirectory, highlightLayers: cityHighlightLayers, elevatedScopePanels: cityElevatedPanels ?? folderElevatedPanels, selectedPath: activeTab === 'files' ? selectedPanelFolder : null, selectionStyle: { color: theme.colors.warning }, onBuildingClick: handleBuildingClick, animation: {
|
|
693
663
|
startFlat: true,
|
|
694
664
|
autoStartDelay: null,
|
|
695
665
|
staggerDelay: 5,
|
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, ElevatedScopePanel, } from './components/FileCity3D';
|
|
17
|
+
export type { FileCity3DProps, AnimationConfig, HighlightLayer as FileCity3DHighlightLayer, IsolationMode, HeightScaling, FlatPattern, ElevatedScopePanel, SelectionStyle, } from './components/FileCity3D';
|
|
18
18
|
export type { HighlightLayer as FileCity3DHL } from './components/FileCity3D';
|
|
19
19
|
export { buildFolderElevatedPanels, buildFolderIndex, hashFolderColor, } from './utils/folderElevatedPanels';
|
|
20
20
|
export type { BuildFolderElevatedPanelsOptions } from './utils/folderElevatedPanels';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,EACX,kBAAkB,
|
|
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,EAClB,cAAc,GACf,MAAM,yBAAyB,CAAC;AAIjC,YAAY,EAAE,cAAc,IAAI,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAG9E,OAAO,EACL,yBAAyB,EACzB,gBAAgB,EAChB,eAAe,GAChB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EAAE,gCAAgC,EAAE,MAAM,8BAA8B,CAAC;AAIrF,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,YAAY,EACV,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,iCAAiC,CAAC"}
|