@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.
Files changed (80) hide show
  1. package/.turbo/turbo-build.log +16 -16
  2. package/.turbo/turbo-typecheck.log +1 -1
  3. package/CHANGELOG.md +117 -0
  4. package/DESKTOP_CONTRACT_VERSION +1 -1
  5. package/dist/assets/{basketViewActivator-BmnNtVfZ.js → basketViewActivator-86rgogji.js} +1 -1
  6. package/dist/assets/drawing-2d-DoxKMqbO.js +257 -0
  7. package/dist/assets/{exporters-ChAtBmlj.js → exporters-CcPS9MK5.js} +2274 -2227
  8. package/dist/assets/{geometry.worker-BQ0rzNo-.js → geometry.worker-BFUYA08u.js} +1 -1
  9. package/dist/assets/ids-DQ5jY0E8.js +1 -0
  10. package/dist/assets/ifc-lite_bg-BINvzoCP.wasm +0 -0
  11. package/dist/assets/{index-Co8E2-FE.js → index-Bfms9I4A.js} +35160 -33084
  12. package/dist/assets/index-_bfZsDCC.css +1 -0
  13. package/dist/assets/{native-bridge-BRvbckFQ.js → native-bridge-DUyLCMZS.js} +104 -104
  14. package/dist/assets/{sandbox-DZiNLNMk.js → sandbox-C8575tul.js} +4340 -4322
  15. package/dist/assets/{server-client-BV8zHZ7Y.js → server-client-BuZK7OST.js} +1 -1
  16. package/dist/assets/{wasm-bridge-g01g7T9b.js → wasm-bridge-JsqEGDV8.js} +1 -1
  17. package/dist/index.html +8 -7
  18. package/index.html +1 -0
  19. package/package.json +7 -7
  20. package/src/App.tsx +16 -2
  21. package/src/components/viewer/CesiumOverlay.tsx +62 -19
  22. package/src/components/viewer/ChatPanel.tsx +195 -91
  23. package/src/components/viewer/MainToolbar.tsx +4 -3
  24. package/src/components/viewer/PropertiesPanel.tsx +16 -2
  25. package/src/components/viewer/SettingsPage.tsx +252 -101
  26. package/src/components/viewer/ThemeSwitch.tsx +63 -7
  27. package/src/components/viewer/ViewerLayout.tsx +1 -0
  28. package/src/components/viewer/Viewport.tsx +14 -2
  29. package/src/components/viewer/ViewportContainer.tsx +49 -64
  30. package/src/components/viewer/ViewportOverlays.tsx +5 -2
  31. package/src/components/viewer/bcf/BCFTopicDetail.tsx +4 -4
  32. package/src/components/viewer/chat/ModelSelector.tsx +90 -54
  33. package/src/components/viewer/properties/GeoreferencingPanel.tsx +113 -51
  34. package/src/components/viewer/properties/LocationMap.tsx +9 -7
  35. package/src/components/viewer/properties/ModelMetadataPanel.tsx +1 -1
  36. package/src/components/viewer/tools/SectionCapControls.tsx +237 -0
  37. package/src/components/viewer/tools/SectionPanel.tsx +39 -18
  38. package/src/components/viewer/useAnimationLoop.ts +9 -1
  39. package/src/components/viewer/useRenderUpdates.ts +1 -1
  40. package/src/hooks/ids/idsDataAccessor.ts +60 -24
  41. package/src/hooks/ingest/viewerModelIngest.ts +7 -2
  42. package/src/hooks/useIfcFederation.ts +326 -71
  43. package/src/hooks/useIfcLoader.ts +1 -0
  44. package/src/hooks/useViewControls.ts +13 -5
  45. package/src/index.css +484 -10
  46. package/src/lib/desktop-entitlement.ts +2 -4
  47. package/src/lib/geo/cesium-bridge.ts +15 -7
  48. package/src/lib/geo/effective-georef.test.ts +73 -0
  49. package/src/lib/geo/effective-georef.ts +111 -0
  50. package/src/lib/geo/reproject.ts +105 -19
  51. package/src/lib/llm/byok-guard.test.ts +77 -0
  52. package/src/lib/llm/byok-guard.ts +39 -0
  53. package/src/lib/llm/free-models.test.ts +0 -6
  54. package/src/lib/llm/models.ts +104 -42
  55. package/src/lib/llm/stream-client.ts +74 -110
  56. package/src/lib/llm/stream-direct.test.ts +130 -0
  57. package/src/lib/llm/stream-direct.ts +316 -0
  58. package/src/lib/llm/types.ts +14 -2
  59. package/src/main.tsx +1 -10
  60. package/src/services/api-keys.ts +73 -0
  61. package/src/store/constants.ts +20 -2
  62. package/src/store/index.ts +12 -5
  63. package/src/store/slices/cesiumSlice.ts +5 -0
  64. package/src/store/slices/chatSlice.test.ts +6 -76
  65. package/src/store/slices/chatSlice.ts +17 -58
  66. package/src/store/slices/sectionSlice.test.ts +87 -7
  67. package/src/store/slices/sectionSlice.ts +151 -5
  68. package/src/store/slices/uiSlice.ts +28 -5
  69. package/src/store/types.ts +26 -0
  70. package/src/utils/nativeSpatialDataStore.ts +4 -1
  71. package/src/utils/viewportUtils.ts +7 -2
  72. package/src/vite-env.d.ts +0 -4
  73. package/dist/assets/drawing-2d-gWfpdfYe.js +0 -257
  74. package/dist/assets/ids-B4jTqB1O.js +0 -1
  75. package/dist/assets/ifc-lite_bg-BX4E7TX8.wasm +0 -0
  76. package/dist/assets/index-DckuDqlv.css +0 -1
  77. package/src/components/viewer/UpgradePage.tsx +0 -71
  78. package/src/lib/desktop/ClerkDesktopEntitlementSync.tsx +0 -175
  79. package/src/lib/llm/ClerkChatSync.tsx +0 -74
  80. 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
- }