feat(06-02): add segment overlay canvas rendering and click interaction

- Create lightsync/frontend/timeline/segments.js with SegmentOverlay class
- Integrate SegmentOverlay into timeline.js render pipeline (between row banding and waveform)
- Add loadSegments method to TimelineCanvas
- Wire loadSegments call in app.js loadedmetadata handler
This commit is contained in:
Claude
2026-04-07 11:41:41 +00:00
parent d339ba588d
commit 48e15ee013
3 changed files with 138 additions and 1 deletions

View File

@@ -5,6 +5,7 @@
import { CommandHistory } from './history.js';
import { PlaceBlockCommand, MoveBlockCommand, ResizeBlockCommand, DeleteBlockCommand, setCurrentShowId } from './commands.js';
import { BeatGrid } from './beats.js';
import { SegmentOverlay } from './segments.js';
const PIXELS_PER_SECOND = 100; // default, adjustable via zoom
const TRACK_HEIGHT = 48;
@@ -26,6 +27,7 @@ class TimelineCanvas {
this.waveformPeaks = [];
this.audioDuration = 0;
this.beatGrid = new BeatGrid();
this.segmentOverlay = new SegmentOverlay();
this.selectedBlock = null;
this._dragState = null;
this._onSelectBlock = null; // callback(cue) for inspector
@@ -120,6 +122,11 @@ class TimelineCanvas {
ctx.fillRect(HEADER_WIDTH, y, W - HEADER_WIDTH, TRACK_HEIGHT);
});
// 2.5 Segment overlay bands (drawn below waveform)
if (this.segmentOverlay.segments.length > 0) {
this.segmentOverlay.draw(ctx, W, H, this.tracks.length, (t) => this.timeToX(t));
}
// 3. Waveform background
if (this.waveformPeaks.length > 0 && this.audioDuration > 0) {
const totalH = this.tracks.length * TRACK_HEIGHT;
@@ -406,9 +413,11 @@ class TimelineCanvas {
const hit = this.hitTest(mx, my);
if (!hit) {
// Clicked on empty canvas — deselect
// Clicked on empty canvas — deselect block, update segment selection
this.selectedBlock = null;
if (this._onSelectBlock) this._onSelectBlock(null);
const clickTime = this.xToTime(mx);
this.segmentOverlay.handleClick(clickTime);
return;
}
@@ -622,6 +631,10 @@ class TimelineCanvas {
async loadBeats(audioPath) {
await this.beatGrid.load(audioPath);
}
async loadSegments(audioPath) {
await this.segmentOverlay.load(audioPath);
}
}
export { TimelineCanvas };