docs(02): plan backing track seek controls
Create executable plan for Phase 2 with 3 tasks: - Add state for position/duration in milliseconds - Wire slider to reflect current position - Implement functional handleSeek with SessionTrackSeekMs() Plan: .planning/phases/02-backing-track-seek-controls/02-01-PLAN.md Updates: ROADMAP.md (Phase 2: 0/1 planned), STATE.md (Phase 2 current) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
4a3f09436a
commit
2e312849a6
|
|
@ -39,10 +39,10 @@ Plans:
|
|||
**Goal**: Make the seek bar functional with drag-to-position capability
|
||||
**Depends on**: Phase 1
|
||||
**Research**: Unlikely (building on Phase 1, same API patterns)
|
||||
**Plans**: TBD
|
||||
**Plans**: 1 plan
|
||||
|
||||
Plans:
|
||||
- [ ] TBD during phase planning
|
||||
- [ ] 02-01: Wire slider to position state, implement drag-to-seek with SessionTrackSeekMs()
|
||||
|
||||
### Phase 3: Backing Track Finalization
|
||||
**Goal**: Complete Backing Track with error handling, edge cases, and performance optimization
|
||||
|
|
@ -99,7 +99,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 → 5 → 6 → 7
|
|||
| Phase | Plans Complete | Status | Completed |
|
||||
|-------|----------------|--------|-----------|
|
||||
| 1. Backing Track Playback Monitoring | 1/1 | Complete | 2026-01-13 |
|
||||
| 2. Backing Track Seek Controls | 0/TBD | Not started | - |
|
||||
| 2. Backing Track Seek Controls | 0/1 | Planned | - |
|
||||
| 3. Backing Track Finalization | 0/TBD | Not started | - |
|
||||
| 4. JamTrack Research & Design | 0/TBD | Not started | - |
|
||||
| 5. JamTrack Implementation | 0/TBD | Not started | - |
|
||||
|
|
|
|||
|
|
@ -5,14 +5,14 @@
|
|||
See: .planning/PROJECT.md (updated 2026-01-13)
|
||||
|
||||
**Core value:** Modernize media opening features (Backing Track, JamTrack, Metronome) from legacy jQuery/Rails to React patterns in jam-ui
|
||||
**Current focus:** Phase 1 — Backing Track Playback Monitoring
|
||||
**Current focus:** Phase 2 — Backing Track Seek Controls
|
||||
|
||||
## Current Position
|
||||
|
||||
Phase: 1 of 7 (Backing Track Playback Monitoring)
|
||||
Phase: 2 of 7 (Backing Track Seek Controls)
|
||||
Plan: 1 of 1 in current phase
|
||||
Status: Phase complete
|
||||
Last activity: 2026-01-13 — Completed 01-01-PLAN.md
|
||||
Status: Plan created, ready to execute
|
||||
Last activity: 2026-01-13 — Created 02-01-PLAN.md
|
||||
|
||||
Progress: ██░░░░░░░░ 14%
|
||||
|
||||
|
|
@ -52,6 +52,6 @@ None yet.
|
|||
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-01-13T14:53:53Z
|
||||
Stopped at: Completed 01-01-PLAN.md (Phase 1 complete)
|
||||
Last session: 2026-01-13
|
||||
Stopped at: Created 02-01-PLAN.md (Phase 2 planned, ready to execute)
|
||||
Resume file: None
|
||||
|
|
|
|||
|
|
@ -0,0 +1,249 @@
|
|||
---
|
||||
phase: 02-backing-track-seek-controls
|
||||
type: execute
|
||||
---
|
||||
|
||||
<objective>
|
||||
Make the backing track seek bar functional by wiring it to real playback position and implementing drag-to-position capability using jamClient.SessionTrackSeekMs().
|
||||
|
||||
Purpose: Complete the player's seek functionality, matching the legacy web implementation's behavior while using modern React patterns. This builds directly on Phase 1's monitoring foundation.
|
||||
|
||||
Output: Backing track player with a fully functional seek slider that reflects current position and allows users to seek to any position by dragging.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@./.claude/get-shit-done/workflows/execute-phase.md
|
||||
@./.claude/get-shit-down/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/01-backing-track-playback-monitoring/01-01-SUMMARY.md
|
||||
@jam-ui/src/components/client/JKSessionBackingTrackPlayer.js
|
||||
|
||||
**Codebase conventions:**
|
||||
- React functional components with hooks (React 16.13.1)
|
||||
- camelCase for functions and variables
|
||||
- Single quotes for strings (Prettier enforced)
|
||||
- 2-space indentation
|
||||
|
||||
**Legacy implementation reference:**
|
||||
- web/app/assets/javascripts/playbackControls.js: Slider calculation (lines 142-150, 403-410)
|
||||
- jamClient method: SessionTrackSeekMs(positionMs) - Seeks to specific position in milliseconds
|
||||
- Formula: `positionMs = (sliderValue / sliderMax) * durationMs`
|
||||
- Slider updates during playback: `xPos = (timeMs / durationMs) * sliderWidth`
|
||||
|
||||
**Phase 1 foundation:**
|
||||
- formatTime utility converts milliseconds to M:SS (lines 25-31)
|
||||
- Polling useEffect updates currentTime and duration every 500ms (lines 50-83)
|
||||
- Duration state available in milliseconds (before formatting)
|
||||
- currentTime state available (formatted as M:SS)
|
||||
|
||||
**Current state:**
|
||||
- Seek slider hardcoded: min="0" max="100" value="0" (lines 256-269 popup, 352-364 modal)
|
||||
- handleSeek is placeholder: console.log('Seek to:', e.target.value) (line 147)
|
||||
- Slider doesn't reflect current position
|
||||
- Dragging slider has no effect
|
||||
|
||||
**Vision:**
|
||||
- Slider value reflects current playback position (moves as track plays)
|
||||
- Dragging slider seeks to that position immediately
|
||||
- Slider range is 0 to duration in milliseconds
|
||||
- Works during playback and when paused
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Add state for position in milliseconds</name>
|
||||
<files>jam-ui/src/components/client/JKSessionBackingTrackPlayer.js</files>
|
||||
<action>Add state to track current position in milliseconds (separate from formatted currentTime string). This allows the slider to use numeric values while still displaying formatted time.
|
||||
|
||||
Implementation:
|
||||
- Add state after line 21: `const [currentPositionMs, setCurrentPositionMs] = useState(0);`
|
||||
- Add state after line 21: `const [durationMs, setDurationMs] = useState(0);`
|
||||
- Modify initialization useEffect (lines 33-48) to set both:
|
||||
```javascript
|
||||
const durationInMs = jamClient.SessionGetTracksPlayDurationMs();
|
||||
setDurationMs(durationInMs);
|
||||
setDuration(formatTime(durationInMs));
|
||||
setCurrentPositionMs(0);
|
||||
```
|
||||
- Modify polling useEffect (lines 50-83) to set both:
|
||||
```javascript
|
||||
const positionMs = jamClient.SessionCurrrentPlayPosMs();
|
||||
const durationInMs = jamClient.SessionGetTracksPlayDurationMs();
|
||||
setCurrentPositionMs(positionMs);
|
||||
setCurrentTime(formatTime(positionMs));
|
||||
setDurationMs(durationInMs);
|
||||
setDuration(formatTime(durationInMs));
|
||||
```
|
||||
- Modify handleStop (lines 101-109) to reset position: `setCurrentPositionMs(0);`
|
||||
|
||||
This separates concerns: formatted strings for display, milliseconds for slider calculations.</action>
|
||||
<verify>Check state initialization:
|
||||
- Open backing track player
|
||||
- Verify durationMs state is set (use React DevTools)
|
||||
- Click play, verify currentPositionMs increments every 500ms
|
||||
- Click stop, verify currentPositionMs resets to 0</verify>
|
||||
<done>State exists for currentPositionMs and durationMs, properly updated by initialization and polling useEffects</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Wire slider to reflect current position</name>
|
||||
<files>jam-ui/src/components/client/JKSessionBackingTrackPlayer.js</files>
|
||||
<action>Update the seek slider inputs to use real values from state instead of hardcoded values.
|
||||
|
||||
Popup version (lines 256-269):
|
||||
```javascript
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max={durationMs || 100}
|
||||
value={currentPositionMs}
|
||||
onChange={handleSeek}
|
||||
style={{ flex: 1 }}
|
||||
disabled={!backingTrack}
|
||||
/>
|
||||
```
|
||||
|
||||
Modal version (lines 352-364):
|
||||
```javascript
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max={durationMs || 100}
|
||||
value={currentPositionMs}
|
||||
onChange={handleSeek}
|
||||
style={{ flex: 1 }}
|
||||
disabled={!backingTrack}
|
||||
/>
|
||||
```
|
||||
|
||||
Key changes:
|
||||
- max={durationMs || 100} - Use actual duration, fallback to 100 if not loaded
|
||||
- value={currentPositionMs} - Reflects current position (updates via polling)
|
||||
- Keep onChange={handleSeek} (will implement in Task 3)
|
||||
|
||||
This makes the slider "live" - it will automatically move as playback progresses due to the polling useEffect updating currentPositionMs.</action>
|
||||
<verify>Play backing track:
|
||||
- Verify slider knob moves smoothly from left to right as track plays
|
||||
- Slider should be at ~20% when currentTime shows ~1:00 of a 5:00 track
|
||||
- Pause playback, verify slider stops moving
|
||||
- Resume playback, verify slider continues moving
|
||||
- Slider fills completely when track finishes</verify>
|
||||
<done>Slider visually reflects current playback position, moves during playback, accurate to within 500ms</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Implement functional handleSeek</name>
|
||||
<files>jam-ui/src/components/client/JKSessionBackingTrackPlayer.js</files>
|
||||
<action>Replace the placeholder handleSeek function (line 147) with functional implementation that calls jamClient.SessionTrackSeekMs().
|
||||
|
||||
Implementation:
|
||||
```javascript
|
||||
const handleSeek = async (e) => {
|
||||
const seekPositionMs = parseInt(e.target.value);
|
||||
|
||||
try {
|
||||
// Update local state immediately for responsive UI
|
||||
setCurrentPositionMs(seekPositionMs);
|
||||
setCurrentTime(formatTime(seekPositionMs));
|
||||
|
||||
// Seek the native client to the new position
|
||||
await jamClient.SessionTrackSeekMs(seekPositionMs);
|
||||
} catch (error) {
|
||||
console.error('Error seeking:', error);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Pattern explanation:
|
||||
- Get seekPositionMs from slider value (already in milliseconds due to Task 2)
|
||||
- Update state immediately (optimistic update for responsive UX)
|
||||
- Call jamClient.SessionTrackSeekMs(seekPositionMs) to seek native player
|
||||
- Error handling: log but don't crash
|
||||
|
||||
Note: The polling useEffect will sync the actual position within 500ms, correcting any drift.
|
||||
|
||||
IMPORTANT: Remove the placeholder console.log line entirely.</action>
|
||||
<verify>Test seek functionality:
|
||||
- Open backing track, click play
|
||||
- Drag slider to middle (e.g., 2:30 of 5:00 track)
|
||||
- Verify playback jumps to that position (audio changes)
|
||||
- Verify time display updates to match slider position
|
||||
- Drag slider while paused - should work identically
|
||||
- Drag to end, verify track near completion
|
||||
- Drag to beginning, verify track restarts
|
||||
- Check console for errors (should be none)</verify>
|
||||
<done>Seek slider fully functional, dragging seeks to position, works during playback and when paused, no console errors</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
Before declaring phase complete:
|
||||
- [ ] Open backing track player, verify slider starts at far left (0:00)
|
||||
- [ ] Click play, verify slider moves smoothly from left to right
|
||||
- [ ] Verify slider position matches time display (e.g., ~40% at 2:00 of 5:00 track)
|
||||
- [ ] Drag slider to middle while playing, verify playback jumps and audio changes
|
||||
- [ ] Verify time display updates to match dragged position
|
||||
- [ ] Pause playback, drag slider, verify seeking works when paused
|
||||
- [ ] Drag to very end, verify track near completion (within 1 second)
|
||||
- [ ] Drag to beginning (far left), verify track restarts from 0:00
|
||||
- [ ] Check browser console for errors (should be none)
|
||||
- [ ] Verify both modal and popup versions work identically
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
|
||||
- currentPositionMs and durationMs state exists and updates correctly
|
||||
- Seek slider value reflects real playback position (live updates)
|
||||
- Slider range is 0 to actual duration in milliseconds
|
||||
- handleSeek calls jamClient.SessionTrackSeekMs() with correct position
|
||||
- Seeking works during playback and when paused
|
||||
- Audio playback position changes when slider is dragged
|
||||
- Time display updates to match slider position
|
||||
- No console errors during seek operations
|
||||
- Functional parity with legacy web seek behavior
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/02-backing-track-seek-controls/02-01-SUMMARY.md`:
|
||||
|
||||
# Phase 2 Plan 1: Backing Track Seek Controls Summary
|
||||
|
||||
**Functional seek slider with drag-to-position capability using jamClient.SessionTrackSeekMs()**
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Added currentPositionMs and durationMs state for numeric slider calculations
|
||||
- Wired slider to reflect live playback position via polling updates
|
||||
- Implemented handleSeek with jamClient.SessionTrackSeekMs() integration
|
||||
- Slider now functional during both playback and pause
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
- `jam-ui/src/components/client/JKSessionBackingTrackPlayer.js` - Added position state, wired slider, implemented seek
|
||||
|
||||
## Decisions Made
|
||||
|
||||
- Separate state for milliseconds (slider calculations) vs formatted strings (display)
|
||||
- Optimistic UI update before jamClient call for responsive UX
|
||||
- Polling useEffect auto-corrects position drift within 500ms
|
||||
- Works identically during playback and when paused (no special handling needed)
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
(None expected - straightforward slider integration)
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
Phase 3 (Backing Track Finalization) can now proceed. The player has complete core functionality (play/pause/stop, time display, seek). Phase 3 will add:
|
||||
- Comprehensive error handling (jamClient failures, file not found, invalid formats)
|
||||
- Edge cases (seek beyond duration, rapid seeks, seek during stop transition)
|
||||
- Performance optimization (reduce polling frequency when not visible, cleanup on unmount)
|
||||
- UI polish (loading states, disabled states during transitions)
|
||||
</output>
|
||||
Loading…
Reference in New Issue