Frontend UI
The frontend lives under frontend/ and is built with React 19, TypeScript, and Vite 7. The styling is hand-written CSS — no Tailwind, no component library. Vite emits the production build and nginx serves it in Docker.
How the code is laid out
Section titled “How the code is laid out”src/main.tsx— the entry point. Mounts the app inside a top-levelErrorBoundary.src/AppNew.tsx— the root component that drives the app state machine.src/pages/— the screen-level components:BackupSelector,PasswordPrompt, andExplorer.src/pages/modules/— the per-artifact modules the Explorer renders (FilesModule,WhatsAppModule,MessagesModule, the tabular*Tabcomponents,SearchTab,TimelineTab) plus the sharedAttachmentrenderer.src/components/— cross-cutting components such asErrorBoundary.src/lib/—api.ts(the typedfetchwrapper),types.ts(types mirroring the backend schemas),csv.ts(client-side CSV export), anduseLocalStorage.src/styles/— component CSS such asExplorer.css.
The four states
Section titled “The four states”AppNew walks through four states, and the token plus per-backup session tokens persist in localStorage:
- Token input. You enter the API token (default
dev-token). Every request reuses it. - Backup selection.
api.listBackups()lists what was discovered. Picking a decrypted backup jumps to the explorer; otherwise the password prompt appears. - Decrypt and unlock. Decryption runs as a worker job. The UI stays responsive and polls
getDecryptStatusuntil the backup reachesdecryptedorfailed. On success it also unlocks, producing the session token used for attachment downloads. - Explorer. The tabbed view over a decrypted backup.
The Explorer is a shell
Section titled “The Explorer is a shell”Explorer renders the tab bar and hands off to one module at a time. Each module owns its own data fetching, pagination, search, and — for conversations — the unlock prompt, the extraction overlay, and the image preview. The shell wraps the active module in an ErrorBoundary keyed by the active tab, so a crash in one module shows an inline fallback and switching tabs recovers instead of blanking the whole app. Search results deep-link into a conversation through an initialSelectedGuid prop on the WhatsApp and Messages modules.
API calls use the base URL from import.meta.env.VITE_API_BASE_URL (the backend, in Docker). During local development Vite proxies to the backend on port 8080.
Component behavior is covered by Vitest with React Testing Library and jsdom. Two suites carry most of the weight: Explorer.test.tsx pins the WhatsApp and Messages conversation flows — it was the regression guard for splitting those modules out — and ErrorBoundary.test.tsx covers the fallback and reset behavior. Run them with npm run test.
Adding an artifact type
Section titled “Adding an artifact type”A new type is two halves. On the backend, register an ArtifactSpec. On the frontend, add a module under src/pages/modules/, wire it into the Explorer tab list, add a typed helper in src/lib/api.ts, and a type in src/lib/types.ts. Reuse the shared Attachment component for anything that renders message attachments.