@ifc-lite/viewer 1.17.4 → 1.17.6
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 +16 -16
- package/.turbo/turbo-typecheck.log +1 -1
- package/CHANGELOG.md +117 -0
- package/DESKTOP_CONTRACT_VERSION +1 -1
- package/dist/assets/{basketViewActivator-BmnNtVfZ.js → basketViewActivator-86rgogji.js} +1 -1
- package/dist/assets/drawing-2d-DoxKMqbO.js +257 -0
- package/dist/assets/{exporters-ChAtBmlj.js → exporters-CcPS9MK5.js} +2274 -2227
- package/dist/assets/{geometry.worker-BQ0rzNo-.js → geometry.worker-BFUYA08u.js} +1 -1
- package/dist/assets/ids-DQ5jY0E8.js +1 -0
- package/dist/assets/ifc-lite_bg-BINvzoCP.wasm +0 -0
- package/dist/assets/{index-Co8E2-FE.js → index-Bfms9I4A.js} +35160 -33084
- package/dist/assets/index-_bfZsDCC.css +1 -0
- package/dist/assets/{native-bridge-BRvbckFQ.js → native-bridge-DUyLCMZS.js} +104 -104
- package/dist/assets/{sandbox-DZiNLNMk.js → sandbox-C8575tul.js} +4340 -4322
- package/dist/assets/{server-client-BV8zHZ7Y.js → server-client-BuZK7OST.js} +1 -1
- package/dist/assets/{wasm-bridge-g01g7T9b.js → wasm-bridge-JsqEGDV8.js} +1 -1
- package/dist/index.html +8 -7
- package/index.html +1 -0
- package/package.json +7 -7
- package/src/App.tsx +16 -2
- package/src/components/viewer/CesiumOverlay.tsx +62 -19
- package/src/components/viewer/ChatPanel.tsx +195 -91
- package/src/components/viewer/MainToolbar.tsx +4 -3
- package/src/components/viewer/PropertiesPanel.tsx +16 -2
- package/src/components/viewer/SettingsPage.tsx +252 -101
- package/src/components/viewer/ThemeSwitch.tsx +63 -7
- package/src/components/viewer/ViewerLayout.tsx +1 -0
- package/src/components/viewer/Viewport.tsx +14 -2
- package/src/components/viewer/ViewportContainer.tsx +49 -64
- package/src/components/viewer/ViewportOverlays.tsx +5 -2
- package/src/components/viewer/bcf/BCFTopicDetail.tsx +4 -4
- package/src/components/viewer/chat/ModelSelector.tsx +90 -54
- package/src/components/viewer/properties/GeoreferencingPanel.tsx +113 -51
- package/src/components/viewer/properties/LocationMap.tsx +9 -7
- package/src/components/viewer/properties/ModelMetadataPanel.tsx +1 -1
- package/src/components/viewer/tools/SectionCapControls.tsx +237 -0
- package/src/components/viewer/tools/SectionPanel.tsx +39 -18
- package/src/components/viewer/useAnimationLoop.ts +9 -1
- package/src/components/viewer/useRenderUpdates.ts +1 -1
- package/src/hooks/ids/idsDataAccessor.ts +60 -24
- package/src/hooks/ingest/viewerModelIngest.ts +7 -2
- package/src/hooks/useIfcFederation.ts +326 -71
- package/src/hooks/useIfcLoader.ts +1 -0
- package/src/hooks/useViewControls.ts +13 -5
- package/src/index.css +484 -10
- package/src/lib/desktop-entitlement.ts +2 -4
- package/src/lib/geo/cesium-bridge.ts +15 -7
- package/src/lib/geo/effective-georef.test.ts +73 -0
- package/src/lib/geo/effective-georef.ts +111 -0
- package/src/lib/geo/reproject.ts +105 -19
- package/src/lib/llm/byok-guard.test.ts +77 -0
- package/src/lib/llm/byok-guard.ts +39 -0
- package/src/lib/llm/free-models.test.ts +0 -6
- package/src/lib/llm/models.ts +104 -42
- package/src/lib/llm/stream-client.ts +74 -110
- package/src/lib/llm/stream-direct.test.ts +130 -0
- package/src/lib/llm/stream-direct.ts +316 -0
- package/src/lib/llm/types.ts +14 -2
- package/src/main.tsx +1 -10
- package/src/services/api-keys.ts +73 -0
- package/src/store/constants.ts +20 -2
- package/src/store/index.ts +12 -5
- package/src/store/slices/cesiumSlice.ts +5 -0
- package/src/store/slices/chatSlice.test.ts +6 -76
- package/src/store/slices/chatSlice.ts +17 -58
- package/src/store/slices/sectionSlice.test.ts +87 -7
- package/src/store/slices/sectionSlice.ts +151 -5
- package/src/store/slices/uiSlice.ts +28 -5
- package/src/store/types.ts +26 -0
- package/src/utils/nativeSpatialDataStore.ts +4 -1
- package/src/utils/viewportUtils.ts +7 -2
- package/src/vite-env.d.ts +0 -4
- package/dist/assets/drawing-2d-gWfpdfYe.js +0 -257
- package/dist/assets/ids-B4jTqB1O.js +0 -1
- package/dist/assets/ifc-lite_bg-BX4E7TX8.wasm +0 -0
- package/dist/assets/index-DckuDqlv.css +0 -1
- package/src/components/viewer/UpgradePage.tsx +0 -71
- package/src/lib/desktop/ClerkDesktopEntitlementSync.tsx +0 -175
- package/src/lib/llm/ClerkChatSync.tsx +0 -74
- package/src/lib/llm/clerk-auth.ts +0 -62
|
@@ -1,175 +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 { useEffect } from 'react';
|
|
6
|
-
import { useAuth, useUser } from '@clerk/clerk-react';
|
|
7
|
-
import { getDefaultDesktopEntitlement, type DesktopEntitlement } from '@/lib/desktop-product';
|
|
8
|
-
import { resolveDesktopEntitlement } from '@/lib/desktop-entitlement';
|
|
9
|
-
import { useViewerStore } from '@/store';
|
|
10
|
-
import { DESKTOP_ENTITLEMENT_REFRESH_EVENT } from './desktopEntitlementEvents';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Sync Clerk auth + entitlement state into the viewer store.
|
|
14
|
-
* Desktop plan state is authoritative here; chat inherits only the AI-specific permission.
|
|
15
|
-
*/
|
|
16
|
-
const ENTITLEMENT_REFRESH_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
17
|
-
|
|
18
|
-
function entitlementsEqual(left: DesktopEntitlement, right: DesktopEntitlement): boolean {
|
|
19
|
-
return left.tier === right.tier
|
|
20
|
-
&& left.status === right.status
|
|
21
|
-
&& left.source === right.source
|
|
22
|
-
&& left.userId === right.userId
|
|
23
|
-
&& left.validatedAt === right.validatedAt
|
|
24
|
-
&& left.graceUntil === right.graceUntil
|
|
25
|
-
&& left.trialEndsAt === right.trialEndsAt;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function ClerkDesktopEntitlementSync() {
|
|
29
|
-
const { isLoaded, isSignedIn, userId, getToken, has } = useAuth();
|
|
30
|
-
const { user } = useUser();
|
|
31
|
-
const setChatAuthToken = useViewerStore((s) => s.setChatAuthToken);
|
|
32
|
-
const switchChatUserContext = useViewerStore((s) => s.switchChatUserContext);
|
|
33
|
-
const setDesktopEntitlement = useViewerStore((s) => s.setDesktopEntitlement);
|
|
34
|
-
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
if (!isLoaded) return;
|
|
37
|
-
|
|
38
|
-
if (!isSignedIn) {
|
|
39
|
-
const state = useViewerStore.getState();
|
|
40
|
-
const next = (
|
|
41
|
-
state.desktopEntitlement.tier === 'pro'
|
|
42
|
-
&& state.desktopEntitlement.graceUntil
|
|
43
|
-
&& state.desktopEntitlement.graceUntil > Date.now()
|
|
44
|
-
)
|
|
45
|
-
? {
|
|
46
|
-
...state.desktopEntitlement,
|
|
47
|
-
status: 'grace_offline' as const,
|
|
48
|
-
source: 'cached' as const,
|
|
49
|
-
}
|
|
50
|
-
: {
|
|
51
|
-
...getDefaultDesktopEntitlement(),
|
|
52
|
-
status: 'signed_out' as const,
|
|
53
|
-
source: 'cached' as const,
|
|
54
|
-
};
|
|
55
|
-
if (!entitlementsEqual(state.desktopEntitlement, next)) {
|
|
56
|
-
setDesktopEntitlement(next);
|
|
57
|
-
}
|
|
58
|
-
if (next.status === 'grace_offline') {
|
|
59
|
-
switchChatUserContext(next.userId, false, {
|
|
60
|
-
clearPersistedCurrent: false,
|
|
61
|
-
restoreMessages: true,
|
|
62
|
-
});
|
|
63
|
-
} else {
|
|
64
|
-
switchChatUserContext(null, false, {
|
|
65
|
-
clearPersistedCurrent: state.chatStorageUserId !== null,
|
|
66
|
-
restoreMessages: false,
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
setChatAuthToken(null);
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
let cancelled = false;
|
|
74
|
-
|
|
75
|
-
const syncAuth = async () => {
|
|
76
|
-
try {
|
|
77
|
-
const token = await getToken({ skipCache: true });
|
|
78
|
-
const { entitlement, aiAssistantEnabled } = resolveDesktopEntitlement({
|
|
79
|
-
userId: userId ?? null,
|
|
80
|
-
token,
|
|
81
|
-
has,
|
|
82
|
-
publicMetadata: (user?.publicMetadata ?? null) as Record<string, unknown> | null,
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
if (cancelled) {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const state = useViewerStore.getState();
|
|
90
|
-
if (!entitlementsEqual(state.desktopEntitlement, entitlement)) {
|
|
91
|
-
setDesktopEntitlement(entitlement);
|
|
92
|
-
}
|
|
93
|
-
if (state.chatStorageUserId !== (userId ?? null) || state.chatHasPro !== aiAssistantEnabled) {
|
|
94
|
-
switchChatUserContext(userId ?? null, aiAssistantEnabled, {
|
|
95
|
-
clearPersistedCurrent: state.chatStorageUserId !== null && state.chatStorageUserId !== userId,
|
|
96
|
-
restoreMessages: true,
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
setChatAuthToken(token ?? null);
|
|
100
|
-
} catch {
|
|
101
|
-
if (cancelled) {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const state = useViewerStore.getState();
|
|
106
|
-
if (
|
|
107
|
-
state.desktopEntitlement.tier === 'pro'
|
|
108
|
-
&& state.desktopEntitlement.graceUntil
|
|
109
|
-
&& state.desktopEntitlement.graceUntil > Date.now()
|
|
110
|
-
) {
|
|
111
|
-
const next = {
|
|
112
|
-
...state.desktopEntitlement,
|
|
113
|
-
status: 'grace_offline',
|
|
114
|
-
source: 'cached',
|
|
115
|
-
} satisfies DesktopEntitlement;
|
|
116
|
-
if (!entitlementsEqual(state.desktopEntitlement, next)) {
|
|
117
|
-
setDesktopEntitlement(next);
|
|
118
|
-
}
|
|
119
|
-
if (state.chatStorageUserId !== (userId ?? null) || state.chatHasPro !== true) {
|
|
120
|
-
switchChatUserContext(userId ?? null, true, {
|
|
121
|
-
clearPersistedCurrent: false,
|
|
122
|
-
restoreMessages: true,
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const next = {
|
|
129
|
-
...getDefaultDesktopEntitlement(),
|
|
130
|
-
userId: userId ?? null,
|
|
131
|
-
status: 'signed_out' as const,
|
|
132
|
-
source: 'cached' as const,
|
|
133
|
-
};
|
|
134
|
-
if (!entitlementsEqual(state.desktopEntitlement, next)) {
|
|
135
|
-
setDesktopEntitlement(next);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
void syncAuth();
|
|
141
|
-
const timer = window.setInterval(() => {
|
|
142
|
-
void syncAuth();
|
|
143
|
-
}, ENTITLEMENT_REFRESH_INTERVAL_MS);
|
|
144
|
-
const onWindowFocus = () => {
|
|
145
|
-
void syncAuth();
|
|
146
|
-
};
|
|
147
|
-
const onManualRefresh = (event: Event) => {
|
|
148
|
-
const detail = (event as CustomEvent<{ resolve: () => void; reject: (error?: unknown) => void }>).detail;
|
|
149
|
-
void syncAuth()
|
|
150
|
-
.then(() => detail?.resolve())
|
|
151
|
-
.catch((error) => detail?.reject(error));
|
|
152
|
-
};
|
|
153
|
-
window.addEventListener('focus', onWindowFocus);
|
|
154
|
-
window.addEventListener(DESKTOP_ENTITLEMENT_REFRESH_EVENT, onManualRefresh);
|
|
155
|
-
|
|
156
|
-
return () => {
|
|
157
|
-
cancelled = true;
|
|
158
|
-
window.clearInterval(timer);
|
|
159
|
-
window.removeEventListener('focus', onWindowFocus);
|
|
160
|
-
window.removeEventListener(DESKTOP_ENTITLEMENT_REFRESH_EVENT, onManualRefresh);
|
|
161
|
-
};
|
|
162
|
-
}, [
|
|
163
|
-
getToken,
|
|
164
|
-
has,
|
|
165
|
-
isLoaded,
|
|
166
|
-
isSignedIn,
|
|
167
|
-
setChatAuthToken,
|
|
168
|
-
setDesktopEntitlement,
|
|
169
|
-
switchChatUserContext,
|
|
170
|
-
user?.publicMetadata,
|
|
171
|
-
userId,
|
|
172
|
-
]);
|
|
173
|
-
|
|
174
|
-
return null;
|
|
175
|
-
}
|
|
@@ -1,74 +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 { useEffect } from 'react';
|
|
6
|
-
import { useAuth } from '@clerk/clerk-react';
|
|
7
|
-
import { useViewerStore } from '@/store';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Sync Clerk session state into chat store for authenticated LLM requests.
|
|
11
|
-
* Keeps chat UX decoupled from auth/billing details.
|
|
12
|
-
*/
|
|
13
|
-
export function ClerkChatSync() {
|
|
14
|
-
const { isLoaded, isSignedIn, userId, getToken, has } = useAuth();
|
|
15
|
-
const setChatAuthToken = useViewerStore((s) => s.setChatAuthToken);
|
|
16
|
-
const switchChatUserContext = useViewerStore((s) => s.switchChatUserContext);
|
|
17
|
-
const currentChatUserId = useViewerStore((s) => s.chatStorageUserId);
|
|
18
|
-
const currentChatHasPro = useViewerStore((s) => s.chatHasPro);
|
|
19
|
-
|
|
20
|
-
useEffect(() => {
|
|
21
|
-
if (!isLoaded) return;
|
|
22
|
-
|
|
23
|
-
if (!isSignedIn) {
|
|
24
|
-
switchChatUserContext(null, false, {
|
|
25
|
-
clearPersistedCurrent: currentChatUserId !== null,
|
|
26
|
-
restoreMessages: false,
|
|
27
|
-
});
|
|
28
|
-
setChatAuthToken(null);
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
let cancelled = false;
|
|
33
|
-
|
|
34
|
-
const syncAuth = async () => {
|
|
35
|
-
try {
|
|
36
|
-
const token = await getToken({ skipCache: true });
|
|
37
|
-
const proPlan = has?.({ plan: 'pro' }) ?? false;
|
|
38
|
-
const proFeature = has?.({ feature: 'pro_models' }) ?? false;
|
|
39
|
-
const nextHasPro = proPlan || proFeature;
|
|
40
|
-
if (!cancelled) {
|
|
41
|
-
// Avoid resetting chat usage/messages on routine token refreshes for
|
|
42
|
-
// the same signed-in user. Only switch context when identity or
|
|
43
|
-
// entitlement actually changes.
|
|
44
|
-
if (currentChatUserId !== (userId ?? null) || currentChatHasPro !== nextHasPro) {
|
|
45
|
-
switchChatUserContext(userId ?? null, nextHasPro, {
|
|
46
|
-
clearPersistedCurrent: currentChatUserId !== null && currentChatUserId !== userId,
|
|
47
|
-
restoreMessages: true,
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
if (token) {
|
|
51
|
-
setChatAuthToken(token);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
} catch {
|
|
55
|
-
if (!cancelled) {
|
|
56
|
-
// Preserve the current signed-in chat context on transient token
|
|
57
|
-
// refresh failures. Explicit sign-out is handled above.
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
void syncAuth();
|
|
63
|
-
// Keep short-lived JWTs fresh so chat/usage polling doesn't reuse expired tokens.
|
|
64
|
-
const timer = window.setInterval(() => {
|
|
65
|
-
void syncAuth();
|
|
66
|
-
}, 15_000);
|
|
67
|
-
return () => {
|
|
68
|
-
cancelled = true;
|
|
69
|
-
window.clearInterval(timer);
|
|
70
|
-
};
|
|
71
|
-
}, [currentChatHasPro, currentChatUserId, getToken, has, isLoaded, isSignedIn, setChatAuthToken, switchChatUserContext, userId]);
|
|
72
|
-
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
@@ -1,62 +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
|
-
* Auth helpers for the LLM chat integration.
|
|
7
|
-
*
|
|
8
|
-
* Setup:
|
|
9
|
-
* 1. Install: pnpm add @clerk/clerk-react
|
|
10
|
-
* 2. Set VITE_CLERK_PUBLISHABLE_KEY in .env
|
|
11
|
-
* 3. Create plans in dashboard:
|
|
12
|
-
* - Free plan (slug: 'free', features: ['llm_chat', 'free_models'])
|
|
13
|
-
* - Pro plan (slug: 'pro', $8/month, features: ['llm_chat', 'free_models', 'pro_models'])
|
|
14
|
-
* 4. Wrap app with <ClerkProvider>
|
|
15
|
-
*
|
|
16
|
-
* Usage in components:
|
|
17
|
-
*
|
|
18
|
-
* ```tsx
|
|
19
|
-
* import { useAuth, useUser, Protect } from '@clerk/clerk-react';
|
|
20
|
-
*
|
|
21
|
-
* const { has } = useAuth();
|
|
22
|
-
* const hasPro = has?.({ feature: 'pro_models' }) ?? false;
|
|
23
|
-
*
|
|
24
|
-
* const { getToken } = useAuth();
|
|
25
|
-
* const token = await getToken();
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Subscription tiers and their features.
|
|
31
|
-
*/
|
|
32
|
-
export const SUBSCRIPTION_PLANS = {
|
|
33
|
-
free: {
|
|
34
|
-
slug: 'free',
|
|
35
|
-
name: 'Free',
|
|
36
|
-
features: ['llm_chat', 'free_models'],
|
|
37
|
-
description: 'AI chat with free models',
|
|
38
|
-
},
|
|
39
|
-
pro: {
|
|
40
|
-
slug: 'pro',
|
|
41
|
-
name: 'Pro',
|
|
42
|
-
features: ['llm_chat', 'free_models', 'pro_models'],
|
|
43
|
-
description: 'All models with monthly credits ($8/month)',
|
|
44
|
-
},
|
|
45
|
-
} as const;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Feature flags that map to plan features.
|
|
49
|
-
*/
|
|
50
|
-
export const FEATURES = {
|
|
51
|
-
LLM_CHAT: 'llm_chat',
|
|
52
|
-
FREE_MODELS: 'free_models',
|
|
53
|
-
PRO_MODELS: 'pro_models',
|
|
54
|
-
} as const;
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Check if auth is configured (publishable key present).
|
|
58
|
-
* When not configured, the chat works in anonymous free-tier mode.
|
|
59
|
-
*/
|
|
60
|
-
export function isClerkConfigured(): boolean {
|
|
61
|
-
return Boolean(import.meta.env.VITE_CLERK_PUBLISHABLE_KEY);
|
|
62
|
-
}
|