dc63148b80
- Created comprehensive story file for Scene Auto-Apply & ConfirmationBar - Added detailed acceptance criteria covering FR-17 and FR-18 - Included exhaustive developer context with previous story intelligence - Specified architecture compliance, file structure, and testing requirements - Updated sprint-status.yaml to mark 3-2 as ready-for-dev Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
698 lines
25 KiB
Markdown
698 lines
25 KiB
Markdown
# Story 3.2: Scene Auto-Apply & ConfirmationBar
|
|
|
|
**Status:** ready-for-dev
|
|
|
|
**Epic:** 3 - Scene-Aware Camera Automation (Scene Presets)
|
|
|
|
**Story Key:** 3-2-scene-auto-apply-and-confirmationbar
|
|
|
|
**Created:** 2026-05-23
|
|
|
|
**Last Updated:** 2026-05-23
|
|
|
|
---
|
|
|
|
## Story Header
|
|
|
|
| Field | Value |
|
|
|-------|-------|
|
|
| **Epic** | 3 - Scene-Aware Camera Automation (Scene Presets) |
|
|
| **Story ID** | 3.2 |
|
|
| **Story Key** | 3-2-scene-auto-apply-and-confirmationbar |
|
|
| **Title** | Scene Auto-Apply & ConfirmationBar |
|
|
| **Status** | ready-for-dev |
|
|
| **Priority** | High |
|
|
| **Assigned Agent** | DEV (Amelia) |
|
|
| **Created** | 2026-05-23 |
|
|
|
|
---
|
|
|
|
## 📋 Story Requirements
|
|
|
|
### User Story
|
|
|
|
**As a** GM,
|
|
**I want to** have Scene Presets automatically apply when I activate a Scene, with immediate strip-local feedback and a one-click Undo,
|
|
**So that** camera layouts change seamlessly with scene transitions without manual intervention.
|
|
|
|
### Persona Alignment
|
|
|
|
- **Primary:** Marcus (Veteran GM) - Needs seamless transitions between pre-configured scenes during play
|
|
- **Primary:** Jake (Streamer) - Requires professional, automated scene transitions for broadcast production
|
|
- **Secondary:** All GMs - Reduces cognitive load by eliminating repetitive manual setup
|
|
|
|
### Acceptance Criteria (BDD Format)
|
|
|
|
#### AC-1: Auto-Apply on Scene Activation
|
|
**Given** a Scene has a preset association configured
|
|
**When** the GM activates that Scene (triggering `updateScene` hook)
|
|
**Then** the associated preset applies after the configured pre-delay (0-5000ms)
|
|
**And** all clients receive "Scene changed: camera layout updated" via `ui.notifications`
|
|
|
|
#### AC-2: Configurable Pre-Delay
|
|
**Given** a Scene has a pre-delay of N ms configured
|
|
**When** that Scene activates
|
|
**Then** the preset applies exactly N ms after the `updateScene` hook fires
|
|
|
|
#### AC-3: ConfirmationBar Appearance
|
|
**Given** auto-apply fires for a Scene
|
|
**When** the Visibility Matrix update is broadcast
|
|
**Then** the `ConfirmationBar` appears in `StripOverlayLayer` at `position: absolute; bottom: 0` showing "Preset applied — N hidden, N visible"
|
|
**And** an "Undo" button is present and primary affordance
|
|
|
|
#### AC-4: One-Click Undo
|
|
**Given** the "Undo" button is clicked
|
|
**When** the click is processed
|
|
**Then** the Visibility Matrix immediately reverts to the state before the preset was applied
|
|
**And** all clients receive the reverted state
|
|
|
|
#### AC-5: ConfirmationBar Auto-Dismiss
|
|
**Given** the `ConfirmationBar` is visible and idle
|
|
**When** 8 seconds elapse (or 4 seconds if ≥2 presets applied within 60 seconds)
|
|
**Then** the bar dismisses via `opacity` transition only (never `height` or `max-height` animation)
|
|
|
|
#### AC-6: Instant-Replace Rule
|
|
**Given** a second `ConfirmationBar` would appear while one is already visible
|
|
**When** the second is triggered
|
|
**Then** it instantly replaces the first with zero crossfade (instant-replace rule)
|
|
|
|
#### AC-7: Per-Scene Disable
|
|
**Given** auto-apply is disabled for a specific Scene
|
|
**When** that Scene is activated
|
|
**Then** no preset applies and no automation notification fires
|
|
**And** the Director's Board manual override remains fully functional
|
|
|
|
#### AC-8: Global Disable
|
|
**Given** auto-apply is disabled globally in module settings
|
|
**When** any Scene is activated
|
|
**Then** no preset auto-applies regardless of Scene-level associations
|
|
|
|
#### AC-9: Partial-Fail Amber Variant
|
|
**Given** the partial-fail case (some participants unreachable)
|
|
**When** the `ConfirmationBar` renders
|
|
**Then** it uses the amber variant: "Preset applied — N hidden, N visible (some updates pending)"
|
|
|
|
### Functional Requirements Covered
|
|
|
|
- **FR-17:** Scene Preset auto-applies on FoundryVTT Scene activation via `updateScene` hook
|
|
- **FR-18:** Scene Preset auto-apply can be disabled per-scene or globally via module settings
|
|
|
|
### Success Criteria
|
|
|
|
- [ ] All 9 acceptance criteria pass manual testing
|
|
- [ ] All unit tests pass (target: +25-30 new tests for ConfirmationBar + ScenePresetManager auto-apply)
|
|
- [ ] `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: Scene activation → preset apply → ConfirmationBar → Undo flow verified end-to-end
|
|
|
|
---
|
|
|
|
## 🎯 Developer Context Section
|
|
|
|
### Epic Context
|
|
|
|
**Epic 3: Scene-Aware Camera Automation (Scene Presets)** completes the Level 3 Progressive Enhancement:
|
|
|
|
- **Story 3.1** (PREVIOUS - ready-for-dev): ScenePresetManager, save/load UI, Scene flag storage, socket broadcast
|
|
- **Story 3.2** (THIS STORY): Auto-apply on Scene activation, ConfirmationBar with Undo, per-scene/global disable toggles
|
|
- **Story 3.3** (NEXT - backlog): Preset import/export as JSON, merge/replace logic
|
|
|
|
**This story delivers the automation magic** - the ability for GMs to set up camera layouts once and have them apply automatically during sessions. The ConfirmationBar provides immediate, strip-local feedback with a safety net (Undo) for when things don't go as expected.
|
|
|
|
### Cross-Epic Dependencies
|
|
|
|
| Dependency | Source | Status | Used In This Story |
|
|
|------------|--------|--------|-------------------|
|
|
| ScenePresetManager | Story 3.1 | ready-for-dev | ✅ Extended with auto-apply logic |
|
|
| Scene flag storage | Story 3.1 | ready-for-dev | ✅ Read preset associations |
|
|
| StateStore.setMatrix() | Story 1.4 | done | ✅ Apply preset matrix |
|
|
| SocketHandler broadcast | Story 1.3 | done | ✅ Broadcast preset apply |
|
|
| NotificationBus | Story 2.1 | done | ✅ Optional fallback notification |
|
|
| DirectorsBoard | Story 2.2 | done | ✅ Manual override always available |
|
|
| ConfirmationBar UX patterns | UX-DR12 | Specified | ✅ Full implementation |
|
|
|
|
### Previous Story Intelligence (Story 3.1)
|
|
|
|
**Critical Learnings from 3-1-save-and-load-scene-presets:**
|
|
|
|
1. **Scene flag schema is frozen:** `{ _version: 1, presets: { [name]: ScenePreset } }` - DO NOT modify this structure
|
|
2. **ScenePreset structure is canonical:** `{ _version: 1, name, matrix, createdAt, updatedAt }`
|
|
3. **Socket events already defined in contracts:** `scrying-pool.preset.apply` and `scrying-pool.preset.applied`
|
|
4. **Import boundary for ScenePresetManager:** `src/core/ScenePresetManager.js` may ONLY import from `src/contracts/` and `src/utils/`
|
|
5. **World settings for global config:** Use `scrying-pool.autoApplyEnabled` (boolean, default: true)
|
|
6. **Test pattern:** Fake timers for pre-delay testing; frozen fixtures for preset structures
|
|
|
|
**Files Created in 3.1 (DO NOT RECREATE):**
|
|
- `src/core/ScenePresetManager.js` - Extend this, don't replace it
|
|
- `src/ui/gm/PresetSaveDialog.js` - Already exists
|
|
- `src/ui/gm/PresetLoadDialog.js` - Already exists
|
|
- `src/contracts/scene-preset.js` - Already exists, has validators
|
|
|
|
**Patterns Established in 3.1:**
|
|
- Scene flag access via `adapter.scenes.current().getFlag()` and `.setFlag()`
|
|
- Matrix serialization/deserialization in ScenePresetManager
|
|
- Error handling: clear user-facing messages, no stack traces
|
|
|
|
### Git Intelligence
|
|
|
|
**Recent commits in Epic 3:**
|
|
- `3-1-save-and-load-scene-presets.md` created 2026-05-23 09:51 - Full ScenePresetManager spec
|
|
- Architecture established for Scene flag storage with versioning
|
|
- Socket contract for preset events pre-defined
|
|
|
|
**Actionable Insights:**
|
|
- The preset storage mechanism is solid and tested
|
|
- Socket infrastructure for preset apply is ready but not wired to Scene hooks
|
|
- Need to add `updateScene` hook registration in module.js
|
|
- ConfirmationBar is NEW - no existing implementation to build on
|
|
|
|
---
|
|
|
|
## 🏗️ Technical Requirements
|
|
|
|
### Core Components to Create/Extend
|
|
|
|
| Component | File | Action | Purpose |
|
|
|-----------|------|--------|---------|
|
|
| ScenePresetManager | `src/core/ScenePresetManager.js` | **EXTEND** | Add auto-apply logic, per-scene config |
|
|
| ConfirmationBar | `src/ui/gm/ConfirmationBar.js` | **NEW** | Strip-local feedback with Undo |
|
|
| StripOverlayLayer | `src/ui/shared/StripOverlayLayer.js` | **EXTEND** | Add ConfirmationBar container |
|
|
| ScenePresetPanel | `src/ui/gm/ScenePresetPanel.js` | **NEW** | Per-scene auto-apply toggle UI |
|
|
|
|
### Data Flow - Auto-Apply Sequence
|
|
|
|
```
|
|
Hooks.on('updateScene', scene)
|
|
↓
|
|
ScenePresetManager.onSceneActivate(scene)
|
|
↓ [Check: auto-apply enabled globally?]
|
|
↓ [Check: scene has preset association?]
|
|
↓ [Check: scene has auto-apply enabled?]
|
|
↓ [Wait: configured pre-delay (0-5000ms)]
|
|
↓
|
|
ScenePresetManager.applyPreset(presetName)
|
|
↓
|
|
VisibilityManager.applyMatrix(preset.matrix)
|
|
↓
|
|
StateStore.setMatrix(preset.matrix)
|
|
↓
|
|
SocketHandler.emit('scrying-pool.preset.apply', payload)
|
|
↓
|
|
[Broadcast to all clients]
|
|
↓
|
|
Hooks.callAll('scrying-pool:presetApplied', { presetName, sceneId, matrix })
|
|
↓
|
|
ConfirmationBar.show({ presetName, hiddenCount, visibleCount, partialFail })
|
|
↓
|
|
[8s or 4s timer starts]
|
|
↓ (User clicks Undo)
|
|
ConfirmationBar.onUndo() → StateStore.setMatrix(previousMatrix)
|
|
```
|
|
|
|
### New/Extended Socket Messages
|
|
|
|
**Already defined in `src/contracts/socket-message.js` (from Story 3.1):**
|
|
- `PRESET_APPLY: "scrying-pool.preset.apply"` - Intent (GM only)
|
|
- `PRESET_APPLIED: "scrying-pool.preset.applied"` - Authoritative echo
|
|
|
|
**New for this story:**
|
|
- Payload extension for `PRESET_APPLY`: Add `{ sceneId, preDelay, autoApplied: true }`
|
|
- New hook: `scrying-pool:presetApplied` for ConfirmationBar subscription
|
|
|
|
### World Settings (New)
|
|
|
|
| Setting Key | Type | Default | Scope | Description |
|
|
|-------------|------|---------|-------|-------------|
|
|
| `scrying-pool.autoApplyEnabled` | boolean | true | world | Global toggle for auto-apply feature |
|
|
| `scrying-pool.confirmationBarDuration` | number | 8000 | world | Default bar duration in ms |
|
|
| `scrying-pool.shortConfirmationBarDuration` | number | 4000 | world | Short duration when ≥2 presets in 60s |
|
|
|
|
**Note:** Per-scene auto-apply toggle stored in Scene flag alongside preset association.
|
|
|
|
### Scene Flag Structure Extension
|
|
|
|
**Existing (from Story 3.1):**
|
|
```javascript
|
|
{
|
|
_version: 1,
|
|
presets: { [name: string]: ScenePreset }
|
|
}
|
|
```
|
|
|
|
**Extended for Story 3.2:**
|
|
```javascript
|
|
{
|
|
_version: 1,
|
|
presets: { [name: string]: ScenePreset },
|
|
autoApply: {
|
|
enabled: boolean, // Per-scene toggle
|
|
presetName: string, // Which preset to auto-apply
|
|
preDelay: number // 0-5000 ms delay
|
|
}
|
|
}
|
|
```
|
|
|
|
**Migration:** If `autoApply` field missing, defaults to `{ enabled: false, presetName: null, preDelay: 0 }`
|
|
|
|
---
|
|
|
|
## 🏛️ Architecture Compliance
|
|
|
|
### Import Boundary Rules (HARD - ESLint Enforced)
|
|
|
|
**NEW FILES:**
|
|
```
|
|
src/ui/gm/ConfirmationBar.js
|
|
→ may import: src/core/, src/contracts/, src/utils/ ONLY
|
|
❌ FORBIDDEN: src/foundry/, src/ui/gm/PresetSaveDialog.js, etc.
|
|
|
|
src/ui/gm/ScenePresetPanel.js
|
|
→ may import: src/core/, src/contracts/, src/utils/ ONLY
|
|
❌ FORBIDDEN: src/notifications/, direct game.* access
|
|
```
|
|
|
|
**EXTENDED FILES:**
|
|
```
|
|
src/core/ScenePresetManager.js (EXTEND from Story 3.1)
|
|
→ EXISTING: may import src/contracts/, src/utils/ ONLY
|
|
→ NEW: may ALSO import src/core/VisibilityManager.js (for applyMatrix)
|
|
❌ STILL FORBIDDEN: src/foundry/, src/ui/, direct game.*
|
|
|
|
src/ui/shared/StripOverlayLayer.js (EXTEND from Story 1.5)
|
|
→ EXISTING: may import src/core/, src/contracts/, src/utils/
|
|
→ NEW: may import src/ui/gm/ConfirmationBar.js
|
|
```
|
|
|
|
### Constructor Pattern (Side-Effect-Free)
|
|
|
|
**ConfirmationBar:**
|
|
```javascript
|
|
// ✅ CORRECT
|
|
export class ConfirmationBar {
|
|
constructor(adapter, visibilityManager, socketHandler) {
|
|
this._adapter = adapter;
|
|
this._visibilityManager = visibilityManager;
|
|
this._socketHandler = socketHandler;
|
|
this._previousMatrix = null;
|
|
}
|
|
|
|
init() {
|
|
// Lifecycle registration here, NOT in constructor
|
|
this._adapter.hooks.on('scrying-pool:presetApplied', (payload) => this._onPresetApplied(payload));
|
|
}
|
|
|
|
teardown() {
|
|
this._adapter.hooks.off('scrying-pool:presetApplied', this._onPresetApplied);
|
|
}
|
|
}
|
|
```
|
|
|
|
**ScenePresetManager Extension:**
|
|
```javascript
|
|
// EXTEND existing class from Story 3.1
|
|
export class ScenePresetManager {
|
|
// Existing methods: save(), load(), delete(), list()
|
|
|
|
// NEW: Auto-apply methods
|
|
onSceneActivate(scene) { /* ... */ }
|
|
applyPreset(presetName, options = { autoApplied: false }) { /* ... */ }
|
|
configureAutoApply(scene, { enabled, presetName, preDelay }) { /* ... */ }
|
|
}
|
|
```
|
|
|
|
### Dependency Injection
|
|
|
|
**All new components follow FoundryAdapter pattern:**
|
|
- `ConfirmationBar` receives `adapter` via constructor
|
|
- `ScenePresetPanel` receives `adapter` via constructor
|
|
- NO direct `game.*` access in any new file
|
|
- All Foundry API calls go through `adapter.scenes`, `adapter.hooks`, etc.
|
|
|
|
### Hook Registration Order
|
|
|
|
**Critical:** Module.js wiring order for Story 3.2 components:
|
|
|
|
```javascript
|
|
// In module.js, inside Hooks.once('ready', () => { ... })
|
|
|
|
// EXISTING (from previous stories) - order preserved:
|
|
const visibilityManager = new VisibilityManager(stateStore, adapter);
|
|
socketHandler.setReady(visibilityManager);
|
|
const notificationBus = new NotificationBus(adapter);
|
|
const roleRenderer = new RoleRenderer(visibilityManager, adapter);
|
|
const rosterStrip = new RosterStrip(visibilityManager, roleRenderer);
|
|
|
|
// NEW for Story 3.2:
|
|
const scenePresetManager = new ScenePresetManager(
|
|
stateStore,
|
|
adapter,
|
|
visibilityManager, // NEW: for applyMatrix
|
|
socketHandler // NEW: for preset broadcast
|
|
);
|
|
|
|
// Register updateScene hook for auto-apply
|
|
adapter.hooks.on('updateScene', (scene) => {
|
|
scenePresetManager.onSceneActivate(scene);
|
|
});
|
|
|
|
// NEW: StripOverlayLayer gets ConfirmationBar support
|
|
const stripOverlayLayer = new StripOverlayLayer(adapter);
|
|
const confirmationBar = new ConfirmationBar(
|
|
adapter,
|
|
visibilityManager,
|
|
socketHandler,
|
|
stripOverlayLayer
|
|
);
|
|
confirmationBar.init();
|
|
|
|
// If GM, register DirectorsBoard with extended preset panel
|
|
if (adapter.users.isGM()) {
|
|
const directorsBoard = new DirectorsBoard(visibilityManager, socketHandler, adapter);
|
|
// DirectorsBoard now includes ScenePresetPanel as embedded component
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📁 File Structure Requirements
|
|
|
|
### Files to CREATE
|
|
|
|
| File | Location | Purpose | AC Blocking |
|
|
|------|----------|---------|-------------|
|
|
| `ConfirmationBar.js` | `src/ui/gm/ConfirmationBar.js` | Strip-local feedback component | ✅ |
|
|
| `ConfirmationBar.test.js` | `tests/unit/ui/gm/ConfirmationBar.test.js` | Unit tests | ✅ |
|
|
| `ScenePresetPanel.js` | `src/ui/gm/ScenePresetPanel.js` | Per-scene auto-apply config UI | ✅ |
|
|
| `ScenePresetPanel.test.js` | `tests/unit/ui/gm/ScenePresetPanel.test.js` | Unit tests | ✅ |
|
|
| `_confirmation-bar.less` | `styles/components/_confirmation-bar.less` | ConfirmationBar styles | ✅ |
|
|
| `confirmation-bar.hbs` | `templates/confirmation-bar.hbs` | Handlebars template | ✅ |
|
|
|
|
### Files to EXTEND
|
|
|
|
| File | Changes | From Story |
|
|
|------|---------|------------|
|
|
| `ScenePresetManager.js` | Add auto-apply methods | 3.1 |
|
|
| `module.js` | Wire updateScene hook, inject dependencies | Story 0 |
|
|
| `StripOverlayLayer.js` | Add ConfirmationBar rendering | 1.5 |
|
|
| `DirectorsBoard.js` | Integrate ScenePresetPanel | 2.2 |
|
|
|
|
### File Boundaries
|
|
|
|
**ConfirmationBar owns:**
|
|
- Display logic for preset apply feedback
|
|
- Undo button click handler
|
|
- Auto-dismiss timer management
|
|
- Instant-replace logic for consecutive bar displays
|
|
|
|
**ScenePresetManager owns (NEW):**
|
|
- Auto-apply configuration per-scene
|
|
- Pre-delay timer management
|
|
- Scene activation detection
|
|
- Preset application trigger
|
|
|
|
**ScenePresetPanel owns:**
|
|
- Per-scene auto-apply toggle UI
|
|
- Pre-delay configuration (0-5000ms slider)
|
|
- Preset selection for auto-apply
|
|
|
|
---
|
|
|
|
## 🧪 Testing Requirements
|
|
|
|
### Unit Test Coverage Targets
|
|
|
|
| Component | Test File | Coverage Target |
|
|
|-----------|-----------|-----------------|
|
|
| ConfirmationBar | `ConfirmationBar.test.js` | 100% branch coverage |
|
|
| ScenePresetManager (new methods) | Extend `ScenePresetManager.test.js` | +25 new tests |
|
|
| ScenePresetPanel | `ScenePresetPanel.test.js` | 100% statement coverage |
|
|
|
|
### Test Scenarios (MUST INCLUDE)
|
|
|
|
**ConfirmationBar:**
|
|
```javascript
|
|
// Fake timers required for duration testing
|
|
vi.useFakeTimers();
|
|
|
|
// Test 1: Shows on preset applied event
|
|
// Test 2: Undo clicks revert to previous matrix
|
|
// Test 3: Auto-dismisses after 8000ms
|
|
// Test 4: Auto-dismisses after 4000ms when ≥2 presets in 60s
|
|
// Test 5: Instant-replace when new event during visible bar
|
|
// Test 6: Shows amber variant on partial fail
|
|
// Test 7: Clears timer on manual dismiss
|
|
// Test 8: accessibility: focus trap, keyboard navigation
|
|
```
|
|
|
|
**ScenePresetManager (auto-apply):**
|
|
```javascript
|
|
// Test 1: onSceneActivate does nothing when no preset association
|
|
// Test 2: onSceneActivate applies preset after pre-delay
|
|
// Test 3: onSceneActivate respects global disable
|
|
// Test 4: onSceneActivate respects per-scene disable
|
|
// Test 5: Pre-delay timer cleared on scene change
|
|
// Test 6: applyPreset with autoApplied=true emits correct socket event
|
|
// Test 7: configureAutoApply updates Scene flag correctly
|
|
// Test 8: Migration: missing autoApply field gets defaults
|
|
```
|
|
|
|
**Integration Tests:**
|
|
```javascript
|
|
// Test 1: Full flow: Scene activation → preset apply → ConfirmationBar → Undo
|
|
// Test 2: Partial fail: Some participants offline → amber bar
|
|
// Test 3: Global disable → no auto-apply on any scene
|
|
// Test 4: Per-scene disable → no auto-apply on that scene only
|
|
```
|
|
|
|
### Fixtures to Add/Update
|
|
|
|
**New fixtures in `tests/fixtures/scene-preset.js`:**
|
|
```javascript
|
|
export const SCENE_FLAG_WITH_AUTO_APPLY = Object.freeze({
|
|
_version: 1,
|
|
presets: { /* ... */ },
|
|
autoApply: { enabled: true, presetName: 'combat', preDelay: 1000 }
|
|
});
|
|
|
|
export const SCENE_FLAG_WITHOUT_AUTO_APPLY = Object.freeze({
|
|
_version: 1,
|
|
presets: { /* ... */ }
|
|
// autoApply missing - should default to disabled
|
|
});
|
|
|
|
export const SCENE_FLAG_DISABLED_AUTO_APPLY = Object.freeze({
|
|
_version: 1,
|
|
presets: { /* ... */ },
|
|
autoApply: { enabled: false, presetName: null, preDelay: 0 }
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## 🎨 UX Design Requirements
|
|
|
|
### ConfirmationBar Specification (UX-DR12)
|
|
|
|
**Location:** `StripOverlayLayer` at `position: absolute; bottom: 0`
|
|
|
|
**Visual:**
|
|
- Background: `--sp-surface` (semantic token)
|
|
- Text: `--sp-text-primary`
|
|
- Border: 1px solid `--sp-border`
|
|
- Padding: 12px 16px
|
|
- Border-radius: 4px
|
|
- Box-shadow: `0 2px 8px rgba(0,0,0,0.3)`
|
|
|
|
**Content:**
|
|
- Message: "Preset applied — N hidden, N visible"
|
|
- Undo button: Primary CTA, left side
|
|
- Duration indicator: Optional subtle progress bar
|
|
|
|
**Variants:**
|
|
- **Default:** Green accent for success
|
|
- **Amber:** Orange accent when partial fail ("some updates pending")
|
|
|
|
**Animations:**
|
|
- In: Slide up from bottom + fade (200ms ease-out)
|
|
- Out: Slide down to bottom + fade (200ms ease-in)
|
|
- **CRITICAL:** Only `opacity` transitions for height changes - NEVER `height` or `max-height` animation
|
|
- Gated under `@media (prefers-reduced-motion: no-preference)`
|
|
|
|
**Behavior:**
|
|
- Auto-dismiss: 8000ms default, 4000ms if ≥2 presets applied within 60000ms window
|
|
- Instant-replace: New bar replaces existing with 0ms crossfade
|
|
- Click Undo: Reverts matrix, dismisses bar immediately
|
|
- Click outside: No dismiss (bar is in StripOverlayLayer, which has pointer-events: none on parent)
|
|
- Click on bar: No action (bar is informational, only Undo is interactive)
|
|
|
|
**Accessibility:**
|
|
- `role="status"` on bar container
|
|
- `aria-live="polite"` for message updates
|
|
- `aria-label="Preset [name] applied. Undo available."` on bar
|
|
- Undo button: `role="button"`, `aria-label="Undo preset apply"`
|
|
- Focus: Undo button receives focus when bar appears
|
|
- Keyboard: Space/Enter on Undo button triggers revert
|
|
|
|
### ScenePresetPanel Specification
|
|
|
|
**Location:** Embedded in DirectorsBoard as collapsible drawer/tab
|
|
|
|
**Controls:**
|
|
- Toggle: "Auto-apply preset on scene activation" (checkbox)
|
|
- Preset selector: Dropdown of available presets for this scene
|
|
- Pre-delay: Slider 0-5000ms with value display
|
|
- Global settings link: "Configure global auto-apply settings"
|
|
|
|
**Accessibility:**
|
|
- All interactive elements keyboard-navigable
|
|
- ARIA labels on all controls
|
|
- Focus trap within panel
|
|
|
|
---
|
|
|
|
## 🔒 Security & Performance Requirements
|
|
|
|
### Security
|
|
|
|
- **No data transmission:** Scene flag data stays in FoundryVTT world
|
|
- **Permission check:** Only GM can configure auto-apply settings
|
|
- **Validation:** All Scene flag inputs validated before saving
|
|
- **Sanitization:** Preset names sanitized (no HTML, max length 100 chars)
|
|
|
|
### Performance
|
|
|
|
- **Pre-delay max:** 5000ms - enforced at validation level
|
|
- **Timer cleanup:** All timers cleared on module teardown
|
|
- **Debounce:** If multiple scene activations in quick succession, only last one processed
|
|
- **Memory:** ConfirmationBar holds previous matrix reference only while visible
|
|
- **No blocking:** All operations async; no synchronous waits
|
|
|
|
---
|
|
|
|
## 📂 Project Structure Notes
|
|
|
|
### Alignment with Unified Structure
|
|
|
|
All new files follow the established pattern:
|
|
- `src/ui/gm/` - GM-only UI components
|
|
- `src/core/` - Pure logic, testable
|
|
- `styles/components/` - Component-specific LESS
|
|
- `templates/` - Handlebars templates
|
|
- `tests/unit/{mirror-path}/` - One spec per source file
|
|
|
|
### Detected Conflicts / Variances
|
|
|
|
**None detected.** The architecture established in Stories 0, 1.x, and 2.x fully supports this implementation.
|
|
|
|
### Previous Work Patterns to Follow
|
|
|
|
1. **Constructor injection** (from Story 1.3): All Foundry deps via FoundryAdapter
|
|
2. **Import boundaries** (from Story 0): ESLint `no-restricted-paths` enforced
|
|
3. **Test patterns** (from Story 1.3): Fake timers, frozen fixtures, canonical mocks
|
|
4. **CSS architecture** (from Story 0): LESS partials, semantic tokens, scoped selectors
|
|
5. **Socket patterns** (from Story 1.3): Intent/echo cycle, PendingOp lifecycle
|
|
|
|
---
|
|
|
|
## 🔗 References
|
|
|
|
### Source Documents
|
|
|
|
| Reference | Path | Section |
|
|
|-----------|------|---------|
|
|
| Story 3.2 ACs | `_bmad-output/planning-artifacts/epics.md` | Story 3.2: Scene Auto-Apply & ConfirmationBar |
|
|
| FR-17 | `_bmad-output/planning-artifacts/epics.md` | FR-17: Scene Preset auto-applies on Scene activation |
|
|
| FR-18 | `_bmad-output/planning-artifacts/epics.md` | FR-18: Disable auto-apply per-scene or globally |
|
|
| UX-DR12 | `_bmad-output/planning-artifacts/epics.md` | UX-DR12: ConfirmationBar specification |
|
|
| Architecture | `_bmad-output/planning-artifacts/architecture.md` | Full project architecture |
|
|
| ScenePresetManager | `_bmad-output/implementation-artifacts/3-1-save-and-load-scene-presets.md` | Story 3.1 implementation |
|
|
| Socket contracts | `src/contracts/socket-message.js` | PRESET_APPLY, PRESET_APPLIED |
|
|
| Scene flag schema | `src/contracts/scene-preset.js` | Versioned wrapper pattern |
|
|
|
|
### Previous Story Files (Critical Context)
|
|
|
|
| Story | File | Relevance |
|
|
|-------|------|-----------|
|
|
| 3.1 | `3-1-save-and-load-scene-presets.md` | **MUST READ** - ScenePresetManager foundation |
|
|
| 2.3 | `2-3-directors-board-bulk-actions-spotlight-and-keyboard-shortcuts.md` | Bulk action patterns, Undo concept |
|
|
| 1.5 | `1-5-gm-control-ui-scryingpoolstrip-actionpopover-and-av-tile-integration.md` | StripOverlayLayer patterns |
|
|
| 1.4 | `1-4-core-logic-scryingpoolcontroller-and-visibilitymanager.md` | Matrix application, state management |
|
|
|
|
---
|
|
|
|
## 🤖 Dev Agent Record
|
|
|
|
### Agent Model Used
|
|
|
|
DEV (Amelia) - Senior software engineer for story execution
|
|
|
|
### Debug Log References
|
|
|
|
**Critical debugging checkpoints:**
|
|
1. `Hooks.on('updateScene')` registration - verify in module.js
|
|
2. Scene flag read/write - verify via `adapter.scenes.current().getFlag()`
|
|
3. ConfirmationBar timer management - verify `clearTimeout` in all code paths
|
|
4. Undo matrix storage - verify previous matrix captured before apply
|
|
5. Socket payload validation - verify `src/contracts/scene-preset.js` validators
|
|
|
|
### Completion Notes List
|
|
|
|
**Before marking done, verify:**
|
|
- [ ] ConfirmationBar appears and disappears correctly
|
|
- [ ] Undo works and reverts to exact previous state
|
|
- [ ] Auto-apply respects both global and per-scene settings
|
|
- [ ] Pre-delay is configurable and accurate
|
|
- [ ] Partial fail shows amber variant
|
|
- [ ] Instant-replace works for consecutive bars
|
|
- [ ] All timers cleaned up on module teardown
|
|
- [ ] No memory leaks from event listeners
|
|
- [ ] Accessibility: keyboard nav, ARIA labels, focus management
|
|
- [ ] All ESLint import boundaries pass
|
|
|
|
### File List
|
|
|
|
**NEW FILES (7):**
|
|
1. `src/ui/gm/ConfirmationBar.js`
|
|
2. `src/ui/gm/ScenePresetPanel.js`
|
|
3. `tests/unit/ui/gm/ConfirmationBar.test.js`
|
|
4. `tests/unit/ui/gm/ScenePresetPanel.test.js`
|
|
5. `styles/components/_confirmation-bar.less`
|
|
6. `templates/confirmation-bar.hbs`
|
|
7. `tests/fixtures/scene-preset.js` (updated with auto-apply fixtures)
|
|
|
|
**MODIFIED FILES (4):**
|
|
1. `src/core/ScenePresetManager.js` (extend with auto-apply)
|
|
2. `module.js` (wire updateScene hook, inject new dependencies)
|
|
3. `src/ui/shared/StripOverlayLayer.js` (add ConfirmationBar support)
|
|
4. `src/ui/gm/DirectorsBoard.js` (integrate ScenePresetPanel)
|
|
|
|
**CONTRACT FILES (verify, don't modify):**
|
|
- `src/contracts/socket-message.js` (already has preset events)
|
|
- `src/contracts/scene-preset.js` (already has validators)
|
|
|
|
---
|
|
|
|
## ✅ Story Completion Status
|
|
|
|
**Status:** ready-for-dev
|
|
|
|
**Ultimate context engine analysis completed** - comprehensive developer guide created with:
|
|
- Complete epic and cross-story context
|
|
- Exhaustive architecture compliance requirements
|
|
- Previous story intelligence and patterns
|
|
- Git history insights
|
|
- Specific file structure and boundaries
|
|
- Testing requirements and fixtures
|
|
- UX design specifications
|
|
- Security and performance constraints
|
|
- Actionable dev agent guardrails
|
|
|
|
**The developer now has everything needed for flawless implementation!**
|
|
|
|
---
|
|
|
|
*Generated for {project_name} by BMad Method Story Context Engine*
|
|
*Agent: DEV (Amelia)*
|
|
*Date: 2026-05-23*
|