@semiont/react-ui 0.4.14 → 0.4.15

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 (49) hide show
  1. package/README.md +18 -12
  2. package/dist/KnowledgeBaseSessionContext-CpYaCbnC.d.mts +174 -0
  3. package/dist/{PdfAnnotationCanvas.client-CW6SKH2U.mjs → PdfAnnotationCanvas.client-CHDCGQBR.mjs} +3 -3
  4. package/dist/{chunk-HNZOXH4L.mjs → chunk-OZICDVH7.mjs} +5 -3
  5. package/dist/chunk-OZICDVH7.mjs.map +1 -0
  6. package/dist/chunk-R2U7P4TK.mjs +865 -0
  7. package/dist/chunk-R2U7P4TK.mjs.map +1 -0
  8. package/dist/{chunk-BQJWOK4C.mjs → chunk-VN5NY4SN.mjs} +9 -8
  9. package/dist/chunk-VN5NY4SN.mjs.map +1 -0
  10. package/dist/index.d.mts +139 -169
  11. package/dist/index.mjs +2197 -1947
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/test-utils.d.mts +13 -62
  14. package/dist/test-utils.mjs +40 -21
  15. package/dist/test-utils.mjs.map +1 -1
  16. package/package.json +5 -3
  17. package/src/components/ProtectedErrorBoundary.tsx +95 -0
  18. package/src/components/__tests__/ProtectedErrorBoundary.test.tsx +197 -0
  19. package/src/components/modals/PermissionDeniedModal.tsx +140 -0
  20. package/src/components/modals/ReferenceWizardModal.tsx +3 -2
  21. package/src/components/modals/SessionExpiredModal.tsx +101 -0
  22. package/src/components/modals/__tests__/PermissionDeniedModal.test.tsx +150 -0
  23. package/src/components/modals/__tests__/SessionExpiredModal.test.tsx +115 -0
  24. package/src/components/resource/AnnotationHistory.tsx +5 -6
  25. package/src/components/resource/HistoryEvent.tsx +7 -7
  26. package/src/components/resource/__tests__/AnnotationHistory.test.tsx +33 -34
  27. package/src/components/resource/__tests__/HistoryEvent.test.tsx +17 -19
  28. package/src/components/resource/__tests__/event-formatting.test.ts +70 -94
  29. package/src/components/resource/event-formatting.ts +56 -56
  30. package/src/components/resource/panels/ReferenceEntry.tsx +7 -5
  31. package/src/components/resource/panels/ResourceInfoPanel.tsx +8 -6
  32. package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +12 -12
  33. package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +1 -0
  34. package/src/features/resource-viewer/__tests__/AnnotationCreationPending.test.tsx +1 -1
  35. package/src/features/resource-viewer/__tests__/AnnotationDeletionIntegration.test.tsx +4 -4
  36. package/src/features/resource-viewer/__tests__/AnnotationProgressDismissal.test.tsx +5 -10
  37. package/src/features/resource-viewer/__tests__/BindFlowIntegration.test.tsx +23 -54
  38. package/src/features/resource-viewer/__tests__/DetectionFlowBug.test.tsx +6 -6
  39. package/src/features/resource-viewer/__tests__/DetectionFlowIntegration.test.tsx +7 -19
  40. package/src/features/resource-viewer/__tests__/ToastNotifications.test.tsx +1 -1
  41. package/src/features/resource-viewer/__tests__/YieldFlowIntegration.test.tsx +18 -44
  42. package/src/features/resource-viewer/__tests__/annotation-progress-flow.test.tsx +6 -6
  43. package/src/features/resource-viewer/components/ResourceViewerPage.tsx +24 -26
  44. package/dist/TranslationManager-CudgH3gw.d.mts +0 -107
  45. package/dist/chunk-BQJWOK4C.mjs.map +0 -1
  46. package/dist/chunk-HNZOXH4L.mjs.map +0 -1
  47. package/dist/chunk-OL5UST25.mjs +0 -413
  48. package/dist/chunk-OL5UST25.mjs.map +0 -1
  49. /package/dist/{PdfAnnotationCanvas.client-CW6SKH2U.mjs.map → PdfAnnotationCanvas.client-CHDCGQBR.mjs.map} +0 -0
