@principal-ai/file-city-react 0.5.23 → 0.5.24
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/ArchitectureMapHighlightLayers.d.ts +6 -2
- package/dist/components/ArchitectureMapHighlightLayers.d.ts.map +1 -1
- package/dist/components/ArchitectureMapHighlightLayers.js +74 -2
- package/dist/components/FileCity3D/FileCity3D.d.ts +18 -1
- package/dist/components/FileCity3D/FileCity3D.d.ts.map +1 -1
- package/dist/components/FileCity3D/FileCity3D.js +91 -46
- package/dist/components/FileCity3D/index.d.ts +2 -2
- package/dist/components/FileCity3D/index.d.ts.map +1 -1
- package/dist/components/FileCity3D/index.js +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/stories/sample-data.d.ts.map +1 -1
- package/dist/stories/sample-data.js +44 -42
- package/dist/utils/visualizationResolution.d.ts +72 -0
- package/dist/utils/visualizationResolution.d.ts.map +1 -0
- package/dist/utils/visualizationResolution.js +100 -0
- package/package.json +1 -1
- package/src/components/ArchitectureMapHighlightLayers.tsx +101 -2
- package/src/components/FileCity3D/FileCity3D.tsx +125 -52
- package/src/components/FileCity3D/index.ts +2 -0
- package/src/index.ts +10 -1
- package/src/stories/2D3DComparison.stories.tsx +527 -0
- package/src/stories/sample-data.ts +47 -45
- package/src/utils/visualizationResolution.ts +170 -0
|
@@ -59,9 +59,13 @@ export interface ArchitectureMapHighlightLayersProps {
|
|
|
59
59
|
}) => void;
|
|
60
60
|
buildingBorderRadius?: number;
|
|
61
61
|
districtBorderRadius?: number;
|
|
62
|
+
/** Color to highlight the focused directory border (hex color, e.g. "#3b82f6") */
|
|
63
|
+
focusColor?: string | null;
|
|
64
|
+
/** Base file type color layers (resolved with highlightLayers) */
|
|
65
|
+
fileColorLayers?: HighlightLayer[];
|
|
62
66
|
}
|
|
63
|
-
declare function ArchitectureMapHighlightLayersInner({ cityData, highlightLayers, onLayerToggle, focusDirectory, rootDirectoryName, onDirectorySelect, onFileClick, enableZoom, zoomToPath, onZoomComplete, zoomAnimationSpeed, allowZoomToPath, fullSize, showGrid, showFileNames, className, selectiveRender, canvasBackgroundColor, maxCanvasSize, hoverBorderColor, disableOpacityDimming, defaultDirectoryColor, defaultBuildingColor, subdirectoryMode, showLayerControls, showFileTypeIcons, showDirectoryLabels, transform, // Default to no rotation
|
|
64
|
-
onHover, buildingBorderRadius, districtBorderRadius, }: ArchitectureMapHighlightLayersProps): import("react/jsx-runtime").JSX.Element;
|
|
67
|
+
declare function ArchitectureMapHighlightLayersInner({ cityData, highlightLayers: externalHighlightLayers, onLayerToggle, focusDirectory, rootDirectoryName, onDirectorySelect, onFileClick, enableZoom, zoomToPath: externalZoomToPath, onZoomComplete, zoomAnimationSpeed, allowZoomToPath, fullSize, showGrid, showFileNames, className, selectiveRender, canvasBackgroundColor, maxCanvasSize, hoverBorderColor, disableOpacityDimming, defaultDirectoryColor, defaultBuildingColor, subdirectoryMode, showLayerControls, showFileTypeIcons, showDirectoryLabels, transform, // Default to no rotation
|
|
68
|
+
onHover, buildingBorderRadius, districtBorderRadius, focusColor: externalFocusColor, fileColorLayers, }: ArchitectureMapHighlightLayersProps): import("react/jsx-runtime").JSX.Element;
|
|
65
69
|
export declare const ArchitectureMapHighlightLayers: typeof ArchitectureMapHighlightLayersInner;
|
|
66
70
|
export {};
|
|
67
71
|
//# sourceMappingURL=ArchitectureMapHighlightLayers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ArchitectureMapHighlightLayers.d.ts","sourceRoot":"","sources":["../../src/components/ArchitectureMapHighlightLayers.tsx"],"names":[],"mappings":"AAQA,OAAO,EAIL,cAAc,EAGf,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,sBAAsB,EACvB,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"ArchitectureMapHighlightLayers.d.ts","sourceRoot":"","sources":["../../src/components/ArchitectureMapHighlightLayers.tsx"],"names":[],"mappings":"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"}
|
|
@@ -5,6 +5,7 @@ import { filterCityDataForSelectiveRender, filterCityDataForSubdirectory, filter
|
|
|
5
5
|
import { drawLayeredBuildings, drawLayeredDistricts, drawGrid, LayerIndex, } from '../render/client/drawLayeredBuildings';
|
|
6
6
|
import { getDefaultFileColorConfig } from '../utils/fileColorHighlightLayers';
|
|
7
7
|
import { extractIconConfig } from '../utils/fileTypeIcons';
|
|
8
|
+
import { resolveVisualizationIntent } from '../utils/visualizationResolution';
|
|
8
9
|
const DEFAULT_DISPLAY_OPTIONS = {
|
|
9
10
|
showGrid: false,
|
|
10
11
|
showConnections: true,
|
|
@@ -144,9 +145,41 @@ class PathHierarchyLookup {
|
|
|
144
145
|
return this.abstractedPaths.has(path);
|
|
145
146
|
}
|
|
146
147
|
}
|
|
147
|
-
function ArchitectureMapHighlightLayersInner({ cityData, highlightLayers
|
|
148
|
-
onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
148
|
+
function ArchitectureMapHighlightLayersInner({ cityData, highlightLayers: externalHighlightLayers, onLayerToggle, focusDirectory = null, rootDirectoryName, onDirectorySelect, onFileClick, enableZoom = false, zoomToPath: externalZoomToPath, onZoomComplete, zoomAnimationSpeed = 0.12, allowZoomToPath = true, fullSize = false, showGrid = false, showFileNames = false, className = '', selectiveRender, canvasBackgroundColor, maxCanvasSize, hoverBorderColor, disableOpacityDimming = true, defaultDirectoryColor, defaultBuildingColor, subdirectoryMode, showLayerControls = false, showFileTypeIcons = true, showDirectoryLabels = true, transform = { rotation: 0 }, // Default to no rotation
|
|
149
|
+
onHover, buildingBorderRadius = 0, districtBorderRadius = 0, focusColor: externalFocusColor, fileColorLayers, }) {
|
|
149
150
|
const { theme } = useTheme();
|
|
151
|
+
// ============================================================================
|
|
152
|
+
// Visualization Resolution
|
|
153
|
+
// Always resolve: combines highlightLayers with fileColorLayers,
|
|
154
|
+
// filtering fileColorLayers based on focus/highlight scope.
|
|
155
|
+
// ============================================================================
|
|
156
|
+
const resolved = useMemo(() => {
|
|
157
|
+
// Cast to InputHighlightLayer[] for resolution - types are compatible at runtime
|
|
158
|
+
const resolution = resolveVisualizationIntent({
|
|
159
|
+
focusPath: externalZoomToPath,
|
|
160
|
+
focusColor: externalFocusColor,
|
|
161
|
+
highlightLayers: (externalHighlightLayers ?? []),
|
|
162
|
+
fileColorLayers: (fileColorLayers ?? []),
|
|
163
|
+
});
|
|
164
|
+
return {
|
|
165
|
+
highlightLayers: resolution.highlightLayers,
|
|
166
|
+
zoomToPath: resolution.cameraFocusPath,
|
|
167
|
+
focusColor: resolution.focusColor,
|
|
168
|
+
shouldDim: resolution.shouldIsolate,
|
|
169
|
+
};
|
|
170
|
+
}, [
|
|
171
|
+
fileColorLayers,
|
|
172
|
+
externalHighlightLayers,
|
|
173
|
+
externalZoomToPath,
|
|
174
|
+
externalFocusColor,
|
|
175
|
+
]);
|
|
176
|
+
// Use resolved values, ensuring layers have required priority for drawing functions
|
|
177
|
+
const highlightLayers = useMemo(() => resolved.highlightLayers.map((layer, index) => ({
|
|
178
|
+
...layer,
|
|
179
|
+
priority: layer.priority ?? index,
|
|
180
|
+
})), [resolved.highlightLayers]);
|
|
181
|
+
const zoomToPath = resolved.zoomToPath;
|
|
182
|
+
const focusColor = resolved.focusColor;
|
|
150
183
|
// Use theme colors as defaults, with prop overrides
|
|
151
184
|
const resolvedCanvasBackgroundColor = canvasBackgroundColor ?? theme.colors.background;
|
|
152
185
|
const resolvedHoverBorderColor = hoverBorderColor ?? theme.colors.text;
|
|
@@ -814,6 +847,42 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
814
847
|
// Draw buildings with layer support
|
|
815
848
|
drawLayeredBuildings(ctx, visibleBuildingsMemo, worldToCanvas, scale * zoomState.scale, allLayers, interactionState.hoveredBuilding, resolvedDefaultBuildingColor, showFileNames, resolvedHoverBorderColor, disableOpacityDimming, showFileTypeIcons, buildingBorderRadius, layerIndex, // Pre-built index for O(1) lookups
|
|
816
849
|
iconMap);
|
|
850
|
+
// Draw focus color border around the zoomToPath target
|
|
851
|
+
if (focusColor && zoomToPath) {
|
|
852
|
+
const normalizedPath = zoomToPath.replace(/^\/+|\/+$/g, '');
|
|
853
|
+
// Find the district or building that matches the path
|
|
854
|
+
const targetDistrict = filteredCityData.districts?.find(d => d.path === normalizedPath || d.path === zoomToPath);
|
|
855
|
+
const targetBuilding = !targetDistrict
|
|
856
|
+
? filteredCityData.buildings?.find(b => b.path === normalizedPath || b.path === zoomToPath)
|
|
857
|
+
: null;
|
|
858
|
+
if (targetDistrict) {
|
|
859
|
+
// Draw border around district
|
|
860
|
+
const { minX, maxX, minZ, maxZ } = targetDistrict.worldBounds;
|
|
861
|
+
const topLeft = worldToCanvas(minX, minZ);
|
|
862
|
+
const bottomRight = worldToCanvas(maxX, maxZ);
|
|
863
|
+
const width = bottomRight.x - topLeft.x;
|
|
864
|
+
const height = bottomRight.y - topLeft.y;
|
|
865
|
+
ctx.save();
|
|
866
|
+
ctx.strokeStyle = focusColor;
|
|
867
|
+
ctx.lineWidth = 3 * zoomState.scale;
|
|
868
|
+
ctx.strokeRect(topLeft.x, topLeft.y, width, height);
|
|
869
|
+
ctx.restore();
|
|
870
|
+
}
|
|
871
|
+
else if (targetBuilding) {
|
|
872
|
+
// Draw border around building
|
|
873
|
+
const size = Math.max(targetBuilding.dimensions[0], targetBuilding.dimensions[2]);
|
|
874
|
+
const halfSize = size / 2;
|
|
875
|
+
const topLeft = worldToCanvas(targetBuilding.position.x - halfSize, targetBuilding.position.z - halfSize);
|
|
876
|
+
const bottomRight = worldToCanvas(targetBuilding.position.x + halfSize, targetBuilding.position.z + halfSize);
|
|
877
|
+
const width = bottomRight.x - topLeft.x;
|
|
878
|
+
const height = bottomRight.y - topLeft.y;
|
|
879
|
+
ctx.save();
|
|
880
|
+
ctx.strokeStyle = focusColor;
|
|
881
|
+
ctx.lineWidth = 3 * zoomState.scale;
|
|
882
|
+
ctx.strokeRect(topLeft.x, topLeft.y, width, height);
|
|
883
|
+
ctx.restore();
|
|
884
|
+
}
|
|
885
|
+
}
|
|
817
886
|
// Performance monitoring end available for debugging
|
|
818
887
|
// Performance stats available but not logged to reduce console noise
|
|
819
888
|
// Uncomment for debugging: render time, buildings/districts counts, layer counts
|
|
@@ -848,6 +917,9 @@ onHover, buildingBorderRadius = 0, districtBorderRadius = 0, }) {
|
|
|
848
917
|
abstractedPathsSet,
|
|
849
918
|
layerIndex,
|
|
850
919
|
iconMap,
|
|
920
|
+
// Focus color border
|
|
921
|
+
focusColor,
|
|
922
|
+
zoomToPath,
|
|
851
923
|
]);
|
|
852
924
|
// Optimized hit testing
|
|
853
925
|
const performHitTest = useCallback((canvasX, canvasY) => {
|
|
@@ -29,6 +29,10 @@ export interface HighlightLayer {
|
|
|
29
29
|
items: HighlightItem[];
|
|
30
30
|
/** Opacity for highlighted items (0-1) */
|
|
31
31
|
opacity?: number;
|
|
32
|
+
/** Rendering priority (higher renders on top) */
|
|
33
|
+
priority?: number;
|
|
34
|
+
/** Border width in pixels */
|
|
35
|
+
borderWidth?: number;
|
|
32
36
|
}
|
|
33
37
|
export interface HighlightItem {
|
|
34
38
|
/** File or directory path */
|
|
@@ -54,6 +58,15 @@ export interface AnimationConfig {
|
|
|
54
58
|
}
|
|
55
59
|
/** Height scaling mode for buildings */
|
|
56
60
|
export type HeightScaling = 'logarithmic' | 'linear';
|
|
61
|
+
/** Pattern for files that should render flat (e.g., lock files, generated files) */
|
|
62
|
+
export interface FlatPattern {
|
|
63
|
+
/** Glob-like pattern or regex to match file paths */
|
|
64
|
+
pattern: string | RegExp;
|
|
65
|
+
/** Height to use for matched files (default: 0.5) */
|
|
66
|
+
height?: number;
|
|
67
|
+
}
|
|
68
|
+
/** Default patterns for files that should render flat */
|
|
69
|
+
export declare const DEFAULT_FLAT_PATTERNS: FlatPattern[];
|
|
57
70
|
export interface RotateOptions {
|
|
58
71
|
/** Animation duration in milliseconds. Default uses spring physics (~800ms feel). */
|
|
59
72
|
duration?: number;
|
|
@@ -156,6 +169,8 @@ export interface FileCity3DProps {
|
|
|
156
169
|
heightScaling?: HeightScaling;
|
|
157
170
|
/** Scale factor for linear mode (height per line, default 0.05) */
|
|
158
171
|
linearScale?: number;
|
|
172
|
+
/** Patterns for files that should render flat (e.g., lock files). Set to DEFAULT_FLAT_PATTERNS for common lock files, or [] to disable. */
|
|
173
|
+
flatPatterns?: FlatPattern[];
|
|
159
174
|
/** Directory path to focus on - buildings outside will collapse */
|
|
160
175
|
focusDirectory?: string | null;
|
|
161
176
|
/** Color to highlight the focused directory (hex color, e.g. "#3b82f6") */
|
|
@@ -170,6 +185,8 @@ export interface FileCity3DProps {
|
|
|
170
185
|
selectedBuilding?: CityBuilding | null;
|
|
171
186
|
/** When true, camera height adjusts based on tallest building when grown */
|
|
172
187
|
adaptCameraToBuildings?: boolean;
|
|
188
|
+
/** Base file type color layers (resolved with highlightLayers) */
|
|
189
|
+
fileColorLayers?: HighlightLayer[];
|
|
173
190
|
}
|
|
174
191
|
/**
|
|
175
192
|
* FileCity3D - 3D visualization of codebase structure
|
|
@@ -177,6 +194,6 @@ export interface FileCity3DProps {
|
|
|
177
194
|
* Renders CityData as an interactive 3D city where buildings represent files
|
|
178
195
|
* and their height corresponds to line count or file size.
|
|
179
196
|
*/
|
|
180
|
-
export declare function FileCity3D({ cityData, width, height, onBuildingClick, className, style, animation, isGrown: externalIsGrown, onGrowChange, showControls, highlightLayers, isolationMode, dimOpacity: _dimOpacity, isLoading, loadingMessage, emptyMessage, heightScaling, linearScale, focusDirectory, focusColor, onDirectorySelect: _onDirectorySelect, backgroundColor, textColor, selectedBuilding, adaptCameraToBuildings, }: FileCity3DProps): import("react/jsx-runtime").JSX.Element;
|
|
197
|
+
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;
|
|
181
198
|
export default FileCity3D;
|
|
182
199
|
//# 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;AAMjF,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EACZ,YAAY,EAEb,MAAM,iCAAiC,CAAC;AAEzC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"FileCity3D.d.ts","sourceRoot":"","sources":["../../../src/components/FileCity3D/FileCity3D.tsx"],"names":[],"mappings":"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"}
|
|
@@ -13,6 +13,31 @@ import { useSpring } from '@react-spring/three';
|
|
|
13
13
|
import { OrbitControls, PerspectiveCamera, Text } from '@react-three/drei';
|
|
14
14
|
import { getFileConfig } from '@principal-ai/file-city-builder';
|
|
15
15
|
import * as THREE from 'three';
|
|
16
|
+
import { resolveVisualizationIntent } from '../../utils/visualizationResolution';
|
|
17
|
+
/** Default patterns for files that should render flat */
|
|
18
|
+
export const DEFAULT_FLAT_PATTERNS = [
|
|
19
|
+
{ pattern: /package-lock\.json$/ },
|
|
20
|
+
{ pattern: /yarn\.lock$/ },
|
|
21
|
+
{ pattern: /pnpm-lock\.yaml$/ },
|
|
22
|
+
{ pattern: /composer\.lock$/ },
|
|
23
|
+
{ pattern: /Gemfile\.lock$/ },
|
|
24
|
+
{ pattern: /Cargo\.lock$/ },
|
|
25
|
+
{ pattern: /poetry\.lock$/ },
|
|
26
|
+
{ pattern: /\.lock$/ }, // Generic lock files
|
|
27
|
+
];
|
|
28
|
+
/**
|
|
29
|
+
* Check if a file path matches any flat pattern.
|
|
30
|
+
* Returns the matched pattern's height or undefined if no match.
|
|
31
|
+
*/
|
|
32
|
+
function matchFlatPattern(path, patterns) {
|
|
33
|
+
for (const { pattern, height } of patterns) {
|
|
34
|
+
const regex = typeof pattern === 'string' ? new RegExp(pattern) : pattern;
|
|
35
|
+
if (regex.test(path)) {
|
|
36
|
+
return height ?? 0.5; // Default flat height
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
16
41
|
const DEFAULT_ANIMATION = {
|
|
17
42
|
startFlat: false,
|
|
18
43
|
autoStartDelay: 500,
|
|
@@ -26,7 +51,12 @@ const DEFAULT_ANIMATION = {
|
|
|
26
51
|
* - logarithmic: Compresses large values (default, good for mixed codebases)
|
|
27
52
|
* - linear: Direct scaling (1 line = linearScale units of height)
|
|
28
53
|
*/
|
|
29
|
-
function calculateBuildingHeight(building, scaling = 'logarithmic', linearScale =
|
|
54
|
+
function calculateBuildingHeight(building, scaling = 'logarithmic', linearScale = 1, flatPatterns = []) {
|
|
55
|
+
// Check if this file matches a flat pattern (e.g., lock files)
|
|
56
|
+
const flatHeight = matchFlatPattern(building.path, flatPatterns);
|
|
57
|
+
if (flatHeight !== undefined) {
|
|
58
|
+
return flatHeight;
|
|
59
|
+
}
|
|
30
60
|
const minHeight = 2;
|
|
31
61
|
// Use lineCount if available (any text file), otherwise fall back to size
|
|
32
62
|
if (building.lineCount !== undefined) {
|
|
@@ -209,7 +239,7 @@ function isPathInDirectory(path, directory) {
|
|
|
209
239
|
return true;
|
|
210
240
|
return path === directory || path.startsWith(directory + '/');
|
|
211
241
|
}
|
|
212
|
-
function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hoveredIndex, selectedIndex, growProgress, animationConfig, heightScaling, linearScale, staggerIndices, focusDirectory, highlightLayers, isolationMode, }) {
|
|
242
|
+
function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hoveredIndex, selectedIndex, growProgress, animationConfig, heightScaling, linearScale, flatPatterns, staggerIndices, focusDirectory, highlightLayers, isolationMode, }) {
|
|
213
243
|
const meshRef = useRef(null);
|
|
214
244
|
const startTimeRef = useRef(null);
|
|
215
245
|
const tempObject = useMemo(() => new THREE.Object3D(), []);
|
|
@@ -282,8 +312,10 @@ function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hovered
|
|
|
282
312
|
const buildingData = useMemo(() => {
|
|
283
313
|
return buildings.map((building, index) => {
|
|
284
314
|
const [width, , depth] = building.dimensions;
|
|
285
|
-
const fullHeight = calculateBuildingHeight(building, heightScaling, linearScale);
|
|
286
|
-
|
|
315
|
+
const fullHeight = calculateBuildingHeight(building, heightScaling, linearScale, flatPatterns);
|
|
316
|
+
// Use highlight layer color if available, otherwise fall back to file config color
|
|
317
|
+
const highlight = getHighlightForPath(building.path, highlightLayers);
|
|
318
|
+
const color = highlight?.color ?? getColorForFile(building);
|
|
287
319
|
const x = building.position.x - centerOffset.x;
|
|
288
320
|
const z = building.position.z - centerOffset.z;
|
|
289
321
|
const staggerIndex = staggerIndices[index] ?? index;
|
|
@@ -305,8 +337,10 @@ function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hovered
|
|
|
305
337
|
centerOffset,
|
|
306
338
|
heightScaling,
|
|
307
339
|
linearScale,
|
|
340
|
+
flatPatterns,
|
|
308
341
|
staggerIndices,
|
|
309
342
|
animationConfig.staggerDelay,
|
|
343
|
+
highlightLayers,
|
|
310
344
|
]);
|
|
311
345
|
const minHeight = 0.3;
|
|
312
346
|
const baseOffset = 0.2;
|
|
@@ -428,7 +462,7 @@ function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hovered
|
|
|
428
462
|
}, [buildingData, onClick]);
|
|
429
463
|
if (buildingData.length === 0)
|
|
430
464
|
return null;
|
|
431
|
-
return (_jsxs("group", { children: [_jsxs("instancedMesh", { ref: meshRef, args: [undefined, undefined, buildingData.length], onPointerMove: handlePointerMove, onPointerOut: handlePointerOut, onClick: handleClick, frustumCulled: false, children: [_jsx("boxGeometry", { args: [1, 1, 1] }), _jsx("
|
|
465
|
+
return (_jsxs("group", { children: [_jsxs("instancedMesh", { ref: meshRef, args: [undefined, undefined, buildingData.length], onPointerMove: handlePointerMove, onPointerOut: handlePointerOut, onClick: handleClick, frustumCulled: false, children: [_jsx("boxGeometry", { args: [1, 1, 1] }), _jsx("meshBasicMaterial", {})] }), _jsx(BuildingEdges, { buildings: buildingData.map(d => ({
|
|
432
466
|
width: d.width,
|
|
433
467
|
depth: d.depth,
|
|
434
468
|
fullHeight: d.fullHeight,
|
|
@@ -486,7 +520,7 @@ function AnimatedIcon({ x, z, targetHeight, iconSize, texture, opacity, growProg
|
|
|
486
520
|
});
|
|
487
521
|
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 })] }));
|
|
488
522
|
}
|
|
489
|
-
function BuildingIcons({ buildings, centerOffset, growProgress, heightScaling, linearScale, highlightLayers, isolationMode, hasActiveHighlights, staggerIndices, springDuration, staggerDelay, }) {
|
|
523
|
+
function BuildingIcons({ buildings, centerOffset, growProgress, heightScaling, linearScale, flatPatterns, highlightLayers, isolationMode, hasActiveHighlights, staggerIndices, springDuration, staggerDelay, }) {
|
|
490
524
|
// Pre-compute buildings with icons
|
|
491
525
|
const buildingsWithIcons = useMemo(() => {
|
|
492
526
|
return buildings
|
|
@@ -501,7 +535,7 @@ function BuildingIcons({ buildings, centerOffset, growProgress, heightScaling, l
|
|
|
501
535
|
const shouldCollapse = shouldDim && isolationMode === 'collapse';
|
|
502
536
|
if (shouldHide)
|
|
503
537
|
return null;
|
|
504
|
-
const fullHeight = calculateBuildingHeight(building, heightScaling, linearScale);
|
|
538
|
+
const fullHeight = calculateBuildingHeight(building, heightScaling, linearScale, flatPatterns);
|
|
505
539
|
const targetHeight = shouldCollapse ? 0.5 : fullHeight;
|
|
506
540
|
const x = building.position.x - centerOffset.x;
|
|
507
541
|
const z = building.position.z - centerOffset.z;
|
|
@@ -526,6 +560,7 @@ function BuildingIcons({ buildings, centerOffset, growProgress, heightScaling, l
|
|
|
526
560
|
hasActiveHighlights,
|
|
527
561
|
heightScaling,
|
|
528
562
|
linearScale,
|
|
563
|
+
flatPatterns,
|
|
529
564
|
staggerIndices,
|
|
530
565
|
staggerDelay,
|
|
531
566
|
]);
|
|
@@ -535,11 +570,10 @@ function BuildingIcons({ buildings, centerOffset, growProgress, heightScaling, l
|
|
|
535
570
|
const texture = getIconTexture(icon.name, icon.color || '#ffffff');
|
|
536
571
|
if (!texture)
|
|
537
572
|
return null;
|
|
538
|
-
// Icon size based on building dimensions
|
|
539
|
-
const [width] = building.dimensions;
|
|
540
|
-
const
|
|
541
|
-
const
|
|
542
|
-
const iconSize = (baseSize + heightBoost) * (icon.size || 1);
|
|
573
|
+
// Icon size based on building dimensions (matching 2D calculation)
|
|
574
|
+
const [width, , depth] = building.dimensions;
|
|
575
|
+
const minDimension = Math.min(width, depth);
|
|
576
|
+
const iconSize = minDimension * (icon.size || 0.6) * 1.7;
|
|
543
577
|
const opacity = shouldDim && isolationMode === 'transparent' ? 0.3 : 1;
|
|
544
578
|
return (_jsx(AnimatedIcon, { x: x, z: z, targetHeight: targetHeight, iconSize: iconSize, texture: texture, opacity: opacity, growProgress: growProgress, staggerDelayMs: staggerDelayMs, springDuration: springDuration }, building.path));
|
|
545
579
|
}) }));
|
|
@@ -656,6 +690,7 @@ function AnimatedCamera({ citySize, isFlat, focusTarget, maxBuildingHeight = 0 }
|
|
|
656
690
|
const frameCount = useRef(0);
|
|
657
691
|
// Calculate camera height to fit city in viewport (for top-down view)
|
|
658
692
|
// Formula: height = citySize / (2 * tan(fov/2) * min(1, aspect))
|
|
693
|
+
// Padding factor adds space around the city to match 2D component
|
|
659
694
|
const calculateFlatCameraHeight = useCallback(() => {
|
|
660
695
|
const perspCam = camera;
|
|
661
696
|
const fovRad = (perspCam.fov * Math.PI) / 180;
|
|
@@ -663,7 +698,10 @@ function AnimatedCamera({ citySize, isFlat, focusTarget, maxBuildingHeight = 0 }
|
|
|
663
698
|
const aspect = perspCam.aspect || 1;
|
|
664
699
|
// Use min(1, aspect) to handle both landscape and portrait viewports
|
|
665
700
|
const effectiveAspect = Math.min(1, aspect);
|
|
666
|
-
|
|
701
|
+
const baseHeight = citySize / (2 * tanHalfFov * effectiveAspect);
|
|
702
|
+
// Add padding to match 2D component's default padding
|
|
703
|
+
const paddingFactor = 1.08;
|
|
704
|
+
return baseHeight * paddingFactor;
|
|
667
705
|
}, [camera, citySize]);
|
|
668
706
|
// Compute target camera position
|
|
669
707
|
// When flat, always use top-down view (ignore focusTarget)
|
|
@@ -1125,7 +1163,7 @@ function ControlsOverlay({ isFlat, onToggle, onResetCamera }) {
|
|
|
1125
1163
|
gap: 8,
|
|
1126
1164
|
}, children: [_jsx("button", { onClick: onResetCamera, style: buttonStyle, children: "Reset View" }), _jsx("button", { onClick: onToggle, style: buttonStyle, children: isFlat ? 'Grow to 3D' : 'Flatten to 2D' })] }));
|
|
1127
1165
|
}
|
|
1128
|
-
function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding, selectedBuilding, growProgress, animationConfig, highlightLayers, isolationMode, heightScaling, linearScale, focusDirectory, focusColor, adaptCameraToBuildings = false, }) {
|
|
1166
|
+
function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding, selectedBuilding, growProgress, animationConfig, highlightLayers, isolationMode, heightScaling, linearScale, flatPatterns, focusDirectory, focusColor, adaptCameraToBuildings = false, }) {
|
|
1129
1167
|
const centerOffset = useMemo(() => ({
|
|
1130
1168
|
x: (cityData.bounds.minX + cityData.bounds.maxX) / 2,
|
|
1131
1169
|
z: (cityData.bounds.minZ + cityData.bounds.maxZ) / 2,
|
|
@@ -1257,37 +1295,11 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
|
|
|
1257
1295
|
const size = Math.max(maxX - minX, maxZ - minZ);
|
|
1258
1296
|
return { x: centerX, z: centerZ, size };
|
|
1259
1297
|
}
|
|
1260
|
-
//
|
|
1261
|
-
|
|
1262
|
-
if (!activeHighlights || focusDirectory)
|
|
1263
|
-
return null;
|
|
1264
|
-
const highlightedBuildings = cityData.buildings.filter(building => {
|
|
1265
|
-
const highlight = getHighlightForPath(building.path, highlightLayers);
|
|
1266
|
-
return highlight !== null;
|
|
1267
|
-
});
|
|
1268
|
-
if (highlightedBuildings.length === 0)
|
|
1269
|
-
return null;
|
|
1270
|
-
let minX = Infinity, maxX = -Infinity;
|
|
1271
|
-
let minZ = Infinity, maxZ = -Infinity;
|
|
1272
|
-
for (const building of highlightedBuildings) {
|
|
1273
|
-
const x = building.position.x - centerOffset.x;
|
|
1274
|
-
const z = building.position.z - centerOffset.z;
|
|
1275
|
-
const [width, , depth] = building.dimensions;
|
|
1276
|
-
minX = Math.min(minX, x - width / 2);
|
|
1277
|
-
maxX = Math.max(maxX, x + width / 2);
|
|
1278
|
-
minZ = Math.min(minZ, z - depth / 2);
|
|
1279
|
-
maxZ = Math.max(maxZ, z + depth / 2);
|
|
1280
|
-
}
|
|
1281
|
-
const centerX = (minX + maxX) / 2;
|
|
1282
|
-
const centerZ = (minZ + maxZ) / 2;
|
|
1283
|
-
const size = Math.max(maxX - minX, maxZ - minZ);
|
|
1284
|
-
return { x: centerX, z: centerZ, size };
|
|
1298
|
+
// No auto-focus on highlights - camera only moves with explicit focusDirectory
|
|
1299
|
+
return null;
|
|
1285
1300
|
}, [
|
|
1286
1301
|
cameraFocusDirectory,
|
|
1287
|
-
focusDirectory,
|
|
1288
|
-
activeHighlights,
|
|
1289
1302
|
cityData.buildings,
|
|
1290
|
-
highlightLayers,
|
|
1291
1303
|
centerOffset,
|
|
1292
1304
|
isPathInDirectory,
|
|
1293
1305
|
]);
|
|
@@ -1342,7 +1354,7 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
|
|
|
1342
1354
|
// Focus color takes priority, then highlight layer color
|
|
1343
1355
|
const districtColor = (isFocused && buildingFocusColor) ? buildingFocusColor : highlightLayerColor;
|
|
1344
1356
|
return (_jsx(DistrictFloor, { district: district, centerOffset: centerOffset, opacity: 1, highlightColor: districtColor, growProgress: growProgress }, district.path));
|
|
1345
|
-
}), _jsx(InstancedBuildings, { buildings: cityData.buildings, centerOffset: centerOffset, onHover: onBuildingHover, onClick: onBuildingClick, hoveredIndex: hoveredIndex, selectedIndex: selectedIndex, growProgress: growProgress, animationConfig: animationConfig, heightScaling: heightScaling, linearScale: linearScale, staggerIndices: staggerIndices, focusDirectory: buildingFocusDirectory, highlightLayers: highlightLayers, isolationMode: isolationMode }), _jsx(BuildingIcons, { buildings: cityData.buildings, centerOffset: centerOffset, growProgress: growProgress, heightScaling: heightScaling, linearScale: linearScale, highlightLayers: highlightLayers, isolationMode: isolationMode, hasActiveHighlights: activeHighlights, staggerIndices: staggerIndices, springDuration: springDuration, staggerDelay: animationConfig.staggerDelay || 15 })] }));
|
|
1357
|
+
}), _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, staggerIndices: staggerIndices, springDuration: springDuration, staggerDelay: animationConfig.staggerDelay || 15 })] }));
|
|
1346
1358
|
}
|
|
1347
1359
|
/**
|
|
1348
1360
|
* FileCity3D - 3D visualization of codebase structure
|
|
@@ -1350,10 +1362,42 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
|
|
|
1350
1362
|
* Renders CityData as an interactive 3D city where buildings represent files
|
|
1351
1363
|
* and their height corresponds to line count or file size.
|
|
1352
1364
|
*/
|
|
1353
|
-
export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingClick, className, style, animation, isGrown: externalIsGrown, onGrowChange, showControls = true, highlightLayers
|
|
1365
|
+
export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingClick, className, style, animation, isGrown: externalIsGrown, onGrowChange, showControls = true, 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, }) {
|
|
1354
1366
|
const [hoveredBuilding, setHoveredBuilding] = useState(null);
|
|
1355
1367
|
const [internalIsGrown, setInternalIsGrown] = useState(false);
|
|
1356
1368
|
const animationConfig = useMemo(() => ({ ...DEFAULT_ANIMATION, ...animation }), [animation]);
|
|
1369
|
+
// ============================================================================
|
|
1370
|
+
// Visualization Resolution
|
|
1371
|
+
// Always resolve: combines highlightLayers with fileColorLayers,
|
|
1372
|
+
// filtering fileColorLayers based on focus/highlight scope.
|
|
1373
|
+
// ============================================================================
|
|
1374
|
+
const resolved = useMemo(() => {
|
|
1375
|
+
// Cast to InputHighlightLayer[] for resolution - types are compatible at runtime
|
|
1376
|
+
const resolution = resolveVisualizationIntent({
|
|
1377
|
+
focusPath: externalFocusDirectory,
|
|
1378
|
+
focusColor: externalFocusColor,
|
|
1379
|
+
highlightLayers: (externalHighlightLayers ?? []),
|
|
1380
|
+
fileColorLayers: (fileColorLayers ?? []),
|
|
1381
|
+
});
|
|
1382
|
+
return {
|
|
1383
|
+
highlightLayers: resolution.highlightLayers,
|
|
1384
|
+
focusDirectory: resolution.cameraFocusPath,
|
|
1385
|
+
focusColor: resolution.focusColor,
|
|
1386
|
+
// Use explicit isolation mode if provided, otherwise auto-determine
|
|
1387
|
+
isolationMode: externalIsolationMode ?? (resolution.shouldIsolate ? 'collapse' : 'none'),
|
|
1388
|
+
};
|
|
1389
|
+
}, [
|
|
1390
|
+
fileColorLayers,
|
|
1391
|
+
externalHighlightLayers,
|
|
1392
|
+
externalFocusDirectory,
|
|
1393
|
+
externalFocusColor,
|
|
1394
|
+
externalIsolationMode,
|
|
1395
|
+
]);
|
|
1396
|
+
// Use resolved values
|
|
1397
|
+
const highlightLayers = resolved.highlightLayers;
|
|
1398
|
+
const focusDirectory = resolved.focusDirectory;
|
|
1399
|
+
const focusColor = resolved.focusColor;
|
|
1400
|
+
const isolationMode = resolved.isolationMode;
|
|
1357
1401
|
const isGrown = externalIsGrown !== undefined ? externalIsGrown : internalIsGrown;
|
|
1358
1402
|
const setIsGrown = (value) => {
|
|
1359
1403
|
setInternalIsGrown(value);
|
|
@@ -1415,12 +1459,13 @@ export function FileCity3D({ cityData, width = '100%', height = 600, onBuildingC
|
|
|
1415
1459
|
background: backgroundColor,
|
|
1416
1460
|
overflow: 'hidden',
|
|
1417
1461
|
...style,
|
|
1418
|
-
}, children: [_jsx(Canvas, { shadows: true,
|
|
1462
|
+
}, children: [_jsx(Canvas, { shadows: true, flat // Disables tone mapping for true colors
|
|
1463
|
+
: true, style: {
|
|
1419
1464
|
position: 'absolute',
|
|
1420
1465
|
top: 0,
|
|
1421
1466
|
left: 0,
|
|
1422
1467
|
width: '100%',
|
|
1423
1468
|
height: '100%',
|
|
1424
|
-
}, 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, focusDirectory: focusDirectory, focusColor: focusColor, adaptCameraToBuildings: adaptCameraToBuildings }) }), _jsx(InfoPanel, { building: selectedBuilding || hoveredBuilding }), showControls && (_jsx(ControlsOverlay, { isFlat: !isGrown, onToggle: handleToggle, onResetCamera: resetCamera }))] }));
|
|
1469
|
+
}, 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 }) }), _jsx(InfoPanel, { building: selectedBuilding || hoveredBuilding }), showControls && (_jsx(ControlsOverlay, { isFlat: !isGrown, onToggle: handleToggle, onResetCamera: resetCamera }))] }));
|
|
1425
1470
|
}
|
|
1426
1471
|
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, } from './FileCity3D';
|
|
5
|
-
export type { FileCity3DProps, AnimationConfig, HighlightLayer, HighlightItem, IsolationMode, HeightScaling, CityData, CityBuilding, CityDistrict, } from './FileCity3D';
|
|
4
|
+
export { FileCity3D, resetCamera, getCameraAngle, getCameraTarget, getCameraTilt, rotateCameraTo, rotateCameraBy, tiltCameraTo, tiltCameraBy, moveCameraTo, setCameraTarget, DEFAULT_FLAT_PATTERNS, } from './FileCity3D';
|
|
5
|
+
export type { FileCity3DProps, AnimationConfig, HighlightLayer, HighlightItem, IsolationMode, HeightScaling, FlatPattern, CityData, CityBuilding, CityDistrict, } 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,
|
|
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,aAAa,EACb,aAAa,EACb,aAAa,EACb,WAAW,EACX,QAAQ,EACR,YAAY,EACZ,YAAY,GACb,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, } from './FileCity3D';
|
|
4
|
+
export { FileCity3D, resetCamera, getCameraAngle, getCameraTarget, getCameraTilt, rotateCameraTo, rotateCameraBy, tiltCameraTo, tiltCameraBy, moveCameraTo, setCameraTarget, DEFAULT_FLAT_PATTERNS, } from './FileCity3D';
|
package/dist/index.d.ts
CHANGED
|
@@ -13,7 +13,9 @@ export type { UseCodeCityDataOptions, UseCodeCityDataReturn } from './hooks/useC
|
|
|
13
13
|
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
|
-
export { FileCity3D, resetCamera } from './components/FileCity3D';
|
|
17
|
-
export type { FileCity3DProps, AnimationConfig, HighlightLayer as FileCity3DHighlightLayer, HighlightItem as FileCity3DHighlightItem, HighlightItem, IsolationMode, HeightScaling, } from './components/FileCity3D';
|
|
16
|
+
export { FileCity3D, resetCamera, DEFAULT_FLAT_PATTERNS } from './components/FileCity3D';
|
|
17
|
+
export type { FileCity3DProps, AnimationConfig, HighlightLayer as FileCity3DHighlightLayer, HighlightItem as FileCity3DHighlightItem, HighlightItem, IsolationMode, HeightScaling, FlatPattern, } from './components/FileCity3D';
|
|
18
18
|
export type { HighlightLayer as FileCity3DHL } from './components/FileCity3D';
|
|
19
|
+
export { resolveVisualizationIntent } from './utils/visualizationResolution';
|
|
20
|
+
export type { VisualizationIntent, ResolvedVisualizationState, } from './utils/visualizationResolution';
|
|
19
21
|
//# sourceMappingURL=index.d.ts.map
|
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,MAAM,yBAAyB,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,IAAI,uBAAuB,EACxC,aAAa,EACb,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"}
|
package/dist/index.js
CHANGED
|
@@ -19,4 +19,7 @@ export { CityViewWithReactFlow, } from './components/CityViewWithReactFlow';
|
|
|
19
19
|
// Re-export theme utilities for consumers
|
|
20
20
|
export { ThemeProvider, useTheme } from '@principal-ade/industry-theme';
|
|
21
21
|
// 3D visualization component
|
|
22
|
-
export { FileCity3D, resetCamera } from './components/FileCity3D';
|
|
22
|
+
export { FileCity3D, resetCamera, DEFAULT_FLAT_PATTERNS } from './components/FileCity3D';
|
|
23
|
+
// Visualization resolution utilities
|
|
24
|
+
// See docs/VISUALIZATION_STATE_RESOLUTION.md for documentation
|
|
25
|
+
export { resolveVisualizationIntent } from './utils/visualizationResolution';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sample-data.d.ts","sourceRoot":"","sources":["../../src/stories/sample-data.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EAGT,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"sample-data.d.ts","sourceRoot":"","sources":["../../src/stories/sample-data.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EAGT,MAAM,iCAAiC,CAAC;AA6EzC,wBAAgB,oBAAoB,IAAI,QAAQ,CAmB/C;AAaD,wBAAgB,yBAAyB,IAAI,QAAQ,CAmBpD"}
|