Programmatic Track & Clip API
Summary
Adds an imperative API on <daw-editor> for tracks and clips, wires <daw-clip> lifecycle events so dynamic DOM mutation works alongside the imperative methods, and adds indefinite-playback so the ruler renders before any audio is loaded.
Closes #381.
Published Packages
| Package | Version | Change |
|---|---|---|
@dawcore/components |
0.0.13 | Programmatic track/clip API + indefinite-playback + helper refactor |
Programmatic API
| Method | Behavior |
|---|---|
editor.ready() |
Builds engine eagerly without requiring a track |
editor.addTrack(config) |
Builds and appends a <daw-track>, returns the element when loaded |
editor.updateTrack(id, partial) |
Mutates reflected attrs on the <daw-track> (or descriptor + engine for non-DOM tracks) |
editor.removeTrack(id) |
Removes via MutationObserver, falls back for non-DOM tracks |
editor.addClip(trackId, config) |
Validates src, builds and appends a <daw-clip>, returns the clipId |
editor.updateClip(trackId, clipId, partial) |
Sets reflected props on the <daw-clip> |
editor.removeClip(trackId, clipId) |
Removes the <daw-clip> DOM element |
Both declarative DOM mutation and these methods feed the same load pipeline — descriptors, peaks, and clip buffers populate identically.
Declarative DOM parity
<daw-clip> now dispatches daw-clip-connected (deferred via setTimeout(0)) and daw-clip-update (post-render, includes trackId resolved at dispatch). The editor handles both events plus clip-removal via the existing MutationObserver. Engine clip ids are aligned with <daw-clip>.clipId so DOM and engine refer to the same id.
indefinite-playback for empty editors
New boolean attribute. When set, the timeline fills the visible viewport even with no clips. The ruler covers max(naturalDuration, viewportWidth) so a short clip on an empty editor doesn't shrink the ruler. An empty controls-column placeholder prevents horizontal shift when the first track loads. ViewportController gains a ResizeObserver for window-resize support.
New Example
examples/dawcore-native/programmatic.html— ImperativeaddTrack/addClip/updateClip/removeClipplus declarative DOM mutation, side-by-side. Usesindefinite-playbackso the ruler renders from the start.
Usage
import '@dawcore/components';
import { NativePlayoutAdapter } from '@dawcore/transport';
const editor = document.querySelector('daw-editor');
editor.adapter = new NativePlayoutAdapter(new AudioContext());
// Build engine before any track exists — useful for wiring analyzers,
// effects, or master taps before content arrives.
await editor.ready();
// Imperative track creation
const track = await editor.addTrack({
name: 'Drums',
volume: 0.8,
clips: [{ src: '/audio/drums.opus', start: 0 }],
});
// Imperative clip mutation
const clipId = await editor.addClip(track.trackId, {
src: '/audio/snare.opus',
start: 4,
});
editor.updateClip(track.trackId, clipId, { start: 6, gain: 0.5 });
editor.removeClip(track.trackId, clipId);
// indefinite-playback in HTML
// <daw-editor timescale indefinite-playback>...</daw-editor>Internal Refactor
- Extracted
_resolvePeaksand_finalizeAudioCliphelpers to consolidate the per-clip load pipeline previously duplicated between_loadTrackand_loadAndAppendClip._finalizeAudioClipself-purges its caches ongeneratePeaksfailure so callers don't leak. ClipDescriptoris now a discriminated union:DomClipDescriptor (kind: 'dom', clipId)|DropClipDescriptor (kind: 'drop'). Replaces the optionalclipId?field. New exportedisDomClip(c)type predicate.- Per-clip isolation in
_loadTrack: a single bad clip dispatchesdaw-clip-errorand skips, rather than aborting the whole track and leaking earlier clips' cache writes. - Many silent paths now warn (unknown
removeTrack/removeClip/updateClipids, DOM/engine clip-id misalignment, etc.).
Breaking Changes (@dawcore/components 0.0.x line)
ClipConfig.srcis now required.ClipDescriptor.clipId?replaced with discriminated union (kind: 'dom' | 'drop').
Test Plan
pnpm typecheck✓pnpm lint(0 errors) ✓cd packages/dawcore && npx vitest run— 374/374 tests pass (was 320 before, +54 new test cases)
Documentation
- Spec at
docs/specs/web-components-migration.mdupdated with the full Programmatic Track Mutation section. - README updated with the imperative API summary and a link to the spec.
PRs
- #382 — programmatic track & clip API + indefinite-playback
- #383 — extract clip-load helpers + discriminated ClipDescriptor
Deferred
- Engine-side fade application from
<daw-clip>fadeIn/fadeOut/fadeTypeproperties — needs adapter-side investigation to know ifFadeobjects are honored byNativePlayoutAdapterandTonePlayoutAdapter.