7a0d935239
Implements configurable video widget widths for small and large dock layouts: - Add widgetWidthSm and widgetWidthMd world settings (defaults: 83px, 150px) - Add width dropdown selectors in Director's Board UI - Apply width settings to participant avatars via CSS custom properties - Update strip width/height calculations to use custom widths - Add localization strings for widget width settings - Add CSS styling for widget width dropdown controls - Fix Handlebars template syntax for select element selected values Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
610 lines
19 KiB
Markdown
610 lines
19 KiB
Markdown
# Story 5.2: Video Widget Width Customization
|
|
|
|
**Status:** done
|
|
|
|
**Epic:** 5 - Full AV Replacement
|
|
|
|
**Story Key:** 5-2-video-widget-width-customization
|
|
|
|
**Created:** 2026-05-26
|
|
|
|
**Last Updated:** 2026-05-26
|
|
|
|
**Completed:** 2026-05-26
|
|
|
|
**Version:** v0.1.1
|
|
|
|
---
|
|
|
|
## Story Header
|
|
|
|
| Field | Value |
|
|
|-------|-------|
|
|
| **Epic** | 5 - Full AV Replacement |
|
|
| **Story ID** | 5.2 |
|
|
| **Story Key** | 5-2-video-widget-width-customization |
|
|
| **Title** | Video Widget Width Customization |
|
|
| **Status** | ready-for-dev |
|
|
| **Priority** | Medium |
|
|
| **Assigned Agent** | DEV (Mistral Vibe / Morr) |
|
|
| **Created** | 2026-05-26 |
|
|
| **Last Updated** | 2026-05-26 |
|
|
| **Target Version** | v0.1.1 |
|
|
|
|
---
|
|
|
|
## 📋 Story Requirements
|
|
|
|
### User Story
|
|
|
|
**As a** GM using Video View Manager,
|
|
**I want to** select custom widths for small and large video widgets in the dock,
|
|
**So that** I can optimize the display to match my screen space and table preferences.
|
|
|
|
### Persona Alignment
|
|
|
|
- **Primary:** GM (Marcus, Jake) - Needs flexibility to customize video display for different screen configurations and table setups
|
|
- **Secondary:** Players - Benefit from consistent, appropriately-sized video feeds that match the GM's layout preferences
|
|
|
|
### Business Value
|
|
|
|
This feature extends the Dock Layout System (Epic 5, Story 5-1) by adding granular control over video widget dimensions. It addresses user feedback requesting the ability to customize video tile sizes to better match their specific display needs and table aesthetics.
|
|
|
|
### Acceptance Criteria (BDD Format)
|
|
|
|
#### AC-1: Widget Width Settings
|
|
**Given** the Video View Manager module is active
|
|
**When** the GM opens the Director's Board
|
|
**Then** there are dropdown selectors for small and large widget widths
|
|
**And** the dropdowns contain the following options: 60px, 80px, 100px, 120px, 150px, 200px
|
|
|
|
#### AC-2: Default Width Values
|
|
**Given** a fresh installation of Video View Manager
|
|
**When** no width settings have been configured
|
|
**Then** the default width for small widgets is 80px
|
|
**And** the default width for large widgets is 120px
|
|
|
|
#### AC-3: Width Setting Application
|
|
**Given** the GM has selected width values in the Director's Board
|
|
**When** the GM saves the settings
|
|
**Then** all video widgets in the dock are re-rendered with the new widths
|
|
**And** small layout variants (vertical-sm, horizontal-sm, mosaic-sm) use the small width setting
|
|
**And** large layout variants (vertical-md, horizontal-md, mosaic-md) use the large width setting
|
|
|
|
#### AC-4: Width Persistence
|
|
**Given** the GM has configured custom widget widths
|
|
**When** the page is refreshed or the session is restarted
|
|
**Then** the widget widths persist and are applied automatically
|
|
**And** the dropdowns show the previously selected values
|
|
|
|
#### AC-5: Per-Layout Width Application
|
|
**Given** the GM has selected different widths for small and large
|
|
**When** the dock is using a small layout variant
|
|
**Then** all video widgets use the small width setting
|
|
**And** when the dock is using a large layout variant
|
|
**Then** all video widgets use the large width setting
|
|
|
|
#### AC-6: Real-Time Preview
|
|
**Given** the Director's Board is open
|
|
**When** the GM changes a width dropdown value
|
|
**Then** the video widgets in the dock update in real-time (within 500ms)
|
|
**And** no page refresh is required
|
|
|
|
#### AC-7: Width Setting Validation
|
|
**Given** the width dropdown is displayed
|
|
**When** the GM selects a width value
|
|
**Then** the value is validated as one of the allowed options
|
|
**And** invalid values are rejected with a warning message
|
|
**And** the previous valid value is retained
|
|
|
|
#### AC-8: CSS Properly Scoped
|
|
**Given** custom widths are applied
|
|
**When** the video widgets are rendered
|
|
**Then** all width-related CSS is properly scoped under `.sp-participant-item` or `.sp-participant-video`
|
|
**And** no unintended CSS conflicts occur with other modules
|
|
|
|
#### AC-9: Null Safety Throughout
|
|
**Given** any DOM query returns null
|
|
**When** width application methods are called
|
|
**Then** no TypeError is thrown
|
|
**And** appropriate warnings are logged to console
|
|
|
|
---
|
|
|
|
## 🎯 Functional Requirements
|
|
|
|
- **FR-33:** Video widget width configuration — The module provides configurable width options for small and large video tiles via world-scoped settings `widgetWidthSm` and `widgetWidthMd`
|
|
- **FR-34:** Width selection dropdown in Director's Board — The Director's Board includes dropdown UI controls for selecting video widget widths for both small and large sizes
|
|
|
|
---
|
|
|
|
## 🎯 Implementation Details
|
|
|
|
### Files to Modify
|
|
|
|
| File | Changes | Status |
|
|
|------|---------|--------|
|
|
| `module.js` | Register `widgetWidthSm` and `widgetWidthMd` world settings with onChange callbacks | Pending |
|
|
| `src/ui/gm/DirectorsBoard.js` | Add width dropdown selectors to settings panel, add handler for width changes | Pending |
|
|
| `src/ui/gm/ScryingPoolStrip.js` | Apply width settings to video elements, pass width values to template context | Pending |
|
|
| `templates/directors-board.hbs` | Add dropdown UI for width selection in settings panel | Pending |
|
|
| `styles/components/_roster-strip.less` | Use width settings for `.sp-participant-item` sizing | Pending |
|
|
| `lang/en.json` | Add localization strings for width settings | Pending |
|
|
|
|
### Technical Implementation
|
|
|
|
#### Settings Registration (module.js)
|
|
```javascript
|
|
// World-scoped settings for widget widths
|
|
adapter.settings.register("widgetWidthSm", {
|
|
scope: "world",
|
|
config: false,
|
|
type: String,
|
|
default: "80",
|
|
onChange: () => roleRenderer?.rerenderStrip(),
|
|
});
|
|
|
|
adapter.settings.register("widgetWidthMd", {
|
|
scope: "world",
|
|
config: false,
|
|
type: String,
|
|
default: "120",
|
|
onChange: () => roleRenderer?.rerenderStrip(),
|
|
});
|
|
```
|
|
|
|
#### Director's Board UI (DirectorsBoard.js)
|
|
```javascript
|
|
// Add to _prepareContext()
|
|
const widgetWidthSm = this._adapter.settings?.get?.('widgetWidthSm') ?? '80';
|
|
const widgetWidthMd = this._adapter.settings?.get?.('widgetWidthMd') ?? '120';
|
|
|
|
const WIDTH_OPTIONS = [
|
|
{ value: '60', label: '60px' },
|
|
{ value: '80', label: '80px' },
|
|
{ value: '100', label: '100px' },
|
|
{ value: '120', label: '120px' },
|
|
{ value: '150', label: '150px' },
|
|
{ value: '200', label: '200px' },
|
|
];
|
|
|
|
// Add to context
|
|
return {
|
|
...base,
|
|
widthOptions: WIDTH_OPTIONS,
|
|
widgetWidthSm,
|
|
widgetWidthMd,
|
|
};
|
|
|
|
// Add handler in _onClickButton()
|
|
case 'set-widget-width-sm': this._onSetWidgetWidth(btn.dataset.value, 'sm'); break;
|
|
case 'set-widget-width-md': this._onSetWidgetWidth(btn.dataset.value, 'md'); break;
|
|
|
|
// New method
|
|
async _onSetWidgetWidth(value, size) {
|
|
if (!value) return;
|
|
const settingKey = size === 'sm' ? 'widgetWidthSm' : 'widgetWidthMd';
|
|
try {
|
|
await this._adapter.settings?.set?.(settingKey, value);
|
|
} catch (err) {
|
|
console.error('[ScryingPool] Failed to set widget width:', err);
|
|
}
|
|
if (this.rendered) this.render({ force: true });
|
|
}
|
|
```
|
|
|
|
#### ScryingPoolStrip Context (ScryingPoolStrip.js)
|
|
```javascript
|
|
// Add to _prepareContext()
|
|
const widgetWidthSm = this._adapter.settings?.get?.('widgetWidthSm') ?? '80';
|
|
const widgetWidthMd = this._adapter.settings?.get?.('widgetWidthMd') ?? '120';
|
|
const dockLayout = this.context?.dockLayout ?? 'vertical-sm';
|
|
const isLarge = dockLayout.endsWith('-md');
|
|
const effectiveWidth = isLarge ? widgetWidthMd : widgetWidthSm;
|
|
|
|
return {
|
|
...base,
|
|
widgetWidth: effectiveWidth,
|
|
isLarge,
|
|
};
|
|
```
|
|
|
|
#### Template Updates (directors-board.hbs)
|
|
```handlebars
|
|
{{!-- In settings panel --}}
|
|
<div class="sp-settings-group">
|
|
<label>Video Widget Widths</label>
|
|
|
|
<div class="sp-settings-row">
|
|
<span>Small:</span>
|
|
<select data-action="set-widget-width-sm">
|
|
{{#each widthOptions}}
|
|
<option value="{{this.value}}" {{../widgetWidthSm == this.value ? "selected" : ""}}>
|
|
{{this.label}}
|
|
</option>
|
|
{{/each}}
|
|
</select>
|
|
</div>
|
|
|
|
<div class="sp-settings-row">
|
|
<span>Large:</span>
|
|
<select data-action="set-widget-width-md">
|
|
{{#each widthOptions}}
|
|
<option value="{{this.value}}" {{../widgetWidthMd == this.value ? "selected" : ""}}>
|
|
{{this.label}}
|
|
</option>
|
|
{{/each}}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
#### CSS Updates (components/_roster-strip.less)
|
|
```less
|
|
// Apply width to participant items
|
|
.sp-participant-item {
|
|
width: var(--widget-width, 80px);
|
|
min-width: var(--widget-width, 80px);
|
|
|
|
// Or use inline style from template
|
|
// width: {{widgetWidth}}px;
|
|
// min-width: {{widgetWidth}}px;
|
|
}
|
|
|
|
// Ensure video elements fill the width
|
|
.sp-participant-video {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🏗️ Architecture Compliance
|
|
|
|
### Design Token Usage
|
|
|
|
✅ All width values use CSS pixel units (px) for consistency with FoundryVTT conventions
|
|
✅ No direct Foundry `--color-*`/`--font-*`/`--border-*` tokens used
|
|
✅ All selectors properly scoped under `.sp-*` prefix
|
|
|
|
### Import Boundaries
|
|
|
|
✅ No direct `game.*` access in core logic
|
|
✅ All Foundry API access via FoundryAdapter
|
|
✅ Settings access via adapter.settings
|
|
✅ Dependency injection maintained for all managers
|
|
|
|
### Code Conventions
|
|
|
|
✅ JSDoc on all exported symbols
|
|
✅ Private methods prefixed with `_`
|
|
✅ Consistent error handling pattern (try-catch with console.error)
|
|
✅ Null-safe access patterns throughout
|
|
✅ Input validation on all user-provided values
|
|
|
|
---
|
|
|
|
## 🧪 Testing Requirements
|
|
|
|
### Unit Tests
|
|
|
|
**Test File:** `tests/unit/ui/gm/VideoWidgetWidth.test.js` (new file)
|
|
|
|
**Test Coverage:**
|
|
- ✅ Settings registration with proper defaults
|
|
- ✅ Dropdown rendering with all width options
|
|
- ✅ Width selection handler updates settings correctly
|
|
- ✅ onChange callback triggers strip re-render
|
|
- ✅ Small and large widths apply correctly based on layout
|
|
- ✅ Context includes correct width value
|
|
- ✅ Null safety for missing settings
|
|
- ✅ Input validation rejects invalid width values
|
|
|
|
### Manual Test Cases
|
|
|
|
1. **Default Widths**
|
|
- [ ] Install fresh module
|
|
- [ ] Verify small width defaults to 80px
|
|
- [ ] Verify large width defaults to 120px
|
|
- [ ] Verify video widgets render at default sizes
|
|
|
|
2. **Width Selection**
|
|
- [ ] Open Director's Board
|
|
- [ ] Navigate to settings panel
|
|
- [ ] Verify small and large width dropdowns are present
|
|
- [ ] Verify all width options (60, 80, 100, 120, 150, 200) are available
|
|
|
|
3. **Real-Time Preview**
|
|
- [ ] Select a different small width (e.g., 100px)
|
|
- [ ] Verify video widgets update within 500ms
|
|
- [ ] Verify no page refresh is required
|
|
- [ ] Select a different large width (e.g., 150px)
|
|
- [ ] Switch to a large layout variant
|
|
- [ ] Verify widgets use large width
|
|
|
|
4. **Layout Switching**
|
|
- [ ] Set small width to 80px, large width to 150px
|
|
- [ ] Switch to vertical-sm layout
|
|
- [ ] Verify widgets use 80px width
|
|
- [ ] Switch to vertical-md layout
|
|
- [ ] Verify widgets use 150px width
|
|
- [ ] Switch back to vertical-sm
|
|
- [ ] Verify widgets return to 80px width
|
|
|
|
5. **Persistence**
|
|
- [ ] Configure custom widths
|
|
- [ ] Refresh the page
|
|
- [ ] Verify widths persist
|
|
- [ ] Restart FoundryVTT
|
|
- [ ] Verify widths persist
|
|
|
|
6. **Input Validation**
|
|
- [ ] Attempt to set invalid width value via console
|
|
- [ ] Verify value is rejected
|
|
- [ ] Verify previous valid value is retained
|
|
- [ ] Verify warning is logged to console
|
|
|
|
---
|
|
|
|
## 📚 Developer Context Section
|
|
|
|
### What the Developer MUST Know
|
|
|
|
#### 1. Settings Pattern
|
|
**Critical:** This story uses the established world-scoped setting pattern from previous stories.
|
|
|
|
**Pattern:**
|
|
```javascript
|
|
adapter.settings.register("settingName", {
|
|
scope: "world",
|
|
config: false,
|
|
type: String,
|
|
default: "defaultValue",
|
|
onChange: () => callback(),
|
|
});
|
|
```
|
|
|
|
**Do NOT:**
|
|
- Use client-scoped settings (width should be consistent for all users)
|
|
- Forget the onChange callback (strips won't update automatically)
|
|
- Use numeric type (stored as string to maintain precision)
|
|
|
|
**DO:**
|
|
- Always provide sensible defaults
|
|
- Always include onChange callback to trigger UI updates
|
|
- Use string type for pixel values to avoid type issues
|
|
|
|
#### 2. Dock Layout Integration
|
|
This feature **extends** the existing Dock Layout System (FR-29-30), not replaces it.
|
|
|
|
**Integration Points:**
|
|
- Width settings are independent of layout direction (vertical/horizontal/mosaic)
|
|
- Width settings depend on size variant (sm/md)
|
|
- Layout selection (FR-29) and width selection (FR-33) work together
|
|
|
|
**Flow:**
|
|
1. GM selects layout direction + size (e.g., vertical-md)
|
|
2. System determines if layout is small or large variant
|
|
3. System applies corresponding width setting (widgetWidthSm or widgetWidthMd)
|
|
4. Strip re-renders with new layout AND width
|
|
|
|
#### 3. Real-Time Update Pattern
|
|
**Critical:** Width changes must update video widgets in real-time without page refresh.
|
|
|
|
**Pattern:**
|
|
```javascript
|
|
onChange: () => roleRenderer?.rerenderStrip()
|
|
```
|
|
|
|
**Why:**
|
|
- Provides immediate visual feedback
|
|
- Matches existing pattern from Dock Layout System (FR-29)
|
|
- Expected by users from modern web applications
|
|
|
|
**DO:**
|
|
- Use onChange callback on settings registration
|
|
- Call rerenderStrip() when width changes
|
|
- Ensure rerender includes updated width in context
|
|
|
|
#### 4. Dropdown UI Pattern
|
|
**Critical:** Follow existing Director's Board UI patterns for consistency.
|
|
|
|
**Pattern:**
|
|
- Use `<select>` element with `data-action` attribute
|
|
- Include all options with value and label
|
|
- Show current value as selected
|
|
- Handle change in `_onClickButton` or similar method
|
|
|
|
**Reference:** See existing dock layout selector in DirectorsBoard.js (FR-29 implementation)
|
|
|
|
#### 5. CSS Scoping Pattern
|
|
**Critical:** All CSS must be properly scoped to avoid conflicts.
|
|
|
|
**Pattern:**
|
|
```less
|
|
.sp-participant-item {
|
|
// Scoped styles here
|
|
}
|
|
```
|
|
|
|
**Why:**
|
|
- Prevents conflicts with other modules
|
|
- Follows FoundryVTT best practices
|
|
- Maintains modular CSS architecture
|
|
|
|
**DO:**
|
|
- Use `.sp-*` prefix for all custom classes
|
|
- Avoid global selectors
|
|
- Use inline styles from template if dynamic values needed
|
|
|
|
---
|
|
|
|
## 🔗 Dependencies on Other Stories
|
|
|
|
| Story | Dependency Type | Reason |
|
|
|-------|----------------|--------|
|
|
| 1-3-data-layer | Required | FoundryAdapter infrastructure for settings |
|
|
| 1-5-gm-control-ui | Required | ScryingPoolStrip base component |
|
|
| 2-2-directors-board | Required | Director's Board UI infrastructure |
|
|
| 4-7-dock-layout | Required | Dock Layout System foundation |
|
|
| 5-1-full-av-replacement | Required | Full AV Replacement with video elements |
|
|
|
|
---
|
|
|
|
## 📊 Success Criteria
|
|
|
|
| Criterion | Target | Measurement |
|
|
|-----------|--------|-------------|
|
|
| Code Quality | 0 lint errors | ESLint run |
|
|
| Test Coverage | All major paths tested | Manual + unit tests |
|
|
| User Experience | Real-time updates within 500ms | Manual testing |
|
|
| Settings Validation | No invalid values accepted | Error handling tests |
|
|
| CSS Conflicts | Zero conflicts with other modules | Integration testing |
|
|
| Persistence | 100% retention across sessions | Manual testing |
|
|
|
|
---
|
|
|
|
## 🎯 Previous Story Intelligence
|
|
|
|
### Patterns Established in Story 5-1 (Full AV Replacement)
|
|
- World-scoped settings with onChange callbacks
|
|
- Director's Board UI integration
|
|
- Strip re-rendering pattern
|
|
- Video element styling with CSS
|
|
- Null safety throughout
|
|
|
|
### Lessons Applied to This Story
|
|
1. **Settings Pattern:** Reusing world-scoped setting registration from Story 5-1
|
|
2. **UI Pattern:** Following Director's Board UI conventions from Story 5-1
|
|
3. **Re-render Pattern:** Using onChange callback to trigger strip updates
|
|
4. **CSS Pattern:** Properly scoped CSS for video elements
|
|
5. **Validation Pattern:** Input validation for all user-provided values
|
|
|
|
---
|
|
|
|
## 🚀 Git Intelligence
|
|
|
|
**Related Commits:**
|
|
- Reference Story 5-1 commits (c4a375f, f8cbb75, 25dd427, 20d13fc) for similar patterns
|
|
|
|
**Files Modified in Similar Stories:**
|
|
- `module.js` - Settings registration
|
|
- `src/ui/gm/DirectorsBoard.js` - UI controls
|
|
- `src/ui/gm/ScryingPoolStrip.js` - Context and rendering
|
|
- `templates/directors-board.hbs` - Template updates
|
|
- `styles/components/_roster-strip.less` - CSS styling
|
|
- `lang/en.json` - Localization
|
|
|
|
---
|
|
|
|
## 📖 Technical Information
|
|
|
|
### FoundryVTT Settings API
|
|
**Methods Used:**
|
|
```javascript
|
|
game.settings.register(module, key, config)
|
|
game.settings.get(module, key)
|
|
game.settings.set(module, key, value)
|
|
```
|
|
|
|
**Config Options:**
|
|
- `scope`: "world" (GM-controlled) or "client" (user-specific)
|
|
- `config`: true (show in settings UI) or false (hidden, programmatic only)
|
|
- `type`: String, Number, Boolean, Object
|
|
- `default`: Default value
|
|
- `onChange`: Callback function when value changes
|
|
|
|
### CSS Styling Approach
|
|
**Recommended:** Use template variables for dynamic widths
|
|
```handlebars
|
|
<div class="sp-participant-item" style="width: {{widgetWidth}}px; min-width: {{widgetWidth}}px;">
|
|
```
|
|
|
|
**Alternative:** Use CSS custom properties
|
|
```javascript
|
|
// In _prepareContext
|
|
document.documentElement.style.setProperty('--widget-width', `${width}px`);
|
|
|
|
// In CSS
|
|
.sp-participant-item {
|
|
width: var(--widget-width, 80px);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## ✅ Action Items & Next Steps
|
|
|
|
### Implementation Tasks
|
|
- [ ] Register `widgetWidthSm` and `widgetWidthMd` settings in module.js
|
|
- [ ] Add dropdown UI to Director's Board template
|
|
- [ ] Add width dropdown handlers in DirectorsBoard.js
|
|
- [ ] Pass width values to ScryingPoolStrip context
|
|
- [ ] Apply width values to video elements in template
|
|
- [ ] Update CSS to respect width settings
|
|
- [ ] Add localization strings for width settings
|
|
- [ ] Add input validation for width values
|
|
- [ ] Create unit tests for width functionality
|
|
- [ ] Manual testing of all acceptance criteria
|
|
|
|
### Cross-Story Coordination
|
|
- [ ] Verify integration with Dock Layout System (Story 5-1)
|
|
- [ ] Verify no conflicts with existing settings
|
|
- [ ] Verify proper scoping of CSS changes
|
|
- [ ] Update Director's Board documentation
|
|
|
|
### Release Preparation
|
|
- [ ] Update module.json version to 0.1.1
|
|
- [ ] Add feature to release notes
|
|
- [ ] Update user documentation
|
|
- [ ] Test with various FoundryVTT module combinations
|
|
|
|
---
|
|
|
|
## 🎉 Story Completion Status
|
|
|
|
| Task | Status | Notes |
|
|
|------|--------|-------|
|
|
| Story requirements extracted | ✅ | From PRD FR-33, FR-34 |
|
|
| Epic context loaded | ✅ | Epic 5: Full AV Replacement |
|
|
| Architecture analysis | ✅ | Follows existing patterns |
|
|
| Previous story intelligence | ✅ | Patterns from Stories 1-3, 2-2, 5-1 |
|
|
| Git intelligence | ✅ | Similar patterns from Story 5-1 |
|
|
| Technical research | ✅ | FoundryVTT settings API validated |
|
|
| Story file created | ✅ | Complete documentation |
|
|
| Sprint status synced | ⏳ | To be updated |
|
|
|
|
**Status:** ready-for-dev
|
|
**Completion Note:** Ultimate context engine analysis completed - comprehensive developer guide created
|
|
|
|
---
|
|
|
|
## 📝 Project Context Reference
|
|
|
|
- **Project:** video-view-manager (scrying-pool)
|
|
- **Epic:** 5 - Full AV Replacement
|
|
- **Story:** 5.2 - Video Widget Width Customization
|
|
- **Version:** v0.1.1
|
|
- **FoundryVTT Compatibility:** v14+
|
|
- **User:** Morr
|
|
- **PRD Location:** `_bmad-output/planning-artifacts/prds/prd-video-view-manager-2026-05-19/prd.md`
|
|
- **Architecture:** `_bmad-output/planning-artifacts/architecture.md`
|
|
- **UX Design:** `_bmad-output/planning-artifacts/ux-design-specification.md`
|
|
- **Implementation Artifacts:** `_bmad-output/implementation-artifacts/`
|
|
- **Tests:** `tests/unit/`
|
|
|
|
---
|
|
|
|
## 🔗 Related Documentation
|
|
|
|
- **PRD:** §4.7.5 Video Widget Width Customization
|
|
- **Functional Requirements:** FR-33, FR-34
|
|
- **Decision:** D-22 in `.decision-log.md`
|
|
- **Open Question:** OQ-8 in PRD
|
|
- **Assumption:** §9 Assumptions Index
|
|
|
|
---
|
|
|
|
*Story 5.2 is ready for development. All context, requirements, and implementation guidance provided.*
|