@ifc-lite/viewer 1.27.0 → 1.28.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.
Files changed (162) hide show
  1. package/.turbo/turbo-build.log +35 -42
  2. package/CHANGELOG.md +74 -0
  3. package/dist/assets/{basketViewActivator-B3CdrLsb.js → basketViewActivator-Ce38DhXd.js} +8 -8
  4. package/dist/assets/{bcf-QeHK_Aud.js → bcf-Cv_O3JfD.js} +56 -56
  5. package/dist/assets/{decode-worker-CgM1iNSK.js → decode-worker-Cjign7Zh.js} +1 -1
  6. package/dist/assets/{deflate-B-d0SYQM.js → deflate-HbyMq59o.js} +1 -1
  7. package/dist/assets/drawing-2d-DW98umlt.js +257 -0
  8. package/dist/assets/e57-source-2wI9jkCA.js +1 -0
  9. package/dist/assets/{exporters-B4LbZFeT.js → exporters-BuD3XRzB.js} +1309 -1153
  10. package/dist/assets/geometry.worker-TH3fCCoY.js +1 -0
  11. package/dist/assets/{geotiff-CrVtDRFq.js → geotiff-B2HA8Bwm.js} +10 -10
  12. package/dist/assets/{ids-DjsGFN10.js → ids-DYUFMd5f.js} +952 -945
  13. package/dist/assets/{ifc-lite_bg-DsYUIHm3.wasm → ifc-lite_bg-BEA5DLmg.wasm} +0 -0
  14. package/dist/assets/index-E9wB0zWt.css +1 -0
  15. package/dist/assets/{index-COYokSKc.js → index-n5O1QJMM.js} +37877 -38126
  16. package/dist/assets/{index.es-CY202jA3.js → index.es-BKVIpZgL.js} +9 -9
  17. package/dist/assets/{jpeg-D4wOkf5h.js → jpeg-C7hjKjPX.js} +1 -1
  18. package/dist/assets/{jspdf.es.min-DIGb9BHN.js → jspdf.es.min-oWlFc42Y.js} +4 -4
  19. package/dist/assets/lens-C4p1kQ0p.js +1 -0
  20. package/dist/assets/{lerc-DmW0_tgf.js → lerc-BfIOGhQz.js} +1 -1
  21. package/dist/assets/{lzw-oWetY-d6.js → lzw-B0jRuuW5.js} +1 -1
  22. package/dist/assets/{native-bridge-BX8_tHXE.js → native-bridge-DpB-dtEn.js} +6 -3
  23. package/dist/assets/{packbits-F8Nkp4NY.js → packbits-DVvBTC39.js} +1 -1
  24. package/dist/assets/parser.worker-BDsWQ6rc.js +182 -0
  25. package/dist/assets/{pdf-Dsh3HPZB.js → pdf-dVIqI5ac.js} +10 -10
  26. package/dist/assets/raw-C0ZJYGmN.js +1 -0
  27. package/dist/assets/{sandbox-BAC3a-eN.js → sandbox-qpJlrNN0.js} +2962 -2554
  28. package/dist/assets/server-client-DVZ2huNS.js +719 -0
  29. package/dist/assets/{webimage-BLV1dgmd.js → webimage-B394g0Tw.js} +1 -1
  30. package/dist/assets/{xlsx-Bc2HTrjC.js → xlsx-D-oHO76J.js} +8 -8
  31. package/dist/assets/{zstd-C_1HxVrA.js → zstd-Bf38MwV2.js} +1 -1
  32. package/dist/index.html +9 -9
  33. package/package.json +24 -23
  34. package/src/App.tsx +1 -3
  35. package/src/components/mcp/playground-dispatcher.ts +3 -0
  36. package/src/components/mcp/playground-files.ts +33 -1
  37. package/src/components/viewer/BCFPanel.tsx +1 -16
  38. package/src/components/viewer/ChatPanel.tsx +11 -46
  39. package/src/components/viewer/CommandPalette.tsx +6 -1
  40. package/src/components/viewer/ComparePanel.tsx +420 -0
  41. package/src/components/viewer/HierarchyPanel.tsx +48 -183
  42. package/src/components/viewer/IDSPanel.tsx +1 -26
  43. package/src/components/viewer/MainToolbar.tsx +94 -187
  44. package/src/components/viewer/MobileToolbar.tsx +1 -9
  45. package/src/components/viewer/PropertiesPanel.tsx +98 -127
  46. package/src/components/viewer/ScriptPanel.tsx +8 -34
  47. package/src/components/viewer/Section2DPanel.tsx +32 -1
  48. package/src/components/viewer/ViewerLayout.tsx +5 -2
  49. package/src/components/viewer/Viewport.tsx +3 -0
  50. package/src/components/viewer/ViewportContainer.tsx +24 -42
  51. package/src/components/viewer/ViewportOverlays.tsx +1 -4
  52. package/src/components/viewer/hierarchy/HierarchyNode.tsx +3 -3
  53. package/src/components/viewer/hierarchy/ifc-icons.ts +9 -0
  54. package/src/components/viewer/hierarchy/treeDataBuilder.ts +87 -0
  55. package/src/components/viewer/hierarchy/types.ts +1 -0
  56. package/src/components/viewer/hierarchy/useHierarchyTree.ts +6 -2
  57. package/src/components/viewer/properties/MaterialTotalsPanel.tsx +283 -0
  58. package/src/components/viewer/useGeometryStreaming.ts +0 -2
  59. package/src/hooks/federationLoadGate.test.ts +12 -2
  60. package/src/hooks/federationLoadGate.ts +9 -2
  61. package/src/hooks/ingest/federationAlign.ts +488 -0
  62. package/src/hooks/ingest/viewerModelIngest.ts +3 -212
  63. package/src/hooks/useCompare.ts +0 -0
  64. package/src/hooks/useCompareOverlay.ts +119 -0
  65. package/src/hooks/useDrawingGeneration.ts +234 -14
  66. package/src/hooks/useIfc.ts +1 -1
  67. package/src/hooks/useIfcCache.ts +100 -24
  68. package/src/hooks/useIfcFederation.ts +42 -811
  69. package/src/hooks/useIfcLoader.ts +349 -1517
  70. package/src/hooks/useIfcServer.ts +3 -0
  71. package/src/hooks/useLens.ts +5 -1
  72. package/src/hooks/useSymbolicAnnotations.ts +70 -38
  73. package/src/lib/compare/buildFingerprints.ts +173 -0
  74. package/src/lib/compare/describeChange.ts +0 -0
  75. package/src/lib/compare/geometricData.test.ts +54 -0
  76. package/src/lib/compare/geometricData.ts +37 -0
  77. package/src/lib/compare/overlay.test.ts +99 -0
  78. package/src/lib/compare/overlay.ts +91 -0
  79. package/src/lib/geo/cesium-placement.ts +1 -1
  80. package/src/lib/geo/reproject.ts +4 -1
  81. package/src/lib/llm/script-edit-ops.ts +23 -0
  82. package/src/lib/llm/stream-client.ts +8 -1
  83. package/src/lib/search/result-export.ts +7 -1
  84. package/src/sdk/adapters/export-adapter.ts +6 -1
  85. package/src/services/cacheService.ts +9 -25
  86. package/src/services/desktop-export.ts +2 -59
  87. package/src/services/file-dialog.ts +8 -142
  88. package/src/store/constants.ts +23 -0
  89. package/src/store/globalId.ts +15 -13
  90. package/src/store/index.ts +19 -6
  91. package/src/store/slices/cesiumSlice.ts +8 -1
  92. package/src/store/slices/compareSlice.ts +96 -0
  93. package/src/store/slices/drawing2DSlice.ts +8 -0
  94. package/src/store/slices/lensSlice.ts +8 -0
  95. package/src/store/slices/visibilitySlice.ts +22 -1
  96. package/src/store/types.ts +1 -71
  97. package/src/utils/acquireFileBuffer.test.ts +12 -4
  98. package/src/utils/ifcConfig.ts +0 -12
  99. package/src/utils/loadingUtils.ts +32 -0
  100. package/src/utils/spatialHierarchy.test.ts +53 -1
  101. package/src/utils/spatialHierarchy.ts +42 -2
  102. package/src/vite-env.d.ts +2 -0
  103. package/vite.config.ts +6 -3
  104. package/DESKTOP_CONTRACT_VERSION +0 -1
  105. package/dist/assets/drawing-2d-C71b8Ugx.js +0 -257
  106. package/dist/assets/e57-source-CQHxE8n3.js +0 -1
  107. package/dist/assets/event-B0kAzHa-.js +0 -1
  108. package/dist/assets/geometry.worker-BdH-E6NB.js +0 -1
  109. package/dist/assets/index-ajK6D32J.css +0 -1
  110. package/dist/assets/lens-PYsLu_MA.js +0 -1
  111. package/dist/assets/parser.worker-D591Zu_-.js +0 -182
  112. package/dist/assets/raw-D9iw0tmc.js +0 -1
  113. package/dist/assets/server-client-Cjwnm7il.js +0 -706
  114. package/dist/assets/tauri-core-stub-D8Fa-u43.js +0 -1
  115. package/dist/assets/tauri-dialog-stub-r7Wksg7o.js +0 -1
  116. package/dist/assets/tauri-fs-stub-BdeRC7aK.js +0 -1
  117. package/src/components/viewer/DesktopEntitlementBanner.tsx +0 -74
  118. package/src/components/viewer/SettingsPage.tsx +0 -581
  119. package/src/hooks/ingest/resolveDataStoreOrAbort.test.ts +0 -61
  120. package/src/hooks/ingest/resolveDataStoreOrAbort.ts +0 -28
  121. package/src/hooks/ingest/watchedGeometryStream.test.ts +0 -78
  122. package/src/hooks/ingest/watchedGeometryStream.ts +0 -76
  123. package/src/lib/desktop/desktopEntitlementEvents.ts +0 -39
  124. package/src/lib/desktop-entitlement.ts +0 -43
  125. package/src/lib/desktop-product.ts +0 -130
  126. package/src/lib/platform.ts +0 -23
  127. package/src/services/desktop-cache.ts +0 -186
  128. package/src/services/desktop-harness.ts +0 -196
  129. package/src/services/desktop-logger.ts +0 -20
  130. package/src/services/desktop-native-metadata.ts +0 -230
  131. package/src/services/desktop-panel-actions.ts +0 -43
  132. package/src/services/desktop-preferences.ts +0 -44
  133. package/src/services/fs-cache.ts +0 -212
  134. package/src/services/tauri-core-stub.ts +0 -7
  135. package/src/services/tauri-dialog-stub.ts +0 -7
  136. package/src/services/tauri-fs-stub.ts +0 -7
  137. package/src/services/tauri-modules.d.ts +0 -50
  138. package/src/store/slices/desktopEntitlementSlice.ts +0 -86
  139. package/src/utils/desktopModelSnapshot.ts +0 -358
  140. package/src/utils/nativeSpatialDataStore.ts +0 -277
  141. package/src-tauri/Cargo.toml +0 -29
  142. package/src-tauri/build.rs +0 -7
  143. package/src-tauri/capabilities/default.json +0 -18
  144. package/src-tauri/icons/128x128.png +0 -0
  145. package/src-tauri/icons/128x128@2x.png +0 -0
  146. package/src-tauri/icons/32x32.png +0 -0
  147. package/src-tauri/icons/Square107x107Logo.png +0 -0
  148. package/src-tauri/icons/Square142x142Logo.png +0 -0
  149. package/src-tauri/icons/Square150x150Logo.png +0 -0
  150. package/src-tauri/icons/Square284x284Logo.png +0 -0
  151. package/src-tauri/icons/Square30x30Logo.png +0 -0
  152. package/src-tauri/icons/Square310x310Logo.png +0 -0
  153. package/src-tauri/icons/Square44x44Logo.png +0 -0
  154. package/src-tauri/icons/Square71x71Logo.png +0 -0
  155. package/src-tauri/icons/Square89x89Logo.png +0 -0
  156. package/src-tauri/icons/StoreLogo.png +0 -0
  157. package/src-tauri/icons/icon.icns +0 -0
  158. package/src-tauri/icons/icon.ico +0 -0
  159. package/src-tauri/icons/icon.png +0 -0
  160. package/src-tauri/src/lib.rs +0 -21
  161. package/src-tauri/src/main.rs +0 -10
  162. package/src-tauri/tauri.conf.json +0 -39
