@nyris/nyris-webapp 0.3.97 → 0.3.98

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 (32) hide show
  1. package/build/asset-manifest.json +6 -6
  2. package/build/index.html +1 -1
  3. package/build/js/settings.example.js +1 -0
  4. package/build/static/css/main.81216c5d.css +4 -0
  5. package/build/static/css/{main.7c7f5447.css.map → main.81216c5d.css.map} +1 -1
  6. package/build/static/js/main.93fdbd6e.js +3 -0
  7. package/build/static/js/{main.9e7fb38a.js.map → main.93fdbd6e.js.map} +1 -1
  8. package/package.json +3 -3
  9. package/public/js/settings.example.js +1 -0
  10. package/src/components/AuthApiKeySync.tsx +34 -0
  11. package/src/components/ChatAssistant/useChatAssistantLogic.ts +3 -2
  12. package/src/components/ImageUpload.tsx +2 -2
  13. package/src/components/LogoutModal.tsx +2 -0
  14. package/src/components/PostFilter/PostFilterComponent.tsx +8 -22
  15. package/src/components/Product/ProductList.tsx +2 -0
  16. package/src/components/SidePanel.tsx +10 -0
  17. package/src/components/TextSearch.tsx +3 -3
  18. package/src/hooks/useImageSearch.ts +35 -8
  19. package/src/index.tsx +4 -0
  20. package/src/layouts/AppLayout.tsx +52 -6
  21. package/src/pages/Logout.tsx +2 -0
  22. package/src/pages/Result.tsx +12 -6
  23. package/src/providers/AuthProvider.tsx +5 -0
  24. package/src/services/Feedback.ts +2 -1
  25. package/src/services/filter.ts +3 -2
  26. package/src/services/visualSearch.ts +5 -4
  27. package/src/services/vizo.ts +13 -2
  28. package/src/utils/derivePostFilterFacets.ts +68 -0
  29. package/src/utils/nyrisApiKey.ts +115 -0
  30. package/build/static/css/main.7c7f5447.css +0 -4
  31. package/build/static/js/main.9e7fb38a.js +0 -3
  32. /package/build/static/js/{main.9e7fb38a.js.LICENSE.txt → main.93fdbd6e.js.LICENSE.txt} +0 -0
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@nyris/nyris-webapp",
3
- "version": "0.3.97",
3
+ "version": "0.3.98",
4
4
  "dependencies": {
5
5
  "@auth0/auth0-react": "^2.2.4",
6
6
  "@emailjs/browser": "^4.3.3",
7
7
  "@google/genai": "^1.46.0",
8
- "@nyris/nyris-api": "^0.3.97",
9
- "@nyris/nyris-react-components": "^0.3.97",
8
+ "@nyris/nyris-api": "^0.3.98",
9
+ "@nyris/nyris-react-components": "^0.3.98",
10
10
  "@radix-ui/react-accordion": "^1.2.2",
11
11
  "@radix-ui/react-dialog": "^1.1.4",
12
12
  "@radix-ui/react-popover": "^1.1.4",
@@ -23,6 +23,7 @@ var settings = {
23
23
  },
24
24
  ],
25
25
  // Nyris - visual search
26
+ // When auth0.enabled is true, leave apiKey empty — the app uses Auth0 claim
26
27
  apiKey: '',
27
28
  aiApiKey: '',
28
29
  baseUrl: 'https://api.nyris.io',
@@ -0,0 +1,34 @@
1
+ import { useAuth0 } from '@auth0/auth0-react';
2
+ import { useEffect } from 'react';
3
+ import {
4
+ applyAuthApiKey,
5
+ clearAuthApiKey,
6
+ readAuthApiKeyFromUser,
7
+ } from 'utils/nyrisApiKey';
8
+
9
+ /**
10
+ * Keeps window.settings.apiKey in sync with Auth0 claim
11
+ * `https://nyris.io/authApiKey`.
12
+ * Must only render inside Auth0Provider when auth0.enabled is true.
13
+ */
14
+ export function AuthApiKeySync() {
15
+ const { user, isAuthenticated, isLoading } = useAuth0();
16
+
17
+ useEffect(() => {
18
+ if (isLoading) {
19
+ return;
20
+ }
21
+
22
+ if (!isAuthenticated || !user) {
23
+ clearAuthApiKey();
24
+ return;
25
+ }
26
+
27
+ const authApiKey = readAuthApiKeyFromUser(
28
+ user as Record<string, unknown>,
29
+ );
30
+ applyAuthApiKey(authApiKey);
31
+ }, [user, isAuthenticated, isLoading]);
32
+
33
+ return null;
34
+ }
@@ -12,6 +12,7 @@ import {
12
12
  fetchProductImageBase64,
13
13
  getProductImageUrl,
14
14
  } from 'utils/fetchProductImage';
