feat(04-04): wire BlockInspector into timeline and app.js
- app.js imports BlockInspector and DeleteBlockCommand - Inspector created with #block-inspector element and timeline.history - timeline._onSelectBlock callback shows/hides inspector on block select - inspector._onDelete fires DeleteBlockCommand and hides inspector - inspector.tracks kept in sync after loadTracks() resolves - timeline.js block rendering uses blockColor + '1a' for color accent fill - undo/redo handlers re-trigger _onSelectBlock to refresh inspector values
This commit is contained in:
@@ -2,6 +2,8 @@
|
|||||||
// Browser <audio> handles playback; server receives position ticks and sends beat events
|
// Browser <audio> handles playback; server receives position ticks and sends beat events
|
||||||
|
|
||||||
import { TimelineCanvas } from './timeline/timeline.js';
|
import { TimelineCanvas } from './timeline/timeline.js';
|
||||||
|
import { BlockInspector } from './timeline/inspector.js';
|
||||||
|
import { DeleteBlockCommand } from './timeline/commands.js';
|
||||||
|
|
||||||
const WS_URL = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${location.host}/ws`;
|
const WS_URL = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${location.host}/ws`;
|
||||||
|
|
||||||
@@ -261,9 +263,28 @@ function initTimeline() {
|
|||||||
|
|
||||||
timeline = new TimelineCanvas(canvas, audio);
|
timeline = new TimelineCanvas(canvas, audio);
|
||||||
|
|
||||||
|
// Inspector
|
||||||
|
const inspectorEl = document.getElementById('block-inspector');
|
||||||
|
const inspector = new BlockInspector(inspectorEl, timeline.history);
|
||||||
|
inspector.tracks = timeline.tracks;
|
||||||
|
|
||||||
|
// Wire timeline selection → inspector
|
||||||
|
timeline._onSelectBlock = (block) => {
|
||||||
|
inspector.show(block);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wire inspector delete → timeline
|
||||||
|
inspector._onDelete = (block) => {
|
||||||
|
const cmd = new DeleteBlockCommand(timeline.tracks, block);
|
||||||
|
timeline.history.execute(cmd);
|
||||||
|
timeline.selectedBlock = null;
|
||||||
|
inspector.hide();
|
||||||
|
};
|
||||||
|
|
||||||
// Load device tracks
|
// Load device tracks
|
||||||
fetch('/api/devices').then(r => r.json()).then(devices => {
|
fetch('/api/devices').then(r => r.json()).then(devices => {
|
||||||
timeline.loadTracks(devices);
|
timeline.loadTracks(devices);
|
||||||
|
inspector.tracks = timeline.tracks; // keep reference in sync
|
||||||
}).catch(e => console.warn('[timeline]', e));
|
}).catch(e => console.warn('[timeline]', e));
|
||||||
|
|
||||||
// When audio loads, fetch waveform + beats
|
// When audio loads, fetch waveform + beats
|
||||||
|
|||||||
@@ -185,7 +185,13 @@ class TimelineCanvas {
|
|||||||
if (x + w < HEADER_WIDTH || x > W) continue;
|
if (x + w < HEADER_WIDTH || x > W) continue;
|
||||||
|
|
||||||
const isSelected = this.selectedBlock === cue;
|
const isSelected = this.selectedBlock === cue;
|
||||||
ctx.fillStyle = isSelected ? '#0d3a3a' : '#0a2a2a';
|
// Block fill — use block color at low opacity for visual distinction
|
||||||
|
const blockColor = cue.params?.color || '#00ffff';
|
||||||
|
if (isSelected) {
|
||||||
|
ctx.fillStyle = '#0d3a3a';
|
||||||
|
} else {
|
||||||
|
ctx.fillStyle = blockColor + '1a'; // ~10% opacity hex suffix
|
||||||
|
}
|
||||||
ctx.fillRect(x, y + 1, w, TRACK_HEIGHT - 2);
|
ctx.fillRect(x, y + 1, w, TRACK_HEIGHT - 2);
|
||||||
|
|
||||||
ctx.strokeStyle = isSelected ? '#00ffff' : '#1a4a4a';
|
ctx.strokeStyle = isSelected ? '#00ffff' : '#1a4a4a';
|
||||||
@@ -445,11 +451,13 @@ class TimelineCanvas {
|
|||||||
if (e.ctrlKey && !e.shiftKey && e.key === 'z') {
|
if (e.ctrlKey && !e.shiftKey && e.key === 'z') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.history.undo();
|
this.history.undo();
|
||||||
|
if (this._onSelectBlock) this._onSelectBlock(this.selectedBlock);
|
||||||
}
|
}
|
||||||
// Redo: Ctrl+Shift+Z or Ctrl+Y
|
// Redo: Ctrl+Shift+Z or Ctrl+Y
|
||||||
if ((e.ctrlKey && e.shiftKey && e.key === 'Z') || (e.ctrlKey && e.key === 'y')) {
|
if ((e.ctrlKey && e.shiftKey && e.key === 'Z') || (e.ctrlKey && e.key === 'y')) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.history.redo();
|
this.history.redo();
|
||||||
|
if (this._onSelectBlock) this._onSelectBlock(this.selectedBlock);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user