@collabdt/core 0.0.56 → 0.0.57

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 (256) hide show
  1. package/dist/core/components/AppSidebarContent.d.ts +2 -1
  2. package/dist/core/components/AppSidebarContent.d.ts.map +1 -1
  3. package/dist/core/components/AppSidebarContent.js +10 -7
  4. package/dist/core/components/AppSidebarContent.js.map +1 -1
  5. package/dist/core/components/Toolbar.d.ts +7 -1
  6. package/dist/core/components/Toolbar.d.ts.map +1 -1
  7. package/dist/core/components/Toolbar.js +3 -3
  8. package/dist/core/components/Toolbar.js.map +1 -1
  9. package/dist/core/components/TopNavigationBar.d.ts +6 -1
  10. package/dist/core/components/TopNavigationBar.d.ts.map +1 -1
  11. package/dist/core/components/TopNavigationBar.js +2 -2
  12. package/dist/core/components/TopNavigationBar.js.map +1 -1
  13. package/dist/core/components/authentication/AuthPage.d.ts +2 -1
  14. package/dist/core/components/authentication/AuthPage.d.ts.map +1 -1
  15. package/dist/core/components/authentication/AuthPage.js +3 -5
  16. package/dist/core/components/authentication/AuthPage.js.map +1 -1
  17. package/dist/core/components/authentication/Signin.d.ts +2 -1
  18. package/dist/core/components/authentication/Signin.d.ts.map +1 -1
  19. package/dist/core/components/authentication/Signin.js +2 -2
  20. package/dist/core/components/authentication/Signin.js.map +1 -1
  21. package/dist/core/components/organizationConfig/src/organizationConfigContent.d.ts.map +1 -1
  22. package/dist/core/components/organizationConfig/src/organizationConfigContent.js +109 -36
  23. package/dist/core/components/organizationConfig/src/organizationConfigContent.js.map +1 -1
  24. package/dist/core/components/settings/index.d.ts +3 -1
  25. package/dist/core/components/settings/index.d.ts.map +1 -1
  26. package/dist/core/components/settings/index.js +2 -2
  27. package/dist/core/components/settings/index.js.map +1 -1
  28. package/dist/core/components/settings/src/AccountSettingsPanel.d.ts.map +1 -1
  29. package/dist/core/components/settings/src/AccountSettingsPanel.js +9 -2
  30. package/dist/core/components/settings/src/AccountSettingsPanel.js.map +1 -1
  31. package/dist/core/components/settings/src/OrganizationSettingsPanel.d.ts +5 -1
  32. package/dist/core/components/settings/src/OrganizationSettingsPanel.d.ts.map +1 -1
  33. package/dist/core/components/settings/src/OrganizationSettingsPanel.js +7 -5
  34. package/dist/core/components/settings/src/OrganizationSettingsPanel.js.map +1 -1
  35. package/dist/core/components/settings/src/SettingsContent.d.ts +2 -1
  36. package/dist/core/components/settings/src/SettingsContent.d.ts.map +1 -1
  37. package/dist/core/components/settings/src/SettingsContent.js +2 -2
  38. package/dist/core/components/settings/src/SettingsContent.js.map +1 -1
  39. package/dist/core/components/support/BugReportDialog.d.ts +3 -2
  40. package/dist/core/components/support/BugReportDialog.d.ts.map +1 -1
  41. package/dist/core/components/support/BugReportDialog.js +71 -25
  42. package/dist/core/components/support/BugReportDialog.js.map +1 -1
  43. package/dist/core/components/support/FeatureRequestDialog.d.ts +3 -1
  44. package/dist/core/components/support/FeatureRequestDialog.d.ts.map +1 -1
  45. package/dist/core/components/support/FeatureRequestDialog.js +7 -4
  46. package/dist/core/components/support/FeatureRequestDialog.js.map +1 -1
  47. package/dist/core/components/support/SupportMenu.d.ts.map +1 -1
  48. package/dist/core/components/support/SupportMenu.js +1 -4
  49. package/dist/core/components/support/SupportMenu.js.map +1 -1
  50. package/dist/core/components/ui/AnimatedBackground.d.ts +2 -1
  51. package/dist/core/components/ui/AnimatedBackground.d.ts.map +1 -1
  52. package/dist/core/components/ui/AnimatedBackground.js +2 -5
  53. package/dist/core/components/ui/AnimatedBackground.js.map +1 -1
  54. package/dist/core/components/ui/InfoSidebar/Container.d.ts +3 -1
  55. package/dist/core/components/ui/InfoSidebar/Container.d.ts.map +1 -1
  56. package/dist/core/components/ui/InfoSidebar/Container.js +4 -2
  57. package/dist/core/components/ui/InfoSidebar/Container.js.map +1 -1
  58. package/dist/core/components/ui/InfoSidebar/Header.d.ts +3 -2
  59. package/dist/core/components/ui/InfoSidebar/Header.d.ts.map +1 -1
  60. package/dist/core/components/ui/InfoSidebar/Header.js +5 -6
  61. package/dist/core/components/ui/InfoSidebar/Header.js.map +1 -1
  62. package/dist/core/components/ui/InfoSidebar/index.d.ts +7 -1
  63. package/dist/core/components/ui/InfoSidebar/index.d.ts.map +1 -1
  64. package/dist/core/components/ui/InfoSidebar/index.js +4 -4
  65. package/dist/core/components/ui/InfoSidebar/index.js.map +1 -1
  66. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts +2 -1
  67. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts.map +1 -1
  68. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js +3 -4
  69. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js.map +1 -1
  70. package/dist/core/components/ui/Sensors/SensorsSection.d.ts +3 -1
  71. package/dist/core/components/ui/Sensors/SensorsSection.d.ts.map +1 -1
  72. package/dist/core/components/ui/Sensors/SensorsSection.js +3 -1
  73. package/dist/core/components/ui/Sensors/SensorsSection.js.map +1 -1
  74. package/dist/core/components/ui/Sidebar.d.ts +8 -0
  75. package/dist/core/components/ui/Sidebar.d.ts.map +1 -1
  76. package/dist/core/components/ui/Sidebar.js +21 -7
  77. package/dist/core/components/ui/Sidebar.js.map +1 -1
  78. package/dist/core/components/ui/ToolbarButton.js +18 -1
  79. package/dist/core/components/ui/ToolbarButton.js.map +1 -1
  80. package/dist/core/components/viewers/Data/DataMenu.d.ts +5 -1
  81. package/dist/core/components/viewers/Data/DataMenu.d.ts.map +1 -1
  82. package/dist/core/components/viewers/Data/DataMenu.js +6 -3
  83. package/dist/core/components/viewers/Data/DataMenu.js.map +1 -1
  84. package/dist/core/components/viewers/Data/HeaderButtons.d.ts +3 -1
  85. package/dist/core/components/viewers/Data/HeaderButtons.d.ts.map +1 -1
  86. package/dist/core/components/viewers/Data/HeaderButtons.js +6 -2
  87. package/dist/core/components/viewers/Data/HeaderButtons.js.map +1 -1
  88. package/dist/core/components/viewers/Data/buildingDetails/AddBuilding.d.ts +3 -1
  89. package/dist/core/components/viewers/Data/buildingDetails/AddBuilding.d.ts.map +1 -1
  90. package/dist/core/components/viewers/Data/buildingDetails/AddBuilding.js +4 -2
  91. package/dist/core/components/viewers/Data/buildingDetails/AddBuilding.js.map +1 -1
  92. package/dist/core/components/viewers/Data/buildingDetails/GeocoderInput.d.ts +3 -1
  93. package/dist/core/components/viewers/Data/buildingDetails/GeocoderInput.d.ts.map +1 -1
  94. package/dist/core/components/viewers/Data/buildingDetails/GeocoderInput.js +3 -1
  95. package/dist/core/components/viewers/Data/buildingDetails/GeocoderInput.js.map +1 -1
  96. package/dist/core/components/viewers/Data/details/DetailHeader.d.ts +2 -1
  97. package/dist/core/components/viewers/Data/details/DetailHeader.d.ts.map +1 -1
  98. package/dist/core/components/viewers/Data/details/DetailHeader.js +3 -5
  99. package/dist/core/components/viewers/Data/details/DetailHeader.js.map +1 -1
  100. package/dist/core/components/viewers/Viewer.d.ts +7 -1
  101. package/dist/core/components/viewers/Viewer.d.ts.map +1 -1
  102. package/dist/core/components/viewers/Viewer.js +6 -6
  103. package/dist/core/components/viewers/Viewer.js.map +1 -1
  104. package/dist/core/components/viewers/bim/BimToolbar.d.ts +3 -1
  105. package/dist/core/components/viewers/bim/BimToolbar.d.ts.map +1 -1
  106. package/dist/core/components/viewers/bim/BimToolbar.js +2 -2
  107. package/dist/core/components/viewers/bim/BimToolbar.js.map +1 -1
  108. package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.d.ts +3 -1
  109. package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.d.ts.map +1 -1
  110. package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.js +2 -2
  111. package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.js.map +1 -1
  112. package/dist/core/components/viewers/bim/src/BimSidebar/src/index.d.ts +5 -1
  113. package/dist/core/components/viewers/bim/src/BimSidebar/src/index.d.ts.map +1 -1
  114. package/dist/core/components/viewers/bim/src/BimSidebar/src/index.js +3 -2
  115. package/dist/core/components/viewers/bim/src/BimSidebar/src/index.js.map +1 -1
  116. package/dist/core/components/viewers/bim/src/tools/AddToBim/index.d.ts +3 -1
  117. package/dist/core/components/viewers/bim/src/tools/AddToBim/index.d.ts.map +1 -1
  118. package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js +2 -2
  119. package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js.map +1 -1
  120. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts +1 -1
  121. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts.map +1 -1
  122. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js +3 -5
  123. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js.map +1 -1
  124. package/dist/core/components/viewers/bim/src/tools/bimToolbar.d.ts +3 -1
  125. package/dist/core/components/viewers/bim/src/tools/bimToolbar.d.ts.map +1 -1
  126. package/dist/core/components/viewers/bim/src/tools/bimToolbar.js +4 -2
  127. package/dist/core/components/viewers/bim/src/tools/bimToolbar.js.map +1 -1
  128. package/dist/core/components/viewers/map/MapViewer.d.ts +3 -1
  129. package/dist/core/components/viewers/map/MapViewer.d.ts.map +1 -1
  130. package/dist/core/components/viewers/map/MapViewer.js +2 -2
  131. package/dist/core/components/viewers/map/MapViewer.js.map +1 -1
  132. package/dist/core/components/viewers/map/datasets/RowActions.d.ts +2 -1
  133. package/dist/core/components/viewers/map/datasets/RowActions.d.ts.map +1 -1
  134. package/dist/core/components/viewers/map/datasets/RowActions.js +5 -6
  135. package/dist/core/components/viewers/map/datasets/RowActions.js.map +1 -1
  136. package/dist/core/components/viewers/map/datasets/index.d.ts +5 -1
  137. package/dist/core/components/viewers/map/datasets/index.d.ts.map +1 -1
  138. package/dist/core/components/viewers/map/datasets/index.js +15 -16
  139. package/dist/core/components/viewers/map/datasets/index.js.map +1 -1
  140. package/dist/core/components/viewers/map/src/Geocoder.d.ts +3 -1
  141. package/dist/core/components/viewers/map/src/Geocoder.d.ts.map +1 -1
  142. package/dist/core/components/viewers/map/src/Geocoder.js +5 -1
  143. package/dist/core/components/viewers/map/src/Geocoder.js.map +1 -1
  144. package/dist/core/components/viewers/map/src/MapLayers/index.d.ts +6 -1
  145. package/dist/core/components/viewers/map/src/MapLayers/index.d.ts.map +1 -1
  146. package/dist/core/components/viewers/map/src/MapLayers/index.js +3 -3
  147. package/dist/core/components/viewers/map/src/MapLayers/index.js.map +1 -1
  148. package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.d.ts +5 -1
  149. package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.d.ts.map +1 -1
  150. package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.js +6 -10
  151. package/dist/core/components/viewers/map/src/MapLayers/src/CountryLayer/index.js.map +1 -1
  152. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts +3 -1
  153. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts.map +1 -1
  154. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js +4 -6
  155. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js.map +1 -1
  156. package/dist/core/components/viewers/map/src/MapSidebar/index.d.ts +6 -1
  157. package/dist/core/components/viewers/map/src/MapSidebar/index.d.ts.map +1 -1
  158. package/dist/core/components/viewers/map/src/MapSidebar/index.js +5 -4
  159. package/dist/core/components/viewers/map/src/MapSidebar/index.js.map +1 -1
  160. package/dist/core/components/viewers/map/src/MapSidebar/src/LayersTab/index.d.ts +5 -1
  161. package/dist/core/components/viewers/map/src/MapSidebar/src/LayersTab/index.d.ts.map +1 -1
  162. package/dist/core/components/viewers/map/src/MapSidebar/src/LayersTab/index.js +5 -8
  163. package/dist/core/components/viewers/map/src/MapSidebar/src/LayersTab/index.js.map +1 -1
  164. package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.d.ts +3 -1
  165. package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.d.ts.map +1 -1
  166. package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.js +2 -2
  167. package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.js.map +1 -1
  168. package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/index.d.ts +3 -1
  169. package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/index.d.ts.map +1 -1
  170. package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/index.js +2 -2
  171. package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/index.js.map +1 -1
  172. package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/LocationSettings.d.ts +3 -1
  173. package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/LocationSettings.d.ts.map +1 -1
  174. package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/LocationSettings.js +4 -7
  175. package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/LocationSettings.js.map +1 -1
  176. package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/TerrainLevel.d.ts.map +1 -1
  177. package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/TerrainLevel.js +8 -8
  178. package/dist/core/components/viewers/map/src/MapSidebar/src/SettingsTab/src/TerrainLevel.js.map +1 -1
  179. package/dist/core/components/viewers/map/src/tools/AddTools/AddToMap.d.ts +8 -1
  180. package/dist/core/components/viewers/map/src/tools/AddTools/AddToMap.d.ts.map +1 -1
  181. package/dist/core/components/viewers/map/src/tools/AddTools/AddToMap.js +8 -3
  182. package/dist/core/components/viewers/map/src/tools/AddTools/AddToMap.js.map +1 -1
  183. package/dist/core/components/viewers/map/src/tools/DatasetMapTool.d.ts +11 -1
  184. package/dist/core/components/viewers/map/src/tools/DatasetMapTool.d.ts.map +1 -1
  185. package/dist/core/components/viewers/map/src/tools/DatasetMapTool.js +5 -3
  186. package/dist/core/components/viewers/map/src/tools/DatasetMapTool.js.map +1 -1
  187. package/dist/core/components/viewers/map/src/tools/mapTools.d.ts +10 -1
  188. package/dist/core/components/viewers/map/src/tools/mapTools.d.ts.map +1 -1
  189. package/dist/core/components/viewers/map/src/tools/mapTools.js +5 -3
  190. package/dist/core/components/viewers/map/src/tools/mapTools.js.map +1 -1
  191. package/dist/core/components/viewers/map/utils/geocoding/config.d.ts +14 -5
  192. package/dist/core/components/viewers/map/utils/geocoding/config.d.ts.map +1 -1
  193. package/dist/core/components/viewers/map/utils/geocoding/config.js +22 -11
  194. package/dist/core/components/viewers/map/utils/geocoding/config.js.map +1 -1
  195. package/dist/core/components/viewers/map/utils/geocoding/index.d.ts.map +1 -1
  196. package/dist/core/components/viewers/map/utils/geocoding/index.js +2 -2
  197. package/dist/core/components/viewers/map/utils/geocoding/index.js.map +1 -1
  198. package/dist/core/components/viewers/map/utils/geocoding/osm.d.ts.map +1 -1
  199. package/dist/core/components/viewers/map/utils/geocoding/osm.js +5 -4
  200. package/dist/core/components/viewers/map/utils/geocoding/osm.js.map +1 -1
  201. package/dist/core/components/viewers/map/utils/geocoding/pelias.d.ts.map +1 -1
  202. package/dist/core/components/viewers/map/utils/geocoding/pelias.js +7 -5
  203. package/dist/core/components/viewers/map/utils/geocoding/pelias.js.map +1 -1
  204. package/dist/core/components/viewers/pointcloud/PointCloudViewer.d.ts +3 -1
  205. package/dist/core/components/viewers/pointcloud/PointCloudViewer.d.ts.map +1 -1
  206. package/dist/core/components/viewers/pointcloud/PointCloudViewer.js +11 -14
  207. package/dist/core/components/viewers/pointcloud/PointCloudViewer.js.map +1 -1
  208. package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.d.ts +3 -1
  209. package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.d.ts.map +1 -1
  210. package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.js +37 -52
  211. package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.js.map +1 -1
  212. package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudManagementTable.d.ts +3 -1
  213. package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudManagementTable.d.ts.map +1 -1
  214. package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudManagementTable.js +5 -3
  215. package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudManagementTable.js.map +1 -1
  216. package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudTable.d.ts +2 -1
  217. package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudTable.d.ts.map +1 -1
  218. package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudTable.js +5 -6
  219. package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudTable.js.map +1 -1
  220. package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudUploadPanel.d.ts +2 -1
  221. package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudUploadPanel.d.ts.map +1 -1
  222. package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudUploadPanel.js +15 -9
  223. package/dist/core/components/viewers/pointcloud/src/PointCloudManagement/PointCloudUploadPanel.js.map +1 -1
  224. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/index.d.ts +5 -1
  225. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/index.d.ts.map +1 -1
  226. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/index.js +3 -2
  227. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/index.js.map +1 -1
  228. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/index.d.ts +3 -1
  229. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/index.d.ts.map +1 -1
  230. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/index.js +2 -2
  231. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/index.js.map +1 -1
  232. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.d.ts +2 -1
  233. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.d.ts.map +1 -1
  234. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.js +12 -23
  235. package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.js.map +1 -1
  236. package/dist/core/hooks/useCaptureScreenshot.d.ts +2 -0
  237. package/dist/core/hooks/useCaptureScreenshot.d.ts.map +1 -0
  238. package/dist/core/hooks/useCaptureScreenshot.js +63 -0
  239. package/dist/core/hooks/useCaptureScreenshot.js.map +1 -0
  240. package/dist/core/hooks/useGeocodingRuntimeConfig.d.ts +3 -0
  241. package/dist/core/hooks/useGeocodingRuntimeConfig.d.ts.map +1 -0
  242. package/dist/core/hooks/useGeocodingRuntimeConfig.js +17 -0
  243. package/dist/core/hooks/useGeocodingRuntimeConfig.js.map +1 -0
  244. package/dist/core/store/AppConfig/context.d.ts +5 -0
  245. package/dist/core/store/AppConfig/context.d.ts.map +1 -1
  246. package/dist/core/store/AppConfig/context.js.map +1 -1
  247. package/dist/core/store/CombineProviders.d.ts +6 -1
  248. package/dist/core/store/CombineProviders.d.ts.map +1 -1
  249. package/dist/core/store/CombineProviders.js +4 -3
  250. package/dist/core/store/CombineProviders.js.map +1 -1
  251. package/dist/core/types/tools.d.ts +2 -1
  252. package/dist/core/types/tools.d.ts.map +1 -1
  253. package/dist/core/utils/imageUtils.d.ts.map +1 -1
  254. package/dist/core/utils/imageUtils.js +2 -1
  255. package/dist/core/utils/imageUtils.js.map +1 -1
  256. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/map/utils/geocoding/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAiCjC,eAAO,MAAM,mBAAmB,GAAU,MAAM,MAAM,EAAE,aAAa,MAAM,EAAE,MAAM,MAAM,KAAG,OAAO,CAAC,OAAO,EAAE,CAU5G,CAAA;AAED,eAAO,MAAM,cAAc,GACzB,UAAU,MAAM,EAChB,WAAW,MAAM,EACjB,aAAa,MAAM,EACnB,2BAAsC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,KAC9F,OAAO,CAAC,OAAO,EAAE,CAUnB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/map/utils/geocoding/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAkCjC,eAAO,MAAM,mBAAmB,GAAU,MAAM,MAAM,EAAE,aAAa,MAAM,EAAE,MAAM,MAAM,KAAG,OAAO,CAAC,OAAO,EAAE,CAU5G,CAAA;AAED,eAAO,MAAM,cAAc,GACzB,UAAU,MAAM,EAChB,WAAW,MAAM,EACjB,aAAa,MAAM,EACnB,2BAAsC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,KAC9F,OAAO,CAAC,OAAO,EAAE,CAUnB,CAAA"}
