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

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.
Files changed (59) hide show
  1. package/dist/components/FileCity3D/FileCity3D.d.ts +8 -2
  2. package/dist/components/FileCity3D/FileCity3D.d.ts.map +1 -1
  3. package/dist/components/FileCity3D/FileCity3D.js +129 -40
  4. package/dist/components/FileCityExplorer/AddToAreaModal.d.ts +14 -0
  5. package/dist/components/FileCityExplorer/AddToAreaModal.d.ts.map +1 -0
  6. package/dist/components/FileCityExplorer/AddToAreaModal.js +140 -0
  7. package/dist/components/FileCityExplorer/AddToScopeModal.d.ts +14 -0
  8. package/dist/components/FileCityExplorer/AddToScopeModal.d.ts.map +1 -0
  9. package/dist/components/FileCityExplorer/AddToScopeModal.js +176 -0
  10. package/dist/components/FileCityExplorer/FileCityExplorer.d.ts +30 -0
  11. package/dist/components/FileCityExplorer/FileCityExplorer.d.ts.map +1 -0
  12. package/dist/components/FileCityExplorer/FileCityExplorer.js +1045 -0
  13. package/dist/components/FileCityExplorer/ScopeInfoOverlay.d.ts +10 -0
  14. package/dist/components/FileCityExplorer/ScopeInfoOverlay.d.ts.map +1 -0
  15. package/dist/components/FileCityExplorer/ScopeInfoOverlay.js +73 -0
  16. package/dist/components/FileCityExplorer/index.d.ts +3 -0
  17. package/dist/components/FileCityExplorer/index.d.ts.map +1 -0
  18. package/dist/components/FileCityExplorer/index.js +1 -0
  19. package/dist/components/FileCityExplorer/layers.d.ts +16 -0
  20. package/dist/components/FileCityExplorer/layers.d.ts.map +1 -0
  21. package/dist/components/FileCityExplorer/layers.js +61 -0
  22. package/dist/components/FileCityExplorer/model.d.ts +32 -0
  23. package/dist/components/FileCityExplorer/model.d.ts.map +1 -0
  24. package/dist/components/FileCityExplorer/model.js +14 -0
  25. package/dist/components/FileCityExplorer/pathConversion.d.ts +19 -0
  26. package/dist/components/FileCityExplorer/pathConversion.d.ts.map +1 -0
  27. package/dist/components/FileCityExplorer/pathConversion.js +26 -0
  28. package/dist/components/FileCityExplorer/scopeTreePaths.d.ts +21 -0
  29. package/dist/components/FileCityExplorer/scopeTreePaths.d.ts.map +1 -0
  30. package/dist/components/FileCityExplorer/scopeTreePaths.js +42 -0
  31. package/dist/components/FileCityExplorer/styles.d.ts +9 -0
  32. package/dist/components/FileCityExplorer/styles.d.ts.map +1 -0
  33. package/dist/components/FileCityExplorer/styles.js +28 -0
  34. package/dist/index.d.ts +2 -0
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +2 -0
  37. package/dist/utils/folderElevatedPanels.d.ts +62 -0
  38. package/dist/utils/folderElevatedPanels.d.ts.map +1 -0
  39. package/dist/utils/folderElevatedPanels.js +130 -0
  40. package/package.json +2 -1
  41. package/src/components/FileCity3D/FileCity3D.tsx +200 -52
  42. package/src/components/FileCityExplorer/AddToAreaModal.tsx +273 -0
  43. package/src/components/FileCityExplorer/AddToScopeModal.tsx +320 -0
  44. package/src/components/FileCityExplorer/FileCityExplorer.tsx +1457 -0
  45. package/src/components/FileCityExplorer/ScopeInfoOverlay.tsx +229 -0
  46. package/src/components/FileCityExplorer/index.ts +2 -0
  47. package/src/components/FileCityExplorer/layers.ts +72 -0
  48. package/src/components/FileCityExplorer/model.ts +35 -0
  49. package/src/components/FileCityExplorer/pathConversion.ts +32 -0
  50. package/src/components/FileCityExplorer/scopeTreePaths.ts +52 -0
  51. package/src/components/FileCityExplorer/styles.ts +34 -0
  52. package/src/index.ts +8 -0
  53. package/src/stories/2D3DComparison.stories.tsx +13 -2
  54. package/src/stories/ElevatedScopePanels.stories.tsx +295 -0
  55. package/src/stories/FileCity3D.stories.tsx +24 -3
  56. package/src/stories/FileCityExplorer.stories.tsx +2474 -0
  57. package/src/stories/FileCityExplorerComponent.stories.tsx +59 -0
  58. package/src/utils/folderElevatedPanels.ts +176 -0
  59. package/src/stories/ScopeOverlay.stories.tsx +0 -1687
