@collabdt/core 0.3.2 → 0.3.3

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 (145) hide show
  1. package/dist/core/components/ui/Comments/CollapsibleCommentItem.js.map +1 -1
  2. package/dist/core/components/ui/Comments/Comment.js.map +1 -1
  3. package/dist/core/components/ui/Comments/CommentActionButtons.d.ts +5 -1
  4. package/dist/core/components/ui/Comments/CommentActionButtons.d.ts.map +1 -1
  5. package/dist/core/components/ui/Comments/CommentActionButtons.js +37 -9
  6. package/dist/core/components/ui/Comments/CommentActionButtons.js.map +1 -1
  7. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts +1 -2
  8. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.d.ts.map +1 -1
  9. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js +66 -94
  10. package/dist/core/components/ui/Sensors/CollapsibleSensorItem.js.map +1 -1
  11. package/dist/core/components/ui/Sensors/Sensor.d.ts +16 -3
  12. package/dist/core/components/ui/Sensors/Sensor.d.ts.map +1 -1
  13. package/dist/core/components/ui/Sensors/Sensor.js +76 -37
  14. package/dist/core/components/ui/Sensors/Sensor.js.map +1 -1
  15. package/dist/core/components/ui/Sensors/SensorChart.d.ts +12 -2
  16. package/dist/core/components/ui/Sensors/SensorChart.d.ts.map +1 -1
  17. package/dist/core/components/ui/Sensors/SensorChart.js +49 -12
  18. package/dist/core/components/ui/Sensors/SensorChart.js.map +1 -1
  19. package/dist/core/components/ui/Sensors/SensorDetailDialog.d.ts +9 -0
  20. package/dist/core/components/ui/Sensors/SensorDetailDialog.d.ts.map +1 -0
  21. package/dist/core/components/ui/Sensors/SensorDetailDialog.js +128 -0
  22. package/dist/core/components/ui/Sensors/SensorDetailDialog.js.map +1 -0
  23. package/dist/core/components/ui/Sensors/SensorInput.d.ts +6 -1
  24. package/dist/core/components/ui/Sensors/SensorInput.d.ts.map +1 -1
  25. package/dist/core/components/ui/Sensors/SensorInput.js +94 -8
  26. package/dist/core/components/ui/Sensors/SensorInput.js.map +1 -1
  27. package/dist/core/components/ui/Sensors/SensorTagsSection.d.ts +3 -1
  28. package/dist/core/components/ui/Sensors/SensorTagsSection.d.ts.map +1 -1
  29. package/dist/core/components/ui/Sensors/SensorTagsSection.js +4 -1
  30. package/dist/core/components/ui/Sensors/SensorTagsSection.js.map +1 -1
  31. package/dist/core/components/ui/Sensors/SensorsSection.d.ts +1 -3
  32. package/dist/core/components/ui/Sensors/SensorsSection.d.ts.map +1 -1
  33. package/dist/core/components/ui/Sensors/SensorsSection.js +33 -8
  34. package/dist/core/components/ui/Sensors/SensorsSection.js.map +1 -1
  35. package/dist/core/components/ui/Sensors/TimeZoneSelect.d.ts +7 -0
  36. package/dist/core/components/ui/Sensors/TimeZoneSelect.d.ts.map +1 -0
  37. package/dist/core/components/ui/Sensors/TimeZoneSelect.js +56 -0
  38. package/dist/core/components/ui/Sensors/TimeZoneSelect.js.map +1 -0
  39. package/dist/core/components/ui/Sensors/sensorData.d.ts +42 -0
  40. package/dist/core/components/ui/Sensors/sensorData.d.ts.map +1 -0
  41. package/dist/core/components/ui/Sensors/sensorData.js +118 -0
  42. package/dist/core/components/ui/Sensors/sensorData.js.map +1 -0
  43. package/dist/core/components/ui/Sensors/sensorRange.d.ts +24 -0
  44. package/dist/core/components/ui/Sensors/sensorRange.d.ts.map +1 -0
  45. package/dist/core/components/ui/Sensors/sensorRange.js +34 -0
  46. package/dist/core/components/ui/Sensors/sensorRange.js.map +1 -0
  47. package/dist/core/components/ui/Sensors/useSensorSeries.d.ts +15 -0
  48. package/dist/core/components/ui/Sensors/useSensorSeries.d.ts.map +1 -0
  49. package/dist/core/components/ui/Sensors/useSensorSeries.js +63 -0
  50. package/dist/core/components/ui/Sensors/useSensorSeries.js.map +1 -0
  51. package/dist/core/components/viewers/Viewer.js +1 -1
  52. package/dist/core/components/viewers/Viewer.js.map +1 -1
  53. package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.d.ts +1 -3
  54. package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.d.ts.map +1 -1
  55. package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.js +2 -2
  56. package/dist/core/components/viewers/bim/src/BimSidebar/src/SensorsTab/index.js.map +1 -1
  57. package/dist/core/components/viewers/bim/src/BimSidebar/src/index.js +1 -1
  58. package/dist/core/components/viewers/bim/src/BimSidebar/src/index.js.map +1 -1
  59. package/dist/core/components/viewers/bim/src/tools/AddToBim/index.d.ts +1 -2
  60. package/dist/core/components/viewers/bim/src/tools/AddToBim/index.d.ts.map +1 -1
  61. package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js +25 -2
  62. package/dist/core/components/viewers/bim/src/tools/AddToBim/index.js.map +1 -1
  63. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimComment.js.map +1 -1
  64. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.d.ts +1 -22
  65. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.d.ts.map +1 -1
  66. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.js +2 -105
  67. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimCommentCluster.js.map +1 -1
  68. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.d.ts +8 -1
  69. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.d.ts.map +1 -1
  70. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.js +15 -3
  71. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.js.map +1 -1
  72. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.js.map +1 -1
  73. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.d.ts +2 -23
  74. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.d.ts.map +1 -1
  75. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.js +2 -24
  76. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.js.map +1 -1
  77. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.d.ts +33 -0
  78. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.d.ts.map +1 -0
  79. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.js +90 -0
  80. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.js.map +1 -0
  81. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.d.ts +7 -1
  82. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.d.ts.map +1 -1
  83. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.js +26 -230
  84. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.js.map +1 -1
  85. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts +7 -1
  86. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.d.ts.map +1 -1
  87. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js +39 -11
  88. package/dist/core/components/viewers/bim/src/tools/AddToBim/src/useSensorMarkers.js.map +1 -1
  89. package/dist/core/components/viewers/map/MapViewer.d.ts +1 -2
  90. package/dist/core/components/viewers/map/MapViewer.d.ts.map +1 -1
  91. package/dist/core/components/viewers/map/MapViewer.js +2 -2
  92. package/dist/core/components/viewers/map/MapViewer.js.map +1 -1
  93. package/dist/core/components/viewers/map/src/MapLayers/index.d.ts +1 -2
  94. package/dist/core/components/viewers/map/src/MapLayers/index.d.ts.map +1 -1
  95. package/dist/core/components/viewers/map/src/MapLayers/index.js +2 -2
  96. package/dist/core/components/viewers/map/src/MapLayers/index.js.map +1 -1
  97. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts +1 -3
  98. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.d.ts.map +1 -1
  99. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js +74 -39
  100. package/dist/core/components/viewers/map/src/MapLayers/src/SensorsLayer/index.js.map +1 -1
  101. package/dist/core/components/viewers/map/src/MapSidebar/index.js +1 -1
  102. package/dist/core/components/viewers/map/src/MapSidebar/index.js.map +1 -1
  103. package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.d.ts +1 -3
  104. package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.d.ts.map +1 -1
  105. package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.js +2 -2
  106. package/dist/core/components/viewers/map/src/MapSidebar/src/SensorsTab/index.js.map +1 -1
  107. package/dist/core/components/viewers/shared/markers/BimMarkerCluster.d.ts +19 -0
  108. package/dist/core/components/viewers/shared/markers/BimMarkerCluster.d.ts.map +1 -0
  109. package/dist/core/components/viewers/shared/markers/BimMarkerCluster.js +108 -0
  110. package/dist/core/components/viewers/shared/markers/BimMarkerCluster.js.map +1 -0
  111. package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.d.ts +24 -0
  112. package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.d.ts.map +1 -0
  113. package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.js +27 -0
  114. package/dist/core/components/viewers/shared/markers/computeMarkerLookAt.js.map +1 -0
  115. package/dist/core/components/viewers/shared/markers/types.d.ts +66 -0
  116. package/dist/core/components/viewers/shared/markers/types.d.ts.map +1 -0
  117. package/dist/core/components/viewers/shared/markers/types.js +1 -0
  118. package/dist/core/components/viewers/shared/markers/types.js.map +1 -0
  119. package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.d.ts +22 -0
  120. package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.d.ts.map +1 -0
  121. package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.js +233 -0
  122. package/dist/core/components/viewers/shared/markers/useMarkerLayerBim.js.map +1 -0
  123. package/dist/core/hooks/ui/useResizableSidebarWidth.js.map +1 -1
  124. package/dist/core/i18n/messages/en.json +5 -1
  125. package/dist/core/i18n/messages/es.json +5 -1
  126. package/dist/core/i18n/messages/fr.json +5 -1
  127. package/dist/core/store/AppConfig/context.d.ts.map +1 -1
  128. package/dist/core/store/AppConfig/context.js +4 -1
  129. package/dist/core/store/AppConfig/context.js.map +1 -1
  130. package/dist/core/store/AppConfig/reducer.d.ts +10 -0
  131. package/dist/core/store/AppConfig/reducer.d.ts.map +1 -1
  132. package/dist/core/store/AppConfig/reducer.js +10 -0
  133. package/dist/core/store/AppConfig/reducer.js.map +1 -1
  134. package/dist/core/store/Menus/context.d.ts.map +1 -1
  135. package/dist/core/store/Menus/context.js +5 -2
  136. package/dist/core/store/Menus/context.js.map +1 -1
  137. package/dist/core/store/Menus/reducer.d.ts +45 -1
  138. package/dist/core/store/Menus/reducer.d.ts.map +1 -1
  139. package/dist/core/store/Menus/reducer.js +21 -3
  140. package/dist/core/store/Menus/reducer.js.map +1 -1
  141. package/dist/core/utils/timeUtils.d.ts +24 -1
  142. package/dist/core/utils/timeUtils.d.ts.map +1 -1
  143. package/dist/core/utils/timeUtils.js +52 -3
  144. package/dist/core/utils/timeUtils.js.map +1 -1
  145. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"BimSensor.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AACvF,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAA;AAEnC,UAAU,WAAW;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,UAAU,CAAA;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,gBAAgB,CAAA;IAC5B,eAAe,EAAE,MAAM,CAAA;IACvB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,WAAW,EAAE,KAAK,CAAC,MAAM,CAAA;IACzB,WAAW,EAAE,KAAK,CAAC,OAAO,CAAA;IAC1B,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,IAAI,CAAA;IACf,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAA;IACtB,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAChC,UAAU,EACV,UAAU,EACV,IAAI,EACJ,OAAO,EACP,UAAU,EACV,eAAe,EACf,QAAQ,EACR,MAAM,EACN,OAAO,EACP,UAAU,EACV,SAAS,EACT,MAAM,EACN,SAAiB,GAClB,EAAE,WAAW,GAAG,KAAK,CAAC,YAAY,CAwBlC"}
