@dawcore/components 0.0.18 → 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +44 -1
- package/dist/index.d.ts +44 -1
- package/dist/index.js +118 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +118 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -4
package/dist/index.d.mts
CHANGED
|
@@ -3,11 +3,18 @@ import { LitElement, PropertyValues, ReactiveController, ReactiveControllerHost
|
|
|
3
3
|
import { MidiNoteData, SpectrogramConfig, FadeType, Peaks, Bits, PeakData, MeterEntry, SnapTo, ColorMapValue, ClipTrack, KeyboardShortcut } from '@waveform-playlist/core';
|
|
4
4
|
import WaveformData from 'waveform-data';
|
|
5
5
|
import { PlayoutAdapter, PlaylistEngine } from '@waveform-playlist/engine';
|
|
6
|
+
import { MidiLoadOptions, MidiLoadResult } from '@dawcore/midi';
|
|
6
7
|
import { ClipRegistration, CanvasRegistration, ViewportState } from '@dawcore/spectrogram';
|
|
7
8
|
|
|
8
9
|
declare class DawClipElement extends LitElement {
|
|
9
10
|
src: string;
|
|
10
11
|
peaksSrc: string;
|
|
12
|
+
/**
|
|
13
|
+
* Timeline position in seconds. JS property only — NOT reflected to the
|
|
14
|
+
* `start` attribute (Lit's `reflect` defaults to false). Tests that need
|
|
15
|
+
* to assert clip position must read this property directly; reading
|
|
16
|
+
* `el.getAttribute('start')` returns `null` regardless of correctness.
|
|
17
|
+
*/
|
|
11
18
|
start: number;
|
|
12
19
|
duration: number;
|
|
13
20
|
offset: number;
|
|
@@ -824,7 +831,29 @@ interface LoadFilesResult {
|
|
|
824
831
|
}>;
|
|
825
832
|
}
|
|
826
833
|
|
|
827
|
-
|
|
834
|
+
/**
|
|
835
|
+
* MIDI loading logic extracted from daw-editor. Operates on the editor via a
|
|
836
|
+
* narrow host interface (`addTrack` + `querySelectorAll`) — `<daw-editor>`
|
|
837
|
+
* satisfies it without any new public surface.
|
|
838
|
+
*
|
|
839
|
+
* Numbered steps below match the Data Flow diagram in
|
|
840
|
+
* `docs/specs/2026-05-23-dawcore-load-midi-design.md`.
|
|
841
|
+
*/
|
|
842
|
+
|
|
843
|
+
/**
|
|
844
|
+
* Minimal host surface needed by `loadMidiImpl`. `<daw-editor>` satisfies this
|
|
845
|
+
* structurally. `querySelectorAll` is needed for cleanup-on-failure so the
|
|
846
|
+
* loader can identify `<daw-track>` elements appended during this call (both
|
|
847
|
+
* those whose `addTrack` resolved and those that rejected after `_loadTrack`
|
|
848
|
+
* fired `daw-track-error` — the latter aren't in the `addTrack` resolution
|
|
849
|
+
* value, so we need DOM observation to find them).
|
|
850
|
+
*/
|
|
851
|
+
interface MidiLoaderHost {
|
|
852
|
+
addTrack(config: TrackConfig): Promise<DawTrackElement>;
|
|
853
|
+
querySelectorAll(selector: string): NodeListOf<Element>;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
declare class DawEditorElement extends LitElement implements MidiLoaderHost {
|
|
828
857
|
get samplesPerPixel(): number;
|
|
829
858
|
set samplesPerPixel(value: number);
|
|
830
859
|
private _samplesPerPixel;
|
|
@@ -1066,6 +1095,20 @@ declare class DawEditorElement extends LitElement {
|
|
|
1066
1095
|
private _onDragLeave;
|
|
1067
1096
|
private _onDrop;
|
|
1068
1097
|
loadFiles(files: FileList | File[]): Promise<LoadFilesResult>;
|
|
1098
|
+
/**
|
|
1099
|
+
* Imperatively load a `.mid` file (URL or File) and create N `<daw-track>`
|
|
1100
|
+
* elements — one per note-bearing MIDI track. On any per-track failure,
|
|
1101
|
+
* every `<daw-track>` appended during the call is removed (both successful
|
|
1102
|
+
* and failed) so the editor returns to its pre-call state.
|
|
1103
|
+
*
|
|
1104
|
+
* `options.signal` is forwarded to `fetch()` only for URL sources; aborting
|
|
1105
|
+
* after parsing does not cancel in-flight `addTrack` calls.
|
|
1106
|
+
*
|
|
1107
|
+
* Requires the optional `@dawcore/midi` peer dep — throws with an install
|
|
1108
|
+
* hint (and `console.warn`s the original error) when the dynamic import
|
|
1109
|
+
* fails for any reason.
|
|
1110
|
+
*/
|
|
1111
|
+
loadMidi(source: string | File, options?: MidiLoadOptions): Promise<MidiLoadResult>;
|
|
1069
1112
|
/**
|
|
1070
1113
|
* Build the engine if it hasn't been built yet. Lets consumers obtain a
|
|
1071
1114
|
* non-null `editor.engine` before any track has been loaded — useful for
|
package/dist/index.d.ts
CHANGED
|
@@ -3,11 +3,18 @@ import { LitElement, PropertyValues, ReactiveController, ReactiveControllerHost
|
|
|
3
3
|
import { MidiNoteData, SpectrogramConfig, FadeType, Peaks, Bits, PeakData, MeterEntry, SnapTo, ColorMapValue, ClipTrack, KeyboardShortcut } from '@waveform-playlist/core';
|
|
4
4
|
import WaveformData from 'waveform-data';
|
|
5
5
|
import { PlayoutAdapter, PlaylistEngine } from '@waveform-playlist/engine';
|
|
6
|
+
import { MidiLoadOptions, MidiLoadResult } from '@dawcore/midi';
|
|
6
7
|
import { ClipRegistration, CanvasRegistration, ViewportState } from '@dawcore/spectrogram';
|
|
7
8
|
|
|
8
9
|
declare class DawClipElement extends LitElement {
|
|
9
10
|
src: string;
|
|
10
11
|
peaksSrc: string;
|
|
12
|
+
/**
|
|
13
|
+
* Timeline position in seconds. JS property only — NOT reflected to the
|
|
14
|
+
* `start` attribute (Lit's `reflect` defaults to false). Tests that need
|
|
15
|
+
* to assert clip position must read this property directly; reading
|
|
16
|
+
* `el.getAttribute('start')` returns `null` regardless of correctness.
|
|
17
|
+
*/
|
|
11
18
|
start: number;
|
|
12
19
|
duration: number;
|
|
13
20
|
offset: number;
|
|
@@ -824,7 +831,29 @@ interface LoadFilesResult {
|
|
|
824
831
|
}>;
|
|
825
832
|
}
|
|
826
833
|
|
|
827
|
-
|
|
834
|
+
/**
|
|
835
|
+
* MIDI loading logic extracted from daw-editor. Operates on the editor via a
|
|
836
|
+
* narrow host interface (`addTrack` + `querySelectorAll`) — `<daw-editor>`
|
|
837
|
+
* satisfies it without any new public surface.
|
|
838
|
+
*
|
|
839
|
+
* Numbered steps below match the Data Flow diagram in
|
|
840
|
+
* `docs/specs/2026-05-23-dawcore-load-midi-design.md`.
|
|
841
|
+
*/
|
|
842
|
+
|
|
843
|
+
/**
|
|
844
|
+
* Minimal host surface needed by `loadMidiImpl`. `<daw-editor>` satisfies this
|
|
845
|
+
* structurally. `querySelectorAll` is needed for cleanup-on-failure so the
|
|
846
|
+
* loader can identify `<daw-track>` elements appended during this call (both
|
|
847
|
+
* those whose `addTrack` resolved and those that rejected after `_loadTrack`
|
|
848
|
+
* fired `daw-track-error` — the latter aren't in the `addTrack` resolution
|
|
849
|
+
* value, so we need DOM observation to find them).
|
|
850
|
+
*/
|
|
851
|
+
interface MidiLoaderHost {
|
|
852
|
+
addTrack(config: TrackConfig): Promise<DawTrackElement>;
|
|
853
|
+
querySelectorAll(selector: string): NodeListOf<Element>;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
declare class DawEditorElement extends LitElement implements MidiLoaderHost {
|
|
828
857
|
get samplesPerPixel(): number;
|
|
829
858
|
set samplesPerPixel(value: number);
|
|
830
859
|
private _samplesPerPixel;
|
|
@@ -1066,6 +1095,20 @@ declare class DawEditorElement extends LitElement {
|
|
|
1066
1095
|
private _onDragLeave;
|
|
1067
1096
|
private _onDrop;
|
|
1068
1097
|
loadFiles(files: FileList | File[]): Promise<LoadFilesResult>;
|
|
1098
|
+
/**
|
|
1099
|
+
* Imperatively load a `.mid` file (URL or File) and create N `<daw-track>`
|
|
1100
|
+
* elements — one per note-bearing MIDI track. On any per-track failure,
|
|
1101
|
+
* every `<daw-track>` appended during the call is removed (both successful
|
|
1102
|
+
* and failed) so the editor returns to its pre-call state.
|
|
1103
|
+
*
|
|
1104
|
+
* `options.signal` is forwarded to `fetch()` only for URL sources; aborting
|
|
1105
|
+
* after parsing does not cancel in-flight `addTrack` calls.
|
|
1106
|
+
*
|
|
1107
|
+
* Requires the optional `@dawcore/midi` peer dep — throws with an install
|
|
1108
|
+
* hint (and `console.warn`s the original error) when the dynamic import
|
|
1109
|
+
* fails for any reason.
|
|
1110
|
+
*/
|
|
1111
|
+
loadMidi(source: string | File, options?: MidiLoadOptions): Promise<MidiLoadResult>;
|
|
1069
1112
|
/**
|
|
1070
1113
|
* Build the engine if it hasn't been built yet. Lets consumers obtain a
|
|
1071
1114
|
* non-null `editor.engine` before any track has been loaded — useful for
|
package/dist/index.js
CHANGED
|
@@ -3495,6 +3495,108 @@ async function loadFiles(host, files) {
|
|
|
3495
3495
|
return { loaded, failed };
|
|
3496
3496
|
}
|
|
3497
3497
|
|
|
3498
|
+
// src/interactions/midi-loader.ts
|
|
3499
|
+
var INSTALL_HINT = "@dawcore/midi is required for loadMidi(). Install with: npm install @dawcore/midi";
|
|
3500
|
+
async function loadMidiImpl(host, source, options = {}) {
|
|
3501
|
+
const startTime = options.startTime ?? 0;
|
|
3502
|
+
if (!Number.isFinite(startTime) || startTime < 0) {
|
|
3503
|
+
throw new RangeError(
|
|
3504
|
+
"loadMidi: startTime must be a non-negative finite number (got " + String(options.startTime) + ")"
|
|
3505
|
+
);
|
|
3506
|
+
}
|
|
3507
|
+
let midiModule;
|
|
3508
|
+
try {
|
|
3509
|
+
midiModule = await import("@dawcore/midi");
|
|
3510
|
+
} catch (originalErr) {
|
|
3511
|
+
console.warn("[dawcore] @dawcore/midi dynamic import failed: " + String(originalErr));
|
|
3512
|
+
throw new Error(INSTALL_HINT);
|
|
3513
|
+
}
|
|
3514
|
+
const { parseMidiUrl, parseMidiFile } = midiModule;
|
|
3515
|
+
let parsed;
|
|
3516
|
+
if (typeof source === "string") {
|
|
3517
|
+
parsed = await parseMidiUrl(source, void 0, options.signal);
|
|
3518
|
+
} else {
|
|
3519
|
+
let buffer;
|
|
3520
|
+
try {
|
|
3521
|
+
buffer = await source.arrayBuffer();
|
|
3522
|
+
} catch (err) {
|
|
3523
|
+
throw new Error(
|
|
3524
|
+
'loadMidi: failed to read File "' + source.name + '" (' + source.size + " bytes): " + String(err)
|
|
3525
|
+
);
|
|
3526
|
+
}
|
|
3527
|
+
parsed = parseMidiFile(buffer);
|
|
3528
|
+
}
|
|
3529
|
+
const childrenBefore = new Set(host.querySelectorAll("daw-track"));
|
|
3530
|
+
const settlements = await Promise.allSettled(
|
|
3531
|
+
parsed.tracks.map(
|
|
3532
|
+
(t) => host.addTrack({
|
|
3533
|
+
name: t.name,
|
|
3534
|
+
renderMode: "piano-roll",
|
|
3535
|
+
clips: [
|
|
3536
|
+
{
|
|
3537
|
+
midiNotes: t.notes,
|
|
3538
|
+
midiChannel: t.channel,
|
|
3539
|
+
midiProgram: t.programNumber,
|
|
3540
|
+
start: startTime
|
|
3541
|
+
}
|
|
3542
|
+
]
|
|
3543
|
+
})
|
|
3544
|
+
)
|
|
3545
|
+
);
|
|
3546
|
+
const succeeded = [];
|
|
3547
|
+
const rejections = [];
|
|
3548
|
+
for (const s of settlements) {
|
|
3549
|
+
if (s.status === "fulfilled") {
|
|
3550
|
+
succeeded.push(s.value);
|
|
3551
|
+
} else {
|
|
3552
|
+
rejections.push(s.reason);
|
|
3553
|
+
}
|
|
3554
|
+
}
|
|
3555
|
+
if (rejections.length > 0) {
|
|
3556
|
+
const appendedTracks = Array.from(host.querySelectorAll("daw-track")).filter(
|
|
3557
|
+
(el) => !childrenBefore.has(el)
|
|
3558
|
+
);
|
|
3559
|
+
for (const el of appendedTracks) {
|
|
3560
|
+
try {
|
|
3561
|
+
el.remove();
|
|
3562
|
+
} catch (cleanupErr) {
|
|
3563
|
+
console.warn("[dawcore] loadMidi cleanup failed for a track: " + String(cleanupErr));
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
await Promise.resolve();
|
|
3567
|
+
for (let i = 1; i < rejections.length; i++) {
|
|
3568
|
+
console.warn(
|
|
3569
|
+
"[dawcore] loadMidi: additional track failure (" + i + "): " + stringifyReason(rejections[i])
|
|
3570
|
+
);
|
|
3571
|
+
}
|
|
3572
|
+
const first = rejections[0];
|
|
3573
|
+
if (rejections.length > 1) {
|
|
3574
|
+
const message = "loadMidi: " + rejections.length + " of " + settlements.length + " tracks failed; first: " + (first instanceof Error ? first.message : stringifyReason(first));
|
|
3575
|
+
throw new Error(message);
|
|
3576
|
+
}
|
|
3577
|
+
throw first instanceof Error ? first : new Error(stringifyReason(first));
|
|
3578
|
+
}
|
|
3579
|
+
return {
|
|
3580
|
+
trackIds: succeeded.map((el) => el.trackId),
|
|
3581
|
+
bpm: parsed.bpm,
|
|
3582
|
+
timeSignature: parsed.timeSignature,
|
|
3583
|
+
duration: parsed.duration,
|
|
3584
|
+
name: parsed.name
|
|
3585
|
+
};
|
|
3586
|
+
}
|
|
3587
|
+
function stringifyReason(reason) {
|
|
3588
|
+
if (reason === null) return "null";
|
|
3589
|
+
if (reason === void 0) return "undefined";
|
|
3590
|
+
if (typeof reason === "object") {
|
|
3591
|
+
try {
|
|
3592
|
+
return JSON.stringify(reason);
|
|
3593
|
+
} catch {
|
|
3594
|
+
return Object.prototype.toString.call(reason);
|
|
3595
|
+
}
|
|
3596
|
+
}
|
|
3597
|
+
return String(reason);
|
|
3598
|
+
}
|
|
3599
|
+
|
|
3498
3600
|
// src/interactions/recording-clip.ts
|
|
3499
3601
|
var import_core7 = require("@waveform-playlist/core");
|
|
3500
3602
|
function addRecordedClip(host, trackId, buf, startSample, durSamples, offsetSamples = 0) {
|
|
@@ -5067,6 +5169,22 @@ var DawEditorElement = class extends import_lit14.LitElement {
|
|
|
5067
5169
|
async loadFiles(files) {
|
|
5068
5170
|
return loadFiles(this, files);
|
|
5069
5171
|
}
|
|
5172
|
+
/**
|
|
5173
|
+
* Imperatively load a `.mid` file (URL or File) and create N `<daw-track>`
|
|
5174
|
+
* elements — one per note-bearing MIDI track. On any per-track failure,
|
|
5175
|
+
* every `<daw-track>` appended during the call is removed (both successful
|
|
5176
|
+
* and failed) so the editor returns to its pre-call state.
|
|
5177
|
+
*
|
|
5178
|
+
* `options.signal` is forwarded to `fetch()` only for URL sources; aborting
|
|
5179
|
+
* after parsing does not cancel in-flight `addTrack` calls.
|
|
5180
|
+
*
|
|
5181
|
+
* Requires the optional `@dawcore/midi` peer dep — throws with an install
|
|
5182
|
+
* hint (and `console.warn`s the original error) when the dynamic import
|
|
5183
|
+
* fails for any reason.
|
|
5184
|
+
*/
|
|
5185
|
+
async loadMidi(source, options) {
|
|
5186
|
+
return loadMidiImpl(this, source, options);
|
|
5187
|
+
}
|
|
5070
5188
|
// --- Programmatic Track API ---
|
|
5071
5189
|
/**
|
|
5072
5190
|
* Build the engine if it hasn't been built yet. Lets consumers obtain a
|