@@ -68,8 +68,14 @@ export interface ElevatedScopePanel {
68
68
  * size derived from the panel's footprint. Always clamped to fit the tile.
69
69
  */
70
70
  labelSize?: number;
71
+ /** Optional secondary label rendered above the main label in a smaller font. */
72
+ displayLabel?: string;
73
+ /** Hex color for the display label (default `labelColor` or white). */
74
+ displayLabelColor?: string;
71
75
  /** Click handler. When set, the slab becomes interactive and shows a pointer cursor. */
72
- onClick?: () => void;
76
+ onClick?: (event: MouseEvent) => void;
77
+ /** Double-click handler. The slab becomes interactive (pointer cursor) when either onClick or onDoubleClick is set. */
78
+ onDoubleClick?: (event: MouseEvent) => void;
73
79
  }
74
80
  /** Pattern for files that should render flat (e.g., lock files, generated files) */
75
81
  export interface FlatPattern {
@@ -179,7 +185,7 @@ export interface FileCity3DProps {
179
185
  /** Height of the container */
180
186
  height?: number | string;
181
187
  /** Callback when a building is clicked */
182
- onBuildingClick?: (building: CityBuilding) => void;
188
+ onBuildingClick?: (building: CityBuilding, event: MouseEvent) => void;
183
189
  /** CSS class name */
184
190
  className?: string;
185
191
  /** Inline styles */
@@ -1 +1 @@
1
- {"version":3,"file":"FileCity3D.d.ts","sourceRoot":"","sources":["../../../src/components/FileCity3D/FileCity3D.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"FileCity3D.d.ts","sourceRoot":"","sources":["../../../src/components/FileCity3D/FileCity3D.tsx"],"names":[],"mappings":"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;AAEzC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAKxD,OAAO,QAAQ,OAAO,CAAC;IAErB,UAAU,GAAG,CAAC;QAEZ,UAAU,iBAAkB,SAAQ,aAAa;SAAG;KACrD;CACF;AAGD,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,mBAAmB,EAAE,CAAC;AACrF,MAAM,MAAM,cAAc,GAAG,qBAAqB,CAAC;AAEnD,gDAAgD;AAChD,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,aAAa,GACb,UAAU,GACV,MAAM,CAAC;AAGX,MAAM,WAAW,eAAe;IAC9B,0CAA0C;IAC1C,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,mFAAmF;IACnF,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,2CAA2C;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wCAAwC;AACxC,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG,QAAQ,CAAC;AAErD;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,4CAA4C;IAC5C,EAAE,EAAE,MAAM,CAAC;IACX,gBAAgB;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACnE,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,wFAAwF;IACxF,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,uHAAuH;IACvH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;CAC7C;AAED,oFAAoF;AACpF,MAAM,WAAW,WAAW;IAC1B,qDAAqD;IACrD,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,yDAAyD;AACzD,eAAO,MAAM,qBAAqB,EAAE,WAAW,EAS9C,CAAC;AA6sCF,MAAM,WAAW,aAAa;IAC5B,qFAAqF;IACrF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AACjE,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AACvD,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,QAAQ,GAAG,WAAW,GAAG,cAAc,GAAG,MAAM,CAAC;AACtF,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,KAAK,CAAC;AAEzC,MAAM,WAAW,oBAAoB;IACnC,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,iDAAiD;IACjD,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,gDAAgD;IAChD,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B;yEACqE;IACrE,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,uCAAuC;IACvC,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,6CAA6C;IAC7C,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,IAAI,CAAC,oBAAoB,EAAE,UAAU,GAAG,aAAa,GAAG,WAAW,CAAC,CAAC,GAAG,IAAI,CAAC,oBAAoB,EAAE,UAAU,GAAG,aAAa,GAAG,WAAW,CAOzL,CAAC;AA8CF,wBAAgB,WAAW,SAE1B;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,QAE/D;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,QAEvF;AAED;;GAEG;AACH,wBAAgB,eAAe;OA9BA,MAAM;OAAK,MAAM;OAAK,MAAM;SAgC1D;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,gBAAgB,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,EAC9D,OAAO,CAAC,EAAE,aAAa,QAGxB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,QAEtE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,EAChD,OAAO,CAAC,EAAE,aAAa,QAGxB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,QAEpE;AAED,wBAAgB,iBAAiB;OAhFA,MAAM;OAAK,MAAM;OAAK,MAAM;SAkF5D;AAED;;;GAGG;AACH,wBAAgB,cAAc,kBAE7B;AAED;;;GAGG;AACH,wBAAgB,aAAa,kBAE5B;AAs1CD,MAAM,WAAW,eAAe;IAC9B,uCAAuC;IACvC,QAAQ,EAAE,QAAQ,CAAC;IACnB,6BAA6B;IAC7B,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,8BAA8B;IAC9B,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,0CAA0C;IAC1C,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtE,qBAAqB;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B,8BAA8B;IAC9B,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,wEAAwE;IACxE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,uCAAuC;IACvC,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1C,0GAA0G;IAC1G,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,kEAAkE;IAClE,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;IACnC,yEAAyE;IACzE,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uCAAuC;IACvC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2IAA2I;IAC3I,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;IAC7B,mEAAmE;IACnE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,0DAA0D;IAC1D,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACvD,gDAAgD;IAChD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,gBAAgB,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACvC,4EAA4E;IAC5E,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAEjC,kEAAkE;IAClE,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;IAEnC;;;OAGG;IACH,mBAAmB,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAE3C;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,oBAAoB,CAAC;CACvC;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,KAAc,EACd,MAAY,EACZ,eAAe,EACf,SAAS,EACT,KAAK,EACL,SAAS,EACT,OAAO,EAAE,eAAe,EACxB,YAAY,EACZ,YAAoB,EACpB,mBAAmB,EACnB,eAAe,EAAE,uBAAuB,EACxC,aAAa,EAAE,qBAAqB,EACpC,UAAU,EAAE,WAAkB,EAC9B,SAAiB,EACjB,cAAuC,EACvC,YAA4C,EAC5C,aAAwB,EACxB,WAAe,EACf,YAAoC,EACpC,cAAc,EAAE,sBAAsB,EACtC,UAAU,EAAE,kBAAkB,EAC9B,iBAAiB,EAAE,kBAAkB,EACrC,eAA2B,EAC3B,SAAqB,EACrB,gBAAuB,EACvB,sBAA8B,EAC9B,eAAe,EACf,cAAc,GACf,EAAE,eAAe,2CA4KjB;AAED,eAAe,UAAU,CAAC"}
@@ -8,6 +8,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
8
8
  * Supports animated transition from 2D (flat) to 3D (grown buildings).
9
9
  */
10
10
  import React, { useMemo, useRef, useState, useEffect, useCallback } from 'react';
11
+ import { useTheme } from '@principal-ade/industry-theme';
11
12
  import { Canvas, useFrame, useThree } from '@react-three/fiber';
12
13
  import { useSpring } from '@react-spring/three';
13
14
  import { MapControls, PerspectiveCamera, Text } from '@react-three/drei';
@@ -377,7 +378,7 @@ function BorderHighlights({ buildings, centerOffset, highlightLayers, growProgre
377
378
  const currentTime = clock.elapsedTime * 1000;
378
379
  const animStartTime = startTimeRef.current ?? currentTime;
379
380
  borderEdgeData.forEach((edge, idx) => {
380
- const { x, z, fullHeight, staggerDelayMs, buildingIndex, color, opacity, borderWidth, edgeType, width, depth } = edge;
381
+ const { x, z, fullHeight, staggerDelayMs, buildingIndex, color, borderWidth, edgeType, width, depth } = edge;
381
382
  // Get height multiplier from shared ref (for collapse animation)
382
383
  const heightMultiplier = heightMultipliersRef.current?.[buildingIndex] ?? 1;
383
384
  // Calculate per-building animation progress
@@ -656,7 +657,7 @@ function InstancedBuildings({ buildings, centerOffset, onHover, onClick, hovered
656
657
  e.stopPropagation();
657
658
  if (e.instanceId !== undefined && e.instanceId < buildingData.length) {
658
659
  const data = buildingData[e.instanceId];
659
- onClick?.(data.building);
660
+ onClick?.(data.building, e.nativeEvent);
660
661
  }
661
662
  }, [buildingData, onClick]);
662
663
  if (buildingData.length === 0)
@@ -1034,6 +1035,68 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1034
1035
  });
1035
1036
  // eslint-disable-next-line react-hooks/exhaustive-deps
1036
1037
  }, [isFlat]); // Only animate when isFlat changes, not when focusTarget/citySize/etc change
1038
+ // Animate the camera when focusTarget changes (works in both 2D and 3D).
1039
+ // - 3D + target: frame the directory using the same math as the 2D→3D path
1040
+ // - 3D + null: ease back to the overview position
1041
+ // - 2D + target: pan top-down camera over the directory and zoom to fit it
1042
+ // - 2D + null: return to the centered top-down overview
1043
+ useEffect(() => {
1044
+ if (!hasAppliedInitial.current)
1045
+ return;
1046
+ let newPos;
1047
+ if (isFlat) {
1048
+ if (focusTarget) {
1049
+ const perspCam = camera;
1050
+ const aspect = perspCam.aspect || 1;
1051
+ const fovRad = (50 * Math.PI) / 180;
1052
+ const tanHalfFov = Math.tan(fovRad / 2);
1053
+ const effectiveAspect = Math.min(1, aspect);
1054
+ // Same framing math as calculateFlatCameraHeight, but using the focus
1055
+ // region's size instead of citySize so the directory fills the view.
1056
+ const height = (focusTarget.size / (2 * tanHalfFov * effectiveAspect)) * 1.08;
1057
+ newPos = {
1058
+ x: focusTarget.x,
1059
+ y: height,
1060
+ z: focusTarget.z + 0.001,
1061
+ targetX: focusTarget.x,
1062
+ targetY: 0,
1063
+ targetZ: focusTarget.z,
1064
+ };
1065
+ }
1066
+ else {
1067
+ newPos = getInitial2DPosition();
1068
+ }
1069
+ }
1070
+ else if (focusTarget) {
1071
+ newPos = {
1072
+ x: focusTarget.x,
1073
+ y: Math.max(focusTarget.size * 1.5, 40),
1074
+ z: focusTarget.z + Math.max(focusTarget.size * 2, 50),
1075
+ targetX: focusTarget.x,
1076
+ targetY: 0,
1077
+ targetZ: focusTarget.z,
1078
+ };
1079
+ }
1080
+ else {
1081
+ newPos = {
1082
+ x: 0,
1083
+ y: maxBuildingHeight > 0 ? Math.max(citySize * 1.1, maxBuildingHeight * 2.5) : citySize * 1.1,
1084
+ z: citySize * 1.3,
1085
+ targetX: 0,
1086
+ targetY: 0,
1087
+ targetZ: 0,
1088
+ };
1089
+ }
1090
+ api.start({
1091
+ camX: newPos.x,
1092
+ camY: newPos.y,
1093
+ camZ: newPos.z,
1094
+ lookX: newPos.targetX,
1095
+ lookY: newPos.targetY,
1096
+ lookZ: newPos.targetZ,
1097
+ });
1098
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1099
+ }, [focusTarget, isFlat]);
1037
1100
  // Update camera each frame
1038
1101
  useFrame(() => {
1039
1102
  frameCount.current++;
@@ -1126,11 +1189,6 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1126
1189
  }
1127
1190
  // Handle position animation
1128
1191
  else if (isAnimatingRef.current) {
1129
- const springY = camY.get();
1130
- const currentY = camera.position.y;
1131
- if (Math.abs(springY - currentY) > 1) {
1132
- console.log('[useFrame] Spring animating - springY:', springY, 'currentY:', currentY);
1133
- }
1134
1192
  camera.position.set(camX.get(), camY.get(), camZ.get());
1135
1193
  controlsRef.current.target.set(lookX.get(), lookY.get(), lookZ.get());
1136
1194
  controlsRef.current.update();
@@ -1426,14 +1484,17 @@ const AnimatedCamera = React.memo(function AnimatedCamera({ citySize, isFlat, fo
1426
1484
  }), [controlsConfig.oneFingerTouch, controlsConfig.twoFingerTouch]);
1427
1485
  return (_jsxs(_Fragment, { children: [_jsx(PerspectiveCamera, { makeDefault: true, fov: 50, near: 1, far: citySize * 10 }), _jsx(MapControls, { ref: controlsRef, enableDamping: true, dampingFactor: 0.05, minDistance: 10, maxDistance: citySize * 3, maxPolarAngle: Math.PI / 2.1, mouseButtons: mouseButtons, touches: touches, enableZoom: controlsConfig.wheel !== 'pan', panSpeed: controlsConfig.panSpeed ?? 1, rotateSpeed: controlsConfig.rotateSpeed ?? 1, zoomSpeed: controlsConfig.zoomSpeed ?? 1 })] }));
1428
1486
  }, (prevProps, nextProps) => {
1429
- // Custom comparison: only re-render if isFlat, citySize, maxBuildingHeight, or cameraControls changes
1430
- // Skip re-render when only focusTarget changes (handled internally by useEffect on isFlat)
1487
+ // Custom comparison: re-render when isFlat, citySize, maxBuildingHeight,
1488
+ // cameraControls, or focusTarget change. focusTarget is included so the
1489
+ // useEffect that animates the camera on focus changes actually fires.
1431
1490
  return (prevProps.isFlat === nextProps.isFlat &&
1432
1491
  prevProps.citySize === nextProps.citySize &&
1433
1492
  prevProps.maxBuildingHeight === nextProps.maxBuildingHeight &&
1434
- prevProps.cameraControls === nextProps.cameraControls);
1493
+ prevProps.cameraControls === nextProps.cameraControls &&
1494
+ prevProps.focusTarget === nextProps.focusTarget);
1435
1495
  });
1436
1496
  function InfoPanel({ building }) {
1497
+ const { theme } = useTheme();
1437
1498
  if (!building)
1438
1499
  return null;
1439
1500
  const fileName = building.path.split('/').pop();
@@ -1442,55 +1503,56 @@ function InfoPanel({ building }) {
1442
1503
  position: 'absolute',
1443
1504
  bottom: 16,
1444
1505
  left: 60,
1445
- background: 'rgba(15, 23, 42, 0.9)',
1446
- border: '1px solid #334155',
1447
- borderRadius: 8,
1506
+ background: `color-mix(in oklab, ${theme.colors.background} 90%, transparent)`,
1507
+ border: `1px solid ${theme.colors.border}`,
1508
+ borderRadius: theme.radii[4],
1448
1509
  padding: '12px 16px',
1449
- color: '#e2e8f0',
1450
- fontSize: 14,
1451
- fontFamily: 'monospace',
1510
+ color: theme.colors.text,
1511
+ fontSize: theme.fontSizes[1],
1512
+ fontFamily: theme.fonts.monospace,
1452
1513
  maxWidth: 400,
1453
1514
  pointerEvents: 'none',
1454
- }, children: [_jsx("div", { style: { fontWeight: 600, marginBottom: 4 }, children: fileName }), _jsx("div", { style: { color: '#94a3b8', fontSize: 12 }, children: dirPath }), _jsxs("div", { style: {
1455
- color: '#64748b',
1515
+ }, children: [_jsx("div", { style: { fontWeight: theme.fontWeights.semibold, marginBottom: 4 }, children: fileName }), _jsx("div", { style: { color: theme.colors.textMuted, fontSize: theme.fontSizes[0] }, children: dirPath }), _jsxs("div", { style: {
1516
+ color: theme.colors.textTertiary,
1456
1517
  fontSize: 11,
1457
1518
  marginTop: 4,
1458
1519
  display: 'flex',
1459
- gap: 12,
1520
+ gap: theme.space[3],
1460
1521
  }, children: [building.lineCount !== undefined && (_jsxs("span", { children: [building.lineCount.toLocaleString(), " lines"] })), building.size !== undefined && _jsxs("span", { children: [(building.size / 1024).toFixed(1), " KB"] })] })] }));
1461
1522
  }
1462
1523
  function ControlsOverlay({ isFlat, onToggle, onResetCamera, onLookDown }) {
1524
+ const { theme } = useTheme();
1463
1525
  const buttonStyle = {
1464
- background: 'rgba(15, 23, 42, 0.9)',
1465
- border: '1px solid #334155',
1466
- borderRadius: 8,
1526
+ background: `color-mix(in oklab, ${theme.colors.background} 90%, transparent)`,
1527
+ border: `1px solid ${theme.colors.border}`,
1528
+ borderRadius: theme.radii[4],
1467
1529
  padding: '10px',
1468
- color: '#e2e8f0',
1469
- fontSize: 14,
1530
+ color: theme.colors.text,
1531
+ fontSize: theme.fontSizes[1],
1470
1532
  cursor: 'pointer',
1471
1533
  display: 'flex',
1472
1534
  alignItems: 'center',
1473
1535
  justifyContent: 'center',
1474
1536
  width: 40,
1475
1537
  height: 40,
1476
- fontWeight: 500,
1538
+ fontWeight: theme.fontWeights.medium,
1477
1539
  };
1478
1540
  return (_jsxs(_Fragment, { children: [_jsx("button", { onClick: onToggle, style: {
1479
1541
  ...buttonStyle,
1480
1542
  position: 'absolute',
1481
- top: 8,
1482
- left: 8,
1483
- }, children: isFlat ? '3D' : '2D' }), _jsx("button", { onClick: onResetCamera, style: {
1484
- ...buttonStyle,
1485
- position: 'absolute',
1486
- top: 8,
1543
+ bottom: 8,
1487
1544
  right: 8,
1488
- }, title: "Reset View", children: "\u21BB" }), _jsx("button", { onClick: onLookDown, style: {
1545
+ }, children: isFlat ? '3D' : '2D' }), _jsx("button", { onClick: onLookDown, style: {
1489
1546
  ...buttonStyle,
1490
1547
  position: 'absolute',
1491
1548
  bottom: 8,
1492
1549
  left: 8,
1493
- }, title: "Look down", children: "\u2B07" })] }));
1550
+ }, title: "Look down", children: "\u2B07" }), _jsx("button", { onClick: onResetCamera, style: {
1551
+ ...buttonStyle,
1552
+ position: 'absolute',
1553
+ bottom: 8,
1554
+ left: 56,
1555
+ }, title: "Reset View", children: "\u21BB" })] }));
1494
1556
  }
