@ifc-lite/viewer 1.28.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 +34 -41
- package/CHANGELOG.md +10 -0
- package/dist/assets/{basketViewActivator-BNRDNuUJ.js → basketViewActivator-Ce38DhXd.js} +7 -7
- package/dist/assets/{bcf-DCwCuP7n.js → bcf-Cv_O3JfD.js} +1 -1
- package/dist/assets/{deflate-DNGgs8Ur.js → deflate-HbyMq59o.js} +1 -1
- package/dist/assets/drawing-2d-DW98umlt.js +257 -0
- package/dist/assets/{exporters-B9v81gi9.js → exporters-BuD3XRzB.js} +463 -416
- package/dist/assets/geometry.worker-TH3fCCoY.js +1 -0
- package/dist/assets/{geotiff-D-YCLS4g.js → geotiff-B2HA8Bwm.js} +10 -10
- package/dist/assets/{ids-CCpq-5d3.js → ids-DYUFMd5f.js} +4 -4
- package/dist/assets/{ifc-lite_bg-DbgS5EUA.wasm → ifc-lite_bg-BEA5DLmg.wasm} +0 -0
- package/dist/assets/index-E9wB0zWt.css +1 -0
- package/dist/assets/{index-Bgb3_Pu_.js → index-n5O1QJMM.js} +36808 -39415
- package/dist/assets/{index.es-CWfqZyyr.js → index.es-BKVIpZgL.js} +8 -8
- package/dist/assets/{jpeg-DGOAeUqU.js → jpeg-C7hjKjPX.js} +1 -1
- package/dist/assets/{jspdf.es.min-XPLU2Wkq.js → jspdf.es.min-oWlFc42Y.js} +4 -4
- package/dist/assets/{lerc-1PMSCHwX.js → lerc-BfIOGhQz.js} +1 -1
- package/dist/assets/{lzw-C65U9lNM.js → lzw-B0jRuuW5.js} +1 -1
- package/dist/assets/{native-bridge-XxXos6yI.js → native-bridge-DpB-dtEn.js} +5 -2
- package/dist/assets/{packbits-BdMWXC3m.js → packbits-DVvBTC39.js} +1 -1
- package/dist/assets/{parser.worker-Ddwo3_06.js → parser.worker-BDsWQ6rc.js} +1 -1
- package/dist/assets/{pdf-CRwaZf3s.js → pdf-dVIqI5ac.js} +9 -9
- package/dist/assets/raw-C0ZJYGmN.js +1 -0
- package/dist/assets/{sandbox-0sDo3g3m.js → sandbox-qpJlrNN0.js} +8 -8
- package/dist/assets/{server-client-cTCJ-853.js → server-client-DVZ2huNS.js} +1 -1
- package/dist/assets/{webimage-BtakWX7W.js → webimage-B394g0Tw.js} +1 -1
- package/dist/assets/{xlsx-B1YOg2QB.js → xlsx-D-oHO76J.js} +7 -7
- package/dist/assets/{zstd-CmwsbxmM.js → zstd-Bf38MwV2.js} +1 -1
- package/dist/index.html +8 -8
- package/package.json +5 -5
- package/src/App.tsx +1 -3
- package/src/components/viewer/BCFPanel.tsx +1 -16
- package/src/components/viewer/ChatPanel.tsx +11 -46
- package/src/components/viewer/HierarchyPanel.tsx +2 -176
- package/src/components/viewer/IDSPanel.tsx +1 -26
- package/src/components/viewer/MainToolbar.tsx +75 -185
- package/src/components/viewer/MobileToolbar.tsx +1 -9
- package/src/components/viewer/PropertiesPanel.tsx +28 -126
- package/src/components/viewer/ScriptPanel.tsx +8 -34
- package/src/components/viewer/Section2DPanel.tsx +32 -1
- package/src/components/viewer/ViewerLayout.tsx +0 -2
- package/src/components/viewer/ViewportContainer.tsx +24 -42
- package/src/components/viewer/ViewportOverlays.tsx +1 -4
- package/src/components/viewer/useGeometryStreaming.ts +0 -2
- package/src/hooks/ingest/federationAlign.ts +7 -0
- package/src/hooks/useDrawingGeneration.ts +211 -13
- package/src/hooks/useIfcCache.ts +94 -41
- package/src/hooks/useIfcFederation.ts +2 -3
- package/src/hooks/useIfcLoader.ts +10 -1051
- 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/index.ts +3 -5
- package/src/store/slices/drawing2DSlice.ts +8 -0
- package/src/store/slices/visibilitySlice.ts +22 -1
- package/src/store/types.ts +1 -71
- package/src/utils/ifcConfig.ts +0 -12
- package/vite.config.ts +6 -3
- package/DESKTOP_CONTRACT_VERSION +0 -1
- package/dist/assets/drawing-2d-D0dDf6Lh.js +0 -257
- package/dist/assets/event-B0kAzHa-.js +0 -1
- package/dist/assets/geometry.worker-Bpa3115V.js +0 -1
- package/dist/assets/index-BtbXFKsX.css +0 -1
- package/dist/assets/raw-CJgQdyuZ.js +0 -1
- 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/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 -359
- 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,39 +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 const DESKTOP_ENTITLEMENT_REFRESH_EVENT = 'ifc-lite:desktop-entitlement-refresh';
|
|
6
|
-
|
|
7
|
-
interface DesktopEntitlementRefreshDetail {
|
|
8
|
-
resolve: () => void;
|
|
9
|
-
reject: (error?: unknown) => void;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const ENTITLEMENT_REFRESH_TIMEOUT_MS = 10_000;
|
|
13
|
-
|
|
14
|
-
export function requestDesktopEntitlementRefresh(): Promise<void> {
|
|
15
|
-
return new Promise((resolve, reject) => {
|
|
16
|
-
const timeoutId = globalThis.setTimeout(() => {
|
|
17
|
-
reject(new Error(
|
|
18
|
-
`Desktop entitlement refresh timed out after ${ENTITLEMENT_REFRESH_TIMEOUT_MS}ms — ` +
|
|
19
|
-
'no listener responded to the refresh event.',
|
|
20
|
-
));
|
|
21
|
-
}, ENTITLEMENT_REFRESH_TIMEOUT_MS);
|
|
22
|
-
|
|
23
|
-
window.dispatchEvent(new CustomEvent<DesktopEntitlementRefreshDetail>(
|
|
24
|
-
DESKTOP_ENTITLEMENT_REFRESH_EVENT,
|
|
25
|
-
{
|
|
26
|
-
detail: {
|
|
27
|
-
resolve: () => {
|
|
28
|
-
globalThis.clearTimeout(timeoutId);
|
|
29
|
-
resolve();
|
|
30
|
-
},
|
|
31
|
-
reject: (error?: unknown) => {
|
|
32
|
-
globalThis.clearTimeout(timeoutId);
|
|
33
|
-
reject(error);
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
));
|
|
38
|
-
});
|
|
39
|
-
}
|
|
@@ -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
|
-
import {
|
|
6
|
-
getDefaultDesktopEntitlement,
|
|
7
|
-
hasDesktopFeatureAccess,
|
|
8
|
-
type DesktopEntitlement,
|
|
9
|
-
type DesktopFeature,
|
|
10
|
-
} from './desktop-product';
|
|
11
|
-
|
|
12
|
-
type HasFn = ((params: { plan?: string; feature?: string }) => boolean) | undefined;
|
|
13
|
-
|
|
14
|
-
interface ResolveDesktopEntitlementOptions {
|
|
15
|
-
userId: string | null;
|
|
16
|
-
token: string | null;
|
|
17
|
-
has: HasFn;
|
|
18
|
-
publicMetadata: Record<string, unknown> | null | undefined;
|
|
19
|
-
now?: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
interface ResolvedDesktopEntitlement {
|
|
23
|
-
entitlement: DesktopEntitlement;
|
|
24
|
-
aiAssistantEnabled: boolean;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function resolveDesktopEntitlement(options: ResolveDesktopEntitlementOptions): ResolvedDesktopEntitlement {
|
|
28
|
-
// NOTE: `token`, `has`, `publicMetadata`, and `now` are accepted but
|
|
29
|
-
// intentionally unused in this implementation. They are kept for future
|
|
30
|
-
// extensibility if a desktop-specific entitlement provider is added.
|
|
31
|
-
const { userId } = options;
|
|
32
|
-
const entitlement: DesktopEntitlement = {
|
|
33
|
-
...getDefaultDesktopEntitlement(),
|
|
34
|
-
userId,
|
|
35
|
-
};
|
|
36
|
-
const aiAssistantEnabled = hasDesktopFeatureAccess(entitlement, 'ai_assistant');
|
|
37
|
-
return { entitlement, aiAssistantEnabled };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function canUseDesktopFeatureOffline(entitlement: DesktopEntitlement, feature: DesktopFeature, now = Date.now()): boolean {
|
|
41
|
-
void now;
|
|
42
|
-
return hasDesktopFeatureAccess(entitlement, feature);
|
|
43
|
-
}
|
|
@@ -1,130 +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 DesktopPlanTier = 'free' | 'pro';
|
|
6
|
-
export type DesktopEntitlementStatus = 'anonymous' | 'signed_out' | 'active' | 'trial' | 'expired' | 'grace_offline';
|
|
7
|
-
export type DesktopEntitlementSource = 'anonymous' | 'clerk_claims' | 'cached';
|
|
8
|
-
|
|
9
|
-
export type DesktopFeature =
|
|
10
|
-
| 'viewer_basic'
|
|
11
|
-
| 'workspace_restore'
|
|
12
|
-
| 'exports'
|
|
13
|
-
| 'ids_validation'
|
|
14
|
-
| 'bcf_issue_management'
|
|
15
|
-
| 'ai_assistant'
|
|
16
|
-
| 'extensions';
|
|
17
|
-
|
|
18
|
-
export interface DesktopEntitlement {
|
|
19
|
-
tier: DesktopPlanTier;
|
|
20
|
-
status: DesktopEntitlementStatus;
|
|
21
|
-
source: DesktopEntitlementSource;
|
|
22
|
-
userId: string | null;
|
|
23
|
-
validatedAt: number | null;
|
|
24
|
-
graceUntil: number | null;
|
|
25
|
-
trialEndsAt: number | null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface DesktopUsageSummary {
|
|
29
|
-
type: 'credits' | 'requests';
|
|
30
|
-
used: number;
|
|
31
|
-
limit: number;
|
|
32
|
-
pct?: number;
|
|
33
|
-
resetAt?: number;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
interface DesktopFeatureDefinition {
|
|
37
|
-
label: string;
|
|
38
|
-
description: string;
|
|
39
|
-
free: boolean;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const DESKTOP_FEATURES: Record<DesktopFeature, DesktopFeatureDefinition> = {
|
|
43
|
-
viewer_basic: {
|
|
44
|
-
label: 'Viewer features',
|
|
45
|
-
description: 'Core model viewing and inspection capabilities.',
|
|
46
|
-
free: true,
|
|
47
|
-
},
|
|
48
|
-
workspace_restore: {
|
|
49
|
-
label: 'Workspace restore',
|
|
50
|
-
description: 'Host-provided workspace persistence features.',
|
|
51
|
-
free: true,
|
|
52
|
-
},
|
|
53
|
-
exports: {
|
|
54
|
-
label: 'Exports',
|
|
55
|
-
description: 'Host-provided export integrations.',
|
|
56
|
-
free: true,
|
|
57
|
-
},
|
|
58
|
-
ids_validation: {
|
|
59
|
-
label: 'IDS validation',
|
|
60
|
-
description: 'Host-provided IDS workflows.',
|
|
61
|
-
free: true,
|
|
62
|
-
},
|
|
63
|
-
bcf_issue_management: {
|
|
64
|
-
label: 'BCF issue management',
|
|
65
|
-
description: 'Host-provided BCF workflows.',
|
|
66
|
-
free: true,
|
|
67
|
-
},
|
|
68
|
-
ai_assistant: {
|
|
69
|
-
label: 'Host AI assistant',
|
|
70
|
-
description: 'Optional host-provided AI integrations.',
|
|
71
|
-
free: true,
|
|
72
|
-
},
|
|
73
|
-
extensions: {
|
|
74
|
-
label: 'Extensions & flavors',
|
|
75
|
-
description: 'Install user-authored extensions, manage flavors, and access the authoring loop.',
|
|
76
|
-
free: true,
|
|
77
|
-
},
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
export function isDesktopBillingEnforced(): boolean {
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export function getDesktopPlanTier(entitlementOrHasPro: DesktopEntitlement | boolean): DesktopPlanTier {
|
|
85
|
-
return typeof entitlementOrHasPro === 'boolean'
|
|
86
|
-
? (entitlementOrHasPro ? 'pro' : 'free')
|
|
87
|
-
: entitlementOrHasPro.tier;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function hasDesktopPro(entitlementOrHasPro: DesktopEntitlement | boolean): boolean {
|
|
91
|
-
return getDesktopPlanTier(entitlementOrHasPro) === 'pro';
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function hasDesktopFeatureAccess(entitlementOrHasPro: DesktopEntitlement | boolean, feature: DesktopFeature): boolean {
|
|
95
|
-
return hasDesktopPro(entitlementOrHasPro) || DESKTOP_FEATURES[feature].free;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export function getDesktopFeatureCatalog(entitlementOrHasPro: DesktopEntitlement | boolean) {
|
|
99
|
-
return (Object.entries(DESKTOP_FEATURES) as Array<[DesktopFeature, DesktopFeatureDefinition]>).map(([key, value]) => ({
|
|
100
|
-
key,
|
|
101
|
-
...value,
|
|
102
|
-
enabled: hasDesktopFeatureAccess(entitlementOrHasPro, key),
|
|
103
|
-
}));
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export function buildDesktopUpgradeUrl(returnTo?: string): string {
|
|
107
|
-
const fallbackReturnTo = typeof window !== 'undefined'
|
|
108
|
-
? `${window.location.pathname}${window.location.search}`
|
|
109
|
-
: '/';
|
|
110
|
-
const nextReturnTo = returnTo ?? fallbackReturnTo;
|
|
111
|
-
return `/upgrade?returnTo=${encodeURIComponent(nextReturnTo)}`;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export function getDefaultDesktopEntitlement(): DesktopEntitlement {
|
|
115
|
-
return {
|
|
116
|
-
tier: 'free',
|
|
117
|
-
status: 'anonymous',
|
|
118
|
-
source: 'anonymous',
|
|
119
|
-
userId: null,
|
|
120
|
-
validatedAt: null,
|
|
121
|
-
graceUntil: null,
|
|
122
|
-
trialEndsAt: null,
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export function getDesktopPlanSummary(entitlementOrHasPro: DesktopEntitlement | boolean, usage: DesktopUsageSummary | null): string {
|
|
127
|
-
void entitlementOrHasPro;
|
|
128
|
-
void usage;
|
|
129
|
-
return 'Desktop entitlements are host-defined and disabled in the open-source web viewer build.';
|
|
130
|
-
}
|
package/src/lib/platform.ts
DELETED
|
@@ -1,23 +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
|
-
/**
|
|
6
|
-
* Platform detection utilities for runtime bridge pattern
|
|
7
|
-
* Routes to WASM (browser) or native (Tauri desktop) implementations
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Detects if running in Tauri desktop environment
|
|
12
|
-
*/
|
|
13
|
-
export function isTauri(): boolean {
|
|
14
|
-
return typeof window !== 'undefined' && '__TAURI_INTERNALS__' in window;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Platform-specific cache implementation
|
|
19
|
-
* Returns 'indexeddb' for browser, 'filesystem' for desktop
|
|
20
|
-
*/
|
|
21
|
-
export function getCacheType(): 'indexeddb' | 'filesystem' {
|
|
22
|
-
return isTauri() ? 'filesystem' : 'indexeddb';
|
|
23
|
-
}
|
|
@@ -1,186 +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
|
-
/**
|
|
6
|
-
* Desktop Cache Service
|
|
7
|
-
*
|
|
8
|
-
* File system-based caching for Tauri desktop apps.
|
|
9
|
-
* Replaces IndexedDB caching used in the web version.
|
|
10
|
-
*
|
|
11
|
-
* This module uses dynamic imports to avoid bundler issues in web builds.
|
|
12
|
-
* The @tauri-apps/api package is only loaded at runtime in Tauri environments.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
// Tauri API types - dynamically imported to avoid issues in web builds
|
|
16
|
-
type InvokeFn = <T>(cmd: string, args?: Record<string, unknown>) => Promise<T>;
|
|
17
|
-
|
|
18
|
-
let invoke: InvokeFn | null = null;
|
|
19
|
-
|
|
20
|
-
async function getInvoke(): Promise<InvokeFn> {
|
|
21
|
-
if (invoke) return invoke;
|
|
22
|
-
// Use globalThis.__TAURI_INTERNALS__ which is set by Tauri runtime
|
|
23
|
-
// This avoids bundler trying to resolve @tauri-apps/api in web builds
|
|
24
|
-
const win = globalThis as unknown as { __TAURI_INTERNALS__?: { invoke: InvokeFn } };
|
|
25
|
-
if (win.__TAURI_INTERNALS__?.invoke) {
|
|
26
|
-
invoke = win.__TAURI_INTERNALS__.invoke;
|
|
27
|
-
return invoke;
|
|
28
|
-
}
|
|
29
|
-
throw new Error('Tauri API not available - this module should only be used in Tauri apps');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface CacheEntry {
|
|
33
|
-
key: string;
|
|
34
|
-
fileName: string;
|
|
35
|
-
fileSize: number;
|
|
36
|
-
cacheSize: number;
|
|
37
|
-
createdAt: number;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface CacheStats {
|
|
41
|
-
entries: CacheEntry[];
|
|
42
|
-
totalSize: number;
|
|
43
|
-
entryCount: number;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface CacheResult {
|
|
47
|
-
buffer: ArrayBuffer;
|
|
48
|
-
sourceBuffer?: ArrayBuffer;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Get cached data by key
|
|
53
|
-
* @param key Cache key (typically file hash)
|
|
54
|
-
* @returns Cached data as CacheResult, or null if not found
|
|
55
|
-
*/
|
|
56
|
-
export async function getCached(key: string): Promise<CacheResult | null> {
|
|
57
|
-
try {
|
|
58
|
-
const inv = await getInvoke();
|
|
59
|
-
const data = await inv<number[] | null>('get_cached', { cacheKey: key });
|
|
60
|
-
if (data) {
|
|
61
|
-
console.log(`[DesktopCache] Cache HIT for key: ${key} (${data.length} bytes)`);
|
|
62
|
-
return { buffer: new Uint8Array(data).buffer };
|
|
63
|
-
}
|
|
64
|
-
console.log(`[DesktopCache] Cache MISS for key: ${key}`);
|
|
65
|
-
return null;
|
|
66
|
-
} catch (error) {
|
|
67
|
-
console.warn('[DesktopCache] Failed to get cached data:', error);
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Save data to cache
|
|
74
|
-
* @param key Cache key (typically file hash)
|
|
75
|
-
* @param buffer Data to cache
|
|
76
|
-
* @param fileName Original file name (for metadata)
|
|
77
|
-
* @param fileSize Original file size (for metadata)
|
|
78
|
-
* @param _sourceBuffer Source buffer (not used in desktop cache, but kept for API compatibility)
|
|
79
|
-
*/
|
|
80
|
-
export async function setCached(
|
|
81
|
-
key: string,
|
|
82
|
-
buffer: ArrayBuffer,
|
|
83
|
-
fileName: string,
|
|
84
|
-
fileSize: number,
|
|
85
|
-
_sourceBuffer?: ArrayBuffer
|
|
86
|
-
): Promise<void> {
|
|
87
|
-
try {
|
|
88
|
-
const inv = await getInvoke();
|
|
89
|
-
const data = Array.from(new Uint8Array(buffer));
|
|
90
|
-
console.log(`[DesktopCache] Caching ${fileName} (${data.length} bytes) with key: ${key}`);
|
|
91
|
-
await inv('set_cached', { cacheKey: key, data });
|
|
92
|
-
console.log(`[DesktopCache] Successfully cached ${fileName}`);
|
|
93
|
-
} catch (error) {
|
|
94
|
-
console.warn('[DesktopCache] Failed to save to cache:', error);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Clear all cached data
|
|
100
|
-
*/
|
|
101
|
-
export async function clearCache(): Promise<void> {
|
|
102
|
-
try {
|
|
103
|
-
const inv = await getInvoke();
|
|
104
|
-
await inv('clear_cache');
|
|
105
|
-
console.log('[DesktopCache] Cache cleared');
|
|
106
|
-
} catch (error) {
|
|
107
|
-
console.warn('[DesktopCache] Failed to clear cache:', error);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Get cache statistics
|
|
113
|
-
*/
|
|
114
|
-
export async function getCacheStats(): Promise<CacheStats> {
|
|
115
|
-
try {
|
|
116
|
-
const inv = await getInvoke();
|
|
117
|
-
return await inv<CacheStats>('get_cache_stats');
|
|
118
|
-
} catch (error) {
|
|
119
|
-
console.warn('[DesktopCache] Failed to get cache stats:', error);
|
|
120
|
-
return { entries: [], totalSize: 0, entryCount: 0 };
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Delete a cache entry
|
|
126
|
-
*/
|
|
127
|
-
export async function deleteCached(key: string): Promise<void> {
|
|
128
|
-
try {
|
|
129
|
-
const inv = await getInvoke();
|
|
130
|
-
await inv('delete_cache_entry', { cacheKey: key });
|
|
131
|
-
console.log('[DesktopCache] Cache entry deleted:', key);
|
|
132
|
-
} catch (error) {
|
|
133
|
-
console.warn('[DesktopCache] Failed to delete cache entry:', error);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Check if a key exists in cache
|
|
139
|
-
*/
|
|
140
|
-
export async function hasCached(key: string): Promise<boolean> {
|
|
141
|
-
const data = await getCached(key);
|
|
142
|
-
return data !== null;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export async function hasNativeGeometryCache(key: string): Promise<boolean> {
|
|
146
|
-
try {
|
|
147
|
-
const inv = await getInvoke();
|
|
148
|
-
return await inv<boolean>('has_native_geometry_cache', { cacheKey: key });
|
|
149
|
-
} catch (error) {
|
|
150
|
-
console.warn('[DesktopCache] Failed to check native geometry cache:', error);
|
|
151
|
-
return false;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
export async function getNativeModelSnapshot(key: string): Promise<ArrayBuffer | null> {
|
|
156
|
-
try {
|
|
157
|
-
const inv = await getInvoke();
|
|
158
|
-
const data = await inv<number[] | null>('get_native_model_snapshot', { cacheKey: key });
|
|
159
|
-
return data ? new Uint8Array(data).buffer : null;
|
|
160
|
-
} catch (error) {
|
|
161
|
-
console.warn('[DesktopCache] Failed to read native model snapshot:', error);
|
|
162
|
-
return null;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export async function setNativeModelSnapshot(key: string, buffer: ArrayBuffer): Promise<void> {
|
|
167
|
-
try {
|
|
168
|
-
const inv = await getInvoke();
|
|
169
|
-
await inv('set_native_model_snapshot', {
|
|
170
|
-
cacheKey: key,
|
|
171
|
-
data: Array.from(new Uint8Array(buffer)),
|
|
172
|
-
});
|
|
173
|
-
} catch (error) {
|
|
174
|
-
console.warn('[DesktopCache] Failed to write native model snapshot:', error);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export async function hasNativeModelSnapshot(key: string): Promise<boolean> {
|
|
179
|
-
try {
|
|
180
|
-
const inv = await getInvoke();
|
|
181
|
-
return await inv<boolean>('has_native_model_snapshot', { cacheKey: key });
|
|
182
|
-
} catch (error) {
|
|
183
|
-
console.warn('[DesktopCache] Failed to check native model snapshot:', error);
|
|
184
|
-
return false;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
@@ -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
|
-
}
|