@semiont/react-ui 0.4.20 → 0.4.22

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 (125) hide show
  1. package/README.md +8 -5
  2. package/dist/{PdfAnnotationCanvas.client-CHDCGQBR.mjs → PdfAnnotationCanvas.client-5QESNO5H.mjs} +13 -16
  3. package/dist/PdfAnnotationCanvas.client-5QESNO5H.mjs.map +1 -0
  4. package/dist/TranslationManager-9Xj3MIWQ.d.mts +16 -0
  5. package/dist/chunk-4NOUO3W6.mjs +7788 -0
  6. package/dist/chunk-4NOUO3W6.mjs.map +1 -0
  7. package/dist/index.d.mts +212 -1206
  8. package/dist/index.mjs +3332 -13712
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/test-utils.d.mts +48 -21
  11. package/dist/test-utils.mjs +2505 -87
  12. package/dist/test-utils.mjs.map +1 -1
  13. package/package.json +2 -2
  14. package/src/components/AnnotateReferencesProgressWidget.tsx +21 -28
  15. package/src/components/CodeMirrorRenderer.tsx +12 -12
  16. package/src/components/LiveRegion.tsx +1 -2
  17. package/src/components/StatusDisplay.tsx +42 -16
  18. package/src/components/Toolbar.tsx +4 -4
  19. package/src/components/__tests__/AnnotateReferencesProgressWidget.test.tsx +34 -20
  20. package/src/components/__tests__/StatusDisplay.test.tsx +50 -65
  21. package/src/components/__tests__/Toolbar.test.tsx +4 -4
  22. package/src/components/annotation/AnnotateToolbar.tsx +8 -9
  23. package/src/components/annotation/__tests__/AnnotateToolbar.test.tsx +31 -77
  24. package/src/components/annotation-popups/JsonLdView.tsx +1 -2
  25. package/src/components/annotation-popups/__tests__/JsonLdView.test.tsx +1 -2
  26. package/src/components/image-annotation/AnnotationOverlay.tsx +15 -18
  27. package/src/components/image-annotation/SvgDrawingCanvas.tsx +12 -17
  28. package/src/components/modals/ConfigureGenerationStep.tsx +1 -2
  29. package/src/components/modals/PermissionDeniedModal.tsx +11 -11
  30. package/src/components/modals/ReferenceWizardModal.tsx +14 -18
  31. package/src/components/modals/ResourceSearchModal.tsx +12 -8
  32. package/src/components/modals/SearchModal.tsx +11 -6
  33. package/src/components/modals/SearchResultsStep.tsx +1 -3
  34. package/src/components/modals/SessionExpiredModal.tsx +11 -11
  35. package/src/components/modals/__tests__/PermissionDeniedModal.test.tsx +7 -7
  36. package/src/components/modals/__tests__/ResourceSearchModal.test.tsx +10 -8
  37. package/src/components/modals/__tests__/SearchModal.accessibility.test.tsx +6 -2
  38. package/src/components/modals/__tests__/SearchModal.basic.test.tsx +6 -2
  39. package/src/components/modals/__tests__/SearchModal.keyboard.test.tsx +6 -2
  40. package/src/components/modals/__tests__/SearchModal.search-wiring.test.tsx +10 -7
  41. package/src/components/modals/__tests__/SearchModal.visual.test.tsx +6 -2
  42. package/src/components/modals/__tests__/SessionExpiredModal.test.tsx +5 -5
  43. package/src/components/navigation/CollapsibleResourceNavigation.tsx +10 -10
  44. package/src/components/navigation/ObservableLink.tsx +6 -6
  45. package/src/components/navigation/SimpleNavigation.tsx +4 -4
  46. package/src/components/navigation/__tests__/ObservableLink.test.tsx +4 -4
  47. package/src/components/navigation/__tests__/SimpleNavigation.test.tsx +4 -4
  48. package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +15 -18
  49. package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +1 -2
  50. package/src/components/resource/AnnotateView.tsx +8 -10
  51. package/src/components/resource/AnnotationHistory.tsx +9 -12
  52. package/src/components/resource/BrowseView.tsx +11 -8
  53. package/src/components/resource/ResourceViewer.tsx +22 -34
  54. package/src/components/resource/__tests__/AnnotationHistory.test.tsx +54 -192
  55. package/src/components/resource/__tests__/BrowseView.test.tsx +38 -87
  56. package/src/components/resource/__tests__/ResourceViewer.mode-switch.test.tsx +41 -31
  57. package/src/components/resource/__tests__/event-formatting.test.ts +6 -2
  58. package/src/components/resource/event-formatting.ts +2 -3
  59. package/src/components/resource/panels/AssessmentEntry.tsx +7 -8
  60. package/src/components/resource/panels/AssessmentPanel.tsx +21 -17
  61. package/src/components/resource/panels/AssistSection.tsx +15 -21
  62. package/src/components/resource/panels/CollaborationPanel.tsx +29 -7
  63. package/src/components/resource/panels/CommentEntry.tsx +7 -8
  64. package/src/components/resource/panels/CommentsPanel.tsx +11 -13
  65. package/src/components/resource/panels/HighlightEntry.tsx +7 -8
  66. package/src/components/resource/panels/HighlightPanel.tsx +12 -13
  67. package/src/components/resource/panels/ReferenceEntry.tsx +13 -15
  68. package/src/components/resource/panels/ReferencesPanel.tsx +17 -19
  69. package/src/components/resource/panels/ResourceInfoPanel.tsx +8 -7
  70. package/src/components/resource/panels/StatisticsPanel.tsx +2 -3
  71. package/src/components/resource/panels/TagEntry.tsx +7 -8
  72. package/src/components/resource/panels/TaggingPanel.tsx +14 -23
  73. package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +4 -3
  74. package/src/components/resource/panels/__tests__/AssessmentEntry.test.tsx +4 -4
  75. package/src/components/resource/panels/__tests__/AssessmentPanel.test.tsx +22 -57
  76. package/src/components/resource/panels/__tests__/CollaborationPanel.test.tsx +51 -20
  77. package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +4 -4
  78. package/src/components/resource/panels/__tests__/CommentsPanel.test.tsx +22 -61
  79. package/src/components/resource/panels/__tests__/HighlightEntry.test.tsx +4 -4
  80. package/src/components/resource/panels/__tests__/HighlightPanel.annotationProgress.test.tsx +1 -2
  81. package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +7 -8
  82. package/src/components/resource/panels/__tests__/ReferencesPanel.observable-flow.test.tsx +153 -0
  83. package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +51 -106
  84. package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +28 -53
  85. package/src/components/resource/panels/__tests__/StatisticsPanel.test.tsx +3 -3
  86. package/src/components/resource/panels/__tests__/TagEntry.test.tsx +4 -4
  87. package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +19 -52
  88. package/src/components/settings/SettingsPanel.tsx +9 -9
  89. package/src/components/settings/__tests__/SettingsPanel.test.tsx +15 -15
  90. package/src/features/admin-devops/components/AdminDevOpsPage.tsx +1 -2
  91. package/src/features/admin-exchange/components/AdminExchangePage.tsx +1 -1
  92. package/src/features/admin-exchange/components/ImportCard.tsx +2 -7
  93. package/src/features/admin-security/components/AdminSecurityPage.tsx +1 -2
  94. package/src/features/admin-users/components/AdminUsersPage.tsx +1 -1
  95. package/src/features/moderate-entity-tags/components/EntityTagsPage.tsx +1 -2
  96. package/src/features/moderate-recent/components/RecentDocumentsPage.tsx +1 -2
  97. package/src/features/moderate-tag-schemas/components/TagSchemasPage.tsx +1 -1
  98. package/src/features/moderation-linked-data/components/LinkedDataPage.tsx +1 -1
  99. package/src/features/resource-compose/__tests__/ResourceComposePage.test.tsx +5 -3
  100. package/src/features/resource-compose/components/ResourceComposePage.tsx +6 -22
  101. package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +4 -3
  102. package/src/features/resource-discovery/components/ResourceCard.tsx +1 -2
  103. package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +3 -4
  104. package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +37 -45
  105. package/src/features/resource-viewer/components/ResourceViewerPage.tsx +129 -197
  106. package/dist/KnowledgeBaseSessionContext-BNNunwzO.d.mts +0 -175
  107. package/dist/PdfAnnotationCanvas.client-CHDCGQBR.mjs.map +0 -1
  108. package/dist/chunk-OZICDVH7.mjs +0 -62
  109. package/dist/chunk-OZICDVH7.mjs.map +0 -1
  110. package/dist/chunk-R4CCMFJH.mjs +0 -877
  111. package/dist/chunk-R4CCMFJH.mjs.map +0 -1
  112. package/dist/chunk-VN5NY4SN.mjs +0 -200
  113. package/dist/chunk-VN5NY4SN.mjs.map +0 -1
  114. package/src/components/modals/ProposeEntitiesModal.tsx +0 -179
  115. package/src/components/modals/__tests__/ProposeEntitiesModal.test.tsx +0 -129
  116. package/src/features/resource-viewer/__tests__/AnnotationCreationPending.test.tsx +0 -323
  117. package/src/features/resource-viewer/__tests__/AnnotationDeletionIntegration.test.tsx +0 -245
  118. package/src/features/resource-viewer/__tests__/AnnotationProgressDismissal.test.tsx +0 -303
  119. package/src/features/resource-viewer/__tests__/BindFlowIntegration.test.tsx +0 -150
  120. package/src/features/resource-viewer/__tests__/DetectionFlowBug.test.tsx +0 -243
  121. package/src/features/resource-viewer/__tests__/DetectionFlowIntegration.test.tsx +0 -383
  122. package/src/features/resource-viewer/__tests__/ResourceMutations.test.tsx +0 -299
  123. package/src/features/resource-viewer/__tests__/ToastNotifications.test.tsx +0 -186
  124. package/src/features/resource-viewer/__tests__/YieldFlowIntegration.test.tsx +0 -429
  125. package/src/features/resource-viewer/__tests__/annotation-progress-flow.test.tsx +0 -348