1495
1557
  function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding, selectedBuilding, growProgress, animationConfig, highlightLayers, isolationMode, heightScaling, linearScale, flatPatterns, focusDirectory, focusColor, adaptCameraToBuildings = false, elevatedScopePanels, cameraControls, onCameraReady, }) {
1496
1558
  const centerOffset = useMemo(() => ({
@@ -1580,14 +1642,31 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
1580
1642
  }
1581
1643
  // Case 3: Switching between directories (dirA -> dirB)
1582
1644
  if (prevFocus !== null && focusDirectory !== null) {
1583
- // Phase 1: Zoom camera out
1645
+ // Direct transition when the two directories are visually adjacent:
1646
+ // - parent ↔ child (one is a prefix of the other)
1647
+ // - immediate siblings (same parent folder)
1648
+ // In both cases the new directory is already in or near the current
1649
+ // view, so a zoom-out detour would feel like extra travel.
1650
+ const isDescendant = focusDirectory.startsWith(prevFocus + '/');
1651
+ const isAncestor = prevFocus.startsWith(focusDirectory + '/');
1652
+ const parentOf = (p) => {
1653
+ const i = p.lastIndexOf('/');
1654
+ return i >= 0 ? p.slice(0, i) : '';
1655
+ };
1656
+ const isSibling = parentOf(prevFocus) === parentOf(focusDirectory);
1657
+ if (isDescendant || isAncestor || isSibling) {
1658
+ setBuildingFocusDirectory(focusDirectory);
1659
+ setBuildingFocusColor(focusColor ?? null);
1660
+ setCameraFocusDirectory(focusDirectory);
1661
+ return;
1662
+ }
1663
+ // Unrelated branches — keep the 3-phase out/in transition so the
1664
+ // long camera flight stays legible.
1584
1665
  setCameraFocusDirectory(null);
1585
- // Phase 2: After zoom-out, collapse/expand buildings with new color
1586
1666
  const timer1 = setTimeout(() => {
1587
1667
  setBuildingFocusDirectory(focusDirectory);
1588
1668
  setBuildingFocusColor(focusColor ?? null);
1589
1669
  }, 500);
1590
- // Phase 3: After collapse settles, zoom camera into new directory
1591
1670
  const timer2 = setTimeout(() => {
1592
1671
  setCameraFocusDirectory(focusDirectory);
1593
1672
  }, 1100); // 500ms zoom-out + 600ms collapse
@@ -1697,24 +1776,34 @@ function CityScene({ cityData, onBuildingHover, onBuildingClick, hoveredBuilding
1697
1776
  const tileMax = Math.min(w, d) / 2;
1698
1777
  const requested = panel.labelSize ?? Math.min(w, d) / 6;
1699
1778
  const labelSize = Math.max(4, Math.min(tileMax, requested));
1779
+ const interactive = Boolean(panel.onClick || panel.onDoubleClick);
1700
1780
  const handleClick = panel.onClick
1701
1781
  ? (e) => {
1702
1782
  e.stopPropagation();
1703
- panel.onClick();
1783
+ panel.onClick(e.nativeEvent);
1784
+ }
1785
+ : undefined;
1786
+ const handleDoubleClick = panel.onDoubleClick
1787
+ ? (e) => {
1788
+ e.stopPropagation();
1789
+ panel.onDoubleClick(e.nativeEvent);
1704
1790
  }
1705
1791
  : undefined;
1706
- const handlePointerOver = panel.onClick
1792
+ const handlePointerOver = interactive
1707
1793
  ? (e) => {
1708
1794
  e.stopPropagation();
1709
1795
  document.body.style.cursor = 'pointer';
1710
1796
  }
1711
1797
  : undefined;
1712
- const handlePointerOut = panel.onClick
1798
+ const handlePointerOut = interactive
1713
1799
  ? () => {
1714
1800
  document.body.style.cursor = '';
1715
1801
  }
1716
1802
  : undefined;
1717
- return (_jsxs("group", { children: [_jsxs("mesh", { position: [cx, y, cz], renderOrder: 10, onClick: handleClick, onPointerOver: handlePointerOver, onPointerOut: handlePointerOut, children: [_jsx("boxGeometry", { args: [w, t, d] }), _jsx("meshBasicMaterial", { color: panel.color, transparent: !isOpaque, opacity: opacity, depthWrite: isOpaque })] }), panel.label && (_jsxs(Text, { position: [cx, topY + 0.05, cz], rotation: [-Math.PI / 2, 0, 0], fontSize: labelSize, color: panel.labelColor ?? '#ffffff', anchorX: "center", anchorY: "middle", maxWidth: w * 0.9, textAlign: "center", renderOrder: 11, frustumCulled: false, children: [panel.label, _jsx("meshBasicMaterial", { attach: "material", color: panel.labelColor ?? '#ffffff', depthWrite: false, depthTest: false })] }))] }, panel.id));
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));
1718
1807
  })] }));
