# Story 3.3: Preset Import & Export **Status:** ready-for-dev **Epic:** 3 - Scene-Aware Camera Automation (Scene Presets) **Story Key:** 3-3-preset-import-and-export **Created:** 2026-05-23 **Last Updated:** 2026-05-23 --- ## Story Header | Field | Value | |-------|-------| | **Epic** | 3 - Scene-Aware Camera Automation (Scene Presets) | | **Story ID** | 3.3 | | **Story Key** | 3-3-preset-import-and-export | | **Title** | Preset Import & Export | | **Status** | ready-for-dev | | **Priority** | High | | **Assigned Agent** | DEV (Amelia) | | **Created** | 2026-05-23 | | **Last Updated** | 2026-05-23 | --- ## 📋 Story Requirements ### User Story **As a** GM, **I want to** export all Scene Presets as a JSON file and import them into another world or campaign, **So that** I can reuse proven camera arrangements across campaigns without re-entering them manually. ### Persona Alignment - **Primary:** Marcus (Veteran GM) - Needs to reuse camera configurations across multiple campaigns and scenes - **Primary:** Jake (Streamer) - Requires consistent camera setups across different streaming sessions - **Secondary:** All GMs - Reduces setup time by sharing configurations between worlds ### Acceptance Criteria (BDD Format) #### AC-1: Export All Presets as JSON **Given** the GM opens the Preset management UI **When** they click "Export Presets" **Then** a JSON file is downloaded containing all world presets in human-readable format **And** the exported file matches the format documented in the module README #### AC-2: Export File Format **Given** presets exist in the world **When** the GM exports them **Then** the JSON file contains all presets with their names, matrices, and metadata **And** the file structure is: `{ _version: 1, presets: { [name: string]: ScenePreset } }` **And** the file is named `scrying-pool-presets-[world-name]-[timestamp].json` #### AC-3: Import Dialog with Merge/Replace Options **Given** the GM clicks "Import Presets" and selects a valid JSON file **When** the file is parsed successfully **Then** the GM is prompted to choose: "Merge" (add new, keep existing) or "Replace" (overwrite all) **And** the dialog shows a preview of the presets to be imported #### AC-4: Merge Behavior **Given** the GM selects "Merge" **When** the import is processed **Then** new presets from the file are added to the current world **And** existing presets with matching names are left unchanged **And** a success message shows how many presets were added #### AC-5: Replace Behavior with Confirmation **Given** the GM selects "Replace" **When** the import action is initiated **Then** a confirmation dialog warns about data loss before proceeding **And** the dialog lists how many existing presets will be removed **And** on confirmation, all existing presets are removed and replaced with the imported set #### AC-6: Invalid JSON Error Handling **Given** the imported file contains invalid JSON **When** parsing fails **Then** an error notification shows: "Import failed: invalid JSON format" **And** no changes are made to existing presets #### AC-7: Schema Validation Error Handling **Given** the imported JSON has an unrecognized schema version or missing required fields **When** validation fails **Then** an error notification shows with specific field details **And** no changes are made to existing presets #### AC-8: Import Confirmation Notification **Given** a preset import succeeds **When** the operation completes **Then** a success notification shows: "Imported N presets (M new, K replaced)" or "Replaced all presets with N from import" #### AC-9: README Documentation Update **Given** the module README exists **When** story 3.3 is complete **Then** the README contains a "Preset Import/Export" section documenting: - The JSON schema format - Example import/export usage - Merge vs Replace behavior - Error handling and limitations ### Functional Requirements Covered - **FR-19:** Preset import/export as JSON; export downloads all presets as one human-readable JSON file; import reads JSON and merges or replaces (user's choice); invalid JSON shows error; README documents exported format. ### Success Criteria - [ ] All 9 acceptance criteria pass manual testing - [ ] All unit tests pass (target: +15-20 new tests for PresetImportExportManager) - [ ] `npm run lint` exits 0 (ESLint import boundaries enforced) - [ ] `npm run typecheck` exits 0 (strict JSDoc compliance) - [ ] Code review passes with no critical findings - [ ] Integration test: Export → verify file format → Import with merge → Import with replace → Error handling flows verified end-to-end --- ## 📝 Tasks / Subtasks ### Task 1: Create Import/Export UI Components **Files:** `src/ui/gm/PresetImportDialog.js`, `src/ui/gm/PresetExportDialog.js`, `templates/preset-import.hbs`, `templates/preset-export.hbs`, `styles/components/_preset-import-export.less` **Subtasks:** - [ ] 1.1: Create `PresetExportDialog` class extending `ApplicationV2` with export button - [ ] 1.2: Create `PresetImportDialog` class extending `ApplicationV2` with file picker, merge/replace radio buttons, preview list, and confirm button - [ ] 1.3: Create Handlebars template for export dialog (`preset-export.hbs`) - [ ] 1.4: Create Handlebars template for import dialog (`preset-import.hbs`) with preview list - [ ] 1.5: Create LESS styles for both dialogs in `_preset-import-export.less` - [ ] 1.6: Implement file download logic with proper naming convention - [ ] 1.7: Implement file upload with validation and parsing **Acceptance Criteria:** AC-1, AC-2, AC-3 --- ### Task 2: Create Preset Import/Export Manager **Files:** `src/core/PresetImportExportManager.js`, `tests/unit/core/PresetImportExportManager.test.js` **Subtasks:** - [ ] 2.1: Write TDD red tests for exportAllPresets, importPresets, validateImportData, generateExportFilename - [ ] 2.2: Create `PresetImportExportManager` class with constructor `(adapter, scenePresetManager)` - [ ] 2.3: Implement `exportAllPresets()` — returns JSON string of all presets across all scenes - [ ] 2.4: Implement `generateExportFilename()` — generates filename with world name and timestamp - [ ] 2.5: Implement `downloadExportFile(jsonString, filename)` — triggers browser download - [ ] 2.6: Implement `validateImportData(data)` — validates JSON structure and schema version - [ ] 2.7: Implement `importPresets(jsonData, mode)` — processes import with 'merge' or 'replace' mode - [ ] 2.8: Implement `_mergePresets(jsonData)` — adds new presets, skips duplicates - [ ] 2.9: Implement `_replacePresets(jsonData)` — replaces all presets with imported ones - [ ] 2.10: Implement `_extractPresetsFromJson(data)` — extracts and validates presets from JSON - [ ] 2.11: Green all PresetImportExportManager tests **Acceptance Criteria:** AC-2, AC-4, AC-5, AC-6, AC-7, AC-8 **Dev Notes:** - Export format: `{ _version: 1, presets: { [name]: ScenePreset } }` matching the scene flag structure - Import validation: check _version, validate each preset with isValidScenePreset(), report errors - Merge mode: skip presets with existing names, count new additions - Replace mode: clear all existing, add all imported, warn about data loss - Both modes return summary: { success: boolean, message: string, added: number, replaced: number, skipped: number, errors: string[] } --- ### Task 3: Integrate Import/Export with Director's Board **Files:** `src/ui/gm/DirectorsBoard.js`, `src/ui/gm/PresetLoadDialog.js` **Subtasks:** - [ ] 3.1: Add "Export Presets" button to Director's Board footer (next to "Save Preset..." and "Load Preset...") - [ ] 3.2: Add "Import Presets" button to Director's Board footer - [ ] 3.3: Register click handlers that open the respective dialogs - [ ] 3.4: Update `PresetLoadDialog` to also include Export/Import options or redirect to new dialogs - [ ] 3.5: Ensure keyboard shortcuts work with new dialogs (Esc to close, etc.) **Acceptance Criteria:** AC-1, AC-3 **Dev Notes:** - Buttons should be in the footer area for consistency with Save/Load - Import/Export buttons only visible to GM (same as existing preset buttons) - Dialogs should be modal and focus-trapped --- ### Task 4: Add Module Settings for Import/Export Defaults **Files:** `module.js`, `src/foundry/FoundryAdapter.js` **Subtasks:** - [ ] 4.1: Register world setting for default import mode (merge/replace) - [ ] 4.2: Register world setting for export include timestamp in filename (default: true) - [ ] 4.3: Update FoundryAdapter to expose new settings if needed - [ ] 4.4: Update settings template to show new settings in appropriate category **Acceptance Criteria:** None (enhancement) **Dev Notes:** - Settings should be in the "Scene Presets" category - Settings are optional - UI should work without them --- ### Task 5: Update README Documentation **Files:** `README.md` **Subtasks:** - [ ] 5.1: Add "Preset Import/Export" section under Features - [ ] 5.2: Document JSON schema format with example - [ ] 5.3: Document merge vs replace behavior - [ ] 5.4: Document error handling and limitations (max presets, validation, etc.) - [ ] 5.5: Add usage examples **Acceptance Criteria:** AC-9 --- ### Task 6: Integration with ScenePresetManager **Files:** `src/core/ScenePresetManager.js` **Subtasks:** - [ ] 6.1: Add method `exportAllPresets()` to ScenePresetManager that collects presets from all scenes - [ ] 6.2: Add method `importPresets(data, mode)` to ScenePresetManager that handles multi-scene import - [ ] 6.3: Ensure import preserves scene associations where possible - [ ] 6.4: Update existing tests to cover new methods **Acceptance Criteria:** AC-4, AC-5, AC-8 **Dev Notes:** - Import can add presets to the current scene or distribute across scenes based on metadata - For v1, simple approach: import all presets to current scene - Future enhancement: support scene-specific import --- ### Task 7: Error Handling and Edge Cases **Files:** All relevant files **Subtasks:** - [ ] 7.1: Handle case where file picker is cancelled by user - [ ] 7.2: Handle case where downloaded file exceeds browser limits - [ ] 7.3: Handle case where import file contains duplicate names in merge mode - [ ] 7.4: Handle case where import file is empty - [ ] 7.5: Handle case where import would exceed MAX_PRESETS_PER_WORLD limit - [ ] 7.6: Add comprehensive error messages for all failure modes **Acceptance Criteria:** AC-6, AC-7 --- ## 🎯 Developer Context ### Epic Context **Epic 3: Scene-Aware Camera Automation (Scene Presets)** delivers the Level 3 experience of the Progressive Enhancement Architecture. The GM can save named camera configurations and apply them manually or automatically. Story 3.1 implemented Save/Load, Story 3.2 implemented Auto-Apply and ConfirmationBar, and **Story 3.3 completes the epic with Import/Export functionality**. **Business Value:** GMs running multiple campaigns or reusing scenes across worlds need a way to share camera configurations. This eliminates repetitive setup and ensures consistent experiences across sessions. **Dependencies:** - Story 3.1 (Save & Load Scene Presets) - COMPLETE - Story 3.2 (Scene Auto-Apply & ConfirmationBar) - COMPLETE - No external dependencies required **Blockers:** None identified --- ### Previous Story Intelligence (Story 3.2) **Learnings from Story 3.2:** 1. **ScenePresetManager is the central hub** - All preset operations flow through this class 2. **Contract validation is critical** - `isValidScenePreset()` catches malformed data early 3. **Socket events need proper payloads** - Use constants from `socket-message.js` for consistency 4. **Hooks must be registered at the right lifecycle** - `Hooks.once('ready')` for module initialization 5. **Test coverage is expected** - Story 3.2 added 25-30 new tests **Code Patterns Established:** - Constructor dependency injection for testability - JSDoc on all exported symbols (enforced by ESLint) - Private methods prefixed with `_` - Error handling with descriptive messages - Type validation at boundaries **Files Created/Modified in Story 3.2:** - `src/core/ScenePresetManager.js` - Extended with auto-apply logic - `src/ui/gm/ConfirmationBar.js` - New component - `templates/confirmation-bar.hbs` - New template - `styles/components/_confirmation-bar.less` - New styles - Tests for all new functionality **Problems Encountered & Solutions:** - Timer management across scene changes → Clear all timers on new scene activation - Multiple ConfirmationBar instances → Singleton pattern in StripOverlayLayer - Partial fail cases → Amber variant with "some updates pending" message --- ### Architecture Compliance **Technical Stack:** - Vanilla JavaScript ES2022+ with native ESM - LESS 4.6.4 → CSS via chokidar watch - Handlebars `.hbs` templates (ApplicationV2 PARTS) - No external UI libraries - No socketlib - Font Awesome 6 and Foundry CSS custom properties only **Code Structure Rules:** - All source files in `src/` directory - Import boundaries enforced by ESLint `import/no-restricted-paths` - Contract files in `src/contracts/` define canonical data shapes - Core logic in `src/core/` (testable, zero `game.*` access) - Foundry adapter layer in `src/foundry/` - UI components in `src/ui/` (gm/ or player/ subdirectories) **Import Restrictions:** - `src/contracts/` - May import nothing (pure data) - `src/utils/` - May only import from `src/contracts/` - `src/core/` - May only import from `src/contracts/`, `src/utils/` - `src/foundry/` - May import from anywhere (adapter layer) - `src/ui/` - May import from `src/core/`, `src/foundry/`, `src/contracts/` **This Story's Import Plan:** - `PresetImportExportManager` (`src/core/`) → imports from `src/contracts/scene-preset.js` - `PresetExportDialog`/`PresetImportDialog` (`src/ui/gm/`) → imports from `src/core/PresetImportExportManager.js`, `src/foundry/FoundryAdapter.js` --- ### Critical Implementation Requirements **1. File Download Implementation:** ```javascript // Use standard browser download approach const blob = new Blob([jsonString], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); ``` **2. File Upload Implementation:** ```javascript // Use // Parse file content with FileReader or response.text() // Validate JSON.parse() and schema ``` **3. Export Format:** ```json { "_version": 1, "worldName": "My Campaign", "exportedAt": 1234567890, "presets": { "Combat Setup": { "_version": 1, "name": "Combat Setup", "matrix": { "user1": "active", "user2": "hidden" }, "createdAt": 1234567890, "updatedAt": 1234567890 } } } ``` **4. Validation Strategy:** - Validate JSON parsing first - Validate schema version (_version === 1) - Validate each preset with existing `isValidScenePreset()` - Collect all errors and report together **5. Import Mode Logic:** ```javascript // Merge: for each preset in import // if preset.name doesn't exist in current scene → add it // if preset.name exists → skip it // Replace: clear all existing presets, add all from import ``` --- ### Library & Framework Requirements **Existing Libraries Used:** - FoundryVTT v14 native APIs: `game.settings`, `game.scenes`, `ui.notifications` - Native ES modules - Handlebars templates - LESS for CSS **No New Dependencies Required** - All file operations use native browser APIs - JSON parsing/stringification use native methods - No external libraries needed --- ### File Structure Requirements **New Files to Create:** ``` src/ ├── core/ │ └── PresetImportExportManager.js # NEW - Core import/export logic ├── ui/ │ └── gm/ │ ├── PresetExportDialog.js # NEW - Export dialog │ ├── PresetImportDialog.js # NEW - Import dialog with merge/replace │ └── (existing files updated) templates/ ├── preset-export.hbs # NEW - Export dialog template └── preset-import.hbs # NEW - Import dialog template styles/ └── components/ └── _preset-import-export.less # NEW - Dialog styles tests/ └── unit/ └── core/ └── PresetImportExportManager.test.js # NEW - Unit tests ``` **Modified Files:** ``` src/ui/gm/DirectorsBoard.js # Add Export/Import buttons src/ui/gm/PresetLoadDialog.js # Optionally add Import option module.js # Register new dialog classes src/core/ScenePresetManager.js # Add exportAllPresets method README.md # Add documentation ``` --- ### Testing Requirements **Unit Test Targets (15-20 new tests):** - `PresetImportExportManager` constructor validation - `exportAllPresets()` returns valid JSON with all presets - `generateExportFilename()` creates correct format - `validateImportData()` accepts valid data, rejects invalid - `importPresets()` with merge mode adds new presets - `importPresets()` with replace mode replaces all presets - Error handling for invalid JSON - Error handling for schema version mismatch - Error handling for invalid preset structures - Error handling for max presets exceeded **Integration Test Targets:** - Export flow: Click Export → File downloads → Verify format - Import merge flow: Upload file → Select Merge → Verify new presets added - Import replace flow: Upload file → Select Replace → Confirm → Verify all replaced - Error flow: Upload invalid file → Verify error message **Test Files to Create/Modify:** - `tests/unit/core/PresetImportExportManager.test.js` - NEW - Update existing `tests/unit/core/ScenePresetManager.test.js` for new methods **Testing Standards:** - Use Vitest with happy-dom environment - Mock all Foundry API dependencies via FoundryAdapter - Test both happy path and error cases - Aim for 80%+ coverage on new code --- ### Git Intelligence Summary **Recent Commit Pattern (from Story 3.2):** - Feature implemented in small, focused commits - Tests written alongside implementation (TDD approach) - Contracts validated before implementation - ESLint and typecheck passing before merge **Files Modified in Story 3.2:** - Added: `src/ui/gm/ConfirmationBar.js`, `templates/confirmation-bar.hbs`, `styles/components/_confirmation-bar.less` - Modified: `src/core/ScenePresetManager.js`, `module.js` - Tests added: Comprehensive test coverage for new functionality **Key Insight:** Story 3.2 followed the pattern of adding new UI components and extending core managers. Story 3.3 should follow the same pattern. --- ### Latest Technical Specifics **Scene Preset Contract (from `src/contracts/scene-preset.js`):** - Schema version: 1 - Max presets per world: 50 - Preset structure: `{ _version, name, matrix, createdAt, updatedAt }` - Matrix format: `{ [userId]: VisibilityState }` - Storage: Scene document flags **Export/Import Considerations:** - Export collects presets from current scene only (for v1) - Future enhancement: export from all scenes - Import to current scene only - Merge mode preserves existing presets with same names - Replace mode removes all existing presets first --- ## 📚 Project Context Reference **Project Name:** video-view-manager (Scrying Pool) **Project Type:** FoundryVTT v14 Module **Module ID:** video-view-manager **Planning Artifacts:** - PRD: `_bmad-output/planning-artifacts/prds/prd-video-view-manager-2026-05-19/prd.md` - Architecture: `_bmad-output/planning-artifacts/architecture.md` - Epics: `_bmad-output/planning-artifacts/epics.md` - UX Design: `_bmad-output/planning-artifacts/ux-design-specification.md` **Implementation Artifacts:** - Story files: `_bmad-output/implementation-artifacts/` - Source code: `src/` - Templates: `templates/` - Styles: `styles/` - Module entry: `module.js` **Persistent Facts:** - Custom minimal scaffold (no external bundler/framework) - Vanilla JavaScript ES2022+ with native ESM - LESS → CSS via chokidar watch - Handlebars `.hbs` templates - No external UI libraries - No socketlib - Dependency injection for testability - ESLint with `jsdoc/require-jsdoc` on exported symbols - Vitest with happy-dom for unit testing --- ## ✅ Story Completion Status **Ultimate context engine analysis completed - comprehensive developer guide created** - [x] Epic 3 context analyzed - [x] Story 3.3 requirements extracted from epics.md - [x] Previous story (3.2) intelligence gathered - [x] Architecture compliance verified - [x] Technical requirements documented - [x] File structure planned - [x] Testing requirements defined - [x] Edge cases identified - [x] Developer guardrails established **Status:** ready-for-dev --- ## 🎯 Next Steps 1. **Review** this comprehensive story file in `3-3-preset-import-and-export.md` 2. **Run** `bmad-dev-story` workflow for optimized implementation 3. **Run** `code-review` when complete (auto-marks done) 4. **Optional:** If Test Architect module installed, run `/bmad:tea:automate` after `dev-story` to generate guardrail tests --- ## 📌 Dev Agent Notes ### What the Developer MUST Know 1. **Export collects from current scene only** - For v1, export presets from the currently active scene. Future enhancement could export from all scenes. 2. **Import goes to current scene** - Imported presets are added to the currently active scene. The merge/replace operation works within that scene's preset collection. 3. **Use existing validation** - Leverage `isValidScenePreset()` from `src/contracts/scene-preset.js` for validation. Don't re-implement validation logic. 4. **Follow established patterns** - Look at `ScenePresetManager.js` for patterns on async operations, error handling, and socket event emission. 5. **Dialogs are ApplicationV2** - Both export and import dialogs should extend `ApplicationV2` to match FoundryVTT conventions used in existing dialogs. 6. **File operations are browser-native** - Use standard browser APIs for file download and upload. No server-side code needed. 7. **No new external dependencies** - All functionality must use native browser APIs and existing module code. 8. **Test coverage expected** - Story 3.2 added 25-30 tests. Aim for similar coverage (15-20 new tests for this story). ### Implementation Order Recommendation 1. **Start with PresetImportExportManager** - Core logic first, testable without UI 2. **Create dialogs and templates** - UI components that use the manager 3. **Integrate with Director's Board** - Add buttons and wire up handlers 4. **Update README** - Documentation last 5. **Write tests throughout** - Follow TDD approach used in Story 3.2 ### Critical Path Warnings - **Don't block on UI** - Core logic in `PresetImportExportManager` can be developed and tested independently - **Validation is critical** - Import must handle malformed data gracefully to prevent data loss - **User experience matters** - Clear feedback on success/failure, especially for destructive operations like replace - **File size considerations** - While unlikely with 50 presets max, consider that JSON files could be large ### Files to Read Before Starting **MUST READ (in order):** 1. `src/contracts/scene-preset.js` - Understand the preset data structure 2. `src/core/ScenePresetManager.js` - Understand existing preset management patterns 3. `src/ui/gm/DirectorsBoard.js` - Understand UI integration patterns 4. `src/ui/gm/ConfirmationBar.js` - Example of ApplicationV2 dialog pattern 5. `templates/confirmation-bar.hbs` - Template pattern reference 6. `styles/components/_confirmation-bar.less` - Styling pattern reference 7. `src/foundry/FoundryAdapter.js` - Understand adapter pattern for Foundry API access **SHOULD READ:** - `module.js` - Module initialization pattern - `tests/unit/core/ScenePresetManager.test.js` - Testing patterns - `architecture.md` - Overall architecture constraints --- *This story file was created using the BMad Method Ultimate Context Engine. The developer now has everything needed for flawless implementation.*