@@ -1,877 +0,0 @@
1
- 'use client';
2
- import {
3
- en_default
4
- } from "./chunk-NOD3NCXE.mjs";
5
- import {
6
- __glob
7
- } from "./chunk-D4GAAQMM.mjs";
8
-
9
- // src/contexts/knowledge-base-session/storage.ts
10
- var SESSION_PREFIX = "semiont.session.";
11
- var STORAGE_KEY = "semiont.knowledgeBases";
12
- var ACTIVE_KEY = "semiont.activeKnowledgeBaseId";
13
- var REFRESH_BEFORE_EXP_MS = 5 * 60 * 1e3;
14
- function sessionKey(kbId) {
15
- return `${SESSION_PREFIX}${kbId}`;
16
- }
17
- function getStoredSession(kbId) {
18
- const raw = localStorage.getItem(sessionKey(kbId));
19
- if (!raw) return null;
20
- try {
21
- const parsed = JSON.parse(raw);
22
- if (parsed && typeof parsed.access === "string" && typeof parsed.refresh === "string") {
23
- return { access: parsed.access, refresh: parsed.refresh };
24
- }
25
- } catch {
26
- }
27
- return null;
28
- }
29
- function setStoredSession(kbId, session) {
30
- localStorage.setItem(sessionKey(kbId), JSON.stringify(session));
31
- }
32
- function clearStoredSession(kbId) {
33
- localStorage.removeItem(sessionKey(kbId));
34
- }
35
- function parseJwtExpiry(token) {
36
- try {
37
- const parts = token.split(".");
38
- if (parts.length !== 3 || !parts[1]) return null;
39
- const payload = JSON.parse(atob(parts[1]));
40
- if (!payload.exp) return null;
41
- return new Date(payload.exp * 1e3);
42
- } catch {
43
- return null;
44
- }
45
- }
46
- function isJwtExpired(token) {
47
- const expiry = parseJwtExpiry(token);
48
- if (!expiry) return true;
49
- return expiry.getTime() < Date.now();
50
- }
51
- function migrateLegacyEntry(entry) {
52
- if (entry.host !== void 0) return entry;
53
- try {
54
- const url = new URL(entry.backendUrl);
55
- return {
56
- id: entry.id,
57
- label: entry.label,
58
- host: url.hostname,
59
- port: parseInt(url.port, 10) || (url.protocol === "https:" ? 443 : 80),
60
- protocol: url.protocol === "https:" ? "https" : "http",
61
- email: ""
62
- };
63
- } catch {
64
- return {
65
- id: entry.id,
66
- label: entry.label || "Unknown",
67
- host: "localhost",
68
- port: 4e3,
69
- protocol: "http",
70
- email: ""
71
- };
72
- }
73
- }
74
- function loadKnowledgeBases() {
75
- try {
76
- const raw = localStorage.getItem(STORAGE_KEY);
77
- if (!raw) return [];
78
- const entries = JSON.parse(raw);
79
- return entries.map(migrateLegacyEntry);
80
- } catch {
81
- return [];
82
- }
83
- }
84
- function saveKnowledgeBases(knowledgeBases) {
85
- localStorage.setItem(STORAGE_KEY, JSON.stringify(knowledgeBases));
86
- }
87
- function defaultProtocol(host) {
88
- return host === "localhost" || host === "127.0.0.1" ? "http" : "https";
89
- }
90
- var HOSTNAME_RE = /^(([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?|localhost|\d{1,3}(\.\d{1,3}){3})$/;
91
- function isValidHostname(host) {
92
- return HOSTNAME_RE.test(host);
93
- }
94
- function kbBackendUrl(kb) {
95
- if (!isValidHostname(kb.host)) {
96
- throw new Error(`Invalid KB hostname: "${kb.host}"`);
97
- }
98
- const url = new URL("http://x");
99
- url.protocol = kb.protocol + ":";
100
- url.hostname = kb.host;
101
- url.port = String(kb.port);
102
- return `${kb.protocol}://${url.hostname}:${kb.port}`;
103
- }
104
- function getKbSessionStatus(kbId) {
105
- const stored = getStoredSession(kbId);
106
- if (!stored) return "signed-out";
107
- return isJwtExpired(stored.access) ? "expired" : "authenticated";
108
- }
109
- function generateKbId() {
110
- return crypto.randomUUID();
111
- }
112
-
113
- // src/contexts/knowledge-base-session/notify.ts
114
- var activeOnSessionExpired = null;
115
- var activeOnPermissionDenied = null;
116
- function notifySessionExpired(message) {
117
- activeOnSessionExpired?.(message);
118
- }
119
- function notifyPermissionDenied(message) {
120
- activeOnPermissionDenied?.(message);
121
- }
122
- function registerAuthNotifyHandlers(handlers) {
123
- activeOnSessionExpired = handlers.onSessionExpired;
124
- activeOnPermissionDenied = handlers.onPermissionDenied;
125
- return () => {
126
- activeOnSessionExpired = null;
127
- activeOnPermissionDenied = null;
128
- };
129
- }
130
-
131
- // src/contexts/KnowledgeBaseSessionContext.tsx
132
- import {
133
- createContext,
134
- useCallback,
135
- useContext,
136
- useEffect,
137
- useMemo,
138
- useRef,
139
- useState
140
- } from "react";
141
- import { SemiontApiClient as SemiontApiClient2, APIError } from "@semiont/api-client";
142
- import { baseUrl as baseUrl2, EventBus as EventBus2, accessToken } from "@semiont/core";
143
-
144
- // src/contexts/knowledge-base-session/refresh.ts
145
- import { SemiontApiClient } from "@semiont/api-client";
146
- import { baseUrl, EventBus, refreshToken as makeRefreshToken } from "@semiont/core";
147
- var inFlightRefreshes = /* @__PURE__ */ new Map();
148
- async function performRefresh(kb) {
149
- const existing = inFlightRefreshes.get(kb.id);
150
- if (existing) return existing;
151
- const promise = (async () => {
152
- const stored = getStoredSession(kb.id);
153
- if (!stored) return null;
154
- const client = new SemiontApiClient({
155
- baseUrl: baseUrl(kbBackendUrl(kb)),
156
- eventBus: new EventBus()
157
- });
158
- try {
159
- const response = await client.refreshToken(makeRefreshToken(stored.refresh));
160
- const newAccess = response.access_token;
161
- if (!newAccess) return null;
162
- setStoredSession(kb.id, { access: newAccess, refresh: stored.refresh });
163
- return newAccess;
164
- } catch {
165
- return null;
166
- }
167
- })();
168
- inFlightRefreshes.set(kb.id, promise);
169
- try {
170
- return await promise;
171
- } finally {
172
- inFlightRefreshes.delete(kb.id);
173
- }
174
- }
175
-
176
- // src/contexts/KnowledgeBaseSessionContext.tsx
177
- import { jsx } from "react/jsx-runtime";
178
- var KnowledgeBaseSessionContext = createContext(void 0);
179
- function KnowledgeBaseSessionProvider({ children }) {
180
- const [knowledgeBases, setKnowledgeBases] = useState(() => loadKnowledgeBases());
181
- const [activeKnowledgeBaseId, setActiveKnowledgeBaseId] = useState(() => {
182
- const saved = localStorage.getItem(ACTIVE_KEY);
183
- const loaded = loadKnowledgeBases();
184
- if (saved && loaded.some((kb) => kb.id === saved)) return saved;
185
- return loaded[0]?.id ?? null;
186
- });
187
- const [session, setSession] = useState(null);
188
- const [isLoading, setIsLoading] = useState(() => {
189
- const id = activeKnowledgeBaseId;
190
- if (!id) return false;
191
- const stored = getStoredSession(id);
192
- if (!stored) return false;
193
- return !isJwtExpired(stored.access) || stored.refresh != null;
194
- });
195
- const [sessionExpiredAt, setSessionExpiredAt] = useState(null);
196
- const [sessionExpiredMessage, setSessionExpiredMessage] = useState(null);
197
- const [permissionDeniedAt, setPermissionDeniedAt] = useState(null);
198
- const [permissionDeniedMessage, setPermissionDeniedMessage] = useState(null);
199
- useEffect(() => {
200
- saveKnowledgeBases(knowledgeBases);
201
- }, [knowledgeBases]);
202
- useEffect(() => {
203
- if (activeKnowledgeBaseId) {
204
- localStorage.setItem(ACTIVE_KEY, activeKnowledgeBaseId);
205
- } else {
206
- localStorage.removeItem(ACTIVE_KEY);
207
- }
208
- }, [activeKnowledgeBaseId]);
209
- const activeKnowledgeBase = useMemo(
210
- () => knowledgeBases.find((kb) => kb.id === activeKnowledgeBaseId) ?? null,
211
- [knowledgeBases, activeKnowledgeBaseId]
212
- );
213
- const activeKbRef = useRef(activeKnowledgeBase);
214
- useEffect(() => {
215
- activeKbRef.current = activeKnowledgeBase;
216
- }, [activeKnowledgeBase]);
217
- const proactiveRefreshTimerRef = useRef(null);
218
- const scheduleProactiveRefresh = useCallback((accessTokenStr) => {
219
- if (proactiveRefreshTimerRef.current) {
220
- clearTimeout(proactiveRefreshTimerRef.current);
221
- proactiveRefreshTimerRef.current = null;
222
- }
223
- const expiresAt = parseJwtExpiry(accessTokenStr);
224
- if (!expiresAt) return;
225
- const refreshAt = expiresAt.getTime() - REFRESH_BEFORE_EXP_MS;
226
- const delay = Math.max(0, refreshAt - Date.now());
227
- proactiveRefreshTimerRef.current = setTimeout(() => {
228
- proactiveRefreshTimerRef.current = null;
229
- refreshActiveRef.current?.();
230
- }, delay);
231
- }, []);
232
- const refreshActiveRef = useRef(null);
233
- const refreshActive = useCallback(async () => {
234
- const kb = activeKbRef.current;
235
- if (!kb) return null;
236
- const newAccess = await performRefresh(kb);
237
- if (newAccess) {
238
- setSession((prev) => prev ? { ...prev, token: newAccess } : prev);
239
- scheduleProactiveRefresh(newAccess);
240
- } else {
241
- setSession(null);
242
- clearStoredSession(kb.id);
243
- setSessionExpiredMessage("Your session has expired. Please sign in again.");
244
- setSessionExpiredAt(Date.now());
245
- if (proactiveRefreshTimerRef.current) {
246
- clearTimeout(proactiveRefreshTimerRef.current);
247
- proactiveRefreshTimerRef.current = null;
248
- }
249
- }
250
- return newAccess;
251
- }, [scheduleProactiveRefresh]);
252
- refreshActiveRef.current = refreshActive;
253
- useEffect(() => {
254
- if (!activeKnowledgeBase) {
255
- setSession(null);
256
- setIsLoading(false);
257
- return;
258
- }
259
- const stored = getStoredSession(activeKnowledgeBase.id);
260
- if (!stored) {
261
- setSession(null);
262
- setIsLoading(false);
263
- return;
264
- }
265
- let cancelled = false;
266
- const validate = async (tokenToUse) => {
267
- const client = new SemiontApiClient2({
268
- baseUrl: baseUrl2(kbBackendUrl(activeKnowledgeBase)),
269
- eventBus: new EventBus2()
270
- });
271
- try {
272
- const data = await client.getMe({ auth: accessToken(tokenToUse) });
273
- if (cancelled) return;
274
- setSession({ token: tokenToUse, user: data });
275
- scheduleProactiveRefresh(tokenToUse);
276
- } catch (error) {
277
- if (cancelled) return;
278
- setSession(null);
279
- if (error instanceof APIError && error.status === 401) {
280
- const refreshed = await performRefresh(activeKnowledgeBase);
281
- if (cancelled) return;
282
- if (refreshed) {
283
- return validate(refreshed);
284
- }
285
- clearStoredSession(activeKnowledgeBase.id);
286
- setSessionExpiredMessage("Your session has expired. Please sign in again.");
287
- setSessionExpiredAt(Date.now());
288
- }
289
- } finally {
290
- if (!cancelled) setIsLoading(false);
291
- }
292
- };
293
- setIsLoading(true);
294
- if (isJwtExpired(stored.access)) {
295
- (async () => {
296
- const refreshed = await performRefresh(activeKnowledgeBase);
297
- if (cancelled) return;
298
- if (refreshed) {
299
- await validate(refreshed);
300
- } else {
301
- setSession(null);
302
- clearStoredSession(activeKnowledgeBase.id);
303
- setIsLoading(false);
304
- }
305
- })();
306
- } else {
307
- validate(stored.access);
308
- }
309
- return () => {
310
- cancelled = true;
311
- };
312
- }, [activeKnowledgeBase, scheduleProactiveRefresh]);
313
- useEffect(() => {
314
- return () => {
315
- if (proactiveRefreshTimerRef.current) {
316
- clearTimeout(proactiveRefreshTimerRef.current);
317
- proactiveRefreshTimerRef.current = null;
318
- }
319
- };
320
- }, []);
321
- useEffect(() => {
322
- if (!activeKnowledgeBaseId) return;
323
- const watchKey = sessionKey(activeKnowledgeBaseId);
324
- const handler = (e) => {
325
- if (e.key !== watchKey) return;
326
- if (!e.newValue) {
327
- setSession(null);
328
- if (proactiveRefreshTimerRef.current) {
329
- clearTimeout(proactiveRefreshTimerRef.current);
330
- proactiveRefreshTimerRef.current = null;
331
- }
332
- return;
333
- }
334
- try {
335
- const parsed = JSON.parse(e.newValue);
336
- if (typeof parsed.access === "string") {
337
- setSession((prev) => prev ? { ...prev, token: parsed.access } : prev);
338
- scheduleProactiveRefresh(parsed.access);
339
- }
340
- } catch {
341
- }
342
- };
343
- window.addEventListener("storage", handler);
344
- return () => window.removeEventListener("storage", handler);
345
- }, [activeKnowledgeBaseId, scheduleProactiveRefresh]);
346
- useEffect(() => {
347
- return registerAuthNotifyHandlers({
348
- onSessionExpired: (message) => {
349
- setSessionExpiredMessage(message ?? "Your session has expired. Please sign in again.");
350
- setSessionExpiredAt(Date.now());
351
- setSession(null);
352
- if (activeKnowledgeBaseId) {
353
- clearStoredSession(activeKnowledgeBaseId);
354
- }
355
- if (proactiveRefreshTimerRef.current) {
356
- clearTimeout(proactiveRefreshTimerRef.current);
357
- proactiveRefreshTimerRef.current = null;
358
- }
359
- },
360
- onPermissionDenied: (message) => {
361
- setPermissionDeniedMessage(message ?? "You do not have permission to perform this action.");
362
- setPermissionDeniedAt(Date.now());
363
- }
364
- });
365
- }, [activeKnowledgeBaseId]);
366
- const addKnowledgeBase = useCallback((input, access, refresh) => {
367
- const kb = { id: generateKbId(), ...input };
368
- setStoredSession(kb.id, { access, refresh });
369
- setKnowledgeBases((prev) => [...prev, kb]);
370
- setActiveKnowledgeBaseId(kb.id);
371
- return kb;
372
- }, []);
373
- const removeKnowledgeBase = useCallback((id) => {
374
- clearStoredSession(id);
375
- setKnowledgeBases((prev) => {
376
- const remaining = prev.filter((kb) => kb.id !== id);
377
- setActiveKnowledgeBaseId((activeId) => activeId === id ? remaining[0]?.id ?? null : activeId);
378
- return remaining;
379
- });
380
- }, []);
381
- const setActiveKnowledgeBase = useCallback((id) => {
382
- setActiveKnowledgeBaseId(id);
383
- }, []);
384
- const updateKnowledgeBase = useCallback((id, updates) => {
385
- setKnowledgeBases((prev) => prev.map((kb) => kb.id === id ? { ...kb, ...updates } : kb));
386
- }, []);
387
- const signIn = useCallback((id, access, refresh) => {
388
- setStoredSession(id, { access, refresh });
389
- setKnowledgeBases((prev) => prev.map((kb) => kb.id === id ? { ...kb } : kb));
390
- setActiveKnowledgeBaseId(id);
391
- }, []);
392
- const signOut = useCallback((id) => {
393
- clearStoredSession(id);
394
- setActiveKnowledgeBaseId((activeId) => {
395
- if (activeId === id) {
396
- setSession(null);
397
- if (proactiveRefreshTimerRef.current) {
398
- clearTimeout(proactiveRefreshTimerRef.current);
399
- proactiveRefreshTimerRef.current = null;
400
- }
401
- }
402
- return activeId;
403
- });
404
- setKnowledgeBases((prev) => [...prev]);
405
- }, []);
406
- const acknowledgeSessionExpired = useCallback(() => {
407
- setSessionExpiredAt(null);
408
- setSessionExpiredMessage(null);
409
- }, []);
410
- const acknowledgePermissionDenied = useCallback(() => {
411
- setPermissionDeniedAt(null);
412
- setPermissionDeniedMessage(null);
413
- }, []);
414
- const [, setTick] = useState(0);
415
- useEffect(() => {
416
- const interval = setInterval(() => setTick((t) => t + 1), 3e4);
417
- return () => clearInterval(interval);
418
- }, []);
419
- const value = useMemo(() => {
420
- const user = session?.user ?? null;
421
- const token = session?.token ?? null;
422
- const expiresAt = token ? parseJwtExpiry(token) : null;
423
- return {
424
- knowledgeBases,
425
- activeKnowledgeBase,
426
- session,
427
- isLoading,
428
- user,
429
- token,
430
- isAuthenticated: !!session,
431
- hasValidBackendToken: !!token,
432
- isFullyAuthenticated: !!session,
433
- displayName: user?.name ?? user?.email?.split("@")[0] ?? "User",
434
- avatarUrl: user?.image ?? null,
435
- userDomain: user?.domain || user?.email?.split("@")[1],
436
- isAdmin: user?.isAdmin ?? false,
437
- isModerator: user?.isModerator ?? false,
438
- expiresAt,
439
- sessionExpiredAt,
440
- sessionExpiredMessage,
441
- permissionDeniedAt,
442
- permissionDeniedMessage,
443
- addKnowledgeBase,
444
- removeKnowledgeBase,
445
- setActiveKnowledgeBase,
446
- updateKnowledgeBase,
447
- signIn,
448
- signOut,
449
- refreshActive,
450
- acknowledgeSessionExpired,
451
- acknowledgePermissionDenied
452
- };
453
- }, [
454
- knowledgeBases,
455
- activeKnowledgeBase,
456
- session,
457
- isLoading,
458
- sessionExpiredAt,
459
- sessionExpiredMessage,
460
- permissionDeniedAt,
461
- permissionDeniedMessage,
462
- addKnowledgeBase,
463
- removeKnowledgeBase,
464
- setActiveKnowledgeBase,
465
- updateKnowledgeBase,
466
- signIn,
467
- signOut,
468
- refreshActive,
469
- acknowledgeSessionExpired,
470
- acknowledgePermissionDenied
471
- ]);
472
- return /* @__PURE__ */ jsx(KnowledgeBaseSessionContext.Provider, { value, children });
473
- }
474
- function useKnowledgeBaseSession() {
475
- const ctx = useContext(KnowledgeBaseSessionContext);
476
- if (!ctx) {
477
- throw new Error(
478
- "useKnowledgeBaseSession requires KnowledgeBaseSessionProvider. This component is rendered outside the auth boundary. Move it into a protected layout."
479
- );
480
- }
481
- return ctx;
482
- }
483
-
484
- // src/components/Toast.tsx
485
- import React2, { useEffect as useEffect2, useState as useState2 } from "react";
486
- import { createPortal } from "react-dom";
487
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
488
- var icons = {
489
- success: /* @__PURE__ */ jsx2("svg", { className: "semiont-toast-icon", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx2("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" }) }),
490
- error: /* @__PURE__ */ jsx2("svg", { className: "semiont-toast-icon", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx2("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }),
491
- warning: /* @__PURE__ */ jsx2("svg", { className: "semiont-toast-icon", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx2("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" }) }),
492
- info: /* @__PURE__ */ jsx2("svg", { className: "semiont-toast-icon", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx2("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) })
493
- };
494
- function Toast({ toast, onClose }) {
495
- useEffect2(() => {
496
- const timer = setTimeout(() => {
497
- onClose(toast.id);
498
- }, toast.duration || 3e3);
499
- return () => clearTimeout(timer);
500
- }, [toast, onClose]);
501
- return /* @__PURE__ */ jsxs(
502
- "div",
503
- {
504
- className: "semiont-toast",
505
- "data-variant": toast.type,
506
- role: "alert",
507
- children: [
508
- /* @__PURE__ */ jsx2("div", { className: "semiont-toast-icon-wrapper", children: icons[toast.type] }),
509
- /* @__PURE__ */ jsx2("p", { className: "semiont-toast-message", children: toast.message }),
510
- /* @__PURE__ */ jsx2(
511
- "button",
512
- {
513
- onClick: () => onClose(toast.id),
514
- className: "semiont-toast-close",
515
- "aria-label": "Close",
516
- children: /* @__PURE__ */ jsx2("svg", { className: "semiont-toast-close-icon", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx2("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
517
- }
518
- )
519
- ]
520
- }
521
- );
522
- }
523
- function ToastContainer({ toasts, onClose }) {
524
- const [mounted, setMounted] = useState2(false);
525
- useEffect2(() => {
526
- setMounted(true);
527
- return () => setMounted(false);
528
- }, []);
529
- if (!mounted) return null;
530
- return createPortal(
531
- /* @__PURE__ */ jsx2("div", { className: "semiont-toast-container", children: toasts.map((toast) => /* @__PURE__ */ jsx2(Toast, { toast, onClose }, toast.id)) }),
532
- document.body
533
- );
534
- }
535
- var ToastContext = React2.createContext(void 0);
536
- function ToastProvider({ children }) {
537
- const [toasts, setToasts] = useState2([]);
538
- const showToast = React2.useCallback((message, type = "info", duration) => {
539
- const id = Date.now().toString();
540
- const newToast = duration !== void 0 ? { id, message, type, duration } : { id, message, type };
541
- setToasts((prev) => [...prev, newToast]);
542
- }, []);
543
- const showSuccess = React2.useCallback((message, duration) => showToast(message, "success", duration), [showToast]);
544
- const showError = React2.useCallback((message, duration) => showToast(message, "error", duration), [showToast]);
545
- const showWarning = React2.useCallback((message, duration) => showToast(message, "warning", duration), [showToast]);
546
- const showInfo = React2.useCallback((message, duration) => showToast(message, "info", duration), [showToast]);
547
- const handleClose = React2.useCallback((id) => {
548
- setToasts((prev) => prev.filter((toast) => toast.id !== id));
549
- }, []);
550
- const contextValue = React2.useMemo(
551
- () => ({ showToast, showSuccess, showError, showWarning, showInfo }),
552
- [showToast, showSuccess, showError, showWarning, showInfo]
553
- );
554
- return /* @__PURE__ */ jsxs(ToastContext.Provider, { value: contextValue, children: [
555
- children,
556
- /* @__PURE__ */ jsx2(ToastContainer, { toasts, onClose: handleClose })
557
- ] });
558
- }
559
- function useToast() {
560
- const context = React2.useContext(ToastContext);
561
- if (context === void 0) {
562
- throw new Error("useToast must be used within a ToastProvider");
563
- }
564
- return context;
565
- }
566
-
567
- // src/contexts/OpenResourcesContext.tsx
568
- import { createContext as createContext2, useContext as useContext2 } from "react";
569
- import { jsx as jsx3 } from "react/jsx-runtime";
570
- var OpenResourcesContext = createContext2(void 0);
571
- function OpenResourcesProvider({
572
- openResourcesManager,
573
- children
574
- }) {
575
- return /* @__PURE__ */ jsx3(OpenResourcesContext.Provider, { value: openResourcesManager, children });
576
- }
577
- function useOpenResources() {
578
- const context = useContext2(OpenResourcesContext);
579
- if (context === void 0) {
580
- throw new Error("useOpenResources must be used within an OpenResourcesProvider");
581
- }
582
- return context;
583
- }
584
-
585
- // src/contexts/TranslationContext.tsx
586
- import { createContext as createContext3, useContext as useContext3, useState as useState3, useEffect as useEffect3, useMemo as useMemo2 } from "react";
587
- import { Fragment, jsx as jsx4 } from "react/jsx-runtime";
588
-
589
- // import("../../translations/**/*.json") in src/contexts/TranslationContext.tsx
590
- var globImport_translations_json = __glob({
591
- "../../translations/ar.json": () => import("./ar-3W37O3R3.mjs"),
592
- "../../translations/bn.json": () => import("./bn-JZTJLMVE.mjs"),
593
- "../../translations/cs.json": () => import("./cs-XYHH7HNE.mjs"),
594
- "../../translations/da.json": () => import("./da-MZKIECVT.mjs"),
595
- "../../translations/de.json": () => import("./de-AYXTMRQW.mjs"),
596
- "../../translations/el.json": () => import("./el-A6CVQWAW.mjs"),
597
- "../../translations/en.json": () => import("./en-YPQQBI4T.mjs"),
598
- "../../translations/es.json": () => import("./es-M2HXLJGT.mjs"),
599
- "../../translations/fa.json": () => import("./fa-V6JZJDYP.mjs"),
600
- "../../translations/fi.json": () => import("./fi-ONDTZ5H7.mjs"),
601
- "../../translations/fr.json": () => import("./fr-PAPV4H4G.mjs"),
602
- "../../translations/he.json": () => import("./he-F6VTLJLW.mjs"),
603
- "../../translations/hi.json": () => import("./hi-CFUAV4BF.mjs"),
604
- "../../translations/id.json": () => import("./id-NBKLCCI7.mjs"),
605
- "../../translations/it.json": () => import("./it-SLSOWVVU.mjs"),
606
- "../../translations/ja.json": () => import("./ja-L5IG4ECE.mjs"),
607
- "../../translations/ko.json": () => import("./ko-QYMTULKK.mjs"),
608
- "../../translations/ms.json": () => import("./ms-5DGSFKM2.mjs"),
609
- "../../translations/nl.json": () => import("./nl-VZPCGONO.mjs"),
610
- "../../translations/no.json": () => import("./no-MF6F352I.mjs"),
611
- "../../translations/pl.json": () => import("./pl-WIK72JUO.mjs"),
612
- "../../translations/pt.json": () => import("./pt-RRP5ZF6A.mjs"),
613
- "../../translations/ro.json": () => import("./ro-XHQLC3T7.mjs"),
614
- "../../translations/sv.json": () => import("./sv-EWULDN6E.mjs"),
615
- "../../translations/th.json": () => import("./th-TGOBHFG4.mjs"),
616
- "../../translations/tr.json": () => import("./tr-LMMPBMV7.mjs"),
617
- "../../translations/uk.json": () => import("./uk-IPGRRJY6.mjs"),
618
- "../../translations/vi.json": () => import("./vi-Q676OJQS.mjs"),
619
- "../../translations/zh.json": () => import("./zh-F3MTWQDX.mjs")
620
- });
621
-
622
- // src/contexts/TranslationContext.tsx
623
- var TranslationContext = createContext3(null);
624
- var translationCache = /* @__PURE__ */ new Map();
625
- function processPluralFormat(text, params) {
626
- const pluralMatch = text.match(/\{(\w+),\s*plural,\s*/);
627
- if (!pluralMatch) {
628
- return text;
629
- }
630
- const paramName = pluralMatch[1];
631
- const count = params[paramName];
632
- if (count === void 0) {
633
- return text;
634
- }
635
- let startPos = pluralMatch[0].length;
636
- let braceCount = 1;
637
- let endPos = startPos;
638
- for (let i = startPos; i < text.length; i++) {
639
- if (text[i] === "{") braceCount++;
640
- else if (text[i] === "}") {
641
- braceCount--;
642
- if (braceCount === 0) {
643
- endPos = i;
644
- break;
645
- }
646
- }
647
- }
648
- const pluralCases = text.substring(startPos, endPos);
649
- const cases = {};
650
- const caseRegex = /(?:=(\d+)|(\w+))\s*\{([^}]+)\}/g;
651
- let caseMatch;
652
- while ((caseMatch = caseRegex.exec(pluralCases)) !== null) {
653
- const [, exactNumber, keyword, textContent] = caseMatch;
654
- const key = exactNumber !== void 0 ? `=${exactNumber}` : keyword;
655
- cases[key] = textContent;
656
- }
657
- const exactMatch = cases[`=${count}`];
658
- if (exactMatch !== void 0) {
659
- const result = exactMatch.replace(/#/g, String(count));
660
- return text.substring(0, pluralMatch.index) + result + text.substring(endPos + 1);
661
- }
662
- const otherCase = cases["other"];
663
- if (otherCase !== void 0) {
664
- const result = otherCase.replace(/#/g, String(count));
665
- return text.substring(0, pluralMatch.index) + result + text.substring(endPos + 1);
666
- }
667
- return text;
668
- }
669
- var AVAILABLE_LOCALES = [
670
- "ar",
671
- // Arabic
672
- "bn",
673
- // Bengali
674
- "cs",
675
- // Czech
676
- "da",
677
- // Danish
678
- "de",
679
- // German
680
- "el",
681
- // Greek
682
- "en",
683
- // English
684
- "es",
685
- // Spanish
686
- "fa",
687
- // Persian/Farsi
688
- "fi",
689
- // Finnish
690
- "fr",
691
- // French
692
- "he",
693
- // Hebrew
694
- "hi",
695
- // Hindi
696
- "id",
697
- // Indonesian
698
- "it",
699
- // Italian
700
- "ja",
701
- // Japanese
702
- "ko",
703
- // Korean
704
- "ms",
705
- // Malay
706
- "nl",
707
- // Dutch
708
- "no",
709
- // Norwegian
710
- "pl",
711
- // Polish
712
- "pt",
713
- // Portuguese
714
- "ro",
715
- // Romanian
716
- "sv",
717
- // Swedish
718
- "th",
719
- // Thai
720
- "tr",
721
- // Turkish
722
- "uk",
723
- // Ukrainian
724
- "vi",
725
- // Vietnamese
726
- "zh"
727
- // Chinese
728
- ];
729
- async function loadTranslations(locale) {
730
- if (translationCache.has(locale)) {
731
- return translationCache.get(locale);
732
- }
733
- if (locale === "en") {
734
- translationCache.set("en", en_default);
735
- return en_default;
736
- }
737
- try {
738
- const translations = await globImport_translations_json(`../../translations/${locale}.json`);
739
- const translationData = translations.default || translations;
740
- translationCache.set(locale, translationData);
741
- return translationData;
742
- } catch (error) {
743
- console.error(`Failed to load translations for locale: ${locale}`, error);
744
- return en_default;
745
- }
746
- }
747
- var defaultTranslationManager = {
748
- t: (namespace, key, params) => {
749
- const translations = en_default;
750
- const translation = translations[namespace]?.[key];
751
- if (!translation) {
752
- console.warn(`Translation not found for ${namespace}.${key}`);
753
- return `${namespace}.${key}`;
754
- }
755
- if (params && typeof translation === "string") {
756
- let result = translation;
757
- result = processPluralFormat(result, params);
758
- Object.entries(params).forEach(([paramKey, paramValue]) => {
759
- result = result.replace(new RegExp(`\\{\\{${paramKey}\\}\\}`, "g"), String(paramValue));
760
- });
761
- return result;
762
- }
763
- return translation;
764
- }
765
- };
766
- function TranslationProvider({
767
- translationManager,
768
- locale,
769
- loadingComponent = null,
770
- children
771
- }) {
772
- const [loadedTranslations, setLoadedTranslations] = useState3(null);
773
- const [isLoading, setIsLoading] = useState3(false);
774
- useEffect3(() => {
775
- if (locale && !translationManager) {
776
- setIsLoading(true);
777
- loadTranslations(locale).then((translations) => {
778
- setLoadedTranslations(translations);
779
- setIsLoading(false);
780
- }).catch((error) => {
781
- console.error("Failed to load translations:", error);
782
- setLoadedTranslations(en_default);
783
- setIsLoading(false);
784
- });
785
- }
786
- }, [locale, translationManager]);
787
- const localeManager = useMemo2(() => {
788
- if (!loadedTranslations) return null;
789
- return {
790
- t: (namespace, key, params) => {
791
- const translation = loadedTranslations[namespace]?.[key];
792
- if (!translation) {
793
- console.warn(`Translation not found for ${namespace}.${key} in locale ${locale}`);
794
- return `${namespace}.${key}`;
795
- }
796
- if (params && typeof translation === "string") {
797
- let result = translation;
798
- result = processPluralFormat(result, params);
799
- Object.entries(params).forEach(([paramKey, paramValue]) => {
800
- result = result.replace(new RegExp(`\\{\\{${paramKey}\\}\\}`, "g"), String(paramValue));
801
- });
802
- return result;
803
- }
804
- return translation;
805
- }
806
- };
807
- }, [loadedTranslations, locale]);
808
- if (translationManager) {
809
- return /* @__PURE__ */ jsx4(TranslationContext.Provider, { value: translationManager, children });
810
- }
811
- if (locale && isLoading) {
812
- return /* @__PURE__ */ jsx4(Fragment, { children: loadingComponent });
813
- }
814
- if (locale && localeManager) {
815
- return /* @__PURE__ */ jsx4(TranslationContext.Provider, { value: localeManager, children });
816
- }
817
- return /* @__PURE__ */ jsx4(TranslationContext.Provider, { value: defaultTranslationManager, children });
818
- }
819
- function useTranslations(namespace) {
820
- const context = useContext3(TranslationContext);
821
- if (!context) {
822
- return (key, params) => {
823
- const translations = en_default;
824
- const translation = translations[namespace]?.[key];
825
- if (!translation) {
826
- console.warn(`Translation not found for ${namespace}.${key}`);
827
- return `${namespace}.${key}`;
828
- }
829
- if (params && typeof translation === "string") {
830
- let result = translation;
831
- result = processPluralFormat(result, params);
832
- Object.entries(params).forEach(([paramKey, paramValue]) => {
833
- result = result.replace(new RegExp(`\\{\\{${paramKey}\\}\\}`, "g"), String(paramValue));
834
- });
835
- return result;
836
- }
837
- return translation;
838
- };
839
- }
840
- return (key, params) => context.t(namespace, key, params);
841
- }
842
- function usePreloadTranslations() {
843
- return {
844
- preload: async (locale) => {
845
- try {
846
- await loadTranslations(locale);
847
- return true;
848
- } catch (error) {
849
- console.error(`Failed to preload translations for ${locale}:`, error);
850
- return false;
851
- }
852
- },
853
- isLoaded: (locale) => translationCache.has(locale)
854
- };
855
- }
856
-
857
- export {
858
- defaultProtocol,
859
- isValidHostname,
860
- kbBackendUrl,
861
- getKbSessionStatus,
862
- notifySessionExpired,
863
- notifyPermissionDenied,
864
- KnowledgeBaseSessionContext,
865
- KnowledgeBaseSessionProvider,
866
- useKnowledgeBaseSession,
867
- ToastContainer,
868
- ToastProvider,
869
- useToast,
870
- OpenResourcesProvider,
871
- useOpenResources,
872
- AVAILABLE_LOCALES,
873
- TranslationProvider,
874
- useTranslations,
875
- usePreloadTranslations
876
- };
877
- //# sourceMappingURL=chunk-R4CCMFJH.mjs.map