Add JSON import/export for full encounter state

Export and import encounter, undo/redo history, and player characters
as a downloadable .json file. Export/import actions are in the action
bar overflow menu. Import validates using existing rehydration functions
and shows a confirmation dialog when replacing a non-empty encounter.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lukas
2026-03-27 14:28:39 +01:00
parent f6766b729d
commit fba83bebd6
19 changed files with 1339 additions and 2 deletions

View File

@@ -0,0 +1,52 @@
# Quickstart: JSON Import/Export
**Feature**: 007-json-import-export
**Date**: 2026-03-27
## Overview
Export and import the full application state (encounter, undo/redo history, player characters) as a JSON file. Enables backup/restore and sharing encounters between DMs.
## Key Concepts
- **ExportBundle**: A JSON object containing `version`, `exportedAt`, `encounter`, `undoStack`, `redoStack`, and `playerCharacters`. This is the file format.
- **Full replacement**: Import replaces all existing state — encounter, undo/redo, and player characters. It's not a merge.
- **Validation reuse**: Import validation uses the same `rehydrateEncounter()` and player character validation functions that localStorage loading uses.
## Implementation Layers
### Domain Layer
No new domain functions are needed. The existing types (`Encounter`, `PlayerCharacter`, `UndoRedoState`) and validation functions are reused as-is.
A new `ExportBundle` type is defined in the domain layer as a pure data structure.
### Application Layer
A new use case for import validation and bundle assembly:
- `validateImportBundle(data: unknown)` — validates and rehydrates an export bundle, returning the validated bundle or an error.
Export assembly is straightforward enough to live in the adapter layer (it's just reading and packaging existing state).
### Adapter Layer (Web)
- **Export**: Read state from contexts, assemble bundle, trigger browser download via `URL.createObjectURL()` + anchor element.
- **Import**: File picker input, parse JSON, delegate to application-layer validation, show confirmation dialog if encounter is non-empty, replace state via context setters.
- **UI**: Two new overflow menu items in the action bar — "Export Encounter" and "Import Encounter".
## File Locations
| Artifact | Path |
|----------|------|
| ExportBundle type | `packages/domain/src/types.ts` or new file |
| Import validation use case | `packages/application/src/` |
| Export/import adapter functions | `apps/web/src/persistence/` |
| UI integration | `apps/web/src/components/action-bar.tsx` |
| Confirmation dialog | `apps/web/src/components/` (new or reuse existing confirm pattern) |
## Testing Strategy
- **Domain**: No new domain tests needed (existing types unchanged).
- **Application**: Test `validateImportBundle()` with valid bundles, invalid bundles, missing fields, wrong types, and edge cases (empty encounter, empty stacks).
- **Adapter**: Test export bundle assembly and import file handling.
- **Integration**: Round-trip test — export then import should produce identical state.