{"version":3,"sources":["../src/operations/clipOperations.ts","../src/operations/viewportOperations.ts","../src/operations/timelineOperations.ts","../src/PlaylistEngine.ts"],"sourcesContent":["/**\n * Clip Operations\n *\n * Pure functions for constraining clip movement, boundary trimming,\n * and splitting clips on a timeline. All positions are in samples (integers).\n */\n\nimport type { AudioClip } from '@waveform-playlist/core';\nimport { createClip } from '@waveform-playlist/core';\n\n/**\n * Constrain clip movement delta to prevent overlaps with adjacent clips\n * and going before sample 0.\n *\n * @param clip - The clip being dragged\n * @param deltaSamples - Requested movement in samples (negative = left, positive = right)\n * @param sortedClips - All clips on the track, sorted by startSample\n * @param clipIndex - Index of the dragged clip in sortedClips\n * @returns Constrained delta that prevents overlaps\n */\nexport function constrainClipDrag(\n  clip: AudioClip,\n  deltaSamples: number,\n  sortedClips: AudioClip[],\n  clipIndex: number\n): number {\n  let delta = deltaSamples;\n\n  // Constraint 1: Cannot go before sample 0\n  const minDelta = -clip.startSample;\n  delta = Math.max(delta, minDelta);\n\n  // Constraint 2: Cannot overlap previous clip\n  if (clipIndex > 0) {\n    const prevClip = sortedClips[clipIndex - 1];\n    const prevClipEnd = prevClip.startSample + prevClip.durationSamples;\n    // clip.startSample + delta >= prevClipEnd\n    const minDeltaPrev = prevClipEnd - clip.startSample;\n    delta = Math.max(delta, minDeltaPrev);\n  }\n\n  // Constraint 3: Cannot overlap next clip\n  if (clipIndex < sortedClips.length - 1) {\n    const nextClip = sortedClips[clipIndex + 1];\n    // clip.startSample + clip.durationSamples + delta <= nextClip.startSample\n    const maxDeltaNext = nextClip.startSample - (clip.startSample + clip.durationSamples);\n    delta = Math.min(delta, maxDeltaNext);\n  }\n\n  return delta;\n}\n\n/**\n * Constrain boundary trim delta for left or right edge of a clip.\n *\n * LEFT boundary: delta moves the left edge (positive = shrink, negative = expand)\n * - startSample += delta, offsetSamples += delta, durationSamples -= delta\n *\n * RIGHT boundary: delta applied to durationSamples (positive = expand, negative = shrink)\n * - durationSamples += delta\n *\n * @param clip - The clip being trimmed\n * @param deltaSamples - Requested trim delta in samples\n * @param boundary - Which edge is being trimmed: 'left' or 'right'\n * @param sortedClips - All clips on the track, sorted by startSample\n * @param clipIndex - Index of the trimmed clip in sortedClips\n * @param minDurationSamples - Minimum allowed clip duration in samples\n * @returns Constrained delta\n */\nexport function constrainBoundaryTrim(\n  clip: AudioClip,\n  deltaSamples: number,\n  boundary: 'left' | 'right',\n  sortedClips: AudioClip[],\n  clipIndex: number,\n  minDurationSamples: number\n): number {\n  let delta = deltaSamples;\n\n  if (boundary === 'left') {\n    // Constraint 1: startSample + delta >= 0\n    delta = Math.max(delta, -clip.startSample);\n\n    // Constraint 2: offsetSamples + delta >= 0\n    delta = Math.max(delta, -clip.offsetSamples);\n\n    // Constraint 3: Cannot overlap previous clip\n    if (clipIndex > 0) {\n      const prevClip = sortedClips[clipIndex - 1];\n      const prevClipEnd = prevClip.startSample + prevClip.durationSamples;\n      // startSample + delta >= prevClipEnd\n      delta = Math.max(delta, prevClipEnd - clip.startSample);\n    }\n\n    // Constraint 4: durationSamples - delta >= minDurationSamples\n    // delta <= durationSamples - minDurationSamples\n    delta = Math.min(delta, clip.durationSamples - minDurationSamples);\n  } else {\n    // RIGHT boundary\n\n    // Constraint 1: durationSamples + delta >= minDurationSamples\n    // delta >= minDurationSamples - durationSamples\n    delta = Math.max(delta, minDurationSamples - clip.durationSamples);\n\n    // Constraint 2: offsetSamples + (durationSamples + delta) <= sourceDurationSamples\n    // delta <= sourceDurationSamples - offsetSamples - durationSamples\n    delta = Math.min(delta, clip.sourceDurationSamples - clip.offsetSamples - clip.durationSamples);\n\n    // Constraint 3: startSample + (durationSamples + delta) <= nextClip.startSample\n    if (clipIndex < sortedClips.length - 1) {\n      const nextClip = sortedClips[clipIndex + 1];\n      // delta <= nextClip.startSample - startSample - durationSamples\n      delta = Math.min(delta, nextClip.startSample - clip.startSample - clip.durationSamples);\n    }\n  }\n\n  return delta;\n}\n\n/**\n * Snap a split sample position to the nearest pixel boundary.\n *\n * @param splitSample - The sample position to snap\n * @param samplesPerPixel - Current zoom level (samples per pixel)\n * @returns Snapped sample position\n */\nexport function calculateSplitPoint(splitSample: number, samplesPerPixel: number): number {\n  return Math.floor(splitSample / samplesPerPixel) * samplesPerPixel;\n}\n\n/**\n * Split a clip into two clips at the given sample position.\n *\n * The left clip retains the original fadeIn; the right clip retains the original fadeOut.\n * Both clips share the same waveformData reference.\n * If the clip has a name, suffixes \" (1)\" and \" (2)\" are appended.\n *\n * @param clip - The clip to split\n * @param splitSample - The timeline sample position where the split occurs\n * @returns Object with `left` and `right` AudioClip\n */\nexport function splitClip(\n  clip: AudioClip,\n  splitSample: number\n): { left: AudioClip; right: AudioClip } {\n  const leftDuration = splitSample - clip.startSample;\n  const rightDuration = clip.durationSamples - leftDuration;\n\n  const leftName = clip.name ? `${clip.name} (1)` : undefined;\n  const rightName = clip.name ? `${clip.name} (2)` : undefined;\n\n  const left = createClip({\n    startSample: clip.startSample,\n    durationSamples: leftDuration,\n    offsetSamples: clip.offsetSamples,\n    sampleRate: clip.sampleRate,\n    sourceDurationSamples: clip.sourceDurationSamples,\n    gain: clip.gain,\n    name: leftName,\n    color: clip.color,\n    fadeIn: clip.fadeIn,\n    audioBuffer: clip.audioBuffer,\n    waveformData: clip.waveformData,\n  });\n\n  const right = createClip({\n    startSample: splitSample,\n    durationSamples: rightDuration,\n    offsetSamples: clip.offsetSamples + leftDuration,\n    sampleRate: clip.sampleRate,\n    sourceDurationSamples: clip.sourceDurationSamples,\n    gain: clip.gain,\n    name: rightName,\n    color: clip.color,\n    fadeOut: clip.fadeOut,\n    audioBuffer: clip.audioBuffer,\n    waveformData: clip.waveformData,\n  });\n\n  return { left, right };\n}\n\n/**\n * Check whether a clip can be split at the given sample position.\n *\n * The split point must be strictly inside the clip (not at start or end),\n * and both resulting clips must meet the minimum duration requirement.\n *\n * @param clip - The clip to check\n * @param sample - The timeline sample position to test\n * @param minDurationSamples - Minimum allowed clip duration in samples\n * @returns true if the split is valid\n */\nexport function canSplitAt(clip: AudioClip, sample: number, minDurationSamples: number): boolean {\n  const clipEnd = clip.startSample + clip.durationSamples;\n\n  // Must be strictly within clip bounds\n  if (sample <= clip.startSample || sample >= clipEnd) {\n    return false;\n  }\n\n  // Both resulting clips must meet minimum duration\n  const leftDuration = sample - clip.startSample;\n  const rightDuration = clipEnd - sample;\n\n  return leftDuration >= minDurationSamples && rightDuration >= minDurationSamples;\n}\n","/**\n * Viewport operations for virtual scrolling.\n *\n * Pure math helpers that determine which portion of the timeline\n * is visible and which canvas chunks need to be mounted.\n */\n\n/**\n * Calculate the visible region with an overscan buffer for virtual scrolling.\n *\n * The buffer extends the visible range on both sides so that chunks are\n * mounted slightly before they scroll into view, preventing flicker.\n *\n * @param scrollLeft - Current horizontal scroll position in pixels\n * @param containerWidth - Width of the scroll container in pixels\n * @param bufferRatio - Multiplier for buffer size (default 1.5x container width)\n * @returns Object with visibleStart and visibleEnd in pixels\n */\nexport function calculateViewportBounds(\n  scrollLeft: number,\n  containerWidth: number,\n  bufferRatio: number = 1.5\n): { visibleStart: number; visibleEnd: number } {\n  const buffer = containerWidth * bufferRatio;\n  return {\n    visibleStart: Math.max(0, scrollLeft - buffer),\n    visibleEnd: scrollLeft + containerWidth + buffer,\n  };\n}\n\n/**\n * Get an array of chunk indices that overlap the visible viewport.\n *\n * Chunks are fixed-width segments of the total timeline width. Only chunks\n * that intersect [visibleStart, visibleEnd) are included. The last chunk\n * may be narrower than chunkWidth if totalWidth is not evenly divisible.\n *\n * @param totalWidth - Total width of the timeline in pixels\n * @param chunkWidth - Width of each chunk in pixels\n * @param visibleStart - Left edge of the visible region in pixels\n * @param visibleEnd - Right edge of the visible region in pixels\n * @returns Array of chunk indices (0-based) that are visible\n */\nexport function getVisibleChunkIndices(\n  totalWidth: number,\n  chunkWidth: number,\n  visibleStart: number,\n  visibleEnd: number\n): number[] {\n  const totalChunks = Math.ceil(totalWidth / chunkWidth);\n  const indices: number[] = [];\n\n  for (let i = 0; i < totalChunks; i++) {\n    const chunkLeft = i * chunkWidth;\n    const thisChunkWidth = Math.min(totalWidth - chunkLeft, chunkWidth);\n    const chunkEnd = chunkLeft + thisChunkWidth;\n\n    if (chunkEnd <= visibleStart || chunkLeft >= visibleEnd) {\n      continue;\n    }\n\n    indices.push(i);\n  }\n\n  return indices;\n}\n\n/**\n * Determine whether a scroll change is large enough to warrant\n * recalculating the viewport and re-rendering chunks.\n *\n * Small scroll movements are ignored to avoid excessive recomputation\n * during smooth scrolling.\n *\n * @param oldScrollLeft - Previous scroll position in pixels\n * @param newScrollLeft - Current scroll position in pixels\n * @param threshold - Minimum pixel delta to trigger an update (default 100)\n * @returns true if the scroll delta meets or exceeds the threshold\n */\nexport function shouldUpdateViewport(\n  oldScrollLeft: number,\n  newScrollLeft: number,\n  threshold: number = 100\n): boolean {\n  return Math.abs(oldScrollLeft - newScrollLeft) >= threshold;\n}\n","import type { ClipTrack } from '@waveform-playlist/core';\n\n/**\n * Calculate total timeline duration in seconds from all tracks/clips.\n * Iterates all clips, finds the furthest clip end (startSample + durationSamples),\n * converts to seconds using each clip's sampleRate.\n *\n * @param tracks - Array of clip tracks\n * @returns Duration in seconds\n */\nexport function calculateDuration(tracks: ClipTrack[]): number {\n  let maxDuration = 0;\n  for (const track of tracks) {\n    for (const clip of track.clips) {\n      const clipEndSample = clip.startSample + clip.durationSamples;\n      const clipEnd = clipEndSample / clip.sampleRate;\n      maxDuration = Math.max(maxDuration, clipEnd);\n    }\n  }\n  return maxDuration;\n}\n\n/**\n * Find the zoom level index closest to a given samplesPerPixel.\n * Returns exact match if found, otherwise the index whose value is\n * nearest to the target (by absolute difference).\n *\n * @param targetSamplesPerPixel - The samplesPerPixel value to find\n * @param zoomLevels - Array of available zoom levels (samplesPerPixel values)\n * @returns Index into the zoomLevels array\n */\nexport function findClosestZoomIndex(targetSamplesPerPixel: number, zoomLevels: number[]): number {\n  if (zoomLevels.length === 0) return 0;\n\n  let bestIndex = 0;\n  let bestDiff = Math.abs(zoomLevels[0] - targetSamplesPerPixel);\n\n  for (let i = 1; i < zoomLevels.length; i++) {\n    const diff = Math.abs(zoomLevels[i] - targetSamplesPerPixel);\n    if (diff < bestDiff) {\n      bestDiff = diff;\n      bestIndex = i;\n    }\n  }\n\n  return bestIndex;\n}\n\n/**\n * Keep viewport centered during zoom changes.\n * Calculates center time from old zoom, computes new pixel position at new zoom,\n * and returns new scrollLeft clamped to >= 0.\n *\n * @param oldSamplesPerPixel - Previous zoom level\n * @param newSamplesPerPixel - New zoom level\n * @param scrollLeft - Current horizontal scroll position\n * @param containerWidth - Viewport width in pixels\n * @param sampleRate - Audio sample rate\n * @param controlWidth - Width of track controls panel (defaults to 0)\n * @returns New scrollLeft value\n */\nexport function calculateZoomScrollPosition(\n  oldSamplesPerPixel: number,\n  newSamplesPerPixel: number,\n  scrollLeft: number,\n  containerWidth: number,\n  sampleRate: number,\n  controlWidth: number = 0\n): number {\n  const centerPixel = scrollLeft + containerWidth / 2 - controlWidth;\n  const centerTime = (centerPixel * oldSamplesPerPixel) / sampleRate;\n  const newCenterPixel = (centerTime * sampleRate) / newSamplesPerPixel;\n  const newScrollLeft = newCenterPixel + controlWidth - containerWidth / 2;\n  return Math.max(0, newScrollLeft);\n}\n\n/**\n * Clamp a seek position to the valid range [0, duration].\n *\n * @param time - Requested seek time in seconds\n * @param duration - Maximum duration in seconds\n * @returns Clamped time value\n */\nexport function clampSeekPosition(time: number, duration: number): number {\n  return Math.max(0, Math.min(time, duration));\n}\n","/**\n * PlaylistEngine — Stateful, framework-agnostic timeline engine.\n *\n * Composes pure operations from ./operations with an event emitter\n * and optional PlayoutAdapter for audio playback delegation.\n */\n\nimport type { AudioClip, ClipTrack } from '@waveform-playlist/core';\nimport { sortClipsByTime } from '@waveform-playlist/core';\nimport {\n  constrainClipDrag,\n  constrainBoundaryTrim,\n  canSplitAt,\n  splitClip as splitClipOp,\n} from './operations/clipOperations';\nimport { calculateDuration, findClosestZoomIndex } from './operations/timelineOperations';\nimport type { PlayoutAdapter, EngineState, EngineEvents, PlaylistEngineOptions } from './types';\n\nconst DEFAULT_SAMPLE_RATE = 48000;\nconst DEFAULT_SAMPLES_PER_PIXEL = 1024;\nconst DEFAULT_ZOOM_LEVELS = [256, 512, 1024, 2048, 4096, 8192];\nconst DEFAULT_MIN_DURATION_SECONDS = 0.1;\n\ntype EventName = keyof EngineEvents;\n\nexport class PlaylistEngine {\n  private _tracks: ClipTrack[] = [];\n  private _currentTime = 0;\n  private _playStartPosition = 0;\n  private _isPlaying = false;\n  private _selectedTrackId: string | null = null;\n  private _sampleRate: number;\n  private _zoomLevels: number[];\n  private _zoomIndex: number;\n  private _selectionStart = 0;\n  private _selectionEnd = 0;\n  private _masterVolume = 1.0;\n  private _loopStart = 0;\n  private _loopEnd = 0;\n  private _isLoopEnabled = false;\n  private _bpm: number;\n  private _ppqn: number;\n  private _tracksVersion = 0;\n  private _mixerVersion = 0;\n  private _adapter: PlayoutAdapter | null;\n  private _disposed = false;\n  // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n  private _listeners: Map<string, Set<Function>> = new Map();\n\n  private _undoStack: ClipTrack[][] = [];\n  private _redoStack: ClipTrack[][] = [];\n  private _inTransaction = false;\n  private _transactionSnapshot: ClipTrack[] | null = null;\n  private _transactionMutated = false;\n  readonly undoLimit: number;\n\n  constructor(options: PlaylistEngineOptions = {}) {\n    this._sampleRate = options.sampleRate ?? DEFAULT_SAMPLE_RATE;\n    this._zoomLevels = [...(options.zoomLevels ?? DEFAULT_ZOOM_LEVELS)];\n    this._adapter = options.adapter ?? null;\n    this.undoLimit = options.undoLimit ?? 100;\n\n    // Bounded playback (#608): adapters that self-terminate at an endTime\n    // report completion here. Deferred via queueMicrotask — the Tone adapter\n    // fires this from inside the Transport tick chain, which catches nothing;\n    // stopping the Transport re-entrantly from a tick callback is the hazard\n    // the microtask sidesteps. Re-check _isPlaying inside: a consumer stop()\n    // can land between the callback and the microtask.\n    this._adapter?.onPlaybackEnded?.(() => {\n      queueMicrotask(() => {\n        if (this._isPlaying && !this._disposed) {\n          this.stop();\n        }\n      });\n    });\n\n    if (this._zoomLevels.length === 0) {\n      throw new Error('PlaylistEngine: zoomLevels must not be empty');\n    }\n\n    const initialSpp = options.samplesPerPixel ?? DEFAULT_SAMPLES_PER_PIXEL;\n    const zoomIndex = this._zoomLevels.indexOf(initialSpp);\n    if (zoomIndex === -1) {\n      throw new Error(\n        `PlaylistEngine: samplesPerPixel ${initialSpp} is not in zoomLevels [${this._zoomLevels.join(', ')}]. ` +\n          `Either pass a samplesPerPixel value that exists in zoomLevels, or include ${initialSpp} in your zoomLevels array.`\n      );\n    }\n    this._zoomIndex = zoomIndex;\n    this._bpm = options.bpm ?? 120;\n    // Adapter is the source of truth for PPQN. Headless mode falls back to options.ppqn or 960.\n    this._ppqn = options.adapter?.ppqn ?? options.ppqn ?? 960;\n  }\n\n  // ---------------------------------------------------------------------------\n  // Undo/Redo\n  // ---------------------------------------------------------------------------\n\n  get canUndo(): boolean {\n    return this._undoStack.length > 0;\n  }\n\n  get canRedo(): boolean {\n    return this._redoStack.length > 0;\n  }\n\n  undo(): void {\n    if (this._undoStack.length === 0) return;\n    const snapshot = this._undoStack.pop()!;\n    this._redoStack.push(this._snapshotTracks());\n    this._restoreTracks(snapshot);\n  }\n\n  redo(): void {\n    if (this._redoStack.length === 0) return;\n    const snapshot = this._redoStack.pop()!;\n    this._undoStack.push(this._snapshotTracks());\n    this._restoreTracks(snapshot);\n  }\n\n  clearHistory(): void {\n    this._undoStack = [];\n    this._redoStack = [];\n  }\n\n  beginTransaction(): void {\n    if (this._inTransaction) {\n      console.warn(\n        '[waveform-playlist/engine] beginTransaction: already in a transaction, ' +\n          'previous snapshot will be overwritten'\n      );\n    }\n    this._transactionSnapshot = this._snapshotTracks();\n    this._inTransaction = true;\n    this._transactionMutated = false;\n  }\n\n  commitTransaction(): void {\n    if (!this._inTransaction || this._transactionSnapshot === null) {\n      console.warn('[waveform-playlist/engine] commitTransaction: no active transaction to commit');\n      return;\n    }\n    // Only push undo entry if mutations actually occurred during the transaction.\n    // Without this, no-op drags (e.g., against a boundary) create phantom undo entries.\n    if (this._transactionMutated) {\n      this._undoStack.push(this._transactionSnapshot);\n      if (this._undoStack.length > this.undoLimit) {\n        this._undoStack.shift();\n      }\n      this._redoStack = [];\n    }\n    this._transactionSnapshot = null;\n    this._inTransaction = false;\n    this._transactionMutated = false;\n  }\n\n  abortTransaction(): void {\n    if (!this._inTransaction || this._transactionSnapshot === null) {\n      console.warn('[waveform-playlist/engine] abortTransaction: no active transaction to abort');\n      return;\n    }\n    const snapshot = this._transactionSnapshot;\n    const mutated = this._transactionMutated;\n    this._transactionSnapshot = null;\n    this._inTransaction = false;\n    this._transactionMutated = false;\n    // Only restore if mutations occurred — avoids full adapter rebuild on click\n    if (mutated) {\n      this._restoreTracks(snapshot);\n    }\n  }\n\n  // ---------------------------------------------------------------------------\n  // State snapshot\n  // ---------------------------------------------------------------------------\n\n  getState(): EngineState {\n    return {\n      tracks: this._tracks.map((t) => ({ ...t, clips: [...t.clips] })),\n      tracksVersion: this._tracksVersion,\n      mixerVersion: this._mixerVersion,\n      duration: calculateDuration(this._tracks),\n      currentTime: this._currentTime,\n      isPlaying: this._isPlaying,\n      samplesPerPixel: this._zoomLevels[this._zoomIndex],\n      sampleRate: this._sampleRate,\n      selectedTrackId: this._selectedTrackId,\n      zoomIndex: this._zoomIndex,\n      canZoomIn: this._zoomIndex > 0,\n      canZoomOut: this._zoomIndex < this._zoomLevels.length - 1,\n      selectionStart: this._selectionStart,\n      selectionEnd: this._selectionEnd,\n      masterVolume: this._masterVolume,\n      loopStart: this._loopStart,\n      loopEnd: this._loopEnd,\n      isLoopEnabled: this._isLoopEnabled,\n      bpm: this._bpm,\n      ppqn: this._ppqn,\n      canUndo: this.canUndo,\n      canRedo: this.canRedo,\n    };\n  }\n\n  // ---------------------------------------------------------------------------\n  // Track Management\n  // ---------------------------------------------------------------------------\n\n  setTracks(tracks: ClipTrack[]): void {\n    this.clearHistory();\n    this._tracks = tracks.map((track) => ({\n      ...track,\n      clips: track.clips.map((clip) => ({\n        ...clip,\n        startTick: clip.startTick ?? this._secondsToTicks(clip.startSample / this._sampleRate),\n      })),\n    }));\n    this._tracksVersion++;\n    this._adapter?.setTracks(this._tracks);\n    this._emitStateChange();\n  }\n\n  addTrack(track: ClipTrack): void {\n    this._pushUndoSnapshot();\n    const enriched = {\n      ...track,\n      clips: track.clips.map((clip) => ({\n        ...clip,\n        startTick: clip.startTick ?? this._secondsToTicks(clip.startSample / this._sampleRate),\n      })),\n    };\n    this._tracks = [...this._tracks, enriched];\n    this._tracksVersion++;\n    if (this._adapter?.addTrack) {\n      this._adapter.addTrack(enriched);\n    } else {\n      this._adapter?.setTracks(this._tracks);\n    }\n    this._emitStateChange();\n  }\n\n  removeTrack(trackId: string): void {\n    if (!this._tracks.some((t) => t.id === trackId)) return;\n    this._pushUndoSnapshot();\n    this._tracks = this._tracks.filter((t) => t.id !== trackId);\n    this._tracksVersion++;\n    if (this._selectedTrackId === trackId) {\n      this._selectedTrackId = null;\n    }\n    if (this._adapter?.removeTrack) {\n      this._adapter.removeTrack(trackId);\n    } else {\n      this._adapter?.setTracks(this._tracks);\n    }\n    this._emitStateChange();\n  }\n\n  /**\n   * Move a track to a new position in the track order. Purely organizational —\n   * track order is not audible (adapter nodes are keyed by track id), so the\n   * adapter is deliberately NOT called: a setTracks() here would rebuild\n   * playout and interrupt playback for a visual-only change.\n   */\n  reorderTrack(trackId: string, toIndex: number): void {\n    const fromIndex = this._tracks.findIndex((t) => t.id === trackId);\n    if (fromIndex === -1) return;\n    const clamped = Math.max(0, Math.min(toIndex, this._tracks.length - 1));\n    if (clamped === fromIndex) return;\n    this._pushUndoSnapshot();\n    const track = this._tracks[fromIndex];\n    const without = this._tracks.filter((t) => t.id !== trackId);\n    this._tracks = [...without.slice(0, clamped), track, ...without.slice(clamped)];\n    this._tracksVersion++;\n    this._emitStateChange();\n  }\n\n  /** Update a single track's clips on the adapter (no full rebuild). */\n  updateTrack(trackId: string, track?: ClipTrack): void {\n    const resolved = track ?? this._tracks.find((t) => t.id === trackId);\n    if (!resolved) return;\n    if (track) {\n      this._pushUndoSnapshot();\n      const enriched = {\n        ...track,\n        clips: track.clips.map((clip) => ({\n          ...clip,\n          startTick: clip.startTick ?? this._secondsToTicks(clip.startSample / this._sampleRate),\n        })),\n      };\n      this._tracks = this._tracks.map((t) => (t.id === trackId ? enriched : t));\n      this._tracksVersion++;\n      // Forward enriched track to adapter\n      if (this._adapter?.updateTrack) {\n        this._adapter.updateTrack(trackId, enriched);\n      } else {\n        this._adapter?.setTracks(this._tracks);\n      }\n      this._emitStateChange();\n      return;\n    }\n    // No track arg — just forward existing track to adapter\n    if (this._adapter?.updateTrack) {\n      this._adapter.updateTrack(trackId, resolved);\n    } else {\n      this._adapter?.setTracks(this._tracks);\n    }\n  }\n\n  /** Internal: update adapter after modifying this._tracks in place. */\n  private _updateTrackOnAdapter(trackId: string): void {\n    const t = this._tracks.find((tr) => tr.id === trackId);\n    if (!t) return;\n    if (this._adapter?.updateTrack) {\n      this._adapter.updateTrack(trackId, t);\n    } else {\n      this._adapter?.setTracks(this._tracks);\n    }\n  }\n\n  selectTrack(trackId: string | null): void {\n    if (trackId === this._selectedTrackId) return;\n    this._selectedTrackId = trackId;\n    this._emitStateChange();\n  }\n\n  // ---------------------------------------------------------------------------\n  // Clip Queries\n  // ---------------------------------------------------------------------------\n\n  /** Get a clip's full bounds for trim constraint computation. Returns null if not found. */\n  getClipBounds(\n    trackId: string,\n    clipId: string\n  ): {\n    offsetSamples: number;\n    durationSamples: number;\n    startSample: number;\n    sourceDurationSamples: number;\n  } | null {\n    const track = this._tracks.find((t) => t.id === trackId);\n    if (!track) return null;\n    const clip = track.clips.find((c: AudioClip) => c.id === clipId);\n    if (!clip) return null;\n    return {\n      offsetSamples: clip.offsetSamples,\n      durationSamples: clip.durationSamples,\n      startSample: clip.startSample,\n      sourceDurationSamples: clip.sourceDurationSamples,\n    };\n  }\n\n  /** Constrain a trim delta using the engine's collision/bounds logic.\n   *  Uses the clip's current state and neighboring clips for constraints. */\n  constrainTrimDelta(\n    trackId: string,\n    clipId: string,\n    boundary: 'left' | 'right',\n    deltaSamples: number\n  ): number {\n    const track = this._tracks.find((t) => t.id === trackId);\n    if (!track) return 0;\n    const clipIndex = track.clips.findIndex((c: AudioClip) => c.id === clipId);\n    if (clipIndex === -1) return 0;\n\n    const clip = track.clips[clipIndex];\n    const sortedClips = sortClipsByTime(track.clips);\n    const sortedIndex = sortedClips.findIndex((c: AudioClip) => c.id === clipId);\n    const minDuration = Math.floor(DEFAULT_MIN_DURATION_SECONDS * this._sampleRate);\n\n    return constrainBoundaryTrim(\n      clip,\n      deltaSamples,\n      boundary,\n      sortedClips,\n      sortedIndex,\n      minDuration\n    );\n  }\n\n  // ---------------------------------------------------------------------------\n  // Clip Editing (delegates to operations/)\n  // ---------------------------------------------------------------------------\n\n  /** Move a clip by deltaSamples. Returns the constrained delta actually applied (0 if no-op). */\n  moveClip(trackId: string, clipId: string, deltaSamples: number, skipAdapter = false): number {\n    const track = this._tracks.find((t) => t.id === trackId);\n    if (!track) {\n      console.warn(`[waveform-playlist/engine] moveClip: track \"${trackId}\" not found`);\n      return 0;\n    }\n\n    const clipIndex = track.clips.findIndex((c: AudioClip) => c.id === clipId);\n    if (clipIndex === -1) {\n      console.warn(\n        `[waveform-playlist/engine] moveClip: clip \"${clipId}\" not found in track \"${trackId}\"`\n      );\n      return 0;\n    }\n\n    const clip = track.clips[clipIndex];\n    const sortedClips = sortClipsByTime(track.clips);\n    const sortedIndex = sortedClips.findIndex((c: AudioClip) => c.id === clipId);\n\n    const constrainedDelta = constrainClipDrag(clip, deltaSamples, sortedClips, sortedIndex);\n\n    if (constrainedDelta === 0) return 0;\n\n    this._pushUndoSnapshot();\n\n    this._tracks = this._tracks.map((t) => {\n      if (t.id !== trackId) return t;\n      const newClips = t.clips.map((c: AudioClip, i: number) => {\n        if (i !== clipIndex) return c;\n        const newStartSample = Math.floor(c.startSample + constrainedDelta);\n        return {\n          ...c,\n          startSample: newStartSample,\n          startTick:\n            c.startTick !== undefined\n              ? this._secondsToTicks(newStartSample / this._sampleRate)\n              : undefined,\n        };\n      });\n      return { ...t, clips: newClips };\n    });\n\n    this._tracksVersion++;\n    if (!skipAdapter) {\n      this._updateTrackOnAdapter(trackId);\n    }\n    this._emitStateChange();\n    return constrainedDelta;\n  }\n\n  splitClip(trackId: string, clipId: string, atSample: number): void {\n    const track = this._tracks.find((t) => t.id === trackId);\n    if (!track) {\n      console.warn(`[waveform-playlist/engine] splitClip: track \"${trackId}\" not found`);\n      return;\n    }\n\n    const clipIndex = track.clips.findIndex((c: AudioClip) => c.id === clipId);\n    if (clipIndex === -1) {\n      console.warn(\n        `[waveform-playlist/engine] splitClip: clip \"${clipId}\" not found in track \"${trackId}\"`\n      );\n      return;\n    }\n\n    const clip = track.clips[clipIndex];\n    const minDuration = Math.floor(DEFAULT_MIN_DURATION_SECONDS * this._sampleRate);\n\n    if (!canSplitAt(clip, atSample, minDuration)) {\n      console.warn(\n        `[waveform-playlist/engine] splitClip: cannot split clip \"${clipId}\" at sample ${atSample} ` +\n          `(clip range: ${clip.startSample}–${clip.startSample + clip.durationSamples}, minDuration: ${minDuration})`\n      );\n      return;\n    }\n\n    this._pushUndoSnapshot();\n\n    const { left, right } = splitClipOp(clip, atSample);\n\n    // Enrich split clips with startTick if the original had one\n    const enrichedLeft =\n      clip.startTick !== undefined\n        ? { ...left, startTick: this._secondsToTicks(left.startSample / this._sampleRate) }\n        : left;\n    const enrichedRight =\n      clip.startTick !== undefined\n        ? { ...right, startTick: this._secondsToTicks(right.startSample / this._sampleRate) }\n        : right;\n\n    this._tracks = this._tracks.map((t) => {\n      if (t.id !== trackId) return t;\n      const newClips = [...t.clips];\n      newClips.splice(clipIndex, 1, enrichedLeft, enrichedRight);\n      return { ...t, clips: newClips };\n    });\n\n    this._tracksVersion++;\n    this._updateTrackOnAdapter(trackId);\n    this._emitStateChange();\n  }\n\n  trimClip(\n    trackId: string,\n    clipId: string,\n    boundary: 'left' | 'right',\n    deltaSamples: number,\n    skipAdapter = false\n  ): void {\n    const track = this._tracks.find((t) => t.id === trackId);\n    if (!track) {\n      console.warn(`[waveform-playlist/engine] trimClip: track \"${trackId}\" not found`);\n      return;\n    }\n\n    const clipIndex = track.clips.findIndex((c: AudioClip) => c.id === clipId);\n    if (clipIndex === -1) {\n      console.warn(\n        `[waveform-playlist/engine] trimClip: clip \"${clipId}\" not found in track \"${trackId}\"`\n      );\n      return;\n    }\n\n    const clip = track.clips[clipIndex];\n    const sortedClips = sortClipsByTime(track.clips);\n    const sortedIndex = sortedClips.findIndex((c: AudioClip) => c.id === clipId);\n    const minDuration = Math.floor(DEFAULT_MIN_DURATION_SECONDS * this._sampleRate);\n\n    const constrained = constrainBoundaryTrim(\n      clip,\n      deltaSamples,\n      boundary,\n      sortedClips,\n      sortedIndex,\n      minDuration\n    );\n\n    if (constrained === 0) return;\n\n    this._pushUndoSnapshot();\n\n    this._tracks = this._tracks.map((t) => {\n      if (t.id !== trackId) return t;\n      const newClips = t.clips.map((c: AudioClip, i: number) => {\n        if (i !== clipIndex) return c;\n        if (boundary === 'left') {\n          const newStartSample = c.startSample + constrained;\n          return {\n            ...c,\n            startSample: newStartSample,\n            startTick:\n              c.startTick !== undefined\n                ? this._secondsToTicks(newStartSample / this._sampleRate)\n                : undefined,\n            offsetSamples: c.offsetSamples + constrained,\n            durationSamples: c.durationSamples - constrained,\n          };\n        } else {\n          return { ...c, durationSamples: c.durationSamples + constrained };\n        }\n      });\n      return { ...t, clips: newClips };\n    });\n\n    this._tracksVersion++;\n    if (!skipAdapter) {\n      this._updateTrackOnAdapter(trackId);\n    }\n    this._emitStateChange();\n  }\n\n  // ---------------------------------------------------------------------------\n  // Playback (delegates to adapter, no-ops without adapter)\n  // ---------------------------------------------------------------------------\n\n  async init(): Promise<void> {\n    if (this._adapter) {\n      await this._adapter.init();\n    }\n  }\n\n  play(startTime?: number, endTime?: number): void {\n    const prevCurrentTime = this._currentTime;\n    const prevPlayStartPosition = this._playStartPosition;\n\n    if (startTime !== undefined) {\n      this._currentTime = Math.max(0, startTime);\n    }\n\n    // Remember where playback started (Audacity-style: stop returns here)\n    this._playStartPosition = this._currentTime;\n\n    if (this._adapter) {\n      // Configure loop state BEFORE play(). The adapter caches loopStart/\n      // loopEnd/enabled from setLoop(), then TonePlayout.play() applies\n      // them to the Transport before transport.start() and advances\n      // Clock._lastUpdate to skip stale ghost ticks.\n      if (endTime !== undefined) {\n        // Disable Transport loop for duration-limited playback (selection/annotation)\n        this._adapter.setLoop(false, this._loopStart, this._loopEnd);\n      } else if (this._isLoopEnabled) {\n        // Activate Transport loop if starting before loopEnd. Starting at or\n        // past loopEnd plays to the end without looping (click-past-loop behavior).\n        const beforeLoopEnd = this._currentTime < this._loopEnd;\n        this._adapter.setLoop(beforeLoopEnd, this._loopStart, this._loopEnd);\n      }\n      try {\n        this._adapter.play(this._currentTime, endTime);\n      } catch (err) {\n        // Restore state so the engine isn't left with a moved playhead\n        // but no audio. The throw propagates to the caller.\n        this._currentTime = prevCurrentTime;\n        this._playStartPosition = prevPlayStartPosition;\n        throw err;\n      }\n    }\n\n    this._isPlaying = true;\n    this._emit('play');\n    this._emitStateChange();\n  }\n\n  pause(): void {\n    this._isPlaying = false;\n    this._adapter?.pause();\n    if (this._adapter) {\n      this._currentTime = this._adapter.getCurrentTime();\n    }\n    this._emit('pause');\n    this._emitStateChange();\n  }\n\n  stop(): void {\n    this._isPlaying = false;\n    this._currentTime = this._playStartPosition;\n    this._adapter?.setLoop(false, this._loopStart, this._loopEnd);\n    this._adapter?.stop();\n    // Seek adapter to play-start position so getCurrentTime() returns it\n    this._adapter?.seek(this._currentTime);\n    this._emit('stop');\n    this._emitStateChange();\n  }\n\n  seek(time: number): void {\n    this._currentTime = Math.max(0, time);\n    this._adapter?.seek(this._currentTime);\n    this._emitStateChange();\n  }\n\n  setMasterVolume(volume: number): void {\n    if (volume === this._masterVolume) return;\n    this._masterVolume = volume;\n    this._adapter?.setMasterVolume(volume);\n    this._emitStateChange();\n  }\n\n  setTempo(bpm: number, atTick?: number): void {\n    if (!Number.isFinite(bpm) || bpm <= 0) {\n      console.warn('[waveform-playlist/engine] setTempo: bpm must be a positive finite number');\n      return;\n    }\n    if (bpm === this._bpm && atTick === undefined) return;\n    // Commit only when the adapter accepts (an explicit `false` means refused,\n    // e.g. the transport's multi-entry tempo-map guard — #407). Void-returning\n    // and absent adapters count as accepted.\n    const accepted = this._adapter?.setTempo?.(bpm, atTick);\n    if (accepted === false) return;\n    this._bpm = bpm;\n    this._recomputeStartSamples();\n    this._emitStateChange();\n  }\n\n  getCurrentTime(): number {\n    if (this._adapter) {\n      return this._adapter.getCurrentTime();\n    }\n    return this._currentTime;\n  }\n\n  /**\n   * Audio scheduling lookahead in seconds — `getCurrentTime()` is this far ahead of\n   * what the listener actually hears. Tone.js Transport reports a position that's\n   * `lookAhead` ahead of audible (default 0.1s); native AudioContext-based adapters\n   * have no lookahead and return 0. Visual consumers should use `getAudibleTime()`,\n   * which applies this compensation only while playing.\n   */\n  get lookAhead(): number {\n    return this._adapter?.lookAhead ?? 0;\n  }\n\n  /**\n   * Playback position aligned with what the listener actually hears, for\n   * visual consumers (playhead, progress overlays, auto-scroll).\n   *\n   * While playing: `getCurrentTime() − outputLatency − lookAhead`, held at\n   * the play-start position during the pre-roll window (audio at the start\n   * position isn't audible until ~outputLatency + lookAhead after `play()`,\n   * so the cursor waits there instead of jumping backward).\n   *\n   * While not playing: the raw resting position. A stationary cursor has no\n   * audible counterpart — seek/stop/pause positions display exactly.\n   *\n   * Storage stays raw: never feed this value back into `play()`/`seek()`,\n   * which compounds the subtraction on every cycle. Use `getCurrentTime()`\n   * for storage and resume positions.\n   */\n  getAudibleTime(): number {\n    if (!this._isPlaying) {\n      return this._currentTime;\n    }\n    const ctx = this._adapter?.audioContext;\n    const outputLatency =\n      ctx && 'outputLatency' in ctx ? ((ctx as AudioContext).outputLatency ?? 0) : 0;\n    const raw = this.getCurrentTime();\n    let t = raw - outputLatency - this.lookAhead;\n    if (raw >= this._playStartPosition && t < this._playStartPosition) {\n      t = this._playStartPosition;\n    }\n    return Number.isFinite(t) ? Math.max(0, t) : 0;\n  }\n\n  // ---------------------------------------------------------------------------\n  // Selection & Loop\n  // ---------------------------------------------------------------------------\n\n  setSelection(start: number, end: number): void {\n    const s = Math.min(start, end);\n    const e = Math.max(start, end);\n    if (s === this._selectionStart && e === this._selectionEnd) return;\n    this._selectionStart = s;\n    this._selectionEnd = e;\n    this._emitStateChange();\n  }\n\n  setLoopRegion(start: number, end: number): void {\n    const s = Math.min(start, end);\n    const e = Math.max(start, end);\n    if (s === this._loopStart && e === this._loopEnd) return;\n    this._loopStart = s;\n    this._loopEnd = e;\n    this._adapter?.setLoop(\n      this._isLoopEnabled && this._isBeforeLoopEnd(),\n      this._loopStart,\n      this._loopEnd\n    );\n    this._emitStateChange();\n  }\n\n  setLoopEnabled(enabled: boolean): void {\n    if (enabled === this._isLoopEnabled) return;\n    this._isLoopEnabled = enabled;\n    this._adapter?.setLoop(enabled && this._isBeforeLoopEnd(), this._loopStart, this._loopEnd);\n    this._emitStateChange();\n  }\n\n  // ---------------------------------------------------------------------------\n  // Per-Track Audio (forwards to adapter, persists into _tracks, emits)\n  // ---------------------------------------------------------------------------\n  //\n  // Each setter forwards to the adapter, mirrors the change into the engine's\n  // own _tracks (so a later adapter rebuild via setTracks keeps the value),\n  // and bumps _mixerVersion + emits statechange. These are NOT structural\n  // changes, so they deliberately do NOT bump _tracksVersion — that would make\n  // tracksVersion-gated consumers re-run expensive structural work (audio-graph\n  // rewire, peak regeneration) on every volume/pan drag frame. Consumers that\n  // cache a track snapshot (e.g. @dawcore/components' <daw-editor>) watch\n  // _mixerVersion to refresh that cache cheaply, so an unrelated setTracks\n  // rebuild no longer reverts live mixer state (#501).\n\n  setTrackVolume(trackId: string, volume: number): void {\n    const track = this._tracks.find((t) => t.id === trackId);\n    if (track) track.volume = volume;\n    this._adapter?.setTrackVolume(trackId, volume);\n    this._mixerVersion++;\n    this._emitStateChange();\n  }\n\n  setTrackMute(trackId: string, muted: boolean): void {\n    const track = this._tracks.find((t) => t.id === trackId);\n    if (track) track.muted = muted;\n    this._adapter?.setTrackMute(trackId, muted);\n    this._mixerVersion++;\n    this._emitStateChange();\n  }\n\n  setTrackSolo(trackId: string, soloed: boolean): void {\n    const track = this._tracks.find((t) => t.id === trackId);\n    if (track) track.soloed = soloed;\n    this._adapter?.setTrackSolo(trackId, soloed);\n    this._mixerVersion++;\n    this._emitStateChange();\n  }\n\n  setTrackPan(trackId: string, pan: number): void {\n    const track = this._tracks.find((t) => t.id === trackId);\n    if (track) track.pan = pan;\n    this._adapter?.setTrackPan(trackId, pan);\n    this._mixerVersion++;\n    this._emitStateChange();\n  }\n\n  // ---------------------------------------------------------------------------\n  // Zoom\n  // ---------------------------------------------------------------------------\n\n  zoomIn(): void {\n    if (this._zoomIndex > 0) {\n      this._zoomIndex--;\n      this._emitStateChange();\n    }\n  }\n\n  zoomOut(): void {\n    if (this._zoomIndex < this._zoomLevels.length - 1) {\n      this._zoomIndex++;\n      this._emitStateChange();\n    }\n  }\n\n  setZoomLevel(samplesPerPixel: number): void {\n    const newIndex = findClosestZoomIndex(samplesPerPixel, this._zoomLevels);\n    if (newIndex === this._zoomIndex) return;\n    this._zoomIndex = newIndex;\n    this._emitStateChange();\n  }\n\n  // ---------------------------------------------------------------------------\n  // Events\n  // ---------------------------------------------------------------------------\n\n  on<K extends EventName>(event: K, listener: EngineEvents[K]): void {\n    if (!this._listeners.has(event)) {\n      this._listeners.set(event, new Set());\n    }\n    this._listeners.get(event)!.add(listener);\n  }\n\n  off<K extends EventName>(event: K, listener: EngineEvents[K]): void {\n    this._listeners.get(event)?.delete(listener);\n  }\n\n  // ---------------------------------------------------------------------------\n  // Lifecycle\n  // ---------------------------------------------------------------------------\n\n  dispose(): void {\n    if (this._disposed) return;\n    this._disposed = true;\n    this._isPlaying = false;\n    this._adapter?.onPlaybackEnded?.(null);\n    try {\n      this._adapter?.dispose();\n    } catch (err) {\n      console.warn('[waveform-playlist/engine] Error disposing adapter:', err);\n    }\n    this._listeners.clear();\n  }\n\n  // ---------------------------------------------------------------------------\n  // Private helpers\n  // ---------------------------------------------------------------------------\n\n  private _snapshotTracks(): ClipTrack[] {\n    return this._tracks.map((t) => ({ ...t, clips: t.clips.map((c) => ({ ...c })) }));\n  }\n\n  private _pushUndoSnapshot(): void {\n    if (this._inTransaction) {\n      this._transactionMutated = true;\n      return;\n    }\n    this._undoStack.push(this._snapshotTracks());\n    if (this._undoStack.length > this.undoLimit) {\n      this._undoStack.shift();\n    }\n    this._redoStack = [];\n  }\n\n  private _restoreTracks(snapshot: ClipTrack[]): void {\n    const oldTracks = this._tracks;\n    this._tracks = snapshot;\n    this._tracksVersion++;\n    // Use incremental adapter updates when track count is unchanged —\n    // avoids full playout rebuild that interrupts playback during undo/redo.\n    if (this._adapter && oldTracks.length === snapshot.length) {\n      for (let i = 0; i < snapshot.length; i++) {\n        if (oldTracks[i] !== snapshot[i]) {\n          this._updateTrackOnAdapter(snapshot[i].id);\n        }\n      }\n    } else {\n      this._adapter?.setTracks(this._tracks);\n    }\n    this._emitStateChange();\n  }\n\n  private _emit(event: string, ...args: unknown[]): void {\n    const listeners = this._listeners.get(event);\n    if (listeners) {\n      for (const listener of listeners) {\n        try {\n          listener(...args);\n        } catch (error) {\n          console.warn('[waveform-playlist/engine] Error in event listener:', error);\n        }\n      }\n    }\n  }\n\n  /**\n   * Returns whether the current playback position is before loopEnd.\n   * Used by setLoopEnabled/setLoopRegion during playback — if past loopEnd,\n   * Transport loop stays off so playback continues to the end.\n   * Note: play() uses an inline check instead — _isPlaying is still false\n   * when play() runs, and this method returns true unconditionally when\n   * not playing.\n   */\n  private _isBeforeLoopEnd(): boolean {\n    if (!this._isPlaying) return true;\n    const t = this._adapter?.getCurrentTime() ?? this._currentTime;\n    return t < this._loopEnd;\n  }\n\n  private _ticksToSeconds(tick: number): number {\n    if (this._adapter?.ticksToSeconds) {\n      return this._adapter.ticksToSeconds(tick);\n    }\n    return (tick * 60) / (this._ppqn * this._bpm);\n  }\n\n  private _secondsToTicks(seconds: number): number {\n    if (this._adapter?.secondsToTicks) {\n      return this._adapter.secondsToTicks(seconds);\n    }\n    return Math.round((seconds * this._ppqn * this._bpm) / 60);\n  }\n\n  private _recomputeStartSamples(): void {\n    this._tracks = this._tracks.map((track) => ({\n      ...track,\n      clips: track.clips.map((clip) => {\n        if (clip.startTick === undefined) return clip;\n        return {\n          ...clip,\n          startSample: Math.round(this._ticksToSeconds(clip.startTick) * this._sampleRate),\n        };\n      }),\n    }));\n    this._tracksVersion++;\n    // Use incremental updates to avoid full playout rebuild during playback\n    for (const track of this._tracks) {\n      this._updateTrackOnAdapter(track.id);\n    }\n  }\n\n  private _emitStateChange(): void {\n    this._emit('statechange', this.getState());\n  }\n}\n"],"mappings":"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","names":[]}