A cross-linguistic archive for exploring noun phrases and their internal grammatical structure. The application presents phrases collected from natural speech and text, with token-level glosses, structural annotations, translations, language metadata, context, and provenance.
The Noun Phrase Index is a browser-based research tool for browsing and comparing noun phrase data across languages.
Key capabilities:
- Browse an overview of the archive and live collection statistics.
- Explore phrases by wording, translation, language, and annotation sequence.
- Use search operators for precise text and token searches:
"word"for exact whole-word matching.word*for prefix matching.*word*for contains matching.- Unqualified text keeps the default contains behavior.
- Reorder Sequence Query slots with pointer drag-and-drop or Up/Down controls.
- Inspect phrase details, token glosses, contexts, annotations, and provenance.
- Export the current Explore result set as CSV.
- Compare annotation distributions and ordered annotation sequences across languages.
- Validate and run protected CSV data migrations from the admin interface.
- Frontend: React 18
- Build tool: Vite 5 with
@vitejs/plugin-react - Backend/API: Public reads call Supabase PostgREST directly. A separate FastAPI service handles authenticated admin migrations and runs the Python pipeline server-side.
- Database/storage: Supabase tables, including phrases, tokens, annotations, languages, sessions, contexts, sources, and annotators.
- Browser APIs:
fetchfor data access, React portals for help popovers, pointer events for slot reordering, and Blob/download APIs for CSV export. - Styling: Component-local CSS emitted by the
Stylecomponent insrc/app.jsx, plus the global stylesheet insrc/index.css. - Fonts: Fraunces, IBM Plex Sans, and IBM Plex Mono loaded from Google Fonts at runtime.
.
├── index.html # Vite HTML entry point
├── package.json # Dependencies and npm scripts
├── server/ # Protected FastAPI migration API
├── my-pipeline/ # CSV validation, transformation, and PostgreSQL import commands
├── vite.config.js # Vite and React plugin configuration
├── src/
│ ├── main.jsx # React root and StrictMode bootstrap
│ ├── app.jsx # Browser routing, shared data loading, and app shell
│ ├── archive.jsx # Shared data helpers, view implementations, and styles
│ ├── pages/ # Route-level public pages and protected admin migration page
│ └── index.css # Global base styles
└── README.md # Project and coding-agent guide
Route definitions and shared application data loading live in src/app.jsx. Existing view behavior and reusable UI remain in src/archive.jsx, while src/pages provides a separate component for each route.
- Explains the archive and its linguistic purpose.
- Displays live counts for languages with data, noun phrases, and glossed tokens.
- Shows a rotating example phrase with token-level interlinear glossing.
- Provides the archive data hierarchy and navigation into Explore.
- Lists languages available in the archive.
- Supports filtering by language name or ISO code.
- Shows phrase counts per language.
- Opens Explore with a selected language filter.
The main search and filtering workspace.
- Search wording: Searches phrase text and translations using the shared
buildSearchFilteroperator parser. - Language filter: Compact multi-select language dropdown.
- Sequence Query: Builds an ordered annotation pattern. Each slot can constrain category, subcategory, type, and an optional token word.
- Sequence slot word search: Uses the same operators as the main search field and matches annotation tokens.
- Slot ordering: Drag the handle to reorder slots, or use the Up and Down arrow buttons. Both paths update the same
slotsstate used by sequence matching. - Sorting: Click the Phrase or Language table header to switch field or direction.
- Results: Paginated phrase table with language, structure, and lazily loaded context.
- CSV export: Exports all rows matching the current language, text, and sequence filters, including language metadata and context.
- Detail navigation: Clicking a result opens the full record view.
- Search help: The small
?controls explain supported search operators. The popover is rendered through a portal so it is not clipped by the sidebar overflow container.
- Shows the phrase, translation, language, and structural tag sequence.
- Displays an interlinear gloss of tokens and glosses.
- Highlights the phrase inside its full context.
- Lists annotation category, subcategory, type, tag, token, and order.
- Shows provenance: source, session date, annotator, and language metadata.
- Links to related phrases from the same language.
- Select up to four languages for comparison.
- Compare category, subcategory, and type distributions with counts and percentages.
- Display proportional breakdown bars.
- Inspect ordered annotation-pair heatmaps at category, subcategory, or type level.
- Filter sequence statistics by category and subcategory.
/admin/data-migration is not linked from the public navigation. Supabase Auth signs the administrator in, and every migration API endpoint independently verifies the access token and requires either an app_metadata.role of admin, an admin entry in app_metadata.roles, or an email in the server-side NPINDEX_ADMIN_EMAILS allowlist.
The page accepts the pipeline's four CSV files (lexicon, phrases, tokens, and annotations), identifies them by headers, validates required values and cross-file references, checks languages against public.languages, and displays migration progress plus processed, successful, skipped, and failed counts.
The pipeline writes in foreign-key order:
- Existing languages are resolved (languages are never created implicitly).
- Source and annotator metadata are resolved or inserted.
- Lexicon and gloss records are upserted.
- Sessions and contexts are upserted.
- Phrases are upserted.
- Tokens are upserted.
- Annotations are upserted.
IDs are deterministic from source, language, and stable source identifiers. Reimporting the same dataset targets the same records instead of creating another copy. Database failures are isolated with savepoints and reported by table and input row.
my-pipeline/clean_tables.py is a separate destructive maintenance command. It is not callable through the API and requires an exact CLI confirmation phrase.
Before changing behavior, inspect the nearest owning page in src/pages, its view implementation in src/archive.jsx, and any related styles in the same file. Avoid scanning or refactoring unrelated views unless the requested behavior crosses a shared boundary.
Important locations:
sbandbuildSearchFilter: Supabase REST access and shared search-operator construction.SearchTips: reusable search-operator help popover.SequenceBuilder: sequence slots, slot editing, drag reordering, and arrow reordering.Explore: search state, filtering, pagination, export, and sequence resolution.Detail,Languages, andStatistics: their corresponding page-level workflows.Style: the application’s component CSS and responsive layout rules.src/app.jsx: route definitions, shared metadata loading, and navigation mapping.src/main.jsx: application bootstrap only; it should rarely need changes.
Prefer reusing existing helpers, components, state, and CSS conventions. In particular, do not duplicate search parsing or reorder logic. Keep changes focused, preserve the current Supabase schema and public behavior, and avoid broad rewrites of src/app.jsx for local feature changes.
Install dependencies:
npm installStart the development server:
npm run devCreate a Python environment and install the migration API dependencies:
python3 -m venv .venv
.venv/bin/pip install -r server/requirements.txtCopy .env.example to .env, then set:
NPINDEX_DATABASE_URL: the server-only Supabase PostgreSQL connection string, normally withsslmode=require.SUPABASE_URLandSUPABASE_ANON_KEY: used server-side to verify submitted Auth tokens.NPINDEX_ADMIN_EMAILS: optional comma-separated server-side admin allowlist. Prefer an admin role in Supabase Authapp_metadatafor production.NPINDEX_ADMIN_ORIGINS: allowed frontend origins.VITE_MIGRATION_API_URL: the public URL of the migration API; this is a URL only, never a credential.
Start the migration API (the command loads the local .env file):
npm run apiFor a CLI validation without database access:
python3 my-pipeline/import_pipeline.py --validate-only \
--source "Corpus name" --annotator "Full name" \
lexicon.csv phrases.csv tokens.csv annotations.csvRemove --validate-only and set NPINDEX_DATABASE_URL to perform the same import through the CLI. Do not set database passwords or privileged keys in a VITE_* variable.
Create a production build:
npm run buildPreview the production build locally:
npm run previewThe public React application is deployed on Vercel and the protected FastAPI admin API is deployed as a separate Render web service. The admin URL is public, but every admin API request still requires a Supabase Auth user allowed by the server.
Create a Render Blueprint from the repository's render.yaml. During the initial Blueprint setup, Render prompts for these values because they are declared with sync: false:
NPINDEX_DATABASE_URL: the server-only Supabase PostgreSQL connection string. Use the session pooler URL when the host requires IPv4 and includesslmode=require.SUPABASE_URL: the Supabase project URL.SUPABASE_ANON_KEY: the Supabase anonymous client key used to verify Auth access tokens.NPINDEX_ADMIN_EMAILS: a comma-separated allowlist of administrator email addresses.NPINDEX_ADMIN_ORIGINS: a comma-separated list of exact frontend origins, such ashttps://your-project.vercel.app, without trailing slashes.
The Blueprint initially selects Render's free plan to avoid creating a paid resource automatically. Upgrade to an always-on plan before relying on long-running imports: migration progress and uploaded files are held in the service process while a job runs.
After deployment, verify that https://YOUR-RENDER-SERVICE.onrender.com/api/health returns {"status":"ok"}.
In the Vercel project, add this Production environment variable:
VITE_MIGRATION_API_URL=https://YOUR-RENDER-SERVICE.onrender.com
Add it to Preview too only if preview deployments should access the production admin API. If so, add every permitted preview origin to NPINDEX_ADMIN_ORIGINS; CORS requires exact origins.
Redeploy after changing the variable because Vite embeds VITE_* values at build time. The root vercel.json sends direct SPA routes such as /admin to index.html.
Create or select a password-based user in Supabase Authentication. Ensure its email is present in NPINDEX_ADMIN_EMAILS, then visit https://YOUR-VERCEL-DOMAIN/admin in a private browser window and sign in. Verify Data management before running a CSV migration.
Keep NPINDEX_DATABASE_URL and all database passwords exclusively on the backend host. VITE_MIGRATION_API_URL is only a public service URL and contains no credential.
The Supabase URL and anonymous API key are currently configured near the top of src/archive.jsx. They are public client configuration; Supabase row-level security and table permissions determine which data the public application can read. PostgreSQL credentials remain exclusively in the migration API environment.