15
+ import { getEffectiveApiKey } from 'utils/nyrisApiKey';
15
16
 
16
17
  export function useChatAssistantLogic() {
17
18
  const [inputValue, setInputValue] = useState('');
@@ -482,7 +483,7 @@ Each result includes a unique SKU in brackets. Use the result thumbnails and met
482
483
  setCatalogSearching(true);
483
484
  try {
484
485
  const settings = (window as any).settings;
485
- const nyrisKey = settings?.apiKey || settings?.nyrisApiKey;
486
+ const nyrisKey = getEffectiveApiKey();
486
487
  const baseUrl = settings?.baseUrl || 'https://api.nyris.io';
487
488
 
488
489
  store.setPreCatalogResults([...activeProducts]);
@@ -580,7 +581,7 @@ Each result includes a unique SKU in brackets. Use the result thumbnails and met
580
581
 
581
582
  try {
582
583
  const settings = (window as any).settings;
583
- const nyrisKey = settings?.apiKey || settings?.nyrisApiKey;
584
+ const nyrisKey = getEffectiveApiKey();
584
585
  const baseUrl = settings?.baseUrl || 'https://api.nyris.io';
585
586
 
586
587
  let searchQuery = rawText;
@@ -31,13 +31,13 @@ function ImageUpload({
31
31
 
32
32
  const showPreFilter = useMemo(() => {
33
33
  if (settings.shouldUseUserMetadata && user) {
34
- if (user['/user_metadata'].value) {
34
+ if (user['/user_metadata']?.value) {
35
35
  setMetaFilter(user['/user_metadata'].value);
36
36
  }
37
37
  }
38
38
 
39
39
  if (settings.shouldUseUserMetadata && user) {
40
- if (settings.preFilterOption && !user['/user_metadata'].value) {
40
+ if (settings.preFilterOption && !user['/user_metadata']?.value) {
41
41
  return true;
42
42
  }
43
43
  return false;
@@ -1,5 +1,6 @@
1
1
  import { useAuth0 } from '@auth0/auth0-react';
2
2
  import { Dialog, DialogContent } from './Modal/Dialog';
3
+ import { clearAuthApiKey } from 'utils/nyrisApiKey';
3
4
 
4
5
  function LogoutModal({
5
6
  setShowLogoutModal,
@@ -41,6 +42,7 @@ function LogoutModal({
41
42
  cursor: 'pointer',
42
43
  }}
43
44
  onClick={() => {
45
+ clearAuthApiKey();
44
46
  logout({
45
47
  logoutParams: { returnTo: window.location.origin },
46
48
  });
@@ -6,7 +6,6 @@ import {
6
6
  import React, { useCallback, useMemo, useState } from 'react';
7
7
  import { useTranslation } from 'react-i18next';
8
8
  import { twMerge } from 'tailwind-merge';
9
- import PostFilter from './PostFilter';
10
9
  import PostFilterFindApi from './PostFilterFindApi';
11
10
 
12
11
  function PostFilterComponent({ className }: { className?: string }) {
@@ -117,27 +116,14 @@ function PostFilterComponent({ className }: { className?: string }) {
117
116
  refinement.attribute;
118
117
 
119
118
  return (
120
- <React.Fragment key={index}>
121
- {window.settings.algolia.enabled && (
122
- <PostFilter
123
- attribute={refinement.attribute}
124
- label={refinement.header}
125
- searchable={refinement.searchable}
126
- onVisibilityChange={handleVisibilityChange}
127
- isLastVisible={isLastVisible}
128
- />
129
- )}
130
-
131
- {!window.settings.algolia.enabled && (
132
- <PostFilterFindApi
133
- attribute={refinement.attribute}
134
- label={refinement.header}
135
- searchable={false}
136
- onVisibilityChange={handleVisibilityChange}
137
- isLastVisible={isLastVisible}
138
- />
139
- )}
140
- </React.Fragment>
119
+ <PostFilterFindApi
120
+ key={index}
121
+ attribute={refinement.attribute}
122
+ label={refinement.header}
123
+ searchable={false}
124
+ onVisibilityChange={handleVisibilityChange}
125
+ isLastVisible={isLastVisible}
126
+ />
141
127
  );
142
128
  },
143
129
  )}
@@ -63,6 +63,7 @@ function ProductList({ sendFeedBackAction }: Props): JSX.Element {
63
63
  }, [productsFromFindApi, query, requestImages]);
64
64
 
65
65
  const postFilteredProducts = useMemo(() => {
66
+ // Find-API post-filters re-query when Algolia is off; do not also client-filter.
66
67
  if (!window.settings.algolia?.enabled && window.settings.postFilterOption) {
67
68
  return textFilteredProducts;
68
69
  }
@@ -114,6 +115,7 @@ function ProductList({ sendFeedBackAction }: Props): JSX.Element {
114
115
  );
115
116
  }
116
117
 
118
+ // Product source follows search backend (Algolia vs Find API), not postFilterOption.
117
119
  if (!isAlgoliaEnabled) {
118
120
  return baseFindApiProducts;
119
121
  }
@@ -18,6 +18,9 @@ export default function SidePanel({ className }: { className?: string }) {
18
18
  const specificationFilter = useRequestStore(
19
19
  state => state.specificationFilter,
20
20
  );
21
+ const postFilterSelections = useRequestStore(
22
+ state => state.postFilterSelections,
23
+ );
21
24
 
22
25
  const findApiProducts = useResultStore(state => state.productsFromFindApi);
23
26
 
@@ -45,13 +48,20 @@ export default function SidePanel({ className }: { className?: string }) {
45
48
 
46
49
  const effectiveGroundingResults = useEffectiveGroundingResults();
47
50
 
51
+ const hasPostFilterSelections = Object.values(
52
+ postFilterSelections || {},
53
+ ).some(values => values?.length > 0);
54
+
48
55
  if (!showPostFilter && requestImages.length === 0) {
49
56
  return <></>;
50
57
  }
51
58
 
59
+ // Keep the panel when post-filters are applied so the user can unselect
60
+ // after a filtered find returns zero results.
52
61
  if (
53
62
  requestImages.length === 0 &&
54
63
  findApiProducts.length === 0 &&
64
+ !hasPostFilterSelections &&
55
65
  !window.settings.algolia.enabled
56
66
  ) {
57
67
  return <></>;
@@ -65,13 +65,13 @@ function TextSearch({
65
65
 
66
66
  const showPreFilter = useMemo(() => {
67
67
  if (settings.shouldUseUserMetadata && user) {
68
- if (user['/user_metadata'].value) {
68
+ if (user['/user_metadata']?.value) {
69
69
  setMetaFilter(user['/user_metadata'].value);
70
70
  }
71
71
  }
72
72
 
73
73
  if (settings.shouldUseUserMetadata && user) {
74
- if (settings.preFilterOption && !user['/user_metadata'].value) {
74
+ if (settings.preFilterOption && !user['/user_metadata']?.value) {
75
75
  return true;
76
76
  }
77
77
  return false;
@@ -103,7 +103,7 @@ function TextSearch({
103
103
  imageRegion: regions[0],
104
104
  text: value,
105
105
  settings,
106
- showFeedback: false,
106
+ showFeedback: true,
107
107
  newSearch: false,
108
108
  });
109
109
  }
@@ -20,6 +20,7 @@ import { useGroundedSearch } from './useGroundedSearch';
20
20
  import { generateSmartFilters } from '../services/vizo';
21
21
  import { cropImage } from 'utils/cropImageToBase64';
22
22
  import { invalidatePostFilterRequests } from 'components/PostFilter/PostFilterFindApi';
23
+ import { derivePostFilterFacetsFromResults } from 'utils/derivePostFilterFacets';
23
24
  import { useAuth0 } from '@auth0/auth0-react';
24
25
 
25
26
  export const useImageSearch = () => {
@@ -293,17 +294,20 @@ export const useImageSearch = () => {
293
294
  });
294
295
  }
295
296
 
297
+ const usesFindApiPostFilters =
298
+ !settings.algolia?.enabled && settings.postFilterOption;
299
+ // Crop/region finds replace results without sending postFilterSelections;
300
+ // clear stale selections so checkboxes match the new unfiltered grid.
296
301
  const shouldResetPostFilters =
297
302
  clearPostFilter ||
298
- (!settings.algolia?.enabled &&
299
- !requestImage &&
300
- settings.postFilterOption);
303
+ (Boolean(imageRegion) && usesFindApiPostFilters) ||
304
+ (!requestImage && settings.postFilterOption);
301
305
 
302
306
  if (shouldResetPostFilters) {
303
307
  invalidatePostFilterRequests();
304
308
  refine();
305
309
  clearPostFilterSelections();
306
- if (clearPostFilter) {
310
+ if (clearPostFilter || !requestImage) {
307
311
  setPostFilterFacets([]);
308
312
  setInitialFindSearchSnapshot(null);
309
313
  }
@@ -335,12 +339,35 @@ export const useImageSearch = () => {
335
339
  useResultStore.getState().resetGrounding();
336
340
 
337
341
  setFindApiProducts(res?.results);
338
- const initialFacets = res?.facets || [];
339
- setPostFilterFacets(initialFacets);
342
+
343
+ // Image: API facets if present, else derive from result.filters.
344
+ // Text: API facets only.
345
+ // Empty results with no facet payload may keep prior facet UI for unselect,
346
+ // but the search snapshot always updates to this response.
347
+ const isImageSearch = Boolean(requestImage);
348
+ const results = res?.results || [];
349
+ const apiFacets = Array.isArray(res?.facets) ? res.facets : [];
350
+ const nextFacets = isImageSearch
351
+ ? apiFacets.length > 0
352
+ ? apiFacets
353
+ : derivePostFilterFacetsFromResults(results)
354
+ : apiFacets;
355
+ const previousFacets = useResultStore.getState().postFilterFacets;
356
+ const shouldPreserveFacets =
357
+ results.length === 0 &&
358
+ nextFacets.length === 0 &&
359
+ Array.isArray(previousFacets) &&
360
+ previousFacets.length > 0;
361
+
362
+ // Facet UI may stay for recovery; snapshot always matches this search.
363
+ if (!shouldPreserveFacets) {
364
+ setPostFilterFacets(nextFacets);
365
+ }
340
366
  setInitialFindSearchSnapshot({
341
- results: res?.results || [],
342
- postFilterFacets: initialFacets,
367
+ results,
368
+ postFilterFacets: nextFacets,
343
369
  });
370
+
344
371
  setImageAnalysis(res?.image_analysis);
345
372
  setSessionId(res?.session);
346
373
  setRequestId(res?.id);
package/src/index.tsx CHANGED
@@ -1,6 +1,10 @@
1
1
  import ReactDOM from 'react-dom/client';
2
2
  import './index.css';
3
3
  import App from './App';
4
+ import { bootstrapAuth0ApiKeyGuard } from 'utils/nyrisApiKey';
5
+
6
+ // Clear settings.js apiKey before any React tree can issue Nyris requests (Auth0 mode).
7
+ bootstrapAuth0ApiKeyGuard();
4
8
 
5
9
  const root = ReactDOM.createRoot(
6
10
  document.getElementById('root') as HTMLElement,
@@ -22,6 +22,11 @@ import { getFilters } from 'services/filter';
22
22
  import { useUploadSearch } from 'hooks/useUploadSearch';
23
23
  import { ToastHelper } from 'helpers/ToastHelper';
24
24
  import { useTranslation } from 'react-i18next';
25
+ import {
26
+ clearAuthApiKey,
27
+ isNyrisApiKeyReady,
28
+ useAuthApiKeyStore,
29
+ } from 'utils/nyrisApiKey';
25
30
  import useScopeStore from 'stores/scope/scopeStore';
26
31
 
27
32
  i18n.use(initReactI18next).init({
@@ -34,7 +39,7 @@ i18n.use(initReactI18next).init({
34
39
  });
35
40
 
36
41
  function AppLayout(): JSX.Element {
37
- const { isAuthenticated } = useAuth0();
42
+ const { isAuthenticated, logout } = useAuth0();
38
43
  const { t } = useTranslation();
39
44
  const { auth0, alogoliaFilterField, showPoweredByNyris } = window.settings;
40
45
  const { uploadFile } = useUploadSearch();
@@ -114,13 +119,13 @@ function AppLayout(): JSX.Element {
114
119
 
115
120
  const showPreFilter = useMemo(() => {
116
121
  if (settings.shouldUseUserMetadata && user) {
117
- if (user['/user_metadata'].value) {
122
+ if (user['/user_metadata']?.value) {
118
123
  setMetaFilter(user['/user_metadata'].value);
119
124
  }
120
125
  }
121
126
 
122
127
  if (settings.shouldUseUserMetadata && user) {
123
- if (settings.preFilterOption && !user['/user_metadata'].value) {
128
+ if (settings.preFilterOption && !user['/user_metadata']?.value) {
124
129
  return true;
125
130
  }
126
131
  return false;
@@ -134,7 +139,15 @@ function AppLayout(): JSX.Element {
134
139
  user,
135
140
  ]);
136
141
 
142
+ const authApiKeyVersion = useAuthApiKeyStore(state => state.version);
143
+ const isMissingAuthApiKey = useAuthApiKeyStore(state => state.isMissingClaim);
144
+ const isApiKeyReady = isNyrisApiKeyReady();
145
+
137
146
  const getPreFilters = useCallback(async () => {
147
+ if (!isNyrisApiKeyReady()) {
148
+ return;
149
+ }
150
+
138
151
  setPreFilterLoading(true);
139
152
  try {
140
153
  const res = await getFilters(1000, settings);
@@ -147,10 +160,10 @@ function AppLayout(): JSX.Element {
147
160
  }, [settings, setPreFilterList, setPreFilterLoading]);
148
161
 
149
162
  useEffect(() => {
150
- if (showPreFilter && !settings.scopeFilter) {
163
+ if (showPreFilter && !settings.scopeFilter && isApiKeyReady) {
151
164
  getPreFilters();
152
165
  }
153
- }, [getPreFilters, showPreFilter, settings.scopeFilter]);
166
+ }, [getPreFilters, showPreFilter, settings.scopeFilter, isApiKeyReady, authApiKeyVersion]);
154
167
 
155
168
  const getImageFromClipboard = useCallback(
156
169
  (event: ClipboardEvent): File | null => {
@@ -208,12 +221,17 @@ function AppLayout(): JSX.Element {
208
221
  return;
209
222
  }
210
223
 
224
+ // Auth0: only listen once authApiKey is resolved (not while waiting / missing claim).
225
+ if (auth0.enabled && !isApiKeyReady) {
226
+ return;
227
+ }
228
+
211
229
  document.addEventListener('paste', handleClipboardPaste);
212
230
 
213
231
  return () => {
214
232
  document.removeEventListener('paste', handleClipboardPaste);
215
233
  };
216
- }, [handleClipboardPaste, showLayout]);
234
+ }, [auth0.enabled, handleClipboardPaste, isApiKeyReady, showLayout]);
217
235
 
218
236
  const applyScopeFilter = (filters: string | undefined) => {
219
237
  const scopeFilterClause =
@@ -238,6 +256,34 @@ function AppLayout(): JSX.Element {
238
256
  return <Outlet />;
239
257
  }
240
258
 
259
+ // Auth0 mode: wait for AuthApiKeySync before rendering search UI / prefilters.
260
+ if (auth0.enabled && !isApiKeyReady && !isMissingAuthApiKey) {
261
+ return <div />;
262
+ }
263
+
264
+ if (isMissingAuthApiKey) {
265
+ return (
266
+ <div className="full-height flex flex-col items-center justify-center bg-[#F3F4F8] p-6 text-center">
267
+ <p className="mt-2 max-w-md text-sm text-gray-600">
268
+ Search is currently unavailable for your account. Please contact your
269
+ administrator.
270
+ </p>
271
+ <button
272
+ type="button"
273
+ className="mt-6 bg-[#2B2C46] px-6 py-3 text-sm text-white"
274
+ onClick={() => {
275
+ clearAuthApiKey();
276
+ logout({
277
+ logoutParams: { returnTo: window.location.origin },
278
+ });
279
+ }}
280
+ >
281
+ {t('Log out')}
282
+ </button>
283
+ </div>
284
+ );
285
+ }
286
+
241
287
  return (
242
288
  <div className="full-height flex flex-col bg-[#F3F4F8]">
243
289
  <div>
@@ -1,5 +1,6 @@
1
1
  import { useEffect } from 'react';
2
2
  import { useAuth0 } from '@auth0/auth0-react';
3
+ import { clearAuthApiKey } from 'utils/nyrisApiKey';
3
4
 
4
5
  const Logout = () => {
5
6
  const { user, isAuthenticated, isLoading, loginWithRedirect, logout } =
@@ -10,6 +11,7 @@ const Logout = () => {
10
11
  if (!isLoading && !user && !isAuthenticated && auth0.enabled) {
11
12
  loginWithRedirect();
12
13
  } else if (isAuthenticated) {
14
+ clearAuthApiKey();
13
15
  logout({ logoutParams: { returnTo: window.location.origin } });
14
16
  } else if (!auth0.enabled) {
15
17
  window.location.href = '/';
@@ -174,16 +174,21 @@ function Results() {
174
174
  ]);
175
175
 
176
176
  const disablePostFilter = useMemo(() => {
177
- if (!settings.algolia.enabled) {
177
+ // Find-API post-filters do not depend on Algolia hits.
178
+ if (!settings.algolia.enabled || settings.postFilterOption) {
178
179
  return false;
179
180
  }
180
181
 
181
- return settings.postFilterOption && productsFromAlgolia.length > 0
182
- ? false
183
- : true;
182
+ return productsFromAlgolia.length > 0 ? false : true;
184
183
  }, [settings, productsFromAlgolia]);
185
184
 
186
- const isPostFilterApplied = items.length > 0;
185
+ const postFilterSelections = useRequestStore(
186
+ state => state.postFilterSelections,
187
+ );
188
+ const hasFindApiPostFilterSelection = Object.values(
189
+ postFilterSelections || {},
190
+ ).some(values => values?.length > 0);
191
+ const isPostFilterApplied = items.length > 0 || hasFindApiPostFilterSelection;
187
192
 
188
193
  return (
189
194
  <>
@@ -364,7 +369,8 @@ function Results() {
364
369
 
365
370
  {feedbackStatus === 'visible' &&
366
371
  !showFeedbackSuccess &&
367
- productsFromAlgolia.length > 0 && (
372
+ (productsFromAlgolia.length > 0 ||
373
+ productsFromFindApi.length > 0) && (
368
374
  <div className={'feedback-floating'}>
369
375
  <div className="feedback-section feedback-backdrop-blur" />
370
376
 
@@ -1,4 +1,8 @@
1
1
  import { Auth0Provider } from '@auth0/auth0-react';
2
+ import { AuthApiKeySync } from 'components/AuthApiKeySync';
3
+ import { bootstrapAuth0ApiKeyGuard } from 'utils/nyrisApiKey';
4
+
5
+ bootstrapAuth0ApiKeyGuard();
2
6
 
3
7
  const AuthProvider = ({ children }: any) => {
4
8
  const settings = window.settings;
@@ -15,6 +19,7 @@ const AuthProvider = ({ children }: any) => {
15
19
  redirect_uri: window.location.origin,
16
20
  }}
17
21
  >
22
+ <AuthApiKeySync />
18
23
  {children}
19
24
  </Auth0Provider>
20
25
  );
@@ -3,6 +3,7 @@ import NyrisAPI, {
3
3
  NyrisAPISettings,
4
4
  RectCoords,
5
5
  } from '@nyris/nyris-api';
6
+ import { getNyrisSettings } from 'utils/nyrisApiKey';
6
7
 
7
8
  export const feedbackSuccessEpic = async (
8
9
  requestId: string,
@@ -62,7 +63,7 @@ export const sendFeedbackByApi = async (
62
63
  requestId: string | undefined,
63
64
  payload: FeedbackEventPayload,
64
65
  ) => {
65
- const api = new NyrisAPI(settings);
66
+ const api = new NyrisAPI(getNyrisSettings(settings));
66
67
  if (requestId) {
67
68
  try {
68
69
  await api.sendFeedback({ sessionId, requestId, payload }).then(() => {});
@@ -1,4 +1,5 @@
1
1
  import NyrisAPI, { NyrisAPISettings } from '@nyris/nyris-api';
2
+ import { getNyrisSettings } from 'utils/nyrisApiKey';
2
3
 
3
4
  export interface Filter {
4
5
  key?: string;
@@ -9,7 +10,7 @@ export const getFilters = async (
9
10
  number: number = 10,
10
11
  settings: NyrisAPISettings,
11
12
  ) => {
12
- const nyrisApi = new NyrisAPI(settings);
13
+ const nyrisApi = new NyrisAPI(getNyrisSettings(settings));
13
14
  return nyrisApi.getFilters(number);
14
15
  };
15
16
 
@@ -18,7 +19,7 @@ export const searchFilters = async (
18
19
  value: string,
19
20
  settingsNyris: NyrisAPISettings,
20
21
  ) => {
21
- const nyrisApi = new NyrisAPI(settingsNyris);
22
+ const nyrisApi = new NyrisAPI(getNyrisSettings(settingsNyris));
22
23
  const newValue = value ? value : '';
23
24
  return nyrisApi.searchFilters(key, newValue);
24
25
  };
@@ -9,6 +9,7 @@ import { isEqual } from 'lodash';
9
9
 
10
10
  import { DEFAULT_REGION } from '../constants';
11
11
  import { getUserLocation } from 'helpers/getGeoLocation';
12
+ import { getNyrisSettings } from 'utils/nyrisApiKey';
12
13
 
13
14
  export interface Filter {
14
15
  key?: string;
@@ -41,7 +42,7 @@ export const findRegions = async (
41
42
  image: HTMLCanvasElement,
42
43
  settings: NyrisAPISettings,
43
44
  ) => {
44
- const nyrisApi = new NyrisAPI(settings);
45
+ const nyrisApi = new NyrisAPI(getNyrisSettings(settings));
45
46
  let regions = await nyrisApi.findRegions(image);
46
47
  const selectedRegion = getRegionByMaxConfidence(regions);
47
48
  return {
@@ -67,7 +68,7 @@ export const find = async ({
67
68
  text?: string;
68
69
  scopeIds?: string;
69
70
  }) => {
70
- const nyrisApi = new NyrisAPI(settings);
71
+ const nyrisApi = new NyrisAPI(getNyrisSettings(settings));
71
72
  let options: ImageSearchOptions = text ? { text } : {};
72
73
 
73
74
  if (window.settings?.geoLocation) {
@@ -103,7 +104,7 @@ export const findCad = ({
103
104
  settings: NyrisAPISettings;
104
105
  filters?: Filter[];
105
106
  }) => {
106
- const nyrisApi = new NyrisAPI(settings);
107
+ const nyrisApi = new NyrisAPI(getNyrisSettings(settings));
107
108
 
108
109
  return nyrisApi.findByCad(file, {}, filters);
109
110
  };
@@ -115,7 +116,7 @@ export const getRequestImage = ({
115
116
  url: string;
116
117
  settings: NyrisAPISettings;
117
118
  }) => {
118
- const nyrisApi = new NyrisAPI(settings);
119
+ const nyrisApi = new NyrisAPI(getNyrisSettings(settings));
119
120
 
120
121
  return nyrisApi.getCadRequestImage(url);
121
122
  };
@@ -1,6 +1,17 @@
1
1
  import { RectCoords } from '@nyris/nyris-api';
2
2
  import axios from 'axios';
3
3
  import { cropImage } from 'utils/cropImageToBase64';
4
+ import { getEffectiveApiKey } from 'utils/nyrisApiKey';
5
+
6
+ function resolveAiOrNyrisApiKey(explicitApiKey?: string): string {
7
+ if (explicitApiKey) {
8
+ return explicitApiKey;
9
+ }
10
+ if (window.settings.aiApiKey) {
11
+ return window.settings.aiApiKey;
12
+ }
13
+ return getEffectiveApiKey();
14
+ }
4
15
 
5
16
  export interface ChatMultimodalInput {
6
17
  metadata: string;
@@ -111,7 +122,7 @@ export async function rerankSmartFilters({
111
122
  formData,
112
123
  {
113
124
  headers: {
114
- 'x-api-key': apiKey || window.settings.aiApiKey || window.settings.apiKey,
125
+ 'x-api-key': resolveAiOrNyrisApiKey(apiKey),
115
126
  },
116
127
  },
117
128
  );
@@ -156,7 +167,7 @@ export async function generateSmartFilters({
156
167
  formData,
157
168
  {
158
169
  headers: {
159
- 'x-api-key': window.settings.aiApiKey || window.settings.apiKey,
170
+ 'x-api-key': resolveAiOrNyrisApiKey(),
160
171
  },
161
172
  },
162
173
  );