@dawcore/components 0.0.15 → 0.0.17

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 CHANGED
@@ -1,8 +1,9 @@
1
1
  import * as lit from 'lit';
2
2
  import { LitElement, PropertyValues, ReactiveController, ReactiveControllerHost } from 'lit';
3
- import { Peaks, Bits, FadeType, PeakData, MeterEntry, SnapTo, ClipTrack, KeyboardShortcut } from '@waveform-playlist/core';
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 { ClipRegistration, CanvasRegistration, ViewportState } from '@dawcore/spectrogram';
6
7
 
7
8
  declare class DawClipElement extends LitElement {
8
9
  src: string;
@@ -16,6 +17,16 @@ declare class DawClipElement extends LitElement {
16
17
  fadeIn: number;
17
18
  fadeOut: number;
18
19
  fadeType: string;
20
+ /** MIDI notes — JS property only, not reflected (note arrays are too large for attributes). */
21
+ midiNotes: MidiNoteData[] | null;
22
+ /** MIDI channel (0-indexed). Channel 9 = GM percussion. */
23
+ get midiChannel(): number | null;
24
+ set midiChannel(value: number | null);
25
+ private _midiChannel;
26
+ /** MIDI program (GM instrument 0-127). Used by SoundFontToneTrack. */
27
+ get midiProgram(): number | null;
28
+ set midiProgram(value: number | null);
29
+ private _midiProgram;
19
30
  readonly clipId: `${string}-${string}-${string}-${string}-${string}`;
20
31
  createRenderRoot(): this;
21
32
  connectedCallback(): void;
@@ -28,6 +39,113 @@ declare global {
28
39
  }
29
40
  }
30
41
 
42
+ type TrackRenderMode = 'waveform' | 'piano-roll' | 'spectrogram';
43
+ interface TrackDescriptor {
44
+ name: string;
45
+ src: string;
46
+ volume: number;
47
+ pan: number;
48
+ muted: boolean;
49
+ soloed: boolean;
50
+ renderMode: TrackRenderMode;
51
+ spectrogramConfig?: SpectrogramConfig | null;
52
+ clips: ClipDescriptor[];
53
+ }
54
+ /**
55
+ * Common fields shared by all clip descriptors regardless of source.
56
+ */
57
+ interface BaseClipDescriptor {
58
+ src: string;
59
+ peaksSrc: string;
60
+ start: number;
61
+ duration: number;
62
+ offset: number;
63
+ gain: number;
64
+ name: string;
65
+ fadeIn: number;
66
+ fadeOut: number;
67
+ fadeType: FadeType;
68
+ midiNotes: MidiNoteData[] | null;
69
+ midiChannel: number | null;
70
+ midiProgram: number | null;
71
+ }
72
+ /**
73
+ * A clip descriptor sourced from a `<daw-clip>` DOM element. `clipId` is
74
+ * always set — `<daw-clip>.clipId` is a `crypto.randomUUID()` generated at
75
+ * construction. Engine `clip.id` is aligned with this id in `_loadTrack`
76
+ * and `_loadAndAppendClip` so DOM and engine reference the same clip.
77
+ */
78
+ interface DomClipDescriptor extends BaseClipDescriptor {
79
+ kind: 'dom';
80
+ clipId: string;
81
+ }
82
+ /**
83
+ * A clip descriptor synthesized from a non-DOM source — file drops, the
84
+ * `<daw-track src>` shorthand fallback, or recording-clip insertion. No
85
+ * `clipId` because there's no DOM element to align with; the engine
86
+ * generates its own id at clip-creation time.
87
+ */
88
+ interface DropClipDescriptor extends BaseClipDescriptor {
89
+ kind: 'drop';
90
+ }
91
+ type ClipDescriptor = DomClipDescriptor | DropClipDescriptor;
92
+ /**
93
+ * Type predicate for the `'dom'` discriminator. Use to narrow a
94
+ * `ClipDescriptor` to `DomClipDescriptor` (which has `clipId`) without
95
+ * inline `c.kind === 'dom'` repetition.
96
+ */
97
+ declare function isDomClip(desc: ClipDescriptor): desc is DomClipDescriptor;
98
+ /**
99
+ * Public input shape for `editor.addTrack(config)`. All fields optional —
100
+ * defaults match the declarative `<daw-track>` defaults.
101
+ */
102
+ interface TrackConfig {
103
+ name?: string;
104
+ volume?: number;
105
+ pan?: number;
106
+ muted?: boolean;
107
+ soloed?: boolean;
108
+ renderMode?: TrackRenderMode;
109
+ spectrogramConfig?: SpectrogramConfig | null;
110
+ clips?: ClipConfig[];
111
+ /**
112
+ * Convenience: creates a single piano-roll `<daw-clip>` child with these
113
+ * notes and sets `render-mode="piano-roll"` on the track. Equivalent to
114
+ * passing `{ renderMode: 'piano-roll', clips: [{ midiNotes, midiChannel, midiProgram }] }`.
115
+ * An explicit `renderMode` takes precedence over the inferred `'piano-roll'`.
116
+ *
117
+ * Creation-only — ignored by `updateTrack`. To modify notes after the
118
+ * track is built, use `editor.updateClip(trackId, clipId, { midiNotes })` or
119
+ * mutate the `<daw-clip>` element's `midiNotes` property directly.
120
+ */
121
+ midi?: {
122
+ notes: MidiNoteData[];
123
+ channel?: number;
124
+ program?: number;
125
+ };
126
+ }
127
+ /**
128
+ * Public input shape for clips passed via `TrackConfig.clips` or
129
+ * `editor.addClip(trackId, config)`. `src` is optional to support MIDI clips
130
+ * with no audio source. Other fields default to the matching `<daw-clip>`
131
+ * attribute defaults.
132
+ */
133
+ interface ClipConfig {
134
+ src?: string;
135
+ peaksSrc?: string;
136
+ start?: number;
137
+ duration?: number;
138
+ offset?: number;
139
+ gain?: number;
140
+ name?: string;
141
+ fadeIn?: number;
142
+ fadeOut?: number;
143
+ fadeType?: FadeType;
144
+ midiNotes?: MidiNoteData[];
145
+ midiChannel?: number;
146
+ midiProgram?: number;
147
+ }
148
+
31
149
  declare class DawTrackElement extends LitElement {
32
150
  src: string;
33
151
  name: string;
@@ -35,6 +153,8 @@ declare class DawTrackElement extends LitElement {
35
153
  pan: number;
36
154
  muted: boolean;
37
155
  soloed: boolean;
156
+ renderMode: TrackRenderMode;
157
+ spectrogramConfig: SpectrogramConfig | null;
38
158
  readonly trackId: `${string}-${string}-${string}-${string}-${string}`;
39
159
  createRenderRoot(): this;
40
160
  connectedCallback(): void;
@@ -112,6 +232,42 @@ declare global {
112
232
  }
113
233
  }
114
234
 
235
+ declare class DawPianoRollElement extends LitElement {
236
+ midiNotes: MidiNoteData[];
237
+ length: number;
238
+ waveHeight: number;
239
+ get samplesPerPixel(): number;
240
+ set samplesPerPixel(value: number);
241
+ private _samplesPerPixel;
242
+ get sampleRate(): number;
243
+ set sampleRate(value: number);
244
+ private _sampleRate;
245
+ clipOffsetSeconds: number;
246
+ /** Visible viewport start in pixels (relative to timeline origin). */
247
+ visibleStart: number;
248
+ /** Visible viewport end in pixels (relative to timeline origin). */
249
+ visibleEnd: number;
250
+ /** This element's left offset on the timeline (for viewport intersection). */
251
+ originX: number;
252
+ selected: boolean;
253
+ static styles: lit.CSSResult;
254
+ private _rafHandle;
255
+ private _scheduleDraw;
256
+ willUpdate(_changed: PropertyValues): void;
257
+ updated(changedProperties: Map<string, unknown>): void;
258
+ private _getPitchRange;
259
+ private _getNoteColor;
260
+ private _draw;
261
+ connectedCallback(): void;
262
+ disconnectedCallback(): void;
263
+ render(): lit.TemplateResult<1>;
264
+ }
265
+ declare global {
266
+ interface HTMLElementTagNameMap {
267
+ 'daw-piano-roll': DawPianoRollElement;
268
+ }
269
+ }
270
+
115
271
  declare class DawPlayheadElement extends LitElement {
116
272
  private _animation;
117
273
  private _line;
@@ -173,6 +329,8 @@ declare class DawPauseButtonElement extends DawTransportButton {
173
329
  private _targetRef;
174
330
  private _onRecStart;
175
331
  private _onRecEnd;
332
+ private _onRecPause;
333
+ private _onRecResume;
176
334
  static styles: lit.CSSResultGroup[];
177
335
  connectedCallback(): void;
178
336
  disconnectedCallback(): void;
@@ -195,87 +353,6 @@ declare global {
195
353
  }
196
354
  }
197
355
 
198
- interface TrackDescriptor {
199
- name: string;
200
- src: string;
201
- volume: number;
202
- pan: number;
203
- muted: boolean;
204
- soloed: boolean;
205
- clips: ClipDescriptor[];
206
- }
207
- /**
208
- * Common fields shared by all clip descriptors regardless of source.
209
- */
210
- interface BaseClipDescriptor {
211
- src: string;
212
- peaksSrc: string;
213
- start: number;
214
- duration: number;
215
- offset: number;
216
- gain: number;
217
- name: string;
218
- fadeIn: number;
219
- fadeOut: number;
220
- fadeType: FadeType;
221
- }
222
- /**
223
- * A clip descriptor sourced from a `<daw-clip>` DOM element. `clipId` is
224
- * always set — `<daw-clip>.clipId` is a `crypto.randomUUID()` generated at
225
- * construction. Engine `clip.id` is aligned with this id in `_loadTrack`
226
- * and `_loadAndAppendClip` so DOM and engine reference the same clip.
227
- */
228
- interface DomClipDescriptor extends BaseClipDescriptor {
229
- kind: 'dom';
230
- clipId: string;
231
- }
232
- /**
233
- * A clip descriptor synthesized from a non-DOM source — file drops, the
234
- * `<daw-track src>` shorthand fallback, or recording-clip insertion. No
235
- * `clipId` because there's no DOM element to align with; the engine
236
- * generates its own id at clip-creation time.
237
- */
238
- interface DropClipDescriptor extends BaseClipDescriptor {
239
- kind: 'drop';
240
- }
241
- type ClipDescriptor = DomClipDescriptor | DropClipDescriptor;
242
- /**
243
- * Type predicate for the `'dom'` discriminator. Use to narrow a
244
- * `ClipDescriptor` to `DomClipDescriptor` (which has `clipId`) without
245
- * inline `c.kind === 'dom'` repetition.
246
- */
247
- declare function isDomClip(desc: ClipDescriptor): desc is DomClipDescriptor;
248
- /**
249
- * Public input shape for `editor.addTrack(config)`. All fields optional —
250
- * defaults match the declarative `<daw-track>` defaults.
251
- */
252
- interface TrackConfig {
253
- name?: string;
254
- volume?: number;
255
- pan?: number;
256
- muted?: boolean;
257
- soloed?: boolean;
258
- clips?: ClipConfig[];
259
- }
260
- /**
261
- * Public input shape for clips passed via `TrackConfig.clips` or
262
- * `editor.addClip(trackId, config)`. `src` is required — every clip needs
263
- * an audio source to load. Other fields default to the matching
264
- * `<daw-clip>` attribute defaults.
265
- */
266
- interface ClipConfig {
267
- src: string;
268
- peaksSrc?: string;
269
- start?: number;
270
- duration?: number;
271
- offset?: number;
272
- gain?: number;
273
- name?: string;
274
- fadeIn?: number;
275
- fadeOut?: number;
276
- fadeType?: FadeType;
277
- }
278
-
279
356
  /**
280
357
  * Peak generation pipeline: AudioBuffer → web worker → WaveformData → PeakData.
281
358
  *
@@ -434,6 +511,12 @@ interface RecordingSession {
434
511
  /** Stored so it can be removed on stop/cleanup — not just when stream ends. */
435
512
  readonly _onTrackEnded: (() => void) | null;
436
513
  readonly _audioTrack: MediaStreamTrack | null;
514
+ /** Set during stopRecording, cleared by the done message from the worklet. */
515
+ stopAckResolve: (() => void) | null;
516
+ /** True from the start of stopRecording until the session is deleted.
517
+ * Guards pauseRecording / resumeRecording so a mid-drain pause toggle
518
+ * can't dispatch events for a session that's already terminating. */
519
+ stopping: boolean;
437
520
  }
438
521
  /** Readonly view of a recording session for external consumers. */
439
522
  type ReadonlyRecordingSession = Readonly<Omit<RecordingSession, 'chunks' | 'peaks' | '_onTrackEnded' | '_audioTrack' | 'latencySamples'>> & {
@@ -475,16 +558,21 @@ declare class RecordingController implements ReactiveController {
475
558
  private _host;
476
559
  private _sessions;
477
560
  private _workletLoadedCtx;
561
+ /** Tracks worklet pause state explicitly so external consumers (editor,
562
+ * pause button, spacebar) can share one source of truth. */
563
+ private _isPaused;
478
564
  constructor(host: RecordingHost & HTMLElement);
479
565
  hostConnected(): void;
480
566
  hostDisconnected(): void;
481
567
  get isRecording(): boolean;
568
+ get isPaused(): boolean;
482
569
  getSession(trackId: string): ReadonlyRecordingSession | undefined;
483
570
  startRecording(stream: MediaStream, options?: RecordingOptions): Promise<void>;
484
571
  pauseRecording(trackId?: string): void;
485
572
  resumeRecording(trackId?: string): void;
486
- stopRecording(trackId?: string): void;
573
+ stopRecording(trackId?: string): Promise<void>;
487
574
  private _onWorkletMessage;
575
+ private _processWorkletSamples;
488
576
  private _createClipFromRecording;
489
577
  private _removeTrackEndedListener;
490
578
  private _cleanupSession;
@@ -538,6 +626,11 @@ interface ClipPointerHost {
538
626
  dispatchEvent(event: Event): boolean;
539
627
  /** Re-extract peaks for a clip at new offset/duration from cached WaveformData. */
540
628
  reextractClipPeaks(clipId: string, offsetSamples: number, durationSamples: number): ClipPeakSlice | null;
629
+ /**
630
+ * Returns true if the clip is a MIDI clip (has midiNotes).
631
+ * Trim handles are inert for MIDI clips — note slicing is not yet implemented.
632
+ */
633
+ isMidiClip(trackId: string, clipId: string): boolean;
541
634
  }
542
635
  /**
543
636
  * Handles pointer interactions for clip move and trim drag operations.
@@ -646,6 +739,12 @@ interface DawRecordingErrorDetail {
646
739
  trackId: string;
647
740
  error: unknown;
648
741
  }
742
+ interface DawRecordingPauseDetail {
743
+ trackId: string;
744
+ }
745
+ interface DawRecordingResumeDetail {
746
+ trackId: string;
747
+ }
649
748
  interface DawClipConnectedDetail {
650
749
  clipId: string;
651
750
  element: DawClipElement;
@@ -684,6 +783,9 @@ interface DawClipSplitDetail {
684
783
  readonly leftClipId: string;
685
784
  readonly rightClipId: string;
686
785
  }
786
+ interface DawSpectrogramReadyDetail {
787
+ trackId: string;
788
+ }
687
789
  interface DawEventMap {
688
790
  'daw-selection': CustomEvent<DawSelectionDetail>;
689
791
  'daw-seek': CustomEvent<DawSeekDetail>;
@@ -702,6 +804,8 @@ interface DawEventMap {
702
804
  'daw-recording-start': CustomEvent<DawRecordingStartDetail>;
703
805
  'daw-recording-complete': CustomEvent<DawRecordingCompleteDetail>;
704
806
  'daw-recording-error': CustomEvent<DawRecordingErrorDetail>;
807
+ 'daw-recording-pause': CustomEvent<DawRecordingPauseDetail>;
808
+ 'daw-recording-resume': CustomEvent<DawRecordingResumeDetail>;
705
809
  'daw-clip-connected': CustomEvent<DawClipConnectedDetail>;
706
810
  'daw-clip-update': CustomEvent<DawClipUpdateDetail>;
707
811
  'daw-clip-ready': CustomEvent<DawClipIdDetail>;
@@ -709,6 +813,7 @@ interface DawEventMap {
709
813
  'daw-clip-move': CustomEvent<DawClipMoveDetail>;
710
814
  'daw-clip-trim': CustomEvent<DawClipTrimDetail>;
711
815
  'daw-clip-split': CustomEvent<DawClipSplitDetail>;
816
+ 'daw-spectrogram-ready': CustomEvent<DawSpectrogramReadyDetail>;
712
817
  }
713
818
  type DawEvent<K extends keyof DawEventMap> = DawEventMap[K];
714
819
  interface LoadFilesResult {
@@ -739,6 +844,44 @@ declare class DawEditorElement extends LitElement {
739
844
  * floor already provides this; this attribute controls the temporal mode.
740
845
  */
741
846
  indefinitePlayback: boolean;
847
+ /**
848
+ * Default spectrogram FFT/render config inherited by tracks with
849
+ * `render-mode="spectrogram"` that do not set their own. Wired into the
850
+ * orchestrator by `SpectrogramController` in Task 14.
851
+ */
852
+ get spectrogramConfig(): SpectrogramConfig | null;
853
+ set spectrogramConfig(value: SpectrogramConfig | null);
854
+ private _spectrogramConfig;
855
+ /**
856
+ * Default color map for spectrogram tracks. Tracks may override via
857
+ * their own per-track property in a future iteration. Separate from
858
+ * `spectrogramConfig` because `ColorMapValue` is not part of the
859
+ * `SpectrogramConfig` shape.
860
+ */
861
+ get spectrogramColorMap(): ColorMapValue | null;
862
+ set spectrogramColorMap(value: ColorMapValue | null);
863
+ private _spectrogramColorMap;
864
+ private _ensureSpectrogramController;
865
+ /** Called by <daw-spectrogram> after transferControlToOffscreen. */
866
+ _spectrogramRegisterCanvas(reg: {
867
+ canvasId: string;
868
+ canvas: OffscreenCanvas;
869
+ clipId: string;
870
+ trackId: string;
871
+ channelIndex: number;
872
+ chunkIndex: number;
873
+ globalPixelOffset: number;
874
+ widthPx: number;
875
+ heightPx: number;
876
+ }): void;
877
+ /** Called by <daw-spectrogram> on chunk unmount / element disconnect. */
878
+ _spectrogramUnregisterCanvas(canvasId: string): void;
879
+ /**
880
+ * Push a clip's decoded audio into the spectrogram controller. No-op
881
+ * unless the track is in spectrogram render-mode and the controller
882
+ * already exists (it bootstraps from canvas registration).
883
+ */
884
+ private _maybeRegisterSpectrogramClipAudio;
742
885
  scaleMode: 'temporal' | 'beats';
743
886
  get ticksPerPixel(): number;
744
887
  set ticksPerPixel(value: number);
@@ -795,12 +938,19 @@ declare class DawEditorElement extends LitElement {
795
938
  private _audioResume;
796
939
  eagerResume?: string;
797
940
  private _recordingController;
941
+ private _spectrogramController;
798
942
  private _clipPointer;
799
943
  get _clipHandler(): ClipPointerHandler | null;
800
944
  get engine(): PlaylistEngine | null;
801
945
  get renderSamplesPerPixel(): number;
802
946
  /** Re-extract peaks for a clip at new offset/duration from cached WaveformData. */
803
947
  reextractClipPeaks(clipId: string, offsetSamples: number, durationSamples: number): PeakData | null;
948
+ /**
949
+ * Returns true if the clip is a MIDI clip (has midiNotes).
950
+ * Used by ClipPointerHandler to make trim handles inert for MIDI clips.
951
+ * Returns false for unknown track/clip IDs (defensive).
952
+ */
953
+ isMidiClip(trackId: string, clipId: string): boolean;
804
954
  private _pointer;
805
955
  private _viewport;
806
956
  static styles: lit.CSSResult[];
@@ -829,9 +979,12 @@ declare class DawEditorElement extends LitElement {
829
979
  connectedCallback(): void;
830
980
  disconnectedCallback(): void;
831
981
  willUpdate(changedProperties: Map<string, unknown>): void;
982
+ protected updated(_changed: Map<string, unknown>): void;
832
983
  private _onTrackConnected;
833
984
  private _onTrackRemoved;
834
985
  private _onTrackUpdate;
986
+ /** Drop the controller when no spectrogram tracks remain. */
987
+ private _disposeSpectrogramControllerIfEmpty;
835
988
  private static _CONTROL_PROPS;
836
989
  private _onTrackControl;
837
990
  private _onTrackRemoveRequest;
@@ -859,6 +1012,27 @@ declare class DawEditorElement extends LitElement {
859
1012
  * uses sync `extractPeaks` and inserts a preview track BEFORE audio decode.
860
1013
  */
861
1014
  private _finalizeAudioClip;
1015
+ /**
1016
+ * Filter MIDI notes to only those with finite, in-range fields. Logs a
1017
+ * warning for each dropped note. Used by _buildMidiClip and the
1018
+ * _applyClipUpdate MIDI branch to prevent NaN propagation through the
1019
+ * timeline.
1020
+ */
1021
+ private _validMidiNotes;
1022
+ /**
1023
+ * A clip descriptor is treated as MIDI when it has no audio src.
1024
+ * Includes placeholder MIDI clips (no notes, no duration yet — registered
1025
+ * with a 1s default span; notes upgrade via _applyClipUpdate). Warns when
1026
+ * a clip ambiguously has both src and midiNotes — the audio path runs
1027
+ * and notes would be silently ignored.
1028
+ */
1029
+ private _isMidiDescriptor;
1030
+ /**
1031
+ * Build an engine clip from a MIDI clip descriptor. Always returns a clip
1032
+ * — empty notes / no declared duration get a 1-second placeholder span so
1033
+ * the clip is reachable via `engine.updateTrack` once notes arrive.
1034
+ */
1035
+ private _buildMidiClip;
862
1036
  /** Remove a single clip from all per-clip caches. Used by error rollbacks. */
863
1037
  private _purgeClipCaches;
864
1038
  /**
@@ -963,9 +1137,20 @@ declare class DawEditorElement extends LitElement {
963
1137
  recordingStream: MediaStream | null;
964
1138
  get currentTime(): number;
965
1139
  get isRecording(): boolean;
1140
+ get isRecordingPaused(): boolean;
966
1141
  pauseRecording(): void;
967
1142
  resumeRecording(): void;
968
- stopRecording(): void;
1143
+ /**
1144
+ * Audacity-style pause toggle for active recordings: pauses both the
1145
+ * worklet capture and (if running) the playback Transport. On resume,
1146
+ * Transport restarts only if it was running before — non-overdub
1147
+ * recordings stay silent on resume.
1148
+ */
1149
+ togglePauseRecording(): void;
1150
+ /** Set in togglePauseRecording when Transport is paused alongside the
1151
+ * worklet, so resume can restart it. Cleared on resume and on stop. */
1152
+ private _wasPlayingDuringRecording;
1153
+ stopRecording(): Promise<void>;
969
1154
  _addRecordedClip(trackId: string, buf: AudioBuffer, startSample: number, durSamples: number, offsetSamples?: number): void;
970
1155
  addWorkletModule(url: string): Promise<void>;
971
1156
  createAudioWorkletNode(name: string, options?: AudioWorkletNodeOptions): AudioWorkletNode;
@@ -1093,6 +1278,89 @@ declare global {
1093
1278
  }
1094
1279
  }
1095
1280
 
1281
+ declare class DawSpectrogramElement extends LitElement {
1282
+ clipId: string;
1283
+ trackId: string;
1284
+ channelIndex: number;
1285
+ length: number;
1286
+ waveHeight: number;
1287
+ get samplesPerPixel(): number;
1288
+ set samplesPerPixel(value: number);
1289
+ private _samplesPerPixel;
1290
+ get sampleRate(): number;
1291
+ set sampleRate(value: number);
1292
+ private _sampleRate;
1293
+ clipOffsetSeconds: number;
1294
+ visibleStart: number;
1295
+ visibleEnd: number;
1296
+ originX: number;
1297
+ static styles: lit.CSSResult;
1298
+ private _canvases;
1299
+ private _registeredCanvasIds;
1300
+ /**
1301
+ * Walk up to the editor host. `closest('daw-editor')` does NOT cross
1302
+ * shadow boundaries — and this element lives inside the editor's shadow
1303
+ * DOM — so use getRootNode().host to step out.
1304
+ */
1305
+ private _findHostEditor;
1306
+ willUpdate(changed: PropertyValues): void;
1307
+ private _rebuildChunks;
1308
+ protected updated(_changed: PropertyValues): void;
1309
+ private _registerCanvases;
1310
+ private _unregisterAllCanvases;
1311
+ disconnectedCallback(): void;
1312
+ render(): lit.TemplateResult<1>;
1313
+ }
1314
+ declare global {
1315
+ interface HTMLElementTagNameMap {
1316
+ 'daw-spectrogram': DawSpectrogramElement;
1317
+ }
1318
+ }
1319
+
1320
+ interface SpectrogramControllerHost extends ReactiveControllerHost {
1321
+ dispatchEvent(event: Event): boolean;
1322
+ }
1323
+ /**
1324
+ * Lit reactive controller that owns a `SpectrogramOrchestrator` for the
1325
+ * `<daw-editor>` host. Creates the orchestrator lazily on first canvas
1326
+ * registration so editors without spectrogram tracks pay nothing.
1327
+ *
1328
+ * Holds editor-level defaults and per-track overrides separately; merges
1329
+ * them down to a single (config, colorMap) pair for the orchestrator on
1330
+ * each change.
1331
+ *
1332
+ * v1 limitation: orchestrator accepts ONE config / colorMap at a time, so
1333
+ * the "first registered track wins" rule applies when multiple tracks are
1334
+ * in spectrogram mode with different overrides. Multi-track per-clip
1335
+ * configs are deferred to a follow-up.
1336
+ */
1337
+ declare class SpectrogramController implements ReactiveController {
1338
+ private host;
1339
+ private workerFactory;
1340
+ private orchestrator;
1341
+ private editorConfig;
1342
+ private editorColorMap;
1343
+ private trackConfigs;
1344
+ private trackColorMaps;
1345
+ constructor(host: SpectrogramControllerHost, workerFactory: () => Worker);
1346
+ hostConnected(): void;
1347
+ hostDisconnected(): void;
1348
+ setEditorConfig(config: SpectrogramConfig | null): void;
1349
+ setEditorColorMap(colorMap: ColorMapValue | null): void;
1350
+ setTrackConfig(trackId: string, config: SpectrogramConfig | null): void;
1351
+ setTrackColorMap(trackId: string, colorMap: ColorMapValue | null): void;
1352
+ registerClipAudio(reg: ClipRegistration): void;
1353
+ unregisterClipAudio(clipId: string): void;
1354
+ registerCanvas(reg: CanvasRegistration): void;
1355
+ unregisterCanvas(canvasId: string): void;
1356
+ setViewport(state: ViewportState): void;
1357
+ dispose(): void;
1358
+ private ensureOrchestrator;
1359
+ private reapply;
1360
+ private mergedConfig;
1361
+ private mergedColorMap;
1362
+ }
1363
+
1096
1364
  interface AudioResumeHost extends ReactiveControllerHost, HTMLElement {
1097
1365
  /** Returns the AudioContext to resume on user gesture */
1098
1366
  readonly audioContext: AudioContext;
@@ -1149,4 +1417,4 @@ interface SplitHost {
1149
1417
  */
1150
1418
  declare function splitAtPlayhead(host: SplitHost): boolean;
1151
1419
 
1152
- export { AudioResumeController, type ClipConfig, type ClipDescriptor, type ClipEngineContract, ClipPointerHandler, type ClipPointerHost, type DawClipConnectedDetail, DawClipElement, type DawClipErrorDetail, type DawClipIdDetail, type DawClipMoveDetail, type DawClipSplitDetail, type DawClipTrimDetail, type DawClipUpdateDetail, DawEditorElement, type DawErrorDetail, type DawEvent, type DawEventMap, type DawFilesLoadErrorDetail, DawGridElement, DawKeyboardShortcutsElement, DawPauseButtonElement, DawPlayButtonElement, DawPlayheadElement, DawRecordButtonElement, type DawRecordingCompleteDetail, type DawRecordingErrorDetail, type DawRecordingStartDetail, DawRulerElement, type DawSeekDetail, type DawSelectionDetail, DawSelectionElement, DawStopButtonElement, type DawTrackConnectedDetail, type DawTrackControlDetail, DawTrackControlsElement, DawTrackElement, type DawTrackErrorDetail, type DawTrackIdDetail, type DawTrackRemoveDetail, type DawTrackSelectDetail, DawTransportButton, DawTransportElement, DawWaveformElement, type DomClipDescriptor, type DropClipDescriptor, type KeyBinding, type LoadFilesResult, type PlaybackShortcutMap, type PointerEngineContract, RecordingController, type RecordingOptions, type RecordingSession, type SplitEngineContract, type SplitHost, type SplittingShortcutMap, type TrackConfig, type TrackDescriptor, type UndoShortcutMap, type WaveformSegment, isDomClip, splitAtPlayhead };
1420
+ export { AudioResumeController, type ClipConfig, type ClipDescriptor, type ClipEngineContract, ClipPointerHandler, type ClipPointerHost, type DawClipConnectedDetail, DawClipElement, type DawClipErrorDetail, type DawClipIdDetail, type DawClipMoveDetail, type DawClipSplitDetail, type DawClipTrimDetail, type DawClipUpdateDetail, DawEditorElement, type DawErrorDetail, type DawEvent, type DawEventMap, type DawFilesLoadErrorDetail, DawGridElement, DawKeyboardShortcutsElement, DawPauseButtonElement, DawPianoRollElement, DawPlayButtonElement, DawPlayheadElement, DawRecordButtonElement, type DawRecordingCompleteDetail, type DawRecordingErrorDetail, type DawRecordingStartDetail, DawRulerElement, type DawSeekDetail, type DawSelectionDetail, DawSelectionElement, DawSpectrogramElement, DawStopButtonElement, type DawTrackConnectedDetail, type DawTrackControlDetail, DawTrackControlsElement, DawTrackElement, type DawTrackErrorDetail, type DawTrackIdDetail, type DawTrackRemoveDetail, type DawTrackSelectDetail, DawTransportButton, DawTransportElement, DawWaveformElement, type DomClipDescriptor, type DropClipDescriptor, type KeyBinding, type LoadFilesResult, type PlaybackShortcutMap, type PointerEngineContract, RecordingController, type RecordingOptions, type RecordingSession, SpectrogramController, type SplitEngineContract, type SplitHost, type SplittingShortcutMap, type TrackConfig, type TrackDescriptor, type TrackRenderMode, type UndoShortcutMap, type WaveformSegment, isDomClip, splitAtPlayhead };