@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.
Files changed (110) hide show
  1. package/.turbo/turbo-build.log +34 -41
  2. package/CHANGELOG.md +10 -0
  3. package/dist/assets/{basketViewActivator-BNRDNuUJ.js → basketViewActivator-Ce38DhXd.js} +7 -7
  4. package/dist/assets/{bcf-DCwCuP7n.js → bcf-Cv_O3JfD.js} +1 -1
  5. package/dist/assets/{deflate-DNGgs8Ur.js → deflate-HbyMq59o.js} +1 -1
  6. package/dist/assets/drawing-2d-DW98umlt.js +257 -0
  7. package/dist/assets/{exporters-B9v81gi9.js → exporters-BuD3XRzB.js} +463 -416
  8. package/dist/assets/geometry.worker-TH3fCCoY.js +1 -0
  9. package/dist/assets/{geotiff-D-YCLS4g.js → geotiff-B2HA8Bwm.js} +10 -10
  10. package/dist/assets/{ids-CCpq-5d3.js → ids-DYUFMd5f.js} +4 -4
  11. package/dist/assets/{ifc-lite_bg-DbgS5EUA.wasm → ifc-lite_bg-BEA5DLmg.wasm} +0 -0
  12. package/dist/assets/index-E9wB0zWt.css +1 -0
  13. package/dist/assets/{index-Bgb3_Pu_.js → index-n5O1QJMM.js} +36808 -39415
  14. package/dist/assets/{index.es-CWfqZyyr.js → index.es-BKVIpZgL.js} +8 -8
  15. package/dist/assets/{jpeg-DGOAeUqU.js → jpeg-C7hjKjPX.js} +1 -1
  16. package/dist/assets/{jspdf.es.min-XPLU2Wkq.js → jspdf.es.min-oWlFc42Y.js} +4 -4
  17. package/dist/assets/{lerc-1PMSCHwX.js → lerc-BfIOGhQz.js} +1 -1
  18. package/dist/assets/{lzw-C65U9lNM.js → lzw-B0jRuuW5.js} +1 -1
  19. package/dist/assets/{native-bridge-XxXos6yI.js → native-bridge-DpB-dtEn.js} +5 -2
  20. package/dist/assets/{packbits-BdMWXC3m.js → packbits-DVvBTC39.js} +1 -1
  21. package/dist/assets/{parser.worker-Ddwo3_06.js → parser.worker-BDsWQ6rc.js} +1 -1
  22. package/dist/assets/{pdf-CRwaZf3s.js → pdf-dVIqI5ac.js} +9 -9
  23. package/dist/assets/raw-C0ZJYGmN.js +1 -0
  24. package/dist/assets/{sandbox-0sDo3g3m.js → sandbox-qpJlrNN0.js} +8 -8
  25. package/dist/assets/{server-client-cTCJ-853.js → server-client-DVZ2huNS.js} +1 -1
  26. package/dist/assets/{webimage-BtakWX7W.js → webimage-B394g0Tw.js} +1 -1
  27. package/dist/assets/{xlsx-B1YOg2QB.js → xlsx-D-oHO76J.js} +7 -7
  28. package/dist/assets/{zstd-CmwsbxmM.js → zstd-Bf38MwV2.js} +1 -1
  29. package/dist/index.html +8 -8
  30. package/package.json +5 -5
  31. package/src/App.tsx +1 -3
  32. package/src/components/viewer/BCFPanel.tsx +1 -16
  33. package/src/components/viewer/ChatPanel.tsx +11 -46
  34. package/src/components/viewer/HierarchyPanel.tsx +2 -176
  35. package/src/components/viewer/IDSPanel.tsx +1 -26
  36. package/src/components/viewer/MainToolbar.tsx +75 -185
  37. package/src/components/viewer/MobileToolbar.tsx +1 -9
  38. package/src/components/viewer/PropertiesPanel.tsx +28 -126
  39. package/src/components/viewer/ScriptPanel.tsx +8 -34
  40. package/src/components/viewer/Section2DPanel.tsx +32 -1
  41. package/src/components/viewer/ViewerLayout.tsx +0 -2
  42. package/src/components/viewer/ViewportContainer.tsx +24 -42
  43. package/src/components/viewer/ViewportOverlays.tsx +1 -4
  44. package/src/components/viewer/useGeometryStreaming.ts +0 -2
  45. package/src/hooks/ingest/federationAlign.ts +7 -0
  46. package/src/hooks/useDrawingGeneration.ts +211 -13
  47. package/src/hooks/useIfcCache.ts +94 -41
  48. package/src/hooks/useIfcFederation.ts +2 -3
  49. package/src/hooks/useIfcLoader.ts +10 -1051
  50. package/src/services/cacheService.ts +9 -25
  51. package/src/services/desktop-export.ts +2 -59
  52. package/src/services/file-dialog.ts +8 -142
  53. package/src/store/constants.ts +23 -0
  54. package/src/store/index.ts +3 -5
  55. package/src/store/slices/drawing2DSlice.ts +8 -0
  56. package/src/store/slices/visibilitySlice.ts +22 -1
  57. package/src/store/types.ts +1 -71
  58. package/src/utils/ifcConfig.ts +0 -12
  59. package/vite.config.ts +6 -3
  60. package/DESKTOP_CONTRACT_VERSION +0 -1
  61. package/dist/assets/drawing-2d-D0dDf6Lh.js +0 -257
  62. package/dist/assets/event-B0kAzHa-.js +0 -1
  63. package/dist/assets/geometry.worker-Bpa3115V.js +0 -1
  64. package/dist/assets/index-BtbXFKsX.css +0 -1
  65. package/dist/assets/raw-CJgQdyuZ.js +0 -1
  66. package/dist/assets/tauri-core-stub-D8Fa-u43.js +0 -1
  67. package/dist/assets/tauri-dialog-stub-r7Wksg7o.js +0 -1
  68. package/dist/assets/tauri-fs-stub-BdeRC7aK.js +0 -1
  69. package/src/components/viewer/DesktopEntitlementBanner.tsx +0 -74
  70. package/src/components/viewer/SettingsPage.tsx +0 -581
  71. package/src/lib/desktop/desktopEntitlementEvents.ts +0 -39
  72. package/src/lib/desktop-entitlement.ts +0 -43
  73. package/src/lib/desktop-product.ts +0 -130
  74. package/src/lib/platform.ts +0 -23
  75. package/src/services/desktop-cache.ts +0 -186
  76. package/src/services/desktop-harness.ts +0 -196
  77. package/src/services/desktop-logger.ts +0 -20
  78. package/src/services/desktop-native-metadata.ts +0 -230
  79. package/src/services/desktop-panel-actions.ts +0 -43
  80. package/src/services/desktop-preferences.ts +0 -44
  81. package/src/services/fs-cache.ts +0 -212
  82. package/src/services/tauri-core-stub.ts +0 -7
  83. package/src/services/tauri-dialog-stub.ts +0 -7
  84. package/src/services/tauri-fs-stub.ts +0 -7
  85. package/src/services/tauri-modules.d.ts +0 -50
  86. package/src/store/slices/desktopEntitlementSlice.ts +0 -86
  87. package/src/utils/desktopModelSnapshot.ts +0 -359
  88. package/src/utils/nativeSpatialDataStore.ts +0 -277
  89. package/src-tauri/Cargo.toml +0 -29
  90. package/src-tauri/build.rs +0 -7
  91. package/src-tauri/capabilities/default.json +0 -18
  92. package/src-tauri/icons/128x128.png +0 -0
  93. package/src-tauri/icons/128x128@2x.png +0 -0
  94. package/src-tauri/icons/32x32.png +0 -0
  95. package/src-tauri/icons/Square107x107Logo.png +0 -0
  96. package/src-tauri/icons/Square142x142Logo.png +0 -0
  97. package/src-tauri/icons/Square150x150Logo.png +0 -0
  98. package/src-tauri/icons/Square284x284Logo.png +0 -0
  99. package/src-tauri/icons/Square30x30Logo.png +0 -0
  100. package/src-tauri/icons/Square310x310Logo.png +0 -0
  101. package/src-tauri/icons/Square44x44Logo.png +0 -0
  102. package/src-tauri/icons/Square71x71Logo.png +0 -0
  103. package/src-tauri/icons/Square89x89Logo.png +0 -0
  104. package/src-tauri/icons/StoreLogo.png +0 -0
  105. package/src-tauri/icons/icon.icns +0 -0
  106. package/src-tauri/icons/icon.ico +0 -0
  107. package/src-tauri/icons/icon.png +0 -0
  108. package/src-tauri/src/lib.rs +0 -21
  109. package/src-tauri/src/main.rs +0 -10
  110. 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
- }
@@ -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
- }