@@ -1,196 +0,0 @@
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
- import type { NativeFileHandle } from './file-dialog.js';
6
-
7
- type InvokeFn = <T>(cmd: string, args?: Record<string, unknown>) => Promise<T>;
8
-
9
- interface TauriInternals {
10
- invoke: InvokeFn;
11
- }
12
-
13
- export interface DesktopHarnessRequest {
14
- file: NativeFileHandle;
15
- replaceFile?: NativeFileHandle;
16
- telemetryOutputPath?: string;
17
- runLabel?: string;
18
- exitAfterTelemetry: boolean;
19
- waitForMetadataCompletion: boolean;
20
- }
21
-
22
- export interface DesktopTelemetryReport {
23
- schemaVersion: number;
24
- source: 'desktop-native';
25
- mode: 'startup-harness' | 'manual';
26
- success: boolean;
27
- runLabel?: string;
28
- cache?: {
29
- key?: string | null;
30
- hit?: boolean | null;
31
- manifestMeshCount?: number | null;
32
- manifestShardCount?: number | null;
33
- } | null;
34
- file: {
35
- path: string;
36
- name: string;
37
- sizeBytes: number;
38
- sizeMB: number;
39
- };
40
- timings: Record<string, number | null>;
41
- batches: Record<string, number | string | null>;
42
- nativeStats?: Record<string, number | null> | null;
43
- metadata?: Record<string, number | boolean | null> | null;
44
- firstBatchTelemetry?: Record<string, number | string | null> | null;
45
- error?: string;
46
- }
47
-
48
- let activeHarnessRequest: DesktopHarnessRequest | null = null;
49
- let startupHarnessRequestPromise: Promise<DesktopHarnessRequest | null> | null = null;
50
- const STORAGE_KEY = 'ifc-lite:desktop-harness-request';
51
- const CLAIMED_KEY = 'ifc-lite:desktop-harness-claimed';
52
-
53
- function getInvoke(): InvokeFn | null {
54
- const win = globalThis as unknown as { __TAURI_INTERNALS__?: TauriInternals };
55
- return win.__TAURI_INTERNALS__?.invoke ?? null;
56
- }
57
-
58
- async function consumeStartupHarnessRequest(): Promise<DesktopHarnessRequest | null> {
59
- const invoke = getInvoke();
60
- if (!invoke) {
61
- return null;
62
- }
63
-
64
- try {
65
- return await invoke<DesktopHarnessRequest | null>('consume_startup_harness_request');
66
- } catch (error) {
67
- console.warn('[DesktopHarness] Failed to consume startup harness request:', error);
68
- return null;
69
- }
70
- }
71
-
72
- function getHarnessRequestFingerprint(request: DesktopHarnessRequest): string {
73
- return JSON.stringify({
74
- path: request.file.path,
75
- replacePath: request.replaceFile?.path ?? null,
76
- telemetryOutputPath: request.telemetryOutputPath ?? null,
77
- runLabel: request.runLabel ?? null,
78
- exitAfterTelemetry: request.exitAfterTelemetry,
79
- waitForMetadataCompletion: request.waitForMetadataCompletion,
80
- });
81
- }
82
-
83
- function getClaimedFingerprint(): string | null {
84
- try {
85
- return sessionStorage.getItem(CLAIMED_KEY);
86
- } catch {
87
- return null;
88
- }
89
- }
90
-
91
- function setClaimedFingerprint(fingerprint: string | null): void {
92
- try {
93
- if (fingerprint) {
94
- sessionStorage.setItem(CLAIMED_KEY, fingerprint);
95
- } else {
96
- sessionStorage.removeItem(CLAIMED_KEY);
97
- }
98
- } catch {
99
- // Ignore storage issues.
100
- }
101
- }
102
-
103
- export function setActiveHarnessRequest(request: DesktopHarnessRequest | null): void {
104
- activeHarnessRequest = request;
105
- try {
106
- if (request) {
107
- sessionStorage.setItem(STORAGE_KEY, JSON.stringify(request));
108
- if (getClaimedFingerprint() !== getHarnessRequestFingerprint(request)) {
109
- setClaimedFingerprint(null);
110
- }
111
- } else {
112
- sessionStorage.removeItem(STORAGE_KEY);
113
- }
114
- } catch {
115
- // Ignore storage issues in non-browser or restricted contexts.
116
- }
117
- }
118
-
119
- export function getActiveHarnessRequest(): DesktopHarnessRequest | null {
120
- if (!activeHarnessRequest) {
121
- try {
122
- const raw = sessionStorage.getItem(STORAGE_KEY);
123
- if (raw) {
124
- activeHarnessRequest = JSON.parse(raw) as DesktopHarnessRequest;
125
- }
126
- } catch {
127
- // Ignore storage parse failures and fall back to in-memory state.
128
- }
129
- }
130
- return activeHarnessRequest;
131
- }
132
-
133
- export async function getStartupHarnessRequest(): Promise<DesktopHarnessRequest | null> {
134
- const existing = getActiveHarnessRequest();
135
- if (existing) {
136
- return existing;
137
- }
138
-
139
- if (!startupHarnessRequestPromise) {
140
- startupHarnessRequestPromise = consumeStartupHarnessRequest().then((request) => {
141
- if (request) {
142
- setActiveHarnessRequest(request);
143
- }
144
- return request;
145
- });
146
- }
147
-
148
- return await startupHarnessRequestPromise;
149
- }
150
-
151
- export function tryClaimStartupHarnessRequest(request: DesktopHarnessRequest): boolean {
152
- const active = getActiveHarnessRequest();
153
- if (!active) {
154
- return false;
155
- }
156
-
157
- const requestFingerprint = getHarnessRequestFingerprint(request);
158
- if (getHarnessRequestFingerprint(active) !== requestFingerprint) {
159
- return false;
160
- }
161
-
162
- if (getClaimedFingerprint() === requestFingerprint) {
163
- return false;
164
- }
165
-
166
- setClaimedFingerprint(requestFingerprint);
167
- return true;
168
- }
169
-
170
- export function clearActiveHarnessRequest(): void {
171
- activeHarnessRequest = null;
172
- startupHarnessRequestPromise = null;
173
- try {
174
- sessionStorage.removeItem(STORAGE_KEY);
175
- } catch {
176
- // Ignore storage issues.
177
- }
178
- setClaimedFingerprint(null);
179
- }
180
-
181
- export async function finalizeActiveHarnessRun(report: DesktopTelemetryReport): Promise<string | null> {
182
- const invoke = getInvoke();
183
- const request = getActiveHarnessRequest();
184
- clearActiveHarnessRequest();
185
-
186
- if (!invoke || !request) {
187
- return null;
188
- }
189
-
190
- try {
191
- return await invoke<string>('write_desktop_telemetry', { report });
192
- } catch (error) {
193
- console.warn('[DesktopHarness] Failed to write desktop telemetry:', error);
194
- return null;
195
- }
196
- }
@@ -1,20 +0,0 @@
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
- type InvokeFn = <T>(cmd: string, args?: Record<string, unknown>) => Promise<T>;
6
-
7
- type LogLevel = 'info' | 'warn' | 'error';
8
-
9
- export async function logToDesktopTerminal(level: LogLevel, message: string): Promise<void> {
10
- const win = globalThis as unknown as { __TAURI_INTERNALS__?: { invoke: InvokeFn } };
11
- if (!win.__TAURI_INTERNALS__?.invoke) {
12
- return;
13
- }
14
-
15
- try {
16
- await win.__TAURI_INTERNALS__.invoke('frontend_debug_log', { level, message });
17
- } catch {
18
- // Debug logging should never break app behavior.
19
- }
20
- }
@@ -1,230 +0,0 @@
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
- import type {
6
- NativeMetadataEntityDetails,
7
- NativeMetadataEntitySummary,
8
- NativeMetadataSnapshot,
9
- NativeMetadataSpatialNode,
10
- } from '@/store/types';
11
- import type {
12
- MetadataBootstrapPayload,
13
- MetadataBootstrapEntitySummary,
14
- MetadataBootstrapSpatialNode,
15
- } from '@ifc-lite/geometry';
16
- import { getNativeModelSnapshot, setNativeModelSnapshot } from './desktop-cache';
17
-
18
- function bootstrapKindToNative(kind: string): 'spatial' | 'element' {
19
- return kind === 'spatial' ? 'spatial' : 'element';
20
- }
21
-
22
- function summaryFromBootstrap(node: MetadataBootstrapEntitySummary): NativeMetadataEntitySummary {
23
- return {
24
- expressId: node.expressId,
25
- type: node.typeName,
26
- name: node.name,
27
- globalId: node.globalId ?? null,
28
- kind: bootstrapKindToNative(node.kind),
29
- hasChildren: node.hasChildren,
30
- elementCount: node.elementCount,
31
- elevation: node.elevation ?? null,
32
- };
33
- }
34
-
35
- function spatialNodeFromBootstrap(node: MetadataBootstrapSpatialNode | null): NativeMetadataSpatialNode | null {
36
- if (!node) return null;
37
- return {
38
- ...summaryFromBootstrap(node),
39
- children: node.children.map((c) => spatialNodeFromBootstrap(c)).filter((n): n is NativeMetadataSpatialNode => n !== null),
40
- elements: node.elements.map(summaryFromBootstrap),
41
- };
42
- }
43
-
44
- type InvokeFn = <T>(cmd: string, args?: Record<string, unknown>) => Promise<T>;
45
-
46
- async function getInvoke(): Promise<InvokeFn> {
47
- const win = globalThis as unknown as { __TAURI_INTERNALS__?: { invoke: InvokeFn } };
48
- if (win.__TAURI_INTERNALS__?.invoke) {
49
- return win.__TAURI_INTERNALS__.invoke;
50
- }
51
- const core = await import('@tauri-apps/api/core');
52
- return core.invoke as InvokeFn;
53
- }
54
-
55
- function toSchemaVersion(schemaVersion: string): NativeMetadataSnapshot['schemaVersion'] {
56
- if (schemaVersion === 'IFC4X3' || schemaVersion === 'IFC4' || schemaVersion === 'IFC5') {
57
- return schemaVersion;
58
- }
59
- return 'IFC2X3';
60
- }
61
-
62
- export function nativeMetadataSnapshotFromBootstrap(
63
- path: string,
64
- payload: MetadataBootstrapPayload
65
- ): NativeMetadataSnapshot {
66
- return {
67
- mode: 'desktop-lazy',
68
- cacheKey: payload.cacheKey,
69
- filePath: path,
70
- schemaVersion: toSchemaVersion(payload.schemaVersion),
71
- entityCount: payload.entityCount,
72
- spatialTree: spatialNodeFromBootstrap(payload.spatialTree ?? null),
73
- };
74
- }
75
-
76
- export async function bootstrapNativeMetadata(path: string, cacheKey: string): Promise<NativeMetadataSnapshot> {
77
- const invoke = await getInvoke();
78
- // The Tauri command returns a bootstrap-shaped payload (typeName /
79
- // kind unfolded). Route through the shared `nativeMetadataSnapshotFromBootstrap`
80
- // helper so both constructors apply the same `spatialNodeFromBootstrap`
81
- // normalization — without this the `from cached snapshot` and
82
- // `bootstrap fresh` paths produce subtly different shapes and the
83
- // property panel reads break for the freshly-bootstrapped case.
84
- const result = await invoke<MetadataBootstrapPayload>('bootstrap_native_metadata', {
85
- path,
86
- cacheKey,
87
- });
88
- return nativeMetadataSnapshotFromBootstrap(path, result);
89
- }
90
-
91
- export async function restoreNativeMetadataSnapshot(cacheKey: string): Promise<NativeMetadataSnapshot | null> {
92
- const buffer = await getNativeModelSnapshot(cacheKey);
93
- if (!buffer) return null;
94
- try {
95
- const payload = JSON.parse(new TextDecoder().decode(new Uint8Array(buffer))) as NativeMetadataSnapshot;
96
- if (payload?.mode !== 'desktop-lazy' || payload.cacheKey !== cacheKey) {
97
- return null;
98
- }
99
- return {
100
- ...payload,
101
- schemaVersion: toSchemaVersion(payload.schemaVersion),
102
- spatialTree: payload.spatialTree ?? null,
103
- };
104
- } catch {
105
- return null;
106
- }
107
- }
108
-
109
- export async function persistNativeMetadataSnapshot(snapshot: NativeMetadataSnapshot): Promise<void> {
110
- const bytes = new TextEncoder().encode(JSON.stringify(snapshot));
111
- await setNativeModelSnapshot(snapshot.cacheKey, bytes.buffer);
112
- }
113
-
114
- export async function getNativeMetadataChildren(cacheKey: string, expressId: number): Promise<NativeMetadataEntitySummary[]> {
115
- const invoke = await getInvoke();
116
- const result = await invoke<Array<{
117
- expressId: number;
118
- typeName: string;
119
- name: string;
120
- globalId?: string | null;
121
- kind: 'spatial' | 'element';
122
- hasChildren: boolean;
123
- elementCount?: number;
124
- elevation?: number | null;
125
- }>>('get_native_metadata_children', {
126
- cacheKey,
127
- expressId,
128
- });
129
- return result.map((entry) => ({
130
- expressId: entry.expressId,
131
- type: entry.typeName,
132
- name: entry.name,
133
- globalId: entry.globalId ?? undefined,
134
- kind: entry.kind,
135
- hasChildren: entry.hasChildren,
136
- elementCount: entry.elementCount,
137
- elevation: entry.elevation ?? undefined,
138
- }));
139
- }
140
-
141
- export async function getNativeEntityDetails(cacheKey: string, expressId: number): Promise<NativeMetadataEntityDetails> {
142
- const invoke = await getInvoke();
143
- const result = await invoke<{
144
- summary: {
145
- expressId: number;
146
- typeName: string;
147
- name: string;
148
- globalId?: string | null;
149
- kind: 'spatial' | 'element';
150
- hasChildren: boolean;
151
- elementCount?: number;
152
- elevation?: number | null;
153
- };
154
- typeSummary?: {
155
- expressId: number;
156
- typeName: string;
157
- name: string;
158
- globalId?: string | null;
159
- kind: 'spatial' | 'element';
160
- hasChildren: boolean;
161
- elementCount?: number;
162
- elevation?: number | null;
163
- } | null;
164
- spatial?: {
165
- storeyId?: number | null;
166
- storeyName?: string | null;
167
- elevation?: number | null;
168
- height?: number | null;
169
- } | null;
170
- properties: NativeMetadataEntityDetails['properties'];
171
- quantities: NativeMetadataEntityDetails['quantities'];
172
- }>('get_native_entity_details', {
173
- cacheKey,
174
- expressId,
175
- });
176
-
177
- const mapSummary = (entry: NonNullable<typeof result['summary']>): NativeMetadataEntitySummary => ({
178
- expressId: entry.expressId,
179
- type: entry.typeName,
180
- name: entry.name,
181
- globalId: entry.globalId ?? undefined,
182
- kind: entry.kind,
183
- hasChildren: entry.hasChildren,
184
- elementCount: entry.elementCount,
185
- elevation: entry.elevation ?? undefined,
186
- });
187
-
188
- return {
189
- summary: mapSummary(result.summary),
190
- typeSummary: result.typeSummary ? mapSummary(result.typeSummary) : null,
191
- spatial: result.spatial
192
- ? {
193
- storeyId: result.spatial.storeyId ?? null,
194
- storeyName: result.spatial.storeyName ?? null,
195
- elevation: result.spatial.elevation ?? null,
196
- height: result.spatial.height ?? null,
197
- }
198
- : null,
199
- properties: result.properties,
200
- quantities: result.quantities,
201
- };
202
- }
203
-
204
- export async function searchNativeMetadataEntities(cacheKey: string, query: string, limit = 100): Promise<NativeMetadataEntitySummary[]> {
205
- const invoke = await getInvoke();
206
- const result = await invoke<Array<{
207
- expressId: number;
208
- typeName: string;
209
- name: string;
210
- globalId?: string | null;
211
- kind: 'spatial' | 'element';
212
- hasChildren: boolean;
213
- elementCount?: number;
214
- elevation?: number | null;
215
- }>>('search_native_metadata_entities', {
216
- cacheKey,
217
- query,
218
- limit,
219
- });
220
- return result.map((entry) => ({
221
- expressId: entry.expressId,
222
- type: entry.typeName,
223
- name: entry.name,
224
- globalId: entry.globalId ?? undefined,
225
- kind: entry.kind,
226
- hasChildren: entry.hasChildren,
227
- elementCount: entry.elementCount,
228
- elevation: entry.elevation ?? undefined,
229
- }));
230
- }
@@ -1,43 +0,0 @@
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
- export type DesktopPanelActionType =
6
- | 'bcf-import'
7
- | 'bcf-export'
8
- | 'ids-open'
9
- | 'ids-run-validation';
10
-
11
- interface DesktopPanelAction {
12
- id: number;
13
- type: DesktopPanelActionType;
14
- }
15
-
16
- type DesktopPanelActionListener = () => void;
17
-
18
- let nextActionId = 1;
19
- const pendingActions: DesktopPanelAction[] = [];
20
- const listeners = new Set<DesktopPanelActionListener>();
21
-
22
- export function requestDesktopPanelAction(type: DesktopPanelActionType): void {
23
- pendingActions.push({ id: nextActionId++, type });
24
- for (const listener of listeners) {
25
- listener();
26
- }
27
- }
28
-
29
- export function claimNextDesktopPanelAction(type: DesktopPanelActionType): DesktopPanelAction | null {
30
- const index = pendingActions.findIndex((action) => action.type === type);
31
- if (index < 0) {
32
- return null;
33
- }
34
- const [action] = pendingActions.splice(index, 1);
35
- return action ?? null;
36
- }
37
-
38
- export function subscribeDesktopPanelActions(listener: DesktopPanelActionListener): () => void {
39
- listeners.add(listener);
40
- return () => {
41
- listeners.delete(listener);
42
- };
43
- }
@@ -1,44 +0,0 @@
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
- export interface DesktopPreferences {
6
- reopenLastModelOnLaunch: boolean;
7
- restoreWorkspaceLayoutOnLaunch: boolean;
8
- }
9
-
10
- const STORAGE_KEY = 'ifc-lite:desktop-preferences:v1';
11
- const CHANGED_EVENT = 'ifc-lite:desktop-preferences-changed';
12
-
13
- const DEFAULT_PREFERENCES: DesktopPreferences = {
14
- reopenLastModelOnLaunch: true,
15
- restoreWorkspaceLayoutOnLaunch: true,
16
- };
17
-
18
- function sanitizePreferences(value: unknown): DesktopPreferences {
19
- const parsed = (value && typeof value === 'object') ? value as Partial<DesktopPreferences> : {};
20
- return {
21
- reopenLastModelOnLaunch: parsed.reopenLastModelOnLaunch ?? DEFAULT_PREFERENCES.reopenLastModelOnLaunch,
22
- restoreWorkspaceLayoutOnLaunch: parsed.restoreWorkspaceLayoutOnLaunch ?? DEFAULT_PREFERENCES.restoreWorkspaceLayoutOnLaunch,
23
- };
24
- }
25
-
26
- export function getDesktopPreferences(): DesktopPreferences {
27
- try {
28
- return sanitizePreferences(JSON.parse(localStorage.getItem(STORAGE_KEY) ?? '{}'));
29
- } catch {
30
- return { ...DEFAULT_PREFERENCES };
31
- }
32
- }
33
-
34
- export function updateDesktopPreferences(updates: Partial<DesktopPreferences>): DesktopPreferences {
35
- const next = { ...getDesktopPreferences(), ...updates };
36
- localStorage.setItem(STORAGE_KEY, JSON.stringify(next));
37
- window.dispatchEvent(new Event(CHANGED_EVENT));
38
- return next;
39
- }
40
-
41
- export function subscribeDesktopPreferences(listener: () => void): () => void {
42
- window.addEventListener(CHANGED_EVENT, listener);
43
- return () => window.removeEventListener(CHANGED_EVENT, listener);
44
- }