@collabdt/core 0.0.57 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -43
- package/dist/core/components/AppSidebarContent.d.ts.map +1 -1
- package/dist/core/components/AppSidebarContent.js +21 -27
- package/dist/core/components/AppSidebarContent.js.map +1 -1
- package/dist/core/components/authentication/AuthPage.d.ts.map +1 -1
- package/dist/core/components/authentication/AuthPage.js +32 -7
- package/dist/core/components/authentication/AuthPage.js.map +1 -1
- package/dist/core/components/authentication/SignInAuthHeader.d.ts +7 -0
- package/dist/core/components/authentication/SignInAuthHeader.d.ts.map +1 -0
- package/dist/core/components/authentication/SignInAuthHeader.js +32 -0
- package/dist/core/components/authentication/SignInAuthHeader.js.map +1 -0
- package/dist/core/components/authentication/Signin.d.ts.map +1 -1
- package/dist/core/components/authentication/Signin.js +308 -10
- package/dist/core/components/authentication/Signin.js.map +1 -1
- package/dist/core/components/settings/index.js +3 -3
- package/dist/core/components/settings/index.js.map +1 -1
- package/dist/core/components/settings/src/OrganizationSettingsPanel.d.ts.map +1 -1
- package/dist/core/components/settings/src/OrganizationSettingsPanel.js +5 -4
- package/dist/core/components/settings/src/OrganizationSettingsPanel.js.map +1 -1
- package/dist/core/components/settings/src/SettingsSidebar.d.ts.map +1 -1
- package/dist/core/components/settings/src/SettingsSidebar.js +12 -2
- package/dist/core/components/settings/src/SettingsSidebar.js.map +1 -1
- package/dist/core/components/support/BugReportDialog.d.ts.map +1 -1
- package/dist/core/components/support/BugReportDialog.js +122 -9
- package/dist/core/components/support/BugReportDialog.js.map +1 -1
- package/dist/core/components/support/FeatureRequestDialog.d.ts.map +1 -1
- package/dist/core/components/support/FeatureRequestDialog.js +61 -2
- package/dist/core/components/support/FeatureRequestDialog.js.map +1 -1
- package/dist/core/components/ui/AppSidebar.d.ts.map +1 -1
- package/dist/core/components/ui/AppSidebar.js +52 -9
- package/dist/core/components/ui/AppSidebar.js.map +1 -1
- package/dist/core/components/ui/DataTable.js +3 -3
- package/dist/core/components/ui/DataTable.js.map +1 -1
- package/dist/core/components/ui/Dialog.js +1 -1
- package/dist/core/components/ui/Dialog.js.map +1 -1
- package/dist/core/components/ui/InfoSidebar/Container.js +1 -1
- package/dist/core/components/ui/InfoSidebar/Container.js.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js +2 -2
- package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/SensorsSection.js +2 -1
- package/dist/core/components/ui/Sensors/SensorsSection.js.map +1 -1
- package/dist/core/components/ui/Sensors/sensorUtils.d.ts +9 -0
- package/dist/core/components/ui/Sensors/sensorUtils.d.ts.map +1 -1
- package/dist/core/components/ui/Sensors/sensorUtils.js +6 -0
- package/dist/core/components/ui/Sensors/sensorUtils.js.map +1 -1
- package/dist/core/components/ui/Sidebar.d.ts +4 -0
- package/dist/core/components/ui/Sidebar.d.ts.map +1 -1
- package/dist/core/components/ui/Sidebar.js +11 -5
- package/dist/core/components/ui/Sidebar.js.map +1 -1
- package/dist/core/components/ui/Table.js +2 -2
- package/dist/core/components/ui/Table.js.map +1 -1
- package/dist/core/components/viewers/Data/DataMenu.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/DataMenu.js +17 -9
- package/dist/core/components/viewers/Data/DataMenu.js.map +1 -1
- package/dist/core/components/viewers/Data/HeaderButtons.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/HeaderButtons.js +5 -12
- package/dist/core/components/viewers/Data/HeaderButtons.js.map +1 -1
- package/dist/core/components/viewers/Data/MoreOptions.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/MoreOptions.js +14 -27
- package/dist/core/components/viewers/Data/MoreOptions.js.map +1 -1
- package/dist/core/components/viewers/Data/buildingDetails/BuildingDetails.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/buildingDetails/BuildingDetails.js +26 -0
- package/dist/core/components/viewers/Data/buildingDetails/BuildingDetails.js.map +1 -1
- package/dist/core/components/viewers/Data/buildingDetails/FieldRenderer.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/buildingDetails/FieldRenderer.js +13 -18
- package/dist/core/components/viewers/Data/buildingDetails/FieldRenderer.js.map +1 -1
- package/dist/core/components/viewers/Data/details/DetailHeader.js +14 -14
- package/dist/core/components/viewers/Data/details/DetailHeader.js.map +1 -1
- package/dist/core/components/viewers/Data/details/FieldValue.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/details/FieldValue.js +13 -3
- package/dist/core/components/viewers/Data/details/FieldValue.js.map +1 -1
- package/dist/core/components/viewers/Data/details/TabSidebar.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/details/TabSidebar.js +22 -0
- package/dist/core/components/viewers/Data/details/TabSidebar.js.map +1 -1
- package/dist/core/components/viewers/Data/infrastructureDetails/InfrastructureDetails.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/infrastructureDetails/InfrastructureDetails.js +19 -0
- package/dist/core/components/viewers/Data/infrastructureDetails/InfrastructureDetails.js.map +1 -1
- package/dist/core/components/viewers/Data/siteDetails/AssociatedBuildings.d.ts +2 -0
- package/dist/core/components/viewers/Data/siteDetails/AssociatedBuildings.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/siteDetails/AssociatedBuildings.js +84 -3
- package/dist/core/components/viewers/Data/siteDetails/AssociatedBuildings.js.map +1 -1
- package/dist/core/components/viewers/Data/siteDetails/SiteDetails.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/siteDetails/SiteDetails.js +42 -2
- package/dist/core/components/viewers/Data/siteDetails/SiteDetails.js.map +1 -1
- package/dist/core/components/viewers/Data/utils/Columns.js +2 -2
- package/dist/core/components/viewers/Data/utils/Columns.js.map +1 -1
- package/dist/core/components/viewers/Data/utils/positionParser.d.ts.map +1 -1
- package/dist/core/components/viewers/Data/utils/positionParser.js +6 -5
- package/dist/core/components/viewers/Data/utils/positionParser.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.js +8 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/CommunicationTab/src/TopicsSection.js.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.d.ts.map +1 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.js +7 -1
- package/dist/core/components/viewers/bim/src/BimSidebar/src/FileTab/src/FilesSection.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/RowActions.js +6 -6
- package/dist/core/components/viewers/map/datasets/RowActions.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/data.js +1 -1
- package/dist/core/components/viewers/map/datasets/data.js.map +1 -1
- package/dist/core/components/viewers/map/datasets/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/datasets/index.js +28 -9
- package/dist/core/components/viewers/map/datasets/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/index.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/index.js +2 -0
- package/dist/core/components/viewers/map/src/MapLayers/index.js.map +1 -1
- package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/SiteContextMenu.d.ts +19 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/SiteContextMenu.d.ts.map +1 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/SiteContextMenu.js +163 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/SiteContextMenu.js.map +1 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/SiteLayer.d.ts +3 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/SiteLayer.d.ts.map +1 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/SiteLayer.js +310 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/SiteLayer.js.map +1 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/siteGeometry.d.ts +30 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/siteGeometry.d.ts.map +1 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/siteGeometry.js +122 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/siteGeometry.js.map +1 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/useShowSitesOnMap.d.ts +13 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/useShowSitesOnMap.d.ts.map +1 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/useShowSitesOnMap.js +58 -0
- package/dist/core/components/viewers/map/src/MapLayers/src/SiteLayer/useShowSitesOnMap.js.map +1 -0
- package/dist/core/components/viewers/map/src/tools/AddTools/AddSite/AssociateBuildingsDialog.d.ts +18 -0
- package/dist/core/components/viewers/map/src/tools/AddTools/AddSite/AssociateBuildingsDialog.d.ts.map +1 -0
- package/dist/core/components/viewers/map/src/tools/AddTools/AddSite/AssociateBuildingsDialog.js +102 -0
- package/dist/core/components/viewers/map/src/tools/AddTools/AddSite/AssociateBuildingsDialog.js.map +1 -0
- package/dist/core/components/viewers/map/src/tools/AddTools/AddSite/SiteAdder.d.ts.map +1 -1
- package/dist/core/components/viewers/map/src/tools/AddTools/AddSite/SiteAdder.js +243 -176
- package/dist/core/components/viewers/map/src/tools/AddTools/AddSite/SiteAdder.js.map +1 -1
- package/dist/core/components/viewers/map/src/tools/AddTools/AddSite/persistSite.d.ts +24 -0
- package/dist/core/components/viewers/map/src/tools/AddTools/AddSite/persistSite.d.ts.map +1 -0
- package/dist/core/components/viewers/map/src/tools/AddTools/AddSite/persistSite.js +46 -0
- package/dist/core/components/viewers/map/src/tools/AddTools/AddSite/persistSite.js.map +1 -0
- package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.d.ts +1 -0
- package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.d.ts.map +1 -1
- package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.js +1 -0
- package/dist/core/components/viewers/map/utils/MapEventManager/MapClickManager.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.js +54 -31
- package/dist/core/components/viewers/pointcloud/src/PointCloudLoadingState/index.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/index.js +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/index.js.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.d.ts +2 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.d.ts.map +1 -1
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.js +5 -4
- package/dist/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.js.map +1 -1
- package/dist/core/hooks/sites/createSiteHooks.d.ts.map +1 -1
- package/dist/core/hooks/sites/createSiteHooks.js +0 -1
- package/dist/core/hooks/sites/createSiteHooks.js.map +1 -1
- package/dist/core/hooks/useCaptureScreenshot.d.ts.map +1 -1
- package/dist/core/hooks/useCaptureScreenshot.js +111 -33
- package/dist/core/hooks/useCaptureScreenshot.js.map +1 -1
- package/dist/core/hooks/useCurrentViewerPosition.d.ts +18 -0
- package/dist/core/hooks/useCurrentViewerPosition.d.ts.map +1 -0
- package/dist/core/hooks/useCurrentViewerPosition.js +66 -0
- package/dist/core/hooks/useCurrentViewerPosition.js.map +1 -0
- package/dist/core/hooks/useShareUrl.d.ts +2 -0
- package/dist/core/hooks/useShareUrl.d.ts.map +1 -0
- package/dist/core/hooks/useShareUrl.js +74 -0
- package/dist/core/hooks/useShareUrl.js.map +1 -0
- package/dist/core/store/CombineProviders.d.ts.map +1 -1
- package/dist/core/store/CombineProviders.js +2 -0
- package/dist/core/store/CombineProviders.js.map +1 -1
- package/dist/core/store/MapSites/context.d.ts +16 -0
- package/dist/core/store/MapSites/context.d.ts.map +1 -0
- package/dist/core/store/MapSites/context.js +29 -0
- package/dist/core/store/MapSites/context.js.map +1 -0
- package/dist/core/store/MapSites/reducer.d.ts +52 -0
- package/dist/core/store/MapSites/reducer.d.ts.map +1 -0
- package/dist/core/store/MapSites/reducer.js +70 -0
- package/dist/core/store/MapSites/reducer.js.map +1 -0
- package/dist/core/store/index.d.ts +1 -0
- package/dist/core/store/index.d.ts.map +1 -1
- package/dist/core/store/index.js +1 -0
- package/dist/core/store/index.js.map +1 -1
- package/dist/core/styles/globals.css +46 -33
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../../../../src/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.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 LR from 'lucide-react'\r\nimport { useTranslations } from 'next-intl'\r\nimport { useSession } from 'next-auth/react'\r\nimport { CollapsibleSection } from '../../../../../../../ui/CollapsibleSection'\r\nimport { FileItemComponent } from '../../../../../../../ui/FilesManager/src/FileItemComponent'\r\nimport { useFileActions } from '../../../../../../../ui/FilesManager/src/useFileActions'\r\nimport ConfirmDialog from '../../../../../../../ConfirmDialog'\r\nimport { Button } from '../../../../../../../ui/Button'\r\nimport { DbFile } from '../../../../../../../../types/dbTypes'\r\nimport { PointCloudContext } from '../../../../../../../../store'\r\n// import { useAppConfigContext } from '../../../../../../../../store/AppConfig/context'\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\ntype StartConversionResponse = {\r\n jobId: string\r\n}\r\n\r\nconst POINT_CLOUD_OPTIONS: import('../../../../../../../../types/global').FileAction[] = ['view', 'delete', 'info']\r\n\r\ninterface PointCloudsSectionProps {\r\n files: DbFile[]\r\n pointcloudApiUrl?: string\r\n}\r\n\r\nexport function PointCloudsSection({ files, pointcloudApiUrl }: PointCloudsSectionProps) {\r\n // Translation\r\n const t = useTranslations('PointCloudManagement')\r\n const API_BASE = pointcloudApiUrl ?? 'http://localhost:5101'\r\n\r\n // Get current user session (needed for uploadedBy)\r\n const { data: session } = useSession()\r\n\r\n // Point cloud context for loading/unloading clouds in the viewer\r\n const { dispatch: pointCloudDispatch } = React.useContext(PointCloudContext)\r\n\r\n // State for point clouds — includes isVisible to track which are loaded in viewer\r\n const [pointcloudsFiles, setPointcloudsFiles] = React.useState<(DbFile & { isVisible?: boolean })[]>(\r\n () => files.map(f => ({ ...f, isVisible: false }))\r\n )\r\n const esRef = React.useRef<EventSource | null>(null)\r\n const autoLoadedRef = React.useRef(false)\r\n\r\n const fileSyncKey = files\r\n .map(\r\n (file) =>\r\n `${file.id}:${file.name}:${Boolean(file.pointCloudUploaded)}:${Boolean(file.pointCloudPotreeConverted)}:${file.uploadedAt}:${file.potreeMetadataFileKey ?? ''}`\r\n )\r\n .join('|')\r\n\r\n // Sync incoming files from props while preserving isVisible state for known files\r\n React.useEffect(() => {\r\n setPointcloudsFiles(prev => {\r\n const prevMap = new Map(prev.map(f => [String(f.id), f]))\r\n return files.map(f => ({\r\n ...f,\r\n isVisible: prevMap.get(String(f.id))?.isVisible ?? false,\r\n }))\r\n })\r\n }, [fileSyncKey])\r\n\r\n // Auto-load the most recently uploaded converted file on first access\r\n React.useEffect(() => {\r\n if (autoLoadedRef.current || files.length === 0) return\r\n\r\n const readyFiles = files.filter(f => Boolean(f.pointCloudPotreeConverted))\r\n if (readyFiles.length === 0) return\r\n\r\n const lastFile = readyFiles.reduce((latest, f) =>\r\n new Date(f.uploadedAt) > new Date(latest.uploadedAt) ? f : latest\r\n )\r\n\r\n autoLoadedRef.current = true\r\n pointCloudDispatch({ type: 'LOAD_POINT_CLOUD', payload: { id: String(lastFile.id) } })\r\n setPointcloudsFiles(prev =>\r\n prev.map(f => ({ ...f, isVisible: String(f.id) === String(lastFile.id) }))\r\n )\r\n }, [fileSyncKey, files, pointCloudDispatch])\r\n\r\n React.useEffect(() => {\r\n // Cleanup SSE on unmount\r\n return () => {\r\n if (esRef.current) {\r\n esRef.current.close()\r\n }\r\n }\r\n }, [])\r\n\r\n // Toggle a point cloud on/off in the viewer via context\r\n const handleView = React.useCallback((file: DbFile, newVisibility: boolean) => {\r\n const id = String(file.id)\r\n if (newVisibility) {\r\n pointCloudDispatch({ type: 'LOAD_POINT_CLOUD', payload: { id } })\r\n } else {\r\n pointCloudDispatch({ type: 'UNLOAD_POINT_CLOUD', payload: { id } })\r\n }\r\n }, [pointCloudDispatch])\r\n\r\n const handleDeleteFile = React.useCallback(async (file: DbFile) => {\r\n const res = await fetch(`${API_BASE}/point-cloud/${encodeURIComponent(String(file.id))}`, {\r\n method: 'DELETE',\r\n })\r\n\r\n if (!res.ok) {\r\n const msg = await res.text()\r\n throw new Error(msg || `Delete failed (${res.status})`)\r\n }\r\n }, [])\r\n\r\n const handleConvert = React.useCallback(async (file: DbFile) => {\r\n const res = await fetch(\r\n `${API_BASE}/point-cloud/${encodeURIComponent(String(file.id))}/convert-to-potree`,\r\n {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ options: { sampling_method: 'poisson' } }),\r\n }\r\n )\r\n\r\n if (!res.ok) {\r\n const msg = await res.text()\r\n throw new Error(msg || `Conversion failed (${res.status})`)\r\n }\r\n\r\n const { jobId } = await res.json() as StartConversionResponse\r\n subscribeToProgress(jobId, file.id)\r\n }, [])\r\n\r\n const handleDeletePointCloud = React.useCallback((file: DbFile) => {\r\n pointCloudDispatch({ type: 'UNLOAD_POINT_CLOUD', payload: { id: String(file.id) } })\r\n }, [pointCloudDispatch])\r\n\r\n // Use the generic file actions hook\r\n const { handleAction, deleteDialog } = useFileActions({\r\n files: pointcloudsFiles,\r\n setFiles: setPointcloudsFiles as React.Dispatch<React.SetStateAction<(DbFile & { isVisible?: boolean })[]>>,\r\n buildingId: 0,\r\n handleDeleteFile,\r\n onView: handleView,\r\n onDelete: handleDeletePointCloud,\r\n })\r\n\r\n // Sort: visible (loaded) files at the top, preserving relative order within each group\r\n const sortedFiles = React.useMemo(() => {\r\n return [...pointcloudsFiles].sort((a, b) => {\r\n const aLoaded = a.isVisible !== false ? 1 : 0\r\n const bLoaded = b.isVisible !== false ? 1 : 0\r\n return bLoaded - aLoaded\r\n })\r\n }, [pointcloudsFiles])\r\n\r\n // Create point cloud entry — routed through the CDT proxy so that\r\n // organizationId is injected server-side from the authenticated session.\r\n async function createPointCloud(name: string, uploadedBy: string | null): Promise<CreatePointCloudResponse> {\r\n const res = await fetch('/api/point-cloud', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ name, uploadedBy }),\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 // Start conversion\r\n async function startConversion(pointCloudId: string): Promise<StartConversionResponse> {\r\n const res = await fetch(\r\n `${API_BASE}/point-cloud/${encodeURIComponent(pointCloudId)}/convert-to-potree`,\r\n {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ options: { sampling_method: 'poisson' } }),\r\n }\r\n )\r\n\r\n if (!res.ok) {\r\n const msg = await res.text()\r\n throw new Error(`convert-to-potree failed: ${msg || res.status}`)\r\n }\r\n\r\n return res.json()\r\n }\r\n\r\n // Subscribe to conversion progress\r\n function subscribeToProgress(jobId: string, pointCloudId: number) {\r\n if (!jobId) return\r\n\r\n if (esRef.current) {\r\n esRef.current.close()\r\n }\r\n\r\n const es = new EventSource(\r\n `${API_BASE}/events/convert-progress/${encodeURIComponent(jobId)}`\r\n )\r\n esRef.current = es\r\n\r\n es.onopen = () => console.log('SSE connection opened')\r\n\r\n es.onerror = (event) => {\r\n console.error('SSE error:', event)\r\n // Update to failed state\r\n setPointcloudsFiles((prev) =>\r\n prev.map((pc) =>\r\n Number(pc.id) === pointCloudId\r\n ? { ...pc, status: 'idle', conversionProgress: 0 }\r\n : pc\r\n )\r\n )\r\n }\r\n\r\n es.addEventListener('progress', (event) => {\r\n try {\r\n const msgEvent = event as MessageEvent\r\n const data = JSON.parse(msgEvent.data)\r\n\r\n console.log('[progress]', data.status, data.message, data.progress)\r\n\r\n if (typeof data.progress === 'number') {\r\n setPointcloudsFiles((prev) =>\r\n prev.map((pc) =>\r\n Number(pc.id) === pointCloudId\r\n ? { ...pc, conversionProgress: data.progress }\r\n : pc\r\n )\r\n )\r\n }\r\n } catch (err) {\r\n console.error('Failed to parse progress event', err)\r\n }\r\n })\r\n\r\n es.addEventListener('finished', () => {\r\n console.log('[finished] Conversion completed')\r\n setPointcloudsFiles((prev) =>\r\n prev.map((pc) =>\r\n Number(pc.id) === pointCloudId\r\n ? { ...pc, status: 'idle', conversionProgress: 100 }\r\n : pc\r\n )\r\n )\r\n es.close()\r\n esRef.current = null\r\n })\r\n\r\n es.addEventListener('failed', () => {\r\n console.log('[failed] Conversion failed')\r\n setPointcloudsFiles((prev) =>\r\n prev.map((pc) =>\r\n Number(pc.id) === pointCloudId\r\n ? { ...pc, status: 'idle', conversionProgress: 0 }\r\n : pc\r\n )\r\n )\r\n es.close()\r\n esRef.current = null\r\n })\r\n }\r\n\r\n // Handle file upload\r\n const handleFileUpload = async (file: File) => {\r\n if (!file) return\r\n\r\n const originalFileName = file.name\r\n const fileExt = originalFileName.toLowerCase()\r\n const baseFileName = originalFileName.replace(/\\.(laz|las)$/i, '')\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 // Check for duplicate names and append (1), (2), etc.\r\n let fileName = baseFileName\r\n let counter = 1\r\n const existingNames = pointcloudsFiles.map((pc) => pc.name)\r\n \r\n while (existingNames.includes(fileName)) {\r\n fileName = `${baseFileName} (${counter})`\r\n counter++\r\n }\r\n\r\n try {\r\n console.log('Creating point cloud entry...')\r\n const userEmail = session?.user?.email || null\r\n const { pointCloud, upload } = await createPointCloud(fileName, userEmail)\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 console.log('Starting conversion...')\r\n const conversion = await startConversion(pointCloud.id)\r\n console.log('Conversion started:', conversion.jobId)\r\n\r\n // Subscribe to progress\r\n subscribeToProgress(conversion.jobId, Number(pointCloud.id))\r\n } catch (error) {\r\n console.error('Error uploading point cloud:', error)\r\n \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 }\r\n }\r\n\r\n // Handle upload button click\r\n const handleUpload = () => {\r\n const input = document.createElement('input')\r\n input.type = 'file'\r\n input.accept = '.laz,.las'\r\n input.onchange = (e) => {\r\n const file = (e.target as HTMLInputElement).files?.[0]\r\n if (file) {\r\n handleFileUpload(file)\r\n }\r\n }\r\n input.click()\r\n }\r\n\r\n return (\r\n <>\r\n <CollapsibleSection\r\n title={t('title')}\r\n icon={LR.Grip}\r\n className=\"h-1/2 overflow-y-auto\"\r\n itemCount={pointcloudsFiles.length}\r\n onAddItem={handleUpload}\r\n addItemTitle={t('uploadTitle')}\r\n >\r\n <div className=\"space-y-1\">\r\n {sortedFiles.length === 0 ? (\r\n <div className=\"px-2 py-3 text-sm text-muted-foreground text-center\">\r\n {t('noPointClouds')}\r\n </div>\r\n ) : (\r\n sortedFiles.map((file) => (\r\n <div key={file.id} className=\"flex items-center gap-1\">\r\n <div className=\"flex-1 min-w-0\">\r\n <FileItemComponent\r\n file={file}\r\n onAction={handleAction}\r\n options={POINT_CLOUD_OPTIONS}\r\n />\r\n </div>\r\n {Boolean(file.pointCloudUploaded) && !Boolean(file.pointCloudPotreeConverted) && (\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n className=\"h-6 w-6 p-0\"\r\n onClick={() => void handleConvert(file)}\r\n title={t('convertTitle')}\r\n >\r\n <LR.RefreshCw className=\"h-3 w-3\" />\r\n </Button>\r\n )}\r\n </div>\r\n ))\r\n )}\r\n </div>\r\n </CollapsibleSection>\r\n\r\n <ConfirmDialog\r\n isOpen={deleteDialog.isOpen}\r\n isDeleting={deleteDialog.isDeleting}\r\n onOpenChange={deleteDialog.onOpenChange}\r\n handleConfirm={deleteDialog.onConfirm}\r\n itemName={deleteDialog.itemName}\r\n dataType=\"file\"\r\n />\r\n </>\r\n )\r\n}\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../../../../src/core/components/viewers/pointcloud/src/PointCloudSidebar/src/FileTab/src/PointCloudSection.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 LR from 'lucide-react'\r\nimport { useTranslations } from 'next-intl'\r\nimport { useSession } from 'next-auth/react'\r\nimport { CollapsibleSection } from '../../../../../../../ui/CollapsibleSection'\r\nimport { FileItemComponent } from '../../../../../../../ui/FilesManager/src/FileItemComponent'\r\nimport { useFileActions } from '../../../../../../../ui/FilesManager/src/useFileActions'\r\nimport ConfirmDialog from '../../../../../../../ConfirmDialog'\r\nimport { Button } from '../../../../../../../ui/Button'\r\nimport { DbFile } from '../../../../../../../../types/dbTypes'\r\nimport { PointCloudContext } from '../../../../../../../../store'\r\n// import { useAppConfigContext } from '../../../../../../../../store/AppConfig/context'\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\ntype StartConversionResponse = {\r\n jobId: string\r\n}\r\n\r\nconst POINT_CLOUD_OPTIONS: import('../../../../../../../../types/global').FileAction[] = ['view', 'delete', 'info']\r\n\r\ninterface PointCloudsSectionProps {\r\n files: DbFile[]\r\n pointcloudApiUrl?: string\r\n buildingId?: number\r\n}\r\n\r\nexport function PointCloudsSection({ files, pointcloudApiUrl, buildingId }: PointCloudsSectionProps) {\r\n // Translation\r\n const t = useTranslations('PointCloudManagement')\r\n const API_BASE = pointcloudApiUrl ?? 'http://localhost:5101'\r\n\r\n // Get current user session (needed for uploadedBy)\r\n const { data: session } = useSession()\r\n\r\n // Point cloud context for loading/unloading clouds in the viewer\r\n const { dispatch: pointCloudDispatch } = React.useContext(PointCloudContext)\r\n\r\n // State for point clouds — includes isVisible to track which are loaded in viewer\r\n const [pointcloudsFiles, setPointcloudsFiles] = React.useState<(DbFile & { isVisible?: boolean })[]>(\r\n () => files.map(f => ({ ...f, isVisible: false }))\r\n )\r\n const esRef = React.useRef<EventSource | null>(null)\r\n const autoLoadedRef = React.useRef(false)\r\n\r\n const fileSyncKey = files\r\n .map(\r\n (file) =>\r\n `${file.id}:${file.name}:${Boolean(file.pointCloudUploaded)}:${Boolean(file.pointCloudPotreeConverted)}:${file.uploadedAt}:${file.potreeMetadataFileKey ?? ''}`\r\n )\r\n .join('|')\r\n\r\n // Sync incoming files from props while preserving isVisible state for known files\r\n React.useEffect(() => {\r\n setPointcloudsFiles(prev => {\r\n const prevMap = new Map(prev.map(f => [String(f.id), f]))\r\n return files.map(f => ({\r\n ...f,\r\n isVisible: prevMap.get(String(f.id))?.isVisible ?? false,\r\n }))\r\n })\r\n }, [fileSyncKey])\r\n\r\n // Auto-load the most recently uploaded converted file on first access\r\n React.useEffect(() => {\r\n if (autoLoadedRef.current || files.length === 0) return\r\n\r\n const readyFiles = files.filter(f => Boolean(f.pointCloudPotreeConverted))\r\n if (readyFiles.length === 0) return\r\n\r\n const lastFile = readyFiles.reduce((latest, f) =>\r\n new Date(f.uploadedAt) > new Date(latest.uploadedAt) ? f : latest\r\n )\r\n\r\n autoLoadedRef.current = true\r\n pointCloudDispatch({ type: 'LOAD_POINT_CLOUD', payload: { id: String(lastFile.id) } })\r\n setPointcloudsFiles(prev =>\r\n prev.map(f => ({ ...f, isVisible: String(f.id) === String(lastFile.id) }))\r\n )\r\n }, [fileSyncKey, files, pointCloudDispatch])\r\n\r\n React.useEffect(() => {\r\n // Cleanup SSE on unmount\r\n return () => {\r\n if (esRef.current) {\r\n esRef.current.close()\r\n }\r\n }\r\n }, [])\r\n\r\n // Toggle a point cloud on/off in the viewer via context\r\n const handleView = React.useCallback((file: DbFile, newVisibility: boolean) => {\r\n const id = String(file.id)\r\n if (newVisibility) {\r\n pointCloudDispatch({ type: 'LOAD_POINT_CLOUD', payload: { id } })\r\n } else {\r\n pointCloudDispatch({ type: 'UNLOAD_POINT_CLOUD', payload: { id } })\r\n }\r\n }, [pointCloudDispatch])\r\n\r\n const handleDeleteFile = React.useCallback(async (file: DbFile) => {\r\n const res = await fetch(`${API_BASE}/point-cloud/${encodeURIComponent(String(file.id))}`, {\r\n method: 'DELETE',\r\n })\r\n\r\n if (!res.ok) {\r\n const msg = await res.text()\r\n throw new Error(msg || `Delete failed (${res.status})`)\r\n }\r\n }, [])\r\n\r\n const handleConvert = React.useCallback(async (file: DbFile) => {\r\n const res = await fetch(\r\n `${API_BASE}/point-cloud/${encodeURIComponent(String(file.id))}/convert-to-potree`,\r\n {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ options: { sampling_method: 'poisson' } }),\r\n }\r\n )\r\n\r\n if (!res.ok) {\r\n const msg = await res.text()\r\n throw new Error(msg || `Conversion failed (${res.status})`)\r\n }\r\n\r\n const { jobId } = await res.json() as StartConversionResponse\r\n subscribeToProgress(jobId, file.id)\r\n }, [])\r\n\r\n const handleDeletePointCloud = React.useCallback((file: DbFile) => {\r\n pointCloudDispatch({ type: 'UNLOAD_POINT_CLOUD', payload: { id: String(file.id) } })\r\n }, [pointCloudDispatch])\r\n\r\n // Use the generic file actions hook\r\n const { handleAction, deleteDialog } = useFileActions({\r\n files: pointcloudsFiles,\r\n setFiles: setPointcloudsFiles as React.Dispatch<React.SetStateAction<(DbFile & { isVisible?: boolean })[]>>,\r\n buildingId: 0,\r\n handleDeleteFile,\r\n onView: handleView,\r\n onDelete: handleDeletePointCloud,\r\n })\r\n\r\n // Sort: visible (loaded) files at the top, preserving relative order within each group\r\n const sortedFiles = React.useMemo(() => {\r\n return [...pointcloudsFiles].sort((a, b) => {\r\n const aLoaded = a.isVisible !== false ? 1 : 0\r\n const bLoaded = b.isVisible !== false ? 1 : 0\r\n return bLoaded - aLoaded\r\n })\r\n }, [pointcloudsFiles])\r\n\r\n // Create point cloud entry — routed through the CDT proxy so that\r\n // organizationId is injected server-side from the authenticated session.\r\n async function createPointCloud(name: string, buildingId?: number): Promise<CreatePointCloudResponse> {\r\n const res = await fetch('/api/point-cloud', {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ name, buildingId }),\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 // Start conversion\r\n async function startConversion(pointCloudId: string): Promise<StartConversionResponse> {\r\n const res = await fetch(\r\n `${API_BASE}/point-cloud/${encodeURIComponent(pointCloudId)}/convert-to-potree`,\r\n {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ options: { sampling_method: 'poisson' } }),\r\n }\r\n )\r\n\r\n if (!res.ok) {\r\n const msg = await res.text()\r\n throw new Error(`convert-to-potree failed: ${msg || res.status}`)\r\n }\r\n\r\n return res.json()\r\n }\r\n\r\n // Subscribe to conversion progress\r\n function subscribeToProgress(jobId: string, pointCloudId: number) {\r\n if (!jobId) return\r\n\r\n if (esRef.current) {\r\n esRef.current.close()\r\n }\r\n\r\n const es = new EventSource(\r\n `${API_BASE}/events/convert-progress/${encodeURIComponent(jobId)}`\r\n )\r\n esRef.current = es\r\n\r\n es.onopen = () => console.log('SSE connection opened')\r\n\r\n es.onerror = (event) => {\r\n console.error('SSE error:', event)\r\n // Update to failed state\r\n setPointcloudsFiles((prev) =>\r\n prev.map((pc) =>\r\n Number(pc.id) === pointCloudId\r\n ? { ...pc, status: 'idle', conversionProgress: 0 }\r\n : pc\r\n )\r\n )\r\n }\r\n\r\n es.addEventListener('progress', (event) => {\r\n try {\r\n const msgEvent = event as MessageEvent\r\n const data = JSON.parse(msgEvent.data)\r\n\r\n console.log('[progress]', data.status, data.message, data.progress)\r\n\r\n if (typeof data.progress === 'number') {\r\n setPointcloudsFiles((prev) =>\r\n prev.map((pc) =>\r\n Number(pc.id) === pointCloudId\r\n ? { ...pc, conversionProgress: data.progress }\r\n : pc\r\n )\r\n )\r\n }\r\n } catch (err) {\r\n console.error('Failed to parse progress event', err)\r\n }\r\n })\r\n\r\n es.addEventListener('finished', () => {\r\n console.log('[finished] Conversion completed')\r\n setPointcloudsFiles((prev) =>\r\n prev.map((pc) =>\r\n Number(pc.id) === pointCloudId\r\n ? { ...pc, status: 'idle', conversionProgress: 100 }\r\n : pc\r\n )\r\n )\r\n es.close()\r\n esRef.current = null\r\n window.location.reload()\r\n })\r\n\r\n es.addEventListener('failed', () => {\r\n console.log('[failed] Conversion failed')\r\n setPointcloudsFiles((prev) =>\r\n prev.map((pc) =>\r\n Number(pc.id) === pointCloudId\r\n ? { ...pc, status: 'idle', conversionProgress: 0 }\r\n : pc\r\n )\r\n )\r\n es.close()\r\n esRef.current = null\r\n })\r\n }\r\n\r\n // Handle file upload\r\n const handleFileUpload = async (file: File) => {\r\n if (!file) return\r\n\r\n const originalFileName = file.name\r\n const fileExt = originalFileName.toLowerCase()\r\n const baseFileName = originalFileName.replace(/\\.(laz|las)$/i, '')\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 // Check for duplicate names and append (1), (2), etc.\r\n let fileName = baseFileName\r\n let counter = 1\r\n const existingNames = pointcloudsFiles.map((pc) => pc.name)\r\n \r\n while (existingNames.includes(fileName)) {\r\n fileName = `${baseFileName} (${counter})`\r\n counter++\r\n }\r\n\r\n try {\r\n console.log('Creating point cloud entry...')\r\n const userEmail = session?.user?.email || null\r\n const { pointCloud, upload } = await createPointCloud(fileName, buildingId)\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 console.log('Starting conversion...')\r\n const conversion = await startConversion(pointCloud.id)\r\n console.log('Conversion started:', conversion.jobId)\r\n\r\n subscribeToProgress(conversion.jobId, Number(pointCloud.id))\r\n } catch (error) {\r\n console.error('Error uploading point cloud:', error)\r\n \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 }\r\n }\r\n\r\n // Handle upload button click\r\n const handleUpload = () => {\r\n const input = document.createElement('input')\r\n input.type = 'file'\r\n input.accept = '.laz,.las'\r\n input.onchange = (e) => {\r\n const file = (e.target as HTMLInputElement).files?.[0]\r\n if (file) {\r\n handleFileUpload(file)\r\n }\r\n }\r\n input.click()\r\n }\r\n\r\n return (\r\n <>\r\n <CollapsibleSection\r\n title={t('title')}\r\n icon={LR.Grip}\r\n className=\"h-1/2 overflow-y-auto\"\r\n itemCount={pointcloudsFiles.length}\r\n onAddItem={handleUpload}\r\n addItemTitle={t('uploadTitle')}\r\n >\r\n <div className=\"space-y-1\">\r\n {sortedFiles.length === 0 ? (\r\n <div className=\"px-2 py-3 text-sm text-muted-foreground text-center\">\r\n {t('noPointClouds')}\r\n </div>\r\n ) : (\r\n sortedFiles.map((file) => (\r\n <div key={file.id} className=\"flex items-center gap-1\">\r\n <div className=\"flex-1 min-w-0\">\r\n <FileItemComponent\r\n file={file}\r\n onAction={handleAction}\r\n options={POINT_CLOUD_OPTIONS}\r\n />\r\n </div>\r\n {Boolean(file.pointCloudUploaded) && !Boolean(file.pointCloudPotreeConverted) && (\r\n <Button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n className=\"h-6 w-6 p-0\"\r\n onClick={() => void handleConvert(file)}\r\n title={t('convertTitle')}\r\n >\r\n <LR.RefreshCw className=\"h-3 w-3\" />\r\n </Button>\r\n )}\r\n </div>\r\n ))\r\n )}\r\n </div>\r\n </CollapsibleSection>\r\n\r\n <ConfirmDialog\r\n isOpen={deleteDialog.isOpen}\r\n isDeleting={deleteDialog.isDeleting}\r\n onOpenChange={deleteDialog.onOpenChange}\r\n handleConfirm={deleteDialog.onConfirm}\r\n itemName={deleteDialog.itemName}\r\n dataType=\"file\"\r\n />\r\n </>\r\n )\r\n}\r\n"],"mappings":"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","names":["buildingId"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createSiteHooks.d.ts","sourceRoot":"","sources":["../../../../src/core/hooks/sites/createSiteHooks.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAEhD,KAAK,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG;IACrC,aAAa,CAAC,EAAE;QACd,OAAO,CAAC,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAC3B,UAAU,CAAC,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;KAC/B,CAAC;CACH,CAAC;AAEF,wBAAgB,eAAe,CAAC,OAAO,EAAE,UAAU;;;;;;sBASxB,MAAM;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"createSiteHooks.d.ts","sourceRoot":"","sources":["../../../../src/core/hooks/sites/createSiteHooks.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAEhD,KAAK,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG;IACrC,aAAa,CAAC,EAAE;QACd,OAAO,CAAC,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAC3B,UAAU,CAAC,EAAE;YAAE,EAAE,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;KAC/B,CAAC;CACH,CAAC;AAEF,wBAAgB,eAAe,CAAC,OAAO,EAAE,UAAU;;;;;;sBASxB,MAAM;;;;;;;;;;;;;;;6BAoCC,MAAM,GAAG,MAAM;yBAMf,MAAM,GAAG,MAAM;;;;;EAehD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/core/hooks/sites/createSiteHooks.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport useSWR, { mutate } from \"swr\";\r\nimport useSWRMutation from \"swr/mutation\";\r\nimport type { ApiAdapter } from \"../ports/apiAdapter\";\r\nimport type { Site } from \"../../types/dbTypes\";\r\n\r\ntype SiteUpdateInput = Partial<Site> & {\r\n siteBuildings?: {\r\n connect?: { id: number }[];\r\n disconnect?: { id: number }[];\r\n };\r\n};\r\n\r\nexport function createSiteHooks(adapter: ApiAdapter) {\r\n // Call adapter methods inside hooks\r\n \r\n const useSites = () => {\r\n const key = [\"sites\"];\r\n const { data, error, isLoading } = useSWR<Site[]>(key, () => adapter.listSites());\r\n return { sites: data ?? [], isLoading, isError: error };\r\n };\r\n\r\n const useSite = (siteId: string) => {\r\n const key = siteId ? ([\"site\", siteId] as const) : null;\r\n const { data, error, isLoading } = useSWR<Site>(key, () => adapter.getSite(siteId));\r\n\r\n const { trigger: updateSite, isMutating, data: updatedData, error: updateError } = useSWRMutation(\r\n siteId ? [\"updateSite\", siteId] : null,\r\n async (_k, { arg }: { arg: SiteUpdateInput }) => adapter.updateSite(siteId, arg),\r\n {\r\n onSuccess: () => {\r\n mutate([\"site\", siteId]);\r\n mutate([\"sites\"]);\r\n
|
|
1
|
+
{"version":3,"sources":["../../../../src/core/hooks/sites/createSiteHooks.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport useSWR, { mutate } from \"swr\";\r\nimport useSWRMutation from \"swr/mutation\";\r\nimport type { ApiAdapter } from \"../ports/apiAdapter\";\r\nimport type { Site } from \"../../types/dbTypes\";\r\n\r\ntype SiteUpdateInput = Partial<Site> & {\r\n siteBuildings?: {\r\n connect?: { id: number }[];\r\n disconnect?: { id: number }[];\r\n };\r\n};\r\n\r\nexport function createSiteHooks(adapter: ApiAdapter) {\r\n // Call adapter methods inside hooks\r\n \r\n const useSites = () => {\r\n const key = [\"sites\"];\r\n const { data, error, isLoading } = useSWR<Site[]>(key, () => adapter.listSites());\r\n return { sites: data ?? [], isLoading, isError: error };\r\n };\r\n\r\n const useSite = (siteId: string) => {\r\n const key = siteId ? ([\"site\", siteId] as const) : null;\r\n const { data, error, isLoading } = useSWR<Site>(key, () => adapter.getSite(siteId));\r\n\r\n const { trigger: updateSite, isMutating, data: updatedData, error: updateError } = useSWRMutation(\r\n siteId ? [\"updateSite\", siteId] : null,\r\n async (_k, { arg }: { arg: SiteUpdateInput }) => adapter.updateSite(siteId, arg),\r\n {\r\n onSuccess: () => {\r\n mutate([\"site\", siteId]);\r\n mutate([\"sites\"]);\r\n // NOTE: intentionally not revalidating [\"buildings\"] — associating a\r\n // building to a site doesn't change the buildings list's displayed\r\n // data, and revalidating it re-renders the always-mounted map's\r\n // building layer (a heavy, jank-inducing churn after bulk associate).\r\n },\r\n }\r\n );\r\n\r\n return { site: data ?? null, isLoading, isError: error, updateSite, isMutating, updateError, updatedData };\r\n };\r\n\r\n const useCreateSite = () => {\r\n const { trigger: createSite, isMutating, data: createdData, error } = useSWRMutation(\r\n [\"createSite\"],\r\n async (_k, { arg }: { arg: Partial<Site> }) => adapter.createSite(arg),\r\n {\r\n onSuccess: () => {\r\n mutate([\"sites\"]);\r\n },\r\n }\r\n );\r\n\r\n return { createSite, isMutating, createError: error, createdData };\r\n };\r\n\r\n const useDeleteSite = (siteId?: number | string) => {\r\n const { trigger, isMutating, data, error } = useSWRMutation(\r\n siteId ? [\"deleteSite\", String(siteId)] : [\"deleteSite\"],\r\n async (_k, { arg }: { arg: string | number }) => adapter.deleteSite(arg)\r\n );\r\n\r\n const deleteSite = async (id: string | number) => {\r\n await trigger(id);\r\n mutate([\"sites\"]);\r\n mutate([\"buildings\"]) // Revalidate buildings in case any were associated with the deleted site\r\n };\r\n\r\n return { deleteSite, isMutating, deleteError: error, deletedData: data };\r\n };\r\n\r\n return { \r\n useSites, \r\n useSite, \r\n useCreateSite, \r\n useDeleteSite \r\n };\r\n}\r\n"],"mappings":"AAGA,OAAO,UAAU,cAAc;AAC/B,OAAO,oBAAoB;AAWpB,SAAS,gBAAgB,SAAqB;AAGnD,QAAM,WAAW,MAAM;AACrB,UAAM,MAAM,CAAC,OAAO;AACpB,UAAM,EAAE,MAAM,OAAO,UAAU,IAAI,OAAe,KAAK,MAAM,QAAQ,UAAU,CAAC;AAChF,WAAO,EAAE,OAAO,sBAAQ,CAAC,GAAG,WAAW,SAAS,MAAM;AAAA,EACxD;AAEA,QAAM,UAAU,CAAC,WAAmB;AAClC,UAAM,MAAM,SAAU,CAAC,QAAQ,MAAM,IAAc;AACnD,UAAM,EAAE,MAAM,OAAO,UAAU,IAAI,OAAa,KAAK,MAAM,QAAQ,QAAQ,MAAM,CAAC;AAElF,UAAM,EAAE,SAAS,YAAY,YAAY,MAAM,aAAa,OAAO,YAAY,IAAI;AAAA,MACjF,SAAS,CAAC,cAAc,MAAM,IAAI;AAAA,MAClC,OAAO,IAAI,EAAE,IAAI,MAAgC,QAAQ,WAAW,QAAQ,GAAG;AAAA,MAC/E;AAAA,QACE,WAAW,MAAM;AACf,iBAAO,CAAC,QAAQ,MAAM,CAAC;AACvB,iBAAO,CAAC,OAAO,CAAC;AAAA,QAKlB;AAAA,MACF;AAAA,IACF;AAEA,WAAO,EAAE,MAAM,sBAAQ,MAAM,WAAW,SAAS,OAAO,YAAY,YAAY,aAAa,YAAY;AAAA,EAC3G;AAEA,QAAM,gBAAgB,MAAM;AAC1B,UAAM,EAAE,SAAS,YAAY,YAAY,MAAM,aAAa,MAAM,IAAI;AAAA,MACpE,CAAC,YAAY;AAAA,MACb,OAAO,IAAI,EAAE,IAAI,MAA8B,QAAQ,WAAW,GAAG;AAAA,MACrE;AAAA,QACE,WAAW,MAAM;AACf,iBAAO,CAAC,OAAO,CAAC;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AAEA,WAAO,EAAE,YAAY,YAAY,aAAa,OAAO,YAAY;AAAA,EACnE;AAEA,QAAM,gBAAgB,CAAC,WAA6B;AAClD,UAAM,EAAE,SAAS,YAAY,MAAM,MAAM,IAAI;AAAA,MAC3C,SAAS,CAAC,cAAc,OAAO,MAAM,CAAC,IAAI,CAAC,YAAY;AAAA,MACvD,OAAO,IAAI,EAAE,IAAI,MAAgC,QAAQ,WAAW,GAAG;AAAA,IACzE;AAEA,UAAM,aAAa,OAAO,OAAwB;AAChD,YAAM,QAAQ,EAAE;AAChB,aAAO,CAAC,OAAO,CAAC;AAChB,aAAO,CAAC,WAAW,CAAC;AAAA,IACtB;AAEA,WAAO,EAAE,YAAY,YAAY,aAAa,OAAO,aAAa,KAAK;AAAA,EACzE;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCaptureScreenshot.d.ts","sourceRoot":"","sources":["../../../src/core/hooks/useCaptureScreenshot.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useCaptureScreenshot.d.ts","sourceRoot":"","sources":["../../../src/core/hooks/useCaptureScreenshot.ts"],"names":[],"mappings":"AAuBA,wBAAgB,oBAAoB,IAAI,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiInE"}
|
|
@@ -9,6 +9,14 @@ function blobToBase64(blob) {
|
|
|
9
9
|
reader.readAsDataURL(blob);
|
|
10
10
|
});
|
|
11
11
|
}
|
|
12
|
+
function loadImage(src) {
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
const img = new Image();
|
|
15
|
+
img.onload = () => resolve(img);
|
|
16
|
+
img.onerror = reject;
|
|
17
|
+
img.src = src;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
12
20
|
function useCaptureScreenshot() {
|
|
13
21
|
const { state: menusState } = React.useContext(MenusContext);
|
|
14
22
|
const { currentViewer } = menusState.menus;
|
|
@@ -16,45 +24,115 @@ function useCaptureScreenshot() {
|
|
|
16
24
|
const { map } = mapState.map;
|
|
17
25
|
const { state: bimState } = React.useContext(BimContext);
|
|
18
26
|
const { world } = bimState.bim;
|
|
19
|
-
return React.useCallback(() => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
return React.useCallback(async () => {
|
|
28
|
+
try {
|
|
29
|
+
let viewerDataUrl = null;
|
|
30
|
+
let viewerCanvas = null;
|
|
31
|
+
let viewerBounds = null;
|
|
32
|
+
if (currentViewer === ViewerNames.map && map) {
|
|
33
|
+
await Promise.race([
|
|
34
|
+
new Promise((res) => {
|
|
35
|
+
map.once("render", () => {
|
|
36
|
+
viewerCanvas = map.getCanvas();
|
|
37
|
+
viewerBounds = viewerCanvas.getBoundingClientRect();
|
|
38
|
+
try {
|
|
39
|
+
viewerDataUrl = viewerCanvas.toDataURL("image/png");
|
|
40
|
+
} catch (e) {
|
|
41
|
+
}
|
|
42
|
+
res();
|
|
43
|
+
});
|
|
44
|
+
map.triggerRepaint();
|
|
45
|
+
}),
|
|
46
|
+
new Promise((r) => setTimeout(r, 3e3))
|
|
47
|
+
]);
|
|
48
|
+
} else if (currentViewer === ViewerNames.bim && (world == null ? void 0 : world.renderer) && (world == null ? void 0 : world.scene) && (world == null ? void 0 : world.camera)) {
|
|
49
|
+
world.renderer.three.render(world.scene.three, world.camera.three);
|
|
50
|
+
viewerCanvas = document.querySelector("#bim-viewer-container canvas");
|
|
51
|
+
if (viewerCanvas) {
|
|
52
|
+
viewerBounds = viewerCanvas.getBoundingClientRect();
|
|
53
|
+
try {
|
|
54
|
+
viewerDataUrl = viewerCanvas.toDataURL("image/png");
|
|
55
|
+
} catch (e) {
|
|
56
|
+
}
|
|
30
57
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
58
|
+
} else {
|
|
59
|
+
const sel = currentViewer === ViewerNames.pointcloud ? "#pointcloud-viewer-container" : null;
|
|
60
|
+
viewerCanvas = sel ? document.querySelector(`${sel} canvas`) : document.querySelector("canvas");
|
|
61
|
+
if (viewerCanvas) {
|
|
62
|
+
viewerBounds = viewerCanvas.getBoundingClientRect();
|
|
63
|
+
try {
|
|
64
|
+
viewerDataUrl = viewerCanvas.toDataURL("image/png");
|
|
65
|
+
} catch (e) {
|
|
39
66
|
}
|
|
40
67
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
68
|
+
}
|
|
69
|
+
const { default: html2canvas } = await import("html2canvas");
|
|
70
|
+
let dialogPortal = null;
|
|
71
|
+
const dialogEl = document.querySelector('[role="dialog"]');
|
|
72
|
+
if (dialogEl) {
|
|
73
|
+
let node = dialogEl;
|
|
74
|
+
while (node && node.parentElement !== document.body) {
|
|
75
|
+
node = node.parentElement;
|
|
46
76
|
}
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
|
|
77
|
+
if (node && node !== document.body) dialogPortal = node;
|
|
78
|
+
}
|
|
79
|
+
if (dialogPortal) dialogPortal.style.display = "none";
|
|
80
|
+
const captureUi = (ignoreImages) => html2canvas(document.body, {
|
|
81
|
+
useCORS: true,
|
|
82
|
+
allowTaint: false,
|
|
83
|
+
logging: false,
|
|
84
|
+
scale: window.devicePixelRatio || 1,
|
|
85
|
+
width: window.innerWidth,
|
|
86
|
+
height: window.innerHeight,
|
|
87
|
+
x: window.scrollX,
|
|
88
|
+
y: window.scrollY,
|
|
89
|
+
windowWidth: window.innerWidth,
|
|
90
|
+
windowHeight: window.innerHeight,
|
|
91
|
+
scrollX: -window.scrollX,
|
|
92
|
+
scrollY: -window.scrollY,
|
|
93
|
+
ignoreElements: (el) => el === viewerCanvas || ignoreImages && el.tagName === "IMG"
|
|
94
|
+
});
|
|
95
|
+
let uiCanvas;
|
|
96
|
+
try {
|
|
97
|
+
try {
|
|
98
|
+
uiCanvas = await captureUi(false);
|
|
99
|
+
} catch (err) {
|
|
100
|
+
console.warn("[useCaptureScreenshot] retrying without <img> elements after taint error:", err);
|
|
101
|
+
uiCanvas = await captureUi(true);
|
|
102
|
+
}
|
|
103
|
+
} finally {
|
|
104
|
+
if (dialogPortal) dialogPortal.style.display = "";
|
|
105
|
+
}
|
|
106
|
+
if (viewerDataUrl && viewerBounds) {
|
|
107
|
+
const ctx = uiCanvas.getContext("2d");
|
|
108
|
+
if (ctx) {
|
|
109
|
+
try {
|
|
110
|
+
const img = await loadImage(viewerDataUrl);
|
|
111
|
+
const dpr = window.devicePixelRatio || 1;
|
|
112
|
+
ctx.drawImage(
|
|
113
|
+
img,
|
|
114
|
+
viewerBounds.left * dpr,
|
|
115
|
+
viewerBounds.top * dpr,
|
|
116
|
+
viewerBounds.width * dpr,
|
|
117
|
+
viewerBounds.height * dpr
|
|
118
|
+
);
|
|
119
|
+
} catch (e) {
|
|
120
|
+
}
|
|
50
121
|
}
|
|
51
|
-
canvas.toBlob(async (blob) => {
|
|
52
|
-
resolve(blob ? await blobToBase64(blob) : null);
|
|
53
|
-
}, "image/png", 1);
|
|
54
|
-
} catch (e) {
|
|
55
|
-
resolve(null);
|
|
56
122
|
}
|
|
57
|
-
|
|
123
|
+
return new Promise((resolve) => {
|
|
124
|
+
uiCanvas.toBlob((blob) => {
|
|
125
|
+
if (!blob) {
|
|
126
|
+
resolve(null);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
blobToBase64(blob).then(resolve);
|
|
130
|
+
}, "image/png", 1);
|
|
131
|
+
});
|
|
132
|
+
} catch (err) {
|
|
133
|
+
console.error("[useCaptureScreenshot] failed:", err);
|
|
134
|
+
throw err instanceof Error ? err : new Error(String(err));
|
|
135
|
+
}
|
|
58
136
|
}, [currentViewer, map, world]);
|
|
59
137
|
}
|
|
60
138
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/hooks/useCaptureScreenshot.ts"],"sourcesContent":["'use client'\r\n\r\nimport * as React from 'react'\r\nimport { MenusContext, MapContext, BimContext } from '../store'\r\nimport { ViewerNames } from '../types'\r\n\r\nfunction blobToBase64(blob: Blob): Promise<string> {\r\n return new Promise((resolve) => {\r\n const reader = new FileReader()\r\n reader.onload = () => resolve(reader.result as string)\r\n reader.readAsDataURL(blob)\r\n })\r\n}\r\n\r\nexport function useCaptureScreenshot(): () => Promise<string | null> {\r\n const { state: menusState } = React.useContext(MenusContext)\r\n const { currentViewer } = menusState.menus\r\n\r\n const { state: mapState } = React.useContext(MapContext)\r\n const { map } = mapState.map\r\n\r\n const { state: bimState } = React.useContext(BimContext)\r\n const { world } = bimState.bim\r\n\r\n return React.useCallback((): Promise<string | null> => {\r\n
|
|
1
|
+
{"version":3,"sources":["../../../src/core/hooks/useCaptureScreenshot.ts"],"sourcesContent":["'use client'\r\n\r\nimport * as React from 'react'\r\nimport { MenusContext, MapContext, BimContext } from '../store'\r\nimport { ViewerNames } from '../types'\r\n\r\nfunction blobToBase64(blob: Blob): Promise<string> {\r\n return new Promise((resolve) => {\r\n const reader = new FileReader()\r\n reader.onload = () => resolve(reader.result as string)\r\n reader.readAsDataURL(blob)\r\n })\r\n}\r\n\r\nfunction loadImage(src: string): Promise<HTMLImageElement> {\r\n return new Promise((resolve, reject) => {\r\n const img = new Image()\r\n img.onload = () => resolve(img)\r\n img.onerror = reject\r\n img.src = src\r\n })\r\n}\r\n\r\nexport function useCaptureScreenshot(): () => Promise<string | null> {\r\n const { state: menusState } = React.useContext(MenusContext)\r\n const { currentViewer } = menusState.menus\r\n\r\n const { state: mapState } = React.useContext(MapContext)\r\n const { map } = mapState.map\r\n\r\n const { state: bimState } = React.useContext(BimContext)\r\n const { world } = bimState.bim\r\n\r\n return React.useCallback(async (): Promise<string | null> => {\r\n try {\r\n let viewerDataUrl: string | null = null\r\n let viewerCanvas: HTMLCanvasElement | null = null\r\n let viewerBounds: DOMRect | null = null\r\n\r\n if (currentViewer === ViewerNames.map && map) {\r\n await Promise.race([\r\n new Promise<void>((res) => {\r\n map.once('render', () => {\r\n viewerCanvas = map.getCanvas()\r\n viewerBounds = viewerCanvas.getBoundingClientRect()\r\n try { viewerDataUrl = viewerCanvas.toDataURL('image/png') } catch { /* cross-origin */ }\r\n res()\r\n })\r\n map.triggerRepaint()\r\n }),\r\n new Promise<void>((r) => setTimeout(r, 3000)),\r\n ])\r\n } else if (currentViewer === ViewerNames.bim && world?.renderer && world?.scene && world?.camera) {\r\n world.renderer.three.render(world.scene.three, world.camera.three)\r\n viewerCanvas = document.querySelector<HTMLCanvasElement>('#bim-viewer-container canvas')\r\n if (viewerCanvas) {\r\n viewerBounds = viewerCanvas.getBoundingClientRect()\r\n try { viewerDataUrl = viewerCanvas.toDataURL('image/png') } catch { /* ignore */ }\r\n }\r\n } else {\r\n const sel = currentViewer === ViewerNames.pointcloud ? '#pointcloud-viewer-container' : null\r\n viewerCanvas = sel\r\n ? document.querySelector<HTMLCanvasElement>(`${sel} canvas`)\r\n : document.querySelector<HTMLCanvasElement>('canvas')\r\n if (viewerCanvas) {\r\n viewerBounds = viewerCanvas.getBoundingClientRect()\r\n try { viewerDataUrl = viewerCanvas.toDataURL('image/png') } catch { /* ignore */ }\r\n }\r\n }\r\n\r\n const { default: html2canvas } = await import('html2canvas')\r\n\r\n // Find the dialog portal: walk up from [role=\"dialog\"] to its direct\r\n // child-of-body ancestor. We hide it with display:none before capture\r\n // and restore it after. This avoids ignoreElements on a body child,\r\n // which causes \"Unable to find element in cloned iframe\".\r\n let dialogPortal: HTMLElement | null = null\r\n const dialogEl = document.querySelector('[role=\"dialog\"]')\r\n if (dialogEl) {\r\n let node: Element | null = dialogEl\r\n while (node && node.parentElement !== document.body) {\r\n node = node.parentElement\r\n }\r\n if (node && node !== document.body) dialogPortal = node as HTMLElement\r\n }\r\n\r\n if (dialogPortal) dialogPortal.style.display = 'none'\r\n\r\n const captureUi = (ignoreImages: boolean) => html2canvas(document.body, {\r\n useCORS: true,\r\n allowTaint: false,\r\n logging: false,\r\n scale: window.devicePixelRatio || 1,\r\n width: window.innerWidth,\r\n height: window.innerHeight,\r\n x: window.scrollX,\r\n y: window.scrollY,\r\n windowWidth: window.innerWidth,\r\n windowHeight: window.innerHeight,\r\n scrollX: -window.scrollX,\r\n scrollY: -window.scrollY,\r\n ignoreElements: (el: Element) =>\r\n el === viewerCanvas || (ignoreImages && el.tagName === 'IMG'),\r\n })\r\n\r\n let uiCanvas: HTMLCanvasElement\r\n try {\r\n try {\r\n uiCanvas = await captureUi(false)\r\n } catch (err) {\r\n // Safari throws \"The operation is insecure\" when a cross-origin\r\n // image (org logo, user avatar, ...) taints html2canvas's internal\r\n // canvas even with allowTaint:false — retry once with all <img>\r\n // elements excluded so the capture still succeeds, just without\r\n // those images. Chrome is more lenient here, which is why this\r\n // only shows up on mobile Safari.\r\n console.warn('[useCaptureScreenshot] retrying without <img> elements after taint error:', err)\r\n uiCanvas = await captureUi(true)\r\n }\r\n } finally {\r\n if (dialogPortal) dialogPortal.style.display = ''\r\n }\r\n\r\n // Composite the pre-captured viewer image at the exact canvas bounds.\r\n if (viewerDataUrl && viewerBounds) {\r\n const ctx = uiCanvas.getContext('2d')\r\n if (ctx) {\r\n try {\r\n const img = await loadImage(viewerDataUrl)\r\n const dpr = window.devicePixelRatio || 1\r\n ctx.drawImage(\r\n img,\r\n viewerBounds.left * dpr,\r\n viewerBounds.top * dpr,\r\n viewerBounds.width * dpr,\r\n viewerBounds.height * dpr,\r\n )\r\n } catch { /* skip compositing on error */ }\r\n }\r\n }\r\n\r\n return new Promise<string | null>((resolve) => {\r\n uiCanvas.toBlob((blob) => {\r\n if (!blob) { resolve(null); return }\r\n blobToBase64(blob).then(resolve)\r\n }, 'image/png', 1.0)\r\n })\r\n } catch (err) {\r\n console.error('[useCaptureScreenshot] failed:', err)\r\n throw err instanceof Error ? err : new Error(String(err))\r\n }\r\n }, [currentViewer, map, world])\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type ViewerPosition = {
|
|
2
|
+
type: 'map';
|
|
3
|
+
lat: number;
|
|
4
|
+
lng: number;
|
|
5
|
+
zoom: number;
|
|
6
|
+
bearing: number;
|
|
7
|
+
pitch: number;
|
|
8
|
+
} | {
|
|
9
|
+
type: 'bim' | 'pointcloud';
|
|
10
|
+
camX: number;
|
|
11
|
+
camY: number;
|
|
12
|
+
camZ: number;
|
|
13
|
+
tarX: number;
|
|
14
|
+
tarY: number;
|
|
15
|
+
tarZ: number;
|
|
16
|
+
};
|
|
17
|
+
export declare function useCurrentViewerPosition(): () => ViewerPosition | null;
|
|
18
|
+
//# sourceMappingURL=useCurrentViewerPosition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCurrentViewerPosition.d.ts","sourceRoot":"","sources":["../../../src/core/hooks/useCurrentViewerPosition.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACvF;IAAE,IAAI,EAAE,KAAK,GAAG,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEtH,wBAAgB,wBAAwB,IAAI,MAAM,cAAc,GAAG,IAAI,CA4DtE"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import * as THREE from "three";
|
|
4
|
+
import { MenusContext, MapContext, BimContext, PointCloudContext } from "../store";
|
|
5
|
+
import { ViewerNames } from "../types";
|
|
6
|
+
import { getCameraPosition } from "../components/viewers/pointcloud/src/tools/SharePointCloudTool/src/getCameraPosition";
|
|
7
|
+
function useCurrentViewerPosition() {
|
|
8
|
+
const { state: menusState } = React.useContext(MenusContext);
|
|
9
|
+
const { currentViewer } = menusState.menus;
|
|
10
|
+
const { state: mapState } = React.useContext(MapContext);
|
|
11
|
+
const { map } = mapState.map;
|
|
12
|
+
const { state: bimState } = React.useContext(BimContext);
|
|
13
|
+
const { world } = bimState.bim;
|
|
14
|
+
const { state: pcState } = React.useContext(PointCloudContext);
|
|
15
|
+
const { viewer } = pcState.pointcloud;
|
|
16
|
+
return React.useCallback(() => {
|
|
17
|
+
if (currentViewer === ViewerNames.map && map) {
|
|
18
|
+
const center = map.getCenter();
|
|
19
|
+
return {
|
|
20
|
+
type: "map",
|
|
21
|
+
lat: parseFloat(center.lat.toFixed(7)),
|
|
22
|
+
lng: parseFloat(center.lng.toFixed(7)),
|
|
23
|
+
zoom: parseFloat(map.getZoom().toFixed(3)),
|
|
24
|
+
bearing: parseFloat(map.getBearing().toFixed(1)),
|
|
25
|
+
pitch: parseFloat(map.getPitch().toFixed(1))
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (currentViewer === ViewerNames.bim && (world == null ? void 0 : world.camera)) {
|
|
29
|
+
try {
|
|
30
|
+
const pos = new THREE.Vector3();
|
|
31
|
+
const tar = new THREE.Vector3();
|
|
32
|
+
world.camera.controls.getPosition(pos);
|
|
33
|
+
world.camera.controls.getTarget(tar);
|
|
34
|
+
return {
|
|
35
|
+
type: "bim",
|
|
36
|
+
camX: parseFloat(pos.x.toFixed(3)),
|
|
37
|
+
camY: parseFloat(pos.y.toFixed(3)),
|
|
38
|
+
camZ: parseFloat(pos.z.toFixed(3)),
|
|
39
|
+
tarX: parseFloat(tar.x.toFixed(3)),
|
|
40
|
+
tarY: parseFloat(tar.y.toFixed(3)),
|
|
41
|
+
tarZ: parseFloat(tar.z.toFixed(3))
|
|
42
|
+
};
|
|
43
|
+
} catch (e) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (currentViewer === ViewerNames.pointcloud && viewer) {
|
|
48
|
+
const cam = getCameraPosition(viewer);
|
|
49
|
+
if (!cam) return null;
|
|
50
|
+
return {
|
|
51
|
+
type: "pointcloud",
|
|
52
|
+
camX: parseFloat(cam.position.x.toFixed(3)),
|
|
53
|
+
camY: parseFloat(cam.position.y.toFixed(3)),
|
|
54
|
+
camZ: parseFloat(cam.position.z.toFixed(3)),
|
|
55
|
+
tarX: parseFloat(cam.target.x.toFixed(3)),
|
|
56
|
+
tarY: parseFloat(cam.target.y.toFixed(3)),
|
|
57
|
+
tarZ: parseFloat(cam.target.z.toFixed(3))
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}, [currentViewer, map, world, viewer]);
|
|
62
|
+
}
|
|
63
|
+
export {
|
|
64
|
+
useCurrentViewerPosition
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=useCurrentViewerPosition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/core/hooks/useCurrentViewerPosition.ts"],"sourcesContent":["'use client'\r\n\r\nimport * as React from 'react'\r\nimport * as THREE from 'three'\r\nimport { MenusContext, MapContext, BimContext, PointCloudContext } from '../store'\r\nimport { ViewerNames } from '../types'\r\nimport { getCameraPosition } from '../components/viewers/pointcloud/src/tools/SharePointCloudTool/src/getCameraPosition'\r\n\r\nexport type ViewerPosition =\r\n | { type: 'map'; lat: number; lng: number; zoom: number; bearing: number; pitch: number }\r\n | { type: 'bim' | 'pointcloud'; camX: number; camY: number; camZ: number; tarX: number; tarY: number; tarZ: number }\r\n\r\nexport function useCurrentViewerPosition(): () => ViewerPosition | null {\r\n const { state: menusState } = React.useContext(MenusContext)\r\n const { currentViewer } = menusState.menus\r\n\r\n const { state: mapState } = React.useContext(MapContext)\r\n const { map } = mapState.map\r\n\r\n const { state: bimState } = React.useContext(BimContext)\r\n const { world } = bimState.bim\r\n\r\n const { state: pcState } = React.useContext(PointCloudContext)\r\n const { viewer } = pcState.pointcloud\r\n\r\n return React.useCallback((): ViewerPosition | null => {\r\n if (currentViewer === ViewerNames.map && map) {\r\n const center = map.getCenter()\r\n return {\r\n type: 'map',\r\n lat: parseFloat(center.lat.toFixed(7)),\r\n lng: parseFloat(center.lng.toFixed(7)),\r\n zoom: parseFloat(map.getZoom().toFixed(3)),\r\n bearing: parseFloat(map.getBearing().toFixed(1)),\r\n pitch: parseFloat(map.getPitch().toFixed(1)),\r\n }\r\n }\r\n\r\n if (currentViewer === ViewerNames.bim && world?.camera) {\r\n try {\r\n const pos = new THREE.Vector3()\r\n const tar = new THREE.Vector3()\r\n world.camera.controls.getPosition(pos)\r\n world.camera.controls.getTarget(tar)\r\n return {\r\n type: 'bim',\r\n camX: parseFloat(pos.x.toFixed(3)),\r\n camY: parseFloat(pos.y.toFixed(3)),\r\n camZ: parseFloat(pos.z.toFixed(3)),\r\n tarX: parseFloat(tar.x.toFixed(3)),\r\n tarY: parseFloat(tar.y.toFixed(3)),\r\n tarZ: parseFloat(tar.z.toFixed(3)),\r\n }\r\n } catch { return null }\r\n }\r\n\r\n if (currentViewer === ViewerNames.pointcloud && viewer) {\r\n const cam = getCameraPosition(viewer)\r\n if (!cam) return null\r\n return {\r\n type: 'pointcloud',\r\n camX: parseFloat(cam.position.x.toFixed(3)),\r\n camY: parseFloat(cam.position.y.toFixed(3)),\r\n camZ: parseFloat(cam.position.z.toFixed(3)),\r\n tarX: parseFloat(cam.target.x.toFixed(3)),\r\n tarY: parseFloat(cam.target.y.toFixed(3)),\r\n tarZ: parseFloat(cam.target.z.toFixed(3)),\r\n }\r\n }\r\n\r\n return null\r\n }, [currentViewer, map, world, viewer])\r\n}\r\n"],"mappings":";AAEA,YAAY,WAAW;AACvB,YAAY,WAAW;AACvB,SAAS,cAAc,YAAY,YAAY,yBAAyB;AACxE,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAM3B,SAAS,2BAAwD;AACtE,QAAM,EAAE,OAAO,WAAW,IAAI,MAAM,WAAW,YAAY;AAC3D,QAAM,EAAE,cAAc,IAAI,WAAW;AAErC,QAAM,EAAE,OAAO,SAAS,IAAI,MAAM,WAAW,UAAU;AACvD,QAAM,EAAE,IAAI,IAAI,SAAS;AAEzB,QAAM,EAAE,OAAO,SAAS,IAAI,MAAM,WAAW,UAAU;AACvD,QAAM,EAAE,MAAM,IAAI,SAAS;AAE3B,QAAM,EAAE,OAAO,QAAQ,IAAI,MAAM,WAAW,iBAAiB;AAC7D,QAAM,EAAE,OAAO,IAAI,QAAQ;AAE3B,SAAO,MAAM,YAAY,MAA6B;AACpD,QAAI,kBAAkB,YAAY,OAAO,KAAK;AAC5C,YAAM,SAAS,IAAI,UAAU;AAC7B,aAAO;AAAA,QACL,MAAM;AAAA,QACN,KAAK,WAAW,OAAO,IAAI,QAAQ,CAAC,CAAC;AAAA,QACrC,KAAK,WAAW,OAAO,IAAI,QAAQ,CAAC,CAAC;AAAA,QACrC,MAAM,WAAW,IAAI,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAAA,QACzC,SAAS,WAAW,IAAI,WAAW,EAAE,QAAQ,CAAC,CAAC;AAAA,QAC/C,OAAO,WAAW,IAAI,SAAS,EAAE,QAAQ,CAAC,CAAC;AAAA,MAC7C;AAAA,IACF;AAEA,QAAI,kBAAkB,YAAY,QAAO,+BAAO,SAAQ;AACtD,UAAI;AACF,cAAM,MAAM,IAAI,MAAM,QAAQ;AAC9B,cAAM,MAAM,IAAI,MAAM,QAAQ;AAC9B,cAAM,OAAO,SAAS,YAAY,GAAG;AACrC,cAAM,OAAO,SAAS,UAAU,GAAG;AACnC,eAAO;AAAA,UACL,MAAM;AAAA,UACN,MAAM,WAAW,IAAI,EAAE,QAAQ,CAAC,CAAC;AAAA,UACjC,MAAM,WAAW,IAAI,EAAE,QAAQ,CAAC,CAAC;AAAA,UACjC,MAAM,WAAW,IAAI,EAAE,QAAQ,CAAC,CAAC;AAAA,UACjC,MAAM,WAAW,IAAI,EAAE,QAAQ,CAAC,CAAC;AAAA,UACjC,MAAM,WAAW,IAAI,EAAE,QAAQ,CAAC,CAAC;AAAA,UACjC,MAAM,WAAW,IAAI,EAAE,QAAQ,CAAC,CAAC;AAAA,QACnC;AAAA,MACF,SAAQ;AAAE,eAAO;AAAA,MAAK;AAAA,IACxB;AAEA,QAAI,kBAAkB,YAAY,cAAc,QAAQ;AACtD,YAAM,MAAM,kBAAkB,MAAM;AACpC,UAAI,CAAC,IAAK,QAAO;AACjB,aAAO;AAAA,QACL,MAAM;AAAA,QACN,MAAM,WAAW,IAAI,SAAS,EAAE,QAAQ,CAAC,CAAC;AAAA,QAC1C,MAAM,WAAW,IAAI,SAAS,EAAE,QAAQ,CAAC,CAAC;AAAA,QAC1C,MAAM,WAAW,IAAI,SAAS,EAAE,QAAQ,CAAC,CAAC;AAAA,QAC1C,MAAM,WAAW,IAAI,OAAO,EAAE,QAAQ,CAAC,CAAC;AAAA,QACxC,MAAM,WAAW,IAAI,OAAO,EAAE,QAAQ,CAAC,CAAC;AAAA,QACxC,MAAM,WAAW,IAAI,OAAO,EAAE,QAAQ,CAAC,CAAC;AAAA,MAC1C;AAAA,IACF;AAEA,WAAO;AAAA,EACT,GAAG,CAAC,eAAe,KAAK,OAAO,MAAM,CAAC;AACxC;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useShareUrl.d.ts","sourceRoot":"","sources":["../../../src/core/hooks/useShareUrl.ts"],"names":[],"mappings":"AAQA,wBAAgB,WAAW,IAAI,MAAM,OAAO,CAAC,MAAM,CAAC,CAwEnD"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import * as THREE from "three";
|
|
4
|
+
import { MenusContext, MapContext, BimContext, PointCloudContext, BuildingsContext } from "../store";
|
|
5
|
+
import { ViewerNames } from "../types";
|
|
6
|
+
import { getCameraPosition } from "../components/viewers/pointcloud/src/tools/SharePointCloudTool/src/getCameraPosition";
|
|
7
|
+
function useShareUrl() {
|
|
8
|
+
const { state: menusState } = React.useContext(MenusContext);
|
|
9
|
+
const { currentViewer } = menusState.menus;
|
|
10
|
+
const { state: mapState } = React.useContext(MapContext);
|
|
11
|
+
const { map } = mapState.map;
|
|
12
|
+
const { state: bimState } = React.useContext(BimContext);
|
|
13
|
+
const { world } = bimState.bim;
|
|
14
|
+
const { state: pcState } = React.useContext(PointCloudContext);
|
|
15
|
+
const { viewer } = pcState.pointcloud;
|
|
16
|
+
const { state: buildingState } = React.useContext(BuildingsContext);
|
|
17
|
+
const { building } = buildingState.buildings;
|
|
18
|
+
return React.useCallback(async () => {
|
|
19
|
+
const origin = window.location.origin;
|
|
20
|
+
const pathname = window.location.pathname;
|
|
21
|
+
const fallback = window.location.href;
|
|
22
|
+
if (currentViewer === ViewerNames.map && map) {
|
|
23
|
+
const params = new URLSearchParams({
|
|
24
|
+
viewer: "map",
|
|
25
|
+
lat: map.getCenter().lat.toFixed(7),
|
|
26
|
+
lng: map.getCenter().lng.toFixed(7),
|
|
27
|
+
zoom: map.getZoom().toFixed(3),
|
|
28
|
+
bearing: map.getBearing().toFixed(1),
|
|
29
|
+
pitch: map.getPitch().toFixed(1)
|
|
30
|
+
});
|
|
31
|
+
return `${origin}${pathname}?${params.toString()}`;
|
|
32
|
+
}
|
|
33
|
+
if (currentViewer === ViewerNames.bim && (world == null ? void 0 : world.camera)) {
|
|
34
|
+
try {
|
|
35
|
+
const pos = new THREE.Vector3();
|
|
36
|
+
const tar = new THREE.Vector3();
|
|
37
|
+
world.camera.controls.getPosition(pos);
|
|
38
|
+
world.camera.controls.getTarget(tar);
|
|
39
|
+
const params = new URLSearchParams(new URL(window.location.href).search);
|
|
40
|
+
params.set("viewer", "bim");
|
|
41
|
+
params.set("camX", pos.x.toFixed(3));
|
|
42
|
+
params.set("camY", pos.y.toFixed(3));
|
|
43
|
+
params.set("camZ", pos.z.toFixed(3));
|
|
44
|
+
params.set("tarX", tar.x.toFixed(3));
|
|
45
|
+
params.set("tarY", tar.y.toFixed(3));
|
|
46
|
+
params.set("tarZ", tar.z.toFixed(3));
|
|
47
|
+
if (building) params.set("buildingId", String(building.id));
|
|
48
|
+
return `${origin}${pathname}?${params.toString()}`;
|
|
49
|
+
} catch (e) {
|
|
50
|
+
return fallback;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (currentViewer === ViewerNames.pointcloud && viewer) {
|
|
54
|
+
const cam = getCameraPosition(viewer);
|
|
55
|
+
if (cam) {
|
|
56
|
+
const params = new URLSearchParams(new URL(window.location.href).search);
|
|
57
|
+
params.set("viewer", "pointcloud");
|
|
58
|
+
params.set("camX", cam.position.x.toFixed(3));
|
|
59
|
+
params.set("camY", cam.position.y.toFixed(3));
|
|
60
|
+
params.set("camZ", cam.position.z.toFixed(3));
|
|
61
|
+
params.set("tarX", cam.target.x.toFixed(3));
|
|
62
|
+
params.set("tarY", cam.target.y.toFixed(3));
|
|
63
|
+
params.set("tarZ", cam.target.z.toFixed(3));
|
|
64
|
+
if (building) params.set("buildingId", String(building.id));
|
|
65
|
+
return `${origin}${pathname}?${params.toString()}`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return fallback;
|
|
69
|
+
}, [currentViewer, map, world, viewer, building]);
|
|
70
|
+
}
|
|
71
|
+
export {
|
|
72
|
+
useShareUrl
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=useShareUrl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/core/hooks/useShareUrl.ts"],"sourcesContent":["'use client'\r\n\r\nimport * as React from 'react'\r\nimport * as THREE from 'three'\r\nimport { MenusContext, MapContext, BimContext, PointCloudContext, BuildingsContext } from '../store'\r\nimport { ViewerNames } from '../types'\r\nimport { getCameraPosition } from '../components/viewers/pointcloud/src/tools/SharePointCloudTool/src/getCameraPosition'\r\n\r\nexport function useShareUrl(): () => Promise<string> {\r\n const { state: menusState } = React.useContext(MenusContext)\r\n const { currentViewer } = menusState.menus\r\n\r\n const { state: mapState } = React.useContext(MapContext)\r\n const { map } = mapState.map\r\n\r\n const { state: bimState } = React.useContext(BimContext)\r\n const { world } = bimState.bim\r\n\r\n const { state: pcState } = React.useContext(PointCloudContext)\r\n const { viewer } = pcState.pointcloud\r\n\r\n const { state: buildingState } = React.useContext(BuildingsContext)\r\n const { building } = buildingState.buildings\r\n\r\n return React.useCallback(async (): Promise<string> => {\r\n const origin = window.location.origin\r\n const pathname = window.location.pathname\r\n const fallback = window.location.href\r\n\r\n if (currentViewer === ViewerNames.map && map) {\r\n const params = new URLSearchParams({\r\n viewer: 'map',\r\n lat: map.getCenter().lat.toFixed(7),\r\n lng: map.getCenter().lng.toFixed(7),\r\n zoom: map.getZoom().toFixed(3),\r\n bearing: map.getBearing().toFixed(1),\r\n pitch: map.getPitch().toFixed(1),\r\n })\r\n return `${origin}${pathname}?${params.toString()}`\r\n }\r\n\r\n if (currentViewer === ViewerNames.bim && world?.camera) {\r\n try {\r\n const pos = new THREE.Vector3()\r\n const tar = new THREE.Vector3()\r\n world.camera.controls.getPosition(pos)\r\n world.camera.controls.getTarget(tar)\r\n const params = new URLSearchParams(new URL(window.location.href).search)\r\n params.set('viewer', 'bim')\r\n params.set('camX', pos.x.toFixed(3))\r\n params.set('camY', pos.y.toFixed(3))\r\n params.set('camZ', pos.z.toFixed(3))\r\n params.set('tarX', tar.x.toFixed(3))\r\n params.set('tarY', tar.y.toFixed(3))\r\n params.set('tarZ', tar.z.toFixed(3))\r\n if (building) params.set('buildingId', String(building.id))\r\n return `${origin}${pathname}?${params.toString()}`\r\n } catch {\r\n return fallback\r\n }\r\n }\r\n\r\n if (currentViewer === ViewerNames.pointcloud && viewer) {\r\n const cam = getCameraPosition(viewer)\r\n if (cam) {\r\n const params = new URLSearchParams(new URL(window.location.href).search)\r\n params.set('viewer', 'pointcloud')\r\n params.set('camX', cam.position.x.toFixed(3))\r\n params.set('camY', cam.position.y.toFixed(3))\r\n params.set('camZ', cam.position.z.toFixed(3))\r\n params.set('tarX', cam.target.x.toFixed(3))\r\n params.set('tarY', cam.target.y.toFixed(3))\r\n params.set('tarZ', cam.target.z.toFixed(3))\r\n if (building) params.set('buildingId', String(building.id))\r\n return `${origin}${pathname}?${params.toString()}`\r\n }\r\n }\r\n\r\n return fallback\r\n }, [currentViewer, map, world, viewer, building])\r\n}\r\n"],"mappings":";AAEA,YAAY,WAAW;AACvB,YAAY,WAAW;AACvB,SAAS,cAAc,YAAY,YAAY,mBAAmB,wBAAwB;AAC1F,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAE3B,SAAS,cAAqC;AACnD,QAAM,EAAE,OAAO,WAAW,IAAI,MAAM,WAAW,YAAY;AAC3D,QAAM,EAAE,cAAc,IAAI,WAAW;AAErC,QAAM,EAAE,OAAO,SAAS,IAAI,MAAM,WAAW,UAAU;AACvD,QAAM,EAAE,IAAI,IAAI,SAAS;AAEzB,QAAM,EAAE,OAAO,SAAS,IAAI,MAAM,WAAW,UAAU;AACvD,QAAM,EAAE,MAAM,IAAI,SAAS;AAE3B,QAAM,EAAE,OAAO,QAAQ,IAAI,MAAM,WAAW,iBAAiB;AAC7D,QAAM,EAAE,OAAO,IAAI,QAAQ;AAE3B,QAAM,EAAE,OAAO,cAAc,IAAI,MAAM,WAAW,gBAAgB;AAClE,QAAM,EAAE,SAAS,IAAI,cAAc;AAEnC,SAAO,MAAM,YAAY,YAA6B;AACpD,UAAM,SAAS,OAAO,SAAS;AAC/B,UAAM,WAAW,OAAO,SAAS;AACjC,UAAM,WAAW,OAAO,SAAS;AAEjC,QAAI,kBAAkB,YAAY,OAAO,KAAK;AAC5C,YAAM,SAAS,IAAI,gBAAgB;AAAA,QACjC,QAAQ;AAAA,QACR,KAAK,IAAI,UAAU,EAAE,IAAI,QAAQ,CAAC;AAAA,QAClC,KAAK,IAAI,UAAU,EAAE,IAAI,QAAQ,CAAC;AAAA,QAClC,MAAM,IAAI,QAAQ,EAAE,QAAQ,CAAC;AAAA,QAC7B,SAAS,IAAI,WAAW,EAAE,QAAQ,CAAC;AAAA,QACnC,OAAO,IAAI,SAAS,EAAE,QAAQ,CAAC;AAAA,MACjC,CAAC;AACD,aAAO,GAAG,MAAM,GAAG,QAAQ,IAAI,OAAO,SAAS,CAAC;AAAA,IAClD;AAEA,QAAI,kBAAkB,YAAY,QAAO,+BAAO,SAAQ;AACtD,UAAI;AACF,cAAM,MAAM,IAAI,MAAM,QAAQ;AAC9B,cAAM,MAAM,IAAI,MAAM,QAAQ;AAC9B,cAAM,OAAO,SAAS,YAAY,GAAG;AACrC,cAAM,OAAO,SAAS,UAAU,GAAG;AACnC,cAAM,SAAS,IAAI,gBAAgB,IAAI,IAAI,OAAO,SAAS,IAAI,EAAE,MAAM;AACvE,eAAO,IAAI,UAAU,KAAK;AAC1B,eAAO,IAAI,QAAQ,IAAI,EAAE,QAAQ,CAAC,CAAC;AACnC,eAAO,IAAI,QAAQ,IAAI,EAAE,QAAQ,CAAC,CAAC;AACnC,eAAO,IAAI,QAAQ,IAAI,EAAE,QAAQ,CAAC,CAAC;AACnC,eAAO,IAAI,QAAQ,IAAI,EAAE,QAAQ,CAAC,CAAC;AACnC,eAAO,IAAI,QAAQ,IAAI,EAAE,QAAQ,CAAC,CAAC;AACnC,eAAO,IAAI,QAAQ,IAAI,EAAE,QAAQ,CAAC,CAAC;AACnC,YAAI,SAAU,QAAO,IAAI,cAAc,OAAO,SAAS,EAAE,CAAC;AAC1D,eAAO,GAAG,MAAM,GAAG,QAAQ,IAAI,OAAO,SAAS,CAAC;AAAA,MAClD,SAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAEA,QAAI,kBAAkB,YAAY,cAAc,QAAQ;AACtD,YAAM,MAAM,kBAAkB,MAAM;AACpC,UAAI,KAAK;AACP,cAAM,SAAS,IAAI,gBAAgB,IAAI,IAAI,OAAO,SAAS,IAAI,EAAE,MAAM;AACvE,eAAO,IAAI,UAAU,YAAY;AACjC,eAAO,IAAI,QAAQ,IAAI,SAAS,EAAE,QAAQ,CAAC,CAAC;AAC5C,eAAO,IAAI,QAAQ,IAAI,SAAS,EAAE,QAAQ,CAAC,CAAC;AAC5C,eAAO,IAAI,QAAQ,IAAI,SAAS,EAAE,QAAQ,CAAC,CAAC;AAC5C,eAAO,IAAI,QAAQ,IAAI,OAAO,EAAE,QAAQ,CAAC,CAAC;AAC1C,eAAO,IAAI,QAAQ,IAAI,OAAO,EAAE,QAAQ,CAAC,CAAC;AAC1C,eAAO,IAAI,QAAQ,IAAI,OAAO,EAAE,QAAQ,CAAC,CAAC;AAC1C,YAAI,SAAU,QAAO,IAAI,cAAc,OAAO,SAAS,EAAE,CAAC;AAC1D,eAAO,GAAG,MAAM,GAAG,QAAQ,IAAI,OAAO,SAAS,CAAC;AAAA,MAClD;AAAA,IACF;AAEA,WAAO;AAAA,EACT,GAAG,CAAC,eAAe,KAAK,OAAO,QAAQ,QAAQ,CAAC;AAClD;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CombineProviders.d.ts","sourceRoot":"","sources":["../../../src/core/store/CombineProviders.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAqB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"CombineProviders.d.ts","sourceRoot":"","sources":["../../../src/core/store/CombineProviders.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAqB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAyC3E,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAAC,aAAa,CAAC,EAAE,aAAa,CAAA;CAAE,qBAQpH"}
|
|
@@ -9,6 +9,7 @@ import { ToolsProvider } from "./Tools/context";
|
|
|
9
9
|
import { FilesProvider } from "./Files/context";
|
|
10
10
|
import { BuildingsProvider } from "./Buildings/context";
|
|
11
11
|
import { DatasetsProvider } from "./Datasets/context";
|
|
12
|
+
import { MapSitesProvider } from "./MapSites/context";
|
|
12
13
|
import { PointCloudProvider } from "./PointCloud/context";
|
|
13
14
|
import { PermissionsProvider } from "./Permissions/context";
|
|
14
15
|
import { PluginHostProvider } from "../plugins/host/provider";
|
|
@@ -24,6 +25,7 @@ const InnerProviders = compose([
|
|
|
24
25
|
ToolsProvider,
|
|
25
26
|
ContentProvider,
|
|
26
27
|
DatasetsProvider,
|
|
28
|
+
MapSitesProvider,
|
|
27
29
|
FilesProvider,
|
|
28
30
|
BuildingsProvider,
|
|
29
31
|
PointCloudProvider,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/core/store/CombineProviders.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 { AppConfigProvider, type RuntimeConfig } from './AppConfig/context'\r\nimport { BimProvider } from './BIM/context'\r\nimport { ContentProvider } from './Content/context'\r\nimport { MapProvider } from './Map/context'\r\nimport { MenusProvider } from './Menus/context'\r\nimport { ToolsProvider } from './Tools/context'\r\nimport { FilesProvider } from './Files/context'\r\nimport { BuildingsProvider } from './Buildings/context'\r\nimport { DatasetsProvider } from './Datasets/context'\r\nimport { PointCloudProvider } from './PointCloud/context'\r\nimport { PermissionsProvider } from './Permissions/context'\r\nimport { PluginHostProvider } from '../plugins/host/provider'\r\n\r\nconst compose = providers =>\r\n providers.reduce(\r\n (Prev, Curr) =>\r\n function ComposedProvider({ children }) {\r\n return (\r\n <Prev>\r\n <Curr>{children}</Curr>\r\n </Prev>\r\n )\r\n },\r\n )\r\n\r\nconst InnerProviders = compose([\r\n BimProvider,\r\n MapProvider,\r\n MenusProvider,\r\n ToolsProvider,\r\n ContentProvider,\r\n DatasetsProvider,\r\n FilesProvider,\r\n BuildingsProvider,\r\n PointCloudProvider,\r\n PermissionsProvider,\r\n PluginHostProvider,\r\n])\r\n\r\nexport function AppProvider({ children, runtimeConfig }: { children: React.ReactNode, runtimeConfig?: RuntimeConfig }) {\r\n return (\r\n <AppConfigProvider runtimeConfig={runtimeConfig}>\r\n <InnerProviders>\r\n {children}\r\n </InnerProviders>\r\n </AppConfigProvider>\r\n )\r\n}\r\n"],"mappings":";
|
|
1
|
+
{"version":3,"sources":["../../../src/core/store/CombineProviders.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 { AppConfigProvider, type RuntimeConfig } from './AppConfig/context'\r\nimport { BimProvider } from './BIM/context'\r\nimport { ContentProvider } from './Content/context'\r\nimport { MapProvider } from './Map/context'\r\nimport { MenusProvider } from './Menus/context'\r\nimport { ToolsProvider } from './Tools/context'\r\nimport { FilesProvider } from './Files/context'\r\nimport { BuildingsProvider } from './Buildings/context'\r\nimport { DatasetsProvider } from './Datasets/context'\r\nimport { MapSitesProvider } from './MapSites/context'\r\nimport { PointCloudProvider } from './PointCloud/context'\r\nimport { PermissionsProvider } from './Permissions/context'\r\nimport { PluginHostProvider } from '../plugins/host/provider'\r\n\r\nconst compose = providers =>\r\n providers.reduce(\r\n (Prev, Curr) =>\r\n function ComposedProvider({ children }) {\r\n return (\r\n <Prev>\r\n <Curr>{children}</Curr>\r\n </Prev>\r\n )\r\n },\r\n )\r\n\r\nconst InnerProviders = compose([\r\n BimProvider,\r\n MapProvider,\r\n MenusProvider,\r\n ToolsProvider,\r\n ContentProvider,\r\n DatasetsProvider,\r\n MapSitesProvider,\r\n FilesProvider,\r\n BuildingsProvider,\r\n PointCloudProvider,\r\n PermissionsProvider,\r\n PluginHostProvider,\r\n])\r\n\r\nexport function AppProvider({ children, runtimeConfig }: { children: React.ReactNode, runtimeConfig?: RuntimeConfig }) {\r\n return (\r\n <AppConfigProvider runtimeConfig={runtimeConfig}>\r\n <InnerProviders>\r\n {children}\r\n </InnerProviders>\r\n </AppConfigProvider>\r\n )\r\n}\r\n"],"mappings":";AA0BY;AApBZ,SAAS,yBAA6C;AACtD,SAAS,mBAAmB;AAC5B,SAAS,uBAAuB;AAChC,SAAS,mBAAmB;AAC5B,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAC9B,SAAS,yBAAyB;AAClC,SAAS,wBAAwB;AACjC,SAAS,wBAAwB;AACjC,SAAS,0BAA0B;AACnC,SAAS,2BAA2B;AACpC,SAAS,0BAA0B;AAEnC,MAAM,UAAU,eACd,UAAU;AAAA,EACR,CAAC,MAAM,SACL,SAAS,iBAAiB,EAAE,SAAS,GAAG;AACtC,WACE,oBAAC,QACC,8BAAC,QAAM,UAAS,GAClB;AAAA,EAEJ;AACJ;AAEF,MAAM,iBAAiB,QAAQ;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,SAAS,YAAY,EAAE,UAAU,cAAc,GAAiE;AACrH,SACE,oBAAC,qBAAkB,eACjB,8BAAC,kBACE,UACH,GACF;AAEJ;","names":[]}
|