@ifc-lite/viewer 1.17.4 → 1.18.0

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 (196) hide show
  1. package/.turbo/turbo-build.log +20 -17
  2. package/.turbo/turbo-typecheck.log +1 -1
  3. package/CHANGELOG.md +630 -0
  4. package/DESKTOP_CONTRACT_VERSION +1 -1
  5. package/dist/assets/{basketViewActivator-BmnNtVfZ.js → basketViewActivator-Cm1QEk_R.js} +1 -1
  6. package/dist/assets/drawing-2d-DoxKMqbO.js +257 -0
  7. package/dist/assets/{exporters-ChAtBmlj.js → exporters-B_OBqIyD.js} +3479 -2845
  8. package/dist/assets/{geometry.worker-BQ0rzNo-.js → geometry.worker-xHHy-9DV.js} +1 -1
  9. package/dist/assets/ids-DQ5jY0E8.js +1 -0
  10. package/dist/assets/ifc-lite_bg-ADjKXSms.wasm +0 -0
  11. package/dist/assets/{index-Co8E2-FE.js → index-BKq-M3Mk.js} +55873 -40593
  12. package/dist/assets/index-COnQRuqY.css +1 -0
  13. package/dist/assets/{native-bridge-BRvbckFQ.js → native-bridge-SHXiQwFW.js} +104 -104
  14. package/dist/assets/sandbox-jez21HtV.js +9627 -0
  15. package/dist/assets/{server-client-BV8zHZ7Y.js → server-client-ncOQVNso.js} +1 -1
  16. package/dist/assets/{wasm-bridge-g01g7T9b.js → wasm-bridge-DyfBSB8z.js} +1 -1
  17. package/dist/index.html +8 -7
  18. package/index.html +1 -0
  19. package/package.json +13 -13
  20. package/src/App.tsx +16 -2
  21. package/src/apache-arrow.d.ts +30 -0
  22. package/src/components/viewer/AddElementPanel.tsx +758 -0
  23. package/src/components/viewer/BulkPropertyEditor.tsx +7 -0
  24. package/src/components/viewer/CesiumOverlay.tsx +62 -19
  25. package/src/components/viewer/ChatPanel.tsx +259 -93
  26. package/src/components/viewer/CommandPalette.tsx +56 -7
  27. package/src/components/viewer/EntityContextMenu.tsx +168 -4
  28. package/src/components/viewer/ExportChangesButton.tsx +25 -5
  29. package/src/components/viewer/ExportDialog.tsx +19 -1
  30. package/src/components/viewer/MainToolbar.tsx +73 -13
  31. package/src/components/viewer/PropertiesPanel.tsx +237 -23
  32. package/src/components/viewer/SearchInline.tsx +669 -0
  33. package/src/components/viewer/SearchModal.filter.builder.tsx +766 -0
  34. package/src/components/viewer/SearchModal.filter.tsx +514 -0
  35. package/src/components/viewer/SearchModal.text.tsx +388 -0
  36. package/src/components/viewer/SearchModal.tsx +235 -0
  37. package/src/components/viewer/SettingsPage.tsx +252 -101
  38. package/src/components/viewer/ThemeSwitch.tsx +63 -7
  39. package/src/components/viewer/ToolOverlays.tsx +5 -0
  40. package/src/components/viewer/ViewerLayout.tsx +25 -4
  41. package/src/components/viewer/Viewport.tsx +25 -3
  42. package/src/components/viewer/ViewportContainer.tsx +51 -64
  43. package/src/components/viewer/ViewportOverlays.tsx +5 -2
  44. package/src/components/viewer/annotations/AnnotationDropInput.tsx +203 -0
  45. package/src/components/viewer/annotations/AnnotationLayer.tsx +287 -0
  46. package/src/components/viewer/annotations/AnnotationPin.tsx +90 -0
  47. package/src/components/viewer/annotations/AnnotationPopover.tsx +296 -0
  48. package/src/components/viewer/bcf/BCFTopicDetail.tsx +4 -4
  49. package/src/components/viewer/chat/ModelSelector.tsx +90 -54
  50. package/src/components/viewer/lists/ListPanel.tsx +14 -21
  51. package/src/components/viewer/properties/GeoreferencingPanel.tsx +113 -51
  52. package/src/components/viewer/properties/LocationMap.tsx +9 -7
  53. package/src/components/viewer/properties/ModelMetadataPanel.tsx +1 -1
  54. package/src/components/viewer/properties/RawStepCard.tsx +332 -0
  55. package/src/components/viewer/properties/RawStepRow.tsx +261 -0
  56. package/src/components/viewer/properties/ScheduleCard.tsx +224 -0
  57. package/src/components/viewer/properties/TaskEditCard.tsx +510 -0
  58. package/src/components/viewer/properties/raw-step-format.ts +193 -0
  59. package/src/components/viewer/schedule/AnimationSettingsPopover.tsx +542 -0
  60. package/src/components/viewer/schedule/GanttDependencyArrows.tsx +89 -0
  61. package/src/components/viewer/schedule/GanttDragTooltip.tsx +48 -0
  62. package/src/components/viewer/schedule/GanttEmptyState.tsx +97 -0
  63. package/src/components/viewer/schedule/GanttPanel.tsx +295 -0
  64. package/src/components/viewer/schedule/GanttTaskBar.tsx +199 -0
  65. package/src/components/viewer/schedule/GanttTaskTree.tsx +250 -0
  66. package/src/components/viewer/schedule/GanttTimeline.tsx +305 -0
  67. package/src/components/viewer/schedule/GanttToolbar.tsx +406 -0
  68. package/src/components/viewer/schedule/GenerateAdvancedPanel.tsx +147 -0
  69. package/src/components/viewer/schedule/GenerateScheduleDialog.tsx +392 -0
  70. package/src/components/viewer/schedule/HeightStrategyPanel.tsx +120 -0
  71. package/src/components/viewer/schedule/generate-schedule.test.ts +439 -0
  72. package/src/components/viewer/schedule/generate-schedule.ts +648 -0
  73. package/src/components/viewer/schedule/schedule-animator.test.ts +452 -0
  74. package/src/components/viewer/schedule/schedule-animator.ts +488 -0
  75. package/src/components/viewer/schedule/schedule-selection.test.ts +148 -0
  76. package/src/components/viewer/schedule/schedule-selection.ts +163 -0
  77. package/src/components/viewer/schedule/schedule-utils.ts +223 -0
  78. package/src/components/viewer/schedule/useConstructionSequence.ts +156 -0
  79. package/src/components/viewer/schedule/useGanttBarDrag.test.ts +90 -0
  80. package/src/components/viewer/schedule/useGanttBarDrag.ts +305 -0
  81. package/src/components/viewer/schedule/useGanttSelection3DHighlight.ts +152 -0
  82. package/src/components/viewer/schedule/useOverlayCompositor.ts +108 -0
  83. package/src/components/viewer/selectionHandlers.ts +446 -0
  84. package/src/components/viewer/tools/AddElementOverlay.tsx +540 -0
  85. package/src/components/viewer/tools/SectionCapControls.tsx +237 -0
  86. package/src/components/viewer/tools/SectionPanel.tsx +39 -18
  87. package/src/components/viewer/useAnimationLoop.ts +9 -1
  88. package/src/components/viewer/useDuplicateShortcut.ts +77 -0
  89. package/src/components/viewer/useMouseControls.ts +9 -1
  90. package/src/components/viewer/useRenderUpdates.ts +1 -1
  91. package/src/hooks/ids/idsDataAccessor.ts +60 -24
  92. package/src/hooks/ingest/viewerModelIngest.ts +7 -2
  93. package/src/hooks/useIfcFederation.ts +326 -71
  94. package/src/hooks/useIfcLoader.ts +23 -10
  95. package/src/hooks/useKeyboardShortcuts.ts +25 -0
  96. package/src/hooks/useSandbox.ts +1 -1
  97. package/src/hooks/useSearchIndex.ts +125 -0
  98. package/src/hooks/useViewControls.ts +13 -5
  99. package/src/index.css +550 -10
  100. package/src/lib/desktop-entitlement.ts +2 -4
  101. package/src/lib/geo/cesium-bridge.ts +15 -7
  102. package/src/lib/geo/effective-georef.test.ts +73 -0
  103. package/src/lib/geo/effective-georef.ts +111 -0
  104. package/src/lib/geo/reproject.ts +105 -19
  105. package/src/lib/llm/byok-guard.test.ts +77 -0
  106. package/src/lib/llm/byok-guard.ts +39 -0
  107. package/src/lib/llm/free-models.test.ts +0 -6
  108. package/src/lib/llm/models.ts +104 -42
  109. package/src/lib/llm/stream-client.ts +74 -110
  110. package/src/lib/llm/stream-direct.test.ts +130 -0
  111. package/src/lib/llm/stream-direct.ts +316 -0
  112. package/src/lib/llm/system-prompt.test.ts +14 -0
  113. package/src/lib/llm/system-prompt.ts +102 -1
  114. package/src/lib/llm/types.ts +20 -2
  115. package/src/lib/recent-files.ts +38 -4
  116. package/src/lib/scripts/templates/bim-globals.d.ts +136 -114
  117. package/src/lib/scripts/templates/construction-schedule.ts +223 -0
  118. package/src/lib/scripts/templates.ts +7 -0
  119. package/src/lib/search/common-ifc-types.ts +36 -0
  120. package/src/lib/search/filter-evaluate.test.ts +537 -0
  121. package/src/lib/search/filter-evaluate.ts +610 -0
  122. package/src/lib/search/filter-rules.test.ts +119 -0
  123. package/src/lib/search/filter-rules.ts +198 -0
  124. package/src/lib/search/filter-schema.test.ts +233 -0
  125. package/src/lib/search/filter-schema.ts +146 -0
  126. package/src/lib/search/recent-searches.test.ts +116 -0
  127. package/src/lib/search/recent-searches.ts +93 -0
  128. package/src/lib/search/result-export.test.ts +101 -0
  129. package/src/lib/search/result-export.ts +104 -0
  130. package/src/lib/search/saved-filters.test.ts +118 -0
  131. package/src/lib/search/saved-filters.ts +154 -0
  132. package/src/lib/search/tier0-scan.test.ts +196 -0
  133. package/src/lib/search/tier0-scan.ts +237 -0
  134. package/src/lib/search/tier1-index.test.ts +242 -0
  135. package/src/lib/search/tier1-index.ts +448 -0
  136. package/src/main.tsx +1 -10
  137. package/src/sdk/adapters/export-adapter.test.ts +434 -1
  138. package/src/sdk/adapters/export-adapter.ts +404 -1
  139. package/src/sdk/adapters/export-schedule-splice.test.ts +127 -0
  140. package/src/sdk/adapters/export-schedule-splice.ts +87 -0
  141. package/src/sdk/adapters/model-compat.ts +8 -2
  142. package/src/sdk/adapters/schedule-adapter.ts +73 -0
  143. package/src/sdk/adapters/store-adapter.ts +201 -0
  144. package/src/sdk/adapters/visibility-adapter.ts +3 -0
  145. package/src/sdk/local-backend.ts +16 -8
  146. package/src/services/api-keys.ts +73 -0
  147. package/src/services/desktop-export.ts +3 -1
  148. package/src/services/desktop-native-metadata.ts +41 -18
  149. package/src/services/file-dialog.ts +4 -1
  150. package/src/services/tauri-modules.d.ts +25 -0
  151. package/src/store/basketVisibleSet.ts +3 -0
  152. package/src/store/constants.ts +20 -2
  153. package/src/store/globalId.ts +4 -1
  154. package/src/store/index.ts +82 -6
  155. package/src/store/slices/addElementMeshes.ts +365 -0
  156. package/src/store/slices/addElementSlice.ts +275 -0
  157. package/src/store/slices/annotationsSlice.test.ts +133 -0
  158. package/src/store/slices/annotationsSlice.ts +251 -0
  159. package/src/store/slices/cesiumSlice.ts +5 -0
  160. package/src/store/slices/chatSlice.test.ts +6 -76
  161. package/src/store/slices/chatSlice.ts +17 -58
  162. package/src/store/slices/dataSlice.test.ts +23 -4
  163. package/src/store/slices/dataSlice.ts +1 -1
  164. package/src/store/slices/modelSlice.test.ts +67 -9
  165. package/src/store/slices/modelSlice.ts +39 -7
  166. package/src/store/slices/mutationSlice.ts +964 -3
  167. package/src/store/slices/overlayCompositor.test.ts +164 -0
  168. package/src/store/slices/overlaySlice.test.ts +93 -0
  169. package/src/store/slices/overlaySlice.ts +151 -0
  170. package/src/store/slices/pinboardSlice.test.ts +6 -1
  171. package/src/store/slices/playbackSlice.ts +128 -0
  172. package/src/store/slices/schedule-edit-helpers.test.ts +97 -0
  173. package/src/store/slices/schedule-edit-helpers.ts +179 -0
  174. package/src/store/slices/scheduleSlice.test.ts +694 -0
  175. package/src/store/slices/scheduleSlice.ts +1330 -0
  176. package/src/store/slices/searchSlice.test.ts +342 -0
  177. package/src/store/slices/searchSlice.ts +341 -0
  178. package/src/store/slices/sectionSlice.test.ts +87 -7
  179. package/src/store/slices/sectionSlice.ts +151 -5
  180. package/src/store/slices/selectionSlice.test.ts +46 -0
  181. package/src/store/slices/selectionSlice.ts +20 -0
  182. package/src/store/slices/uiSlice.ts +28 -5
  183. package/src/store/types.ts +26 -0
  184. package/src/store.ts +14 -0
  185. package/src/utils/nativeSpatialDataStore.ts +4 -1
  186. package/src/utils/viewportUtils.ts +7 -2
  187. package/src/vite-env.d.ts +0 -4
  188. package/dist/assets/drawing-2d-gWfpdfYe.js +0 -257
  189. package/dist/assets/ids-B4jTqB1O.js +0 -1
  190. package/dist/assets/ifc-lite_bg-BX4E7TX8.wasm +0 -0
  191. package/dist/assets/index-DckuDqlv.css +0 -1
  192. package/dist/assets/sandbox-DZiNLNMk.js +0 -5933
  193. package/src/components/viewer/UpgradePage.tsx +0 -71
  194. package/src/lib/desktop/ClerkDesktopEntitlementSync.tsx +0 -175
  195. package/src/lib/llm/ClerkChatSync.tsx +0 -74
  196. package/src/lib/llm/clerk-auth.ts +0 -62