@@ -1,107 +0,0 @@
1
- /**
2
- * Open Resources Manager Interface
3
- *
4
- * Manages a list of open resources (documents/files) with persistence.
5
- * This interface allows apps to provide their own implementation of resource management
6
- * (localStorage, sessionStorage, database, etc.) while components remain framework-agnostic.
7
- *
8
- * Components accept this manager as a prop instead of consuming from Context.
9
- *
10
- * @example
11
- * ```tsx
12
- * // In app (e.g., frontend/src/hooks/useOpenResourcesManager.ts)
13
- * export function useOpenResourcesManager(): OpenResourcesManager {
14
- * const [openResources, setOpenResources] = useState<OpenResource[]>([]);
15
- *
16
- * // Implementation details...
17
- *
18
- * return {
19
- * openResources,
20
- * addResource,
21
- * removeResource,
22
- * updateResourceName,
23
- * reorderResources
24
- * };
25
- * }
26
- *
27
- * // Pass to components as props
28
- * <KnowledgeNavigation openResourcesManager={openResourcesManager} />
29
- * ```
30
- */
31
- interface OpenResource {
32
- /** Unique identifier for the resource */
33
- id: string;
34
- /** Display name of the resource */
35
- name: string;
36
- /** Timestamp when the resource was opened */
37
- openedAt: number;
38
- /** Order/position for manual sorting (optional for backward compatibility) */
39
- order?: number;
40
- /** Media type for icon display (e.g., 'application/pdf', 'text/plain') */
41
- mediaType?: string;
42
- /** Working-tree URI (e.g. "file://docs/overview.md") — used as tooltip in navigation */
43
- storageUri?: string;
44
- }
45
- interface OpenResourcesManager {
46
- /** List of currently open resources */
47
- openResources: OpenResource[];
48
- /**
49
- * Add a new resource to the open list or update if already exists
50
- * @param id - Unique resource identifier
51
- * @param name - Display name of the resource
52
- * @param mediaType - Optional media type for icon display
53
- * @param storageUri - Optional working-tree URI (e.g. "file://docs/overview.md")
54
- */
55
- addResource: (id: string, name: string, mediaType?: string, storageUri?: string) => void;
56
- /**
57
- * Remove a resource from the open list
58
- * @param id - Resource identifier to remove
59
- */
60
- removeResource: (id: string) => void;
61
- /**
62
- * Update the display name of an open resource
63
- * @param id - Resource identifier
64
- * @param name - New display name
65
- */
66
- updateResourceName: (id: string, name: string) => void;
67
- /**
68
- * Reorder resources by moving from one index to another
69
- * @param oldIndex - Current position index
70
- * @param newIndex - Desired position index
71
- */
72
- reorderResources: (oldIndex: number, newIndex: number) => void;
73
- }
74
-
75
- /**
76
- * Session management interface for handling authentication state and session expiry
77
- * Apps implement this interface and pass it to SessionProvider
78
- */
79
- interface SessionState {
80
- /** Whether the user is currently authenticated */
81
- isAuthenticated: boolean;
82
- /** When the session expires (null if not authenticated) */
83
- expiresAt: Date | null;
84
- /** Time in milliseconds until session expires (null if not authenticated) */
85
- timeUntilExpiry: number | null;
86
- /** Whether the session is expiring soon (< 5 minutes) */
87
- isExpiringSoon: boolean;
88
- }
89
- interface SessionManager extends SessionState {
90
- }
91
-
92
- /**
93
- * Translation management interface
94
- * Apps implement this to provide translations using their preferred i18n library
95
- */
96
- interface TranslationManager {
97
- /**
98
- * Translate a key within a namespace
99
- * @param namespace - Translation namespace (e.g., 'Toolbar', 'ResourceViewer')
100
- * @param key - Translation key within the namespace
101
- * @param params - Optional parameters for interpolation
102
- * @returns Translated string
103
- */
104
- t: (namespace: string, key: string, params?: Record<string, any>) => string;
105
- }
106
-
107
- export type { OpenResourcesManager as O, SessionManager as S, TranslationManager as T, OpenResource as a, SessionState as b };
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/contexts/AuthTokenContext.tsx","../src/contexts/useEventSubscription.ts","../src/hooks/useBeckonFlow.ts"],"sourcesContent":["'use client';\n\n/**\n * Auth Token Context - Manages authentication token lifecycle\n *\n * Simple approach: Just pass the token value through context.\n * When the token changes, context updates, components re-render.\n * No complex machinery needed.\n */\n\nimport { createContext, useContext, ReactNode } from 'react';\n\nconst AuthTokenContext = createContext<string | null | undefined>(undefined);\n\nexport interface AuthTokenProviderProps {\n token: string | null;\n children: ReactNode;\n}\n\n/**\n * Provider for auth token\n * Pass the current token value - React handles the rest\n */\nexport function AuthTokenProvider({\n token,\n children,\n}: AuthTokenProviderProps) {\n return (\n <AuthTokenContext.Provider value={token}>\n {children}\n </AuthTokenContext.Provider>\n );\n}\n\n/**\n * Hook to get current auth token\n *\n * Returns the current token value from context.\n * Re-renders automatically when token changes (normal React behavior).\n *\n * @returns Current access token (null if not authenticated)\n * @throws Error if used outside AuthTokenProvider\n */\nexport function useAuthToken(): string | null {\n const context = useContext(AuthTokenContext);\n\n if (context === undefined) {\n throw new Error('useAuthToken must be used within an AuthTokenProvider');\n }\n\n return context;\n}\n","import { useEffect, useRef, useMemo } from 'react';\nimport type { EventMap } from '@semiont/core';\nimport { useEventBus } from './EventBusContext';\n\n/**\n * Subscribe to an event bus event with automatic cleanup.\n *\n * This hook solves the \"stale closure\" problem by always using the latest\n * version of the handler without re-subscribing.\n *\n * @example\n * ```tsx\n * useEventSubscription('mark:created', ({ annotation }) => {\n * // This always uses the latest props/state\n * triggerSparkleAnimation(annotation.id);\n * });\n * ```\n */\nexport function useEventSubscription<K extends keyof EventMap>(\n eventName: K,\n handler: (payload: EventMap[K]) => void\n): void {\n const eventBus = useEventBus();\n\n // Store the latest handler in a ref to avoid stale closures\n const handlerRef = useRef(handler);\n\n // Update ref on every render (no re-subscription needed)\n useEffect(() => {\n handlerRef.current = handler;\n });\n\n // Subscribe once, using a stable wrapper that calls the current handler\n useEffect(() => {\n const stableHandler = (payload: EventMap[K]) => {\n handlerRef.current(payload);\n };\n\n // RxJS EventBus.get() returns Subject, subscribe returns Subscription\n const subscription = eventBus.get(eventName).subscribe(stableHandler);\n\n return () => {\n subscription.unsubscribe();\n };\n }, [eventName, eventBus]); // eventBus is stable, only re-subscribe if event name changes\n}\n\n/**\n * Subscribe to multiple events at once.\n *\n * @example\n * ```tsx\n * useEventSubscriptions({\n * 'mark:created': ({ annotation }) => setNewAnnotation(annotation),\n * 'mark:deleted': ({ annotationId }) => removeAnnotation(annotationId),\n * });\n * ```\n */\nexport function useEventSubscriptions(\n subscriptions: {\n [K in keyof EventMap]?: (payload: EventMap[K]) => void;\n }\n): void {\n const eventBus = useEventBus();\n\n // Store the latest handlers in refs\n const handlersRef = useRef(subscriptions);\n\n // Update refs on every render\n useEffect(() => {\n handlersRef.current = subscriptions;\n });\n\n // Get stable list of event names to subscribe to\n const eventNames = useMemo(\n () => Object.keys(subscriptions).sort(),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [Object.keys(subscriptions).sort().join(',')]\n );\n\n // Subscribe once per event - only re-subscribe if event names actually change\n useEffect(() => {\n const subscriptions: Array<{ unsubscribe: () => void }> = [];\n\n // Create stable wrappers for each subscription\n for (const eventName of eventNames) {\n const stableHandler = (payload: any) => {\n const currentHandler = handlersRef.current[eventName as keyof EventMap];\n if (currentHandler) {\n currentHandler(payload);\n } else {\n console.warn('[useEventSubscriptions] No current handler found for:', eventName);\n }\n };\n\n // RxJS EventBus.get() returns Subject, subscribe returns Subscription\n const subscription = eventBus.get(eventName as keyof EventMap).subscribe(stableHandler);\n subscriptions.push(subscription);\n }\n\n // Cleanup: unsubscribe from all subscriptions\n return () => {\n for (const subscription of subscriptions) {\n subscription.unsubscribe();\n }\n };\n }, [eventNames, eventBus]); // eventBus is stable singleton - never in deps; only re-subscribe if event names change\n}\n","/**\n * useBeckonFlow — Annotation attention / pointer coordination hook\n *\n * Manages which annotation currently has the user's attention:\n * - Hover state (hoveredAnnotationId)\n * - Hover → sparkle relay\n * - Click → focus relay\n *\n * Follows react-rxjs-guide.md Layer 2 pattern: Hook bridge that\n * subscribes to events and pushes values into React state.\n *\n * Note: beckon:sparkle visual effect (triggerSparkleAnimation) is owned by\n * ResourceViewerPage, which subscribes to beckon:sparkle and delegates to\n * ResourceAnnotationsContext. This hook emits the signal; it does not render the effect.\n *\n * @subscribes beckon:hover - Sets hoveredAnnotationId; emits beckon:sparkle\n * @subscribes browse:click - Emits beckon:focus (attention relay only)\n * @emits beckon:sparkle\n * @emits beckon:focus\n */\n\n/**\n * useHoverEmitter / createHoverHandlers — annotation hover emission utilities\n *\n * Centralises two hover quality-of-life behaviours:\n *\n * 1. currentHover guard — suppresses redundant emissions when the mouse\n * moves within the same annotation element (prevents event bus noise).\n *\n * 2. Debounce delay (HOVER_DELAY_MS) — a short timer before emitting\n * beckon:hover, so that transient pass-through movements (user dragging\n * the mouse across the panel to reach a button elsewhere) do not trigger\n * sparkle animations or cross-highlight effects.\n * The delay is cancelled immediately on mouseLeave, so leaving is always instant.\n *\n * Two forms are provided:\n *\n * useHoverEmitter(annotationId)\n * React hook. Returns { onMouseEnter, onMouseLeave } props for JSX elements.\n * Use in panel entries (HighlightEntry, CommentEntry, …).\n *\n * createHoverHandlers(emit)\n * Plain factory. Returns { handleMouseEnter(id), handleMouseLeave(), cleanup }.\n * Use inside useEffect / imperative setup code where hooks cannot be called\n * (BrowseView, CodeMirrorRenderer, AnnotationOverlay, PdfAnnotationCanvas).\n */\n\nimport { useState, useRef, useCallback, useEffect } from 'react';\nimport { accessToken } from '@semiont/core';\nimport { useEventBus } from '../contexts/EventBusContext';\nimport { useEventSubscriptions } from '../contexts/useEventSubscription';\nimport { useApiClient } from '../contexts/ApiClientContext';\nimport { useAuthToken } from '../contexts/AuthTokenContext';\nimport type { StreamStatus } from './useResourceEvents';\n\n// ─── useBeckonFlow ─────────────────────────────────────────────────────────\n\nexport interface BeckonFlowState {\n hoveredAnnotationId: string | null;\n}\n\nexport function useBeckonFlow(): BeckonFlowState {\n const eventBus = useEventBus();\n const [hoveredAnnotationId, setHoveredAnnotationId] = useState<string | null>(null);\n\n const handleAnnotationHover = useCallback(({ annotationId }: { annotationId: string | null }) => {\n setHoveredAnnotationId(annotationId);\n if (annotationId) {\n eventBus.get('beckon:sparkle').next({ annotationId });\n }\n }, []); // eventBus is stable singleton - never in deps\n\n const handleAnnotationClick = useCallback(({ annotationId }: { annotationId: string }) => {\n eventBus.get('beckon:focus').next({ annotationId });\n // Scroll to annotation handled by BrowseView via beckon:focus subscription\n }, []); // eventBus is stable singleton - never in deps\n\n useEventSubscriptions({\n 'beckon:hover': handleAnnotationHover,\n 'browse:click': handleAnnotationClick,\n });\n\n return { hoveredAnnotationId };\n}\n\n// ─── createHoverHandlers (use inside useEffect / imperative setup) ────────────\n\n/** Default milliseconds the mouse must dwell before beckon:hover is emitted. */\nexport const HOVER_DELAY_MS = 150;\n\ntype EmitHover = (annotationId: string | null) => void;\n\nexport interface HoverHandlers {\n /** Call with the annotation ID when the mouse enters an annotation element. */\n handleMouseEnter: (annotationId: string) => void;\n /** Call when the mouse leaves the annotation element. */\n handleMouseLeave: () => void;\n /** Cancel any pending timer — call in the useEffect cleanup. */\n cleanup: () => void;\n}\n\n/**\n * Creates hover handlers for imperative code (non-hook contexts).\n * @param emit - Callback to emit hover events\n * @param delayMs - Hover delay in milliseconds\n */\nexport function createHoverHandlers(emit: EmitHover, delayMs: number): HoverHandlers {\n let currentHover: string | null = null;\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const cancelTimer = () => {\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n };\n\n const handleMouseEnter = (annotationId: string) => {\n if (currentHover === annotationId) return; // already hovering this one\n cancelTimer();\n timer = setTimeout(() => {\n timer = null;\n currentHover = annotationId;\n emit(annotationId);\n }, delayMs);\n };\n\n const handleMouseLeave = () => {\n cancelTimer();\n if (currentHover !== null) {\n currentHover = null;\n emit(null);\n }\n };\n\n return { handleMouseEnter, handleMouseLeave, cleanup: cancelTimer };\n}\n\n// ─── useHoverEmitter (use in JSX onMouseEnter / onMouseLeave props) ───────────\n\nexport interface HoverEmitterProps {\n onMouseEnter: () => void;\n onMouseLeave: () => void;\n}\n\n/**\n * React hook that returns onMouseEnter / onMouseLeave props for a single\n * annotation entry element.\n *\n * @param annotationId - The ID of the annotation this element represents.\n * @param hoverDelayMs - Hover delay in milliseconds (defaults to HOVER_DELAY_MS for panel entries)\n */\nexport function useHoverEmitter(annotationId: string, hoverDelayMs: number = HOVER_DELAY_MS): HoverEmitterProps {\n const eventBus = useEventBus();\n const currentHoverRef = useRef<string | null>(null);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const onMouseEnter = useCallback(() => {\n if (currentHoverRef.current === annotationId) return;\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n }\n timerRef.current = setTimeout(() => {\n timerRef.current = null;\n currentHoverRef.current = annotationId;\n eventBus.get('beckon:hover').next({ annotationId });\n }, hoverDelayMs);\n }, [annotationId, hoverDelayMs]); // eventBus is stable singleton - never in deps\n\n const onMouseLeave = useCallback(() => {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n if (currentHoverRef.current !== null) {\n currentHoverRef.current = null;\n eventBus.get('beckon:hover').next({ annotationId: null });\n }\n }, []); // eventBus is stable singleton - never in deps\n\n // Cleanup timer on unmount\n useEffect(() => {\n return () => {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n };\n }, []);\n\n return { onMouseEnter, onMouseLeave };\n}\n\n// ─── useAttentionStream ───────────────────────────────────────────────────────\n\n/**\n * Opens a participant-scoped SSE connection to receive cross-participant\n * beckon signals. Each incoming signal is emitted as beckon:focus on the\n * EventBus — the existing scroll/highlight machinery handles it automatically.\n *\n * Signals are ephemeral: delivered if connected, silently dropped if not.\n *\n * @example\n * ```tsx\n * // In your layout (render-nothing connector):\n * useAttentionStream();\n * ```\n */\nexport function useAttentionStream(): { status: StreamStatus } {\n const client = useApiClient();\n const token = useAuthToken();\n const tokenRef = useRef(token);\n useEffect(() => { tokenRef.current = token; });\n const [status, setStatus] = useState<StreamStatus>('disconnected');\n\n useEffect(() => {\n setStatus('connecting');\n try {\n const sub = client.flows.attentionStream(() =>\n tokenRef.current ? accessToken(tokenRef.current) : undefined\n );\n setStatus('connected');\n return () => { sub.unsubscribe(); setStatus('disconnected'); };\n } catch (error) {\n console.error('[AttentionStream] Failed to connect:', error);\n setStatus('error');\n return;\n }\n }, [client]);\n\n return { status };\n}\n"],"mappings":"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","names":["subscriptions","useRef","useEffect","useRef","useEffect"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/contexts/EventBusContext.tsx","../src/contexts/ApiClientContext.tsx"],"sourcesContent":["'use client';\n\nimport { createContext, useContext, useRef, type ReactNode } from 'react';\nimport { EventBus } from '@semiont/core';\n\nconst EventBusContext = createContext<EventBus | null>(null);\n\nexport interface EventBusProviderProps {\n children: ReactNode;\n}\n\n/**\n * Unified event bus provider for all application events.\n *\n * Each provider mount creates a fresh EventBus instance. This means:\n * - Workspace switches (which remount via key prop) get isolated buses\n * - Tests get isolation naturally — no resetEventBusForTesting needed\n *\n * Operation handlers (API calls triggered by events) are set up separately via\n * the useBindFlow hook, which should be called at the resource page level.\n */\nexport function EventBusProvider({ children }: EventBusProviderProps) {\n const eventBusRef = useRef<EventBus | null>(null);\n if (!eventBusRef.current) {\n eventBusRef.current = new EventBus();\n }\n const eventBus = eventBusRef.current;\n\n return (\n <EventBusContext.Provider value={eventBus}>\n {children}\n </EventBusContext.Provider>\n );\n}\n\n/**\n * Hook to access the unified event bus\n *\n * Use this everywhere instead of:\n * - useMakeMeaningEvents()\n * - useNavigationEvents()\n * - useGlobalSettingsEvents()\n *\n * @example\n * ```typescript\n * const eventBus = useEventBus();\n *\n * // Emit any event\n * eventBus.get('beckon:hover').next({ annotationId: '123' });\n * eventBus.get('browse:sidebar-toggle').next(undefined);\n * eventBus.get('settings:theme-changed').next({ theme: 'dark' });\n *\n * // Subscribe to any event\n * useEffect(() => {\n * const unsubscribe = eventBus.on('beckon:hover', ({ annotationId }) => {\n * console.log(annotationId);\n * });\n * return () => unsubscribe();\n * }, []);\n * ```\n */\nexport function useEventBus(): EventBus {\n const eventBus = useContext(EventBusContext);\n if (!eventBus) {\n throw new Error('useEventBus must be used within EventBusProvider');\n }\n return eventBus;\n}\n","'use client';\n\nimport { createContext, useContext, ReactNode, useMemo } from 'react';\nimport { baseUrl } from '@semiont/core';\nimport { SemiontApiClient } from '@semiont/api-client';\nimport { useEventBus } from './EventBusContext';\n\nconst ApiClientContext = createContext<SemiontApiClient | undefined>(undefined);\n\nexport interface ApiClientProviderProps {\n baseUrl: string;\n children: ReactNode;\n}\n\n/**\n * Provider for API client — must be nested inside EventBusProvider.\n * The client is re-created when the baseUrl changes (workspace switch).\n * The EventBus is taken from EventBusContext so client and UI components\n * share the same workspace-scoped bus.\n */\nexport function ApiClientProvider({\n baseUrl: url,\n children,\n}: ApiClientProviderProps) {\n const eventBus = useEventBus();\n\n const client = useMemo(\n () => new SemiontApiClient({\n baseUrl: baseUrl(url),\n eventBus,\n // Use no timeout in test environment to avoid AbortController issues with ky + vitest\n ...(process.env.NODE_ENV !== 'test' && { timeout: 30000 }),\n }),\n [url, eventBus]\n );\n\n return (\n <ApiClientContext.Provider value={client}>\n {children}\n </ApiClientContext.Provider>\n );\n}\n\n/**\n * Hook to access the stateless API client singleton\n * Must be used within an ApiClientProvider\n * @returns Stateless SemiontApiClient instance\n */\nexport function useApiClient(): SemiontApiClient {\n const context = useContext(ApiClientContext);\n\n if (context === undefined) {\n throw new Error('useApiClient must be used within an ApiClientProvider');\n }\n\n return context;\n}\n"],"mappings":";;;AAEA,SAAS,eAAe,YAAY,cAA8B;AAClE,SAAS,gBAAgB;AA0BrB;AAxBJ,IAAM,kBAAkB,cAA+B,IAAI;AAgBpD,SAAS,iBAAiB,EAAE,SAAS,GAA0B;AACpE,QAAM,cAAc,OAAwB,IAAI;AAChD,MAAI,CAAC,YAAY,SAAS;AACxB,gBAAY,UAAU,IAAI,SAAS;AAAA,EACrC;AACA,QAAM,WAAW,YAAY;AAE7B,SACE,oBAAC,gBAAgB,UAAhB,EAAyB,OAAO,UAC9B,UACH;AAEJ;AA4BO,SAAS,cAAwB;AACtC,QAAM,WAAW,WAAW,eAAe;AAC3C,MAAI,CAAC,UAAU;AACb,UAAM,IAAI,MAAM,kDAAkD;AAAA,EACpE;AACA,SAAO;AACT;;;ACjEA,SAAS,iBAAAA,gBAAe,cAAAC,aAAuB,eAAe;AAC9D,SAAS,eAAe;AACxB,SAAS,wBAAwB;AAiC7B,gBAAAC,YAAA;AA9BJ,IAAM,mBAAmBC,eAA4C,MAAS;AAavE,SAAS,kBAAkB;AAAA,EAChC,SAAS;AAAA,EACT;AACF,GAA2B;AACzB,QAAM,WAAW,YAAY;AAE7B,QAAM,SAAS;AAAA,IACb,MAAM,IAAI,iBAAiB;AAAA,MACzB,SAAS,QAAQ,GAAG;AAAA,MACpB;AAAA;AAAA,MAEA,GAAI,QAAQ,IAAI,aAAa,UAAU,EAAE,SAAS,IAAM;AAAA,IAC1D,CAAC;AAAA,IACD,CAAC,KAAK,QAAQ;AAAA,EAChB;AAEA,SACE,gBAAAD,KAAC,iBAAiB,UAAjB,EAA0B,OAAO,QAC/B,UACH;AAEJ;AAOO,SAAS,eAAiC;AAC/C,QAAM,UAAUE,YAAW,gBAAgB;AAE3C,MAAI,YAAY,QAAW;AACzB,UAAM,IAAI,MAAM,uDAAuD;AAAA,EACzE;AAEA,SAAO;AACT;","names":["createContext","useContext","jsx","createContext","useContext"]}
@@ -1,413 +0,0 @@
1
- 'use client';
2
- import {
3
- en_default
4
- } from "./chunk-HVMAGUFA.mjs";
5
- import {
6
- __glob
7
- } from "./chunk-D4GAAQMM.mjs";
8
-
9
- // src/contexts/SessionContext.tsx
10
- import { createContext, useContext } from "react";
11
- import { jsx } from "react/jsx-runtime";
12
- var SessionContext = createContext(null);
13
- function SessionProvider({
14
- sessionManager,
15
- children
16
- }) {
17
- return /* @__PURE__ */ jsx(SessionContext.Provider, { value: sessionManager, children });
18
- }
19
- function useSessionContext() {
20
- const context = useContext(SessionContext);
21
- if (!context) {
22
- throw new Error("useSessionContext must be used within SessionProvider");
23
- }
24
- return context;
25
- }
26
-
27
- // src/components/Toast.tsx
28
- import React, { useEffect, useState } from "react";
29
- import { createPortal } from "react-dom";
30
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
31
- var icons = {
32
- 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" }) }),
33
- 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" }) }),
34
- 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" }) }),
35
- 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" }) })
36
- };
37
- function Toast({ toast, onClose }) {
38
- useEffect(() => {
39
- const timer = setTimeout(() => {
40
- onClose(toast.id);
41
- }, toast.duration || 3e3);
42
- return () => clearTimeout(timer);
43
- }, [toast, onClose]);
44
- return /* @__PURE__ */ jsxs(
45
- "div",
46
- {
47
- className: "semiont-toast",
48
- "data-variant": toast.type,
49
- role: "alert",
50
- children: [
51
- /* @__PURE__ */ jsx2("div", { className: "semiont-toast-icon-wrapper", children: icons[toast.type] }),
52
- /* @__PURE__ */ jsx2("p", { className: "semiont-toast-message", children: toast.message }),
53
- /* @__PURE__ */ jsx2(
54
- "button",
55
- {
56
- onClick: () => onClose(toast.id),
57
- className: "semiont-toast-close",
58
- "aria-label": "Close",
59
- 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" }) })
60
- }
61
- )
62
- ]
63
- }
64
- );
65
- }
66
- function ToastContainer({ toasts, onClose }) {
67
- const [mounted, setMounted] = useState(false);
68
- useEffect(() => {
69
- setMounted(true);
70
- return () => setMounted(false);
71
- }, []);
72
- if (!mounted) return null;
73
- return createPortal(
74
- /* @__PURE__ */ jsx2("div", { className: "semiont-toast-container", children: toasts.map((toast) => /* @__PURE__ */ jsx2(Toast, { toast, onClose }, toast.id)) }),
75
- document.body
76
- );
77
- }
78
- var ToastContext = React.createContext(void 0);
79
- function ToastProvider({ children }) {
80
- const [toasts, setToasts] = useState([]);
81
- const showToast = React.useCallback((message, type = "info", duration) => {
82
- const id = Date.now().toString();
83
- const newToast = duration !== void 0 ? { id, message, type, duration } : { id, message, type };
84
- setToasts((prev) => [...prev, newToast]);
85
- }, []);
86
- const showSuccess = React.useCallback((message, duration) => showToast(message, "success", duration), [showToast]);
87
- const showError = React.useCallback((message, duration) => showToast(message, "error", duration), [showToast]);
88
- const showWarning = React.useCallback((message, duration) => showToast(message, "warning", duration), [showToast]);
89
- const showInfo = React.useCallback((message, duration) => showToast(message, "info", duration), [showToast]);
90
- const handleClose = React.useCallback((id) => {
91
- setToasts((prev) => prev.filter((toast) => toast.id !== id));
92
- }, []);
93
- const contextValue = React.useMemo(
94
- () => ({ showToast, showSuccess, showError, showWarning, showInfo }),
95
- [showToast, showSuccess, showError, showWarning, showInfo]
96
- );
97
- return /* @__PURE__ */ jsxs(ToastContext.Provider, { value: contextValue, children: [
98
- children,
99
- /* @__PURE__ */ jsx2(ToastContainer, { toasts, onClose: handleClose })
100
- ] });
101
- }
102
- function useToast() {
103
- const context = React.useContext(ToastContext);
104
- if (context === void 0) {
105
- throw new Error("useToast must be used within a ToastProvider");
106
- }
107
- return context;
108
- }
109
-
110
- // src/contexts/OpenResourcesContext.tsx
111
- import { createContext as createContext2, useContext as useContext2 } from "react";
112
- import { jsx as jsx3 } from "react/jsx-runtime";
113
- var OpenResourcesContext = createContext2(void 0);
114
- function OpenResourcesProvider({
115
- openResourcesManager,
116
- children
117
- }) {
118
- return /* @__PURE__ */ jsx3(OpenResourcesContext.Provider, { value: openResourcesManager, children });
119
- }
120
- function useOpenResources() {
121
- const context = useContext2(OpenResourcesContext);
122
- if (context === void 0) {
123
- throw new Error("useOpenResources must be used within an OpenResourcesProvider");
124
- }
125
- return context;
126
- }
127
-
128
- // src/contexts/TranslationContext.tsx
129
- import { createContext as createContext3, useContext as useContext3, useState as useState2, useEffect as useEffect2, useMemo } from "react";
130
- import { Fragment, jsx as jsx4 } from "react/jsx-runtime";
131
-
132
- // import("../../translations/**/*.json") in src/contexts/TranslationContext.tsx
133
- var globImport_translations_json = __glob({
134
- "../../translations/ar.json": () => import("./ar-R4CRNXEF.mjs"),
135
- "../../translations/bn.json": () => import("./bn-CZKGRHTA.mjs"),
136
- "../../translations/cs.json": () => import("./cs-4WIB2IHH.mjs"),
137
- "../../translations/da.json": () => import("./da-JWYEUYPX.mjs"),
138
- "../../translations/de.json": () => import("./de-GWUQZGER.mjs"),
139
- "../../translations/el.json": () => import("./el-DM2GT7P5.mjs"),
140
- "../../translations/en.json": () => import("./en-IUV4ZXKH.mjs"),
141
- "../../translations/es.json": () => import("./es-6LVQIM3D.mjs"),
142
- "../../translations/fa.json": () => import("./fa-IRUJY3QI.mjs"),
143
- "../../translations/fi.json": () => import("./fi-53FBOEVT.mjs"),
144
- "../../translations/fr.json": () => import("./fr-Q5KY7QL6.mjs"),
145
- "../../translations/he.json": () => import("./he-HJNKULBY.mjs"),
146
- "../../translations/hi.json": () => import("./hi-UYZ4X6CR.mjs"),
147
- "../../translations/id.json": () => import("./id-UAQMH6U2.mjs"),
148
- "../../translations/it.json": () => import("./it-C7QEBNFA.mjs"),
149
- "../../translations/ja.json": () => import("./ja-THS6AOSJ.mjs"),
150
- "../../translations/ko.json": () => import("./ko-XKK3TWQG.mjs"),
151
- "../../translations/ms.json": () => import("./ms-GSK7LIF7.mjs"),
152
- "../../translations/nl.json": () => import("./nl-KUBWITGY.mjs"),
153
- "../../translations/no.json": () => import("./no-ECWZUHT6.mjs"),
154
- "../../translations/pl.json": () => import("./pl-PLVWSZWS.mjs"),
155
- "../../translations/pt.json": () => import("./pt-AL74ZTKB.mjs"),
156
- "../../translations/ro.json": () => import("./ro-WTPHLHGS.mjs"),
157
- "../../translations/sv.json": () => import("./sv-QCLI7SG4.mjs"),
158
- "../../translations/th.json": () => import("./th-WCKVZU6U.mjs"),
159
- "../../translations/tr.json": () => import("./tr-2CAFS2XS.mjs"),
160
- "../../translations/uk.json": () => import("./uk-TDE4JLCY.mjs"),
161
- "../../translations/vi.json": () => import("./vi-KKXZ4PCX.mjs"),
162
- "../../translations/zh.json": () => import("./zh-VH4XN5PV.mjs")
163
- });
164
-
165
- // src/contexts/TranslationContext.tsx
166
- var TranslationContext = createContext3(null);
167
- var translationCache = /* @__PURE__ */ new Map();
168
- function processPluralFormat(text, params) {
169
- const pluralMatch = text.match(/\{(\w+),\s*plural,\s*/);
170
- if (!pluralMatch) {
171
- return text;
172
- }
173
- const paramName = pluralMatch[1];
174
- const count = params[paramName];
175
- if (count === void 0) {
176
- return text;
177
- }
178
- let startPos = pluralMatch[0].length;
179
- let braceCount = 1;
180
- let endPos = startPos;
181
- for (let i = startPos; i < text.length; i++) {
182
- if (text[i] === "{") braceCount++;
183
- else if (text[i] === "}") {
184
- braceCount--;
185
- if (braceCount === 0) {
186
- endPos = i;
187
- break;
188
- }
189
- }
190
- }
191
- const pluralCases = text.substring(startPos, endPos);
192
- const cases = {};
193
- const caseRegex = /(?:=(\d+)|(\w+))\s*\{([^}]+)\}/g;
194
- let caseMatch;
195
- while ((caseMatch = caseRegex.exec(pluralCases)) !== null) {
196
- const [, exactNumber, keyword, textContent] = caseMatch;
197
- const key = exactNumber !== void 0 ? `=${exactNumber}` : keyword;
198
- cases[key] = textContent;
199
- }
200
- const exactMatch = cases[`=${count}`];
201
- if (exactMatch !== void 0) {
202
- const result = exactMatch.replace(/#/g, String(count));
203
- return text.substring(0, pluralMatch.index) + result + text.substring(endPos + 1);
204
- }
205
- const otherCase = cases["other"];
206
- if (otherCase !== void 0) {
207
- const result = otherCase.replace(/#/g, String(count));
208
- return text.substring(0, pluralMatch.index) + result + text.substring(endPos + 1);
209
- }
210
- return text;
211
- }
212
- var AVAILABLE_LOCALES = [
213
- "ar",
214
- // Arabic
215
- "bn",
216
- // Bengali
217
- "cs",
218
- // Czech
219
- "da",
220
- // Danish
221
- "de",
222
- // German
223
- "el",
224
- // Greek
225
- "en",
226
- // English
227
- "es",
228
- // Spanish
229
- "fa",
230
- // Persian/Farsi
231
- "fi",
232
- // Finnish
233
- "fr",
234
- // French
235
- "he",
236
- // Hebrew
237
- "hi",
238
- // Hindi
239
- "id",
240
- // Indonesian
241
- "it",
242
- // Italian
243
- "ja",
244
- // Japanese
245
- "ko",
246
- // Korean
247
- "ms",
248
- // Malay
249
- "nl",
250
- // Dutch
251
- "no",
252
- // Norwegian
253
- "pl",
254
- // Polish
255
- "pt",
256
- // Portuguese
257
- "ro",
258
- // Romanian
259
- "sv",
260
- // Swedish
261
- "th",
262
- // Thai
263
- "tr",
264
- // Turkish
265
- "uk",
266
- // Ukrainian
267
- "vi",
268
- // Vietnamese
269
- "zh"
270
- // Chinese
271
- ];
272
- async function loadTranslations(locale) {
273
- if (translationCache.has(locale)) {
274
- return translationCache.get(locale);
275
- }
276
- if (locale === "en") {
277
- translationCache.set("en", en_default);
278
- return en_default;
279
- }
280
- try {
281
- const translations = await globImport_translations_json(`../../translations/${locale}.json`);
282
- const translationData = translations.default || translations;
283
- translationCache.set(locale, translationData);
284
- return translationData;
285
- } catch (error) {
286
- console.error(`Failed to load translations for locale: ${locale}`, error);
287
- return en_default;
288
- }
289
- }
290
- var defaultTranslationManager = {
291
- t: (namespace, key, params) => {
292
- const translations = en_default;
293
- const translation = translations[namespace]?.[key];
294
- if (!translation) {
295
- console.warn(`Translation not found for ${namespace}.${key}`);
296
- return `${namespace}.${key}`;
297
- }
298
- if (params && typeof translation === "string") {
299
- let result = translation;
300
- result = processPluralFormat(result, params);
301
- Object.entries(params).forEach(([paramKey, paramValue]) => {
302
- result = result.replace(new RegExp(`\\{${paramKey}\\}`, "g"), String(paramValue));
303
- });
304
- return result;
305
- }
306
- return translation;
307
- }
308
- };
309
- function TranslationProvider({
310
- translationManager,
311
- locale,
312
- loadingComponent = null,
313
- children
314
- }) {
315
- const [loadedTranslations, setLoadedTranslations] = useState2(null);
316
- const [isLoading, setIsLoading] = useState2(false);
317
- useEffect2(() => {
318
- if (locale && !translationManager) {
319
- setIsLoading(true);
320
- loadTranslations(locale).then((translations) => {
321
- setLoadedTranslations(translations);
322
- setIsLoading(false);
323
- }).catch((error) => {
324
- console.error("Failed to load translations:", error);
325
- setLoadedTranslations(en_default);
326
- setIsLoading(false);
327
- });
328
- }
329
- }, [locale, translationManager]);
330
- const localeManager = useMemo(() => {
331
- if (!loadedTranslations) return null;
332
- return {
333
- t: (namespace, key, params) => {
334
- const translation = loadedTranslations[namespace]?.[key];
335
- if (!translation) {
336
- console.warn(`Translation not found for ${namespace}.${key} in locale ${locale}`);
337
- return `${namespace}.${key}`;
338
- }
339
- if (params && typeof translation === "string") {
340
- let result = translation;
341
- result = processPluralFormat(result, params);
342
- Object.entries(params).forEach(([paramKey, paramValue]) => {
343
- result = result.replace(new RegExp(`\\{${paramKey}\\}`, "g"), String(paramValue));
344
- });
345
- return result;
346
- }
347
- return translation;
348
- }
349
- };
350
- }, [loadedTranslations, locale]);
351
- if (translationManager) {
352
- return /* @__PURE__ */ jsx4(TranslationContext.Provider, { value: translationManager, children });
353
- }
354
- if (locale && isLoading) {
355
- return /* @__PURE__ */ jsx4(Fragment, { children: loadingComponent });
356
- }
357
- if (locale && localeManager) {
358
- return /* @__PURE__ */ jsx4(TranslationContext.Provider, { value: localeManager, children });
359
- }
360
- return /* @__PURE__ */ jsx4(TranslationContext.Provider, { value: defaultTranslationManager, children });
361
- }
362
- function useTranslations(namespace) {
363
- const context = useContext3(TranslationContext);
364
- if (!context) {
365
- return (key, params) => {
366
- const translations = en_default;
367
- const translation = translations[namespace]?.[key];
368
- if (!translation) {
369
- console.warn(`Translation not found for ${namespace}.${key}`);
370
- return `${namespace}.${key}`;
371
- }
372
- if (params && typeof translation === "string") {
373
- let result = translation;
374
- result = processPluralFormat(result, params);
375
- Object.entries(params).forEach(([paramKey, paramValue]) => {
376
- result = result.replace(new RegExp(`\\{${paramKey}\\}`, "g"), String(paramValue));
377
- });
378
- return result;
379
- }
380
- return translation;
381
- };
382
- }
383
- return (key, params) => context.t(namespace, key, params);
384
- }
385
- function usePreloadTranslations() {
386
- return {
387
- preload: async (locale) => {
388
- try {
389
- await loadTranslations(locale);
390
- return true;
391
- } catch (error) {
392
- console.error(`Failed to preload translations for ${locale}:`, error);
393
- return false;
394
- }
395
- },
396
- isLoaded: (locale) => translationCache.has(locale)
397
- };
398
- }
399
-
400
- export {
401
- SessionProvider,
402
- useSessionContext,
403
- ToastContainer,
404
- ToastProvider,
405
- useToast,
406
- OpenResourcesProvider,
407
- useOpenResources,
408
- AVAILABLE_LOCALES,
409
- TranslationProvider,
410
- useTranslations,
411
- usePreloadTranslations
412
- };
413
- //# sourceMappingURL=chunk-OL5UST25.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/contexts/SessionContext.tsx","../src/components/Toast.tsx","../src/contexts/OpenResourcesContext.tsx","../src/contexts/TranslationContext.tsx"],"sourcesContent":["'use client';\n\nimport { createContext, useContext, ReactNode } from 'react';\nimport type { SessionManager } from '../types/SessionManager';\n\nconst SessionContext = createContext<SessionManager | null>(null);\n\n/**\n * Provider Pattern: Accepts SessionManager implementation as prop\n * and makes it available to child components via Context.\n *\n * Apps provide their own implementation (next-auth, custom auth, etc.)\n * and pass it to this provider at the root level.\n *\n * @example\n * ```tsx\n * // In app root\n * const sessionManager = useSessionManager(); // App's implementation\n *\n * <SessionProvider sessionManager={sessionManager}>\n * <App />\n * </SessionProvider>\n * ```\n */\nexport function SessionProvider({\n sessionManager,\n children\n}: {\n sessionManager: SessionManager;\n children: ReactNode;\n}) {\n return (\n <SessionContext.Provider value={sessionManager}>\n {children}\n </SessionContext.Provider>\n );\n}\n\n/**\n * Hook to access SessionManager from Context\n * Components use this hook to access session state and expiry information\n */\nexport function useSessionContext() {\n const context = useContext(SessionContext);\n if (!context) {\n throw new Error('useSessionContext must be used within SessionProvider');\n }\n return context;\n}","'use client';\n\nimport React, { useEffect, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport './Toast.css';\n\nexport type ToastType = 'success' | 'error' | 'info' | 'warning';\n\nexport interface ToastMessage {\n id: string;\n message: string;\n type: ToastType;\n duration?: number;\n}\n\ninterface ToastProps {\n toast: ToastMessage;\n onClose: (id: string) => void;\n}\n\nconst icons = {\n success: (\n <svg className=\"semiont-toast-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M5 13l4 4L19 7\" />\n </svg>\n ),\n error: (\n <svg className=\"semiont-toast-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n ),\n warning: (\n <svg className=\"semiont-toast-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <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\" />\n </svg>\n ),\n info: (\n <svg className=\"semiont-toast-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <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\" />\n </svg>\n ),\n};\n\nfunction Toast({ toast, onClose }: ToastProps) {\n useEffect(() => {\n const timer = setTimeout(() => {\n onClose(toast.id);\n }, toast.duration || 3000);\n\n return () => clearTimeout(timer);\n }, [toast, onClose]);\n\n return (\n <div\n className=\"semiont-toast\"\n data-variant={toast.type}\n role=\"alert\"\n >\n <div className=\"semiont-toast-icon-wrapper\">{icons[toast.type]}</div>\n <p className=\"semiont-toast-message\">{toast.message}</p>\n <button\n onClick={() => onClose(toast.id)}\n className=\"semiont-toast-close\"\n aria-label=\"Close\"\n >\n <svg className=\"semiont-toast-close-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n );\n}\n\ninterface ToastContainerProps {\n toasts: ToastMessage[];\n onClose: (id: string) => void;\n}\n\nexport function ToastContainer({ toasts, onClose }: ToastContainerProps) {\n const [mounted, setMounted] = useState(false);\n\n useEffect(() => {\n setMounted(true);\n return () => setMounted(false);\n }, []);\n\n if (!mounted) return null;\n\n return createPortal(\n <div className=\"semiont-toast-container\">\n {toasts.map((toast) => (\n <Toast key={toast.id} toast={toast} onClose={onClose} />\n ))}\n </div>,\n document.body\n );\n}\n\n// Toast context and hook for global toast management\ninterface ToastContextType {\n showToast: (message: string, type?: ToastType, duration?: number) => void;\n showSuccess: (message: string, duration?: number) => void;\n showError: (message: string, duration?: number) => void;\n showWarning: (message: string, duration?: number) => void;\n showInfo: (message: string, duration?: number) => void;\n}\n\nconst ToastContext = React.createContext<ToastContextType | undefined>(undefined);\n\nexport function ToastProvider({ children }: { children: React.ReactNode }) {\n const [toasts, setToasts] = useState<ToastMessage[]>([]);\n\n const showToast = React.useCallback((message: string, type: ToastType = 'info', duration?: number) => {\n const id = Date.now().toString();\n const newToast: ToastMessage = duration !== undefined\n ? { id, message, type, duration }\n : { id, message, type };\n setToasts((prev) => [...prev, newToast]);\n }, []);\n\n const showSuccess = React.useCallback((message: string, duration?: number) => showToast(message, 'success', duration), [showToast]);\n const showError = React.useCallback((message: string, duration?: number) => showToast(message, 'error', duration), [showToast]);\n const showWarning = React.useCallback((message: string, duration?: number) => showToast(message, 'warning', duration), [showToast]);\n const showInfo = React.useCallback((message: string, duration?: number) => showToast(message, 'info', duration), [showToast]);\n\n const handleClose = React.useCallback((id: string) => {\n setToasts((prev) => prev.filter((toast) => toast.id !== id));\n }, []);\n\n const contextValue = React.useMemo(\n () => ({ showToast, showSuccess, showError, showWarning, showInfo }),\n [showToast, showSuccess, showError, showWarning, showInfo]\n );\n\n return (\n <ToastContext.Provider value={contextValue}>\n {children}\n <ToastContainer toasts={toasts} onClose={handleClose} />\n </ToastContext.Provider>\n );\n}\n\nexport function useToast() {\n const context = React.useContext(ToastContext);\n if (context === undefined) {\n throw new Error('useToast must be used within a ToastProvider');\n }\n return context;\n}","'use client';\n\nimport React, { createContext, useContext } from 'react';\nimport type { OpenResourcesManager } from '../types/OpenResourcesManager';\n\nconst OpenResourcesContext = createContext<OpenResourcesManager | undefined>(undefined);\n\n/**\n * Provider Pattern: Accepts OpenResourcesManager implementation as prop\n * and makes it available to child components via Context.\n *\n * Apps provide their own implementation (localStorage, sessionStorage, database, etc.)\n * and pass it to this provider at the root level.\n *\n * @example\n * ```tsx\n * // In app root\n * const openResourcesManager = useOpenResourcesManager(); // App's implementation\n *\n * <OpenResourcesProvider openResourcesManager={openResourcesManager}>\n * <App />\n * </OpenResourcesProvider>\n * ```\n */\nexport function OpenResourcesProvider({\n openResourcesManager,\n children\n}: {\n openResourcesManager: OpenResourcesManager;\n children: React.ReactNode;\n}) {\n return (\n <OpenResourcesContext.Provider value={openResourcesManager}>\n {children}\n </OpenResourcesContext.Provider>\n );\n}\n\n/**\n * Hook to access OpenResourcesManager from Context\n * Components use this hook to access open resources functionality\n */\nexport function useOpenResources(): OpenResourcesManager {\n const context = useContext(OpenResourcesContext);\n if (context === undefined) {\n throw new Error('useOpenResources must be used within an OpenResourcesProvider');\n }\n return context;\n}","'use client';\n\nimport { createContext, useContext, ReactNode, useState, useEffect, useMemo } from 'react';\nimport type { TranslationManager } from '../types/TranslationManager';\n\n// Static import for default English only - always needed as fallback\nimport enTranslations from '../../translations/en.json';\n\nconst TranslationContext = createContext<TranslationManager | null>(null);\n\n// Cache for dynamically loaded translations\nconst translationCache = new Map<string, any>();\n\n/**\n * Process ICU MessageFormat plural syntax\n * Supports: {count, plural, =0 {text} =1 {text} other {text}}\n */\nfunction processPluralFormat(text: string, params: Record<string, any>): string {\n // Match {paramName, plural, ...} with proper brace counting\n const pluralMatch = text.match(/\\{(\\w+),\\s*plural,\\s*/);\n if (!pluralMatch) {\n return text;\n }\n\n const paramName = pluralMatch[1];\n const count = params[paramName];\n if (count === undefined) {\n return text;\n }\n\n // Find the matching closing brace by counting\n let startPos = pluralMatch[0].length;\n let braceCount = 1; // We're inside the first {\n let endPos = startPos;\n\n for (let i = startPos; i < text.length; i++) {\n if (text[i] === '{') braceCount++;\n else if (text[i] === '}') {\n braceCount--;\n if (braceCount === 0) {\n endPos = i;\n break;\n }\n }\n }\n\n const pluralCases = text.substring(startPos, endPos);\n\n // Parse plural cases: =0 {text} =1 {text} other {text}\n const cases: Record<string, string> = {};\n const caseRegex = /(?:=(\\d+)|(\\w+))\\s*\\{([^}]+)\\}/g;\n let caseMatch;\n\n while ((caseMatch = caseRegex.exec(pluralCases)) !== null) {\n const [, exactNumber, keyword, textContent] = caseMatch;\n const key = exactNumber !== undefined ? `=${exactNumber}` : keyword;\n cases[key] = textContent;\n }\n\n // Select appropriate case\n const exactMatch = cases[`=${count}`];\n if (exactMatch !== undefined) {\n const result = exactMatch.replace(/#/g, String(count));\n return text.substring(0, pluralMatch.index!) + result + text.substring(endPos + 1);\n }\n\n const otherCase = cases['other'];\n if (otherCase !== undefined) {\n const result = otherCase.replace(/#/g, String(count));\n return text.substring(0, pluralMatch.index!) + result + text.substring(endPos + 1);\n }\n\n return text;\n}\n\n// List of available locales (can be extended without importing all files)\nexport const AVAILABLE_LOCALES = [\n 'ar', // Arabic\n 'bn', // Bengali\n 'cs', // Czech\n 'da', // Danish\n 'de', // German\n 'el', // Greek\n 'en', // English\n 'es', // Spanish\n 'fa', // Persian/Farsi\n 'fi', // Finnish\n 'fr', // French\n 'he', // Hebrew\n 'hi', // Hindi\n 'id', // Indonesian\n 'it', // Italian\n 'ja', // Japanese\n 'ko', // Korean\n 'ms', // Malay\n 'nl', // Dutch\n 'no', // Norwegian\n 'pl', // Polish\n 'pt', // Portuguese\n 'ro', // Romanian\n 'sv', // Swedish\n 'th', // Thai\n 'tr', // Turkish\n 'uk', // Ukrainian\n 'vi', // Vietnamese\n 'zh', // Chinese\n] as const;\nexport type AvailableLocale = typeof AVAILABLE_LOCALES[number];\n\n// Lazy load translations for a specific locale\nasync function loadTranslations(locale: string): Promise<any> {\n // Check cache first\n if (translationCache.has(locale)) {\n return translationCache.get(locale);\n }\n\n // English is already loaded statically\n if (locale === 'en') {\n translationCache.set('en', enTranslations);\n return enTranslations;\n }\n\n try {\n // Dynamic import for all other locales\n const translations = await import(`../../translations/${locale}.json`);\n const translationData = translations.default || translations;\n translationCache.set(locale, translationData);\n return translationData;\n } catch (error) {\n console.error(`Failed to load translations for locale: ${locale}`, error);\n // Fall back to English\n return enTranslations;\n }\n}\n\n// Default English translation manager (using static import)\nconst defaultTranslationManager: TranslationManager = {\n t: (namespace: string, key: string, params?: Record<string, any>) => {\n const translations = enTranslations as Record<string, Record<string, string>>;\n const translation = translations[namespace]?.[key];\n\n if (!translation) {\n console.warn(`Translation not found for ${namespace}.${key}`);\n return `${namespace}.${key}`;\n }\n\n // Handle parameter interpolation and plural format\n if (params && typeof translation === 'string') {\n let result = translation;\n // First process plural format\n result = processPluralFormat(result, params);\n // Then handle simple parameter interpolation\n Object.entries(params).forEach(([paramKey, paramValue]) => {\n result = result.replace(new RegExp(`\\\\{${paramKey}\\\\}`, 'g'), String(paramValue));\n });\n return result;\n }\n\n return translation;\n },\n};\n\nexport interface TranslationProviderProps {\n /**\n * Option 1: Provide a complete TranslationManager implementation\n */\n translationManager?: TranslationManager;\n\n /**\n * Option 2: Use built-in translations by specifying a locale\n * When adding new locales, just add the JSON file and update AVAILABLE_LOCALES\n */\n locale?: string;\n\n /**\n * Loading component to show while translations are being loaded\n * Only relevant when using dynamic locale loading\n */\n loadingComponent?: ReactNode;\n\n children: ReactNode;\n}\n\n/**\n * Provider for translation management with dynamic loading\n *\n * Three modes of operation:\n * 1. No provider: Components use default English strings\n * 2. With locale prop: Dynamically loads translations for that locale\n * 3. With translationManager: Use custom translation implementation\n */\nexport function TranslationProvider({\n translationManager,\n locale,\n loadingComponent = null,\n children,\n}: TranslationProviderProps) {\n const [loadedTranslations, setLoadedTranslations] = useState<any>(null);\n const [isLoading, setIsLoading] = useState(false);\n\n // Load translations when locale changes\n useEffect(() => {\n if (locale && !translationManager) {\n setIsLoading(true);\n loadTranslations(locale)\n .then(translations => {\n setLoadedTranslations(translations);\n setIsLoading(false);\n })\n .catch(error => {\n console.error('Failed to load translations:', error);\n setLoadedTranslations(enTranslations); // Fall back to English\n setIsLoading(false);\n });\n }\n }, [locale, translationManager]);\n\n // Create translation manager from loaded translations\n const localeManager = useMemo<TranslationManager | null>(() => {\n if (!loadedTranslations) return null;\n\n return {\n t: (namespace: string, key: string, params?: Record<string, any>) => {\n const translation = loadedTranslations[namespace]?.[key];\n\n if (!translation) {\n console.warn(`Translation not found for ${namespace}.${key} in locale ${locale}`);\n return `${namespace}.${key}`;\n }\n\n // Handle parameter interpolation and plural format\n if (params && typeof translation === 'string') {\n let result = translation;\n // First process plural format\n result = processPluralFormat(result, params);\n // Then handle simple parameter interpolation\n Object.entries(params).forEach(([paramKey, paramValue]) => {\n result = result.replace(new RegExp(`\\\\{${paramKey}\\\\}`, 'g'), String(paramValue));\n });\n return result;\n }\n\n return translation;\n },\n };\n }, [loadedTranslations, locale]);\n\n // If custom translation manager provided, use it\n if (translationManager) {\n return (\n <TranslationContext.Provider value={translationManager}>\n {children}\n </TranslationContext.Provider>\n );\n }\n\n // If locale provided and still loading, show loading component\n if (locale && isLoading) {\n return <>{loadingComponent}</>;\n }\n\n // If locale provided and translations loaded, use them\n if (locale && localeManager) {\n return (\n <TranslationContext.Provider value={localeManager}>\n {children}\n </TranslationContext.Provider>\n );\n }\n\n // Default: use English translations\n return (\n <TranslationContext.Provider value={defaultTranslationManager}>\n {children}\n </TranslationContext.Provider>\n );\n}\n\n/**\n * Hook to access translations within a namespace\n *\n * Works in three modes:\n * 1. Without provider: Returns default English translations\n * 2. With provider using locale: Returns dynamically loaded translations for that locale\n * 3. With custom provider: Uses the custom translation manager\n *\n * @param namespace - Translation namespace (e.g., 'Toolbar', 'ResourceViewer')\n * @returns Function to translate keys within the namespace\n */\nexport function useTranslations(namespace: string) {\n const context = useContext(TranslationContext);\n\n // If no context (no provider), use default English translations\n if (!context) {\n return (key: string, params?: Record<string, any>) => {\n const translations = enTranslations as Record<string, Record<string, string>>;\n const translation = translations[namespace]?.[key];\n\n if (!translation) {\n console.warn(`Translation not found for ${namespace}.${key}`);\n return `${namespace}.${key}`;\n }\n\n // Handle parameter interpolation and plural format\n if (params && typeof translation === 'string') {\n let result = translation;\n // First process plural format\n result = processPluralFormat(result, params);\n // Then handle simple parameter interpolation\n Object.entries(params).forEach(([paramKey, paramValue]) => {\n result = result.replace(new RegExp(`\\\\{${paramKey}\\\\}`, 'g'), String(paramValue));\n });\n return result;\n }\n\n return translation;\n };\n }\n\n // Return a function that translates keys within this namespace\n return (key: string, params?: Record<string, any>) => context.t(namespace, key, params);\n}\n\n/**\n * Hook to preload translations for a locale\n * Useful for preloading translations before navigation\n */\nexport function usePreloadTranslations() {\n return {\n preload: async (locale: string) => {\n try {\n await loadTranslations(locale);\n return true;\n } catch (error) {\n console.error(`Failed to preload translations for ${locale}:`, error);\n return false;\n }\n },\n isLoaded: (locale: string) => translationCache.has(locale),\n };\n}"],"mappings":"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","names":["jsx","createContext","useContext","jsx","createContext","useContext","useState","useEffect","jsx","createContext","useState","useEffect","jsx","useContext"]}