1719
1808
  }
1720
1809
  /**
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import type { ProjectArea } from './model';
3
+ export declare const AddToAreaModal: React.FC<{
4
+ path: string;
5
+ areas: readonly ProjectArea[];
6
+ areaName: string;
7
+ description: string;
8
+ onAreaNameChange: (value: string) => void;
9
+ onDescriptionChange: (value: string) => void;
10
+ onPickExisting: (areaName: string) => void;
11
+ onSubmit: () => void;
12
+ onClose: () => void;
13
+ }>;
14
+ //# sourceMappingURL=AddToAreaModal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AddToAreaModal.d.ts","sourceRoot":"","sources":["../../../src/components/FileCityExplorer/AddToAreaModal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAI3C,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,mBAAmB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAgQA,CAAC"}
@@ -0,0 +1,140 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { useTheme } from '@principal-ade/industry-theme';
4
+ import { AREA_PANEL_COLOR } from './layers';
5
+ import { makeSectionLabelStyle } from './styles';
6
+ export const AddToAreaModal = ({ path, areas, areaName, description, onAreaNameChange, onDescriptionChange, onPickExisting, onSubmit, onClose, }) => {
7
+ const { theme } = useTheme();
8
+ const sectionLabelStyle = makeSectionLabelStyle(theme);
9
+ React.useEffect(() => {
10
+ const onKey = (e) => {
11
+ if (e.key === 'Escape')
12
+ onClose();
13
+ };
14
+ window.addEventListener('keydown', onKey);
15
+ return () => window.removeEventListener('keydown', onKey);
16
+ }, [onClose]);
17
+ const trimmedName = areaName.trim();
18
+ const canSubmit = trimmedName.length > 0;
19
+ const targetArea = areas.find(a => a.name === trimmedName);
20
+ const alreadyClaimed = targetArea?.paths.includes(path) ?? false;
21
+ let actionLabel = 'Add';
22
+ if (alreadyClaimed)
23
+ actionLabel = 'Already added';
24
+ else if (!targetArea)
25
+ actionLabel = 'Create area';
26
+ else
27
+ actionLabel = 'Add path';
28
+ const sectionDivider = `1px solid ${theme.colors.backgroundSecondary}`;
29
+ const inputStyle = {
30
+ padding: '8px 10px',
31
+ background: theme.colors.backgroundDark ?? theme.colors.background,
32
+ color: theme.colors.text,
33
+ border: `1px solid ${theme.colors.border}`,
34
+ borderRadius: theme.radii[2],
35
+ fontFamily: theme.fonts.monospace,
36
+ fontSize: theme.fontSizes[1],
37
+ };
38
+ return (_jsx("div", { onClick: onClose, style: {
39
+ position: 'fixed',
40
+ inset: 0,
41
+ background: 'rgba(0, 0, 0, 0.55)',
42
+ display: 'flex',
43
+ alignItems: 'center',
44
+ justifyContent: 'center',
45
+ zIndex: 1000,
46
+ fontFamily: theme.fonts.body,
47
+ }, children: _jsxs("div", { onClick: e => e.stopPropagation(), style: {
48
+ width: 520,
49
+ maxHeight: 'min(80vh, 700px)',
50
+ display: 'flex',
51
+ flexDirection: 'column',
52
+ background: theme.colors.background,
53
+ color: theme.colors.text,
54
+ borderRadius: theme.radii[4],
55
+ border: `1px solid ${theme.colors.border}`,
56
+ boxShadow: theme.shadows[4],
57
+ overflow: 'hidden',
58
+ }, children: [_jsxs("div", { style: {
59
+ padding: '14px 18px',
60
+ borderBottom: sectionDivider,
61
+ display: 'flex',
62
+ justifyContent: 'space-between',
63
+ alignItems: 'flex-start',
64
+ gap: theme.space[3],
65
+ }, children: [_jsxs("div", { children: [_jsx("div", { style: sectionLabelStyle, children: "Add to area" }), _jsx("div", { style: {
66
+ fontFamily: theme.fonts.monospace,
67
+ fontSize: theme.fontSizes[0],
68
+ color: theme.colors.textMuted,
69
+ marginTop: 6,
70
+ wordBreak: 'break-all',
71
+ }, children: path })] }), _jsx("button", { onClick: onClose, style: {
72
+ background: 'transparent',
73
+ border: 'none',
74
+ color: theme.colors.textTertiary,
75
+ fontSize: theme.fontSizes[3],
76
+ cursor: 'pointer',
77
+ lineHeight: 1,
78
+ padding: 0,
79
+ }, "aria-label": "Close", children: "\u00D7" })] }), _jsxs("div", { style: {
80
+ padding: '14px 18px',
81
+ borderBottom: sectionDivider,
82
+ display: 'flex',
83
+ flexDirection: 'column',
84
+ gap: theme.space[3],
85
+ }, children: [_jsxs("label", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: [_jsx("span", { style: sectionLabelStyle, children: "Area" }), _jsx("input", { type: "text", value: areaName, list: "area-name-options", autoFocus: true, placeholder: "e.g. Documentation", onChange: e => onAreaNameChange(e.target.value), onKeyDown: e => {
86
+ if (e.key === 'Enter' && canSubmit && !alreadyClaimed)
87
+ onSubmit();
88
+ }, style: inputStyle }), _jsx("datalist", { id: "area-name-options", children: areas.map(a => (_jsx("option", { value: a.name }, a.name))) })] }), !targetArea && trimmedName.length > 0 && (_jsxs("label", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: [_jsx("span", { style: sectionLabelStyle, children: "Description (optional)" }), _jsx("input", { type: "text", value: description, placeholder: "Why this area exists, what it covers", onChange: e => onDescriptionChange(e.target.value), onKeyDown: e => {
89
+ if (e.key === 'Enter' && canSubmit)
90
+ onSubmit();
91
+ }, style: { ...inputStyle, fontFamily: theme.fonts.body } })] })), _jsxs("div", { style: { display: 'flex', justifyContent: 'flex-end', gap: theme.space[2] }, children: [_jsx("button", { onClick: onClose, style: {
92
+ padding: '8px 14px',
93
+ background: 'transparent',
94
+ color: theme.colors.textSecondary,
95
+ border: `1px solid ${theme.colors.border}`,
96
+ borderRadius: theme.radii[2],
97
+ cursor: 'pointer',
98
+ fontSize: theme.fontSizes[1],
99
+ }, children: "Cancel" }), _jsx("button", { onClick: onSubmit, disabled: !canSubmit || alreadyClaimed, style: {
100
+ padding: '8px 14px',
101
+ background: !canSubmit || alreadyClaimed
102
+ ? theme.colors.backgroundSecondary
103
+ : theme.colors.textMuted,
104
+ color: !canSubmit || alreadyClaimed
105
+ ? theme.colors.muted
106
+ : theme.colors.background,
107
+ border: `1px solid ${theme.colors.border}`,
108
+ borderRadius: theme.radii[2],
109
+ cursor: !canSubmit || alreadyClaimed ? 'not-allowed' : 'pointer',
110
+ fontSize: theme.fontSizes[1],
111
+ fontWeight: theme.fontWeights.medium,
112
+ }, children: actionLabel })] })] }), _jsxs("div", { style: { padding: '14px 18px', overflowY: 'auto', flex: 1 }, children: [_jsx("div", { style: sectionLabelStyle, children: "Existing areas (click to prefill)" }), _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 6, marginTop: theme.space[2] }, children: [areas.length === 0 && (_jsx("div", { style: { fontSize: theme.fontSizes[0], color: theme.colors.textTertiary, fontStyle: 'italic' }, children: "No areas yet. Type a name above to create the first one." })), areas.map(area => {
113
+ const claims = area.paths.includes(path);
114
+ return (_jsxs("button", { onClick: () => onPickExisting(area.name), title: claims ? 'Area already claims this path' : 'Prefill the area name', style: {
115
+ fontSize: theme.fontSizes[0],
116
+ padding: '6px 10px',
117
+ background: claims ? theme.colors.background : theme.colors.backgroundSecondary,
118
+ color: claims ? theme.colors.muted : theme.colors.text,
119
+ border: `1px solid ${theme.colors.border}`,
120
+ borderRadius: theme.radii[2],
121
+ cursor: 'pointer',
122
+ textAlign: 'left',
123
+ display: 'flex',
124
+ alignItems: 'center',
125
+ gap: theme.space[2],
126
+ opacity: claims ? 0.6 : 1,
127
+ }, children: [_jsx("span", { style: {
128
+ width: 10,
129
+ height: 10,
130
+ borderRadius: theme.radii[1],
131
+ background: AREA_PANEL_COLOR,
132
+ border: `1px dashed ${theme.colors.textMuted}`,
133
+ flexShrink: 0,
134
+ } }), _jsx("span", { style: { fontFamily: theme.fonts.monospace }, children: area.name }), _jsxs("span", { style: {
135
+ marginLeft: 'auto',
136
+ fontSize: theme.fontSizes[0],
137
+ color: theme.colors.textTertiary,
138
+ }, children: [area.paths.length, " path", area.paths.length === 1 ? '' : 's'] }), claims && _jsx("span", { style: { marginLeft: theme.space[1], fontSize: theme.fontSizes[0] }, children: "\u2713" })] }, area.name));
139
+ })] })] })] }) }));
140
+ };
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import type { Scope } from './model';
3
+ export declare const AddToScopeModal: React.FC<{
4
+ path: string;
5
+ scopes: readonly Scope[];
6
+ scopeId: string;
7
+ namespaceName: string;
8
+ onScopeIdChange: (value: string) => void;
9
+ onNamespaceNameChange: (value: string) => void;
10
+ onPickExisting: (scopeId: string, namespaceName: string) => void;
11
+ onSubmit: () => void;
12
+ onClose: () => void;
13
+ }>;
14
+ //# sourceMappingURL=AddToScopeModal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AddToScopeModal.d.ts","sourceRoot":"","sources":["../../../src/components/FileCityExplorer/AddToScopeModal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAGrC,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,qBAAqB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,cAAc,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IACjE,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAgTA,CAAC"}