@@ -0,0 +1,125 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+
5
+ /**
6
+ * useSearchIndex — lazy builder for the Tier-1 search index.
7
+ *
8
+ * Mount once near the root of the viewer shell (currently `SearchInline`,
9
+ * since it's always rendered once the toolbar is up). The hook watches
10
+ * the federated `models` map; for each model with a populated
11
+ * `ifcDataStore` that doesn't yet have a Tier-1 record, it spawns a
12
+ * chunked build. Models that disappear get their index record dropped.
13
+ *
14
+ * Load-perf guarantee: the build NEVER runs during the actual IFC load
15
+ * because `ifcDataStore` is non-null only after the parser reports the
16
+ * model is ready (`onSpatialReady` + geometry). The build itself yields
17
+ * to the event loop every `DEFAULT_CHUNK_SIZE` rows so a 4M-entity
18
+ * index doesn't hog the main thread.
19
+ */
20
+
21
+ import { useEffect, useRef } from 'react';
22
+ import { useShallow } from 'zustand/react/shallow';
23
+ import { useViewerStore } from '@/store';
24
+ import { buildTier1Index } from '@/lib/search/tier1-index';
25
+
26
+ export function useSearchIndex(): void {
27
+ const {
28
+ models,
29
+ searchIndexes,
30
+ setSearchIndexRecord,
31
+ removeSearchIndexRecord,
32
+ searchFilterSchema,
33
+ removeFilterSchema,
34
+ } = useViewerStore(
35
+ useShallow((s) => ({
36
+ models: s.models,
37
+ searchIndexes: s.searchIndexes,
38
+ setSearchIndexRecord: s.setSearchIndexRecord,
39
+ removeSearchIndexRecord: s.removeSearchIndexRecord,
40
+ searchFilterSchema: s.searchFilterSchema,
41
+ removeFilterSchema: s.removeFilterSchema,
42
+ })),
43
+ );
44
+
45
+ // One AbortController per in-flight build. Lets us cancel cleanly when a
46
+ // model is removed mid-build or when the component unmounts.
47
+ const controllersRef = useRef<Map<string, AbortController>>(new Map());
48
+
49
+ useEffect(() => {
50
+ const controllers = controllersRef.current;
51
+
52
+ // Drop records / abort builds for models that no longer exist.
53
+ for (const modelId of Array.from(searchIndexes.keys())) {
54
+ if (!models.has(modelId)) {
55
+ controllers.get(modelId)?.abort();
56
+ controllers.delete(modelId);
57
+ removeSearchIndexRecord(modelId);
58
+ }
59
+ }
60
+
61
+ // Drop the filter-schema cache for departed models too. Stale entries
62
+ // would surface in the chip dropdowns the next time a model with the
63
+ // same id loaded (e.g. user reopens a different file as model_0).
64
+ for (const modelId of Array.from(searchFilterSchema.keys())) {
65
+ if (!models.has(modelId)) removeFilterSchema(modelId);
66
+ }
67
+
68
+ // Kick off builds for models that are loaded but not yet indexed.
69
+ for (const [modelId, model] of models) {
70
+ if (!model.ifcDataStore) continue;
71
+ const existing = searchIndexes.get(modelId);
72
+ if (existing && existing.status !== 'pending') continue;
73
+ if (controllers.has(modelId)) continue;
74
+
75
+ const controller = new AbortController();
76
+ controllers.set(modelId, controller);
77
+
78
+ setSearchIndexRecord(modelId, { status: 'building', progress: 0 });
79
+
80
+ // Fire-and-forget — the build is cancellable via the controller, and
81
+ // the completion handlers update the store without needing a ref.
82
+ void buildTier1Index(modelId, model.ifcDataStore, {
83
+ signal: controller.signal,
84
+ onProgress: (done, total) => {
85
+ if (controller.signal.aborted) return;
86
+ const progress = total > 0 ? done / total : 1;
87
+ setSearchIndexRecord(modelId, { status: 'building', progress });
88
+ },
89
+ })
90
+ .then((index) => {
91
+ if (controller.signal.aborted) return;
92
+ controllers.delete(modelId);
93
+ setSearchIndexRecord(modelId, { status: 'ready', index, progress: 1 });
94
+ })
95
+ .catch((err: unknown) => {
96
+ controllers.delete(modelId);
97
+ if (err instanceof DOMException && err.name === 'AbortError') return;
98
+ const message = err instanceof Error ? err.message : String(err);
99
+ // Don't set a 'ready' record — Tier-0 fallback stays live.
100
+ console.warn(`[useSearchIndex] build failed for ${modelId}:`, message);
101
+ setSearchIndexRecord(modelId, { status: 'error', error: message });
102
+ });
103
+ }
104
+
105
+ // On unmount OR next effect pass, abort everything. The effect re-runs
106
+ // only when `models` / `searchIndexes` changes, so steady-state
107
+ // incurs no abort — the `controllers.has(modelId)` guard above makes
108
+ // re-entry idempotent.
109
+ return () => {
110
+ // Intentionally NOT aborting everything on every re-render — only
111
+ // models that went missing got aborted above. The real cleanup is
112
+ // the component-unmount pass below.
113
+ };
114
+ }, [models, searchIndexes, setSearchIndexRecord, removeSearchIndexRecord, searchFilterSchema, removeFilterSchema]);
115
+
116
+ // Abort any in-flight builds when the consumer unmounts. Separate effect
117
+ // so it only fires on unmount (no deps).
118
+ useEffect(() => {
119
+ const controllers = controllersRef.current;
120
+ return () => {
121
+ for (const c of controllers.values()) c.abort();
122
+ controllers.clear();
123
+ };
124
+ }, []);
125
+ }
@@ -43,6 +43,7 @@ function useViewControls({
43
43
  const [viewTransform, setViewTransform] = useState({ x: 0, y: 0, scale: 1 });
44
44
  const [needsFit, setNeedsFit] = useState(true); // Force fit on first open and axis change
45
45
  const prevAxisRef = useRef(sectionPlane.axis); // Track axis changes
46
+ const prevFlippedRef = useRef(sectionPlane.flipped); // Track flip changes
46
47
 
47
48
  // Wheel zoom handler
48
49
  useEffect(() => {
@@ -158,14 +159,21 @@ function useViewControls({
158
159
  // Track axis changes for forced fit-to-view
159
160
  const lastFitAxisRef = useRef(sectionPlane.axis);
160
161
 
161
- // Set needsFit when axis changes
162
+ // Set needsFit when axis OR flip changes. Flip mirrors the projection's U
163
+ // axis (see `projectTo2D` in @ifc-lite/drawing-2d), so the polygon bounds
164
+ // jump from positive X into negative X (or vice versa). Without re-fitting
165
+ // the new bounds end up off-screen and the user sees "empty 2D panel after
166
+ // I pressed Flip" — that was the bug behind the recent screenshot report.
162
167
  useEffect(() => {
163
- if (sectionPlane.axis !== prevAxisRef.current) {
168
+ const axisChanged = sectionPlane.axis !== prevAxisRef.current;
169
+ const flipChanged = sectionPlane.flipped !== prevFlippedRef.current;
170
+ if (axisChanged || flipChanged) {
164
171
  prevAxisRef.current = sectionPlane.axis;
165
- setNeedsFit(true); // Force fit when axis changes
166
- cachedSheetTransformRef.current = null; // Clear cached transform for new axis
172
+ prevFlippedRef.current = sectionPlane.flipped;
173
+ setNeedsFit(true);
174
+ cachedSheetTransformRef.current = null;
167
175
  }
168
- }, [sectionPlane.axis]);
176
+ }, [sectionPlane.axis, sectionPlane.flipped]);
169
177
 
170
178
  // Track previous sheet mode to detect toggle
171
179
  const prevSheetEnabledRef = useRef(sheetEnabled);