@dawcore/components 0.0.21 → 0.0.22

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/elements/daw-clip.ts","../src/elements/daw-track.ts","../src/elements/daw-waveform.ts","../src/utils/peak-rendering.ts","../src/utils/viewport.ts","../src/elements/daw-piano-roll.ts","../src/elements/daw-playhead.ts","../src/controllers/animation-controller.ts","../src/elements/daw-transport.ts","../src/elements/daw-play-button.ts","../src/elements/daw-transport-button.ts","../src/elements/daw-pause-button.ts","../src/elements/daw-stop-button.ts","../src/elements/daw-editor.ts","../src/types.ts","../src/workers/peaksWorker.ts","../src/workers/waveformDataUtils.ts","../src/workers/peakPipeline.ts","../src/elements/daw-ruler.ts","../src/utils/time-format.ts","../src/utils/smart-scale.ts","../src/utils/musical-tick-cache.ts","../src/elements/daw-track-controls.ts","../src/elements/daw-grid.ts","../src/styles/theme.ts","../src/controllers/viewport-controller.ts","../src/controllers/audio-resume-controller.ts","../src/controllers/recording-controller.ts","../src/controllers/spectrogram-controller.ts","../src/interactions/pointer-handler.ts","../src/interactions/constants.ts","../src/interactions/clip-pointer-handler.ts","../src/interactions/file-loader.ts","../src/interactions/midi-loader.ts","../src/interactions/recording-clip.ts","../src/interactions/split-handler.ts","../src/interactions/clip-peak-sync.ts","../src/interactions/peaks-loader.ts","../src/controllers/scroll-sync-controller.ts","../src/elements/daw-selection.ts","../src/elements/daw-record-button.ts","../src/elements/daw-keyboard-shortcuts.ts","../src/elements/daw-spectrogram.ts"],"sourcesContent":["import { LitElement } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport type { PropertyValues } from 'lit';\nimport type { MidiNoteData } from '@waveform-playlist/core';\n\n@customElement('daw-clip')\nexport class DawClipElement extends LitElement {\n @property() src = '';\n @property({ attribute: 'peaks-src' }) peaksSrc = '';\n /**\n * Timeline position in seconds. JS property only — NOT reflected to the\n * `start` attribute (Lit's `reflect` defaults to false). Tests that need\n * to assert clip position must read this property directly; reading\n * `el.getAttribute('start')` returns `null` regardless of correctness.\n */\n @property({ type: Number }) start = 0;\n @property({ type: Number }) duration = 0;\n @property({ type: Number }) offset = 0;\n @property({ type: Number }) gain = 1;\n @property() name = '';\n @property() color = '';\n @property({ type: Number, attribute: 'fade-in' }) fadeIn = 0;\n @property({ type: Number, attribute: 'fade-out' }) fadeOut = 0;\n @property({ attribute: 'fade-type' }) fadeType = 'linear';\n\n /** MIDI notes — JS property only, not reflected (note arrays are too large for attributes). */\n @property({ attribute: false }) midiNotes: MidiNoteData[] | null = null;\n\n /** MIDI channel (0-indexed). Channel 9 = GM percussion. */\n @property({ type: Number, attribute: 'midi-channel', noAccessor: true })\n get midiChannel(): number | null {\n return this._midiChannel;\n }\n set midiChannel(value: number | null) {\n const old = this._midiChannel;\n if (value === null) {\n this._midiChannel = null;\n this.requestUpdate('midiChannel', old);\n return;\n }\n if (!Number.isFinite(value) || !Number.isInteger(value) || value < 0 || value > 15) {\n console.warn('[dawcore] daw-clip midi-channel ' + value + ' is out of range 0-15 — ignored');\n return;\n }\n this._midiChannel = value;\n this.requestUpdate('midiChannel', old);\n }\n private _midiChannel: number | null = null;\n\n /** MIDI program (GM instrument 0-127). Used by SoundFontToneTrack. */\n @property({ type: Number, attribute: 'midi-program', noAccessor: true })\n get midiProgram(): number | null {\n return this._midiProgram;\n }\n set midiProgram(value: number | null) {\n const old = this._midiProgram;\n if (value === null) {\n this._midiProgram = null;\n this.requestUpdate('midiProgram', old);\n return;\n }\n if (!Number.isFinite(value) || !Number.isInteger(value) || value < 0 || value > 127) {\n console.warn('[dawcore] daw-clip midi-program ' + value + ' is out of range 0-127 — ignored');\n return;\n }\n this._midiProgram = value;\n this.requestUpdate('midiProgram', old);\n }\n private _midiProgram: number | null = null;\n\n readonly clipId = crypto.randomUUID();\n\n // Light DOM — no visual rendering, just a data container\n createRenderRoot() {\n return this;\n }\n\n connectedCallback() {\n super.connectedCallback();\n // Defer dispatch so:\n // 1. The editor's connectedCallback (which registers the daw-clip-connected\n // listener) has time to run when parsed all-at-once.\n // 2. The parent's deferred daw-track-connected event fires first; the editor\n // reads <daw-clip> children synchronously via _readTrackDescriptor, so\n // this event is the canonical late-append signal.\n // _onClipConnected skips this event during the parent track's initial load\n // (parent not yet in _engineTracks) and runs an incremental load otherwise.\n setTimeout(() => {\n this.dispatchEvent(\n new CustomEvent('daw-clip-connected', {\n bubbles: true,\n composed: true,\n detail: { clipId: this.clipId, element: this },\n })\n );\n }, 0);\n }\n\n // Removal is detected by the editor's MutationObserver — detached elements\n // cannot bubble events to ancestors.\n\n private _hasRendered = false;\n\n updated(changed: PropertyValues) {\n if (!this._hasRendered) {\n this._hasRendered = true;\n return;\n }\n const clipProps = [\n 'src',\n 'peaksSrc',\n 'start',\n 'duration',\n 'offset',\n 'gain',\n 'name',\n 'fadeIn',\n 'fadeOut',\n 'fadeType',\n 'midiNotes',\n 'midiChannel',\n 'midiProgram',\n ];\n if (clipProps.some((p) => changed.has(p as keyof this))) {\n // Resolve parent <daw-track> at dispatch time so consumers don't need\n // to walk closest('daw-track') themselves. trackId may be empty if the\n // clip is mounted outside a track (developer error).\n const trackEl = this.closest('daw-track') as { trackId?: string } | null;\n this.dispatchEvent(\n new CustomEvent('daw-clip-update', {\n bubbles: true,\n composed: true,\n detail: { trackId: trackEl?.trackId ?? '', clipId: this.clipId },\n })\n );\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-clip': DawClipElement;\n }\n}\n","import { LitElement } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport type { PropertyValues } from 'lit';\nimport type { RenderMode, SpectrogramConfig } from '@waveform-playlist/core';\n\n@customElement('daw-track')\nexport class DawTrackElement extends LitElement {\n @property() src = '';\n @property() name = '';\n @property({ type: Number }) volume = 1;\n @property({ type: Number }) pan = 0;\n @property({ type: Boolean }) muted = false;\n @property({ type: Boolean }) soloed = false;\n\n // Custom getter/setter so we can warn-and-fallback on 'both' (dawcore\n // doesn't yet support rendering waveform + spectrogram simultaneously).\n @property({ attribute: 'render-mode', noAccessor: true })\n get renderMode(): RenderMode {\n return this._renderMode;\n }\n set renderMode(value: RenderMode) {\n const old = this._renderMode;\n let next = value;\n if (next === 'both') {\n console.warn(\n '[dawcore] <daw-track render-mode=\"both\"> is not yet supported; falling back to \\'spectrogram\\''\n );\n next = 'spectrogram';\n }\n this._renderMode = next;\n this.requestUpdate('renderMode', old);\n }\n private _renderMode: RenderMode = 'waveform';\n\n @property({ attribute: false }) spectrogramConfig: SpectrogramConfig | null = null;\n\n readonly trackId = crypto.randomUUID();\n\n // Light DOM so <daw-clip> children are queryable.\n createRenderRoot() {\n return this;\n }\n\n connectedCallback() {\n super.connectedCallback();\n // Defer so the editor's connectedCallback (which registers the\n // daw-track-connected listener) has time to run. Without this,\n // tracks parsed before the editor would fire events with no listener.\n setTimeout(() => {\n this.dispatchEvent(\n new CustomEvent('daw-track-connected', {\n bubbles: true,\n composed: true,\n detail: { trackId: this.trackId, element: this },\n })\n );\n }, 0);\n }\n\n // Track removal is detected by the editor's MutationObserver,\n // not by dispatching from disconnectedCallback (detached elements\n // cannot bubble events to ancestors).\n\n private _hasRendered = false;\n\n updated(changed: PropertyValues) {\n // Skip the initial render — all properties appear in `changed` on first\n // update, but the editor handles initial state via daw-track-connected.\n if (!this._hasRendered) {\n this._hasRendered = true;\n return;\n }\n\n const trackProps = [\n 'volume',\n 'pan',\n 'muted',\n 'soloed',\n 'src',\n 'name',\n 'renderMode',\n 'spectrogramConfig',\n ];\n const hasTrackChange = trackProps.some((p) => changed.has(p as keyof this));\n\n if (hasTrackChange) {\n this.dispatchEvent(\n new CustomEvent('daw-track-update', {\n bubbles: true,\n composed: true,\n detail: { trackId: this.trackId },\n })\n );\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-track': DawTrackElement;\n }\n}\n","import { LitElement, html, css } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport type { Peaks, Bits } from '@waveform-playlist/core';\nimport { aggregatePeaks, calculateBarRects } from '../utils/peak-rendering';\nimport { getVisibleChunkIndices } from '../utils/viewport';\n\n/** A segment mapping a peak-index range to a pixel range within the waveform. */\nexport interface WaveformSegment {\n /** Start position in the peaks array (fractional index). */\n peakStart: number;\n /** End position in the peaks array (fractional index). */\n peakEnd: number;\n /** Start pixel position within the waveform. */\n pixelStart: number;\n /** End pixel position within the waveform. */\n pixelEnd: number;\n}\n\nconst MAX_CANVAS_WIDTH = 1000;\n\n/** Layout/data properties that require a full redraw when changed. */\nconst LAYOUT_PROPS = new Set(['length', 'waveHeight', 'barWidth', 'barGap', 'segments']);\n\n/**\n * Group dirty peak indices by canvas chunk. Returns bar-pixel-aligned\n * min/max positions per chunk for correct clearRect coordinates,\n * including when barWidth > 1 or barGap > 0.\n */\nfunction groupDirtyByChunk(\n dirtyPixels: Set<number>,\n step: number\n): Map<number, { min: number; max: number }> {\n const dirtyByChunk = new Map<number, { min: number; max: number }>();\n for (const peakIdx of dirtyPixels) {\n // Map peak index to its bar's global pixel position\n const barPixel = Math.floor(peakIdx / step) * step;\n const chunkIdx = Math.floor(barPixel / MAX_CANVAS_WIDTH);\n const existing = dirtyByChunk.get(chunkIdx);\n if (existing) {\n dirtyByChunk.set(chunkIdx, {\n min: Math.min(existing.min, barPixel),\n max: Math.max(existing.max, barPixel),\n });\n } else {\n dirtyByChunk.set(chunkIdx, { min: barPixel, max: barPixel });\n }\n }\n return dirtyByChunk;\n}\n\n@customElement('daw-waveform')\nexport class DawWaveformElement extends LitElement {\n private _peaks: Peaks = new Int16Array(0);\n private _dirtyPixels: Set<number> = new Set();\n private _drawScheduled = false;\n private _rafId = 0;\n /** Chunk indices visible in the last draw pass — used to detect new chunks on scroll. */\n private _drawnChunks: Set<number> = new Set();\n\n set peaks(value: Peaks) {\n this._peaks = value;\n this._markAllDirty();\n this.requestUpdate();\n }\n\n get peaks(): Peaks {\n return this._peaks;\n }\n\n /**\n * Replace the internal peaks reference without marking all dirty.\n * Use with updatePeaks() for incremental recording updates where\n * appendPeaks() returns a new array but only the tail changed.\n */\n setPeaksQuiet(value: Peaks) {\n this._peaks = value;\n }\n\n get bits(): Bits {\n return this._peaks instanceof Int8Array ? 8 : 16;\n }\n\n @property({ type: Number, attribute: false }) length = 0;\n @property({ type: Number, attribute: false }) waveHeight = 128;\n @property({ type: Number, attribute: false }) barWidth = 1;\n @property({ type: Number, attribute: false }) barGap = 0;\n /** Visible viewport start in pixels (relative to timeline origin). */\n @property({ type: Number, attribute: false }) visibleStart = -Infinity;\n /** Visible viewport end in pixels (relative to timeline origin). */\n @property({ type: Number, attribute: false }) visibleEnd = Infinity;\n /** This element's left offset on the timeline (for viewport intersection). */\n @property({ type: Number, attribute: false }) originX = 0;\n /** When set, draws per-segment with independent samples-per-pixel ratios. */\n @property({ attribute: false }) segments?: WaveformSegment[];\n\n static styles = css`\n :host {\n display: block;\n position: relative;\n }\n .container {\n position: relative;\n }\n canvas {\n position: absolute;\n top: 0;\n }\n `;\n\n private _getVisibleChunkIndices(): number[] {\n return getVisibleChunkIndices(\n this.length,\n MAX_CANVAS_WIDTH,\n this.visibleStart,\n this.visibleEnd,\n this.originX\n );\n }\n\n /**\n * Mark a range of peak indices as dirty for incremental redraw.\n * The caller must have already updated the underlying peaks array.\n * Does NOT trigger a Lit re-render — bypasses Lit entirely.\n */\n updatePeaks(startIndex: number, endIndex: number) {\n const peakCount = Math.floor(this._peaks.length / 2);\n const clampedStart = Math.max(0, startIndex);\n const clampedEnd = Math.min(peakCount, endIndex);\n for (let i = clampedStart; i < clampedEnd; i++) {\n this._dirtyPixels.add(i);\n }\n this._scheduleDraw();\n }\n\n private _markAllDirty() {\n const peakCount = Math.floor(this._peaks.length / 2);\n for (let i = 0; i < peakCount; i++) {\n this._dirtyPixels.add(i);\n }\n this._scheduleDraw();\n }\n\n private _scheduleDraw() {\n if (!this._drawScheduled) {\n this._drawScheduled = true;\n this._rafId = requestAnimationFrame(() => {\n this._drawScheduled = false;\n this._drawDirty();\n });\n }\n }\n\n private _drawDirty() {\n if (this._dirtyPixels.size === 0 || this.length === 0 || this._peaks.length === 0) {\n this._dirtyPixels.clear();\n return;\n }\n\n const canvases = this.shadowRoot?.querySelectorAll('canvas');\n if (!canvases || canvases.length === 0) {\n // Don't clear _dirtyPixels — canvases may appear after Lit renders.\n // connectedCallback or updated() will reschedule the draw.\n return;\n }\n\n const step = this.barWidth + this.barGap;\n const dpr = typeof devicePixelRatio !== 'undefined' ? devicePixelRatio : 1;\n const halfHeight = this.waveHeight / 2;\n const bits = this.bits;\n const waveColor =\n getComputedStyle(this).getPropertyValue('--daw-wave-color').trim() || '#c49a6c';\n\n this._drawnChunks.clear();\n\n if (this.segments) {\n // Segment mode: full redraw per chunk (segments have variable SPP)\n for (const canvas of canvases) {\n const chunkIdx = Number(canvas.dataset.index);\n this._drawnChunks.add(chunkIdx);\n this._drawSegments(canvas, chunkIdx, dpr, halfHeight, bits, waveColor);\n }\n } else {\n const dirtyByChunk = groupDirtyByChunk(this._dirtyPixels, step);\n for (const canvas of canvases) {\n const chunkIdx = Number(canvas.dataset.index);\n this._drawnChunks.add(chunkIdx);\n const range = dirtyByChunk.get(chunkIdx);\n if (!range) continue;\n this._drawChunk(canvas, chunkIdx, range, step, dpr, halfHeight, bits, waveColor);\n }\n }\n\n this._dirtyPixels.clear();\n }\n\n private _drawChunk(\n canvas: HTMLCanvasElement,\n chunkIdx: number,\n range: { min: number; max: number },\n step: number,\n dpr: number,\n halfHeight: number,\n bits: Bits,\n waveColor: string\n ) {\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const globalOffset = chunkIdx * MAX_CANVAS_WIDTH;\n // range.min/max are bar-pixel-aligned global positions from groupDirtyByChunk\n const clearStart = Math.max(0, range.min - globalOffset);\n const clearEnd = range.max - globalOffset + this.barWidth;\n const clearWidth = clearEnd - clearStart;\n const firstBar = range.min;\n\n ctx.resetTransform();\n ctx.clearRect(clearStart * dpr, 0, clearWidth * dpr, canvas.height);\n ctx.scale(dpr, dpr);\n ctx.fillStyle = waveColor;\n\n const canvasWidth = Math.min(MAX_CANVAS_WIDTH, this.length - globalOffset);\n const regionEnd = Math.min(globalOffset + clearEnd, globalOffset + canvasWidth);\n\n for (let bar = Math.max(0, firstBar); bar < regionEnd; bar += step) {\n const peak = aggregatePeaks(this._peaks, bits, bar, bar + step);\n if (!peak) continue;\n const rects = calculateBarRects(\n bar - globalOffset,\n this.barWidth,\n halfHeight,\n peak.min,\n peak.max,\n 'normal'\n );\n for (const r of rects) {\n ctx.fillRect(r.x, r.y, r.width, r.height);\n }\n }\n }\n\n private _drawSegments(\n canvas: HTMLCanvasElement,\n chunkIdx: number,\n dpr: number,\n halfHeight: number,\n bits: Bits,\n waveColor: string\n ) {\n if (!this.segments) return;\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const globalOffset = chunkIdx * MAX_CANVAS_WIDTH;\n const canvasWidth = Math.min(MAX_CANVAS_WIDTH, this.length - globalOffset);\n\n ctx.resetTransform();\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.scale(dpr, dpr);\n ctx.fillStyle = waveColor;\n\n const step = Math.max(1, Math.round(this.barWidth + this.barGap));\n\n for (const seg of this.segments) {\n // Skip segments outside this chunk\n if (seg.pixelEnd <= globalOffset || seg.pixelStart >= globalOffset + canvasWidth) continue;\n\n const localStart = Math.max(0, seg.pixelStart - globalOffset);\n const localEnd = Math.min(canvasWidth, seg.pixelEnd - globalOffset);\n const segPixelWidth = seg.pixelEnd - seg.pixelStart;\n const segPeakWidth = seg.peakEnd - seg.peakStart;\n if (segPixelWidth <= 0 || segPeakWidth <= 0) continue;\n\n // Per-segment peaks-per-pixel ratio\n const peaksPerPixel = segPeakWidth / segPixelWidth;\n\n for (let px = Math.floor(localStart); px < Math.ceil(localEnd); px += step) {\n // Map this pixel to a peak index range\n const pxInSeg = px + globalOffset - seg.pixelStart;\n const peakPos = seg.peakStart + pxInSeg * peaksPerPixel;\n const peakEnd = peakPos + step * peaksPerPixel;\n\n const peak = aggregatePeaks(this._peaks, bits, Math.floor(peakPos), Math.ceil(peakEnd));\n if (!peak) continue;\n\n const rects = calculateBarRects(\n px,\n this.barWidth,\n halfHeight,\n peak.min,\n peak.max,\n 'normal'\n );\n for (const r of rects) {\n ctx.fillRect(r.x, r.y, r.width, r.height);\n }\n }\n }\n }\n\n connectedCallback() {\n super.connectedCallback();\n // Reschedule draw if dirty pixels survived a disconnect/reconnect cycle\n if (this._dirtyPixels.size > 0) {\n this._scheduleDraw();\n }\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n if (this._drawScheduled) {\n cancelAnimationFrame(this._rafId);\n this._drawScheduled = false;\n }\n // Keep _dirtyPixels — connectedCallback will reschedule if reconnected\n }\n\n render() {\n const indices = this._getVisibleChunkIndices();\n const dpr = typeof devicePixelRatio !== 'undefined' ? devicePixelRatio : 1;\n\n return html`\n <div class=\"container\" style=\"width: ${this.length}px; height: ${this.waveHeight}px;\">\n ${indices.map((i) => {\n const width = Math.min(MAX_CANVAS_WIDTH, this.length - i * MAX_CANVAS_WIDTH);\n return html`\n <canvas\n data-index=${i}\n width=${width * dpr}\n height=${this.waveHeight * dpr}\n style=\"left: ${i * MAX_CANVAS_WIDTH}px; width: ${width}px; height: ${this\n .waveHeight}px;\"\n ></canvas>\n `;\n })}\n </div>\n `;\n }\n\n /** Mark peaks dirty only for chunks that weren't drawn in the previous frame. */\n private _markNewChunksDirty() {\n const currentIndices = this._getVisibleChunkIndices();\n const peakCount = Math.floor(this._peaks.length / 2);\n for (const chunkIdx of currentIndices) {\n if (!this._drawnChunks.has(chunkIdx)) {\n const start = chunkIdx * MAX_CANVAS_WIDTH;\n const end = Math.min(start + MAX_CANVAS_WIDTH, peakCount);\n for (let i = start; i < end; i++) {\n this._dirtyPixels.add(i);\n }\n }\n }\n if (this._dirtyPixels.size > 0) {\n this._scheduleDraw();\n }\n }\n\n updated(changedProperties: Map<string, unknown>) {\n // Layout/data changes require full redraw of all peaks\n const needsFullDirty = [...changedProperties.keys()].some((key) => LAYOUT_PROPS.has(key));\n if (needsFullDirty) {\n this._markAllDirty();\n return;\n }\n // Viewport-only changes: only draw newly visible chunks, skip already-drawn ones\n if (\n changedProperties.has('visibleStart') ||\n changedProperties.has('visibleEnd') ||\n changedProperties.has('originX')\n ) {\n this._markNewChunksDirty();\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-waveform': DawWaveformElement;\n }\n}\n","import type { Peaks, Bits } from '@waveform-playlist/core';\n\nexport type WaveformDrawMode = 'normal' | 'inverted';\n\n/**\n * Result of aggregating peaks over a range.\n *\n * Invariants (assumed from valid waveform input):\n * - min and max are normalized to [-1, 1] by dividing by 2^(bits-1)\n * - min <= max (min-of-mins, max-of-maxes — guaranteed by the interleaved min/max peak format)\n * - Values are finite (derived from integer typed arrays)\n *\n * Construct via aggregatePeaks() — do not create directly.\n */\nexport interface AggregatedPeak {\n min: number;\n max: number;\n}\n\n/**\n * Canvas fillRect parameters for a single waveform bar.\n * width >= 0 and height >= 0 when peak values are in [-1, 1] (guaranteed by\n * the interleaved min/max peak format normalization).\n */\nexport interface BarRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * Aggregates peaks over a range of interleaved min/max pairs.\n * Finds min-of-mins and max-of-maxes, normalized by bit depth.\n *\n * @param data - Interleaved peak data [min0, max0, min1, max1, ...]\n * @param bits - Bit depth (8 or 16)\n * @param startIndex - First peak index (not array index — peak i is at data[i*2], data[i*2+1])\n * @param endIndex - One past the last peak index to include\n * @returns Normalized { min, max } or null if startIndex is out of bounds\n */\nexport function aggregatePeaks(\n data: Peaks,\n bits: Bits,\n startIndex: number,\n endIndex: number\n): AggregatedPeak | null {\n if (startIndex * 2 + 1 >= data.length) {\n return null;\n }\n\n const maxValue = 2 ** (bits - 1);\n let minPeak = data[startIndex * 2] / maxValue;\n let maxPeak = data[startIndex * 2 + 1] / maxValue;\n\n for (let p = startIndex + 1; p < endIndex; p++) {\n if (p * 2 + 1 >= data.length) break;\n const pMin = data[p * 2] / maxValue;\n const pMax = data[p * 2 + 1] / maxValue;\n if (pMin < minPeak) minPeak = pMin;\n if (pMax > maxPeak) maxPeak = pMax;\n }\n\n return { min: minPeak, max: maxPeak };\n}\n\n/**\n * Computes canvas fillRect parameters for a single waveform bar.\n *\n * @param x - Bar x position in canvas coordinates\n * @param barWidth - Width of the bar in pixels\n * @param halfHeight - Half the waveform height (center line y)\n * @param minPeak - Normalized min peak value (negative for below center)\n * @param maxPeak - Normalized max peak value (positive for above center)\n * @param drawMode - 'normal' draws the peak region, 'inverted' draws the non-peak regions\n * @returns Array of BarRect — 1 rect for 'normal', 2 rects for 'inverted'\n */\nexport function calculateBarRects(\n x: number,\n barWidth: number,\n halfHeight: number,\n minPeak: number,\n maxPeak: number,\n drawMode: WaveformDrawMode\n): BarRect[] {\n const min = Math.abs(minPeak * halfHeight);\n const max = Math.abs(maxPeak * halfHeight);\n\n if (drawMode === 'normal') {\n return [{ x, y: halfHeight - max, width: barWidth, height: max + min }];\n }\n\n // Inverted: draw areas WITHOUT audio (top gap + bottom gap)\n return [\n { x, y: 0, width: barWidth, height: halfHeight - max },\n { x, y: halfHeight + min, width: barWidth, height: halfHeight - min },\n ];\n}\n\n/**\n * Computes the first bar position (in global pixel coordinates) that could\n * affect a given canvas chunk.\n *\n * A bar at position X extends from X to X+barWidth-1, so we need bars where\n * barStart + barWidth > canvasStartGlobal.\n *\n * @param canvasStartGlobal - Global pixel offset of the canvas chunk\n * @param barWidth - Width of each bar in pixels\n * @param step - Bar stride (barWidth + barGap)\n * @returns The first bar's global position (always >= 0 when step >= barWidth; caller clamps to 0)\n */\nexport function calculateFirstBarPosition(\n canvasStartGlobal: number,\n barWidth: number,\n step: number\n): number {\n return Math.floor((canvasStartGlobal - barWidth + step) / step) * step;\n}\n","/**\n * Compute which canvas chunk indices are visible within a viewport range.\n *\n * @param totalWidth - Total pixel width of the content\n * @param chunkWidth - Width of each canvas chunk (e.g. 1000px)\n * @param visibleStart - Viewport start in pixels (relative to timeline origin)\n * @param visibleEnd - Viewport end in pixels (relative to timeline origin)\n * @param originX - Content's left offset on the timeline\n */\nexport function getVisibleChunkIndices(\n totalWidth: number,\n chunkWidth: number,\n visibleStart: number,\n visibleEnd: number,\n originX = 0\n): number[] {\n const totalChunks = Math.ceil(totalWidth / chunkWidth);\n const indices: number[] = [];\n for (let i = 0; i < totalChunks; i++) {\n const chunkStart = originX + i * chunkWidth;\n const chunkEnd = chunkStart + chunkWidth;\n if (chunkEnd > visibleStart && chunkStart < visibleEnd) {\n indices.push(i);\n }\n }\n return indices;\n}\n","import { LitElement, html, css } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport type { PropertyValues } from 'lit';\nimport type { MidiNoteData } from '@waveform-playlist/core';\nimport { getVisibleChunkIndices } from '../utils/viewport';\n\nconst MAX_CANVAS_WIDTH = 1000;\n\n/** Layout/data properties that require a full redraw when changed. */\nconst LAYOUT_PROPS = new Set([\n 'length',\n 'waveHeight',\n 'samplesPerPixel',\n 'sampleRate',\n 'clipOffsetSeconds',\n 'midiNotes',\n 'selected',\n]);\n\n@customElement('daw-piano-roll')\nexport class DawPianoRollElement extends LitElement {\n @property({ attribute: false }) midiNotes: MidiNoteData[] = [];\n @property({ type: Number, attribute: false }) length = 0;\n @property({ type: Number, attribute: false }) waveHeight = 128;\n @property({ type: Number, attribute: 'samples-per-pixel', noAccessor: true })\n get samplesPerPixel(): number {\n return this._samplesPerPixel;\n }\n set samplesPerPixel(value: number) {\n if (!Number.isFinite(value) || value <= 0) {\n console.warn('[dawcore] daw-piano-roll samplesPerPixel ' + value + ' is invalid — ignored');\n return;\n }\n const old = this._samplesPerPixel;\n this._samplesPerPixel = value;\n this.requestUpdate('samplesPerPixel', old);\n }\n private _samplesPerPixel = 1024;\n\n @property({ type: Number, attribute: 'sample-rate', noAccessor: true })\n get sampleRate(): number {\n return this._sampleRate;\n }\n set sampleRate(value: number) {\n if (!Number.isFinite(value) || value <= 0) {\n console.warn('[dawcore] daw-piano-roll sampleRate ' + value + ' is invalid — ignored');\n return;\n }\n const old = this._sampleRate;\n this._sampleRate = value;\n this.requestUpdate('sampleRate', old);\n }\n private _sampleRate = 48000;\n @property({ type: Number, attribute: false }) clipOffsetSeconds = 0;\n /** Visible viewport start in pixels (relative to timeline origin). */\n @property({ type: Number, attribute: false }) visibleStart = -Infinity;\n /** Visible viewport end in pixels (relative to timeline origin). */\n @property({ type: Number, attribute: false }) visibleEnd = Infinity;\n /** This element's left offset on the timeline (for viewport intersection). */\n @property({ type: Number, attribute: false }) originX = 0;\n @property({ type: Boolean, reflect: true }) selected = false;\n\n static styles = css`\n :host {\n display: block;\n position: relative;\n }\n .container {\n position: relative;\n background: var(--daw-piano-roll-background, #1a1a2e);\n }\n canvas {\n position: absolute;\n top: 0;\n image-rendering: pixelated;\n image-rendering: crisp-edges;\n }\n `;\n\n private _rafHandle: number | null = null;\n\n private _scheduleDraw() {\n if (this._rafHandle !== null) return;\n this._rafHandle = requestAnimationFrame(() => {\n this._rafHandle = null;\n this._draw();\n });\n }\n\n willUpdate(_changed: PropertyValues) {\n this._scheduleDraw();\n }\n\n updated(changedProperties: Map<string, unknown>) {\n // Layout/data changes: willUpdate already scheduled a draw — nothing extra needed.\n const needsFullDraw = [...changedProperties.keys()].some((key) => LAYOUT_PROPS.has(key));\n if (needsFullDraw) return;\n // Viewport-only changes: new canvases may have mounted; schedule a draw so they get painted.\n if (\n changedProperties.has('visibleStart') ||\n changedProperties.has('visibleEnd') ||\n changedProperties.has('originX')\n ) {\n this._scheduleDraw();\n }\n }\n\n private _getPitchRange(): { minMidi: number; maxMidi: number } {\n if (this.midiNotes.length === 0) return { minMidi: 0, maxMidi: 127 };\n let min = 127;\n let max = 0;\n for (const note of this.midiNotes) {\n if (note.midi < min) min = note.midi;\n if (note.midi > max) max = note.midi;\n }\n return {\n minMidi: Math.max(0, min - 1),\n maxMidi: Math.min(127, max + 1),\n };\n }\n\n private _getNoteColor(): string {\n const cs = getComputedStyle(this);\n const note = cs.getPropertyValue('--daw-piano-roll-note-color').trim() || '#2a7070';\n const selectedColor =\n cs.getPropertyValue('--daw-piano-roll-selected-note-color').trim() || '#3d9e9e';\n return this.selected ? selectedColor : note;\n }\n\n private _draw() {\n if (!this.shadowRoot) return;\n const canvases = this.shadowRoot.querySelectorAll('canvas');\n if (canvases.length === 0) return;\n\n const { minMidi, maxMidi } = this._getPitchRange();\n const noteRange = maxMidi - minMidi + 1;\n const noteHeight = Math.max(2, this.waveHeight / noteRange);\n const pixelsPerSecond = this.sampleRate / this.samplesPerPixel;\n const dpr = typeof devicePixelRatio !== 'undefined' ? devicePixelRatio : 1;\n const color = this._getNoteColor();\n\n for (const canvas of canvases) {\n // Read data-index (matching daw-waveform pattern)\n const chunkIdx = Number((canvas as HTMLCanvasElement).dataset.index);\n const chunkPixelStart = chunkIdx * MAX_CANVAS_WIDTH;\n const canvasWidth = (canvas as HTMLCanvasElement).width / dpr;\n\n const ctx = (canvas as HTMLCanvasElement).getContext('2d');\n if (!ctx) continue;\n\n ctx.resetTransform();\n ctx.clearRect(\n 0,\n 0,\n (canvas as HTMLCanvasElement).width,\n (canvas as HTMLCanvasElement).height\n );\n ctx.imageSmoothingEnabled = false;\n ctx.scale(dpr, dpr);\n\n const chunkStartTime = (chunkPixelStart * this.samplesPerPixel) / this.sampleRate;\n const chunkEndTime =\n ((chunkPixelStart + canvasWidth) * this.samplesPerPixel) / this.sampleRate;\n\n for (const note of this.midiNotes) {\n const noteStart = note.time - this.clipOffsetSeconds;\n const noteEnd = noteStart + note.duration;\n if (noteEnd <= chunkStartTime || noteStart >= chunkEndTime) continue;\n\n const x = noteStart * pixelsPerSecond - chunkPixelStart;\n const w = Math.max(2, note.duration * pixelsPerSecond);\n const y = ((maxMidi - note.midi) / noteRange) * this.waveHeight;\n\n const alpha = 0.3 + note.velocity * 0.7;\n ctx.fillStyle = color;\n ctx.globalAlpha = alpha;\n\n ctx.beginPath();\n ctx.roundRect(x, y, w, noteHeight, 1);\n ctx.fill();\n }\n ctx.globalAlpha = 1;\n }\n }\n\n connectedCallback() {\n super.connectedCallback();\n // Reschedule draw if a RAF was pending during disconnect/reconnect cycle\n this._scheduleDraw();\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n if (this._rafHandle !== null) {\n cancelAnimationFrame(this._rafHandle);\n this._rafHandle = null;\n }\n }\n\n render() {\n if (this.length <= 0)\n return html`<div class=\"container\" style=\"width: 0; height: ${this.waveHeight}px;\"></div>`;\n const dpr = typeof devicePixelRatio !== 'undefined' ? devicePixelRatio : 1;\n const visibleIndices = getVisibleChunkIndices(\n this.length,\n MAX_CANVAS_WIDTH,\n this.visibleStart,\n this.visibleEnd,\n this.originX\n );\n // Wrap canvases in a .container div with explicit width/height,\n // matching daw-waveform's container pattern. Background is on .container (not :host)\n // so it has the correct measurable size.\n return html`\n <div class=\"container\" style=\"width: ${this.length}px; height: ${this.waveHeight}px;\">\n ${visibleIndices.map((i) => {\n const chunkLeft = i * MAX_CANVAS_WIDTH;\n const chunkWidth = Math.min(this.length - chunkLeft, MAX_CANVAS_WIDTH);\n // Use data-index (not data-chunk-idx) to match daw-waveform pattern\n return html`<canvas\n data-index=${i}\n width=${chunkWidth * dpr}\n height=${this.waveHeight * dpr}\n style=\"left: ${chunkLeft}px; width: ${chunkWidth}px; height: ${this.waveHeight}px;\"\n ></canvas>`;\n })}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-piano-roll': DawPianoRollElement;\n }\n}\n","import { LitElement, html, css } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport { AnimationController } from '../controllers/animation-controller';\n\n@customElement('daw-playhead')\nexport class DawPlayheadElement extends LitElement {\n private _animation = new AnimationController(this);\n private _line: HTMLElement | null = null;\n\n static styles = css`\n :host {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n pointer-events: none;\n z-index: 10;\n }\n div {\n position: absolute;\n top: 0;\n bottom: 0;\n width: 1px;\n background: var(--daw-playhead-color, #d08070);\n will-change: transform;\n }\n `;\n\n render() {\n return html`<div></div>`;\n }\n\n firstUpdated() {\n this._line = this.shadowRoot!.querySelector('div');\n }\n\n startAnimation(getTime: () => number, sampleRate: number, samplesPerPixel: number) {\n this._animation.start(() => {\n const time = getTime();\n const px = (time * sampleRate) / samplesPerPixel;\n if (this._line) {\n this._line.style.transform = `translate3d(${px}px, 0, 0)`;\n }\n });\n }\n\n stopAnimation(time: number, sampleRate: number, samplesPerPixel: number) {\n this._animation.stop();\n const px = (time * sampleRate) / samplesPerPixel;\n if (this._line) {\n this._line.style.transform = `translate3d(${px}px, 0, 0)`;\n }\n }\n\n startBeatsAnimation(getTime: () => number, bpm: number, ppqn: number, ticksPerPixel: number) {\n const ticksPerSecond = (bpm * ppqn) / 60;\n this._animation.start(() => {\n const time = getTime();\n const px = (time * ticksPerSecond) / ticksPerPixel;\n if (this._line) {\n this._line.style.transform = `translate3d(${px}px, 0, 0)`;\n }\n });\n }\n\n stopBeatsAnimation(time: number, bpm: number, ppqn: number, ticksPerPixel: number) {\n this._animation.stop();\n const px = (time * bpm * ppqn) / (60 * ticksPerPixel);\n if (this._line) {\n this._line.style.transform = `translate3d(${px}px, 0, 0)`;\n }\n }\n\n startBeatsAnimationWithMap(\n getTime: () => number,\n secondsToTicks: (s: number) => number,\n ticksPerPixel: number\n ) {\n this._animation.start(() => {\n const time = getTime();\n const tick = secondsToTicks(time);\n const px = tick / ticksPerPixel;\n if (this._line) {\n this._line.style.transform = `translate3d(${px}px, 0, 0)`;\n }\n });\n }\n\n stopBeatsAnimationWithMap(\n time: number,\n secondsToTicks: (s: number) => number,\n ticksPerPixel: number\n ) {\n this._animation.stop();\n const px = secondsToTicks(time) / ticksPerPixel;\n if (this._line) {\n this._line.style.transform = `translate3d(${px}px, 0, 0)`;\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-playhead': DawPlayheadElement;\n }\n}\n","import type { ReactiveController, ReactiveControllerHost } from 'lit';\n\nexport class AnimationController implements ReactiveController {\n private _rafId: number | null = null;\n private _callback: (() => void) | null = null;\n\n constructor(host: ReactiveControllerHost) {\n host.addController(this);\n }\n\n start(callback: () => void) {\n this.stop();\n this._callback = callback;\n const loop = () => {\n this._callback?.();\n this._rafId = requestAnimationFrame(loop);\n };\n this._rafId = requestAnimationFrame(loop);\n }\n\n stop() {\n if (this._rafId !== null) {\n cancelAnimationFrame(this._rafId);\n this._rafId = null;\n }\n this._callback = null;\n }\n\n hostConnected() {}\n\n hostDisconnected() {\n this.stop();\n }\n}\n","import { LitElement } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\n\n@customElement('daw-transport')\nexport class DawTransportElement extends LitElement {\n @property() for = '';\n\n get target(): HTMLElement | null {\n return this.for ? document.getElementById(this.for) : null;\n }\n\n // Light DOM — button children stay in consumer's DOM.\n // No render() needed; light DOM elements don't use <slot>.\n createRenderRoot() {\n return this;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-transport': DawTransportElement;\n }\n}\n","import { html } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\nimport { DawTransportButton } from './daw-transport-button';\n\n@customElement('daw-play-button')\nexport class DawPlayButtonElement extends DawTransportButton {\n @state() private _isRecording = false;\n private _targetRef: HTMLElement | null = null;\n private _onRecStart = () => {\n this._isRecording = true;\n };\n private _onRecEnd = () => {\n this._isRecording = false;\n };\n\n connectedCallback() {\n super.connectedCallback();\n // Defer so <daw-transport for=\"...\"> and the target editor are resolved\n requestAnimationFrame(() => {\n const target = this.target;\n if (!target) return;\n this._targetRef = target;\n target.addEventListener('daw-recording-start', this._onRecStart);\n target.addEventListener('daw-recording-complete', this._onRecEnd);\n target.addEventListener('daw-recording-error', this._onRecEnd);\n });\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n if (this._targetRef) {\n this._targetRef.removeEventListener('daw-recording-start', this._onRecStart);\n this._targetRef.removeEventListener('daw-recording-complete', this._onRecEnd);\n this._targetRef.removeEventListener('daw-recording-error', this._onRecEnd);\n this._targetRef = null;\n }\n }\n\n render() {\n return html`\n <button part=\"button\" ?disabled=${this._isRecording} @click=${this._onClick}>\n <slot>Play</slot>\n </button>\n `;\n }\n\n private _onClick() {\n const target = this.target;\n if (!target) {\n console.warn(\n '[dawcore] <daw-play-button> has no target. Check <daw-transport for=\"...\"> references a valid <daw-editor> id.'\n );\n return;\n }\n target.play();\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-play-button': DawPlayButtonElement;\n }\n}\n","import { LitElement, css } from 'lit';\nimport type { DawTransportElement } from './daw-transport';\n\n/**\n * Base class for transport button elements.\n * Finds target daw-editor via closest <daw-transport>.\n */\nexport class DawTransportButton extends LitElement {\n protected get target(): any {\n const transport = this.closest('daw-transport') as DawTransportElement | null;\n return transport?.target ?? null;\n }\n\n static styles: import('lit').CSSResultGroup = css`\n button {\n cursor: pointer;\n background: var(--daw-controls-background, #1a1a2e);\n color: var(--daw-controls-text, #e0d4c8);\n border: 1px solid currentColor;\n padding: 4px 8px;\n font: inherit;\n }\n button:hover {\n opacity: 0.8;\n }\n button:disabled {\n opacity: 0.4;\n cursor: default;\n }\n `;\n}\n","import { html, css } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\nimport { DawTransportButton } from './daw-transport-button';\n\n@customElement('daw-pause-button')\nexport class DawPauseButtonElement extends DawTransportButton {\n @state() private _isPaused = false;\n @state() private _isRecording = false;\n private _targetRef: HTMLElement | null = null;\n private _onRecStart = () => {\n this._isRecording = true;\n };\n private _onRecEnd = () => {\n this._isRecording = false;\n this._isPaused = false;\n };\n private _onRecPause = () => {\n this._isPaused = true;\n };\n private _onRecResume = () => {\n this._isPaused = false;\n };\n\n static override styles = [\n DawTransportButton.styles,\n css`\n button[data-paused] {\n background: rgba(255, 255, 255, 0.1);\n border-color: var(--daw-controls-text, #e0d4c8);\n }\n `,\n ];\n\n connectedCallback() {\n super.connectedCallback();\n // Defer so <daw-transport for=\"...\"> and the target editor are resolved\n requestAnimationFrame(() => {\n const target = this.target;\n if (!target) return;\n this._targetRef = target;\n target.addEventListener('daw-recording-start', this._onRecStart);\n target.addEventListener('daw-recording-complete', this._onRecEnd);\n target.addEventListener('daw-recording-error', this._onRecEnd);\n target.addEventListener('daw-recording-pause', this._onRecPause);\n target.addEventListener('daw-recording-resume', this._onRecResume);\n });\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n if (this._targetRef) {\n this._targetRef.removeEventListener('daw-recording-start', this._onRecStart);\n this._targetRef.removeEventListener('daw-recording-complete', this._onRecEnd);\n this._targetRef.removeEventListener('daw-recording-error', this._onRecEnd);\n this._targetRef.removeEventListener('daw-recording-pause', this._onRecPause);\n this._targetRef.removeEventListener('daw-recording-resume', this._onRecResume);\n this._targetRef = null;\n }\n }\n\n render() {\n return html`\n <button part=\"button\" ?data-paused=${this._isPaused} @click=${this._onClick}>\n <slot>Pause</slot>\n </button>\n `;\n }\n\n private _onClick() {\n const target = this.target;\n if (!target) {\n console.warn(\n '[dawcore] <daw-pause-button> has no target. Check <daw-transport for=\"...\"> references a valid <daw-editor> id.'\n );\n return;\n }\n\n if (this._isRecording) {\n // Delegate to editor — events keep visual state in sync via\n // daw-recording-pause / daw-recording-resume.\n target.togglePauseRecording();\n } else {\n target.pause();\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-pause-button': DawPauseButtonElement;\n }\n}\n","import { html } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport { DawTransportButton } from './daw-transport-button';\n\n@customElement('daw-stop-button')\nexport class DawStopButtonElement extends DawTransportButton {\n render() {\n return html`\n <button part=\"button\" @click=${this._onClick}>\n <slot>Stop</slot>\n </button>\n `;\n }\n\n private _onClick() {\n const target = this.target;\n if (!target) {\n console.warn(\n '[dawcore] <daw-stop-button> has no target. Check <daw-transport for=\"...\"> references a valid <daw-editor> id.'\n );\n return;\n }\n // When recording: await stopRecording (which awaits the worklet's\n // done ack) BEFORE calling editor.stop(). Calling stop() in parallel\n // disrupts the audio thread mid-handshake — engine.stop() can pause\n // worklet rendering, which prevents the done message from arriving.\n // ALWAYS run stop() afterward (even if stopRecording rejected), so the\n // engine cleans up; wrap stop() in try/catch since it's synchronous\n // void and can throw.\n if (target.isRecording) {\n target\n .stopRecording()\n .catch((err: unknown) => {\n console.warn('[dawcore] stopRecording failed: ' + String(err));\n })\n .then(() => {\n try {\n target.stop();\n } catch (err) {\n console.warn('[dawcore] stop after stopRecording failed: ' + String(err));\n }\n });\n } else {\n try {\n target.stop();\n } catch (err) {\n console.warn('[dawcore] stop failed: ' + String(err));\n }\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-stop-button': DawStopButtonElement;\n }\n}\n","import { LitElement, html, css } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport type {\n AudioClip,\n ClipTrack,\n FadeType,\n MidiNoteData,\n Peaks,\n PeakData,\n SnapTo,\n MeterEntry,\n SpectrogramConfig,\n ColorMapValue,\n} from '@waveform-playlist/core';\nimport type {\n TrackDescriptor,\n ClipDescriptor,\n DomClipDescriptor,\n TrackConfig,\n ClipConfig,\n} from '../types';\nimport { isDomClip } from '../types';\nimport {\n createClip,\n createClipFromSeconds,\n createTrack,\n clipPixelWidth,\n} from '@waveform-playlist/core';\nimport { PeakPipeline } from '../workers/peakPipeline';\nimport type { DawTrackElement } from './daw-track';\nimport type { DawClipElement } from './daw-clip';\nimport type { DawPlayheadElement } from './daw-playhead';\nimport type { WaveformSegment } from './daw-waveform';\n// Register the visual child elements this editor's template always renders.\n// The shadow template emits <daw-playhead>/<daw-waveform>/<daw-ruler> directly,\n// so the editor must guarantee they're defined. Without these side-effect\n// imports, importing daw-editor alone (not the package barrel) leaves the\n// elements un-upgraded — querySelector returns generic HTMLElements, and calls\n// like playhead.stopAnimation() throw \"is not a function\". (Module caching +\n// @customElement make this safe to import here and from the index barrel.)\nimport './daw-playhead';\nimport './daw-waveform';\nimport './daw-ruler';\nimport type { PlaylistEngine, PlayoutAdapter } from '@waveform-playlist/engine';\nimport '../elements/daw-track-controls';\nimport '../elements/daw-grid';\nimport { hostStyles, clipStyles } from '../styles/theme';\nimport { ViewportController } from '../controllers/viewport-controller';\nimport { AudioResumeController } from '../controllers/audio-resume-controller';\nimport { RecordingController } from '../controllers/recording-controller';\nimport type { RecordingOptions } from '../controllers/recording-controller';\nimport { SpectrogramController } from '../controllers/spectrogram-controller';\nimport { PointerHandler } from '../interactions/pointer-handler';\nimport { ClipPointerHandler } from '../interactions/clip-pointer-handler';\nimport type {\n DawSelectionDetail,\n DawTrackIdDetail,\n DawTrackErrorDetail,\n DawClipIdDetail,\n DawClipErrorDetail,\n DawErrorDetail,\n LoadFilesResult,\n} from '../events';\nimport { loadFiles as loadFilesImpl } from '../interactions/file-loader';\nimport {\n loadMidiImpl,\n type MidiLoaderHost,\n type MidiLoadOptions,\n type MidiLoadResult,\n} from '../interactions/midi-loader';\nimport { addRecordedClip } from '../interactions/recording-clip';\nimport { splitAtPlayhead as performSplitAtPlayhead } from '../interactions/split-handler';\nimport { syncPeaksForChangedClips } from '../interactions/clip-peak-sync';\nimport { loadWaveformDataFromUrl } from '../interactions/peaks-loader';\nimport { extractPeaks } from '../workers/waveformDataUtils';\nimport { ScrollSyncController } from '../controllers/scroll-sync-controller';\n\n/** Height of the ruler band — single source for the ruler element and the header row. */\nconst RULER_HEIGHT = 30;\n\nconst NO_ADAPTER_ERROR =\n 'No PlayoutAdapter set on <daw-editor>. ' +\n 'Set editor.adapter before use.\\n\\n' +\n ' // Option 1: Native Web Audio (no Tone.js)\\n' +\n ' npm install @dawcore/transport\\n' +\n \" import { NativePlayoutAdapter } from '@dawcore/transport';\\n\" +\n ' editor.adapter = new NativePlayoutAdapter(new AudioContext());\\n\\n' +\n ' // Option 2: Tone.js (effects, MIDI synths)\\n' +\n ' npm install @waveform-playlist/playout\\n' +\n \" import { createToneAdapter } from '@waveform-playlist/playout';\\n\" +\n ' editor.adapter = createToneAdapter();';\n\n@customElement('daw-editor')\nexport class DawEditorElement extends LitElement implements MidiLoaderHost {\n @property({ type: Number, attribute: 'samples-per-pixel', noAccessor: true })\n get samplesPerPixel(): number {\n return this._samplesPerPixel;\n }\n set samplesPerPixel(value: number) {\n const old = this._samplesPerPixel;\n if (!Number.isFinite(value) || value <= 0) return;\n const clamped =\n this._minSamplesPerPixel > 0 && value < this._minSamplesPerPixel\n ? this._minSamplesPerPixel\n : value;\n if (clamped !== value) {\n console.warn(\n '[dawcore] Zoom ' +\n value +\n ' spp rejected — pre-computed peaks limit is ' +\n this._minSamplesPerPixel +\n ' spp'\n );\n }\n this._samplesPerPixel = clamped;\n this.requestUpdate('samplesPerPixel', old);\n }\n private _samplesPerPixel = 1024;\n @property({ type: Number, attribute: 'wave-height' }) waveHeight = 128;\n @property({ type: Boolean }) timescale = false;\n @property({ type: Boolean }) mono = false;\n @property({ type: Number, attribute: 'bar-width' }) barWidth = 1;\n @property({ type: Number, attribute: 'bar-gap' }) barGap = 0;\n @property({ type: Boolean, attribute: 'file-drop' }) fileDrop = false;\n @property({ type: Boolean, attribute: 'clip-headers' }) clipHeaders = false;\n @property({ type: Number, attribute: 'clip-header-height' }) clipHeaderHeight = 20;\n @property({ type: Boolean, attribute: 'interactive-clips' }) interactiveClips = false;\n /**\n * When true, the timeline fills the visible viewport even if total clip\n * duration is less. Lets the ruler render before any audio is loaded —\n * useful for empty editors and recording UIs. In beats mode the 32-bar\n * floor already provides this; this attribute controls the temporal mode.\n */\n @property({ type: Boolean, attribute: 'indefinite-playback' }) indefinitePlayback = false;\n /**\n * Default spectrogram FFT/render config inherited by tracks with\n * `render-mode=\"spectrogram\"` that do not set their own. Wired into the\n * orchestrator by `SpectrogramController` in Task 14.\n */\n @property({ attribute: false, noAccessor: true })\n get spectrogramConfig(): SpectrogramConfig | null {\n return this._spectrogramConfig;\n }\n set spectrogramConfig(value: SpectrogramConfig | null) {\n const old = this._spectrogramConfig;\n this._spectrogramConfig = value;\n this._spectrogramController?.setEditorConfig(value);\n this.requestUpdate('spectrogramConfig', old);\n }\n private _spectrogramConfig: SpectrogramConfig | null = null;\n\n /**\n * Default color map for spectrogram tracks. Tracks may override via\n * their own per-track property in a future iteration. Separate from\n * `spectrogramConfig` because `ColorMapValue` is not part of the\n * `SpectrogramConfig` shape.\n */\n @property({ attribute: false, noAccessor: true })\n get spectrogramColorMap(): ColorMapValue | null {\n return this._spectrogramColorMap;\n }\n set spectrogramColorMap(value: ColorMapValue | null) {\n const old = this._spectrogramColorMap;\n this._spectrogramColorMap = value;\n this._spectrogramController?.setEditorColorMap(value);\n this.requestUpdate('spectrogramColorMap', old);\n }\n private _spectrogramColorMap: ColorMapValue | null = null;\n\n private _ensureSpectrogramController(): SpectrogramController {\n if (!this._spectrogramController) {\n this._spectrogramController = new SpectrogramController(\n this,\n () =>\n new Worker(new URL('@dawcore/spectrogram/worker/spectrogram.worker', import.meta.url), {\n type: 'module',\n })\n );\n if (this._spectrogramConfig) {\n this._spectrogramController.setEditorConfig(this._spectrogramConfig);\n }\n if (this._spectrogramColorMap) {\n this._spectrogramController.setEditorColorMap(this._spectrogramColorMap);\n }\n }\n return this._spectrogramController;\n }\n\n /** Called by <daw-spectrogram> after transferControlToOffscreen. */\n _spectrogramRegisterCanvas(reg: {\n canvasId: string;\n canvas: OffscreenCanvas;\n clipId: string;\n trackId: string;\n channelIndex: number;\n chunkIndex: number;\n globalPixelOffset: number;\n widthPx: number;\n heightPx: number;\n }): void {\n this._ensureSpectrogramController().registerCanvas(reg);\n }\n\n /** Called by <daw-spectrogram> on chunk unmount / element disconnect. */\n _spectrogramUnregisterCanvas(canvasId: string): void {\n this._spectrogramController?.unregisterCanvas(canvasId);\n }\n\n /**\n * Forward a clip's AudioBuffer to the spectrogram controller if the parent\n * track is in spectrogram render-mode. Eagerly creates the controller via\n * `_ensureSpectrogramController` so the audio data is queued for the first\n * render — even if no canvases have been registered yet.\n */\n private _maybeRegisterSpectrogramClipAudio(trackId: string, clip: AudioClip): void {\n const descriptor = this._tracks.get(trackId);\n if (descriptor?.renderMode !== 'spectrogram') return;\n // Read the buffer off the clip itself — `_clipBuffers` is mutated in-place\n // by `cleanupOrphanedClipData` during concurrent track loading and may\n // briefly miss this clip even though the clip object still holds the\n // buffer reference.\n const buffer = clip.audioBuffer ?? this._clipBuffers.get(clip.id);\n if (!buffer) return;\n const channelData: Float32Array[] = [];\n for (let i = 0; i < buffer.numberOfChannels; i++) {\n channelData.push(buffer.getChannelData(i));\n }\n this._ensureSpectrogramController().registerClipAudio({\n clipId: clip.id,\n trackId,\n channelData,\n sampleRate: buffer.sampleRate,\n durationSamples: clip.durationSamples,\n offsetSamples: clip.offsetSamples,\n });\n }\n @property({ type: String, attribute: 'scale-mode' })\n scaleMode: 'temporal' | 'beats' = 'temporal';\n @property({ type: Number, attribute: 'ticks-per-pixel', noAccessor: true })\n get ticksPerPixel(): number {\n return this._ticksPerPixel;\n }\n set ticksPerPixel(value: number) {\n const old = this._ticksPerPixel;\n if (!Number.isFinite(value) || value <= 0) return;\n this._ticksPerPixel = value;\n this.requestUpdate('ticksPerPixel', old);\n }\n private _ticksPerPixel = 24;\n @property({ type: Number, noAccessor: true })\n get bpm(): number {\n return this._bpm;\n }\n set bpm(value: number) {\n const old = this._bpm;\n if (!Number.isFinite(value) || value <= 0) return;\n this._bpm = value;\n // Forward to engine (which forwards to adapter's Transport)\n if (this._engine) {\n this._engine.setTempo(value);\n }\n this.requestUpdate('bpm', old);\n }\n private _bpm = 120;\n @property({ attribute: false })\n timeSignature: [number, number] = [4, 4];\n @property({ attribute: false })\n meterEntries?: MeterEntry[];\n /** MeterEntries for grid/ruler: explicit meterEntries if set, otherwise derived from timeSignature. */\n get _meterEntries(): MeterEntry[] {\n if (this.meterEntries && this.meterEntries.length > 0) return this.meterEntries;\n return [{ tick: 0, numerator: this.timeSignature[0], denominator: this.timeSignature[1] }];\n }\n @property({ type: Number, noAccessor: true })\n get ppqn(): number {\n return this._ppqn;\n }\n set ppqn(value: number) {\n const old = this._ppqn;\n if (!Number.isFinite(value) || value <= 0) return;\n this._ppqn = value;\n this.requestUpdate('ppqn', old);\n }\n private _ppqn = 960;\n @property({ type: String, attribute: 'snap-to' })\n snapTo: SnapTo = 'off';\n /** Optional tempo-aware conversion: seconds → PPQN ticks. When provided, enables variable tempo. */\n @property({ attribute: false })\n secondsToTicks?: (seconds: number) => number;\n /** Optional tempo-aware conversion: PPQN ticks → seconds. Required alongside secondsToTicks. */\n @property({ attribute: false })\n ticksToSeconds?: (ticks: number) => number;\n /** Sample rate — reads from adapter's AudioContext when available, otherwise falls back to 48000. */\n get sampleRate(): number {\n return this._resolvedSampleRate ?? this._externalAdapter?.audioContext.sampleRate ?? 48000;\n }\n /** Resolved sample rate — falls back to sampleRate property until first audio decode. */\n _resolvedSampleRate: number | null = null;\n @state() _tracks: Map<string, TrackDescriptor> = new Map();\n @state() _engineTracks: Map<string, ClipTrack> = new Map();\n @state() _peaksData: Map<string, PeakData> = new Map();\n @state() _isPlaying = false;\n @state() private _duration = 0;\n @state() _selectedTrackId: string | null = null;\n @state() _dragOver = false;\n // Not @state — updated directly to avoid 60fps Lit re-renders\n _selectionStartTime = 0;\n _selectionEndTime = 0;\n _currentTime = 0;\n @property({ attribute: false })\n set adapter(value: PlayoutAdapter | null) {\n if (value && value.audioContext.state === 'closed') {\n console.warn('[dawcore] Adapter AudioContext is already closed. Ignoring.');\n return;\n }\n if (this._engine) {\n console.warn(\n '[dawcore] adapter set after engine is built. ' +\n 'The engine will continue using the previous adapter.'\n );\n }\n this._externalAdapter = value;\n }\n get adapter(): PlayoutAdapter | null {\n return this._externalAdapter;\n }\n private _externalAdapter: PlayoutAdapter | null = null;\n\n get audioContext(): AudioContext {\n if (!this._externalAdapter) {\n throw new Error(NO_ADAPTER_ERROR);\n }\n return this._externalAdapter.audioContext;\n }\n _engine: PlaylistEngine | null = null;\n private _warnedMissingTicksToSeconds = false;\n private _warnedMissingSecondsToTicks = false;\n private _enginePromise: Promise<PlaylistEngine> | null = null;\n _audioCache = new Map<string, Promise<AudioBuffer>>();\n private _peaksCache = new Map<string, Promise<import('waveform-data').default>>();\n _clipBuffers = new Map<string, AudioBuffer>();\n _clipOffsets = new Map<string, { offsetSamples: number; durationSamples: number }>();\n _peakPipeline = new PeakPipeline();\n /** Coarsest scale from pre-computed peaks — zoom cannot go finer than this. 0 = no limit. */\n private _minSamplesPerPixel = 0;\n private _trackElements = new Map<string, DawTrackElement>();\n private _childObserver: MutationObserver | null = null;\n private _audioResume = new AudioResumeController(this);\n @property({ attribute: 'eager-resume' })\n eagerResume?: string;\n private _recordingController = new RecordingController(this);\n private _spectrogramController: SpectrogramController | null = null;\n private _clipPointer = new ClipPointerHandler(this);\n get _clipHandler() {\n return this.interactiveClips ? this._clipPointer : null;\n }\n get engine() {\n return this._engine;\n }\n get renderSamplesPerPixel() {\n return this._renderSpp;\n }\n /** Re-extract peaks for a clip at new offset/duration from cached WaveformData. */\n reextractClipPeaks(\n clipId: string,\n offsetSamples: number,\n durationSamples: number\n ): PeakData | null {\n const buf = this._clipBuffers.get(clipId);\n if (!buf) return null;\n const singleClipBuffers = new Map([[clipId, buf]]);\n const singleClipOffsets = new Map([[clipId, { offsetSamples, durationSamples }]]);\n const result = this._peakPipeline.reextractPeaks(\n singleClipBuffers,\n this._renderSpp,\n this.mono,\n singleClipOffsets\n );\n return result.get(clipId) ?? null;\n }\n\n /**\n * Returns true if the clip is a MIDI clip (has midiNotes).\n * Used by ClipPointerHandler to make trim handles inert for MIDI clips.\n * Returns false for unknown track/clip IDs (defensive).\n */\n isMidiClip(trackId: string, clipId: string): boolean {\n const track = this._engineTracks.get(trackId);\n if (!track) return false;\n const clip = track.clips.find((c) => c.id === clipId);\n return clip?.midiNotes != null;\n }\n\n private _pointer = new PointerHandler(this);\n private _viewport = (() => {\n const v = new ViewportController(this);\n v.scrollSelector = '.scroll-area';\n return v;\n })();\n private _scrollSync = (() => {\n const s = new ScrollSyncController(this);\n s.scrollSelector = '.scroll-area';\n // xTargetSelector, yTargetSelector, and wheelForwardSelector are kept\n // truthful by updated() — they start empty and are set before any scroll\n // is possible (updated() runs before paint on every reactive update).\n return s;\n })();\n\n static styles = [\n hostStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n position: relative;\n background: var(--daw-background, #1a1a2e);\n overflow: hidden;\n }\n .header-row {\n display: flex;\n flex-shrink: 0;\n }\n .ruler-gap {\n flex-shrink: 0;\n width: var(--daw-controls-width, 180px);\n }\n .ruler-viewport {\n flex: 1;\n position: relative;\n overflow: hidden;\n cursor: text;\n }\n .ruler-content {\n will-change: transform;\n }\n .body {\n flex: 1;\n min-height: 0;\n display: flex;\n }\n .controls-viewport {\n flex-shrink: 0;\n width: var(--daw-controls-width, 180px);\n overflow: hidden;\n }\n .controls-column {\n will-change: transform;\n }\n .scroll-area {\n flex: 1;\n overflow: auto;\n overflow-anchor: none;\n min-height: var(--daw-min-height, 200px);\n }\n .timeline {\n position: relative;\n min-height: 100%;\n cursor: text;\n }\n .track-row {\n position: relative;\n box-sizing: border-box;\n background: var(--daw-track-background, #16213e);\n border-bottom: 1px solid rgba(255, 255, 255, 0.05);\n }\n .track-row.selected {\n background: rgba(99, 199, 95, 0.08);\n }\n :host([scale-mode='beats']) .track-row {\n background: transparent;\n }\n :host([scale-mode='beats']) .clip-container {\n background: var(--daw-track-background, #16213e);\n }\n :host([scale-mode='beats']) .track-row.selected .clip-container {\n box-shadow: inset 0 0 0 1000px rgba(99, 199, 95, 0.06);\n }\n .timeline.drag-over {\n outline: 2px dashed var(--daw-selection-color, rgba(99, 199, 95, 0.3));\n outline-offset: -2px;\n }\n `,\n clipStyles,\n ];\n\n get effectiveSampleRate(): number {\n return this._resolvedSampleRate ?? this.sampleRate;\n }\n resolveAudioContextSampleRate(rate: number) {\n if (!this._resolvedSampleRate) this._resolvedSampleRate = rate;\n }\n /**\n * In beats mode, derive samplesPerPixel from ticksPerPixel so that\n * clip positions, waveforms, and the tick-space grid all align.\n */\n private get _renderSpp(): number {\n if (this.scaleMode === 'beats') {\n // Round to integer — WaveformData.resample() uses integer scale math.\n const spp = Math.ceil(\n (60 * this.effectiveSampleRate * this.ticksPerPixel) / (this.ppqn * this.bpm)\n );\n // Floor at the peak pipeline's base scale so peaks can always be extracted.\n // Without this, fine zoom levels request a scale finer than what WaveformData\n // can resample to, causing blank waveforms.\n return this._minSamplesPerPixel > 0 ? Math.max(spp, this._minSamplesPerPixel) : spp;\n }\n return this.samplesPerPixel;\n }\n /** Convert seconds to ticks — uses callback if provided, otherwise single-BPM fallback. */\n _secondsToTicks(seconds: number): number {\n if (this.secondsToTicks) {\n if (!this.ticksToSeconds && !this._warnedMissingTicksToSeconds) {\n this._warnedMissingTicksToSeconds = true;\n console.warn(\n '[waveform-playlist] daw-editor: secondsToTicks is set but ticksToSeconds is missing. Both callbacks are required for variable tempo.'\n );\n }\n return this.secondsToTicks(seconds);\n }\n return (seconds * this.bpm * this.ppqn) / 60;\n }\n /** Convert ticks to seconds — uses callback if provided, otherwise single-BPM fallback. */\n _ticksToSeconds(ticks: number): number {\n if (this.ticksToSeconds) {\n if (!this.secondsToTicks && !this._warnedMissingSecondsToTicks) {\n this._warnedMissingSecondsToTicks = true;\n console.warn(\n '[waveform-playlist] daw-editor: ticksToSeconds is set but secondsToTicks is missing. Both callbacks are required for variable tempo.'\n );\n }\n return this.ticksToSeconds(ticks);\n }\n return (ticks * 60) / (this.bpm * this.ppqn);\n }\n private get _totalWidth(): number {\n if (this.scaleMode === 'beats') {\n const contentTicks = this._secondsToTicks(this._duration);\n // Floor at 32 bars so the grid is always visible — DAW convention.\n const [num] = this.timeSignature;\n const minTicks = 32 * num * this.ppqn;\n return Math.ceil(Math.max(contentTicks, minTicks) / this.ticksPerPixel);\n }\n const naturalWidth = Math.ceil(\n (this._duration * this.effectiveSampleRate) / this.samplesPerPixel\n );\n if (this.indefinitePlayback) {\n // Fill the visible viewport when natural duration is shorter — lets the\n // ruler render before any audio is loaded. ViewportController exposes\n // the scroll-area's clientWidth (updated on attach + ResizeObserver).\n return Math.max(naturalWidth, this._viewport.containerWidth);\n }\n return naturalWidth;\n }\n /** Grid height when no tracks exist — matches scroll area's rendered height. */\n private get _emptyGridHeight(): number {\n const scrollArea = this.shadowRoot?.querySelector('.scroll-area') as HTMLElement | null;\n return scrollArea?.clientHeight ?? 200;\n }\n _setSelectedTrackId(trackId: string | null) {\n this._selectedTrackId = trackId;\n }\n get tracks(): TrackDescriptor[] {\n return [...this._tracks.values()];\n }\n get selectedTrackId(): string | null {\n return this._selectedTrackId;\n }\n get selection(): { start: number; end: number } | null {\n if (this._selectionStartTime === 0 && this._selectionEndTime === 0) return null;\n return { start: this._selectionStartTime, end: this._selectionEndTime };\n }\n setSelection(start: number, end: number) {\n this._selectionStartTime = Math.min(start, end);\n this._selectionEndTime = Math.max(start, end);\n if (this._engine) {\n this._engine.setSelection(this._selectionStartTime, this._selectionEndTime);\n }\n this.requestUpdate();\n this.dispatchEvent(\n new CustomEvent<DawSelectionDetail>('daw-selection', {\n bubbles: true,\n composed: true,\n detail: { start: this._selectionStartTime, end: this._selectionEndTime },\n })\n );\n }\n // --- Lifecycle ---\n connectedCallback() {\n super.connectedCallback();\n this.addEventListener('daw-track-connected', this._onTrackConnected as EventListener);\n this.addEventListener('daw-track-update', this._onTrackUpdate as EventListener);\n this.addEventListener('daw-track-control', this._onTrackControl as EventListener);\n this.addEventListener('daw-track-remove', this._onTrackRemoveRequest as EventListener);\n this.addEventListener('daw-clip-connected', this._onClipConnected as EventListener);\n this.addEventListener('daw-clip-update', this._onClipUpdate as EventListener);\n // Detect track + clip removal via MutationObserver (detached elements can't bubble events).\n this._childObserver = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n for (const node of mutation.removedNodes) {\n if (node instanceof HTMLElement) {\n if (node.tagName === 'DAW-TRACK') {\n this._onTrackRemoved((node as DawTrackElement).trackId);\n } else if (node.tagName === 'DAW-CLIP') {\n this._onClipRemovedFromDom(node as DawClipElement);\n }\n const nestedTracks = node.querySelectorAll?.('daw-track');\n if (nestedTracks) {\n for (const track of nestedTracks) {\n this._onTrackRemoved((track as DawTrackElement).trackId);\n }\n }\n const nestedClips = node.querySelectorAll?.('daw-clip');\n if (nestedClips) {\n for (const clip of nestedClips) {\n this._onClipRemovedFromDom(clip as DawClipElement);\n }\n }\n }\n }\n }\n });\n this._childObserver.observe(this, { childList: true, subtree: true });\n }\n disconnectedCallback() {\n super.disconnectedCallback();\n this.removeEventListener('daw-track-connected', this._onTrackConnected as EventListener);\n this.removeEventListener('daw-track-update', this._onTrackUpdate as EventListener);\n this.removeEventListener('daw-track-control', this._onTrackControl as EventListener);\n this.removeEventListener('daw-track-remove', this._onTrackRemoveRequest as EventListener);\n this.removeEventListener('daw-clip-connected', this._onClipConnected as EventListener);\n this.removeEventListener('daw-clip-update', this._onClipUpdate as EventListener);\n this._childObserver?.disconnect();\n this._childObserver = null;\n this._trackElements.clear();\n this._audioCache.clear();\n this._peaksCache.clear();\n this._clipBuffers.clear();\n this._clipOffsets.clear();\n this._peakPipeline.terminate();\n this._minSamplesPerPixel = 0;\n this._spectrogramController?.dispose();\n this._spectrogramController = null;\n try {\n this._disposeEngine();\n } catch (err) {\n console.warn('[dawcore] Error disposing engine: ' + String(err));\n }\n }\n willUpdate(changedProperties: Map<string, unknown>) {\n if (changedProperties.has('eagerResume')) {\n this._audioResume.target = this.eagerResume;\n }\n // Restart playhead animation when zoom or beats params change during playback\n if (\n (changedProperties.has('samplesPerPixel') ||\n changedProperties.has('ticksPerPixel') ||\n changedProperties.has('bpm') ||\n changedProperties.has('secondsToTicks')) &&\n this._isPlaying\n ) {\n this._startPlayhead();\n }\n // Re-extract peaks at new zoom level from cached WaveformData (near-instant).\n // For worker-generated peaks, baseScale (128) is finest; for pre-computed .dat\n // peaks (only cached when rates match), the file's scale is the limit.\n // In beats mode, _renderSpp changes when ticksPerPixel or bpm changes.\n const zoomChanged =\n changedProperties.has('samplesPerPixel') ||\n changedProperties.has('ticksPerPixel') ||\n changedProperties.has('bpm') ||\n changedProperties.has('scaleMode') ||\n changedProperties.has('secondsToTicks');\n if (zoomChanged && this._clipBuffers.size > 0) {\n const re = this._peakPipeline.reextractPeaks(\n this._clipBuffers,\n this._renderSpp,\n this.mono,\n this._clipOffsets\n );\n if (re.size > 0) {\n const next = new Map(this._peaksData);\n for (const [id, pd] of re) next.set(id, pd);\n this._peaksData = next;\n }\n }\n }\n\n /**\n * Cache of the last ViewportState forwarded to the spectrogram controller.\n * Lit's `updated()` fires on every reactive state change (`_isPlaying`,\n * `_selectedTrackId`, etc.) — most of which don't affect the spectrogram\n * viewport. Skip the cross-controller call when nothing changed.\n *\n * The orchestrator dedupes identical viewports too, so removing this cache\n * wouldn't change observable behavior — but it would push a fresh\n * `setViewport` call (with object allocation) into every Lit reactive\n * update for properties unrelated to the viewport.\n */\n private _lastSpectrogramViewport: {\n vs: number;\n ve: number;\n spp: number;\n } | null = null;\n\n protected updated(_changed: Map<string, unknown>): void {\n // Keep selectors truthful so the controller can distinguish \"intentionally\n // not rendered\" from \"selector broken\" (it warns on the latter when scrolled).\n this._scrollSync.xTargetSelector = this._showRuler ? '.ruler-content' : '';\n this._scrollSync.yTargetSelector = this._showControls ? '.controls-column' : '';\n this._scrollSync.wheelForwardSelector = [\n this._showControls ? '.controls-viewport' : '',\n this._showRuler ? '.ruler-viewport' : '',\n ]\n .filter(Boolean)\n .join(', ');\n // Re-applies transforms clobbered by the .ruler-content style binding —\n // Lit rewrites the style attribute (width) on _totalWidth changes, wiping\n // the imperatively-set transform. Must run before paint on every update.\n this._scrollSync.sync();\n // Forward viewport + zoom into the spectrogram controller on every update\n // so scroll, resize, and zoom all trigger orchestrator.setViewport.\n if (this._spectrogramController) {\n const vs = this._viewport.visibleStart;\n const ve = this._viewport.visibleEnd;\n const spp = this._renderSpp;\n if (Number.isFinite(vs) && Number.isFinite(ve)) {\n const prev = this._lastSpectrogramViewport;\n if (prev && prev.vs === vs && prev.ve === ve && prev.spp === spp) return;\n this._lastSpectrogramViewport = { vs, ve, spp };\n const span = ve - vs;\n const bufferPad = span * 0.25;\n this._spectrogramController.setViewport({\n visibleStartPx: vs,\n visibleEndPx: ve,\n bufferStartPx: Math.max(0, vs - bufferPad),\n bufferEndPx: ve + bufferPad,\n samplesPerPixel: spp,\n });\n }\n }\n }\n\n // --- Track Events ---\n private _onTrackConnected = (e: CustomEvent) => {\n const trackId = e.detail?.trackId;\n const trackEl = e.detail?.element;\n if (!trackId || !(trackEl instanceof HTMLElement)) {\n console.warn('[dawcore] Invalid daw-track-connected event detail: ' + String(e.detail));\n return;\n }\n const descriptor = this._readTrackDescriptor(trackEl as DawTrackElement);\n this._tracks = new Map(this._tracks).set(trackId, descriptor);\n this._trackElements.set(trackId, trackEl as DawTrackElement);\n this._loadTrack(trackId, descriptor);\n };\n private _onTrackRemoved(trackId: string) {\n this._trackElements.delete(trackId);\n // Clean up per-clip data before removing the track (need clip IDs from engine tracks)\n const removedTrack = this._engineTracks.get(trackId);\n if (removedTrack) {\n const nextPeaks = new Map(this._peaksData);\n for (const clip of removedTrack.clips) {\n this._clipBuffers.delete(clip.id);\n this._clipOffsets.delete(clip.id);\n nextPeaks.delete(clip.id);\n this._spectrogramController?.unregisterClipAudio(clip.id);\n }\n this._peaksData = nextPeaks;\n }\n const nextTracks = new Map(this._tracks);\n nextTracks.delete(trackId);\n this._tracks = nextTracks;\n const nextEngine = new Map(this._engineTracks);\n nextEngine.delete(trackId);\n this._engineTracks = nextEngine;\n this._recomputeDuration();\n if (this._engine) {\n // Incremental removal preserves playback (no playout rebuild)\n this._engine.removeTrack(trackId);\n }\n // Recompute zoom floor from remaining cached WaveformData scales\n this._minSamplesPerPixel = this._peakPipeline.getMaxCachedScale(this._clipBuffers);\n this._disposeSpectrogramControllerIfEmpty();\n if (nextEngine.size === 0) {\n this._currentTime = 0;\n this._stopPlayhead();\n }\n }\n private _onTrackUpdate = (e: CustomEvent) => {\n const trackId = e.detail?.trackId as string;\n if (!trackId) return;\n const trackEl = (e.target as HTMLElement).closest('daw-track') as DawTrackElement | null;\n if (!trackEl) return;\n const oldDescriptor = this._tracks.get(trackId);\n const descriptor = this._readTrackDescriptor(trackEl);\n this._tracks = new Map(this._tracks).set(trackId, descriptor);\n if (this._engine) {\n if (oldDescriptor?.volume !== descriptor.volume)\n this._engine.setTrackVolume(trackId, descriptor.volume);\n if (oldDescriptor?.pan !== descriptor.pan) this._engine.setTrackPan(trackId, descriptor.pan);\n if (oldDescriptor?.muted !== descriptor.muted)\n this._engine.setTrackMute(trackId, descriptor.muted);\n if (oldDescriptor?.soloed !== descriptor.soloed)\n this._engine.setTrackSolo(trackId, descriptor.soloed);\n }\n if (oldDescriptor?.src !== descriptor.src) {\n this._loadTrack(trackId, descriptor);\n }\n // Track switched into spectrogram mode — push existing clip audio into the controller.\n if (descriptor.renderMode === 'spectrogram' && oldDescriptor?.renderMode !== 'spectrogram') {\n const engineTrack = this._engineTracks.get(trackId);\n if (engineTrack) {\n for (const clip of engineTrack.clips) {\n this._maybeRegisterSpectrogramClipAudio(trackId, clip);\n }\n }\n }\n // Track switched OUT of spectrogram mode — drop clip audio.\n if (descriptor.renderMode !== 'spectrogram' && oldDescriptor?.renderMode === 'spectrogram') {\n const engineTrack = this._engineTracks.get(trackId);\n if (engineTrack && this._spectrogramController) {\n for (const clip of engineTrack.clips) {\n this._spectrogramController.unregisterClipAudio(clip.id);\n }\n }\n this._disposeSpectrogramControllerIfEmpty();\n }\n if (descriptor.spectrogramConfig !== oldDescriptor?.spectrogramConfig) {\n this._spectrogramController?.setTrackConfig(trackId, descriptor.spectrogramConfig ?? null);\n }\n };\n\n /** Drop the controller when no spectrogram tracks remain. */\n private _disposeSpectrogramControllerIfEmpty(): void {\n if (!this._spectrogramController) return;\n const stillNeeded = Array.from(this._tracks.values()).some(\n (d) => d.renderMode === 'spectrogram'\n );\n if (!stillNeeded) {\n this._spectrogramController.dispose();\n this._spectrogramController = null;\n }\n }\n private static _CONTROL_PROPS = new Set(['volume', 'pan', 'muted', 'soloed']);\n private _onTrackControl = (e: CustomEvent) => {\n const { trackId, prop, value } = e.detail ?? {};\n if (!trackId || !prop || !DawEditorElement._CONTROL_PROPS.has(prop)) return;\n // Select the track when interacting with its controls\n if (this._selectedTrackId !== trackId) {\n this._setSelectedTrackId(trackId);\n if (this._engine) {\n this._engine.selectTrack(trackId);\n }\n this.dispatchEvent(\n new CustomEvent('daw-track-select', {\n bubbles: true,\n composed: true,\n detail: { trackId },\n })\n );\n }\n const oldDescriptor = this._tracks.get(trackId);\n if (oldDescriptor) {\n const descriptor = { ...oldDescriptor, [prop]: value };\n this._tracks = new Map(this._tracks).set(trackId, descriptor);\n // Forward to engine with validated values\n if (this._engine) {\n if (prop === 'volume')\n this._engine.setTrackVolume(trackId, Math.max(0, Math.min(1, Number(value))));\n if (prop === 'pan')\n this._engine.setTrackPan(trackId, Math.max(-1, Math.min(1, Number(value))));\n if (prop === 'muted') this._engine.setTrackMute(trackId, Boolean(value));\n if (prop === 'soloed') this._engine.setTrackSolo(trackId, Boolean(value));\n }\n }\n // Don't sync back to <daw-track> DOM element — avoids daw-track-update loop.\n // _tracks descriptor map is the source of truth for control values.\n };\n private _onTrackRemoveRequest = (e: CustomEvent) => {\n const { trackId } = e.detail ?? {};\n if (!trackId) return;\n const trackEl = this._trackElements.get(trackId);\n if (trackEl) {\n trackEl.remove(); // MutationObserver will trigger _onTrackRemoved\n } else {\n this._onTrackRemoved(trackId); // File-dropped tracks: no DOM element\n }\n };\n // --- Clip lifecycle ---\n private _onClipConnected = (e: CustomEvent) => {\n const detail = e.detail as { clipId: string; element: DawClipElement };\n const clipEl = detail.element;\n if (!(clipEl instanceof HTMLElement)) return;\n const trackEl = clipEl.closest('daw-track') as DawTrackElement | null;\n if (!trackEl) return;\n const trackId = trackEl.trackId;\n // Skip during initial track load — daw-track-connected reads all <daw-clip>\n // children synchronously via _readTrackDescriptor. Late-append clips trigger\n // an incremental load below.\n if (!this._engineTracks.has(trackId)) {\n // _tracks is populated in _onTrackConnected before _loadTrack runs, so\n // having a descriptor without an engine track means the parent is still\n // loading. _readTrackDescriptor already captured each existing <daw-clip>\n // child's id into descriptor.clips — those deferred daw-clip-connected\n // events are redundant and silent skip is correct. Only a clipId that\n // wasn't in the pre-load capture is a true late-append that risks being\n // missed; warn for those.\n const desc = this._tracks.get(trackId);\n if (desc && !desc.clips.some((c) => isDomClip(c) && c.clipId === clipEl.clipId)) {\n console.warn(\n '[dawcore] daw-clip-connected fired while parent track \"' +\n trackId +\n '\" is still loading — late-appended clip may be missed. ' +\n 'Wait for daw-track-ready before appending more <daw-clip> children, ' +\n 'or use editor.addClip(trackId, config) after the track finishes loading.'\n );\n }\n return;\n }\n const clipDesc: ClipDescriptor = {\n kind: 'dom',\n clipId: clipEl.clipId,\n src: clipEl.src,\n peaksSrc: clipEl.peaksSrc,\n start: clipEl.start,\n duration: clipEl.duration,\n offset: clipEl.offset,\n gain: clipEl.gain,\n name: clipEl.name,\n fadeIn: clipEl.fadeIn,\n fadeOut: clipEl.fadeOut,\n fadeType: clipEl.fadeType as FadeType,\n midiNotes: clipEl.midiNotes,\n midiChannel: clipEl.midiChannel,\n midiProgram: clipEl.midiProgram,\n };\n this._loadAndAppendClip(trackId, clipDesc);\n };\n private _onClipUpdate = (e: CustomEvent) => {\n const clipEl = e.target as DawClipElement;\n if (!(clipEl instanceof HTMLElement) || clipEl.tagName !== 'DAW-CLIP') return;\n const detail = e.detail as { trackId: string; clipId: string };\n if (!detail.trackId) {\n // <daw-clip> mounted outside a <daw-track> — developer error.\n console.warn(\n '[dawcore] daw-clip-update fired from a <daw-clip> not nested in a <daw-track> — ignored'\n );\n return;\n }\n this._applyClipUpdate(detail.trackId, detail.clipId, clipEl);\n };\n private _onClipRemovedFromDom(clipEl: DawClipElement) {\n const clipId = clipEl.clipId;\n for (const [trackId, t] of this._engineTracks.entries()) {\n if (t.clips.some((c) => c.id === clipId)) {\n this._removeClipFromTrack(trackId, clipId);\n return;\n }\n }\n // No matching engine clip. May be benign (clip was removed before its load\n // completed), but it can also indicate a DOM/engine id mismatch — purge any\n // orphan cache entries and warn so the leak is visible.\n if (\n this._clipBuffers.has(clipId) ||\n this._clipOffsets.has(clipId) ||\n this._peaksData.has(clipId)\n ) {\n console.warn(\n '[dawcore] _onClipRemovedFromDom: orphaned cache entries for clip \"' +\n clipId +\n '\" — purging (DOM/engine id mismatch?)'\n );\n this._purgeClipCaches(clipId);\n }\n }\n private async _loadAndAppendClip(trackId: string, clipDesc: DomClipDescriptor) {\n if (!clipDesc.src) return; // Late-append of MIDI clips is not yet supported by _loadAndAppendClip — only _loadTrack handles the no-src MIDI branch on initial load.\n // Late-append always comes via _onClipConnected, which only fires for\n // <daw-clip> elements — so clipId is always known. We use it for the\n // error dispatch so the consumer's addClip Promise rejects with a usable\n // identifier even if _finalizeAudioClip throws before clip.id is set.\n const clipId = clipDesc.clipId;\n // Track which clip id has been inserted into per-clip caches so the catch\n // block can roll back partial state on any error past the cache writes.\n let insertedClipId: string | null = null;\n try {\n // Concurrent fetches: peaks (if provided) + audio decode.\n const waveformDataPromise = clipDesc.peaksSrc\n ? this._resolvePeaks(clipDesc.peaksSrc)\n : Promise.resolve(null);\n const audioPromise = this._fetchAndDecode(clipDesc.src);\n const [waveformData, audioBuffer] = await Promise.all([waveformDataPromise, audioPromise]);\n this._resolvedSampleRate = audioBuffer.sampleRate;\n\n const clip = await this._finalizeAudioClip(clipDesc, audioBuffer, waveformData);\n insertedClipId = clip.id;\n\n const t = this._engineTracks.get(trackId);\n if (!t) {\n // Track was removed during load — purge clip state\n this._purgeClipCaches(clip.id);\n return;\n }\n const updatedTrack: ClipTrack = { ...t, clips: [...t.clips, clip] };\n this._engineTracks = new Map(this._engineTracks).set(trackId, updatedTrack);\n\n const desc = this._tracks.get(trackId);\n if (desc) {\n this._tracks = new Map(this._tracks).set(trackId, {\n ...desc,\n clips: [...desc.clips, clipDesc],\n });\n }\n this._commitTrackChange(trackId, updatedTrack);\n this._maybeRegisterSpectrogramClipAudio(trackId, clip);\n\n this.dispatchEvent(\n new CustomEvent<DawClipIdDetail>('daw-clip-ready', {\n bubbles: true,\n composed: true,\n detail: { trackId, clipId: clip.id },\n })\n );\n } catch (err) {\n // Always warn — even when disconnected — so the failure isn't silent.\n console.warn('[dawcore] _loadAndAppendClip failed: ' + String(err));\n // Roll back partial cache state so a retry isn't poisoned by stale entries.\n if (insertedClipId) this._purgeClipCaches(insertedClipId);\n // Detached elements can't bubble events, but the listener registered on\n // `this` via addClip will still fire — dispatch directly so the addClip\n // promise rejects instead of orphaning.\n this.dispatchEvent(\n new CustomEvent<DawClipErrorDetail>('daw-clip-error', {\n bubbles: true,\n composed: true,\n detail: { trackId, clipId: insertedClipId ?? clipId, error: err },\n })\n );\n }\n }\n /**\n * Resolve pre-computed peaks for a clip: fetch the .dat/.json, validate the\n * sample rate matches the AudioContext, return the WaveformData or null.\n * Warns on fetch failure and on sample-rate mismatch — never silent.\n *\n * Shared between `_loadTrack` (peaks-first preview path) and\n * `_loadAndAppendClip` (incremental late-append).\n */\n private async _resolvePeaks(peaksSrc: string): Promise<import('waveform-data').default | null> {\n try {\n const wd = await this._fetchPeaks(peaksSrc);\n const contextRate = this.audioContext.sampleRate;\n if (wd.sample_rate === contextRate) return wd;\n console.warn(\n '[dawcore] Pre-computed peaks at ' +\n wd.sample_rate +\n ' Hz do not match AudioContext at ' +\n contextRate +\n ' Hz — ignoring ' +\n peaksSrc +\n ', generating from audio'\n );\n return null;\n } catch (err) {\n console.warn(\n '[dawcore] Failed to load peaks from ' +\n peaksSrc +\n ': ' +\n String(err) +\n ' — falling back to AudioBuffer generation'\n );\n return null;\n }\n }\n /**\n * Construct an AudioClip from a decoded buffer (and optional WaveformData),\n * align its id with the source `<daw-clip>.clipId` when present, populate\n * `_clipBuffers` / `_clipOffsets`, generate peaks via the worker pipeline,\n * and populate `_peaksData`. Returns the finished AudioClip.\n *\n * Shared between `_loadTrack`'s standard path and `_loadAndAppendClip`.\n * Not used by `_loadTrack`'s peaks-first preview path because that path\n * uses sync `extractPeaks` and inserts a preview track BEFORE audio decode.\n */\n private async _finalizeAudioClip(\n clipDesc: ClipDescriptor,\n audioBuffer: AudioBuffer,\n waveformData: import('waveform-data').default | null\n ): Promise<AudioClip> {\n let clip: AudioClip;\n if (waveformData) {\n const wdRate = waveformData.sample_rate;\n clip = createClip({\n audioBuffer,\n waveformData,\n startSample: Math.round(clipDesc.start * wdRate),\n durationSamples: Math.round((clipDesc.duration || waveformData.duration) * wdRate),\n offsetSamples: Math.round(clipDesc.offset * wdRate),\n gain: clipDesc.gain,\n name: clipDesc.name,\n sampleRate: wdRate,\n sourceDurationSamples: Math.ceil(waveformData.duration * wdRate),\n });\n this._peakPipeline.cacheWaveformData(audioBuffer, waveformData);\n } else {\n clip = createClipFromSeconds({\n audioBuffer,\n startTime: clipDesc.start,\n duration: clipDesc.duration || audioBuffer.duration,\n offset: clipDesc.offset,\n gain: clipDesc.gain,\n name: clipDesc.name,\n sampleRate: audioBuffer.sampleRate,\n sourceDuration: audioBuffer.duration,\n });\n }\n if (isDomClip(clipDesc)) clip.id = clipDesc.clipId;\n\n this._clipBuffers = new Map(this._clipBuffers).set(clip.id, audioBuffer);\n this._clipOffsets.set(clip.id, {\n offsetSamples: clip.offsetSamples,\n durationSamples: clip.durationSamples,\n });\n // generatePeaks can fail (worker crash, CSP, OOM). Purge the cache entries\n // we just inserted so the caller's catch path doesn't leak. The caller may\n // also call _purgeClipCaches in its own error handler — _purgeClipCaches\n // is idempotent.\n let peakData: PeakData;\n try {\n peakData = await this._peakPipeline.generatePeaks(\n audioBuffer,\n this._renderSpp,\n this.mono,\n clip.offsetSamples,\n clip.durationSamples\n );\n } catch (err) {\n this._purgeClipCaches(clip.id);\n throw err;\n }\n this._peaksData = new Map(this._peaksData).set(clip.id, peakData);\n // Raise the zoom-floor only after generatePeaks succeeds — without this,\n // a generatePeaks failure would strand _minSamplesPerPixel at a value\n // backed by no actual peaks (CLAUDE.md `samplesPerPixel` Zoom Floor rule).\n if (waveformData) {\n this._minSamplesPerPixel = Math.max(this._minSamplesPerPixel, waveformData.scale);\n }\n return clip;\n }\n /**\n * Filter MIDI notes to only those with finite, in-range fields. Logs a\n * warning for each dropped note. Used by _buildMidiClip and the\n * _applyClipUpdate MIDI branch to prevent NaN propagation through the\n * timeline.\n */\n private _validMidiNotes(notes: MidiNoteData[]): MidiNoteData[] {\n return notes.filter((n) => {\n const ok =\n Number.isFinite(n.time) &&\n n.time >= 0 &&\n Number.isFinite(n.duration) &&\n n.duration > 0 &&\n Number.isInteger(n.midi) &&\n n.midi >= 0 &&\n n.midi <= 127 &&\n Number.isFinite(n.velocity) &&\n n.velocity >= 0 &&\n n.velocity <= 1;\n if (!ok) {\n console.warn('[dawcore] dropping malformed MIDI note: ' + JSON.stringify(n));\n }\n return ok;\n });\n }\n /**\n * A clip descriptor is treated as MIDI when it has no audio src.\n * Includes placeholder MIDI clips (no notes, no duration yet — registered\n * with a 1s default span; notes upgrade via _applyClipUpdate). Warns when\n * a clip ambiguously has both src and midiNotes — the audio path runs\n * and notes would be silently ignored.\n */\n private _isMidiDescriptor(clipDesc: ClipDescriptor): boolean {\n if (clipDesc.src) {\n if (clipDesc.midiNotes != null) {\n console.warn(\n '[dawcore] clip \"' +\n (clipDesc.name || (isDomClip(clipDesc) ? clipDesc.clipId : '?')) +\n '\" has both src and midiNotes — treating as audio (notes will be ignored)'\n );\n }\n return false;\n }\n return true;\n }\n /**\n * Build an engine clip from a MIDI clip descriptor. Always returns a clip\n * — empty notes / no declared duration get a 1-second placeholder span so\n * the clip is reachable via `engine.updateTrack` once notes arrive.\n */\n private _buildMidiClip(clipDesc: ClipDescriptor): AudioClip {\n const sr = this.effectiveSampleRate;\n const notes = this._validMidiNotes(clipDesc.midiNotes ?? []);\n const noteSpanSeconds = notes.length\n ? notes.reduce((max, n) => Math.max(max, n.time + n.duration), 0)\n : 0;\n const sourceDurationSamples = Math.ceil(Math.max(noteSpanSeconds, clipDesc.duration, 1) * sr);\n const requestedDurationSamples =\n clipDesc.duration > 0 ? Math.round(clipDesc.duration * sr) : sourceDurationSamples;\n\n const clip = createClip({\n startSample: Math.round(clipDesc.start * sr),\n durationSamples: requestedDurationSamples,\n offsetSamples: Math.round(clipDesc.offset * sr),\n sampleRate: sr,\n sourceDurationSamples,\n gain: clipDesc.gain,\n name: clipDesc.name,\n midiNotes: notes,\n midiChannel: clipDesc.midiChannel ?? undefined,\n midiProgram: clipDesc.midiProgram ?? undefined,\n });\n if (isDomClip(clipDesc)) clip.id = clipDesc.clipId;\n return clip;\n }\n /** Remove a single clip from all per-clip caches. Used by error rollbacks. */\n private _purgeClipCaches(clipId: string) {\n const nextBuffers = new Map(this._clipBuffers);\n nextBuffers.delete(clipId);\n this._clipBuffers = nextBuffers;\n const nextPeaks = new Map(this._peaksData);\n nextPeaks.delete(clipId);\n this._peaksData = nextPeaks;\n this._clipOffsets.delete(clipId);\n this._spectrogramController?.unregisterClipAudio(clipId);\n }\n /**\n * Recompute duration and forward an updated track to the engine. Single\n * source of truth for the incremental-vs-full-rebuild policy used by every\n * clip-level mutation (addClip, updateClip, removeClip, _applyClipUpdate).\n * Use the engine's incremental updateTrack when available; otherwise fall\n * back to full setTracks (legacy adapters).\n */\n private _commitTrackChange(trackId: string, updatedTrack: ClipTrack) {\n this._recomputeDuration();\n if (this._engine?.updateTrack) this._engine.updateTrack(trackId, updatedTrack);\n else if (this._engine) this._engine.setTracks([...this._engineTracks.values()]);\n }\n private _applyClipUpdate(trackId: string, clipId: string, clipEl: DawClipElement) {\n const t = this._engineTracks.get(trackId);\n if (!t) {\n console.warn('[dawcore] _applyClipUpdate: no engine track for id \"' + trackId + '\"');\n return;\n }\n const idx = t.clips.findIndex((c) => c.id === clipId);\n if (idx === -1) {\n console.warn(\n '[dawcore] _applyClipUpdate: clip \"' +\n clipId +\n '\" not found in track \"' +\n trackId +\n '\" (DOM/engine clip-id misalignment?)'\n );\n return;\n }\n const oldClip = t.clips[idx];\n const sr = oldClip.sampleRate ?? this.effectiveSampleRate;\n\n // MIDI clips: rebuild the clip when notes/channel/program change. Detect\n // MIDI by either current or previous state being MIDI. oldClip.midiNotes\n // is an array (possibly empty []) for any clip registered via _buildMidiClip\n // — including placeholders. For audio clips it is undefined.\n // Use loose != null consistently: treats both null and undefined as \"not MIDI\"\n // for both clipEl.midiNotes (DawClipElement defaults to null) and oldClip.midiNotes.\n const isMidiNow = clipEl.midiNotes != null;\n const wasMidi = oldClip.midiNotes != null;\n if (isMidiNow || wasMidi) {\n const notes = this._validMidiNotes(clipEl.midiNotes ?? []);\n const noteSpanSeconds = notes.length\n ? notes.reduce((max, n) => Math.max(max, n.time + n.duration), 0)\n : 0;\n const sourceDurationSamples = Math.ceil(Math.max(noteSpanSeconds, clipEl.duration, 1) * sr);\n const requestedDurationSamples =\n clipEl.duration > 0 ? Math.round(clipEl.duration * sr) : sourceDurationSamples;\n\n const updatedClip: AudioClip = {\n ...oldClip,\n audioBuffer: undefined,\n startSample: Math.round(clipEl.start * sr),\n offsetSamples: Math.round(clipEl.offset * sr),\n durationSamples: requestedDurationSamples,\n sourceDurationSamples,\n gain: clipEl.gain,\n name: clipEl.name || oldClip.name,\n midiNotes: notes,\n midiChannel: clipEl.midiChannel ?? undefined,\n midiProgram: clipEl.midiProgram ?? undefined,\n };\n const updatedClips = [...t.clips];\n updatedClips[idx] = updatedClip;\n const updatedTrack: ClipTrack = { ...t, clips: updatedClips };\n this._engineTracks = new Map(this._engineTracks).set(trackId, updatedTrack);\n // Drop any audio caches in case this clip was previously loaded as audio.\n this._purgeClipCaches(clipId);\n this._commitTrackChange(trackId, updatedTrack);\n return;\n }\n\n const newStartSample = Math.round(clipEl.start * sr);\n const newDurationSamples =\n clipEl.duration > 0 ? Math.round(clipEl.duration * sr) : oldClip.durationSamples;\n const newOffsetSamples = Math.round(clipEl.offset * sr);\n const updatedClip = {\n ...oldClip,\n startSample: newStartSample,\n durationSamples: newDurationSamples,\n offsetSamples: newOffsetSamples,\n gain: clipEl.gain,\n name: clipEl.name || oldClip.name,\n };\n const updatedClips = [...t.clips];\n updatedClips[idx] = updatedClip;\n const updatedTrack: ClipTrack = { ...t, clips: updatedClips };\n this._engineTracks = new Map(this._engineTracks).set(trackId, updatedTrack);\n\n const boundsChanged =\n oldClip.offsetSamples !== newOffsetSamples || oldClip.durationSamples !== newDurationSamples;\n if (boundsChanged) {\n this._clipOffsets.set(clipId, {\n offsetSamples: newOffsetSamples,\n durationSamples: newDurationSamples,\n });\n const peaks = this.reextractClipPeaks(clipId, newOffsetSamples, newDurationSamples);\n if (peaks) {\n this._peaksData = new Map(this._peaksData).set(clipId, peaks);\n }\n }\n\n this._commitTrackChange(trackId, updatedTrack);\n }\n private _removeClipFromTrack(trackId: string, clipId: string) {\n const t = this._engineTracks.get(trackId);\n if (!t) {\n console.warn('[dawcore] _removeClipFromTrack: no engine track for id \"' + trackId + '\"');\n return;\n }\n const updatedClips = t.clips.filter((c) => c.id !== clipId);\n if (updatedClips.length === t.clips.length) {\n console.warn(\n '[dawcore] _removeClipFromTrack: clip \"' + clipId + '\" not found in track \"' + trackId + '\"'\n );\n return;\n }\n const updatedTrack: ClipTrack = { ...t, clips: updatedClips };\n this._engineTracks = new Map(this._engineTracks).set(trackId, updatedTrack);\n\n const nextBuffers = new Map(this._clipBuffers);\n nextBuffers.delete(clipId);\n this._clipBuffers = nextBuffers;\n this._clipOffsets.delete(clipId);\n const nextPeaks = new Map(this._peaksData);\n nextPeaks.delete(clipId);\n this._peaksData = nextPeaks;\n\n const desc = this._tracks.get(trackId);\n if (desc) {\n this._tracks = new Map(this._tracks).set(trackId, {\n ...desc,\n // Only DOM-sourced clips have an id to match; drop-sourced clips are\n // filtered through unchanged (their identity is the descriptor itself).\n clips: desc.clips.filter((c) => !(isDomClip(c) && c.clipId === clipId)),\n });\n }\n this._commitTrackChange(trackId, updatedTrack);\n }\n private _readTrackDescriptor(trackEl: DawTrackElement): TrackDescriptor {\n const clipEls = trackEl.querySelectorAll('daw-clip') as NodeListOf<DawClipElement>;\n const clips: ClipDescriptor[] = [];\n\n if (clipEls.length === 0 && trackEl.src) {\n // <daw-track src> shorthand — synthetic descriptor with no <daw-clip>\n // backing element. No DOM clipId to align with.\n clips.push({\n kind: 'drop',\n src: trackEl.src,\n peaksSrc: '',\n start: 0,\n duration: 0,\n offset: 0,\n gain: 1,\n name: trackEl.name || '',\n fadeIn: 0,\n fadeOut: 0,\n fadeType: 'linear',\n midiNotes: null,\n midiChannel: null,\n midiProgram: null,\n });\n } else {\n for (const clipEl of clipEls) {\n clips.push({\n kind: 'dom',\n clipId: clipEl.clipId,\n src: clipEl.src,\n peaksSrc: clipEl.peaksSrc,\n start: clipEl.start,\n duration: clipEl.duration,\n offset: clipEl.offset,\n gain: clipEl.gain,\n name: clipEl.name,\n fadeIn: clipEl.fadeIn,\n fadeOut: clipEl.fadeOut,\n fadeType: clipEl.fadeType as FadeType,\n midiNotes: clipEl.midiNotes,\n midiChannel: clipEl.midiChannel,\n midiProgram: clipEl.midiProgram,\n });\n }\n }\n return {\n name: trackEl.name || 'Untitled',\n src: trackEl.src,\n volume: trackEl.volume,\n pan: trackEl.pan,\n muted: trackEl.muted,\n soloed: trackEl.soloed,\n renderMode: trackEl.renderMode,\n clips,\n };\n }\n // --- Audio Loading ---\n private async _loadTrack(trackId: string, descriptor: TrackDescriptor) {\n try {\n const clips = [];\n for (const clipDesc of descriptor.clips) {\n if (this._isMidiDescriptor(clipDesc)) {\n // MIDI clip path: no fetch, no peaks, register the clip directly.\n // Always registers (even with no notes / no duration) so late note\n // arrivals via daw-clip-update — handled in _applyClipUpdate\n // — can find the clip in _engineTracks.\n clips.push(this._buildMidiClip(clipDesc));\n continue;\n }\n // Per-clip try/catch: a single bad clip dispatches daw-clip-error and\n // skips to the next clip rather than aborting the whole track. Without\n // this, clip N's failure leaks earlier clips' cache writes from this\n // loop because the outer catch never reaches engine.setTracks().\n try {\n // Start both fetches concurrently — await peaks first to render preview before audio decode\n const waveformDataPromise = clipDesc.peaksSrc\n ? this._resolvePeaks(clipDesc.peaksSrc)\n : Promise.resolve(null);\n const audioPromise = this._fetchAndDecode(clipDesc.src);\n\n // --- Peaks-first path: render waveform before audio decode completes ---\n // _resolvePeaks returns null on fetch failure or sample-rate mismatch\n // (warns in either case); the standard path below handles the null case.\n const waveformData = await waveformDataPromise;\n if (waveformData) {\n // Create clip with integer samples to avoid float round-trip drift\n // (CLAUDE.md pattern #40: prefer createClip when samples known)\n const wdRate = waveformData.sample_rate;\n const clip = createClip({\n waveformData,\n startSample: Math.round(clipDesc.start * wdRate),\n durationSamples: Math.round((clipDesc.duration || waveformData.duration) * wdRate),\n offsetSamples: Math.round(clipDesc.offset * wdRate),\n gain: clipDesc.gain,\n name: clipDesc.name,\n sampleRate: wdRate,\n sourceDurationSamples: Math.ceil(waveformData.duration * wdRate),\n });\n // Align engine clip.id with the source <daw-clip>.clipId (if any) so\n // DOM and engine refer to the same clip — required for editor.removeClip\n // and editor.updateClip lookups.\n if (isDomClip(clipDesc)) clip.id = clipDesc.clipId;\n const effectiveScale = Math.max(this._renderSpp, waveformData.scale);\n const peakData = extractPeaks(\n waveformData,\n effectiveScale,\n this.mono,\n clip.offsetSamples,\n clip.durationSamples\n );\n this._clipOffsets.set(clip.id, {\n offsetSamples: clip.offsetSamples,\n durationSamples: clip.durationSamples,\n });\n this._peaksData = new Map(this._peaksData).set(clip.id, peakData);\n this._minSamplesPerPixel = Math.max(this._minSamplesPerPixel, waveformData.scale);\n\n // Render preview track immediately with peaks (render-only until audio\n // completes and engine.setTracks() runs at end of _loadTrack)\n const previewTrack = createTrack({\n name: descriptor.name,\n clips: [clip],\n volume: descriptor.volume,\n pan: descriptor.pan,\n muted: descriptor.muted,\n soloed: descriptor.soloed,\n });\n previewTrack.id = trackId;\n this._engineTracks = new Map(this._engineTracks).set(trackId, previewTrack);\n this._recomputeDuration();\n\n // Wait for audio decode — clean up preview state if it fails\n let audioBuffer: AudioBuffer;\n try {\n audioBuffer = await audioPromise;\n } catch (audioErr) {\n // Remove ghost preview so the user doesn't see a waveform with no audio\n const nextPeaks = new Map(this._peaksData);\n nextPeaks.delete(clip.id);\n this._peaksData = nextPeaks;\n this._clipOffsets.delete(clip.id);\n const nextEngine = new Map(this._engineTracks);\n nextEngine.delete(trackId);\n this._engineTracks = nextEngine;\n this._minSamplesPerPixel = this._peakPipeline.getMaxCachedScale(this._clipBuffers);\n this._recomputeDuration();\n throw audioErr; // Propagate to outer catch for daw-track-error event\n }\n this._resolvedSampleRate = audioBuffer.sampleRate;\n // Backfill audioBuffer immutably: new clip replaces the preview clip\n const updatedClip = { ...clip, audioBuffer };\n this._clipBuffers = new Map(this._clipBuffers).set(clip.id, audioBuffer);\n this._peakPipeline.cacheWaveformData(audioBuffer, waveformData);\n clips.push(updatedClip);\n continue;\n }\n\n // --- Standard path: decode audio first, then generate peaks ---\n // Reached only when the peaks-first branch above didn't kick in\n // (no peaksSrc, fetch failure, or sample-rate mismatch). waveformData\n // is always null here.\n const audioBuffer = await audioPromise;\n this._resolvedSampleRate = audioBuffer.sampleRate;\n const clip = await this._finalizeAudioClip(clipDesc, audioBuffer, null);\n clips.push(clip);\n } catch (clipErr) {\n // _finalizeAudioClip and the peaks-first audio-decode catch already\n // purged their own per-clip caches before throwing here. Dispatch\n // daw-clip-error so consumers can correlate the failure to a clip.\n console.warn(\n '[dawcore] _loadTrack: clip \"' + clipDesc.src + '\" failed: ' + String(clipErr)\n );\n if (this.isConnected) {\n this.dispatchEvent(\n new CustomEvent<DawClipErrorDetail>('daw-clip-error', {\n bubbles: true,\n composed: true,\n detail: {\n trackId,\n clipId: isDomClip(clipDesc) ? clipDesc.clipId : '',\n error: clipErr,\n },\n })\n );\n }\n }\n }\n const track = createTrack({\n name: descriptor.name,\n clips,\n volume: descriptor.volume,\n pan: descriptor.pan,\n muted: descriptor.muted,\n soloed: descriptor.soloed,\n });\n // If clips were requested but ALL failed to load, surface as a track-level\n // error so addTrack({clips: [...]}) rejects appropriately. Per-clip\n // daw-clip-error events have already fired for each individual failure.\n const requestedClips = descriptor.clips.filter((c) => c.src).length;\n if (requestedClips > 0 && clips.length === 0) {\n throw new Error(\n 'all ' + requestedClips + ' clip(s) failed to load — see prior daw-clip-error events'\n );\n }\n // Align track.id with the editor's trackId so engine.setTrackSolo/Mute/etc. find it\n track.id = trackId;\n this._engineTracks = new Map(this._engineTracks).set(trackId, track);\n this._recomputeDuration();\n // Now that descriptor is recorded and clips have ids, push audio into\n // the spectrogram controller (no-op unless renderMode === 'spectrogram').\n for (const c of clips) {\n this._maybeRegisterSpectrogramClipAudio(trackId, c);\n }\n const engine = await this._ensureEngine();\n engine.setTracks([...this._engineTracks.values()]);\n this.dispatchEvent(\n new CustomEvent<DawTrackIdDetail>('daw-track-ready', {\n bubbles: true,\n composed: true,\n detail: { trackId },\n })\n );\n } catch (err) {\n // Guard against dispatching on a disconnected element (CLAUDE.md pattern #36)\n if (!this.isConnected) return;\n console.warn('[dawcore] Failed to load track \"' + trackId + '\": ' + String(err));\n this.dispatchEvent(\n new CustomEvent<DawTrackErrorDetail>('daw-track-error', {\n bubbles: true,\n composed: true,\n detail: { trackId, error: err },\n })\n );\n }\n }\n async _fetchAndDecode(src: string): Promise<AudioBuffer> {\n if (this._audioCache.has(src)) {\n return this._audioCache.get(src)!;\n }\n const promise = (async () => {\n const response = await fetch(src);\n if (!response.ok) {\n throw new Error(\n 'Failed to fetch audio \"' + src + '\": ' + response.status + ' ' + response.statusText\n );\n }\n const arrayBuffer = await response.arrayBuffer();\n return this.audioContext.decodeAudioData(arrayBuffer);\n })();\n this._audioCache.set(src, promise);\n try {\n return await promise;\n } catch (err) {\n this._audioCache.delete(src);\n throw err;\n }\n }\n private _fetchPeaks(src: string): Promise<import('waveform-data').default> {\n const cached = this._peaksCache.get(src);\n if (cached) return cached;\n const promise = loadWaveformDataFromUrl(src).catch((err) => {\n this._peaksCache.delete(src);\n throw err;\n });\n this._peaksCache.set(src, promise);\n return promise;\n }\n _recomputeDuration() {\n let maxSample = 0;\n for (const track of this._engineTracks.values()) {\n for (const clip of track.clips) {\n const endSample = clip.startSample + clip.durationSamples;\n if (endSample > maxSample) maxSample = endSample;\n }\n }\n this._duration = maxSample / this.effectiveSampleRate;\n }\n // --- Engine ---\n _ensureEngine(): Promise<PlaylistEngine> {\n if (this._engine) return Promise.resolve(this._engine);\n if (this._enginePromise) return this._enginePromise;\n this._enginePromise = this._buildEngine().catch((err) => {\n this._enginePromise = null;\n throw err;\n });\n return this._enginePromise;\n }\n private async _buildEngine() {\n if (!this._externalAdapter) {\n throw new Error(NO_ADAPTER_ERROR);\n }\n\n const { PlaylistEngine } = await import('@waveform-playlist/engine');\n const adapter = this._externalAdapter;\n\n // Forward initial tempo if adapter supports it\n if (adapter.setTempo) {\n adapter.setTempo(this._bpm);\n } else if (this._bpm !== 120) {\n console.warn(\n '[dawcore] Adapter does not implement setTempo. ' +\n 'Initial BPM ' +\n this._bpm +\n ' will not be applied — clips may use wrong tempo.'\n );\n }\n\n // Try to set the editor's desired PPQN on the adapter, then sync back.\n // Adapter is the source of truth — it may ignore the request.\n adapter.setPpqn?.(this._ppqn);\n this.ppqn = adapter.ppqn;\n\n const engine = new PlaylistEngine({\n adapter,\n sampleRate: this.effectiveSampleRate,\n samplesPerPixel: this.samplesPerPixel,\n bpm: this._bpm,\n zoomLevels: [256, 512, 1024, 2048, 4096, 8192, this.samplesPerPixel]\n .filter((v, i, a) => a.indexOf(v) === i)\n .sort((a, b) => a - b),\n });\n let lastTracksVersion = -1;\n engine.on('statechange', (engineState) => {\n this._isPlaying = engineState.isPlaying;\n this._duration = engineState.duration;\n this._selectedTrackId = engineState.selectedTrackId;\n // Sync clip positions when tracks change (moveClip, trimClip, splitClip)\n if (engineState.tracksVersion !== lastTracksVersion) {\n lastTracksVersion = engineState.tracksVersion;\n const nextTracks = new Map<string, ClipTrack>();\n for (const track of engineState.tracks) {\n nextTracks.set(track.id, track);\n }\n this._engineTracks = nextTracks;\n // Regenerate peaks for new or trimmed clips.\n // Piano-roll tracks have no AudioBuffer — skip them to avoid noisy\n // \"no AudioBuffer\" warnings on every tracksVersion bump.\n const audioTracks = engineState.tracks.filter((t) => {\n const desc = this._tracks.get(t.id);\n return desc?.renderMode !== 'piano-roll';\n });\n syncPeaksForChangedClips(this, audioTracks);\n }\n });\n engine.on('pause', () => {\n this._currentTime = engine.getCurrentTime();\n });\n engine.on('stop', () => {\n this._currentTime = engine.getCurrentTime();\n this._stopPlayhead();\n });\n\n this._engine = engine;\n return engine;\n }\n private _disposeEngine() {\n if (this._engine) {\n this._engine.dispose();\n this._engine = null;\n }\n this._enginePromise = null;\n }\n // --- File Drop ---\n private _onDragOver = (e: DragEvent) => {\n if (!this.fileDrop) return;\n e.preventDefault();\n if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy';\n this._dragOver = true;\n };\n private _onDragLeave = (e: DragEvent) => {\n if (!this.fileDrop) return;\n // relatedTarget is null when cursor leaves the browser window — that's fine,\n // we still want to clear _dragOver in that case.\n const timeline = this.shadowRoot?.querySelector('.timeline');\n if (timeline && !timeline.contains(e.relatedTarget as Node)) {\n this._dragOver = false;\n }\n };\n private _onDrop = async (e: DragEvent) => {\n if (!this.fileDrop) return;\n e.preventDefault();\n this._dragOver = false;\n const files = e.dataTransfer?.files;\n if (!files || files.length === 0) return;\n try {\n await this.loadFiles(files);\n } catch (err) {\n console.warn('[dawcore] File drop failed: ' + String(err));\n this.dispatchEvent(\n new CustomEvent<DawErrorDetail>('daw-error', {\n bubbles: true,\n composed: true,\n detail: { operation: 'file-drop', error: err },\n })\n );\n }\n };\n async loadFiles(files: FileList | File[]): Promise<LoadFilesResult> {\n return loadFilesImpl(this, files);\n }\n /**\n * Imperatively load a `.mid` file (URL or File) and create N `<daw-track>`\n * elements — one per note-bearing MIDI track. On any per-track failure,\n * every `<daw-track>` appended during the call is removed (both successful\n * and failed) so the editor returns to its pre-call state.\n *\n * `options.signal` is forwarded to `fetch()` only for URL sources; aborting\n * after parsing does not cancel in-flight `addTrack` calls.\n *\n * Requires the optional `@dawcore/midi` peer dep — throws with an install\n * hint (and `console.warn`s the original error) when the dynamic import\n * fails for any reason.\n */\n async loadMidi(source: string | File, options?: MidiLoadOptions): Promise<MidiLoadResult> {\n return loadMidiImpl(this, source, options);\n }\n // --- Programmatic Track API ---\n /**\n * Build the engine if it hasn't been built yet. Lets consumers obtain a\n * non-null `editor.engine` before any track has been loaded — useful for\n * wiring analyzers, effects, or master taps before content arrives.\n */\n async ready(): Promise<PlaylistEngine> {\n return this._ensureEngine();\n }\n /**\n * Wait for either `readyEvent` or `errorEvent` to fire on this editor for\n * the entity matching `matchesId`. Listeners are wired synchronously, then\n * `setup` is called (typical: appendChild). Resolves with `resolveValue`\n * on ready; rejects with a normalized Error on error. Used by addTrack and\n * addClip to share their Promise-with-listener-cleanup machinery.\n */\n private _awaitId<T>(\n readyEvent: string,\n errorEvent: string,\n matchesId: (detail: { trackId?: string; clipId?: string }) => boolean,\n resolveValue: T,\n setup: () => void\n ): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const onReady = (e: Event) => {\n if (!matchesId((e as CustomEvent).detail)) return;\n cleanup();\n resolve(resolveValue);\n };\n const onError = (e: Event) => {\n const detail = (e as CustomEvent).detail as { error?: unknown };\n if (!matchesId((e as CustomEvent).detail)) return;\n cleanup();\n const err = detail.error;\n reject(err instanceof Error ? err : new Error(String(err)));\n };\n const cleanup = () => {\n this.removeEventListener(readyEvent, onReady);\n this.removeEventListener(errorEvent, onError);\n };\n this.addEventListener(readyEvent, onReady);\n this.addEventListener(errorEvent, onError);\n setup();\n });\n }\n /**\n * Append a `<daw-track>` element built from `config` and resolve once the\n * track finishes loading (or reject on `daw-track-error`). Goes through\n * the same `_loadTrack` pipeline as declarative tracks, so descriptors,\n * peaks, and clip buffers are populated correctly.\n */\n addTrack(config: TrackConfig = {}): Promise<DawTrackElement> {\n const trackEl = document.createElement('daw-track') as DawTrackElement;\n if (config.name !== undefined) trackEl.setAttribute('name', config.name);\n if (config.volume !== undefined) trackEl.volume = config.volume;\n if (config.pan !== undefined) trackEl.pan = config.pan;\n if (config.muted) trackEl.setAttribute('muted', '');\n if (config.soloed) trackEl.setAttribute('soloed', '');\n\n // render-mode: explicit > inferred from midi shorthand > default 'waveform'\n const renderMode = config.renderMode ?? (config.midi ? 'piano-roll' : undefined);\n if (renderMode !== undefined) trackEl.setAttribute('render-mode', renderMode);\n\n const clipConfigs: ClipConfig[] = [...(config.clips ?? [])];\n if (config.midi) {\n clipConfigs.push({\n midiNotes: config.midi.notes,\n midiChannel: config.midi.channel,\n midiProgram: config.midi.program,\n });\n }\n for (const clipConfig of clipConfigs) {\n trackEl.appendChild(this._buildClipElement(clipConfig));\n }\n\n return this._awaitId(\n 'daw-track-ready',\n 'daw-track-error',\n (d) => d.trackId === trackEl.trackId,\n trackEl,\n () => this.appendChild(trackEl)\n );\n }\n /**\n * Remove a track by id. Equivalent to `trackElement.remove()` —\n * the editor's MutationObserver handles engine and cache cleanup.\n * No-op if no matching track exists.\n */\n removeTrack(trackId: string): void {\n const trackEl = this._trackElements.get(trackId);\n if (trackEl) {\n trackEl.remove();\n } else if (this._engineTracks.has(trackId)) {\n // File-dropped tracks have no DOM element; clean up engine state directly.\n this._onTrackRemoved(trackId);\n } else {\n console.warn('[dawcore] removeTrack: no track found for id \"' + trackId + '\"');\n }\n }\n /**\n * Update reflected attributes on a track. For DOM-element tracks the changes\n * are written to the `<daw-track>` element (which fires `daw-track-update`);\n * for tracks without a DOM element (file drops) the descriptor and engine\n * state are updated in place.\n */\n updateTrack(trackId: string, partial: Partial<TrackConfig>): void {\n const trackEl = this._trackElements.get(trackId);\n if (trackEl) {\n // Mutating reflected props triggers daw-track-update which propagates\n // to engine via _onTrackUpdate.\n if (partial.name !== undefined) trackEl.setAttribute('name', partial.name);\n if (partial.volume !== undefined) trackEl.volume = partial.volume;\n if (partial.pan !== undefined) trackEl.pan = partial.pan;\n if (partial.muted !== undefined) {\n if (partial.muted) trackEl.setAttribute('muted', '');\n else trackEl.removeAttribute('muted');\n }\n if (partial.soloed !== undefined) {\n if (partial.soloed) trackEl.setAttribute('soloed', '');\n else trackEl.removeAttribute('soloed');\n }\n if (partial.renderMode !== undefined) trackEl.setAttribute('render-mode', partial.renderMode);\n return;\n }\n // No DOM element — apply directly to descriptor + engine.\n const oldDesc = this._tracks.get(trackId);\n if (!oldDesc) return;\n let normalizedRenderMode = partial.renderMode;\n if (normalizedRenderMode === 'both') {\n console.warn(\n '[dawcore] render-mode=\"both\" is not yet supported; falling back to \\'spectrogram\\''\n );\n normalizedRenderMode = 'spectrogram';\n }\n const newDesc: TrackDescriptor = {\n ...oldDesc,\n ...(partial.name !== undefined && { name: partial.name }),\n ...(partial.volume !== undefined && { volume: partial.volume }),\n ...(partial.pan !== undefined && { pan: partial.pan }),\n ...(partial.muted !== undefined && { muted: partial.muted }),\n ...(partial.soloed !== undefined && { soloed: partial.soloed }),\n ...(normalizedRenderMode !== undefined && { renderMode: normalizedRenderMode }),\n };\n this._tracks = new Map(this._tracks).set(trackId, newDesc);\n if (this._engine) {\n if (partial.volume !== undefined) this._engine.setTrackVolume(trackId, partial.volume);\n if (partial.pan !== undefined) this._engine.setTrackPan(trackId, partial.pan);\n if (partial.muted !== undefined) this._engine.setTrackMute(trackId, partial.muted);\n if (partial.soloed !== undefined) this._engine.setTrackSolo(trackId, partial.soloed);\n }\n }\n /**\n * Append a clip to an existing track. Builds a `<daw-clip>` from `config`\n * and appends it to the track's DOM element when one exists; resolves with\n * the new clip's id once the audio decode + peak generation finish.\n */\n addClip(trackId: string, config: ClipConfig): Promise<string> {\n if (!config.src) {\n return Promise.reject(\n new Error(\n 'addClip: config.src is required — pass a URL to load. ' +\n 'Empty/recording clips are not yet supported via addClip.'\n )\n );\n }\n const trackEl = this._trackElements.get(trackId);\n if (!trackEl) {\n return Promise.reject(\n new Error(\n 'addClip: no <daw-track> element for trackId \"' +\n trackId +\n '\" — addClip currently requires a DOM-backed track. Use editor.addTrack(config) first.'\n )\n );\n }\n const clipEl = this._buildClipElement(config);\n return this._awaitId(\n 'daw-clip-ready',\n 'daw-clip-error',\n (d) => d.clipId === clipEl.clipId,\n clipEl.clipId,\n () => trackEl.appendChild(clipEl)\n );\n }\n /**\n * Remove a clip by id. Removes the matching `<daw-clip>` DOM element when\n * present (MutationObserver handles cleanup); otherwise updates engine\n * state directly. No-op if no matching clip exists.\n */\n removeClip(trackId: string, clipId: string): void {\n const trackEl = this._trackElements.get(trackId);\n if (trackEl) {\n const clipEl = [...trackEl.querySelectorAll('daw-clip')].find(\n (c) => (c as DawClipElement).clipId === clipId\n ) as DawClipElement | undefined;\n if (clipEl) {\n clipEl.remove();\n return;\n }\n }\n if (this._engineTracks.has(trackId)) {\n this._removeClipFromTrack(trackId, clipId);\n return;\n }\n console.warn(\n '[dawcore] removeClip: no track found for id \"' + trackId + '\" (clipId \"' + clipId + '\")'\n );\n }\n /**\n * Update a clip's position (start/duration/offset) or properties (gain/name).\n * For DOM-element clips, writes properties on the `<daw-clip>` element which\n * fires `daw-clip-update`; otherwise applies directly via `_applyClipUpdate`.\n *\n * Re-decoding (changing `src`) is not supported via this method — remove and\n * re-add the clip instead.\n *\n * Note: `fadeIn` / `fadeOut` / `fadeType` on the partial are written to the\n * `<daw-clip>` element (so they round-trip in the descriptor), but engine-side\n * fade application from `<daw-clip>` properties is not yet implemented — see\n * the broader fade-engine integration tracked separately.\n */\n updateClip(trackId: string, clipId: string, partial: Partial<ClipConfig>): void {\n const trackEl = this._trackElements.get(trackId);\n if (trackEl) {\n const clipEl = [...trackEl.querySelectorAll('daw-clip')].find(\n (c) => (c as DawClipElement).clipId === clipId\n ) as DawClipElement | undefined;\n if (clipEl) {\n if (partial.start !== undefined) clipEl.start = partial.start;\n if (partial.duration !== undefined) clipEl.duration = partial.duration;\n if (partial.offset !== undefined) clipEl.offset = partial.offset;\n if (partial.gain !== undefined) clipEl.gain = partial.gain;\n if (partial.name !== undefined) clipEl.setAttribute('name', partial.name);\n if (partial.fadeIn !== undefined) clipEl.fadeIn = partial.fadeIn;\n if (partial.fadeOut !== undefined) clipEl.fadeOut = partial.fadeOut;\n if (partial.fadeType !== undefined) clipEl.setAttribute('fade-type', partial.fadeType);\n return;\n }\n }\n // No DOM element — apply changes directly.\n const t = this._engineTracks.get(trackId);\n if (!t) {\n console.warn('[dawcore] updateClip: no track found for id \"' + trackId + '\"');\n return;\n }\n const idx = t.clips.findIndex((c) => c.id === clipId);\n if (idx === -1) {\n console.warn(\n '[dawcore] updateClip: clip \"' + clipId + '\" not found in track \"' + trackId + '\"'\n );\n return;\n }\n const oldClip = t.clips[idx];\n const sr = oldClip.sampleRate ?? this.effectiveSampleRate;\n const updatedClip = {\n ...oldClip,\n ...(partial.start !== undefined && { startSample: Math.round(partial.start * sr) }),\n ...(partial.duration !== undefined &&\n partial.duration > 0 && { durationSamples: Math.round(partial.duration * sr) }),\n ...(partial.offset !== undefined && { offsetSamples: Math.round(partial.offset * sr) }),\n ...(partial.gain !== undefined && { gain: partial.gain }),\n ...(partial.name !== undefined && { name: partial.name }),\n };\n const updatedClips = [...t.clips];\n updatedClips[idx] = updatedClip;\n const updatedTrack: ClipTrack = { ...t, clips: updatedClips };\n this._engineTracks = new Map(this._engineTracks).set(trackId, updatedTrack);\n this._commitTrackChange(trackId, updatedTrack);\n }\n private _buildClipElement(config: ClipConfig): DawClipElement {\n const clipEl = document.createElement('daw-clip') as DawClipElement;\n if (config.src !== undefined) clipEl.setAttribute('src', config.src);\n if (config.peaksSrc !== undefined) clipEl.setAttribute('peaks-src', config.peaksSrc);\n if (config.start !== undefined) clipEl.start = config.start;\n if (config.duration !== undefined) clipEl.duration = config.duration;\n if (config.offset !== undefined) clipEl.offset = config.offset;\n if (config.gain !== undefined) clipEl.gain = config.gain;\n if (config.name !== undefined) clipEl.setAttribute('name', config.name);\n if (config.fadeIn !== undefined) clipEl.fadeIn = config.fadeIn;\n if (config.fadeOut !== undefined) clipEl.fadeOut = config.fadeOut;\n if (config.fadeType !== undefined) clipEl.setAttribute('fade-type', config.fadeType);\n if (config.midiNotes !== undefined) clipEl.midiNotes = config.midiNotes;\n if (config.midiChannel !== undefined)\n clipEl.setAttribute('midi-channel', String(config.midiChannel));\n if (config.midiProgram !== undefined)\n clipEl.setAttribute('midi-program', String(config.midiProgram));\n return clipEl;\n }\n // --- Playback ---\n async play(startTime?: number) {\n try {\n const engine = await this._ensureEngine();\n // Always init — resumes AudioContext if suspended (requires user gesture).\n await engine.init();\n engine.play(startTime);\n this._startPlayhead();\n this.dispatchEvent(new CustomEvent('daw-play', { bubbles: true, composed: true }));\n } catch (err) {\n console.warn('[dawcore] Playback failed: ' + String(err));\n this.dispatchEvent(\n new CustomEvent<DawErrorDetail>('daw-error', {\n bubbles: true,\n composed: true,\n detail: { operation: 'play', error: err },\n })\n );\n }\n }\n pause() {\n if (!this._engine) return;\n this._engine.pause();\n this._stopPlayhead();\n this.dispatchEvent(new CustomEvent('daw-pause', { bubbles: true, composed: true }));\n }\n stop() {\n if (!this._engine) return;\n this._engine.stop();\n this._stopPlayhead();\n this.dispatchEvent(new CustomEvent('daw-stop', { bubbles: true, composed: true }));\n }\n /** Toggle between play and pause. */\n togglePlayPause() {\n if (this.isRecording) {\n this.togglePauseRecording();\n } else if (this._isPlaying) {\n this.pause();\n } else {\n this.play();\n }\n }\n seekTo(time: number) {\n if (!this._engine) {\n console.warn('[dawcore] seekTo: engine not ready, call ignored');\n return;\n }\n if (this._isPlaying) {\n // Transport needs stop+play to reschedule audio sources at new position\n this.stop();\n this.play(time);\n } else {\n this._engine.seek(time);\n this._currentTime = time;\n this._stopPlayhead();\n }\n }\n\n /** Undo the last structural edit. */\n undo(): void {\n if (!this._engine) {\n console.warn('[dawcore] undo: engine not ready, call ignored');\n return;\n }\n this._engine.undo();\n }\n\n /** Redo the last undone edit. */\n redo(): void {\n if (!this._engine) {\n console.warn('[dawcore] redo: engine not ready, call ignored');\n return;\n }\n this._engine.redo();\n }\n\n /** Whether undo is available. */\n get canUndo(): boolean {\n return this._engine?.canUndo ?? false;\n }\n\n /** Whether redo is available. */\n get canRedo(): boolean {\n return this._engine?.canRedo ?? false;\n }\n\n /** Split the clip under the playhead on the selected track. */\n splitAtPlayhead(): boolean {\n return performSplitAtPlayhead({\n effectiveSampleRate: this.effectiveSampleRate,\n currentTime: this.currentTime,\n isPlaying: this._isPlaying,\n engine: this._engine,\n dispatchEvent: (e: Event) => this.dispatchEvent(e),\n stop: () => {\n this._engine?.stop();\n this._stopPlayhead();\n },\n // Call engine.play directly (synchronous) — not the async editor play()\n // which yields to microtask queue via await engine.init(). Engine is\n // already initialized at split time; the async gap causes audio desync.\n play: (time: number) => {\n this._engine?.play(time);\n this._startPlayhead();\n },\n });\n }\n\n // --- Recording ---\n recordingStream: MediaStream | null = null;\n get currentTime(): number {\n // During playback, read live from engine (not cached _currentTime)\n if (this._isPlaying && this._engine) {\n return this._engine.getCurrentTime();\n }\n return this._currentTime;\n }\n get isRecording(): boolean {\n return this._recordingController.isRecording;\n }\n get isRecordingPaused(): boolean {\n return this._recordingController.isPaused;\n }\n pauseRecording(): void {\n this._recordingController.pauseRecording();\n }\n resumeRecording(): void {\n this._recordingController.resumeRecording();\n // Programmatic resume bypasses togglePauseRecording — clear the\n // ref so the next pause cycle doesn't restart Transport based on\n // stale state from an earlier toggle.\n this._wasPlayingDuringRecording = false;\n }\n /**\n * Audacity-style pause toggle for active recordings: pauses both the\n * worklet capture and (if running) the playback Transport. On resume,\n * Transport restarts only if it was running before — non-overdub\n * recordings stay silent on resume.\n */\n togglePauseRecording(): void {\n if (!this.isRecording) return;\n if (this.isRecordingPaused) {\n // Snapshot the flag before resumeRecording — that method clears\n // the ref defensively (so external callers don't leak it into the\n // next cycle), so reading after the call is too late.\n const wasPlaying = this._wasPlayingDuringRecording;\n this.resumeRecording();\n if (wasPlaying) {\n // play() is async on the editor; fire and forget — caller doesn't await.\n void this.play(this.currentTime);\n }\n } else {\n this.pauseRecording();\n if (this._isPlaying) {\n this._wasPlayingDuringRecording = true;\n this.pause();\n }\n }\n }\n /** Set in togglePauseRecording when Transport is paused alongside the\n * worklet, so resume can restart it. Cleared on resume and on stop. */\n private _wasPlayingDuringRecording = false;\n stopRecording(): Promise<void> {\n this._wasPlayingDuringRecording = false;\n return this._recordingController.stopRecording();\n }\n _addRecordedClip(\n trackId: string,\n buf: AudioBuffer,\n startSample: number,\n durSamples: number,\n offsetSamples = 0\n ) {\n addRecordedClip(this, trackId, buf, startSample, durSamples, offsetSamples);\n }\n // --- RecordingHost bridge methods for cross-context worklet support ---\n // These delegate to the adapter's context type (native or standardized-audio-context).\n // The RecordingController calls these when available, falling back to native APIs.\n\n addWorkletModule(url: string): Promise<void> {\n return (\n this._externalAdapter?.addWorkletModule?.(url) ??\n this.audioContext.audioWorklet.addModule(url)\n );\n }\n\n createAudioWorkletNode(name: string, options?: AudioWorkletNodeOptions): AudioWorkletNode {\n return (\n this._externalAdapter?.createAudioWorkletNode?.(name, options) ??\n new AudioWorkletNode(this.audioContext, name, options)\n );\n }\n\n createMediaStreamSource(stream: MediaStream): MediaStreamAudioSourceNode {\n return (\n this._externalAdapter?.createMediaStreamSource?.(stream) ??\n this.audioContext.createMediaStreamSource(stream)\n );\n }\n\n async startRecording(stream?: MediaStream, options?: RecordingOptions): Promise<void> {\n const s = stream ?? this.recordingStream;\n if (!s) {\n console.warn('[dawcore] startRecording: no stream provided and recordingStream is null');\n return;\n }\n await this._recordingController.startRecording(s, options);\n }\n\n private _renderRecordingPreview(trackId: string, chH: number) {\n const rs = this._recordingController.getSession(trackId);\n if (!rs) return '';\n // Skip latency samples in the preview — they'll be sliced on finalization.\n // Position stays at startSample (same as finalized clip).\n const audibleSamples = Math.max(0, rs.totalSamples - rs.latencySamples);\n if (audibleSamples === 0) return '';\n const renderSpp = this._renderSpp;\n const latencyPixels = Math.floor(rs.latencySamples / renderSpp);\n const left = Math.floor(rs.startSample / renderSpp);\n const w = Math.floor(audibleSamples / renderSpp);\n return rs.peaks.map((chPeaks, ch) => {\n // Slice peaks to skip latency prefix (2 entries per pixel: min/max)\n const slicedPeaks = latencyPixels > 0 ? chPeaks.slice(latencyPixels * 2) : chPeaks;\n return html`\n <daw-waveform\n data-recording-track=${trackId}\n data-recording-channel=${ch}\n style=\"position:absolute;left:${left}px;top:${ch * chH}px;\"\n .peaks=${slicedPeaks}\n .length=${w}\n .waveHeight=${chH}\n .barWidth=${this.barWidth}\n .barGap=${this.barGap}\n .visibleStart=${this._viewport.visibleStart}\n .visibleEnd=${this._viewport.visibleEnd}\n .originX=${left}\n ></daw-waveform>\n `;\n });\n }\n // --- Playhead ---\n _startPlayhead() {\n const playhead = this._getPlayhead();\n if (!playhead || !this._engine) return;\n const engine = this._engine;\n const ctx = this.audioContext;\n // Audible position = engine time - hardware DAC latency - scheduler lookahead.\n // - outputLatency: present on native AudioContext (~3ms Chrome, ~15ms Safari).\n // - engine.lookAhead: proxies adapter.lookAhead (0.1s on Tone-backed adapters,\n // 0 on native). Transport.seconds is the scheduling position, which runs\n // lookAhead ahead of what the listener hears. Without this subtraction the\n // playhead leads audio by ~100ms with the Tone adapter; native is a no-op.\n const audibleTime = (): number => {\n const outputLatency = 'outputLatency' in ctx ? (ctx as AudioContext).outputLatency : 0;\n const t = engine.getCurrentTime() - outputLatency - engine.lookAhead;\n return Number.isFinite(t) ? Math.max(0, t) : 0;\n };\n if (this.scaleMode === 'beats') {\n const secondsToTicksFn = (s: number) => this._secondsToTicks(s);\n playhead.startBeatsAnimationWithMap(audibleTime, secondsToTicksFn, this.ticksPerPixel);\n } else {\n playhead.startAnimation(audibleTime, this.effectiveSampleRate, this.samplesPerPixel);\n }\n }\n _stopPlayhead() {\n const playhead = this._getPlayhead();\n if (!playhead) return;\n // Resting playhead must use audible time so it lines up with where audio\n // actually stopped. Storage stays raw (`_currentTime`) so the next play()\n // resumes correctly — only the visual position is shifted.\n const ctx = this.audioContext;\n const outputLatency = 'outputLatency' in ctx ? (ctx as AudioContext).outputLatency : 0;\n const lookAhead = this._engine?.lookAhead ?? 0;\n const t = this._currentTime - outputLatency - lookAhead;\n const visualTime = Number.isFinite(t) ? Math.max(0, t) : 0;\n if (this.scaleMode === 'beats') {\n playhead.stopBeatsAnimationWithMap(\n visualTime,\n (s: number) => this._secondsToTicks(s),\n this.ticksPerPixel\n );\n } else {\n playhead.stopAnimation(visualTime, this.effectiveSampleRate, this.samplesPerPixel);\n }\n }\n private _getPlayhead(): DawPlayheadElement | null {\n return this.shadowRoot?.querySelector('daw-playhead') as DawPlayheadElement | null;\n }\n\n /** True when the controls column should be rendered (and its selector is valid). */\n private get _showControls(): boolean {\n return this._getOrderedTracks().length > 0 || this.indefinitePlayback;\n }\n\n /** True when the ruler header band should be rendered (and its selector is valid). */\n private get _showRuler(): boolean {\n return (\n (this._getOrderedTracks().length > 0 ||\n this.scaleMode === 'beats' ||\n this.indefinitePlayback) &&\n this.timescale\n );\n }\n\n private _getOrderedTracks(): Array<[string, ClipTrack]> {\n const domOrder: string[] = [...this.querySelectorAll('daw-track')].map(\n (el) => (el as DawTrackElement).trackId\n );\n return [...this._engineTracks.entries()].sort((a, b) => {\n const ai = domOrder.indexOf(a[0]);\n const bi = domOrder.indexOf(b[0]);\n // Both not in DOM (e.g. file drops): preserve Map insertion order\n if (ai === -1 && bi === -1) return 0;\n // Only one not in DOM: sort it after DOM tracks\n if (ai === -1) return 1;\n if (bi === -1) return -1;\n return ai - bi;\n });\n }\n\n // --- Render ---\n render() {\n const sr = this.effectiveSampleRate;\n const spp = this._renderSpp;\n // In beats mode, derive selection pixels from tick space (same as clip positions)\n // to avoid 1-2px quantization error from the sample round-trip.\n let selStartPx: number;\n let selEndPx: number;\n if (this.scaleMode === 'beats') {\n const startTick = this._secondsToTicks(this._selectionStartTime);\n const endTick = this._secondsToTicks(this._selectionEndTime);\n selStartPx = startTick / this.ticksPerPixel;\n selEndPx = endTick / this.ticksPerPixel;\n } else {\n selStartPx = (this._selectionStartTime * sr) / spp;\n selEndPx = (this._selectionEndTime * sr) / spp;\n }\n\n // Precompute track info once for both controls column and timeline\n const orderedTracks = this._getOrderedTracks().map(([trackId, track]) => {\n const descriptor = this._tracks.get(trackId);\n const firstPeaks = track.clips\n .map((c) => this._peaksData.get(c.id))\n .find((p) => p && p.data.length > 0);\n // Use recording session channel count if no finalized clips yet\n const recSession = this._recordingController.getSession(trackId);\n const numChannels = firstPeaks\n ? firstPeaks.data.length\n : recSession\n ? recSession.channelCount\n : 1;\n return {\n trackId,\n track,\n descriptor,\n numChannels,\n trackHeight: this.waveHeight * numChannels + (this.clipHeaders ? this.clipHeaderHeight : 0),\n };\n });\n\n const showControls = this._showControls;\n const showRuler = this._showRuler;\n\n return html`\n ${showRuler\n ? html`<div class=\"header-row\" style=\"height: ${RULER_HEIGHT}px;\">\n ${showControls ? html`<div class=\"ruler-gap\"></div>` : ''}\n <div class=\"ruler-viewport\" @pointerdown=${this._pointer.onPointerDown}>\n <div\n class=\"ruler-content\"\n style=\"width: ${this._totalWidth > 0 ? this._totalWidth + 'px' : '100%'};\"\n >\n <daw-ruler\n .samplesPerPixel=${spp}\n .sampleRate=${this.effectiveSampleRate}\n .duration=${this._duration}\n .scaleMode=${this.scaleMode}\n .ticksPerPixel=${this.ticksPerPixel}\n .meterEntries=${this._meterEntries}\n .ppqn=${this.ppqn}\n .totalWidth=${this._totalWidth}\n .rulerHeight=${RULER_HEIGHT}\n ></daw-ruler>\n </div>\n </div>\n </div>`\n : ''}\n <div class=\"body\">\n ${showControls\n ? html`<div class=\"controls-viewport\">\n <div class=\"controls-column\">\n ${orderedTracks.map(\n (t) => html`\n <daw-track-controls\n style=\"height: ${t.trackHeight}px;\"\n .trackId=${t.trackId}\n .trackName=${t.descriptor?.name ?? 'Untitled'}\n .volume=${t.descriptor?.volume ?? 1}\n .pan=${t.descriptor?.pan ?? 0}\n .muted=${t.descriptor?.muted ?? false}\n .soloed=${t.descriptor?.soloed ?? false}\n ></daw-track-controls>\n `\n )}\n </div>\n </div>`\n : ''}\n <div class=\"scroll-area\">\n <div\n class=\"timeline ${this._dragOver ? 'drag-over' : ''}\"\n style=\"width: ${this._totalWidth > 0 ? this._totalWidth + 'px' : '100%'};\"\n data-playing=${this._isPlaying}\n @pointerdown=${this._pointer.onPointerDown}\n @dragover=${this._onDragOver}\n @dragleave=${this._onDragLeave}\n @drop=${this._onDrop}\n >\n ${this.scaleMode === 'beats'\n ? html`<daw-grid\n style=\"top: 0px;\"\n .ticksPerPixel=${this.ticksPerPixel}\n .meterEntries=${this._meterEntries}\n .ppqn=${this.ppqn}\n .visibleStart=${this._viewport.visibleStart}\n .visibleEnd=${this._viewport.visibleEnd}\n .length=${this._totalWidth}\n .height=${orderedTracks.length > 0\n ? orderedTracks.reduce((sum, t) => sum + t.trackHeight, 0)\n : this._emptyGridHeight}\n ></daw-grid>`\n : ''}\n ${orderedTracks.length > 0 || this.scaleMode === 'beats' || this.indefinitePlayback\n ? html`<daw-selection .startPx=${selStartPx} .endPx=${selEndPx}></daw-selection>\n <daw-playhead></daw-playhead>`\n : ''}\n ${orderedTracks.map((t) => {\n const channelHeight = this.waveHeight;\n return html`\n <div\n class=\"track-row ${t.trackId === this._selectedTrackId ? 'selected' : ''}\"\n style=\"height: ${t.trackHeight}px;\"\n data-track-id=${t.trackId}\n >\n ${t.track.clips.map((clip) => {\n const peakData = this._peaksData.get(clip.id);\n // In beats mode, derive pixel positions from tick space to\n // match grid lines exactly. The sample→spp path introduces\n // 1-2px quantization error from integer sample rounding.\n let clipLeft: number;\n let width: number;\n if (this.scaleMode === 'beats') {\n // Use startTick directly when available — stable across BPM changes.\n // Fall back to sample→seconds→ticks for clips without startTick.\n const startTick =\n clip.startTick !== undefined\n ? clip.startTick\n : this._secondsToTicks(clip.startSample / sr);\n const durSec = clip.durationSamples / sr;\n const startSec =\n clip.startTick !== undefined\n ? this._ticksToSeconds(clip.startTick)\n : clip.startSample / sr;\n const endTick = this._secondsToTicks(startSec + durSec);\n clipLeft = Math.round(startTick / this.ticksPerPixel);\n width = Math.round(endTick / this.ticksPerPixel) - clipLeft;\n } else {\n clipLeft = Math.floor(clip.startSample / spp);\n width = clipPixelWidth(clip.startSample, clip.durationSamples, spp);\n }\n // Per-segment waveform rendering for variable tempo.\n // Uses base-scale (128) peaks directly — segments handle stretching,\n // so no BPM-dependent intermediate resampling needed.\n let clipSegments: WaveformSegment[] | undefined;\n let segmentChannels: Peaks[] | undefined;\n if (this.scaleMode === 'beats' && this.secondsToTicks) {\n const audioBuffer = this._clipBuffers.get(clip.id);\n const basePeaks = audioBuffer\n ? this._peakPipeline.getBaseScalePeaks(\n audioBuffer,\n this.mono,\n clip.offsetSamples,\n clip.durationSamples\n )\n : null;\n if (basePeaks) {\n const baseScale = basePeaks.scale;\n segmentChannels = basePeaks.peaks.data;\n const MIN_RENDER_STEP = 80;\n const stepTicks = Math.max(MIN_RENDER_STEP, Math.ceil(this.ticksPerPixel));\n const startSec =\n clip.startTick !== undefined\n ? this._ticksToSeconds(clip.startTick)\n : clip.startSample / sr;\n const clipOffsetSec = clip.offsetSamples / sr;\n const segStartTick =\n clip.startTick !== undefined\n ? clip.startTick\n : this._secondsToTicks(startSec);\n const endTick = this._secondsToTicks(startSec + clip.durationSamples / sr);\n clipSegments = [];\n for (let tick = segStartTick; tick < endTick; tick += stepTicks) {\n const segEndTick = Math.min(tick + stepTicks, endTick);\n const segStartAudioSec =\n this._ticksToSeconds(tick) - startSec + clipOffsetSec;\n const segEndAudioSec =\n this._ticksToSeconds(segEndTick) - startSec + clipOffsetSec;\n // Peak indices at base scale (128) — clamped to valid range.\n const segStartSample = Math.round(segStartAudioSec * sr);\n const segEndSample = Math.round(segEndAudioSec * sr);\n const totalPeaks = clip.durationSamples / baseScale;\n clipSegments.push({\n peakStart: Math.max(\n 0,\n (segStartSample - clip.offsetSamples) / baseScale\n ),\n peakEnd: Math.min(\n totalPeaks,\n (segEndSample - clip.offsetSamples) / baseScale\n ),\n pixelStart: (tick - segStartTick) / this.ticksPerPixel,\n pixelEnd: (segEndTick - segStartTick) / this.ticksPerPixel,\n });\n }\n }\n }\n const channels: Peaks[] = segmentChannels ??\n peakData?.data ?? [new Int16Array(0)];\n const hdrH = this.clipHeaders ? this.clipHeaderHeight : 0;\n const chH = this.waveHeight;\n return html` <div\n class=\"clip-container\"\n style=\"left:${clipLeft}px;top:0;width:${width}px;height:${t.trackHeight}px;\"\n data-clip-id=${clip.id}\n >\n ${hdrH > 0\n ? html`<div\n class=\"clip-header\"\n data-clip-id=${clip.id}\n data-track-id=${t.trackId}\n ?data-interactive=${this.interactiveClips}\n >\n <span>${clip.name || t.descriptor?.name || ''}</span>\n </div>`\n : ''}\n ${t.descriptor?.renderMode === 'piano-roll'\n ? html`<daw-piano-roll\n style=\"position:absolute;left:0;top:${hdrH}px;\"\n .midiNotes=${clip.midiNotes ?? []}\n .length=${peakData?.length ?? width}\n .waveHeight=${chH * channels.length}\n .samplesPerPixel=${this._renderSpp}\n .sampleRate=${this.effectiveSampleRate}\n .clipOffsetSeconds=${(clip.offsetSamples ?? 0) /\n this.effectiveSampleRate}\n .visibleStart=${this._viewport.visibleStart}\n .visibleEnd=${this._viewport.visibleEnd}\n .originX=${clipLeft}\n ?selected=${t.trackId === this._selectedTrackId}\n ></daw-piano-roll>`\n : t.descriptor?.renderMode === 'spectrogram'\n ? channels.map(\n (_chPeaks, chIdx) =>\n html`<daw-spectrogram\n style=\"position:absolute;left:0;top:${hdrH +\n chIdx * chH}px;height:${chH}px;width:${peakData?.length ??\n width}px;\"\n .clipId=${clip.id}\n .trackId=${t.trackId}\n .channelIndex=${chIdx}\n .length=${peakData?.length ?? width}\n .waveHeight=${chH}\n .samplesPerPixel=${this._renderSpp}\n .sampleRate=${this.effectiveSampleRate}\n .clipOffsetSeconds=${(clip.offsetSamples ?? 0) /\n this.effectiveSampleRate}\n .visibleStart=${this._viewport.visibleStart}\n .visibleEnd=${this._viewport.visibleEnd}\n .originX=${clipLeft}\n ></daw-spectrogram>`\n )\n : channels.map(\n (chPeaks, chIdx) =>\n html` <daw-waveform\n style=\"position:absolute;left:0;top:${hdrH + chIdx * chH}px;\"\n .peaks=${chPeaks}\n .length=${peakData?.length ?? width}\n .waveHeight=${chH}\n .barWidth=${this.barWidth}\n .barGap=${this.barGap}\n .visibleStart=${this._viewport.visibleStart}\n .visibleEnd=${this._viewport.visibleEnd}\n .originX=${clipLeft}\n .segments=${clipSegments}\n ></daw-waveform>`\n )}\n ${this.interactiveClips\n ? html` <div\n class=\"clip-boundary\"\n data-boundary-edge=\"left\"\n data-clip-id=${clip.id}\n data-track-id=${t.trackId}\n ></div>\n <div\n class=\"clip-boundary\"\n data-boundary-edge=\"right\"\n data-clip-id=${clip.id}\n data-track-id=${t.trackId}\n ></div>`\n : ''}\n </div>`;\n })}\n ${this._renderRecordingPreview(t.trackId, channelHeight)}\n </div>\n `;\n })}\n </div>\n </div>\n </div>\n <slot></slot>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-editor': DawEditorElement;\n }\n}\n","import type {\n FadeType,\n MidiNoteData,\n RenderMode,\n SpectrogramConfig,\n} from '@waveform-playlist/core';\n\n// Re-export RenderMode from core as TrackRenderMode for backward compatibility\n// during the migration; consumers should prefer RenderMode directly.\nexport type { RenderMode } from '@waveform-playlist/core';\nexport type TrackRenderMode = RenderMode;\n\nexport interface TrackDescriptor {\n name: string;\n src: string;\n volume: number;\n pan: number;\n muted: boolean;\n soloed: boolean;\n renderMode: TrackRenderMode;\n spectrogramConfig?: SpectrogramConfig | null;\n clips: ClipDescriptor[];\n}\n\n/**\n * Common fields shared by all clip descriptors regardless of source.\n */\ninterface BaseClipDescriptor {\n src: string;\n peaksSrc: string;\n start: number;\n duration: number;\n offset: number;\n gain: number;\n name: string;\n fadeIn: number;\n fadeOut: number;\n fadeType: FadeType;\n midiNotes: MidiNoteData[] | null;\n midiChannel: number | null;\n midiProgram: number | null;\n}\n\n/**\n * A clip descriptor sourced from a `<daw-clip>` DOM element. `clipId` is\n * always set — `<daw-clip>.clipId` is a `crypto.randomUUID()` generated at\n * construction. Engine `clip.id` is aligned with this id in `_loadTrack`\n * and `_loadAndAppendClip` so DOM and engine reference the same clip.\n */\nexport interface DomClipDescriptor extends BaseClipDescriptor {\n kind: 'dom';\n clipId: string;\n}\n\n/**\n * A clip descriptor synthesized from a non-DOM source — file drops, the\n * `<daw-track src>` shorthand fallback, or recording-clip insertion. No\n * `clipId` because there's no DOM element to align with; the engine\n * generates its own id at clip-creation time.\n */\nexport interface DropClipDescriptor extends BaseClipDescriptor {\n kind: 'drop';\n}\n\nexport type ClipDescriptor = DomClipDescriptor | DropClipDescriptor;\n\n/**\n * Type predicate for the `'dom'` discriminator. Use to narrow a\n * `ClipDescriptor` to `DomClipDescriptor` (which has `clipId`) without\n * inline `c.kind === 'dom'` repetition.\n */\nexport function isDomClip(desc: ClipDescriptor): desc is DomClipDescriptor {\n return desc.kind === 'dom';\n}\n\n/**\n * Public input shape for `editor.addTrack(config)`. All fields optional —\n * defaults match the declarative `<daw-track>` defaults.\n */\nexport interface TrackConfig {\n name?: string;\n volume?: number;\n pan?: number;\n muted?: boolean;\n soloed?: boolean;\n renderMode?: TrackRenderMode;\n spectrogramConfig?: SpectrogramConfig | null;\n clips?: ClipConfig[];\n /**\n * Convenience: creates a single piano-roll `<daw-clip>` child with these\n * notes and sets `render-mode=\"piano-roll\"` on the track. Equivalent to\n * passing `{ renderMode: 'piano-roll', clips: [{ midiNotes, midiChannel, midiProgram }] }`.\n * An explicit `renderMode` takes precedence over the inferred `'piano-roll'`.\n *\n * Creation-only — ignored by `updateTrack`. To modify notes after the\n * track is built, use `editor.updateClip(trackId, clipId, { midiNotes })` or\n * mutate the `<daw-clip>` element's `midiNotes` property directly.\n */\n midi?: {\n notes: MidiNoteData[];\n channel?: number;\n program?: number;\n };\n}\n\n/**\n * Public input shape for clips passed via `TrackConfig.clips` or\n * `editor.addClip(trackId, config)`. `src` is optional to support MIDI clips\n * with no audio source. Other fields default to the matching `<daw-clip>`\n * attribute defaults.\n */\nexport interface ClipConfig {\n src?: string;\n peaksSrc?: string;\n start?: number;\n duration?: number;\n offset?: number;\n gain?: number;\n name?: string;\n fadeIn?: number;\n fadeOut?: number;\n fadeType?: FadeType;\n midiNotes?: MidiNoteData[];\n midiChannel?: number;\n midiProgram?: number;\n}\n","/**\n * Inline Web Worker for generating WaveformData binary format from AudioBuffer channels.\n *\n * Uses a Blob URL approach (portable across all bundlers) with the generateWaveformData\n * algorithm adapted from BBC's waveform-data.js (MIT licensed, adapted from Audacity).\n *\n * The worker generates peaks at a base scale (finest zoom level). The main thread\n * then uses WaveformData.resample() for near-instant zoom changes.\n */\n\nimport WaveformData from 'waveform-data';\n\n// ────────────────────────────────────────────────────────────────────────────\n// Worker code (runs inside the Blob URL worker)\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Self-contained worker source.\n * Contains the generateWaveformData function from waveform-data.js/src/waveform-generator.js\n * (MIT License, BBC). Pure JS, zero dependencies.\n */\nconst workerSource = `\n\"use strict\";\n\nvar INT8_MAX = 127;\nvar INT8_MIN = -128;\nvar INT16_MAX = 32767;\nvar INT16_MIN = -32768;\n\nfunction calculateWaveformDataLength(audio_sample_count, scale) {\n var data_length = Math.floor(audio_sample_count / scale);\n var samples_remaining = audio_sample_count - (data_length * scale);\n if (samples_remaining > 0) {\n data_length++;\n }\n return data_length;\n}\n\nfunction generateWaveformData(options) {\n var scale = options.scale;\n var amplitude_scale = options.amplitude_scale;\n var split_channels = options.split_channels;\n var length = options.length;\n var sample_rate = options.sample_rate;\n var channels = options.channels.map(function(channel) {\n return new Float32Array(channel);\n });\n var output_channels = split_channels ? channels.length : 1;\n var header_size = 24;\n var data_length = calculateWaveformDataLength(length, scale);\n var bytes_per_sample = options.bits === 8 ? 1 : 2;\n var total_size = header_size + data_length * 2 * bytes_per_sample * output_channels;\n var buffer = new ArrayBuffer(total_size);\n var data_view = new DataView(buffer);\n\n var scale_counter = 0;\n var offset = header_size;\n\n var min_value = new Array(output_channels);\n var max_value = new Array(output_channels);\n\n for (var channel = 0; channel < output_channels; channel++) {\n min_value[channel] = Infinity;\n max_value[channel] = -Infinity;\n }\n\n var range_min = options.bits === 8 ? INT8_MIN : INT16_MIN;\n var range_max = options.bits === 8 ? INT8_MAX : INT16_MAX;\n\n data_view.setInt32(0, 2, true);\n data_view.setUint32(4, options.bits === 8, true);\n data_view.setInt32(8, sample_rate, true);\n data_view.setInt32(12, scale, true);\n data_view.setInt32(16, data_length, true);\n data_view.setInt32(20, output_channels, true);\n\n for (var i = 0; i < length; i++) {\n var sample = 0;\n\n if (output_channels === 1) {\n for (var ch = 0; ch < channels.length; ++ch) {\n sample += channels[ch][i];\n }\n sample = Math.floor(range_max * sample * amplitude_scale / channels.length);\n\n if (sample < min_value[0]) {\n min_value[0] = sample;\n if (min_value[0] < range_min) {\n min_value[0] = range_min;\n }\n }\n if (sample > max_value[0]) {\n max_value[0] = sample;\n if (max_value[0] > range_max) {\n max_value[0] = range_max;\n }\n }\n }\n else {\n for (var ch2 = 0; ch2 < output_channels; ++ch2) {\n sample = Math.floor(range_max * channels[ch2][i] * amplitude_scale);\n\n if (sample < min_value[ch2]) {\n min_value[ch2] = sample;\n if (min_value[ch2] < range_min) {\n min_value[ch2] = range_min;\n }\n }\n if (sample > max_value[ch2]) {\n max_value[ch2] = sample;\n if (max_value[ch2] > range_max) {\n max_value[ch2] = range_max;\n }\n }\n }\n }\n\n if (++scale_counter === scale) {\n for (var ch3 = 0; ch3 < output_channels; ch3++) {\n if (options.bits === 8) {\n data_view.setInt8(offset++, min_value[ch3]);\n data_view.setInt8(offset++, max_value[ch3]);\n }\n else {\n data_view.setInt16(offset, min_value[ch3], true);\n data_view.setInt16(offset + 2, max_value[ch3], true);\n offset += 4;\n }\n min_value[ch3] = Infinity;\n max_value[ch3] = -Infinity;\n }\n scale_counter = 0;\n }\n }\n\n if (scale_counter > 0) {\n for (var ch4 = 0; ch4 < output_channels; ch4++) {\n if (options.bits === 8) {\n data_view.setInt8(offset++, min_value[ch4]);\n data_view.setInt8(offset++, max_value[ch4]);\n }\n else {\n data_view.setInt16(offset, min_value[ch4], true);\n data_view.setInt16(offset + 2, max_value[ch4], true);\n offset += 4;\n }\n }\n }\n\n return buffer;\n}\n\nself.onmessage = function(e) {\n var msg = e.data;\n try {\n var result = generateWaveformData({\n scale: msg.scale,\n bits: msg.bits,\n amplitude_scale: msg.amplitude_scale,\n split_channels: msg.split_channels,\n length: msg.length,\n sample_rate: msg.sample_rate,\n channels: msg.channels\n });\n self.postMessage({ id: msg.id, buffer: result }, [result]);\n } catch (err) {\n self.postMessage({ id: msg.id, error: err.message || String(err) });\n }\n};\n`;\n\n// ────────────────────────────────────────────────────────────────────────────\n// Promise-based worker API (runs on the main thread)\n// ────────────────────────────────────────────────────────────────────────────\n\nexport interface PeaksWorkerApi {\n generate(params: {\n channels: ArrayBuffer[];\n length: number;\n sampleRate: number;\n scale: number;\n bits: 8 | 16;\n splitChannels: boolean;\n }): Promise<WaveformData>;\n terminate(): void;\n}\n\ninterface PendingEntry {\n resolve: (value: WaveformData) => void;\n reject: (reason: unknown) => void;\n}\n\nexport function createPeaksWorker(): PeaksWorkerApi {\n let worker: Worker;\n try {\n const blob = new Blob([workerSource], { type: 'application/javascript' });\n const url = URL.createObjectURL(blob);\n worker = new Worker(url);\n URL.revokeObjectURL(url);\n } catch (err) {\n // Worker creation can fail in CSP-restricted environments that block blob: URLs.\n console.warn('[dawcore] Failed to create peaks worker (CSP restriction?): ' + String(err));\n return {\n generate() {\n return Promise.reject(\n new Error(\n 'Peaks worker unavailable (CSP may block blob: URLs). Add blob: to worker-src directive.'\n )\n );\n },\n terminate() {\n /* no-op */\n },\n };\n }\n\n const pending = new Map<string, PendingEntry>();\n let terminated = false;\n let idCounter = 0;\n\n worker.onmessage = (e: MessageEvent) => {\n const msg = e.data;\n const entry = pending.get(msg.id);\n if (!entry) {\n console.warn('[dawcore] Received worker message for unknown id: ' + String(msg.id));\n return;\n }\n pending.delete(msg.id);\n\n if (msg.error) {\n entry.reject(new Error(msg.error));\n } else {\n try {\n const waveformData = WaveformData.create(msg.buffer);\n entry.resolve(waveformData);\n } catch (err) {\n entry.reject(err);\n }\n }\n };\n\n worker.onerror = (e: ErrorEvent) => {\n const reason = e.error ?? new Error(e.message);\n console.warn('[dawcore] Peaks worker crashed: ' + String(reason));\n terminated = true;\n worker.terminate();\n for (const [, entry] of pending) {\n entry.reject(reason);\n }\n pending.clear();\n };\n\n return {\n generate(params) {\n if (terminated) return Promise.reject(new Error('Worker terminated'));\n const messageId = String(++idCounter);\n\n return new Promise<WaveformData>((resolve, reject) => {\n pending.set(messageId, { resolve, reject });\n\n worker.postMessage(\n {\n id: messageId,\n scale: params.scale,\n bits: params.bits,\n amplitude_scale: 1.0,\n split_channels: params.splitChannels,\n length: params.length,\n sample_rate: params.sampleRate,\n channels: params.channels,\n },\n params.channels // Transfer ownership\n );\n });\n },\n\n terminate() {\n terminated = true;\n worker.terminate();\n for (const [, entry] of pending) {\n entry.reject(new Error('Worker terminated'));\n }\n pending.clear();\n },\n };\n}\n","/**\n * Utilities for converting WaveformData to PeakData format.\n * Adapted from @waveform-playlist/browser waveformDataLoader.ts.\n */\n\nimport WaveformData from 'waveform-data';\nimport type { PeakData, Peaks } from '@waveform-playlist/core';\n\n/**\n * Slice and resample WaveformData with aligned source indices.\n */\nfunction sliceAndResample(\n waveformData: WaveformData,\n samplesPerPixel: number,\n offsetSamples?: number,\n durationSamples?: number\n): WaveformData | null {\n let processedData = waveformData;\n\n if (offsetSamples !== undefined && durationSamples !== undefined) {\n if (processedData.scale !== samplesPerPixel) {\n const sourceScale = waveformData.scale;\n const ratio = samplesPerPixel / sourceScale;\n\n const targetStart = Math.floor(offsetSamples / samplesPerPixel);\n const targetEnd = Math.ceil((offsetSamples + durationSamples) / samplesPerPixel);\n\n const sourceStart = Math.max(0, Math.floor(targetStart * ratio));\n const sourceEnd = Math.min(waveformData.length, Math.ceil(targetEnd * ratio));\n\n if (sourceStart >= sourceEnd) {\n return null;\n }\n\n processedData = processedData.slice({\n startIndex: sourceStart,\n endIndex: sourceEnd,\n });\n processedData = processedData.resample({ scale: samplesPerPixel });\n } else {\n const startIndex = Math.floor(offsetSamples / samplesPerPixel);\n const endIndex = Math.ceil((offsetSamples + durationSamples) / samplesPerPixel);\n processedData = processedData.slice({ startIndex, endIndex });\n }\n } else if (processedData.scale !== samplesPerPixel) {\n processedData = processedData.resample({ scale: samplesPerPixel });\n }\n\n return processedData;\n}\n\n/**\n * Extract peaks from a WaveformData object, handling all channels, mono merging,\n * slicing, and resampling.\n */\nexport function extractPeaks(\n waveformData: WaveformData,\n samplesPerPixel: number,\n isMono: boolean,\n offsetSamples?: number,\n durationSamples?: number\n): PeakData {\n const processedData = sliceAndResample(\n waveformData,\n samplesPerPixel,\n offsetSamples,\n durationSamples\n );\n\n if (processedData === null) {\n const bits = waveformData.bits as 8 | 16;\n const numChannels = isMono ? 1 : waveformData.channels;\n const emptyData: Peaks[] = Array.from({ length: numChannels }, () =>\n bits === 8 ? new Int8Array(0) : new Int16Array(0)\n );\n return { length: 0, data: emptyData, bits };\n }\n\n const numChannels = processedData.channels;\n const bits = processedData.bits as 8 | 16;\n\n const channelPeaks: Peaks[] = [];\n for (let c = 0; c < numChannels; c++) {\n const channel = processedData.channel(c);\n const minArray = channel.min_array();\n const maxArray = channel.max_array();\n const len = minArray.length;\n\n const peaks: Peaks = bits === 8 ? new Int8Array(len * 2) : new Int16Array(len * 2);\n\n for (let i = 0; i < len; i++) {\n peaks[i * 2] = minArray[i];\n peaks[i * 2 + 1] = maxArray[i];\n }\n channelPeaks.push(peaks);\n }\n\n if (isMono && channelPeaks.length > 1) {\n const weight = 1 / channelPeaks.length;\n const numPeaks = channelPeaks[0].length / 2;\n const monoPeaks: Peaks =\n bits === 8 ? new Int8Array(numPeaks * 2) : new Int16Array(numPeaks * 2);\n\n for (let i = 0; i < numPeaks; i++) {\n let min = 0;\n let max = 0;\n for (let c = 0; c < channelPeaks.length; c++) {\n min += weight * channelPeaks[c][i * 2];\n max += weight * channelPeaks[c][i * 2 + 1];\n }\n monoPeaks[i * 2] = min;\n monoPeaks[i * 2 + 1] = max;\n }\n\n return { length: numPeaks, data: [monoPeaks], bits };\n }\n\n const peakLength = channelPeaks.length > 0 ? channelPeaks[0].length / 2 : 0;\n return { length: peakLength, data: channelPeaks, bits };\n}\n","/**\n * Peak generation pipeline: AudioBuffer → web worker → WaveformData → PeakData.\n *\n * Manages worker lifecycle, WaveformData caching per AudioBuffer (WeakMap),\n * inflight dedup, and peak extraction via resample() for any zoom level\n * coarser than the base scale.\n *\n * The base scale determines the finest zoom level that can be rendered without\n * regenerating. Resampling only works to coarser (larger) scales. Set baseScale\n * to the finest zoom level the user might need.\n */\n\nimport type WaveformData from 'waveform-data';\nimport type { PeakData } from '@waveform-playlist/core';\nimport { createPeaksWorker, type PeaksWorkerApi } from './peaksWorker';\nimport { extractPeaks } from './waveformDataUtils';\n\nexport class PeakPipeline {\n private _worker: PeaksWorkerApi | null = null;\n private _cache = new WeakMap<AudioBuffer, WaveformData>();\n private _inflight = new WeakMap<AudioBuffer, Promise<WaveformData>>();\n private _baseScale: number;\n private _bits: 8 | 16;\n\n constructor(baseScale = 128, bits: 8 | 16 = 16) {\n this._baseScale = baseScale;\n this._bits = bits;\n }\n\n /**\n * Inject externally-loaded WaveformData (e.g., from a .dat file) into the cache.\n * Prevents worker generation for this AudioBuffer on all subsequent calls.\n */\n cacheWaveformData(audioBuffer: AudioBuffer, waveformData: WaveformData): void {\n this._cache.set(audioBuffer, waveformData);\n }\n\n /**\n * Generate PeakData for a clip from its AudioBuffer.\n * Uses cached WaveformData when available; otherwise generates via worker.\n * Worker generates at baseScale (default 128); extractPeaks resamples to the requested zoom.\n */\n async generatePeaks(\n audioBuffer: AudioBuffer,\n samplesPerPixel: number,\n isMono: boolean,\n offsetSamples?: number,\n durationSamples?: number\n ): Promise<PeakData> {\n const waveformData = await this._getWaveformData(audioBuffer);\n const effectiveScale = this._clampScale(waveformData, samplesPerPixel);\n try {\n return extractPeaks(waveformData, effectiveScale, isMono, offsetSamples, durationSamples);\n } catch (err) {\n console.warn('[dawcore] extractPeaks failed: ' + String(err));\n throw err;\n }\n }\n\n /**\n * Re-extract peaks for all clips at a new zoom level using cached WaveformData.\n * Returns a new Map of clipId → PeakData. Clips without cached data are skipped.\n * When the requested scale is finer than cached data, peaks are clamped to the\n * cached scale and a single summary warning is logged.\n */\n reextractPeaks(\n clipBuffers: ReadonlyMap<string, AudioBuffer>,\n samplesPerPixel: number,\n isMono: boolean,\n clipOffsets?: ReadonlyMap<string, { offsetSamples: number; durationSamples: number }>\n ): Map<string, PeakData> {\n const result = new Map<string, PeakData>();\n let clampedCount = 0;\n let clampedScale = 0;\n for (const [clipId, audioBuffer] of clipBuffers) {\n const cached = this._cache.get(audioBuffer);\n if (cached) {\n const effectiveScale = this._clampScale(cached, samplesPerPixel, false);\n if (effectiveScale !== samplesPerPixel) {\n clampedCount++;\n clampedScale = effectiveScale;\n }\n try {\n const offsets = clipOffsets?.get(clipId);\n result.set(\n clipId,\n extractPeaks(\n cached,\n effectiveScale,\n isMono,\n offsets?.offsetSamples,\n offsets?.durationSamples\n )\n );\n } catch (err) {\n console.warn('[dawcore] reextractPeaks failed for clip ' + clipId + ': ' + String(err));\n }\n }\n }\n if (clampedCount > 0) {\n console.warn(\n '[dawcore] Requested zoom ' +\n samplesPerPixel +\n ' spp is finer than pre-computed peaks (' +\n clampedScale +\n ' spp) — ' +\n clampedCount +\n ' clip(s) using available resolution'\n );\n }\n return result;\n }\n\n /**\n * Clamp requested scale to cached WaveformData scale.\n * WaveformData.resample() can only go coarser — if the requested zoom is\n * finer than the cached data, use the cached scale. Set warn=true to log\n * (default); reextractPeaks passes false and logs a single summary instead.\n */\n private _clampScale(waveformData: WaveformData, requestedScale: number, warn = true): number {\n if (requestedScale < waveformData.scale) {\n if (warn) {\n console.warn(\n '[dawcore] Requested zoom ' +\n requestedScale +\n ' spp is finer than pre-computed peaks (' +\n waveformData.scale +\n ' spp) — using available resolution'\n );\n }\n return waveformData.scale;\n }\n return requestedScale;\n }\n\n /**\n * Extract peaks at the base scale from cached WaveformData.\n * Returns null if no cached data exists for this buffer.\n * Used by variable-tempo segments which handle stretching themselves.\n */\n getBaseScalePeaks(\n audioBuffer: AudioBuffer,\n isMono: boolean,\n offsetSamples?: number,\n durationSamples?: number\n ): { peaks: PeakData; scale: number } | null {\n const cached = this._cache.get(audioBuffer);\n if (!cached) return null;\n return {\n peaks: extractPeaks(cached, cached.scale, isMono, offsetSamples, durationSamples),\n scale: cached.scale,\n };\n }\n\n /**\n * Return the coarsest (largest) scale among cached WaveformData entries\n * that correspond to the given clip buffers. Returns 0 if none are cached.\n */\n getMaxCachedScale(clipBuffers: ReadonlyMap<string, AudioBuffer>): number {\n let max = 0;\n for (const audioBuffer of clipBuffers.values()) {\n const cached = this._cache.get(audioBuffer);\n if (cached && cached.scale > max) max = cached.scale;\n }\n return max;\n }\n\n terminate() {\n this._worker?.terminate();\n this._worker = null;\n }\n\n private async _getWaveformData(audioBuffer: AudioBuffer): Promise<WaveformData> {\n const cached = this._cache.get(audioBuffer);\n if (cached) return cached;\n\n const inflight = this._inflight.get(audioBuffer);\n if (inflight) return inflight;\n\n if (!this._worker) {\n this._worker = createPeaksWorker();\n }\n\n // Generate at baseScale — the finest zoom we can resample from\n // .slice() channel buffers to avoid detaching the original AudioBuffer views\n const channels: ArrayBuffer[] = [];\n for (let c = 0; c < audioBuffer.numberOfChannels; c++) {\n channels.push(audioBuffer.getChannelData(c).slice().buffer as ArrayBuffer);\n }\n\n const promise = this._worker\n .generate({\n channels,\n length: audioBuffer.length,\n sampleRate: audioBuffer.sampleRate,\n scale: this._baseScale,\n bits: this._bits,\n splitChannels: true,\n })\n .then((waveformData) => {\n this._cache.set(audioBuffer, waveformData);\n this._inflight.delete(audioBuffer);\n return waveformData;\n })\n .catch((err) => {\n this._inflight.delete(audioBuffer);\n console.warn('[dawcore] Peak generation via worker failed: ' + String(err));\n throw err;\n });\n\n this._inflight.set(audioBuffer, promise);\n return promise;\n }\n}\n","import { LitElement, html, css } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { computeTemporalTicks, type TickData } from '../utils/smart-scale';\nimport { getCachedMusicalTicks } from '../utils/musical-tick-cache';\nimport type { MusicalTickData, MeterEntry } from '@waveform-playlist/core';\n\nconst MAX_CANVAS_WIDTH = 1000;\n\n@customElement('daw-ruler')\nexport class DawRulerElement extends LitElement {\n @property({ type: Number, attribute: false }) samplesPerPixel = 1024;\n @property({ type: Number, attribute: false }) sampleRate = 48000;\n @property({ type: Number, attribute: false }) duration = 0;\n @property({ type: Number, attribute: false }) rulerHeight = 30;\n @property({ type: String, attribute: false }) scaleMode: 'temporal' | 'beats' = 'temporal';\n @property({ type: Number, attribute: false }) ticksPerPixel = 4;\n @property({ attribute: false }) meterEntries: MeterEntry[] = [\n { tick: 0, numerator: 4, denominator: 4 },\n ];\n @property({ type: Number, attribute: false }) ppqn = 960;\n @property({ type: Number, attribute: false }) totalWidth = 0;\n\n private _tickData: TickData | null = null;\n private _musicalTickData: MusicalTickData | null = null;\n\n static styles = css`\n :host {\n display: block;\n position: relative;\n background: var(--daw-ruler-background, #0f0f1a);\n }\n .container {\n position: relative;\n }\n canvas {\n position: absolute;\n top: 0;\n }\n .label {\n position: absolute;\n font-size: 0.7rem;\n line-height: 1;\n white-space: nowrap;\n color: var(--daw-ruler-color, #c49a6c);\n top: 1px;\n }\n .label.centered {\n transform: translateX(-50%);\n }\n `;\n\n willUpdate() {\n if (this.scaleMode === 'beats' && this.totalWidth > 0) {\n this._musicalTickData = getCachedMusicalTicks({\n meterEntries: this.meterEntries,\n ticksPerPixel: this.ticksPerPixel,\n startPixel: 0,\n endPixel: this.totalWidth,\n ppqn: this.ppqn,\n });\n this._tickData = null;\n } else if (this.duration > 0 || this.totalWidth > 0) {\n // Cover the larger of natural duration and viewport-floored width.\n // When indefinite-playback is set, totalWidth can exceed the natural\n // duration's width — use that to keep ruler ticks across the full\n // visible area instead of only the audible portion.\n const widthDerivedDuration = (this.totalWidth * this.samplesPerPixel) / this.sampleRate;\n const effectiveDuration = Math.max(this.duration, widthDerivedDuration);\n this._musicalTickData = null;\n this._tickData = computeTemporalTicks(\n this.samplesPerPixel,\n this.sampleRate,\n effectiveDuration,\n this.rulerHeight\n );\n } else {\n this._musicalTickData = null;\n this._tickData = null;\n }\n }\n\n render() {\n const widthX = this.scaleMode === 'beats' ? this.totalWidth : (this._tickData?.widthX ?? 0);\n if (widthX <= 0) return html``;\n\n const totalChunks = Math.ceil(widthX / MAX_CANVAS_WIDTH);\n const indices = Array.from({ length: totalChunks }, (_, i) => i);\n const dpr = typeof devicePixelRatio !== 'undefined' ? devicePixelRatio : 1;\n\n const beatsLabels =\n this.scaleMode === 'beats' ? (this._musicalTickData?.ticks.filter((t) => t.label) ?? []) : [];\n const temporalLabels = this.scaleMode !== 'beats' ? (this._tickData?.labels ?? []) : [];\n\n return html`\n <div class=\"container\" style=\"width: ${widthX}px; height: ${this.rulerHeight}px;\">\n ${indices.map((i) => {\n const width = Math.min(MAX_CANVAS_WIDTH, widthX - i * MAX_CANVAS_WIDTH);\n return html`\n <canvas\n data-index=${i}\n width=${width * dpr}\n height=${this.rulerHeight * dpr}\n style=\"left: ${i * MAX_CANVAS_WIDTH}px; width: ${width}px; height: ${this\n .rulerHeight}px;\"\n ></canvas>\n `;\n })}\n ${this.scaleMode === 'beats'\n ? beatsLabels.map(\n (t) =>\n html`<span\n class=\"label ${t.pixel > 0 ? 'centered' : ''}\"\n style=\"left: ${t.pixel > 0 ? t.pixel : t.pixel + 4}px;\"\n >${t.label}</span\n >`\n )\n : temporalLabels.map(\n ({ pix, text }) =>\n html`<span class=\"label\" style=\"left: ${pix + 4}px;\">${text}</span>`\n )}\n </div>\n `;\n }\n\n updated() {\n this._drawTicks();\n }\n\n private _drawTicks() {\n const canvases = this.shadowRoot?.querySelectorAll('canvas');\n if (!canvases) return;\n\n const dpr = typeof devicePixelRatio !== 'undefined' ? devicePixelRatio : 1;\n const rulerColor =\n getComputedStyle(this).getPropertyValue('--daw-ruler-color').trim() || '#c49a6c';\n const widthX = this.scaleMode === 'beats' ? this.totalWidth : (this._tickData?.widthX ?? 0);\n\n for (const canvas of canvases) {\n const idx = Number(canvas.dataset.index);\n const ctx = canvas.getContext('2d');\n if (!ctx) continue;\n\n const canvasWidth = Math.min(MAX_CANVAS_WIDTH, widthX - idx * MAX_CANVAS_WIDTH);\n const globalOffset = idx * MAX_CANVAS_WIDTH;\n\n ctx.resetTransform();\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.scale(dpr, dpr);\n ctx.strokeStyle = rulerColor;\n ctx.lineWidth = 1;\n\n if (this.scaleMode === 'beats' && this._musicalTickData) {\n // Ticks draw upward from the bottom. Leave top ~40% for labels.\n // major=60%, minor=35%, minorMinor=15%\n const h = this.rulerHeight;\n for (const tick of this._musicalTickData.ticks) {\n const localX = tick.pixel - globalOffset;\n if (localX < 0 || localX >= canvasWidth) continue;\n\n const tickH =\n tick.type === 'major' ? h * 0.6 : tick.type === 'minor' ? h * 0.35 : h * 0.15;\n\n ctx.globalAlpha = tick.type === 'major' ? 1.0 : 0.5;\n ctx.beginPath();\n ctx.moveTo(localX + 0.5, h);\n ctx.lineTo(localX + 0.5, h - tickH);\n ctx.stroke();\n }\n ctx.globalAlpha = 1.0;\n } else if (this._tickData) {\n for (const [pix, height] of this._tickData.canvasInfo) {\n const localX = pix - globalOffset;\n if (localX < 0 || localX >= canvasWidth) continue;\n\n ctx.beginPath();\n ctx.moveTo(localX + 0.5, this.rulerHeight);\n ctx.lineTo(localX + 0.5, this.rulerHeight - height);\n ctx.stroke();\n }\n }\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-ruler': DawRulerElement;\n }\n}\n","/**\n * Format milliseconds into a m:ss display string.\n */\nexport function formatTime(milliseconds: number): string {\n const seconds = Math.floor(milliseconds / 1000);\n const s = seconds % 60;\n const m = (seconds - s) / 60;\n\n return `${m}:${String(s).padStart(2, '0')}`;\n}\n","import { formatTime } from './time-format';\n\nconst timeinfo = new Map([\n [700, { marker: 1000, bigStep: 500, smallStep: 100 }],\n [1500, { marker: 2000, bigStep: 1000, smallStep: 200 }],\n [2500, { marker: 2000, bigStep: 1000, smallStep: 500 }],\n [5000, { marker: 5000, bigStep: 1000, smallStep: 500 }],\n [10000, { marker: 10000, bigStep: 5000, smallStep: 1000 }],\n [12000, { marker: 15000, bigStep: 5000, smallStep: 1000 }],\n [Infinity, { marker: 30000, bigStep: 10000, smallStep: 5000 }],\n]);\n\nexport function getScaleInfo(samplesPerPixel: number) {\n for (const [resolution, config] of timeinfo) {\n if (samplesPerPixel < resolution) {\n return config;\n }\n }\n return { marker: 30000, bigStep: 10000, smallStep: 5000 };\n}\n\nexport interface TickData {\n /** Map of pixel position → tick height */\n canvasInfo: Map<number, number>;\n /** Labeled ticks with pixel positions */\n labels: Array<{ pix: number; text: string }>;\n /** Total width in pixels */\n widthX: number;\n}\n\n/**\n * Compute temporal tick data for a ruler.\n *\n * Pure function — no DOM dependencies.\n */\nexport function computeTemporalTicks(\n samplesPerPixel: number,\n sampleRate: number,\n duration: number,\n rulerHeight: number\n): TickData {\n const widthX = Math.ceil((duration * sampleRate) / samplesPerPixel);\n const config = getScaleInfo(samplesPerPixel);\n const { marker, bigStep, smallStep } = config;\n const canvasInfo = new Map<number, number>();\n const labels: Array<{ pix: number; text: string }> = [];\n const pixPerSec = sampleRate / samplesPerPixel;\n\n // Iterate with integer counter (milliseconds) to avoid float precision drift.\n // Compute pixel position from counter each iteration instead of accumulating.\n for (let counter = 0; ; counter += smallStep) {\n const pix = Math.floor((counter / 1000) * pixPerSec);\n if (pix >= widthX) break;\n\n if (counter % marker === 0) {\n canvasInfo.set(pix, rulerHeight);\n labels.push({ pix, text: formatTime(counter) });\n } else if (counter % bigStep === 0) {\n canvasInfo.set(pix, Math.floor(rulerHeight / 2));\n } else if (counter % smallStep === 0) {\n canvasInfo.set(pix, Math.floor(rulerHeight / 5));\n }\n }\n\n return { widthX, canvasInfo, labels };\n}\n","import { computeMusicalTicks } from '@waveform-playlist/core';\nimport type { MusicalTickParams, MusicalTickData, MeterEntry } from '@waveform-playlist/core';\n\nlet cachedParams: MusicalTickParams | null = null;\nlet cachedResult: MusicalTickData | null = null;\n\nfunction meterEntriesMatch(\n a: { tick: number; numerator: number; denominator: number }[],\n b: { tick: number; numerator: number; denominator: number }[]\n): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (\n a[i].tick !== b[i].tick ||\n a[i].numerator !== b[i].numerator ||\n a[i].denominator !== b[i].denominator\n )\n return false;\n }\n return true;\n}\n\nfunction paramsMatch(a: MusicalTickParams, b: MusicalTickParams): boolean {\n return (\n a.ticksPerPixel === b.ticksPerPixel &&\n a.startPixel === b.startPixel &&\n a.endPixel === b.endPixel &&\n meterEntriesMatch(a.meterEntries, b.meterEntries) &&\n (a.ppqn ?? 960) === (b.ppqn ?? 960)\n );\n}\n\nexport function getCachedMusicalTicks(params: MusicalTickParams): MusicalTickData {\n if (cachedParams && cachedResult && paramsMatch(cachedParams, params)) {\n return cachedResult;\n }\n cachedResult = computeMusicalTicks(params);\n cachedParams = {\n ...params,\n meterEntries: params.meterEntries.map((e: MeterEntry) => ({ ...e })),\n };\n return cachedResult;\n}\n\nexport function clearMusicalTickCache(): void {\n cachedParams = null;\n cachedResult = null;\n}\n","import { LitElement, html, css } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\n\n@customElement('daw-track-controls')\nexport class DawTrackControlsElement extends LitElement {\n /** Track ID — set by the editor to link controls to a track row. */\n @property({ attribute: false }) trackId: string | null = null;\n @property({ attribute: false }) trackName = '';\n @property({ type: Number, attribute: false }) volume = 1;\n @property({ type: Number, attribute: false }) pan = 0;\n @property({ type: Boolean, attribute: false }) muted = false;\n @property({ type: Boolean, attribute: false }) soloed = false;\n\n static styles = css`\n :host {\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n box-sizing: border-box;\n padding: 6px 8px;\n background: var(--daw-controls-background, #0f0f1a);\n color: var(--daw-controls-text, #c49a6c);\n border-bottom: 1px solid rgba(255, 255, 255, 0.05);\n font-family: system-ui, sans-serif;\n font-size: 11px;\n overflow: hidden;\n container-type: size;\n }\n .header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 4px;\n margin-bottom: 3px;\n }\n .name {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 600;\n font-size: 11px;\n }\n .remove-btn {\n background: none;\n border: none;\n color: var(--daw-controls-text, #c49a6c);\n cursor: pointer;\n padding: 0 2px;\n font-size: 14px;\n line-height: 1;\n opacity: 0.4;\n }\n .remove-btn:hover {\n opacity: 1;\n color: #d08070;\n }\n .buttons {\n display: flex;\n gap: 3px;\n margin-bottom: 3px;\n }\n .btn {\n background: rgba(255, 255, 255, 0.06);\n border: 1px solid rgba(255, 255, 255, 0.1);\n border-radius: 3px;\n color: var(--daw-controls-text, #c49a6c);\n cursor: pointer;\n font-size: 10px;\n font-weight: 600;\n padding: 2px 8px;\n text-align: center;\n }\n .btn:hover {\n background: rgba(255, 255, 255, 0.12);\n }\n .btn.active {\n background: rgba(99, 199, 95, 0.25);\n border-color: rgba(99, 199, 95, 0.5);\n color: #63c75f;\n }\n .btn.muted-active {\n background: rgba(208, 128, 112, 0.25);\n border-color: rgba(208, 128, 112, 0.5);\n color: #d08070;\n }\n .slider-row {\n display: flex;\n align-items: center;\n gap: 4px;\n height: 16px;\n }\n .slider-label {\n width: 50px;\n font-size: 9px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n opacity: 0.6;\n flex-shrink: 0;\n display: flex;\n justify-content: space-between;\n }\n .slider-label-name {\n opacity: 0.5;\n }\n .slider-label-value {\n font-family: 'Courier New', monospace;\n }\n input[type='range'] {\n flex: 1;\n min-width: 0;\n height: 20px;\n margin: 0;\n -webkit-appearance: none;\n appearance: none;\n background: transparent;\n cursor: pointer;\n }\n input[type='range']::-webkit-slider-runnable-track {\n height: 3px;\n background: rgba(255, 255, 255, 0.12);\n border-radius: 2px;\n }\n input[type='range']::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 12px;\n height: 12px;\n border-radius: 50%;\n background: var(--daw-controls-text, #c49a6c);\n margin-top: -4.5px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);\n }\n input[type='range']::-moz-range-track {\n height: 3px;\n background: rgba(255, 255, 255, 0.12);\n border-radius: 2px;\n border: none;\n }\n input[type='range']::-moz-range-thumb {\n width: 12px;\n height: 12px;\n border-radius: 50%;\n background: var(--daw-controls-text, #c49a6c);\n border: none;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);\n }\n /* Compact modes: drop sliders when the row is too short for the full\n stack. Thresholds are CONTENT-BOX heights — the host is border-box\n with 12px vertical padding + 1px border, so an editor-given height H\n enters compact mode at H <= 89px (Pan hidden) and H <= 73px (Vol also\n hidden). NOTE: container-type: size requires an explicit height on\n the host — the editor always provides one; standalone consumers must\n too (see the firstUpdated guard). */\n @container (max-height: 76px) {\n .pan-row {\n display: none;\n }\n }\n @container (max-height: 60px) {\n .vol-row {\n display: none;\n }\n }\n `;\n\n protected firstUpdated(): void {\n requestAnimationFrame(() => {\n if (!this.isConnected) return;\n const rect = this.getBoundingClientRect();\n // width > 0 proves a real layout engine ran (happy-dom reports 0×0,\n // so unit tests stay quiet); height 0 then means size containment\n // collapsed the element because no explicit height was provided.\n if (rect.width > 0 && rect.height === 0) {\n console.warn(\n '[dawcore] <daw-track-controls> has zero height: container-type: size requires an explicit height on the element (the editor sets one automatically; standalone usage must too). The controls are currently invisible.'\n );\n }\n });\n }\n\n private _onVolumeInput = (e: Event) => {\n const value = Number((e.target as HTMLInputElement).value);\n if (Number.isFinite(value)) this._dispatchControl('volume', value);\n };\n\n private _onPanInput = (e: Event) => {\n const value = Number((e.target as HTMLInputElement).value);\n if (Number.isFinite(value)) this._dispatchControl('pan', value);\n };\n\n private _onMuteClick = () => {\n this._dispatchControl('muted', !this.muted);\n };\n\n private _onSoloClick = () => {\n this._dispatchControl('soloed', !this.soloed);\n };\n\n private _onRemoveClick = () => {\n if (!this.trackId) return;\n this.dispatchEvent(\n new CustomEvent('daw-track-remove', {\n bubbles: true,\n composed: true,\n detail: { trackId: this.trackId },\n })\n );\n };\n\n private _dispatchControl(prop: string, value: number | boolean) {\n if (!this.trackId) return;\n this.dispatchEvent(\n new CustomEvent('daw-track-control', {\n bubbles: true,\n composed: true,\n detail: { trackId: this.trackId, prop, value },\n })\n );\n }\n\n render() {\n const volPercent = Math.round(this.volume * 100);\n const panPercent = Math.round(Math.abs(this.pan) * 100);\n const panDisplay = this.pan === 0 ? 'C' : (this.pan > 0 ? 'R' : 'L') + panPercent;\n\n return html`\n <div class=\"header\">\n <span class=\"name\" title=${this.trackName}>${this.trackName || 'Untitled'}</span>\n <button class=\"remove-btn\" @click=${this._onRemoveClick} title=\"Remove track\">\n &times;\n </button>\n </div>\n <div class=\"buttons\">\n <button\n class=\"btn ${this.muted ? 'muted-active' : ''}\"\n @click=${this._onMuteClick}\n title=\"Mute\"\n >\n M\n </button>\n <button class=\"btn ${this.soloed ? 'active' : ''}\" @click=${this._onSoloClick} title=\"Solo\">\n S\n </button>\n </div>\n <div class=\"slider-row vol-row\">\n <span class=\"slider-label\">\n <span class=\"slider-label-name\">Vol</span>\n <span class=\"slider-label-value\">${volPercent}%</span>\n </span>\n <input\n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.01\"\n .value=${String(this.volume)}\n @input=${this._onVolumeInput}\n />\n </div>\n <div class=\"slider-row pan-row\">\n <span class=\"slider-label\">\n <span class=\"slider-label-name\">Pan</span>\n <span class=\"slider-label-value\">${panDisplay}</span>\n </span>\n <input\n type=\"range\"\n min=\"-1\"\n max=\"1\"\n step=\"0.01\"\n .value=${String(this.pan)}\n @input=${this._onPanInput}\n />\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-track-controls': DawTrackControlsElement;\n }\n}\n","import { LitElement, html, css } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { MIN_PIXELS_PER_UNIT } from '@waveform-playlist/core';\nimport type { MusicalTickData, MeterEntry } from '@waveform-playlist/core';\nimport { getCachedMusicalTicks } from '../utils/musical-tick-cache';\nimport { getVisibleChunkIndices } from '../utils/viewport';\n\nconst MAX_CANVAS_WIDTH = 1000;\n\n/**\n * `<daw-grid>` renders a musical grid overlay behind waveforms using the\n * Audacity three-tier model:\n *\n * - **Zebra stripes**: Alternating bar backgrounds at 2% white opacity.\n * - **Major lines** (bars): 10% white opacity, full height.\n * - **Minor lines** (beats): 6% white opacity, full height.\n * - **MinorMinor** (subdivisions): Ruler ticks only — no grid lines.\n *\n * Uses chunked 1000px canvases with virtual scrolling (same as daw-waveform).\n *\n * CSS custom properties:\n * --daw-grid-bar-highlight Alternating bar fill (default: rgba(255,255,255,0.02))\n * --daw-grid-major-line Bar line color (default: rgba(255,255,255,0.1))\n * --daw-grid-minor-line Beat line color (default: rgba(255,255,255,0.06))\n */\n@customElement('daw-grid')\nexport class DawGridElement extends LitElement {\n @property({ type: Number, attribute: false }) ticksPerPixel = 24;\n @property({ attribute: false }) meterEntries: MeterEntry[] = [\n { tick: 0, numerator: 4, denominator: 4 },\n ];\n @property({ type: Number, attribute: false }) ppqn = 960;\n @property({ type: Number, attribute: false }) visibleStart = -Infinity;\n @property({ type: Number, attribute: false }) visibleEnd = Infinity;\n @property({ type: Number, attribute: false }) length = 0;\n @property({ type: Number, attribute: false }) height = 200;\n\n private _tickData: MusicalTickData | null = null;\n\n static styles = css`\n :host {\n display: block;\n position: absolute;\n top: 0;\n left: 0;\n pointer-events: none;\n z-index: 0;\n }\n .container {\n position: relative;\n }\n canvas {\n position: absolute;\n top: 0;\n }\n `;\n\n willUpdate() {\n if (this.length > 0) {\n this._tickData = getCachedMusicalTicks({\n ticksPerPixel: this.ticksPerPixel,\n meterEntries: this.meterEntries,\n ppqn: this.ppqn,\n startPixel: 0,\n endPixel: this.length,\n });\n } else {\n this._tickData = null;\n }\n }\n\n render() {\n if (!this._tickData) return html``;\n\n const totalWidth = this.length;\n const dpr = typeof devicePixelRatio !== 'undefined' ? devicePixelRatio : 1;\n const indices = getVisibleChunkIndices(\n totalWidth,\n MAX_CANVAS_WIDTH,\n this.visibleStart,\n this.visibleEnd\n );\n\n return html`\n <div class=\"container\" style=\"width: ${totalWidth}px; height: ${this.height}px;\">\n ${indices.map((i) => {\n const width = Math.min(MAX_CANVAS_WIDTH, totalWidth - i * MAX_CANVAS_WIDTH);\n return html`\n <canvas\n data-index=${i}\n width=${width * dpr}\n height=${this.height * dpr}\n style=\"left: ${i * MAX_CANVAS_WIDTH}px; width: ${width}px; height: ${this.height}px;\"\n ></canvas>\n `;\n })}\n </div>\n `;\n }\n\n updated() {\n this._drawGrid();\n }\n\n private _drawGrid() {\n if (!this._tickData) return;\n\n const canvases = this.shadowRoot?.querySelectorAll('canvas');\n if (!canvases) return;\n\n const dpr = typeof devicePixelRatio !== 'undefined' ? devicePixelRatio : 1;\n const style = getComputedStyle(this);\n const barHighlight =\n style.getPropertyValue('--daw-grid-bar-highlight').trim() || 'rgba(255,255,255,0.02)';\n const majorLine =\n style.getPropertyValue('--daw-grid-major-line').trim() || 'rgba(255,255,255,0.1)';\n const minorLine =\n style.getPropertyValue('--daw-grid-minor-line').trim() || 'rgba(255,255,255,0.06)';\n\n const { ticks, pixelsPerQuarterNote } = this._tickData;\n\n for (const canvas of canvases) {\n const idx = Number(canvas.dataset.index);\n const ctx = canvas.getContext('2d');\n if (!ctx) continue;\n\n const chunkLeft = idx * MAX_CANVAS_WIDTH;\n const canvasWidth = Math.min(MAX_CANVAS_WIDTH, this.length - chunkLeft);\n\n ctx.resetTransform();\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.scale(dpr, dpr);\n\n // Zebra stripes: use barIndex from tick data for exact alignment with grid lines.\n // Draw a stripe from each odd-barIndex major tick to the next major tick.\n // Threshold: show stripes when a 4-beat bar would be >= MIN_PIXELS_PER_UNIT wide.\n if (pixelsPerQuarterNote * 4 >= MIN_PIXELS_PER_UNIT) {\n ctx.fillStyle = barHighlight;\n const majorTicks = ticks.filter((t) => t.type === 'major');\n for (let i = 0; i < majorTicks.length; i++) {\n if (majorTicks[i].barIndex % 2 === 1) {\n const x = majorTicks[i].pixel - chunkLeft;\n // Use actual next major tick pixel for exact bar width (handles variable meter).\n // Last bar: use the last meter entry's numerator for correct width.\n const lastMeter = this.meterEntries[this.meterEntries.length - 1];\n const lastBarWidth =\n pixelsPerQuarterNote * lastMeter.numerator * (4 / lastMeter.denominator);\n const nextX =\n i + 1 < majorTicks.length ? majorTicks[i + 1].pixel - chunkLeft : x + lastBarWidth;\n ctx.fillRect(x, 0, nextX - x, this.height);\n }\n }\n }\n\n // Grid lines: major (bars) and minor (beats) only.\n // Subdivisions (minorMinor) get ruler ticks but no grid lines.\n ctx.lineWidth = 1;\n for (const tick of ticks) {\n if (tick.type === 'minorMinor') continue;\n\n const localX = tick.pixel - chunkLeft;\n if (localX < 0 || localX >= canvasWidth) continue;\n\n ctx.strokeStyle = tick.type === 'major' ? majorLine : minorLine;\n ctx.beginPath();\n ctx.moveTo(localX + 0.5, 0);\n ctx.lineTo(localX + 0.5, this.height);\n ctx.stroke();\n }\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-grid': DawGridElement;\n }\n}\n","import { css } from 'lit';\n\n/**\n * Default CSS custom properties for dawcore elements.\n * Consumers override these on <daw-editor> or any ancestor.\n * Values inherit through Shadow DOM boundaries automatically.\n */\nexport const hostStyles = css`\n :host {\n --daw-wave-color: #c49a6c;\n --daw-progress-color: #63c75f;\n --daw-playhead-color: #d08070;\n --daw-background: #1a1a2e;\n --daw-track-background: #16213e;\n --daw-ruler-color: #c49a6c;\n --daw-ruler-background: #0f0f1a;\n --daw-controls-background: #1a1a2e;\n --daw-controls-text: #e0d4c8;\n --daw-selection-color: rgba(99, 199, 95, 0.3);\n --daw-clip-header-background: rgba(0, 0, 0, 0.4);\n --daw-clip-header-text: #e0d4c8;\n }\n`;\n\n/** Clip container and header styles for the editor timeline. */\nexport const clipStyles = css`\n .clip-container {\n position: absolute;\n overflow: hidden;\n }\n .clip-header {\n position: relative;\n z-index: 1;\n height: 20px;\n background: var(--daw-clip-header-background, rgba(0, 0, 0, 0.4));\n border-bottom: 1px solid rgba(255, 255, 255, 0.08);\n display: flex;\n align-items: center;\n padding: 0 6px;\n user-select: none;\n -webkit-user-drag: none;\n }\n .clip-header span {\n font-size: 10px;\n font-weight: 500;\n letter-spacing: 0.02em;\n font-family: system-ui, sans-serif;\n color: var(--daw-clip-header-text, #e0d4c8);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n opacity: 0.8;\n }\n .clip-boundary {\n position: absolute;\n top: 0;\n width: 8px;\n height: 100%;\n z-index: 2;\n cursor: col-resize;\n background: transparent;\n border: none;\n touch-action: none;\n user-select: none;\n -webkit-user-drag: none;\n transition: background 0.1s, border-color 0.1s;\n }\n .clip-boundary[data-boundary-edge='left'] {\n left: 0;\n }\n .clip-boundary[data-boundary-edge='right'] {\n right: 0;\n }\n .clip-boundary[data-boundary-edge='left']:hover {\n background: rgba(255, 255, 255, 0.2);\n border-left: 2px solid rgba(255, 255, 255, 0.5);\n }\n .clip-boundary[data-boundary-edge='right']:hover {\n background: rgba(255, 255, 255, 0.2);\n border-right: 2px solid rgba(255, 255, 255, 0.5);\n }\n .clip-boundary[data-boundary-edge='left'].dragging {\n background: rgba(255, 255, 255, 0.4);\n border-left: 2px solid rgba(255, 255, 255, 0.8);\n }\n .clip-boundary[data-boundary-edge='right'].dragging {\n background: rgba(255, 255, 255, 0.4);\n border-right: 2px solid rgba(255, 255, 255, 0.8);\n }\n .clip-header[data-interactive] {\n cursor: grab;\n }\n .clip-header[data-interactive]:active {\n cursor: grabbing;\n }\n`;\n","import type { ReactiveController, ReactiveControllerHost } from 'lit';\nimport { getVisibleChunkIndices } from '../utils/viewport';\n\nexport { getVisibleChunkIndices };\n\nconst OVERSCAN_MULTIPLIER = 1.5;\nconst SCROLL_THRESHOLD = 100;\n\nexport class ViewportController implements ReactiveController {\n private _host: ReactiveControllerHost & HTMLElement;\n private _scrollContainer: HTMLElement | null = null;\n private _lastScrollLeft = 0;\n private _resizeObserver: ResizeObserver | null = null;\n\n // Permissive defaults: render everything until scroll container is attached\n visibleStart = -Infinity;\n visibleEnd = Infinity;\n containerWidth = 0;\n\n constructor(host: ReactiveControllerHost & HTMLElement) {\n this._host = host;\n host.addController(this);\n }\n\n /** CSS selector for the scroll container inside the host's Shadow DOM. */\n scrollSelector = '';\n\n hostConnected() {\n // Defer to allow Shadow DOM to render before querying\n requestAnimationFrame(() => {\n if (!this._host.isConnected) return;\n const container = this.scrollSelector\n ? (this._host.shadowRoot?.querySelector(this.scrollSelector) as HTMLElement)\n : this._host;\n if (container) {\n this._attachScrollContainer(container);\n } else if (this.scrollSelector) {\n console.warn(\n '[dawcore] ViewportController: scroll container not found for \"' +\n this.scrollSelector +\n '\"'\n );\n }\n });\n }\n\n hostDisconnected() {\n this._scrollContainer?.removeEventListener('scroll', this._onScroll);\n this._resizeObserver?.disconnect();\n this._resizeObserver = null;\n this._scrollContainer = null;\n }\n\n private _attachScrollContainer(container: HTMLElement) {\n this._scrollContainer?.removeEventListener('scroll', this._onScroll);\n this._resizeObserver?.disconnect();\n this._scrollContainer = container;\n container.addEventListener('scroll', this._onScroll, { passive: true });\n this._update(container.scrollLeft, container.clientWidth);\n if (typeof ResizeObserver !== 'undefined') {\n this._resizeObserver = new ResizeObserver(() => {\n if (!this._scrollContainer) return;\n const next = this._scrollContainer.clientWidth;\n if (next === this.containerWidth) return;\n this._update(this._scrollContainer.scrollLeft, next);\n this._host.requestUpdate();\n });\n this._resizeObserver.observe(container);\n }\n this._host.requestUpdate();\n }\n\n private _onScroll = () => {\n if (!this._scrollContainer) return;\n const { scrollLeft, clientWidth } = this._scrollContainer;\n if (Math.abs(scrollLeft - this._lastScrollLeft) >= SCROLL_THRESHOLD) {\n this._update(scrollLeft, clientWidth);\n this._host.requestUpdate();\n }\n };\n\n private _update(scrollLeft: number, containerWidth: number) {\n this._lastScrollLeft = scrollLeft;\n this.containerWidth = containerWidth;\n const buffer = containerWidth * OVERSCAN_MULTIPLIER;\n this.visibleStart = scrollLeft - buffer;\n this.visibleEnd = scrollLeft + containerWidth + buffer;\n }\n}\n","import type { ReactiveController, ReactiveControllerHost } from 'lit';\n\nexport interface AudioResumeHost extends ReactiveControllerHost, HTMLElement {\n /** Returns the AudioContext to resume on user gesture */\n readonly audioContext: AudioContext;\n}\n\nexport class AudioResumeController implements ReactiveController {\n private _host: AudioResumeHost;\n private _target: EventTarget | null = null;\n private _attached = false;\n private _generation = 0;\n\n /** CSS selector, or 'document'. When undefined, controller is inert. */\n target?: string;\n\n constructor(host: AudioResumeHost) {\n this._host = host;\n host.addController(this);\n }\n\n hostConnected() {\n // Defer to next frame so Lit's willUpdate() can set `target` from\n // the host's attribute before we read it. Same pattern as ViewportController.\n const gen = ++this._generation;\n requestAnimationFrame(() => {\n if (gen !== this._generation) return; // stale callback from previous connect\n if (!this._host.isConnected || this._attached || this.target === undefined) return;\n\n let resolvedTarget: EventTarget | null;\n try {\n resolvedTarget = this._resolveTarget();\n } catch (err) {\n console.warn(\n '[dawcore] AudioResumeController: failed to resolve target \"' +\n this.target +\n '\": ' +\n String(err)\n );\n return;\n }\n if (!resolvedTarget) return;\n\n this._target = resolvedTarget;\n this._attached = true;\n resolvedTarget.addEventListener('pointerdown', this._onGesture, {\n once: true,\n capture: true,\n });\n resolvedTarget.addEventListener('keydown', this._onGesture, {\n once: true,\n capture: true,\n });\n });\n }\n\n hostDisconnected() {\n this._removeListeners();\n this._attached = false;\n }\n\n private _onGesture = (e: Event) => {\n let ctx: AudioContext;\n try {\n ctx = this._host.audioContext;\n } catch (err) {\n if (err instanceof Error && err.message.includes('No PlayoutAdapter set')) {\n // No adapter set yet — nothing to resume. Will be resumed on first play().\n this._removeListeners();\n return;\n }\n console.warn(\n '[dawcore] AudioResumeController: unexpected error accessing audioContext: ' + String(err)\n );\n this._removeListeners();\n return;\n }\n if (ctx.state === 'closed') {\n console.warn('[dawcore] AudioResumeController: AudioContext is closed, cannot resume.');\n } else if (ctx.state === 'suspended') {\n ctx.resume().catch((err) => {\n console.warn(\n '[dawcore] AudioResumeController: eager resume failed, will retry on play: ' + String(err)\n );\n });\n }\n // Remove the other listener (the fired one was auto-removed by { once: true })\n const otherType = e.type === 'pointerdown' ? 'keydown' : 'pointerdown';\n this._target?.removeEventListener(otherType, this._onGesture, {\n capture: true,\n });\n this._target = null;\n };\n\n private _resolveTarget(): EventTarget | null {\n const t = this.target;\n if (t === undefined) return null;\n if (t === '') return this._host;\n if (t === 'document') return document;\n\n const el = document.querySelector(t);\n if (!el) {\n console.warn(\n '[dawcore] AudioResumeController: target \"' +\n t +\n '\" not found in DOM at attach time, falling back to host element. ' +\n 'Ensure the target exists before <daw-editor> connects.'\n );\n return this._host;\n }\n return el;\n }\n\n private _removeListeners() {\n if (!this._target) return;\n this._target.removeEventListener('pointerdown', this._onGesture, {\n capture: true,\n });\n this._target.removeEventListener('keydown', this._onGesture, {\n capture: true,\n });\n this._target = null;\n }\n}\n","import type { ReactiveController, ReactiveControllerHost } from 'lit';\nimport type { Bits } from '@waveform-playlist/core';\nimport type { DawWaveformElement } from '../elements/daw-waveform';\nimport { appendPeaks, concatenateAudioData, createAudioBuffer } from '@waveform-playlist/core';\nimport type {\n DawRecordingStartDetail,\n DawRecordingCompleteDetail,\n DawRecordingErrorDetail,\n DawRecordingPauseDetail,\n DawRecordingResumeDetail,\n} from '../events';\n\nexport interface RecordingOptions {\n trackId?: string;\n bits?: 8 | 16;\n /** Fallback channel count when stream doesn't report one via getSettings(). Must be 1 or 2. */\n channelCount?: 1 | 2;\n startSample?: number;\n /** Start playback during recording so user hears existing tracks. */\n overdub?: boolean;\n}\n\nexport interface RecordingSession {\n readonly trackId: string;\n readonly stream: MediaStream;\n readonly source: { disconnect(): void; connect(dest: unknown): void };\n readonly workletNode: { port: MessagePort; disconnect(): void };\n readonly chunks: Float32Array[][];\n totalSamples: number;\n readonly peaks: (Int8Array | Int16Array)[];\n readonly startSample: number;\n readonly channelCount: number;\n readonly bits: Bits;\n isFirstMessage: boolean;\n /** Latency samples to skip in live preview (outputLatency only). */\n readonly latencySamples: number;\n readonly wasOverdub: boolean;\n /** Stored so it can be removed on stop/cleanup — not just when stream ends. */\n readonly _onTrackEnded: (() => void) | null;\n readonly _audioTrack: MediaStreamTrack | null;\n /** Set during stopRecording, cleared by the done message from the worklet. */\n stopAckResolve: (() => void) | null;\n /** True from the start of stopRecording until the session is deleted.\n * Guards pauseRecording / resumeRecording so a mid-drain pause toggle\n * can't dispatch events for a session that's already terminating. */\n stopping: boolean;\n}\n\n/** Readonly view of a recording session for external consumers. */\nexport type ReadonlyRecordingSession = Readonly<\n Omit<RecordingSession, 'chunks' | 'peaks' | '_onTrackEnded' | '_audioTrack' | 'latencySamples'>\n> & {\n readonly latencySamples: number;\n readonly chunks: ReadonlyArray<ReadonlyArray<Float32Array>>;\n readonly peaks: ReadonlyArray<Int8Array | Int16Array>;\n};\n\n/** Narrow interface for the host editor. */\nexport interface RecordingHost extends ReactiveControllerHost {\n readonly audioContext: AudioContext;\n readonly samplesPerPixel: number;\n readonly effectiveSampleRate: number;\n readonly _selectedTrackId: string | null;\n readonly _currentTime: number;\n readonly shadowRoot: ShadowRoot | null;\n resolveAudioContextSampleRate(rate: number): void;\n _addRecordedClip?(\n trackId: string,\n buf: AudioBuffer,\n startSample: number,\n durSamples: number,\n offsetSamples?: number\n ): void;\n play?(startTime?: number): Promise<void>;\n stop?(): void;\n dispatchEvent(event: Event): boolean;\n /**\n * Register a worklet module URL on the adapter's AudioContext.\n * Abstracts native vs standardized-audio-context differences.\n * When absent, falls back to native audioContext.audioWorklet.addModule().\n */\n addWorkletModule?(url: string): Promise<void>;\n /**\n * Create an AudioWorkletNode on the adapter's context.\n * When absent, falls back to new AudioWorkletNode(audioContext, ...).\n */\n createAudioWorkletNode?(name: string, options?: AudioWorkletNodeOptions): AudioWorkletNode;\n /**\n * Create a MediaStreamSource on the adapter's context.\n * When absent, falls back to audioContext.createMediaStreamSource().\n */\n createMediaStreamSource?(stream: MediaStream): MediaStreamAudioSourceNode;\n}\n\nexport class RecordingController implements ReactiveController {\n private _host: RecordingHost & HTMLElement;\n private _sessions = new Map<string, RecordingSession>();\n private _workletLoadedCtx: AudioContext | null = null;\n /** Tracks worklet pause state explicitly so external consumers (editor,\n * pause button, spacebar) can share one source of truth. */\n private _isPaused = false;\n\n constructor(host: RecordingHost & HTMLElement) {\n this._host = host;\n host.addController(this);\n }\n\n hostConnected() {}\n\n hostDisconnected() {\n for (const trackId of [...this._sessions.keys()]) {\n this._cleanupSession(trackId);\n }\n this._workletLoadedCtx = null;\n }\n\n get isRecording(): boolean {\n return this._sessions.size > 0;\n }\n\n get isPaused(): boolean {\n return this._isPaused && this._sessions.size > 0;\n }\n\n getSession(trackId: string): ReadonlyRecordingSession | undefined {\n return this._sessions.get(trackId);\n }\n\n async startRecording(stream: MediaStream, options: RecordingOptions = {}): Promise<void> {\n const trackId = options.trackId ?? this._host._selectedTrackId;\n if (!trackId) {\n console.warn('[dawcore] RecordingController: No track selected for recording');\n return;\n }\n if (this._sessions.has(trackId)) {\n console.warn('[dawcore] RecordingController: Already recording on track \"' + trackId + '\"');\n return;\n }\n\n const bits: Bits = options.bits ?? 16;\n\n try {\n const rawCtx = this._host.audioContext;\n\n // Resolve editor sample rate from AudioContext before computing startSample\n this._host.resolveAudioContextSampleRate(rawCtx.sampleRate);\n\n // Load worklet module — guard tied to context identity so a\n // swapped AudioContext gets the module re-registered.\n if (!this._workletLoadedCtx || this._workletLoadedCtx !== rawCtx) {\n let addRecordingWorkletModule: (addModule: (url: string) => Promise<void>) => Promise<void>;\n try {\n ({ addRecordingWorkletModule } = await import('@waveform-playlist/worklets'));\n } catch (importErr) {\n throw new Error(\n 'Recording requires @waveform-playlist/worklets. ' +\n 'Install it: npm install @waveform-playlist/worklets' +\n ' (cause: ' +\n String(importErr) +\n ')'\n );\n }\n const addModule = this._host.addWorkletModule\n ? (url: string) => this._host.addWorkletModule!(url)\n : (url: string) => rawCtx.audioWorklet.addModule(url);\n await addRecordingWorkletModule(addModule);\n this._workletLoadedCtx = rawCtx;\n }\n\n // Detect channel count from stream (not source.channelCount — defaults to 2 per spec).\n // Fall back to user-provided channelCount option, then 1.\n const detectedChannelCount = stream.getAudioTracks()[0]?.getSettings()?.channelCount;\n if (detectedChannelCount === undefined && options.channelCount !== undefined) {\n console.warn(\n '[dawcore] Could not detect stream channel count, using fallback: ' + options.channelCount\n );\n }\n const channelCount = detectedChannelCount ?? options.channelCount ?? 1;\n\n const startSample =\n options.startSample ?? Math.floor(this._host._currentTime * this._host.effectiveSampleRate);\n\n // Compute latency offset once at start (doesn't change during session)\n const outputLatency = rawCtx.outputLatency ?? 0;\n const latencySamples = Math.floor(outputLatency * rawCtx.sampleRate);\n\n // Use host methods when available (supports standardized-audio-context),\n // fall back to native AudioContext methods.\n const source = this._host.createMediaStreamSource\n ? this._host.createMediaStreamSource(stream)\n : rawCtx.createMediaStreamSource(stream);\n const workletNode = this._host.createAudioWorkletNode\n ? this._host.createAudioWorkletNode('recording-processor', {\n channelCount,\n channelCountMode: 'explicit' as globalThis.ChannelCountMode,\n })\n : new AudioWorkletNode(rawCtx, 'recording-processor', {\n channelCount,\n channelCountMode: 'explicit' as globalThis.ChannelCountMode,\n });\n\n // Listen on MediaStreamTrack (not MediaStream — MediaStream has no 'ended' event)\n const audioTrack = stream.getAudioTracks()[0] ?? null;\n const onTrackEnded = audioTrack\n ? () => {\n if (this._sessions.has(trackId)) {\n this.stopRecording(trackId);\n }\n }\n : null;\n\n const session: RecordingSession = {\n trackId,\n stream,\n source,\n workletNode,\n chunks: Array.from({ length: channelCount }, () => []),\n totalSamples: 0,\n peaks: Array.from({ length: channelCount }, () =>\n bits === 8 ? new Int8Array(0) : new Int16Array(0)\n ),\n startSample,\n channelCount,\n bits,\n isFirstMessage: true,\n latencySamples,\n wasOverdub: options.overdub ?? false,\n _onTrackEnded: onTrackEnded,\n _audioTrack: audioTrack,\n stopAckResolve: null,\n stopping: false,\n };\n this._sessions.set(trackId, session);\n\n // dawcore CLAUDE.md: wire onmessage BEFORE source.connect() and postMessage start\n workletNode.port.onmessage = (e: MessageEvent) => {\n this._onWorkletMessage(trackId, e.data);\n };\n source.connect(workletNode);\n workletNode.port.postMessage({ command: 'start', channelCount });\n\n // Attach mic-unplug listener (stored in session for cleanup)\n if (audioTrack && onTrackEnded) {\n audioTrack.addEventListener('ended', onTrackEnded);\n }\n\n this._host.dispatchEvent(\n new CustomEvent<DawRecordingStartDetail>('daw-recording-start', {\n bubbles: true,\n composed: true,\n detail: { trackId, stream },\n })\n );\n\n this._host.requestUpdate();\n\n // Overdub: start playback so user hears existing tracks and playhead advances\n if (options.overdub && typeof this._host.play === 'function') {\n await this._host.play(this._host._currentTime);\n }\n } catch (err) {\n // Clean up partially-created session to prevent stuck isRecording state\n this._cleanupSession(trackId);\n console.warn('[dawcore] RecordingController: Failed to start recording: ' + String(err));\n this._host.dispatchEvent(\n new CustomEvent<DawRecordingErrorDetail>('daw-recording-error', {\n bubbles: true,\n composed: true,\n detail: { trackId, error: err },\n })\n );\n }\n }\n\n pauseRecording(trackId?: string): void {\n const id = trackId ?? [...this._sessions.keys()][0];\n if (!id) return;\n const session = this._sessions.get(id);\n // Skip if the session is already on its way out — would otherwise\n // dispatch a pause event for a session about to be deleted.\n if (!session || session.stopping) return;\n session.workletNode.port.postMessage({ command: 'pause' });\n this._isPaused = true;\n this._host.dispatchEvent(\n new CustomEvent<DawRecordingPauseDetail>('daw-recording-pause', {\n bubbles: true,\n composed: true,\n detail: { trackId: id },\n })\n );\n }\n\n resumeRecording(trackId?: string): void {\n const id = trackId ?? [...this._sessions.keys()][0];\n if (!id) return;\n const session = this._sessions.get(id);\n if (!session || session.stopping) return;\n session.workletNode.port.postMessage({ command: 'resume' });\n this._isPaused = false;\n this._host.dispatchEvent(\n new CustomEvent<DawRecordingResumeDetail>('daw-recording-resume', {\n bubbles: true,\n composed: true,\n detail: { trackId: id },\n })\n );\n }\n\n async stopRecording(trackId?: string): Promise<void> {\n const id = trackId ?? [...this._sessions.keys()][0];\n if (!id) return;\n\n const session = this._sessions.get(id);\n if (!session) return;\n\n // Capture whether we were paused — pause already flushed the partial\n // buffer, so there's nothing to wait for from the worklet on stop.\n const wasPaused = this._isPaused;\n this._isPaused = false;\n // Block any further pause/resume calls on this session — they could\n // race with the drain loop and dispatch events for a session that's\n // about to be deleted.\n session.stopping = true;\n\n // Stop playback only if this was an overdub session\n if (session.wasOverdub && typeof this._host.stop === 'function') {\n this._host.stop();\n }\n\n // Send stop. If the recording was already paused, pause's flushBuffers()\n // already drained the partial buffer — no need to await the terminal\n // message (and the worklet's audio thread may have throttled back while\n // paused, making the round-trip slow / unreliable).\n if (wasPaused) {\n session.workletNode.port.postMessage({ command: 'stop' });\n } else {\n // Active recording: await the worklet's terminal flush so the partial\n // buffer at stop time isn't silently dropped.\n const stopAck = new Promise<void>((resolve) => {\n session.stopAckResolve = resolve;\n });\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n // Safety timeout (1s). Prevents infinite hang on a real failure\n // (worklet crashed, context closed). The drain loop below catches any\n // straggler flush messages that were queued before the timeout fired,\n // so this is purely a circuit breaker — not a data-correctness budget.\n const timeout = new Promise<void>((resolve) => {\n timeoutId = setTimeout(resolve, 1000);\n });\n session.workletNode.port.postMessage({ command: 'stop' });\n await Promise.race([stopAck, timeout]);\n clearTimeout(timeoutId);\n session.stopAckResolve = null;\n\n // Drain the event-loop queue. During recording the worklet posts a\n // flush every ~16ms; if main was slower than that, messages back up.\n // Yield repeatedly until session.totalSamples stabilizes (no new\n // messages processed) for several consecutive ticks — at that point\n // the queue is empty and chunks are complete.\n let lastSamples = -1;\n let stable = 0;\n for (let i = 0; i < 50; i++) {\n if (session.totalSamples === lastSamples) {\n if (++stable >= 3) break;\n } else {\n stable = 0;\n lastSamples = session.totalSamples;\n }\n await new Promise<void>((r) => setTimeout(r, 5));\n }\n }\n session.source.disconnect();\n session.workletNode.disconnect();\n\n // Remove mic-unplug listener\n this._removeTrackEndedListener(session);\n\n // Build AudioBuffer from accumulated chunks\n if (session.totalSamples === 0) {\n console.warn('[dawcore] RecordingController: No audio data captured');\n this._sessions.delete(id);\n this._host.requestUpdate();\n // Dispatch error so record button can reset its state (fix #3)\n this._host.dispatchEvent(\n new CustomEvent<DawRecordingErrorDetail>('daw-recording-error', {\n bubbles: true,\n composed: true,\n detail: { trackId: id, error: new Error('No audio data captured') },\n })\n );\n return;\n }\n const stopCtx = this._host.audioContext;\n const channelData = session.chunks.map((chunkArr) => concatenateAudioData(chunkArr));\n const audioBuffer = createAudioBuffer(\n stopCtx,\n channelData,\n this._host.effectiveSampleRate,\n session.channelCount\n );\n\n // Latency compensation: use the offset computed at start time\n const latencyOffsetSamples = session.latencySamples;\n const effectiveDuration = Math.max(0, audioBuffer.length - latencyOffsetSamples);\n\n if (effectiveDuration === 0) {\n console.warn('[dawcore] RecordingController: Recording too short for latency compensation');\n this._sessions.delete(id);\n this._host.requestUpdate();\n this._host.dispatchEvent(\n new CustomEvent<DawRecordingErrorDetail>('daw-recording-error', {\n bubbles: true,\n composed: true,\n detail: { trackId: id, error: new Error('Recording too short to save') },\n })\n );\n return;\n }\n\n // Dispatch cancelable event\n const event = new CustomEvent<DawRecordingCompleteDetail>('daw-recording-complete', {\n bubbles: true,\n composed: true,\n cancelable: true,\n detail: {\n trackId: id,\n audioBuffer,\n startSample: session.startSample,\n durationSamples: effectiveDuration,\n offsetSamples: latencyOffsetSamples,\n },\n });\n const notPrevented = this._host.dispatchEvent(event);\n\n // Clean up session\n this._sessions.delete(id);\n this._host.requestUpdate();\n\n if (notPrevented) {\n this._createClipFromRecording(\n id,\n audioBuffer,\n session.startSample,\n effectiveDuration,\n latencyOffsetSamples\n );\n }\n }\n\n // Session fields are mutated in place on the hot path (~60fps worklet messages).\n // This is intentional — creating new session objects + Map entries per message\n // would cause significant GC pressure. Mutations are confined to the controller's\n // private map and do not affect Lit's reactive rendering.\n private _onWorkletMessage(trackId: string, data: unknown) {\n const session = this._sessions.get(trackId);\n if (!session) return;\n\n const { channels, done } = data as { channels: Float32Array[]; done?: boolean };\n\n // Resolve the stop barrier in a finally block so a throw in peak generation\n // (e.g. samplesPerPixel=0 transient, host shadowRoot churn) doesn't strand\n // the await in stopRecording for the full 250ms timeout.\n try {\n const hasSamples = !!(\n channels &&\n channels.length > 0 &&\n channels[0] &&\n channels[0].length > 0\n );\n if (!hasSamples) return;\n this._processWorkletSamples(trackId, session, channels);\n } finally {\n if (done && session.stopAckResolve) {\n const resolve = session.stopAckResolve;\n session.stopAckResolve = null;\n resolve();\n }\n }\n }\n\n private _processWorkletSamples(\n trackId: string,\n session: RecordingSession,\n channels: Float32Array[]\n ) {\n // Capture pre-increment value for appendPeaks\n const samplesProcessedBefore = session.totalSamples;\n\n // Accumulate chunks per channel — always do this, even during stop,\n // so the AudioBuffer captures every sample.\n for (let ch = 0; ch < session.channelCount; ch++) {\n if (channels[ch]) {\n session.chunks[ch].push(channels[ch]);\n }\n }\n session.totalSamples += channels[0].length;\n\n // If stop is already in flight, skip peak generation and live-preview\n // waveform updates. The recording is ending; the AudioBuffer's final\n // peaks will be regenerated by the consumer. Skipping this drains the\n // backlog of queued flush messages faster, getting us to the terminal\n // `done` message sooner.\n if (session.stopAckResolve !== null) return;\n\n // Generate peaks per channel and update live preview waveforms\n for (let ch = 0; ch < session.channelCount; ch++) {\n if (!channels[ch]) continue;\n const oldPeakCount = Math.floor(session.peaks[ch].length / 2);\n (session.peaks as (Int8Array | Int16Array)[])[ch] = appendPeaks(\n session.peaks[ch],\n channels[ch],\n this._host.samplesPerPixel,\n samplesProcessedBefore,\n session.bits\n );\n const newPeakCount = Math.floor(session.peaks[ch].length / 2);\n\n // Update live preview waveform — host is already & HTMLElement so shadowRoot is typed\n const waveformSelector = `daw-waveform[data-recording-track=\"${trackId}\"][data-recording-channel=\"${ch}\"]`;\n const waveformEl = this._host.shadowRoot?.querySelector(\n waveformSelector\n ) as DawWaveformElement | null;\n if (waveformEl) {\n if (session.isFirstMessage) {\n waveformEl.peaks = session.peaks[ch];\n } else {\n waveformEl.setPeaksQuiet(session.peaks[ch]);\n waveformEl.updatePeaks(Math.max(0, oldPeakCount - 1), newPeakCount);\n }\n }\n }\n\n session.isFirstMessage = false;\n\n // Throttle requestUpdate — only when container width needs to grow\n const newPixelWidth = Math.floor(session.totalSamples / this._host.samplesPerPixel);\n const oldPixelWidth = Math.floor(\n (session.totalSamples - channels[0].length) / this._host.samplesPerPixel\n );\n if (newPixelWidth > oldPixelWidth) {\n this._host.requestUpdate();\n }\n }\n\n private _createClipFromRecording(\n trackId: string,\n audioBuffer: AudioBuffer,\n startSample: number,\n durationSamples: number,\n offsetSamples = 0\n ) {\n if (typeof this._host._addRecordedClip === 'function') {\n this._host._addRecordedClip(\n trackId,\n audioBuffer,\n startSample,\n durationSamples,\n offsetSamples\n );\n } else {\n console.warn(\n '[dawcore] RecordingController: host does not implement _addRecordedClip — clip not created for track \"' +\n trackId +\n '\"'\n );\n }\n }\n\n private _removeTrackEndedListener(session: RecordingSession) {\n if (session._audioTrack && session._onTrackEnded) {\n session._audioTrack.removeEventListener('ended', session._onTrackEnded);\n }\n }\n\n private _cleanupSession(trackId: string) {\n const session = this._sessions.get(trackId);\n if (!session) return;\n try {\n this._removeTrackEndedListener(session);\n session.workletNode.port.postMessage({ command: 'stop' });\n session.source.disconnect();\n session.workletNode.disconnect();\n } catch (err) {\n console.warn(\n '[dawcore] RecordingController: disconnect error during cleanup for track \"' +\n trackId +\n '\": ' +\n String(err)\n );\n }\n this._sessions.delete(trackId);\n }\n}\n","import type { ReactiveController, ReactiveControllerHost } from 'lit';\nimport {\n SpectrogramOrchestrator,\n type ClipRegistration,\n type CanvasRegistration,\n type ViewportState,\n} from '@dawcore/spectrogram';\nimport type { SpectrogramConfig, ColorMapValue } from '@waveform-playlist/core';\nimport {\n SPECTROGRAM_DEFAULTS as LIBRARY_DEFAULTS,\n DEFAULT_SPECTROGRAM_COLOR_MAP as LIBRARY_DEFAULT_COLOR_MAP,\n} from '@waveform-playlist/core';\n\nexport interface SpectrogramControllerHost extends ReactiveControllerHost {\n dispatchEvent(event: Event): boolean;\n}\n\n/**\n * Lit reactive controller that owns a `SpectrogramOrchestrator` for the\n * `<daw-editor>` host. Creates the orchestrator lazily on first canvas\n * registration so editors without spectrogram tracks pay nothing.\n *\n * Holds editor-level defaults and per-track overrides separately; merges\n * them down to a single (config, colorMap) pair for the orchestrator on\n * each change.\n *\n * v1 limitation: orchestrator accepts ONE config / colorMap at a time, so\n * the \"first registered track wins\" rule applies when multiple tracks are\n * in spectrogram mode with different overrides. Multi-track per-clip\n * configs are deferred to a follow-up.\n */\nexport class SpectrogramController implements ReactiveController {\n private host: SpectrogramControllerHost;\n private workerFactory: () => Worker;\n private orchestrator: SpectrogramOrchestrator | null = null;\n\n private editorConfig: SpectrogramConfig | null = null;\n private editorColorMap: ColorMapValue | null = null;\n private trackConfigs = new Map<string, SpectrogramConfig | null>();\n private trackColorMaps = new Map<string, ColorMapValue | null>();\n\n constructor(host: SpectrogramControllerHost, workerFactory: () => Worker) {\n this.host = host;\n this.workerFactory = workerFactory;\n this.host.addController(this);\n }\n\n hostConnected(): void {\n // Lazy — orchestrator created on first registerCanvas.\n }\n\n hostDisconnected(): void {\n this.dispose();\n }\n\n setEditorConfig(config: SpectrogramConfig | null): void {\n this.editorConfig = config;\n this.reapply();\n }\n\n setEditorColorMap(colorMap: ColorMapValue | null): void {\n this.editorColorMap = colorMap;\n this.reapply();\n }\n\n setTrackConfig(trackId: string, config: SpectrogramConfig | null): void {\n if (config === null) {\n this.trackConfigs.delete(trackId);\n } else {\n this.trackConfigs.set(trackId, config);\n }\n this.reapply();\n }\n\n setTrackColorMap(trackId: string, colorMap: ColorMapValue | null): void {\n if (colorMap === null) {\n this.trackColorMaps.delete(trackId);\n } else {\n this.trackColorMaps.set(trackId, colorMap);\n }\n this.reapply();\n }\n\n registerClipAudio(reg: ClipRegistration): void {\n this.ensureOrchestrator().registerClip(reg);\n }\n\n unregisterClipAudio(clipId: string): void {\n this.orchestrator?.unregisterClip(clipId);\n }\n\n registerCanvas(reg: CanvasRegistration): void {\n this.ensureOrchestrator().registerCanvas(reg);\n }\n\n unregisterCanvas(canvasId: string): void {\n this.orchestrator?.unregisterCanvas(canvasId);\n }\n\n setViewport(state: ViewportState): void {\n this.orchestrator?.setViewport(state);\n }\n\n dispose(): void {\n if (this.orchestrator) {\n this.orchestrator.dispose();\n this.orchestrator = null;\n }\n }\n\n private ensureOrchestrator(): SpectrogramOrchestrator {\n if (!this.orchestrator) {\n this.orchestrator = new SpectrogramOrchestrator({\n workerFactory: this.workerFactory,\n workerPoolSize: 2,\n config: this.mergedConfig(),\n colorMap: this.mergedColorMap(),\n });\n this.orchestrator.addEventListener('viewport-ready', (e: Event) => {\n const detail = (e as CustomEvent<{ trackId: string; generation: number }>).detail;\n this.host.dispatchEvent(\n new CustomEvent('daw-spectrogram-ready', {\n detail: { trackId: detail.trackId, generation: detail.generation },\n bubbles: true,\n composed: true,\n })\n );\n });\n this.orchestrator.addEventListener('viewport-error', (e: Event) => {\n const detail = (e as CustomEvent<{ trackId: string; generation: number; error: Error }>)\n .detail;\n this.host.dispatchEvent(\n new CustomEvent('daw-spectrogram-error', {\n detail: {\n trackId: detail.trackId,\n generation: detail.generation,\n error: detail.error,\n },\n bubbles: true,\n composed: true,\n })\n );\n });\n this.reapply();\n }\n return this.orchestrator;\n }\n\n private reapply(): void {\n if (!this.orchestrator) return;\n this.orchestrator.setConfig(this.mergedConfig());\n this.orchestrator.setColorMap(this.mergedColorMap());\n }\n\n private mergedConfig(): SpectrogramConfig {\n // First track override wins (v1 limitation noted above).\n let track: SpectrogramConfig | null = null;\n for (const c of this.trackConfigs.values()) {\n track = c;\n break;\n }\n return { ...LIBRARY_DEFAULTS, ...(this.editorConfig ?? {}), ...(track ?? {}) };\n }\n\n private mergedColorMap(): ColorMapValue {\n for (const c of this.trackColorMaps.values()) {\n return c ?? LIBRARY_DEFAULT_COLOR_MAP;\n }\n return this.editorColorMap ?? LIBRARY_DEFAULT_COLOR_MAP;\n }\n}\n","import { pixelsToSeconds, snapTickToGrid } from '@waveform-playlist/core';\nimport type { SnapTo, MeterEntry } from '@waveform-playlist/core';\nimport { DRAG_THRESHOLD } from './constants';\n\n/** Narrow engine contract for pointer interactions. */\nexport interface PointerEngineContract {\n setSelection(start: number, end: number): void;\n stop(): void;\n play(time: number): void;\n seek(time: number): void;\n selectTrack(trackId: string | null): void;\n}\n\n/** Manages pointer interactions on the timeline: click-to-seek and drag-to-select. */\nexport interface PointerHandlerHost {\n readonly samplesPerPixel: number;\n readonly _engine: PointerEngineContract | null;\n readonly _isPlaying: boolean;\n readonly effectiveSampleRate: number;\n _currentTime: number;\n _selectionStartTime: number;\n _selectionEndTime: number;\n _dragOver: boolean;\n _setSelectedTrackId(trackId: string | null): void;\n _startPlayhead(): void;\n _stopPlayhead(): void;\n dispatchEvent(event: Event): boolean;\n shadowRoot: ShadowRoot | null;\n requestUpdate(): void;\n readonly _clipHandler: {\n tryHandle(target: Element, e: PointerEvent): boolean;\n onPointerMove(e: PointerEvent): void;\n onPointerUp(e: PointerEvent): void;\n isActive: boolean;\n } | null;\n readonly scaleMode: 'temporal' | 'beats';\n readonly ticksPerPixel: number;\n readonly bpm: number;\n readonly ppqn: number;\n readonly _meterEntries: MeterEntry[];\n readonly snapTo: SnapTo;\n readonly _secondsToTicks: (seconds: number) => number;\n readonly _ticksToSeconds: (ticks: number) => number;\n}\n\nexport class PointerHandler {\n private _host: PointerHandlerHost;\n private _isDragging = false;\n private _dragStartPx = 0;\n private _dragStartTime = 0;\n private _timeline: HTMLElement | null = null;\n // Cached from onPointerDown to avoid forced layout reflows at 60fps during drag\n private _timelineRect: DOMRect | null = null;\n\n constructor(host: PointerHandlerHost) {\n this._host = host;\n }\n\n private _pxFromPointer(e: PointerEvent): number {\n if (!this._timelineRect) {\n console.warn('[dawcore] _pxFromPointer called without timeline reference');\n return 0;\n }\n // .timeline is wider than :host (which has overflow-x: auto).\n // getBoundingClientRect().left already reflects scroll position\n // (goes negative when scrolled), so no scrollLeft adjustment needed.\n return e.clientX - this._timelineRect.left;\n }\n\n private _pxToTime(px: number): number {\n const h = this._host;\n if (h.scaleMode === 'beats') {\n let tick = px * h.ticksPerPixel;\n tick = snapTickToGrid(tick, h.snapTo, h._meterEntries, h.ppqn);\n return h._ticksToSeconds(tick);\n }\n return pixelsToSeconds(px, h.samplesPerPixel, h.effectiveSampleRate);\n }\n\n private _timeToPx(time: number): number {\n const h = this._host;\n if (h.scaleMode === 'beats') {\n const tick = h._secondsToTicks(time);\n return tick / h.ticksPerPixel;\n }\n return (time * h.effectiveSampleRate) / h.samplesPerPixel;\n }\n\n onPointerDown = (e: PointerEvent) => {\n // Check if click landed on an interactive clip element\n const clipHandler = this._host._clipHandler;\n if (clipHandler) {\n const target = e.composedPath()[0] as Element;\n if (target && clipHandler.tryHandle(target, e)) {\n // Prevent browser native drag (globe icon) and text selection\n e.preventDefault();\n // Clip handler took over — wire move/up to it\n this._timeline = this._host.shadowRoot?.querySelector('.timeline') as HTMLElement | null;\n if (this._timeline) {\n try {\n this._timeline.setPointerCapture(e.pointerId);\n } catch (err) {\n console.warn('[dawcore] setPointerCapture failed: ' + String(err));\n }\n const onMove = (me: Event) => clipHandler.onPointerMove(me as PointerEvent);\n const onUp = (ue: Event) => {\n clipHandler.onPointerUp(ue as PointerEvent);\n this._timeline?.removeEventListener('pointermove', onMove);\n this._timeline?.removeEventListener('pointerup', onUp);\n try {\n this._timeline?.releasePointerCapture((ue as PointerEvent).pointerId);\n } catch (err) {\n console.warn(\n '[dawcore] releasePointerCapture failed (may already be released): ' + String(err)\n );\n }\n this._timeline = null;\n };\n this._timeline.addEventListener('pointermove', onMove);\n this._timeline.addEventListener('pointerup', onUp);\n }\n return;\n }\n }\n\n this._timeline = this._host.shadowRoot?.querySelector('.timeline') as HTMLElement | null;\n if (!this._timeline) {\n console.warn(\n '[dawcore] PointerHandler: .timeline not found in shadow root — seek/selection ignored'\n );\n return;\n }\n\n this._timelineRect = this._timeline.getBoundingClientRect();\n this._dragStartPx = this._pxFromPointer(e);\n this._isDragging = false;\n\n try {\n this._timeline.setPointerCapture(e.pointerId);\n } catch (err) {\n console.warn('[dawcore] setPointerCapture failed: ' + String(err));\n }\n this._timeline.addEventListener('pointermove', this._onPointerMove);\n this._timeline.addEventListener('pointerup', this._onPointerUp);\n };\n\n private _onPointerMove = (e: PointerEvent) => {\n if (!this._timeline) return;\n\n const currentPx = this._pxFromPointer(e);\n\n if (!this._isDragging && Math.abs(currentPx - this._dragStartPx) > DRAG_THRESHOLD) {\n this._isDragging = true;\n // Cache snapped start time once — don't recompute every frame\n this._dragStartTime = this._pxToTime(this._dragStartPx);\n }\n\n if (this._isDragging) {\n const h = this._host;\n const startTime = this._dragStartTime;\n const endTime = this._pxToTime(currentPx);\n // Mutate host fields directly (not @state) and update <daw-selection>\n // imperatively to avoid triggering Lit re-renders at 60fps during drag\n h._selectionStartTime = Math.min(startTime, endTime);\n h._selectionEndTime = Math.max(startTime, endTime);\n const sel = h.shadowRoot?.querySelector('daw-selection') as\n | { startPx: number; endPx: number }\n | undefined;\n if (sel) {\n sel.startPx = this._timeToPx(h._selectionStartTime);\n sel.endPx = this._timeToPx(h._selectionEndTime);\n }\n }\n };\n\n private _onPointerUp = (e: PointerEvent) => {\n if (!this._timeline) return;\n\n try {\n this._timeline.releasePointerCapture(e.pointerId);\n } catch (err) {\n console.warn(\n '[dawcore] releasePointerCapture failed (may already be released): ' + String(err)\n );\n }\n this._timeline.removeEventListener('pointermove', this._onPointerMove);\n this._timeline.removeEventListener('pointerup', this._onPointerUp);\n\n try {\n if (this._isDragging) {\n this._finalizeSelection();\n } else {\n this._handleSeekClick(e);\n }\n } catch (err) {\n console.warn('[dawcore] Pointer interaction failed: ' + String(err));\n } finally {\n this._isDragging = false;\n this._timeline = null;\n this._timelineRect = null;\n }\n };\n\n private _finalizeSelection() {\n const h = this._host;\n if (h._engine) {\n h._engine.setSelection(h._selectionStartTime, h._selectionEndTime);\n }\n h.dispatchEvent(\n new CustomEvent('daw-selection', {\n bubbles: true,\n composed: true,\n detail: { start: h._selectionStartTime, end: h._selectionEndTime },\n })\n );\n h.requestUpdate();\n }\n\n private _handleSeekClick(e: PointerEvent) {\n const h = this._host;\n const px = this._pxFromPointer(e);\n const time = this._pxToTime(px);\n\n // Clear selection\n h._selectionStartTime = 0;\n h._selectionEndTime = 0;\n\n // Detect which track was clicked by Y position\n if (this._timeline) {\n const trackRows = this._timeline.querySelectorAll('.track-row');\n for (const row of trackRows) {\n const rowRect = row.getBoundingClientRect();\n if (e.clientY >= rowRect.top && e.clientY < rowRect.bottom) {\n const trackId = (row as HTMLElement).dataset.trackId;\n if (trackId) {\n this._selectTrack(trackId);\n }\n break;\n }\n }\n }\n\n // Capture playing state before engine calls (stop emits statechange\n // which synchronously flips _isPlaying — use wasPlaying for all guards)\n const wasPlaying = h._isPlaying;\n\n if (h._engine) {\n h._engine.setSelection(0, 0);\n if (wasPlaying) {\n // Tone.js needs stop + play to reschedule audio sources\n h._engine.stop();\n h._engine.play(time);\n h._startPlayhead();\n } else {\n h._engine.seek(time);\n }\n }\n\n h._currentTime = time;\n if (!wasPlaying) {\n h._stopPlayhead();\n }\n\n h.dispatchEvent(\n new CustomEvent('daw-seek', {\n bubbles: true,\n composed: true,\n detail: { time },\n })\n );\n h.requestUpdate();\n }\n\n private _selectTrack(trackId: string) {\n const h = this._host;\n if (h._engine) {\n try {\n h._engine.selectTrack(trackId);\n // Engine sets _selectedTrackId via statechange — don't set locally\n } catch (err) {\n console.warn(\n '[dawcore] selectTrack via engine failed, falling back to local: ' + String(err)\n );\n // Fall through to local selection below\n h._setSelectedTrackId(trackId);\n }\n } else {\n // No engine — set locally (will be lost when engine builds, acceptable for Phase 2)\n h._setSelectedTrackId(trackId);\n }\n h.dispatchEvent(\n new CustomEvent('daw-track-select', {\n bubbles: true,\n composed: true,\n detail: { trackId },\n })\n );\n }\n}\n","/** Minimum pixel movement before a drag is activated (click vs drag). */\nexport const DRAG_THRESHOLD = 3;\n\n// Boundary width (8px) is defined in CSS (styles/theme.ts .clip-boundary).\n// Not exported as a JS constant since CSS template literals can't import it.\n","import { snapTickToGrid } from '@waveform-playlist/core';\nimport type { SnapTo, MeterEntry } from '@waveform-playlist/core';\nimport { DRAG_THRESHOLD } from './constants';\n\n/** Snapshot of a clip's bounds for trim constraint computation. */\nexport interface ClipBounds {\n readonly offsetSamples: number;\n readonly durationSamples: number;\n readonly startSample: number;\n readonly sourceDurationSamples: number;\n}\n\n/** Narrow engine contract for clip move/trim interactions. */\nexport interface ClipEngineContract {\n moveClip(trackId: string, clipId: string, deltaSamples: number, skipAdapter?: boolean): number;\n trimClip(\n trackId: string,\n clipId: string,\n boundary: 'left' | 'right',\n deltaSamples: number,\n skipAdapter?: boolean\n ): void;\n updateTrack(trackId: string): void;\n /** Get a clip's full bounds for trim constraint computation. */\n getClipBounds(trackId: string, clipId: string): ClipBounds | null;\n /** Constrain a trim delta using the engine's collision/bounds logic. */\n constrainTrimDelta(\n trackId: string,\n clipId: string,\n boundary: 'left' | 'right',\n deltaSamples: number\n ): number;\n /** Begin a transaction — groups mutations into one undo step. */\n beginTransaction(): void;\n /** Commit the transaction — pushes one undo step for all grouped mutations. */\n commitTransaction(): void;\n /** Abort the transaction — restores pre-transaction state without pushing to undo. */\n abortTransaction(): void;\n}\n\n/** Peak data returned by reextractClipPeaks for imperative waveform updates. */\nexport interface ClipPeakSlice {\n data: ArrayLike<number>[];\n length: number;\n}\n\n/** Host interface required by ClipPointerHandler. */\nexport interface ClipPointerHost {\n readonly samplesPerPixel: number;\n /** In beats mode, the tick-derived SPP used for rendering. */\n readonly renderSamplesPerPixel: number;\n readonly effectiveSampleRate: number;\n readonly interactiveClips: boolean;\n readonly engine: ClipEngineContract | null;\n readonly shadowRoot: ShadowRoot | null;\n readonly scaleMode: 'temporal' | 'beats';\n readonly ticksPerPixel: number;\n readonly bpm: number;\n readonly ppqn: number;\n readonly _meterEntries: MeterEntry[];\n readonly snapTo: SnapTo;\n readonly _secondsToTicks: (seconds: number) => number;\n readonly _ticksToSeconds: (ticks: number) => number;\n dispatchEvent(event: Event): boolean;\n /** Re-extract peaks for a clip at new offset/duration from cached WaveformData. */\n reextractClipPeaks(\n clipId: string,\n offsetSamples: number,\n durationSamples: number\n ): ClipPeakSlice | null;\n /**\n * Returns true if the clip is a MIDI clip (has midiNotes).\n * Trim handles are inert for MIDI clips — note slicing is not yet implemented.\n */\n isMidiClip(trackId: string, clipId: string): boolean;\n}\n\ntype DragMode = 'move' | 'trim-left' | 'trim-right';\n\n/**\n * Handles pointer interactions for clip move and trim drag operations.\n * Converts pixel deltas to sample deltas and delegates to the engine.\n *\n * Move: sends incremental deltas per-frame with skipAdapter=true (engine shifts\n * startSample additively without touching audio adapter). Adapter synced once\n * via updateTrack() at drag end.\n * Trim: updates clip container CSS imperatively during drag for visual feedback,\n * then applies cumulative delta to engine once at drag end.\n */\nexport class ClipPointerHandler {\n private _host: ClipPointerHost;\n private _mode: DragMode | null = null;\n private _clipId = '';\n private _trackId = '';\n private _startPx = 0;\n private _isDragging = false;\n private _cumulativeDeltaSamples = 0;\n // Trim visual feedback: snapshot of original clip state\n private _clipContainer: HTMLElement | null = null;\n private _boundaryEl: HTMLElement | null = null;\n private _originalLeft = 0;\n private _originalWidth = 0;\n private _originalOffsetSamples = 0;\n private _originalDurationSamples = 0;\n private _originalStartSample = 0;\n\n constructor(host: ClipPointerHost) {\n this._host = host;\n }\n\n /**\n * Convert a pixel delta to samples, snapping in tick space when in beats mode.\n *\n * The anchor is the absolute sample position being moved (e.g., clip start\n * for move/left-trim, clip end for right-trim). Snapping the absolute\n * position — not just the delta — ensures clips land exactly on grid lines\n * even if they started off-grid.\n */\n private _snapDeltaToSamples(totalDeltaPx: number, anchorSample: number): number {\n const h = this._host;\n if (h.scaleMode === 'beats') {\n const anchorSeconds = anchorSample / h.effectiveSampleRate;\n const anchorTick = h._secondsToTicks(anchorSeconds);\n const deltaTicks = totalDeltaPx * h.ticksPerPixel;\n const targetTick = anchorTick + deltaTicks;\n const snappedTick =\n h.snapTo !== 'off'\n ? snapTickToGrid(targetTick, h.snapTo, h._meterEntries, h.ppqn)\n : targetTick;\n const snappedSeconds = h._ticksToSeconds(snappedTick);\n const snappedSample = Math.round(snappedSeconds * h.effectiveSampleRate);\n return snappedSample - anchorSample;\n }\n return Math.round(totalDeltaPx * h.renderSamplesPerPixel);\n }\n\n /** Returns true if a drag interaction is currently in progress. */\n get isActive(): boolean {\n return this._mode !== null;\n }\n\n /**\n * Attempts to handle a pointerdown event on the given target element.\n * Returns true if the target is a recognized clip interaction element.\n */\n tryHandle(target: Element, e: PointerEvent): boolean {\n if (!this._host.interactiveClips) return false;\n\n // Walk up from click target to find clip interaction elements.\n // composedPath()[0] may be a child (e.g. <span> inside .clip-header).\n const boundary = (target as HTMLElement).closest?.('.clip-boundary') as HTMLElement | null;\n const header = (target as HTMLElement).closest?.('.clip-header') as HTMLElement | null;\n\n // Check boundary first (higher z-index, overlaps header at corners)\n if (boundary && boundary.dataset.boundaryEdge !== undefined) {\n const clipId = boundary.dataset.clipId;\n const trackId = boundary.dataset.trackId;\n const edge = boundary.dataset.boundaryEdge as 'left' | 'right';\n if (!clipId || !trackId || (edge !== 'left' && edge !== 'right')) return false;\n\n // Trim is not supported for MIDI clips — note slicing is a follow-up PR.\n // Consume the event: no trim drag starts, but prevent the click from\n // falling through to the timeline seek handler.\n if (this._host.isMidiClip(trackId, clipId)) return true;\n\n this._beginDrag(edge === 'left' ? 'trim-left' : 'trim-right', clipId, trackId, e);\n this._boundaryEl = boundary;\n return true;\n }\n\n // Check for clip header (move target)\n if (header && header.dataset.interactive !== undefined) {\n const clipId = header.dataset.clipId;\n const trackId = header.dataset.trackId;\n if (!clipId || !trackId) return false;\n\n this._beginDrag('move', clipId, trackId, e);\n return true;\n }\n\n return false;\n }\n\n private _beginDrag(mode: DragMode, clipId: string, trackId: string, e: PointerEvent): void {\n this._mode = mode;\n this._clipId = clipId;\n this._trackId = trackId;\n this._startPx = e.clientX;\n this._isDragging = false;\n\n this._cumulativeDeltaSamples = 0;\n\n // Snapshot clip start position for snap calculations\n const engine = this._host.engine;\n if (engine) {\n const bounds = engine.getClipBounds(trackId, clipId);\n if (bounds) {\n this._originalStartSample = bounds.startSample;\n }\n }\n\n // Group all drag mutations into one undo step\n if (engine) {\n engine.beginTransaction();\n } else {\n console.warn(\n '[dawcore] beginDrag: engine unavailable, drag mutations will not be grouped for undo'\n );\n }\n\n // For trim: snapshot the clip container's current position/width\n if (mode === 'trim-left' || mode === 'trim-right') {\n const container = this._host.shadowRoot?.querySelector(\n `.clip-container[data-clip-id=\"${clipId}\"]`\n ) as HTMLElement | null;\n if (container) {\n this._clipContainer = container;\n this._originalLeft = parseFloat(container.style.left) || 0;\n this._originalWidth = parseFloat(container.style.width) || 0;\n } else {\n console.warn('[dawcore] clip container not found for trim visual feedback: ' + clipId);\n }\n // Snapshot clip audio bounds for peak re-extraction during drag\n const engine = this._host.engine;\n if (engine) {\n const bounds = engine.getClipBounds(trackId, clipId);\n if (bounds) {\n this._originalOffsetSamples = bounds.offsetSamples;\n this._originalDurationSamples = bounds.durationSamples;\n }\n }\n }\n }\n\n /** Processes pointermove events during an active drag. */\n onPointerMove(e: PointerEvent): void {\n if (this._mode === null) return;\n\n const totalDeltaPx = e.clientX - this._startPx;\n\n // Activate drag after threshold is exceeded\n if (!this._isDragging && Math.abs(totalDeltaPx) > DRAG_THRESHOLD) {\n this._isDragging = true;\n // Apply .dragging class to boundary element for active drag styling\n if (this._boundaryEl) {\n this._boundaryEl.classList.add('dragging');\n }\n }\n\n if (!this._isDragging) return;\n\n const engine = this._host.engine;\n if (!engine) return;\n\n if (this._mode === 'move') {\n // Move: compute total snapped delta from original position, then derive\n // the incremental from what's already been applied to the engine.\n // Anchor = clip start position (move shifts the whole clip)\n const totalSnappedDelta = this._snapDeltaToSamples(totalDeltaPx, this._originalStartSample);\n const incrementalDeltaSamples = totalSnappedDelta - this._cumulativeDeltaSamples;\n if (incrementalDeltaSamples !== 0) {\n // Track constrained delta (not raw) so undo transactions are accurate\n const applied = engine.moveClip(this._trackId, this._clipId, incrementalDeltaSamples, true);\n this._cumulativeDeltaSamples += applied;\n }\n } else {\n // Trim: constrain delta using engine's full collision/bounds logic,\n // then track for visual feedback. Engine called once at pointerup.\n const boundary = this._mode === 'trim-left' ? 'left' : 'right';\n // Anchor = the boundary edge being dragged\n // Left trim: snap the left edge (startSample)\n // Right trim: snap the right edge (startSample + durationSamples)\n const anchor =\n boundary === 'left'\n ? this._originalStartSample\n : this._originalStartSample + this._originalDurationSamples;\n const rawDeltaSamples = this._snapDeltaToSamples(totalDeltaPx, anchor);\n const deltaSamples = engine.constrainTrimDelta(\n this._trackId,\n this._clipId,\n boundary,\n rawDeltaSamples\n );\n // Convert sample delta to pixels — use tick space in beats mode\n // to avoid quantization drift from the sample round-trip.\n let deltaPx: number;\n if (this._host.scaleMode === 'beats') {\n const h = this._host;\n const anchorSec = anchor / h.effectiveSampleRate;\n const anchorTick = h._secondsToTicks(anchorSec);\n const newSec = anchorSec + deltaSamples / h.effectiveSampleRate;\n const newTick = h._secondsToTicks(newSec);\n deltaPx = Math.round((newTick - anchorTick) / h.ticksPerPixel);\n } else {\n deltaPx = Math.round(deltaSamples / this._host.renderSamplesPerPixel);\n }\n\n this._cumulativeDeltaSamples = deltaSamples;\n\n // Visual feedback: update clip container CSS and re-extract peaks\n if (this._clipContainer) {\n if (this._mode === 'trim-left') {\n // Left trim: container shifts and resizes\n const newLeft = this._originalLeft + deltaPx;\n const newWidth = this._originalWidth - deltaPx;\n if (newWidth > 0) {\n this._clipContainer.style.left = newLeft + 'px';\n this._clipContainer.style.width = newWidth + 'px';\n // Re-extract peaks at new offset/duration from cached WaveformData.\n // New peaks cover the full new bounds, so waveforms stay at left:0\n // (no shift needed — the container position handles global alignment).\n const newOffset = this._originalOffsetSamples + deltaSamples;\n const newDuration = this._originalDurationSamples - deltaSamples;\n if (this._updateWaveformPeaks(newOffset, newDuration)) {\n // Peaks updated — reset waveform positions to fill container\n const waveforms = this._clipContainer.querySelectorAll('daw-waveform');\n for (const wf of waveforms) {\n (wf as HTMLElement).style.left = '0px';\n }\n } else {\n // No cached peaks — fall back to shifting waveforms for visual stability\n const waveforms = this._clipContainer.querySelectorAll('daw-waveform');\n for (const wf of waveforms) {\n (wf as HTMLElement).style.left = -deltaPx + 'px';\n }\n }\n }\n } else {\n // Right trim: extend/shrink right edge — left stays fixed\n const newWidth = this._originalWidth + deltaPx;\n if (newWidth > 0) {\n this._clipContainer.style.width = newWidth + 'px';\n // Re-extract peaks at new duration from cached WaveformData\n const newDuration = this._originalDurationSamples + deltaSamples;\n this._updateWaveformPeaks(this._originalOffsetSamples, newDuration);\n }\n }\n }\n }\n }\n\n /** Processes pointerup events to finalize and dispatch result events. */\n onPointerUp(_e: PointerEvent): void {\n if (this._mode === null) return;\n\n try {\n if (!this._isDragging || this._cumulativeDeltaSamples === 0) {\n // Restore original CSS if trim drag didn't produce a delta\n this._restoreTrimVisual();\n return;\n }\n\n const engine = this._host.engine;\n\n if (this._mode === 'move') {\n // Sync adapter once on drop (skipped during drag for performance)\n if (engine) {\n engine.updateTrack(this._trackId);\n this._host.dispatchEvent(\n new CustomEvent('daw-clip-move', {\n bubbles: true,\n composed: true,\n detail: {\n trackId: this._trackId,\n clipId: this._clipId,\n deltaSamples: this._cumulativeDeltaSamples,\n },\n })\n );\n } else {\n console.warn(\n '[dawcore] engine unavailable at move drop — audio may be out of sync for track ' +\n this._trackId\n );\n }\n } else {\n // Restore visual before engine applies — Lit will re-render with correct values\n this._restoreTrimVisual();\n\n // Trim: apply cumulative delta to engine in one shot (adapter updated internally)\n const boundary = this._mode === 'trim-left' ? 'left' : 'right';\n if (engine) {\n engine.trimClip(this._trackId, this._clipId, boundary, this._cumulativeDeltaSamples);\n this._host.dispatchEvent(\n new CustomEvent('daw-clip-trim', {\n bubbles: true,\n composed: true,\n detail: {\n trackId: this._trackId,\n clipId: this._clipId,\n boundary,\n deltaSamples: this._cumulativeDeltaSamples,\n },\n })\n );\n } else {\n console.warn(\n '[dawcore] engine unavailable at trim drop — trim not applied for clip ' + this._clipId\n );\n }\n }\n } finally {\n // Commit transaction if mutations occurred, abort if click/no-op.\n // Abort does NOT push to undo stack or clear redo stack.\n if (this._isDragging && this._cumulativeDeltaSamples !== 0) {\n this._host.engine?.commitTransaction();\n } else {\n this._host.engine?.abortTransaction();\n }\n this._reset();\n }\n }\n\n /** Re-extract peaks from cache and set on waveform elements during trim drag.\n * Returns true if peaks were successfully updated. */\n private _updateWaveformPeaks(offsetSamples: number, durationSamples: number): boolean {\n if (!this._clipContainer || durationSamples <= 0) return false;\n const peakSlice = this._host.reextractClipPeaks(this._clipId, offsetSamples, durationSamples);\n if (!peakSlice) return false;\n\n const waveforms = this._clipContainer.querySelectorAll('daw-waveform');\n for (let i = 0; i < waveforms.length; i++) {\n const wf = waveforms[i] as HTMLElement & { peaks: unknown; length: number };\n const channelPeaks = peakSlice.data[i];\n if (channelPeaks) {\n wf.peaks = channelPeaks;\n wf.length = peakSlice.length;\n }\n }\n return true;\n }\n\n /** Restore clip container CSS to original values after trim visual preview. */\n private _restoreTrimVisual(): void {\n if (this._clipContainer) {\n this._clipContainer.style.left = this._originalLeft + 'px';\n this._clipContainer.style.width = this._originalWidth + 'px';\n // Restore waveform positions (shifted during left trim preview)\n const waveforms = this._clipContainer.querySelectorAll('daw-waveform');\n for (const wf of waveforms) {\n (wf as HTMLElement).style.left = '0px';\n }\n }\n }\n\n private _reset(): void {\n // Remove .dragging class from boundary element\n if (this._boundaryEl) {\n this._boundaryEl.classList.remove('dragging');\n this._boundaryEl = null;\n }\n this._mode = null;\n this._clipId = '';\n this._trackId = '';\n this._startPx = 0;\n this._isDragging = false;\n\n this._cumulativeDeltaSamples = 0;\n this._clipContainer = null;\n this._originalLeft = 0;\n this._originalWidth = 0;\n this._originalOffsetSamples = 0;\n this._originalDurationSamples = 0;\n this._originalStartSample = 0;\n }\n}\n","/**\n * File loading logic extracted from daw-editor to keep the editor under 800 lines.\n * Operates on the editor instance via a narrow interface.\n */\n\nimport type { ClipTrack, PeakData } from '@waveform-playlist/core';\nimport { createClip, createTrack } from '@waveform-playlist/core';\nimport type { PeakPipeline } from '../workers/peakPipeline';\nimport type { DawTrackIdDetail, DawFilesLoadErrorDetail, LoadFilesResult } from '../events';\nimport type { TrackDescriptor } from '../types';\n\nexport interface FileLoaderHost {\n readonly samplesPerPixel: number;\n /** In beats mode, the tick-derived SPP used for rendering. */\n readonly renderSamplesPerPixel: number;\n readonly mono: boolean;\n readonly isConnected: boolean;\n _resolvedSampleRate: number | null;\n _tracks: Map<string, TrackDescriptor>;\n _engineTracks: Map<string, ClipTrack>;\n _peaksData: Map<string, PeakData>;\n _clipBuffers: Map<string, AudioBuffer>;\n _clipOffsets: Map<string, { offsetSamples: number; durationSamples: number }>;\n _audioCache: Map<string, Promise<AudioBuffer>>;\n _peakPipeline: PeakPipeline;\n _fetchAndDecode(src: string): Promise<AudioBuffer>;\n _recomputeDuration(): void;\n _ensureEngine(): Promise<{ setTracks(tracks: ClipTrack[]): void }>;\n dispatchEvent(event: Event): boolean;\n}\n\nexport async function loadFiles(\n host: FileLoaderHost,\n files: FileList | File[]\n): Promise<LoadFilesResult> {\n if (!files) {\n console.warn('[dawcore] loadFiles called with null/undefined');\n return { loaded: [], failed: [] };\n }\n\n const fileArray = Array.from(files);\n const loaded: string[] = [];\n const failed: Array<{ file: File; error: unknown }> = [];\n\n for (const file of fileArray) {\n if (file.type && !file.type.startsWith('audio/')) {\n failed.push({ file, error: new Error('Non-audio MIME type: ' + file.type) });\n console.warn('[dawcore] Skipping non-audio file: ' + file.name + ' (' + file.type + ')');\n continue;\n }\n\n const blobUrl = URL.createObjectURL(file);\n try {\n const audioBuffer = await host._fetchAndDecode(blobUrl);\n URL.revokeObjectURL(blobUrl);\n host._audioCache.delete(blobUrl);\n\n host._resolvedSampleRate = audioBuffer.sampleRate;\n\n const name = file.name.replace(/\\.\\w+$/, '');\n const clip = createClip({\n audioBuffer,\n startSample: 0,\n durationSamples: audioBuffer.length,\n offsetSamples: 0,\n gain: 1,\n name,\n sampleRate: audioBuffer.sampleRate,\n sourceDurationSamples: audioBuffer.length,\n });\n\n host._clipBuffers = new Map(host._clipBuffers).set(clip.id, audioBuffer);\n host._clipOffsets = new Map(host._clipOffsets).set(clip.id, {\n offsetSamples: clip.offsetSamples,\n durationSamples: clip.durationSamples,\n });\n const peakData = await host._peakPipeline.generatePeaks(\n audioBuffer,\n host.renderSamplesPerPixel,\n host.mono,\n clip.offsetSamples,\n clip.durationSamples\n );\n host._peaksData = new Map(host._peaksData).set(clip.id, peakData);\n\n const trackId = crypto.randomUUID();\n const track = createTrack({ name, clips: [clip] });\n track.id = trackId;\n\n host._tracks = new Map(host._tracks).set(trackId, {\n name,\n src: '',\n volume: 1,\n pan: 0,\n muted: false,\n soloed: false,\n renderMode: 'waveform',\n clips: [\n {\n kind: 'drop',\n src: '',\n peaksSrc: '',\n start: 0,\n duration: audioBuffer.duration,\n offset: 0,\n gain: 1,\n name,\n fadeIn: 0,\n fadeOut: 0,\n fadeType: 'linear',\n midiNotes: null,\n midiChannel: null,\n midiProgram: null,\n },\n ],\n });\n host._engineTracks = new Map(host._engineTracks).set(trackId, track);\n host._recomputeDuration();\n\n const engine = await host._ensureEngine();\n engine.setTracks([...host._engineTracks.values()]);\n\n loaded.push(trackId);\n host.dispatchEvent(\n new CustomEvent<DawTrackIdDetail>('daw-track-ready', {\n bubbles: true,\n composed: true,\n detail: { trackId },\n })\n );\n } catch (err) {\n URL.revokeObjectURL(blobUrl);\n console.warn('[dawcore] Failed to load file: ' + file.name + ' — ' + String(err));\n failed.push({ file, error: err });\n if (host.isConnected) {\n host.dispatchEvent(\n new CustomEvent<DawFilesLoadErrorDetail>('daw-files-load-error', {\n bubbles: true,\n composed: true,\n detail: { file, error: err },\n })\n );\n }\n }\n }\n\n return { loaded, failed };\n}\n","/**\n * MIDI loading logic extracted from daw-editor. Operates on the editor via a\n * narrow host interface (`addTrack` + `querySelectorAll`) — `<daw-editor>`\n * satisfies it without any new public surface.\n *\n * Numbered steps below match the Data Flow diagram in\n * `docs/specs/2026-05-23-dawcore-load-midi-design.md`.\n */\n\nimport type { MidiLoadOptions, MidiLoadResult } from '@dawcore/midi';\nimport type { TrackConfig } from '../types';\nimport type { DawTrackElement } from '../elements/daw-track';\n\nexport type { MidiLoadOptions, MidiLoadResult };\n\n/**\n * Minimal host surface needed by `loadMidiImpl`. `<daw-editor>` satisfies this\n * structurally. `querySelectorAll` is needed for cleanup-on-failure so the\n * loader can identify `<daw-track>` elements appended during this call (both\n * those whose `addTrack` resolved and those that rejected after `_loadTrack`\n * fired `daw-track-error` — the latter aren't in the `addTrack` resolution\n * value, so we need DOM observation to find them).\n */\nexport interface MidiLoaderHost {\n addTrack(config: TrackConfig): Promise<DawTrackElement>;\n querySelectorAll(selector: string): NodeListOf<Element>;\n}\n\nconst INSTALL_HINT =\n '@dawcore/midi is required for loadMidi(). Install with: npm install @dawcore/midi';\n\n/**\n * Loads a `.mid` file (URL or `File`) and creates N `<daw-track>` elements,\n * one per note-bearing MIDI track. On any per-track failure, every\n * `<daw-track>` appended during this call is removed so the editor returns\n * to its pre-call state (cleanup-on-failure — covers both `addTrack`\n * successes and the elements that `addTrack` left in the DOM before its\n * promise rejected).\n *\n * AbortSignal is forwarded to the fetch phase only. Aborting after parsing\n * does not cancel in-flight `addTrack` calls (documented limitation).\n */\nexport async function loadMidiImpl(\n host: MidiLoaderHost,\n source: string | File,\n options: MidiLoadOptions = {}\n): Promise<MidiLoadResult> {\n // Reject NaN / Infinity / negative startTime up front. Without this, every\n // clip silently inherits the bogus value and the timeline corrupts.\n const startTime = options.startTime ?? 0;\n if (!Number.isFinite(startTime) || startTime < 0) {\n throw new RangeError(\n 'loadMidi: startTime must be a non-negative finite number (got ' +\n String(options.startTime) +\n ')'\n );\n }\n\n // (1) Dynamic-import the optional peer dep. Log the original error so\n // debugging isn't blocked when the failure is something other than\n // \"not installed\" (broken exports map, 404 chunk, CSP, etc.). Targeting\n // ES2020 means we can't use Error.cause yet; console.warn carries the\n // diagnostic detail.\n let midiModule: typeof import('@dawcore/midi');\n try {\n midiModule = await import('@dawcore/midi');\n } catch (originalErr) {\n console.warn('[dawcore] @dawcore/midi dynamic import failed: ' + String(originalErr));\n throw new Error(INSTALL_HINT);\n }\n const { parseMidiUrl, parseMidiFile } = midiModule;\n\n // (2) Branch on source type. Wrap the File path so a failed disk read\n // surfaces with file context instead of a bare DOMException.\n let parsed;\n if (typeof source === 'string') {\n parsed = await parseMidiUrl(source, undefined, options.signal);\n } else {\n let buffer: ArrayBuffer;\n try {\n buffer = await source.arrayBuffer();\n } catch (err) {\n throw new Error(\n 'loadMidi: failed to read File \"' +\n source.name +\n '\" (' +\n source.size +\n ' bytes): ' +\n String(err)\n );\n }\n parsed = parseMidiFile(buffer);\n }\n\n // (3) Snapshot existing tracks so cleanup can identify the ones we appended.\n const childrenBefore = new Set<Element>(host.querySelectorAll('daw-track'));\n\n // (4) Concurrent addTrack with allSettled so we can wait for every settlement\n // before deciding. Promise.all would early-reject while other addTrack calls\n // keep running, leaving orphan tracks after cleanup.\n const settlements = await Promise.allSettled(\n parsed.tracks.map((t) =>\n host.addTrack({\n name: t.name,\n renderMode: 'piano-roll',\n clips: [\n {\n midiNotes: t.notes,\n midiChannel: t.channel,\n midiProgram: t.programNumber,\n start: startTime,\n },\n ],\n })\n )\n );\n\n // (5) Partition fulfilled vs rejected. Capture EVERY rejection (not just\n // the first) for diagnosability of multi-track failures.\n const succeeded: DawTrackElement[] = [];\n const rejections: unknown[] = [];\n for (const s of settlements) {\n if (s.status === 'fulfilled') {\n succeeded.push(s.value);\n } else {\n rejections.push(s.reason);\n }\n }\n\n // (6) On any rejection: remove ALL tracks appended during this call (both\n // succeeded and the ones addTrack left in the DOM before rejecting), then\n // throw a summary Error and console.warn each additional rejection so\n // multi-track failure modes stay diagnosable.\n if (rejections.length > 0) {\n const appendedTracks = Array.from(host.querySelectorAll('daw-track')).filter(\n (el) => !childrenBefore.has(el)\n );\n for (const el of appendedTracks) {\n try {\n el.remove();\n } catch (cleanupErr) {\n console.warn('[dawcore] loadMidi cleanup failed for a track: ' + String(cleanupErr));\n }\n }\n // Let MutationObserver microtasks flush so engine state is consistent\n // before rethrow.\n await Promise.resolve();\n\n // Log every rejection beyond the first so multi-track failure modes are\n // diagnosable (the thrown Error only carries the first). AggregateError\n // would be ideal here but requires ES2021+; defer until the project\n // bumps its lib target.\n for (let i = 1; i < rejections.length; i++) {\n console.warn(\n '[dawcore] loadMidi: additional track failure (' +\n i +\n '): ' +\n stringifyReason(rejections[i])\n );\n }\n const first = rejections[0];\n if (rejections.length > 1) {\n const message =\n 'loadMidi: ' +\n rejections.length +\n ' of ' +\n settlements.length +\n ' tracks failed; first: ' +\n (first instanceof Error ? first.message : stringifyReason(first));\n throw new Error(message);\n }\n throw first instanceof Error ? first : new Error(stringifyReason(first));\n }\n\n // (7) Build the result.\n return {\n trackIds: succeeded.map((el) => el.trackId),\n bpm: parsed.bpm,\n timeSignature: parsed.timeSignature,\n duration: parsed.duration,\n name: parsed.name,\n };\n}\n\n/**\n * Render a non-Error rejection reason as a useful string. Plain `String(x)`\n * gives `\"[object Object]\"` for object rejections, losing all structured info.\n */\nfunction stringifyReason(reason: unknown): string {\n if (reason === null) return 'null';\n if (reason === undefined) return 'undefined';\n if (typeof reason === 'object') {\n try {\n return JSON.stringify(reason);\n } catch {\n return Object.prototype.toString.call(reason);\n }\n }\n return String(reason);\n}\n","/**\n * Recording clip creation extracted from daw-editor to keep the editor under 800 lines.\n * Operates on the editor instance via a narrow interface.\n */\n\nimport type { ClipTrack, PeakData } from '@waveform-playlist/core';\nimport { createClip } from '@waveform-playlist/core';\nimport type { PeakPipeline } from '../workers/peakPipeline';\nimport type { DawErrorDetail } from '../events';\nimport type { TrackDescriptor, ClipDescriptor } from '../types';\n\nexport interface RecordingClipHost {\n readonly samplesPerPixel: number;\n readonly mono: boolean;\n readonly isConnected: boolean;\n readonly effectiveSampleRate: number;\n _tracks: Map<string, TrackDescriptor>;\n _engineTracks: Map<string, ClipTrack>;\n _peaksData: Map<string, PeakData>;\n _clipBuffers: Map<string, AudioBuffer>;\n _peakPipeline: PeakPipeline;\n _engine: {\n setTracks(tracks: ClipTrack[]): void;\n updateTrack?(trackId: string, track: ClipTrack): void;\n } | null;\n _recomputeDuration(): void;\n dispatchEvent(event: Event): boolean;\n}\n\nexport function addRecordedClip(\n host: RecordingClipHost,\n trackId: string,\n buf: AudioBuffer,\n startSample: number,\n durSamples: number,\n offsetSamples = 0\n) {\n // Slice off latency samples so peaks and playback only cover the audible portion\n let trimmedBuf = buf;\n if (offsetSamples > 0 && offsetSamples < buf.length) {\n const trimmed = new AudioBuffer({\n numberOfChannels: buf.numberOfChannels,\n length: durSamples,\n sampleRate: buf.sampleRate,\n });\n for (let ch = 0; ch < buf.numberOfChannels; ch++) {\n const source = buf.getChannelData(ch);\n trimmed.copyToChannel(source.subarray(offsetSamples, offsetSamples + durSamples), ch);\n }\n trimmedBuf = trimmed;\n }\n\n const clip = createClip({\n audioBuffer: trimmedBuf,\n startSample,\n durationSamples: durSamples,\n offsetSamples: 0, // offset already applied by slicing\n gain: 1,\n name: 'Recording',\n });\n host._clipBuffers = new Map(host._clipBuffers).set(clip.id, trimmedBuf);\n host._peakPipeline\n .generatePeaks(trimmedBuf, host.samplesPerPixel, host.mono)\n .then((pd) => {\n host._peaksData = new Map(host._peaksData).set(clip.id, pd);\n const t = host._engineTracks.get(trackId);\n if (!t) {\n // Track was removed during peak generation — clean up orphaned buffer\n const next = new Map(host._clipBuffers);\n next.delete(clip.id);\n host._clipBuffers = next;\n return;\n }\n host._engineTracks = new Map(host._engineTracks).set(trackId, {\n ...t,\n clips: [...t.clips, clip],\n });\n // Keep _tracks in sync so public API and track controls reflect the clip\n const desc = host._tracks.get(trackId);\n if (desc) {\n const sr = host.effectiveSampleRate;\n const clipDesc: ClipDescriptor = {\n kind: 'drop',\n src: '',\n peaksSrc: '',\n start: startSample / sr,\n duration: durSamples / sr,\n offset: 0,\n gain: 1,\n name: 'Recording',\n fadeIn: 0,\n fadeOut: 0,\n fadeType: 'linear',\n midiNotes: null,\n midiChannel: null,\n midiProgram: null,\n };\n host._tracks = new Map(host._tracks).set(trackId, {\n ...desc,\n clips: [...desc.clips, clipDesc],\n });\n }\n host._recomputeDuration();\n const updatedTrack = host._engineTracks.get(trackId);\n if (host._engine?.updateTrack && updatedTrack) {\n host._engine.updateTrack(trackId, updatedTrack);\n } else {\n host._engine?.setTracks([...host._engineTracks.values()]);\n }\n })\n .catch((err) => {\n console.warn('[dawcore] Failed to generate peaks for recorded clip: ' + String(err));\n const next = new Map(host._clipBuffers);\n next.delete(clip.id);\n host._clipBuffers = next;\n if (host.isConnected) {\n host.dispatchEvent(\n new CustomEvent<DawErrorDetail>('daw-error', {\n bubbles: true,\n composed: true,\n detail: { operation: 'recording-peaks', error: err },\n })\n );\n }\n });\n}\n","import type { AudioClip, ClipTrack } from '@waveform-playlist/core';\nimport type { DawClipSplitDetail } from '../events';\n\n// ---------------------------------------------------------------------------\n// Contracts\n// ---------------------------------------------------------------------------\n\n/** Narrow engine contract for split operations. */\nexport interface SplitEngineContract {\n getState(): { selectedTrackId: string | null; tracks: ClipTrack[] };\n splitClip(trackId: string, clipId: string, atSample: number): void;\n}\n\n/** Host interface for splitAtPlayhead. */\nexport interface SplitHost {\n readonly effectiveSampleRate: number;\n readonly currentTime: number;\n readonly isPlaying: boolean;\n readonly engine: SplitEngineContract | null;\n dispatchEvent(event: Event): boolean;\n stop(): void;\n play(time: number): void;\n}\n\n// ---------------------------------------------------------------------------\n// splitAtPlayhead\n// ---------------------------------------------------------------------------\n\n/**\n * Split the clip under the playhead on the selected track.\n * Stops playback before split and resumes after to avoid duplicate audio\n * from Transport rescheduling during playback.\n *\n * Returns true if the split occurred and dispatched a daw-clip-split event.\n * Returns false for any guard failure or engine no-op.\n */\nexport function splitAtPlayhead(host: SplitHost): boolean {\n const wasPlaying = host.isPlaying;\n const time = host.currentTime;\n\n // Check guards before stopping playback — don't interrupt audio for a no-op\n if (!canSplitAtTime(host, time)) return false;\n\n if (wasPlaying) {\n host.stop();\n }\n\n let result: boolean;\n try {\n result = performSplit(host, time);\n } catch (err) {\n console.warn('[dawcore] splitAtPlayhead failed: ' + String(err));\n result = false;\n }\n\n // Always resume if was playing — even on failed split, don't leave audio stopped\n if (wasPlaying) {\n host.play(time);\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Internal\n// ---------------------------------------------------------------------------\n\n/** Pre-flight check — can a split happen at this time without touching playback? */\nfunction canSplitAtTime(host: SplitHost, time: number): boolean {\n const { engine } = host;\n if (!engine) return false;\n\n const state = engine.getState();\n if (!state.selectedTrackId) return false;\n\n const track = state.tracks.find((t) => t.id === state.selectedTrackId);\n if (!track) return false;\n\n const atSample = Math.round(time * host.effectiveSampleRate);\n const clip = findClipAtSample(track.clips, atSample);\n if (!clip) return false;\n\n // MIDI clip splitting requires note slicing — not implemented yet.\n if (clip.midiNotes != null) return false;\n\n return true;\n}\n\n/** Core split logic — finds clip at position, calls engine, diffs state for new IDs. */\nfunction performSplit(host: SplitHost, time: number): boolean {\n const { engine } = host;\n if (!engine) return false;\n\n const stateBefore = engine.getState();\n const { selectedTrackId, tracks } = stateBefore;\n\n if (!selectedTrackId) return false;\n\n const track = tracks.find((t) => t.id === selectedTrackId);\n if (!track) return false;\n\n const atSample = Math.round(time * host.effectiveSampleRate);\n\n const clip = findClipAtSample(track.clips, atSample);\n if (!clip) return false;\n\n const originalClipId = clip.id;\n const clipIdsBefore = new Set(track.clips.map((c) => c.id));\n\n engine.splitClip(selectedTrackId, originalClipId, atSample);\n\n const stateAfter = engine.getState();\n const trackAfter = stateAfter.tracks.find((t) => t.id === selectedTrackId);\n if (!trackAfter) {\n console.warn(\n '[dawcore] splitAtPlayhead: track \"' + selectedTrackId + '\" disappeared after split'\n );\n return false;\n }\n\n // Engine replaces the original clip with two halves; both get new IDs\n const newClips = trackAfter.clips.filter((c) => !clipIdsBefore.has(c.id));\n if (newClips.length !== 2) {\n if (newClips.length > 0) {\n console.warn(\n '[dawcore] splitAtPlayhead: expected 2 new clips after split but got ' + newClips.length\n );\n }\n return false;\n }\n\n // Sort by startSample: lower = left, higher = right\n const sorted = [...newClips].sort((a, b) => a.startSample - b.startSample);\n const leftClipId = sorted[0].id;\n const rightClipId = sorted[1].id;\n\n host.dispatchEvent(\n new CustomEvent<DawClipSplitDetail>('daw-clip-split', {\n bubbles: true,\n composed: true,\n detail: {\n trackId: selectedTrackId,\n originalClipId,\n leftClipId,\n rightClipId,\n },\n })\n );\n\n return true;\n}\n\n/**\n * Finds a clip that strictly contains the given sample position.\n * The position must be > clip.startSample and < clip.startSample + clip.durationSamples.\n */\nfunction findClipAtSample(clips: AudioClip[], atSample: number): AudioClip | undefined {\n return clips.find(\n (c) => atSample > c.startSample && atSample < c.startSample + c.durationSamples\n );\n}\n","import type { AudioClip, ClipTrack, PeakData } from '@waveform-playlist/core';\nimport type { PeakPipeline } from '../workers/peakPipeline';\n\n/** Host interface for clip peak synchronization. */\nexport interface ClipPeakSyncHost {\n /** The effective samplesPerPixel for rendering (tick-derived in beats mode). */\n readonly renderSamplesPerPixel: number;\n readonly mono: boolean;\n _clipBuffers: Map<string, AudioBuffer>;\n _clipOffsets: Map<string, { offsetSamples: number; durationSamples: number }>;\n _peaksData: Map<string, PeakData>;\n _peakPipeline: PeakPipeline;\n}\n\n/**\n * Regenerate peaks for clips that are new or whose offset/duration changed.\n * Handles split (new clip IDs) and trim (same ID, changed bounds).\n *\n * Called from the statechange handler when tracksVersion changes.\n */\nexport function syncPeaksForChangedClips(host: ClipPeakSyncHost, tracks: ClipTrack[]): void {\n // Collect all current clip IDs for orphan detection\n const currentClipIds = new Set<string>();\n\n for (const track of tracks) {\n for (const clip of track.clips) {\n currentClipIds.add(clip.id);\n\n // Check if peaks need regeneration: new clip or changed offset/duration\n const cached = host._clipOffsets.get(clip.id);\n const needsPeaks =\n !host._peaksData.has(clip.id) ||\n !cached ||\n cached.offsetSamples !== clip.offsetSamples ||\n cached.durationSamples !== clip.durationSamples;\n\n if (!needsPeaks) continue;\n\n const audioBuffer =\n clip.audioBuffer ??\n host._clipBuffers.get(clip.id) ??\n findAudioBufferForClip(host, clip, track);\n if (!audioBuffer) {\n console.warn(\n '[dawcore] syncPeaksForChangedClips: no AudioBuffer for clip ' +\n clip.id +\n ' — waveform will be blank'\n );\n continue;\n }\n\n // Update cached state\n host._clipBuffers = new Map(host._clipBuffers).set(clip.id, audioBuffer);\n host._clipOffsets = new Map(host._clipOffsets).set(clip.id, {\n offsetSamples: clip.offsetSamples,\n durationSamples: clip.durationSamples,\n });\n\n // Generate peaks asynchronously\n host._peakPipeline\n .generatePeaks(\n audioBuffer,\n host.renderSamplesPerPixel,\n host.mono,\n clip.offsetSamples,\n clip.durationSamples\n )\n .then((peakData) => {\n host._peaksData = new Map(host._peaksData).set(clip.id, peakData);\n })\n .catch((err) => {\n console.warn(\n '[dawcore] Failed to generate peaks for clip ' + clip.id + ': ' + String(err)\n );\n });\n }\n }\n\n // Clean up orphaned entries for clip IDs no longer in any track\n // (e.g., the original clip after a split is replaced by two new clips)\n cleanupOrphanedClipData(host, currentClipIds);\n}\n\n/**\n * Remove entries from per-clip Maps for clip IDs that no longer exist in any track.\n * Prevents memory leaks from orphaned AudioBuffer references after split operations.\n */\nfunction cleanupOrphanedClipData(host: ClipPeakSyncHost, currentClipIds: Set<string>): void {\n let buffersChanged = false;\n let peaksChanged = false;\n\n for (const id of host._clipBuffers.keys()) {\n if (!currentClipIds.has(id)) {\n host._clipBuffers.delete(id);\n buffersChanged = true;\n }\n }\n let offsetsChanged = false;\n for (const id of host._clipOffsets.keys()) {\n if (!currentClipIds.has(id)) {\n host._clipOffsets.delete(id);\n offsetsChanged = true;\n }\n }\n for (const id of host._peaksData.keys()) {\n if (!currentClipIds.has(id)) {\n host._peaksData.delete(id);\n peaksChanged = true;\n }\n }\n\n // Reassign Maps that changed — _peaksData is @state() (triggers Lit re-render),\n // _clipBuffers uses reference identity for change detection in syncPeaksForChangedClips\n if (buffersChanged) {\n host._clipBuffers = new Map(host._clipBuffers);\n }\n if (offsetsChanged) {\n host._clipOffsets = new Map(host._clipOffsets);\n }\n if (peaksChanged) {\n host._peaksData = new Map(host._peaksData);\n }\n}\n\n/** Find an AudioBuffer for a clip by checking siblings on the same track. */\nfunction findAudioBufferForClip(\n host: ClipPeakSyncHost,\n clip: AudioClip,\n track: ClipTrack\n): AudioBuffer | null {\n for (const sibling of track.clips) {\n if (sibling.id === clip.id) continue;\n const buf = host._clipBuffers.get(sibling.id);\n if (buf) return buf;\n }\n return null;\n}\n","/**\n * Load pre-computed waveform data from a .dat or .json file (BBC audiowaveform format).\n */\n\nimport WaveformData from 'waveform-data';\n\n/**\n * Fetch and parse a waveform data file (.dat binary or .json).\n */\nexport async function loadWaveformDataFromUrl(src: string): Promise<WaveformData> {\n const response = await fetch(src);\n\n if (!response.ok) {\n throw new Error('[dawcore] Failed to fetch peaks data: ' + response.statusText);\n }\n\n // Detect binary format from pathname (ignores query string and fragment)\n const { pathname } = new URL(src, globalThis.location?.href ?? 'http://localhost');\n const isBinary = pathname.toLowerCase().endsWith('.dat');\n\n if (isBinary) {\n const arrayBuffer = await response.arrayBuffer();\n return WaveformData.create(arrayBuffer);\n } else {\n const json = await response.json();\n return WaveformData.create(json);\n }\n}\n","import type { ReactiveController, ReactiveControllerHost } from 'lit';\n\n// Line scrolling uses 16px per line (standard line height).\nconst LINE_HEIGHT_PX = 16;\n\n/**\n * Frozen-panes scroll sync. The editor's `.scroll-area` owns both scroll\n * axes; this controller keeps the ruler band (x) and controls column (y)\n * visually locked to it by applying translate3d transforms on EVERY scroll\n * event. (ViewportController's 100px threshold exists for chunk\n * virtualization and is too coarse for visual sync.)\n *\n * Also forwards wheel events from ALL elements matched by `wheelForwardSelector`\n * to the scroll container for both axes:\n * - Horizontal scrub (deltaX) — ruler band stays in sync when the user\n * scrolls the timeline left/right while hovering the ruler.\n * - Vertical scroll (deltaY) — controls column lets the user scroll track\n * rows while hovering the track controls.\n *\n * preventDefault fires only when at least one axis actually moved, so page\n * scrolling is unaffected for unconstrained editors and boundary scrolling\n * chains properly.\n */\nexport class ScrollSyncController implements ReactiveController {\n private _host: ReactiveControllerHost & HTMLElement;\n private _scrollContainer: HTMLElement | null = null;\n private _wheelTargets: Set<HTMLElement> = new Set();\n private _warnedX = false;\n private _warnedY = false;\n\n /** Selector (in host shadow DOM) for the scroll container. */\n scrollSelector = '';\n /** Selector for the element receiving translate3d(-scrollLeft, 0, 0). */\n xTargetSelector = '';\n /** Selector for the element receiving translate3d(0, -scrollTop, 0). */\n yTargetSelector = '';\n /**\n * Selector (or comma-separated selectors) for elements whose wheel events\n * forward to the scroll container. All matching elements receive listeners.\n */\n wheelForwardSelector = '';\n\n constructor(host: ReactiveControllerHost & HTMLElement) {\n this._host = host;\n host.addController(this);\n }\n\n hostConnected() {\n // Defer so the Shadow DOM renders before querying (same pattern as\n // ViewportController).\n requestAnimationFrame(() => {\n if (!this._host.isConnected) return;\n this._attach();\n if (!this._scrollContainer && this.scrollSelector) {\n console.warn(\n '[dawcore] ScrollSyncController: scroll container not found for \"' +\n this.scrollSelector +\n '\"'\n );\n }\n });\n }\n\n hostDisconnected() {\n this._scrollContainer?.removeEventListener('scroll', this._onScroll);\n this._scrollContainer = null;\n for (const target of this._wheelTargets) {\n target.removeEventListener('wheel', this._onWheel);\n }\n this._wheelTargets.clear();\n }\n\n /**\n * Re-attach and re-apply transforms from the current scroll position.\n * Called from the host's updated() so elements created by a re-render\n * (e.g. the ruler appearing when the first track loads) pick up the\n * current offset and listeners.\n */\n sync() {\n this._attach();\n }\n\n private _query(selector: string): HTMLElement | null {\n return selector ? (this._host.shadowRoot?.querySelector(selector) as HTMLElement | null) : null;\n }\n\n private _queryAll(selector: string): HTMLElement[] {\n if (!selector) return [];\n return Array.from(this._host.shadowRoot?.querySelectorAll(selector) ?? []) as HTMLElement[];\n }\n\n private _attach() {\n const container = this._query(this.scrollSelector);\n if (!container) {\n if (this._scrollContainer && !this._scrollContainer.isConnected) {\n console.warn(\n '[dawcore] ScrollSyncController: scroll container \"' +\n this.scrollSelector +\n '\" was removed from the DOM — detaching listeners until it reappears.'\n );\n this._scrollContainer.removeEventListener('scroll', this._onScroll);\n this._scrollContainer = null;\n for (const t of this._wheelTargets) t.removeEventListener('wheel', this._onWheel);\n this._wheelTargets.clear();\n }\n return;\n }\n if (container !== this._scrollContainer) {\n this._scrollContainer?.removeEventListener('scroll', this._onScroll);\n this._scrollContainer = container;\n container.addEventListener('scroll', this._onScroll, { passive: true });\n }\n\n // Diff the current set of matched wheel targets.\n const nextTargets = new Set(this._queryAll(this.wheelForwardSelector));\n for (const old of this._wheelTargets) {\n if (!nextTargets.has(old)) {\n old.removeEventListener('wheel', this._onWheel);\n this._wheelTargets.delete(old);\n }\n }\n for (const next of nextTargets) {\n if (!this._wheelTargets.has(next)) {\n next.addEventListener('wheel', this._onWheel, { passive: false });\n this._wheelTargets.add(next);\n }\n }\n\n this._apply();\n }\n\n private _onScroll = () => {\n this._apply();\n };\n\n private _onWheel = (e: WheelEvent) => {\n const sc = this._scrollContainer;\n if (!sc) return;\n\n const scale =\n e.deltaMode === WheelEvent.DOM_DELTA_LINE\n ? LINE_HEIGHT_PX\n : e.deltaMode === WheelEvent.DOM_DELTA_PAGE\n ? sc.clientHeight // used for Y; X uses clientWidth below\n : 1;\n\n const scaleX = e.deltaMode === WheelEvent.DOM_DELTA_PAGE ? sc.clientWidth : scale;\n\n const beforeLeft = sc.scrollLeft;\n const beforeTop = sc.scrollTop;\n\n sc.scrollLeft += e.deltaX * scaleX;\n sc.scrollTop += e.deltaY * scale;\n\n if (sc.scrollLeft !== beforeLeft || sc.scrollTop !== beforeTop) {\n e.preventDefault();\n }\n };\n\n private _apply() {\n const sc = this._scrollContainer;\n if (!sc) return;\n // Re-query targets each time: Lit conditional templates create/replace\n // these elements between renders (e.g. the header row appears with the\n // first loaded track).\n const xTarget = this._query(this.xTargetSelector);\n if (xTarget) {\n xTarget.style.transform = `translate3d(${-sc.scrollLeft}px, 0, 0)`;\n this._warnedX = false;\n } else if (this.xTargetSelector && sc.scrollLeft !== 0 && !this._warnedX) {\n this._warnedX = true;\n console.warn(\n '[dawcore] ScrollSyncController: x target \"' +\n this.xTargetSelector +\n '\" not found while scrolled — the synced pane will appear frozen. Check the selector, or clear it if the target is intentionally not rendered.'\n );\n }\n const yTarget = this._query(this.yTargetSelector);\n if (yTarget) {\n yTarget.style.transform = `translate3d(0, ${-sc.scrollTop}px, 0)`;\n this._warnedY = false;\n } else if (this.yTargetSelector && sc.scrollTop !== 0 && !this._warnedY) {\n this._warnedY = true;\n console.warn(\n '[dawcore] ScrollSyncController: y target \"' +\n this.yTargetSelector +\n '\" not found while scrolled — the synced pane will appear frozen. Check the selector, or clear it if the target is intentionally not rendered.'\n );\n }\n }\n}\n","import { LitElement, html, css } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\n\n@customElement('daw-selection')\nexport class DawSelectionElement extends LitElement {\n @property({ type: Number, attribute: false }) startPx = 0;\n @property({ type: Number, attribute: false }) endPx = 0;\n\n static styles = css`\n :host {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n pointer-events: none;\n z-index: 5;\n }\n div {\n position: absolute;\n top: 0;\n bottom: 0;\n background: var(--daw-selection-color, rgba(99, 199, 95, 0.3));\n }\n `;\n\n render() {\n const left = Math.min(this.startPx, this.endPx);\n const width = Math.abs(this.endPx - this.startPx);\n if (width === 0) return html``;\n return html`<div style=\"left: ${left}px; width: ${width}px;\"></div>`;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-selection': DawSelectionElement;\n }\n}\n","import { html, css } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\nimport { DawTransportButton } from './daw-transport-button';\n\n@customElement('daw-record-button')\nexport class DawRecordButtonElement extends DawTransportButton {\n @state() private _isRecording = false;\n private _targetRef: HTMLElement | null = null;\n private _onStart = () => {\n this._isRecording = true;\n };\n private _onComplete = () => {\n this._isRecording = false;\n };\n private _onError = () => {\n this._isRecording = false;\n };\n\n static override styles = [\n DawTransportButton.styles,\n css`\n button[data-recording] {\n color: #d08070;\n border-color: #d08070;\n background: rgba(208, 128, 112, 0.15);\n }\n `,\n ];\n\n connectedCallback() {\n super.connectedCallback();\n // Defer so <daw-transport for=\"...\"> and the target editor are resolved\n requestAnimationFrame(() => this._listenToTarget());\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this._cleanupListeners();\n }\n\n private _listenToTarget() {\n const target = this.target;\n if (!target) return;\n this._targetRef = target;\n target.addEventListener('daw-recording-start', this._onStart);\n target.addEventListener('daw-recording-complete', this._onComplete);\n target.addEventListener('daw-recording-error', this._onError);\n }\n\n private _cleanupListeners() {\n if (this._targetRef) {\n this._targetRef.removeEventListener('daw-recording-start', this._onStart);\n this._targetRef.removeEventListener('daw-recording-complete', this._onComplete);\n this._targetRef.removeEventListener('daw-recording-error', this._onError);\n this._targetRef = null;\n }\n }\n\n render() {\n return html`\n <button part=\"button\" ?data-recording=${this._isRecording} @click=${this._onClick}>\n <slot>Record</slot>\n </button>\n `;\n }\n\n private _onClick() {\n // Start-only — stop is handled by the stop button\n if (this._isRecording) return;\n const target = this.target;\n if (!target) {\n console.warn(\n '[dawcore] <daw-record-button> has no target. Check <daw-transport for=\"...\"> references a valid <daw-editor> id.'\n );\n return;\n }\n target.startRecording(target.recordingStream);\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-record-button': DawRecordButtonElement;\n }\n}\n","import { LitElement } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { handleKeyboardEvent } from '@waveform-playlist/core';\nimport type { KeyboardShortcut } from '@waveform-playlist/core';\nimport type { DawEditorElement } from './daw-editor';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Key binding for remapping — derived from KeyboardShortcut to stay in sync. */\nexport type KeyBinding = Pick<\n KeyboardShortcut,\n 'key' | 'ctrlKey' | 'shiftKey' | 'metaKey' | 'altKey'\n>;\n\nexport interface PlaybackShortcutMap {\n playPause?: KeyBinding;\n stop?: KeyBinding;\n rewindToStart?: KeyBinding;\n}\n\nexport interface SplittingShortcutMap {\n splitAtPlayhead?: KeyBinding;\n}\n\nexport interface UndoShortcutMap {\n undo?: KeyBinding;\n redo?: KeyBinding;\n}\n\n// ---------------------------------------------------------------------------\n// Element\n// ---------------------------------------------------------------------------\n\n/**\n * Render-less element that enables keyboard shortcuts for a parent <daw-editor>.\n * Place inside the editor element. Boolean attributes enable preset categories;\n * JS properties allow remapping and custom shortcuts.\n *\n * ```html\n * <daw-editor>\n * <daw-keyboard-shortcuts playback splitting undo></daw-keyboard-shortcuts>\n * </daw-editor>\n * ```\n */\n@customElement('daw-keyboard-shortcuts')\nexport class DawKeyboardShortcutsElement extends LitElement {\n // --- Preset attributes ---\n @property({ type: Boolean }) playback = false;\n @property({ type: Boolean }) splitting = false;\n @property({ type: Boolean }) undo = false;\n\n // --- JS properties for remapping ---\n playbackShortcuts: PlaybackShortcutMap | null = null;\n splittingShortcuts: SplittingShortcutMap | null = null;\n undoShortcuts: UndoShortcutMap | null = null;\n\n /** Additional custom shortcuts. */\n customShortcuts: KeyboardShortcut[] = [];\n\n private _editor: DawEditorElement | null = null;\n private _cachedShortcuts: KeyboardShortcut[] | null = null;\n\n /** All active shortcuts (read-only, cached). */\n get shortcuts(): KeyboardShortcut[] {\n if (!this._cachedShortcuts) {\n this._cachedShortcuts = this._buildShortcuts();\n }\n return this._cachedShortcuts;\n }\n\n /** Invalidate cached shortcuts when Lit properties change. */\n override updated(): void {\n this._cachedShortcuts = null;\n }\n\n // --- Lifecycle ---\n\n override connectedCallback(): void {\n super.connectedCallback();\n this._editor = this.closest('daw-editor') as DawEditorElement | null;\n if (!this._editor) {\n console.warn(\n '[dawcore] <daw-keyboard-shortcuts> must be placed inside a <daw-editor>. ' +\n 'Preset shortcuts (playback, splitting, undo) will be inactive; only customShortcuts will fire.'\n );\n }\n document.addEventListener('keydown', this._onKeyDown);\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n document.removeEventListener('keydown', this._onKeyDown);\n this._editor = null;\n }\n\n // No shadow DOM — render-less element\n override createRenderRoot(): this {\n return this;\n }\n\n // --- Shortcut building ---\n\n private _buildShortcuts(): KeyboardShortcut[] {\n const editor = this._editor;\n if (!editor) return this.customShortcuts;\n\n const result: KeyboardShortcut[] = [];\n\n if (this.playback) {\n const map = this.playbackShortcuts;\n // Explicit ctrlKey/metaKey: false prevents Cmd+Space (Spotlight on Mac)\n // and Ctrl+0 etc. from triggering playback shortcuts.\n result.push(\n this._makeShortcut(\n map?.playPause ?? { key: ' ', ctrlKey: false, metaKey: false },\n () => editor.togglePlayPause(),\n 'Play/Pause'\n ),\n this._makeShortcut(\n map?.stop ?? { key: 'Escape', ctrlKey: false, metaKey: false },\n () => editor.stop(),\n 'Stop'\n ),\n this._makeShortcut(\n map?.rewindToStart ?? { key: '0', ctrlKey: false, metaKey: false },\n () => editor.seekTo(0),\n 'Rewind to start'\n )\n );\n }\n\n if (this.splitting) {\n const map = this.splittingShortcuts;\n const binding = map?.splitAtPlayhead ?? {\n key: 's',\n ctrlKey: false,\n metaKey: false,\n altKey: false,\n };\n result.push(this._makeShortcut(binding, () => editor.splitAtPlayhead(), 'Split at playhead'));\n }\n\n if (this.undo) {\n const map = this.undoShortcuts;\n const undoBinding = map?.undo ?? { key: 'z' };\n const redoBinding = map?.redo ?? { key: 'z', shiftKey: true };\n\n // Undo: Ctrl+Z (Win/Linux) and Cmd+Z (Mac)\n // Use === undefined to distinguish \"not specified\" (auto-expand) from\n // \"explicitly false\" (user wants no modifier — respect their intent).\n if (undoBinding.ctrlKey === undefined && undoBinding.metaKey === undefined) {\n // Only set shiftKey: false when user didn't provide a value\n const undoShift = undoBinding.shiftKey === undefined ? { shiftKey: false } : {};\n result.push(\n this._makeShortcut(\n { ...undoBinding, ctrlKey: true, ...undoShift },\n () => editor.undo(),\n 'Undo'\n ),\n this._makeShortcut(\n { ...undoBinding, metaKey: true, ...undoShift },\n () => editor.undo(),\n 'Undo'\n )\n );\n } else {\n result.push(this._makeShortcut(undoBinding, () => editor.undo(), 'Undo'));\n }\n\n // Redo: Ctrl+Shift+Z (Win/Linux) and Cmd+Shift+Z (Mac)\n if (redoBinding.ctrlKey === undefined && redoBinding.metaKey === undefined) {\n const redoShift = redoBinding.shiftKey === undefined ? { shiftKey: true } : {};\n result.push(\n this._makeShortcut(\n { ...redoBinding, ctrlKey: true, ...redoShift },\n () => editor.redo(),\n 'Redo'\n ),\n this._makeShortcut(\n { ...redoBinding, metaKey: true, ...redoShift },\n () => editor.redo(),\n 'Redo'\n )\n );\n } else {\n result.push(this._makeShortcut(redoBinding, () => editor.redo(), 'Redo'));\n }\n }\n\n result.push(...this.customShortcuts);\n return result;\n }\n\n private _makeShortcut(\n binding: KeyBinding,\n action: () => void,\n description: string\n ): KeyboardShortcut {\n return {\n key: binding.key,\n ...(binding.ctrlKey !== undefined && { ctrlKey: binding.ctrlKey }),\n ...(binding.shiftKey !== undefined && { shiftKey: binding.shiftKey }),\n ...(binding.metaKey !== undefined && { metaKey: binding.metaKey }),\n ...(binding.altKey !== undefined && { altKey: binding.altKey }),\n action,\n description,\n };\n }\n\n // --- Event handler ---\n\n private _onKeyDown = (e: KeyboardEvent) => {\n const shortcuts = this.shortcuts;\n if (shortcuts.length === 0) return;\n try {\n handleKeyboardEvent(e, shortcuts, true);\n } catch (err) {\n console.warn('[dawcore] Keyboard shortcut failed (key=' + e.key + '): ' + String(err));\n const target = this._editor ?? this;\n target.dispatchEvent(\n new CustomEvent('daw-error', {\n bubbles: true,\n composed: true,\n detail: { operation: 'keyboard-shortcut', key: e.key, error: err },\n })\n );\n }\n };\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-keyboard-shortcuts': DawKeyboardShortcutsElement;\n }\n}\n","import { LitElement, html, css } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport type { PropertyValues } from 'lit';\n\nconst MAX_CANVAS_WIDTH = 1000;\n\ninterface SpectrogramHost extends HTMLElement {\n _spectrogramRegisterCanvas?: (reg: {\n canvasId: string;\n canvas: OffscreenCanvas;\n clipId: string;\n trackId: string;\n channelIndex: number;\n chunkIndex: number;\n globalPixelOffset: number;\n widthPx: number;\n heightPx: number;\n }) => void;\n _spectrogramUnregisterCanvas?: (canvasId: string) => void;\n}\n\n@customElement('daw-spectrogram')\nexport class DawSpectrogramElement extends LitElement {\n @property({ attribute: false }) clipId = '';\n @property({ attribute: false }) trackId = '';\n @property({ type: Number, attribute: false }) channelIndex = 0;\n @property({ type: Number, attribute: false }) length = 0;\n @property({ type: Number, attribute: false }) waveHeight = 128;\n\n @property({ type: Number, attribute: false, noAccessor: true })\n get samplesPerPixel(): number {\n return this._samplesPerPixel;\n }\n set samplesPerPixel(value: number) {\n if (!Number.isFinite(value) || value <= 0) {\n console.warn('[dawcore] daw-spectrogram samplesPerPixel ' + value + ' is invalid — ignored');\n return;\n }\n const old = this._samplesPerPixel;\n this._samplesPerPixel = value;\n this.requestUpdate('samplesPerPixel', old);\n }\n private _samplesPerPixel = 1024;\n\n @property({ type: Number, attribute: false, noAccessor: true })\n get sampleRate(): number {\n return this._sampleRate;\n }\n set sampleRate(value: number) {\n if (!Number.isFinite(value) || value <= 0) {\n console.warn('[dawcore] daw-spectrogram sampleRate ' + value + ' is invalid — ignored');\n return;\n }\n const old = this._sampleRate;\n this._sampleRate = value;\n this.requestUpdate('sampleRate', old);\n }\n private _sampleRate = 44100;\n\n @property({ type: Number, attribute: false }) clipOffsetSeconds = 0;\n @property({ type: Number, attribute: false }) visibleStart = -Infinity;\n @property({ type: Number, attribute: false }) visibleEnd = Infinity;\n @property({ type: Number, attribute: false }) originX = 0;\n\n static styles = css`\n :host {\n display: block;\n position: relative;\n background: var(--daw-spectrogram-background, #000);\n }\n canvas {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n pointer-events: none;\n }\n `;\n\n private _canvases: HTMLCanvasElement[] = [];\n private _registeredCanvasIds: string[] = [];\n private _warnedNoHost = false;\n\n /**\n * Walk up to the editor host. `closest('daw-editor')` does NOT cross\n * shadow boundaries — and this element lives inside the editor's shadow\n * DOM — so use getRootNode().host to step out.\n */\n private _findHostEditor(): SpectrogramHost | null {\n const root = this.getRootNode();\n const host = root instanceof ShadowRoot ? root.host : null;\n if (!host) return null;\n if (host.tagName === 'DAW-EDITOR') return host as SpectrogramHost;\n return host.closest('daw-editor') as SpectrogramHost | null;\n }\n\n willUpdate(changed: PropertyValues): void {\n const layoutChanged =\n changed.has('length') ||\n changed.has('waveHeight') ||\n changed.has('samplesPerPixel') ||\n changed.has('clipId') ||\n changed.has('channelIndex');\n if (layoutChanged) {\n this._rebuildChunks();\n }\n }\n\n private _rebuildChunks(): void {\n this._unregisterAllCanvases();\n this._canvases = [];\n\n if (this.length <= 0) return;\n\n const chunkCount = Math.ceil(this.length / MAX_CANVAS_WIDTH);\n for (let i = 0; i < chunkCount; i++) {\n const widthPx = Math.min(MAX_CANVAS_WIDTH, this.length - i * MAX_CANVAS_WIDTH);\n const canvas = document.createElement('canvas');\n canvas.style.left = i * MAX_CANVAS_WIDTH + 'px';\n canvas.style.width = widthPx + 'px';\n const dpr = window.devicePixelRatio || 1;\n canvas.width = widthPx * dpr;\n canvas.height = this.waveHeight * dpr;\n this._canvases.push(canvas);\n }\n }\n\n protected updated(_changed: PropertyValues): void {\n if (this._registeredCanvasIds.length === 0 && this._canvases.length > 0) {\n requestAnimationFrame(() => this._registerCanvases());\n }\n }\n\n private _registerCanvases(): void {\n const editor = this._findHostEditor();\n if (!editor || typeof editor._spectrogramRegisterCanvas !== 'function') {\n if (!this._warnedNoHost) {\n this._warnedNoHost = true;\n console.warn(\n '[dawcore] <daw-spectrogram> (clip ' +\n this.clipId +\n ') could not find host <daw-editor>. Canvases will not render. ' +\n 'Ensure the element is mounted inside a <daw-editor>.'\n );\n }\n return;\n }\n\n for (let i = 0; i < this._canvases.length; i++) {\n const canvas = this._canvases[i];\n const canvasId = this.clipId + '-ch' + this.channelIndex + '-chunk' + i;\n let offscreen: OffscreenCanvas;\n try {\n offscreen = canvas.transferControlToOffscreen();\n } catch (err) {\n console.warn(\n '[dawcore] daw-spectrogram transferControlToOffscreen failed for ' +\n canvasId +\n ': ' +\n (err instanceof Error ? err.message : String(err))\n );\n continue;\n }\n editor._spectrogramRegisterCanvas({\n canvasId,\n canvas: offscreen,\n clipId: this.clipId,\n trackId: this.trackId,\n channelIndex: this.channelIndex,\n chunkIndex: i,\n globalPixelOffset: this.originX + i * MAX_CANVAS_WIDTH,\n widthPx: parseFloat(canvas.style.width),\n heightPx: this.waveHeight,\n });\n this._registeredCanvasIds.push(canvasId);\n }\n }\n\n private _unregisterAllCanvases(): void {\n const editor = this._findHostEditor();\n if (editor && typeof editor._spectrogramUnregisterCanvas === 'function') {\n for (const id of this._registeredCanvasIds) {\n editor._spectrogramUnregisterCanvas(id);\n }\n }\n this._registeredCanvasIds = [];\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n this._unregisterAllCanvases();\n }\n\n render() {\n return html`${this._canvases.map((c) => c)}`;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-spectrogram': DawSpectrogramElement;\n }\n}\n"],"mappings":"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","names":["LitElement","customElement","property","LitElement","property","customElement","LitElement","customElement","property","LitElement","property","customElement","LitElement","html","css","customElement","property","MAX_CANVAS_WIDTH","LAYOUT_PROPS","LitElement","html","css","property","customElement","LitElement","html","css","customElement","LitElement","html","css","customElement","LitElement","customElement","property","LitElement","property","customElement","html","customElement","LitElement","css","html","customElement","html","css","customElement","state","html","css","state","customElement","html","customElement","html","customElement","LitElement","html","css","customElement","property","state","createClip","createTrack","bits","numChannels","LitElement","html","css","customElement","property","MAX_CANVAS_WIDTH","LitElement","html","css","property","customElement","LitElement","html","css","customElement","property","LitElement","html","css","property","customElement","LitElement","html","css","customElement","property","MAX_CANVAS_WIDTH","LitElement","html","css","property","customElement","css","state","snapTickToGrid","snapTickToGrid","engine","createClip","state","WaveformData","LitElement","createClip","updatedClip","updatedClips","updatedTrack","clip","createTrack","audioBuffer","html","css","property","state","customElement","LitElement","html","css","customElement","property","LitElement","html","css","property","customElement","html","css","customElement","state","html","css","state","customElement","LitElement","customElement","property","LitElement","property","customElement","LitElement","html","css","customElement","property","MAX_CANVAS_WIDTH","LitElement","html","css","property","customElement"]}
1
+ {"version":3,"sources":["../src/elements/daw-clip.ts","../src/elements/daw-track.ts","../src/elements/daw-waveform.ts","../src/utils/peak-rendering.ts","../src/utils/viewport.ts","../src/elements/daw-piano-roll.ts","../src/elements/daw-playhead.ts","../src/controllers/animation-controller.ts","../src/elements/daw-transport.ts","../src/elements/daw-play-button.ts","../src/elements/daw-transport-button.ts","../src/elements/daw-pause-button.ts","../src/elements/daw-stop-button.ts","../src/elements/daw-editor.ts","../src/types.ts","../src/workers/peaksWorker.ts","../src/workers/waveformDataUtils.ts","../src/workers/peakPipeline.ts","../src/elements/daw-ruler.ts","../src/utils/time-format.ts","../src/utils/smart-scale.ts","../src/utils/musical-tick-cache.ts","../src/elements/daw-track-controls.ts","../src/elements/daw-grid.ts","../src/styles/theme.ts","../src/controllers/viewport-controller.ts","../src/controllers/audio-resume-controller.ts","../src/controllers/recording-controller.ts","../src/controllers/spectrogram-controller.ts","../src/interactions/pointer-handler.ts","../src/interactions/constants.ts","../src/interactions/clip-pointer-handler.ts","../src/interactions/file-loader.ts","../src/interactions/midi-loader.ts","../src/interactions/recording-clip.ts","../src/interactions/split-handler.ts","../src/interactions/clip-peak-sync.ts","../src/interactions/peaks-loader.ts","../src/controllers/scroll-sync-controller.ts","../src/elements/daw-selection.ts","../src/elements/daw-record-button.ts","../src/elements/daw-keyboard-shortcuts.ts","../src/elements/daw-spectrogram.ts"],"sourcesContent":["import { LitElement } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport type { PropertyValues } from 'lit';\nimport type { MidiNoteData } from '@waveform-playlist/core';\n\n@customElement('daw-clip')\nexport class DawClipElement extends LitElement {\n @property() src = '';\n @property({ attribute: 'peaks-src' }) peaksSrc = '';\n /**\n * Timeline position in seconds. JS property only — NOT reflected to the\n * `start` attribute (Lit's `reflect` defaults to false). Tests that need\n * to assert clip position must read this property directly; reading\n * `el.getAttribute('start')` returns `null` regardless of correctness.\n */\n @property({ type: Number }) start = 0;\n @property({ type: Number }) duration = 0;\n @property({ type: Number }) offset = 0;\n @property({ type: Number }) gain = 1;\n @property() name = '';\n @property() color = '';\n @property({ type: Number, attribute: 'fade-in' }) fadeIn = 0;\n @property({ type: Number, attribute: 'fade-out' }) fadeOut = 0;\n @property({ attribute: 'fade-type' }) fadeType = 'linear';\n\n /** MIDI notes — JS property only, not reflected (note arrays are too large for attributes). */\n @property({ attribute: false }) midiNotes: MidiNoteData[] | null = null;\n\n /** MIDI channel (0-indexed). Channel 9 = GM percussion. */\n @property({ type: Number, attribute: 'midi-channel', noAccessor: true })\n get midiChannel(): number | null {\n return this._midiChannel;\n }\n set midiChannel(value: number | null) {\n const old = this._midiChannel;\n if (value === null) {\n this._midiChannel = null;\n this.requestUpdate('midiChannel', old);\n return;\n }\n if (!Number.isFinite(value) || !Number.isInteger(value) || value < 0 || value > 15) {\n console.warn('[dawcore] daw-clip midi-channel ' + value + ' is out of range 0-15 — ignored');\n return;\n }\n this._midiChannel = value;\n this.requestUpdate('midiChannel', old);\n }\n private _midiChannel: number | null = null;\n\n /** MIDI program (GM instrument 0-127). Used by SoundFontToneTrack. */\n @property({ type: Number, attribute: 'midi-program', noAccessor: true })\n get midiProgram(): number | null {\n return this._midiProgram;\n }\n set midiProgram(value: number | null) {\n const old = this._midiProgram;\n if (value === null) {\n this._midiProgram = null;\n this.requestUpdate('midiProgram', old);\n return;\n }\n if (!Number.isFinite(value) || !Number.isInteger(value) || value < 0 || value > 127) {\n console.warn('[dawcore] daw-clip midi-program ' + value + ' is out of range 0-127 — ignored');\n return;\n }\n this._midiProgram = value;\n this.requestUpdate('midiProgram', old);\n }\n private _midiProgram: number | null = null;\n\n readonly clipId = crypto.randomUUID();\n\n // Light DOM — no visual rendering, just a data container\n createRenderRoot() {\n return this;\n }\n\n connectedCallback() {\n super.connectedCallback();\n // Defer dispatch so:\n // 1. The editor's connectedCallback (which registers the daw-clip-connected\n // listener) has time to run when parsed all-at-once.\n // 2. The parent's deferred daw-track-connected event fires first; the editor\n // reads <daw-clip> children synchronously via _readTrackDescriptor, so\n // this event is the canonical late-append signal.\n // _onClipConnected skips this event during the parent track's initial load\n // (parent not yet in _engineTracks) and runs an incremental load otherwise.\n setTimeout(() => {\n this.dispatchEvent(\n new CustomEvent('daw-clip-connected', {\n bubbles: true,\n composed: true,\n detail: { clipId: this.clipId, element: this },\n })\n );\n }, 0);\n }\n\n // Removal is detected by the editor's MutationObserver — detached elements\n // cannot bubble events to ancestors.\n\n private _hasRendered = false;\n\n updated(changed: PropertyValues) {\n if (!this._hasRendered) {\n this._hasRendered = true;\n return;\n }\n const clipProps = [\n 'src',\n 'peaksSrc',\n 'start',\n 'duration',\n 'offset',\n 'gain',\n 'name',\n 'fadeIn',\n 'fadeOut',\n 'fadeType',\n 'midiNotes',\n 'midiChannel',\n 'midiProgram',\n ];\n if (clipProps.some((p) => changed.has(p as keyof this))) {\n // Resolve parent <daw-track> at dispatch time so consumers don't need\n // to walk closest('daw-track') themselves. trackId may be empty if the\n // clip is mounted outside a track (developer error).\n const trackEl = this.closest('daw-track') as { trackId?: string } | null;\n this.dispatchEvent(\n new CustomEvent('daw-clip-update', {\n bubbles: true,\n composed: true,\n detail: { trackId: trackEl?.trackId ?? '', clipId: this.clipId },\n })\n );\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-clip': DawClipElement;\n }\n}\n","import { LitElement } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport type { PropertyValues } from 'lit';\nimport type { RenderMode, SpectrogramConfig } from '@waveform-playlist/core';\n\n@customElement('daw-track')\nexport class DawTrackElement extends LitElement {\n @property() src = '';\n @property() name = '';\n @property({ type: Number }) volume = 1;\n @property({ type: Number }) pan = 0;\n @property({ type: Boolean }) muted = false;\n @property({ type: Boolean }) soloed = false;\n\n // Custom getter/setter so we can warn-and-fallback on 'both' (dawcore\n // doesn't yet support rendering waveform + spectrogram simultaneously).\n @property({ attribute: 'render-mode', noAccessor: true })\n get renderMode(): RenderMode {\n return this._renderMode;\n }\n set renderMode(value: RenderMode) {\n const old = this._renderMode;\n let next = value;\n if (next === 'both') {\n console.warn(\n '[dawcore] <daw-track render-mode=\"both\"> is not yet supported; falling back to \\'spectrogram\\''\n );\n next = 'spectrogram';\n }\n this._renderMode = next;\n this.requestUpdate('renderMode', old);\n }\n private _renderMode: RenderMode = 'waveform';\n\n @property({ attribute: false }) spectrogramConfig: SpectrogramConfig | null = null;\n\n readonly trackId = crypto.randomUUID();\n\n // Light DOM so <daw-clip> children are queryable.\n createRenderRoot() {\n return this;\n }\n\n connectedCallback() {\n super.connectedCallback();\n // Defer so the editor's connectedCallback (which registers the\n // daw-track-connected listener) has time to run. Without this,\n // tracks parsed before the editor would fire events with no listener.\n setTimeout(() => {\n this.dispatchEvent(\n new CustomEvent('daw-track-connected', {\n bubbles: true,\n composed: true,\n detail: { trackId: this.trackId, element: this },\n })\n );\n }, 0);\n }\n\n // Track removal is detected by the editor's MutationObserver,\n // not by dispatching from disconnectedCallback (detached elements\n // cannot bubble events to ancestors).\n\n private _hasRendered = false;\n\n updated(changed: PropertyValues) {\n // Skip the initial render — all properties appear in `changed` on first\n // update, but the editor handles initial state via daw-track-connected.\n if (!this._hasRendered) {\n this._hasRendered = true;\n return;\n }\n\n const trackProps = [\n 'volume',\n 'pan',\n 'muted',\n 'soloed',\n 'src',\n 'name',\n 'renderMode',\n 'spectrogramConfig',\n ];\n const hasTrackChange = trackProps.some((p) => changed.has(p as keyof this));\n\n if (hasTrackChange) {\n this.dispatchEvent(\n new CustomEvent('daw-track-update', {\n bubbles: true,\n composed: true,\n detail: { trackId: this.trackId },\n })\n );\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-track': DawTrackElement;\n }\n}\n","import { LitElement, html, css } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport type { Peaks, Bits } from '@waveform-playlist/core';\nimport { aggregatePeaks, calculateBarRects } from '../utils/peak-rendering';\nimport { getVisibleChunkIndices } from '../utils/viewport';\n\n/** A segment mapping a peak-index range to a pixel range within the waveform. */\nexport interface WaveformSegment {\n /** Start position in the peaks array (fractional index). */\n peakStart: number;\n /** End position in the peaks array (fractional index). */\n peakEnd: number;\n /** Start pixel position within the waveform. */\n pixelStart: number;\n /** End pixel position within the waveform. */\n pixelEnd: number;\n}\n\nconst MAX_CANVAS_WIDTH = 1000;\n\n/** Layout/data properties that require a full redraw when changed. */\nconst LAYOUT_PROPS = new Set(['length', 'waveHeight', 'barWidth', 'barGap', 'segments']);\n\n/**\n * Group dirty peak indices by canvas chunk. Returns bar-pixel-aligned\n * min/max positions per chunk for correct clearRect coordinates,\n * including when barWidth > 1 or barGap > 0.\n */\nfunction groupDirtyByChunk(\n dirtyPixels: Set<number>,\n step: number\n): Map<number, { min: number; max: number }> {\n const dirtyByChunk = new Map<number, { min: number; max: number }>();\n for (const peakIdx of dirtyPixels) {\n // Map peak index to its bar's global pixel position\n const barPixel = Math.floor(peakIdx / step) * step;\n const chunkIdx = Math.floor(barPixel / MAX_CANVAS_WIDTH);\n const existing = dirtyByChunk.get(chunkIdx);\n if (existing) {\n dirtyByChunk.set(chunkIdx, {\n min: Math.min(existing.min, barPixel),\n max: Math.max(existing.max, barPixel),\n });\n } else {\n dirtyByChunk.set(chunkIdx, { min: barPixel, max: barPixel });\n }\n }\n return dirtyByChunk;\n}\n\n@customElement('daw-waveform')\nexport class DawWaveformElement extends LitElement {\n private _peaks: Peaks = new Int16Array(0);\n private _dirtyPixels: Set<number> = new Set();\n private _drawScheduled = false;\n private _rafId = 0;\n /** Chunk indices visible in the last draw pass — used to detect new chunks on scroll. */\n private _drawnChunks: Set<number> = new Set();\n\n set peaks(value: Peaks) {\n this._peaks = value;\n this._markAllDirty();\n this.requestUpdate();\n }\n\n get peaks(): Peaks {\n return this._peaks;\n }\n\n /**\n * Replace the internal peaks reference without marking all dirty.\n * Use with updatePeaks() for incremental recording updates where\n * appendPeaks() returns a new array but only the tail changed.\n */\n setPeaksQuiet(value: Peaks) {\n this._peaks = value;\n }\n\n get bits(): Bits {\n return this._peaks instanceof Int8Array ? 8 : 16;\n }\n\n @property({ type: Number, attribute: false }) length = 0;\n @property({ type: Number, attribute: false }) waveHeight = 128;\n @property({ type: Number, attribute: false }) barWidth = 1;\n @property({ type: Number, attribute: false }) barGap = 0;\n /** Visible viewport start in pixels (relative to timeline origin). */\n @property({ type: Number, attribute: false }) visibleStart = -Infinity;\n /** Visible viewport end in pixels (relative to timeline origin). */\n @property({ type: Number, attribute: false }) visibleEnd = Infinity;\n /** This element's left offset on the timeline (for viewport intersection). */\n @property({ type: Number, attribute: false }) originX = 0;\n /** When set, draws per-segment with independent samples-per-pixel ratios. */\n @property({ attribute: false }) segments?: WaveformSegment[];\n\n static styles = css`\n :host {\n display: block;\n position: relative;\n }\n .container {\n position: relative;\n }\n canvas {\n position: absolute;\n top: 0;\n }\n `;\n\n private _getVisibleChunkIndices(): number[] {\n return getVisibleChunkIndices(\n this.length,\n MAX_CANVAS_WIDTH,\n this.visibleStart,\n this.visibleEnd,\n this.originX\n );\n }\n\n /**\n * Mark a range of peak indices as dirty for incremental redraw.\n * The caller must have already updated the underlying peaks array.\n * Does NOT trigger a Lit re-render — bypasses Lit entirely.\n */\n updatePeaks(startIndex: number, endIndex: number) {\n const peakCount = Math.floor(this._peaks.length / 2);\n const clampedStart = Math.max(0, startIndex);\n const clampedEnd = Math.min(peakCount, endIndex);\n for (let i = clampedStart; i < clampedEnd; i++) {\n this._dirtyPixels.add(i);\n }\n this._scheduleDraw();\n }\n\n private _markAllDirty() {\n const peakCount = Math.floor(this._peaks.length / 2);\n for (let i = 0; i < peakCount; i++) {\n this._dirtyPixels.add(i);\n }\n this._scheduleDraw();\n }\n\n private _scheduleDraw() {\n if (!this._drawScheduled) {\n this._drawScheduled = true;\n this._rafId = requestAnimationFrame(() => {\n this._drawScheduled = false;\n this._drawDirty();\n });\n }\n }\n\n private _drawDirty() {\n if (this._dirtyPixels.size === 0 || this.length === 0 || this._peaks.length === 0) {\n this._dirtyPixels.clear();\n return;\n }\n\n const canvases = this.shadowRoot?.querySelectorAll('canvas');\n if (!canvases || canvases.length === 0) {\n // Don't clear _dirtyPixels — canvases may appear after Lit renders.\n // connectedCallback or updated() will reschedule the draw.\n return;\n }\n\n const step = this.barWidth + this.barGap;\n const dpr = typeof devicePixelRatio !== 'undefined' ? devicePixelRatio : 1;\n const halfHeight = this.waveHeight / 2;\n const bits = this.bits;\n const waveColor =\n getComputedStyle(this).getPropertyValue('--daw-wave-color').trim() || '#c49a6c';\n\n this._drawnChunks.clear();\n\n if (this.segments) {\n // Segment mode: full redraw per chunk (segments have variable SPP)\n for (const canvas of canvases) {\n const chunkIdx = Number(canvas.dataset.index);\n this._drawnChunks.add(chunkIdx);\n this._drawSegments(canvas, chunkIdx, dpr, halfHeight, bits, waveColor);\n }\n } else {\n const dirtyByChunk = groupDirtyByChunk(this._dirtyPixels, step);\n for (const canvas of canvases) {\n const chunkIdx = Number(canvas.dataset.index);\n this._drawnChunks.add(chunkIdx);\n const range = dirtyByChunk.get(chunkIdx);\n if (!range) continue;\n this._drawChunk(canvas, chunkIdx, range, step, dpr, halfHeight, bits, waveColor);\n }\n }\n\n this._dirtyPixels.clear();\n }\n\n private _drawChunk(\n canvas: HTMLCanvasElement,\n chunkIdx: number,\n range: { min: number; max: number },\n step: number,\n dpr: number,\n halfHeight: number,\n bits: Bits,\n waveColor: string\n ) {\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const globalOffset = chunkIdx * MAX_CANVAS_WIDTH;\n // range.min/max are bar-pixel-aligned global positions from groupDirtyByChunk\n const clearStart = Math.max(0, range.min - globalOffset);\n const clearEnd = range.max - globalOffset + this.barWidth;\n const clearWidth = clearEnd - clearStart;\n const firstBar = range.min;\n\n ctx.resetTransform();\n ctx.clearRect(clearStart * dpr, 0, clearWidth * dpr, canvas.height);\n ctx.scale(dpr, dpr);\n ctx.fillStyle = waveColor;\n\n const canvasWidth = Math.min(MAX_CANVAS_WIDTH, this.length - globalOffset);\n const regionEnd = Math.min(globalOffset + clearEnd, globalOffset + canvasWidth);\n\n for (let bar = Math.max(0, firstBar); bar < regionEnd; bar += step) {\n const peak = aggregatePeaks(this._peaks, bits, bar, bar + step);\n if (!peak) continue;\n const rects = calculateBarRects(\n bar - globalOffset,\n this.barWidth,\n halfHeight,\n peak.min,\n peak.max,\n 'normal'\n );\n for (const r of rects) {\n ctx.fillRect(r.x, r.y, r.width, r.height);\n }\n }\n }\n\n private _drawSegments(\n canvas: HTMLCanvasElement,\n chunkIdx: number,\n dpr: number,\n halfHeight: number,\n bits: Bits,\n waveColor: string\n ) {\n if (!this.segments) return;\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n const globalOffset = chunkIdx * MAX_CANVAS_WIDTH;\n const canvasWidth = Math.min(MAX_CANVAS_WIDTH, this.length - globalOffset);\n\n ctx.resetTransform();\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.scale(dpr, dpr);\n ctx.fillStyle = waveColor;\n\n const step = Math.max(1, Math.round(this.barWidth + this.barGap));\n\n for (const seg of this.segments) {\n // Skip segments outside this chunk\n if (seg.pixelEnd <= globalOffset || seg.pixelStart >= globalOffset + canvasWidth) continue;\n\n const localStart = Math.max(0, seg.pixelStart - globalOffset);\n const localEnd = Math.min(canvasWidth, seg.pixelEnd - globalOffset);\n const segPixelWidth = seg.pixelEnd - seg.pixelStart;\n const segPeakWidth = seg.peakEnd - seg.peakStart;\n if (segPixelWidth <= 0 || segPeakWidth <= 0) continue;\n\n // Per-segment peaks-per-pixel ratio\n const peaksPerPixel = segPeakWidth / segPixelWidth;\n\n for (let px = Math.floor(localStart); px < Math.ceil(localEnd); px += step) {\n // Map this pixel to a peak index range\n const pxInSeg = px + globalOffset - seg.pixelStart;\n const peakPos = seg.peakStart + pxInSeg * peaksPerPixel;\n const peakEnd = peakPos + step * peaksPerPixel;\n\n const peak = aggregatePeaks(this._peaks, bits, Math.floor(peakPos), Math.ceil(peakEnd));\n if (!peak) continue;\n\n const rects = calculateBarRects(\n px,\n this.barWidth,\n halfHeight,\n peak.min,\n peak.max,\n 'normal'\n );\n for (const r of rects) {\n ctx.fillRect(r.x, r.y, r.width, r.height);\n }\n }\n }\n }\n\n connectedCallback() {\n super.connectedCallback();\n // Reschedule draw if dirty pixels survived a disconnect/reconnect cycle\n if (this._dirtyPixels.size > 0) {\n this._scheduleDraw();\n }\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n if (this._drawScheduled) {\n cancelAnimationFrame(this._rafId);\n this._drawScheduled = false;\n }\n // Keep _dirtyPixels — connectedCallback will reschedule if reconnected\n }\n\n render() {\n const indices = this._getVisibleChunkIndices();\n const dpr = typeof devicePixelRatio !== 'undefined' ? devicePixelRatio : 1;\n\n return html`\n <div class=\"container\" style=\"width: ${this.length}px; height: ${this.waveHeight}px;\">\n ${indices.map((i) => {\n const width = Math.min(MAX_CANVAS_WIDTH, this.length - i * MAX_CANVAS_WIDTH);\n return html`\n <canvas\n data-index=${i}\n width=${width * dpr}\n height=${this.waveHeight * dpr}\n style=\"left: ${i * MAX_CANVAS_WIDTH}px; width: ${width}px; height: ${this\n .waveHeight}px;\"\n ></canvas>\n `;\n })}\n </div>\n `;\n }\n\n /** Mark peaks dirty only for chunks that weren't drawn in the previous frame. */\n private _markNewChunksDirty() {\n const currentIndices = this._getVisibleChunkIndices();\n const peakCount = Math.floor(this._peaks.length / 2);\n for (const chunkIdx of currentIndices) {\n if (!this._drawnChunks.has(chunkIdx)) {\n const start = chunkIdx * MAX_CANVAS_WIDTH;\n const end = Math.min(start + MAX_CANVAS_WIDTH, peakCount);\n for (let i = start; i < end; i++) {\n this._dirtyPixels.add(i);\n }\n }\n }\n if (this._dirtyPixels.size > 0) {\n this._scheduleDraw();\n }\n }\n\n updated(changedProperties: Map<string, unknown>) {\n // Layout/data changes require full redraw of all peaks\n const needsFullDirty = [...changedProperties.keys()].some((key) => LAYOUT_PROPS.has(key));\n if (needsFullDirty) {\n this._markAllDirty();\n return;\n }\n // Viewport-only changes: only draw newly visible chunks, skip already-drawn ones\n if (\n changedProperties.has('visibleStart') ||\n changedProperties.has('visibleEnd') ||\n changedProperties.has('originX')\n ) {\n this._markNewChunksDirty();\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-waveform': DawWaveformElement;\n }\n}\n","import type { Peaks, Bits } from '@waveform-playlist/core';\n\nexport type WaveformDrawMode = 'normal' | 'inverted';\n\n/**\n * Result of aggregating peaks over a range.\n *\n * Invariants (assumed from valid waveform input):\n * - min and max are normalized to [-1, 1] by dividing by 2^(bits-1)\n * - min <= max (min-of-mins, max-of-maxes — guaranteed by the interleaved min/max peak format)\n * - Values are finite (derived from integer typed arrays)\n *\n * Construct via aggregatePeaks() — do not create directly.\n */\nexport interface AggregatedPeak {\n min: number;\n max: number;\n}\n\n/**\n * Canvas fillRect parameters for a single waveform bar.\n * width >= 0 and height >= 0 when peak values are in [-1, 1] (guaranteed by\n * the interleaved min/max peak format normalization).\n */\nexport interface BarRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * Aggregates peaks over a range of interleaved min/max pairs.\n * Finds min-of-mins and max-of-maxes, normalized by bit depth.\n *\n * @param data - Interleaved peak data [min0, max0, min1, max1, ...]\n * @param bits - Bit depth (8 or 16)\n * @param startIndex - First peak index (not array index — peak i is at data[i*2], data[i*2+1])\n * @param endIndex - One past the last peak index to include\n * @returns Normalized { min, max } or null if startIndex is out of bounds\n */\nexport function aggregatePeaks(\n data: Peaks,\n bits: Bits,\n startIndex: number,\n endIndex: number\n): AggregatedPeak | null {\n if (startIndex * 2 + 1 >= data.length) {\n return null;\n }\n\n const maxValue = 2 ** (bits - 1);\n let minPeak = data[startIndex * 2] / maxValue;\n let maxPeak = data[startIndex * 2 + 1] / maxValue;\n\n for (let p = startIndex + 1; p < endIndex; p++) {\n if (p * 2 + 1 >= data.length) break;\n const pMin = data[p * 2] / maxValue;\n const pMax = data[p * 2 + 1] / maxValue;\n if (pMin < minPeak) minPeak = pMin;\n if (pMax > maxPeak) maxPeak = pMax;\n }\n\n return { min: minPeak, max: maxPeak };\n}\n\n/**\n * Computes canvas fillRect parameters for a single waveform bar.\n *\n * @param x - Bar x position in canvas coordinates\n * @param barWidth - Width of the bar in pixels\n * @param halfHeight - Half the waveform height (center line y)\n * @param minPeak - Normalized min peak value (negative for below center)\n * @param maxPeak - Normalized max peak value (positive for above center)\n * @param drawMode - 'normal' draws the peak region, 'inverted' draws the non-peak regions\n * @returns Array of BarRect — 1 rect for 'normal', 2 rects for 'inverted'\n */\nexport function calculateBarRects(\n x: number,\n barWidth: number,\n halfHeight: number,\n minPeak: number,\n maxPeak: number,\n drawMode: WaveformDrawMode\n): BarRect[] {\n const min = Math.abs(minPeak * halfHeight);\n const max = Math.abs(maxPeak * halfHeight);\n\n if (drawMode === 'normal') {\n return [{ x, y: halfHeight - max, width: barWidth, height: max + min }];\n }\n\n // Inverted: draw areas WITHOUT audio (top gap + bottom gap)\n return [\n { x, y: 0, width: barWidth, height: halfHeight - max },\n { x, y: halfHeight + min, width: barWidth, height: halfHeight - min },\n ];\n}\n\n/**\n * Computes the first bar position (in global pixel coordinates) that could\n * affect a given canvas chunk.\n *\n * A bar at position X extends from X to X+barWidth-1, so we need bars where\n * barStart + barWidth > canvasStartGlobal.\n *\n * @param canvasStartGlobal - Global pixel offset of the canvas chunk\n * @param barWidth - Width of each bar in pixels\n * @param step - Bar stride (barWidth + barGap)\n * @returns The first bar's global position (always >= 0 when step >= barWidth; caller clamps to 0)\n */\nexport function calculateFirstBarPosition(\n canvasStartGlobal: number,\n barWidth: number,\n step: number\n): number {\n return Math.floor((canvasStartGlobal - barWidth + step) / step) * step;\n}\n","/**\n * Compute which canvas chunk indices are visible within a viewport range.\n *\n * @param totalWidth - Total pixel width of the content\n * @param chunkWidth - Width of each canvas chunk (e.g. 1000px)\n * @param visibleStart - Viewport start in pixels (relative to timeline origin)\n * @param visibleEnd - Viewport end in pixels (relative to timeline origin)\n * @param originX - Content's left offset on the timeline\n */\nexport function getVisibleChunkIndices(\n totalWidth: number,\n chunkWidth: number,\n visibleStart: number,\n visibleEnd: number,\n originX = 0\n): number[] {\n const totalChunks = Math.ceil(totalWidth / chunkWidth);\n const indices: number[] = [];\n for (let i = 0; i < totalChunks; i++) {\n const chunkStart = originX + i * chunkWidth;\n const chunkEnd = chunkStart + chunkWidth;\n if (chunkEnd > visibleStart && chunkStart < visibleEnd) {\n indices.push(i);\n }\n }\n return indices;\n}\n","import { LitElement, html, css } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport type { PropertyValues } from 'lit';\nimport type { MidiNoteData } from '@waveform-playlist/core';\nimport { getVisibleChunkIndices } from '../utils/viewport';\n\nconst MAX_CANVAS_WIDTH = 1000;\n\n/** Layout/data properties that require a full redraw when changed. */\nconst LAYOUT_PROPS = new Set([\n 'length',\n 'waveHeight',\n 'samplesPerPixel',\n 'sampleRate',\n 'clipOffsetSeconds',\n 'midiNotes',\n 'selected',\n]);\n\n@customElement('daw-piano-roll')\nexport class DawPianoRollElement extends LitElement {\n @property({ attribute: false }) midiNotes: MidiNoteData[] = [];\n @property({ type: Number, attribute: false }) length = 0;\n @property({ type: Number, attribute: false }) waveHeight = 128;\n @property({ type: Number, attribute: 'samples-per-pixel', noAccessor: true })\n get samplesPerPixel(): number {\n return this._samplesPerPixel;\n }\n set samplesPerPixel(value: number) {\n if (!Number.isFinite(value) || value <= 0) {\n console.warn('[dawcore] daw-piano-roll samplesPerPixel ' + value + ' is invalid — ignored');\n return;\n }\n const old = this._samplesPerPixel;\n this._samplesPerPixel = value;\n this.requestUpdate('samplesPerPixel', old);\n }\n private _samplesPerPixel = 1024;\n\n @property({ type: Number, attribute: 'sample-rate', noAccessor: true })\n get sampleRate(): number {\n return this._sampleRate;\n }\n set sampleRate(value: number) {\n if (!Number.isFinite(value) || value <= 0) {\n console.warn('[dawcore] daw-piano-roll sampleRate ' + value + ' is invalid — ignored');\n return;\n }\n const old = this._sampleRate;\n this._sampleRate = value;\n this.requestUpdate('sampleRate', old);\n }\n private _sampleRate = 48000;\n @property({ type: Number, attribute: false }) clipOffsetSeconds = 0;\n /** Visible viewport start in pixels (relative to timeline origin). */\n @property({ type: Number, attribute: false }) visibleStart = -Infinity;\n /** Visible viewport end in pixels (relative to timeline origin). */\n @property({ type: Number, attribute: false }) visibleEnd = Infinity;\n /** This element's left offset on the timeline (for viewport intersection). */\n @property({ type: Number, attribute: false }) originX = 0;\n @property({ type: Boolean, reflect: true }) selected = false;\n\n static styles = css`\n :host {\n display: block;\n position: relative;\n }\n .container {\n position: relative;\n background: var(--daw-piano-roll-background, #1a1a2e);\n }\n canvas {\n position: absolute;\n top: 0;\n image-rendering: pixelated;\n image-rendering: crisp-edges;\n }\n `;\n\n private _rafHandle: number | null = null;\n\n private _scheduleDraw() {\n if (this._rafHandle !== null) return;\n this._rafHandle = requestAnimationFrame(() => {\n this._rafHandle = null;\n this._draw();\n });\n }\n\n willUpdate(_changed: PropertyValues) {\n this._scheduleDraw();\n }\n\n updated(changedProperties: Map<string, unknown>) {\n // Layout/data changes: willUpdate already scheduled a draw — nothing extra needed.\n const needsFullDraw = [...changedProperties.keys()].some((key) => LAYOUT_PROPS.has(key));\n if (needsFullDraw) return;\n // Viewport-only changes: new canvases may have mounted; schedule a draw so they get painted.\n if (\n changedProperties.has('visibleStart') ||\n changedProperties.has('visibleEnd') ||\n changedProperties.has('originX')\n ) {\n this._scheduleDraw();\n }\n }\n\n private _getPitchRange(): { minMidi: number; maxMidi: number } {\n if (this.midiNotes.length === 0) return { minMidi: 0, maxMidi: 127 };\n let min = 127;\n let max = 0;\n for (const note of this.midiNotes) {\n if (note.midi < min) min = note.midi;\n if (note.midi > max) max = note.midi;\n }\n return {\n minMidi: Math.max(0, min - 1),\n maxMidi: Math.min(127, max + 1),\n };\n }\n\n private _getNoteColor(): string {\n const cs = getComputedStyle(this);\n const note = cs.getPropertyValue('--daw-piano-roll-note-color').trim() || '#2a7070';\n const selectedColor =\n cs.getPropertyValue('--daw-piano-roll-selected-note-color').trim() || '#3d9e9e';\n return this.selected ? selectedColor : note;\n }\n\n private _draw() {\n if (!this.shadowRoot) return;\n const canvases = this.shadowRoot.querySelectorAll('canvas');\n if (canvases.length === 0) return;\n\n const { minMidi, maxMidi } = this._getPitchRange();\n const noteRange = maxMidi - minMidi + 1;\n const noteHeight = Math.max(2, this.waveHeight / noteRange);\n const pixelsPerSecond = this.sampleRate / this.samplesPerPixel;\n const dpr = typeof devicePixelRatio !== 'undefined' ? devicePixelRatio : 1;\n const color = this._getNoteColor();\n\n for (const canvas of canvases) {\n // Read data-index (matching daw-waveform pattern)\n const chunkIdx = Number((canvas as HTMLCanvasElement).dataset.index);\n const chunkPixelStart = chunkIdx * MAX_CANVAS_WIDTH;\n const canvasWidth = (canvas as HTMLCanvasElement).width / dpr;\n\n const ctx = (canvas as HTMLCanvasElement).getContext('2d');\n if (!ctx) continue;\n\n ctx.resetTransform();\n ctx.clearRect(\n 0,\n 0,\n (canvas as HTMLCanvasElement).width,\n (canvas as HTMLCanvasElement).height\n );\n ctx.imageSmoothingEnabled = false;\n ctx.scale(dpr, dpr);\n\n const chunkStartTime = (chunkPixelStart * this.samplesPerPixel) / this.sampleRate;\n const chunkEndTime =\n ((chunkPixelStart + canvasWidth) * this.samplesPerPixel) / this.sampleRate;\n\n for (const note of this.midiNotes) {\n const noteStart = note.time - this.clipOffsetSeconds;\n const noteEnd = noteStart + note.duration;\n if (noteEnd <= chunkStartTime || noteStart >= chunkEndTime) continue;\n\n const x = noteStart * pixelsPerSecond - chunkPixelStart;\n const w = Math.max(2, note.duration * pixelsPerSecond);\n const y = ((maxMidi - note.midi) / noteRange) * this.waveHeight;\n\n const alpha = 0.3 + note.velocity * 0.7;\n ctx.fillStyle = color;\n ctx.globalAlpha = alpha;\n\n ctx.beginPath();\n ctx.roundRect(x, y, w, noteHeight, 1);\n ctx.fill();\n }\n ctx.globalAlpha = 1;\n }\n }\n\n connectedCallback() {\n super.connectedCallback();\n // Reschedule draw if a RAF was pending during disconnect/reconnect cycle\n this._scheduleDraw();\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n if (this._rafHandle !== null) {\n cancelAnimationFrame(this._rafHandle);\n this._rafHandle = null;\n }\n }\n\n render() {\n if (this.length <= 0)\n return html`<div class=\"container\" style=\"width: 0; height: ${this.waveHeight}px;\"></div>`;\n const dpr = typeof devicePixelRatio !== 'undefined' ? devicePixelRatio : 1;\n const visibleIndices = getVisibleChunkIndices(\n this.length,\n MAX_CANVAS_WIDTH,\n this.visibleStart,\n this.visibleEnd,\n this.originX\n );\n // Wrap canvases in a .container div with explicit width/height,\n // matching daw-waveform's container pattern. Background is on .container (not :host)\n // so it has the correct measurable size.\n return html`\n <div class=\"container\" style=\"width: ${this.length}px; height: ${this.waveHeight}px;\">\n ${visibleIndices.map((i) => {\n const chunkLeft = i * MAX_CANVAS_WIDTH;\n const chunkWidth = Math.min(this.length - chunkLeft, MAX_CANVAS_WIDTH);\n // Use data-index (not data-chunk-idx) to match daw-waveform pattern\n return html`<canvas\n data-index=${i}\n width=${chunkWidth * dpr}\n height=${this.waveHeight * dpr}\n style=\"left: ${chunkLeft}px; width: ${chunkWidth}px; height: ${this.waveHeight}px;\"\n ></canvas>`;\n })}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-piano-roll': DawPianoRollElement;\n }\n}\n","import { LitElement, html, css } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport { AnimationController } from '../controllers/animation-controller';\n\n@customElement('daw-playhead')\nexport class DawPlayheadElement extends LitElement {\n private _animation = new AnimationController(this);\n private _line: HTMLElement | null = null;\n\n static styles = css`\n :host {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n pointer-events: none;\n z-index: 10;\n }\n div {\n position: absolute;\n top: 0;\n bottom: 0;\n width: 1px;\n background: var(--daw-playhead-color, #d08070);\n will-change: transform;\n }\n `;\n\n render() {\n return html`<div></div>`;\n }\n\n firstUpdated() {\n this._line = this.shadowRoot!.querySelector('div');\n }\n\n startAnimation(getTime: () => number, sampleRate: number, samplesPerPixel: number) {\n this._animation.start(() => {\n const time = getTime();\n const px = (time * sampleRate) / samplesPerPixel;\n if (this._line) {\n this._line.style.transform = `translate3d(${px}px, 0, 0)`;\n }\n });\n }\n\n stopAnimation(time: number, sampleRate: number, samplesPerPixel: number) {\n this._animation.stop();\n const px = (time * sampleRate) / samplesPerPixel;\n if (this._line) {\n this._line.style.transform = `translate3d(${px}px, 0, 0)`;\n }\n }\n\n startBeatsAnimation(getTime: () => number, bpm: number, ppqn: number, ticksPerPixel: number) {\n const ticksPerSecond = (bpm * ppqn) / 60;\n this._animation.start(() => {\n const time = getTime();\n const px = (time * ticksPerSecond) / ticksPerPixel;\n if (this._line) {\n this._line.style.transform = `translate3d(${px}px, 0, 0)`;\n }\n });\n }\n\n stopBeatsAnimation(time: number, bpm: number, ppqn: number, ticksPerPixel: number) {\n this._animation.stop();\n const px = (time * bpm * ppqn) / (60 * ticksPerPixel);\n if (this._line) {\n this._line.style.transform = `translate3d(${px}px, 0, 0)`;\n }\n }\n\n startBeatsAnimationWithMap(\n getTime: () => number,\n secondsToTicks: (s: number) => number,\n ticksPerPixel: number\n ) {\n this._animation.start(() => {\n const time = getTime();\n const tick = secondsToTicks(time);\n const px = tick / ticksPerPixel;\n if (this._line) {\n this._line.style.transform = `translate3d(${px}px, 0, 0)`;\n }\n });\n }\n\n stopBeatsAnimationWithMap(\n time: number,\n secondsToTicks: (s: number) => number,\n ticksPerPixel: number\n ) {\n this._animation.stop();\n const px = secondsToTicks(time) / ticksPerPixel;\n if (this._line) {\n this._line.style.transform = `translate3d(${px}px, 0, 0)`;\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-playhead': DawPlayheadElement;\n }\n}\n","import type { ReactiveController, ReactiveControllerHost } from 'lit';\n\nexport class AnimationController implements ReactiveController {\n private _rafId: number | null = null;\n private _callback: (() => void) | null = null;\n\n constructor(host: ReactiveControllerHost) {\n host.addController(this);\n }\n\n start(callback: () => void) {\n this.stop();\n this._callback = callback;\n const loop = () => {\n this._callback?.();\n this._rafId = requestAnimationFrame(loop);\n };\n this._rafId = requestAnimationFrame(loop);\n }\n\n stop() {\n if (this._rafId !== null) {\n cancelAnimationFrame(this._rafId);\n this._rafId = null;\n }\n this._callback = null;\n }\n\n hostConnected() {}\n\n hostDisconnected() {\n this.stop();\n }\n}\n","import { LitElement } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\n\n@customElement('daw-transport')\nexport class DawTransportElement extends LitElement {\n @property() for = '';\n\n get target(): HTMLElement | null {\n return this.for ? document.getElementById(this.for) : null;\n }\n\n // Light DOM — button children stay in consumer's DOM.\n // No render() needed; light DOM elements don't use <slot>.\n createRenderRoot() {\n return this;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-transport': DawTransportElement;\n }\n}\n","import { html } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\nimport { DawTransportButton } from './daw-transport-button';\n\n@customElement('daw-play-button')\nexport class DawPlayButtonElement extends DawTransportButton {\n @state() private _isRecording = false;\n private _targetRef: HTMLElement | null = null;\n private _onRecStart = () => {\n this._isRecording = true;\n };\n private _onRecEnd = () => {\n this._isRecording = false;\n };\n\n connectedCallback() {\n super.connectedCallback();\n // Defer so <daw-transport for=\"...\"> and the target editor are resolved\n requestAnimationFrame(() => {\n const target = this.target;\n if (!target) return;\n this._targetRef = target;\n target.addEventListener('daw-recording-start', this._onRecStart);\n target.addEventListener('daw-recording-complete', this._onRecEnd);\n target.addEventListener('daw-recording-error', this._onRecEnd);\n });\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n if (this._targetRef) {\n this._targetRef.removeEventListener('daw-recording-start', this._onRecStart);\n this._targetRef.removeEventListener('daw-recording-complete', this._onRecEnd);\n this._targetRef.removeEventListener('daw-recording-error', this._onRecEnd);\n this._targetRef = null;\n }\n }\n\n render() {\n return html`\n <button part=\"button\" ?disabled=${this._isRecording} @click=${this._onClick}>\n <slot>Play</slot>\n </button>\n `;\n }\n\n private _onClick() {\n const target = this.target;\n if (!target) {\n console.warn(\n '[dawcore] <daw-play-button> has no target. Check <daw-transport for=\"...\"> references a valid <daw-editor> id.'\n );\n return;\n }\n target.play();\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-play-button': DawPlayButtonElement;\n }\n}\n","import { LitElement, css } from 'lit';\nimport type { DawTransportElement } from './daw-transport';\n\n/**\n * Base class for transport button elements.\n * Finds target daw-editor via closest <daw-transport>.\n */\nexport class DawTransportButton extends LitElement {\n protected get target(): any {\n const transport = this.closest('daw-transport') as DawTransportElement | null;\n return transport?.target ?? null;\n }\n\n static styles: import('lit').CSSResultGroup = css`\n button {\n cursor: pointer;\n background: var(--daw-controls-background, #1a1a2e);\n color: var(--daw-controls-text, #e0d4c8);\n border: 1px solid currentColor;\n padding: 4px 8px;\n font: inherit;\n }\n button:hover {\n opacity: 0.8;\n }\n button:disabled {\n opacity: 0.4;\n cursor: default;\n }\n `;\n}\n","import { html, css } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\nimport { DawTransportButton } from './daw-transport-button';\n\n@customElement('daw-pause-button')\nexport class DawPauseButtonElement extends DawTransportButton {\n @state() private _isPaused = false;\n @state() private _isRecording = false;\n private _targetRef: HTMLElement | null = null;\n private _onRecStart = () => {\n this._isRecording = true;\n };\n private _onRecEnd = () => {\n this._isRecording = false;\n this._isPaused = false;\n };\n private _onRecPause = () => {\n this._isPaused = true;\n };\n private _onRecResume = () => {\n this._isPaused = false;\n };\n\n static override styles = [\n DawTransportButton.styles,\n css`\n button[data-paused] {\n background: rgba(255, 255, 255, 0.1);\n border-color: var(--daw-controls-text, #e0d4c8);\n }\n `,\n ];\n\n connectedCallback() {\n super.connectedCallback();\n // Defer so <daw-transport for=\"...\"> and the target editor are resolved\n requestAnimationFrame(() => {\n const target = this.target;\n if (!target) return;\n this._targetRef = target;\n target.addEventListener('daw-recording-start', this._onRecStart);\n target.addEventListener('daw-recording-complete', this._onRecEnd);\n target.addEventListener('daw-recording-error', this._onRecEnd);\n target.addEventListener('daw-recording-pause', this._onRecPause);\n target.addEventListener('daw-recording-resume', this._onRecResume);\n });\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n if (this._targetRef) {\n this._targetRef.removeEventListener('daw-recording-start', this._onRecStart);\n this._targetRef.removeEventListener('daw-recording-complete', this._onRecEnd);\n this._targetRef.removeEventListener('daw-recording-error', this._onRecEnd);\n this._targetRef.removeEventListener('daw-recording-pause', this._onRecPause);\n this._targetRef.removeEventListener('daw-recording-resume', this._onRecResume);\n this._targetRef = null;\n }\n }\n\n render() {\n return html`\n <button part=\"button\" ?data-paused=${this._isPaused} @click=${this._onClick}>\n <slot>Pause</slot>\n </button>\n `;\n }\n\n private _onClick() {\n const target = this.target;\n if (!target) {\n console.warn(\n '[dawcore] <daw-pause-button> has no target. Check <daw-transport for=\"...\"> references a valid <daw-editor> id.'\n );\n return;\n }\n\n if (this._isRecording) {\n // Delegate to editor — events keep visual state in sync via\n // daw-recording-pause / daw-recording-resume.\n target.togglePauseRecording();\n } else {\n target.pause();\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-pause-button': DawPauseButtonElement;\n }\n}\n","import { html } from 'lit';\nimport { customElement } from 'lit/decorators.js';\nimport { DawTransportButton } from './daw-transport-button';\n\n@customElement('daw-stop-button')\nexport class DawStopButtonElement extends DawTransportButton {\n render() {\n return html`\n <button part=\"button\" @click=${this._onClick}>\n <slot>Stop</slot>\n </button>\n `;\n }\n\n private _onClick() {\n const target = this.target;\n if (!target) {\n console.warn(\n '[dawcore] <daw-stop-button> has no target. Check <daw-transport for=\"...\"> references a valid <daw-editor> id.'\n );\n return;\n }\n // When recording: await stopRecording (which awaits the worklet's\n // done ack) BEFORE calling editor.stop(). Calling stop() in parallel\n // disrupts the audio thread mid-handshake — engine.stop() can pause\n // worklet rendering, which prevents the done message from arriving.\n // ALWAYS run stop() afterward (even if stopRecording rejected), so the\n // engine cleans up; wrap stop() in try/catch since it's synchronous\n // void and can throw.\n if (target.isRecording) {\n target\n .stopRecording()\n .catch((err: unknown) => {\n console.warn('[dawcore] stopRecording failed: ' + String(err));\n })\n .then(() => {\n try {\n target.stop();\n } catch (err) {\n console.warn('[dawcore] stop after stopRecording failed: ' + String(err));\n }\n });\n } else {\n try {\n target.stop();\n } catch (err) {\n console.warn('[dawcore] stop failed: ' + String(err));\n }\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-stop-button': DawStopButtonElement;\n }\n}\n","import { LitElement, html, css } from 'lit';\nimport { customElement, property, state } from 'lit/decorators.js';\nimport type {\n AudioClip,\n ClipTrack,\n FadeType,\n MidiNoteData,\n Peaks,\n PeakData,\n SnapTo,\n MeterEntry,\n SpectrogramConfig,\n ColorMapValue,\n} from '@waveform-playlist/core';\nimport type {\n TrackDescriptor,\n ClipDescriptor,\n DomClipDescriptor,\n TrackConfig,\n ClipConfig,\n} from '../types';\nimport { isDomClip } from '../types';\nimport {\n createClip,\n createClipFromSeconds,\n createTrack,\n clipPixelWidth,\n} from '@waveform-playlist/core';\nimport { PeakPipeline } from '../workers/peakPipeline';\nimport type { DawTrackElement } from './daw-track';\nimport type { DawClipElement } from './daw-clip';\nimport type { DawPlayheadElement } from './daw-playhead';\nimport type { WaveformSegment } from './daw-waveform';\n// Register the visual child elements this editor's template always renders.\n// The shadow template emits <daw-playhead>/<daw-waveform>/<daw-ruler> directly,\n// so the editor must guarantee they're defined. Without these side-effect\n// imports, importing daw-editor alone (not the package barrel) leaves the\n// elements un-upgraded — querySelector returns generic HTMLElements, and calls\n// like playhead.stopAnimation() throw \"is not a function\". (Module caching +\n// @customElement make this safe to import here and from the index barrel.)\nimport './daw-playhead';\nimport './daw-waveform';\nimport './daw-ruler';\nimport type { PlaylistEngine, PlayoutAdapter } from '@waveform-playlist/engine';\nimport '../elements/daw-track-controls';\nimport '../elements/daw-grid';\nimport { hostStyles, clipStyles } from '../styles/theme';\nimport { ViewportController } from '../controllers/viewport-controller';\nimport { AudioResumeController } from '../controllers/audio-resume-controller';\nimport { RecordingController } from '../controllers/recording-controller';\nimport type { RecordingOptions } from '../controllers/recording-controller';\nimport { SpectrogramController } from '../controllers/spectrogram-controller';\nimport { PointerHandler } from '../interactions/pointer-handler';\nimport { ClipPointerHandler } from '../interactions/clip-pointer-handler';\nimport type {\n DawSelectionDetail,\n DawTrackIdDetail,\n DawTrackErrorDetail,\n DawClipIdDetail,\n DawClipErrorDetail,\n DawErrorDetail,\n LoadFilesResult,\n} from '../events';\nimport { loadFiles as loadFilesImpl } from '../interactions/file-loader';\nimport {\n loadMidiImpl,\n type MidiLoaderHost,\n type MidiLoadOptions,\n type MidiLoadResult,\n} from '../interactions/midi-loader';\nimport { addRecordedClip } from '../interactions/recording-clip';\nimport { splitAtPlayhead as performSplitAtPlayhead } from '../interactions/split-handler';\nimport { syncPeaksForChangedClips } from '../interactions/clip-peak-sync';\nimport { loadWaveformDataFromUrl } from '../interactions/peaks-loader';\nimport { extractPeaks } from '../workers/waveformDataUtils';\nimport { ScrollSyncController } from '../controllers/scroll-sync-controller';\n\n/** Height of the ruler band — single source for the ruler element and the header row. */\nconst RULER_HEIGHT = 30;\n\nconst NO_ADAPTER_ERROR =\n 'No PlayoutAdapter set on <daw-editor>. ' +\n 'Set editor.adapter before use.\\n\\n' +\n ' // Option 1: Native Web Audio (no Tone.js)\\n' +\n ' npm install @dawcore/transport\\n' +\n \" import { NativePlayoutAdapter } from '@dawcore/transport';\\n\" +\n ' editor.adapter = new NativePlayoutAdapter(new AudioContext());\\n\\n' +\n ' // Option 2: Tone.js (effects, MIDI synths)\\n' +\n ' npm install @waveform-playlist/playout\\n' +\n \" import { createToneAdapter } from '@waveform-playlist/playout';\\n\" +\n ' editor.adapter = createToneAdapter();';\n\n@customElement('daw-editor')\nexport class DawEditorElement extends LitElement implements MidiLoaderHost {\n @property({ type: Number, attribute: 'samples-per-pixel', noAccessor: true })\n get samplesPerPixel(): number {\n return this._samplesPerPixel;\n }\n set samplesPerPixel(value: number) {\n const old = this._samplesPerPixel;\n if (!Number.isFinite(value) || value <= 0) return;\n const clamped =\n this._minSamplesPerPixel > 0 && value < this._minSamplesPerPixel\n ? this._minSamplesPerPixel\n : value;\n if (clamped !== value) {\n console.warn(\n '[dawcore] Zoom ' +\n value +\n ' spp rejected — pre-computed peaks limit is ' +\n this._minSamplesPerPixel +\n ' spp'\n );\n }\n this._samplesPerPixel = clamped;\n this.requestUpdate('samplesPerPixel', old);\n }\n private _samplesPerPixel = 1024;\n @property({ type: Number, attribute: 'wave-height' }) waveHeight = 128;\n @property({ type: Boolean }) timescale = false;\n @property({ type: Boolean }) mono = false;\n @property({ type: Number, attribute: 'bar-width' }) barWidth = 1;\n @property({ type: Number, attribute: 'bar-gap' }) barGap = 0;\n @property({ type: Boolean, attribute: 'file-drop' }) fileDrop = false;\n @property({ type: Boolean, attribute: 'clip-headers' }) clipHeaders = false;\n @property({ type: Number, attribute: 'clip-header-height' }) clipHeaderHeight = 20;\n @property({ type: Boolean, attribute: 'interactive-clips' }) interactiveClips = false;\n /**\n * When true, the timeline fills the visible viewport even if total clip\n * duration is less. Lets the ruler render before any audio is loaded —\n * useful for empty editors and recording UIs. In beats mode the 32-bar\n * floor already provides this; this attribute controls the temporal mode.\n */\n @property({ type: Boolean, attribute: 'indefinite-playback' }) indefinitePlayback = false;\n /**\n * Default spectrogram FFT/render config inherited by tracks with\n * `render-mode=\"spectrogram\"` that do not set their own. Wired into the\n * orchestrator by `SpectrogramController` in Task 14.\n */\n @property({ attribute: false, noAccessor: true })\n get spectrogramConfig(): SpectrogramConfig | null {\n return this._spectrogramConfig;\n }\n set spectrogramConfig(value: SpectrogramConfig | null) {\n const old = this._spectrogramConfig;\n this._spectrogramConfig = value;\n this._spectrogramController?.setEditorConfig(value);\n this.requestUpdate('spectrogramConfig', old);\n }\n private _spectrogramConfig: SpectrogramConfig | null = null;\n\n /**\n * Default color map for spectrogram tracks. Tracks may override via\n * their own per-track property in a future iteration. Separate from\n * `spectrogramConfig` because `ColorMapValue` is not part of the\n * `SpectrogramConfig` shape.\n */\n @property({ attribute: false, noAccessor: true })\n get spectrogramColorMap(): ColorMapValue | null {\n return this._spectrogramColorMap;\n }\n set spectrogramColorMap(value: ColorMapValue | null) {\n const old = this._spectrogramColorMap;\n this._spectrogramColorMap = value;\n this._spectrogramController?.setEditorColorMap(value);\n this.requestUpdate('spectrogramColorMap', old);\n }\n private _spectrogramColorMap: ColorMapValue | null = null;\n\n private _ensureSpectrogramController(): SpectrogramController {\n if (!this._spectrogramController) {\n this._spectrogramController = new SpectrogramController(\n this,\n () =>\n new Worker(new URL('@dawcore/spectrogram/worker/spectrogram.worker', import.meta.url), {\n type: 'module',\n })\n );\n if (this._spectrogramConfig) {\n this._spectrogramController.setEditorConfig(this._spectrogramConfig);\n }\n if (this._spectrogramColorMap) {\n this._spectrogramController.setEditorColorMap(this._spectrogramColorMap);\n }\n }\n return this._spectrogramController;\n }\n\n /** Called by <daw-spectrogram> after transferControlToOffscreen. */\n _spectrogramRegisterCanvas(reg: {\n canvasId: string;\n canvas: OffscreenCanvas;\n clipId: string;\n trackId: string;\n channelIndex: number;\n chunkIndex: number;\n globalPixelOffset: number;\n widthPx: number;\n heightPx: number;\n }): void {\n this._ensureSpectrogramController().registerCanvas(reg);\n }\n\n /** Called by <daw-spectrogram> on chunk unmount / element disconnect. */\n _spectrogramUnregisterCanvas(canvasId: string): void {\n this._spectrogramController?.unregisterCanvas(canvasId);\n }\n\n /**\n * Forward a clip's AudioBuffer to the spectrogram controller if the parent\n * track is in spectrogram render-mode. Eagerly creates the controller via\n * `_ensureSpectrogramController` so the audio data is queued for the first\n * render — even if no canvases have been registered yet.\n */\n private _maybeRegisterSpectrogramClipAudio(trackId: string, clip: AudioClip): void {\n const descriptor = this._tracks.get(trackId);\n if (descriptor?.renderMode !== 'spectrogram') return;\n // Read the buffer off the clip itself — `_clipBuffers` is mutated in-place\n // by `cleanupOrphanedClipData` during concurrent track loading and may\n // briefly miss this clip even though the clip object still holds the\n // buffer reference.\n const buffer = clip.audioBuffer ?? this._clipBuffers.get(clip.id);\n if (!buffer) return;\n const channelData: Float32Array[] = [];\n for (let i = 0; i < buffer.numberOfChannels; i++) {\n channelData.push(buffer.getChannelData(i));\n }\n this._ensureSpectrogramController().registerClipAudio({\n clipId: clip.id,\n trackId,\n channelData,\n sampleRate: buffer.sampleRate,\n durationSamples: clip.durationSamples,\n offsetSamples: clip.offsetSamples,\n });\n }\n @property({ type: String, attribute: 'scale-mode' })\n scaleMode: 'temporal' | 'beats' = 'temporal';\n @property({ type: Number, attribute: 'ticks-per-pixel', noAccessor: true })\n get ticksPerPixel(): number {\n return this._ticksPerPixel;\n }\n set ticksPerPixel(value: number) {\n const old = this._ticksPerPixel;\n if (!Number.isFinite(value) || value <= 0) return;\n this._ticksPerPixel = value;\n this.requestUpdate('ticksPerPixel', old);\n }\n private _ticksPerPixel = 24;\n @property({ type: Number, noAccessor: true })\n get bpm(): number {\n return this._bpm;\n }\n set bpm(value: number) {\n const old = this._bpm;\n if (!Number.isFinite(value) || value <= 0) return;\n this._bpm = value;\n // Forward to engine (which forwards to adapter's Transport)\n if (this._engine) {\n this._engine.setTempo(value);\n }\n this.requestUpdate('bpm', old);\n }\n private _bpm = 120;\n @property({ attribute: false })\n timeSignature: [number, number] = [4, 4];\n @property({ attribute: false })\n meterEntries?: MeterEntry[];\n /** MeterEntries for grid/ruler: explicit meterEntries if set, otherwise derived from timeSignature. */\n get _meterEntries(): MeterEntry[] {\n if (this.meterEntries && this.meterEntries.length > 0) return this.meterEntries;\n return [{ tick: 0, numerator: this.timeSignature[0], denominator: this.timeSignature[1] }];\n }\n @property({ type: Number, noAccessor: true })\n get ppqn(): number {\n return this._ppqn;\n }\n set ppqn(value: number) {\n const old = this._ppqn;\n if (!Number.isFinite(value) || value <= 0) return;\n this._ppqn = value;\n this.requestUpdate('ppqn', old);\n }\n private _ppqn = 960;\n @property({ type: String, attribute: 'snap-to' })\n snapTo: SnapTo = 'off';\n /** Optional tempo-aware conversion: seconds → PPQN ticks. When provided, enables variable tempo. */\n @property({ attribute: false })\n secondsToTicks?: (seconds: number) => number;\n /** Optional tempo-aware conversion: PPQN ticks → seconds. Required alongside secondsToTicks. */\n @property({ attribute: false })\n ticksToSeconds?: (ticks: number) => number;\n /** Sample rate — reads from adapter's AudioContext when available, otherwise falls back to 48000. */\n get sampleRate(): number {\n return this._resolvedSampleRate ?? this._externalAdapter?.audioContext.sampleRate ?? 48000;\n }\n /** Resolved sample rate — falls back to sampleRate property until first audio decode. */\n _resolvedSampleRate: number | null = null;\n @state() _tracks: Map<string, TrackDescriptor> = new Map();\n @state() _engineTracks: Map<string, ClipTrack> = new Map();\n @state() _peaksData: Map<string, PeakData> = new Map();\n @state() _isPlaying = false;\n @state() private _duration = 0;\n @state() _selectedTrackId: string | null = null;\n @state() _dragOver = false;\n // Not @state — updated directly to avoid 60fps Lit re-renders\n _selectionStartTime = 0;\n _selectionEndTime = 0;\n _currentTime = 0;\n @property({ attribute: false })\n set adapter(value: PlayoutAdapter | null) {\n if (value && value.audioContext.state === 'closed') {\n console.warn('[dawcore] Adapter AudioContext is already closed. Ignoring.');\n return;\n }\n if (this._engine) {\n console.warn(\n '[dawcore] adapter set after engine is built. ' +\n 'The engine will continue using the previous adapter.'\n );\n }\n this._externalAdapter = value;\n }\n get adapter(): PlayoutAdapter | null {\n return this._externalAdapter;\n }\n private _externalAdapter: PlayoutAdapter | null = null;\n\n get audioContext(): AudioContext {\n if (!this._externalAdapter) {\n throw new Error(NO_ADAPTER_ERROR);\n }\n return this._externalAdapter.audioContext;\n }\n _engine: PlaylistEngine | null = null;\n private _warnedMissingTicksToSeconds = false;\n private _warnedMissingSecondsToTicks = false;\n private _enginePromise: Promise<PlaylistEngine> | null = null;\n _audioCache = new Map<string, Promise<AudioBuffer>>();\n private _peaksCache = new Map<string, Promise<import('waveform-data').default>>();\n _clipBuffers = new Map<string, AudioBuffer>();\n _clipOffsets = new Map<string, { offsetSamples: number; durationSamples: number }>();\n _peakPipeline = new PeakPipeline();\n /** Coarsest scale from pre-computed peaks — zoom cannot go finer than this. 0 = no limit. */\n private _minSamplesPerPixel = 0;\n private _trackElements = new Map<string, DawTrackElement>();\n private _childObserver: MutationObserver | null = null;\n private _audioResume = new AudioResumeController(this);\n @property({ attribute: 'eager-resume' })\n eagerResume?: string;\n private _recordingController = new RecordingController(this);\n private _spectrogramController: SpectrogramController | null = null;\n private _clipPointer = new ClipPointerHandler(this);\n get _clipHandler() {\n return this.interactiveClips ? this._clipPointer : null;\n }\n get engine() {\n return this._engine;\n }\n get renderSamplesPerPixel() {\n return this._renderSpp;\n }\n /** Re-extract peaks for a clip at new offset/duration from cached WaveformData. */\n reextractClipPeaks(\n clipId: string,\n offsetSamples: number,\n durationSamples: number\n ): PeakData | null {\n const buf = this._clipBuffers.get(clipId);\n if (!buf) return null;\n const singleClipBuffers = new Map([[clipId, buf]]);\n const singleClipOffsets = new Map([[clipId, { offsetSamples, durationSamples }]]);\n const result = this._peakPipeline.reextractPeaks(\n singleClipBuffers,\n this._renderSpp,\n this.mono,\n singleClipOffsets\n );\n return result.get(clipId) ?? null;\n }\n\n /**\n * Returns true if the clip is a MIDI clip (has midiNotes).\n * Used by ClipPointerHandler to make trim handles inert for MIDI clips.\n * Returns false for unknown track/clip IDs (defensive).\n */\n isMidiClip(trackId: string, clipId: string): boolean {\n const track = this._engineTracks.get(trackId);\n if (!track) return false;\n const clip = track.clips.find((c) => c.id === clipId);\n return clip?.midiNotes != null;\n }\n\n private _pointer = new PointerHandler(this);\n private _viewport = (() => {\n const v = new ViewportController(this);\n v.scrollSelector = '.scroll-area';\n return v;\n })();\n private _scrollSync = (() => {\n const s = new ScrollSyncController(this);\n s.scrollSelector = '.scroll-area';\n // xTargetSelector, yTargetSelector, and wheelForwardSelector are kept\n // truthful by updated() — they start empty and are set before any scroll\n // is possible (updated() runs before paint on every reactive update).\n return s;\n })();\n\n static styles = [\n hostStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n position: relative;\n background: var(--daw-background, #1a1a2e);\n overflow: hidden;\n }\n .header-row {\n display: flex;\n flex-shrink: 0;\n }\n .ruler-gap {\n flex-shrink: 0;\n width: var(--daw-controls-width, 180px);\n }\n .ruler-viewport {\n flex: 1;\n position: relative;\n overflow: hidden;\n cursor: text;\n }\n .ruler-content {\n will-change: transform;\n }\n .body {\n flex: 1;\n min-height: 0;\n display: flex;\n }\n .controls-viewport {\n flex-shrink: 0;\n width: var(--daw-controls-width, 180px);\n overflow: hidden;\n }\n .controls-column {\n will-change: transform;\n }\n .scroll-area {\n flex: 1;\n overflow: auto;\n overflow-anchor: none;\n min-height: var(--daw-min-height, 200px);\n }\n .timeline {\n position: relative;\n min-height: 100%;\n cursor: text;\n }\n .track-row {\n position: relative;\n box-sizing: border-box;\n background: var(--daw-track-background, #16213e);\n border-bottom: 1px solid rgba(255, 255, 255, 0.05);\n }\n .track-row.selected {\n background: rgba(99, 199, 95, 0.08);\n }\n :host([scale-mode='beats']) .track-row {\n background: transparent;\n }\n :host([scale-mode='beats']) .clip-container {\n background: var(--daw-track-background, #16213e);\n }\n :host([scale-mode='beats']) .track-row.selected .clip-container {\n box-shadow: inset 0 0 0 1000px rgba(99, 199, 95, 0.06);\n }\n .timeline.drag-over {\n outline: 2px dashed var(--daw-selection-color, rgba(99, 199, 95, 0.3));\n outline-offset: -2px;\n }\n `,\n clipStyles,\n ];\n\n get effectiveSampleRate(): number {\n return this._resolvedSampleRate ?? this.sampleRate;\n }\n resolveAudioContextSampleRate(rate: number) {\n if (!this._resolvedSampleRate) this._resolvedSampleRate = rate;\n }\n /**\n * In beats mode, derive samplesPerPixel from ticksPerPixel so that\n * clip positions, waveforms, and the tick-space grid all align.\n */\n private get _renderSpp(): number {\n if (this.scaleMode === 'beats') {\n // Round to integer — WaveformData.resample() uses integer scale math.\n const spp = Math.ceil(\n (60 * this.effectiveSampleRate * this.ticksPerPixel) / (this.ppqn * this.bpm)\n );\n // Floor at the peak pipeline's base scale so peaks can always be extracted.\n // Without this, fine zoom levels request a scale finer than what WaveformData\n // can resample to, causing blank waveforms.\n return this._minSamplesPerPixel > 0 ? Math.max(spp, this._minSamplesPerPixel) : spp;\n }\n return this.samplesPerPixel;\n }\n /** Convert seconds to ticks — uses callback if provided, otherwise single-BPM fallback. */\n _secondsToTicks(seconds: number): number {\n if (this.secondsToTicks) {\n if (!this.ticksToSeconds && !this._warnedMissingTicksToSeconds) {\n this._warnedMissingTicksToSeconds = true;\n console.warn(\n '[waveform-playlist] daw-editor: secondsToTicks is set but ticksToSeconds is missing. Both callbacks are required for variable tempo.'\n );\n }\n return this.secondsToTicks(seconds);\n }\n return (seconds * this.bpm * this.ppqn) / 60;\n }\n /** Convert ticks to seconds — uses callback if provided, otherwise single-BPM fallback. */\n _ticksToSeconds(ticks: number): number {\n if (this.ticksToSeconds) {\n if (!this.secondsToTicks && !this._warnedMissingSecondsToTicks) {\n this._warnedMissingSecondsToTicks = true;\n console.warn(\n '[waveform-playlist] daw-editor: ticksToSeconds is set but secondsToTicks is missing. Both callbacks are required for variable tempo.'\n );\n }\n return this.ticksToSeconds(ticks);\n }\n return (ticks * 60) / (this.bpm * this.ppqn);\n }\n private get _totalWidth(): number {\n if (this.scaleMode === 'beats') {\n const contentTicks = this._secondsToTicks(this._duration);\n // Floor at 32 bars so the grid is always visible — DAW convention.\n const [num] = this.timeSignature;\n const minTicks = 32 * num * this.ppqn;\n return Math.ceil(Math.max(contentTicks, minTicks) / this.ticksPerPixel);\n }\n const naturalWidth = Math.ceil(\n (this._duration * this.effectiveSampleRate) / this.samplesPerPixel\n );\n if (this.indefinitePlayback) {\n // Fill the visible viewport when natural duration is shorter — lets the\n // ruler render before any audio is loaded. ViewportController exposes\n // the scroll-area's clientWidth (updated on attach + ResizeObserver).\n return Math.max(naturalWidth, this._viewport.containerWidth);\n }\n return naturalWidth;\n }\n /** Grid height when no tracks exist — matches scroll area's rendered height. */\n private get _emptyGridHeight(): number {\n const scrollArea = this.shadowRoot?.querySelector('.scroll-area') as HTMLElement | null;\n return scrollArea?.clientHeight ?? 200;\n }\n _setSelectedTrackId(trackId: string | null) {\n this._selectedTrackId = trackId;\n }\n get tracks(): TrackDescriptor[] {\n return [...this._tracks.values()];\n }\n get selectedTrackId(): string | null {\n return this._selectedTrackId;\n }\n get selection(): { start: number; end: number } | null {\n if (this._selectionStartTime === 0 && this._selectionEndTime === 0) return null;\n return { start: this._selectionStartTime, end: this._selectionEndTime };\n }\n setSelection(start: number, end: number) {\n this._selectionStartTime = Math.min(start, end);\n this._selectionEndTime = Math.max(start, end);\n if (this._engine) {\n this._engine.setSelection(this._selectionStartTime, this._selectionEndTime);\n }\n this.requestUpdate();\n this.dispatchEvent(\n new CustomEvent<DawSelectionDetail>('daw-selection', {\n bubbles: true,\n composed: true,\n detail: { start: this._selectionStartTime, end: this._selectionEndTime },\n })\n );\n }\n // --- Lifecycle ---\n connectedCallback() {\n super.connectedCallback();\n this.addEventListener('daw-track-connected', this._onTrackConnected as EventListener);\n this.addEventListener('daw-track-update', this._onTrackUpdate as EventListener);\n this.addEventListener('daw-track-control', this._onTrackControl as EventListener);\n this.addEventListener('daw-track-remove', this._onTrackRemoveRequest as EventListener);\n this.addEventListener('daw-clip-connected', this._onClipConnected as EventListener);\n this.addEventListener('daw-clip-update', this._onClipUpdate as EventListener);\n // Detect track + clip removal via MutationObserver (detached elements can't bubble events).\n this._childObserver = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n for (const node of mutation.removedNodes) {\n if (node instanceof HTMLElement) {\n if (node.tagName === 'DAW-TRACK') {\n this._onTrackRemoved((node as DawTrackElement).trackId);\n } else if (node.tagName === 'DAW-CLIP') {\n this._onClipRemovedFromDom(node as DawClipElement);\n }\n const nestedTracks = node.querySelectorAll?.('daw-track');\n if (nestedTracks) {\n for (const track of nestedTracks) {\n this._onTrackRemoved((track as DawTrackElement).trackId);\n }\n }\n const nestedClips = node.querySelectorAll?.('daw-clip');\n if (nestedClips) {\n for (const clip of nestedClips) {\n this._onClipRemovedFromDom(clip as DawClipElement);\n }\n }\n }\n }\n }\n });\n this._childObserver.observe(this, { childList: true, subtree: true });\n }\n disconnectedCallback() {\n super.disconnectedCallback();\n this.removeEventListener('daw-track-connected', this._onTrackConnected as EventListener);\n this.removeEventListener('daw-track-update', this._onTrackUpdate as EventListener);\n this.removeEventListener('daw-track-control', this._onTrackControl as EventListener);\n this.removeEventListener('daw-track-remove', this._onTrackRemoveRequest as EventListener);\n this.removeEventListener('daw-clip-connected', this._onClipConnected as EventListener);\n this.removeEventListener('daw-clip-update', this._onClipUpdate as EventListener);\n this._childObserver?.disconnect();\n this._childObserver = null;\n this._trackElements.clear();\n this._audioCache.clear();\n this._peaksCache.clear();\n this._clipBuffers.clear();\n this._clipOffsets.clear();\n this._peakPipeline.terminate();\n this._minSamplesPerPixel = 0;\n this._spectrogramController?.dispose();\n this._spectrogramController = null;\n try {\n this._disposeEngine();\n } catch (err) {\n console.warn('[dawcore] Error disposing engine: ' + String(err));\n }\n }\n willUpdate(changedProperties: Map<string, unknown>) {\n if (changedProperties.has('eagerResume')) {\n this._audioResume.target = this.eagerResume;\n }\n // Restart playhead animation when zoom or beats params change during playback\n if (\n (changedProperties.has('samplesPerPixel') ||\n changedProperties.has('ticksPerPixel') ||\n changedProperties.has('bpm') ||\n changedProperties.has('secondsToTicks')) &&\n this._isPlaying\n ) {\n this._startPlayhead();\n }\n // Re-extract peaks at new zoom level from cached WaveformData (near-instant).\n // For worker-generated peaks, baseScale (128) is finest; for pre-computed .dat\n // peaks (only cached when rates match), the file's scale is the limit.\n // In beats mode, _renderSpp changes when ticksPerPixel or bpm changes.\n const zoomChanged =\n changedProperties.has('samplesPerPixel') ||\n changedProperties.has('ticksPerPixel') ||\n changedProperties.has('bpm') ||\n changedProperties.has('scaleMode') ||\n changedProperties.has('secondsToTicks');\n if (zoomChanged && this._clipBuffers.size > 0) {\n const re = this._peakPipeline.reextractPeaks(\n this._clipBuffers,\n this._renderSpp,\n this.mono,\n this._clipOffsets\n );\n if (re.size > 0) {\n const next = new Map(this._peaksData);\n for (const [id, pd] of re) next.set(id, pd);\n this._peaksData = next;\n }\n }\n }\n\n /**\n * Cache of the last ViewportState forwarded to the spectrogram controller.\n * Lit's `updated()` fires on every reactive state change (`_isPlaying`,\n * `_selectedTrackId`, etc.) — most of which don't affect the spectrogram\n * viewport. Skip the cross-controller call when nothing changed.\n *\n * The orchestrator dedupes identical viewports too, so removing this cache\n * wouldn't change observable behavior — but it would push a fresh\n * `setViewport` call (with object allocation) into every Lit reactive\n * update for properties unrelated to the viewport.\n */\n private _lastSpectrogramViewport: {\n vs: number;\n ve: number;\n spp: number;\n } | null = null;\n\n protected updated(_changed: Map<string, unknown>): void {\n // Keep selectors truthful so the controller can distinguish \"intentionally\n // not rendered\" from \"selector broken\" (it warns on the latter when scrolled).\n this._scrollSync.xTargetSelector = this._showRuler ? '.ruler-content' : '';\n this._scrollSync.yTargetSelector = this._showControls ? '.controls-column' : '';\n this._scrollSync.wheelForwardSelector = [\n this._showControls ? '.controls-viewport' : '',\n this._showRuler ? '.ruler-viewport' : '',\n ]\n .filter(Boolean)\n .join(', ');\n // Re-applies transforms clobbered by the .ruler-content style binding —\n // Lit rewrites the style attribute (width) on _totalWidth changes, wiping\n // the imperatively-set transform. Must run before paint on every update.\n this._scrollSync.sync();\n // Forward viewport + zoom into the spectrogram controller on every update\n // so scroll, resize, and zoom all trigger orchestrator.setViewport.\n if (this._spectrogramController) {\n const vs = this._viewport.visibleStart;\n const ve = this._viewport.visibleEnd;\n const spp = this._renderSpp;\n if (Number.isFinite(vs) && Number.isFinite(ve)) {\n const prev = this._lastSpectrogramViewport;\n if (prev && prev.vs === vs && prev.ve === ve && prev.spp === spp) return;\n this._lastSpectrogramViewport = { vs, ve, spp };\n const span = ve - vs;\n const bufferPad = span * 0.25;\n this._spectrogramController.setViewport({\n visibleStartPx: vs,\n visibleEndPx: ve,\n bufferStartPx: Math.max(0, vs - bufferPad),\n bufferEndPx: ve + bufferPad,\n samplesPerPixel: spp,\n });\n }\n }\n }\n\n // --- Track Events ---\n private _onTrackConnected = (e: CustomEvent) => {\n const trackId = e.detail?.trackId;\n const trackEl = e.detail?.element;\n if (!trackId || !(trackEl instanceof HTMLElement)) {\n console.warn('[dawcore] Invalid daw-track-connected event detail: ' + String(e.detail));\n return;\n }\n const descriptor = this._readTrackDescriptor(trackEl as DawTrackElement);\n this._tracks = new Map(this._tracks).set(trackId, descriptor);\n this._trackElements.set(trackId, trackEl as DawTrackElement);\n this._loadTrack(trackId, descriptor);\n };\n private _onTrackRemoved(trackId: string) {\n this._trackElements.delete(trackId);\n // Clean up per-clip data before removing the track (need clip IDs from engine tracks)\n const removedTrack = this._engineTracks.get(trackId);\n if (removedTrack) {\n const nextPeaks = new Map(this._peaksData);\n for (const clip of removedTrack.clips) {\n this._clipBuffers.delete(clip.id);\n this._clipOffsets.delete(clip.id);\n nextPeaks.delete(clip.id);\n this._spectrogramController?.unregisterClipAudio(clip.id);\n }\n this._peaksData = nextPeaks;\n }\n const nextTracks = new Map(this._tracks);\n nextTracks.delete(trackId);\n this._tracks = nextTracks;\n const nextEngine = new Map(this._engineTracks);\n nextEngine.delete(trackId);\n this._engineTracks = nextEngine;\n this._recomputeDuration();\n if (this._engine) {\n // Incremental removal preserves playback (no playout rebuild)\n this._engine.removeTrack(trackId);\n }\n // Recompute zoom floor from remaining cached WaveformData scales\n this._minSamplesPerPixel = this._peakPipeline.getMaxCachedScale(this._clipBuffers);\n this._disposeSpectrogramControllerIfEmpty();\n if (nextEngine.size === 0) {\n this._currentTime = 0;\n this._stopPlayhead();\n }\n }\n private _onTrackUpdate = (e: CustomEvent) => {\n const trackId = e.detail?.trackId as string;\n if (!trackId) return;\n const trackEl = (e.target as HTMLElement).closest('daw-track') as DawTrackElement | null;\n if (!trackEl) return;\n const oldDescriptor = this._tracks.get(trackId);\n const descriptor = this._readTrackDescriptor(trackEl);\n this._tracks = new Map(this._tracks).set(trackId, descriptor);\n if (this._engine) {\n if (oldDescriptor?.volume !== descriptor.volume)\n this._engine.setTrackVolume(trackId, descriptor.volume);\n if (oldDescriptor?.pan !== descriptor.pan) this._engine.setTrackPan(trackId, descriptor.pan);\n if (oldDescriptor?.muted !== descriptor.muted)\n this._engine.setTrackMute(trackId, descriptor.muted);\n if (oldDescriptor?.soloed !== descriptor.soloed)\n this._engine.setTrackSolo(trackId, descriptor.soloed);\n }\n if (oldDescriptor?.src !== descriptor.src) {\n this._loadTrack(trackId, descriptor);\n }\n // Track switched into spectrogram mode — push existing clip audio into the controller.\n if (descriptor.renderMode === 'spectrogram' && oldDescriptor?.renderMode !== 'spectrogram') {\n const engineTrack = this._engineTracks.get(trackId);\n if (engineTrack) {\n for (const clip of engineTrack.clips) {\n this._maybeRegisterSpectrogramClipAudio(trackId, clip);\n }\n }\n }\n // Track switched OUT of spectrogram mode — drop clip audio.\n if (descriptor.renderMode !== 'spectrogram' && oldDescriptor?.renderMode === 'spectrogram') {\n const engineTrack = this._engineTracks.get(trackId);\n if (engineTrack && this._spectrogramController) {\n for (const clip of engineTrack.clips) {\n this._spectrogramController.unregisterClipAudio(clip.id);\n }\n }\n this._disposeSpectrogramControllerIfEmpty();\n }\n if (descriptor.spectrogramConfig !== oldDescriptor?.spectrogramConfig) {\n this._spectrogramController?.setTrackConfig(trackId, descriptor.spectrogramConfig ?? null);\n }\n };\n\n /** Drop the controller when no spectrogram tracks remain. */\n private _disposeSpectrogramControllerIfEmpty(): void {\n if (!this._spectrogramController) return;\n const stillNeeded = Array.from(this._tracks.values()).some(\n (d) => d.renderMode === 'spectrogram'\n );\n if (!stillNeeded) {\n this._spectrogramController.dispose();\n this._spectrogramController = null;\n }\n }\n private static _CONTROL_PROPS = new Set(['volume', 'pan', 'muted', 'soloed']);\n private _onTrackControl = (e: CustomEvent) => {\n const { trackId, prop, value } = e.detail ?? {};\n if (!trackId || !prop || !DawEditorElement._CONTROL_PROPS.has(prop)) return;\n // Select the track when interacting with its controls\n if (this._selectedTrackId !== trackId) {\n this._setSelectedTrackId(trackId);\n if (this._engine) {\n this._engine.selectTrack(trackId);\n }\n this.dispatchEvent(\n new CustomEvent('daw-track-select', {\n bubbles: true,\n composed: true,\n detail: { trackId },\n })\n );\n }\n const oldDescriptor = this._tracks.get(trackId);\n if (oldDescriptor) {\n const descriptor = { ...oldDescriptor, [prop]: value };\n this._tracks = new Map(this._tracks).set(trackId, descriptor);\n // Forward to engine with validated values\n if (this._engine) {\n if (prop === 'volume')\n this._engine.setTrackVolume(trackId, Math.max(0, Math.min(1, Number(value))));\n if (prop === 'pan')\n this._engine.setTrackPan(trackId, Math.max(-1, Math.min(1, Number(value))));\n if (prop === 'muted') this._engine.setTrackMute(trackId, Boolean(value));\n if (prop === 'soloed') this._engine.setTrackSolo(trackId, Boolean(value));\n }\n }\n // Don't sync back to <daw-track> DOM element — avoids daw-track-update loop.\n // _tracks descriptor map is the source of truth for control values.\n };\n private _onTrackRemoveRequest = (e: CustomEvent) => {\n const { trackId } = e.detail ?? {};\n if (!trackId) return;\n const trackEl = this._trackElements.get(trackId);\n if (trackEl) {\n trackEl.remove(); // MutationObserver will trigger _onTrackRemoved\n } else {\n this._onTrackRemoved(trackId); // File-dropped tracks: no DOM element\n }\n };\n // --- Clip lifecycle ---\n private _onClipConnected = (e: CustomEvent) => {\n const detail = e.detail as { clipId: string; element: DawClipElement };\n const clipEl = detail.element;\n if (!(clipEl instanceof HTMLElement)) return;\n const trackEl = clipEl.closest('daw-track') as DawTrackElement | null;\n if (!trackEl) return;\n const trackId = trackEl.trackId;\n // Skip during initial track load — daw-track-connected reads all <daw-clip>\n // children synchronously via _readTrackDescriptor. Late-append clips trigger\n // an incremental load below.\n if (!this._engineTracks.has(trackId)) {\n // _tracks is populated in _onTrackConnected before _loadTrack runs, so\n // having a descriptor without an engine track means the parent is still\n // loading. _readTrackDescriptor already captured each existing <daw-clip>\n // child's id into descriptor.clips — those deferred daw-clip-connected\n // events are redundant and silent skip is correct. Only a clipId that\n // wasn't in the pre-load capture is a true late-append that risks being\n // missed; warn for those.\n const desc = this._tracks.get(trackId);\n if (desc && !desc.clips.some((c) => isDomClip(c) && c.clipId === clipEl.clipId)) {\n console.warn(\n '[dawcore] daw-clip-connected fired while parent track \"' +\n trackId +\n '\" is still loading — late-appended clip may be missed. ' +\n 'Wait for daw-track-ready before appending more <daw-clip> children, ' +\n 'or use editor.addClip(trackId, config) after the track finishes loading.'\n );\n }\n return;\n }\n const clipDesc: ClipDescriptor = {\n kind: 'dom',\n clipId: clipEl.clipId,\n src: clipEl.src,\n peaksSrc: clipEl.peaksSrc,\n start: clipEl.start,\n duration: clipEl.duration,\n offset: clipEl.offset,\n gain: clipEl.gain,\n name: clipEl.name,\n fadeIn: clipEl.fadeIn,\n fadeOut: clipEl.fadeOut,\n fadeType: clipEl.fadeType as FadeType,\n midiNotes: clipEl.midiNotes,\n midiChannel: clipEl.midiChannel,\n midiProgram: clipEl.midiProgram,\n };\n this._loadAndAppendClip(trackId, clipDesc);\n };\n private _onClipUpdate = (e: CustomEvent) => {\n const clipEl = e.target as DawClipElement;\n if (!(clipEl instanceof HTMLElement) || clipEl.tagName !== 'DAW-CLIP') return;\n const detail = e.detail as { trackId: string; clipId: string };\n if (!detail.trackId) {\n // <daw-clip> mounted outside a <daw-track> — developer error.\n console.warn(\n '[dawcore] daw-clip-update fired from a <daw-clip> not nested in a <daw-track> — ignored'\n );\n return;\n }\n this._applyClipUpdate(detail.trackId, detail.clipId, clipEl);\n };\n private _onClipRemovedFromDom(clipEl: DawClipElement) {\n const clipId = clipEl.clipId;\n for (const [trackId, t] of this._engineTracks.entries()) {\n if (t.clips.some((c) => c.id === clipId)) {\n this._removeClipFromTrack(trackId, clipId);\n return;\n }\n }\n // No matching engine clip. May be benign (clip was removed before its load\n // completed), but it can also indicate a DOM/engine id mismatch — purge any\n // orphan cache entries and warn so the leak is visible.\n if (\n this._clipBuffers.has(clipId) ||\n this._clipOffsets.has(clipId) ||\n this._peaksData.has(clipId)\n ) {\n console.warn(\n '[dawcore] _onClipRemovedFromDom: orphaned cache entries for clip \"' +\n clipId +\n '\" — purging (DOM/engine id mismatch?)'\n );\n this._purgeClipCaches(clipId);\n }\n }\n private async _loadAndAppendClip(trackId: string, clipDesc: DomClipDescriptor) {\n if (!clipDesc.src) return; // Late-append of MIDI clips is not yet supported by _loadAndAppendClip — only _loadTrack handles the no-src MIDI branch on initial load.\n // Late-append always comes via _onClipConnected, which only fires for\n // <daw-clip> elements — so clipId is always known. We use it for the\n // error dispatch so the consumer's addClip Promise rejects with a usable\n // identifier even if _finalizeAudioClip throws before clip.id is set.\n const clipId = clipDesc.clipId;\n // Track which clip id has been inserted into per-clip caches so the catch\n // block can roll back partial state on any error past the cache writes.\n let insertedClipId: string | null = null;\n try {\n // Concurrent fetches: peaks (if provided) + audio decode.\n const waveformDataPromise = clipDesc.peaksSrc\n ? this._resolvePeaks(clipDesc.peaksSrc)\n : Promise.resolve(null);\n const audioPromise = this._fetchAndDecode(clipDesc.src);\n const [waveformData, audioBuffer] = await Promise.all([waveformDataPromise, audioPromise]);\n this._resolvedSampleRate = audioBuffer.sampleRate;\n\n const clip = await this._finalizeAudioClip(clipDesc, audioBuffer, waveformData);\n insertedClipId = clip.id;\n\n const t = this._engineTracks.get(trackId);\n if (!t) {\n // Track was removed during load — purge clip state\n this._purgeClipCaches(clip.id);\n return;\n }\n const updatedTrack: ClipTrack = { ...t, clips: [...t.clips, clip] };\n this._engineTracks = new Map(this._engineTracks).set(trackId, updatedTrack);\n\n const desc = this._tracks.get(trackId);\n if (desc) {\n this._tracks = new Map(this._tracks).set(trackId, {\n ...desc,\n clips: [...desc.clips, clipDesc],\n });\n }\n this._commitTrackChange(trackId, updatedTrack);\n this._maybeRegisterSpectrogramClipAudio(trackId, clip);\n\n this.dispatchEvent(\n new CustomEvent<DawClipIdDetail>('daw-clip-ready', {\n bubbles: true,\n composed: true,\n detail: { trackId, clipId: clip.id },\n })\n );\n } catch (err) {\n // Always warn — even when disconnected — so the failure isn't silent.\n console.warn('[dawcore] _loadAndAppendClip failed: ' + String(err));\n // Roll back partial cache state so a retry isn't poisoned by stale entries.\n if (insertedClipId) this._purgeClipCaches(insertedClipId);\n // Detached elements can't bubble events, but the listener registered on\n // `this` via addClip will still fire — dispatch directly so the addClip\n // promise rejects instead of orphaning.\n this.dispatchEvent(\n new CustomEvent<DawClipErrorDetail>('daw-clip-error', {\n bubbles: true,\n composed: true,\n detail: { trackId, clipId: insertedClipId ?? clipId, error: err },\n })\n );\n }\n }\n /**\n * Resolve pre-computed peaks for a clip: fetch the .dat/.json, validate the\n * sample rate matches the AudioContext, return the WaveformData or null.\n * Warns on fetch failure and on sample-rate mismatch — never silent.\n *\n * Shared between `_loadTrack` (peaks-first preview path) and\n * `_loadAndAppendClip` (incremental late-append).\n */\n private async _resolvePeaks(peaksSrc: string): Promise<import('waveform-data').default | null> {\n try {\n const wd = await this._fetchPeaks(peaksSrc);\n const contextRate = this.audioContext.sampleRate;\n if (wd.sample_rate === contextRate) return wd;\n console.warn(\n '[dawcore] Pre-computed peaks at ' +\n wd.sample_rate +\n ' Hz do not match AudioContext at ' +\n contextRate +\n ' Hz — ignoring ' +\n peaksSrc +\n ', generating from audio'\n );\n return null;\n } catch (err) {\n console.warn(\n '[dawcore] Failed to load peaks from ' +\n peaksSrc +\n ': ' +\n String(err) +\n ' — falling back to AudioBuffer generation'\n );\n return null;\n }\n }\n /**\n * Construct an AudioClip from a decoded buffer (and optional WaveformData),\n * align its id with the source `<daw-clip>.clipId` when present, populate\n * `_clipBuffers` / `_clipOffsets`, generate peaks via the worker pipeline,\n * and populate `_peaksData`. Returns the finished AudioClip.\n *\n * Shared between `_loadTrack`'s standard path and `_loadAndAppendClip`.\n * Not used by `_loadTrack`'s peaks-first preview path because that path\n * uses sync `extractPeaks` and inserts a preview track BEFORE audio decode.\n */\n private async _finalizeAudioClip(\n clipDesc: ClipDescriptor,\n audioBuffer: AudioBuffer,\n waveformData: import('waveform-data').default | null\n ): Promise<AudioClip> {\n let clip: AudioClip;\n if (waveformData) {\n const wdRate = waveformData.sample_rate;\n clip = createClip({\n audioBuffer,\n waveformData,\n startSample: Math.round(clipDesc.start * wdRate),\n durationSamples: Math.round((clipDesc.duration || waveformData.duration) * wdRate),\n offsetSamples: Math.round(clipDesc.offset * wdRate),\n gain: clipDesc.gain,\n name: clipDesc.name,\n sampleRate: wdRate,\n sourceDurationSamples: Math.ceil(waveformData.duration * wdRate),\n });\n this._peakPipeline.cacheWaveformData(audioBuffer, waveformData);\n } else {\n clip = createClipFromSeconds({\n audioBuffer,\n startTime: clipDesc.start,\n duration: clipDesc.duration || audioBuffer.duration,\n offset: clipDesc.offset,\n gain: clipDesc.gain,\n name: clipDesc.name,\n sampleRate: audioBuffer.sampleRate,\n sourceDuration: audioBuffer.duration,\n });\n }\n if (isDomClip(clipDesc)) clip.id = clipDesc.clipId;\n\n this._clipBuffers = new Map(this._clipBuffers).set(clip.id, audioBuffer);\n this._clipOffsets.set(clip.id, {\n offsetSamples: clip.offsetSamples,\n durationSamples: clip.durationSamples,\n });\n // generatePeaks can fail (worker crash, CSP, OOM). Purge the cache entries\n // we just inserted so the caller's catch path doesn't leak. The caller may\n // also call _purgeClipCaches in its own error handler — _purgeClipCaches\n // is idempotent.\n let peakData: PeakData;\n try {\n peakData = await this._peakPipeline.generatePeaks(\n audioBuffer,\n this._renderSpp,\n this.mono,\n clip.offsetSamples,\n clip.durationSamples\n );\n } catch (err) {\n this._purgeClipCaches(clip.id);\n throw err;\n }\n this._peaksData = new Map(this._peaksData).set(clip.id, peakData);\n // Raise the zoom-floor only after generatePeaks succeeds — without this,\n // a generatePeaks failure would strand _minSamplesPerPixel at a value\n // backed by no actual peaks (CLAUDE.md `samplesPerPixel` Zoom Floor rule).\n if (waveformData) {\n this._minSamplesPerPixel = Math.max(this._minSamplesPerPixel, waveformData.scale);\n }\n return clip;\n }\n /**\n * Filter MIDI notes to only those with finite, in-range fields. Logs a\n * warning for each dropped note. Used by _buildMidiClip and the\n * _applyClipUpdate MIDI branch to prevent NaN propagation through the\n * timeline.\n */\n private _validMidiNotes(notes: MidiNoteData[]): MidiNoteData[] {\n return notes.filter((n) => {\n const ok =\n Number.isFinite(n.time) &&\n n.time >= 0 &&\n Number.isFinite(n.duration) &&\n n.duration > 0 &&\n Number.isInteger(n.midi) &&\n n.midi >= 0 &&\n n.midi <= 127 &&\n Number.isFinite(n.velocity) &&\n n.velocity >= 0 &&\n n.velocity <= 1;\n if (!ok) {\n console.warn('[dawcore] dropping malformed MIDI note: ' + JSON.stringify(n));\n }\n return ok;\n });\n }\n /**\n * A clip descriptor is treated as MIDI when it has no audio src.\n * Includes placeholder MIDI clips (no notes, no duration yet — registered\n * with a 1s default span; notes upgrade via _applyClipUpdate). Warns when\n * a clip ambiguously has both src and midiNotes — the audio path runs\n * and notes would be silently ignored.\n */\n private _isMidiDescriptor(clipDesc: ClipDescriptor): boolean {\n if (clipDesc.src) {\n if (clipDesc.midiNotes != null) {\n console.warn(\n '[dawcore] clip \"' +\n (clipDesc.name || (isDomClip(clipDesc) ? clipDesc.clipId : '?')) +\n '\" has both src and midiNotes — treating as audio (notes will be ignored)'\n );\n }\n return false;\n }\n return true;\n }\n /**\n * Build an engine clip from a MIDI clip descriptor. Always returns a clip\n * — empty notes / no declared duration get a 1-second placeholder span so\n * the clip is reachable via `engine.updateTrack` once notes arrive.\n */\n private _buildMidiClip(clipDesc: ClipDescriptor): AudioClip {\n const sr = this.effectiveSampleRate;\n const notes = this._validMidiNotes(clipDesc.midiNotes ?? []);\n const noteSpanSeconds = notes.length\n ? notes.reduce((max, n) => Math.max(max, n.time + n.duration), 0)\n : 0;\n const sourceDurationSamples = Math.ceil(Math.max(noteSpanSeconds, clipDesc.duration, 1) * sr);\n const requestedDurationSamples =\n clipDesc.duration > 0 ? Math.round(clipDesc.duration * sr) : sourceDurationSamples;\n\n const clip = createClip({\n startSample: Math.round(clipDesc.start * sr),\n durationSamples: requestedDurationSamples,\n offsetSamples: Math.round(clipDesc.offset * sr),\n sampleRate: sr,\n sourceDurationSamples,\n gain: clipDesc.gain,\n name: clipDesc.name,\n midiNotes: notes,\n midiChannel: clipDesc.midiChannel ?? undefined,\n midiProgram: clipDesc.midiProgram ?? undefined,\n });\n if (isDomClip(clipDesc)) clip.id = clipDesc.clipId;\n return clip;\n }\n /** Remove a single clip from all per-clip caches. Used by error rollbacks. */\n private _purgeClipCaches(clipId: string) {\n const nextBuffers = new Map(this._clipBuffers);\n nextBuffers.delete(clipId);\n this._clipBuffers = nextBuffers;\n const nextPeaks = new Map(this._peaksData);\n nextPeaks.delete(clipId);\n this._peaksData = nextPeaks;\n this._clipOffsets.delete(clipId);\n this._spectrogramController?.unregisterClipAudio(clipId);\n }\n /**\n * Recompute duration and forward an updated track to the engine. Single\n * source of truth for the incremental-vs-full-rebuild policy used by every\n * clip-level mutation (addClip, updateClip, removeClip, _applyClipUpdate).\n * Use the engine's incremental updateTrack when available; otherwise fall\n * back to full setTracks (legacy adapters).\n */\n private _commitTrackChange(trackId: string, updatedTrack: ClipTrack) {\n this._recomputeDuration();\n if (this._engine?.updateTrack) this._engine.updateTrack(trackId, updatedTrack);\n else if (this._engine) this._engine.setTracks([...this._engineTracks.values()]);\n }\n private _applyClipUpdate(trackId: string, clipId: string, clipEl: DawClipElement) {\n const t = this._engineTracks.get(trackId);\n if (!t) {\n console.warn('[dawcore] _applyClipUpdate: no engine track for id \"' + trackId + '\"');\n return;\n }\n const idx = t.clips.findIndex((c) => c.id === clipId);\n if (idx === -1) {\n console.warn(\n '[dawcore] _applyClipUpdate: clip \"' +\n clipId +\n '\" not found in track \"' +\n trackId +\n '\" (DOM/engine clip-id misalignment?)'\n );\n return;\n }\n const oldClip = t.clips[idx];\n const sr = oldClip.sampleRate ?? this.effectiveSampleRate;\n\n // MIDI clips: rebuild the clip when notes/channel/program change. Detect\n // MIDI by either current or previous state being MIDI. oldClip.midiNotes\n // is an array (possibly empty []) for any clip registered via _buildMidiClip\n // — including placeholders. For audio clips it is undefined.\n // Use loose != null consistently: treats both null and undefined as \"not MIDI\"\n // for both clipEl.midiNotes (DawClipElement defaults to null) and oldClip.midiNotes.\n const isMidiNow = clipEl.midiNotes != null;\n const wasMidi = oldClip.midiNotes != null;\n if (isMidiNow || wasMidi) {\n const notes = this._validMidiNotes(clipEl.midiNotes ?? []);\n const noteSpanSeconds = notes.length\n ? notes.reduce((max, n) => Math.max(max, n.time + n.duration), 0)\n : 0;\n const sourceDurationSamples = Math.ceil(Math.max(noteSpanSeconds, clipEl.duration, 1) * sr);\n const requestedDurationSamples =\n clipEl.duration > 0 ? Math.round(clipEl.duration * sr) : sourceDurationSamples;\n\n const updatedClip: AudioClip = {\n ...oldClip,\n audioBuffer: undefined,\n startSample: Math.round(clipEl.start * sr),\n offsetSamples: Math.round(clipEl.offset * sr),\n durationSamples: requestedDurationSamples,\n sourceDurationSamples,\n gain: clipEl.gain,\n name: clipEl.name || oldClip.name,\n midiNotes: notes,\n midiChannel: clipEl.midiChannel ?? undefined,\n midiProgram: clipEl.midiProgram ?? undefined,\n };\n const updatedClips = [...t.clips];\n updatedClips[idx] = updatedClip;\n const updatedTrack: ClipTrack = { ...t, clips: updatedClips };\n this._engineTracks = new Map(this._engineTracks).set(trackId, updatedTrack);\n // Drop any audio caches in case this clip was previously loaded as audio.\n this._purgeClipCaches(clipId);\n this._commitTrackChange(trackId, updatedTrack);\n return;\n }\n\n const newStartSample = Math.round(clipEl.start * sr);\n const newDurationSamples =\n clipEl.duration > 0 ? Math.round(clipEl.duration * sr) : oldClip.durationSamples;\n const newOffsetSamples = Math.round(clipEl.offset * sr);\n const updatedClip = {\n ...oldClip,\n startSample: newStartSample,\n durationSamples: newDurationSamples,\n offsetSamples: newOffsetSamples,\n gain: clipEl.gain,\n name: clipEl.name || oldClip.name,\n };\n const updatedClips = [...t.clips];\n updatedClips[idx] = updatedClip;\n const updatedTrack: ClipTrack = { ...t, clips: updatedClips };\n this._engineTracks = new Map(this._engineTracks).set(trackId, updatedTrack);\n\n const boundsChanged =\n oldClip.offsetSamples !== newOffsetSamples || oldClip.durationSamples !== newDurationSamples;\n if (boundsChanged) {\n this._clipOffsets.set(clipId, {\n offsetSamples: newOffsetSamples,\n durationSamples: newDurationSamples,\n });\n const peaks = this.reextractClipPeaks(clipId, newOffsetSamples, newDurationSamples);\n if (peaks) {\n this._peaksData = new Map(this._peaksData).set(clipId, peaks);\n }\n }\n\n this._commitTrackChange(trackId, updatedTrack);\n }\n private _removeClipFromTrack(trackId: string, clipId: string) {\n const t = this._engineTracks.get(trackId);\n if (!t) {\n console.warn('[dawcore] _removeClipFromTrack: no engine track for id \"' + trackId + '\"');\n return;\n }\n const updatedClips = t.clips.filter((c) => c.id !== clipId);\n if (updatedClips.length === t.clips.length) {\n console.warn(\n '[dawcore] _removeClipFromTrack: clip \"' + clipId + '\" not found in track \"' + trackId + '\"'\n );\n return;\n }\n const updatedTrack: ClipTrack = { ...t, clips: updatedClips };\n this._engineTracks = new Map(this._engineTracks).set(trackId, updatedTrack);\n\n const nextBuffers = new Map(this._clipBuffers);\n nextBuffers.delete(clipId);\n this._clipBuffers = nextBuffers;\n this._clipOffsets.delete(clipId);\n const nextPeaks = new Map(this._peaksData);\n nextPeaks.delete(clipId);\n this._peaksData = nextPeaks;\n\n const desc = this._tracks.get(trackId);\n if (desc) {\n this._tracks = new Map(this._tracks).set(trackId, {\n ...desc,\n // Only DOM-sourced clips have an id to match; drop-sourced clips are\n // filtered through unchanged (their identity is the descriptor itself).\n clips: desc.clips.filter((c) => !(isDomClip(c) && c.clipId === clipId)),\n });\n }\n this._commitTrackChange(trackId, updatedTrack);\n }\n private _readTrackDescriptor(trackEl: DawTrackElement): TrackDescriptor {\n const clipEls = trackEl.querySelectorAll('daw-clip') as NodeListOf<DawClipElement>;\n const clips: ClipDescriptor[] = [];\n\n if (clipEls.length === 0 && trackEl.src) {\n // <daw-track src> shorthand — synthetic descriptor with no <daw-clip>\n // backing element. No DOM clipId to align with.\n clips.push({\n kind: 'drop',\n src: trackEl.src,\n peaksSrc: '',\n start: 0,\n duration: 0,\n offset: 0,\n gain: 1,\n name: trackEl.name || '',\n fadeIn: 0,\n fadeOut: 0,\n fadeType: 'linear',\n midiNotes: null,\n midiChannel: null,\n midiProgram: null,\n });\n } else {\n for (const clipEl of clipEls) {\n clips.push({\n kind: 'dom',\n clipId: clipEl.clipId,\n src: clipEl.src,\n peaksSrc: clipEl.peaksSrc,\n start: clipEl.start,\n duration: clipEl.duration,\n offset: clipEl.offset,\n gain: clipEl.gain,\n name: clipEl.name,\n fadeIn: clipEl.fadeIn,\n fadeOut: clipEl.fadeOut,\n fadeType: clipEl.fadeType as FadeType,\n midiNotes: clipEl.midiNotes,\n midiChannel: clipEl.midiChannel,\n midiProgram: clipEl.midiProgram,\n });\n }\n }\n return {\n name: trackEl.name || 'Untitled',\n src: trackEl.src,\n volume: trackEl.volume,\n pan: trackEl.pan,\n muted: trackEl.muted,\n soloed: trackEl.soloed,\n renderMode: trackEl.renderMode,\n clips,\n };\n }\n // --- Audio Loading ---\n private async _loadTrack(trackId: string, descriptor: TrackDescriptor) {\n try {\n const clips = [];\n for (const clipDesc of descriptor.clips) {\n if (this._isMidiDescriptor(clipDesc)) {\n // MIDI clip path: no fetch, no peaks, register the clip directly.\n // Always registers (even with no notes / no duration) so late note\n // arrivals via daw-clip-update — handled in _applyClipUpdate\n // — can find the clip in _engineTracks.\n clips.push(this._buildMidiClip(clipDesc));\n continue;\n }\n // Per-clip try/catch: a single bad clip dispatches daw-clip-error and\n // skips to the next clip rather than aborting the whole track. Without\n // this, clip N's failure leaks earlier clips' cache writes from this\n // loop because the outer catch never reaches engine.setTracks().\n try {\n // Start both fetches concurrently — await peaks first to render preview before audio decode\n const waveformDataPromise = clipDesc.peaksSrc\n ? this._resolvePeaks(clipDesc.peaksSrc)\n : Promise.resolve(null);\n const audioPromise = this._fetchAndDecode(clipDesc.src);\n\n // --- Peaks-first path: render waveform before audio decode completes ---\n // _resolvePeaks returns null on fetch failure or sample-rate mismatch\n // (warns in either case); the standard path below handles the null case.\n const waveformData = await waveformDataPromise;\n if (waveformData) {\n // Create clip with integer samples to avoid float round-trip drift\n // (CLAUDE.md pattern #40: prefer createClip when samples known)\n const wdRate = waveformData.sample_rate;\n const clip = createClip({\n waveformData,\n startSample: Math.round(clipDesc.start * wdRate),\n durationSamples: Math.round((clipDesc.duration || waveformData.duration) * wdRate),\n offsetSamples: Math.round(clipDesc.offset * wdRate),\n gain: clipDesc.gain,\n name: clipDesc.name,\n sampleRate: wdRate,\n sourceDurationSamples: Math.ceil(waveformData.duration * wdRate),\n });\n // Align engine clip.id with the source <daw-clip>.clipId (if any) so\n // DOM and engine refer to the same clip — required for editor.removeClip\n // and editor.updateClip lookups.\n if (isDomClip(clipDesc)) clip.id = clipDesc.clipId;\n const effectiveScale = Math.max(this._renderSpp, waveformData.scale);\n const peakData = extractPeaks(\n waveformData,\n effectiveScale,\n this.mono,\n clip.offsetSamples,\n clip.durationSamples\n );\n this._clipOffsets.set(clip.id, {\n offsetSamples: clip.offsetSamples,\n durationSamples: clip.durationSamples,\n });\n this._peaksData = new Map(this._peaksData).set(clip.id, peakData);\n this._minSamplesPerPixel = Math.max(this._minSamplesPerPixel, waveformData.scale);\n\n // Render preview track immediately with peaks (render-only until audio\n // completes and engine.setTracks() runs at end of _loadTrack)\n const previewTrack = createTrack({\n name: descriptor.name,\n clips: [clip],\n volume: descriptor.volume,\n pan: descriptor.pan,\n muted: descriptor.muted,\n soloed: descriptor.soloed,\n });\n previewTrack.id = trackId;\n this._engineTracks = new Map(this._engineTracks).set(trackId, previewTrack);\n this._recomputeDuration();\n\n // Wait for audio decode — clean up preview state if it fails\n let audioBuffer: AudioBuffer;\n try {\n audioBuffer = await audioPromise;\n } catch (audioErr) {\n // Remove ghost preview so the user doesn't see a waveform with no audio\n const nextPeaks = new Map(this._peaksData);\n nextPeaks.delete(clip.id);\n this._peaksData = nextPeaks;\n this._clipOffsets.delete(clip.id);\n const nextEngine = new Map(this._engineTracks);\n nextEngine.delete(trackId);\n this._engineTracks = nextEngine;\n this._minSamplesPerPixel = this._peakPipeline.getMaxCachedScale(this._clipBuffers);\n this._recomputeDuration();\n throw audioErr; // Propagate to outer catch for daw-track-error event\n }\n this._resolvedSampleRate = audioBuffer.sampleRate;\n // Backfill audioBuffer immutably: new clip replaces the preview clip\n const updatedClip = { ...clip, audioBuffer };\n this._clipBuffers = new Map(this._clipBuffers).set(clip.id, audioBuffer);\n this._peakPipeline.cacheWaveformData(audioBuffer, waveformData);\n clips.push(updatedClip);\n continue;\n }\n\n // --- Standard path: decode audio first, then generate peaks ---\n // Reached only when the peaks-first branch above didn't kick in\n // (no peaksSrc, fetch failure, or sample-rate mismatch). waveformData\n // is always null here.\n const audioBuffer = await audioPromise;\n this._resolvedSampleRate = audioBuffer.sampleRate;\n const clip = await this._finalizeAudioClip(clipDesc, audioBuffer, null);\n clips.push(clip);\n } catch (clipErr) {\n // _finalizeAudioClip and the peaks-first audio-decode catch already\n // purged their own per-clip caches before throwing here. Dispatch\n // daw-clip-error so consumers can correlate the failure to a clip.\n console.warn(\n '[dawcore] _loadTrack: clip \"' + clipDesc.src + '\" failed: ' + String(clipErr)\n );\n if (this.isConnected) {\n this.dispatchEvent(\n new CustomEvent<DawClipErrorDetail>('daw-clip-error', {\n bubbles: true,\n composed: true,\n detail: {\n trackId,\n clipId: isDomClip(clipDesc) ? clipDesc.clipId : '',\n error: clipErr,\n },\n })\n );\n }\n }\n }\n const track = createTrack({\n name: descriptor.name,\n clips,\n volume: descriptor.volume,\n pan: descriptor.pan,\n muted: descriptor.muted,\n soloed: descriptor.soloed,\n });\n // If clips were requested but ALL failed to load, surface as a track-level\n // error so addTrack({clips: [...]}) rejects appropriately. Per-clip\n // daw-clip-error events have already fired for each individual failure.\n const requestedClips = descriptor.clips.filter((c) => c.src).length;\n if (requestedClips > 0 && clips.length === 0) {\n throw new Error(\n 'all ' + requestedClips + ' clip(s) failed to load — see prior daw-clip-error events'\n );\n }\n // Align track.id with the editor's trackId so engine.setTrackSolo/Mute/etc. find it\n track.id = trackId;\n this._engineTracks = new Map(this._engineTracks).set(trackId, track);\n this._recomputeDuration();\n // Now that descriptor is recorded and clips have ids, push audio into\n // the spectrogram controller (no-op unless renderMode === 'spectrogram').\n for (const c of clips) {\n this._maybeRegisterSpectrogramClipAudio(trackId, c);\n }\n const engine = await this._ensureEngine();\n engine.setTracks([...this._engineTracks.values()]);\n this.dispatchEvent(\n new CustomEvent<DawTrackIdDetail>('daw-track-ready', {\n bubbles: true,\n composed: true,\n detail: { trackId },\n })\n );\n } catch (err) {\n // Guard against dispatching on a disconnected element (CLAUDE.md pattern #36)\n if (!this.isConnected) return;\n console.warn('[dawcore] Failed to load track \"' + trackId + '\": ' + String(err));\n this.dispatchEvent(\n new CustomEvent<DawTrackErrorDetail>('daw-track-error', {\n bubbles: true,\n composed: true,\n detail: { trackId, error: err },\n })\n );\n }\n }\n async _fetchAndDecode(src: string): Promise<AudioBuffer> {\n if (this._audioCache.has(src)) {\n return this._audioCache.get(src)!;\n }\n const promise = (async () => {\n const response = await fetch(src);\n if (!response.ok) {\n throw new Error(\n 'Failed to fetch audio \"' + src + '\": ' + response.status + ' ' + response.statusText\n );\n }\n const arrayBuffer = await response.arrayBuffer();\n return this.audioContext.decodeAudioData(arrayBuffer);\n })();\n this._audioCache.set(src, promise);\n try {\n return await promise;\n } catch (err) {\n this._audioCache.delete(src);\n throw err;\n }\n }\n private _fetchPeaks(src: string): Promise<import('waveform-data').default> {\n const cached = this._peaksCache.get(src);\n if (cached) return cached;\n const promise = loadWaveformDataFromUrl(src).catch((err) => {\n this._peaksCache.delete(src);\n throw err;\n });\n this._peaksCache.set(src, promise);\n return promise;\n }\n _recomputeDuration() {\n let maxSample = 0;\n for (const track of this._engineTracks.values()) {\n for (const clip of track.clips) {\n const endSample = clip.startSample + clip.durationSamples;\n if (endSample > maxSample) maxSample = endSample;\n }\n }\n this._duration = maxSample / this.effectiveSampleRate;\n }\n // --- Engine ---\n _ensureEngine(): Promise<PlaylistEngine> {\n if (this._engine) return Promise.resolve(this._engine);\n if (this._enginePromise) return this._enginePromise;\n this._enginePromise = this._buildEngine().catch((err) => {\n this._enginePromise = null;\n throw err;\n });\n return this._enginePromise;\n }\n private async _buildEngine() {\n if (!this._externalAdapter) {\n throw new Error(NO_ADAPTER_ERROR);\n }\n\n const { PlaylistEngine } = await import('@waveform-playlist/engine');\n const adapter = this._externalAdapter;\n\n // Forward initial tempo if adapter supports it\n if (adapter.setTempo) {\n adapter.setTempo(this._bpm);\n } else if (this._bpm !== 120) {\n console.warn(\n '[dawcore] Adapter does not implement setTempo. ' +\n 'Initial BPM ' +\n this._bpm +\n ' will not be applied — clips may use wrong tempo.'\n );\n }\n\n // Try to set the editor's desired PPQN on the adapter, then sync back.\n // Adapter is the source of truth — it may ignore the request.\n adapter.setPpqn?.(this._ppqn);\n this.ppqn = adapter.ppqn;\n\n const engine = new PlaylistEngine({\n adapter,\n sampleRate: this.effectiveSampleRate,\n samplesPerPixel: this.samplesPerPixel,\n bpm: this._bpm,\n zoomLevels: [256, 512, 1024, 2048, 4096, 8192, this.samplesPerPixel]\n .filter((v, i, a) => a.indexOf(v) === i)\n .sort((a, b) => a - b),\n });\n let lastTracksVersion = -1;\n engine.on('statechange', (engineState) => {\n this._isPlaying = engineState.isPlaying;\n this._duration = engineState.duration;\n this._selectedTrackId = engineState.selectedTrackId;\n // Sync clip positions when tracks change (moveClip, trimClip, splitClip)\n if (engineState.tracksVersion !== lastTracksVersion) {\n lastTracksVersion = engineState.tracksVersion;\n const nextTracks = new Map<string, ClipTrack>();\n for (const track of engineState.tracks) {\n nextTracks.set(track.id, track);\n }\n this._engineTracks = nextTracks;\n // Regenerate peaks for new or trimmed clips.\n // Piano-roll tracks have no AudioBuffer — skip them to avoid noisy\n // \"no AudioBuffer\" warnings on every tracksVersion bump.\n const audioTracks = engineState.tracks.filter((t) => {\n const desc = this._tracks.get(t.id);\n return desc?.renderMode !== 'piano-roll';\n });\n syncPeaksForChangedClips(this, audioTracks);\n }\n });\n engine.on('pause', () => {\n this._currentTime = engine.getCurrentTime();\n });\n engine.on('stop', () => {\n this._currentTime = engine.getCurrentTime();\n this._stopPlayhead();\n });\n\n this._engine = engine;\n return engine;\n }\n private _disposeEngine() {\n if (this._engine) {\n this._engine.dispose();\n this._engine = null;\n }\n this._enginePromise = null;\n }\n // --- File Drop ---\n private _onDragOver = (e: DragEvent) => {\n if (!this.fileDrop) return;\n e.preventDefault();\n if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy';\n this._dragOver = true;\n };\n private _onDragLeave = (e: DragEvent) => {\n if (!this.fileDrop) return;\n // relatedTarget is null when cursor leaves the browser window — that's fine,\n // we still want to clear _dragOver in that case.\n const timeline = this.shadowRoot?.querySelector('.timeline');\n if (timeline && !timeline.contains(e.relatedTarget as Node)) {\n this._dragOver = false;\n }\n };\n private _onDrop = async (e: DragEvent) => {\n if (!this.fileDrop) return;\n e.preventDefault();\n this._dragOver = false;\n const files = e.dataTransfer?.files;\n if (!files || files.length === 0) return;\n try {\n await this.loadFiles(files);\n } catch (err) {\n console.warn('[dawcore] File drop failed: ' + String(err));\n this.dispatchEvent(\n new CustomEvent<DawErrorDetail>('daw-error', {\n bubbles: true,\n composed: true,\n detail: { operation: 'file-drop', error: err },\n })\n );\n }\n };\n async loadFiles(files: FileList | File[]): Promise<LoadFilesResult> {\n return loadFilesImpl(this, files);\n }\n /**\n * Imperatively load a `.mid` file (URL or File) and create N `<daw-track>`\n * elements — one per note-bearing MIDI track. On any per-track failure,\n * every `<daw-track>` appended during the call is removed (both successful\n * and failed) so the editor returns to its pre-call state.\n *\n * `options.signal` is forwarded to `fetch()` only for URL sources; aborting\n * after parsing does not cancel in-flight `addTrack` calls.\n *\n * Requires the optional `@dawcore/midi` peer dep — throws with an install\n * hint (and `console.warn`s the original error) when the dynamic import\n * fails for any reason.\n */\n async loadMidi(source: string | File, options?: MidiLoadOptions): Promise<MidiLoadResult> {\n return loadMidiImpl(this, source, options);\n }\n // --- Programmatic Track API ---\n /**\n * Build the engine if it hasn't been built yet. Lets consumers obtain a\n * non-null `editor.engine` before any track has been loaded — useful for\n * wiring analyzers, effects, or master taps before content arrives.\n */\n async ready(): Promise<PlaylistEngine> {\n return this._ensureEngine();\n }\n /**\n * Wait for either `readyEvent` or `errorEvent` to fire on this editor for\n * the entity matching `matchesId`. Listeners are wired synchronously, then\n * `setup` is called (typical: appendChild). Resolves with `resolveValue`\n * on ready; rejects with a normalized Error on error. Used by addTrack and\n * addClip to share their Promise-with-listener-cleanup machinery.\n */\n private _awaitId<T>(\n readyEvent: string,\n errorEvent: string,\n matchesId: (detail: { trackId?: string; clipId?: string }) => boolean,\n resolveValue: T,\n setup: () => void\n ): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const onReady = (e: Event) => {\n if (!matchesId((e as CustomEvent).detail)) return;\n cleanup();\n resolve(resolveValue);\n };\n const onError = (e: Event) => {\n const detail = (e as CustomEvent).detail as { error?: unknown };\n if (!matchesId((e as CustomEvent).detail)) return;\n cleanup();\n const err = detail.error;\n reject(err instanceof Error ? err : new Error(String(err)));\n };\n const cleanup = () => {\n this.removeEventListener(readyEvent, onReady);\n this.removeEventListener(errorEvent, onError);\n };\n this.addEventListener(readyEvent, onReady);\n this.addEventListener(errorEvent, onError);\n setup();\n });\n }\n /**\n * Append a `<daw-track>` element built from `config` and resolve once the\n * track finishes loading (or reject on `daw-track-error`). Goes through\n * the same `_loadTrack` pipeline as declarative tracks, so descriptors,\n * peaks, and clip buffers are populated correctly.\n */\n addTrack(config: TrackConfig = {}): Promise<DawTrackElement> {\n const trackEl = document.createElement('daw-track') as DawTrackElement;\n if (config.name !== undefined) trackEl.setAttribute('name', config.name);\n if (config.volume !== undefined) trackEl.volume = config.volume;\n if (config.pan !== undefined) trackEl.pan = config.pan;\n if (config.muted) trackEl.setAttribute('muted', '');\n if (config.soloed) trackEl.setAttribute('soloed', '');\n\n // render-mode: explicit > inferred from midi shorthand > default 'waveform'\n const renderMode = config.renderMode ?? (config.midi ? 'piano-roll' : undefined);\n if (renderMode !== undefined) trackEl.setAttribute('render-mode', renderMode);\n\n const clipConfigs: ClipConfig[] = [...(config.clips ?? [])];\n if (config.midi) {\n clipConfigs.push({\n midiNotes: config.midi.notes,\n midiChannel: config.midi.channel,\n midiProgram: config.midi.program,\n });\n }\n for (const clipConfig of clipConfigs) {\n trackEl.appendChild(this._buildClipElement(clipConfig));\n }\n\n return this._awaitId(\n 'daw-track-ready',\n 'daw-track-error',\n (d) => d.trackId === trackEl.trackId,\n trackEl,\n () => this.appendChild(trackEl)\n );\n }\n /**\n * Remove a track by id. Equivalent to `trackElement.remove()` —\n * the editor's MutationObserver handles engine and cache cleanup.\n * No-op if no matching track exists.\n */\n removeTrack(trackId: string): void {\n const trackEl = this._trackElements.get(trackId);\n if (trackEl) {\n trackEl.remove();\n } else if (this._engineTracks.has(trackId)) {\n // File-dropped tracks have no DOM element; clean up engine state directly.\n this._onTrackRemoved(trackId);\n } else {\n console.warn('[dawcore] removeTrack: no track found for id \"' + trackId + '\"');\n }\n }\n /**\n * Update reflected attributes on a track. For DOM-element tracks the changes\n * are written to the `<daw-track>` element (which fires `daw-track-update`);\n * for tracks without a DOM element (file drops) the descriptor and engine\n * state are updated in place.\n */\n updateTrack(trackId: string, partial: Partial<TrackConfig>): void {\n const trackEl = this._trackElements.get(trackId);\n if (trackEl) {\n // Mutating reflected props triggers daw-track-update which propagates\n // to engine via _onTrackUpdate.\n if (partial.name !== undefined) trackEl.setAttribute('name', partial.name);\n if (partial.volume !== undefined) trackEl.volume = partial.volume;\n if (partial.pan !== undefined) trackEl.pan = partial.pan;\n if (partial.muted !== undefined) {\n if (partial.muted) trackEl.setAttribute('muted', '');\n else trackEl.removeAttribute('muted');\n }\n if (partial.soloed !== undefined) {\n if (partial.soloed) trackEl.setAttribute('soloed', '');\n else trackEl.removeAttribute('soloed');\n }\n if (partial.renderMode !== undefined) trackEl.setAttribute('render-mode', partial.renderMode);\n return;\n }\n // No DOM element — apply directly to descriptor + engine.\n const oldDesc = this._tracks.get(trackId);\n if (!oldDesc) return;\n let normalizedRenderMode = partial.renderMode;\n if (normalizedRenderMode === 'both') {\n console.warn(\n '[dawcore] render-mode=\"both\" is not yet supported; falling back to \\'spectrogram\\''\n );\n normalizedRenderMode = 'spectrogram';\n }\n const newDesc: TrackDescriptor = {\n ...oldDesc,\n ...(partial.name !== undefined && { name: partial.name }),\n ...(partial.volume !== undefined && { volume: partial.volume }),\n ...(partial.pan !== undefined && { pan: partial.pan }),\n ...(partial.muted !== undefined && { muted: partial.muted }),\n ...(partial.soloed !== undefined && { soloed: partial.soloed }),\n ...(normalizedRenderMode !== undefined && { renderMode: normalizedRenderMode }),\n };\n this._tracks = new Map(this._tracks).set(trackId, newDesc);\n if (this._engine) {\n if (partial.volume !== undefined) this._engine.setTrackVolume(trackId, partial.volume);\n if (partial.pan !== undefined) this._engine.setTrackPan(trackId, partial.pan);\n if (partial.muted !== undefined) this._engine.setTrackMute(trackId, partial.muted);\n if (partial.soloed !== undefined) this._engine.setTrackSolo(trackId, partial.soloed);\n }\n }\n /**\n * Append a clip to an existing track. Builds a `<daw-clip>` from `config`\n * and appends it to the track's DOM element when one exists; resolves with\n * the new clip's id once the audio decode + peak generation finish.\n */\n addClip(trackId: string, config: ClipConfig): Promise<string> {\n if (!config.src) {\n return Promise.reject(\n new Error(\n 'addClip: config.src is required — pass a URL to load. ' +\n 'Empty/recording clips are not yet supported via addClip.'\n )\n );\n }\n const trackEl = this._trackElements.get(trackId);\n if (!trackEl) {\n return Promise.reject(\n new Error(\n 'addClip: no <daw-track> element for trackId \"' +\n trackId +\n '\" — addClip currently requires a DOM-backed track. Use editor.addTrack(config) first.'\n )\n );\n }\n const clipEl = this._buildClipElement(config);\n return this._awaitId(\n 'daw-clip-ready',\n 'daw-clip-error',\n (d) => d.clipId === clipEl.clipId,\n clipEl.clipId,\n () => trackEl.appendChild(clipEl)\n );\n }\n /**\n * Remove a clip by id. Removes the matching `<daw-clip>` DOM element when\n * present (MutationObserver handles cleanup); otherwise updates engine\n * state directly. No-op if no matching clip exists.\n */\n removeClip(trackId: string, clipId: string): void {\n const trackEl = this._trackElements.get(trackId);\n if (trackEl) {\n const clipEl = [...trackEl.querySelectorAll('daw-clip')].find(\n (c) => (c as DawClipElement).clipId === clipId\n ) as DawClipElement | undefined;\n if (clipEl) {\n clipEl.remove();\n return;\n }\n }\n if (this._engineTracks.has(trackId)) {\n this._removeClipFromTrack(trackId, clipId);\n return;\n }\n console.warn(\n '[dawcore] removeClip: no track found for id \"' + trackId + '\" (clipId \"' + clipId + '\")'\n );\n }\n /**\n * Update a clip's position (start/duration/offset) or properties (gain/name).\n * For DOM-element clips, writes properties on the `<daw-clip>` element which\n * fires `daw-clip-update`; otherwise applies directly via `_applyClipUpdate`.\n *\n * Re-decoding (changing `src`) is not supported via this method — remove and\n * re-add the clip instead.\n *\n * Note: `fadeIn` / `fadeOut` / `fadeType` on the partial are written to the\n * `<daw-clip>` element (so they round-trip in the descriptor), but engine-side\n * fade application from `<daw-clip>` properties is not yet implemented — see\n * the broader fade-engine integration tracked separately.\n */\n updateClip(trackId: string, clipId: string, partial: Partial<ClipConfig>): void {\n const trackEl = this._trackElements.get(trackId);\n if (trackEl) {\n const clipEl = [...trackEl.querySelectorAll('daw-clip')].find(\n (c) => (c as DawClipElement).clipId === clipId\n ) as DawClipElement | undefined;\n if (clipEl) {\n if (partial.start !== undefined) clipEl.start = partial.start;\n if (partial.duration !== undefined) clipEl.duration = partial.duration;\n if (partial.offset !== undefined) clipEl.offset = partial.offset;\n if (partial.gain !== undefined) clipEl.gain = partial.gain;\n if (partial.name !== undefined) clipEl.setAttribute('name', partial.name);\n if (partial.fadeIn !== undefined) clipEl.fadeIn = partial.fadeIn;\n if (partial.fadeOut !== undefined) clipEl.fadeOut = partial.fadeOut;\n if (partial.fadeType !== undefined) clipEl.setAttribute('fade-type', partial.fadeType);\n return;\n }\n }\n // No DOM element — apply changes directly.\n const t = this._engineTracks.get(trackId);\n if (!t) {\n console.warn('[dawcore] updateClip: no track found for id \"' + trackId + '\"');\n return;\n }\n const idx = t.clips.findIndex((c) => c.id === clipId);\n if (idx === -1) {\n console.warn(\n '[dawcore] updateClip: clip \"' + clipId + '\" not found in track \"' + trackId + '\"'\n );\n return;\n }\n const oldClip = t.clips[idx];\n const sr = oldClip.sampleRate ?? this.effectiveSampleRate;\n const updatedClip = {\n ...oldClip,\n ...(partial.start !== undefined && { startSample: Math.round(partial.start * sr) }),\n ...(partial.duration !== undefined &&\n partial.duration > 0 && { durationSamples: Math.round(partial.duration * sr) }),\n ...(partial.offset !== undefined && { offsetSamples: Math.round(partial.offset * sr) }),\n ...(partial.gain !== undefined && { gain: partial.gain }),\n ...(partial.name !== undefined && { name: partial.name }),\n };\n const updatedClips = [...t.clips];\n updatedClips[idx] = updatedClip;\n const updatedTrack: ClipTrack = { ...t, clips: updatedClips };\n this._engineTracks = new Map(this._engineTracks).set(trackId, updatedTrack);\n this._commitTrackChange(trackId, updatedTrack);\n }\n private _buildClipElement(config: ClipConfig): DawClipElement {\n const clipEl = document.createElement('daw-clip') as DawClipElement;\n if (config.src !== undefined) clipEl.setAttribute('src', config.src);\n if (config.peaksSrc !== undefined) clipEl.setAttribute('peaks-src', config.peaksSrc);\n if (config.start !== undefined) clipEl.start = config.start;\n if (config.duration !== undefined) clipEl.duration = config.duration;\n if (config.offset !== undefined) clipEl.offset = config.offset;\n if (config.gain !== undefined) clipEl.gain = config.gain;\n if (config.name !== undefined) clipEl.setAttribute('name', config.name);\n if (config.fadeIn !== undefined) clipEl.fadeIn = config.fadeIn;\n if (config.fadeOut !== undefined) clipEl.fadeOut = config.fadeOut;\n if (config.fadeType !== undefined) clipEl.setAttribute('fade-type', config.fadeType);\n if (config.midiNotes !== undefined) clipEl.midiNotes = config.midiNotes;\n if (config.midiChannel !== undefined)\n clipEl.setAttribute('midi-channel', String(config.midiChannel));\n if (config.midiProgram !== undefined)\n clipEl.setAttribute('midi-program', String(config.midiProgram));\n return clipEl;\n }\n // --- Playback ---\n async play(startTime?: number) {\n try {\n const engine = await this._ensureEngine();\n // Always init — resumes AudioContext if suspended (requires user gesture).\n await engine.init();\n engine.play(startTime);\n this._startPlayhead();\n this.dispatchEvent(new CustomEvent('daw-play', { bubbles: true, composed: true }));\n } catch (err) {\n console.warn('[dawcore] Playback failed: ' + String(err));\n this.dispatchEvent(\n new CustomEvent<DawErrorDetail>('daw-error', {\n bubbles: true,\n composed: true,\n detail: { operation: 'play', error: err },\n })\n );\n }\n }\n pause() {\n if (!this._engine) return;\n this._engine.pause();\n this._stopPlayhead();\n this.dispatchEvent(new CustomEvent('daw-pause', { bubbles: true, composed: true }));\n }\n stop() {\n if (!this._engine) return;\n this._engine.stop();\n this._stopPlayhead();\n this.dispatchEvent(new CustomEvent('daw-stop', { bubbles: true, composed: true }));\n }\n /** Toggle between play and pause. */\n togglePlayPause() {\n if (this.isRecording) {\n this.togglePauseRecording();\n } else if (this._isPlaying) {\n this.pause();\n } else {\n this.play();\n }\n }\n seekTo(time: number) {\n if (!this._engine) {\n console.warn('[dawcore] seekTo: engine not ready, call ignored');\n return;\n }\n if (this._isPlaying) {\n // Transport needs stop+play to reschedule audio sources at new position\n this.stop();\n this.play(time);\n } else {\n this._engine.seek(time);\n this._currentTime = time;\n this._stopPlayhead();\n }\n }\n\n /** Undo the last structural edit. */\n undo(): void {\n if (!this._engine) {\n console.warn('[dawcore] undo: engine not ready, call ignored');\n return;\n }\n this._engine.undo();\n }\n\n /** Redo the last undone edit. */\n redo(): void {\n if (!this._engine) {\n console.warn('[dawcore] redo: engine not ready, call ignored');\n return;\n }\n this._engine.redo();\n }\n\n /** Whether undo is available. */\n get canUndo(): boolean {\n return this._engine?.canUndo ?? false;\n }\n\n /** Whether redo is available. */\n get canRedo(): boolean {\n return this._engine?.canRedo ?? false;\n }\n\n /** Split the clip under the playhead on the selected track. */\n splitAtPlayhead(): boolean {\n return performSplitAtPlayhead({\n effectiveSampleRate: this.effectiveSampleRate,\n currentTime: this.currentTime,\n isPlaying: this._isPlaying,\n engine: this._engine,\n dispatchEvent: (e: Event) => this.dispatchEvent(e),\n stop: () => {\n this._engine?.stop();\n this._stopPlayhead();\n },\n // Call engine.play directly (synchronous) — not the async editor play()\n // which yields to microtask queue via await engine.init(). Engine is\n // already initialized at split time; the async gap causes audio desync.\n play: (time: number) => {\n this._engine?.play(time);\n this._startPlayhead();\n },\n });\n }\n\n // --- Recording ---\n recordingStream: MediaStream | null = null;\n get currentTime(): number {\n // During playback, read live from engine (not cached _currentTime)\n if (this._isPlaying && this._engine) {\n return this._engine.getCurrentTime();\n }\n return this._currentTime;\n }\n get isRecording(): boolean {\n return this._recordingController.isRecording;\n }\n get isRecordingPaused(): boolean {\n return this._recordingController.isPaused;\n }\n pauseRecording(): void {\n this._recordingController.pauseRecording();\n }\n resumeRecording(): void {\n this._recordingController.resumeRecording();\n // Programmatic resume bypasses togglePauseRecording — clear the\n // ref so the next pause cycle doesn't restart Transport based on\n // stale state from an earlier toggle.\n this._wasPlayingDuringRecording = false;\n }\n /**\n * Audacity-style pause toggle for active recordings: pauses both the\n * worklet capture and (if running) the playback Transport. On resume,\n * Transport restarts only if it was running before — non-overdub\n * recordings stay silent on resume.\n */\n togglePauseRecording(): void {\n if (!this.isRecording) return;\n if (this.isRecordingPaused) {\n // Snapshot the flag before resumeRecording — that method clears\n // the ref defensively (so external callers don't leak it into the\n // next cycle), so reading after the call is too late.\n const wasPlaying = this._wasPlayingDuringRecording;\n this.resumeRecording();\n if (wasPlaying) {\n // play() is async on the editor; fire and forget — caller doesn't await.\n void this.play(this.currentTime);\n }\n } else {\n this.pauseRecording();\n if (this._isPlaying) {\n this._wasPlayingDuringRecording = true;\n this.pause();\n }\n }\n }\n /** Set in togglePauseRecording when Transport is paused alongside the\n * worklet, so resume can restart it. Cleared on resume and on stop. */\n private _wasPlayingDuringRecording = false;\n stopRecording(): Promise<void> {\n this._wasPlayingDuringRecording = false;\n return this._recordingController.stopRecording();\n }\n _addRecordedClip(\n trackId: string,\n buf: AudioBuffer,\n startSample: number,\n durSamples: number,\n offsetSamples = 0\n ) {\n addRecordedClip(this, trackId, buf, startSample, durSamples, offsetSamples);\n }\n // --- RecordingHost bridge methods for cross-context worklet support ---\n // These delegate to the adapter's context type (native or standardized-audio-context).\n // The RecordingController calls these when available, falling back to native APIs.\n\n addWorkletModule(url: string): Promise<void> {\n return (\n this._externalAdapter?.addWorkletModule?.(url) ??\n this.audioContext.audioWorklet.addModule(url)\n );\n }\n\n createAudioWorkletNode(name: string, options?: AudioWorkletNodeOptions): AudioWorkletNode {\n return (\n this._externalAdapter?.createAudioWorkletNode?.(name, options) ??\n new AudioWorkletNode(this.audioContext, name, options)\n );\n }\n\n createMediaStreamSource(stream: MediaStream): MediaStreamAudioSourceNode {\n return (\n this._externalAdapter?.createMediaStreamSource?.(stream) ??\n this.audioContext.createMediaStreamSource(stream)\n );\n }\n\n async startRecording(stream?: MediaStream, options?: RecordingOptions): Promise<void> {\n const s = stream ?? this.recordingStream;\n if (!s) {\n console.warn('[dawcore] startRecording: no stream provided and recordingStream is null');\n return;\n }\n await this._recordingController.startRecording(s, options);\n }\n\n private _renderRecordingPreview(trackId: string, chH: number) {\n const rs = this._recordingController.getSession(trackId);\n if (!rs) return '';\n // Skip latency samples in the preview — they'll be sliced on finalization.\n // Position stays at startSample (same as finalized clip).\n const audibleSamples = Math.max(0, rs.totalSamples - rs.latencySamples);\n if (audibleSamples === 0) return '';\n const renderSpp = this._renderSpp;\n const latencyPixels = Math.floor(rs.latencySamples / renderSpp);\n const left = Math.floor(rs.startSample / renderSpp);\n const w = Math.floor(audibleSamples / renderSpp);\n return rs.peaks.map((chPeaks, ch) => {\n // Slice peaks to skip latency prefix (2 entries per pixel: min/max)\n const slicedPeaks = latencyPixels > 0 ? chPeaks.slice(latencyPixels * 2) : chPeaks;\n return html`\n <daw-waveform\n data-recording-track=${trackId}\n data-recording-channel=${ch}\n style=\"position:absolute;left:${left}px;top:${ch * chH}px;\"\n .peaks=${slicedPeaks}\n .length=${w}\n .waveHeight=${chH}\n .barWidth=${this.barWidth}\n .barGap=${this.barGap}\n .visibleStart=${this._viewport.visibleStart}\n .visibleEnd=${this._viewport.visibleEnd}\n .originX=${left}\n ></daw-waveform>\n `;\n });\n }\n // --- Playhead ---\n _startPlayhead() {\n const playhead = this._getPlayhead();\n if (!playhead || !this._engine) return;\n const engine = this._engine;\n // engine.getAudibleTime(): while playing, engine time minus hardware DAC\n // latency (outputLatency) and scheduler lookahead (0.1s on Tone-backed\n // adapters, 0 native), held at the play-start position during the\n // pre-roll window. Without the subtraction the playhead leads audio by\n // ~100ms with the Tone adapter.\n const audibleTime = (): number => engine.getAudibleTime();\n if (this.scaleMode === 'beats') {\n const secondsToTicksFn = (s: number) => this._secondsToTicks(s);\n playhead.startBeatsAnimationWithMap(audibleTime, secondsToTicksFn, this.ticksPerPixel);\n } else {\n playhead.startAnimation(audibleTime, this.effectiveSampleRate, this.samplesPerPixel);\n }\n }\n _stopPlayhead() {\n const playhead = this._getPlayhead();\n if (!playhead) return;\n // Resting playhead displays the raw position — latency compensation is a\n // playback-time concept (Transport scheduling vs audible output). A\n // seeked/stopped/paused cursor shows exactly the commanded position.\n // Storage (`_currentTime`) is already raw, so play() resumes correctly.\n const t = this._currentTime;\n const visualTime = Number.isFinite(t) ? Math.max(0, t) : 0;\n if (this.scaleMode === 'beats') {\n playhead.stopBeatsAnimationWithMap(\n visualTime,\n (s: number) => this._secondsToTicks(s),\n this.ticksPerPixel\n );\n } else {\n playhead.stopAnimation(visualTime, this.effectiveSampleRate, this.samplesPerPixel);\n }\n }\n private _getPlayhead(): DawPlayheadElement | null {\n return this.shadowRoot?.querySelector('daw-playhead') as DawPlayheadElement | null;\n }\n\n /** True when the controls column should be rendered (and its selector is valid). */\n private get _showControls(): boolean {\n return this._getOrderedTracks().length > 0 || this.indefinitePlayback;\n }\n\n /** True when the ruler header band should be rendered (and its selector is valid). */\n private get _showRuler(): boolean {\n return (\n (this._getOrderedTracks().length > 0 ||\n this.scaleMode === 'beats' ||\n this.indefinitePlayback) &&\n this.timescale\n );\n }\n\n private _getOrderedTracks(): Array<[string, ClipTrack]> {\n const domOrder: string[] = [...this.querySelectorAll('daw-track')].map(\n (el) => (el as DawTrackElement).trackId\n );\n return [...this._engineTracks.entries()].sort((a, b) => {\n const ai = domOrder.indexOf(a[0]);\n const bi = domOrder.indexOf(b[0]);\n // Both not in DOM (e.g. file drops): preserve Map insertion order\n if (ai === -1 && bi === -1) return 0;\n // Only one not in DOM: sort it after DOM tracks\n if (ai === -1) return 1;\n if (bi === -1) return -1;\n return ai - bi;\n });\n }\n\n // --- Render ---\n render() {\n const sr = this.effectiveSampleRate;\n const spp = this._renderSpp;\n // In beats mode, derive selection pixels from tick space (same as clip positions)\n // to avoid 1-2px quantization error from the sample round-trip.\n let selStartPx: number;\n let selEndPx: number;\n if (this.scaleMode === 'beats') {\n const startTick = this._secondsToTicks(this._selectionStartTime);\n const endTick = this._secondsToTicks(this._selectionEndTime);\n selStartPx = startTick / this.ticksPerPixel;\n selEndPx = endTick / this.ticksPerPixel;\n } else {\n selStartPx = (this._selectionStartTime * sr) / spp;\n selEndPx = (this._selectionEndTime * sr) / spp;\n }\n\n // Precompute track info once for both controls column and timeline\n const orderedTracks = this._getOrderedTracks().map(([trackId, track]) => {\n const descriptor = this._tracks.get(trackId);\n const firstPeaks = track.clips\n .map((c) => this._peaksData.get(c.id))\n .find((p) => p && p.data.length > 0);\n // Use recording session channel count if no finalized clips yet\n const recSession = this._recordingController.getSession(trackId);\n const numChannels = firstPeaks\n ? firstPeaks.data.length\n : recSession\n ? recSession.channelCount\n : 1;\n return {\n trackId,\n track,\n descriptor,\n numChannels,\n trackHeight: this.waveHeight * numChannels + (this.clipHeaders ? this.clipHeaderHeight : 0),\n };\n });\n\n const showControls = this._showControls;\n const showRuler = this._showRuler;\n\n return html`\n ${showRuler\n ? html`<div class=\"header-row\" style=\"height: ${RULER_HEIGHT}px;\">\n ${showControls ? html`<div class=\"ruler-gap\"></div>` : ''}\n <div class=\"ruler-viewport\" @pointerdown=${this._pointer.onPointerDown}>\n <div\n class=\"ruler-content\"\n style=\"width: ${this._totalWidth > 0 ? this._totalWidth + 'px' : '100%'};\"\n >\n <daw-ruler\n .samplesPerPixel=${spp}\n .sampleRate=${this.effectiveSampleRate}\n .duration=${this._duration}\n .scaleMode=${this.scaleMode}\n .ticksPerPixel=${this.ticksPerPixel}\n .meterEntries=${this._meterEntries}\n .ppqn=${this.ppqn}\n .totalWidth=${this._totalWidth}\n .rulerHeight=${RULER_HEIGHT}\n ></daw-ruler>\n </div>\n </div>\n </div>`\n : ''}\n <div class=\"body\">\n ${showControls\n ? html`<div class=\"controls-viewport\">\n <div class=\"controls-column\">\n ${orderedTracks.map(\n (t) => html`\n <daw-track-controls\n style=\"height: ${t.trackHeight}px;\"\n .trackId=${t.trackId}\n .trackName=${t.descriptor?.name ?? 'Untitled'}\n .volume=${t.descriptor?.volume ?? 1}\n .pan=${t.descriptor?.pan ?? 0}\n .muted=${t.descriptor?.muted ?? false}\n .soloed=${t.descriptor?.soloed ?? false}\n ></daw-track-controls>\n `\n )}\n </div>\n </div>`\n : ''}\n <div class=\"scroll-area\">\n <div\n class=\"timeline ${this._dragOver ? 'drag-over' : ''}\"\n style=\"width: ${this._totalWidth > 0 ? this._totalWidth + 'px' : '100%'};\"\n data-playing=${this._isPlaying}\n @pointerdown=${this._pointer.onPointerDown}\n @dragover=${this._onDragOver}\n @dragleave=${this._onDragLeave}\n @drop=${this._onDrop}\n >\n ${this.scaleMode === 'beats'\n ? html`<daw-grid\n style=\"top: 0px;\"\n .ticksPerPixel=${this.ticksPerPixel}\n .meterEntries=${this._meterEntries}\n .ppqn=${this.ppqn}\n .visibleStart=${this._viewport.visibleStart}\n .visibleEnd=${this._viewport.visibleEnd}\n .length=${this._totalWidth}\n .height=${orderedTracks.length > 0\n ? orderedTracks.reduce((sum, t) => sum + t.trackHeight, 0)\n : this._emptyGridHeight}\n ></daw-grid>`\n : ''}\n ${orderedTracks.length > 0 || this.scaleMode === 'beats' || this.indefinitePlayback\n ? html`<daw-selection .startPx=${selStartPx} .endPx=${selEndPx}></daw-selection>\n <daw-playhead></daw-playhead>`\n : ''}\n ${orderedTracks.map((t) => {\n const channelHeight = this.waveHeight;\n return html`\n <div\n class=\"track-row ${t.trackId === this._selectedTrackId ? 'selected' : ''}\"\n style=\"height: ${t.trackHeight}px;\"\n data-track-id=${t.trackId}\n >\n ${t.track.clips.map((clip) => {\n const peakData = this._peaksData.get(clip.id);\n // In beats mode, derive pixel positions from tick space to\n // match grid lines exactly. The sample→spp path introduces\n // 1-2px quantization error from integer sample rounding.\n let clipLeft: number;\n let width: number;\n if (this.scaleMode === 'beats') {\n // Use startTick directly when available — stable across BPM changes.\n // Fall back to sample→seconds→ticks for clips without startTick.\n const startTick =\n clip.startTick !== undefined\n ? clip.startTick\n : this._secondsToTicks(clip.startSample / sr);\n const durSec = clip.durationSamples / sr;\n const startSec =\n clip.startTick !== undefined\n ? this._ticksToSeconds(clip.startTick)\n : clip.startSample / sr;\n const endTick = this._secondsToTicks(startSec + durSec);\n clipLeft = Math.round(startTick / this.ticksPerPixel);\n width = Math.round(endTick / this.ticksPerPixel) - clipLeft;\n } else {\n clipLeft = Math.floor(clip.startSample / spp);\n width = clipPixelWidth(clip.startSample, clip.durationSamples, spp);\n }\n // Per-segment waveform rendering for variable tempo.\n // Uses base-scale (128) peaks directly — segments handle stretching,\n // so no BPM-dependent intermediate resampling needed.\n let clipSegments: WaveformSegment[] | undefined;\n let segmentChannels: Peaks[] | undefined;\n if (this.scaleMode === 'beats' && this.secondsToTicks) {\n const audioBuffer = this._clipBuffers.get(clip.id);\n const basePeaks = audioBuffer\n ? this._peakPipeline.getBaseScalePeaks(\n audioBuffer,\n this.mono,\n clip.offsetSamples,\n clip.durationSamples\n )\n : null;\n if (basePeaks) {\n const baseScale = basePeaks.scale;\n segmentChannels = basePeaks.peaks.data;\n const MIN_RENDER_STEP = 80;\n const stepTicks = Math.max(MIN_RENDER_STEP, Math.ceil(this.ticksPerPixel));\n const startSec =\n clip.startTick !== undefined\n ? this._ticksToSeconds(clip.startTick)\n : clip.startSample / sr;\n const clipOffsetSec = clip.offsetSamples / sr;\n const segStartTick =\n clip.startTick !== undefined\n ? clip.startTick\n : this._secondsToTicks(startSec);\n const endTick = this._secondsToTicks(startSec + clip.durationSamples / sr);\n clipSegments = [];\n for (let tick = segStartTick; tick < endTick; tick += stepTicks) {\n const segEndTick = Math.min(tick + stepTicks, endTick);\n const segStartAudioSec =\n this._ticksToSeconds(tick) - startSec + clipOffsetSec;\n const segEndAudioSec =\n this._ticksToSeconds(segEndTick) - startSec + clipOffsetSec;\n // Peak indices at base scale (128) — clamped to valid range.\n const segStartSample = Math.round(segStartAudioSec * sr);\n const segEndSample = Math.round(segEndAudioSec * sr);\n const totalPeaks = clip.durationSamples / baseScale;\n clipSegments.push({\n peakStart: Math.max(\n 0,\n (segStartSample - clip.offsetSamples) / baseScale\n ),\n peakEnd: Math.min(\n totalPeaks,\n (segEndSample - clip.offsetSamples) / baseScale\n ),\n pixelStart: (tick - segStartTick) / this.ticksPerPixel,\n pixelEnd: (segEndTick - segStartTick) / this.ticksPerPixel,\n });\n }\n }\n }\n const channels: Peaks[] = segmentChannels ??\n peakData?.data ?? [new Int16Array(0)];\n const hdrH = this.clipHeaders ? this.clipHeaderHeight : 0;\n const chH = this.waveHeight;\n return html` <div\n class=\"clip-container\"\n style=\"left:${clipLeft}px;top:0;width:${width}px;height:${t.trackHeight}px;\"\n data-clip-id=${clip.id}\n >\n ${hdrH > 0\n ? html`<div\n class=\"clip-header\"\n data-clip-id=${clip.id}\n data-track-id=${t.trackId}\n ?data-interactive=${this.interactiveClips}\n >\n <span>${clip.name || t.descriptor?.name || ''}</span>\n </div>`\n : ''}\n ${t.descriptor?.renderMode === 'piano-roll'\n ? html`<daw-piano-roll\n style=\"position:absolute;left:0;top:${hdrH}px;\"\n .midiNotes=${clip.midiNotes ?? []}\n .length=${peakData?.length ?? width}\n .waveHeight=${chH * channels.length}\n .samplesPerPixel=${this._renderSpp}\n .sampleRate=${this.effectiveSampleRate}\n .clipOffsetSeconds=${(clip.offsetSamples ?? 0) /\n this.effectiveSampleRate}\n .visibleStart=${this._viewport.visibleStart}\n .visibleEnd=${this._viewport.visibleEnd}\n .originX=${clipLeft}\n ?selected=${t.trackId === this._selectedTrackId}\n ></daw-piano-roll>`\n : t.descriptor?.renderMode === 'spectrogram'\n ? channels.map(\n (_chPeaks, chIdx) =>\n html`<daw-spectrogram\n style=\"position:absolute;left:0;top:${hdrH +\n chIdx * chH}px;height:${chH}px;width:${peakData?.length ??\n width}px;\"\n .clipId=${clip.id}\n .trackId=${t.trackId}\n .channelIndex=${chIdx}\n .length=${peakData?.length ?? width}\n .waveHeight=${chH}\n .samplesPerPixel=${this._renderSpp}\n .sampleRate=${this.effectiveSampleRate}\n .clipOffsetSeconds=${(clip.offsetSamples ?? 0) /\n this.effectiveSampleRate}\n .visibleStart=${this._viewport.visibleStart}\n .visibleEnd=${this._viewport.visibleEnd}\n .originX=${clipLeft}\n ></daw-spectrogram>`\n )\n : channels.map(\n (chPeaks, chIdx) =>\n html` <daw-waveform\n style=\"position:absolute;left:0;top:${hdrH + chIdx * chH}px;\"\n .peaks=${chPeaks}\n .length=${peakData?.length ?? width}\n .waveHeight=${chH}\n .barWidth=${this.barWidth}\n .barGap=${this.barGap}\n .visibleStart=${this._viewport.visibleStart}\n .visibleEnd=${this._viewport.visibleEnd}\n .originX=${clipLeft}\n .segments=${clipSegments}\n ></daw-waveform>`\n )}\n ${this.interactiveClips\n ? html` <div\n class=\"clip-boundary\"\n data-boundary-edge=\"left\"\n data-clip-id=${clip.id}\n data-track-id=${t.trackId}\n ></div>\n <div\n class=\"clip-boundary\"\n data-boundary-edge=\"right\"\n data-clip-id=${clip.id}\n data-track-id=${t.trackId}\n ></div>`\n : ''}\n </div>`;\n })}\n ${this._renderRecordingPreview(t.trackId, channelHeight)}\n </div>\n `;\n })}\n </div>\n </div>\n </div>\n <slot></slot>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-editor': DawEditorElement;\n }\n}\n","import type {\n FadeType,\n MidiNoteData,\n RenderMode,\n SpectrogramConfig,\n} from '@waveform-playlist/core';\n\n// Re-export RenderMode from core as TrackRenderMode for backward compatibility\n// during the migration; consumers should prefer RenderMode directly.\nexport type { RenderMode } from '@waveform-playlist/core';\nexport type TrackRenderMode = RenderMode;\n\nexport interface TrackDescriptor {\n name: string;\n src: string;\n volume: number;\n pan: number;\n muted: boolean;\n soloed: boolean;\n renderMode: TrackRenderMode;\n spectrogramConfig?: SpectrogramConfig | null;\n clips: ClipDescriptor[];\n}\n\n/**\n * Common fields shared by all clip descriptors regardless of source.\n */\ninterface BaseClipDescriptor {\n src: string;\n peaksSrc: string;\n start: number;\n duration: number;\n offset: number;\n gain: number;\n name: string;\n fadeIn: number;\n fadeOut: number;\n fadeType: FadeType;\n midiNotes: MidiNoteData[] | null;\n midiChannel: number | null;\n midiProgram: number | null;\n}\n\n/**\n * A clip descriptor sourced from a `<daw-clip>` DOM element. `clipId` is\n * always set — `<daw-clip>.clipId` is a `crypto.randomUUID()` generated at\n * construction. Engine `clip.id` is aligned with this id in `_loadTrack`\n * and `_loadAndAppendClip` so DOM and engine reference the same clip.\n */\nexport interface DomClipDescriptor extends BaseClipDescriptor {\n kind: 'dom';\n clipId: string;\n}\n\n/**\n * A clip descriptor synthesized from a non-DOM source — file drops, the\n * `<daw-track src>` shorthand fallback, or recording-clip insertion. No\n * `clipId` because there's no DOM element to align with; the engine\n * generates its own id at clip-creation time.\n */\nexport interface DropClipDescriptor extends BaseClipDescriptor {\n kind: 'drop';\n}\n\nexport type ClipDescriptor = DomClipDescriptor | DropClipDescriptor;\n\n/**\n * Type predicate for the `'dom'` discriminator. Use to narrow a\n * `ClipDescriptor` to `DomClipDescriptor` (which has `clipId`) without\n * inline `c.kind === 'dom'` repetition.\n */\nexport function isDomClip(desc: ClipDescriptor): desc is DomClipDescriptor {\n return desc.kind === 'dom';\n}\n\n/**\n * Public input shape for `editor.addTrack(config)`. All fields optional —\n * defaults match the declarative `<daw-track>` defaults.\n */\nexport interface TrackConfig {\n name?: string;\n volume?: number;\n pan?: number;\n muted?: boolean;\n soloed?: boolean;\n renderMode?: TrackRenderMode;\n spectrogramConfig?: SpectrogramConfig | null;\n clips?: ClipConfig[];\n /**\n * Convenience: creates a single piano-roll `<daw-clip>` child with these\n * notes and sets `render-mode=\"piano-roll\"` on the track. Equivalent to\n * passing `{ renderMode: 'piano-roll', clips: [{ midiNotes, midiChannel, midiProgram }] }`.\n * An explicit `renderMode` takes precedence over the inferred `'piano-roll'`.\n *\n * Creation-only — ignored by `updateTrack`. To modify notes after the\n * track is built, use `editor.updateClip(trackId, clipId, { midiNotes })` or\n * mutate the `<daw-clip>` element's `midiNotes` property directly.\n */\n midi?: {\n notes: MidiNoteData[];\n channel?: number;\n program?: number;\n };\n}\n\n/**\n * Public input shape for clips passed via `TrackConfig.clips` or\n * `editor.addClip(trackId, config)`. `src` is optional to support MIDI clips\n * with no audio source. Other fields default to the matching `<daw-clip>`\n * attribute defaults.\n */\nexport interface ClipConfig {\n src?: string;\n peaksSrc?: string;\n start?: number;\n duration?: number;\n offset?: number;\n gain?: number;\n name?: string;\n fadeIn?: number;\n fadeOut?: number;\n fadeType?: FadeType;\n midiNotes?: MidiNoteData[];\n midiChannel?: number;\n midiProgram?: number;\n}\n","/**\n * Inline Web Worker for generating WaveformData binary format from AudioBuffer channels.\n *\n * Uses a Blob URL approach (portable across all bundlers) with the generateWaveformData\n * algorithm adapted from BBC's waveform-data.js (MIT licensed, adapted from Audacity).\n *\n * The worker generates peaks at a base scale (finest zoom level). The main thread\n * then uses WaveformData.resample() for near-instant zoom changes.\n */\n\nimport WaveformData from 'waveform-data';\n\n// ────────────────────────────────────────────────────────────────────────────\n// Worker code (runs inside the Blob URL worker)\n// ────────────────────────────────────────────────────────────────────────────\n\n/**\n * Self-contained worker source.\n * Contains the generateWaveformData function from waveform-data.js/src/waveform-generator.js\n * (MIT License, BBC). Pure JS, zero dependencies.\n */\nconst workerSource = `\n\"use strict\";\n\nvar INT8_MAX = 127;\nvar INT8_MIN = -128;\nvar INT16_MAX = 32767;\nvar INT16_MIN = -32768;\n\nfunction calculateWaveformDataLength(audio_sample_count, scale) {\n var data_length = Math.floor(audio_sample_count / scale);\n var samples_remaining = audio_sample_count - (data_length * scale);\n if (samples_remaining > 0) {\n data_length++;\n }\n return data_length;\n}\n\nfunction generateWaveformData(options) {\n var scale = options.scale;\n var amplitude_scale = options.amplitude_scale;\n var split_channels = options.split_channels;\n var length = options.length;\n var sample_rate = options.sample_rate;\n var channels = options.channels.map(function(channel) {\n return new Float32Array(channel);\n });\n var output_channels = split_channels ? channels.length : 1;\n var header_size = 24;\n var data_length = calculateWaveformDataLength(length, scale);\n var bytes_per_sample = options.bits === 8 ? 1 : 2;\n var total_size = header_size + data_length * 2 * bytes_per_sample * output_channels;\n var buffer = new ArrayBuffer(total_size);\n var data_view = new DataView(buffer);\n\n var scale_counter = 0;\n var offset = header_size;\n\n var min_value = new Array(output_channels);\n var max_value = new Array(output_channels);\n\n for (var channel = 0; channel < output_channels; channel++) {\n min_value[channel] = Infinity;\n max_value[channel] = -Infinity;\n }\n\n var range_min = options.bits === 8 ? INT8_MIN : INT16_MIN;\n var range_max = options.bits === 8 ? INT8_MAX : INT16_MAX;\n\n data_view.setInt32(0, 2, true);\n data_view.setUint32(4, options.bits === 8, true);\n data_view.setInt32(8, sample_rate, true);\n data_view.setInt32(12, scale, true);\n data_view.setInt32(16, data_length, true);\n data_view.setInt32(20, output_channels, true);\n\n for (var i = 0; i < length; i++) {\n var sample = 0;\n\n if (output_channels === 1) {\n for (var ch = 0; ch < channels.length; ++ch) {\n sample += channels[ch][i];\n }\n sample = Math.floor(range_max * sample * amplitude_scale / channels.length);\n\n if (sample < min_value[0]) {\n min_value[0] = sample;\n if (min_value[0] < range_min) {\n min_value[0] = range_min;\n }\n }\n if (sample > max_value[0]) {\n max_value[0] = sample;\n if (max_value[0] > range_max) {\n max_value[0] = range_max;\n }\n }\n }\n else {\n for (var ch2 = 0; ch2 < output_channels; ++ch2) {\n sample = Math.floor(range_max * channels[ch2][i] * amplitude_scale);\n\n if (sample < min_value[ch2]) {\n min_value[ch2] = sample;\n if (min_value[ch2] < range_min) {\n min_value[ch2] = range_min;\n }\n }\n if (sample > max_value[ch2]) {\n max_value[ch2] = sample;\n if (max_value[ch2] > range_max) {\n max_value[ch2] = range_max;\n }\n }\n }\n }\n\n if (++scale_counter === scale) {\n for (var ch3 = 0; ch3 < output_channels; ch3++) {\n if (options.bits === 8) {\n data_view.setInt8(offset++, min_value[ch3]);\n data_view.setInt8(offset++, max_value[ch3]);\n }\n else {\n data_view.setInt16(offset, min_value[ch3], true);\n data_view.setInt16(offset + 2, max_value[ch3], true);\n offset += 4;\n }\n min_value[ch3] = Infinity;\n max_value[ch3] = -Infinity;\n }\n scale_counter = 0;\n }\n }\n\n if (scale_counter > 0) {\n for (var ch4 = 0; ch4 < output_channels; ch4++) {\n if (options.bits === 8) {\n data_view.setInt8(offset++, min_value[ch4]);\n data_view.setInt8(offset++, max_value[ch4]);\n }\n else {\n data_view.setInt16(offset, min_value[ch4], true);\n data_view.setInt16(offset + 2, max_value[ch4], true);\n offset += 4;\n }\n }\n }\n\n return buffer;\n}\n\nself.onmessage = function(e) {\n var msg = e.data;\n try {\n var result = generateWaveformData({\n scale: msg.scale,\n bits: msg.bits,\n amplitude_scale: msg.amplitude_scale,\n split_channels: msg.split_channels,\n length: msg.length,\n sample_rate: msg.sample_rate,\n channels: msg.channels\n });\n self.postMessage({ id: msg.id, buffer: result }, [result]);\n } catch (err) {\n self.postMessage({ id: msg.id, error: err.message || String(err) });\n }\n};\n`;\n\n// ────────────────────────────────────────────────────────────────────────────\n// Promise-based worker API (runs on the main thread)\n// ────────────────────────────────────────────────────────────────────────────\n\nexport interface PeaksWorkerApi {\n generate(params: {\n channels: ArrayBuffer[];\n length: number;\n sampleRate: number;\n scale: number;\n bits: 8 | 16;\n splitChannels: boolean;\n }): Promise<WaveformData>;\n terminate(): void;\n}\n\ninterface PendingEntry {\n resolve: (value: WaveformData) => void;\n reject: (reason: unknown) => void;\n}\n\nexport function createPeaksWorker(): PeaksWorkerApi {\n let worker: Worker;\n try {\n const blob = new Blob([workerSource], { type: 'application/javascript' });\n const url = URL.createObjectURL(blob);\n worker = new Worker(url);\n URL.revokeObjectURL(url);\n } catch (err) {\n // Worker creation can fail in CSP-restricted environments that block blob: URLs.\n console.warn('[dawcore] Failed to create peaks worker (CSP restriction?): ' + String(err));\n return {\n generate() {\n return Promise.reject(\n new Error(\n 'Peaks worker unavailable (CSP may block blob: URLs). Add blob: to worker-src directive.'\n )\n );\n },\n terminate() {\n /* no-op */\n },\n };\n }\n\n const pending = new Map<string, PendingEntry>();\n let terminated = false;\n let idCounter = 0;\n\n worker.onmessage = (e: MessageEvent) => {\n const msg = e.data;\n const entry = pending.get(msg.id);\n if (!entry) {\n console.warn('[dawcore] Received worker message for unknown id: ' + String(msg.id));\n return;\n }\n pending.delete(msg.id);\n\n if (msg.error) {\n entry.reject(new Error(msg.error));\n } else {\n try {\n const waveformData = WaveformData.create(msg.buffer);\n entry.resolve(waveformData);\n } catch (err) {\n entry.reject(err);\n }\n }\n };\n\n worker.onerror = (e: ErrorEvent) => {\n const reason = e.error ?? new Error(e.message);\n console.warn('[dawcore] Peaks worker crashed: ' + String(reason));\n terminated = true;\n worker.terminate();\n for (const [, entry] of pending) {\n entry.reject(reason);\n }\n pending.clear();\n };\n\n return {\n generate(params) {\n if (terminated) return Promise.reject(new Error('Worker terminated'));\n const messageId = String(++idCounter);\n\n return new Promise<WaveformData>((resolve, reject) => {\n pending.set(messageId, { resolve, reject });\n\n worker.postMessage(\n {\n id: messageId,\n scale: params.scale,\n bits: params.bits,\n amplitude_scale: 1.0,\n split_channels: params.splitChannels,\n length: params.length,\n sample_rate: params.sampleRate,\n channels: params.channels,\n },\n params.channels // Transfer ownership\n );\n });\n },\n\n terminate() {\n terminated = true;\n worker.terminate();\n for (const [, entry] of pending) {\n entry.reject(new Error('Worker terminated'));\n }\n pending.clear();\n },\n };\n}\n","/**\n * Utilities for converting WaveformData to PeakData format.\n * Adapted from @waveform-playlist/browser waveformDataLoader.ts.\n */\n\nimport WaveformData from 'waveform-data';\nimport type { PeakData, Peaks } from '@waveform-playlist/core';\n\n/**\n * Slice and resample WaveformData with aligned source indices.\n */\nfunction sliceAndResample(\n waveformData: WaveformData,\n samplesPerPixel: number,\n offsetSamples?: number,\n durationSamples?: number\n): WaveformData | null {\n let processedData = waveformData;\n\n if (offsetSamples !== undefined && durationSamples !== undefined) {\n if (processedData.scale !== samplesPerPixel) {\n const sourceScale = waveformData.scale;\n const ratio = samplesPerPixel / sourceScale;\n\n const targetStart = Math.floor(offsetSamples / samplesPerPixel);\n const targetEnd = Math.ceil((offsetSamples + durationSamples) / samplesPerPixel);\n\n const sourceStart = Math.max(0, Math.floor(targetStart * ratio));\n const sourceEnd = Math.min(waveformData.length, Math.ceil(targetEnd * ratio));\n\n if (sourceStart >= sourceEnd) {\n return null;\n }\n\n processedData = processedData.slice({\n startIndex: sourceStart,\n endIndex: sourceEnd,\n });\n processedData = processedData.resample({ scale: samplesPerPixel });\n } else {\n const startIndex = Math.floor(offsetSamples / samplesPerPixel);\n const endIndex = Math.ceil((offsetSamples + durationSamples) / samplesPerPixel);\n processedData = processedData.slice({ startIndex, endIndex });\n }\n } else if (processedData.scale !== samplesPerPixel) {\n processedData = processedData.resample({ scale: samplesPerPixel });\n }\n\n return processedData;\n}\n\n/**\n * Extract peaks from a WaveformData object, handling all channels, mono merging,\n * slicing, and resampling.\n */\nexport function extractPeaks(\n waveformData: WaveformData,\n samplesPerPixel: number,\n isMono: boolean,\n offsetSamples?: number,\n durationSamples?: number\n): PeakData {\n const processedData = sliceAndResample(\n waveformData,\n samplesPerPixel,\n offsetSamples,\n durationSamples\n );\n\n if (processedData === null) {\n const bits = waveformData.bits as 8 | 16;\n const numChannels = isMono ? 1 : waveformData.channels;\n const emptyData: Peaks[] = Array.from({ length: numChannels }, () =>\n bits === 8 ? new Int8Array(0) : new Int16Array(0)\n );\n return { length: 0, data: emptyData, bits };\n }\n\n const numChannels = processedData.channels;\n const bits = processedData.bits as 8 | 16;\n\n const channelPeaks: Peaks[] = [];\n for (let c = 0; c < numChannels; c++) {\n const channel = processedData.channel(c);\n const minArray = channel.min_array();\n const maxArray = channel.max_array();\n const len = minArray.length;\n\n const peaks: Peaks = bits === 8 ? new Int8Array(len * 2) : new Int16Array(len * 2);\n\n for (let i = 0; i < len; i++) {\n peaks[i * 2] = minArray[i];\n peaks[i * 2 + 1] = maxArray[i];\n }\n channelPeaks.push(peaks);\n }\n\n if (isMono && channelPeaks.length > 1) {\n const weight = 1 / channelPeaks.length;\n const numPeaks = channelPeaks[0].length / 2;\n const monoPeaks: Peaks =\n bits === 8 ? new Int8Array(numPeaks * 2) : new Int16Array(numPeaks * 2);\n\n for (let i = 0; i < numPeaks; i++) {\n let min = 0;\n let max = 0;\n for (let c = 0; c < channelPeaks.length; c++) {\n min += weight * channelPeaks[c][i * 2];\n max += weight * channelPeaks[c][i * 2 + 1];\n }\n monoPeaks[i * 2] = min;\n monoPeaks[i * 2 + 1] = max;\n }\n\n return { length: numPeaks, data: [monoPeaks], bits };\n }\n\n const peakLength = channelPeaks.length > 0 ? channelPeaks[0].length / 2 : 0;\n return { length: peakLength, data: channelPeaks, bits };\n}\n","/**\n * Peak generation pipeline: AudioBuffer → web worker → WaveformData → PeakData.\n *\n * Manages worker lifecycle, WaveformData caching per AudioBuffer (WeakMap),\n * inflight dedup, and peak extraction via resample() for any zoom level\n * coarser than the base scale.\n *\n * The base scale determines the finest zoom level that can be rendered without\n * regenerating. Resampling only works to coarser (larger) scales. Set baseScale\n * to the finest zoom level the user might need.\n */\n\nimport type WaveformData from 'waveform-data';\nimport type { PeakData } from '@waveform-playlist/core';\nimport { createPeaksWorker, type PeaksWorkerApi } from './peaksWorker';\nimport { extractPeaks } from './waveformDataUtils';\n\nexport class PeakPipeline {\n private _worker: PeaksWorkerApi | null = null;\n private _cache = new WeakMap<AudioBuffer, WaveformData>();\n private _inflight = new WeakMap<AudioBuffer, Promise<WaveformData>>();\n private _baseScale: number;\n private _bits: 8 | 16;\n\n constructor(baseScale = 128, bits: 8 | 16 = 16) {\n this._baseScale = baseScale;\n this._bits = bits;\n }\n\n /**\n * Inject externally-loaded WaveformData (e.g., from a .dat file) into the cache.\n * Prevents worker generation for this AudioBuffer on all subsequent calls.\n */\n cacheWaveformData(audioBuffer: AudioBuffer, waveformData: WaveformData): void {\n this._cache.set(audioBuffer, waveformData);\n }\n\n /**\n * Generate PeakData for a clip from its AudioBuffer.\n * Uses cached WaveformData when available; otherwise generates via worker.\n * Worker generates at baseScale (default 128); extractPeaks resamples to the requested zoom.\n */\n async generatePeaks(\n audioBuffer: AudioBuffer,\n samplesPerPixel: number,\n isMono: boolean,\n offsetSamples?: number,\n durationSamples?: number\n ): Promise<PeakData> {\n const waveformData = await this._getWaveformData(audioBuffer);\n const effectiveScale = this._clampScale(waveformData, samplesPerPixel);\n try {\n return extractPeaks(waveformData, effectiveScale, isMono, offsetSamples, durationSamples);\n } catch (err) {\n console.warn('[dawcore] extractPeaks failed: ' + String(err));\n throw err;\n }\n }\n\n /**\n * Re-extract peaks for all clips at a new zoom level using cached WaveformData.\n * Returns a new Map of clipId → PeakData. Clips without cached data are skipped.\n * When the requested scale is finer than cached data, peaks are clamped to the\n * cached scale and a single summary warning is logged.\n */\n reextractPeaks(\n clipBuffers: ReadonlyMap<string, AudioBuffer>,\n samplesPerPixel: number,\n isMono: boolean,\n clipOffsets?: ReadonlyMap<string, { offsetSamples: number; durationSamples: number }>\n ): Map<string, PeakData> {\n const result = new Map<string, PeakData>();\n let clampedCount = 0;\n let clampedScale = 0;\n for (const [clipId, audioBuffer] of clipBuffers) {\n const cached = this._cache.get(audioBuffer);\n if (cached) {\n const effectiveScale = this._clampScale(cached, samplesPerPixel, false);\n if (effectiveScale !== samplesPerPixel) {\n clampedCount++;\n clampedScale = effectiveScale;\n }\n try {\n const offsets = clipOffsets?.get(clipId);\n result.set(\n clipId,\n extractPeaks(\n cached,\n effectiveScale,\n isMono,\n offsets?.offsetSamples,\n offsets?.durationSamples\n )\n );\n } catch (err) {\n console.warn('[dawcore] reextractPeaks failed for clip ' + clipId + ': ' + String(err));\n }\n }\n }\n if (clampedCount > 0) {\n console.warn(\n '[dawcore] Requested zoom ' +\n samplesPerPixel +\n ' spp is finer than pre-computed peaks (' +\n clampedScale +\n ' spp) — ' +\n clampedCount +\n ' clip(s) using available resolution'\n );\n }\n return result;\n }\n\n /**\n * Clamp requested scale to cached WaveformData scale.\n * WaveformData.resample() can only go coarser — if the requested zoom is\n * finer than the cached data, use the cached scale. Set warn=true to log\n * (default); reextractPeaks passes false and logs a single summary instead.\n */\n private _clampScale(waveformData: WaveformData, requestedScale: number, warn = true): number {\n if (requestedScale < waveformData.scale) {\n if (warn) {\n console.warn(\n '[dawcore] Requested zoom ' +\n requestedScale +\n ' spp is finer than pre-computed peaks (' +\n waveformData.scale +\n ' spp) — using available resolution'\n );\n }\n return waveformData.scale;\n }\n return requestedScale;\n }\n\n /**\n * Extract peaks at the base scale from cached WaveformData.\n * Returns null if no cached data exists for this buffer.\n * Used by variable-tempo segments which handle stretching themselves.\n */\n getBaseScalePeaks(\n audioBuffer: AudioBuffer,\n isMono: boolean,\n offsetSamples?: number,\n durationSamples?: number\n ): { peaks: PeakData; scale: number } | null {\n const cached = this._cache.get(audioBuffer);\n if (!cached) return null;\n return {\n peaks: extractPeaks(cached, cached.scale, isMono, offsetSamples, durationSamples),\n scale: cached.scale,\n };\n }\n\n /**\n * Return the coarsest (largest) scale among cached WaveformData entries\n * that correspond to the given clip buffers. Returns 0 if none are cached.\n */\n getMaxCachedScale(clipBuffers: ReadonlyMap<string, AudioBuffer>): number {\n let max = 0;\n for (const audioBuffer of clipBuffers.values()) {\n const cached = this._cache.get(audioBuffer);\n if (cached && cached.scale > max) max = cached.scale;\n }\n return max;\n }\n\n terminate() {\n this._worker?.terminate();\n this._worker = null;\n }\n\n private async _getWaveformData(audioBuffer: AudioBuffer): Promise<WaveformData> {\n const cached = this._cache.get(audioBuffer);\n if (cached) return cached;\n\n const inflight = this._inflight.get(audioBuffer);\n if (inflight) return inflight;\n\n if (!this._worker) {\n this._worker = createPeaksWorker();\n }\n\n // Generate at baseScale — the finest zoom we can resample from\n // .slice() channel buffers to avoid detaching the original AudioBuffer views\n const channels: ArrayBuffer[] = [];\n for (let c = 0; c < audioBuffer.numberOfChannels; c++) {\n channels.push(audioBuffer.getChannelData(c).slice().buffer as ArrayBuffer);\n }\n\n const promise = this._worker\n .generate({\n channels,\n length: audioBuffer.length,\n sampleRate: audioBuffer.sampleRate,\n scale: this._baseScale,\n bits: this._bits,\n splitChannels: true,\n })\n .then((waveformData) => {\n this._cache.set(audioBuffer, waveformData);\n this._inflight.delete(audioBuffer);\n return waveformData;\n })\n .catch((err) => {\n this._inflight.delete(audioBuffer);\n console.warn('[dawcore] Peak generation via worker failed: ' + String(err));\n throw err;\n });\n\n this._inflight.set(audioBuffer, promise);\n return promise;\n }\n}\n","import { LitElement, html, css } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { computeTemporalTicks, type TickData } from '../utils/smart-scale';\nimport { getCachedMusicalTicks } from '../utils/musical-tick-cache';\nimport type { MusicalTickData, MeterEntry } from '@waveform-playlist/core';\n\nconst MAX_CANVAS_WIDTH = 1000;\n\n@customElement('daw-ruler')\nexport class DawRulerElement extends LitElement {\n @property({ type: Number, attribute: false }) samplesPerPixel = 1024;\n @property({ type: Number, attribute: false }) sampleRate = 48000;\n @property({ type: Number, attribute: false }) duration = 0;\n @property({ type: Number, attribute: false }) rulerHeight = 30;\n @property({ type: String, attribute: false }) scaleMode: 'temporal' | 'beats' = 'temporal';\n @property({ type: Number, attribute: false }) ticksPerPixel = 4;\n @property({ attribute: false }) meterEntries: MeterEntry[] = [\n { tick: 0, numerator: 4, denominator: 4 },\n ];\n @property({ type: Number, attribute: false }) ppqn = 960;\n @property({ type: Number, attribute: false }) totalWidth = 0;\n\n private _tickData: TickData | null = null;\n private _musicalTickData: MusicalTickData | null = null;\n\n static styles = css`\n :host {\n display: block;\n position: relative;\n background: var(--daw-ruler-background, #0f0f1a);\n }\n .container {\n position: relative;\n }\n canvas {\n position: absolute;\n top: 0;\n }\n .label {\n position: absolute;\n font-size: 0.7rem;\n line-height: 1;\n white-space: nowrap;\n color: var(--daw-ruler-color, #c49a6c);\n top: 1px;\n }\n .label.centered {\n transform: translateX(-50%);\n }\n `;\n\n willUpdate() {\n if (this.scaleMode === 'beats' && this.totalWidth > 0) {\n this._musicalTickData = getCachedMusicalTicks({\n meterEntries: this.meterEntries,\n ticksPerPixel: this.ticksPerPixel,\n startPixel: 0,\n endPixel: this.totalWidth,\n ppqn: this.ppqn,\n });\n this._tickData = null;\n } else if (this.duration > 0 || this.totalWidth > 0) {\n // Cover the larger of natural duration and viewport-floored width.\n // When indefinite-playback is set, totalWidth can exceed the natural\n // duration's width — use that to keep ruler ticks across the full\n // visible area instead of only the audible portion.\n const widthDerivedDuration = (this.totalWidth * this.samplesPerPixel) / this.sampleRate;\n const effectiveDuration = Math.max(this.duration, widthDerivedDuration);\n this._musicalTickData = null;\n this._tickData = computeTemporalTicks(\n this.samplesPerPixel,\n this.sampleRate,\n effectiveDuration,\n this.rulerHeight\n );\n } else {\n this._musicalTickData = null;\n this._tickData = null;\n }\n }\n\n render() {\n const widthX = this.scaleMode === 'beats' ? this.totalWidth : (this._tickData?.widthX ?? 0);\n if (widthX <= 0) return html``;\n\n const totalChunks = Math.ceil(widthX / MAX_CANVAS_WIDTH);\n const indices = Array.from({ length: totalChunks }, (_, i) => i);\n const dpr = typeof devicePixelRatio !== 'undefined' ? devicePixelRatio : 1;\n\n const beatsLabels =\n this.scaleMode === 'beats' ? (this._musicalTickData?.ticks.filter((t) => t.label) ?? []) : [];\n const temporalLabels = this.scaleMode !== 'beats' ? (this._tickData?.labels ?? []) : [];\n\n return html`\n <div class=\"container\" style=\"width: ${widthX}px; height: ${this.rulerHeight}px;\">\n ${indices.map((i) => {\n const width = Math.min(MAX_CANVAS_WIDTH, widthX - i * MAX_CANVAS_WIDTH);\n return html`\n <canvas\n data-index=${i}\n width=${width * dpr}\n height=${this.rulerHeight * dpr}\n style=\"left: ${i * MAX_CANVAS_WIDTH}px; width: ${width}px; height: ${this\n .rulerHeight}px;\"\n ></canvas>\n `;\n })}\n ${this.scaleMode === 'beats'\n ? beatsLabels.map(\n (t) =>\n html`<span\n class=\"label ${t.pixel > 0 ? 'centered' : ''}\"\n style=\"left: ${t.pixel > 0 ? t.pixel : t.pixel + 4}px;\"\n >${t.label}</span\n >`\n )\n : temporalLabels.map(\n ({ pix, text }) =>\n html`<span class=\"label\" style=\"left: ${pix + 4}px;\">${text}</span>`\n )}\n </div>\n `;\n }\n\n updated() {\n this._drawTicks();\n }\n\n private _drawTicks() {\n const canvases = this.shadowRoot?.querySelectorAll('canvas');\n if (!canvases) return;\n\n const dpr = typeof devicePixelRatio !== 'undefined' ? devicePixelRatio : 1;\n const rulerColor =\n getComputedStyle(this).getPropertyValue('--daw-ruler-color').trim() || '#c49a6c';\n const widthX = this.scaleMode === 'beats' ? this.totalWidth : (this._tickData?.widthX ?? 0);\n\n for (const canvas of canvases) {\n const idx = Number(canvas.dataset.index);\n const ctx = canvas.getContext('2d');\n if (!ctx) continue;\n\n const canvasWidth = Math.min(MAX_CANVAS_WIDTH, widthX - idx * MAX_CANVAS_WIDTH);\n const globalOffset = idx * MAX_CANVAS_WIDTH;\n\n ctx.resetTransform();\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.scale(dpr, dpr);\n ctx.strokeStyle = rulerColor;\n ctx.lineWidth = 1;\n\n if (this.scaleMode === 'beats' && this._musicalTickData) {\n // Ticks draw upward from the bottom. Leave top ~40% for labels.\n // major=60%, minor=35%, minorMinor=15%\n const h = this.rulerHeight;\n for (const tick of this._musicalTickData.ticks) {\n const localX = tick.pixel - globalOffset;\n if (localX < 0 || localX >= canvasWidth) continue;\n\n const tickH =\n tick.type === 'major' ? h * 0.6 : tick.type === 'minor' ? h * 0.35 : h * 0.15;\n\n ctx.globalAlpha = tick.type === 'major' ? 1.0 : 0.5;\n ctx.beginPath();\n ctx.moveTo(localX + 0.5, h);\n ctx.lineTo(localX + 0.5, h - tickH);\n ctx.stroke();\n }\n ctx.globalAlpha = 1.0;\n } else if (this._tickData) {\n for (const [pix, height] of this._tickData.canvasInfo) {\n const localX = pix - globalOffset;\n if (localX < 0 || localX >= canvasWidth) continue;\n\n ctx.beginPath();\n ctx.moveTo(localX + 0.5, this.rulerHeight);\n ctx.lineTo(localX + 0.5, this.rulerHeight - height);\n ctx.stroke();\n }\n }\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-ruler': DawRulerElement;\n }\n}\n","/**\n * Format milliseconds into a m:ss display string.\n */\nexport function formatTime(milliseconds: number): string {\n const seconds = Math.floor(milliseconds / 1000);\n const s = seconds % 60;\n const m = (seconds - s) / 60;\n\n return `${m}:${String(s).padStart(2, '0')}`;\n}\n","import { formatTime } from './time-format';\n\nconst timeinfo = new Map([\n [700, { marker: 1000, bigStep: 500, smallStep: 100 }],\n [1500, { marker: 2000, bigStep: 1000, smallStep: 200 }],\n [2500, { marker: 2000, bigStep: 1000, smallStep: 500 }],\n [5000, { marker: 5000, bigStep: 1000, smallStep: 500 }],\n [10000, { marker: 10000, bigStep: 5000, smallStep: 1000 }],\n [12000, { marker: 15000, bigStep: 5000, smallStep: 1000 }],\n [Infinity, { marker: 30000, bigStep: 10000, smallStep: 5000 }],\n]);\n\nexport function getScaleInfo(samplesPerPixel: number) {\n for (const [resolution, config] of timeinfo) {\n if (samplesPerPixel < resolution) {\n return config;\n }\n }\n return { marker: 30000, bigStep: 10000, smallStep: 5000 };\n}\n\nexport interface TickData {\n /** Map of pixel position → tick height */\n canvasInfo: Map<number, number>;\n /** Labeled ticks with pixel positions */\n labels: Array<{ pix: number; text: string }>;\n /** Total width in pixels */\n widthX: number;\n}\n\n/**\n * Compute temporal tick data for a ruler.\n *\n * Pure function — no DOM dependencies.\n */\nexport function computeTemporalTicks(\n samplesPerPixel: number,\n sampleRate: number,\n duration: number,\n rulerHeight: number\n): TickData {\n const widthX = Math.ceil((duration * sampleRate) / samplesPerPixel);\n const config = getScaleInfo(samplesPerPixel);\n const { marker, bigStep, smallStep } = config;\n const canvasInfo = new Map<number, number>();\n const labels: Array<{ pix: number; text: string }> = [];\n const pixPerSec = sampleRate / samplesPerPixel;\n\n // Iterate with integer counter (milliseconds) to avoid float precision drift.\n // Compute pixel position from counter each iteration instead of accumulating.\n for (let counter = 0; ; counter += smallStep) {\n const pix = Math.floor((counter / 1000) * pixPerSec);\n if (pix >= widthX) break;\n\n if (counter % marker === 0) {\n canvasInfo.set(pix, rulerHeight);\n labels.push({ pix, text: formatTime(counter) });\n } else if (counter % bigStep === 0) {\n canvasInfo.set(pix, Math.floor(rulerHeight / 2));\n } else if (counter % smallStep === 0) {\n canvasInfo.set(pix, Math.floor(rulerHeight / 5));\n }\n }\n\n return { widthX, canvasInfo, labels };\n}\n","import { computeMusicalTicks } from '@waveform-playlist/core';\nimport type { MusicalTickParams, MusicalTickData, MeterEntry } from '@waveform-playlist/core';\n\nlet cachedParams: MusicalTickParams | null = null;\nlet cachedResult: MusicalTickData | null = null;\n\nfunction meterEntriesMatch(\n a: { tick: number; numerator: number; denominator: number }[],\n b: { tick: number; numerator: number; denominator: number }[]\n): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (\n a[i].tick !== b[i].tick ||\n a[i].numerator !== b[i].numerator ||\n a[i].denominator !== b[i].denominator\n )\n return false;\n }\n return true;\n}\n\nfunction paramsMatch(a: MusicalTickParams, b: MusicalTickParams): boolean {\n return (\n a.ticksPerPixel === b.ticksPerPixel &&\n a.startPixel === b.startPixel &&\n a.endPixel === b.endPixel &&\n meterEntriesMatch(a.meterEntries, b.meterEntries) &&\n (a.ppqn ?? 960) === (b.ppqn ?? 960)\n );\n}\n\nexport function getCachedMusicalTicks(params: MusicalTickParams): MusicalTickData {\n if (cachedParams && cachedResult && paramsMatch(cachedParams, params)) {\n return cachedResult;\n }\n cachedResult = computeMusicalTicks(params);\n cachedParams = {\n ...params,\n meterEntries: params.meterEntries.map((e: MeterEntry) => ({ ...e })),\n };\n return cachedResult;\n}\n\nexport function clearMusicalTickCache(): void {\n cachedParams = null;\n cachedResult = null;\n}\n","import { LitElement, html, css } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\n\n@customElement('daw-track-controls')\nexport class DawTrackControlsElement extends LitElement {\n /** Track ID — set by the editor to link controls to a track row. */\n @property({ attribute: false }) trackId: string | null = null;\n @property({ attribute: false }) trackName = '';\n @property({ type: Number, attribute: false }) volume = 1;\n @property({ type: Number, attribute: false }) pan = 0;\n @property({ type: Boolean, attribute: false }) muted = false;\n @property({ type: Boolean, attribute: false }) soloed = false;\n\n static styles = css`\n :host {\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n box-sizing: border-box;\n padding: 6px 8px;\n background: var(--daw-controls-background, #0f0f1a);\n color: var(--daw-controls-text, #c49a6c);\n border-bottom: 1px solid rgba(255, 255, 255, 0.05);\n font-family: system-ui, sans-serif;\n font-size: 11px;\n overflow: hidden;\n container-type: size;\n }\n .header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 4px;\n margin-bottom: 3px;\n }\n .name {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-weight: 600;\n font-size: 11px;\n }\n .remove-btn {\n background: none;\n border: none;\n color: var(--daw-controls-text, #c49a6c);\n cursor: pointer;\n padding: 0 2px;\n font-size: 14px;\n line-height: 1;\n opacity: 0.4;\n }\n .remove-btn:hover {\n opacity: 1;\n color: #d08070;\n }\n .buttons {\n display: flex;\n gap: 3px;\n margin-bottom: 3px;\n }\n .btn {\n background: rgba(255, 255, 255, 0.06);\n border: 1px solid rgba(255, 255, 255, 0.1);\n border-radius: 3px;\n color: var(--daw-controls-text, #c49a6c);\n cursor: pointer;\n font-size: 10px;\n font-weight: 600;\n padding: 2px 8px;\n text-align: center;\n }\n .btn:hover {\n background: rgba(255, 255, 255, 0.12);\n }\n .btn.active {\n background: rgba(99, 199, 95, 0.25);\n border-color: rgba(99, 199, 95, 0.5);\n color: #63c75f;\n }\n .btn.muted-active {\n background: rgba(208, 128, 112, 0.25);\n border-color: rgba(208, 128, 112, 0.5);\n color: #d08070;\n }\n .slider-row {\n display: flex;\n align-items: center;\n gap: 4px;\n height: 16px;\n }\n .slider-label {\n width: 50px;\n font-size: 9px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n opacity: 0.6;\n flex-shrink: 0;\n display: flex;\n justify-content: space-between;\n }\n .slider-label-name {\n opacity: 0.5;\n }\n .slider-label-value {\n font-family: 'Courier New', monospace;\n }\n input[type='range'] {\n flex: 1;\n min-width: 0;\n height: 20px;\n margin: 0;\n -webkit-appearance: none;\n appearance: none;\n background: transparent;\n cursor: pointer;\n }\n input[type='range']::-webkit-slider-runnable-track {\n height: 3px;\n background: rgba(255, 255, 255, 0.12);\n border-radius: 2px;\n }\n input[type='range']::-webkit-slider-thumb {\n -webkit-appearance: none;\n width: 12px;\n height: 12px;\n border-radius: 50%;\n background: var(--daw-controls-text, #c49a6c);\n margin-top: -4.5px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);\n }\n input[type='range']::-moz-range-track {\n height: 3px;\n background: rgba(255, 255, 255, 0.12);\n border-radius: 2px;\n border: none;\n }\n input[type='range']::-moz-range-thumb {\n width: 12px;\n height: 12px;\n border-radius: 50%;\n background: var(--daw-controls-text, #c49a6c);\n border: none;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);\n }\n /* Compact modes: drop sliders when the row is too short for the full\n stack. Thresholds are CONTENT-BOX heights — the host is border-box\n with 12px vertical padding + 1px border, so an editor-given height H\n enters compact mode at H <= 89px (Pan hidden) and H <= 73px (Vol also\n hidden). NOTE: container-type: size requires an explicit height on\n the host — the editor always provides one; standalone consumers must\n too (see the firstUpdated guard). */\n @container (max-height: 76px) {\n .pan-row {\n display: none;\n }\n }\n @container (max-height: 60px) {\n .vol-row {\n display: none;\n }\n }\n `;\n\n protected firstUpdated(): void {\n requestAnimationFrame(() => {\n if (!this.isConnected) return;\n const rect = this.getBoundingClientRect();\n // width > 0 proves a real layout engine ran (happy-dom reports 0×0,\n // so unit tests stay quiet); height 0 then means size containment\n // collapsed the element because no explicit height was provided.\n if (rect.width > 0 && rect.height === 0) {\n console.warn(\n '[dawcore] <daw-track-controls> has zero height: container-type: size requires an explicit height on the element (the editor sets one automatically; standalone usage must too). The controls are currently invisible.'\n );\n }\n });\n }\n\n private _onVolumeInput = (e: Event) => {\n const value = Number((e.target as HTMLInputElement).value);\n if (Number.isFinite(value)) this._dispatchControl('volume', value);\n };\n\n private _onPanInput = (e: Event) => {\n const value = Number((e.target as HTMLInputElement).value);\n if (Number.isFinite(value)) this._dispatchControl('pan', value);\n };\n\n private _onMuteClick = () => {\n this._dispatchControl('muted', !this.muted);\n };\n\n private _onSoloClick = () => {\n this._dispatchControl('soloed', !this.soloed);\n };\n\n private _onRemoveClick = () => {\n if (!this.trackId) return;\n this.dispatchEvent(\n new CustomEvent('daw-track-remove', {\n bubbles: true,\n composed: true,\n detail: { trackId: this.trackId },\n })\n );\n };\n\n private _dispatchControl(prop: string, value: number | boolean) {\n if (!this.trackId) return;\n this.dispatchEvent(\n new CustomEvent('daw-track-control', {\n bubbles: true,\n composed: true,\n detail: { trackId: this.trackId, prop, value },\n })\n );\n }\n\n render() {\n const volPercent = Math.round(this.volume * 100);\n const panPercent = Math.round(Math.abs(this.pan) * 100);\n const panDisplay = this.pan === 0 ? 'C' : (this.pan > 0 ? 'R' : 'L') + panPercent;\n\n return html`\n <div class=\"header\">\n <span class=\"name\" title=${this.trackName}>${this.trackName || 'Untitled'}</span>\n <button class=\"remove-btn\" @click=${this._onRemoveClick} title=\"Remove track\">\n &times;\n </button>\n </div>\n <div class=\"buttons\">\n <button\n class=\"btn ${this.muted ? 'muted-active' : ''}\"\n @click=${this._onMuteClick}\n title=\"Mute\"\n >\n M\n </button>\n <button class=\"btn ${this.soloed ? 'active' : ''}\" @click=${this._onSoloClick} title=\"Solo\">\n S\n </button>\n </div>\n <div class=\"slider-row vol-row\">\n <span class=\"slider-label\">\n <span class=\"slider-label-name\">Vol</span>\n <span class=\"slider-label-value\">${volPercent}%</span>\n </span>\n <input\n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.01\"\n .value=${String(this.volume)}\n @input=${this._onVolumeInput}\n />\n </div>\n <div class=\"slider-row pan-row\">\n <span class=\"slider-label\">\n <span class=\"slider-label-name\">Pan</span>\n <span class=\"slider-label-value\">${panDisplay}</span>\n </span>\n <input\n type=\"range\"\n min=\"-1\"\n max=\"1\"\n step=\"0.01\"\n .value=${String(this.pan)}\n @input=${this._onPanInput}\n />\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-track-controls': DawTrackControlsElement;\n }\n}\n","import { LitElement, html, css } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { MIN_PIXELS_PER_UNIT } from '@waveform-playlist/core';\nimport type { MusicalTickData, MeterEntry } from '@waveform-playlist/core';\nimport { getCachedMusicalTicks } from '../utils/musical-tick-cache';\nimport { getVisibleChunkIndices } from '../utils/viewport';\n\nconst MAX_CANVAS_WIDTH = 1000;\n\n/**\n * `<daw-grid>` renders a musical grid overlay behind waveforms using the\n * Audacity three-tier model:\n *\n * - **Zebra stripes**: Alternating bar backgrounds at 2% white opacity.\n * - **Major lines** (bars): 10% white opacity, full height.\n * - **Minor lines** (beats): 6% white opacity, full height.\n * - **MinorMinor** (subdivisions): Ruler ticks only — no grid lines.\n *\n * Uses chunked 1000px canvases with virtual scrolling (same as daw-waveform).\n *\n * CSS custom properties:\n * --daw-grid-bar-highlight Alternating bar fill (default: rgba(255,255,255,0.02))\n * --daw-grid-major-line Bar line color (default: rgba(255,255,255,0.1))\n * --daw-grid-minor-line Beat line color (default: rgba(255,255,255,0.06))\n */\n@customElement('daw-grid')\nexport class DawGridElement extends LitElement {\n @property({ type: Number, attribute: false }) ticksPerPixel = 24;\n @property({ attribute: false }) meterEntries: MeterEntry[] = [\n { tick: 0, numerator: 4, denominator: 4 },\n ];\n @property({ type: Number, attribute: false }) ppqn = 960;\n @property({ type: Number, attribute: false }) visibleStart = -Infinity;\n @property({ type: Number, attribute: false }) visibleEnd = Infinity;\n @property({ type: Number, attribute: false }) length = 0;\n @property({ type: Number, attribute: false }) height = 200;\n\n private _tickData: MusicalTickData | null = null;\n\n static styles = css`\n :host {\n display: block;\n position: absolute;\n top: 0;\n left: 0;\n pointer-events: none;\n z-index: 0;\n }\n .container {\n position: relative;\n }\n canvas {\n position: absolute;\n top: 0;\n }\n `;\n\n willUpdate() {\n if (this.length > 0) {\n this._tickData = getCachedMusicalTicks({\n ticksPerPixel: this.ticksPerPixel,\n meterEntries: this.meterEntries,\n ppqn: this.ppqn,\n startPixel: 0,\n endPixel: this.length,\n });\n } else {\n this._tickData = null;\n }\n }\n\n render() {\n if (!this._tickData) return html``;\n\n const totalWidth = this.length;\n const dpr = typeof devicePixelRatio !== 'undefined' ? devicePixelRatio : 1;\n const indices = getVisibleChunkIndices(\n totalWidth,\n MAX_CANVAS_WIDTH,\n this.visibleStart,\n this.visibleEnd\n );\n\n return html`\n <div class=\"container\" style=\"width: ${totalWidth}px; height: ${this.height}px;\">\n ${indices.map((i) => {\n const width = Math.min(MAX_CANVAS_WIDTH, totalWidth - i * MAX_CANVAS_WIDTH);\n return html`\n <canvas\n data-index=${i}\n width=${width * dpr}\n height=${this.height * dpr}\n style=\"left: ${i * MAX_CANVAS_WIDTH}px; width: ${width}px; height: ${this.height}px;\"\n ></canvas>\n `;\n })}\n </div>\n `;\n }\n\n updated() {\n this._drawGrid();\n }\n\n private _drawGrid() {\n if (!this._tickData) return;\n\n const canvases = this.shadowRoot?.querySelectorAll('canvas');\n if (!canvases) return;\n\n const dpr = typeof devicePixelRatio !== 'undefined' ? devicePixelRatio : 1;\n const style = getComputedStyle(this);\n const barHighlight =\n style.getPropertyValue('--daw-grid-bar-highlight').trim() || 'rgba(255,255,255,0.02)';\n const majorLine =\n style.getPropertyValue('--daw-grid-major-line').trim() || 'rgba(255,255,255,0.1)';\n const minorLine =\n style.getPropertyValue('--daw-grid-minor-line').trim() || 'rgba(255,255,255,0.06)';\n\n const { ticks, pixelsPerQuarterNote } = this._tickData;\n\n for (const canvas of canvases) {\n const idx = Number(canvas.dataset.index);\n const ctx = canvas.getContext('2d');\n if (!ctx) continue;\n\n const chunkLeft = idx * MAX_CANVAS_WIDTH;\n const canvasWidth = Math.min(MAX_CANVAS_WIDTH, this.length - chunkLeft);\n\n ctx.resetTransform();\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.scale(dpr, dpr);\n\n // Zebra stripes: use barIndex from tick data for exact alignment with grid lines.\n // Draw a stripe from each odd-barIndex major tick to the next major tick.\n // Threshold: show stripes when a 4-beat bar would be >= MIN_PIXELS_PER_UNIT wide.\n if (pixelsPerQuarterNote * 4 >= MIN_PIXELS_PER_UNIT) {\n ctx.fillStyle = barHighlight;\n const majorTicks = ticks.filter((t) => t.type === 'major');\n for (let i = 0; i < majorTicks.length; i++) {\n if (majorTicks[i].barIndex % 2 === 1) {\n const x = majorTicks[i].pixel - chunkLeft;\n // Use actual next major tick pixel for exact bar width (handles variable meter).\n // Last bar: use the last meter entry's numerator for correct width.\n const lastMeter = this.meterEntries[this.meterEntries.length - 1];\n const lastBarWidth =\n pixelsPerQuarterNote * lastMeter.numerator * (4 / lastMeter.denominator);\n const nextX =\n i + 1 < majorTicks.length ? majorTicks[i + 1].pixel - chunkLeft : x + lastBarWidth;\n ctx.fillRect(x, 0, nextX - x, this.height);\n }\n }\n }\n\n // Grid lines: major (bars) and minor (beats) only.\n // Subdivisions (minorMinor) get ruler ticks but no grid lines.\n ctx.lineWidth = 1;\n for (const tick of ticks) {\n if (tick.type === 'minorMinor') continue;\n\n const localX = tick.pixel - chunkLeft;\n if (localX < 0 || localX >= canvasWidth) continue;\n\n ctx.strokeStyle = tick.type === 'major' ? majorLine : minorLine;\n ctx.beginPath();\n ctx.moveTo(localX + 0.5, 0);\n ctx.lineTo(localX + 0.5, this.height);\n ctx.stroke();\n }\n }\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-grid': DawGridElement;\n }\n}\n","import { css } from 'lit';\n\n/**\n * Default CSS custom properties for dawcore elements.\n * Consumers override these on <daw-editor> or any ancestor.\n * Values inherit through Shadow DOM boundaries automatically.\n */\nexport const hostStyles = css`\n :host {\n --daw-wave-color: #c49a6c;\n --daw-progress-color: #63c75f;\n --daw-playhead-color: #d08070;\n --daw-background: #1a1a2e;\n --daw-track-background: #16213e;\n --daw-ruler-color: #c49a6c;\n --daw-ruler-background: #0f0f1a;\n --daw-controls-background: #1a1a2e;\n --daw-controls-text: #e0d4c8;\n --daw-selection-color: rgba(99, 199, 95, 0.3);\n --daw-clip-header-background: rgba(0, 0, 0, 0.4);\n --daw-clip-header-text: #e0d4c8;\n }\n`;\n\n/** Clip container and header styles for the editor timeline. */\nexport const clipStyles = css`\n .clip-container {\n position: absolute;\n overflow: hidden;\n }\n .clip-header {\n position: relative;\n z-index: 1;\n height: 20px;\n background: var(--daw-clip-header-background, rgba(0, 0, 0, 0.4));\n border-bottom: 1px solid rgba(255, 255, 255, 0.08);\n display: flex;\n align-items: center;\n padding: 0 6px;\n user-select: none;\n -webkit-user-drag: none;\n }\n .clip-header span {\n font-size: 10px;\n font-weight: 500;\n letter-spacing: 0.02em;\n font-family: system-ui, sans-serif;\n color: var(--daw-clip-header-text, #e0d4c8);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n opacity: 0.8;\n }\n .clip-boundary {\n position: absolute;\n top: 0;\n width: 8px;\n height: 100%;\n z-index: 2;\n cursor: col-resize;\n background: transparent;\n border: none;\n touch-action: none;\n user-select: none;\n -webkit-user-drag: none;\n transition: background 0.1s, border-color 0.1s;\n }\n .clip-boundary[data-boundary-edge='left'] {\n left: 0;\n }\n .clip-boundary[data-boundary-edge='right'] {\n right: 0;\n }\n .clip-boundary[data-boundary-edge='left']:hover {\n background: rgba(255, 255, 255, 0.2);\n border-left: 2px solid rgba(255, 255, 255, 0.5);\n }\n .clip-boundary[data-boundary-edge='right']:hover {\n background: rgba(255, 255, 255, 0.2);\n border-right: 2px solid rgba(255, 255, 255, 0.5);\n }\n .clip-boundary[data-boundary-edge='left'].dragging {\n background: rgba(255, 255, 255, 0.4);\n border-left: 2px solid rgba(255, 255, 255, 0.8);\n }\n .clip-boundary[data-boundary-edge='right'].dragging {\n background: rgba(255, 255, 255, 0.4);\n border-right: 2px solid rgba(255, 255, 255, 0.8);\n }\n .clip-header[data-interactive] {\n cursor: grab;\n }\n .clip-header[data-interactive]:active {\n cursor: grabbing;\n }\n`;\n","import type { ReactiveController, ReactiveControllerHost } from 'lit';\nimport { getVisibleChunkIndices } from '../utils/viewport';\n\nexport { getVisibleChunkIndices };\n\nconst OVERSCAN_MULTIPLIER = 1.5;\nconst SCROLL_THRESHOLD = 100;\n\nexport class ViewportController implements ReactiveController {\n private _host: ReactiveControllerHost & HTMLElement;\n private _scrollContainer: HTMLElement | null = null;\n private _lastScrollLeft = 0;\n private _resizeObserver: ResizeObserver | null = null;\n\n // Permissive defaults: render everything until scroll container is attached\n visibleStart = -Infinity;\n visibleEnd = Infinity;\n containerWidth = 0;\n\n constructor(host: ReactiveControllerHost & HTMLElement) {\n this._host = host;\n host.addController(this);\n }\n\n /** CSS selector for the scroll container inside the host's Shadow DOM. */\n scrollSelector = '';\n\n hostConnected() {\n // Defer to allow Shadow DOM to render before querying\n requestAnimationFrame(() => {\n if (!this._host.isConnected) return;\n const container = this.scrollSelector\n ? (this._host.shadowRoot?.querySelector(this.scrollSelector) as HTMLElement)\n : this._host;\n if (container) {\n this._attachScrollContainer(container);\n } else if (this.scrollSelector) {\n console.warn(\n '[dawcore] ViewportController: scroll container not found for \"' +\n this.scrollSelector +\n '\"'\n );\n }\n });\n }\n\n hostDisconnected() {\n this._scrollContainer?.removeEventListener('scroll', this._onScroll);\n this._resizeObserver?.disconnect();\n this._resizeObserver = null;\n this._scrollContainer = null;\n }\n\n private _attachScrollContainer(container: HTMLElement) {\n this._scrollContainer?.removeEventListener('scroll', this._onScroll);\n this._resizeObserver?.disconnect();\n this._scrollContainer = container;\n container.addEventListener('scroll', this._onScroll, { passive: true });\n this._update(container.scrollLeft, container.clientWidth);\n if (typeof ResizeObserver !== 'undefined') {\n this._resizeObserver = new ResizeObserver(() => {\n if (!this._scrollContainer) return;\n const next = this._scrollContainer.clientWidth;\n if (next === this.containerWidth) return;\n this._update(this._scrollContainer.scrollLeft, next);\n this._host.requestUpdate();\n });\n this._resizeObserver.observe(container);\n }\n this._host.requestUpdate();\n }\n\n private _onScroll = () => {\n if (!this._scrollContainer) return;\n const { scrollLeft, clientWidth } = this._scrollContainer;\n if (Math.abs(scrollLeft - this._lastScrollLeft) >= SCROLL_THRESHOLD) {\n this._update(scrollLeft, clientWidth);\n this._host.requestUpdate();\n }\n };\n\n private _update(scrollLeft: number, containerWidth: number) {\n this._lastScrollLeft = scrollLeft;\n this.containerWidth = containerWidth;\n const buffer = containerWidth * OVERSCAN_MULTIPLIER;\n this.visibleStart = scrollLeft - buffer;\n this.visibleEnd = scrollLeft + containerWidth + buffer;\n }\n}\n","import type { ReactiveController, ReactiveControllerHost } from 'lit';\n\nexport interface AudioResumeHost extends ReactiveControllerHost, HTMLElement {\n /** Returns the AudioContext to resume on user gesture */\n readonly audioContext: AudioContext;\n}\n\nexport class AudioResumeController implements ReactiveController {\n private _host: AudioResumeHost;\n private _target: EventTarget | null = null;\n private _attached = false;\n private _generation = 0;\n\n /** CSS selector, or 'document'. When undefined, controller is inert. */\n target?: string;\n\n constructor(host: AudioResumeHost) {\n this._host = host;\n host.addController(this);\n }\n\n hostConnected() {\n // Defer to next frame so Lit's willUpdate() can set `target` from\n // the host's attribute before we read it. Same pattern as ViewportController.\n const gen = ++this._generation;\n requestAnimationFrame(() => {\n if (gen !== this._generation) return; // stale callback from previous connect\n if (!this._host.isConnected || this._attached || this.target === undefined) return;\n\n let resolvedTarget: EventTarget | null;\n try {\n resolvedTarget = this._resolveTarget();\n } catch (err) {\n console.warn(\n '[dawcore] AudioResumeController: failed to resolve target \"' +\n this.target +\n '\": ' +\n String(err)\n );\n return;\n }\n if (!resolvedTarget) return;\n\n this._target = resolvedTarget;\n this._attached = true;\n resolvedTarget.addEventListener('pointerdown', this._onGesture, {\n once: true,\n capture: true,\n });\n resolvedTarget.addEventListener('keydown', this._onGesture, {\n once: true,\n capture: true,\n });\n });\n }\n\n hostDisconnected() {\n this._removeListeners();\n this._attached = false;\n }\n\n private _onGesture = (e: Event) => {\n let ctx: AudioContext;\n try {\n ctx = this._host.audioContext;\n } catch (err) {\n if (err instanceof Error && err.message.includes('No PlayoutAdapter set')) {\n // No adapter set yet — nothing to resume. Will be resumed on first play().\n this._removeListeners();\n return;\n }\n console.warn(\n '[dawcore] AudioResumeController: unexpected error accessing audioContext: ' + String(err)\n );\n this._removeListeners();\n return;\n }\n if (ctx.state === 'closed') {\n console.warn('[dawcore] AudioResumeController: AudioContext is closed, cannot resume.');\n } else if (ctx.state === 'suspended') {\n ctx.resume().catch((err) => {\n console.warn(\n '[dawcore] AudioResumeController: eager resume failed, will retry on play: ' + String(err)\n );\n });\n }\n // Remove the other listener (the fired one was auto-removed by { once: true })\n const otherType = e.type === 'pointerdown' ? 'keydown' : 'pointerdown';\n this._target?.removeEventListener(otherType, this._onGesture, {\n capture: true,\n });\n this._target = null;\n };\n\n private _resolveTarget(): EventTarget | null {\n const t = this.target;\n if (t === undefined) return null;\n if (t === '') return this._host;\n if (t === 'document') return document;\n\n const el = document.querySelector(t);\n if (!el) {\n console.warn(\n '[dawcore] AudioResumeController: target \"' +\n t +\n '\" not found in DOM at attach time, falling back to host element. ' +\n 'Ensure the target exists before <daw-editor> connects.'\n );\n return this._host;\n }\n return el;\n }\n\n private _removeListeners() {\n if (!this._target) return;\n this._target.removeEventListener('pointerdown', this._onGesture, {\n capture: true,\n });\n this._target.removeEventListener('keydown', this._onGesture, {\n capture: true,\n });\n this._target = null;\n }\n}\n","import type { ReactiveController, ReactiveControllerHost } from 'lit';\nimport type { Bits } from '@waveform-playlist/core';\nimport type { DawWaveformElement } from '../elements/daw-waveform';\nimport { appendPeaks, concatenateAudioData, createAudioBuffer } from '@waveform-playlist/core';\nimport type {\n DawRecordingStartDetail,\n DawRecordingCompleteDetail,\n DawRecordingErrorDetail,\n DawRecordingPauseDetail,\n DawRecordingResumeDetail,\n} from '../events';\n\nexport interface RecordingOptions {\n trackId?: string;\n bits?: 8 | 16;\n /** Fallback channel count when stream doesn't report one via getSettings(). Must be 1 or 2. */\n channelCount?: 1 | 2;\n startSample?: number;\n /** Start playback during recording so user hears existing tracks. */\n overdub?: boolean;\n}\n\nexport interface RecordingSession {\n readonly trackId: string;\n readonly stream: MediaStream;\n readonly source: { disconnect(): void; connect(dest: unknown): void };\n readonly workletNode: { port: MessagePort; disconnect(): void };\n readonly chunks: Float32Array[][];\n totalSamples: number;\n readonly peaks: (Int8Array | Int16Array)[];\n readonly startSample: number;\n readonly channelCount: number;\n readonly bits: Bits;\n isFirstMessage: boolean;\n /** Latency samples to skip in live preview (outputLatency only). */\n readonly latencySamples: number;\n readonly wasOverdub: boolean;\n /** Stored so it can be removed on stop/cleanup — not just when stream ends. */\n readonly _onTrackEnded: (() => void) | null;\n readonly _audioTrack: MediaStreamTrack | null;\n /** Set during stopRecording, cleared by the done message from the worklet. */\n stopAckResolve: (() => void) | null;\n /** True from the start of stopRecording until the session is deleted.\n * Guards pauseRecording / resumeRecording so a mid-drain pause toggle\n * can't dispatch events for a session that's already terminating. */\n stopping: boolean;\n}\n\n/** Readonly view of a recording session for external consumers. */\nexport type ReadonlyRecordingSession = Readonly<\n Omit<RecordingSession, 'chunks' | 'peaks' | '_onTrackEnded' | '_audioTrack' | 'latencySamples'>\n> & {\n readonly latencySamples: number;\n readonly chunks: ReadonlyArray<ReadonlyArray<Float32Array>>;\n readonly peaks: ReadonlyArray<Int8Array | Int16Array>;\n};\n\n/** Narrow interface for the host editor. */\nexport interface RecordingHost extends ReactiveControllerHost {\n readonly audioContext: AudioContext;\n readonly samplesPerPixel: number;\n readonly effectiveSampleRate: number;\n readonly _selectedTrackId: string | null;\n readonly _currentTime: number;\n readonly shadowRoot: ShadowRoot | null;\n resolveAudioContextSampleRate(rate: number): void;\n _addRecordedClip?(\n trackId: string,\n buf: AudioBuffer,\n startSample: number,\n durSamples: number,\n offsetSamples?: number\n ): void;\n play?(startTime?: number): Promise<void>;\n stop?(): void;\n dispatchEvent(event: Event): boolean;\n /**\n * Register a worklet module URL on the adapter's AudioContext.\n * Abstracts native vs standardized-audio-context differences.\n * When absent, falls back to native audioContext.audioWorklet.addModule().\n */\n addWorkletModule?(url: string): Promise<void>;\n /**\n * Create an AudioWorkletNode on the adapter's context.\n * When absent, falls back to new AudioWorkletNode(audioContext, ...).\n */\n createAudioWorkletNode?(name: string, options?: AudioWorkletNodeOptions): AudioWorkletNode;\n /**\n * Create a MediaStreamSource on the adapter's context.\n * When absent, falls back to audioContext.createMediaStreamSource().\n */\n createMediaStreamSource?(stream: MediaStream): MediaStreamAudioSourceNode;\n}\n\nexport class RecordingController implements ReactiveController {\n private _host: RecordingHost & HTMLElement;\n private _sessions = new Map<string, RecordingSession>();\n private _workletLoadedCtx: AudioContext | null = null;\n /** Tracks worklet pause state explicitly so external consumers (editor,\n * pause button, spacebar) can share one source of truth. */\n private _isPaused = false;\n\n constructor(host: RecordingHost & HTMLElement) {\n this._host = host;\n host.addController(this);\n }\n\n hostConnected() {}\n\n hostDisconnected() {\n for (const trackId of [...this._sessions.keys()]) {\n this._cleanupSession(trackId);\n }\n this._workletLoadedCtx = null;\n }\n\n get isRecording(): boolean {\n return this._sessions.size > 0;\n }\n\n get isPaused(): boolean {\n return this._isPaused && this._sessions.size > 0;\n }\n\n getSession(trackId: string): ReadonlyRecordingSession | undefined {\n return this._sessions.get(trackId);\n }\n\n async startRecording(stream: MediaStream, options: RecordingOptions = {}): Promise<void> {\n const trackId = options.trackId ?? this._host._selectedTrackId;\n if (!trackId) {\n console.warn('[dawcore] RecordingController: No track selected for recording');\n return;\n }\n if (this._sessions.has(trackId)) {\n console.warn('[dawcore] RecordingController: Already recording on track \"' + trackId + '\"');\n return;\n }\n\n const bits: Bits = options.bits ?? 16;\n\n try {\n const rawCtx = this._host.audioContext;\n\n // Resolve editor sample rate from AudioContext before computing startSample\n this._host.resolveAudioContextSampleRate(rawCtx.sampleRate);\n\n // Load worklet module — guard tied to context identity so a\n // swapped AudioContext gets the module re-registered.\n if (!this._workletLoadedCtx || this._workletLoadedCtx !== rawCtx) {\n let addRecordingWorkletModule: (addModule: (url: string) => Promise<void>) => Promise<void>;\n try {\n ({ addRecordingWorkletModule } = await import('@waveform-playlist/worklets'));\n } catch (importErr) {\n throw new Error(\n 'Recording requires @waveform-playlist/worklets. ' +\n 'Install it: npm install @waveform-playlist/worklets' +\n ' (cause: ' +\n String(importErr) +\n ')'\n );\n }\n const addModule = this._host.addWorkletModule\n ? (url: string) => this._host.addWorkletModule!(url)\n : (url: string) => rawCtx.audioWorklet.addModule(url);\n await addRecordingWorkletModule(addModule);\n this._workletLoadedCtx = rawCtx;\n }\n\n // Detect channel count from stream (not source.channelCount — defaults to 2 per spec).\n // Fall back to user-provided channelCount option, then 1.\n const detectedChannelCount = stream.getAudioTracks()[0]?.getSettings()?.channelCount;\n if (detectedChannelCount === undefined && options.channelCount !== undefined) {\n console.warn(\n '[dawcore] Could not detect stream channel count, using fallback: ' + options.channelCount\n );\n }\n const channelCount = detectedChannelCount ?? options.channelCount ?? 1;\n\n const startSample =\n options.startSample ?? Math.floor(this._host._currentTime * this._host.effectiveSampleRate);\n\n // Compute latency offset once at start (doesn't change during session)\n const outputLatency = rawCtx.outputLatency ?? 0;\n const latencySamples = Math.floor(outputLatency * rawCtx.sampleRate);\n\n // Use host methods when available (supports standardized-audio-context),\n // fall back to native AudioContext methods.\n const source = this._host.createMediaStreamSource\n ? this._host.createMediaStreamSource(stream)\n : rawCtx.createMediaStreamSource(stream);\n const workletNode = this._host.createAudioWorkletNode\n ? this._host.createAudioWorkletNode('recording-processor', {\n channelCount,\n channelCountMode: 'explicit' as globalThis.ChannelCountMode,\n })\n : new AudioWorkletNode(rawCtx, 'recording-processor', {\n channelCount,\n channelCountMode: 'explicit' as globalThis.ChannelCountMode,\n });\n\n // Listen on MediaStreamTrack (not MediaStream — MediaStream has no 'ended' event)\n const audioTrack = stream.getAudioTracks()[0] ?? null;\n const onTrackEnded = audioTrack\n ? () => {\n if (this._sessions.has(trackId)) {\n this.stopRecording(trackId);\n }\n }\n : null;\n\n const session: RecordingSession = {\n trackId,\n stream,\n source,\n workletNode,\n chunks: Array.from({ length: channelCount }, () => []),\n totalSamples: 0,\n peaks: Array.from({ length: channelCount }, () =>\n bits === 8 ? new Int8Array(0) : new Int16Array(0)\n ),\n startSample,\n channelCount,\n bits,\n isFirstMessage: true,\n latencySamples,\n wasOverdub: options.overdub ?? false,\n _onTrackEnded: onTrackEnded,\n _audioTrack: audioTrack,\n stopAckResolve: null,\n stopping: false,\n };\n this._sessions.set(trackId, session);\n\n // dawcore CLAUDE.md: wire onmessage BEFORE source.connect() and postMessage start\n workletNode.port.onmessage = (e: MessageEvent) => {\n this._onWorkletMessage(trackId, e.data);\n };\n source.connect(workletNode);\n workletNode.port.postMessage({ command: 'start', channelCount });\n\n // Attach mic-unplug listener (stored in session for cleanup)\n if (audioTrack && onTrackEnded) {\n audioTrack.addEventListener('ended', onTrackEnded);\n }\n\n this._host.dispatchEvent(\n new CustomEvent<DawRecordingStartDetail>('daw-recording-start', {\n bubbles: true,\n composed: true,\n detail: { trackId, stream },\n })\n );\n\n this._host.requestUpdate();\n\n // Overdub: start playback so user hears existing tracks and playhead advances\n if (options.overdub && typeof this._host.play === 'function') {\n await this._host.play(this._host._currentTime);\n }\n } catch (err) {\n // Clean up partially-created session to prevent stuck isRecording state\n this._cleanupSession(trackId);\n console.warn('[dawcore] RecordingController: Failed to start recording: ' + String(err));\n this._host.dispatchEvent(\n new CustomEvent<DawRecordingErrorDetail>('daw-recording-error', {\n bubbles: true,\n composed: true,\n detail: { trackId, error: err },\n })\n );\n }\n }\n\n pauseRecording(trackId?: string): void {\n const id = trackId ?? [...this._sessions.keys()][0];\n if (!id) return;\n const session = this._sessions.get(id);\n // Skip if the session is already on its way out — would otherwise\n // dispatch a pause event for a session about to be deleted.\n if (!session || session.stopping) return;\n session.workletNode.port.postMessage({ command: 'pause' });\n this._isPaused = true;\n this._host.dispatchEvent(\n new CustomEvent<DawRecordingPauseDetail>('daw-recording-pause', {\n bubbles: true,\n composed: true,\n detail: { trackId: id },\n })\n );\n }\n\n resumeRecording(trackId?: string): void {\n const id = trackId ?? [...this._sessions.keys()][0];\n if (!id) return;\n const session = this._sessions.get(id);\n if (!session || session.stopping) return;\n session.workletNode.port.postMessage({ command: 'resume' });\n this._isPaused = false;\n this._host.dispatchEvent(\n new CustomEvent<DawRecordingResumeDetail>('daw-recording-resume', {\n bubbles: true,\n composed: true,\n detail: { trackId: id },\n })\n );\n }\n\n async stopRecording(trackId?: string): Promise<void> {\n const id = trackId ?? [...this._sessions.keys()][0];\n if (!id) return;\n\n const session = this._sessions.get(id);\n if (!session) return;\n\n // Capture whether we were paused — pause already flushed the partial\n // buffer, so there's nothing to wait for from the worklet on stop.\n const wasPaused = this._isPaused;\n this._isPaused = false;\n // Block any further pause/resume calls on this session — they could\n // race with the drain loop and dispatch events for a session that's\n // about to be deleted.\n session.stopping = true;\n\n // Stop playback only if this was an overdub session\n if (session.wasOverdub && typeof this._host.stop === 'function') {\n this._host.stop();\n }\n\n // Send stop. If the recording was already paused, pause's flushBuffers()\n // already drained the partial buffer — no need to await the terminal\n // message (and the worklet's audio thread may have throttled back while\n // paused, making the round-trip slow / unreliable).\n if (wasPaused) {\n session.workletNode.port.postMessage({ command: 'stop' });\n } else {\n // Active recording: await the worklet's terminal flush so the partial\n // buffer at stop time isn't silently dropped.\n const stopAck = new Promise<void>((resolve) => {\n session.stopAckResolve = resolve;\n });\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n // Safety timeout (1s). Prevents infinite hang on a real failure\n // (worklet crashed, context closed). The drain loop below catches any\n // straggler flush messages that were queued before the timeout fired,\n // so this is purely a circuit breaker — not a data-correctness budget.\n const timeout = new Promise<void>((resolve) => {\n timeoutId = setTimeout(resolve, 1000);\n });\n session.workletNode.port.postMessage({ command: 'stop' });\n await Promise.race([stopAck, timeout]);\n clearTimeout(timeoutId);\n session.stopAckResolve = null;\n\n // Drain the event-loop queue. During recording the worklet posts a\n // flush every ~16ms; if main was slower than that, messages back up.\n // Yield repeatedly until session.totalSamples stabilizes (no new\n // messages processed) for several consecutive ticks — at that point\n // the queue is empty and chunks are complete.\n let lastSamples = -1;\n let stable = 0;\n for (let i = 0; i < 50; i++) {\n if (session.totalSamples === lastSamples) {\n if (++stable >= 3) break;\n } else {\n stable = 0;\n lastSamples = session.totalSamples;\n }\n await new Promise<void>((r) => setTimeout(r, 5));\n }\n }\n session.source.disconnect();\n session.workletNode.disconnect();\n\n // Remove mic-unplug listener\n this._removeTrackEndedListener(session);\n\n // Build AudioBuffer from accumulated chunks\n if (session.totalSamples === 0) {\n console.warn('[dawcore] RecordingController: No audio data captured');\n this._sessions.delete(id);\n this._host.requestUpdate();\n // Dispatch error so record button can reset its state (fix #3)\n this._host.dispatchEvent(\n new CustomEvent<DawRecordingErrorDetail>('daw-recording-error', {\n bubbles: true,\n composed: true,\n detail: { trackId: id, error: new Error('No audio data captured') },\n })\n );\n return;\n }\n const stopCtx = this._host.audioContext;\n const channelData = session.chunks.map((chunkArr) => concatenateAudioData(chunkArr));\n const audioBuffer = createAudioBuffer(\n stopCtx,\n channelData,\n this._host.effectiveSampleRate,\n session.channelCount\n );\n\n // Latency compensation: use the offset computed at start time\n const latencyOffsetSamples = session.latencySamples;\n const effectiveDuration = Math.max(0, audioBuffer.length - latencyOffsetSamples);\n\n if (effectiveDuration === 0) {\n console.warn('[dawcore] RecordingController: Recording too short for latency compensation');\n this._sessions.delete(id);\n this._host.requestUpdate();\n this._host.dispatchEvent(\n new CustomEvent<DawRecordingErrorDetail>('daw-recording-error', {\n bubbles: true,\n composed: true,\n detail: { trackId: id, error: new Error('Recording too short to save') },\n })\n );\n return;\n }\n\n // Dispatch cancelable event\n const event = new CustomEvent<DawRecordingCompleteDetail>('daw-recording-complete', {\n bubbles: true,\n composed: true,\n cancelable: true,\n detail: {\n trackId: id,\n audioBuffer,\n startSample: session.startSample,\n durationSamples: effectiveDuration,\n offsetSamples: latencyOffsetSamples,\n },\n });\n const notPrevented = this._host.dispatchEvent(event);\n\n // Clean up session\n this._sessions.delete(id);\n this._host.requestUpdate();\n\n if (notPrevented) {\n this._createClipFromRecording(\n id,\n audioBuffer,\n session.startSample,\n effectiveDuration,\n latencyOffsetSamples\n );\n }\n }\n\n // Session fields are mutated in place on the hot path (~60fps worklet messages).\n // This is intentional — creating new session objects + Map entries per message\n // would cause significant GC pressure. Mutations are confined to the controller's\n // private map and do not affect Lit's reactive rendering.\n private _onWorkletMessage(trackId: string, data: unknown) {\n const session = this._sessions.get(trackId);\n if (!session) return;\n\n const { channels, done } = data as { channels: Float32Array[]; done?: boolean };\n\n // Resolve the stop barrier in a finally block so a throw in peak generation\n // (e.g. samplesPerPixel=0 transient, host shadowRoot churn) doesn't strand\n // the await in stopRecording for the full 250ms timeout.\n try {\n const hasSamples = !!(\n channels &&\n channels.length > 0 &&\n channels[0] &&\n channels[0].length > 0\n );\n if (!hasSamples) return;\n this._processWorkletSamples(trackId, session, channels);\n } finally {\n if (done && session.stopAckResolve) {\n const resolve = session.stopAckResolve;\n session.stopAckResolve = null;\n resolve();\n }\n }\n }\n\n private _processWorkletSamples(\n trackId: string,\n session: RecordingSession,\n channels: Float32Array[]\n ) {\n // Capture pre-increment value for appendPeaks\n const samplesProcessedBefore = session.totalSamples;\n\n // Accumulate chunks per channel — always do this, even during stop,\n // so the AudioBuffer captures every sample.\n for (let ch = 0; ch < session.channelCount; ch++) {\n if (channels[ch]) {\n session.chunks[ch].push(channels[ch]);\n }\n }\n session.totalSamples += channels[0].length;\n\n // If stop is already in flight, skip peak generation and live-preview\n // waveform updates. The recording is ending; the AudioBuffer's final\n // peaks will be regenerated by the consumer. Skipping this drains the\n // backlog of queued flush messages faster, getting us to the terminal\n // `done` message sooner.\n if (session.stopAckResolve !== null) return;\n\n // Generate peaks per channel and update live preview waveforms\n for (let ch = 0; ch < session.channelCount; ch++) {\n if (!channels[ch]) continue;\n const oldPeakCount = Math.floor(session.peaks[ch].length / 2);\n (session.peaks as (Int8Array | Int16Array)[])[ch] = appendPeaks(\n session.peaks[ch],\n channels[ch],\n this._host.samplesPerPixel,\n samplesProcessedBefore,\n session.bits\n );\n const newPeakCount = Math.floor(session.peaks[ch].length / 2);\n\n // Update live preview waveform — host is already & HTMLElement so shadowRoot is typed\n const waveformSelector = `daw-waveform[data-recording-track=\"${trackId}\"][data-recording-channel=\"${ch}\"]`;\n const waveformEl = this._host.shadowRoot?.querySelector(\n waveformSelector\n ) as DawWaveformElement | null;\n if (waveformEl) {\n if (session.isFirstMessage) {\n waveformEl.peaks = session.peaks[ch];\n } else {\n waveformEl.setPeaksQuiet(session.peaks[ch]);\n waveformEl.updatePeaks(Math.max(0, oldPeakCount - 1), newPeakCount);\n }\n }\n }\n\n session.isFirstMessage = false;\n\n // Throttle requestUpdate — only when container width needs to grow\n const newPixelWidth = Math.floor(session.totalSamples / this._host.samplesPerPixel);\n const oldPixelWidth = Math.floor(\n (session.totalSamples - channels[0].length) / this._host.samplesPerPixel\n );\n if (newPixelWidth > oldPixelWidth) {\n this._host.requestUpdate();\n }\n }\n\n private _createClipFromRecording(\n trackId: string,\n audioBuffer: AudioBuffer,\n startSample: number,\n durationSamples: number,\n offsetSamples = 0\n ) {\n if (typeof this._host._addRecordedClip === 'function') {\n this._host._addRecordedClip(\n trackId,\n audioBuffer,\n startSample,\n durationSamples,\n offsetSamples\n );\n } else {\n console.warn(\n '[dawcore] RecordingController: host does not implement _addRecordedClip — clip not created for track \"' +\n trackId +\n '\"'\n );\n }\n }\n\n private _removeTrackEndedListener(session: RecordingSession) {\n if (session._audioTrack && session._onTrackEnded) {\n session._audioTrack.removeEventListener('ended', session._onTrackEnded);\n }\n }\n\n private _cleanupSession(trackId: string) {\n const session = this._sessions.get(trackId);\n if (!session) return;\n try {\n this._removeTrackEndedListener(session);\n session.workletNode.port.postMessage({ command: 'stop' });\n session.source.disconnect();\n session.workletNode.disconnect();\n } catch (err) {\n console.warn(\n '[dawcore] RecordingController: disconnect error during cleanup for track \"' +\n trackId +\n '\": ' +\n String(err)\n );\n }\n this._sessions.delete(trackId);\n }\n}\n","import type { ReactiveController, ReactiveControllerHost } from 'lit';\nimport {\n SpectrogramOrchestrator,\n type ClipRegistration,\n type CanvasRegistration,\n type ViewportState,\n} from '@dawcore/spectrogram';\nimport type { SpectrogramConfig, ColorMapValue } from '@waveform-playlist/core';\nimport {\n SPECTROGRAM_DEFAULTS as LIBRARY_DEFAULTS,\n DEFAULT_SPECTROGRAM_COLOR_MAP as LIBRARY_DEFAULT_COLOR_MAP,\n} from '@waveform-playlist/core';\n\nexport interface SpectrogramControllerHost extends ReactiveControllerHost {\n dispatchEvent(event: Event): boolean;\n}\n\n/**\n * Lit reactive controller that owns a `SpectrogramOrchestrator` for the\n * `<daw-editor>` host. Creates the orchestrator lazily on first canvas\n * registration so editors without spectrogram tracks pay nothing.\n *\n * Holds editor-level defaults and per-track overrides separately; merges\n * them down to a single (config, colorMap) pair for the orchestrator on\n * each change.\n *\n * v1 limitation: orchestrator accepts ONE config / colorMap at a time, so\n * the \"first registered track wins\" rule applies when multiple tracks are\n * in spectrogram mode with different overrides. Multi-track per-clip\n * configs are deferred to a follow-up.\n */\nexport class SpectrogramController implements ReactiveController {\n private host: SpectrogramControllerHost;\n private workerFactory: () => Worker;\n private orchestrator: SpectrogramOrchestrator | null = null;\n\n private editorConfig: SpectrogramConfig | null = null;\n private editorColorMap: ColorMapValue | null = null;\n private trackConfigs = new Map<string, SpectrogramConfig | null>();\n private trackColorMaps = new Map<string, ColorMapValue | null>();\n\n constructor(host: SpectrogramControllerHost, workerFactory: () => Worker) {\n this.host = host;\n this.workerFactory = workerFactory;\n this.host.addController(this);\n }\n\n hostConnected(): void {\n // Lazy — orchestrator created on first registerCanvas.\n }\n\n hostDisconnected(): void {\n this.dispose();\n }\n\n setEditorConfig(config: SpectrogramConfig | null): void {\n this.editorConfig = config;\n this.reapply();\n }\n\n setEditorColorMap(colorMap: ColorMapValue | null): void {\n this.editorColorMap = colorMap;\n this.reapply();\n }\n\n setTrackConfig(trackId: string, config: SpectrogramConfig | null): void {\n if (config === null) {\n this.trackConfigs.delete(trackId);\n } else {\n this.trackConfigs.set(trackId, config);\n }\n this.reapply();\n }\n\n setTrackColorMap(trackId: string, colorMap: ColorMapValue | null): void {\n if (colorMap === null) {\n this.trackColorMaps.delete(trackId);\n } else {\n this.trackColorMaps.set(trackId, colorMap);\n }\n this.reapply();\n }\n\n registerClipAudio(reg: ClipRegistration): void {\n this.ensureOrchestrator().registerClip(reg);\n }\n\n unregisterClipAudio(clipId: string): void {\n this.orchestrator?.unregisterClip(clipId);\n }\n\n registerCanvas(reg: CanvasRegistration): void {\n this.ensureOrchestrator().registerCanvas(reg);\n }\n\n unregisterCanvas(canvasId: string): void {\n this.orchestrator?.unregisterCanvas(canvasId);\n }\n\n setViewport(state: ViewportState): void {\n this.orchestrator?.setViewport(state);\n }\n\n dispose(): void {\n if (this.orchestrator) {\n this.orchestrator.dispose();\n this.orchestrator = null;\n }\n }\n\n private ensureOrchestrator(): SpectrogramOrchestrator {\n if (!this.orchestrator) {\n this.orchestrator = new SpectrogramOrchestrator({\n workerFactory: this.workerFactory,\n workerPoolSize: 2,\n config: this.mergedConfig(),\n colorMap: this.mergedColorMap(),\n });\n this.orchestrator.addEventListener('viewport-ready', (e: Event) => {\n const detail = (e as CustomEvent<{ trackId: string; generation: number }>).detail;\n this.host.dispatchEvent(\n new CustomEvent('daw-spectrogram-ready', {\n detail: { trackId: detail.trackId, generation: detail.generation },\n bubbles: true,\n composed: true,\n })\n );\n });\n this.orchestrator.addEventListener('viewport-error', (e: Event) => {\n const detail = (e as CustomEvent<{ trackId: string; generation: number; error: Error }>)\n .detail;\n this.host.dispatchEvent(\n new CustomEvent('daw-spectrogram-error', {\n detail: {\n trackId: detail.trackId,\n generation: detail.generation,\n error: detail.error,\n },\n bubbles: true,\n composed: true,\n })\n );\n });\n this.reapply();\n }\n return this.orchestrator;\n }\n\n private reapply(): void {\n if (!this.orchestrator) return;\n this.orchestrator.setConfig(this.mergedConfig());\n this.orchestrator.setColorMap(this.mergedColorMap());\n }\n\n private mergedConfig(): SpectrogramConfig {\n // First track override wins (v1 limitation noted above).\n let track: SpectrogramConfig | null = null;\n for (const c of this.trackConfigs.values()) {\n track = c;\n break;\n }\n return { ...LIBRARY_DEFAULTS, ...(this.editorConfig ?? {}), ...(track ?? {}) };\n }\n\n private mergedColorMap(): ColorMapValue {\n for (const c of this.trackColorMaps.values()) {\n return c ?? LIBRARY_DEFAULT_COLOR_MAP;\n }\n return this.editorColorMap ?? LIBRARY_DEFAULT_COLOR_MAP;\n }\n}\n","import { pixelsToSeconds, snapTickToGrid } from '@waveform-playlist/core';\nimport type { SnapTo, MeterEntry } from '@waveform-playlist/core';\nimport { DRAG_THRESHOLD } from './constants';\n\n/** Narrow engine contract for pointer interactions. */\nexport interface PointerEngineContract {\n setSelection(start: number, end: number): void;\n stop(): void;\n play(time: number): void;\n seek(time: number): void;\n selectTrack(trackId: string | null): void;\n}\n\n/** Manages pointer interactions on the timeline: click-to-seek and drag-to-select. */\nexport interface PointerHandlerHost {\n readonly samplesPerPixel: number;\n readonly _engine: PointerEngineContract | null;\n readonly _isPlaying: boolean;\n readonly effectiveSampleRate: number;\n _currentTime: number;\n _selectionStartTime: number;\n _selectionEndTime: number;\n _dragOver: boolean;\n _setSelectedTrackId(trackId: string | null): void;\n _startPlayhead(): void;\n _stopPlayhead(): void;\n dispatchEvent(event: Event): boolean;\n shadowRoot: ShadowRoot | null;\n requestUpdate(): void;\n readonly _clipHandler: {\n tryHandle(target: Element, e: PointerEvent): boolean;\n onPointerMove(e: PointerEvent): void;\n onPointerUp(e: PointerEvent): void;\n isActive: boolean;\n } | null;\n readonly scaleMode: 'temporal' | 'beats';\n readonly ticksPerPixel: number;\n readonly bpm: number;\n readonly ppqn: number;\n readonly _meterEntries: MeterEntry[];\n readonly snapTo: SnapTo;\n readonly _secondsToTicks: (seconds: number) => number;\n readonly _ticksToSeconds: (ticks: number) => number;\n}\n\nexport class PointerHandler {\n private _host: PointerHandlerHost;\n private _isDragging = false;\n private _dragStartPx = 0;\n private _dragStartTime = 0;\n private _timeline: HTMLElement | null = null;\n // Cached from onPointerDown to avoid forced layout reflows at 60fps during drag\n private _timelineRect: DOMRect | null = null;\n\n constructor(host: PointerHandlerHost) {\n this._host = host;\n }\n\n private _pxFromPointer(e: PointerEvent): number {\n if (!this._timelineRect) {\n console.warn('[dawcore] _pxFromPointer called without timeline reference');\n return 0;\n }\n // .timeline is wider than :host (which has overflow-x: auto).\n // getBoundingClientRect().left already reflects scroll position\n // (goes negative when scrolled), so no scrollLeft adjustment needed.\n return e.clientX - this._timelineRect.left;\n }\n\n private _pxToTime(px: number): number {\n const h = this._host;\n if (h.scaleMode === 'beats') {\n let tick = px * h.ticksPerPixel;\n tick = snapTickToGrid(tick, h.snapTo, h._meterEntries, h.ppqn);\n return h._ticksToSeconds(tick);\n }\n return pixelsToSeconds(px, h.samplesPerPixel, h.effectiveSampleRate);\n }\n\n private _timeToPx(time: number): number {\n const h = this._host;\n if (h.scaleMode === 'beats') {\n const tick = h._secondsToTicks(time);\n return tick / h.ticksPerPixel;\n }\n return (time * h.effectiveSampleRate) / h.samplesPerPixel;\n }\n\n onPointerDown = (e: PointerEvent) => {\n // Check if click landed on an interactive clip element\n const clipHandler = this._host._clipHandler;\n if (clipHandler) {\n const target = e.composedPath()[0] as Element;\n if (target && clipHandler.tryHandle(target, e)) {\n // Prevent browser native drag (globe icon) and text selection\n e.preventDefault();\n // Clip handler took over — wire move/up to it\n this._timeline = this._host.shadowRoot?.querySelector('.timeline') as HTMLElement | null;\n if (this._timeline) {\n try {\n this._timeline.setPointerCapture(e.pointerId);\n } catch (err) {\n console.warn('[dawcore] setPointerCapture failed: ' + String(err));\n }\n const onMove = (me: Event) => clipHandler.onPointerMove(me as PointerEvent);\n const onUp = (ue: Event) => {\n clipHandler.onPointerUp(ue as PointerEvent);\n this._timeline?.removeEventListener('pointermove', onMove);\n this._timeline?.removeEventListener('pointerup', onUp);\n try {\n this._timeline?.releasePointerCapture((ue as PointerEvent).pointerId);\n } catch (err) {\n console.warn(\n '[dawcore] releasePointerCapture failed (may already be released): ' + String(err)\n );\n }\n this._timeline = null;\n };\n this._timeline.addEventListener('pointermove', onMove);\n this._timeline.addEventListener('pointerup', onUp);\n }\n return;\n }\n }\n\n this._timeline = this._host.shadowRoot?.querySelector('.timeline') as HTMLElement | null;\n if (!this._timeline) {\n console.warn(\n '[dawcore] PointerHandler: .timeline not found in shadow root — seek/selection ignored'\n );\n return;\n }\n\n this._timelineRect = this._timeline.getBoundingClientRect();\n this._dragStartPx = this._pxFromPointer(e);\n this._isDragging = false;\n\n try {\n this._timeline.setPointerCapture(e.pointerId);\n } catch (err) {\n console.warn('[dawcore] setPointerCapture failed: ' + String(err));\n }\n this._timeline.addEventListener('pointermove', this._onPointerMove);\n this._timeline.addEventListener('pointerup', this._onPointerUp);\n };\n\n private _onPointerMove = (e: PointerEvent) => {\n if (!this._timeline) return;\n\n const currentPx = this._pxFromPointer(e);\n\n if (!this._isDragging && Math.abs(currentPx - this._dragStartPx) > DRAG_THRESHOLD) {\n this._isDragging = true;\n // Cache snapped start time once — don't recompute every frame\n this._dragStartTime = this._pxToTime(this._dragStartPx);\n }\n\n if (this._isDragging) {\n const h = this._host;\n const startTime = this._dragStartTime;\n const endTime = this._pxToTime(currentPx);\n // Mutate host fields directly (not @state) and update <daw-selection>\n // imperatively to avoid triggering Lit re-renders at 60fps during drag\n h._selectionStartTime = Math.min(startTime, endTime);\n h._selectionEndTime = Math.max(startTime, endTime);\n const sel = h.shadowRoot?.querySelector('daw-selection') as\n | { startPx: number; endPx: number }\n | undefined;\n if (sel) {\n sel.startPx = this._timeToPx(h._selectionStartTime);\n sel.endPx = this._timeToPx(h._selectionEndTime);\n }\n }\n };\n\n private _onPointerUp = (e: PointerEvent) => {\n if (!this._timeline) return;\n\n try {\n this._timeline.releasePointerCapture(e.pointerId);\n } catch (err) {\n console.warn(\n '[dawcore] releasePointerCapture failed (may already be released): ' + String(err)\n );\n }\n this._timeline.removeEventListener('pointermove', this._onPointerMove);\n this._timeline.removeEventListener('pointerup', this._onPointerUp);\n\n try {\n if (this._isDragging) {\n this._finalizeSelection();\n } else {\n this._handleSeekClick(e);\n }\n } catch (err) {\n console.warn('[dawcore] Pointer interaction failed: ' + String(err));\n } finally {\n this._isDragging = false;\n this._timeline = null;\n this._timelineRect = null;\n }\n };\n\n private _finalizeSelection() {\n const h = this._host;\n if (h._engine) {\n h._engine.setSelection(h._selectionStartTime, h._selectionEndTime);\n }\n h.dispatchEvent(\n new CustomEvent('daw-selection', {\n bubbles: true,\n composed: true,\n detail: { start: h._selectionStartTime, end: h._selectionEndTime },\n })\n );\n h.requestUpdate();\n }\n\n private _handleSeekClick(e: PointerEvent) {\n const h = this._host;\n const px = this._pxFromPointer(e);\n const time = this._pxToTime(px);\n\n // Clear selection\n h._selectionStartTime = 0;\n h._selectionEndTime = 0;\n\n // Detect which track was clicked by Y position\n if (this._timeline) {\n const trackRows = this._timeline.querySelectorAll('.track-row');\n for (const row of trackRows) {\n const rowRect = row.getBoundingClientRect();\n if (e.clientY >= rowRect.top && e.clientY < rowRect.bottom) {\n const trackId = (row as HTMLElement).dataset.trackId;\n if (trackId) {\n this._selectTrack(trackId);\n }\n break;\n }\n }\n }\n\n // Capture playing state before engine calls (stop emits statechange\n // which synchronously flips _isPlaying — use wasPlaying for all guards)\n const wasPlaying = h._isPlaying;\n\n if (h._engine) {\n h._engine.setSelection(0, 0);\n if (wasPlaying) {\n // Tone.js needs stop + play to reschedule audio sources\n h._engine.stop();\n h._engine.play(time);\n h._startPlayhead();\n } else {\n h._engine.seek(time);\n }\n }\n\n h._currentTime = time;\n if (!wasPlaying) {\n h._stopPlayhead();\n }\n\n h.dispatchEvent(\n new CustomEvent('daw-seek', {\n bubbles: true,\n composed: true,\n detail: { time },\n })\n );\n h.requestUpdate();\n }\n\n private _selectTrack(trackId: string) {\n const h = this._host;\n if (h._engine) {\n try {\n h._engine.selectTrack(trackId);\n // Engine sets _selectedTrackId via statechange — don't set locally\n } catch (err) {\n console.warn(\n '[dawcore] selectTrack via engine failed, falling back to local: ' + String(err)\n );\n // Fall through to local selection below\n h._setSelectedTrackId(trackId);\n }\n } else {\n // No engine — set locally (will be lost when engine builds, acceptable for Phase 2)\n h._setSelectedTrackId(trackId);\n }\n h.dispatchEvent(\n new CustomEvent('daw-track-select', {\n bubbles: true,\n composed: true,\n detail: { trackId },\n })\n );\n }\n}\n","/** Minimum pixel movement before a drag is activated (click vs drag). */\nexport const DRAG_THRESHOLD = 3;\n\n// Boundary width (8px) is defined in CSS (styles/theme.ts .clip-boundary).\n// Not exported as a JS constant since CSS template literals can't import it.\n","import { snapTickToGrid } from '@waveform-playlist/core';\nimport type { SnapTo, MeterEntry } from '@waveform-playlist/core';\nimport { DRAG_THRESHOLD } from './constants';\n\n/** Snapshot of a clip's bounds for trim constraint computation. */\nexport interface ClipBounds {\n readonly offsetSamples: number;\n readonly durationSamples: number;\n readonly startSample: number;\n readonly sourceDurationSamples: number;\n}\n\n/** Narrow engine contract for clip move/trim interactions. */\nexport interface ClipEngineContract {\n moveClip(trackId: string, clipId: string, deltaSamples: number, skipAdapter?: boolean): number;\n trimClip(\n trackId: string,\n clipId: string,\n boundary: 'left' | 'right',\n deltaSamples: number,\n skipAdapter?: boolean\n ): void;\n updateTrack(trackId: string): void;\n /** Get a clip's full bounds for trim constraint computation. */\n getClipBounds(trackId: string, clipId: string): ClipBounds | null;\n /** Constrain a trim delta using the engine's collision/bounds logic. */\n constrainTrimDelta(\n trackId: string,\n clipId: string,\n boundary: 'left' | 'right',\n deltaSamples: number\n ): number;\n /** Begin a transaction — groups mutations into one undo step. */\n beginTransaction(): void;\n /** Commit the transaction — pushes one undo step for all grouped mutations. */\n commitTransaction(): void;\n /** Abort the transaction — restores pre-transaction state without pushing to undo. */\n abortTransaction(): void;\n}\n\n/** Peak data returned by reextractClipPeaks for imperative waveform updates. */\nexport interface ClipPeakSlice {\n data: ArrayLike<number>[];\n length: number;\n}\n\n/** Host interface required by ClipPointerHandler. */\nexport interface ClipPointerHost {\n readonly samplesPerPixel: number;\n /** In beats mode, the tick-derived SPP used for rendering. */\n readonly renderSamplesPerPixel: number;\n readonly effectiveSampleRate: number;\n readonly interactiveClips: boolean;\n readonly engine: ClipEngineContract | null;\n readonly shadowRoot: ShadowRoot | null;\n readonly scaleMode: 'temporal' | 'beats';\n readonly ticksPerPixel: number;\n readonly bpm: number;\n readonly ppqn: number;\n readonly _meterEntries: MeterEntry[];\n readonly snapTo: SnapTo;\n readonly _secondsToTicks: (seconds: number) => number;\n readonly _ticksToSeconds: (ticks: number) => number;\n dispatchEvent(event: Event): boolean;\n /** Re-extract peaks for a clip at new offset/duration from cached WaveformData. */\n reextractClipPeaks(\n clipId: string,\n offsetSamples: number,\n durationSamples: number\n ): ClipPeakSlice | null;\n /**\n * Returns true if the clip is a MIDI clip (has midiNotes).\n * Trim handles are inert for MIDI clips — note slicing is not yet implemented.\n */\n isMidiClip(trackId: string, clipId: string): boolean;\n}\n\ntype DragMode = 'move' | 'trim-left' | 'trim-right';\n\n/**\n * Handles pointer interactions for clip move and trim drag operations.\n * Converts pixel deltas to sample deltas and delegates to the engine.\n *\n * Move: sends incremental deltas per-frame with skipAdapter=true (engine shifts\n * startSample additively without touching audio adapter). Adapter synced once\n * via updateTrack() at drag end.\n * Trim: updates clip container CSS imperatively during drag for visual feedback,\n * then applies cumulative delta to engine once at drag end.\n */\nexport class ClipPointerHandler {\n private _host: ClipPointerHost;\n private _mode: DragMode | null = null;\n private _clipId = '';\n private _trackId = '';\n private _startPx = 0;\n private _isDragging = false;\n private _cumulativeDeltaSamples = 0;\n // Trim visual feedback: snapshot of original clip state\n private _clipContainer: HTMLElement | null = null;\n private _boundaryEl: HTMLElement | null = null;\n private _originalLeft = 0;\n private _originalWidth = 0;\n private _originalOffsetSamples = 0;\n private _originalDurationSamples = 0;\n private _originalStartSample = 0;\n\n constructor(host: ClipPointerHost) {\n this._host = host;\n }\n\n /**\n * Convert a pixel delta to samples, snapping in tick space when in beats mode.\n *\n * The anchor is the absolute sample position being moved (e.g., clip start\n * for move/left-trim, clip end for right-trim). Snapping the absolute\n * position — not just the delta — ensures clips land exactly on grid lines\n * even if they started off-grid.\n */\n private _snapDeltaToSamples(totalDeltaPx: number, anchorSample: number): number {\n const h = this._host;\n if (h.scaleMode === 'beats') {\n const anchorSeconds = anchorSample / h.effectiveSampleRate;\n const anchorTick = h._secondsToTicks(anchorSeconds);\n const deltaTicks = totalDeltaPx * h.ticksPerPixel;\n const targetTick = anchorTick + deltaTicks;\n const snappedTick =\n h.snapTo !== 'off'\n ? snapTickToGrid(targetTick, h.snapTo, h._meterEntries, h.ppqn)\n : targetTick;\n const snappedSeconds = h._ticksToSeconds(snappedTick);\n const snappedSample = Math.round(snappedSeconds * h.effectiveSampleRate);\n return snappedSample - anchorSample;\n }\n return Math.round(totalDeltaPx * h.renderSamplesPerPixel);\n }\n\n /** Returns true if a drag interaction is currently in progress. */\n get isActive(): boolean {\n return this._mode !== null;\n }\n\n /**\n * Attempts to handle a pointerdown event on the given target element.\n * Returns true if the target is a recognized clip interaction element.\n */\n tryHandle(target: Element, e: PointerEvent): boolean {\n if (!this._host.interactiveClips) return false;\n\n // Walk up from click target to find clip interaction elements.\n // composedPath()[0] may be a child (e.g. <span> inside .clip-header).\n const boundary = (target as HTMLElement).closest?.('.clip-boundary') as HTMLElement | null;\n const header = (target as HTMLElement).closest?.('.clip-header') as HTMLElement | null;\n\n // Check boundary first (higher z-index, overlaps header at corners)\n if (boundary && boundary.dataset.boundaryEdge !== undefined) {\n const clipId = boundary.dataset.clipId;\n const trackId = boundary.dataset.trackId;\n const edge = boundary.dataset.boundaryEdge as 'left' | 'right';\n if (!clipId || !trackId || (edge !== 'left' && edge !== 'right')) return false;\n\n // Trim is not supported for MIDI clips — note slicing is a follow-up PR.\n // Consume the event: no trim drag starts, but prevent the click from\n // falling through to the timeline seek handler.\n if (this._host.isMidiClip(trackId, clipId)) return true;\n\n this._beginDrag(edge === 'left' ? 'trim-left' : 'trim-right', clipId, trackId, e);\n this._boundaryEl = boundary;\n return true;\n }\n\n // Check for clip header (move target)\n if (header && header.dataset.interactive !== undefined) {\n const clipId = header.dataset.clipId;\n const trackId = header.dataset.trackId;\n if (!clipId || !trackId) return false;\n\n this._beginDrag('move', clipId, trackId, e);\n return true;\n }\n\n return false;\n }\n\n private _beginDrag(mode: DragMode, clipId: string, trackId: string, e: PointerEvent): void {\n this._mode = mode;\n this._clipId = clipId;\n this._trackId = trackId;\n this._startPx = e.clientX;\n this._isDragging = false;\n\n this._cumulativeDeltaSamples = 0;\n\n // Snapshot clip start position for snap calculations\n const engine = this._host.engine;\n if (engine) {\n const bounds = engine.getClipBounds(trackId, clipId);\n if (bounds) {\n this._originalStartSample = bounds.startSample;\n }\n }\n\n // Group all drag mutations into one undo step\n if (engine) {\n engine.beginTransaction();\n } else {\n console.warn(\n '[dawcore] beginDrag: engine unavailable, drag mutations will not be grouped for undo'\n );\n }\n\n // For trim: snapshot the clip container's current position/width\n if (mode === 'trim-left' || mode === 'trim-right') {\n const container = this._host.shadowRoot?.querySelector(\n `.clip-container[data-clip-id=\"${clipId}\"]`\n ) as HTMLElement | null;\n if (container) {\n this._clipContainer = container;\n this._originalLeft = parseFloat(container.style.left) || 0;\n this._originalWidth = parseFloat(container.style.width) || 0;\n } else {\n console.warn('[dawcore] clip container not found for trim visual feedback: ' + clipId);\n }\n // Snapshot clip audio bounds for peak re-extraction during drag\n const engine = this._host.engine;\n if (engine) {\n const bounds = engine.getClipBounds(trackId, clipId);\n if (bounds) {\n this._originalOffsetSamples = bounds.offsetSamples;\n this._originalDurationSamples = bounds.durationSamples;\n }\n }\n }\n }\n\n /** Processes pointermove events during an active drag. */\n onPointerMove(e: PointerEvent): void {\n if (this._mode === null) return;\n\n const totalDeltaPx = e.clientX - this._startPx;\n\n // Activate drag after threshold is exceeded\n if (!this._isDragging && Math.abs(totalDeltaPx) > DRAG_THRESHOLD) {\n this._isDragging = true;\n // Apply .dragging class to boundary element for active drag styling\n if (this._boundaryEl) {\n this._boundaryEl.classList.add('dragging');\n }\n }\n\n if (!this._isDragging) return;\n\n const engine = this._host.engine;\n if (!engine) return;\n\n if (this._mode === 'move') {\n // Move: compute total snapped delta from original position, then derive\n // the incremental from what's already been applied to the engine.\n // Anchor = clip start position (move shifts the whole clip)\n const totalSnappedDelta = this._snapDeltaToSamples(totalDeltaPx, this._originalStartSample);\n const incrementalDeltaSamples = totalSnappedDelta - this._cumulativeDeltaSamples;\n if (incrementalDeltaSamples !== 0) {\n // Track constrained delta (not raw) so undo transactions are accurate\n const applied = engine.moveClip(this._trackId, this._clipId, incrementalDeltaSamples, true);\n this._cumulativeDeltaSamples += applied;\n }\n } else {\n // Trim: constrain delta using engine's full collision/bounds logic,\n // then track for visual feedback. Engine called once at pointerup.\n const boundary = this._mode === 'trim-left' ? 'left' : 'right';\n // Anchor = the boundary edge being dragged\n // Left trim: snap the left edge (startSample)\n // Right trim: snap the right edge (startSample + durationSamples)\n const anchor =\n boundary === 'left'\n ? this._originalStartSample\n : this._originalStartSample + this._originalDurationSamples;\n const rawDeltaSamples = this._snapDeltaToSamples(totalDeltaPx, anchor);\n const deltaSamples = engine.constrainTrimDelta(\n this._trackId,\n this._clipId,\n boundary,\n rawDeltaSamples\n );\n // Convert sample delta to pixels — use tick space in beats mode\n // to avoid quantization drift from the sample round-trip.\n let deltaPx: number;\n if (this._host.scaleMode === 'beats') {\n const h = this._host;\n const anchorSec = anchor / h.effectiveSampleRate;\n const anchorTick = h._secondsToTicks(anchorSec);\n const newSec = anchorSec + deltaSamples / h.effectiveSampleRate;\n const newTick = h._secondsToTicks(newSec);\n deltaPx = Math.round((newTick - anchorTick) / h.ticksPerPixel);\n } else {\n deltaPx = Math.round(deltaSamples / this._host.renderSamplesPerPixel);\n }\n\n this._cumulativeDeltaSamples = deltaSamples;\n\n // Visual feedback: update clip container CSS and re-extract peaks\n if (this._clipContainer) {\n if (this._mode === 'trim-left') {\n // Left trim: container shifts and resizes\n const newLeft = this._originalLeft + deltaPx;\n const newWidth = this._originalWidth - deltaPx;\n if (newWidth > 0) {\n this._clipContainer.style.left = newLeft + 'px';\n this._clipContainer.style.width = newWidth + 'px';\n // Re-extract peaks at new offset/duration from cached WaveformData.\n // New peaks cover the full new bounds, so waveforms stay at left:0\n // (no shift needed — the container position handles global alignment).\n const newOffset = this._originalOffsetSamples + deltaSamples;\n const newDuration = this._originalDurationSamples - deltaSamples;\n if (this._updateWaveformPeaks(newOffset, newDuration)) {\n // Peaks updated — reset waveform positions to fill container\n const waveforms = this._clipContainer.querySelectorAll('daw-waveform');\n for (const wf of waveforms) {\n (wf as HTMLElement).style.left = '0px';\n }\n } else {\n // No cached peaks — fall back to shifting waveforms for visual stability\n const waveforms = this._clipContainer.querySelectorAll('daw-waveform');\n for (const wf of waveforms) {\n (wf as HTMLElement).style.left = -deltaPx + 'px';\n }\n }\n }\n } else {\n // Right trim: extend/shrink right edge — left stays fixed\n const newWidth = this._originalWidth + deltaPx;\n if (newWidth > 0) {\n this._clipContainer.style.width = newWidth + 'px';\n // Re-extract peaks at new duration from cached WaveformData\n const newDuration = this._originalDurationSamples + deltaSamples;\n this._updateWaveformPeaks(this._originalOffsetSamples, newDuration);\n }\n }\n }\n }\n }\n\n /** Processes pointerup events to finalize and dispatch result events. */\n onPointerUp(_e: PointerEvent): void {\n if (this._mode === null) return;\n\n try {\n if (!this._isDragging || this._cumulativeDeltaSamples === 0) {\n // Restore original CSS if trim drag didn't produce a delta\n this._restoreTrimVisual();\n return;\n }\n\n const engine = this._host.engine;\n\n if (this._mode === 'move') {\n // Sync adapter once on drop (skipped during drag for performance)\n if (engine) {\n engine.updateTrack(this._trackId);\n this._host.dispatchEvent(\n new CustomEvent('daw-clip-move', {\n bubbles: true,\n composed: true,\n detail: {\n trackId: this._trackId,\n clipId: this._clipId,\n deltaSamples: this._cumulativeDeltaSamples,\n },\n })\n );\n } else {\n console.warn(\n '[dawcore] engine unavailable at move drop — audio may be out of sync for track ' +\n this._trackId\n );\n }\n } else {\n // Restore visual before engine applies — Lit will re-render with correct values\n this._restoreTrimVisual();\n\n // Trim: apply cumulative delta to engine in one shot (adapter updated internally)\n const boundary = this._mode === 'trim-left' ? 'left' : 'right';\n if (engine) {\n engine.trimClip(this._trackId, this._clipId, boundary, this._cumulativeDeltaSamples);\n this._host.dispatchEvent(\n new CustomEvent('daw-clip-trim', {\n bubbles: true,\n composed: true,\n detail: {\n trackId: this._trackId,\n clipId: this._clipId,\n boundary,\n deltaSamples: this._cumulativeDeltaSamples,\n },\n })\n );\n } else {\n console.warn(\n '[dawcore] engine unavailable at trim drop — trim not applied for clip ' + this._clipId\n );\n }\n }\n } finally {\n // Commit transaction if mutations occurred, abort if click/no-op.\n // Abort does NOT push to undo stack or clear redo stack.\n if (this._isDragging && this._cumulativeDeltaSamples !== 0) {\n this._host.engine?.commitTransaction();\n } else {\n this._host.engine?.abortTransaction();\n }\n this._reset();\n }\n }\n\n /** Re-extract peaks from cache and set on waveform elements during trim drag.\n * Returns true if peaks were successfully updated. */\n private _updateWaveformPeaks(offsetSamples: number, durationSamples: number): boolean {\n if (!this._clipContainer || durationSamples <= 0) return false;\n const peakSlice = this._host.reextractClipPeaks(this._clipId, offsetSamples, durationSamples);\n if (!peakSlice) return false;\n\n const waveforms = this._clipContainer.querySelectorAll('daw-waveform');\n for (let i = 0; i < waveforms.length; i++) {\n const wf = waveforms[i] as HTMLElement & { peaks: unknown; length: number };\n const channelPeaks = peakSlice.data[i];\n if (channelPeaks) {\n wf.peaks = channelPeaks;\n wf.length = peakSlice.length;\n }\n }\n return true;\n }\n\n /** Restore clip container CSS to original values after trim visual preview. */\n private _restoreTrimVisual(): void {\n if (this._clipContainer) {\n this._clipContainer.style.left = this._originalLeft + 'px';\n this._clipContainer.style.width = this._originalWidth + 'px';\n // Restore waveform positions (shifted during left trim preview)\n const waveforms = this._clipContainer.querySelectorAll('daw-waveform');\n for (const wf of waveforms) {\n (wf as HTMLElement).style.left = '0px';\n }\n }\n }\n\n private _reset(): void {\n // Remove .dragging class from boundary element\n if (this._boundaryEl) {\n this._boundaryEl.classList.remove('dragging');\n this._boundaryEl = null;\n }\n this._mode = null;\n this._clipId = '';\n this._trackId = '';\n this._startPx = 0;\n this._isDragging = false;\n\n this._cumulativeDeltaSamples = 0;\n this._clipContainer = null;\n this._originalLeft = 0;\n this._originalWidth = 0;\n this._originalOffsetSamples = 0;\n this._originalDurationSamples = 0;\n this._originalStartSample = 0;\n }\n}\n","/**\n * File loading logic extracted from daw-editor to keep the editor under 800 lines.\n * Operates on the editor instance via a narrow interface.\n */\n\nimport type { ClipTrack, PeakData } from '@waveform-playlist/core';\nimport { createClip, createTrack } from '@waveform-playlist/core';\nimport type { PeakPipeline } from '../workers/peakPipeline';\nimport type { DawTrackIdDetail, DawFilesLoadErrorDetail, LoadFilesResult } from '../events';\nimport type { TrackDescriptor } from '../types';\n\nexport interface FileLoaderHost {\n readonly samplesPerPixel: number;\n /** In beats mode, the tick-derived SPP used for rendering. */\n readonly renderSamplesPerPixel: number;\n readonly mono: boolean;\n readonly isConnected: boolean;\n _resolvedSampleRate: number | null;\n _tracks: Map<string, TrackDescriptor>;\n _engineTracks: Map<string, ClipTrack>;\n _peaksData: Map<string, PeakData>;\n _clipBuffers: Map<string, AudioBuffer>;\n _clipOffsets: Map<string, { offsetSamples: number; durationSamples: number }>;\n _audioCache: Map<string, Promise<AudioBuffer>>;\n _peakPipeline: PeakPipeline;\n _fetchAndDecode(src: string): Promise<AudioBuffer>;\n _recomputeDuration(): void;\n _ensureEngine(): Promise<{ setTracks(tracks: ClipTrack[]): void }>;\n dispatchEvent(event: Event): boolean;\n}\n\nexport async function loadFiles(\n host: FileLoaderHost,\n files: FileList | File[]\n): Promise<LoadFilesResult> {\n if (!files) {\n console.warn('[dawcore] loadFiles called with null/undefined');\n return { loaded: [], failed: [] };\n }\n\n const fileArray = Array.from(files);\n const loaded: string[] = [];\n const failed: Array<{ file: File; error: unknown }> = [];\n\n for (const file of fileArray) {\n if (file.type && !file.type.startsWith('audio/')) {\n failed.push({ file, error: new Error('Non-audio MIME type: ' + file.type) });\n console.warn('[dawcore] Skipping non-audio file: ' + file.name + ' (' + file.type + ')');\n continue;\n }\n\n const blobUrl = URL.createObjectURL(file);\n try {\n const audioBuffer = await host._fetchAndDecode(blobUrl);\n URL.revokeObjectURL(blobUrl);\n host._audioCache.delete(blobUrl);\n\n host._resolvedSampleRate = audioBuffer.sampleRate;\n\n const name = file.name.replace(/\\.\\w+$/, '');\n const clip = createClip({\n audioBuffer,\n startSample: 0,\n durationSamples: audioBuffer.length,\n offsetSamples: 0,\n gain: 1,\n name,\n sampleRate: audioBuffer.sampleRate,\n sourceDurationSamples: audioBuffer.length,\n });\n\n host._clipBuffers = new Map(host._clipBuffers).set(clip.id, audioBuffer);\n host._clipOffsets = new Map(host._clipOffsets).set(clip.id, {\n offsetSamples: clip.offsetSamples,\n durationSamples: clip.durationSamples,\n });\n const peakData = await host._peakPipeline.generatePeaks(\n audioBuffer,\n host.renderSamplesPerPixel,\n host.mono,\n clip.offsetSamples,\n clip.durationSamples\n );\n host._peaksData = new Map(host._peaksData).set(clip.id, peakData);\n\n const trackId = crypto.randomUUID();\n const track = createTrack({ name, clips: [clip] });\n track.id = trackId;\n\n host._tracks = new Map(host._tracks).set(trackId, {\n name,\n src: '',\n volume: 1,\n pan: 0,\n muted: false,\n soloed: false,\n renderMode: 'waveform',\n clips: [\n {\n kind: 'drop',\n src: '',\n peaksSrc: '',\n start: 0,\n duration: audioBuffer.duration,\n offset: 0,\n gain: 1,\n name,\n fadeIn: 0,\n fadeOut: 0,\n fadeType: 'linear',\n midiNotes: null,\n midiChannel: null,\n midiProgram: null,\n },\n ],\n });\n host._engineTracks = new Map(host._engineTracks).set(trackId, track);\n host._recomputeDuration();\n\n const engine = await host._ensureEngine();\n engine.setTracks([...host._engineTracks.values()]);\n\n loaded.push(trackId);\n host.dispatchEvent(\n new CustomEvent<DawTrackIdDetail>('daw-track-ready', {\n bubbles: true,\n composed: true,\n detail: { trackId },\n })\n );\n } catch (err) {\n URL.revokeObjectURL(blobUrl);\n console.warn('[dawcore] Failed to load file: ' + file.name + ' — ' + String(err));\n failed.push({ file, error: err });\n if (host.isConnected) {\n host.dispatchEvent(\n new CustomEvent<DawFilesLoadErrorDetail>('daw-files-load-error', {\n bubbles: true,\n composed: true,\n detail: { file, error: err },\n })\n );\n }\n }\n }\n\n return { loaded, failed };\n}\n","/**\n * MIDI loading logic extracted from daw-editor. Operates on the editor via a\n * narrow host interface (`addTrack` + `querySelectorAll`) — `<daw-editor>`\n * satisfies it without any new public surface.\n *\n * Numbered steps below match the Data Flow diagram in\n * `docs/specs/2026-05-23-dawcore-load-midi-design.md`.\n */\n\nimport type { MidiLoadOptions, MidiLoadResult } from '@dawcore/midi';\nimport type { TrackConfig } from '../types';\nimport type { DawTrackElement } from '../elements/daw-track';\n\nexport type { MidiLoadOptions, MidiLoadResult };\n\n/**\n * Minimal host surface needed by `loadMidiImpl`. `<daw-editor>` satisfies this\n * structurally. `querySelectorAll` is needed for cleanup-on-failure so the\n * loader can identify `<daw-track>` elements appended during this call (both\n * those whose `addTrack` resolved and those that rejected after `_loadTrack`\n * fired `daw-track-error` — the latter aren't in the `addTrack` resolution\n * value, so we need DOM observation to find them).\n */\nexport interface MidiLoaderHost {\n addTrack(config: TrackConfig): Promise<DawTrackElement>;\n querySelectorAll(selector: string): NodeListOf<Element>;\n}\n\nconst INSTALL_HINT =\n '@dawcore/midi is required for loadMidi(). Install with: npm install @dawcore/midi';\n\n/**\n * Loads a `.mid` file (URL or `File`) and creates N `<daw-track>` elements,\n * one per note-bearing MIDI track. On any per-track failure, every\n * `<daw-track>` appended during this call is removed so the editor returns\n * to its pre-call state (cleanup-on-failure — covers both `addTrack`\n * successes and the elements that `addTrack` left in the DOM before its\n * promise rejected).\n *\n * AbortSignal is forwarded to the fetch phase only. Aborting after parsing\n * does not cancel in-flight `addTrack` calls (documented limitation).\n */\nexport async function loadMidiImpl(\n host: MidiLoaderHost,\n source: string | File,\n options: MidiLoadOptions = {}\n): Promise<MidiLoadResult> {\n // Reject NaN / Infinity / negative startTime up front. Without this, every\n // clip silently inherits the bogus value and the timeline corrupts.\n const startTime = options.startTime ?? 0;\n if (!Number.isFinite(startTime) || startTime < 0) {\n throw new RangeError(\n 'loadMidi: startTime must be a non-negative finite number (got ' +\n String(options.startTime) +\n ')'\n );\n }\n\n // (1) Dynamic-import the optional peer dep. Log the original error so\n // debugging isn't blocked when the failure is something other than\n // \"not installed\" (broken exports map, 404 chunk, CSP, etc.). Targeting\n // ES2020 means we can't use Error.cause yet; console.warn carries the\n // diagnostic detail.\n let midiModule: typeof import('@dawcore/midi');\n try {\n midiModule = await import('@dawcore/midi');\n } catch (originalErr) {\n console.warn('[dawcore] @dawcore/midi dynamic import failed: ' + String(originalErr));\n throw new Error(INSTALL_HINT);\n }\n const { parseMidiUrl, parseMidiFile } = midiModule;\n\n // (2) Branch on source type. Wrap the File path so a failed disk read\n // surfaces with file context instead of a bare DOMException.\n let parsed;\n if (typeof source === 'string') {\n parsed = await parseMidiUrl(source, undefined, options.signal);\n } else {\n let buffer: ArrayBuffer;\n try {\n buffer = await source.arrayBuffer();\n } catch (err) {\n throw new Error(\n 'loadMidi: failed to read File \"' +\n source.name +\n '\" (' +\n source.size +\n ' bytes): ' +\n String(err)\n );\n }\n parsed = parseMidiFile(buffer);\n }\n\n // (3) Snapshot existing tracks so cleanup can identify the ones we appended.\n const childrenBefore = new Set<Element>(host.querySelectorAll('daw-track'));\n\n // (4) Concurrent addTrack with allSettled so we can wait for every settlement\n // before deciding. Promise.all would early-reject while other addTrack calls\n // keep running, leaving orphan tracks after cleanup.\n const settlements = await Promise.allSettled(\n parsed.tracks.map((t) =>\n host.addTrack({\n name: t.name,\n renderMode: 'piano-roll',\n clips: [\n {\n midiNotes: t.notes,\n midiChannel: t.channel,\n midiProgram: t.programNumber,\n start: startTime,\n },\n ],\n })\n )\n );\n\n // (5) Partition fulfilled vs rejected. Capture EVERY rejection (not just\n // the first) for diagnosability of multi-track failures.\n const succeeded: DawTrackElement[] = [];\n const rejections: unknown[] = [];\n for (const s of settlements) {\n if (s.status === 'fulfilled') {\n succeeded.push(s.value);\n } else {\n rejections.push(s.reason);\n }\n }\n\n // (6) On any rejection: remove ALL tracks appended during this call (both\n // succeeded and the ones addTrack left in the DOM before rejecting), then\n // throw a summary Error and console.warn each additional rejection so\n // multi-track failure modes stay diagnosable.\n if (rejections.length > 0) {\n const appendedTracks = Array.from(host.querySelectorAll('daw-track')).filter(\n (el) => !childrenBefore.has(el)\n );\n for (const el of appendedTracks) {\n try {\n el.remove();\n } catch (cleanupErr) {\n console.warn('[dawcore] loadMidi cleanup failed for a track: ' + String(cleanupErr));\n }\n }\n // Let MutationObserver microtasks flush so engine state is consistent\n // before rethrow.\n await Promise.resolve();\n\n // Log every rejection beyond the first so multi-track failure modes are\n // diagnosable (the thrown Error only carries the first). AggregateError\n // would be ideal here but requires ES2021+; defer until the project\n // bumps its lib target.\n for (let i = 1; i < rejections.length; i++) {\n console.warn(\n '[dawcore] loadMidi: additional track failure (' +\n i +\n '): ' +\n stringifyReason(rejections[i])\n );\n }\n const first = rejections[0];\n if (rejections.length > 1) {\n const message =\n 'loadMidi: ' +\n rejections.length +\n ' of ' +\n settlements.length +\n ' tracks failed; first: ' +\n (first instanceof Error ? first.message : stringifyReason(first));\n throw new Error(message);\n }\n throw first instanceof Error ? first : new Error(stringifyReason(first));\n }\n\n // (7) Build the result.\n return {\n trackIds: succeeded.map((el) => el.trackId),\n bpm: parsed.bpm,\n timeSignature: parsed.timeSignature,\n duration: parsed.duration,\n name: parsed.name,\n };\n}\n\n/**\n * Render a non-Error rejection reason as a useful string. Plain `String(x)`\n * gives `\"[object Object]\"` for object rejections, losing all structured info.\n */\nfunction stringifyReason(reason: unknown): string {\n if (reason === null) return 'null';\n if (reason === undefined) return 'undefined';\n if (typeof reason === 'object') {\n try {\n return JSON.stringify(reason);\n } catch {\n return Object.prototype.toString.call(reason);\n }\n }\n return String(reason);\n}\n","/**\n * Recording clip creation extracted from daw-editor to keep the editor under 800 lines.\n * Operates on the editor instance via a narrow interface.\n */\n\nimport type { ClipTrack, PeakData } from '@waveform-playlist/core';\nimport { createClip } from '@waveform-playlist/core';\nimport type { PeakPipeline } from '../workers/peakPipeline';\nimport type { DawErrorDetail } from '../events';\nimport type { TrackDescriptor, ClipDescriptor } from '../types';\n\nexport interface RecordingClipHost {\n readonly samplesPerPixel: number;\n readonly mono: boolean;\n readonly isConnected: boolean;\n readonly effectiveSampleRate: number;\n _tracks: Map<string, TrackDescriptor>;\n _engineTracks: Map<string, ClipTrack>;\n _peaksData: Map<string, PeakData>;\n _clipBuffers: Map<string, AudioBuffer>;\n _peakPipeline: PeakPipeline;\n _engine: {\n setTracks(tracks: ClipTrack[]): void;\n updateTrack?(trackId: string, track: ClipTrack): void;\n } | null;\n _recomputeDuration(): void;\n dispatchEvent(event: Event): boolean;\n}\n\nexport function addRecordedClip(\n host: RecordingClipHost,\n trackId: string,\n buf: AudioBuffer,\n startSample: number,\n durSamples: number,\n offsetSamples = 0\n) {\n // Slice off latency samples so peaks and playback only cover the audible portion\n let trimmedBuf = buf;\n if (offsetSamples > 0 && offsetSamples < buf.length) {\n const trimmed = new AudioBuffer({\n numberOfChannels: buf.numberOfChannels,\n length: durSamples,\n sampleRate: buf.sampleRate,\n });\n for (let ch = 0; ch < buf.numberOfChannels; ch++) {\n const source = buf.getChannelData(ch);\n trimmed.copyToChannel(source.subarray(offsetSamples, offsetSamples + durSamples), ch);\n }\n trimmedBuf = trimmed;\n }\n\n const clip = createClip({\n audioBuffer: trimmedBuf,\n startSample,\n durationSamples: durSamples,\n offsetSamples: 0, // offset already applied by slicing\n gain: 1,\n name: 'Recording',\n });\n host._clipBuffers = new Map(host._clipBuffers).set(clip.id, trimmedBuf);\n host._peakPipeline\n .generatePeaks(trimmedBuf, host.samplesPerPixel, host.mono)\n .then((pd) => {\n host._peaksData = new Map(host._peaksData).set(clip.id, pd);\n const t = host._engineTracks.get(trackId);\n if (!t) {\n // Track was removed during peak generation — clean up orphaned buffer\n const next = new Map(host._clipBuffers);\n next.delete(clip.id);\n host._clipBuffers = next;\n return;\n }\n host._engineTracks = new Map(host._engineTracks).set(trackId, {\n ...t,\n clips: [...t.clips, clip],\n });\n // Keep _tracks in sync so public API and track controls reflect the clip\n const desc = host._tracks.get(trackId);\n if (desc) {\n const sr = host.effectiveSampleRate;\n const clipDesc: ClipDescriptor = {\n kind: 'drop',\n src: '',\n peaksSrc: '',\n start: startSample / sr,\n duration: durSamples / sr,\n offset: 0,\n gain: 1,\n name: 'Recording',\n fadeIn: 0,\n fadeOut: 0,\n fadeType: 'linear',\n midiNotes: null,\n midiChannel: null,\n midiProgram: null,\n };\n host._tracks = new Map(host._tracks).set(trackId, {\n ...desc,\n clips: [...desc.clips, clipDesc],\n });\n }\n host._recomputeDuration();\n const updatedTrack = host._engineTracks.get(trackId);\n if (host._engine?.updateTrack && updatedTrack) {\n host._engine.updateTrack(trackId, updatedTrack);\n } else {\n host._engine?.setTracks([...host._engineTracks.values()]);\n }\n })\n .catch((err) => {\n console.warn('[dawcore] Failed to generate peaks for recorded clip: ' + String(err));\n const next = new Map(host._clipBuffers);\n next.delete(clip.id);\n host._clipBuffers = next;\n if (host.isConnected) {\n host.dispatchEvent(\n new CustomEvent<DawErrorDetail>('daw-error', {\n bubbles: true,\n composed: true,\n detail: { operation: 'recording-peaks', error: err },\n })\n );\n }\n });\n}\n","import type { AudioClip, ClipTrack } from '@waveform-playlist/core';\nimport type { DawClipSplitDetail } from '../events';\n\n// ---------------------------------------------------------------------------\n// Contracts\n// ---------------------------------------------------------------------------\n\n/** Narrow engine contract for split operations. */\nexport interface SplitEngineContract {\n getState(): { selectedTrackId: string | null; tracks: ClipTrack[] };\n splitClip(trackId: string, clipId: string, atSample: number): void;\n}\n\n/** Host interface for splitAtPlayhead. */\nexport interface SplitHost {\n readonly effectiveSampleRate: number;\n readonly currentTime: number;\n readonly isPlaying: boolean;\n readonly engine: SplitEngineContract | null;\n dispatchEvent(event: Event): boolean;\n stop(): void;\n play(time: number): void;\n}\n\n// ---------------------------------------------------------------------------\n// splitAtPlayhead\n// ---------------------------------------------------------------------------\n\n/**\n * Split the clip under the playhead on the selected track.\n * Stops playback before split and resumes after to avoid duplicate audio\n * from Transport rescheduling during playback.\n *\n * Returns true if the split occurred and dispatched a daw-clip-split event.\n * Returns false for any guard failure or engine no-op.\n */\nexport function splitAtPlayhead(host: SplitHost): boolean {\n const wasPlaying = host.isPlaying;\n const time = host.currentTime;\n\n // Check guards before stopping playback — don't interrupt audio for a no-op\n if (!canSplitAtTime(host, time)) return false;\n\n if (wasPlaying) {\n host.stop();\n }\n\n let result: boolean;\n try {\n result = performSplit(host, time);\n } catch (err) {\n console.warn('[dawcore] splitAtPlayhead failed: ' + String(err));\n result = false;\n }\n\n // Always resume if was playing — even on failed split, don't leave audio stopped\n if (wasPlaying) {\n host.play(time);\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Internal\n// ---------------------------------------------------------------------------\n\n/** Pre-flight check — can a split happen at this time without touching playback? */\nfunction canSplitAtTime(host: SplitHost, time: number): boolean {\n const { engine } = host;\n if (!engine) return false;\n\n const state = engine.getState();\n if (!state.selectedTrackId) return false;\n\n const track = state.tracks.find((t) => t.id === state.selectedTrackId);\n if (!track) return false;\n\n const atSample = Math.round(time * host.effectiveSampleRate);\n const clip = findClipAtSample(track.clips, atSample);\n if (!clip) return false;\n\n // MIDI clip splitting requires note slicing — not implemented yet.\n if (clip.midiNotes != null) return false;\n\n return true;\n}\n\n/** Core split logic — finds clip at position, calls engine, diffs state for new IDs. */\nfunction performSplit(host: SplitHost, time: number): boolean {\n const { engine } = host;\n if (!engine) return false;\n\n const stateBefore = engine.getState();\n const { selectedTrackId, tracks } = stateBefore;\n\n if (!selectedTrackId) return false;\n\n const track = tracks.find((t) => t.id === selectedTrackId);\n if (!track) return false;\n\n const atSample = Math.round(time * host.effectiveSampleRate);\n\n const clip = findClipAtSample(track.clips, atSample);\n if (!clip) return false;\n\n const originalClipId = clip.id;\n const clipIdsBefore = new Set(track.clips.map((c) => c.id));\n\n engine.splitClip(selectedTrackId, originalClipId, atSample);\n\n const stateAfter = engine.getState();\n const trackAfter = stateAfter.tracks.find((t) => t.id === selectedTrackId);\n if (!trackAfter) {\n console.warn(\n '[dawcore] splitAtPlayhead: track \"' + selectedTrackId + '\" disappeared after split'\n );\n return false;\n }\n\n // Engine replaces the original clip with two halves; both get new IDs\n const newClips = trackAfter.clips.filter((c) => !clipIdsBefore.has(c.id));\n if (newClips.length !== 2) {\n if (newClips.length > 0) {\n console.warn(\n '[dawcore] splitAtPlayhead: expected 2 new clips after split but got ' + newClips.length\n );\n }\n return false;\n }\n\n // Sort by startSample: lower = left, higher = right\n const sorted = [...newClips].sort((a, b) => a.startSample - b.startSample);\n const leftClipId = sorted[0].id;\n const rightClipId = sorted[1].id;\n\n host.dispatchEvent(\n new CustomEvent<DawClipSplitDetail>('daw-clip-split', {\n bubbles: true,\n composed: true,\n detail: {\n trackId: selectedTrackId,\n originalClipId,\n leftClipId,\n rightClipId,\n },\n })\n );\n\n return true;\n}\n\n/**\n * Finds a clip that strictly contains the given sample position.\n * The position must be > clip.startSample and < clip.startSample + clip.durationSamples.\n */\nfunction findClipAtSample(clips: AudioClip[], atSample: number): AudioClip | undefined {\n return clips.find(\n (c) => atSample > c.startSample && atSample < c.startSample + c.durationSamples\n );\n}\n","import type { AudioClip, ClipTrack, PeakData } from '@waveform-playlist/core';\nimport type { PeakPipeline } from '../workers/peakPipeline';\n\n/** Host interface for clip peak synchronization. */\nexport interface ClipPeakSyncHost {\n /** The effective samplesPerPixel for rendering (tick-derived in beats mode). */\n readonly renderSamplesPerPixel: number;\n readonly mono: boolean;\n _clipBuffers: Map<string, AudioBuffer>;\n _clipOffsets: Map<string, { offsetSamples: number; durationSamples: number }>;\n _peaksData: Map<string, PeakData>;\n _peakPipeline: PeakPipeline;\n}\n\n/**\n * Regenerate peaks for clips that are new or whose offset/duration changed.\n * Handles split (new clip IDs) and trim (same ID, changed bounds).\n *\n * Called from the statechange handler when tracksVersion changes.\n */\nexport function syncPeaksForChangedClips(host: ClipPeakSyncHost, tracks: ClipTrack[]): void {\n // Collect all current clip IDs for orphan detection\n const currentClipIds = new Set<string>();\n\n for (const track of tracks) {\n for (const clip of track.clips) {\n currentClipIds.add(clip.id);\n\n // Check if peaks need regeneration: new clip or changed offset/duration\n const cached = host._clipOffsets.get(clip.id);\n const needsPeaks =\n !host._peaksData.has(clip.id) ||\n !cached ||\n cached.offsetSamples !== clip.offsetSamples ||\n cached.durationSamples !== clip.durationSamples;\n\n if (!needsPeaks) continue;\n\n const audioBuffer =\n clip.audioBuffer ??\n host._clipBuffers.get(clip.id) ??\n findAudioBufferForClip(host, clip, track);\n if (!audioBuffer) {\n console.warn(\n '[dawcore] syncPeaksForChangedClips: no AudioBuffer for clip ' +\n clip.id +\n ' — waveform will be blank'\n );\n continue;\n }\n\n // Update cached state\n host._clipBuffers = new Map(host._clipBuffers).set(clip.id, audioBuffer);\n host._clipOffsets = new Map(host._clipOffsets).set(clip.id, {\n offsetSamples: clip.offsetSamples,\n durationSamples: clip.durationSamples,\n });\n\n // Generate peaks asynchronously\n host._peakPipeline\n .generatePeaks(\n audioBuffer,\n host.renderSamplesPerPixel,\n host.mono,\n clip.offsetSamples,\n clip.durationSamples\n )\n .then((peakData) => {\n host._peaksData = new Map(host._peaksData).set(clip.id, peakData);\n })\n .catch((err) => {\n console.warn(\n '[dawcore] Failed to generate peaks for clip ' + clip.id + ': ' + String(err)\n );\n });\n }\n }\n\n // Clean up orphaned entries for clip IDs no longer in any track\n // (e.g., the original clip after a split is replaced by two new clips)\n cleanupOrphanedClipData(host, currentClipIds);\n}\n\n/**\n * Remove entries from per-clip Maps for clip IDs that no longer exist in any track.\n * Prevents memory leaks from orphaned AudioBuffer references after split operations.\n */\nfunction cleanupOrphanedClipData(host: ClipPeakSyncHost, currentClipIds: Set<string>): void {\n let buffersChanged = false;\n let peaksChanged = false;\n\n for (const id of host._clipBuffers.keys()) {\n if (!currentClipIds.has(id)) {\n host._clipBuffers.delete(id);\n buffersChanged = true;\n }\n }\n let offsetsChanged = false;\n for (const id of host._clipOffsets.keys()) {\n if (!currentClipIds.has(id)) {\n host._clipOffsets.delete(id);\n offsetsChanged = true;\n }\n }\n for (const id of host._peaksData.keys()) {\n if (!currentClipIds.has(id)) {\n host._peaksData.delete(id);\n peaksChanged = true;\n }\n }\n\n // Reassign Maps that changed — _peaksData is @state() (triggers Lit re-render),\n // _clipBuffers uses reference identity for change detection in syncPeaksForChangedClips\n if (buffersChanged) {\n host._clipBuffers = new Map(host._clipBuffers);\n }\n if (offsetsChanged) {\n host._clipOffsets = new Map(host._clipOffsets);\n }\n if (peaksChanged) {\n host._peaksData = new Map(host._peaksData);\n }\n}\n\n/** Find an AudioBuffer for a clip by checking siblings on the same track. */\nfunction findAudioBufferForClip(\n host: ClipPeakSyncHost,\n clip: AudioClip,\n track: ClipTrack\n): AudioBuffer | null {\n for (const sibling of track.clips) {\n if (sibling.id === clip.id) continue;\n const buf = host._clipBuffers.get(sibling.id);\n if (buf) return buf;\n }\n return null;\n}\n","/**\n * Load pre-computed waveform data from a .dat or .json file (BBC audiowaveform format).\n */\n\nimport WaveformData from 'waveform-data';\n\n/**\n * Fetch and parse a waveform data file (.dat binary or .json).\n */\nexport async function loadWaveformDataFromUrl(src: string): Promise<WaveformData> {\n const response = await fetch(src);\n\n if (!response.ok) {\n throw new Error('[dawcore] Failed to fetch peaks data: ' + response.statusText);\n }\n\n // Detect binary format from pathname (ignores query string and fragment)\n const { pathname } = new URL(src, globalThis.location?.href ?? 'http://localhost');\n const isBinary = pathname.toLowerCase().endsWith('.dat');\n\n if (isBinary) {\n const arrayBuffer = await response.arrayBuffer();\n return WaveformData.create(arrayBuffer);\n } else {\n const json = await response.json();\n return WaveformData.create(json);\n }\n}\n","import type { ReactiveController, ReactiveControllerHost } from 'lit';\n\n// Line scrolling uses 16px per line (standard line height).\nconst LINE_HEIGHT_PX = 16;\n\n/**\n * Frozen-panes scroll sync. The editor's `.scroll-area` owns both scroll\n * axes; this controller keeps the ruler band (x) and controls column (y)\n * visually locked to it by applying translate3d transforms on EVERY scroll\n * event. (ViewportController's 100px threshold exists for chunk\n * virtualization and is too coarse for visual sync.)\n *\n * Also forwards wheel events from ALL elements matched by `wheelForwardSelector`\n * to the scroll container for both axes:\n * - Horizontal scrub (deltaX) — ruler band stays in sync when the user\n * scrolls the timeline left/right while hovering the ruler.\n * - Vertical scroll (deltaY) — controls column lets the user scroll track\n * rows while hovering the track controls.\n *\n * preventDefault fires only when at least one axis actually moved, so page\n * scrolling is unaffected for unconstrained editors and boundary scrolling\n * chains properly.\n */\nexport class ScrollSyncController implements ReactiveController {\n private _host: ReactiveControllerHost & HTMLElement;\n private _scrollContainer: HTMLElement | null = null;\n private _wheelTargets: Set<HTMLElement> = new Set();\n private _warnedX = false;\n private _warnedY = false;\n\n /** Selector (in host shadow DOM) for the scroll container. */\n scrollSelector = '';\n /** Selector for the element receiving translate3d(-scrollLeft, 0, 0). */\n xTargetSelector = '';\n /** Selector for the element receiving translate3d(0, -scrollTop, 0). */\n yTargetSelector = '';\n /**\n * Selector (or comma-separated selectors) for elements whose wheel events\n * forward to the scroll container. All matching elements receive listeners.\n */\n wheelForwardSelector = '';\n\n constructor(host: ReactiveControllerHost & HTMLElement) {\n this._host = host;\n host.addController(this);\n }\n\n hostConnected() {\n // Defer so the Shadow DOM renders before querying (same pattern as\n // ViewportController).\n requestAnimationFrame(() => {\n if (!this._host.isConnected) return;\n this._attach();\n if (!this._scrollContainer && this.scrollSelector) {\n console.warn(\n '[dawcore] ScrollSyncController: scroll container not found for \"' +\n this.scrollSelector +\n '\"'\n );\n }\n });\n }\n\n hostDisconnected() {\n this._scrollContainer?.removeEventListener('scroll', this._onScroll);\n this._scrollContainer = null;\n for (const target of this._wheelTargets) {\n target.removeEventListener('wheel', this._onWheel);\n }\n this._wheelTargets.clear();\n }\n\n /**\n * Re-attach and re-apply transforms from the current scroll position.\n * Called from the host's updated() so elements created by a re-render\n * (e.g. the ruler appearing when the first track loads) pick up the\n * current offset and listeners.\n */\n sync() {\n this._attach();\n }\n\n private _query(selector: string): HTMLElement | null {\n return selector ? (this._host.shadowRoot?.querySelector(selector) as HTMLElement | null) : null;\n }\n\n private _queryAll(selector: string): HTMLElement[] {\n if (!selector) return [];\n return Array.from(this._host.shadowRoot?.querySelectorAll(selector) ?? []) as HTMLElement[];\n }\n\n private _attach() {\n const container = this._query(this.scrollSelector);\n if (!container) {\n if (this._scrollContainer && !this._scrollContainer.isConnected) {\n console.warn(\n '[dawcore] ScrollSyncController: scroll container \"' +\n this.scrollSelector +\n '\" was removed from the DOM — detaching listeners until it reappears.'\n );\n this._scrollContainer.removeEventListener('scroll', this._onScroll);\n this._scrollContainer = null;\n for (const t of this._wheelTargets) t.removeEventListener('wheel', this._onWheel);\n this._wheelTargets.clear();\n }\n return;\n }\n if (container !== this._scrollContainer) {\n this._scrollContainer?.removeEventListener('scroll', this._onScroll);\n this._scrollContainer = container;\n container.addEventListener('scroll', this._onScroll, { passive: true });\n }\n\n // Diff the current set of matched wheel targets.\n const nextTargets = new Set(this._queryAll(this.wheelForwardSelector));\n for (const old of this._wheelTargets) {\n if (!nextTargets.has(old)) {\n old.removeEventListener('wheel', this._onWheel);\n this._wheelTargets.delete(old);\n }\n }\n for (const next of nextTargets) {\n if (!this._wheelTargets.has(next)) {\n next.addEventListener('wheel', this._onWheel, { passive: false });\n this._wheelTargets.add(next);\n }\n }\n\n this._apply();\n }\n\n private _onScroll = () => {\n this._apply();\n };\n\n private _onWheel = (e: WheelEvent) => {\n const sc = this._scrollContainer;\n if (!sc) return;\n\n const scale =\n e.deltaMode === WheelEvent.DOM_DELTA_LINE\n ? LINE_HEIGHT_PX\n : e.deltaMode === WheelEvent.DOM_DELTA_PAGE\n ? sc.clientHeight // used for Y; X uses clientWidth below\n : 1;\n\n const scaleX = e.deltaMode === WheelEvent.DOM_DELTA_PAGE ? sc.clientWidth : scale;\n\n const beforeLeft = sc.scrollLeft;\n const beforeTop = sc.scrollTop;\n\n sc.scrollLeft += e.deltaX * scaleX;\n sc.scrollTop += e.deltaY * scale;\n\n if (sc.scrollLeft !== beforeLeft || sc.scrollTop !== beforeTop) {\n e.preventDefault();\n }\n };\n\n private _apply() {\n const sc = this._scrollContainer;\n if (!sc) return;\n // Re-query targets each time: Lit conditional templates create/replace\n // these elements between renders (e.g. the header row appears with the\n // first loaded track).\n const xTarget = this._query(this.xTargetSelector);\n if (xTarget) {\n xTarget.style.transform = `translate3d(${-sc.scrollLeft}px, 0, 0)`;\n this._warnedX = false;\n } else if (this.xTargetSelector && sc.scrollLeft !== 0 && !this._warnedX) {\n this._warnedX = true;\n console.warn(\n '[dawcore] ScrollSyncController: x target \"' +\n this.xTargetSelector +\n '\" not found while scrolled — the synced pane will appear frozen. Check the selector, or clear it if the target is intentionally not rendered.'\n );\n }\n const yTarget = this._query(this.yTargetSelector);\n if (yTarget) {\n yTarget.style.transform = `translate3d(0, ${-sc.scrollTop}px, 0)`;\n this._warnedY = false;\n } else if (this.yTargetSelector && sc.scrollTop !== 0 && !this._warnedY) {\n this._warnedY = true;\n console.warn(\n '[dawcore] ScrollSyncController: y target \"' +\n this.yTargetSelector +\n '\" not found while scrolled — the synced pane will appear frozen. Check the selector, or clear it if the target is intentionally not rendered.'\n );\n }\n }\n}\n","import { LitElement, html, css } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\n\n@customElement('daw-selection')\nexport class DawSelectionElement extends LitElement {\n @property({ type: Number, attribute: false }) startPx = 0;\n @property({ type: Number, attribute: false }) endPx = 0;\n\n static styles = css`\n :host {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n pointer-events: none;\n z-index: 5;\n }\n div {\n position: absolute;\n top: 0;\n bottom: 0;\n background: var(--daw-selection-color, rgba(99, 199, 95, 0.3));\n }\n `;\n\n render() {\n const left = Math.min(this.startPx, this.endPx);\n const width = Math.abs(this.endPx - this.startPx);\n if (width === 0) return html``;\n return html`<div style=\"left: ${left}px; width: ${width}px;\"></div>`;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-selection': DawSelectionElement;\n }\n}\n","import { html, css } from 'lit';\nimport { customElement, state } from 'lit/decorators.js';\nimport { DawTransportButton } from './daw-transport-button';\n\n@customElement('daw-record-button')\nexport class DawRecordButtonElement extends DawTransportButton {\n @state() private _isRecording = false;\n private _targetRef: HTMLElement | null = null;\n private _onStart = () => {\n this._isRecording = true;\n };\n private _onComplete = () => {\n this._isRecording = false;\n };\n private _onError = () => {\n this._isRecording = false;\n };\n\n static override styles = [\n DawTransportButton.styles,\n css`\n button[data-recording] {\n color: #d08070;\n border-color: #d08070;\n background: rgba(208, 128, 112, 0.15);\n }\n `,\n ];\n\n connectedCallback() {\n super.connectedCallback();\n // Defer so <daw-transport for=\"...\"> and the target editor are resolved\n requestAnimationFrame(() => this._listenToTarget());\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this._cleanupListeners();\n }\n\n private _listenToTarget() {\n const target = this.target;\n if (!target) return;\n this._targetRef = target;\n target.addEventListener('daw-recording-start', this._onStart);\n target.addEventListener('daw-recording-complete', this._onComplete);\n target.addEventListener('daw-recording-error', this._onError);\n }\n\n private _cleanupListeners() {\n if (this._targetRef) {\n this._targetRef.removeEventListener('daw-recording-start', this._onStart);\n this._targetRef.removeEventListener('daw-recording-complete', this._onComplete);\n this._targetRef.removeEventListener('daw-recording-error', this._onError);\n this._targetRef = null;\n }\n }\n\n render() {\n return html`\n <button part=\"button\" ?data-recording=${this._isRecording} @click=${this._onClick}>\n <slot>Record</slot>\n </button>\n `;\n }\n\n private _onClick() {\n // Start-only — stop is handled by the stop button\n if (this._isRecording) return;\n const target = this.target;\n if (!target) {\n console.warn(\n '[dawcore] <daw-record-button> has no target. Check <daw-transport for=\"...\"> references a valid <daw-editor> id.'\n );\n return;\n }\n target.startRecording(target.recordingStream);\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-record-button': DawRecordButtonElement;\n }\n}\n","import { LitElement } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { handleKeyboardEvent } from '@waveform-playlist/core';\nimport type { KeyboardShortcut } from '@waveform-playlist/core';\nimport type { DawEditorElement } from './daw-editor';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Key binding for remapping — derived from KeyboardShortcut to stay in sync. */\nexport type KeyBinding = Pick<\n KeyboardShortcut,\n 'key' | 'ctrlKey' | 'shiftKey' | 'metaKey' | 'altKey'\n>;\n\nexport interface PlaybackShortcutMap {\n playPause?: KeyBinding;\n stop?: KeyBinding;\n rewindToStart?: KeyBinding;\n}\n\nexport interface SplittingShortcutMap {\n splitAtPlayhead?: KeyBinding;\n}\n\nexport interface UndoShortcutMap {\n undo?: KeyBinding;\n redo?: KeyBinding;\n}\n\n// ---------------------------------------------------------------------------\n// Element\n// ---------------------------------------------------------------------------\n\n/**\n * Render-less element that enables keyboard shortcuts for a parent <daw-editor>.\n * Place inside the editor element. Boolean attributes enable preset categories;\n * JS properties allow remapping and custom shortcuts.\n *\n * ```html\n * <daw-editor>\n * <daw-keyboard-shortcuts playback splitting undo></daw-keyboard-shortcuts>\n * </daw-editor>\n * ```\n */\n@customElement('daw-keyboard-shortcuts')\nexport class DawKeyboardShortcutsElement extends LitElement {\n // --- Preset attributes ---\n @property({ type: Boolean }) playback = false;\n @property({ type: Boolean }) splitting = false;\n @property({ type: Boolean }) undo = false;\n\n // --- JS properties for remapping ---\n playbackShortcuts: PlaybackShortcutMap | null = null;\n splittingShortcuts: SplittingShortcutMap | null = null;\n undoShortcuts: UndoShortcutMap | null = null;\n\n /** Additional custom shortcuts. */\n customShortcuts: KeyboardShortcut[] = [];\n\n private _editor: DawEditorElement | null = null;\n private _cachedShortcuts: KeyboardShortcut[] | null = null;\n\n /** All active shortcuts (read-only, cached). */\n get shortcuts(): KeyboardShortcut[] {\n if (!this._cachedShortcuts) {\n this._cachedShortcuts = this._buildShortcuts();\n }\n return this._cachedShortcuts;\n }\n\n /** Invalidate cached shortcuts when Lit properties change. */\n override updated(): void {\n this._cachedShortcuts = null;\n }\n\n // --- Lifecycle ---\n\n override connectedCallback(): void {\n super.connectedCallback();\n this._editor = this.closest('daw-editor') as DawEditorElement | null;\n if (!this._editor) {\n console.warn(\n '[dawcore] <daw-keyboard-shortcuts> must be placed inside a <daw-editor>. ' +\n 'Preset shortcuts (playback, splitting, undo) will be inactive; only customShortcuts will fire.'\n );\n }\n document.addEventListener('keydown', this._onKeyDown);\n }\n\n override disconnectedCallback(): void {\n super.disconnectedCallback();\n document.removeEventListener('keydown', this._onKeyDown);\n this._editor = null;\n }\n\n // No shadow DOM — render-less element\n override createRenderRoot(): this {\n return this;\n }\n\n // --- Shortcut building ---\n\n private _buildShortcuts(): KeyboardShortcut[] {\n const editor = this._editor;\n if (!editor) return this.customShortcuts;\n\n const result: KeyboardShortcut[] = [];\n\n if (this.playback) {\n const map = this.playbackShortcuts;\n // Explicit ctrlKey/metaKey: false prevents Cmd+Space (Spotlight on Mac)\n // and Ctrl+0 etc. from triggering playback shortcuts.\n result.push(\n this._makeShortcut(\n map?.playPause ?? { key: ' ', ctrlKey: false, metaKey: false },\n () => editor.togglePlayPause(),\n 'Play/Pause'\n ),\n this._makeShortcut(\n map?.stop ?? { key: 'Escape', ctrlKey: false, metaKey: false },\n () => editor.stop(),\n 'Stop'\n ),\n this._makeShortcut(\n map?.rewindToStart ?? { key: '0', ctrlKey: false, metaKey: false },\n () => editor.seekTo(0),\n 'Rewind to start'\n )\n );\n }\n\n if (this.splitting) {\n const map = this.splittingShortcuts;\n const binding = map?.splitAtPlayhead ?? {\n key: 's',\n ctrlKey: false,\n metaKey: false,\n altKey: false,\n };\n result.push(this._makeShortcut(binding, () => editor.splitAtPlayhead(), 'Split at playhead'));\n }\n\n if (this.undo) {\n const map = this.undoShortcuts;\n const undoBinding = map?.undo ?? { key: 'z' };\n const redoBinding = map?.redo ?? { key: 'z', shiftKey: true };\n\n // Undo: Ctrl+Z (Win/Linux) and Cmd+Z (Mac)\n // Use === undefined to distinguish \"not specified\" (auto-expand) from\n // \"explicitly false\" (user wants no modifier — respect their intent).\n if (undoBinding.ctrlKey === undefined && undoBinding.metaKey === undefined) {\n // Only set shiftKey: false when user didn't provide a value\n const undoShift = undoBinding.shiftKey === undefined ? { shiftKey: false } : {};\n result.push(\n this._makeShortcut(\n { ...undoBinding, ctrlKey: true, ...undoShift },\n () => editor.undo(),\n 'Undo'\n ),\n this._makeShortcut(\n { ...undoBinding, metaKey: true, ...undoShift },\n () => editor.undo(),\n 'Undo'\n )\n );\n } else {\n result.push(this._makeShortcut(undoBinding, () => editor.undo(), 'Undo'));\n }\n\n // Redo: Ctrl+Shift+Z (Win/Linux) and Cmd+Shift+Z (Mac)\n if (redoBinding.ctrlKey === undefined && redoBinding.metaKey === undefined) {\n const redoShift = redoBinding.shiftKey === undefined ? { shiftKey: true } : {};\n result.push(\n this._makeShortcut(\n { ...redoBinding, ctrlKey: true, ...redoShift },\n () => editor.redo(),\n 'Redo'\n ),\n this._makeShortcut(\n { ...redoBinding, metaKey: true, ...redoShift },\n () => editor.redo(),\n 'Redo'\n )\n );\n } else {\n result.push(this._makeShortcut(redoBinding, () => editor.redo(), 'Redo'));\n }\n }\n\n result.push(...this.customShortcuts);\n return result;\n }\n\n private _makeShortcut(\n binding: KeyBinding,\n action: () => void,\n description: string\n ): KeyboardShortcut {\n return {\n key: binding.key,\n ...(binding.ctrlKey !== undefined && { ctrlKey: binding.ctrlKey }),\n ...(binding.shiftKey !== undefined && { shiftKey: binding.shiftKey }),\n ...(binding.metaKey !== undefined && { metaKey: binding.metaKey }),\n ...(binding.altKey !== undefined && { altKey: binding.altKey }),\n action,\n description,\n };\n }\n\n // --- Event handler ---\n\n private _onKeyDown = (e: KeyboardEvent) => {\n const shortcuts = this.shortcuts;\n if (shortcuts.length === 0) return;\n try {\n handleKeyboardEvent(e, shortcuts, true);\n } catch (err) {\n console.warn('[dawcore] Keyboard shortcut failed (key=' + e.key + '): ' + String(err));\n const target = this._editor ?? this;\n target.dispatchEvent(\n new CustomEvent('daw-error', {\n bubbles: true,\n composed: true,\n detail: { operation: 'keyboard-shortcut', key: e.key, error: err },\n })\n );\n }\n };\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-keyboard-shortcuts': DawKeyboardShortcutsElement;\n }\n}\n","import { LitElement, html, css } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport type { PropertyValues } from 'lit';\n\nconst MAX_CANVAS_WIDTH = 1000;\n\ninterface SpectrogramHost extends HTMLElement {\n _spectrogramRegisterCanvas?: (reg: {\n canvasId: string;\n canvas: OffscreenCanvas;\n clipId: string;\n trackId: string;\n channelIndex: number;\n chunkIndex: number;\n globalPixelOffset: number;\n widthPx: number;\n heightPx: number;\n }) => void;\n _spectrogramUnregisterCanvas?: (canvasId: string) => void;\n}\n\n@customElement('daw-spectrogram')\nexport class DawSpectrogramElement extends LitElement {\n @property({ attribute: false }) clipId = '';\n @property({ attribute: false }) trackId = '';\n @property({ type: Number, attribute: false }) channelIndex = 0;\n @property({ type: Number, attribute: false }) length = 0;\n @property({ type: Number, attribute: false }) waveHeight = 128;\n\n @property({ type: Number, attribute: false, noAccessor: true })\n get samplesPerPixel(): number {\n return this._samplesPerPixel;\n }\n set samplesPerPixel(value: number) {\n if (!Number.isFinite(value) || value <= 0) {\n console.warn('[dawcore] daw-spectrogram samplesPerPixel ' + value + ' is invalid — ignored');\n return;\n }\n const old = this._samplesPerPixel;\n this._samplesPerPixel = value;\n this.requestUpdate('samplesPerPixel', old);\n }\n private _samplesPerPixel = 1024;\n\n @property({ type: Number, attribute: false, noAccessor: true })\n get sampleRate(): number {\n return this._sampleRate;\n }\n set sampleRate(value: number) {\n if (!Number.isFinite(value) || value <= 0) {\n console.warn('[dawcore] daw-spectrogram sampleRate ' + value + ' is invalid — ignored');\n return;\n }\n const old = this._sampleRate;\n this._sampleRate = value;\n this.requestUpdate('sampleRate', old);\n }\n private _sampleRate = 44100;\n\n @property({ type: Number, attribute: false }) clipOffsetSeconds = 0;\n @property({ type: Number, attribute: false }) visibleStart = -Infinity;\n @property({ type: Number, attribute: false }) visibleEnd = Infinity;\n @property({ type: Number, attribute: false }) originX = 0;\n\n static styles = css`\n :host {\n display: block;\n position: relative;\n background: var(--daw-spectrogram-background, #000);\n }\n canvas {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n pointer-events: none;\n }\n `;\n\n private _canvases: HTMLCanvasElement[] = [];\n private _registeredCanvasIds: string[] = [];\n private _warnedNoHost = false;\n\n /**\n * Walk up to the editor host. `closest('daw-editor')` does NOT cross\n * shadow boundaries — and this element lives inside the editor's shadow\n * DOM — so use getRootNode().host to step out.\n */\n private _findHostEditor(): SpectrogramHost | null {\n const root = this.getRootNode();\n const host = root instanceof ShadowRoot ? root.host : null;\n if (!host) return null;\n if (host.tagName === 'DAW-EDITOR') return host as SpectrogramHost;\n return host.closest('daw-editor') as SpectrogramHost | null;\n }\n\n willUpdate(changed: PropertyValues): void {\n const layoutChanged =\n changed.has('length') ||\n changed.has('waveHeight') ||\n changed.has('samplesPerPixel') ||\n changed.has('clipId') ||\n changed.has('channelIndex');\n if (layoutChanged) {\n this._rebuildChunks();\n }\n }\n\n private _rebuildChunks(): void {\n this._unregisterAllCanvases();\n this._canvases = [];\n\n if (this.length <= 0) return;\n\n const chunkCount = Math.ceil(this.length / MAX_CANVAS_WIDTH);\n for (let i = 0; i < chunkCount; i++) {\n const widthPx = Math.min(MAX_CANVAS_WIDTH, this.length - i * MAX_CANVAS_WIDTH);\n const canvas = document.createElement('canvas');\n canvas.style.left = i * MAX_CANVAS_WIDTH + 'px';\n canvas.style.width = widthPx + 'px';\n const dpr = window.devicePixelRatio || 1;\n canvas.width = widthPx * dpr;\n canvas.height = this.waveHeight * dpr;\n this._canvases.push(canvas);\n }\n }\n\n protected updated(_changed: PropertyValues): void {\n if (this._registeredCanvasIds.length === 0 && this._canvases.length > 0) {\n requestAnimationFrame(() => this._registerCanvases());\n }\n }\n\n private _registerCanvases(): void {\n const editor = this._findHostEditor();\n if (!editor || typeof editor._spectrogramRegisterCanvas !== 'function') {\n if (!this._warnedNoHost) {\n this._warnedNoHost = true;\n console.warn(\n '[dawcore] <daw-spectrogram> (clip ' +\n this.clipId +\n ') could not find host <daw-editor>. Canvases will not render. ' +\n 'Ensure the element is mounted inside a <daw-editor>.'\n );\n }\n return;\n }\n\n for (let i = 0; i < this._canvases.length; i++) {\n const canvas = this._canvases[i];\n const canvasId = this.clipId + '-ch' + this.channelIndex + '-chunk' + i;\n let offscreen: OffscreenCanvas;\n try {\n offscreen = canvas.transferControlToOffscreen();\n } catch (err) {\n console.warn(\n '[dawcore] daw-spectrogram transferControlToOffscreen failed for ' +\n canvasId +\n ': ' +\n (err instanceof Error ? err.message : String(err))\n );\n continue;\n }\n editor._spectrogramRegisterCanvas({\n canvasId,\n canvas: offscreen,\n clipId: this.clipId,\n trackId: this.trackId,\n channelIndex: this.channelIndex,\n chunkIndex: i,\n globalPixelOffset: this.originX + i * MAX_CANVAS_WIDTH,\n widthPx: parseFloat(canvas.style.width),\n heightPx: this.waveHeight,\n });\n this._registeredCanvasIds.push(canvasId);\n }\n }\n\n private _unregisterAllCanvases(): void {\n const editor = this._findHostEditor();\n if (editor && typeof editor._spectrogramUnregisterCanvas === 'function') {\n for (const id of this._registeredCanvasIds) {\n editor._spectrogramUnregisterCanvas(id);\n }\n }\n this._registeredCanvasIds = [];\n }\n\n disconnectedCallback(): void {\n super.disconnectedCallback();\n this._unregisterAllCanvases();\n }\n\n render() {\n return html`${this._canvases.map((c) => c)}`;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-spectrogram': DawSpectrogramElement;\n }\n}\n"],"mappings":"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","names":["LitElement","customElement","property","LitElement","property","customElement","LitElement","customElement","property","LitElement","property","customElement","LitElement","html","css","customElement","property","MAX_CANVAS_WIDTH","LAYOUT_PROPS","LitElement","html","css","property","customElement","LitElement","html","css","customElement","LitElement","html","css","customElement","LitElement","customElement","property","LitElement","property","customElement","html","customElement","LitElement","css","html","customElement","html","css","customElement","state","html","css","state","customElement","html","customElement","html","customElement","LitElement","html","css","customElement","property","state","createClip","createTrack","bits","numChannels","LitElement","html","css","customElement","property","MAX_CANVAS_WIDTH","LitElement","html","css","property","customElement","LitElement","html","css","customElement","property","LitElement","html","css","property","customElement","LitElement","html","css","customElement","property","MAX_CANVAS_WIDTH","LitElement","html","css","property","customElement","css","state","snapTickToGrid","snapTickToGrid","engine","createClip","state","WaveformData","LitElement","createClip","updatedClip","updatedClips","updatedTrack","clip","createTrack","audioBuffer","html","css","property","state","customElement","LitElement","html","css","customElement","property","LitElement","html","css","property","customElement","html","css","customElement","state","html","css","state","customElement","LitElement","customElement","property","LitElement","property","customElement","LitElement","html","css","customElement","property","MAX_CANVAS_WIDTH","LitElement","html","css","property","customElement"]}