@industry-theme/file-city-panel 0.2.25 → 0.2.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"CodeCityPanel.d.ts","sourceRoot":"","sources":["../../src/panels/CodeCityPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAuBjF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAQpD;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AA8rCD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAEvD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,EAAE,KAAK,CAAC,EA8BxC,CAAC"}
1
+ {"version":3,"file":"CodeCityPanel.d.ts","sourceRoot":"","sources":["../../src/panels/CodeCityPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAuBjF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAQpD;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAksCD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAEvD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,EAAE,KAAK,CAAC,EA8BxC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"qualityLayers.d.ts","sourceRoot":"","sources":["../../../src/panels/utils/qualityLayers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"qualityLayers.d.ts","sourceRoot":"","sources":["../../../src/panels/utils/qualityLayers.ts"],"names":[],"mappings":"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"}
@@ -4292,6 +4292,9 @@ function requireArchitectureMapHighlightLayers() {
4292
4292
  onDirectorySelect,
4293
4293
  onFileClick,
4294
4294
  enableZoom = false,
4295
+ zoomToPath = null,
4296
+ onZoomComplete,
4297
+ zoomAnimationSpeed = 0.12,
4295
4298
  fullSize = false,
4296
4299
  showGrid = false,
4297
4300
  showFileNames = false,
@@ -4332,6 +4335,8 @@ function requireArchitectureMapHighlightLayers() {
4332
4335
  lastMousePos: { x: 0, y: 0 },
4333
4336
  hasMouseMoved: false
4334
4337
  });
4338
+ const [targetZoom, setTargetZoom] = (0, react_1.useState)(null);
4339
+ const lastZoomToPathRef = (0, react_1.useRef)(null);
4335
4340
  (0, react_1.useEffect)(() => {
4336
4341
  if (!enableZoom) {
4337
4342
  setZoomState((prev) => ({
@@ -4342,8 +4347,43 @@ function requireArchitectureMapHighlightLayers() {
4342
4347
  isDragging: false,
4343
4348
  hasMouseMoved: false
4344
4349
  }));
4350
+ setTargetZoom(null);
4345
4351
  }
4346
4352
  }, [enableZoom]);
4353
+ (0, react_1.useEffect)(() => {
4354
+ if (!targetZoom)
4355
+ return;
4356
+ const animate = () => {
4357
+ setZoomState((prev) => {
4358
+ const lerp = (a, b, t) => a + (b - a) * t;
4359
+ const easing = zoomAnimationSpeed;
4360
+ const newScale = lerp(prev.scale, targetZoom.scale, easing);
4361
+ const newOffsetX = lerp(prev.offsetX, targetZoom.offsetX, easing);
4362
+ const newOffsetY = lerp(prev.offsetY, targetZoom.offsetY, easing);
4363
+ const scaleDone = Math.abs(newScale - targetZoom.scale) < 1e-3;
4364
+ const offsetXDone = Math.abs(newOffsetX - targetZoom.offsetX) < 0.5;
4365
+ const offsetYDone = Math.abs(newOffsetY - targetZoom.offsetY) < 0.5;
4366
+ if (scaleDone && offsetXDone && offsetYDone) {
4367
+ setTargetZoom(null);
4368
+ onZoomComplete == null ? void 0 : onZoomComplete();
4369
+ return {
4370
+ ...prev,
4371
+ scale: targetZoom.scale,
4372
+ offsetX: targetZoom.offsetX,
4373
+ offsetY: targetZoom.offsetY
4374
+ };
4375
+ }
4376
+ return {
4377
+ ...prev,
4378
+ scale: newScale,
4379
+ offsetX: newOffsetX,
4380
+ offsetY: newOffsetY
4381
+ };
4382
+ });
4383
+ };
4384
+ const frameId = requestAnimationFrame(animate);
4385
+ return () => cancelAnimationFrame(frameId);
4386
+ }, [targetZoom, zoomState, zoomAnimationSpeed, onZoomComplete]);
4347
4387
  const [hitTestCache, setHitTestCache] = (0, react_1.useState)(null);
4348
4388
  const calculateCanvasResolution = (fileCount, _cityBounds) => {
4349
4389
  const minSize = 400;
@@ -4380,6 +4420,75 @@ function requireArchitectureMapHighlightLayers() {
4380
4420
  }
4381
4421
  return filteredCityData;
4382
4422
  }, [subdirectoryMode == null ? void 0 : subdirectoryMode.enabled, subdirectoryMode == null ? void 0 : subdirectoryMode.autoCenter, cityData, filteredCityData]);
4423
+ (0, react_1.useEffect)(() => {
4424
+ if (!enableZoom || zoomToPath === lastZoomToPathRef.current) {
4425
+ return;
4426
+ }
4427
+ lastZoomToPathRef.current = zoomToPath;
4428
+ if (zoomToPath === null) {
4429
+ setTargetZoom({
4430
+ scale: 1,
4431
+ offsetX: 0,
4432
+ offsetY: 0
4433
+ });
4434
+ return;
4435
+ }
4436
+ if (!filteredCityData || !canvasRef.current) {
4437
+ return;
4438
+ }
4439
+ const displayWidth = canvasRef.current.clientWidth || canvasSize.width;
4440
+ const displayHeight = canvasRef.current.clientHeight || canvasSize.height;
4441
+ if (!displayWidth || !displayHeight) {
4442
+ return;
4443
+ }
4444
+ const normalizedPath = zoomToPath.replace(/^\/+|\/+$/g, "");
4445
+ const targetDistrict = filteredCityData.districts.find((d) => d.path === normalizedPath || d.path === zoomToPath);
4446
+ const targetBuilding = filteredCityData.buildings.find((b) => b.path === normalizedPath || b.path === zoomToPath);
4447
+ if (!targetDistrict && !targetBuilding) {
4448
+ return;
4449
+ }
4450
+ let targetBounds;
4451
+ if (targetDistrict) {
4452
+ targetBounds = targetDistrict.worldBounds;
4453
+ } else if (targetBuilding) {
4454
+ const [width, , depth] = targetBuilding.dimensions;
4455
+ const padding = Math.max(width, depth) * 2;
4456
+ targetBounds = {
4457
+ minX: targetBuilding.position.x - width / 2 - padding,
4458
+ maxX: targetBuilding.position.x + width / 2 + padding,
4459
+ minZ: targetBuilding.position.z - depth / 2 - padding,
4460
+ maxZ: targetBuilding.position.z + depth / 2 + padding
4461
+ };
4462
+ } else {
4463
+ return;
4464
+ }
4465
+ const coordinateData = canvasSizingData || filteredCityData;
4466
+ const { scale: baseScale, offsetX: baseOffsetX, offsetZ: baseOffsetZ } = calculateScaleAndOffset(coordinateData, displayWidth, displayHeight, displayOptions.padding);
4467
+ const targetCenterX = (targetBounds.minX + targetBounds.maxX) / 2;
4468
+ const targetCenterZ = (targetBounds.minZ + targetBounds.maxZ) / 2;
4469
+ const targetScreenWidth = (targetBounds.maxX - targetBounds.minX) * baseScale;
4470
+ const targetScreenHeight = (targetBounds.maxZ - targetBounds.minZ) * baseScale;
4471
+ const paddingFactor = 0.8;
4472
+ const scaleToFitWidth = displayWidth * paddingFactor / targetScreenWidth;
4473
+ const scaleToFitHeight = displayHeight * paddingFactor / targetScreenHeight;
4474
+ const newZoomScale = Math.min(scaleToFitWidth, scaleToFitHeight, 5);
4475
+ const baseScreenX = (targetCenterX - coordinateData.bounds.minX) * baseScale + baseOffsetX;
4476
+ const baseScreenY = (targetCenterZ - coordinateData.bounds.minZ) * baseScale + baseOffsetZ;
4477
+ const newOffsetX = displayWidth / 2 - baseScreenX * newZoomScale;
4478
+ const newOffsetY = displayHeight / 2 - baseScreenY * newZoomScale;
4479
+ setTargetZoom({
4480
+ scale: newZoomScale,
4481
+ offsetX: newOffsetX,
4482
+ offsetY: newOffsetY
4483
+ });
4484
+ }, [
4485
+ zoomToPath,
4486
+ enableZoom,
4487
+ filteredCityData,
4488
+ canvasSizingData,
4489
+ canvasSize,
4490
+ displayOptions.padding
4491
+ ]);
4383
4492
  const buildHitTestCache = (0, react_1.useCallback)((cityData2, scale, offsetX, offsetZ, zoomState2, abstractedPaths) => {
4384
4493
  const spatialGrid = new SpatialGrid(cityData2.bounds);
4385
4494
  cityData2.buildings.forEach((building) => {
@@ -15405,6 +15514,24 @@ function getLensColorModes() {
15405
15514
  function isLensColorMode(mode) {
15406
15515
  return getLensColorModes().includes(mode);
15407
15516
  }
15517
+ function getColorModeConfig(mode) {
15518
+ const builtIn = BUILT_IN_COLOR_MODES.find((m) => m.id === mode);
15519
+ if (builtIn) return builtIn;
15520
+ const lens = getLensById(mode);
15521
+ if (lens && lens.outputsFileMetrics) {
15522
+ const category = getCategoryConfig(lens.category);
15523
+ return {
15524
+ id: mode,
15525
+ name: lens.name,
15526
+ description: lens.description ?? `${lens.name} analysis`,
15527
+ icon: category == null ? void 0 : category.icon,
15528
+ colorScheme: lens.colorScheme,
15529
+ isBuiltIn: false,
15530
+ category: lens.category
15531
+ };
15532
+ }
15533
+ return void 0;
15534
+ }
15408
15535
  function getAllColorModeConfigs() {
15409
15536
  const configs = [...BUILT_IN_COLOR_MODES];
15410
15537
  for (const lens of LENS_REGISTRY) {
@@ -16336,11 +16463,19 @@ function createAlexandriaHighlightLayers(buildings, alexandriaMetrics) {
16336
16463
  }
16337
16464
  return layers;
16338
16465
  }
16466
+ function usesCoverageData(mode) {
16467
+ if (mode === "coverage") return true;
16468
+ if (isLensColorMode(mode)) {
16469
+ const config = getColorModeConfig(mode);
16470
+ return (config == null ? void 0 : config.colorScheme) === "coverage";
16471
+ }
16472
+ return false;
16473
+ }
16339
16474
  function getLayersForColorMode(mode, buildings, qualityData, fileColorLayers, gitLayers) {
16340
16475
  var _a;
16341
16476
  if (mode === "fileTypes") return fileColorLayers;
16342
16477
  if (mode === "git") return gitLayers;
16343
- if (mode === "coverage") {
16478
+ if (usesCoverageData(mode)) {
16344
16479
  if (qualityData == null ? void 0 : qualityData.fileCoverage) {
16345
16480
  return createCoverageHighlightLayers(buildings, qualityData.fileCoverage);
16346
16481
  }
@@ -16399,6 +16534,8 @@ const CodeCityPanelContent = ({
16399
16534
  const colorMode = sliceSelectedColorMode || "fileTypes";
16400
16535
  const [hoveredPackagePath, setHoveredPackagePath] = useState(null);
16401
16536
  const [selectedPackagePath, setSelectedPackagePath] = useState(null);
16537
+ const [zoomToPath, setZoomToPath] = useState(null);
16538
+ const [isZoomAnimating, setIsZoomAnimating] = useState(false);
16402
16539
  useEffect(() => {
16403
16540
  const container = contentContainerRef.current;
16404
16541
  if (!container) return;