- Add comprehensive story file for preset import/export functionality - Update sprint status: 3-3-preset-import-and-export → ready-for-dev - Story covers FR-19: Preset import/export as JSON - Includes 9 acceptance criteria, tasks, and developer context Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
24 KiB
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 lintexits 0 (ESLint import boundaries enforced)npm run typecheckexits 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
PresetExportDialogclass extendingApplicationV2with export button - 1.2: Create
PresetImportDialogclass extendingApplicationV2with 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
PresetImportExportManagerclass 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
PresetLoadDialogto 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:
- ScenePresetManager is the central hub - All preset operations flow through this class
- Contract validation is critical -
isValidScenePreset()catches malformed data early - Socket events need proper payloads - Use constants from
socket-message.jsfor consistency - Hooks must be registered at the right lifecycle -
Hooks.once('ready')for module initialization - 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 logicsrc/ui/gm/ConfirmationBar.js- New componenttemplates/confirmation-bar.hbs- New templatestyles/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
.hbstemplates (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, zerogame.*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 fromsrc/contracts/src/core/- May only import fromsrc/contracts/,src/utils/src/foundry/- May import from anywhere (adapter layer)src/ui/- May import fromsrc/core/,src/foundry/,src/contracts/
This Story's Import Plan:
PresetImportExportManager(src/core/) → imports fromsrc/contracts/scene-preset.jsPresetExportDialog/PresetImportDialog(src/ui/gm/) → imports fromsrc/core/PresetImportExportManager.js,src/foundry/FoundryAdapter.js
Critical Implementation Requirements
1. File Download Implementation:
// 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:
// Use <input type="file" accept=".json">
// Parse file content with FileReader or response.text()
// Validate JSON.parse() and schema
3. Export Format:
{
"_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:
// 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):
PresetImportExportManagerconstructor validationexportAllPresets()returns valid JSON with all presetsgenerateExportFilename()creates correct formatvalidateImportData()accepts valid data, rejects invalidimportPresets()with merge mode adds new presetsimportPresets()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.jsfor 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
.hbstemplates - No external UI libraries
- No socketlib
- Dependency injection for testability
- ESLint with
jsdoc/require-jsdocon exported symbols - Vitest with happy-dom for unit testing
✅ Story Completion Status
Ultimate context engine analysis completed - comprehensive developer guide created
- Epic 3 context analyzed
- Story 3.3 requirements extracted from epics.md
- Previous story (3.2) intelligence gathered
- Architecture compliance verified
- Technical requirements documented
- File structure planned
- Testing requirements defined
- Edge cases identified
- Developer guardrails established
Status: ready-for-dev
🎯 Next Steps
- Review this comprehensive story file in
3-3-preset-import-and-export.md - Run
bmad-dev-storyworkflow for optimized implementation - Run
code-reviewwhen complete (auto-marks done) - Optional: If Test Architect module installed, run
/bmad:tea:automateafterdev-storyto generate guardrail tests
📌 Dev Agent Notes
What the Developer MUST Know
-
Export collects from current scene only - For v1, export presets from the currently active scene. Future enhancement could export from all scenes.
-
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.
-
Use existing validation - Leverage
isValidScenePreset()fromsrc/contracts/scene-preset.jsfor validation. Don't re-implement validation logic. -
Follow established patterns - Look at
ScenePresetManager.jsfor patterns on async operations, error handling, and socket event emission. -
Dialogs are ApplicationV2 - Both export and import dialogs should extend
ApplicationV2to match FoundryVTT conventions used in existing dialogs. -
File operations are browser-native - Use standard browser APIs for file download and upload. No server-side code needed.
-
No new external dependencies - All functionality must use native browser APIs and existing module code.
-
Test coverage expected - Story 3.2 added 25-30 tests. Aim for similar coverage (15-20 new tests for this story).
Implementation Order Recommendation
- Start with PresetImportExportManager - Core logic first, testable without UI
- Create dialogs and templates - UI components that use the manager
- Integrate with Director's Board - Add buttons and wire up handlers
- Update README - Documentation last
- Write tests throughout - Follow TDD approach used in Story 3.2
Critical Path Warnings
- Don't block on UI - Core logic in
PresetImportExportManagercan 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):
src/contracts/scene-preset.js- Understand the preset data structuresrc/core/ScenePresetManager.js- Understand existing preset management patternssrc/ui/gm/DirectorsBoard.js- Understand UI integration patternssrc/ui/gm/ConfirmationBar.js- Example of ApplicationV2 dialog patterntemplates/confirmation-bar.hbs- Template pattern referencestyles/components/_confirmation-bar.less- Styling pattern referencesrc/foundry/FoundryAdapter.js- Understand adapter pattern for Foundry API access
SHOULD READ:
module.js- Module initialization patterntests/unit/core/ScenePresetManager.test.js- Testing patternsarchitecture.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.