@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.
- package/.turbo/turbo-build.log +35 -42
- package/CHANGELOG.md +74 -0
- package/dist/assets/{basketViewActivator-B3CdrLsb.js → basketViewActivator-Ce38DhXd.js} +8 -8
- package/dist/assets/{bcf-QeHK_Aud.js → bcf-Cv_O3JfD.js} +56 -56
- package/dist/assets/{decode-worker-CgM1iNSK.js → decode-worker-Cjign7Zh.js} +1 -1
- package/dist/assets/{deflate-B-d0SYQM.js → deflate-HbyMq59o.js} +1 -1
- package/dist/assets/drawing-2d-DW98umlt.js +257 -0
- package/dist/assets/e57-source-2wI9jkCA.js +1 -0
- package/dist/assets/{exporters-B4LbZFeT.js → exporters-BuD3XRzB.js} +1309 -1153
- package/dist/assets/geometry.worker-TH3fCCoY.js +1 -0
- package/dist/assets/{geotiff-CrVtDRFq.js → geotiff-B2HA8Bwm.js} +10 -10
- package/dist/assets/{ids-DjsGFN10.js → ids-DYUFMd5f.js} +952 -945
- package/dist/assets/{ifc-lite_bg-DsYUIHm3.wasm → ifc-lite_bg-BEA5DLmg.wasm} +0 -0
- package/dist/assets/index-E9wB0zWt.css +1 -0
- package/dist/assets/{index-COYokSKc.js → index-n5O1QJMM.js} +37877 -38126
- package/dist/assets/{index.es-CY202jA3.js → index.es-BKVIpZgL.js} +9 -9
- package/dist/assets/{jpeg-D4wOkf5h.js → jpeg-C7hjKjPX.js} +1 -1
- package/dist/assets/{jspdf.es.min-DIGb9BHN.js → jspdf.es.min-oWlFc42Y.js} +4 -4
- package/dist/assets/lens-C4p1kQ0p.js +1 -0
- package/dist/assets/{lerc-DmW0_tgf.js → lerc-BfIOGhQz.js} +1 -1
- package/dist/assets/{lzw-oWetY-d6.js → lzw-B0jRuuW5.js} +1 -1
- package/dist/assets/{native-bridge-BX8_tHXE.js → native-bridge-DpB-dtEn.js} +6 -3
- package/dist/assets/{packbits-F8Nkp4NY.js → packbits-DVvBTC39.js} +1 -1
- package/dist/assets/parser.worker-BDsWQ6rc.js +182 -0
- package/dist/assets/{pdf-Dsh3HPZB.js → pdf-dVIqI5ac.js} +10 -10
- package/dist/assets/raw-C0ZJYGmN.js +1 -0
- package/dist/assets/{sandbox-BAC3a-eN.js → sandbox-qpJlrNN0.js} +2962 -2554
- package/dist/assets/server-client-DVZ2huNS.js +719 -0
- package/dist/assets/{webimage-BLV1dgmd.js → webimage-B394g0Tw.js} +1 -1
- package/dist/assets/{xlsx-Bc2HTrjC.js → xlsx-D-oHO76J.js} +8 -8
- package/dist/assets/{zstd-C_1HxVrA.js → zstd-Bf38MwV2.js} +1 -1
- package/dist/index.html +9 -9
- package/package.json +24 -23
- package/src/App.tsx +1 -3
- package/src/components/mcp/playground-dispatcher.ts +3 -0
- package/src/components/mcp/playground-files.ts +33 -1
- package/src/components/viewer/BCFPanel.tsx +1 -16
- package/src/components/viewer/ChatPanel.tsx +11 -46
- package/src/components/viewer/CommandPalette.tsx +6 -1
- package/src/components/viewer/ComparePanel.tsx +420 -0
- package/src/components/viewer/HierarchyPanel.tsx +48 -183
- package/src/components/viewer/IDSPanel.tsx +1 -26
- package/src/components/viewer/MainToolbar.tsx +94 -187
- package/src/components/viewer/MobileToolbar.tsx +1 -9
- package/src/components/viewer/PropertiesPanel.tsx +98 -127
- package/src/components/viewer/ScriptPanel.tsx +8 -34
- package/src/components/viewer/Section2DPanel.tsx +32 -1
- package/src/components/viewer/ViewerLayout.tsx +5 -2
- package/src/components/viewer/Viewport.tsx +3 -0
- package/src/components/viewer/ViewportContainer.tsx +24 -42
- package/src/components/viewer/ViewportOverlays.tsx +1 -4
- package/src/components/viewer/hierarchy/HierarchyNode.tsx +3 -3
- package/src/components/viewer/hierarchy/ifc-icons.ts +9 -0
- package/src/components/viewer/hierarchy/treeDataBuilder.ts +87 -0
- package/src/components/viewer/hierarchy/types.ts +1 -0
- package/src/components/viewer/hierarchy/useHierarchyTree.ts +6 -2
- package/src/components/viewer/properties/MaterialTotalsPanel.tsx +283 -0
- package/src/components/viewer/useGeometryStreaming.ts +0 -2
- package/src/hooks/federationLoadGate.test.ts +12 -2
- package/src/hooks/federationLoadGate.ts +9 -2
- package/src/hooks/ingest/federationAlign.ts +488 -0
- package/src/hooks/ingest/viewerModelIngest.ts +3 -212
- package/src/hooks/useCompare.ts +0 -0
- package/src/hooks/useCompareOverlay.ts +119 -0
- package/src/hooks/useDrawingGeneration.ts +234 -14
- package/src/hooks/useIfc.ts +1 -1
- package/src/hooks/useIfcCache.ts +100 -24
- package/src/hooks/useIfcFederation.ts +42 -811
- package/src/hooks/useIfcLoader.ts +349 -1517
- package/src/hooks/useIfcServer.ts +3 -0
- package/src/hooks/useLens.ts +5 -1
- package/src/hooks/useSymbolicAnnotations.ts +70 -38
- package/src/lib/compare/buildFingerprints.ts +173 -0
- package/src/lib/compare/describeChange.ts +0 -0
- package/src/lib/compare/geometricData.test.ts +54 -0
- package/src/lib/compare/geometricData.ts +37 -0
- package/src/lib/compare/overlay.test.ts +99 -0
- package/src/lib/compare/overlay.ts +91 -0
- package/src/lib/geo/cesium-placement.ts +1 -1
- package/src/lib/geo/reproject.ts +4 -1
- package/src/lib/llm/script-edit-ops.ts +23 -0
- package/src/lib/llm/stream-client.ts +8 -1
- package/src/lib/search/result-export.ts +7 -1
- package/src/sdk/adapters/export-adapter.ts +6 -1
- package/src/services/cacheService.ts +9 -25
- package/src/services/desktop-export.ts +2 -59
- package/src/services/file-dialog.ts +8 -142
- package/src/store/constants.ts +23 -0
- package/src/store/globalId.ts +15 -13
- package/src/store/index.ts +19 -6
- package/src/store/slices/cesiumSlice.ts +8 -1
- package/src/store/slices/compareSlice.ts +96 -0
- package/src/store/slices/drawing2DSlice.ts +8 -0
- package/src/store/slices/lensSlice.ts +8 -0
- package/src/store/slices/visibilitySlice.ts +22 -1
- package/src/store/types.ts +1 -71
- package/src/utils/acquireFileBuffer.test.ts +12 -4
- package/src/utils/ifcConfig.ts +0 -12
- package/src/utils/loadingUtils.ts +32 -0
- package/src/utils/spatialHierarchy.test.ts +53 -1
- package/src/utils/spatialHierarchy.ts +42 -2
- package/src/vite-env.d.ts +2 -0
- package/vite.config.ts +6 -3
- package/DESKTOP_CONTRACT_VERSION +0 -1
- package/dist/assets/drawing-2d-C71b8Ugx.js +0 -257
- package/dist/assets/e57-source-CQHxE8n3.js +0 -1
- package/dist/assets/event-B0kAzHa-.js +0 -1
- package/dist/assets/geometry.worker-BdH-E6NB.js +0 -1
- package/dist/assets/index-ajK6D32J.css +0 -1
- package/dist/assets/lens-PYsLu_MA.js +0 -1
- package/dist/assets/parser.worker-D591Zu_-.js +0 -182
- package/dist/assets/raw-D9iw0tmc.js +0 -1
- package/dist/assets/server-client-Cjwnm7il.js +0 -706
- package/dist/assets/tauri-core-stub-D8Fa-u43.js +0 -1
- package/dist/assets/tauri-dialog-stub-r7Wksg7o.js +0 -1
- package/dist/assets/tauri-fs-stub-BdeRC7aK.js +0 -1
- package/src/components/viewer/DesktopEntitlementBanner.tsx +0 -74
- package/src/components/viewer/SettingsPage.tsx +0 -581
- package/src/hooks/ingest/resolveDataStoreOrAbort.test.ts +0 -61
- package/src/hooks/ingest/resolveDataStoreOrAbort.ts +0 -28
- package/src/hooks/ingest/watchedGeometryStream.test.ts +0 -78
- package/src/hooks/ingest/watchedGeometryStream.ts +0 -76
- package/src/lib/desktop/desktopEntitlementEvents.ts +0 -39
- package/src/lib/desktop-entitlement.ts +0 -43
- package/src/lib/desktop-product.ts +0 -130
- package/src/lib/platform.ts +0 -23
- package/src/services/desktop-cache.ts +0 -186
- package/src/services/desktop-harness.ts +0 -196
- package/src/services/desktop-logger.ts +0 -20
- package/src/services/desktop-native-metadata.ts +0 -230
- package/src/services/desktop-panel-actions.ts +0 -43
- package/src/services/desktop-preferences.ts +0 -44
- package/src/services/fs-cache.ts +0 -212
- package/src/services/tauri-core-stub.ts +0 -7
- package/src/services/tauri-dialog-stub.ts +0 -7
- package/src/services/tauri-fs-stub.ts +0 -7
- package/src/services/tauri-modules.d.ts +0 -50
- package/src/store/slices/desktopEntitlementSlice.ts +0 -86
- package/src/utils/desktopModelSnapshot.ts +0 -358
- package/src/utils/nativeSpatialDataStore.ts +0 -277
- package/src-tauri/Cargo.toml +0 -29
- package/src-tauri/build.rs +0 -7
- package/src-tauri/capabilities/default.json +0 -18
- package/src-tauri/icons/128x128.png +0 -0
- package/src-tauri/icons/128x128@2x.png +0 -0
- package/src-tauri/icons/32x32.png +0 -0
- package/src-tauri/icons/Square107x107Logo.png +0 -0
- package/src-tauri/icons/Square142x142Logo.png +0 -0
- package/src-tauri/icons/Square150x150Logo.png +0 -0
- package/src-tauri/icons/Square284x284Logo.png +0 -0
- package/src-tauri/icons/Square30x30Logo.png +0 -0
- package/src-tauri/icons/Square310x310Logo.png +0 -0
- package/src-tauri/icons/Square44x44Logo.png +0 -0
- package/src-tauri/icons/Square71x71Logo.png +0 -0
- package/src-tauri/icons/Square89x89Logo.png +0 -0
- package/src-tauri/icons/StoreLogo.png +0 -0
- package/src-tauri/icons/icon.icns +0 -0
- package/src-tauri/icons/icon.ico +0 -0
- package/src-tauri/icons/icon.png +0 -0
- package/src-tauri/src/lib.rs +0 -21
- package/src-tauri/src/main.rs +0 -10
- 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
|
-
}
|