1
+ {"version":3,"file":"BimSensor.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AACvF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oDAAoD,CAAC;AAC9F,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAA;AAEnC,UAAU,WAAW;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,UAAU,CAAA;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,gBAAgB,CAAA;IAC5B,eAAe,EAAE,MAAM,CAAA;IACvB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,WAAW,EAAE,KAAK,CAAC,MAAM,CAAA;IACzB,WAAW,EAAE,KAAK,CAAC,OAAO,CAAA;IAC1B,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,IAAI,CAAA;IACf,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAA;IACtB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,YAAY,CAAC,EAAE,mBAAmB,CAAA;CACnC;AAED,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAChC,UAAU,EACV,UAAU,EACV,IAAI,EACJ,OAAO,EACP,UAAU,EACV,eAAe,EACf,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,OAAO,EACP,UAAU,EACV,SAAS,EACT,SAAiB,EACjB,QAAQ,EACR,WAAmB,EACnB,OAAc,EACd,SAAgB,EAChB,YAAY,GACb,EAAE,WAAW,GAAG,KAAK,CAAC,YAAY,CA+BlC"}
@@ -11,11 +11,16 @@ function BimSensor({
11
11
  updateFrequency,
12
12
  onRemove,
13
13
  onEdit,
14
+ onExpand,
14
15
  onClose,
15
16
  buildingId,
16
17
  timestamp,
17
- sphere,
18
- highlight = false
18
+ highlight = false,
19
+ timeZone,
20
+ showActions = false,
21
+ canEdit = true,
22
+ canDelete = true,
23
+ actionLabels
19
24
  }) {
20
25
  return /* @__PURE__ */ jsx(
21
26
  "div",
@@ -34,11 +39,18 @@ function BimSensor({
34
39
  updateFrequency,
35
40
  createdAt: timestamp,
36
41
  onRemove,
42
+ onEdit,
43
+ onExpand,
37
44
  onClose,
45
+ showActions,
46
+ canEdit,
47
+ canDelete,
48
+ actionLabels,
38
49
  enableCollapse: true,
39
50
  defaultCollapsed: true,
40
51
  size: "sm",
41
- highlight
52
+ highlight,
53
+ timeZone
42
54
  }
43
55
  )
44
56
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.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\n\r\nimport Sensor from '../../../../../../ui/Sensors/Sensor'\r\n\r\nimport { markerStyle, markerStyleHighlight } from './markerUtils'\r\n\r\nimport type { SensorDataFormat, SensorType } from '../../../../../../../types/dbTypes';\r\nimport type * as THREE from 'three'\r\n\r\ninterface SensorProps {\r\n sensorName: string\r\n sensorType: SensorType\r\n dataUrl: string\r\n dataFormat: SensorDataFormat\r\n updateFrequency: number\r\n tags?: string[]\r\n worldCamera: THREE.Camera\r\n targetPoint: THREE.Vector3\r\n onRemove?: () => void\r\n onEdit?: () => void\r\n onClose: () => void\r\n buildingId?: number\r\n timestamp: Date\r\n sphere: THREE.Object3D\r\n highlight?: boolean\r\n}\r\n\r\nexport default function BimSensor({\r\n sensorName,\r\n sensorType,\r\n tags,\r\n dataUrl,\r\n dataFormat,\r\n updateFrequency,\r\n onRemove,\r\n onEdit,\r\n onClose,\r\n buildingId,\r\n timestamp,\r\n sphere,\r\n highlight = false,\r\n}: SensorProps): React.ReactElement {\r\n return (\r\n <div\r\n className={highlight ? markerStyleHighlight : markerStyle}\r\n >\r\n <Sensor\r\n buildingId={buildingId}\r\n sensorName={sensorName}\r\n sensorType={sensorType}\r\n tags={tags}\r\n tagsVariant=\"read-only\"\r\n dataUrl={dataUrl}\r\n dataFormat={dataFormat}\r\n updateFrequency={updateFrequency}\r\n createdAt={timestamp}\r\n onRemove={onRemove}\r\n onClose={onClose}\r\n enableCollapse\r\n defaultCollapsed\r\n size=\"sm\"\r\n highlight={highlight}\r\n />\r\n </div>\r\n )\r\n}"],"mappings":";AAmDM;AA5CN,OAAO,YAAY;AAEnB,SAAS,aAAa,4BAA4B;AAuBnC,SAAR,UAA2B;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AACd,GAAoC;AAClC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,YAAY,uBAAuB;AAAA,MAE9C;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,aAAY;AAAA,UACZ;AAAA,UACA;AAAA,UACA;AAAA,UACA,WAAW;AAAA,UACX;AAAA,UACA;AAAA,UACA,gBAAc;AAAA,UACd,kBAAgB;AAAA,UAChB,MAAK;AAAA,UACL;AAAA;AAAA,MACF;AAAA;AAAA,EACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/BimSensor.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\n\r\nimport Sensor from '../../../../../../ui/Sensors/Sensor'\r\n\r\nimport { markerStyle, markerStyleHighlight } from './markerUtils'\r\n\r\nimport type { SensorDataFormat, SensorType } from '../../../../../../../types/dbTypes';\r\nimport type { CommentActionLabels } from '../../../../../../ui/Comments/CommentActionButtons';\r\nimport type * as THREE from 'three'\r\n\r\ninterface SensorProps {\r\n sensorName: string\r\n sensorType: SensorType\r\n dataUrl: string\r\n dataFormat: SensorDataFormat\r\n updateFrequency: number\r\n tags?: string[]\r\n worldCamera: THREE.Camera\r\n targetPoint: THREE.Vector3\r\n onRemove?: () => void\r\n onEdit?: () => void\r\n onExpand?: () => void\r\n onClose: () => void\r\n buildingId?: number\r\n timestamp: Date\r\n sphere: THREE.Object3D\r\n highlight?: boolean\r\n timeZone?: string\r\n showActions?: boolean\r\n canEdit?: boolean\r\n canDelete?: boolean\r\n actionLabels?: CommentActionLabels\r\n}\r\n\r\nexport default function BimSensor({\r\n sensorName,\r\n sensorType,\r\n tags,\r\n dataUrl,\r\n dataFormat,\r\n updateFrequency,\r\n onRemove,\r\n onEdit,\r\n onExpand,\r\n onClose,\r\n buildingId,\r\n timestamp,\r\n highlight = false,\r\n timeZone,\r\n showActions = false,\r\n canEdit = true,\r\n canDelete = true,\r\n actionLabels,\r\n}: SensorProps): React.ReactElement {\r\n return (\r\n <div\r\n className={highlight ? markerStyleHighlight : markerStyle}\r\n >\r\n <Sensor\r\n buildingId={buildingId}\r\n sensorName={sensorName}\r\n sensorType={sensorType}\r\n tags={tags}\r\n tagsVariant=\"read-only\"\r\n dataUrl={dataUrl}\r\n dataFormat={dataFormat}\r\n updateFrequency={updateFrequency}\r\n createdAt={timestamp}\r\n onRemove={onRemove}\r\n onEdit={onEdit}\r\n onExpand={onExpand}\r\n onClose={onClose}\r\n showActions={showActions}\r\n canEdit={canEdit}\r\n canDelete={canDelete}\r\n actionLabels={actionLabels}\r\n enableCollapse\r\n defaultCollapsed\r\n size=\"sm\"\r\n highlight={highlight}\r\n timeZone={timeZone}\r\n />\r\n </div>\r\n )\r\n}\r\n"],"mappings":";AA+DM;AAxDN,OAAO,YAAY;AAEnB,SAAS,aAAa,4BAA4B;AA8BnC,SAAR,UAA2B;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA,cAAc;AAAA,EACd,UAAU;AAAA,EACV,YAAY;AAAA,EACZ;AACF,GAAoC;AAClC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,YAAY,uBAAuB;AAAA,MAE9C;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,aAAY;AAAA,UACZ;AAAA,UACA;AAAA,UACA;AAAA,UACA,WAAW;AAAA,UACX;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,gBAAc;AAAA,UACd,kBAAgB;AAAA,UAChB,MAAK;AAAA,UACL;AAAA,UACA;AAAA;AAAA,MACF;AAAA;AAAA,EACF;AAEJ;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n// Copyright (C) 2025 Collab Digital Twins\n\nimport * as THREE from 'three'\n\n/**\n * Screen-space clustering for 3D (BIM) comment markers.\n *\n * The BIM viewer renders each comment as an independent CSS2DObject at a world\n * position, so markers that are close together (or aligned along the camera\n * axis) overlap on screen. This groups markers whose projected screen positions\n * fall within `threshold` pixels of each other so the caller can draw a single\n * numbered cluster bubble instead of a pile of overlapping avatars.\n *\n * Pure and side-effect free (no scene mutation) so it can be unit tested.\n */\n\nexport interface HasWorldPosition {\n id: number\n x?: number | null\n y?: number | null\n z?: number | null\n}\n\nexport interface ScreenCluster<T> {\n /** Stable key derived from member ids (sorted) — used for registry diffing. */\n key: string\n screenX: number\n screenY: number\n worldCenter: { x: number; y: number; z: number }\n members: T[]\n}\n\nexport interface ClusterResult<T> {\n clusters: ScreenCluster<T>[]\n singles: T[]\n}\n\nexport interface ProjectedPoint {\n screenX: number\n screenY: number\n /** false when the point is behind the camera. */\n onScreen: boolean\n}\n\n/** Project a world point to pixel coordinates for the given camera/viewport. */\nexport function projectPoint(\n x: number,\n y: number,\n z: number,\n camera: THREE.Camera,\n width: number,\n height: number,\n): ProjectedPoint {\n const pos = new THREE.Vector3(x, y, z)\n // Camera-space z is positive when the point is behind the camera (it looks down -z).\n const cameraSpace = pos.clone().applyMatrix4(camera.matrixWorldInverse)\n const behind = cameraSpace.z >= 0\n\n const ndc = pos.project(camera)\n return {\n screenX: (ndc.x * 0.5 + 0.5) * width,\n screenY: (-ndc.y * 0.5 + 0.5) * height,\n onScreen: !behind,\n }\n}\n\nfunction average(values: number[]): number {\n if (values.length === 0) return 0\n return values.reduce((sum, v) => sum + v, 0) / values.length\n}\n\nexport function clusterMarkersByScreenSpace<T extends HasWorldPosition>(\n items: T[],\n camera: THREE.Camera,\n width: number,\n height: number,\n threshold = 44,\n): ClusterResult<T> {\n const projected = items\n .filter((it): it is T & { x: number; y: number; z: number } =>\n it.x != null && it.y != null && it.z != null,\n )\n .map((item) => ({ item, ...projectPoint(item.x, item.y, item.z, camera, width, height) }))\n\n const onScreen = projected.filter((p) => p.onScreen)\n const clusters: ScreenCluster<T>[] = []\n // Points behind the camera are never clustered; they fall through as singles.\n const singles: T[] = projected.filter((p) => !p.onScreen).map((p) => p.item)\n\n const used = new Array(onScreen.length).fill(false)\n\n for (let i = 0; i < onScreen.length; i++) {\n if (used[i]) continue\n used[i] = true\n const group = [onScreen[i]]\n\n for (let j = i + 1; j < onScreen.length; j++) {\n if (used[j]) continue\n const dx = onScreen[i].screenX - onScreen[j].screenX\n const dy = onScreen[i].screenY - onScreen[j].screenY\n if (Math.hypot(dx, dy) <= threshold) {\n used[j] = true\n group.push(onScreen[j])\n }\n }\n\n if (group.length === 1) {\n singles.push(group[0].item)\n continue\n }\n\n const members = group.map((g) => g.item)\n clusters.push({\n key: members.map((m) => m.id).sort((a, b) => a - b).join('-'),\n screenX: average(group.map((g) => g.screenX)),\n screenY: average(group.map((g) => g.screenY)),\n worldCenter: {\n x: average(members.map((m) => m.x as number)),\n y: average(members.map((m) => m.y as number)),\n z: average(members.map((m) => m.z as number)),\n },\n members,\n })\n }\n\n return { clusters, singles }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAGA,YAAY,WAAW;AA2ChB,SAAS,aACd,GACA,GACA,GACA,QACA,OACA,QACgB;AAChB,QAAM,MAAM,IAAI,MAAM,QAAQ,GAAG,GAAG,CAAC;AAErC,QAAM,cAAc,IAAI,MAAM,EAAE,aAAa,OAAO,kBAAkB;AACtE,QAAM,SAAS,YAAY,KAAK;AAEhC,QAAM,MAAM,IAAI,QAAQ,MAAM;AAC9B,SAAO;AAAA,IACL,UAAU,IAAI,IAAI,MAAM,OAAO;AAAA,IAC/B,UAAU,CAAC,IAAI,IAAI,MAAM,OAAO;AAAA,IAChC,UAAU,CAAC;AAAA,EACb;AACF;AAEA,SAAS,QAAQ,QAA0B;AACzC,MAAI,OAAO,WAAW,EAAG,QAAO;AAChC,SAAO,OAAO,OAAO,CAAC,KAAK,MAAM,MAAM,GAAG,CAAC,IAAI,OAAO;AACxD;AAEO,SAAS,4BACd,OACA,QACA,OACA,QACA,YAAY,IACM;AAClB,QAAM,YAAY,MACf;AAAA,IAAO,CAAC,OACP,GAAG,KAAK,QAAQ,GAAG,KAAK,QAAQ,GAAG,KAAK;AAAA,EAC1C,EACC,IAAI,CAAC,SAAU,iBAAE,QAAS,aAAa,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,OAAO,MAAM,EAAI;AAE3F,QAAM,WAAW,UAAU,OAAO,CAAC,MAAM,EAAE,QAAQ;AACnD,QAAM,WAA+B,CAAC;AAEtC,QAAM,UAAe,UAAU,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AAE3E,QAAM,OAAO,IAAI,MAAM,SAAS,MAAM,EAAE,KAAK,KAAK;AAElD,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,QAAI,KAAK,CAAC,EAAG;AACb,SAAK,CAAC,IAAI;AACV,UAAM,QAAQ,CAAC,SAAS,CAAC,CAAC;AAE1B,aAAS,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AAC5C,UAAI,KAAK,CAAC,EAAG;AACb,YAAM,KAAK,SAAS,CAAC,EAAE,UAAU,SAAS,CAAC,EAAE;AAC7C,YAAM,KAAK,SAAS,CAAC,EAAE,UAAU,SAAS,CAAC,EAAE;AAC7C,UAAI,KAAK,MAAM,IAAI,EAAE,KAAK,WAAW;AACnC,aAAK,CAAC,IAAI;AACV,cAAM,KAAK,SAAS,CAAC,CAAC;AAAA,MACxB;AAAA,IACF;AAEA,QAAI,MAAM,WAAW,GAAG;AACtB,cAAQ,KAAK,MAAM,CAAC,EAAE,IAAI;AAC1B;AAAA,IACF;AAEA,UAAM,UAAU,MAAM,IAAI,CAAC,MAAM,EAAE,IAAI;AACvC,aAAS,KAAK;AAAA,MACZ,KAAK,QAAQ,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC,EAAE,KAAK,GAAG;AAAA,MAC5D,SAAS,QAAQ,MAAM,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC;AAAA,MAC5C,SAAS,QAAQ,MAAM,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC;AAAA,MAC5C,aAAa;AAAA,QACX,GAAG,QAAQ,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAW,CAAC;AAAA,QAC5C,GAAG,QAAQ,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAW,CAAC;AAAA,QAC5C,GAAG,QAAQ,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAW,CAAC;AAAA,MAC9C;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO,EAAE,UAAU,QAAQ;AAC7B;","names":[]}
1
+ {"version":3,"sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/clusterMarkersByScreenSpace.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport * as THREE from 'three'\r\n\r\n/**\r\n * Screen-space clustering for 3D (BIM) comment markers.\r\n *\r\n * The BIM viewer renders each comment as an independent CSS2DObject at a world\r\n * position, so markers that are close together (or aligned along the camera\r\n * axis) overlap on screen. This groups markers whose projected screen positions\r\n * fall within `threshold` pixels of each other so the caller can draw a single\r\n * numbered cluster bubble instead of a pile of overlapping avatars.\r\n *\r\n * Pure and side-effect free (no scene mutation) so it can be unit tested.\r\n */\r\n\r\nexport interface HasWorldPosition {\r\n id: number\r\n x?: number | null\r\n y?: number | null\r\n z?: number | null\r\n}\r\n\r\nexport interface ScreenCluster<T> {\r\n /** Stable key derived from member ids (sorted) — used for registry diffing. */\r\n key: string\r\n screenX: number\r\n screenY: number\r\n worldCenter: { x: number; y: number; z: number }\r\n members: T[]\r\n}\r\n\r\nexport interface ClusterResult<T> {\r\n clusters: ScreenCluster<T>[]\r\n singles: T[]\r\n}\r\n\r\nexport interface ProjectedPoint {\r\n screenX: number\r\n screenY: number\r\n /** false when the point is behind the camera. */\r\n onScreen: boolean\r\n}\r\n\r\n/** Project a world point to pixel coordinates for the given camera/viewport. */\r\nexport function projectPoint(\r\n x: number,\r\n y: number,\r\n z: number,\r\n camera: THREE.Camera,\r\n width: number,\r\n height: number,\r\n): ProjectedPoint {\r\n const pos = new THREE.Vector3(x, y, z)\r\n // Camera-space z is positive when the point is behind the camera (it looks down -z).\r\n const cameraSpace = pos.clone().applyMatrix4(camera.matrixWorldInverse)\r\n const behind = cameraSpace.z >= 0\r\n\r\n const ndc = pos.project(camera)\r\n return {\r\n screenX: (ndc.x * 0.5 + 0.5) * width,\r\n screenY: (-ndc.y * 0.5 + 0.5) * height,\r\n onScreen: !behind,\r\n }\r\n}\r\n\r\nfunction average(values: number[]): number {\r\n if (values.length === 0) return 0\r\n return values.reduce((sum, v) => sum + v, 0) / values.length\r\n}\r\n\r\nexport function clusterMarkersByScreenSpace<T extends HasWorldPosition>(\r\n items: T[],\r\n camera: THREE.Camera,\r\n width: number,\r\n height: number,\r\n threshold = 44,\r\n): ClusterResult<T> {\r\n const projected = items\r\n .filter((it): it is T & { x: number; y: number; z: number } =>\r\n it.x != null && it.y != null && it.z != null,\r\n )\r\n .map((item) => ({ item, ...projectPoint(item.x, item.y, item.z, camera, width, height) }))\r\n\r\n const onScreen = projected.filter((p) => p.onScreen)\r\n const clusters: ScreenCluster<T>[] = []\r\n // Points behind the camera are never clustered; they fall through as singles.\r\n const singles: T[] = projected.filter((p) => !p.onScreen).map((p) => p.item)\r\n\r\n const used = new Array(onScreen.length).fill(false)\r\n\r\n for (let i = 0; i < onScreen.length; i++) {\r\n if (used[i]) continue\r\n used[i] = true\r\n const group = [onScreen[i]]\r\n\r\n for (let j = i + 1; j < onScreen.length; j++) {\r\n if (used[j]) continue\r\n const dx = onScreen[i].screenX - onScreen[j].screenX\r\n const dy = onScreen[i].screenY - onScreen[j].screenY\r\n if (Math.hypot(dx, dy) <= threshold) {\r\n used[j] = true\r\n group.push(onScreen[j])\r\n }\r\n }\r\n\r\n if (group.length === 1) {\r\n singles.push(group[0].item)\r\n continue\r\n }\r\n\r\n const members = group.map((g) => g.item)\r\n clusters.push({\r\n key: members.map((m) => m.id).sort((a, b) => a - b).join('-'),\r\n screenX: average(group.map((g) => g.screenX)),\r\n screenY: average(group.map((g) => g.screenY)),\r\n worldCenter: {\r\n x: average(members.map((m) => m.x as number)),\r\n y: average(members.map((m) => m.y as number)),\r\n z: average(members.map((m) => m.z as number)),\r\n },\r\n members,\r\n })\r\n }\r\n\r\n return { clusters, singles }\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;AAGA,YAAY,WAAW;AA2ChB,SAAS,aACd,GACA,GACA,GACA,QACA,OACA,QACgB;AAChB,QAAM,MAAM,IAAI,MAAM,QAAQ,GAAG,GAAG,CAAC;AAErC,QAAM,cAAc,IAAI,MAAM,EAAE,aAAa,OAAO,kBAAkB;AACtE,QAAM,SAAS,YAAY,KAAK;AAEhC,QAAM,MAAM,IAAI,QAAQ,MAAM;AAC9B,SAAO;AAAA,IACL,UAAU,IAAI,IAAI,MAAM,OAAO;AAAA,IAC/B,UAAU,CAAC,IAAI,IAAI,MAAM,OAAO;AAAA,IAChC,UAAU,CAAC;AAAA,EACb;AACF;AAEA,SAAS,QAAQ,QAA0B;AACzC,MAAI,OAAO,WAAW,EAAG,QAAO;AAChC,SAAO,OAAO,OAAO,CAAC,KAAK,MAAM,MAAM,GAAG,CAAC,IAAI,OAAO;AACxD;AAEO,SAAS,4BACd,OACA,QACA,OACA,QACA,YAAY,IACM;AAClB,QAAM,YAAY,MACf;AAAA,IAAO,CAAC,OACP,GAAG,KAAK,QAAQ,GAAG,KAAK,QAAQ,GAAG,KAAK;AAAA,EAC1C,EACC,IAAI,CAAC,SAAU,iBAAE,QAAS,aAAa,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,OAAO,MAAM,EAAI;AAE3F,QAAM,WAAW,UAAU,OAAO,CAAC,MAAM,EAAE,QAAQ;AACnD,QAAM,WAA+B,CAAC;AAEtC,QAAM,UAAe,UAAU,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AAE3E,QAAM,OAAO,IAAI,MAAM,SAAS,MAAM,EAAE,KAAK,KAAK;AAElD,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,QAAI,KAAK,CAAC,EAAG;AACb,SAAK,CAAC,IAAI;AACV,UAAM,QAAQ,CAAC,SAAS,CAAC,CAAC;AAE1B,aAAS,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AAC5C,UAAI,KAAK,CAAC,EAAG;AACb,YAAM,KAAK,SAAS,CAAC,EAAE,UAAU,SAAS,CAAC,EAAE;AAC7C,YAAM,KAAK,SAAS,CAAC,EAAE,UAAU,SAAS,CAAC,EAAE;AAC7C,UAAI,KAAK,MAAM,IAAI,EAAE,KAAK,WAAW;AACnC,aAAK,CAAC,IAAI;AACV,cAAM,KAAK,SAAS,CAAC,CAAC;AAAA,MACxB;AAAA,IACF;AAEA,QAAI,MAAM,WAAW,GAAG;AACtB,cAAQ,KAAK,MAAM,CAAC,EAAE,IAAI;AAC1B;AAAA,IACF;AAEA,UAAM,UAAU,MAAM,IAAI,CAAC,MAAM,EAAE,IAAI;AACvC,aAAS,KAAK;AAAA,MACZ,KAAK,QAAQ,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC,EAAE,KAAK,GAAG;AAAA,MAC5D,SAAS,QAAQ,MAAM,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC;AAAA,MAC5C,SAAS,QAAQ,MAAM,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC;AAAA,MAC5C,aAAa;AAAA,QACX,GAAG,QAAQ,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAW,CAAC;AAAA,QAC5C,GAAG,QAAQ,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAW,CAAC;AAAA,QAC5C,GAAG,QAAQ,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAW,CAAC;AAAA,MAC9C;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO,EAAE,UAAU,QAAQ;AAC7B;","names":[]}
@@ -1,24 +1,3 @@
1
- /** Minimal 3D vector shape so this helper stays free of a three.js import (easy to unit test). */
2
- export interface Vec3 {
3
- x: number;
4
- y: number;
5
- z: number;
6
- }
7
- /** Camera pose in the shape `camera-controls` `setLookAt` expects. */
8
- export interface LookAt {
9
- camX: number;
10
- camY: number;
11
- camZ: number;
12
- tarX: number;
13
- tarY: number;
14
- tarZ: number;
15
- }
16
- /**
17
- * Frame a comment without changing the current view direction: keep looking along
18
- * the same axis the user already has, but re-target the comment and pull the camera
19
- * to `distance` away from it. Used for double-click "zoom to comment".
20
- *
21
- * Pure and three.js-free so it can be unit tested with plain objects.
22
- */
23
- export declare function computeCommentLookAt(camPos: Vec3, target: Vec3, commentPoint: Vec3, distance?: number): LookAt;
1
+ export type { Vec3, LookAt } from "../../../../../shared/markers/computeMarkerLookAt";
2
+ export { computeMarkerLookAt as computeCommentLookAt } from "../../../../../shared/markers/computeMarkerLookAt";
24
3
  //# sourceMappingURL=commentCameraUtils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"commentCameraUtils.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.ts"],"names":[],"mappings":"AAGA,kGAAkG;AAClG,MAAM,WAAW,IAAI;IACnB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV;AAED,sEAAsE;AACtE,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb;AAID;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,IAAI,EACZ,MAAM,EAAE,IAAI,EACZ,YAAY,EAAE,IAAI,EAClB,QAAQ,SAAI,GACX,MAAM,CAyBR"}
1
+ {"version":3,"file":"commentCameraUtils.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.ts"],"names":[],"mappings":"AAIA,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,mDAAmD,CAAA;AACrF,OAAO,EAAE,mBAAmB,IAAI,oBAAoB,EAAE,MAAM,mDAAmD,CAAA"}
@@ -1,27 +1,5 @@
1
- const DEFAULT_DIRECTION = { x: 1, y: 1, z: 1 };
2
- function computeCommentLookAt(camPos, target, commentPoint, distance = 8) {
3
- let dx = camPos.x - target.x;
4
- let dy = camPos.y - target.y;
5
- let dz = camPos.z - target.z;
6
- let length = Math.hypot(dx, dy, dz);
7
- if (length < 1e-6) {
8
- dx = DEFAULT_DIRECTION.x;
9
- dy = DEFAULT_DIRECTION.y;
10
- dz = DEFAULT_DIRECTION.z;
11
- length = Math.hypot(dx, dy, dz);
12
- }
13
- const safeDistance = distance > 0 ? distance : 8;
14
- const scale = safeDistance / length;
15
- return {
16
- camX: commentPoint.x + dx * scale,
17
- camY: commentPoint.y + dy * scale,
18
- camZ: commentPoint.z + dz * scale,
19
- tarX: commentPoint.x,
20
- tarY: commentPoint.y,
21
- tarZ: commentPoint.z
22
- };
23
- }
1
+ import { computeMarkerLookAt } from "../../../../../shared/markers/computeMarkerLookAt";
24
2
  export {
25
- computeCommentLookAt
3
+ computeMarkerLookAt as computeCommentLookAt
26
4
  };
