Frontend Architecture¶
The frontend is a React 19 SPA built with Vite 7 and Tailwind CSS 4.
Technology Stack¶
| Technology | Version | Purpose |
|---|---|---|
| React | 19 | UI framework |
| Vite | 7 | Build tool and dev server |
| Tailwind CSS | 4 | Utility-first styling (@theme system) |
| React Router | 7 | Client-side routing |
| Clerk React | 5 | Authentication (conditional) |
Page Structure¶
src/
App.jsx # Root router with auth guards
contexts/
NotebookContext.jsx # Global notebook state
layouts/
AppLayout.jsx # Sidebar + outlet layout
components/
FileDetailPanel.jsx # Document detail slide-out
OneDrivePicker.jsx # OneDrive folder browser + import
PermissionGuard.jsx # Page-level access control
RequireAuth.jsx # Auth wrapper (Clerk or bypass)
Sidebar.jsx # Navigation sidebar
pages/
Dashboard.jsx # Landing page with notebook overview
Notebooks.jsx # Notebook grid with create modal
Documents.jsx # File management + ingestion wizard
NotebookChat.jsx # RAG chat interface
SearchPlayground.jsx # Side-by-side search comparison
AIEnhancer.jsx # Chunk enhancement pipeline UI
IntelligenceSettings.jsx # LLM provider + model config
IngestionSettings.jsx # Parser + chunking + embedding config
GlobalSettings.jsx # API keys + system config (admin)
SystemMonitor.jsx # Health dashboard
UserManagement.jsx # User CRUD + role assignment (admin)
UserEditPage.jsx # User permissions editor (admin)
SignInPage.jsx # Clerk sign-in
SignUpPage.jsx # Clerk sign-up
InviteRedeem.jsx # Notebook invite redemption
utils/
apiClient.js # Fetch wrapper (608 lines)
permissions.js # Client-side permission helpers
Routing Architecture¶
graph TD
Root["/"] --> Dashboard
Root --> Notebooks["/notebooks"]
Root --> Users["/users — admin only"]
Root --> Settings["/settings — admin only"]
Root --> NB["/notebook/:notebookId"]
NB --> Chat["/chat"]
NB --> Docs["/documents"]
NB --> Search["/search"]
NB --> Enhancer["/enhancer"]
NB --> NbSettings["/settings"]
NB --> Monitor["/monitor"]
NB --> Ingestion["/ingestion-settings"]
AuthPages["Auth Pages — no sidebar"]
AuthPages --> SignIn["/sign-in"]
AuthPages --> SignUp["/sign-up"]
AuthPages --> Invite["/invite/:inviteCode"] All app routes are wrapped in <RequireAuth> which checks /api/auth/config at runtime. When Clerk is active, unauthenticated users are redirected to /sign-in with a return-to parameter. In bypass mode, all routes are accessible.
API Client¶
The apiClient.js module (608 lines) provides:
apiFetch(path, options)— Base fetch wrapper with automaticAuthorization: Bearer <token>injection from Clerk- Envelope unwrapping — Extracts
json.datafrom theAPIResponseenvelope - Convenience methods — Named exports for all backend endpoints
setTokenGetter(fn)— Called once during Clerk initialization
// Token setup in AppLayout.jsx
import { useAuth } from '@clerk/clerk-react';
import { setTokenGetter } from '../utils/apiClient';
function AppLayout() {
const { getToken } = useAuth();
useEffect(() => { setTokenGetter(getToken); }, [getToken]);
}
Auth Guards¶
| Component | Purpose |
|---|---|
RequireAuth | Route-level — redirects to sign-in if auth required |
PermissionGuard | Page-level — checks user role before rendering |
State Management¶
- NotebookContext — Single source of truth for the notebook list, shared across pages via React Context
- Local state — Each page manages its own state with
useState/useEffect - Polling — Status monitoring uses
setIntervalinsideuseEffectwith proper cleanup
Design System¶
- Tailwind CSS v4 with
@themesystem - Warm cream/orange light mode palette
- Responsive sidebar layout via
AppLayout.jsx - Consistent card-based UI pattern across all pages