@semiont/react-ui 0.4.17 → 0.4.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-FAI3S4BM.mjs → chunk-R4CCMFJH.mjs} +14 -2
- package/dist/chunk-R4CCMFJH.mjs.map +1 -0
- package/dist/index.d.mts +45 -15
- package/dist/index.mjs +10299 -359
- package/dist/index.mjs.map +1 -1
- package/dist/test-utils.mjs +1 -1
- package/package.json +1 -1
- package/src/components/modals/ResourceSearchModal.tsx +69 -57
- package/src/components/modals/SearchModal.tsx +54 -51
- package/src/components/modals/__tests__/ResourceSearchModal.test.tsx +59 -82
- package/src/components/modals/__tests__/SearchModal.search-wiring.test.tsx +221 -0
- package/src/components/resource/event-formatting.ts +0 -6
- package/src/components/resource/panels/ReferenceEntry.tsx +1 -1
- package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +1 -1
- package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +46 -45
- package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +23 -29
- package/dist/chunk-FAI3S4BM.mjs.map +0 -1
|
@@ -87,8 +87,19 @@ function saveKnowledgeBases(knowledgeBases) {
|
|
|
87
87
|
function defaultProtocol(host) {
|
|
88
88
|
return host === "localhost" || host === "127.0.0.1" ? "http" : "https";
|
|
89
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
|
+
}
|
|
90
94
|
function kbBackendUrl(kb) {
|
|
91
|
-
|
|
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}`;
|
|
92
103
|
}
|
|
93
104
|
function getKbSessionStatus(kbId) {
|
|
94
105
|
const stored = getStoredSession(kbId);
|
|
@@ -845,6 +856,7 @@ function usePreloadTranslations() {
|
|
|
845
856
|
|
|
846
857
|
export {
|
|
847
858
|
defaultProtocol,
|
|
859
|
+
isValidHostname,
|
|
848
860
|
kbBackendUrl,
|
|
849
861
|
getKbSessionStatus,
|
|
850
862
|
notifySessionExpired,
|
|
@@ -862,4 +874,4 @@ export {
|
|
|
862
874
|
useTranslations,
|
|
863
875
|
usePreloadTranslations
|
|
864
876
|
};
|
|
865
|
-
//# sourceMappingURL=chunk-
|
|
877
|
+
//# sourceMappingURL=chunk-R4CCMFJH.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/contexts/knowledge-base-session/storage.ts","../src/contexts/knowledge-base-session/notify.ts","../src/contexts/KnowledgeBaseSessionContext.tsx","../src/contexts/knowledge-base-session/refresh.ts","../src/components/Toast.tsx","../src/contexts/OpenResourcesContext.tsx","../src/contexts/TranslationContext.tsx"],"sourcesContent":["/**\n * Pure helpers for the KnowledgeBaseSession provider.\n *\n * Contains:\n * - localStorage shape and read/write helpers for KB list, active KB id,\n * and per-KB sessions\n * - JWT expiry parsing and \"is expired\" check\n * - URL/protocol helpers for KB instances\n * - The public `getKbSessionStatus(kbId)` helper that the KB-list UI uses\n * to color status dots without subscribing to context changes\n *\n * No React imports, no module-scoped state, no side effects beyond\n * localStorage. Splitting these out of the provider file makes them\n * unit-testable in isolation and keeps the React provider focused on\n * lifecycle and state.\n */\n\nimport type { KnowledgeBase, KbSessionStatus } from '../../types/knowledge-base';\n\n// ---------- Storage keys ----------\n\nconst SESSION_PREFIX = 'semiont.session.';\nexport const STORAGE_KEY = 'semiont.knowledgeBases';\nexport const ACTIVE_KEY = 'semiont.activeKnowledgeBaseId';\n\n/** Refresh the access token this many milliseconds before it expires. */\nexport const REFRESH_BEFORE_EXP_MS = 5 * 60 * 1000;\n\n/** The shape persisted to localStorage per KB. */\nexport interface StoredSession {\n access: string;\n refresh: string;\n}\n\nexport function sessionKey(kbId: string): string {\n return `${SESSION_PREFIX}${kbId}`;\n}\n\n// ---------- Per-KB session storage ----------\n\nexport function getStoredSession(kbId: string): StoredSession | null {\n const raw = localStorage.getItem(sessionKey(kbId));\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed.access === 'string' && typeof parsed.refresh === 'string') {\n return { access: parsed.access, refresh: parsed.refresh };\n }\n } catch {\n // malformed entry — treat as no session\n }\n return null;\n}\n\nexport function setStoredSession(kbId: string, session: StoredSession): void {\n localStorage.setItem(sessionKey(kbId), JSON.stringify(session));\n}\n\nexport function clearStoredSession(kbId: string): void {\n localStorage.removeItem(sessionKey(kbId));\n}\n\n// ---------- JWT helpers ----------\n\nexport function parseJwtExpiry(token: string): Date | null {\n try {\n const parts = token.split('.');\n if (parts.length !== 3 || !parts[1]) return null;\n const payload = JSON.parse(atob(parts[1])) as { exp?: number };\n if (!payload.exp) return null;\n return new Date(payload.exp * 1000);\n } catch {\n return null;\n }\n}\n\nexport function isJwtExpired(token: string): boolean {\n const expiry = parseJwtExpiry(token);\n if (!expiry) return true;\n return expiry.getTime() < Date.now();\n}\n\n// ---------- KB list storage ----------\n\nfunction migrateLegacyEntry(entry: any): KnowledgeBase {\n if (entry.host !== undefined) return entry as KnowledgeBase;\n // Legacy format: { id, label, backendUrl }\n try {\n const url = new URL(entry.backendUrl);\n return {\n id: entry.id,\n label: entry.label,\n host: url.hostname,\n port: parseInt(url.port, 10) || (url.protocol === 'https:' ? 443 : 80),\n protocol: url.protocol === 'https:' ? 'https' : 'http',\n email: '',\n };\n } catch {\n return {\n id: entry.id,\n label: entry.label || 'Unknown',\n host: 'localhost',\n port: 4000,\n protocol: 'http',\n email: '',\n };\n }\n}\n\nexport function loadKnowledgeBases(): KnowledgeBase[] {\n try {\n const raw = localStorage.getItem(STORAGE_KEY);\n if (!raw) return [];\n const entries = JSON.parse(raw) as any[];\n return entries.map(migrateLegacyEntry);\n } catch {\n return [];\n }\n}\n\nexport function saveKnowledgeBases(knowledgeBases: KnowledgeBase[]): void {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(knowledgeBases));\n}\n\n// ---------- Public pure helpers ----------\n\nexport function defaultProtocol(host: string): 'http' | 'https' {\n return host === 'localhost' || host === '127.0.0.1' ? 'http' : 'https';\n}\n\n/** Accepts: localhost, dotted-decimal IPv4, valid DNS labels. Rejects slashes, colons, query strings. */\nconst 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})$/;\n\nexport function isValidHostname(host: string): boolean {\n return HOSTNAME_RE.test(host);\n}\n\nexport function kbBackendUrl(kb: KnowledgeBase): string {\n if (!isValidHostname(kb.host)) {\n throw new Error(`Invalid KB hostname: \"${kb.host}\"`);\n }\n // Use URL property assignment so the parser normalises the hostname (e.g. lowercasing)\n // rather than blindly interpolating a user-supplied string.\n const url = new URL('http://x');\n url.protocol = kb.protocol + ':';\n url.hostname = kb.host;\n url.port = String(kb.port);\n return `${kb.protocol}://${url.hostname}:${kb.port}`;\n}\n\n/**\n * Read the locally-stored credential status for a KB. Pure / synchronous —\n * does not subscribe to context changes. Used by KB-list UI to color status\n * dots without requiring re-renders on every tick.\n */\nexport function getKbSessionStatus(kbId: string): KbSessionStatus {\n const stored = getStoredSession(kbId);\n if (!stored) return 'signed-out';\n return isJwtExpired(stored.access) ? 'expired' : 'authenticated';\n}\n\nexport function generateKbId(): string {\n return crypto.randomUUID();\n}\n","/**\n * Module-scoped session-expired / permission-denied notifier.\n *\n * The provider registers itself with this module-scoped slot on mount and\n * unregisters on unmount via {@link registerAuthNotifyHandlers}. Code outside\n * the React tree (notably the React Query QueryCache.onError handler in app\n * providers) calls {@link notifySessionExpired} or {@link notifyPermissionDenied}\n * to reach the active provider.\n *\n * When no provider is mounted (e.g. on the landing page), these calls are\n * no-ops — there is nothing to notify.\n *\n * No React imports — this is plain module state. The provider effect that\n * calls `registerAuthNotifyHandlers` runs inside React but the module itself\n * is React-agnostic.\n */\n\ntype Notify = (message?: string) => void;\n\nlet activeOnSessionExpired: Notify | null = null;\nlet activeOnPermissionDenied: Notify | null = null;\n\nexport function notifySessionExpired(message?: string): void {\n activeOnSessionExpired?.(message);\n}\n\nexport function notifyPermissionDenied(message?: string): void {\n activeOnPermissionDenied?.(message);\n}\n\n/**\n * Install handlers for session-expired and permission-denied notifications.\n * Returns an unregister callback. Intended to be called from a React useEffect\n * with the cleanup callback returned from the effect.\n *\n * Only one provider is expected to be mounted at a time. If a second provider\n * mounts before the first unmounts, its handlers replace the previous ones —\n * the previous provider becomes deaf to notifications. In practice this only\n * happens during the brief window of a React StrictMode double-mount or a\n * test that mounts and unmounts multiple providers rapidly.\n */\nexport function registerAuthNotifyHandlers(handlers: {\n onSessionExpired: Notify;\n onPermissionDenied: Notify;\n}): () => void {\n activeOnSessionExpired = handlers.onSessionExpired;\n activeOnPermissionDenied = handlers.onPermissionDenied;\n return () => {\n activeOnSessionExpired = null;\n activeOnPermissionDenied = null;\n };\n}\n","/**\n * KnowledgeBaseSessionContext — single source of truth for \"which KB and\n * what's the session against it.\"\n *\n * This provider merges what used to be three separate concerns in the\n * frontend (KnowledgeBaseProvider + KnowledgeBaseAuthBridge + AuthProvider)\n * plus the library-side SessionProvider, into one coherent unit.\n *\n * Why merged: a session in this app is always a session against a specific\n * KB. There is no auth without a KB. Switching KBs means switching sessions\n * atomically.\n *\n * What it owns:\n * - The list of configured KBs (persisted to localStorage)\n * - Which KB is currently active (persisted to localStorage)\n * - The validated session (token + user) for the active KB\n * - The \"session expired\" and \"permission denied\" flags that drive the modals\n * - JWT expiry derivations (for the session-timer UI)\n * - Mount-time validation flow with manual 401 recovery\n * - Proactive refresh: a timer that fires before the access token expires\n * - Cross-tab sync: when another tab refreshes or signs out, this tab updates\n *\n * Implementation is split across the `knowledge-base-session/` directory:\n * - `storage.ts` — localStorage shape, JWT helpers, KB list helpers\n * - `refresh.ts` — `performRefresh` and the in-flight Promise dedup map\n * - `notify.ts` — module-scoped notify functions and the register helper\n *\n * Mounting: must be inside `EventBusProvider` and `TranslationProvider` (it\n * uses neither, but the modals it sits next to do). It does NOT depend on\n * any other library context. Mount it inside the protected layout boundary.\n */\n\nimport React, {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { SemiontApiClient, APIError } from '@semiont/api-client';\nimport { baseUrl, EventBus, accessToken } from '@semiont/core';\nimport type { components } from '@semiont/core';\nimport type {\n KnowledgeBase,\n NewKnowledgeBase,\n} from '../types/knowledge-base';\nimport {\n ACTIVE_KEY,\n REFRESH_BEFORE_EXP_MS,\n clearStoredSession,\n generateKbId,\n getStoredSession,\n isJwtExpired,\n kbBackendUrl,\n loadKnowledgeBases,\n parseJwtExpiry,\n saveKnowledgeBases,\n sessionKey,\n setStoredSession,\n} from './knowledge-base-session/storage';\nimport { performRefresh } from './knowledge-base-session/refresh';\nimport { registerAuthNotifyHandlers } from './knowledge-base-session/notify';\nimport type { StoredSession } from './knowledge-base-session/storage';\n\ntype UserInfo = components['schemas']['UserResponse'];\n\nexport interface AuthSession {\n token: string;\n user: UserInfo;\n}\n\n// Re-export the public surface so consumers can keep importing from this module\nexport {\n defaultProtocol,\n isValidHostname,\n kbBackendUrl,\n getKbSessionStatus,\n} from './knowledge-base-session/storage';\nexport type { StoredSession } from './knowledge-base-session/storage';\nexport {\n notifySessionExpired,\n notifyPermissionDenied,\n} from './knowledge-base-session/notify';\n\n// ---------- Context value ----------\n\ninterface KnowledgeBaseSessionValue {\n // KB list\n knowledgeBases: KnowledgeBase[];\n activeKnowledgeBase: KnowledgeBase | null;\n\n // Session state for the active KB\n session: AuthSession | null;\n isLoading: boolean;\n\n // Derived auth fields (memoized off `session.user`)\n user: UserInfo | null;\n token: string | null;\n isAuthenticated: boolean;\n hasValidBackendToken: boolean;\n isFullyAuthenticated: boolean;\n displayName: string;\n avatarUrl: string | null;\n userDomain: string | undefined;\n isAdmin: boolean;\n isModerator: boolean;\n\n // JWT expiry (derived from session.token)\n expiresAt: Date | null;\n\n // Modal-driving flags\n sessionExpiredAt: number | null;\n sessionExpiredMessage: string | null;\n permissionDeniedAt: number | null;\n permissionDeniedMessage: string | null;\n\n // Mutations\n addKnowledgeBase: (kb: NewKnowledgeBase, access: string, refresh: string) => KnowledgeBase;\n removeKnowledgeBase: (id: string) => void;\n setActiveKnowledgeBase: (id: string) => void;\n updateKnowledgeBase: (id: string, updates: Partial<KnowledgeBase>) => void;\n /** Re-auth on an existing KB: store the new tokens and refresh the session. */\n signIn: (id: string, access: string, refresh: string) => void;\n /** Sign out of a KB: clear its stored tokens. If it's the active KB, clear in-memory session too. */\n signOut: (id: string) => void;\n\n /**\n * Refresh the active KB's access token. Returns the new access token, or\n * null if no refresh token is available or the refresh failed. Concurrent\n * calls deduplicate via an in-flight Promise per KB. Used by the api-client's\n * 401-recovery hook and by the proactive refresh timer.\n */\n refreshActive: () => Promise<string | null>;\n\n // Modal acks\n acknowledgeSessionExpired: () => void;\n acknowledgePermissionDenied: () => void;\n}\n\n/**\n * Raw context export. Exposed for test utilities that need to construct\n * a mock provider without going through localStorage and JWT validation.\n * Production code should always use {@link useKnowledgeBaseSession} instead.\n */\nexport const KnowledgeBaseSessionContext = createContext<KnowledgeBaseSessionValue | undefined>(undefined);\n\nexport type { KnowledgeBaseSessionValue };\n\n// ---------- Provider ----------\n\nexport function KnowledgeBaseSessionProvider({ children }: { children: React.ReactNode }) {\n // KB list and active selection\n const [knowledgeBases, setKnowledgeBases] = useState<KnowledgeBase[]>(() => loadKnowledgeBases());\n const [activeKnowledgeBaseId, setActiveKnowledgeBaseId] = useState<string | null>(() => {\n const saved = localStorage.getItem(ACTIVE_KEY);\n const loaded = loadKnowledgeBases();\n if (saved && loaded.some(kb => kb.id === saved)) return saved;\n return loaded[0]?.id ?? null;\n });\n\n // Session state for the active KB\n const [session, setSession] = useState<AuthSession | null>(null);\n const [isLoading, setIsLoading] = useState<boolean>(() => {\n const id = activeKnowledgeBaseId;\n if (!id) return false;\n const stored = getStoredSession(id);\n if (!stored) return false;\n // We'll either validate (if access fresh) or refresh (if refresh available)\n return !isJwtExpired(stored.access) || stored.refresh != null;\n });\n\n // Modal flags\n const [sessionExpiredAt, setSessionExpiredAt] = useState<number | null>(null);\n const [sessionExpiredMessage, setSessionExpiredMessage] = useState<string | null>(null);\n const [permissionDeniedAt, setPermissionDeniedAt] = useState<number | null>(null);\n const [permissionDeniedMessage, setPermissionDeniedMessage] = useState<string | null>(null);\n\n // Persist KB list and active id\n useEffect(() => {\n saveKnowledgeBases(knowledgeBases);\n }, [knowledgeBases]);\n\n useEffect(() => {\n if (activeKnowledgeBaseId) {\n localStorage.setItem(ACTIVE_KEY, activeKnowledgeBaseId);\n } else {\n localStorage.removeItem(ACTIVE_KEY);\n }\n }, [activeKnowledgeBaseId]);\n\n const activeKnowledgeBase = useMemo(\n () => knowledgeBases.find(kb => kb.id === activeKnowledgeBaseId) ?? null,\n [knowledgeBases, activeKnowledgeBaseId]\n );\n\n // Refs for cross-effect coordination\n const activeKbRef = useRef<KnowledgeBase | null>(activeKnowledgeBase);\n useEffect(() => {\n activeKbRef.current = activeKnowledgeBase;\n }, [activeKnowledgeBase]);\n\n const proactiveRefreshTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n /**\n * Schedule a one-shot timer that fires `REFRESH_BEFORE_EXP_MS` before the\n * given access token expires. Cancels any prior pending timer.\n */\n const scheduleProactiveRefresh = useCallback((accessTokenStr: string) => {\n if (proactiveRefreshTimerRef.current) {\n clearTimeout(proactiveRefreshTimerRef.current);\n proactiveRefreshTimerRef.current = null;\n }\n const expiresAt = parseJwtExpiry(accessTokenStr);\n if (!expiresAt) return;\n const refreshAt = expiresAt.getTime() - REFRESH_BEFORE_EXP_MS;\n const delay = Math.max(0, refreshAt - Date.now());\n proactiveRefreshTimerRef.current = setTimeout(() => {\n proactiveRefreshTimerRef.current = null;\n // Fire-and-forget: refreshActive captures activeKbRef and updates state\n refreshActiveRef.current?.();\n }, delay);\n }, []);\n\n // refreshActive needs to be stable across renders for the api-client wiring\n // and also needs to read fresh activeKnowledgeBase via the ref. We define\n // the function via a ref so callers can capture a stable reference.\n const refreshActiveRef = useRef<(() => Promise<string | null>) | null>(null);\n const refreshActive = useCallback(async (): Promise<string | null> => {\n const kb = activeKbRef.current;\n if (!kb) return null;\n const newAccess = await performRefresh(kb);\n if (newAccess) {\n // Update the in-memory session token so consumers see the new value\n setSession(prev => (prev ? { ...prev, token: newAccess } : prev));\n scheduleProactiveRefresh(newAccess);\n } else {\n // Refresh failed — surface the modal\n setSession(null);\n clearStoredSession(kb.id);\n setSessionExpiredMessage('Your session has expired. Please sign in again.');\n setSessionExpiredAt(Date.now());\n if (proactiveRefreshTimerRef.current) {\n clearTimeout(proactiveRefreshTimerRef.current);\n proactiveRefreshTimerRef.current = null;\n }\n }\n return newAccess;\n }, [scheduleProactiveRefresh]);\n refreshActiveRef.current = refreshActive;\n\n // Mount-time validation. This is the only 401-handling path that does NOT\n // go through the api-client's `tokenRefresher` hook. Two structural reasons:\n //\n // 1. ApiClientProvider hasn't mounted yet — the protected layout mounts\n // ApiClientProvider as a CHILD of this provider, so at validation time\n // the configured api-client (the one with `tokenRefresher`) doesn't\n // exist yet.\n //\n // 2. Even if it did, having the api-client silently recover would mean\n // this effect would never see the 401. But this effect is what BUILDS\n // the session — it needs to know whether validation succeeded so it\n // can either set `session = { token, user }` or surface the modal.\n //\n // So this effect uses a fresh throwaway api-client (no refresher) and\n // handles 401 manually: try one refresh, retry getMe with the new token,\n // surface the modal only if both fail. The duplication with the api-client's\n // beforeRetry hook is structural — do not try to consolidate them.\n useEffect(() => {\n if (!activeKnowledgeBase) {\n setSession(null);\n setIsLoading(false);\n return;\n }\n\n const stored = getStoredSession(activeKnowledgeBase.id);\n if (!stored) {\n setSession(null);\n setIsLoading(false);\n return;\n }\n\n let cancelled = false;\n\n const validate = async (tokenToUse: string) => {\n const client = new SemiontApiClient({\n baseUrl: baseUrl(kbBackendUrl(activeKnowledgeBase)),\n eventBus: new EventBus(),\n });\n try {\n const data = await client.getMe({ auth: accessToken(tokenToUse) });\n if (cancelled) return;\n setSession({ token: tokenToUse, user: data as UserInfo });\n scheduleProactiveRefresh(tokenToUse);\n } catch (error) {\n if (cancelled) return;\n setSession(null);\n if (error instanceof APIError && error.status === 401) {\n // Try one refresh on 401 from getMe before surfacing the modal\n const refreshed = await performRefresh(activeKnowledgeBase);\n if (cancelled) return;\n if (refreshed) {\n return validate(refreshed);\n }\n clearStoredSession(activeKnowledgeBase.id);\n setSessionExpiredMessage('Your session has expired. Please sign in again.');\n setSessionExpiredAt(Date.now());\n }\n } finally {\n if (!cancelled) setIsLoading(false);\n }\n };\n\n setIsLoading(true);\n\n if (isJwtExpired(stored.access)) {\n (async () => {\n const refreshed = await performRefresh(activeKnowledgeBase);\n if (cancelled) return;\n if (refreshed) {\n await validate(refreshed);\n } else {\n setSession(null);\n clearStoredSession(activeKnowledgeBase.id);\n setIsLoading(false);\n }\n })();\n } else {\n validate(stored.access);\n }\n\n return () => {\n cancelled = true;\n };\n }, [activeKnowledgeBase, scheduleProactiveRefresh]);\n\n // Cancel proactive refresh timer on unmount\n useEffect(() => {\n return () => {\n if (proactiveRefreshTimerRef.current) {\n clearTimeout(proactiveRefreshTimerRef.current);\n proactiveRefreshTimerRef.current = null;\n }\n };\n }, []);\n\n // Cross-tab sync: listen for storage events on the active KB's session key\n useEffect(() => {\n if (!activeKnowledgeBaseId) return;\n const watchKey = sessionKey(activeKnowledgeBaseId);\n const handler = (e: StorageEvent) => {\n if (e.key !== watchKey) return;\n if (!e.newValue) {\n // Token was cleared in another tab\n setSession(null);\n if (proactiveRefreshTimerRef.current) {\n clearTimeout(proactiveRefreshTimerRef.current);\n proactiveRefreshTimerRef.current = null;\n }\n return;\n }\n try {\n const parsed = JSON.parse(e.newValue) as StoredSession;\n if (typeof parsed.access === 'string') {\n // Update our in-memory session token (user info is unchanged)\n setSession(prev => (prev ? { ...prev, token: parsed.access } : prev));\n scheduleProactiveRefresh(parsed.access);\n }\n } catch {\n // Ignore malformed payloads\n }\n };\n window.addEventListener('storage', handler);\n return () => window.removeEventListener('storage', handler);\n }, [activeKnowledgeBaseId, scheduleProactiveRefresh]);\n\n // Register module-scoped notify handlers so the QueryCache 401/403 handlers\n // can reach the active provider instance. Returns a cleanup callback that\n // unregisters the handlers when the active KB id changes or the provider\n // unmounts.\n useEffect(() => {\n return registerAuthNotifyHandlers({\n onSessionExpired: (message) => {\n setSessionExpiredMessage(message ?? 'Your session has expired. Please sign in again.');\n setSessionExpiredAt(Date.now());\n setSession(null);\n if (activeKnowledgeBaseId) {\n clearStoredSession(activeKnowledgeBaseId);\n }\n if (proactiveRefreshTimerRef.current) {\n clearTimeout(proactiveRefreshTimerRef.current);\n proactiveRefreshTimerRef.current = null;\n }\n },\n onPermissionDenied: (message) => {\n setPermissionDeniedMessage(message ?? 'You do not have permission to perform this action.');\n setPermissionDeniedAt(Date.now());\n },\n });\n }, [activeKnowledgeBaseId]);\n\n // Mutations\n const addKnowledgeBase = useCallback((input: NewKnowledgeBase, access: string, refresh: string): KnowledgeBase => {\n const kb: KnowledgeBase = { id: generateKbId(), ...input };\n setStoredSession(kb.id, { access, refresh });\n setKnowledgeBases(prev => [...prev, kb]);\n setActiveKnowledgeBaseId(kb.id);\n return kb;\n }, []);\n\n const removeKnowledgeBase = useCallback((id: string) => {\n clearStoredSession(id);\n setKnowledgeBases(prev => {\n const remaining = prev.filter(kb => kb.id !== id);\n setActiveKnowledgeBaseId(activeId => activeId === id ? (remaining[0]?.id ?? null) : activeId);\n return remaining;\n });\n }, []);\n\n const setActiveKnowledgeBase = useCallback((id: string) => {\n setActiveKnowledgeBaseId(id);\n }, []);\n\n const updateKnowledgeBase = useCallback((id: string, updates: Partial<KnowledgeBase>) => {\n setKnowledgeBases(prev => prev.map(kb => kb.id === id ? { ...kb, ...updates } : kb));\n }, []);\n\n const signIn = useCallback((id: string, access: string, refresh: string) => {\n setStoredSession(id, { access, refresh });\n // Replace the matching KB with a fresh object so the activeKnowledgeBase\n // memo's `find()` returns a new reference, the validation effect re-runs,\n // and the new tokens get used.\n setKnowledgeBases(prev => prev.map(kb => kb.id === id ? { ...kb } : kb));\n setActiveKnowledgeBaseId(id);\n }, []);\n\n const signOut = useCallback((id: string) => {\n clearStoredSession(id);\n setActiveKnowledgeBaseId(activeId => {\n if (activeId === id) {\n setSession(null);\n if (proactiveRefreshTimerRef.current) {\n clearTimeout(proactiveRefreshTimerRef.current);\n proactiveRefreshTimerRef.current = null;\n }\n }\n return activeId;\n });\n // Bump the KB list so consumers reading kbStatus(id) see the change\n setKnowledgeBases(prev => [...prev]);\n }, []);\n\n const acknowledgeSessionExpired = useCallback(() => {\n setSessionExpiredAt(null);\n setSessionExpiredMessage(null);\n }, []);\n\n const acknowledgePermissionDenied = useCallback(() => {\n setPermissionDeniedAt(null);\n setPermissionDeniedMessage(null);\n }, []);\n\n // Tick state forces re-derivation of expiresAt-based fields once a minute,\n // so the session-timer UI updates without each consumer running its own interval.\n const [, setTick] = useState(0);\n useEffect(() => {\n const interval = setInterval(() => setTick(t => t + 1), 30_000);\n return () => clearInterval(interval);\n }, []);\n\n // Derived auth fields\n const value = useMemo<KnowledgeBaseSessionValue>(() => {\n const user = session?.user ?? null;\n const token = session?.token ?? null;\n const expiresAt = token ? parseJwtExpiry(token) : null;\n\n return {\n knowledgeBases,\n activeKnowledgeBase,\n session,\n isLoading,\n user,\n token,\n isAuthenticated: !!session,\n hasValidBackendToken: !!token,\n isFullyAuthenticated: !!session,\n displayName: user?.name ?? user?.email?.split('@')[0] ?? 'User',\n avatarUrl: user?.image ?? null,\n userDomain: user?.domain || user?.email?.split('@')[1],\n isAdmin: user?.isAdmin ?? false,\n isModerator: user?.isModerator ?? false,\n expiresAt,\n sessionExpiredAt,\n sessionExpiredMessage,\n permissionDeniedAt,\n permissionDeniedMessage,\n addKnowledgeBase,\n removeKnowledgeBase,\n setActiveKnowledgeBase,\n updateKnowledgeBase,\n signIn,\n signOut,\n refreshActive,\n acknowledgeSessionExpired,\n acknowledgePermissionDenied,\n };\n }, [\n knowledgeBases,\n activeKnowledgeBase,\n session,\n isLoading,\n sessionExpiredAt,\n sessionExpiredMessage,\n permissionDeniedAt,\n permissionDeniedMessage,\n addKnowledgeBase,\n removeKnowledgeBase,\n setActiveKnowledgeBase,\n updateKnowledgeBase,\n signIn,\n signOut,\n refreshActive,\n acknowledgeSessionExpired,\n acknowledgePermissionDenied,\n ]);\n\n return (\n <KnowledgeBaseSessionContext.Provider value={value}>\n {children}\n </KnowledgeBaseSessionContext.Provider>\n );\n}\n\n// ---------- Hook ----------\n\nexport function useKnowledgeBaseSession(): KnowledgeBaseSessionValue {\n const ctx = useContext(KnowledgeBaseSessionContext);\n if (!ctx) {\n throw new Error(\n 'useKnowledgeBaseSession requires KnowledgeBaseSessionProvider. ' +\n 'This component is rendered outside the auth boundary. ' +\n 'Move it into a protected layout.'\n );\n }\n return ctx;\n}\n","/**\n * Refresh-token coordination for the KnowledgeBaseSession provider.\n *\n * Module-scoped state: an in-flight Promise per KB so concurrent 401s for\n * the same KB deduplicate to a single network call. No React, no provider\n * dependency — the React provider calls `performRefresh(kb)` and the\n * api-client's `tokenRefresher` hook indirectly calls it via the provider's\n * `refreshActive` method.\n */\n\nimport { SemiontApiClient } from '@semiont/api-client';\nimport { baseUrl, EventBus, refreshToken as makeRefreshToken } from '@semiont/core';\nimport type { KnowledgeBase } from '../../types/knowledge-base';\nimport { getStoredSession, setStoredSession, kbBackendUrl } from './storage';\n\n/**\n * One in-flight refresh promise per KB. Ensures concurrent 401s for the same\n * KB deduplicate to a single network call.\n */\nconst inFlightRefreshes: Map<string, Promise<string | null>> = new Map();\n\n/**\n * Refresh the active KB's access token. Returns the new access token, or\n * null if no refresh token is available or the refresh failed.\n *\n * IMPORTANT: this constructs a fresh `SemiontApiClient` *without* a\n * `tokenRefresher`. Do not be tempted to reuse the configured client (e.g.\n * via `useApiClient()` from a layout): a refresh-call returning 401 would\n * recursively re-enter the refresher, calling `/api/tokens/refresh` again,\n * in an infinite loop. The throwaway client deliberately has no recovery\n * path — a 401 here propagates as `null` and surfaces the modal upstream.\n *\n * Concurrent calls for the same KB deduplicate via the in-flight Promise\n * Map keyed by `kb.id`, so simultaneous 401s on different requests trigger\n * only one network round-trip to `/api/tokens/refresh`.\n */\nexport async function performRefresh(kb: KnowledgeBase): Promise<string | null> {\n const existing = inFlightRefreshes.get(kb.id);\n if (existing) return existing;\n\n const promise = (async (): Promise<string | null> => {\n const stored = getStoredSession(kb.id);\n if (!stored) return null;\n\n const client = new SemiontApiClient({\n baseUrl: baseUrl(kbBackendUrl(kb)),\n eventBus: new EventBus(),\n });\n\n try {\n const response = await client.refreshToken(makeRefreshToken(stored.refresh));\n const newAccess = response.access_token;\n if (!newAccess) return null;\n setStoredSession(kb.id, { access: newAccess, refresh: stored.refresh });\n return newAccess;\n } catch {\n return null;\n }\n })();\n\n inFlightRefreshes.set(kb.id, promise);\n try {\n return await promise;\n } finally {\n inFlightRefreshes.delete(kb.id);\n }\n}\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":["SemiontApiClient","baseUrl","EventBus","SemiontApiClient","baseUrl","EventBus","React","useEffect","useState","jsx","useEffect","useState","React","createContext","useContext","jsx","createContext","useContext","useState","useEffect","useMemo","jsx","createContext","useState","useEffect","useMemo","jsx","useContext"]}
|
package/dist/index.d.mts
CHANGED
|
@@ -11,9 +11,9 @@ import { SemiontApiClient, ValidationResult, TokenRefresher } from '@semiont/api
|
|
|
11
11
|
import * as _codemirror_state from '@codemirror/state';
|
|
12
12
|
import { HighlightStyle } from '@codemirror/language';
|
|
13
13
|
import { WidgetType } from '@codemirror/view';
|
|
14
|
+
import { Observable } from 'rxjs';
|
|
14
15
|
import { TagSchema } from '@semiont/ontology';
|
|
15
16
|
export { TAG_SCHEMAS, TagCategory, TagSchema, getAllTagSchemas, getSchemaCategory as getTagCategory, getTagSchema, getTagSchemasByDomain, isValidCategory } from '@semiont/ontology';
|
|
16
|
-
import { Observable } from 'rxjs';
|
|
17
17
|
|
|
18
18
|
type Annotation$m = components['schemas']['Annotation'];
|
|
19
19
|
/**
|
|
@@ -395,14 +395,6 @@ declare function useResources(): {
|
|
|
395
395
|
token: string;
|
|
396
396
|
}, Error>;
|
|
397
397
|
};
|
|
398
|
-
search: {
|
|
399
|
-
useQuery: (query: string, limit: number) => _tanstack_react_query.UseQueryResult<{
|
|
400
|
-
resources: _semiont_core.components["schemas"]["ResourceDescriptor"][];
|
|
401
|
-
total: number;
|
|
402
|
-
offset: number;
|
|
403
|
-
limit: number;
|
|
404
|
-
}, Error>;
|
|
405
|
-
};
|
|
406
398
|
create: {
|
|
407
399
|
useMutation: () => _tanstack_react_query.UseMutationResult<{
|
|
408
400
|
resourceId: string;
|
|
@@ -877,10 +869,6 @@ declare const QUERY_KEYS: {
|
|
|
877
869
|
}];
|
|
878
870
|
detail: (id: ResourceId) => readonly ["resources", ResourceId];
|
|
879
871
|
byToken: (token: string) => readonly ["resources", "by-token", string];
|
|
880
|
-
search: (query: string, limit: number) => readonly ["resources", "search", {
|
|
881
|
-
readonly query: string;
|
|
882
|
-
readonly limit: number;
|
|
883
|
-
}];
|
|
884
872
|
events: (id: ResourceId) => readonly ["resources", ResourceId, "events"];
|
|
885
873
|
annotations: (id: ResourceId) => readonly ["resources", ResourceId, "annotations"];
|
|
886
874
|
referencedBy: (id: ResourceId) => readonly ["resources", ResourceId, "referenced-by"];
|
|
@@ -905,6 +893,45 @@ declare const QUERY_KEYS: {
|
|
|
905
893
|
};
|
|
906
894
|
};
|
|
907
895
|
|
|
896
|
+
/**
|
|
897
|
+
* createSearchPipeline
|
|
898
|
+
*
|
|
899
|
+
* A debounced-search RxJS pipeline factory. Combines an input Subject with a
|
|
900
|
+
* downstream fetch function and emits typed `{ results, isSearching }` state.
|
|
901
|
+
*
|
|
902
|
+
* Designed to be created once per component instance via React's
|
|
903
|
+
* `useState(() => createSearchPipeline(...))` lazy initializer, then consumed
|
|
904
|
+
* via `useObservable(pipeline.state$)`. The pipeline holds no React state and
|
|
905
|
+
* has no React imports — it's pure RxJS, unit-testable without a renderer.
|
|
906
|
+
*
|
|
907
|
+
* The fetch function is expected to return `Observable<T[] | undefined>`,
|
|
908
|
+
* matching the cache-miss-then-data shape of `BrowseNamespace` Observables:
|
|
909
|
+
* `undefined` means "fetch in flight, no value yet"; an array means "data
|
|
910
|
+
* available (possibly empty)".
|
|
911
|
+
*/
|
|
912
|
+
|
|
913
|
+
interface SearchState<T> {
|
|
914
|
+
results: T[];
|
|
915
|
+
isSearching: boolean;
|
|
916
|
+
}
|
|
917
|
+
interface SearchPipeline<T> {
|
|
918
|
+
/** Latest query string. Bind to a controlled input via `useObservable`. */
|
|
919
|
+
query$: Observable<string>;
|
|
920
|
+
/** Latest search state — results plus a loading flag. */
|
|
921
|
+
state$: Observable<SearchState<T>>;
|
|
922
|
+
/** Push a new query value. Triggers the debounced fetch. */
|
|
923
|
+
setQuery(value: string): void;
|
|
924
|
+
/** Tear down the input Subject. Call from `useEffect` cleanup. */
|
|
925
|
+
dispose(): void;
|
|
926
|
+
}
|
|
927
|
+
interface SearchPipelineOptions {
|
|
928
|
+
/** Milliseconds to wait after the last keystroke before fetching. Default 250. */
|
|
929
|
+
debounceMs?: number;
|
|
930
|
+
/** Initial query value. Useful for modals that open with a pre-filled term. */
|
|
931
|
+
initialQuery?: string;
|
|
932
|
+
}
|
|
933
|
+
declare function createSearchPipeline<T>(fetch: (query: string) => Observable<T[] | undefined>, options?: SearchPipelineOptions): SearchPipeline<T>;
|
|
934
|
+
|
|
908
935
|
/**
|
|
909
936
|
* Annotation overlay: decouples annotation highlighting from markdown rendering.
|
|
910
937
|
*
|
|
@@ -1703,6 +1730,7 @@ interface StoredSession {
|
|
|
1703
1730
|
refresh: string;
|
|
1704
1731
|
}
|
|
1705
1732
|
declare function defaultProtocol(host: string): 'http' | 'https';
|
|
1733
|
+
declare function isValidHostname(host: string): boolean;
|
|
1706
1734
|
declare function kbBackendUrl(kb: KnowledgeBase): string;
|
|
1707
1735
|
/**
|
|
1708
1736
|
* Read the locally-stored credential status for a KB. Pure / synchronous —
|
|
@@ -3924,6 +3952,8 @@ interface ResourceDiscoveryPageProps {
|
|
|
3924
3952
|
entityTypes: string[];
|
|
3925
3953
|
isLoadingRecent: boolean;
|
|
3926
3954
|
isSearching: boolean;
|
|
3955
|
+
searchQuery: string;
|
|
3956
|
+
onSearchQueryChange: (query: string) => void;
|
|
3927
3957
|
theme: 'light' | 'dark';
|
|
3928
3958
|
showLineNumbers: boolean;
|
|
3929
3959
|
activePanel: string | null;
|
|
@@ -3949,7 +3979,7 @@ interface ResourceDiscoveryPageProps {
|
|
|
3949
3979
|
};
|
|
3950
3980
|
ToolbarPanels: React__default.ComponentType<any>;
|
|
3951
3981
|
}
|
|
3952
|
-
declare function ResourceDiscoveryPage({ recentDocuments, searchDocuments, entityTypes, isLoadingRecent, isSearching, theme, showLineNumbers, activePanel, onNavigateToResource, onNavigateToCompose, translations: t, ToolbarPanels, }: ResourceDiscoveryPageProps): react_jsx_runtime.JSX.Element;
|
|
3982
|
+
declare function ResourceDiscoveryPage({ recentDocuments, searchDocuments, entityTypes, isLoadingRecent, isSearching, searchQuery, onSearchQueryChange, theme, showLineNumbers, activePanel, onNavigateToResource, onNavigateToCompose, translations: t, ToolbarPanels, }: ResourceDiscoveryPageProps): react_jsx_runtime.JSX.Element;
|
|
3953
3983
|
|
|
3954
3984
|
type ResourceDescriptor = components['schemas']['ResourceDescriptor'];
|
|
3955
3985
|
interface ResourceCardProps {
|
|
@@ -4242,4 +4272,4 @@ declare function useObservable<T>(obs$: Observable<T>): T | undefined;
|
|
|
4242
4272
|
*/
|
|
4243
4273
|
declare function useStoreTokenSync(): void;
|
|
4244
4274
|
|
|
4245
|
-
export { ANNOTATORS, AVAILABLE_LOCALES, AdminDevOpsPage, type AdminDevOpsPageProps, AdminExchangePage, type AdminExchangePageProps, type AdminExchangePageTranslations, AdminSecurityPage, type AdminSecurityPageProps, type AdminUser, type AdminUserStats, AdminUsersPage, type AdminUsersPageProps, AnnotateReferencesProgressWidget, AnnotateToolbar, AnnotateView, type Annotation$k as Annotation, type AnnotationConfig, type AnnotationCreationHandler, type AnnotationHandlers, AnnotationHistory, type AnnotationManager, AnnotationOverlay, AnnotationProvider, type AnnotationProviderProps, type AnnotationUIState, type AnnotationsCollection, type Annotator, ApiClientProvider, type ApiClientProviderProps, AssessmentEntry, AssessmentPanel, AssistSection, AsyncErrorBoundary, AuthErrorDisplay, type AuthErrorDisplayProps, AuthTokenProvider, type AuthTokenProviderProps, type AvailableLocale, type BeckonFlowState, type BorderRadiusToken, BrowseView, Button, ButtonGroup, type ButtonGroupProps, type ButtonProps, COMMON_PANELS, type CacheManager, CacheProvider, type CacheProviderProps, type ClickAction, CodeMirrorRenderer, CollaborationPanel, CollapsibleResourceNavigation, type CollapsibleResourceNavigationProps, type ColorToken, CommentEntry, CommentsPanel, ComposeLoadingState, type ComposeLoadingStateProps, type ContextGatherFlowConfig, type ContextGatherFlowState, type CreateAnnotationParams, type CreateConfig, type DeleteAnnotationParams, type DetectionConfig, type DevOpsFeature, type DrawingMode, EntityTagsPage, type EntityTagsPageProps, EntityTypeBadges, ErrorBoundary, EventBusProvider, type EventBusProviderProps, ExportCard, type ExportCardProps, type ExportCardTranslations, Footer, HOVER_DELAY_MS, HighlightEntry, HighlightPanel, HistoryEvent, type HoverEmitterProps, type HoverHandlers, ImageURLSchema, ImageViewer, ImportCard, type ImportCardProps, type ImportCardTranslations, type ImportPreview, ImportProgress, type ImportProgressProps, type ImportProgressTranslations, JsonLdPanel, JsonLdView, KbSessionStatus, type KeyboardShortcut, KeyboardShortcutsHelpModal, KnowledgeBase, LeftSidebar, type LinkComponentProps, LinkedDataPage, type LinkedDataPageProps, type LinkedDataPageTranslations, LiveRegionProvider, type MarkFlowState, type Motivation$8 as Motivation, type NavigationItem, NavigationMenu, type NavigationMenuHelper, type NavigationProps, type OAuthProvider, type OAuthUser, OAuthUserSchema, ObservableLink, type ObservableLinkProps, OpenResource, OpenResourcesManager, OpenResourcesProvider, type OverlayAnnotation, PageLayout, type PanelBrowseState, PanelHeader, PermissionDeniedModal, PopupContainer, PopupHeader, ProposeEntitiesModal, ProtectedErrorBoundary, QUERY_KEYS, RESOURCE_PANELS, RecentDocumentsPage, type RecentDocumentsPageProps, ReferenceEntry, ReferenceResolutionWidget, ReferenceWizardModal, type ReferenceWizardModalProps, ReferencesPanel, ResizeHandle, type ResolvedTheme, ResourceAnnotationsProvider, ResourceCard, type ResourceCardProps, ResourceComposePage, type ResourceComposePageProps, ResourceDiscoveryPage, type ResourceDiscoveryPageProps, ResourceErrorState, type ResourceErrorStateProps, ResourceInfoPanel, ResourceLoadingState, ResourceSearchModal, type ResourceSearchModalProps, ResourceTagsInline, ResourceViewer, ResourceViewerPage, type ResourceViewerPageProps, type RouteBuilder, type SaveResourceParams, SearchModal, type SearchModalProps, SelectedTextDisplay, type SelectionMotivation, type SelectorType, SemiontBranding, SemiontFavicon, type SemiontResource$4 as SemiontResource, SessionExpiredModal, SessionExpiryBanner, SessionTimer, SettingsPanel, type ShadowToken, type ShapeType, SignInForm, type SignInFormProps, SignUpForm, type SignUpFormProps, SimpleNavigation, type SimpleNavigationItem, type SimpleNavigationProps, SkipLinks, SortableResourceTab, type SortableResourceTabProps, type SpacingToken, StatisticsPanel, StatusDisplay, type StoredSession, type StreamStatus, SvgDrawingCanvas, TagEntry, TagSchemasPage, type TagSchemasPageProps, TaggingPanel, type TextSegment, type TextSelection, type Theme, ThemeProvider, ToastContainer, type ToastMessage, ToastProvider, type ToastType, Toolbar, type ToolbarPanelType, type TransitionToken, TranslationManager, TranslationProvider, type TranslationProviderProps, type TypographyToken, type UICreateAnnotationParams, UnifiedAnnotationsPanel, UnifiedHeader, type UseResourceContentResult, UserMenuSkeleton, WelcomePage, type WelcomePageProps, type YieldFlowState, applyHighlights, buildSourceToRenderedMap, buildTextNodeIndex, buttonStyles, clearHighlights, createHoverHandlers, cssVariables, defaultProtocol, faviconPaths, formatTime, generateCSSVariables, getKbSessionStatus, getResourceIcon, getSelectedShapeForSelectorType, getSelectorType, getShortcutDisplay, getSupportedShapes, hideWidgetPreview, isShapeSupported, jsonLightHighlightStyle, jsonLightTheme, kbBackendUrl, notifyPermissionDenied, notifySessionExpired, resolveAnnotationRanges, sanitizeImageURL, saveSelectedShapeForSelectorType, showWidgetPreview, supportsDetection, toOverlayAnnotations, tokens, useAdmin, useAnnotationManager, useAnnotations, useApiClient, useAttentionStream, useAuthApi, useAuthToken, useBeckonFlow, useBindFlow, useCacheManager, useContextGatherFlow, useDebounce, useDebouncedCallback, useDocumentAnnouncements, useDoubleKeyPress, useDropdown, useEntityTypes, useEventBus, useEventSubscription, useEventSubscriptions, useFormAnnouncements, useGlobalEvents, useHealth, useHoverDelay, useHoverEmitter, useIsTyping, useKeyboardShortcuts, useLanguageChangeAnnouncements, useLineNumbers, useLiveRegion, useLoadingState, useLocalStorage, useMarkFlow, useModeration, useObservable, useObservableExternalNavigation, useObservableRouter, useOpenResources, usePanelBrowse, usePanelWidth, usePreloadTranslations, useResourceAnnotations, useResourceContent, useResourceEvents, useResourceLoadingAnnouncements, useResources, useRovingTabIndex, useSearchAnnouncements, useSessionExpiry, useStoreTokenSync, useTheme, useToast, useTranslations, useYieldFlow };
|
|
4275
|
+
export { ANNOTATORS, AVAILABLE_LOCALES, AdminDevOpsPage, type AdminDevOpsPageProps, AdminExchangePage, type AdminExchangePageProps, type AdminExchangePageTranslations, AdminSecurityPage, type AdminSecurityPageProps, type AdminUser, type AdminUserStats, AdminUsersPage, type AdminUsersPageProps, AnnotateReferencesProgressWidget, AnnotateToolbar, AnnotateView, type Annotation$k as Annotation, type AnnotationConfig, type AnnotationCreationHandler, type AnnotationHandlers, AnnotationHistory, type AnnotationManager, AnnotationOverlay, AnnotationProvider, type AnnotationProviderProps, type AnnotationUIState, type AnnotationsCollection, type Annotator, ApiClientProvider, type ApiClientProviderProps, AssessmentEntry, AssessmentPanel, AssistSection, AsyncErrorBoundary, AuthErrorDisplay, type AuthErrorDisplayProps, AuthTokenProvider, type AuthTokenProviderProps, type AvailableLocale, type BeckonFlowState, type BorderRadiusToken, BrowseView, Button, ButtonGroup, type ButtonGroupProps, type ButtonProps, COMMON_PANELS, type CacheManager, CacheProvider, type CacheProviderProps, type ClickAction, CodeMirrorRenderer, CollaborationPanel, CollapsibleResourceNavigation, type CollapsibleResourceNavigationProps, type ColorToken, CommentEntry, CommentsPanel, ComposeLoadingState, type ComposeLoadingStateProps, type ContextGatherFlowConfig, type ContextGatherFlowState, type CreateAnnotationParams, type CreateConfig, type DeleteAnnotationParams, type DetectionConfig, type DevOpsFeature, type DrawingMode, EntityTagsPage, type EntityTagsPageProps, EntityTypeBadges, ErrorBoundary, EventBusProvider, type EventBusProviderProps, ExportCard, type ExportCardProps, type ExportCardTranslations, Footer, HOVER_DELAY_MS, HighlightEntry, HighlightPanel, HistoryEvent, type HoverEmitterProps, type HoverHandlers, ImageURLSchema, ImageViewer, ImportCard, type ImportCardProps, type ImportCardTranslations, type ImportPreview, ImportProgress, type ImportProgressProps, type ImportProgressTranslations, JsonLdPanel, JsonLdView, KbSessionStatus, type KeyboardShortcut, KeyboardShortcutsHelpModal, KnowledgeBase, LeftSidebar, type LinkComponentProps, LinkedDataPage, type LinkedDataPageProps, type LinkedDataPageTranslations, LiveRegionProvider, type MarkFlowState, type Motivation$8 as Motivation, type NavigationItem, NavigationMenu, type NavigationMenuHelper, type NavigationProps, type OAuthProvider, type OAuthUser, OAuthUserSchema, ObservableLink, type ObservableLinkProps, OpenResource, OpenResourcesManager, OpenResourcesProvider, type OverlayAnnotation, PageLayout, type PanelBrowseState, PanelHeader, PermissionDeniedModal, PopupContainer, PopupHeader, ProposeEntitiesModal, ProtectedErrorBoundary, QUERY_KEYS, RESOURCE_PANELS, RecentDocumentsPage, type RecentDocumentsPageProps, ReferenceEntry, ReferenceResolutionWidget, ReferenceWizardModal, type ReferenceWizardModalProps, ReferencesPanel, ResizeHandle, type ResolvedTheme, ResourceAnnotationsProvider, ResourceCard, type ResourceCardProps, ResourceComposePage, type ResourceComposePageProps, ResourceDiscoveryPage, type ResourceDiscoveryPageProps, ResourceErrorState, type ResourceErrorStateProps, ResourceInfoPanel, ResourceLoadingState, ResourceSearchModal, type ResourceSearchModalProps, ResourceTagsInline, ResourceViewer, ResourceViewerPage, type ResourceViewerPageProps, type RouteBuilder, type SaveResourceParams, SearchModal, type SearchModalProps, type SearchPipeline, type SearchPipelineOptions, type SearchState, SelectedTextDisplay, type SelectionMotivation, type SelectorType, SemiontBranding, SemiontFavicon, type SemiontResource$4 as SemiontResource, SessionExpiredModal, SessionExpiryBanner, SessionTimer, SettingsPanel, type ShadowToken, type ShapeType, SignInForm, type SignInFormProps, SignUpForm, type SignUpFormProps, SimpleNavigation, type SimpleNavigationItem, type SimpleNavigationProps, SkipLinks, SortableResourceTab, type SortableResourceTabProps, type SpacingToken, StatisticsPanel, StatusDisplay, type StoredSession, type StreamStatus, SvgDrawingCanvas, TagEntry, TagSchemasPage, type TagSchemasPageProps, TaggingPanel, type TextSegment, type TextSelection, type Theme, ThemeProvider, ToastContainer, type ToastMessage, ToastProvider, type ToastType, Toolbar, type ToolbarPanelType, type TransitionToken, TranslationManager, TranslationProvider, type TranslationProviderProps, type TypographyToken, type UICreateAnnotationParams, UnifiedAnnotationsPanel, UnifiedHeader, type UseResourceContentResult, UserMenuSkeleton, WelcomePage, type WelcomePageProps, type YieldFlowState, applyHighlights, buildSourceToRenderedMap, buildTextNodeIndex, buttonStyles, clearHighlights, createHoverHandlers, createSearchPipeline, cssVariables, defaultProtocol, faviconPaths, formatTime, generateCSSVariables, getKbSessionStatus, getResourceIcon, getSelectedShapeForSelectorType, getSelectorType, getShortcutDisplay, getSupportedShapes, hideWidgetPreview, isShapeSupported, isValidHostname, jsonLightHighlightStyle, jsonLightTheme, kbBackendUrl, notifyPermissionDenied, notifySessionExpired, resolveAnnotationRanges, sanitizeImageURL, saveSelectedShapeForSelectorType, showWidgetPreview, supportsDetection, toOverlayAnnotations, tokens, useAdmin, useAnnotationManager, useAnnotations, useApiClient, useAttentionStream, useAuthApi, useAuthToken, useBeckonFlow, useBindFlow, useCacheManager, useContextGatherFlow, useDebounce, useDebouncedCallback, useDocumentAnnouncements, useDoubleKeyPress, useDropdown, useEntityTypes, useEventBus, useEventSubscription, useEventSubscriptions, useFormAnnouncements, useGlobalEvents, useHealth, useHoverDelay, useHoverEmitter, useIsTyping, useKeyboardShortcuts, useLanguageChangeAnnouncements, useLineNumbers, useLiveRegion, useLoadingState, useLocalStorage, useMarkFlow, useModeration, useObservable, useObservableExternalNavigation, useObservableRouter, useOpenResources, usePanelBrowse, usePanelWidth, usePreloadTranslations, useResourceAnnotations, useResourceContent, useResourceEvents, useResourceLoadingAnnouncements, useResources, useRovingTabIndex, useSearchAnnouncements, useSessionExpiry, useStoreTokenSync, useTheme, useToast, useTranslations, useYieldFlow };
|