27
5
  //# sourceMappingURL=commentCameraUtils.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n// Copyright (C) 2025 Collab Digital Twins\n\n/** Minimal 3D vector shape so this helper stays free of a three.js import (easy to unit test). */\nexport interface Vec3 {\n x: number\n y: number\n z: number\n}\n\n/** Camera pose in the shape `camera-controls` `setLookAt` expects. */\nexport interface LookAt {\n camX: number\n camY: number\n camZ: number\n tarX: number\n tarY: number\n tarZ: number\n}\n\nconst DEFAULT_DIRECTION: Vec3 = { x: 1, y: 1, z: 1 }\n\n/**\n * Frame a comment without changing the current view direction: keep looking along\n * the same axis the user already has, but re-target the comment and pull the camera\n * to `distance` away from it. Used for double-click \"zoom to comment\".\n *\n * Pure and three.js-free so it can be unit tested with plain objects.\n */\nexport function computeCommentLookAt(\n camPos: Vec3,\n target: Vec3,\n commentPoint: Vec3,\n distance = 8,\n): LookAt {\n let dx = camPos.x - target.x\n let dy = camPos.y - target.y\n let dz = camPos.z - target.z\n let length = Math.hypot(dx, dy, dz)\n\n // Degenerate pose (camera sitting on its target): fall back to a sensible angle.\n if (length < 1e-6) {\n dx = DEFAULT_DIRECTION.x\n dy = DEFAULT_DIRECTION.y\n dz = DEFAULT_DIRECTION.z\n length = Math.hypot(dx, dy, dz)\n }\n\n const safeDistance = distance > 0 ? distance : 8\n const scale = safeDistance / length\n\n return {\n camX: commentPoint.x + dx * scale,\n camY: commentPoint.y + dy * scale,\n camZ: commentPoint.z + dz * scale,\n tarX: commentPoint.x,\n tarY: commentPoint.y,\n tarZ: commentPoint.z,\n }\n}\n"],"mappings":"AAoBA,MAAM,oBAA0B,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,EAAE;AAS5C,SAAS,qBACd,QACA,QACA,cACA,WAAW,GACH;AACR,MAAI,KAAK,OAAO,IAAI,OAAO;AAC3B,MAAI,KAAK,OAAO,IAAI,OAAO;AAC3B,MAAI,KAAK,OAAO,IAAI,OAAO;AAC3B,MAAI,SAAS,KAAK,MAAM,IAAI,IAAI,EAAE;AAGlC,MAAI,SAAS,MAAM;AACjB,SAAK,kBAAkB;AACvB,SAAK,kBAAkB;AACvB,SAAK,kBAAkB;AACvB,aAAS,KAAK,MAAM,IAAI,IAAI,EAAE;AAAA,EAChC;AAEA,QAAM,eAAe,WAAW,IAAI,WAAW;AAC/C,QAAM,QAAQ,eAAe;AAE7B,SAAO;AAAA,IACL,MAAM,aAAa,IAAI,KAAK;AAAA,IAC5B,MAAM,aAAa,IAAI,KAAK;AAAA,IAC5B,MAAM,aAAa,IAAI,KAAK;AAAA,IAC5B,MAAM,aAAa;AAAA,IACnB,MAAM,aAAa;AAAA,IACnB,MAAM,aAAa;AAAA,EACrB;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/commentCameraUtils.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\n// Re-export shim: logic moved to the shared marker framework.\r\nexport type { Vec3, LookAt } from \"../../../../../shared/markers/computeMarkerLookAt\"\r\nexport { computeMarkerLookAt as computeCommentLookAt } from \"../../../../../shared/markers/computeMarkerLookAt\"\r\n"],"mappings":"AAKA,SAAgC,2BAA4B;","names":[]}
