Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Core runtime technologies:
| Layer | Current implementation |
| --- | --- |
| API | FastAPI app mounted at `/api/v1`, with Swagger at `/api/docs` and OpenAPI JSON at `/api/openapi.json`. |
| Public web app | React, React Router, TypeScript, Vite, MUI, Leaflet, GeoBlacklight frontend components, and H3 map visualization. |
| Public web app | React, React Router, TypeScript, Vite, MUI, Leaflet, `ogm-viewer` resource previews, GeoBlacklight Leaflet layers for homepage previews, and H3 map visualization. |
| Search | Elasticsearch with versioned index builds and alias swaps through `scripts/reindex_atomic.py`. |
| Database | ParadeDB/PostgreSQL, SQLAlchemy table metadata in `backend/db/models.py`, script-based migrations in `backend/db/migrations/`. |
| Cache | Redis plus durable database caches for generated API responses, resource representations, and visual assets. |
Expand Down
8 changes: 6 additions & 2 deletions docs/frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ Features:

- [Homepage Map Visualization](homepage-map.md) - H3 hex map, featured carousel,
preview layers, and Allmaps behavior.
- Resource-page item previews use the `ogm-viewer` web components with the
Aardvark record already returned by the resource API. Vite leaves the
components un-prebundled and resolves their runtime theme and icon assets in
development, then emits those assets during production builds.

Configuration:

Expand All @@ -39,8 +43,8 @@ The frontend stack currently uses:
- TypeScript, Vite 7, and React Router dev tooling.
- Vitest 3, Testing Library, `happy-dom`, axe, and pa11y for tests and
accessibility checks.
- Material UI 7, Tailwind, Leaflet, GeoBlacklight frontend components, Allmaps,
H3, Recharts, and Lucide icons.
- Material UI 7, Tailwind, Leaflet, `ogm-viewer`, GeoBlacklight Leaflet layers
for homepage previews, Allmaps, H3, Recharts, and Lucide icons.

## Quick Commands

Expand Down
75 changes: 1 addition & 74 deletions frontend/app/providers.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React from 'react';
import { ApiProvider } from '../src/context/ApiContext';
import { BookmarkProvider } from '../src/context/BookmarkContext';
import { DebugProvider } from '../src/context/DebugContext';
Expand All @@ -7,88 +7,15 @@ import { ThemeProvider } from '../src/context/ThemeContext';
import type { ThemeId } from '../src/config/institution';
import { TurnstileGate } from '../src/components/security/TurnstileGate';

/**
* App providers + client-side boot for GeoBlacklight (Stimulus).
*
* We keep this intentionally simple:
* - Start Stimulus once
* - Import GeoBlacklight core once (it registers its own controllers/listeners)
* - Trigger GeoBlacklight activation on initial load and on route changes
*/
export function Providers({
children,
initialThemeId,
locationKey,
turnstilePreview = false,
}: {
children: React.ReactNode;
initialThemeId?: ThemeId;
locationKey?: string;
turnstilePreview?: boolean;
}) {
useEffect(() => {
// Only runs in the browser
if (typeof window === 'undefined') return;

let cancelled = false;

async function boot() {
if (cancelled) return;
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const g = globalThis as any;

// Start Stimulus once and expose it globally (GeoBlacklight expects `Stimulus`).
if (!g.Stimulus) {
const { Application } = await import('@hotwired/stimulus');
g.Stimulus = Application.start();
(window as any).Stimulus = g.Stimulus;
}

// Mirador is embedded via an iframe (see ResourceViewer) so we intentionally do NOT
// register a Stimulus controller for it. This avoids any possibility of Mirador code
// affecting the parent page.

// Import GeoBlacklight core once; it registers its own controllers and listeners.
if (!g.GeoblacklightCore) {
const mod =
await import('@geoblacklight/frontend/app/javascript/geoblacklight/core');
g.GeoblacklightCore = (mod as any).default ?? mod;
}

// GeoBlacklight uses DOMContentLoaded / Turbo events; in our SSR+SPA environment,
// those may not fire at the right time. Trigger activation on initial load + navigation.
if (typeof g.GeoblacklightCore?.activate === 'function') {
// Some initializers assume specific DOM nodes exist and can throw if they don't.
// Never let that take down the app or interfere with the viewer.
// Defer one tick so the new route's DOM is committed before initializers run.
setTimeout(() => {
try {
g.GeoblacklightCore.activate(
new Event('react-router:navigation')
);
} catch (err) {
// eslint-disable-next-line no-console
console.warn('GeoBlacklight activation failed:', err);
}
}, 0);
}
} catch (err) {
// eslint-disable-next-line no-console
console.warn('GeoBlacklight boot failed:', err);
}
}

boot().catch((err) => {
// eslint-disable-next-line no-console
console.warn('GeoBlacklight boot failed:', err);
});

return () => {
cancelled = true;
};
}, [locationKey]);

return (
<ThemeProvider initialThemeId={initialThemeId}>
<TurnstileGate devPreview={turnstilePreview}>
Expand Down
1 change: 0 additions & 1 deletion frontend/app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ export default function Root() {
) : (
<Providers
initialThemeId={themeId}
locationKey={location.key}
turnstilePreview={turnstilePreview}
>
<Outlet />
Expand Down
Loading
Loading