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:
Nuwan 2026-01-13 20:35:30 +05:30
parent 4a3f09436a
commit 2e312849a6
3 changed files with 258 additions and 9 deletions

View File

@ -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 | - |

View File

@ -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

View File

@ -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>