@@ -0,0 +1,33 @@
1
+ import type { BimMarkerConfig } from "../../../../../shared/markers/types";
2
+ /** A BIM comment enriched with author display fields, shaped for the marker layer. */
3
+ export interface CommentItem {
4
+ id: number;
5
+ x: number;
6
+ y: number;
7
+ z: number;
8
+ /** Omitted on the pending (being-created) placeholder — never read before a comment is real. */
9
+ authorId?: number;
10
+ authorName: string;
11
+ imageFileId: number | null;
12
+ image: number | string | null;
13
+ text: string;
14
+ createdAt: string | Date;
15
+ buildingId?: number | null;
16
+ isPending?: boolean;
17
+ }
18
+ interface CommentMarkerDeps {
19
+ currentUserId: string | null;
20
+ onRemove: (id: number) => void;
21
+ /** Reply is comment-only — the generic marker `ctx` has no reply handler, so the wrapper injects it. */
22
+ onReply: (id: number) => void;
23
+ /** Building context closed over for the pending placeholder item. */
24
+ buildingId: number;
25
+ }
26
+ /**
27
+ * Builds the comment-specific `BimMarkerConfig` consumed by `useMarkerLayerBim`. A factory
28
+ * (not a plain object) because `isAuthor`, `onRemove`, `onReply` and the pending placeholder's
29
+ * `buildingId` all need runtime values supplied by the `useCommentMarkers` wrapper.
30
+ */
31
+ export declare function makeCommentMarkerConfig(deps: CommentMarkerDeps): BimMarkerConfig<CommentItem>;
32
+ export {};
33
+ //# sourceMappingURL=commentMarkerConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commentMarkerConfig.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAA;AAE1E,sFAAsF;AACtF,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,gGAAgG;IAChG,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,UAAU,iBAAiB;IACzB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,wGAAwG;IACxG,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,qEAAqE;IACrE,UAAU,EAAE,MAAM,CAAA;CACnB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,iBAAiB,GAAG,eAAe,CAAC,WAAW,CAAC,CAkE7F"}
@@ -0,0 +1,90 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ import { BimMarkerCluster } from "../../../../../shared/markers/BimMarkerCluster";
21
+ import BimComment from "./BimComment";
22
+ function makeCommentMarkerConfig(deps) {
23
+ const { currentUserId, onRemove, onReply, buildingId } = deps;
24
+ return {
25
+ markerIdKey: "commentId",
26
+ sphereColor: "white",
27
+ focusDistance: 8,
28
+ component: BimComment,
29
+ clusterComponent: BimMarkerCluster,
30
+ // Exact reproduction of the pre-refactor `makeCommentPropsMapper`. Any user can select/focus/
31
+ // reply to any comment (showActions/onSelect/onFocus/onReply are NOT author-gated) — only
32
+ // canEdit/canDelete/onEdit/onRemove are, matching the original conditional structure.
33
+ propsMapper: (comment, ctx) => {
34
+ var _a, _b;
35
+ const base = {
36
+ userName: comment.authorName,
37
+ userImageFileId: (_a = comment.imageFileId) != null ? _a : null,
38
+ userImage: (_b = comment.imageFileId) != null ? _b : null,
39
+ buildingId: comment.buildingId,
40
+ timestamp: new Date(comment.createdAt),
41
+ text: comment.text,
42
+ isPending: comment.isPending || false,
43
+ highlight: ctx.highlight,
44
+ focused: ctx.focused
45
+ };
46
+ if (comment.isPending) return base;
47
+ return __spreadProps(__spreadValues({}, base), {
48
+ showActions: true,
49
+ canEdit: ctx.isAuthor,
50
+ canDelete: ctx.isAuthor,
51
+ onSelect: ctx.onSelect,
52
+ onFocus: ctx.onFocus,
53
+ onReply: () => onReply(comment.id),
54
+ onEdit: ctx.isAuthor ? ctx.onEdit : void 0,
55
+ onRemove: ctx.isAuthor ? ctx.onRemove : void 0
56
+ });
57
+ },
58
+ clusterMemberMapper: (c) => {
59
+ var _a;
60
+ return {
61
+ id: c.id,
62
+ userName: c.authorName,
63
+ imageFileId: (_a = c.imageFileId) != null ? _a : null
64
+ };
65
+ },
66
+ dataHashPart: (c) => {
67
+ var _a;
68
+ return `${c.id}:${(_a = c.text) != null ? _a : ""}:${c.isPending ? 1 : 0}`;
69
+ },
70
+ isAuthor: (c) => currentUserId != null && String(c.authorId) === currentUserId,
71
+ onRemove,
72
+ makePendingItem: (id, pos) => ({
73
+ id,
74
+ x: pos.x,
75
+ y: pos.y,
76
+ z: pos.z,
77
+ authorName: "Loading...",
78
+ imageFileId: null,
79
+ image: "",
80
+ text: "Creating comment...",
81
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
82
+ buildingId,
83
+ isPending: true
84
+ })
85
+ };
86
+ }
87
+ export {
88
+ makeCommentMarkerConfig
89
+ };
90
+ //# sourceMappingURL=commentMarkerConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/commentMarkerConfig.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\r\n// Copyright (C) 2025 Collab Digital Twins\r\n\r\nimport { BimMarkerCluster } from \"../../../../../shared/markers/BimMarkerCluster\"\r\n\r\nimport BimComment from \"./BimComment\"\r\n\r\nimport type { BimMarkerConfig } from \"../../../../../shared/markers/types\"\r\n\r\n/** A BIM comment enriched with author display fields, shaped for the marker layer. */\r\nexport interface CommentItem {\r\n id: number\r\n x: number\r\n y: number\r\n z: number\r\n /** Omitted on the pending (being-created) placeholder — never read before a comment is real. */\r\n authorId?: number\r\n authorName: string\r\n imageFileId: number | null\r\n image: number | string | null\r\n text: string\r\n createdAt: string | Date\r\n buildingId?: number | null\r\n isPending?: boolean\r\n}\r\n\r\ninterface CommentMarkerDeps {\r\n currentUserId: string | null\r\n onRemove: (id: number) => void\r\n /** Reply is comment-only — the generic marker `ctx` has no reply handler, so the wrapper injects it. */\r\n onReply: (id: number) => void\r\n /** Building context closed over for the pending placeholder item. */\r\n buildingId: number\r\n}\r\n\r\n/**\r\n * Builds the comment-specific `BimMarkerConfig` consumed by `useMarkerLayerBim`. A factory\r\n * (not a plain object) because `isAuthor`, `onRemove`, `onReply` and the pending placeholder's\r\n * `buildingId` all need runtime values supplied by the `useCommentMarkers` wrapper.\r\n */\r\nexport function makeCommentMarkerConfig(deps: CommentMarkerDeps): BimMarkerConfig<CommentItem> {\r\n const { currentUserId, onRemove, onReply, buildingId } = deps\r\n\r\n return {\r\n markerIdKey: \"commentId\",\r\n sphereColor: \"white\",\r\n focusDistance: 8,\r\n component: BimComment,\r\n clusterComponent: BimMarkerCluster,\r\n\r\n // Exact reproduction of the pre-refactor `makeCommentPropsMapper`. Any user can select/focus/\r\n // reply to any comment (showActions/onSelect/onFocus/onReply are NOT author-gated) — only\r\n // canEdit/canDelete/onEdit/onRemove are, matching the original conditional structure.\r\n propsMapper: (comment, ctx) => {\r\n const base = {\r\n userName: comment.authorName,\r\n userImageFileId: comment.imageFileId ?? null,\r\n userImage: comment.imageFileId ?? null,\r\n buildingId: comment.buildingId,\r\n timestamp: new Date(comment.createdAt),\r\n text: comment.text,\r\n isPending: comment.isPending || false,\r\n highlight: ctx.highlight,\r\n focused: ctx.focused,\r\n }\r\n if (comment.isPending) return base\r\n\r\n return {\r\n ...base,\r\n showActions: true,\r\n canEdit: ctx.isAuthor,\r\n canDelete: ctx.isAuthor,\r\n onSelect: ctx.onSelect,\r\n onFocus: ctx.onFocus,\r\n onReply: () => onReply(comment.id),\r\n onEdit: ctx.isAuthor ? ctx.onEdit : undefined,\r\n onRemove: ctx.isAuthor ? ctx.onRemove : undefined,\r\n }\r\n },\r\n\r\n clusterMemberMapper: (c) => ({\r\n id: c.id,\r\n userName: c.authorName,\r\n imageFileId: c.imageFileId ?? null,\r\n }),\r\n\r\n dataHashPart: (c) => `${c.id}:${c.text ?? \"\"}:${c.isPending ? 1 : 0}`,\r\n\r\n isAuthor: (c) => currentUserId != null && String(c.authorId) === currentUserId,\r\n\r\n onRemove,\r\n\r\n makePendingItem: (id, pos) => ({\r\n id,\r\n x: pos.x,\r\n y: pos.y,\r\n z: pos.z,\r\n authorName: \"Loading...\",\r\n imageFileId: null,\r\n image: \"\",\r\n text: \"Creating comment...\",\r\n createdAt: new Date().toISOString(),\r\n buildingId,\r\n isPending: true,\r\n }),\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAGA,SAAS,wBAAwB;AAEjC,OAAO,gBAAgB;AAmChB,SAAS,wBAAwB,MAAuD;AAC7F,QAAM,EAAE,eAAe,UAAU,SAAS,WAAW,IAAI;AAEzD,SAAO;AAAA,IACL,aAAa;AAAA,IACb,aAAa;AAAA,IACb,eAAe;AAAA,IACf,WAAW;AAAA,IACX,kBAAkB;AAAA;AAAA;AAAA;AAAA,IAKlB,aAAa,CAAC,SAAS,QAAQ;AArDnC;AAsDM,YAAM,OAAO;AAAA,QACX,UAAU,QAAQ;AAAA,QAClB,kBAAiB,aAAQ,gBAAR,YAAuB;AAAA,QACxC,YAAW,aAAQ,gBAAR,YAAuB;AAAA,QAClC,YAAY,QAAQ;AAAA,QACpB,WAAW,IAAI,KAAK,QAAQ,SAAS;AAAA,QACrC,MAAM,QAAQ;AAAA,QACd,WAAW,QAAQ,aAAa;AAAA,QAChC,WAAW,IAAI;AAAA,QACf,SAAS,IAAI;AAAA,MACf;AACA,UAAI,QAAQ,UAAW,QAAO;AAE9B,aAAO,iCACF,OADE;AAAA,QAEL,aAAa;AAAA,QACb,SAAS,IAAI;AAAA,QACb,WAAW,IAAI;AAAA,QACf,UAAU,IAAI;AAAA,QACd,SAAS,IAAI;AAAA,QACb,SAAS,MAAM,QAAQ,QAAQ,EAAE;AAAA,QACjC,QAAQ,IAAI,WAAW,IAAI,SAAS;AAAA,QACpC,UAAU,IAAI,WAAW,IAAI,WAAW;AAAA,MAC1C;AAAA,IACF;AAAA,IAEA,qBAAqB,CAAC,MAAG;AAhF7B;AAgFiC;AAAA,QAC3B,IAAI,EAAE;AAAA,QACN,UAAU,EAAE;AAAA,QACZ,cAAa,OAAE,gBAAF,YAAiB;AAAA,MAChC;AAAA;AAAA,IAEA,cAAc,CAAC,MAAG;AAtFtB;AAsFyB,gBAAG,EAAE,EAAE,KAAI,OAAE,SAAF,YAAU,EAAE,IAAI,EAAE,YAAY,IAAI,CAAC;AAAA;AAAA,IAEnE,UAAU,CAAC,MAAM,iBAAiB,QAAQ,OAAO,EAAE,QAAQ,MAAM;AAAA,IAEjE;AAAA,IAEA,iBAAiB,CAAC,IAAI,SAAS;AAAA,MAC7B;AAAA,MACA,GAAG,IAAI;AAAA,MACP,GAAG,IAAI;AAAA,MACP,GAAG,IAAI;AAAA,MACP,YAAY;AAAA,MACZ,aAAa;AAAA,MACb,OAAO;AAAA,MACP,MAAM;AAAA,MACN,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC;AAAA,MACA,WAAW;AAAA,IACb;AAAA,EACF;AACF;","names":[]}
@@ -1,5 +1,11 @@
1
+ /**
2
+ * Thin wrapper over the generic `useMarkerLayerBim`: owns comment data fetching, the
3
+ * author-cascade delete, and the sidebar reply/edit handoff, then hands rendering off to the
4
+ * shared marker layer via `commentMarkerConfig`. Signature and return shape are unchanged —
5
+ * `AddToBim/index.tsx` consumes `{ addPendingComment, removePendingComment, commentCount }`.
6
+ */
1
7
  export declare function useCommentMarkers(world: any, buildingId: number): {
2
- addPendingComment: (position: {
8
+ addPendingComment: (pos: {
3
9
  x: number;
4
10
  y: number;
5
11
  z: number;
@@ -1 +1 @@
1
- {"version":3,"file":"useCommentMarkers.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.ts"],"names":[],"mappings":"AAkNA,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM;kCAsEP;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE;+BAMtC,MAAM;;EAgF3D"}
1
+ {"version":3,"file":"useCommentMarkers.d.ts","sourceRoot":"","sources":["../../../../../../../../../src/core/components/viewers/bim/src/tools/AddToBim/src/useCommentMarkers.ts"],"names":[],"mappings":"AAmBA;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM;;;;;;;;EAmF/D"}
@@ -20,163 +20,17 @@ var __spreadValues = (a, b) => {
20
20
  var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
21
21
  import { useSession } from "next-auth/react";
22
22
  import * as React from "react";
23
- import { createRoot } from "react-dom/client";
24
- import * as THREE from "three";
25
- import { CSS2DObject } from "three/addons/renderers/CSS2DRenderer.js";
26
23
  import { useComments, useDeleteComments } from "../../../../../../../hooks/comments/comments";
27
- import { HooksCtx, useCoreHooks } from "../../../../../../../hooks/provider";
24
+ import { useCoreHooks } from "../../../../../../../hooks/provider";
28
25
  import { useUsers } from "../../../../../../../hooks/users/users";
29
26
  import { MenusContext } from "../../../../../../../store";
30
27
  import { ViewerNames } from "../../../../../../../types/dbTypes";
31
- import BimComment from "./BimComment";
32
- import BimCommentCluster from "./BimCommentCluster";
33
- import { clusterMarkersByScreenSpace } from "./clusterMarkersByScreenSpace";
34
- import { computeCommentLookAt } from "./commentCameraUtils";
35
- import { renderCSS2DMarkers } from "./renderCSS2DMarkers";
36
- function makeCommentPropsMapper(state) {
37
- const { currentCommentId, focusedCommentId, currentUserId, onSelect, onFocus, onEdit, onReply, onRemove } = state;
38
- return (comment) => {
39
- var _a, _b;
40
- const base = {
41
- userName: comment.authorName,
42
- userImageFileId: (_a = comment.imageFileId) != null ? _a : null,
43
- userImage: (_b = comment.imageFileId) != null ? _b : null,
44
- buildingId: comment.buildingId,
45
- timestamp: new Date(comment.createdAt),
46
- text: comment.text,
47
- isPending: comment.isPending || false,
48
- highlight: currentCommentId === comment.id,
49
- focused: focusedCommentId === comment.id
50
- };
51
- if (comment.isPending) return base;
52
- const isAuthor = currentUserId != null && String(comment.authorId) === currentUserId;
53
- return __spreadProps(__spreadValues({}, base), {
54
- showActions: true,
55
- canEdit: isAuthor,
56
- canDelete: isAuthor,
57
- onSelect: () => onSelect(comment.id),
58
- onFocus: () => onFocus(comment.id),
59
- onReply: () => onReply(comment.id),
60
- onEdit: isAuthor ? () => onEdit(comment.id) : void 0,
61
- onRemove: isAuthor ? () => onRemove(comment.id) : void 0
62
- });
63
- };
64
- }
65
- function clearSingles(world, registry) {
66
- renderCSS2DMarkers(world, {
67
- items: [],
68
- markerIdKey: "commentId",
69
- registry,
70
- component: BimComment,
71
- propsMapper: () => ({}),
72
- sphereColor: "white",
73
- isVisible: false,
74
- onRemove: () => {
75
- }
76
- });
77
- }
78
- function clearClusters(world, registry) {
79
- registry.current.forEach(({ root, css2dObject }) => {
80
- world.scene.three.remove(css2dObject);
81
- const el = css2dObject.element;
82
- el == null ? void 0 : el.remove();
83
- root.unmount();
84
- });
85
- registry.current.clear();
86
- }
87
- function renderMarkers(state, singleRegistry, clusterRegistry, lastSignatureRef) {
88
- var _a, _b, _c;
89
- const { world, items, pending, isVisible, currentCommentId, focusedCommentId, coreHooks, onRemove, onSelect, onFocus } = state;
90
- if (!world) return;
91
- if (!isVisible) {
92
- if (lastSignatureRef.current === "hidden") return;
93
- clearSingles(world, singleRegistry);
94
- clearClusters(world, clusterRegistry);
95
- lastSignatureRef.current = "hidden";
96
- return;
97
- }
98
- const camera = (_a = world.camera) == null ? void 0 : _a.three;
99
- const dom = (_c = (_b = world.renderer) == null ? void 0 : _b.three) == null ? void 0 : _c.domElement;
100
- if (!camera || !dom) return;
101
- const width = dom.clientWidth || dom.width || 0;
102
- const height = dom.clientHeight || dom.height || 0;
103
- if (!width || !height) return;
104
- const { clusters, singles } = clusterMarkersByScreenSpace(items, camera, width, height, 44);
105
- const singleItems = [...singles, ...pending];
106
- const clusterKeys = clusters.map((c) => c.key);
107
- const dataHash = [...items, ...pending].map((c) => {
108
- var _a2;
109
- return `${c.id}:${(_a2 = c.text) != null ? _a2 : ""}:${c.isPending ? 1 : 0}`;
110
- }).join(";");
111
- const signature = `v2|${currentCommentId}|${focusedCommentId}|C:${clusterKeys.join(",")}|S:${singleItems.map((s) => s.id).join(",")}|D:${dataHash}`;
112
- if (signature === lastSignatureRef.current) return;
113
- lastSignatureRef.current = signature;
114
- renderCSS2DMarkers(world, {
115
- items: singleItems,
116
- markerIdKey: "commentId",
117
- registry: singleRegistry,
118
- component: BimComment,
119
- hooksContextValue: coreHooks,
120
- propsMapper: makeCommentPropsMapper(state),
121
- sphereColor: "white",
122
- isVisible: true,
123
- onRemove
124
- });
125
- singleRegistry.current.forEach((ref, id) => {
126
- const nid = Number(id);
127
- ref.css2dObject.renderOrder = nid === currentCommentId || nid === focusedCommentId ? 1 : 0;
128
- });
129
- const reg = clusterRegistry.current;
130
- const wanted = new Set(clusterKeys);
131
- for (const [key, ref] of reg) {
132
- if (!wanted.has(key)) {
133
- world.scene.three.remove(ref.css2dObject);
134
- const el = ref.css2dObject.element;
135
- el == null ? void 0 : el.remove();
136
- ref.root.unmount();
137
- reg.delete(key);
138
- }
139
- }
140
- for (const cluster of clusters) {
141
- const members = cluster.members.map((m) => {
142
- var _a2;
143
- return {
144
- id: m.id,
145
- userName: m.authorName,
146
- imageFileId: (_a2 = m.imageFileId) != null ? _a2 : null
147
- };
148
- });
149
- const highlight = currentCommentId != null && members.some((m) => m.id === currentCommentId);
150
- const element = React.createElement(BimCommentCluster, { members, highlight, onSelect, onFocus });
151
- const wrapped = coreHooks ? React.createElement(HooksCtx.Provider, { value: coreHooks }, element) : element;
152
- const existing = reg.get(cluster.key);
153
- if (existing) {
154
- existing.root.render(wrapped);
155
- existing.css2dObject.position.set(cluster.worldCenter.x, cluster.worldCenter.y + 0.2, cluster.worldCenter.z);
156
- existing.css2dObject.renderOrder = highlight ? 1 : 0;
157
- continue;
158
- }
159
- const labelDiv = document.createElement("div");
160
- labelDiv.style.pointerEvents = "auto";
161
- const root = createRoot(labelDiv);
162
- root.render(wrapped);
163
- const css2dObject = new CSS2DObject(labelDiv);
164
- css2dObject.position.set(cluster.worldCenter.x, cluster.worldCenter.y + 0.2, cluster.worldCenter.z);
165
- css2dObject.element.style.pointerEvents = "auto";
166
- css2dObject.renderOrder = highlight ? 1 : 0;
167
- world.scene.three.add(css2dObject);
168
- reg.set(cluster.key, { root, css2dObject });
169
- }
170
- }
28
+ import { useMarkerLayerBim } from "../../../../../shared/markers/useMarkerLayerBim";
29
+ import { makeCommentMarkerConfig } from "./commentMarkerConfig";
171
30
  function useCommentMarkers(world, buildingId) {
172
31
  var _a, _b, _c, _d;
173
32
  const { state: menusState, dispatch: menusDispatch, setIsSidebarOpen } = React.useContext(MenusContext);
174
33
  const { commentsVisibleInViewer, currentCommentId, focusedCommentId, focusRequestId } = menusState.menus;
175
- const singleRegistry = React.useRef(/* @__PURE__ */ new Map());
176
- const clusterRegistry = React.useRef(/* @__PURE__ */ new Map());
177
- const lastSignatureRef = React.useRef("");
178
- const stateRef = React.useRef(null);
179
- const commentsRef = React.useRef([]);
180
34
  const { comments } = useComments();
181
35
  const { users } = useUsers();
182
36
  const coreHooks = useCoreHooks();
@@ -197,105 +51,47 @@ function useCommentMarkers(world, buildingId) {
197
51
  image: (_c2 = user == null ? void 0 : user.imageFileId) != null ? _c2 : null
198
52
  });
199
53
  });
200
- commentsRef.current = eligibleComments;
201
54
  const handleRemoveComment = React.useCallback((commentId) => {
202
55
  const replyIds = comments.filter((c) => c.replyToId === commentId).map((c) => c.id);
203
56
  void deleteComments({ ids: [commentId, ...replyIds] });
204
57
  }, [comments, deleteComments]);
205
- const handleSelectComment = React.useCallback((commentId) => {
206
- menusDispatch({ type: "SET_CURRENT_COMMENT_ID", payload: { commentId } });
207
- }, [menusDispatch]);
208
- const handleFocusComment = React.useCallback((commentId) => {
209
- menusDispatch({ type: "SET_CURRENT_COMMENT_ID", payload: { commentId } });
210
- menusDispatch({ type: "SET_FOCUSED_COMMENT_ID", payload: { commentId } });
211
- }, [menusDispatch]);
212
58
  const requestSidebarAction = React.useCallback((commentId, action) => {
213
59
  setIsSidebarOpen(true);
214
60
  menusDispatch({ type: "SET_SIDEBAR_SELECTED_TAB", payload: { selectedTab: "communication" } });
215
61
  menusDispatch({ type: "SET_CURRENT_COMMENT_ID", payload: { commentId } });
216
62
  menusDispatch({ type: "REQUEST_COMMENT_ACTION", payload: { commentId, action } });
217
63
  }, [menusDispatch, setIsSidebarOpen]);
218
- const handleEditComment = React.useCallback((commentId) => {
219
- requestSidebarAction(commentId, "edit");
220
- }, [requestSidebarAction]);
221
64
  const handleReplyComment = React.useCallback((commentId) => {
222
65
  requestSidebarAction(commentId, "reply");
223
66
  }, [requestSidebarAction]);
224
- const [pendingComments, setPendingComments] = React.useState([]);
225
- const addPendingComment = React.useCallback((position) => {
226
- const id = -Date.now();
227
- setPendingComments((prev) => [...prev, __spreadValues({ id }, position)]);
228
- return id;
229
- }, []);
230
- const removePendingComment = React.useCallback((id) => {
231
- setPendingComments((prev) => prev.filter((p) => p.id !== id));
232
- }, []);
233
- const isVisible = (_d = commentsVisibleInViewer == null ? void 0 : commentsVisibleInViewer.includes(ViewerNames.bim)) != null ? _d : false;
234
- stateRef.current = {
235
- world,
236
- items: eligibleComments.map((c) => __spreadProps(__spreadValues({}, c), { isPending: false })),
237
- pending: pendingComments.map((p) => ({
238
- id: p.id,
239
- x: p.x,
240
- y: p.y,
241
- z: p.z,
242
- authorName: "Loading...",
243
- imageFileId: null,
244
- image: "",
245
- text: "Creating comment...",
246
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
247
- buildingId,
248
- isPending: true
249
- })),
250
- isVisible,
251
- currentCommentId,
252
- focusedCommentId,
67
+ const store = {
68
+ currentId: currentCommentId,
69
+ focusedId: focusedCommentId,
70
+ focusRequestId,
71
+ setCurrentId: (id) => menusDispatch({ type: "SET_CURRENT_COMMENT_ID", payload: { commentId: id } }),
72
+ setFocusedId: (id) => {
73
+ menusDispatch({ type: "SET_CURRENT_COMMENT_ID", payload: { commentId: id } });
74
+ menusDispatch({ type: "SET_FOCUSED_COMMENT_ID", payload: { commentId: id } });
75
+ },
76
+ requestEdit: (id) => requestSidebarAction(id, "edit")
77
+ };
78
+ const config = makeCommentMarkerConfig({
253
79
  currentUserId,
254
- coreHooks,
255
80
  onRemove: handleRemoveComment,
256
- onSelect: handleSelectComment,
257
- onFocus: handleFocusComment,
258
- onEdit: handleEditComment,
259
- onReply: handleReplyComment
260
- };
261
- React.useEffect(() => {
262
- if (!world) return;
263
- let raf = 0;
264
- let frame = 0;
265
- const loop = () => {
266
- if ((frame++ & 1) === 0 && stateRef.current) {
267
- renderMarkers(stateRef.current, singleRegistry, clusterRegistry, lastSignatureRef);
268
- }
269
- raf = requestAnimationFrame(loop);
270
- };
271
- raf = requestAnimationFrame(loop);
272
- return () => {
273
- cancelAnimationFrame(raf);
274
- clearClusters(world, clusterRegistry);
275
- clearSingles(world, singleRegistry);
276
- lastSignatureRef.current = "";
277
- };
278
- }, [world]);
279
- React.useEffect(() => {
280
- var _a2;
281
- if (focusedCommentId == null || !world) return;
282
- const target = commentsRef.current.find((c) => c.id === focusedCommentId);
283
- if (!target || target.x == null || target.y == null || target.z == null) return;
284
- const controls = (_a2 = world.camera) == null ? void 0 : _a2.controls;
285
- if (!controls) return;
286
- const camPos = controls.getPosition(new THREE.Vector3());
287
- const camTarget = controls.getTarget(new THREE.Vector3());
288
- const lookAt = computeCommentLookAt(
289
- { x: camPos.x, y: camPos.y, z: camPos.z },
290
- { x: camTarget.x, y: camTarget.y, z: camTarget.z },
291
- { x: target.x, y: target.y, z: target.z }
292
- );
293
- void controls.setLookAt(lookAt.camX, lookAt.camY, lookAt.camZ, lookAt.tarX, lookAt.tarY, lookAt.tarZ, true);
294
- }, [focusRequestId, world]);
81
+ onReply: handleReplyComment,
82
+ buildingId
83
+ });
84
+ const itemsForLayer = eligibleComments.map((c) => __spreadProps(__spreadValues({}, c), { isPending: false }));
85
+ const { addPending, removePending } = useMarkerLayerBim(world, itemsForLayer, {
86
+ isVisible: (_d = commentsVisibleInViewer == null ? void 0 : commentsVisibleInViewer.includes(ViewerNames.bim)) != null ? _d : false,
87
+ store,
88
+ coreHooks,
89
+ config
90
+ });
295
91
  const commentCount = comments.filter(
296
92
  (c) => c.viewer === ViewerNames.bim && (!buildingId || buildingId === -1 || c.buildingId === buildingId)
297
93
  ).length;
298
- return { addPendingComment, removePendingComment, commentCount };
94
+ return { addPendingComment: addPending, removePendingComment: removePending, commentCount };
299
95
  }
300
96
  export {
301
97
  useCommentMarkers