@@ -1,8 +1,8 @@
1
- import { USE_PELIAS } from "./config";
1
+ import { getGeocodingConfig } from "./config";
2
2
  import { peliasAutocomplete, peliasReverse } from "./pelias";
3
3
  import { photonAutocomplete, nominatimReverse } from "./osm";
4
4
  let peliasAuthFailed = false;
5
- const peliasActive = () => USE_PELIAS && !peliasAuthFailed;
5
+ const peliasActive = () => getGeocodingConfig().usePelias && !peliasAuthFailed;
6
6
  const isAuthError = (error) => {
7
7
  const message = error instanceof Error ? error.message : String(error);
8
8
  return message.includes("401") || message.includes("403");
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../src/core/components/viewers/map/utils/geocoding/index.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { Feature } from 'geojson'\r\nimport { USE_PELIAS } from './config'\r\nimport { peliasAutocomplete, peliasReverse } from './pelias'\r\nimport { photonAutocomplete, nominatimReverse } from './osm'\r\n\r\n// Provider-agnostic entry points. The preferred provider (Geocode Earth or a\r\n// self-hosted Pelias) is resolved in ./config from env vars; if it fails we fall\r\n// back to the free public OSM services so address search keeps working.\r\n//\r\n// A bad/expired/origin-restricted key surfaces as a 401/403. Autocomplete fires\r\n// on every keystroke, so once we see an auth failure we stop retrying the keyed\r\n// provider for the rest of the session and serve results directly from public OSM.\r\nlet peliasAuthFailed = false\r\n\r\nconst peliasActive = (): boolean => USE_PELIAS && !peliasAuthFailed\r\n\r\nconst isAuthError = (error: unknown): boolean => {\r\n const message = error instanceof Error ? error.message : String(error)\r\n return message.includes('401') || message.includes('403')\r\n}\r\n\r\nconst handlePeliasFailure = (error: unknown, fallbackName: string): void => {\r\n if (isAuthError(error)) {\r\n peliasAuthFailed = true\r\n console.warn(\r\n `Geocoder: the configured provider rejected the API key (auth error). ` +\r\n `Falling back to public OSM services (Photon/Nominatim) for the rest of this session.`,\r\n )\r\n } else {\r\n console.warn(`Geocoder: provider request failed; falling back to ${fallbackName}.`, error)\r\n }\r\n}\r\n\r\nexport const autocompleteGeocode = async (text: string, countryCode: string, size: number): Promise<Feature[]> => {\r\n if (peliasActive()) {\r\n try {\r\n return await peliasAutocomplete(text, countryCode, size)\r\n }\r\n catch (error) {\r\n handlePeliasFailure(error, 'Photon')\r\n }\r\n }\r\n return photonAutocomplete(text, countryCode, size)\r\n}\r\n\r\nexport const reverseGeocode = async (\r\n latitude: string,\r\n longitude: string,\r\n countryCode: string,\r\n { size = 1, coarse = false, layers }: { size?: number; coarse?: boolean; layers?: string } = {},\r\n): Promise<Feature[]> => {\r\n if (peliasActive()) {\r\n try {\r\n return await peliasReverse(latitude, longitude, countryCode, { size, coarse, layers })\r\n }\r\n catch (error) {\r\n handlePeliasFailure(error, 'Nominatim')\r\n }\r\n }\r\n return nominatimReverse(latitude, longitude, { coarse })\r\n}\r\n"],"mappings":"AAIA,SAAS,kBAAkB;AAC3B,SAAS,oBAAoB,qBAAqB;AAClD,SAAS,oBAAoB,wBAAwB;AASrD,IAAI,mBAAmB;AAEvB,MAAM,eAAe,MAAe,cAAc,CAAC;AAEnD,MAAM,cAAc,CAAC,UAA4B;AAC/C,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,SAAO,QAAQ,SAAS,KAAK,KAAK,QAAQ,SAAS,KAAK;AAC1D;AAEA,MAAM,sBAAsB,CAAC,OAAgB,iBAA+B;AAC1E,MAAI,YAAY,KAAK,GAAG;AACtB,uBAAmB;AACnB,YAAQ;AAAA,MACN;AAAA,IAEF;AAAA,EACF,OAAO;AACL,YAAQ,KAAK,sDAAsD,YAAY,KAAK,KAAK;AAAA,EAC3F;AACF;AAEO,MAAM,sBAAsB,OAAO,MAAc,aAAqB,SAAqC;AAChH,MAAI,aAAa,GAAG;AAClB,QAAI;AACF,aAAO,MAAM,mBAAmB,MAAM,aAAa,IAAI;AAAA,IACzD,SACO,OAAO;AACZ,0BAAoB,OAAO,QAAQ;AAAA,IACrC;AAAA,EACF;AACA,SAAO,mBAAmB,MAAM,aAAa,IAAI;AACnD;AAEO,MAAM,iBAAiB,OAC5B,UACA,WACA,aACA,EAAE,OAAO,GAAG,SAAS,OAAO,OAAO,IAA0D,CAAC,MACvE;AACvB,MAAI,aAAa,GAAG;AAClB,QAAI;AACF,aAAO,MAAM,cAAc,UAAU,WAAW,aAAa,EAAE,MAAM,QAAQ,OAAO,CAAC;AAAA,IACvF,SACO,OAAO;AACZ,0BAAoB,OAAO,WAAW;AAAA,IACxC;AAAA,EACF;AACA,SAAO,iBAAiB,UAAU,WAAW,EAAE,OAAO,CAAC;AACzD;","names":[]}
1
+ {"version":3,"sources":["../../../../../../../src/core/components/viewers/map/utils/geocoding/index.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { Feature } from 'geojson'\r\nimport { getGeocodingConfig } from './config'\r\nimport { peliasAutocomplete, peliasReverse } from './pelias'\r\nimport { photonAutocomplete, nominatimReverse } from './osm'\r\n\r\n// Provider-agnostic entry points. The preferred provider (Geocode Earth or a\r\n// self-hosted Pelias) is resolved in ./config from runtime-injected values;\r\n// if it fails we fall back to the free public OSM services so address search\r\n// keeps working.\r\n//\r\n// A bad/expired/origin-restricted key surfaces as a 401/403. Autocomplete fires\r\n// on every keystroke, so once we see an auth failure we stop retrying the keyed\r\n// provider for the rest of the session and serve results directly from public OSM.\r\nlet peliasAuthFailed = false\r\n\r\nconst peliasActive = (): boolean => getGeocodingConfig().usePelias && !peliasAuthFailed\r\n\r\nconst isAuthError = (error: unknown): boolean => {\r\n const message = error instanceof Error ? error.message : String(error)\r\n return message.includes('401') || message.includes('403')\r\n}\r\n\r\nconst handlePeliasFailure = (error: unknown, fallbackName: string): void => {\r\n if (isAuthError(error)) {\r\n peliasAuthFailed = true\r\n console.warn(\r\n `Geocoder: the configured provider rejected the API key (auth error). ` +\r\n `Falling back to public OSM services (Photon/Nominatim) for the rest of this session.`,\r\n )\r\n } else {\r\n console.warn(`Geocoder: provider request failed; falling back to ${fallbackName}.`, error)\r\n }\r\n}\r\n\r\nexport const autocompleteGeocode = async (text: string, countryCode: string, size: number): Promise<Feature[]> => {\r\n if (peliasActive()) {\r\n try {\r\n return await peliasAutocomplete(text, countryCode, size)\r\n }\r\n catch (error) {\r\n handlePeliasFailure(error, 'Photon')\r\n }\r\n }\r\n return photonAutocomplete(text, countryCode, size)\r\n}\r\n\r\nexport const reverseGeocode = async (\r\n latitude: string,\r\n longitude: string,\r\n countryCode: string,\r\n { size = 1, coarse = false, layers }: { size?: number; coarse?: boolean; layers?: string } = {},\r\n): Promise<Feature[]> => {\r\n if (peliasActive()) {\r\n try {\r\n return await peliasReverse(latitude, longitude, countryCode, { size, coarse, layers })\r\n }\r\n catch (error) {\r\n handlePeliasFailure(error, 'Nominatim')\r\n }\r\n }\r\n return nominatimReverse(latitude, longitude, { coarse })\r\n}\r\n"],"mappings":"AAIA,SAAS,0BAA0B;AACnC,SAAS,oBAAoB,qBAAqB;AAClD,SAAS,oBAAoB,wBAAwB;AAUrD,IAAI,mBAAmB;AAEvB,MAAM,eAAe,MAAe,mBAAmB,EAAE,aAAa,CAAC;AAEvE,MAAM,cAAc,CAAC,UAA4B;AAC/C,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,SAAO,QAAQ,SAAS,KAAK,KAAK,QAAQ,SAAS,KAAK;AAC1D;AAEA,MAAM,sBAAsB,CAAC,OAAgB,iBAA+B;AAC1E,MAAI,YAAY,KAAK,GAAG;AACtB,uBAAmB;AACnB,YAAQ;AAAA,MACN;AAAA,IAEF;AAAA,EACF,OAAO;AACL,YAAQ,KAAK,sDAAsD,YAAY,KAAK,KAAK;AAAA,EAC3F;AACF;AAEO,MAAM,sBAAsB,OAAO,MAAc,aAAqB,SAAqC;AAChH,MAAI,aAAa,GAAG;AAClB,QAAI;AACF,aAAO,MAAM,mBAAmB,MAAM,aAAa,IAAI;AAAA,IACzD,SACO,OAAO;AACZ,0BAAoB,OAAO,QAAQ;AAAA,IACrC;AAAA,EACF;AACA,SAAO,mBAAmB,MAAM,aAAa,IAAI;AACnD;AAEO,MAAM,iBAAiB,OAC5B,UACA,WACA,aACA,EAAE,OAAO,GAAG,SAAS,OAAO,OAAO,IAA0D,CAAC,MACvE;AACvB,MAAI,aAAa,GAAG;AAClB,QAAI;AACF,aAAO,MAAM,cAAc,UAAU,WAAW,aAAa,EAAE,MAAM,QAAQ,OAAO,CAAC;AAAA,IACvF,SACO,OAAO;AACZ,0BAAoB,OAAO,WAAW;AAAA,IACxC;AAAA,EACF;AACA,SAAO,iBAAiB,UAAU,WAAW,EAAE,OAAO,CAAC;AACzD;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"osm.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/map/utils/geocoding/osm.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAOjC,eAAO,MAAM,kBAAkB,GAAU,MAAM,MAAM,EAAE,aAAa,MAAM,GAAG,SAAS,EAAE,MAAM,MAAM,KAAG,OAAO,CAAC,OAAO,EAAE,CAoBvH,CAAA;AAED,eAAO,MAAM,gBAAgB,GAC3B,UAAU,MAAM,EAChB,WAAW,MAAM,EACjB,YAAY;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,KAC9B,OAAO,CAAC,OAAO,EAAE,CAenB,CAAA"}
1
+ {"version":3,"file":"osm.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/map/utils/geocoding/osm.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAOjC,eAAO,MAAM,kBAAkB,GAAU,MAAM,MAAM,EAAE,aAAa,MAAM,GAAG,SAAS,EAAE,MAAM,MAAM,KAAG,OAAO,CAAC,OAAO,EAAE,CAsBvH,CAAA;AAED,eAAO,MAAM,gBAAgB,GAC3B,UAAU,MAAM,EAChB,WAAW,MAAM,EACjB,YAAY;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,KAC9B,OAAO,CAAC,OAAO,EAAE,CAiBnB,CAAA"}
@@ -1,8 +1,9 @@
1
- import { PHOTON_URL, NOMINATIM_URL } from "./config";
1
+ import { getGeocodingConfig } from "./config";
2
2
  import { normalizePhotonFeature, normalizeNominatimResult } from "./adapters";
3
3
  const photonAutocomplete = async (text, countryCode, size) => {
4
+ const { photonUrl } = getGeocodingConfig();
4
5
  const params = new URLSearchParams({ q: text, limit: String(size * 2), lang: "en" });
5
- const response = await fetch(`${PHOTON_URL}/api?${params}`);
6
+ const response = await fetch(`${photonUrl}/api?${params}`);
6
7
  if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
7
8
  const data = await response.json();
8
9
  let features = (data.features || []).map(normalizePhotonFeature);
@@ -18,15 +19,15 @@ const photonAutocomplete = async (text, countryCode, size) => {
18
19
  return features.slice(0, size);
19
20
  };
20
21
  const nominatimReverse = async (latitude, longitude, { coarse }) => {
22
+ const { nominatimUrl } = getGeocodingConfig();
21
23
  const params = new URLSearchParams({
22
24
  format: "jsonv2",
23
25
  lat: latitude,
24
26
  lon: longitude,
25
27
  addressdetails: "1",
26
28
  zoom: coarse ? "10" : "18"
27
- // coarse -> city level, otherwise building level
28
29
  });
29
- const response = await fetch(`${NOMINATIM_URL}/reverse?${params}`);
30
+ const response = await fetch(`${nominatimUrl}/reverse?${params}`);
30
31
  if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
31
32
  const data = await response.json();
32
33
  if (!data || data.error || data.lat == null) return [];
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../src/core/components/viewers/map/utils/geocoding/osm.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { Feature } from 'geojson'\r\nimport { PHOTON_URL, NOMINATIM_URL } from './config'\r\nimport { normalizePhotonFeature, normalizeNominatimResult } from './adapters'\r\n\r\n// Free, no-key public OSM fallbacks. Photon serves autocomplete (it is built for\r\n// per-keystroke search); Nominatim serves reverse lookups (occasional, single point).\r\n\r\nexport const photonAutocomplete = async (text: string, countryCode: string | undefined, size: number): Promise<Feature[]> => {\r\n // Photon has no country filter, so over-fetch and post-filter by country code.\r\n const params = new URLSearchParams({ q: text, limit: String(size * 2), lang: 'en' })\r\n\r\n const response = await fetch(`${PHOTON_URL}/api?${params}`)\r\n if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`)\r\n\r\n const data = await response.json()\r\n let features: Feature[] = (data.features || []).map(normalizePhotonFeature)\r\n\r\n if (countryCode) {\r\n const cc = countryCode.toUpperCase()\r\n const inCountry = features.filter(f => {\r\n const ca = (f.properties as any)?.country_a\r\n return !ca || ca === cc\r\n })\r\n if (inCountry.length) features = inCountry\r\n }\r\n\r\n return features.slice(0, size)\r\n}\r\n\r\nexport const nominatimReverse = async (\r\n latitude: string,\r\n longitude: string,\r\n { coarse }: { coarse: boolean },\r\n): Promise<Feature[]> => {\r\n const params = new URLSearchParams({\r\n format: 'jsonv2',\r\n lat: latitude,\r\n lon: longitude,\r\n addressdetails: '1',\r\n zoom: coarse ? '10' : '18', // coarse -> city level, otherwise building level\r\n })\r\n\r\n const response = await fetch(`${NOMINATIM_URL}/reverse?${params}`)\r\n if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`)\r\n\r\n const data = await response.json()\r\n if (!data || data.error || data.lat == null) return []\r\n return [normalizeNominatimResult(data)]\r\n}\r\n"],"mappings":"AAIA,SAAS,YAAY,qBAAqB;AAC1C,SAAS,wBAAwB,gCAAgC;AAK1D,MAAM,qBAAqB,OAAO,MAAc,aAAiC,SAAqC;AAE3H,QAAM,SAAS,IAAI,gBAAgB,EAAE,GAAG,MAAM,OAAO,OAAO,OAAO,CAAC,GAAG,MAAM,KAAK,CAAC;AAEnF,QAAM,WAAW,MAAM,MAAM,GAAG,UAAU,QAAQ,MAAM,EAAE;AAC1D,MAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,uBAAuB,SAAS,MAAM,EAAE;AAE1E,QAAM,OAAO,MAAM,SAAS,KAAK;AACjC,MAAI,YAAuB,KAAK,YAAY,CAAC,GAAG,IAAI,sBAAsB;AAE1E,MAAI,aAAa;AACf,UAAM,KAAK,YAAY,YAAY;AACnC,UAAM,YAAY,SAAS,OAAO,OAAK;AAtB3C;AAuBM,YAAM,MAAM,OAAE,eAAF,mBAAsB;AAClC,aAAO,CAAC,MAAM,OAAO;AAAA,IACvB,CAAC;AACD,QAAI,UAAU,OAAQ,YAAW;AAAA,EACnC;AAEA,SAAO,SAAS,MAAM,GAAG,IAAI;AAC/B;AAEO,MAAM,mBAAmB,OAC9B,UACA,WACA,EAAE,OAAO,MACc;AACvB,QAAM,SAAS,IAAI,gBAAgB;AAAA,IACjC,QAAQ;AAAA,IACR,KAAK;AAAA,IACL,KAAK;AAAA,IACL,gBAAgB;AAAA,IAChB,MAAM,SAAS,OAAO;AAAA;AAAA,EACxB,CAAC;AAED,QAAM,WAAW,MAAM,MAAM,GAAG,aAAa,YAAY,MAAM,EAAE;AACjE,MAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,uBAAuB,SAAS,MAAM,EAAE;AAE1E,QAAM,OAAO,MAAM,SAAS,KAAK;AACjC,MAAI,CAAC,QAAQ,KAAK,SAAS,KAAK,OAAO,KAAM,QAAO,CAAC;AACrD,SAAO,CAAC,yBAAyB,IAAI,CAAC;AACxC;","names":[]}
1
+ {"version":3,"sources":["../../../../../../../src/core/components/viewers/map/utils/geocoding/osm.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { Feature } from 'geojson'\r\nimport { getGeocodingConfig } from './config'\r\nimport { normalizePhotonFeature, normalizeNominatimResult } from './adapters'\r\n\r\n// Free, no-key public OSM fallbacks. Photon serves autocomplete (it is built for\r\n// per-keystroke search); Nominatim serves reverse lookups (occasional, single point).\r\n\r\nexport const photonAutocomplete = async (text: string, countryCode: string | undefined, size: number): Promise<Feature[]> => {\r\n const { photonUrl } = getGeocodingConfig()\r\n\r\n // Photon has no country filter, so over-fetch and post-filter by country code.\r\n const params = new URLSearchParams({ q: text, limit: String(size * 2), lang: 'en' })\r\n\r\n const response = await fetch(`${photonUrl}/api?${params}`)\r\n if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`)\r\n\r\n const data = await response.json()\r\n let features: Feature[] = (data.features || []).map(normalizePhotonFeature)\r\n\r\n if (countryCode) {\r\n const cc = countryCode.toUpperCase()\r\n const inCountry = features.filter(f => {\r\n const ca = (f.properties as any)?.country_a\r\n return !ca || ca === cc\r\n })\r\n if (inCountry.length) features = inCountry\r\n }\r\n\r\n return features.slice(0, size)\r\n}\r\n\r\nexport const nominatimReverse = async (\r\n latitude: string,\r\n longitude: string,\r\n { coarse }: { coarse: boolean },\r\n): Promise<Feature[]> => {\r\n const { nominatimUrl } = getGeocodingConfig()\r\n\r\n const params = new URLSearchParams({\r\n format: 'jsonv2',\r\n lat: latitude,\r\n lon: longitude,\r\n addressdetails: '1',\r\n zoom: coarse ? '10' : '18',\r\n })\r\n\r\n const response = await fetch(`${nominatimUrl}/reverse?${params}`)\r\n if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`)\r\n\r\n const data = await response.json()\r\n if (!data || data.error || data.lat == null) return []\r\n return [normalizeNominatimResult(data)]\r\n}\r\n"],"mappings":"AAIA,SAAS,0BAA0B;AACnC,SAAS,wBAAwB,gCAAgC;AAK1D,MAAM,qBAAqB,OAAO,MAAc,aAAiC,SAAqC;AAC3H,QAAM,EAAE,UAAU,IAAI,mBAAmB;AAGzC,QAAM,SAAS,IAAI,gBAAgB,EAAE,GAAG,MAAM,OAAO,OAAO,OAAO,CAAC,GAAG,MAAM,KAAK,CAAC;AAEnF,QAAM,WAAW,MAAM,MAAM,GAAG,SAAS,QAAQ,MAAM,EAAE;AACzD,MAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,uBAAuB,SAAS,MAAM,EAAE;AAE1E,QAAM,OAAO,MAAM,SAAS,KAAK;AACjC,MAAI,YAAuB,KAAK,YAAY,CAAC,GAAG,IAAI,sBAAsB;AAE1E,MAAI,aAAa;AACf,UAAM,KAAK,YAAY,YAAY;AACnC,UAAM,YAAY,SAAS,OAAO,OAAK;AAxB3C;AAyBM,YAAM,MAAM,OAAE,eAAF,mBAAsB;AAClC,aAAO,CAAC,MAAM,OAAO;AAAA,IACvB,CAAC;AACD,QAAI,UAAU,OAAQ,YAAW;AAAA,EACnC;AAEA,SAAO,SAAS,MAAM,GAAG,IAAI;AAC/B;AAEO,MAAM,mBAAmB,OAC9B,UACA,WACA,EAAE,OAAO,MACc;AACvB,QAAM,EAAE,aAAa,IAAI,mBAAmB;AAE5C,QAAM,SAAS,IAAI,gBAAgB;AAAA,IACjC,QAAQ;AAAA,IACR,KAAK;AAAA,IACL,KAAK;AAAA,IACL,gBAAgB;AAAA,IAChB,MAAM,SAAS,OAAO;AAAA,EACxB,CAAC;AAED,QAAM,WAAW,MAAM,MAAM,GAAG,YAAY,YAAY,MAAM,EAAE;AAChE,MAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,uBAAuB,SAAS,MAAM,EAAE;AAE1E,QAAM,OAAO,MAAM,SAAS,KAAK;AACjC,MAAI,CAAC,QAAQ,KAAK,SAAS,KAAK,OAAO,KAAM,QAAO,CAAC;AACrD,SAAO,CAAC,yBAAyB,IAAI,CAAC;AACxC;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"pelias.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/map/utils/geocoding/pelias.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAMjC,eAAO,MAAM,kBAAkB,GAAU,MAAM,MAAM,EAAE,aAAa,MAAM,GAAG,SAAS,EAAE,MAAM,MAAM,KAAG,OAAO,CAAC,OAAO,EAAE,CAavH,CAAA;AAED,eAAO,MAAM,aAAa,GACxB,UAAU,MAAM,EAChB,WAAW,MAAM,EACjB,aAAa,MAAM,GAAG,SAAS,EAC/B,0BAA0B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,KAC3E,OAAO,CAAC,OAAO,EAAE,CAiBnB,CAAA"}
1
+ {"version":3,"file":"pelias.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/map/utils/geocoding/pelias.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAMjC,eAAO,MAAM,kBAAkB,GAAU,MAAM,MAAM,EAAE,aAAa,MAAM,GAAG,SAAS,EAAE,MAAM,MAAM,KAAG,OAAO,CAAC,OAAO,EAAE,CAevH,CAAA;AAED,eAAO,MAAM,aAAa,GACxB,UAAU,MAAM,EAChB,WAAW,MAAM,EACjB,aAAa,MAAM,GAAG,SAAS,EAC/B,0BAA0B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,KAC3E,OAAO,CAAC,OAAO,EAAE,CAkBnB,CAAA"}
@@ -1,19 +1,21 @@
1
- import { GEOCODE_EARTH_API_KEY, PELIAS_BASE } from "./config";
1
+ import { getGeocodingConfig } from "./config";
2
2
  const peliasAutocomplete = async (text, countryCode, size) => {
3
3
  var _a;
4
+ const { geocodeEarthApiKey, peliasBase } = getGeocodingConfig();
4
5
  const params = new URLSearchParams({
5
6
  "text": text,
6
7
  "size": String(size)
7
8
  });
8
9
  if (countryCode) params.set("boundary.country", countryCode.toUpperCase());
9
- if (GEOCODE_EARTH_API_KEY) params.set("api_key", GEOCODE_EARTH_API_KEY);
10
- const response = await fetch(`${PELIAS_BASE}/v1/autocomplete?${params}`);
10
+ if (geocodeEarthApiKey) params.set("api_key", geocodeEarthApiKey);
11
+ const response = await fetch(`${peliasBase}/v1/autocomplete?${params}`);
11
12
  if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
12
13
  const geojson = await response.json();
13
14
  return ((_a = geojson.features) == null ? void 0 : _a.length) ? geojson.features : [];
14
15
  };
15
16
  const peliasReverse = async (latitude, longitude, countryCode, { size, coarse, layers }) => {
16
17
  var _a;
18
+ const { geocodeEarthApiKey, peliasBase } = getGeocodingConfig();
17
19
  const params = new URLSearchParams({
18
20
  "point.lat": latitude,
19
21
  "point.lon": longitude,
@@ -22,8 +24,8 @@ const peliasReverse = async (latitude, longitude, countryCode, { size, coarse, l
22
24
  if (countryCode) params.set("boundary.country", countryCode.toUpperCase());
23
25
  if (coarse) params.set("layers", "coarse");
24
26
  else if (layers) params.set("layers", layers);
25
- if (GEOCODE_EARTH_API_KEY) params.set("api_key", GEOCODE_EARTH_API_KEY);
26
- const response = await fetch(`${PELIAS_BASE}/v1/reverse?${params}`);
27
+ if (geocodeEarthApiKey) params.set("api_key", geocodeEarthApiKey);
28
+ const response = await fetch(`${peliasBase}/v1/reverse?${params}`);
27
29
  if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
28
30
  const data = await response.json();
29
31
  return ((_a = data.features) == null ? void 0 : _a.length) ? data.features : [];
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../src/core/components/viewers/map/utils/geocoding/pelias.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { Feature } from 'geojson'\r\nimport { GEOCODE_EARTH_API_KEY, PELIAS_BASE } from './config'\r\n\r\n// Geocode Earth and self-hosted Pelias share this dialect; only the base URL and\r\n// the (optional) api_key differ. Responses already match the shape the app expects.\r\n\r\nexport const peliasAutocomplete = async (text: string, countryCode: string | undefined, size: number): Promise<Feature[]> => {\r\n const params = new URLSearchParams({\r\n 'text': text,\r\n 'size': String(size),\r\n })\r\n if (countryCode) params.set('boundary.country', countryCode.toUpperCase())\r\n if (GEOCODE_EARTH_API_KEY) params.set('api_key', GEOCODE_EARTH_API_KEY)\r\n\r\n const response = await fetch(`${PELIAS_BASE}/v1/autocomplete?${params}`)\r\n if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`)\r\n\r\n const geojson = await response.json()\r\n return geojson.features?.length ? geojson.features : []\r\n}\r\n\r\nexport const peliasReverse = async (\r\n latitude: string,\r\n longitude: string,\r\n countryCode: string | undefined,\r\n { size, coarse, layers }: { size: number; coarse: boolean; layers?: string },\r\n): Promise<Feature[]> => {\r\n const params = new URLSearchParams({\r\n 'point.lat': latitude,\r\n 'point.lon': longitude,\r\n 'size': String(size),\r\n })\r\n if (countryCode) params.set('boundary.country', countryCode.toUpperCase())\r\n // coarse (municipality-level) takes precedence; otherwise honor an explicit layer filter.\r\n if (coarse) params.set('layers', 'coarse')\r\n else if (layers) params.set('layers', layers)\r\n if (GEOCODE_EARTH_API_KEY) params.set('api_key', GEOCODE_EARTH_API_KEY)\r\n\r\n const response = await fetch(`${PELIAS_BASE}/v1/reverse?${params}`)\r\n if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`)\r\n\r\n const data = await response.json()\r\n return data.features?.length ? data.features : []\r\n}\r\n"],"mappings":"AAIA,SAAS,uBAAuB,mBAAmB;AAK5C,MAAM,qBAAqB,OAAO,MAAc,aAAiC,SAAqC;AAT7H;AAUE,QAAM,SAAS,IAAI,gBAAgB;AAAA,IACjC,QAAQ;AAAA,IACR,QAAQ,OAAO,IAAI;AAAA,EACrB,CAAC;AACD,MAAI,YAAa,QAAO,IAAI,oBAAoB,YAAY,YAAY,CAAC;AACzE,MAAI,sBAAuB,QAAO,IAAI,WAAW,qBAAqB;AAEtE,QAAM,WAAW,MAAM,MAAM,GAAG,WAAW,oBAAoB,MAAM,EAAE;AACvE,MAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,uBAAuB,SAAS,MAAM,EAAE;AAE1E,QAAM,UAAU,MAAM,SAAS,KAAK;AACpC,WAAO,aAAQ,aAAR,mBAAkB,UAAS,QAAQ,WAAW,CAAC;AACxD;AAEO,MAAM,gBAAgB,OAC3B,UACA,WACA,aACA,EAAE,MAAM,QAAQ,OAAO,MACA;AA7BzB;AA8BE,QAAM,SAAS,IAAI,gBAAgB;AAAA,IACjC,aAAa;AAAA,IACb,aAAa;AAAA,IACb,QAAQ,OAAO,IAAI;AAAA,EACrB,CAAC;AACD,MAAI,YAAa,QAAO,IAAI,oBAAoB,YAAY,YAAY,CAAC;AAEzE,MAAI,OAAQ,QAAO,IAAI,UAAU,QAAQ;AAAA,WAChC,OAAQ,QAAO,IAAI,UAAU,MAAM;AAC5C,MAAI,sBAAuB,QAAO,IAAI,WAAW,qBAAqB;AAEtE,QAAM,WAAW,MAAM,MAAM,GAAG,WAAW,eAAe,MAAM,EAAE;AAClE,MAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,uBAAuB,SAAS,MAAM,EAAE;AAE1E,QAAM,OAAO,MAAM,SAAS,KAAK;AACjC,WAAO,UAAK,aAAL,mBAAe,UAAS,KAAK,WAAW,CAAC;AAClD;","names":[]}
1
+ {"version":3,"sources":["../../../../../../../src/core/components/viewers/map/utils/geocoding/pelias.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { Feature } from 'geojson'\r\nimport { getGeocodingConfig } from './config'\r\n\r\n// Geocode Earth and self-hosted Pelias share this dialect; only the base URL and\r\n// the (optional) api_key differ. Responses already match the shape the app expects.\r\n\r\nexport const peliasAutocomplete = async (text: string, countryCode: string | undefined, size: number): Promise<Feature[]> => {\r\n const { geocodeEarthApiKey, peliasBase } = getGeocodingConfig()\r\n\r\n const params = new URLSearchParams({\r\n 'text': text,\r\n 'size': String(size),\r\n })\r\n if (countryCode) params.set('boundary.country', countryCode.toUpperCase())\r\n if (geocodeEarthApiKey) params.set('api_key', geocodeEarthApiKey)\r\n\r\n const response = await fetch(`${peliasBase}/v1/autocomplete?${params}`)\r\n if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`)\r\n\r\n const geojson = await response.json()\r\n return geojson.features?.length ? geojson.features : []\r\n}\r\n\r\nexport const peliasReverse = async (\r\n latitude: string,\r\n longitude: string,\r\n countryCode: string | undefined,\r\n { size, coarse, layers }: { size: number; coarse: boolean; layers?: string },\r\n): Promise<Feature[]> => {\r\n const { geocodeEarthApiKey, peliasBase } = getGeocodingConfig()\r\n\r\n const params = new URLSearchParams({\r\n 'point.lat': latitude,\r\n 'point.lon': longitude,\r\n 'size': String(size),\r\n })\r\n if (countryCode) params.set('boundary.country', countryCode.toUpperCase())\r\n if (coarse) params.set('layers', 'coarse')\r\n else if (layers) params.set('layers', layers)\r\n if (geocodeEarthApiKey) params.set('api_key', geocodeEarthApiKey)\r\n\r\n const response = await fetch(`${peliasBase}/v1/reverse?${params}`)\r\n if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`)\r\n\r\n const data = await response.json()\r\n return data.features?.length ? data.features : []\r\n}\r\n"],"mappings":"AAIA,SAAS,0BAA0B;AAK5B,MAAM,qBAAqB,OAAO,MAAc,aAAiC,SAAqC;AAT7H;AAUE,QAAM,EAAE,oBAAoB,WAAW,IAAI,mBAAmB;AAE9D,QAAM,SAAS,IAAI,gBAAgB;AAAA,IACjC,QAAQ;AAAA,IACR,QAAQ,OAAO,IAAI;AAAA,EACrB,CAAC;AACD,MAAI,YAAa,QAAO,IAAI,oBAAoB,YAAY,YAAY,CAAC;AACzE,MAAI,mBAAoB,QAAO,IAAI,WAAW,kBAAkB;AAEhE,QAAM,WAAW,MAAM,MAAM,GAAG,UAAU,oBAAoB,MAAM,EAAE;AACtE,MAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,uBAAuB,SAAS,MAAM,EAAE;AAE1E,QAAM,UAAU,MAAM,SAAS,KAAK;AACpC,WAAO,aAAQ,aAAR,mBAAkB,UAAS,QAAQ,WAAW,CAAC;AACxD;AAEO,MAAM,gBAAgB,OAC3B,UACA,WACA,aACA,EAAE,MAAM,QAAQ,OAAO,MACA;AA/BzB;AAgCE,QAAM,EAAE,oBAAoB,WAAW,IAAI,mBAAmB;AAE9D,QAAM,SAAS,IAAI,gBAAgB;AAAA,IACjC,aAAa;AAAA,IACb,aAAa;AAAA,IACb,QAAQ,OAAO,IAAI;AAAA,EACrB,CAAC;AACD,MAAI,YAAa,QAAO,IAAI,oBAAoB,YAAY,YAAY,CAAC;AACzE,MAAI,OAAQ,QAAO,IAAI,UAAU,QAAQ;AAAA,WAChC,OAAQ,QAAO,IAAI,UAAU,MAAM;AAC5C,MAAI,mBAAoB,QAAO,IAAI,WAAW,kBAAkB;AAEhE,QAAM,WAAW,MAAM,MAAM,GAAG,UAAU,eAAe,MAAM,EAAE;AACjE,MAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,uBAAuB,SAAS,MAAM,EAAE;AAE1E,QAAM,OAAO,MAAM,SAAS,KAAK;AACjC,WAAO,UAAK,aAAL,mBAAe,UAAS,KAAK,WAAW,CAAC;AAClD;","names":[]}
@@ -1,3 +1,5 @@
1
1
  import * as React from "react";
2
- export declare function PointCloudViewer(): React.JSX.Element;
2
+ export declare function PointCloudViewer({ pointcloudApiUrl }: {
3
+ pointcloudApiUrl?: string;
4
+ }): React.JSX.Element;
3
5
  //# sourceMappingURL=PointCloudViewer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PointCloudViewer.d.ts","sourceRoot":"","sources":["../../../../../src/core/components/viewers/pointcloud/PointCloudViewer.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAa/B,wBAAgB,gBAAgB,sBAwO/B"}
1
+ {"version":3,"file":"PointCloudViewer.d.ts","sourceRoot":"","sources":["../../../../../src/core/components/viewers/pointcloud/PointCloudViewer.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAa/B,wBAAgB,gBAAgB,CAAC,EAAE,gBAAgB,EAAE,EAAE;IAAE,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,qBAuOnF"}
@@ -4,17 +4,14 @@ import * as React from "react";
4
4
  import { useSearchParams } from "next/navigation";
5
5
  import { loadAllAssets } from "./utils/potreeLoader";
6
6
  import { BuildingsContext, PointCloudContext } from "../../../store";
7
- import { useAppConfigContext } from "../../../store/AppConfig/context";
8
7
  import { restoreCameraFromUrl } from "./utils/restoreCameraFromUrl";
9
8
  import { PointCloudLoadingState } from "./src/PointCloudLoadingState";
10
9
  import { useFilesByBuildingId } from "../../../hooks/files/files";
11
10
  import { ViewportGizmo } from "./src/ViewportGizmo";
12
11
  import { usePointCloudCoordinateSystem } from "../useCoordinateSystem";
13
- function PointCloudViewer() {
14
- var _a;
12
+ function PointCloudViewer({ pointcloudApiUrl }) {
15
13
  usePointCloudCoordinateSystem();
16
- const { state: appConfigState } = useAppConfigContext();
17
- const API_BASE = (_a = appConfigState.runtimeConfig.pointcloudApiUrl) != null ? _a : "http://localhost:5101";
14
+ const API_BASE = pointcloudApiUrl != null ? pointcloudApiUrl : "http://localhost:5101";
18
15
  const containerRef = React.useRef(null);
19
16
  const viewerRef = React.useRef(null);
20
17
  const gizmoRef = React.useRef(null);
@@ -29,8 +26,8 @@ function PointCloudViewer() {
29
26
  const { files, isError: filesError } = useFilesByBuildingId(building == null ? void 0 : building.id);
30
27
  const hasPointCloudFiles = React.useMemo(
31
28
  () => !filesError && files.some((file) => {
32
- var _a2;
33
- const ext = (_a2 = file.extension) == null ? void 0 : _a2.toLowerCase();
29
+ var _a;
30
+ const ext = (_a = file.extension) == null ? void 0 : _a.toLowerCase();
34
31
  if (!ext) return false;
35
32
  return ext === "laz" || ext === "las";
36
33
  }),
@@ -73,7 +70,7 @@ function PointCloudViewer() {
73
70
  }
74
71
  });
75
72
  return () => {
76
- var _a2, _b, _c;
73
+ var _a, _b, _c;
77
74
  try {
78
75
  if (gizmoRef.current) {
79
76
  gizmoRef.current.dispose();
@@ -86,10 +83,10 @@ function PointCloudViewer() {
86
83
  v.update = () => {
87
84
  };
88
85
  }
89
- if ((_a2 = v == null ? void 0 : v.scene) == null ? void 0 : _a2.pointclouds) {
86
+ if ((_a = v == null ? void 0 : v.scene) == null ? void 0 : _a.pointclouds) {
90
87
  v.scene.pointclouds.forEach((pc) => {
91
- var _a3, _b2, _c2, _d, _e;
92
- (_e = (_b2 = (_a3 = v.scene).removePointCloud) == null ? void 0 : _b2.call(_a3, pc)) != null ? _e : (_d = (_c2 = v.scene.scene) == null ? void 0 : _c2.remove) == null ? void 0 : _d.call(_c2, pc);
88
+ var _a2, _b2, _c2, _d, _e;
89
+ (_e = (_b2 = (_a2 = v.scene).removePointCloud) == null ? void 0 : _b2.call(_a2, pc)) != null ? _e : (_d = (_c2 = v.scene.scene) == null ? void 0 : _c2.remove) == null ? void 0 : _d.call(_c2, pc);
93
90
  });
94
91
  }
95
92
  (_c = (_b = v == null ? void 0 : v.renderer) == null ? void 0 : _b.dispose) == null ? void 0 : _c.call(_b);
@@ -102,7 +99,7 @@ function PointCloudViewer() {
102
99
  };
103
100
  }, [PointCloudGlobalState.ready]);
104
101
  React.useEffect(() => {
105
- var _a2, _b;
102
+ var _a, _b;
106
103
  const viewer = viewerRef.current;
107
104
  if (!PointCloudGlobalState.ready || !viewer) return;
108
105
  const Potree = window.Potree;
@@ -114,7 +111,7 @@ function PointCloudViewer() {
114
111
  const pc = loadedCloudsRef.current.get(id);
115
112
  if (pc) {
116
113
  try {
117
- const pcs = (_a2 = viewer.scene.pointclouds) != null ? _a2 : [];
114
+ const pcs = (_a = viewer.scene.pointclouds) != null ? _a : [];
118
115
  const idx = pcs.indexOf(pc);
119
116
  if (idx !== -1) pcs.splice(idx, 1);
120
117
  (_b = viewer.scene.scene) == null ? void 0 : _b.remove(pc);
@@ -202,7 +199,7 @@ function PointCloudViewer() {
202
199
  }
203
200
  )
204
201
  ] }),
205
- !hasPointCloudFiles && !hasLoadedClouds && /* @__PURE__ */ jsx("div", { className: "absolute inset-0", style: { zIndex: 10 }, children: /* @__PURE__ */ jsx(PointCloudLoadingState, {}) })
202
+ !hasPointCloudFiles && !hasLoadedClouds && /* @__PURE__ */ jsx("div", { className: "absolute inset-0", style: { zIndex: 10 }, children: /* @__PURE__ */ jsx(PointCloudLoadingState, { pointcloudApiUrl }) })
206
203
  ]
207
204
  }
208
205
  );
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/core/components/viewers/pointcloud/PointCloudViewer.tsx"],"sourcesContent":["\"use client\"\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as React from \"react\";\r\nimport * as THREE from \"three\";\r\nimport { useSearchParams } from \"next/navigation\";\r\nimport { loadAllAssets } from \"./utils/potreeLoader\";\r\nimport { BuildingsContext, PointCloudContext } from '../../../store'\r\nimport { useAppConfigContext } from '../../../store/AppConfig/context'\r\nimport { restoreCameraFromUrl } from \"./utils/restoreCameraFromUrl\";\r\nimport { PointCloudLoadingState } from \"./src/PointCloudLoadingState\";\r\nimport { useFilesByBuildingId } from '../../../hooks/files/files'\r\nimport { ViewportGizmo } from './src/ViewportGizmo';\r\nimport { usePointCloudCoordinateSystem } from '../useCoordinateSystem';\r\n\r\n\r\nexport function PointCloudViewer() {\r\n // Enforce Z-up coordinate system for Potree. Resets to Y-up on unmount\r\n // so the BIM viewer doesn't inherit Z-up when switching viewers.\r\n usePointCloudCoordinateSystem();\r\n const { state: appConfigState } = useAppConfigContext()\r\n const API_BASE = appConfigState.runtimeConfig.pointcloudApiUrl ?? 'http://localhost:5101'\r\n\r\n const containerRef = React.useRef<HTMLDivElement>(null);\r\n const viewerRef = React.useRef<any | null>(null);\r\n const gizmoRef = React.useRef<ViewportGizmo | null>(null);\r\n // Tracks Potree point cloud objects keyed by point cloud ID\r\n const loadedCloudsRef = React.useRef<Map<string, any>>(new Map());\r\n // IDs currently being fetched/loaded — prevents duplicate loads while async is in-flight\r\n const pendingIdsRef = React.useRef<Set<string>>(new Set());\r\n const cameraRestoredRef = React.useRef(false);\r\n\r\n const { state: pointCloudState, dispatch: pointCloudDispatch } = React.useContext(PointCloudContext)\r\n const PointCloudGlobalState = pointCloudState.pointcloud\r\n\r\n const { state: buildingState } = React.useContext(BuildingsContext)\r\n const { building } = buildingState.buildings\r\n\r\n const searchParams = useSearchParams()\r\n\r\n // Get files for the building to check if there are LAZ/LAS files\r\n const { files, isError: filesError } = useFilesByBuildingId(building?.id)\r\n const hasPointCloudFiles = React.useMemo(() =>\r\n !filesError && files.some(file => {\r\n const ext = file.extension?.toLowerCase()\r\n if (!ext) return false\r\n return ext === \"laz\" || ext === \"las\"\r\n }),\r\n [files, filesError])\r\n\r\n // --- Load CSS/JS assets once\r\n React.useEffect(() => {\r\n (async () => {\r\n try {\r\n await loadAllAssets()\r\n pointCloudDispatch({ type: \"SET_READY\", payload: { ready: true } })\r\n }\r\n catch (err) {\r\n console.error(\"PointCloudViewer - Failed to load assets\", err)\r\n }\r\n })();\r\n }, []);\r\n\r\n // --- Init viewer once when ready. Separate from cloud loading so the viewer\r\n // persists across toggle on/off of individual point clouds.\r\n React.useEffect(() => {\r\n if (!PointCloudGlobalState.ready || !containerRef.current || !(window as any)?.Potree) return;\r\n\r\n const Potree = (window as any).Potree;\r\n const el = containerRef.current;\r\n\r\n // Clear tracking state since we're creating a fresh viewer\r\n loadedCloudsRef.current.clear();\r\n pendingIdsRef.current.clear();\r\n cameraRestoredRef.current = false;\r\n\r\n const viewer = new Potree.Viewer(el);\r\n viewerRef.current = viewer;\r\n\r\n viewer.setEDLEnabled(true);\r\n viewer.setFOV(60);\r\n viewer.setPointBudget(10_000_000);\r\n viewer.loadSettingsFromURL();\r\n viewer.setBackground(\"white\");\r\n\r\n const gizmo = new ViewportGizmo(viewer);\r\n gizmoRef.current = gizmo;\r\n gizmo.enabled = true;\r\n\r\n pointCloudDispatch({\r\n type: \"INIT\",\r\n payload: {\r\n initialState: {\r\n ready: true,\r\n viewer: viewerRef.current,\r\n }\r\n }\r\n })\r\n\r\n return () => {\r\n try {\r\n if (gizmoRef.current) {\r\n gizmoRef.current.dispose();\r\n gizmoRef.current = null;\r\n }\r\n const v = viewerRef.current;\r\n // Potree's render loop re-schedules itself via requestAnimationFrame with no stop API;\r\n // shadow it with a no-op so it halts on unmount instead of running forever against a\r\n // disposed renderer (otherwise a new loop accumulates on every visit).\r\n if (v) {\r\n v.loop = () => { };\r\n v.update = () => { };\r\n }\r\n if (v?.scene?.pointclouds) {\r\n v.scene.pointclouds.forEach((pc: any) => {\r\n v.scene.removePointCloud?.(pc) ?? v.scene.scene?.remove?.(pc);\r\n });\r\n }\r\n v?.renderer?.dispose?.();\r\n } catch { }\r\n loadedCloudsRef.current.clear();\r\n pendingIdsRef.current.clear();\r\n viewerRef.current = null;\r\n el.innerHTML = \"\";\r\n };\r\n }, [PointCloudGlobalState.ready]);\r\n\r\n // --- Add/remove point clouds whenever loadedPointCloudIds changes or the\r\n // viewer is (re)initialized (PointCloudGlobalState.viewer updates on INIT).\r\n React.useEffect(() => {\r\n const viewer = viewerRef.current;\r\n if (!PointCloudGlobalState.ready || !viewer) return;\r\n\r\n const Potree = (window as any).Potree;\r\n if (!Potree) return;\r\n\r\n const currentIds = new Set(PointCloudGlobalState.loadedPointCloudIds);\r\n const alreadyLoadedIds = new Set(loadedCloudsRef.current.keys());\r\n\r\n // Remove point clouds no longer in the list.\r\n // Potree classic has no removePointCloud() — splice from the internal\r\n // pointclouds array and remove from the Three.js scene separately.\r\n for (const id of alreadyLoadedIds) {\r\n if (!currentIds.has(id)) {\r\n const pc = loadedCloudsRef.current.get(id);\r\n if (pc) {\r\n try {\r\n const pcs: any[] = viewer.scene.pointclouds ?? [];\r\n const idx = pcs.indexOf(pc);\r\n if (idx !== -1) pcs.splice(idx, 1);\r\n viewer.scene.scene?.remove(pc);\r\n } catch { }\r\n loadedCloudsRef.current.delete(id);\r\n pendingIdsRef.current.delete(id);\r\n try { viewer.fitToScreen(); } catch { }\r\n }\r\n }\r\n }\r\n\r\n // Add newly requested point clouds.\r\n // Skip if already tracked in loadedCloudsRef OR still in-flight in pendingIdsRef\r\n // — prevents duplicate loads when Effect 2 fires again before the callback runs.\r\n for (const id of currentIds) {\r\n if (alreadyLoadedIds.has(id) || pendingIdsRef.current.has(id)) continue;\r\n\r\n pendingIdsRef.current.add(id);\r\n ; (async () => {\r\n try {\r\n const res = await fetch(`${API_BASE}/point-cloud/${encodeURIComponent(id)}`);\r\n const data = await res.json();\r\n const url = `${API_BASE}/private/${data.bucket}/${data.potreeMetadataFileKey}`;\r\n\r\n Potree.loadPointCloud(url, id, (e: any) => {\r\n pendingIdsRef.current.delete(id);\r\n // Bail if the viewer was torn down (or re-init'd) while this load was in flight —\r\n // the init-effect cleanup nulls viewerRef, so this guard avoids attaching to a dead viewer.\r\n if (viewerRef.current !== viewer) return;\r\n try {\r\n viewer.scene.addPointCloud(e.pointcloud);\r\n loadedCloudsRef.current.set(id, e.pointcloud);\r\n\r\n const mat = e.pointcloud.material;\r\n mat.size = 1;\r\n mat.pointSizeType = Potree.PointSizeType.ADAPTIVE;\r\n\r\n const restored = !cameraRestoredRef.current && restoreCameraFromUrl(viewer, searchParams)\r\n cameraRestoredRef.current = true;\r\n if (!restored) viewer.fitToScreen();\r\n\r\n if (gizmoRef.current) {\r\n gizmoRef.current.add();\r\n }\r\n } catch (err) {\r\n console.error(\"[PointCloudViewer] Error adding point cloud\", err);\r\n }\r\n });\r\n } catch (err) {\r\n pendingIdsRef.current.delete(id);\r\n console.error(\"[PointCloudViewer] Error fetching point cloud metadata\", err);\r\n }\r\n })();\r\n }\r\n }, [PointCloudGlobalState.loadedPointCloudIds, PointCloudGlobalState.viewer, PointCloudGlobalState.ready]);\r\n\r\n const { loadedPointCloudIds } = PointCloudGlobalState;\r\n const hasLoadedClouds = loadedPointCloudIds.length > 0;\r\n\r\n return (\r\n <div\r\n style={{\r\n position: \"absolute\",\r\n left: 0,\r\n top: 0,\r\n width: \"100%\",\r\n height: \"100%\",\r\n }}\r\n >\r\n {/* Potree container — always rendered so the viewer can measure its dimensions on init.\r\n PointCloudLoadingState sits above it via z-index when no clouds are loaded. */}\r\n <div>\r\n <div\r\n className={`pointer-events-none absolute inset-0 `}\r\n style={{\r\n zIndex: 9,\r\n background: 'radial-gradient(circle at center, rgba(220,220,220,0) 0%, rgba(220,220,220,0) 50%, rgba(220,220,220,0.9) 100%)',\r\n mixBlendMode: 'multiply'\r\n }}\r\n />\r\n <div\r\n ref={containerRef}\r\n className=\"potree_container\"\r\n style={{\r\n position: \"absolute\",\r\n inset: 0,\r\n backgroundSize: \"cover\",\r\n }}\r\n />\r\n </div>\r\n\r\n {/* Show PointCloudLoadingState only when there are no point cloud files uploaded at all.\r\n Rendered after the container so it layers on top (higher DOM order = higher paint order). */}\r\n {!hasPointCloudFiles && !hasLoadedClouds && (\r\n <div className=\"absolute inset-0\" style={{ zIndex: 10 }}>\r\n <PointCloudLoadingState />\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n"],"mappings":"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","names":["_a","_b","_c"]}
1
+ {"version":3,"sources":["../../../../../src/core/components/viewers/pointcloud/PointCloudViewer.tsx"],"sourcesContent":["\"use client\"\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as React from \"react\";\r\nimport * as THREE from \"three\";\r\nimport { useSearchParams } from \"next/navigation\";\r\nimport { loadAllAssets } from \"./utils/potreeLoader\";\r\nimport { BuildingsContext, PointCloudContext } from '../../../store'\r\n// import { useAppConfigContext } from '../../../store/AppConfig/context'\r\nimport { restoreCameraFromUrl } from \"./utils/restoreCameraFromUrl\";\r\nimport { PointCloudLoadingState } from \"./src/PointCloudLoadingState\";\r\nimport { useFilesByBuildingId } from '../../../hooks/files/files'\r\nimport { ViewportGizmo } from './src/ViewportGizmo';\r\nimport { usePointCloudCoordinateSystem } from '../useCoordinateSystem';\r\n\r\n\r\nexport function PointCloudViewer({ pointcloudApiUrl }: { pointcloudApiUrl?: string }) {\r\n // Enforce Z-up coordinate system for Potree. Resets to Y-up on unmount\r\n // so the BIM viewer doesn't inherit Z-up when switching viewers.\r\n usePointCloudCoordinateSystem();\r\n const API_BASE = pointcloudApiUrl ?? 'http://localhost:5101'\r\n\r\n const containerRef = React.useRef<HTMLDivElement>(null);\r\n const viewerRef = React.useRef<any | null>(null);\r\n const gizmoRef = React.useRef<ViewportGizmo | null>(null);\r\n // Tracks Potree point cloud objects keyed by point cloud ID\r\n const loadedCloudsRef = React.useRef<Map<string, any>>(new Map());\r\n // IDs currently being fetched/loaded — prevents duplicate loads while async is in-flight\r\n const pendingIdsRef = React.useRef<Set<string>>(new Set());\r\n const cameraRestoredRef = React.useRef(false);\r\n\r\n const { state: pointCloudState, dispatch: pointCloudDispatch } = React.useContext(PointCloudContext)\r\n const PointCloudGlobalState = pointCloudState.pointcloud\r\n\r\n const { state: buildingState } = React.useContext(BuildingsContext)\r\n const { building } = buildingState.buildings\r\n\r\n const searchParams = useSearchParams()\r\n\r\n // Get files for the building to check if there are LAZ/LAS files\r\n const { files, isError: filesError } = useFilesByBuildingId(building?.id)\r\n const hasPointCloudFiles = React.useMemo(() =>\r\n !filesError && files.some(file => {\r\n const ext = file.extension?.toLowerCase()\r\n if (!ext) return false\r\n return ext === \"laz\" || ext === \"las\"\r\n }),\r\n [files, filesError])\r\n\r\n // --- Load CSS/JS assets once\r\n React.useEffect(() => {\r\n (async () => {\r\n try {\r\n await loadAllAssets()\r\n pointCloudDispatch({ type: \"SET_READY\", payload: { ready: true } })\r\n }\r\n catch (err) {\r\n console.error(\"PointCloudViewer - Failed to load assets\", err)\r\n }\r\n })();\r\n }, []);\r\n\r\n // --- Init viewer once when ready. Separate from cloud loading so the viewer\r\n // persists across toggle on/off of individual point clouds.\r\n React.useEffect(() => {\r\n if (!PointCloudGlobalState.ready || !containerRef.current || !(window as any)?.Potree) return;\r\n\r\n const Potree = (window as any).Potree;\r\n const el = containerRef.current;\r\n\r\n // Clear tracking state since we're creating a fresh viewer\r\n loadedCloudsRef.current.clear();\r\n pendingIdsRef.current.clear();\r\n cameraRestoredRef.current = false;\r\n\r\n const viewer = new Potree.Viewer(el);\r\n viewerRef.current = viewer;\r\n\r\n viewer.setEDLEnabled(true);\r\n viewer.setFOV(60);\r\n viewer.setPointBudget(10_000_000);\r\n viewer.loadSettingsFromURL();\r\n viewer.setBackground(\"white\");\r\n\r\n const gizmo = new ViewportGizmo(viewer);\r\n gizmoRef.current = gizmo;\r\n gizmo.enabled = true;\r\n\r\n pointCloudDispatch({\r\n type: \"INIT\",\r\n payload: {\r\n initialState: {\r\n ready: true,\r\n viewer: viewerRef.current,\r\n }\r\n }\r\n })\r\n\r\n return () => {\r\n try {\r\n if (gizmoRef.current) {\r\n gizmoRef.current.dispose();\r\n gizmoRef.current = null;\r\n }\r\n const v = viewerRef.current;\r\n // Potree's render loop re-schedules itself via requestAnimationFrame with no stop API;\r\n // shadow it with a no-op so it halts on unmount instead of running forever against a\r\n // disposed renderer (otherwise a new loop accumulates on every visit).\r\n if (v) {\r\n v.loop = () => { };\r\n v.update = () => { };\r\n }\r\n if (v?.scene?.pointclouds) {\r\n v.scene.pointclouds.forEach((pc: any) => {\r\n v.scene.removePointCloud?.(pc) ?? v.scene.scene?.remove?.(pc);\r\n });\r\n }\r\n v?.renderer?.dispose?.();\r\n } catch { }\r\n loadedCloudsRef.current.clear();\r\n pendingIdsRef.current.clear();\r\n viewerRef.current = null;\r\n el.innerHTML = \"\";\r\n };\r\n }, [PointCloudGlobalState.ready]);\r\n\r\n // --- Add/remove point clouds whenever loadedPointCloudIds changes or the\r\n // viewer is (re)initialized (PointCloudGlobalState.viewer updates on INIT).\r\n React.useEffect(() => {\r\n const viewer = viewerRef.current;\r\n if (!PointCloudGlobalState.ready || !viewer) return;\r\n\r\n const Potree = (window as any).Potree;\r\n if (!Potree) return;\r\n\r\n const currentIds = new Set(PointCloudGlobalState.loadedPointCloudIds);\r\n const alreadyLoadedIds = new Set(loadedCloudsRef.current.keys());\r\n\r\n // Remove point clouds no longer in the list.\r\n // Potree classic has no removePointCloud() — splice from the internal\r\n // pointclouds array and remove from the Three.js scene separately.\r\n for (const id of alreadyLoadedIds) {\r\n if (!currentIds.has(id)) {\r\n const pc = loadedCloudsRef.current.get(id);\r\n if (pc) {\r\n try {\r\n const pcs: any[] = viewer.scene.pointclouds ?? [];\r\n const idx = pcs.indexOf(pc);\r\n if (idx !== -1) pcs.splice(idx, 1);\r\n viewer.scene.scene?.remove(pc);\r\n } catch { }\r\n loadedCloudsRef.current.delete(id);\r\n pendingIdsRef.current.delete(id);\r\n try { viewer.fitToScreen(); } catch { }\r\n }\r\n }\r\n }\r\n\r\n // Add newly requested point clouds.\r\n // Skip if already tracked in loadedCloudsRef OR still in-flight in pendingIdsRef\r\n // — prevents duplicate loads when Effect 2 fires again before the callback runs.\r\n for (const id of currentIds) {\r\n if (alreadyLoadedIds.has(id) || pendingIdsRef.current.has(id)) continue;\r\n\r\n pendingIdsRef.current.add(id);\r\n ; (async () => {\r\n try {\r\n const res = await fetch(`${API_BASE}/point-cloud/${encodeURIComponent(id)}`);\r\n const data = await res.json();\r\n const url = `${API_BASE}/private/${data.bucket}/${data.potreeMetadataFileKey}`;\r\n\r\n Potree.loadPointCloud(url, id, (e: any) => {\r\n pendingIdsRef.current.delete(id);\r\n // Bail if the viewer was torn down (or re-init'd) while this load was in flight —\r\n // the init-effect cleanup nulls viewerRef, so this guard avoids attaching to a dead viewer.\r\n if (viewerRef.current !== viewer) return;\r\n try {\r\n viewer.scene.addPointCloud(e.pointcloud);\r\n loadedCloudsRef.current.set(id, e.pointcloud);\r\n\r\n const mat = e.pointcloud.material;\r\n mat.size = 1;\r\n mat.pointSizeType = Potree.PointSizeType.ADAPTIVE;\r\n\r\n const restored = !cameraRestoredRef.current && restoreCameraFromUrl(viewer, searchParams)\r\n cameraRestoredRef.current = true;\r\n if (!restored) viewer.fitToScreen();\r\n\r\n if (gizmoRef.current) {\r\n gizmoRef.current.add();\r\n }\r\n } catch (err) {\r\n console.error(\"[PointCloudViewer] Error adding point cloud\", err);\r\n }\r\n });\r\n } catch (err) {\r\n pendingIdsRef.current.delete(id);\r\n console.error(\"[PointCloudViewer] Error fetching point cloud metadata\", err);\r\n }\r\n })();\r\n }\r\n }, [PointCloudGlobalState.loadedPointCloudIds, PointCloudGlobalState.viewer, PointCloudGlobalState.ready]);\r\n\r\n const { loadedPointCloudIds } = PointCloudGlobalState;\r\n const hasLoadedClouds = loadedPointCloudIds.length > 0;\r\n\r\n return (\r\n <div\r\n style={{\r\n position: \"absolute\",\r\n left: 0,\r\n top: 0,\r\n width: \"100%\",\r\n height: \"100%\",\r\n }}\r\n >\r\n {/* Potree container — always rendered so the viewer can measure its dimensions on init.\r\n PointCloudLoadingState sits above it via z-index when no clouds are loaded. */}\r\n <div>\r\n <div\r\n className={`pointer-events-none absolute inset-0 `}\r\n style={{\r\n zIndex: 9,\r\n background: 'radial-gradient(circle at center, rgba(220,220,220,0) 0%, rgba(220,220,220,0) 50%, rgba(220,220,220,0.9) 100%)',\r\n mixBlendMode: 'multiply'\r\n }}\r\n />\r\n <div\r\n ref={containerRef}\r\n className=\"potree_container\"\r\n style={{\r\n position: \"absolute\",\r\n inset: 0,\r\n backgroundSize: \"cover\",\r\n }}\r\n />\r\n </div>\r\n\r\n {/* Show PointCloudLoadingState only when there are no point cloud files uploaded at all.\r\n Rendered after the container so it layers on top (higher DOM order = higher paint order). */}\r\n {!hasPointCloudFiles && !hasLoadedClouds && (\r\n <div className=\"absolute inset-0\" style={{ zIndex: 10 }}>\r\n <PointCloudLoadingState pointcloudApiUrl={pointcloudApiUrl} />\r\n </div>\r\n )}\r\n </div>\r\n );\r\n}\r\n"],"mappings":";AA4NM,SACE,KADF;AAvNN,YAAY,WAAW;AAEvB,SAAS,uBAAuB;AAChC,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB,yBAAyB;AAEpD,SAAS,4BAA4B;AACrC,SAAS,8BAA8B;AACvC,SAAS,4BAA4B;AACrC,SAAS,qBAAqB;AAC9B,SAAS,qCAAqC;AAGvC,SAAS,iBAAiB,EAAE,iBAAiB,GAAkC;AAGpF,gCAA8B;AAC9B,QAAM,WAAW,8CAAoB;AAErC,QAAM,eAAe,MAAM,OAAuB,IAAI;AACtD,QAAM,YAAY,MAAM,OAAmB,IAAI;AAC/C,QAAM,WAAW,MAAM,OAA6B,IAAI;AAExD,QAAM,kBAAkB,MAAM,OAAyB,oBAAI,IAAI,CAAC;AAEhE,QAAM,gBAAgB,MAAM,OAAoB,oBAAI,IAAI,CAAC;AACzD,QAAM,oBAAoB,MAAM,OAAO,KAAK;AAE5C,QAAM,EAAE,OAAO,iBAAiB,UAAU,mBAAmB,IAAI,MAAM,WAAW,iBAAiB;AACnG,QAAM,wBAAwB,gBAAgB;AAE9C,QAAM,EAAE,OAAO,cAAc,IAAI,MAAM,WAAW,gBAAgB;AAClE,QAAM,EAAE,SAAS,IAAI,cAAc;AAEnC,QAAM,eAAe,gBAAgB;AAGrC,QAAM,EAAE,OAAO,SAAS,WAAW,IAAI,qBAAqB,qCAAU,EAAE;AACxE,QAAM,qBAAqB,MAAM;AAAA,IAAQ,MACvC,CAAC,cAAc,MAAM,KAAK,UAAQ;AA5CtC;AA6CM,YAAM,OAAM,UAAK,cAAL,mBAAgB;AAC5B,UAAI,CAAC,IAAK,QAAO;AACjB,aAAO,QAAQ,SAAS,QAAQ;AAAA,IAClC,CAAC;AAAA,IACD,CAAC,OAAO,UAAU;AAAA,EAAC;AAGrB,QAAM,UAAU,MAAM;AACpB,KAAC,YAAY;AACX,UAAI;AACF,cAAM,cAAc;AACpB,2BAAmB,EAAE,MAAM,aAAa,SAAS,EAAE,OAAO,KAAK,EAAE,CAAC;AAAA,MACpE,SACO,KAAK;AACV,gBAAQ,MAAM,4CAA4C,GAAG;AAAA,MAC/D;AAAA,IACF,GAAG;AAAA,EACL,GAAG,CAAC,CAAC;AAIL,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,sBAAsB,SAAS,CAAC,aAAa,WAAW,EAAE,iCAAgB,QAAQ;AAEvF,UAAM,SAAU,OAAe;AAC/B,UAAM,KAAK,aAAa;AAGxB,oBAAgB,QAAQ,MAAM;AAC9B,kBAAc,QAAQ,MAAM;AAC5B,sBAAkB,UAAU;AAE5B,UAAM,SAAS,IAAI,OAAO,OAAO,EAAE;AACnC,cAAU,UAAU;AAEpB,WAAO,cAAc,IAAI;AACzB,WAAO,OAAO,EAAE;AAChB,WAAO,eAAe,GAAU;AAChC,WAAO,oBAAoB;AAC3B,WAAO,cAAc,OAAO;AAE5B,UAAM,QAAQ,IAAI,cAAc,MAAM;AACtC,aAAS,UAAU;AACnB,UAAM,UAAU;AAEhB,uBAAmB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS;AAAA,QACP,cAAc;AAAA,UACZ,OAAO;AAAA,UACP,QAAQ,UAAU;AAAA,QACpB;AAAA,MACF;AAAA,IACF,CAAC;AAED,WAAO,MAAM;AApGjB;AAqGM,UAAI;AACF,YAAI,SAAS,SAAS;AACpB,mBAAS,QAAQ,QAAQ;AACzB,mBAAS,UAAU;AAAA,QACrB;AACA,cAAM,IAAI,UAAU;AAIpB,YAAI,GAAG;AACL,YAAE,OAAO,MAAM;AAAA,UAAE;AACjB,YAAE,SAAS,MAAM;AAAA,UAAE;AAAA,QACrB;AACA,aAAI,4BAAG,UAAH,mBAAU,aAAa;AACzB,YAAE,MAAM,YAAY,QAAQ,CAAC,OAAY;AAnHnD,gBAAAA,KAAAC,KAAAC,KAAA;AAoHY,mBAAAD,OAAAD,MAAA,EAAE,OAAM,qBAAR,gBAAAC,IAAA,KAAAD,KAA2B,QAA3B,aAAkC,MAAAE,MAAA,EAAE,MAAM,UAAR,gBAAAA,IAAe,WAAf,wBAAAA,KAAwB;AAAA,UAC5D,CAAC;AAAA,QACH;AACA,2CAAG,aAAH,mBAAa,YAAb;AAAA,MACF,SAAQ;AAAA,MAAE;AACV,sBAAgB,QAAQ,MAAM;AAC9B,oBAAc,QAAQ,MAAM;AAC5B,gBAAU,UAAU;AACpB,SAAG,YAAY;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,sBAAsB,KAAK,CAAC;AAIhC,QAAM,UAAU,MAAM;AAlIxB;AAmII,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,sBAAsB,SAAS,CAAC,OAAQ;AAE7C,UAAM,SAAU,OAAe;AAC/B,QAAI,CAAC,OAAQ;AAEb,UAAM,aAAa,IAAI,IAAI,sBAAsB,mBAAmB;AACpE,UAAM,mBAAmB,IAAI,IAAI,gBAAgB,QAAQ,KAAK,CAAC;AAK/D,eAAW,MAAM,kBAAkB;AACjC,UAAI,CAAC,WAAW,IAAI,EAAE,GAAG;AACvB,cAAM,KAAK,gBAAgB,QAAQ,IAAI,EAAE;AACzC,YAAI,IAAI;AACN,cAAI;AACF,kBAAM,OAAa,YAAO,MAAM,gBAAb,YAA4B,CAAC;AAChD,kBAAM,MAAM,IAAI,QAAQ,EAAE;AAC1B,gBAAI,QAAQ,GAAI,KAAI,OAAO,KAAK,CAAC;AACjC,yBAAO,MAAM,UAAb,mBAAoB,OAAO;AAAA,UAC7B,SAAQ;AAAA,UAAE;AACV,0BAAgB,QAAQ,OAAO,EAAE;AACjC,wBAAc,QAAQ,OAAO,EAAE;AAC/B,cAAI;AAAE,mBAAO,YAAY;AAAA,UAAG,SAAQ;AAAA,UAAE;AAAA,QACxC;AAAA,MACF;AAAA,IACF;AAKA,eAAW,MAAM,YAAY;AAC3B,UAAI,iBAAiB,IAAI,EAAE,KAAK,cAAc,QAAQ,IAAI,EAAE,EAAG;AAE/D,oBAAc,QAAQ,IAAI,EAAE;AAC5B;AAAE,OAAC,YAAY;AACb,YAAI;AACF,gBAAM,MAAM,MAAM,MAAM,GAAG,QAAQ,gBAAgB,mBAAmB,EAAE,CAAC,EAAE;AAC3E,gBAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,gBAAM,MAAM,GAAG,QAAQ,YAAY,KAAK,MAAM,IAAI,KAAK,qBAAqB;AAE5E,iBAAO,eAAe,KAAK,IAAI,CAAC,MAAW;AACzC,0BAAc,QAAQ,OAAO,EAAE;AAG/B,gBAAI,UAAU,YAAY,OAAQ;AAClC,gBAAI;AACF,qBAAO,MAAM,cAAc,EAAE,UAAU;AACvC,8BAAgB,QAAQ,IAAI,IAAI,EAAE,UAAU;AAE5C,oBAAM,MAAM,EAAE,WAAW;AACzB,kBAAI,OAAO;AACX,kBAAI,gBAAgB,OAAO,cAAc;AAEzC,oBAAM,WAAW,CAAC,kBAAkB,WAAW,qBAAqB,QAAQ,YAAY;AACxF,gCAAkB,UAAU;AAC5B,kBAAI,CAAC,SAAU,QAAO,YAAY;AAElC,kBAAI,SAAS,SAAS;AACpB,yBAAS,QAAQ,IAAI;AAAA,cACvB;AAAA,YACF,SAAS,KAAK;AACZ,sBAAQ,MAAM,+CAA+C,GAAG;AAAA,YAClE;AAAA,UACF,CAAC;AAAA,QACH,SAAS,KAAK;AACZ,wBAAc,QAAQ,OAAO,EAAE;AAC/B,kBAAQ,MAAM,0DAA0D,GAAG;AAAA,QAC7E;AAAA,MACF,GAAG;AAAA,IACL;AAAA,EACF,GAAG,CAAC,sBAAsB,qBAAqB,sBAAsB,QAAQ,sBAAsB,KAAK,CAAC;AAEzG,QAAM,EAAE,oBAAoB,IAAI;AAChC,QAAM,kBAAkB,oBAAoB,SAAS;AAErD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO;AAAA,QACL,UAAU;AAAA,QACV,MAAM;AAAA,QACN,KAAK;AAAA,QACL,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,MAIA;AAAA,6BAAC,SACC;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,WAAW;AAAA,cACX,OAAO;AAAA,gBACL,QAAQ;AAAA,gBACR,YAAY;AAAA,gBACZ,cAAc;AAAA,cAChB;AAAA;AAAA,UACF;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,WAAU;AAAA,cACV,OAAO;AAAA,gBACL,UAAU;AAAA,gBACV,OAAO;AAAA,gBACP,gBAAgB;AAAA,cAClB;AAAA;AAAA,UACF;AAAA,WACF;AAAA,QAIC,CAAC,sBAAsB,CAAC,mBACvB,oBAAC,SAAI,WAAU,oBAAmB,OAAO,EAAE,QAAQ,GAAG,GACpD,8BAAC,0BAAuB,kBAAoC,GAC9D;AAAA;AAAA;AAAA,EAEJ;AAEJ;","names":["_a","_b","_c"]}
@@ -1,4 +1,6 @@
1
1
  import React from 'react';
2
2
  export type pointCloudViewerState = 'opening' | 'loading' | 'ready' | 'error' | 'noPointCloudFiles' | 'noBuilding';
3
- export declare function PointCloudLoadingState(): React.JSX.Element;
3
+ export declare function PointCloudLoadingState({ pointcloudApiUrl }: {
4
+ pointcloudApiUrl?: string;
5
+ }): React.JSX.Element;
4
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,MAAM,OAAO,CAAA;AA0BzB,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,mBAAmB,GAAG,YAAY,CAAA;AAElH,wBAAgB,sBAAsB,sBA4WrC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,MAAM,OAAO,CAAA;AAsBzB,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,mBAAmB,GAAG,YAAY,CAAA;AAElH,wBAAgB,sBAAsB,CAAC,EAAE,gBAAgB,EAAE,EAAE;IAAE,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,qBAqVzF"}
@@ -26,17 +26,15 @@ import { Input } from "../../../../ui/Input";
26
26
  import { LoadingSpinner } from "../../../../ui/LoadingSpinner";
27
27
  import * as LR from "lucide-react";
28
28
  import { BuildingsContext, usePermissions } from "../../../../../store";
29
- import { useAppConfigContext } from "../../../../../store/AppConfig/context";
30
29
  import { useTranslations } from "next-intl";
31
30
  import { useFilesByBuildingId } from "../../../../../hooks/files/files";
32
31
  import { cn } from "../../../../../utils/utils";
33
32
  import { useSearchParams, useRouter } from "next/navigation";
34
33
  import { useBuilding, useBuildings } from "../../../../../hooks/buildings/buildings";
35
- function PointCloudLoadingState() {
36
- var _a;
34
+ import { toast } from "sonner";
35
+ function PointCloudLoadingState({ pointcloudApiUrl }) {
37
36
  const t = useTranslations("PointCloudLoadingState");
38
- const { state: appConfigState } = useAppConfigContext();
39
- const API_BASE = (_a = appConfigState.runtimeConfig.pointcloudApiUrl) != null ? _a : "http://localhost:5101";
37
+ const API_BASE = pointcloudApiUrl != null ? pointcloudApiUrl : "http://localhost:5101";
40
38
  const { ability } = usePermissions();
41
39
  const { state: buildingState, dispatch: buildingDispatch } = React.useContext(BuildingsContext);
42
40
  const { building: storeBuilding } = buildingState.buildings;
@@ -59,8 +57,8 @@ function PointCloudLoadingState() {
59
57
  const [selectedBuilding, setSelectedBuilding] = React.useState(null);
60
58
  const { files, isLoading: filesLoading } = useFilesByBuildingId(building == null ? void 0 : building.id);
61
59
  const pointCloudFiles = React.useMemo(() => files.filter((file) => {
62
- var _a2;
63
- const ext = (_a2 = file.extension) == null ? void 0 : _a2.toLowerCase();
60
+ var _a;
61
+ const ext = (_a = file.extension) == null ? void 0 : _a.toLowerCase();
64
62
  if (!ext) return false;
65
63
  return ext === "laz" || ext === "las";
66
64
  }), [files]);
@@ -126,42 +124,6 @@ function PointCloudLoadingState() {
126
124
  setSearchResults([]);
127
125
  router.push(url.toString());
128
126
  };
129
- async function createPointCloud(name) {
130
- const res = await fetch(`${API_BASE}/point-cloud`, {
131
- method: "POST",
132
- headers: { "Content-Type": "application/json" },
133
- body: JSON.stringify({ name })
134
- });
135
- if (!res.ok) {
136
- const msg = await res.text();
137
- throw new Error(`Create point-cloud failed: ${msg || res.status}`);
138
- }
139
- return res.json();
140
- }
141
- function uploadToPresignedUrl(url, file, onProgress) {
142
- return new Promise((resolve, reject) => {
143
- try {
144
- const xhr = new XMLHttpRequest();
145
- xhr.open("PUT", url, true);
146
- if (onProgress) {
147
- xhr.upload.onprogress = (evt) => {
148
- if (evt.lengthComputable) {
149
- const pct = Math.round(evt.loaded / evt.total * 100);
150
- onProgress(pct);
151
- }
152
- };
153
- }
154
- xhr.onload = () => {
155
- if (xhr.status >= 200 && xhr.status < 300) resolve();
156
- else reject(new Error(`Upload failed with status ${xhr.status}`));
157
- };
158
- xhr.onerror = () => reject(new Error("Network error during upload"));
159
- xhr.send(file);
160
- } catch (err) {
161
- reject(err);
162
- }
163
- });
164
- }
165
127
  const handleFileChange = async (event) => {
166
128
  const files2 = event.target.files;
167
129
  if (!files2 || files2.length === 0) return;
@@ -172,16 +134,39 @@ function PointCloudLoadingState() {
172
134
  alert("Please select a LAZ or LAS file.");
173
135
  return;
174
136
  }
137
+ if (!(building == null ? void 0 : building.id)) {
138
+ alert("No building selected.");
139
+ return;
140
+ }
175
141
  setUploading(true);
176
142
  try {
177
- console.log("Creating point cloud entry...");
178
- const { pointCloud, upload } = await createPointCloud(fileName);
179
- console.log("Point cloud created:", pointCloud.id);
180
- console.log("Uploading file...");
181
- await uploadToPresignedUrl(upload.uploadUrl, file, (pct) => {
182
- console.log(`Upload progress: ${pct}%`);
143
+ const assetId = crypto.randomUUID();
144
+ const assetName = `${assetId}.laz`;
145
+ const presignedRes = await fetch(
146
+ `/api/presigned-url-upload?asset=${encodeURIComponent(assetName)}&bucket=pointclouds-demo`
147
+ );
148
+ if (!presignedRes.ok) throw new Error("Failed to get upload URL");
149
+ const { presignedUrl, key } = await presignedRes.json();
150
+ const uploadRes = await fetch(presignedUrl, {
151
+ method: "PUT",
152
+ body: file,
153
+ headers: { "Content-Type": "application/octet-stream" }
154
+ });
155
+ if (!uploadRes.ok) throw new Error(`Upload failed: ${uploadRes.status}`);
156
+ const fileRes = await fetch(`/api/files/building/${building.id}`, {
157
+ method: "POST",
158
+ headers: { "Content-Type": "application/json" },
159
+ body: JSON.stringify({
160
+ type: "point-cloud-file",
161
+ name: fileName,
162
+ assetId,
163
+ lazFileKey: key,
164
+ bucket: "pointclouds-demo",
165
+ extension: fileExt.endsWith(".las") ? "las" : "laz"
166
+ })
183
167
  });
184
- console.log("Upload complete!");
168
+ if (!fileRes.ok) throw new Error("Failed to create file record");
169
+ toast.success("LAZ file uploaded successfully", { duration: 6e3 });
185
170
  window.location.reload();
186
171
  } catch (error) {
187
172
  console.error("Error uploading point cloud:", error);
@@ -192,8 +177,8 @@ function PointCloudLoadingState() {
192
177
  }
193
178
  };
194
179
  const handleUploadClick = () => {
195
- var _a2;
196
- (_a2 = fileInputRef.current) == null ? void 0 : _a2.click();
180
+ var _a;
181
+ (_a = fileInputRef.current) == null ? void 0 : _a.click();
197
182
  };
198
183
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("div", { className: "absolute z-10 inset-0 bg-background/50 backdrop-blur-[1px] flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxs(Card, { className: cn("w-[536px] pointer-events-auto flex flex-col justify-start items-center gap-2"), children: [
199
184
  /* @__PURE__ */ jsx(CardHeader, { className: "flex justify-center items-center", children: /* @__PURE__ */ jsx("div", { className: "w-12 h-12 p-2 bg-card rounded-md shadow-sm border border-border flex justify-center items-center", children: currentState === "noBuilding" ? /* @__PURE__ */ jsx(LR.Building2, { className: cn("w-4 h-5 text-foreground", buildingsLoading && "animate-pulse"), strokeWidth: 2 }) : /* @__PURE__ */ jsx(LR.Grip, { className: cn("w-4 h-5 text-foreground", currentState === "opening" && "animate-pulse"), strokeWidth: 2 }) }) }),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../src/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport React from 'react'\r\nimport { Button } from '../../../../ui/Button'\r\nimport { Card, CardContent, CardHeader } from '../../../../ui/Card'\r\nimport { Input } from '../../../../ui/Input'\r\nimport { LoadingSpinner } from '../../../../ui/LoadingSpinner'\r\nimport * as LR from 'lucide-react'\r\nimport { BuildingsContext, usePermissions } from '../../../../../store'\r\nimport { useAppConfigContext } from '../../../../../store/AppConfig/context'\r\nimport { useTranslations } from 'next-intl'\r\nimport { useFilesByBuildingId } from '../../../../../hooks/files/files'\r\nimport type { DbFile } from '../../../../../types/dbTypes'\r\nimport { cn } from '../../../../../utils/utils'\r\nimport { useSearchParams, useRouter } from 'next/navigation'\r\nimport { useBuilding, useBuildings } from '../../../../../hooks/buildings/buildings'\r\nimport { SquareArrowOutUpRight } from 'lucide-react'\r\n\r\ntype CreatePointCloudResponse = {\r\n pointCloud: {\r\n id: string\r\n name: string\r\n }\r\n upload: {\r\n uploadUrl: string\r\n }\r\n}\r\n\r\nexport type pointCloudViewerState = 'opening' | 'loading' | 'ready' | 'error' | 'noPointCloudFiles' | 'noBuilding'\r\n\r\nexport function PointCloudLoadingState() {\r\n const t = useTranslations('PointCloudLoadingState')\r\n const { state: appConfigState } = useAppConfigContext()\r\n const API_BASE = appConfigState.runtimeConfig.pointcloudApiUrl ?? 'http://localhost:5101'\r\n // Permissions\r\n const { ability } = usePermissions()\r\n\r\n const { state: buildingState, dispatch: buildingDispatch } = React.useContext(BuildingsContext)\r\n const { building: storeBuilding } = buildingState.buildings\r\n\r\n const searchParams = useSearchParams()\r\n const buildingId = searchParams.get(\"buildingId\")\r\n const { building: urlBuilding, isLoading: buildingLoading, isError: buildingError } = useBuilding(buildingId ? Number(buildingId) : 0)\r\n const { buildings, isLoading: buildingsLoading } = useBuildings()\r\n\r\n // Determine the current building to use\r\n const building = storeBuilding || urlBuilding\r\n\r\n React.useEffect(() => {\r\n if (!storeBuilding && urlBuilding) {\r\n buildingDispatch({ type: \"SET-CURRENT-BUILDING\", payload: { building: urlBuilding } })\r\n }\r\n }, [urlBuilding, storeBuilding, buildingDispatch])\r\n\r\n const router = useRouter()\r\n const fileInputRef = React.useRef<HTMLInputElement>(null)\r\n\r\n const [currentState, setCurrentState] = React.useState<pointCloudViewerState>('opening')\r\n const [uploading, setUploading] = React.useState(false)\r\n const [searchTerm, setSearchTerm] = React.useState('')\r\n const [searchResults, setSearchResults] = React.useState<any[]>([])\r\n const [selectedBuilding, setSelectedBuilding] = React.useState<any>(null)\r\n \r\n const { files, isLoading: filesLoading } = useFilesByBuildingId(building?.id)\r\n const pointCloudFiles: DbFile[] = React.useMemo(() => (\r\n files.filter(file => {\r\n const ext = file.extension?.toLowerCase()\r\n if (!ext) return false\r\n return ext === \"laz\" || ext === \"las\"\r\n })\r\n ), [files])\r\n\r\n // Fuzzy search function for buildings\r\n const fuzzySearchBuildings = React.useCallback((buildings: any[], searchTerm: string) => {\r\n if (!searchTerm.trim()) return buildings.slice(0, 10)\r\n \r\n const fuzzySearchScore = (search: string, text: string): number => {\r\n if (!search || !text) return 0\r\n const searchLower = search.toLowerCase()\r\n const textLower = text.toLowerCase()\r\n \r\n if (textLower === searchLower) return 1000\r\n if (textLower.startsWith(searchLower)) return 800\r\n if (textLower.includes(searchLower)) return 600\r\n \r\n return 0\r\n }\r\n\r\n return buildings\r\n .map((building) => {\r\n const address = building.buildingAddress || ''\r\n const name = building.buildingName || ''\r\n const id = building.id.toString()\r\n \r\n const addressScore = fuzzySearchScore(searchTerm, address)\r\n const nameScore = fuzzySearchScore(searchTerm, name)\r\n const idScore = fuzzySearchScore(searchTerm, id)\r\n \r\n const maxScore = Math.max(addressScore, nameScore, idScore)\r\n return { ...building, searchScore: maxScore }\r\n })\r\n .filter(building => building.searchScore > 0)\r\n .sort((a, b) => b.searchScore - a.searchScore)\r\n .slice(0, 10)\r\n }, [])\r\n\r\n // Handle search\r\n const handleSearch = React.useCallback((term: string) => {\r\n setSearchTerm(term)\r\n if (buildings) {\r\n setSearchResults(fuzzySearchBuildings(buildings, term))\r\n }\r\n }, [buildings, fuzzySearchBuildings])\r\n\r\n const shouldShow = (currentState === 'opening' || currentState === 'noPointCloudFiles' || currentState === 'noBuilding')\r\n\r\n // Handle initial state transitions\r\n React.useEffect(() => {\r\n // If building is loading, stay in opening state\r\n if (buildingLoading) {\r\n setCurrentState('opening')\r\n return\r\n }\r\n\r\n // If building failed to load or no buildingId provided, show noBuilding state\r\n if (buildingError || (!buildingId && !building)) {\r\n setCurrentState('noBuilding')\r\n return\r\n }\r\n\r\n // If we have a building but files are still loading, stay in opening state\r\n if (building && filesLoading) {\r\n setCurrentState('opening')\r\n return\r\n }\r\n\r\n // If we have a building but no point cloud files, show noPointCloudFiles state\r\n if (building && pointCloudFiles.length === 0) {\r\n setCurrentState('noPointCloudFiles')\r\n return\r\n }\r\n\r\n // We have point cloud files, hide the loading state\r\n if (building && pointCloudFiles.length > 0) {\r\n setCurrentState('ready')\r\n }\r\n }, [building, buildingLoading, buildingError, buildingId, filesLoading, pointCloudFiles.length])\r\n\r\n // Don't render if not visible\r\n if (!shouldShow) {\r\n return null\r\n }\r\n\r\n const handleBuildingSelect = (building: any) => {\r\n const url = new URL(window.location.href)\r\n url.searchParams.set('buildingId', building.id.toString())\r\n buildingDispatch({ type: 'SET-CURRENT-BUILDING', payload: { building } })\r\n setSelectedBuilding(building)\r\n setSearchTerm('')\r\n setSearchResults([])\r\n router.push(url.toString())\r\n }\r\n\r\n // Create point cloud entry\r\n async function createPointCloud(name: string): Promise<CreatePointCloudResponse> {\r\n const res = await fetch(`${API_BASE}/point-cloud`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ name }),\r\n })\r\n\r\n if (!res.ok) {\r\n const msg = await res.text()\r\n throw new Error(`Create point-cloud failed: ${msg || res.status}`)\r\n }\r\n\r\n return res.json()\r\n }\r\n\r\n // Upload to presigned URL\r\n function uploadToPresignedUrl(\r\n url: string,\r\n file: File,\r\n onProgress?: (pct: number) => void\r\n ): Promise<void> {\r\n return new Promise((resolve, reject) => {\r\n try {\r\n const xhr = new XMLHttpRequest()\r\n xhr.open('PUT', url, true)\r\n\r\n if (onProgress) {\r\n xhr.upload.onprogress = (evt) => {\r\n if (evt.lengthComputable) {\r\n const pct = Math.round((evt.loaded / evt.total) * 100)\r\n onProgress(pct)\r\n }\r\n }\r\n }\r\n\r\n xhr.onload = () => {\r\n if (xhr.status >= 200 && xhr.status < 300) resolve()\r\n else reject(new Error(`Upload failed with status ${xhr.status}`))\r\n }\r\n\r\n xhr.onerror = () => reject(new Error('Network error during upload'))\r\n xhr.send(file)\r\n } catch (err) {\r\n reject(err)\r\n }\r\n })\r\n }\r\n\r\n const handleFileChange = async (event: React.ChangeEvent<HTMLInputElement>) => {\r\n const files = event.target.files\r\n if (!files || files.length === 0) return\r\n\r\n const file = files[0]\r\n const fileName = file.name\r\n const fileExt = fileName.toLowerCase()\r\n\r\n // Validate file extension\r\n if (!fileExt.endsWith('.laz') && !fileExt.endsWith('.las')) {\r\n alert('Please select a LAZ or LAS file.')\r\n return\r\n }\r\n\r\n setUploading(true)\r\n\r\n try {\r\n console.log('Creating point cloud entry...')\r\n const { pointCloud, upload } = await createPointCloud(fileName)\r\n console.log('Point cloud created:', pointCloud.id)\r\n\r\n console.log('Uploading file...')\r\n await uploadToPresignedUrl(upload.uploadUrl, file, (pct) => {\r\n console.log(`Upload progress: ${pct}%`)\r\n })\r\n console.log('Upload complete!')\r\n\r\n // Trigger a refresh of the files - the state will update automatically\r\n window.location.reload()\r\n }\r\n catch (error) {\r\n console.error('Error uploading point cloud:', error)\r\n alert(\r\n error instanceof Error\r\n ? `Failed to upload: ${error.message}`\r\n : 'Failed to upload point cloud'\r\n )\r\n setUploading(false)\r\n }\r\n }\r\n\r\n const handleUploadClick = () => {\r\n fileInputRef.current?.click()\r\n }\r\n\r\n return (\r\n <>\r\n <div className=\"absolute z-10 inset-0 bg-background/50 backdrop-blur-[1px] flex items-center justify-center pointer-events-none\">\r\n <Card className={cn(\"w-[536px] pointer-events-auto flex flex-col justify-start items-center gap-2\")}>\r\n <CardHeader className=\"flex justify-center items-center\">\r\n <div className=\"w-12 h-12 p-2 bg-card rounded-md shadow-sm border border-border flex justify-center items-center\">\r\n {currentState === 'noBuilding' ? (\r\n <LR.Building2 className={cn(\"w-4 h-5 text-foreground\", buildingsLoading && \"animate-pulse\")} strokeWidth={2} />\r\n ) : (\r\n <LR.Grip className={cn(\"w-4 h-5 text-foreground\", currentState === 'opening' && \"animate-pulse\")} strokeWidth={2} />\r\n )}\r\n </div>\r\n </CardHeader>\r\n\r\n <CardContent className=\"self-stretch flex flex-col justify-start items-center gap-6 pt-0\">\r\n <div className=\"self-stretch flex flex-col justify-start items-center gap-2\">\r\n <div className=\"self-stretch text-center text-foreground text-xl font-semibold font-['Inter'] leading-7 flex items-center justify-center gap-2\">\r\n {currentState === 'opening' ? (\r\n <>\r\n {t('openingText')}\r\n <LoadingSpinner size={20} />\r\n </>\r\n ) : currentState === 'noBuilding' ? (\r\n <>\r\n {t('noBuilding')}\r\n </>\r\n ) : uploading ? (\r\n <>\r\n {t('uploading')}\r\n <LoadingSpinner size={20} />\r\n </>\r\n ) : (\r\n t('noPointCloudLoaded')\r\n )}\r\n </div>\r\n {(currentState === 'noPointCloudFiles' || currentState === 'noBuilding') && !uploading && (\r\n <div className=\"self-stretch text-center text-muted-foreground text-sm font-normal font-['Inter'] leading-tight\">\r\n {currentState === 'noBuilding' ? t('selectBuildingHelpText') : t('helpText')}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {currentState === 'noBuilding' && (\r\n <div className=\"self-stretch flex flex-col gap-3 justify-centre items-center\">\r\n {buildingsLoading ? (\r\n <div className=\"flex items-center justify-center gap-2 text-muted-foreground\">\r\n <LoadingSpinner size={16} />\r\n <span>{t('loadingBuildings') || 'Loading buildings...'}</span>\r\n </div>\r\n ) : buildings && buildings.length > 0 ? (\r\n <div className=\"w-full relative\">\r\n {selectedBuilding ? (\r\n <div className=\"relative flex justify-end items-center\">\r\n <Input\r\n type=\"text\"\r\n value={\r\n 'Building: '\r\n + (selectedBuilding.buildingName && selectedBuilding.buildingAddress\r\n ? `${selectedBuilding.buildingName}, ${selectedBuilding.buildingAddress}`\r\n : selectedBuilding.buildingName || selectedBuilding.buildingAddress || `Building ${selectedBuilding.id}`)\r\n }\r\n readOnly\r\n className=\"w-full\"\r\n />\r\n <LR.X \r\n className=\"absolute right-2 cursor-pointer w-4 h-4 text-muted-foreground hover:text-foreground\" \r\n onClick={() => {\r\n setSelectedBuilding(null)\r\n setSearchTerm('')\r\n setSearchResults([])\r\n }} \r\n />\r\n </div>\r\n ) : (\r\n <div className=\"relative\">\r\n <LR.Search className=\"absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground w-4 h-4\" />\r\n <Input\r\n placeholder={t('searchBuildings') || 'Search buildings...'}\r\n value={searchTerm}\r\n onChange={(e) => handleSearch(e.target.value)}\r\n className=\"w-full pl-10\"\r\n disabled={!ability.can('read', 'File')}\r\n />\r\n </div>\r\n )}\r\n \r\n {/* Search results dropdown */}\r\n {searchResults.length > 0 && searchTerm !== '' && !selectedBuilding && (\r\n <div className=\"absolute top-full left-0 right-0 mt-1 bg-background border border-border rounded-md shadow-lg max-h-60 overflow-y-auto z-20\">\r\n {searchResults.map(building => (\r\n <div\r\n key={building.id}\r\n className=\"px-3 py-2 text-sm cursor-pointer hover:bg-muted transition-colors flex gap-2 items-center\"\r\n onClick={() => handleBuildingSelect(building)}\r\n >\r\n <LR.SquareArrowOutUpRight className=\"w-3 h-3\" />\r\n {building.buildingName\r\n ? `${building.buildingName}, ${building.buildingAddress}`\r\n : building.buildingAddress || `Building ${building.id}`}\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n ) : (\r\n <div className=\"text-muted-foreground text-center\">\r\n {t('noBuildings') || 'No buildings available'}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n\r\n {currentState === 'noPointCloudFiles' && (\r\n <div className=\"self-stretch flex justify-center items-center gap-3\">\r\n <Input\r\n ref={fileInputRef}\r\n type=\"file\"\r\n accept=\".laz,.las\"\r\n onChange={handleFileChange}\r\n className=\"hidden\"\r\n />\r\n <Button\r\n onClick={handleUploadClick}\r\n size=\"sm\"\r\n className=\"gap-2\"\r\n disabled={uploading || !ability.can('create', 'File')}\r\n >\r\n <LR.Upload className=\"w-4 h-4\" />\r\n {uploading ? t('uploading') : t('uploadButton')}\r\n </Button>\r\n </div>\r\n )}\r\n </CardContent>\r\n </Card>\r\n </div>\r\n </>\r\n )\r\n}\r\n"],"mappings":"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","names":["_a","buildings","searchTerm","building","files"]}
1
+ {"version":3,"sources":["../../../../../../../src/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.tsx"],"sourcesContent":["'use client'\r\n\r\n// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport React from 'react'\r\nimport { Button } from '../../../../ui/Button'\r\nimport { Card, CardContent, CardHeader } from '../../../../ui/Card'\r\nimport { Input } from '../../../../ui/Input'\r\nimport { LoadingSpinner } from '../../../../ui/LoadingSpinner'\r\nimport * as LR from 'lucide-react'\r\nimport { BuildingsContext, usePermissions } from '../../../../../store'\r\n// import { useAppConfigContext } from '../../../../../store/AppConfig/context'\r\nimport { useTranslations } from 'next-intl'\r\nimport { useFilesByBuildingId } from '../../../../../hooks/files/files'\r\nimport type { DbFile } from '../../../../../types/dbTypes'\r\nimport { cn } from '../../../../../utils/utils'\r\nimport { useSearchParams, useRouter } from 'next/navigation'\r\nimport { useBuilding, useBuildings } from '../../../../../hooks/buildings/buildings'\r\nimport { SquareArrowOutUpRight } from 'lucide-react'\r\nimport { toast } from 'sonner'\r\n\r\ntype PresignedUploadResponse = {\r\n presignedUrl: string\r\n key: string\r\n}\r\n\r\nexport type pointCloudViewerState = 'opening' | 'loading' | 'ready' | 'error' | 'noPointCloudFiles' | 'noBuilding'\r\n\r\nexport function PointCloudLoadingState({ pointcloudApiUrl }: { pointcloudApiUrl?: string }) {\r\n const t = useTranslations('PointCloudLoadingState')\r\n const API_BASE = pointcloudApiUrl ?? 'http://localhost:5101'\r\n // Permissions\r\n const { ability } = usePermissions()\r\n\r\n const { state: buildingState, dispatch: buildingDispatch } = React.useContext(BuildingsContext)\r\n const { building: storeBuilding } = buildingState.buildings\r\n\r\n const searchParams = useSearchParams()\r\n const buildingId = searchParams.get(\"buildingId\")\r\n const { building: urlBuilding, isLoading: buildingLoading, isError: buildingError } = useBuilding(buildingId ? Number(buildingId) : 0)\r\n const { buildings, isLoading: buildingsLoading } = useBuildings()\r\n\r\n // Determine the current building to use\r\n const building = storeBuilding || urlBuilding\r\n\r\n React.useEffect(() => {\r\n if (!storeBuilding && urlBuilding) {\r\n buildingDispatch({ type: \"SET-CURRENT-BUILDING\", payload: { building: urlBuilding } })\r\n }\r\n }, [urlBuilding, storeBuilding, buildingDispatch])\r\n\r\n const router = useRouter()\r\n const fileInputRef = React.useRef<HTMLInputElement>(null)\r\n\r\n const [currentState, setCurrentState] = React.useState<pointCloudViewerState>('opening')\r\n const [uploading, setUploading] = React.useState(false)\r\n const [searchTerm, setSearchTerm] = React.useState('')\r\n const [searchResults, setSearchResults] = React.useState<any[]>([])\r\n const [selectedBuilding, setSelectedBuilding] = React.useState<any>(null)\r\n \r\n const { files, isLoading: filesLoading } = useFilesByBuildingId(building?.id)\r\n const pointCloudFiles: DbFile[] = React.useMemo(() => (\r\n files.filter(file => {\r\n const ext = file.extension?.toLowerCase()\r\n if (!ext) return false\r\n return ext === \"laz\" || ext === \"las\"\r\n })\r\n ), [files])\r\n\r\n // Fuzzy search function for buildings\r\n const fuzzySearchBuildings = React.useCallback((buildings: any[], searchTerm: string) => {\r\n if (!searchTerm.trim()) return buildings.slice(0, 10)\r\n \r\n const fuzzySearchScore = (search: string, text: string): number => {\r\n if (!search || !text) return 0\r\n const searchLower = search.toLowerCase()\r\n const textLower = text.toLowerCase()\r\n \r\n if (textLower === searchLower) return 1000\r\n if (textLower.startsWith(searchLower)) return 800\r\n if (textLower.includes(searchLower)) return 600\r\n \r\n return 0\r\n }\r\n\r\n return buildings\r\n .map((building) => {\r\n const address = building.buildingAddress || ''\r\n const name = building.buildingName || ''\r\n const id = building.id.toString()\r\n \r\n const addressScore = fuzzySearchScore(searchTerm, address)\r\n const nameScore = fuzzySearchScore(searchTerm, name)\r\n const idScore = fuzzySearchScore(searchTerm, id)\r\n \r\n const maxScore = Math.max(addressScore, nameScore, idScore)\r\n return { ...building, searchScore: maxScore }\r\n })\r\n .filter(building => building.searchScore > 0)\r\n .sort((a, b) => b.searchScore - a.searchScore)\r\n .slice(0, 10)\r\n }, [])\r\n\r\n // Handle search\r\n const handleSearch = React.useCallback((term: string) => {\r\n setSearchTerm(term)\r\n if (buildings) {\r\n setSearchResults(fuzzySearchBuildings(buildings, term))\r\n }\r\n }, [buildings, fuzzySearchBuildings])\r\n\r\n const shouldShow = (currentState === 'opening' || currentState === 'noPointCloudFiles' || currentState === 'noBuilding')\r\n\r\n // Handle initial state transitions\r\n React.useEffect(() => {\r\n // If building is loading, stay in opening state\r\n if (buildingLoading) {\r\n setCurrentState('opening')\r\n return\r\n }\r\n\r\n // If building failed to load or no buildingId provided, show noBuilding state\r\n if (buildingError || (!buildingId && !building)) {\r\n setCurrentState('noBuilding')\r\n return\r\n }\r\n\r\n // If we have a building but files are still loading, stay in opening state\r\n if (building && filesLoading) {\r\n setCurrentState('opening')\r\n return\r\n }\r\n\r\n // If we have a building but no point cloud files, show noPointCloudFiles state\r\n if (building && pointCloudFiles.length === 0) {\r\n setCurrentState('noPointCloudFiles')\r\n return\r\n }\r\n\r\n // We have point cloud files, hide the loading state\r\n if (building && pointCloudFiles.length > 0) {\r\n setCurrentState('ready')\r\n }\r\n }, [building, buildingLoading, buildingError, buildingId, filesLoading, pointCloudFiles.length])\r\n\r\n // Don't render if not visible\r\n if (!shouldShow) {\r\n return null\r\n }\r\n\r\n const handleBuildingSelect = (building: any) => {\r\n const url = new URL(window.location.href)\r\n url.searchParams.set('buildingId', building.id.toString())\r\n buildingDispatch({ type: 'SET-CURRENT-BUILDING', payload: { building } })\r\n setSelectedBuilding(building)\r\n setSearchTerm('')\r\n setSearchResults([])\r\n router.push(url.toString())\r\n }\r\n\r\n const handleFileChange = async (event: React.ChangeEvent<HTMLInputElement>) => {\r\n const files = event.target.files\r\n if (!files || files.length === 0) return\r\n\r\n const file = files[0]\r\n const fileName = file.name\r\n const fileExt = fileName.toLowerCase()\r\n\r\n if (!fileExt.endsWith('.laz') && !fileExt.endsWith('.las')) {\r\n alert('Please select a LAZ or LAS file.')\r\n return\r\n }\r\n\r\n if (!building?.id) {\r\n alert('No building selected.')\r\n return\r\n }\r\n\r\n setUploading(true)\r\n\r\n try {\r\n const assetId = crypto.randomUUID()\r\n const assetName = `${assetId}.laz`\r\n\r\n // 1. Get presigned upload URL\r\n const presignedRes = await fetch(\r\n `/api/presigned-url-upload?asset=${encodeURIComponent(assetName)}&bucket=pointclouds-demo`\r\n )\r\n if (!presignedRes.ok) throw new Error('Failed to get upload URL')\r\n const { presignedUrl, key }: PresignedUploadResponse = await presignedRes.json()\r\n\r\n // 2. Upload LAZ directly to MinIO\r\n const uploadRes = await fetch(presignedUrl, {\r\n method: 'PUT',\r\n body: file,\r\n headers: { 'Content-Type': 'application/octet-stream' },\r\n })\r\n if (!uploadRes.ok) throw new Error(`Upload failed: ${uploadRes.status}`)\r\n\r\n // 3. Create file record attached to the building\r\n const fileRes = await fetch(`/api/files/building/${building.id}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n type: 'point-cloud-file',\r\n name: fileName,\r\n assetId,\r\n lazFileKey: key,\r\n bucket: 'pointclouds-demo',\r\n extension: fileExt.endsWith('.las') ? 'las' : 'laz',\r\n }),\r\n })\r\n if (!fileRes.ok) throw new Error('Failed to create file record')\r\n\r\n toast.success('LAZ file uploaded successfully', { duration: 6000 })\r\n window.location.reload()\r\n }\r\n catch (error) {\r\n console.error('Error uploading point cloud:', error)\r\n alert(\r\n error instanceof Error\r\n ? `Failed to upload: ${error.message}`\r\n : 'Failed to upload point cloud'\r\n )\r\n setUploading(false)\r\n }\r\n }\r\n\r\n const handleUploadClick = () => {\r\n fileInputRef.current?.click()\r\n }\r\n\r\n return (\r\n <>\r\n <div className=\"absolute z-10 inset-0 bg-background/50 backdrop-blur-[1px] flex items-center justify-center pointer-events-none\">\r\n <Card className={cn(\"w-[536px] pointer-events-auto flex flex-col justify-start items-center gap-2\")}>\r\n <CardHeader className=\"flex justify-center items-center\">\r\n <div className=\"w-12 h-12 p-2 bg-card rounded-md shadow-sm border border-border flex justify-center items-center\">\r\n {currentState === 'noBuilding' ? (\r\n <LR.Building2 className={cn(\"w-4 h-5 text-foreground\", buildingsLoading && \"animate-pulse\")} strokeWidth={2} />\r\n ) : (\r\n <LR.Grip className={cn(\"w-4 h-5 text-foreground\", currentState === 'opening' && \"animate-pulse\")} strokeWidth={2} />\r\n )}\r\n </div>\r\n </CardHeader>\r\n\r\n <CardContent className=\"self-stretch flex flex-col justify-start items-center gap-6 pt-0\">\r\n <div className=\"self-stretch flex flex-col justify-start items-center gap-2\">\r\n <div className=\"self-stretch text-center text-foreground text-xl font-semibold font-['Inter'] leading-7 flex items-center justify-center gap-2\">\r\n {currentState === 'opening' ? (\r\n <>\r\n {t('openingText')}\r\n <LoadingSpinner size={20} />\r\n </>\r\n ) : currentState === 'noBuilding' ? (\r\n <>\r\n {t('noBuilding')}\r\n </>\r\n ) : uploading ? (\r\n <>\r\n {t('uploading')}\r\n <LoadingSpinner size={20} />\r\n </>\r\n ) : (\r\n t('noPointCloudLoaded')\r\n )}\r\n </div>\r\n {(currentState === 'noPointCloudFiles' || currentState === 'noBuilding') && !uploading && (\r\n <div className=\"self-stretch text-center text-muted-foreground text-sm font-normal font-['Inter'] leading-tight\">\r\n {currentState === 'noBuilding' ? t('selectBuildingHelpText') : t('helpText')}\r\n </div>\r\n )}\r\n </div>\r\n\r\n {currentState === 'noBuilding' && (\r\n <div className=\"self-stretch flex flex-col gap-3 justify-centre items-center\">\r\n {buildingsLoading ? (\r\n <div className=\"flex items-center justify-center gap-2 text-muted-foreground\">\r\n <LoadingSpinner size={16} />\r\n <span>{t('loadingBuildings') || 'Loading buildings...'}</span>\r\n </div>\r\n ) : buildings && buildings.length > 0 ? (\r\n <div className=\"w-full relative\">\r\n {selectedBuilding ? (\r\n <div className=\"relative flex justify-end items-center\">\r\n <Input\r\n type=\"text\"\r\n value={\r\n 'Building: '\r\n + (selectedBuilding.buildingName && selectedBuilding.buildingAddress\r\n ? `${selectedBuilding.buildingName}, ${selectedBuilding.buildingAddress}`\r\n : selectedBuilding.buildingName || selectedBuilding.buildingAddress || `Building ${selectedBuilding.id}`)\r\n }\r\n readOnly\r\n className=\"w-full\"\r\n />\r\n <LR.X \r\n className=\"absolute right-2 cursor-pointer w-4 h-4 text-muted-foreground hover:text-foreground\" \r\n onClick={() => {\r\n setSelectedBuilding(null)\r\n setSearchTerm('')\r\n setSearchResults([])\r\n }} \r\n />\r\n </div>\r\n ) : (\r\n <div className=\"relative\">\r\n <LR.Search className=\"absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground w-4 h-4\" />\r\n <Input\r\n placeholder={t('searchBuildings') || 'Search buildings...'}\r\n value={searchTerm}\r\n onChange={(e) => handleSearch(e.target.value)}\r\n className=\"w-full pl-10\"\r\n disabled={!ability.can('read', 'File')}\r\n />\r\n </div>\r\n )}\r\n \r\n {/* Search results dropdown */}\r\n {searchResults.length > 0 && searchTerm !== '' && !selectedBuilding && (\r\n <div className=\"absolute top-full left-0 right-0 mt-1 bg-background border border-border rounded-md shadow-lg max-h-60 overflow-y-auto z-20\">\r\n {searchResults.map(building => (\r\n <div\r\n key={building.id}\r\n className=\"px-3 py-2 text-sm cursor-pointer hover:bg-muted transition-colors flex gap-2 items-center\"\r\n onClick={() => handleBuildingSelect(building)}\r\n >\r\n <LR.SquareArrowOutUpRight className=\"w-3 h-3\" />\r\n {building.buildingName\r\n ? `${building.buildingName}, ${building.buildingAddress}`\r\n : building.buildingAddress || `Building ${building.id}`}\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n ) : (\r\n <div className=\"text-muted-foreground text-center\">\r\n {t('noBuildings') || 'No buildings available'}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n\r\n {currentState === 'noPointCloudFiles' && (\r\n <div className=\"self-stretch flex justify-center items-center gap-3\">\r\n <Input\r\n ref={fileInputRef}\r\n type=\"file\"\r\n accept=\".laz,.las\"\r\n onChange={handleFileChange}\r\n className=\"hidden\"\r\n />\r\n <Button\r\n onClick={handleUploadClick}\r\n size=\"sm\"\r\n className=\"gap-2\"\r\n disabled={uploading || !ability.can('create', 'File')}\r\n >\r\n <LR.Upload className=\"w-4 h-4\" />\r\n {uploading ? t('uploading') : t('uploadButton')}\r\n </Button>\r\n </div>\r\n )}\r\n </CardContent>\r\n </Card>\r\n </div>\r\n </>\r\n )\r\n}\r\n"],"mappings":"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","names":["buildings","searchTerm","building","files"]}