apps/web. It renders your slideshow, lets you navigate between slides and slideshows, and gives you access to the AI assistant, JSON editor, and save controls.
Architecture overview
The frontend is a React 19 application built with Vite using Feature-Sliced Design principles. All slideshow UI lives underapps/web/src/features/slideshow/.
| Layer | Technology |
|---|---|
| Routing | TanStack Router (file-based, src/routes/) |
| Server state | TanStack Query (data fetching, caching, mutations) |
| Local state | Custom React hooks (useSlideshowStore) |
| API calls | ORPC typed client (src/infra/api/client.ts) |
| Styling | TailwindCSS v4 + shadcn/ui (Radix UI) |
| Domain logic | Adapter layer at src/domain/slideshow/ |
Loading slideshows
You can load slideshows in two ways:- Load from server
- Paste JSON
Click Load from server in the input view. The frontend calls the Slideshows loaded from the server include provenance — metadata about the source file’s location and whether it is writable. Only server-loaded slideshows can be saved back.
slideshowLoad ORPC procedure, which reads JSON files from the server’s data directory and returns validated slideshow data with provenance metadata.Navigating slides
Once a slideshow is loaded, you navigate between slides using:- Arrow keys —
←/→to move between slides (keyboard navigation is handled byuseKeyboardNav). - Slide list panel — click any slide thumbnail to jump directly.
- Fullscreen mode — press the fullscreen button or use the keyboard shortcut; arrow key navigation remains active.
useSlideshowStore and clamped automatically when the slideshow structure changes (e.g., after the AI assistant adds or removes slides).
Switching slideshows
If multiple slideshows are loaded, a slideshow selector lets you switch between them. Switching resets the current slide to 0 and clears any per-slideshow concept color overrides.How slides are organized
Each slide has two key organizational fields:1-based integer that determines the slide’s position in the presentation. The editor sorts and renders slides by
order. When the AI assistant adds or moves slides, it updates this field.The key of the concept this slide belongs to (references an entry in the slideshow’s
concepts map). Concepts are displayed as color-coded labels in the slide list. See Concepts for details.Editing slides
The editor provides two editing modes:- AI assistant
- JSON editor
The AI assistant panel (
assistant-panel.tsx) gives you a chat interface to describe changes in natural language. The assistant returns a patch, which you can review in the pending transaction card and then apply.See AI assistant for the full workflow.Saving changes
The save flow is manual — there is no auto-save. Click Save to persist changes.Trigger save
Click the Save button. The
onSaveCurrentSlideshow handler reads the active slideshow’s provenance to determine the targetId (the dataset ID for the server-side file).Collect slideshows for that target
Multiple slideshows can share a single file. The frontend collects all slideshows whose provenance
targetId matches the current one.Call slideshowSave
The
slideshowSave ORPC mutation is called with { datasetId, slideshows }. An optional expectedDigest can be passed to detect server-side changes since the last load.isSaving: saveMutation.isPending).
Validation
The editor runs continuous validation against all loaded slideshows:- Schema validation —
validateSlideshowCompletechecks the slideshow against the TypeBox schemas. - Runtime validation —
collectRuntimeValidationErrorschecks Mermaid diagram syntax (via themermaidlibrary) and table/CSV data (viapapaparse).
validation-error-store.ts and shown in a dismissible error card. You can copy all errors to the clipboard for debugging.
Domain adapter pattern
All imports of@slides/core in feature code go through the src/domain/slideshow/ adapter layer. This gives a single injection point for future cross-cutting concerns (analytics, retry logic, A/B testing) without touching individual component files.