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,102 @@
# Feature Specification: JSON Import/Export
**Feature Branch**: `007-json-import-export`
**Created**: 2026-03-27
**Status**: Draft
**Input**: Gitea issue #17 — JSON import/export for full encounter state
## User Scenarios & Testing *(mandatory)*
### Story IE-1 — Export encounter to file (Priority: P1)
A DM has set up an encounter (combatants, HP, initiative, conditions) and wants to save it as a file for backup or to share with another DM. They click an export button, and the browser downloads a `.json` file containing the full application state.
**Why this priority**: Export is the foundation — without it, import has nothing to work with. It also delivers standalone value as a backup mechanism.
**Independent Test**: Can be fully tested by creating an encounter, exporting, and verifying the downloaded file contains all encounter data, undo/redo history, and player character templates.
**Acceptance Scenarios**:
1. **Given** an encounter with combatants (some with HP, AC, conditions, initiative), **When** the user clicks the export button, **Then** a `.json` file is downloaded containing the encounter state, undo/redo stacks, and player character templates.
2. **Given** an empty encounter with no combatants, **When** the user clicks the export button, **Then** a `.json` file is downloaded containing the empty encounter state and any existing player character templates.
3. **Given** an encounter with player character combatants (color, icon, linked template), **When** the user exports, **Then** the exported file preserves all player character visual properties and template links.
---
### Story IE-2 — Import encounter from file (Priority: P1)
A DM receives a `.json` file from another DM (or from their own earlier export) and wants to load it. They click an import button, select the file, and the application replaces the current state with the imported data.
**Why this priority**: Import completes the core value proposition — without it, export is just a read-only backup. Both are needed for the feature to be useful.
**Independent Test**: Can be tested by importing a valid `.json` file and verifying the encounter, undo/redo history, and player characters are replaced with the imported data.
**Acceptance Scenarios**:
1. **Given** the current encounter is empty, **When** the user selects a valid exported `.json` file, **Then** the application loads the imported encounter, undo/redo history, and player characters without any confirmation prompt.
2. **Given** the current encounter has combatants, **When** the user selects a valid `.json` file, **Then** a confirmation dialog appears warning that the current encounter will be replaced.
3. **Given** the confirmation dialog is shown, **When** the user confirms, **Then** the current encounter, undo/redo history, and player characters are replaced with the imported data.
4. **Given** the confirmation dialog is shown, **When** the user cancels, **Then** the current state remains unchanged.
---
### Story IE-3 — Reject invalid import files (Priority: P2)
A DM accidentally selects a wrong file (a non-JSON file, a corrupted export, or a JSON file with the wrong structure). The application rejects it and shows a clear error message without losing the current state.
**Why this priority**: Error handling is essential for a good user experience but secondary to the core import/export flow.
**Independent Test**: Can be tested by attempting to import various invalid files and verifying appropriate error messages appear while the current state is preserved.
**Acceptance Scenarios**:
1. **Given** the user selects a non-JSON file (e.g., an image), **When** the import attempts to parse it, **Then** a user-facing error message is shown and the current state is unchanged.
2. **Given** the user selects a JSON file with missing required fields, **When** the import validates it, **Then** a user-facing error message is shown and the current state is unchanged.
3. **Given** the user selects a JSON file with a valid top-level structure but individual combatants with invalid fields (e.g., negative HP, unknown condition IDs), **When** the import validates it, **Then** invalid fields on otherwise valid combatants are dropped/defaulted (same as localStorage rehydration), but if the top-level structure is malformed (missing `encounter` key, wrong types), the file is rejected with an error message.
---
### Edge Cases
- What happens when the exported file is from a newer version of the application with unknown fields? The import ignores unknown fields and loads what it can validate.
- What happens when the user imports a file with player characters that conflict with existing player character IDs? The imported player characters replace the existing ones entirely (full state replacement, not merge).
- What happens when the undo/redo stacks in the imported file are empty or missing? The system loads with empty undo/redo stacks (same as a fresh session).
- What happens when the browser blocks the file download (e.g., popup blocker)? The export uses a direct download mechanism (anchor element with download attribute) that is not subject to popup blocking.
## Requirements *(mandatory)*
### Functional Requirements
- **FR-001**: The system MUST provide an export action accessible from the UI that downloads the full application state as a `.json` file.
- **FR-002**: The exported file MUST contain the current encounter (combatants and turn/round state), undo/redo stacks, and player character templates.
- **FR-003**: The exported file MUST use a human-readable filename that includes context (e.g., date or encounter info).
- **FR-004**: The system MUST provide an import action accessible from the UI that opens a file picker for `.json` files.
- **FR-005**: On import, the system MUST replace the current encounter, undo/redo history, and player characters with the imported data.
- **FR-006**: The system MUST show a confirmation dialog before importing if the current encounter is non-empty (has at least one combatant).
- **FR-007**: The system MUST validate imported data using the same rules applied when loading from localStorage — invalid fields are cleaned or dropped, structurally malformed files are rejected entirely.
- **FR-008**: The system MUST show a user-facing error message when an imported file is rejected as invalid.
- **FR-009**: A failed or cancelled import MUST NOT alter the current application state.
- **FR-010**: Export and import actions MUST be accessible from the same location in the UI.
### Key Entities
- **Export Bundle**: A single JSON structure containing the encounter snapshot, undo stack, redo stack, and player character list. Represents the full application state at the time of export.
## Success Criteria *(mandatory)*
### Measurable Outcomes
- **SC-001**: Users can export the current state to a downloadable file in one click.
- **SC-002**: Users can import a previously exported file and see the full encounter restored, including combatant stats, turn tracking, and player characters.
- **SC-003**: Importing an invalid file shows an error message within 1 second without affecting the current state.
- **SC-004**: A round-trip (export then import) produces an encounter identical to the original.
## Assumptions
- The export format does not need to be backwards-compatible across application versions at this stage. Future format versioning is not included in MVP baseline.
- Export/import covers the three main state stores: encounter, undo/redo, and player characters. Bestiary cache and user settings (theme, rules edition) are excluded.
- The import is a full state replacement, not a merge. There is no selective import of individual pieces.
## Dependencies
- Undo/redo system (spec 006) must be implemented so that undo/redo stacks can be included in the export.