@dawcore/components 0.0.4 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +170 -0
- package/dist/index.d.mts +18 -4
- package/dist/index.d.ts +18 -4
- package/dist/index.js +81 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +87 -33
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -7
package/dist/index.mjs.map
CHANGED
|
@@ -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-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/workers/peaksWorker.ts","../src/workers/waveformDataUtils.ts","../src/workers/peakPipeline.ts","../src/elements/daw-track-controls.ts","../src/styles/theme.ts","../src/controllers/viewport-controller.ts","../src/controllers/audio-resume-controller.ts","../src/controllers/recording-controller.ts","../src/interactions/pointer-handler.ts","../src/interactions/constants.ts","../src/interactions/clip-pointer-handler.ts","../src/interactions/file-loader.ts","../src/interactions/recording-clip.ts","../src/interactions/split-handler.ts","../src/interactions/clip-peak-sync.ts","../src/interactions/peaks-loader.ts","../src/elements/daw-ruler.ts","../src/utils/time-format.ts","../src/utils/smart-scale.ts","../src/elements/daw-selection.ts","../src/elements/daw-record-button.ts","../src/elements/daw-keyboard-shortcuts.ts"],"sourcesContent":["import { LitElement } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\n\n@customElement('daw-clip')\nexport class DawClipElement extends LitElement {\n @property() src = '';\n @property({ attribute: 'peaks-src' }) peaksSrc = '';\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 readonly clipId = crypto.randomUUID();\n\n // Light DOM — no visual rendering, just a data container\n createRenderRoot() {\n return this;\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';\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 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 = ['volume', 'pan', 'muted', 'soloed', 'src', 'name'];\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\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']);\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\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 const dirtyByChunk = groupDirtyByChunk(this._dirtyPixels, step);\n\n this._drawnChunks.clear();\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 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 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 } 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\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\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 });\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\" ?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 // During recording: toggle pause/resume of both worklet and playback (Audacity-style)\n if (this._isPaused) {\n target.resumeRecording();\n target.play(target.currentTime);\n this._isPaused = false;\n } else {\n target.pauseRecording();\n target.pause();\n this._isPaused = true;\n }\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 // Stop recording first (also stops playback via the controller),\n // then stop playback if it was running independently\n if (target.isRecording) {\n target.stopRecording();\n }\n target.stop();\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 { ClipTrack, FadeType, Peaks, PeakData } from '@waveform-playlist/core';\nimport type { TrackDescriptor, ClipDescriptor } from '../types';\nimport { createClipFromSeconds, createTrack, clipPixelWidth } 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 { PlaylistEngine } from '@waveform-playlist/engine';\nimport '../elements/daw-track-controls';\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 { PointerHandler } from '../interactions/pointer-handler';\nimport { ClipPointerHandler } from '../interactions/clip-pointer-handler';\nimport type {\n DawSelectionDetail,\n DawTrackIdDetail,\n DawTrackErrorDetail,\n DawErrorDetail,\n LoadFilesResult,\n} from '../events';\nimport { loadFiles as loadFilesImpl } from '../interactions/file-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';\n\n@customElement('daw-editor')\nexport class DawEditorElement extends LitElement {\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 /** Initial sample rate hint. Overridden by decoded audio buffer's actual rate. */\n @property({ type: Number, attribute: 'sample-rate' }) sampleRate = 48000;\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 _engine: PlaylistEngine | null = null;\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 _clipPointer = new ClipPointerHandler(this);\n get _clipHandler() {\n return this.interactiveClips ? this._clipPointer : null;\n }\n get engine() {\n return this._engine;\n }\n /** Re-extract peaks for a clip at new offset/duration from cached WaveformData. */\n reextractClipPeaks(clipId: string, offsetSamples: number, durationSamples: number) {\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.samplesPerPixel,\n this.mono,\n singleClipOffsets\n );\n const peakData = result.get(clipId);\n if (!peakData) return null;\n return { data: peakData.data, length: peakData.length };\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\n static styles = [\n hostStyles,\n css`\n :host {\n display: flex;\n position: relative;\n background: var(--daw-background, #1a1a2e);\n overflow: hidden;\n }\n .controls-column {\n flex-shrink: 0;\n width: var(--daw-controls-width, 180px);\n }\n .scroll-area {\n flex: 1;\n overflow-x: auto;\n overflow-y: hidden;\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 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 .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 private get _totalWidth(): number {\n return Math.ceil((this._duration * this.effectiveSampleRate) / this.samplesPerPixel);\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 // Detect track 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 }\n const nested = node.querySelectorAll?.('daw-track');\n if (nested) {\n for (const track of nested) {\n this._onTrackRemoved((track as DawTrackElement).trackId);\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._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 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 with new samplesPerPixel if playing\n if (changedProperties.has('samplesPerPixel') && this._isPlaying) {\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, the file's scale is the limit — _clampScale handles either case.\n if (changedProperties.has('samplesPerPixel') && this._clipBuffers.size > 0) {\n const re = this._peakPipeline.reextractPeaks(\n this._clipBuffers,\n this.samplesPerPixel,\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 // --- 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 }\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 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 };\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 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 clips.push({\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 });\n } else {\n for (const clipEl of clipEls) {\n clips.push({\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 });\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 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 (!clipDesc.src) continue;\n\n // Start both fetches concurrently — await peaks first to render preview before audio decode\n const waveformDataPromise = clipDesc.peaksSrc ? this._fetchPeaks(clipDesc.peaksSrc) : null;\n const audioPromise = this._fetchAndDecode(clipDesc.src);\n\n // --- Peaks-first path: render waveform before audio decode completes ---\n // Separate try/catch for peaks so audio errors aren't misattributed\n let waveformData: any = null;\n if (waveformDataPromise) {\n try {\n waveformData = await waveformDataPromise;\n } catch (err) {\n console.warn(\n '[dawcore] Failed to load peaks from ' +\n clipDesc.peaksSrc +\n ': ' +\n String(err) +\n ' — falling back to AudioBuffer generation'\n );\n }\n }\n if (waveformData) {\n // Create clip from WaveformData metadata (no audioBuffer yet)\n const clip = createClipFromSeconds({\n waveformData,\n startTime: clipDesc.start,\n duration: clipDesc.duration || waveformData.duration,\n offset: clipDesc.offset,\n gain: clipDesc.gain,\n name: clipDesc.name,\n sampleRate: waveformData.sample_rate,\n sourceDuration: waveformData.duration,\n });\n const effectiveScale = Math.max(this.samplesPerPixel, 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 const audioBuffer = await audioPromise;\n this._resolvedSampleRate = audioBuffer.sampleRate;\n const 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 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 const peakData = await this._peakPipeline.generatePeaks(\n audioBuffer,\n this.samplesPerPixel,\n this.mono,\n clip.offsetSamples,\n clip.durationSamples\n );\n this._peaksData = new Map(this._peaksData).set(clip.id, peakData);\n clips.push(clip);\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 // 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 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 // decodeAudioData works while context is suspended (pre-gesture)\n const { getGlobalAudioContext } = await import('@waveform-playlist/playout');\n return getGlobalAudioContext().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 const [{ PlaylistEngine }, { createToneAdapter }] = await Promise.all([\n import('@waveform-playlist/engine'),\n import('@waveform-playlist/playout'),\n ]);\n\n const adapter = createToneAdapter();\n const engine = new PlaylistEngine({\n adapter,\n sampleRate: this.effectiveSampleRate,\n samplesPerPixel: this.samplesPerPixel,\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 syncPeaksForChangedClips(this, engineState.tracks);\n }\n });\n engine.on('timeupdate', (time: number) => {\n this._currentTime = time;\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 // --- Playback ---\n async play(startTime?: number) {\n try {\n const engine = await this._ensureEngine();\n // Always init — adapter awaits pending rebuild init if needed,\n // and Tone.start() is a no-op if already running.\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._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 // Tone.js 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 return this._currentTime;\n }\n get isRecording(): boolean {\n return this._recordingController.isRecording;\n }\n pauseRecording(): void {\n this._recordingController.pauseRecording();\n }\n resumeRecording(): void {\n this._recordingController.resumeRecording();\n }\n stopRecording(): void {\n 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 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 latencyPixels = Math.floor(rs.latencySamples / this.samplesPerPixel);\n const left = Math.floor(rs.startSample / this.samplesPerPixel);\n const w = Math.floor(audibleSamples / this.samplesPerPixel);\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 playhead.startAnimation(\n () => engine.getCurrentTime(),\n this.effectiveSampleRate,\n this.samplesPerPixel\n );\n }\n _stopPlayhead() {\n const playhead = this._getPlayhead();\n if (!playhead) return;\n playhead.stopAnimation(this._currentTime, this.effectiveSampleRate, this.samplesPerPixel);\n }\n private _getPlayhead(): DawPlayheadElement | null {\n return this.shadowRoot?.querySelector('daw-playhead') as DawPlayheadElement | null;\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 selStartPx = (this._selectionStartTime * sr) / this.samplesPerPixel;\n const selEndPx = (this._selectionEndTime * sr) / this.samplesPerPixel;\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 return html`\n ${orderedTracks.length > 0\n ? html`<div class=\"controls-column\">\n ${this.timescale ? html`<div style=\"height: 30px;\"></div>` : ''}\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 : ''}\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 ${orderedTracks.length > 0 && this.timescale\n ? html`<daw-ruler\n .samplesPerPixel=${this.samplesPerPixel}\n .sampleRate=${this.effectiveSampleRate}\n .duration=${this._duration}\n ></daw-ruler>`\n : ''}\n ${orderedTracks.length > 0\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 const width = clipPixelWidth(\n clip.startSample,\n clip.durationSamples,\n this.samplesPerPixel\n );\n const clipLeft = Math.floor(clip.startSample / this.samplesPerPixel);\n const channels: Peaks[] = 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 ${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 ></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 <slot></slot>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-editor': DawEditorElement;\n }\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 * 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';\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 }\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 `;\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 ×\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\">\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\">\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 { 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\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._scrollContainer = null;\n }\n\n private _attachScrollContainer(container: HTMLElement) {\n this._scrollContainer?.removeEventListener('scroll', this._onScroll);\n this._scrollContainer = container;\n container.addEventListener('scroll', this._onScroll, { passive: true });\n this._update(container.scrollLeft, container.clientWidth);\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';\nimport { resumeGlobalAudioContext } from '@waveform-playlist/playout';\n\nexport class AudioResumeController implements ReactiveController {\n private _host: ReactiveControllerHost & HTMLElement;\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: ReactiveControllerHost & HTMLElement) {\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 resumeGlobalAudioContext().catch((err) => {\n console.warn(\n '[dawcore] AudioResumeController: eager resume failed, will retry on play: ' + String(err)\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 { getGlobalContext } from '@waveform-playlist/playout';\nimport type { DawWaveformElement } from '../elements/daw-waveform';\nimport { recordingProcessorUrl } from '@waveform-playlist/worklets';\nimport { appendPeaks, concatenateAudioData, createAudioBuffer } from '@waveform-playlist/recording';\nimport type {\n DawRecordingStartDetail,\n DawRecordingCompleteDetail,\n DawRecordingErrorDetail,\n} from '../events';\n\nexport interface RecordingOptions {\n trackId?: string;\n bits?: 8 | 16;\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 + lookAhead). */\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}\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 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\nexport class RecordingController implements ReactiveController {\n private _host: RecordingHost & HTMLElement;\n private _sessions = new Map<string, RecordingSession>();\n private _workletLoaded = 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 }\n\n get isRecording(): boolean {\n return 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 const context = getGlobalContext();\n const rawCtx = context.rawContext as AudioContext;\n\n // Resolve editor sample rate from AudioContext before computing startSample\n this._host.resolveAudioContextSampleRate(rawCtx.sampleRate);\n\n try {\n // Load worklet via native API (not Tone.js addAudioWorkletModule — caches single URL)\n if (!this._workletLoaded) {\n await rawCtx.audioWorklet.addModule(recordingProcessorUrl);\n this._workletLoaded = true;\n }\n\n // Detect channel count from stream (not source.channelCount — defaults to 2)\n const channelCount = stream.getAudioTracks()[0]?.getSettings()?.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 lookAhead = context.lookAhead ?? 0;\n const latencySamples = Math.floor((outputLatency + lookAhead) * rawCtx.sampleRate);\n\n // Use Tone.js Context methods — avoids standardized-audio-context identity issues\n const source = context.createMediaStreamSource(stream);\n const workletNode = context.createAudioWorkletNode('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 };\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 if (!session) return;\n session.workletNode.port.postMessage({ command: 'pause' });\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) return;\n session.workletNode.port.postMessage({ command: 'resume' });\n }\n\n stopRecording(trackId?: string): 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 // 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 BEFORE disconnect so worklet can flush remaining buffered samples\n session.workletNode.port.postMessage({ command: 'stop' });\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 context = getGlobalContext();\n const stopCtx = context.rawContext as 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 } = data as { channels: Float32Array[] };\n if (!channels || channels.length === 0 || !channels[0]) return;\n\n // Capture pre-increment value for appendPeaks\n const samplesProcessedBefore = session.totalSamples;\n\n // Accumulate chunks per channel\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 // 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 { pixelsToSeconds } 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}\n\nexport class PointerHandler {\n private _host: PointerHandlerHost;\n private _isDragging = false;\n private _dragStartPx = 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 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 this._timeline.setPointerCapture(e.pointerId);\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) return;\n\n this._timelineRect = this._timeline.getBoundingClientRect();\n this._dragStartPx = this._pxFromPointer(e);\n this._isDragging = false;\n\n this._timeline.setPointerCapture(e.pointerId);\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 }\n\n if (this._isDragging) {\n const h = this._host;\n const startTime = pixelsToSeconds(\n this._dragStartPx,\n h.samplesPerPixel,\n h.effectiveSampleRate\n );\n const endTime = pixelsToSeconds(currentPx, h.samplesPerPixel, h.effectiveSampleRate);\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 = (h._selectionStartTime * h.effectiveSampleRate) / h.samplesPerPixel;\n sel.endPx = (h._selectionEndTime * h.effectiveSampleRate) / h.samplesPerPixel;\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 = pixelsToSeconds(px, h.samplesPerPixel, h.effectiveSampleRate);\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 { 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 readonly effectiveSampleRate: number;\n readonly interactiveClips: boolean;\n readonly engine: ClipEngineContract | null;\n readonly shadowRoot: ShadowRoot | null;\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\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 _lastDeltaPx = 0;\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\n constructor(host: ClipPointerHost) {\n this._host = host;\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 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 this._lastDeltaPx = 0;\n this._cumulativeDeltaSamples = 0;\n\n // Group all drag mutations into one undo step\n if (this._host.engine) {\n this._host.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: send incremental deltas per-frame with skipAdapter=true.\n // Adapter synced once via updateTrack() at drag end.\n const incrementalDeltaPx = totalDeltaPx - this._lastDeltaPx;\n this._lastDeltaPx = totalDeltaPx;\n const incrementalDeltaSamples = Math.round(incrementalDeltaPx * this._host.samplesPerPixel);\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 } 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 const rawDeltaSamples = Math.round(totalDeltaPx * this._host.samplesPerPixel);\n const deltaSamples = engine.constrainTrimDelta(\n this._trackId,\n this._clipId,\n boundary,\n rawDeltaSamples\n );\n const deltaPx = Math.round(deltaSamples / this._host.samplesPerPixel);\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 this._lastDeltaPx = 0;\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 }\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 { createClipFromSeconds, 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 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 = createClipFromSeconds({\n audioBuffer,\n startTime: 0,\n duration: audioBuffer.duration,\n offset: 0,\n gain: 1,\n name,\n sampleRate: audioBuffer.sampleRate,\n sourceDuration: audioBuffer.duration,\n });\n\n host._clipBuffers = new Map(host._clipBuffers).set(clip.id, audioBuffer);\n 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.samplesPerPixel,\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 clips: [\n {\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 },\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 * 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 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 };\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 return !!findClipAtSample(track.clips, atSample);\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 readonly samplesPerPixel: 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.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.samplesPerPixel,\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 { LitElement, html, css } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { computeTemporalTicks, type TickData } from '../utils/smart-scale';\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\n private _tickData: TickData | 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 white-space: nowrap;\n color: var(--daw-ruler-color, #c49a6c);\n top: 2px;\n }\n `;\n\n willUpdate() {\n // Compute ticks once per update — used by both render() and updated()\n if (this.duration > 0) {\n this._tickData = computeTemporalTicks(\n this.samplesPerPixel,\n this.sampleRate,\n this.duration,\n this.rulerHeight\n );\n } else {\n this._tickData = null;\n }\n }\n\n render() {\n if (!this._tickData) return html``;\n\n const { widthX, labels } = this._tickData;\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 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 ${labels.map(\n ({ pix, text }) => 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 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 rulerColor =\n getComputedStyle(this).getPropertyValue('--daw-ruler-color').trim() || '#c49a6c';\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(\n MAX_CANVAS_WIDTH,\n this._tickData.widthX - idx * MAX_CANVAS_WIDTH\n );\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 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\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 { 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"],"mappings":"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","names":["LitElement","customElement","property","LitElement","property","customElement","LitElement","customElement","property","LitElement","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","createClipFromSeconds","createTrack","bits","numChannels","LitElement","html","css","customElement","property","LitElement","html","css","property","customElement","css","state","WaveformData","LitElement","clip","createClipFromSeconds","peakData","createTrack","audioBuffer","html","css","property","state","customElement","LitElement","html","css","customElement","property","MAX_CANVAS_WIDTH","LitElement","html","css","property","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"]}
|
|
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-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/workers/peaksWorker.ts","../src/workers/waveformDataUtils.ts","../src/workers/peakPipeline.ts","../src/elements/daw-track-controls.ts","../src/styles/theme.ts","../src/controllers/viewport-controller.ts","../src/controllers/audio-resume-controller.ts","../src/controllers/recording-controller.ts","../src/interactions/pointer-handler.ts","../src/interactions/constants.ts","../src/interactions/clip-pointer-handler.ts","../src/interactions/file-loader.ts","../src/interactions/recording-clip.ts","../src/interactions/split-handler.ts","../src/interactions/clip-peak-sync.ts","../src/interactions/peaks-loader.ts","../src/elements/daw-ruler.ts","../src/utils/time-format.ts","../src/utils/smart-scale.ts","../src/elements/daw-selection.ts","../src/elements/daw-record-button.ts","../src/elements/daw-keyboard-shortcuts.ts"],"sourcesContent":["import { LitElement } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\n\n@customElement('daw-clip')\nexport class DawClipElement extends LitElement {\n @property() src = '';\n @property({ attribute: 'peaks-src' }) peaksSrc = '';\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 readonly clipId = crypto.randomUUID();\n\n // Light DOM — no visual rendering, just a data container\n createRenderRoot() {\n return this;\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';\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 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 = ['volume', 'pan', 'muted', 'soloed', 'src', 'name'];\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\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']);\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\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 const dirtyByChunk = groupDirtyByChunk(this._dirtyPixels, step);\n\n this._drawnChunks.clear();\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 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 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 } 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\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\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 });\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\" ?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 // During recording: toggle pause/resume of both worklet and playback (Audacity-style)\n if (this._isPaused) {\n target.resumeRecording();\n target.play(target.currentTime);\n this._isPaused = false;\n } else {\n target.pauseRecording();\n target.pause();\n this._isPaused = true;\n }\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 // Stop recording first (also stops playback via the controller),\n // then stop playback if it was running independently\n if (target.isRecording) {\n target.stopRecording();\n }\n target.stop();\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 { ClipTrack, FadeType, Peaks, PeakData } from '@waveform-playlist/core';\nimport type { TrackDescriptor, ClipDescriptor } 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 { PlaylistEngine } from '@waveform-playlist/engine';\nimport '../elements/daw-track-controls';\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 { PointerHandler } from '../interactions/pointer-handler';\nimport { ClipPointerHandler } from '../interactions/clip-pointer-handler';\nimport type {\n DawSelectionDetail,\n DawTrackIdDetail,\n DawTrackErrorDetail,\n DawErrorDetail,\n LoadFilesResult,\n} from '../events';\nimport { loadFiles as loadFilesImpl } from '../interactions/file-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';\n\n@customElement('daw-editor')\nexport class DawEditorElement extends LitElement {\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 /** Desired sample rate. Creates a cross-browser AudioContext at this rate.\n * Pre-computed .dat peaks render instantly when they match. */\n @property({ type: Number, attribute: 'sample-rate' }) sampleRate = 48000;\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 /** Consumer-provided AudioContext. When set, used for decode, playback, and recording. */\n private _externalAudioContext: AudioContext | null = null;\n private _ownedAudioContext: AudioContext | null = null;\n\n /** Set an AudioContext to use for all audio operations. Must be set before tracks load. */\n set audioContext(ctx: AudioContext | null) {\n if (ctx && ctx.state === 'closed') {\n console.warn('[dawcore] Provided AudioContext is already closed. Ignoring.');\n return;\n }\n if (this._engine) {\n console.warn(\n '[dawcore] audioContext set after engine is built. ' +\n 'The engine will continue using the previous context.'\n );\n }\n this._externalAudioContext = ctx;\n }\n\n get audioContext(): AudioContext {\n if (this._externalAudioContext) return this._externalAudioContext;\n if (!this._ownedAudioContext) {\n this._ownedAudioContext = new AudioContext({ sampleRate: this.sampleRate });\n if (this._ownedAudioContext.sampleRate !== this.sampleRate) {\n console.warn(\n '[dawcore] Requested sampleRate ' +\n this.sampleRate +\n ' but AudioContext is running at ' +\n this._ownedAudioContext.sampleRate\n );\n }\n }\n return this._ownedAudioContext;\n }\n _engine: PlaylistEngine | null = null;\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 _clipPointer = new ClipPointerHandler(this);\n get _clipHandler() {\n return this.interactiveClips ? this._clipPointer : null;\n }\n get engine() {\n return this._engine;\n }\n /** Re-extract peaks for a clip at new offset/duration from cached WaveformData. */\n reextractClipPeaks(clipId: string, offsetSamples: number, durationSamples: number) {\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.samplesPerPixel,\n this.mono,\n singleClipOffsets\n );\n const peakData = result.get(clipId);\n if (!peakData) return null;\n return { data: peakData.data, length: peakData.length };\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\n static styles = [\n hostStyles,\n css`\n :host {\n display: flex;\n position: relative;\n background: var(--daw-background, #1a1a2e);\n overflow: hidden;\n }\n .controls-column {\n flex-shrink: 0;\n width: var(--daw-controls-width, 180px);\n }\n .scroll-area {\n flex: 1;\n overflow-x: auto;\n overflow-y: hidden;\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 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 .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 private get _totalWidth(): number {\n return Math.ceil((this._duration * this.effectiveSampleRate) / this.samplesPerPixel);\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 // Detect track 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 }\n const nested = node.querySelectorAll?.('daw-track');\n if (nested) {\n for (const track of nested) {\n this._onTrackRemoved((track as DawTrackElement).trackId);\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._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 try {\n this._disposeEngine();\n } catch (err) {\n console.warn('[dawcore] Error disposing engine: ' + String(err));\n }\n // Close owned AudioContext to release hardware resources.\n // Skip when consumer provided an external context (they own its lifecycle).\n if (this._ownedAudioContext) {\n this._ownedAudioContext.close().catch((err) => {\n console.warn('[dawcore] Error closing AudioContext: ' + String(err));\n });\n this._ownedAudioContext = null;\n }\n }\n willUpdate(changedProperties: Map<string, unknown>) {\n if (changedProperties.has('eagerResume')) {\n this._audioResume.target = this.eagerResume;\n }\n // Restart playhead animation with new samplesPerPixel if playing\n if (changedProperties.has('samplesPerPixel') && this._isPlaying) {\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 if (changedProperties.has('samplesPerPixel') && this._clipBuffers.size > 0) {\n const re = this._peakPipeline.reextractPeaks(\n this._clipBuffers,\n this.samplesPerPixel,\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 // --- 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 }\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 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 };\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 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 clips.push({\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 });\n } else {\n for (const clipEl of clipEls) {\n clips.push({\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 });\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 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 (!clipDesc.src) continue;\n\n // Start both fetches concurrently — await peaks first to render preview before audio decode\n const waveformDataPromise = clipDesc.peaksSrc ? this._fetchPeaks(clipDesc.peaksSrc) : null;\n const audioPromise = this._fetchAndDecode(clipDesc.src);\n\n // --- Peaks-first path: render waveform before audio decode completes ---\n // Separate try/catch for peaks so audio errors aren't misattributed.\n // If the .dat sample rate doesn't match the AudioContext rate, skip the\n // pre-computed peaks entirely — rate conversion creates subtle mismatches\n // in trim/split/zoom. The worker generates correct peaks from decoded audio.\n let waveformData: any = null;\n if (waveformDataPromise) {\n try {\n const wd = await waveformDataPromise;\n const contextRate = this.audioContext.sampleRate;\n if (wd.sample_rate === contextRate) {\n waveformData = wd;\n } else {\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 clipDesc.peaksSrc +\n ', generating from audio'\n );\n }\n } catch (err) {\n console.warn(\n '[dawcore] Failed to load peaks from ' +\n clipDesc.peaksSrc +\n ': ' +\n String(err) +\n ' — falling back to AudioBuffer generation'\n );\n }\n }\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 const effectiveScale = Math.max(this.samplesPerPixel, 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 const audioBuffer = await audioPromise;\n this._resolvedSampleRate = audioBuffer.sampleRate;\n const 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 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 const peakData = await this._peakPipeline.generatePeaks(\n audioBuffer,\n this.samplesPerPixel,\n this.mono,\n clip.offsetSamples,\n clip.durationSamples\n );\n this._peaksData = new Map(this._peaksData).set(clip.id, peakData);\n clips.push(clip);\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 // 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 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 const [{ PlaylistEngine }, { NativePlayoutAdapter }] = await Promise.all([\n import('@waveform-playlist/engine'),\n import('@dawcore/transport'),\n ]);\n const adapter = new NativePlayoutAdapter(this.audioContext);\n const engine = new PlaylistEngine({\n adapter,\n sampleRate: this.effectiveSampleRate,\n samplesPerPixel: this.samplesPerPixel,\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 syncPeaksForChangedClips(this, engineState.tracks);\n }\n });\n engine.on('timeupdate', (time: number) => {\n this._currentTime = time;\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 // --- 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._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 return this._currentTime;\n }\n get isRecording(): boolean {\n return this._recordingController.isRecording;\n }\n pauseRecording(): void {\n this._recordingController.pauseRecording();\n }\n resumeRecording(): void {\n this._recordingController.resumeRecording();\n }\n stopRecording(): void {\n 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 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 latencyPixels = Math.floor(rs.latencySamples / this.samplesPerPixel);\n const left = Math.floor(rs.startSample / this.samplesPerPixel);\n const w = Math.floor(audibleSamples / this.samplesPerPixel);\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 playhead.startAnimation(\n () => engine.getCurrentTime(),\n this.effectiveSampleRate,\n this.samplesPerPixel\n );\n }\n _stopPlayhead() {\n const playhead = this._getPlayhead();\n if (!playhead) return;\n playhead.stopAnimation(this._currentTime, this.effectiveSampleRate, this.samplesPerPixel);\n }\n private _getPlayhead(): DawPlayheadElement | null {\n return this.shadowRoot?.querySelector('daw-playhead') as DawPlayheadElement | null;\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 selStartPx = (this._selectionStartTime * sr) / this.samplesPerPixel;\n const selEndPx = (this._selectionEndTime * sr) / this.samplesPerPixel;\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 return html`\n ${orderedTracks.length > 0\n ? html`<div class=\"controls-column\">\n ${this.timescale ? html`<div style=\"height: 30px;\"></div>` : ''}\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 : ''}\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 ${orderedTracks.length > 0 && this.timescale\n ? html`<daw-ruler\n .samplesPerPixel=${this.samplesPerPixel}\n .sampleRate=${this.effectiveSampleRate}\n .duration=${this._duration}\n ></daw-ruler>`\n : ''}\n ${orderedTracks.length > 0\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 const width = clipPixelWidth(\n clip.startSample,\n clip.durationSamples,\n this.samplesPerPixel\n );\n const clipLeft = Math.floor(clip.startSample / this.samplesPerPixel);\n const channels: Peaks[] = 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 ${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 ></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 <slot></slot>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'daw-editor': DawEditorElement;\n }\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 * 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';\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 }\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 `;\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 ×\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\">\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\">\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 { 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\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._scrollContainer = null;\n }\n\n private _attachScrollContainer(container: HTMLElement) {\n this._scrollContainer?.removeEventListener('scroll', this._onScroll);\n this._scrollContainer = container;\n container.addEventListener('scroll', this._onScroll, { passive: true });\n this._update(container.scrollLeft, container.clientWidth);\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 const ctx = this._host.audioContext;\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 { recordingProcessorUrl } from '@waveform-playlist/worklets';\nimport { appendPeaks, concatenateAudioData, createAudioBuffer } from '@waveform-playlist/recording';\nimport type {\n DawRecordingStartDetail,\n DawRecordingCompleteDetail,\n DawRecordingErrorDetail,\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}\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\nexport class RecordingController implements ReactiveController {\n private _host: RecordingHost & HTMLElement;\n private _sessions = new Map<string, RecordingSession>();\n private _workletLoadedCtx: AudioContext | null = null;\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 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 via native API — guard tied to context identity so a\n // swapped AudioContext gets the module re-registered.\n if (!this._workletLoadedCtx || this._workletLoadedCtx !== rawCtx) {\n await rawCtx.audioWorklet.addModule(recordingProcessorUrl);\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 native AudioContext methods directly (no Tone.js wrapper)\n const source = rawCtx.createMediaStreamSource(stream);\n const workletNode = 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 };\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 if (!session) return;\n session.workletNode.port.postMessage({ command: 'pause' });\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) return;\n session.workletNode.port.postMessage({ command: 'resume' });\n }\n\n stopRecording(trackId?: string): 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 // 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 BEFORE disconnect so worklet can flush remaining buffered samples\n session.workletNode.port.postMessage({ command: 'stop' });\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 } = data as { channels: Float32Array[] };\n if (!channels || channels.length === 0 || !channels[0]) return;\n\n // Capture pre-increment value for appendPeaks\n const samplesProcessedBefore = session.totalSamples;\n\n // Accumulate chunks per channel\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 // 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 { pixelsToSeconds } 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}\n\nexport class PointerHandler {\n private _host: PointerHandlerHost;\n private _isDragging = false;\n private _dragStartPx = 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 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 this._timeline.setPointerCapture(e.pointerId);\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) return;\n\n this._timelineRect = this._timeline.getBoundingClientRect();\n this._dragStartPx = this._pxFromPointer(e);\n this._isDragging = false;\n\n this._timeline.setPointerCapture(e.pointerId);\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 }\n\n if (this._isDragging) {\n const h = this._host;\n const startTime = pixelsToSeconds(\n this._dragStartPx,\n h.samplesPerPixel,\n h.effectiveSampleRate\n );\n const endTime = pixelsToSeconds(currentPx, h.samplesPerPixel, h.effectiveSampleRate);\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 = (h._selectionStartTime * h.effectiveSampleRate) / h.samplesPerPixel;\n sel.endPx = (h._selectionEndTime * h.effectiveSampleRate) / h.samplesPerPixel;\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 = pixelsToSeconds(px, h.samplesPerPixel, h.effectiveSampleRate);\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 { 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 readonly effectiveSampleRate: number;\n readonly interactiveClips: boolean;\n readonly engine: ClipEngineContract | null;\n readonly shadowRoot: ShadowRoot | null;\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\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 _lastDeltaPx = 0;\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\n constructor(host: ClipPointerHost) {\n this._host = host;\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 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 this._lastDeltaPx = 0;\n this._cumulativeDeltaSamples = 0;\n\n // Group all drag mutations into one undo step\n if (this._host.engine) {\n this._host.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: send incremental deltas per-frame with skipAdapter=true.\n // Adapter synced once via updateTrack() at drag end.\n const incrementalDeltaPx = totalDeltaPx - this._lastDeltaPx;\n this._lastDeltaPx = totalDeltaPx;\n const incrementalDeltaSamples = Math.round(incrementalDeltaPx * this._host.samplesPerPixel);\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 } 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 const rawDeltaSamples = Math.round(totalDeltaPx * this._host.samplesPerPixel);\n const deltaSamples = engine.constrainTrimDelta(\n this._trackId,\n this._clipId,\n boundary,\n rawDeltaSamples\n );\n const deltaPx = Math.round(deltaSamples / this._host.samplesPerPixel);\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 this._lastDeltaPx = 0;\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 }\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 { createClipFromSeconds, 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 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 = createClipFromSeconds({\n audioBuffer,\n startTime: 0,\n duration: audioBuffer.duration,\n offset: 0,\n gain: 1,\n name,\n sampleRate: audioBuffer.sampleRate,\n sourceDuration: audioBuffer.duration,\n });\n\n host._clipBuffers = new Map(host._clipBuffers).set(clip.id, audioBuffer);\n 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.samplesPerPixel,\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 clips: [\n {\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 },\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 * 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 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 };\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 return !!findClipAtSample(track.clips, atSample);\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 readonly samplesPerPixel: 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.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.samplesPerPixel,\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 { LitElement, html, css } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { computeTemporalTicks, type TickData } from '../utils/smart-scale';\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\n private _tickData: TickData | 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 white-space: nowrap;\n color: var(--daw-ruler-color, #c49a6c);\n top: 2px;\n }\n `;\n\n willUpdate() {\n // Compute ticks once per update — used by both render() and updated()\n if (this.duration > 0) {\n this._tickData = computeTemporalTicks(\n this.samplesPerPixel,\n this.sampleRate,\n this.duration,\n this.rulerHeight\n );\n } else {\n this._tickData = null;\n }\n }\n\n render() {\n if (!this._tickData) return html``;\n\n const { widthX, labels } = this._tickData;\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 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 ${labels.map(\n ({ pix, text }) => 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 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 rulerColor =\n getComputedStyle(this).getPropertyValue('--daw-ruler-color').trim() || '#c49a6c';\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(\n MAX_CANVAS_WIDTH,\n this._tickData.widthX - idx * MAX_CANVAS_WIDTH\n );\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 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\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 { 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"],"mappings":"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","names":["LitElement","customElement","property","LitElement","property","customElement","LitElement","customElement","property","LitElement","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","createClipFromSeconds","createTrack","bits","numChannels","LitElement","html","css","customElement","property","LitElement","html","css","property","customElement","css","state","WaveformData","LitElement","clip","createClip","peakData","createTrack","audioBuffer","createClipFromSeconds","html","css","property","state","customElement","LitElement","html","css","customElement","property","MAX_CANVAS_WIDTH","LitElement","html","css","property","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"]}
|