@dawcore/components 0.0.20 → 0.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1196,8 +1196,8 @@ DawStopButtonElement = __decorateClass([
1196
1196
  ], DawStopButtonElement);
1197
1197
 
1198
1198
  // src/elements/daw-editor.ts
1199
- import { LitElement as LitElement10, html as html9, css as css9 } from "lit";
1200
- import { customElement as customElement12, property as property8, state as state3 } from "lit/decorators.js";
1199
+ import { LitElement as LitElement11, html as html10, css as css10 } from "lit";
1200
+ import { customElement as customElement13, property as property9, state as state3 } from "lit/decorators.js";
1201
1201
 
1202
1202
  // src/types.ts
1203
1203
  function isDomClip(desc) {
@@ -1681,10 +1681,268 @@ var PeakPipeline = class {
1681
1681
  }
1682
1682
  };
1683
1683
 
1684
- // src/elements/daw-track-controls.ts
1684
+ // src/elements/daw-ruler.ts
1685
1685
  import { LitElement as LitElement8, html as html7, css as css6 } from "lit";
1686
1686
  import { customElement as customElement10, property as property6 } from "lit/decorators.js";
1687
- var DawTrackControlsElement = class extends LitElement8 {
1687
+
1688
+ // src/utils/time-format.ts
1689
+ function formatTime(milliseconds) {
1690
+ const seconds = Math.floor(milliseconds / 1e3);
1691
+ const s = seconds % 60;
1692
+ const m = (seconds - s) / 60;
1693
+ return `${m}:${String(s).padStart(2, "0")}`;
1694
+ }
1695
+
1696
+ // src/utils/smart-scale.ts
1697
+ var timeinfo = /* @__PURE__ */ new Map([
1698
+ [700, { marker: 1e3, bigStep: 500, smallStep: 100 }],
1699
+ [1500, { marker: 2e3, bigStep: 1e3, smallStep: 200 }],
1700
+ [2500, { marker: 2e3, bigStep: 1e3, smallStep: 500 }],
1701
+ [5e3, { marker: 5e3, bigStep: 1e3, smallStep: 500 }],
1702
+ [1e4, { marker: 1e4, bigStep: 5e3, smallStep: 1e3 }],
1703
+ [12e3, { marker: 15e3, bigStep: 5e3, smallStep: 1e3 }],
1704
+ [Infinity, { marker: 3e4, bigStep: 1e4, smallStep: 5e3 }]
1705
+ ]);
1706
+ function getScaleInfo(samplesPerPixel) {
1707
+ for (const [resolution, config] of timeinfo) {
1708
+ if (samplesPerPixel < resolution) {
1709
+ return config;
1710
+ }
1711
+ }
1712
+ return { marker: 3e4, bigStep: 1e4, smallStep: 5e3 };
1713
+ }
1714
+ function computeTemporalTicks(samplesPerPixel, sampleRate, duration, rulerHeight) {
1715
+ const widthX = Math.ceil(duration * sampleRate / samplesPerPixel);
1716
+ const config = getScaleInfo(samplesPerPixel);
1717
+ const { marker, bigStep, smallStep } = config;
1718
+ const canvasInfo = /* @__PURE__ */ new Map();
1719
+ const labels = [];
1720
+ const pixPerSec = sampleRate / samplesPerPixel;
1721
+ for (let counter = 0; ; counter += smallStep) {
1722
+ const pix = Math.floor(counter / 1e3 * pixPerSec);
1723
+ if (pix >= widthX) break;
1724
+ if (counter % marker === 0) {
1725
+ canvasInfo.set(pix, rulerHeight);
1726
+ labels.push({ pix, text: formatTime(counter) });
1727
+ } else if (counter % bigStep === 0) {
1728
+ canvasInfo.set(pix, Math.floor(rulerHeight / 2));
1729
+ } else if (counter % smallStep === 0) {
1730
+ canvasInfo.set(pix, Math.floor(rulerHeight / 5));
1731
+ }
1732
+ }
1733
+ return { widthX, canvasInfo, labels };
1734
+ }
1735
+
1736
+ // src/utils/musical-tick-cache.ts
1737
+ import { computeMusicalTicks } from "@waveform-playlist/core";
1738
+ var cachedParams = null;
1739
+ var cachedResult = null;
1740
+ function meterEntriesMatch(a, b) {
1741
+ if (a.length !== b.length) return false;
1742
+ for (let i = 0; i < a.length; i++) {
1743
+ if (a[i].tick !== b[i].tick || a[i].numerator !== b[i].numerator || a[i].denominator !== b[i].denominator)
1744
+ return false;
1745
+ }
1746
+ return true;
1747
+ }
1748
+ function paramsMatch(a, b) {
1749
+ return a.ticksPerPixel === b.ticksPerPixel && a.startPixel === b.startPixel && a.endPixel === b.endPixel && meterEntriesMatch(a.meterEntries, b.meterEntries) && (a.ppqn ?? 960) === (b.ppqn ?? 960);
1750
+ }
1751
+ function getCachedMusicalTicks(params) {
1752
+ if (cachedParams && cachedResult && paramsMatch(cachedParams, params)) {
1753
+ return cachedResult;
1754
+ }
1755
+ cachedResult = computeMusicalTicks(params);
1756
+ cachedParams = {
1757
+ ...params,
1758
+ meterEntries: params.meterEntries.map((e) => ({ ...e }))
1759
+ };
1760
+ return cachedResult;
1761
+ }
1762
+
1763
+ // src/elements/daw-ruler.ts
1764
+ var MAX_CANVAS_WIDTH3 = 1e3;
1765
+ var DawRulerElement = class extends LitElement8 {
1766
+ constructor() {
1767
+ super(...arguments);
1768
+ this.samplesPerPixel = 1024;
1769
+ this.sampleRate = 48e3;
1770
+ this.duration = 0;
1771
+ this.rulerHeight = 30;
1772
+ this.scaleMode = "temporal";
1773
+ this.ticksPerPixel = 4;
1774
+ this.meterEntries = [
1775
+ { tick: 0, numerator: 4, denominator: 4 }
1776
+ ];
1777
+ this.ppqn = 960;
1778
+ this.totalWidth = 0;
1779
+ this._tickData = null;
1780
+ this._musicalTickData = null;
1781
+ }
1782
+ willUpdate() {
1783
+ if (this.scaleMode === "beats" && this.totalWidth > 0) {
1784
+ this._musicalTickData = getCachedMusicalTicks({
1785
+ meterEntries: this.meterEntries,
1786
+ ticksPerPixel: this.ticksPerPixel,
1787
+ startPixel: 0,
1788
+ endPixel: this.totalWidth,
1789
+ ppqn: this.ppqn
1790
+ });
1791
+ this._tickData = null;
1792
+ } else if (this.duration > 0 || this.totalWidth > 0) {
1793
+ const widthDerivedDuration = this.totalWidth * this.samplesPerPixel / this.sampleRate;
1794
+ const effectiveDuration = Math.max(this.duration, widthDerivedDuration);
1795
+ this._musicalTickData = null;
1796
+ this._tickData = computeTemporalTicks(
1797
+ this.samplesPerPixel,
1798
+ this.sampleRate,
1799
+ effectiveDuration,
1800
+ this.rulerHeight
1801
+ );
1802
+ } else {
1803
+ this._musicalTickData = null;
1804
+ this._tickData = null;
1805
+ }
1806
+ }
1807
+ render() {
1808
+ const widthX = this.scaleMode === "beats" ? this.totalWidth : this._tickData?.widthX ?? 0;
1809
+ if (widthX <= 0) return html7``;
1810
+ const totalChunks = Math.ceil(widthX / MAX_CANVAS_WIDTH3);
1811
+ const indices = Array.from({ length: totalChunks }, (_, i) => i);
1812
+ const dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
1813
+ const beatsLabels = this.scaleMode === "beats" ? this._musicalTickData?.ticks.filter((t) => t.label) ?? [] : [];
1814
+ const temporalLabels = this.scaleMode !== "beats" ? this._tickData?.labels ?? [] : [];
1815
+ return html7`
1816
+ <div class="container" style="width: ${widthX}px; height: ${this.rulerHeight}px;">
1817
+ ${indices.map((i) => {
1818
+ const width = Math.min(MAX_CANVAS_WIDTH3, widthX - i * MAX_CANVAS_WIDTH3);
1819
+ return html7`
1820
+ <canvas
1821
+ data-index=${i}
1822
+ width=${width * dpr}
1823
+ height=${this.rulerHeight * dpr}
1824
+ style="left: ${i * MAX_CANVAS_WIDTH3}px; width: ${width}px; height: ${this.rulerHeight}px;"
1825
+ ></canvas>
1826
+ `;
1827
+ })}
1828
+ ${this.scaleMode === "beats" ? beatsLabels.map(
1829
+ (t) => html7`<span
1830
+ class="label ${t.pixel > 0 ? "centered" : ""}"
1831
+ style="left: ${t.pixel > 0 ? t.pixel : t.pixel + 4}px;"
1832
+ >${t.label}</span
1833
+ >`
1834
+ ) : temporalLabels.map(
1835
+ ({ pix, text }) => html7`<span class="label" style="left: ${pix + 4}px;">${text}</span>`
1836
+ )}
1837
+ </div>
1838
+ `;
1839
+ }
1840
+ updated() {
1841
+ this._drawTicks();
1842
+ }
1843
+ _drawTicks() {
1844
+ const canvases = this.shadowRoot?.querySelectorAll("canvas");
1845
+ if (!canvases) return;
1846
+ const dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
1847
+ const rulerColor = getComputedStyle(this).getPropertyValue("--daw-ruler-color").trim() || "#c49a6c";
1848
+ const widthX = this.scaleMode === "beats" ? this.totalWidth : this._tickData?.widthX ?? 0;
1849
+ for (const canvas of canvases) {
1850
+ const idx = Number(canvas.dataset.index);
1851
+ const ctx = canvas.getContext("2d");
1852
+ if (!ctx) continue;
1853
+ const canvasWidth = Math.min(MAX_CANVAS_WIDTH3, widthX - idx * MAX_CANVAS_WIDTH3);
1854
+ const globalOffset = idx * MAX_CANVAS_WIDTH3;
1855
+ ctx.resetTransform();
1856
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
1857
+ ctx.scale(dpr, dpr);
1858
+ ctx.strokeStyle = rulerColor;
1859
+ ctx.lineWidth = 1;
1860
+ if (this.scaleMode === "beats" && this._musicalTickData) {
1861
+ const h = this.rulerHeight;
1862
+ for (const tick of this._musicalTickData.ticks) {
1863
+ const localX = tick.pixel - globalOffset;
1864
+ if (localX < 0 || localX >= canvasWidth) continue;
1865
+ const tickH = tick.type === "major" ? h * 0.6 : tick.type === "minor" ? h * 0.35 : h * 0.15;
1866
+ ctx.globalAlpha = tick.type === "major" ? 1 : 0.5;
1867
+ ctx.beginPath();
1868
+ ctx.moveTo(localX + 0.5, h);
1869
+ ctx.lineTo(localX + 0.5, h - tickH);
1870
+ ctx.stroke();
1871
+ }
1872
+ ctx.globalAlpha = 1;
1873
+ } else if (this._tickData) {
1874
+ for (const [pix, height] of this._tickData.canvasInfo) {
1875
+ const localX = pix - globalOffset;
1876
+ if (localX < 0 || localX >= canvasWidth) continue;
1877
+ ctx.beginPath();
1878
+ ctx.moveTo(localX + 0.5, this.rulerHeight);
1879
+ ctx.lineTo(localX + 0.5, this.rulerHeight - height);
1880
+ ctx.stroke();
1881
+ }
1882
+ }
1883
+ }
1884
+ }
1885
+ };
1886
+ DawRulerElement.styles = css6`
1887
+ :host {
1888
+ display: block;
1889
+ position: relative;
1890
+ background: var(--daw-ruler-background, #0f0f1a);
1891
+ }
1892
+ .container {
1893
+ position: relative;
1894
+ }
1895
+ canvas {
1896
+ position: absolute;
1897
+ top: 0;
1898
+ }
1899
+ .label {
1900
+ position: absolute;
1901
+ font-size: 0.7rem;
1902
+ line-height: 1;
1903
+ white-space: nowrap;
1904
+ color: var(--daw-ruler-color, #c49a6c);
1905
+ top: 1px;
1906
+ }
1907
+ .label.centered {
1908
+ transform: translateX(-50%);
1909
+ }
1910
+ `;
1911
+ __decorateClass([
1912
+ property6({ type: Number, attribute: false })
1913
+ ], DawRulerElement.prototype, "samplesPerPixel", 2);
1914
+ __decorateClass([
1915
+ property6({ type: Number, attribute: false })
1916
+ ], DawRulerElement.prototype, "sampleRate", 2);
1917
+ __decorateClass([
1918
+ property6({ type: Number, attribute: false })
1919
+ ], DawRulerElement.prototype, "duration", 2);
1920
+ __decorateClass([
1921
+ property6({ type: Number, attribute: false })
1922
+ ], DawRulerElement.prototype, "rulerHeight", 2);
1923
+ __decorateClass([
1924
+ property6({ type: String, attribute: false })
1925
+ ], DawRulerElement.prototype, "scaleMode", 2);
1926
+ __decorateClass([
1927
+ property6({ type: Number, attribute: false })
1928
+ ], DawRulerElement.prototype, "ticksPerPixel", 2);
1929
+ __decorateClass([
1930
+ property6({ attribute: false })
1931
+ ], DawRulerElement.prototype, "meterEntries", 2);
1932
+ __decorateClass([
1933
+ property6({ type: Number, attribute: false })
1934
+ ], DawRulerElement.prototype, "ppqn", 2);
1935
+ __decorateClass([
1936
+ property6({ type: Number, attribute: false })
1937
+ ], DawRulerElement.prototype, "totalWidth", 2);
1938
+ DawRulerElement = __decorateClass([
1939
+ customElement10("daw-ruler")
1940
+ ], DawRulerElement);
1941
+
1942
+ // src/elements/daw-track-controls.ts
1943
+ import { LitElement as LitElement9, html as html8, css as css7 } from "lit";
1944
+ import { customElement as customElement11, property as property7 } from "lit/decorators.js";
1945
+ var DawTrackControlsElement = class extends LitElement9 {
1688
1946
  constructor() {
1689
1947
  super(...arguments);
1690
1948
  this.trackId = null;
@@ -1718,6 +1976,17 @@ var DawTrackControlsElement = class extends LitElement8 {
1718
1976
  );
1719
1977
  };
1720
1978
  }
1979
+ firstUpdated() {
1980
+ requestAnimationFrame(() => {
1981
+ if (!this.isConnected) return;
1982
+ const rect = this.getBoundingClientRect();
1983
+ if (rect.width > 0 && rect.height === 0) {
1984
+ console.warn(
1985
+ "[dawcore] <daw-track-controls> has zero height: container-type: size requires an explicit height on the element (the editor sets one automatically; standalone usage must too). The controls are currently invisible."
1986
+ );
1987
+ }
1988
+ });
1989
+ }
1721
1990
  _dispatchControl(prop, value) {
1722
1991
  if (!this.trackId) return;
1723
1992
  this.dispatchEvent(
@@ -1732,7 +2001,7 @@ var DawTrackControlsElement = class extends LitElement8 {
1732
2001
  const volPercent = Math.round(this.volume * 100);
1733
2002
  const panPercent = Math.round(Math.abs(this.pan) * 100);
1734
2003
  const panDisplay = this.pan === 0 ? "C" : (this.pan > 0 ? "R" : "L") + panPercent;
1735
- return html7`
2004
+ return html8`
1736
2005
  <div class="header">
1737
2006
  <span class="name" title=${this.trackName}>${this.trackName || "Untitled"}</span>
1738
2007
  <button class="remove-btn" @click=${this._onRemoveClick} title="Remove track">
@@ -1751,7 +2020,7 @@ var DawTrackControlsElement = class extends LitElement8 {
1751
2020
  S
1752
2021
  </button>
1753
2022
  </div>
1754
- <div class="slider-row">
2023
+ <div class="slider-row vol-row">
1755
2024
  <span class="slider-label">
1756
2025
  <span class="slider-label-name">Vol</span>
1757
2026
  <span class="slider-label-value">${volPercent}%</span>
@@ -1765,7 +2034,7 @@ var DawTrackControlsElement = class extends LitElement8 {
1765
2034
  @input=${this._onVolumeInput}
1766
2035
  />
1767
2036
  </div>
1768
- <div class="slider-row">
2037
+ <div class="slider-row pan-row">
1769
2038
  <span class="slider-label">
1770
2039
  <span class="slider-label-name">Pan</span>
1771
2040
  <span class="slider-label-value">${panDisplay}</span>
@@ -1782,7 +2051,7 @@ var DawTrackControlsElement = class extends LitElement8 {
1782
2051
  `;
1783
2052
  }
1784
2053
  };
1785
- DawTrackControlsElement.styles = css6`
2054
+ DawTrackControlsElement.styles = css7`
1786
2055
  :host {
1787
2056
  display: flex;
1788
2057
  flex-direction: column;
@@ -1795,6 +2064,7 @@ DawTrackControlsElement.styles = css6`
1795
2064
  font-family: system-ui, sans-serif;
1796
2065
  font-size: 11px;
1797
2066
  overflow: hidden;
2067
+ container-type: size;
1798
2068
  }
1799
2069
  .header {
1800
2070
  display: flex;
@@ -1914,64 +2184,52 @@ DawTrackControlsElement.styles = css6`
1914
2184
  border: none;
1915
2185
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
1916
2186
  }
2187
+ /* Compact modes: drop sliders when the row is too short for the full
2188
+ stack. Thresholds are CONTENT-BOX heights — the host is border-box
2189
+ with 12px vertical padding + 1px border, so an editor-given height H
2190
+ enters compact mode at H <= 89px (Pan hidden) and H <= 73px (Vol also
2191
+ hidden). NOTE: container-type: size requires an explicit height on
2192
+ the host — the editor always provides one; standalone consumers must
2193
+ too (see the firstUpdated guard). */
2194
+ @container (max-height: 76px) {
2195
+ .pan-row {
2196
+ display: none;
2197
+ }
2198
+ }
2199
+ @container (max-height: 60px) {
2200
+ .vol-row {
2201
+ display: none;
2202
+ }
2203
+ }
1917
2204
  `;
1918
2205
  __decorateClass([
1919
- property6({ attribute: false })
2206
+ property7({ attribute: false })
1920
2207
  ], DawTrackControlsElement.prototype, "trackId", 2);
1921
2208
  __decorateClass([
1922
- property6({ attribute: false })
2209
+ property7({ attribute: false })
1923
2210
  ], DawTrackControlsElement.prototype, "trackName", 2);
1924
2211
  __decorateClass([
1925
- property6({ type: Number, attribute: false })
2212
+ property7({ type: Number, attribute: false })
1926
2213
  ], DawTrackControlsElement.prototype, "volume", 2);
1927
2214
  __decorateClass([
1928
- property6({ type: Number, attribute: false })
2215
+ property7({ type: Number, attribute: false })
1929
2216
  ], DawTrackControlsElement.prototype, "pan", 2);
1930
2217
  __decorateClass([
1931
- property6({ type: Boolean, attribute: false })
2218
+ property7({ type: Boolean, attribute: false })
1932
2219
  ], DawTrackControlsElement.prototype, "muted", 2);
1933
2220
  __decorateClass([
1934
- property6({ type: Boolean, attribute: false })
2221
+ property7({ type: Boolean, attribute: false })
1935
2222
  ], DawTrackControlsElement.prototype, "soloed", 2);
1936
2223
  DawTrackControlsElement = __decorateClass([
1937
- customElement10("daw-track-controls")
2224
+ customElement11("daw-track-controls")
1938
2225
  ], DawTrackControlsElement);
1939
2226
 
1940
2227
  // src/elements/daw-grid.ts
1941
- import { LitElement as LitElement9, html as html8, css as css7 } from "lit";
1942
- import { customElement as customElement11, property as property7 } from "lit/decorators.js";
2228
+ import { LitElement as LitElement10, html as html9, css as css8 } from "lit";
2229
+ import { customElement as customElement12, property as property8 } from "lit/decorators.js";
1943
2230
  import { MIN_PIXELS_PER_UNIT } from "@waveform-playlist/core";
1944
-
1945
- // src/utils/musical-tick-cache.ts
1946
- import { computeMusicalTicks } from "@waveform-playlist/core";
1947
- var cachedParams = null;
1948
- var cachedResult = null;
1949
- function meterEntriesMatch(a, b) {
1950
- if (a.length !== b.length) return false;
1951
- for (let i = 0; i < a.length; i++) {
1952
- if (a[i].tick !== b[i].tick || a[i].numerator !== b[i].numerator || a[i].denominator !== b[i].denominator)
1953
- return false;
1954
- }
1955
- return true;
1956
- }
1957
- function paramsMatch(a, b) {
1958
- return a.ticksPerPixel === b.ticksPerPixel && a.startPixel === b.startPixel && a.endPixel === b.endPixel && meterEntriesMatch(a.meterEntries, b.meterEntries) && (a.ppqn ?? 960) === (b.ppqn ?? 960);
1959
- }
1960
- function getCachedMusicalTicks(params) {
1961
- if (cachedParams && cachedResult && paramsMatch(cachedParams, params)) {
1962
- return cachedResult;
1963
- }
1964
- cachedResult = computeMusicalTicks(params);
1965
- cachedParams = {
1966
- ...params,
1967
- meterEntries: params.meterEntries.map((e) => ({ ...e }))
1968
- };
1969
- return cachedResult;
1970
- }
1971
-
1972
- // src/elements/daw-grid.ts
1973
- var MAX_CANVAS_WIDTH3 = 1e3;
1974
- var DawGridElement = class extends LitElement9 {
2231
+ var MAX_CANVAS_WIDTH4 = 1e3;
2232
+ var DawGridElement = class extends LitElement10 {
1975
2233
  constructor() {
1976
2234
  super(...arguments);
1977
2235
  this.ticksPerPixel = 24;
@@ -1999,25 +2257,25 @@ var DawGridElement = class extends LitElement9 {
1999
2257
  }
2000
2258
  }
2001
2259
  render() {
2002
- if (!this._tickData) return html8``;
2260
+ if (!this._tickData) return html9``;
2003
2261
  const totalWidth = this.length;
2004
2262
  const dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
2005
2263
  const indices = getVisibleChunkIndices(
2006
2264
  totalWidth,
2007
- MAX_CANVAS_WIDTH3,
2265
+ MAX_CANVAS_WIDTH4,
2008
2266
  this.visibleStart,
2009
2267
  this.visibleEnd
2010
2268
  );
2011
- return html8`
2269
+ return html9`
2012
2270
  <div class="container" style="width: ${totalWidth}px; height: ${this.height}px;">
2013
2271
  ${indices.map((i) => {
2014
- const width = Math.min(MAX_CANVAS_WIDTH3, totalWidth - i * MAX_CANVAS_WIDTH3);
2015
- return html8`
2272
+ const width = Math.min(MAX_CANVAS_WIDTH4, totalWidth - i * MAX_CANVAS_WIDTH4);
2273
+ return html9`
2016
2274
  <canvas
2017
2275
  data-index=${i}
2018
2276
  width=${width * dpr}
2019
2277
  height=${this.height * dpr}
2020
- style="left: ${i * MAX_CANVAS_WIDTH3}px; width: ${width}px; height: ${this.height}px;"
2278
+ style="left: ${i * MAX_CANVAS_WIDTH4}px; width: ${width}px; height: ${this.height}px;"
2021
2279
  ></canvas>
2022
2280
  `;
2023
2281
  })}
@@ -2041,8 +2299,8 @@ var DawGridElement = class extends LitElement9 {
2041
2299
  const idx = Number(canvas.dataset.index);
2042
2300
  const ctx = canvas.getContext("2d");
2043
2301
  if (!ctx) continue;
2044
- const chunkLeft = idx * MAX_CANVAS_WIDTH3;
2045
- const canvasWidth = Math.min(MAX_CANVAS_WIDTH3, this.length - chunkLeft);
2302
+ const chunkLeft = idx * MAX_CANVAS_WIDTH4;
2303
+ const canvasWidth = Math.min(MAX_CANVAS_WIDTH4, this.length - chunkLeft);
2046
2304
  ctx.resetTransform();
2047
2305
  ctx.clearRect(0, 0, canvas.width, canvas.height);
2048
2306
  ctx.scale(dpr, dpr);
@@ -2073,7 +2331,7 @@ var DawGridElement = class extends LitElement9 {
2073
2331
  }
2074
2332
  }
2075
2333
  };
2076
- DawGridElement.styles = css7`
2334
+ DawGridElement.styles = css8`
2077
2335
  :host {
2078
2336
  display: block;
2079
2337
  position: absolute;
@@ -2091,33 +2349,33 @@ DawGridElement.styles = css7`
2091
2349
  }
2092
2350
  `;
2093
2351
  __decorateClass([
2094
- property7({ type: Number, attribute: false })
2352
+ property8({ type: Number, attribute: false })
2095
2353
  ], DawGridElement.prototype, "ticksPerPixel", 2);
2096
2354
  __decorateClass([
2097
- property7({ attribute: false })
2355
+ property8({ attribute: false })
2098
2356
  ], DawGridElement.prototype, "meterEntries", 2);
2099
2357
  __decorateClass([
2100
- property7({ type: Number, attribute: false })
2358
+ property8({ type: Number, attribute: false })
2101
2359
  ], DawGridElement.prototype, "ppqn", 2);
2102
2360
  __decorateClass([
2103
- property7({ type: Number, attribute: false })
2361
+ property8({ type: Number, attribute: false })
2104
2362
  ], DawGridElement.prototype, "visibleStart", 2);
2105
2363
  __decorateClass([
2106
- property7({ type: Number, attribute: false })
2364
+ property8({ type: Number, attribute: false })
2107
2365
  ], DawGridElement.prototype, "visibleEnd", 2);
2108
2366
  __decorateClass([
2109
- property7({ type: Number, attribute: false })
2367
+ property8({ type: Number, attribute: false })
2110
2368
  ], DawGridElement.prototype, "length", 2);
2111
2369
  __decorateClass([
2112
- property7({ type: Number, attribute: false })
2370
+ property8({ type: Number, attribute: false })
2113
2371
  ], DawGridElement.prototype, "height", 2);
2114
2372
  DawGridElement = __decorateClass([
2115
- customElement11("daw-grid")
2373
+ customElement12("daw-grid")
2116
2374
  ], DawGridElement);
2117
2375
 
2118
2376
  // src/styles/theme.ts
2119
- import { css as css8 } from "lit";
2120
- var hostStyles = css8`
2377
+ import { css as css9 } from "lit";
2378
+ var hostStyles = css9`
2121
2379
  :host {
2122
2380
  --daw-wave-color: #c49a6c;
2123
2381
  --daw-progress-color: #63c75f;
@@ -2133,7 +2391,7 @@ var hostStyles = css8`
2133
2391
  --daw-clip-header-text: #e0d4c8;
2134
2392
  }
2135
2393
  `;
2136
- var clipStyles = css8`
2394
+ var clipStyles = css9`
2137
2395
  .clip-container {
2138
2396
  position: absolute;
2139
2397
  overflow: hidden;
@@ -2885,7 +3143,11 @@ var PointerHandler = class {
2885
3143
  e.preventDefault();
2886
3144
  this._timeline = this._host.shadowRoot?.querySelector(".timeline");
2887
3145
  if (this._timeline) {
2888
- this._timeline.setPointerCapture(e.pointerId);
3146
+ try {
3147
+ this._timeline.setPointerCapture(e.pointerId);
3148
+ } catch (err) {
3149
+ console.warn("[dawcore] setPointerCapture failed: " + String(err));
3150
+ }
2889
3151
  const onMove = (me) => clipHandler.onPointerMove(me);
2890
3152
  const onUp = (ue) => {
2891
3153
  clipHandler.onPointerUp(ue);
@@ -2907,11 +3169,20 @@ var PointerHandler = class {
2907
3169
  }
2908
3170
  }
2909
3171
  this._timeline = this._host.shadowRoot?.querySelector(".timeline");
2910
- if (!this._timeline) return;
3172
+ if (!this._timeline) {
3173
+ console.warn(
3174
+ "[dawcore] PointerHandler: .timeline not found in shadow root \u2014 seek/selection ignored"
3175
+ );
3176
+ return;
3177
+ }
2911
3178
  this._timelineRect = this._timeline.getBoundingClientRect();
2912
3179
  this._dragStartPx = this._pxFromPointer(e);
2913
3180
  this._isDragging = false;
2914
- this._timeline.setPointerCapture(e.pointerId);
3181
+ try {
3182
+ this._timeline.setPointerCapture(e.pointerId);
3183
+ } catch (err) {
3184
+ console.warn("[dawcore] setPointerCapture failed: " + String(err));
3185
+ }
2915
3186
  this._timeline.addEventListener("pointermove", this._onPointerMove);
2916
3187
  this._timeline.addEventListener("pointerup", this._onPointerUp);
2917
3188
  };
@@ -3843,9 +4114,143 @@ async function loadWaveformDataFromUrl(src) {
3843
4114
  }
3844
4115
  }
3845
4116
 
4117
+ // src/controllers/scroll-sync-controller.ts
4118
+ var LINE_HEIGHT_PX = 16;
4119
+ var ScrollSyncController = class {
4120
+ constructor(host) {
4121
+ this._scrollContainer = null;
4122
+ this._wheelTargets = /* @__PURE__ */ new Set();
4123
+ this._warnedX = false;
4124
+ this._warnedY = false;
4125
+ /** Selector (in host shadow DOM) for the scroll container. */
4126
+ this.scrollSelector = "";
4127
+ /** Selector for the element receiving translate3d(-scrollLeft, 0, 0). */
4128
+ this.xTargetSelector = "";
4129
+ /** Selector for the element receiving translate3d(0, -scrollTop, 0). */
4130
+ this.yTargetSelector = "";
4131
+ /**
4132
+ * Selector (or comma-separated selectors) for elements whose wheel events
4133
+ * forward to the scroll container. All matching elements receive listeners.
4134
+ */
4135
+ this.wheelForwardSelector = "";
4136
+ this._onScroll = () => {
4137
+ this._apply();
4138
+ };
4139
+ this._onWheel = (e) => {
4140
+ const sc = this._scrollContainer;
4141
+ if (!sc) return;
4142
+ const scale = e.deltaMode === WheelEvent.DOM_DELTA_LINE ? LINE_HEIGHT_PX : e.deltaMode === WheelEvent.DOM_DELTA_PAGE ? sc.clientHeight : 1;
4143
+ const scaleX = e.deltaMode === WheelEvent.DOM_DELTA_PAGE ? sc.clientWidth : scale;
4144
+ const beforeLeft = sc.scrollLeft;
4145
+ const beforeTop = sc.scrollTop;
4146
+ sc.scrollLeft += e.deltaX * scaleX;
4147
+ sc.scrollTop += e.deltaY * scale;
4148
+ if (sc.scrollLeft !== beforeLeft || sc.scrollTop !== beforeTop) {
4149
+ e.preventDefault();
4150
+ }
4151
+ };
4152
+ this._host = host;
4153
+ host.addController(this);
4154
+ }
4155
+ hostConnected() {
4156
+ requestAnimationFrame(() => {
4157
+ if (!this._host.isConnected) return;
4158
+ this._attach();
4159
+ if (!this._scrollContainer && this.scrollSelector) {
4160
+ console.warn(
4161
+ '[dawcore] ScrollSyncController: scroll container not found for "' + this.scrollSelector + '"'
4162
+ );
4163
+ }
4164
+ });
4165
+ }
4166
+ hostDisconnected() {
4167
+ this._scrollContainer?.removeEventListener("scroll", this._onScroll);
4168
+ this._scrollContainer = null;
4169
+ for (const target of this._wheelTargets) {
4170
+ target.removeEventListener("wheel", this._onWheel);
4171
+ }
4172
+ this._wheelTargets.clear();
4173
+ }
4174
+ /**
4175
+ * Re-attach and re-apply transforms from the current scroll position.
4176
+ * Called from the host's updated() so elements created by a re-render
4177
+ * (e.g. the ruler appearing when the first track loads) pick up the
4178
+ * current offset and listeners.
4179
+ */
4180
+ sync() {
4181
+ this._attach();
4182
+ }
4183
+ _query(selector) {
4184
+ return selector ? this._host.shadowRoot?.querySelector(selector) : null;
4185
+ }
4186
+ _queryAll(selector) {
4187
+ if (!selector) return [];
4188
+ return Array.from(this._host.shadowRoot?.querySelectorAll(selector) ?? []);
4189
+ }
4190
+ _attach() {
4191
+ const container = this._query(this.scrollSelector);
4192
+ if (!container) {
4193
+ if (this._scrollContainer && !this._scrollContainer.isConnected) {
4194
+ console.warn(
4195
+ '[dawcore] ScrollSyncController: scroll container "' + this.scrollSelector + '" was removed from the DOM \u2014 detaching listeners until it reappears.'
4196
+ );
4197
+ this._scrollContainer.removeEventListener("scroll", this._onScroll);
4198
+ this._scrollContainer = null;
4199
+ for (const t of this._wheelTargets) t.removeEventListener("wheel", this._onWheel);
4200
+ this._wheelTargets.clear();
4201
+ }
4202
+ return;
4203
+ }
4204
+ if (container !== this._scrollContainer) {
4205
+ this._scrollContainer?.removeEventListener("scroll", this._onScroll);
4206
+ this._scrollContainer = container;
4207
+ container.addEventListener("scroll", this._onScroll, { passive: true });
4208
+ }
4209
+ const nextTargets = new Set(this._queryAll(this.wheelForwardSelector));
4210
+ for (const old of this._wheelTargets) {
4211
+ if (!nextTargets.has(old)) {
4212
+ old.removeEventListener("wheel", this._onWheel);
4213
+ this._wheelTargets.delete(old);
4214
+ }
4215
+ }
4216
+ for (const next of nextTargets) {
4217
+ if (!this._wheelTargets.has(next)) {
4218
+ next.addEventListener("wheel", this._onWheel, { passive: false });
4219
+ this._wheelTargets.add(next);
4220
+ }
4221
+ }
4222
+ this._apply();
4223
+ }
4224
+ _apply() {
4225
+ const sc = this._scrollContainer;
4226
+ if (!sc) return;
4227
+ const xTarget = this._query(this.xTargetSelector);
4228
+ if (xTarget) {
4229
+ xTarget.style.transform = `translate3d(${-sc.scrollLeft}px, 0, 0)`;
4230
+ this._warnedX = false;
4231
+ } else if (this.xTargetSelector && sc.scrollLeft !== 0 && !this._warnedX) {
4232
+ this._warnedX = true;
4233
+ console.warn(
4234
+ '[dawcore] ScrollSyncController: x target "' + this.xTargetSelector + '" not found while scrolled \u2014 the synced pane will appear frozen. Check the selector, or clear it if the target is intentionally not rendered.'
4235
+ );
4236
+ }
4237
+ const yTarget = this._query(this.yTargetSelector);
4238
+ if (yTarget) {
4239
+ yTarget.style.transform = `translate3d(0, ${-sc.scrollTop}px, 0)`;
4240
+ this._warnedY = false;
4241
+ } else if (this.yTargetSelector && sc.scrollTop !== 0 && !this._warnedY) {
4242
+ this._warnedY = true;
4243
+ console.warn(
4244
+ '[dawcore] ScrollSyncController: y target "' + this.yTargetSelector + '" not found while scrolled \u2014 the synced pane will appear frozen. Check the selector, or clear it if the target is intentionally not rendered.'
4245
+ );
4246
+ }
4247
+ }
4248
+ };
4249
+
3846
4250
  // src/elements/daw-editor.ts
4251
+ var RULER_HEIGHT = 30;
3847
4252
  var NO_ADAPTER_ERROR = "No PlayoutAdapter set on <daw-editor>. Set editor.adapter before use.\n\n // Option 1: Native Web Audio (no Tone.js)\n npm install @dawcore/transport\n import { NativePlayoutAdapter } from '@dawcore/transport';\n editor.adapter = new NativePlayoutAdapter(new AudioContext());\n\n // Option 2: Tone.js (effects, MIDI synths)\n npm install @waveform-playlist/playout\n import { createToneAdapter } from '@waveform-playlist/playout';\n editor.adapter = createToneAdapter();";
3848
- var DawEditorElement = class extends LitElement10 {
4253
+ var DawEditorElement = class extends LitElement11 {
3849
4254
  constructor() {
3850
4255
  super(...arguments);
3851
4256
  this._samplesPerPixel = 1024;
@@ -3904,6 +4309,11 @@ var DawEditorElement = class extends LitElement10 {
3904
4309
  v.scrollSelector = ".scroll-area";
3905
4310
  return v;
3906
4311
  })();
4312
+ this._scrollSync = (() => {
4313
+ const s = new ScrollSyncController(this);
4314
+ s.scrollSelector = ".scroll-area";
4315
+ return s;
4316
+ })();
3907
4317
  /**
3908
4318
  * Cache of the last ViewportState forwarded to the spectrogram controller.
3909
4319
  * Lit's `updated()` fires on every reactive state change (`_isPlaying`,
@@ -4450,6 +4860,13 @@ var DawEditorElement = class extends LitElement10 {
4450
4860
  }
4451
4861
  }
4452
4862
  updated(_changed) {
4863
+ this._scrollSync.xTargetSelector = this._showRuler ? ".ruler-content" : "";
4864
+ this._scrollSync.yTargetSelector = this._showControls ? ".controls-column" : "";
4865
+ this._scrollSync.wheelForwardSelector = [
4866
+ this._showControls ? ".controls-viewport" : "",
4867
+ this._showRuler ? ".ruler-viewport" : ""
4868
+ ].filter(Boolean).join(", ");
4869
+ this._scrollSync.sync();
4453
4870
  if (this._spectrogramController) {
4454
4871
  const vs = this._viewport.visibleStart;
4455
4872
  const ve = this._viewport.visibleEnd;
@@ -5611,7 +6028,7 @@ var DawEditorElement = class extends LitElement10 {
5611
6028
  const w = Math.floor(audibleSamples / renderSpp);
5612
6029
  return rs.peaks.map((chPeaks, ch) => {
5613
6030
  const slicedPeaks = latencyPixels > 0 ? chPeaks.slice(latencyPixels * 2) : chPeaks;
5614
- return html9`
6031
+ return html10`
5615
6032
  <daw-waveform
5616
6033
  data-recording-track=${trackId}
5617
6034
  data-recording-channel=${ch}
@@ -5667,6 +6084,14 @@ var DawEditorElement = class extends LitElement10 {
5667
6084
  _getPlayhead() {
5668
6085
  return this.shadowRoot?.querySelector("daw-playhead");
5669
6086
  }
6087
+ /** True when the controls column should be rendered (and its selector is valid). */
6088
+ get _showControls() {
6089
+ return this._getOrderedTracks().length > 0 || this.indefinitePlayback;
6090
+ }
6091
+ /** True when the ruler header band should be rendered (and its selector is valid). */
6092
+ get _showRuler() {
6093
+ return (this._getOrderedTracks().length > 0 || this.scaleMode === "beats" || this.indefinitePlayback) && this.timescale;
6094
+ }
5670
6095
  _getOrderedTracks() {
5671
6096
  const domOrder = [...this.querySelectorAll("daw-track")].map(
5672
6097
  (el) => el.trackId
@@ -5708,64 +6133,79 @@ var DawEditorElement = class extends LitElement10 {
5708
6133
  trackHeight: this.waveHeight * numChannels + (this.clipHeaders ? this.clipHeaderHeight : 0)
5709
6134
  };
5710
6135
  });
5711
- return html9`
5712
- ${orderedTracks.length > 0 || this.indefinitePlayback ? html9`<div class="controls-column">
5713
- ${this.timescale ? html9`<div style="height: 30px;"></div>` : ""}
5714
- ${orderedTracks.map(
5715
- (t) => html9`
5716
- <daw-track-controls
5717
- style="height: ${t.trackHeight}px;"
5718
- .trackId=${t.trackId}
5719
- .trackName=${t.descriptor?.name ?? "Untitled"}
5720
- .volume=${t.descriptor?.volume ?? 1}
5721
- .pan=${t.descriptor?.pan ?? 0}
5722
- .muted=${t.descriptor?.muted ?? false}
5723
- .soloed=${t.descriptor?.soloed ?? false}
5724
- ></daw-track-controls>
5725
- `
5726
- )}
5727
- </div>` : ""}
5728
- <div class="scroll-area">
5729
- <div
5730
- class="timeline ${this._dragOver ? "drag-over" : ""}"
5731
- style="width: ${this._totalWidth > 0 ? this._totalWidth + "px" : "100%"};"
5732
- data-playing=${this._isPlaying}
5733
- @pointerdown=${this._pointer.onPointerDown}
5734
- @dragover=${this._onDragOver}
5735
- @dragleave=${this._onDragLeave}
5736
- @drop=${this._onDrop}
5737
- >
5738
- ${(orderedTracks.length > 0 || this.scaleMode === "beats" || this.indefinitePlayback) && this.timescale ? html9`<daw-ruler
5739
- .samplesPerPixel=${spp}
5740
- .sampleRate=${this.effectiveSampleRate}
5741
- .duration=${this._duration}
5742
- .scaleMode=${this.scaleMode}
5743
- .ticksPerPixel=${this.ticksPerPixel}
5744
- .meterEntries=${this._meterEntries}
5745
- .ppqn=${this.ppqn}
5746
- .totalWidth=${this._totalWidth}
5747
- ></daw-ruler>` : ""}
5748
- ${this.scaleMode === "beats" ? html9`<daw-grid
5749
- style="top: ${this.timescale ? 30 : 0}px;"
5750
- .ticksPerPixel=${this.ticksPerPixel}
5751
- .meterEntries=${this._meterEntries}
5752
- .ppqn=${this.ppqn}
5753
- .visibleStart=${this._viewport.visibleStart}
5754
- .visibleEnd=${this._viewport.visibleEnd}
5755
- .length=${this._totalWidth}
5756
- .height=${orderedTracks.length > 0 ? orderedTracks.reduce((sum, t) => sum + t.trackHeight + 1, 0) : this._emptyGridHeight}
5757
- ></daw-grid>` : ""}
5758
- ${orderedTracks.length > 0 || this.scaleMode === "beats" || this.indefinitePlayback ? html9`<daw-selection .startPx=${selStartPx} .endPx=${selEndPx}></daw-selection>
5759
- <daw-playhead></daw-playhead>` : ""}
5760
- ${orderedTracks.map((t) => {
5761
- const channelHeight = this.waveHeight;
5762
- return html9`
6136
+ const showControls = this._showControls;
6137
+ const showRuler = this._showRuler;
6138
+ return html10`
6139
+ ${showRuler ? html10`<div class="header-row" style="height: ${RULER_HEIGHT}px;">
6140
+ ${showControls ? html10`<div class="ruler-gap"></div>` : ""}
6141
+ <div class="ruler-viewport" @pointerdown=${this._pointer.onPointerDown}>
5763
6142
  <div
5764
- class="track-row ${t.trackId === this._selectedTrackId ? "selected" : ""}"
5765
- style="height: ${t.trackHeight}px;"
5766
- data-track-id=${t.trackId}
6143
+ class="ruler-content"
6144
+ style="width: ${this._totalWidth > 0 ? this._totalWidth + "px" : "100%"};"
5767
6145
  >
5768
- ${t.track.clips.map((clip) => {
6146
+ <daw-ruler
6147
+ .samplesPerPixel=${spp}
6148
+ .sampleRate=${this.effectiveSampleRate}
6149
+ .duration=${this._duration}
6150
+ .scaleMode=${this.scaleMode}
6151
+ .ticksPerPixel=${this.ticksPerPixel}
6152
+ .meterEntries=${this._meterEntries}
6153
+ .ppqn=${this.ppqn}
6154
+ .totalWidth=${this._totalWidth}
6155
+ .rulerHeight=${RULER_HEIGHT}
6156
+ ></daw-ruler>
6157
+ </div>
6158
+ </div>
6159
+ </div>` : ""}
6160
+ <div class="body">
6161
+ ${showControls ? html10`<div class="controls-viewport">
6162
+ <div class="controls-column">
6163
+ ${orderedTracks.map(
6164
+ (t) => html10`
6165
+ <daw-track-controls
6166
+ style="height: ${t.trackHeight}px;"
6167
+ .trackId=${t.trackId}
6168
+ .trackName=${t.descriptor?.name ?? "Untitled"}
6169
+ .volume=${t.descriptor?.volume ?? 1}
6170
+ .pan=${t.descriptor?.pan ?? 0}
6171
+ .muted=${t.descriptor?.muted ?? false}
6172
+ .soloed=${t.descriptor?.soloed ?? false}
6173
+ ></daw-track-controls>
6174
+ `
6175
+ )}
6176
+ </div>
6177
+ </div>` : ""}
6178
+ <div class="scroll-area">
6179
+ <div
6180
+ class="timeline ${this._dragOver ? "drag-over" : ""}"
6181
+ style="width: ${this._totalWidth > 0 ? this._totalWidth + "px" : "100%"};"
6182
+ data-playing=${this._isPlaying}
6183
+ @pointerdown=${this._pointer.onPointerDown}
6184
+ @dragover=${this._onDragOver}
6185
+ @dragleave=${this._onDragLeave}
6186
+ @drop=${this._onDrop}
6187
+ >
6188
+ ${this.scaleMode === "beats" ? html10`<daw-grid
6189
+ style="top: 0px;"
6190
+ .ticksPerPixel=${this.ticksPerPixel}
6191
+ .meterEntries=${this._meterEntries}
6192
+ .ppqn=${this.ppqn}
6193
+ .visibleStart=${this._viewport.visibleStart}
6194
+ .visibleEnd=${this._viewport.visibleEnd}
6195
+ .length=${this._totalWidth}
6196
+ .height=${orderedTracks.length > 0 ? orderedTracks.reduce((sum, t) => sum + t.trackHeight, 0) : this._emptyGridHeight}
6197
+ ></daw-grid>` : ""}
6198
+ ${orderedTracks.length > 0 || this.scaleMode === "beats" || this.indefinitePlayback ? html10`<daw-selection .startPx=${selStartPx} .endPx=${selEndPx}></daw-selection>
6199
+ <daw-playhead></daw-playhead>` : ""}
6200
+ ${orderedTracks.map((t) => {
6201
+ const channelHeight = this.waveHeight;
6202
+ return html10`
6203
+ <div
6204
+ class="track-row ${t.trackId === this._selectedTrackId ? "selected" : ""}"
6205
+ style="height: ${t.trackHeight}px;"
6206
+ data-track-id=${t.trackId}
6207
+ >
6208
+ ${t.track.clips.map((clip) => {
5769
6209
  const peakData = this._peaksData.get(clip.id);
5770
6210
  let clipLeft;
5771
6211
  let width;
@@ -5808,7 +6248,10 @@ var DawEditorElement = class extends LitElement10 {
5808
6248
  const segEndSample = Math.round(segEndAudioSec * sr);
5809
6249
  const totalPeaks = clip.durationSamples / baseScale;
5810
6250
  clipSegments.push({
5811
- peakStart: Math.max(0, (segStartSample - clip.offsetSamples) / baseScale),
6251
+ peakStart: Math.max(
6252
+ 0,
6253
+ (segStartSample - clip.offsetSamples) / baseScale
6254
+ ),
5812
6255
  peakEnd: Math.min(
5813
6256
  totalPeaks,
5814
6257
  (segEndSample - clip.offsetSamples) / baseScale
@@ -5822,78 +6265,79 @@ var DawEditorElement = class extends LitElement10 {
5822
6265
  const channels = segmentChannels ?? peakData?.data ?? [new Int16Array(0)];
5823
6266
  const hdrH = this.clipHeaders ? this.clipHeaderHeight : 0;
5824
6267
  const chH = this.waveHeight;
5825
- return html9` <div
5826
- class="clip-container"
5827
- style="left:${clipLeft}px;top:0;width:${width}px;height:${t.trackHeight}px;"
5828
- data-clip-id=${clip.id}
5829
- >
5830
- ${hdrH > 0 ? html9`<div
5831
- class="clip-header"
5832
- data-clip-id=${clip.id}
5833
- data-track-id=${t.trackId}
5834
- ?data-interactive=${this.interactiveClips}
5835
- >
5836
- <span>${clip.name || t.descriptor?.name || ""}</span>
5837
- </div>` : ""}
5838
- ${t.descriptor?.renderMode === "piano-roll" ? html9`<daw-piano-roll
5839
- style="position:absolute;left:0;top:${hdrH}px;"
5840
- .midiNotes=${clip.midiNotes ?? []}
5841
- .length=${peakData?.length ?? width}
5842
- .waveHeight=${chH * channels.length}
5843
- .samplesPerPixel=${this._renderSpp}
5844
- .sampleRate=${this.effectiveSampleRate}
5845
- .clipOffsetSeconds=${(clip.offsetSamples ?? 0) / this.effectiveSampleRate}
5846
- .visibleStart=${this._viewport.visibleStart}
5847
- .visibleEnd=${this._viewport.visibleEnd}
5848
- .originX=${clipLeft}
5849
- ?selected=${t.trackId === this._selectedTrackId}
5850
- ></daw-piano-roll>` : t.descriptor?.renderMode === "spectrogram" ? channels.map(
5851
- (_chPeaks, chIdx) => html9`<daw-spectrogram
5852
- style="position:absolute;left:0;top:${hdrH + chIdx * chH}px;height:${chH}px;width:${peakData?.length ?? width}px;"
5853
- .clipId=${clip.id}
5854
- .trackId=${t.trackId}
5855
- .channelIndex=${chIdx}
5856
- .length=${peakData?.length ?? width}
5857
- .waveHeight=${chH}
5858
- .samplesPerPixel=${this._renderSpp}
5859
- .sampleRate=${this.effectiveSampleRate}
5860
- .clipOffsetSeconds=${(clip.offsetSamples ?? 0) / this.effectiveSampleRate}
5861
- .visibleStart=${this._viewport.visibleStart}
5862
- .visibleEnd=${this._viewport.visibleEnd}
5863
- .originX=${clipLeft}
5864
- ></daw-spectrogram>`
5865
- ) : channels.map(
5866
- (chPeaks, chIdx) => html9` <daw-waveform
5867
- style="position:absolute;left:0;top:${hdrH + chIdx * chH}px;"
5868
- .peaks=${chPeaks}
5869
- .length=${peakData?.length ?? width}
5870
- .waveHeight=${chH}
5871
- .barWidth=${this.barWidth}
5872
- .barGap=${this.barGap}
5873
- .visibleStart=${this._viewport.visibleStart}
5874
- .visibleEnd=${this._viewport.visibleEnd}
5875
- .originX=${clipLeft}
5876
- .segments=${clipSegments}
5877
- ></daw-waveform>`
5878
- )}
5879
- ${this.interactiveClips ? html9` <div
5880
- class="clip-boundary"
5881
- data-boundary-edge="left"
5882
- data-clip-id=${clip.id}
5883
- data-track-id=${t.trackId}
5884
- ></div>
5885
- <div
5886
- class="clip-boundary"
5887
- data-boundary-edge="right"
6268
+ return html10` <div
6269
+ class="clip-container"
6270
+ style="left:${clipLeft}px;top:0;width:${width}px;height:${t.trackHeight}px;"
6271
+ data-clip-id=${clip.id}
6272
+ >
6273
+ ${hdrH > 0 ? html10`<div
6274
+ class="clip-header"
5888
6275
  data-clip-id=${clip.id}
5889
6276
  data-track-id=${t.trackId}
5890
- ></div>` : ""}
5891
- </div>`;
6277
+ ?data-interactive=${this.interactiveClips}
6278
+ >
6279
+ <span>${clip.name || t.descriptor?.name || ""}</span>
6280
+ </div>` : ""}
6281
+ ${t.descriptor?.renderMode === "piano-roll" ? html10`<daw-piano-roll
6282
+ style="position:absolute;left:0;top:${hdrH}px;"
6283
+ .midiNotes=${clip.midiNotes ?? []}
6284
+ .length=${peakData?.length ?? width}
6285
+ .waveHeight=${chH * channels.length}
6286
+ .samplesPerPixel=${this._renderSpp}
6287
+ .sampleRate=${this.effectiveSampleRate}
6288
+ .clipOffsetSeconds=${(clip.offsetSamples ?? 0) / this.effectiveSampleRate}
6289
+ .visibleStart=${this._viewport.visibleStart}
6290
+ .visibleEnd=${this._viewport.visibleEnd}
6291
+ .originX=${clipLeft}
6292
+ ?selected=${t.trackId === this._selectedTrackId}
6293
+ ></daw-piano-roll>` : t.descriptor?.renderMode === "spectrogram" ? channels.map(
6294
+ (_chPeaks, chIdx) => html10`<daw-spectrogram
6295
+ style="position:absolute;left:0;top:${hdrH + chIdx * chH}px;height:${chH}px;width:${peakData?.length ?? width}px;"
6296
+ .clipId=${clip.id}
6297
+ .trackId=${t.trackId}
6298
+ .channelIndex=${chIdx}
6299
+ .length=${peakData?.length ?? width}
6300
+ .waveHeight=${chH}
6301
+ .samplesPerPixel=${this._renderSpp}
6302
+ .sampleRate=${this.effectiveSampleRate}
6303
+ .clipOffsetSeconds=${(clip.offsetSamples ?? 0) / this.effectiveSampleRate}
6304
+ .visibleStart=${this._viewport.visibleStart}
6305
+ .visibleEnd=${this._viewport.visibleEnd}
6306
+ .originX=${clipLeft}
6307
+ ></daw-spectrogram>`
6308
+ ) : channels.map(
6309
+ (chPeaks, chIdx) => html10` <daw-waveform
6310
+ style="position:absolute;left:0;top:${hdrH + chIdx * chH}px;"
6311
+ .peaks=${chPeaks}
6312
+ .length=${peakData?.length ?? width}
6313
+ .waveHeight=${chH}
6314
+ .barWidth=${this.barWidth}
6315
+ .barGap=${this.barGap}
6316
+ .visibleStart=${this._viewport.visibleStart}
6317
+ .visibleEnd=${this._viewport.visibleEnd}
6318
+ .originX=${clipLeft}
6319
+ .segments=${clipSegments}
6320
+ ></daw-waveform>`
6321
+ )}
6322
+ ${this.interactiveClips ? html10` <div
6323
+ class="clip-boundary"
6324
+ data-boundary-edge="left"
6325
+ data-clip-id=${clip.id}
6326
+ data-track-id=${t.trackId}
6327
+ ></div>
6328
+ <div
6329
+ class="clip-boundary"
6330
+ data-boundary-edge="right"
6331
+ data-clip-id=${clip.id}
6332
+ data-track-id=${t.trackId}
6333
+ ></div>` : ""}
6334
+ </div>`;
5892
6335
  })}
5893
- ${this._renderRecordingPreview(t.trackId, channelHeight)}
5894
- </div>
5895
- `;
6336
+ ${this._renderRecordingPreview(t.trackId, channelHeight)}
6337
+ </div>
6338
+ `;
5896
6339
  })}
6340
+ </div>
5897
6341
  </div>
5898
6342
  </div>
5899
6343
  <slot></slot>
@@ -5902,21 +6346,48 @@ var DawEditorElement = class extends LitElement10 {
5902
6346
  };
5903
6347
  DawEditorElement.styles = [
5904
6348
  hostStyles,
5905
- css9`
6349
+ css10`
5906
6350
  :host {
5907
6351
  display: flex;
6352
+ flex-direction: column;
5908
6353
  position: relative;
5909
6354
  background: var(--daw-background, #1a1a2e);
5910
6355
  overflow: hidden;
5911
6356
  }
5912
- .controls-column {
6357
+ .header-row {
6358
+ display: flex;
6359
+ flex-shrink: 0;
6360
+ }
6361
+ .ruler-gap {
6362
+ flex-shrink: 0;
6363
+ width: var(--daw-controls-width, 180px);
6364
+ }
6365
+ .ruler-viewport {
6366
+ flex: 1;
6367
+ position: relative;
6368
+ overflow: hidden;
6369
+ cursor: text;
6370
+ }
6371
+ .ruler-content {
6372
+ will-change: transform;
6373
+ }
6374
+ .body {
6375
+ flex: 1;
6376
+ min-height: 0;
6377
+ display: flex;
6378
+ }
6379
+ .controls-viewport {
5913
6380
  flex-shrink: 0;
5914
6381
  width: var(--daw-controls-width, 180px);
6382
+ overflow: hidden;
6383
+ }
6384
+ .controls-column {
6385
+ will-change: transform;
5915
6386
  }
5916
6387
  .scroll-area {
5917
6388
  flex: 1;
5918
- overflow-x: auto;
5919
- overflow-y: hidden;
6389
+ overflow: auto;
6390
+ overflow-anchor: none;
5920
6391
  min-height: var(--daw-min-height, 200px);
5921
6392
  }
5922
6393
  .timeline {
@@ -5926,6 +6397,7 @@ DawEditorElement.styles = [
5926
6397
  }
5927
6398
  .track-row {
5928
6399
  position: relative;
6400
+ box-sizing: border-box;
5929
6401
  background: var(--daw-track-background, #16213e);
5930
6402
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
5931
6403
  }
@@ -5950,70 +6422,70 @@ DawEditorElement.styles = [
5950
6422
  ];
5951
6423
  DawEditorElement._CONTROL_PROPS = /* @__PURE__ */ new Set(["volume", "pan", "muted", "soloed"]);
5952
6424
  __decorateClass([
5953
- property8({ type: Number, attribute: "samples-per-pixel", noAccessor: true })
6425
+ property9({ type: Number, attribute: "samples-per-pixel", noAccessor: true })
5954
6426
  ], DawEditorElement.prototype, "samplesPerPixel", 1);
5955
6427
  __decorateClass([
5956
- property8({ type: Number, attribute: "wave-height" })
6428
+ property9({ type: Number, attribute: "wave-height" })
5957
6429
  ], DawEditorElement.prototype, "waveHeight", 2);
5958
6430
  __decorateClass([
5959
- property8({ type: Boolean })
6431
+ property9({ type: Boolean })
5960
6432
  ], DawEditorElement.prototype, "timescale", 2);
5961
6433
  __decorateClass([
5962
- property8({ type: Boolean })
6434
+ property9({ type: Boolean })
5963
6435
  ], DawEditorElement.prototype, "mono", 2);
5964
6436
  __decorateClass([
5965
- property8({ type: Number, attribute: "bar-width" })
6437
+ property9({ type: Number, attribute: "bar-width" })
5966
6438
  ], DawEditorElement.prototype, "barWidth", 2);
5967
6439
  __decorateClass([
5968
- property8({ type: Number, attribute: "bar-gap" })
6440
+ property9({ type: Number, attribute: "bar-gap" })
5969
6441
  ], DawEditorElement.prototype, "barGap", 2);
5970
6442
  __decorateClass([
5971
- property8({ type: Boolean, attribute: "file-drop" })
6443
+ property9({ type: Boolean, attribute: "file-drop" })
5972
6444
  ], DawEditorElement.prototype, "fileDrop", 2);
5973
6445
  __decorateClass([
5974
- property8({ type: Boolean, attribute: "clip-headers" })
6446
+ property9({ type: Boolean, attribute: "clip-headers" })
5975
6447
  ], DawEditorElement.prototype, "clipHeaders", 2);
5976
6448
  __decorateClass([
5977
- property8({ type: Number, attribute: "clip-header-height" })
6449
+ property9({ type: Number, attribute: "clip-header-height" })
5978
6450
  ], DawEditorElement.prototype, "clipHeaderHeight", 2);
5979
6451
  __decorateClass([
5980
- property8({ type: Boolean, attribute: "interactive-clips" })
6452
+ property9({ type: Boolean, attribute: "interactive-clips" })
5981
6453
  ], DawEditorElement.prototype, "interactiveClips", 2);
5982
6454
  __decorateClass([
5983
- property8({ type: Boolean, attribute: "indefinite-playback" })
6455
+ property9({ type: Boolean, attribute: "indefinite-playback" })
5984
6456
  ], DawEditorElement.prototype, "indefinitePlayback", 2);
5985
6457
  __decorateClass([
5986
- property8({ attribute: false, noAccessor: true })
6458
+ property9({ attribute: false, noAccessor: true })
5987
6459
  ], DawEditorElement.prototype, "spectrogramConfig", 1);
5988
6460
  __decorateClass([
5989
- property8({ attribute: false, noAccessor: true })
6461
+ property9({ attribute: false, noAccessor: true })
5990
6462
  ], DawEditorElement.prototype, "spectrogramColorMap", 1);
5991
6463
  __decorateClass([
5992
- property8({ type: String, attribute: "scale-mode" })
6464
+ property9({ type: String, attribute: "scale-mode" })
5993
6465
  ], DawEditorElement.prototype, "scaleMode", 2);
5994
6466
  __decorateClass([
5995
- property8({ type: Number, attribute: "ticks-per-pixel", noAccessor: true })
6467
+ property9({ type: Number, attribute: "ticks-per-pixel", noAccessor: true })
5996
6468
  ], DawEditorElement.prototype, "ticksPerPixel", 1);
5997
6469
  __decorateClass([
5998
- property8({ type: Number, noAccessor: true })
6470
+ property9({ type: Number, noAccessor: true })
5999
6471
  ], DawEditorElement.prototype, "bpm", 1);
6000
6472
  __decorateClass([
6001
- property8({ attribute: false })
6473
+ property9({ attribute: false })
6002
6474
  ], DawEditorElement.prototype, "timeSignature", 2);
6003
6475
  __decorateClass([
6004
- property8({ attribute: false })
6476
+ property9({ attribute: false })
6005
6477
  ], DawEditorElement.prototype, "meterEntries", 2);
6006
6478
  __decorateClass([
6007
- property8({ type: Number, noAccessor: true })
6479
+ property9({ type: Number, noAccessor: true })
6008
6480
  ], DawEditorElement.prototype, "ppqn", 1);
6009
6481
  __decorateClass([
6010
- property8({ type: String, attribute: "snap-to" })
6482
+ property9({ type: String, attribute: "snap-to" })
6011
6483
  ], DawEditorElement.prototype, "snapTo", 2);
6012
6484
  __decorateClass([
6013
- property8({ attribute: false })
6485
+ property9({ attribute: false })
6014
6486
  ], DawEditorElement.prototype, "secondsToTicks", 2);
6015
6487
  __decorateClass([
6016
- property8({ attribute: false })
6488
+ property9({ attribute: false })
6017
6489
  ], DawEditorElement.prototype, "ticksToSeconds", 2);
6018
6490
  __decorateClass([
6019
6491
  state3()
@@ -6037,246 +6509,15 @@ __decorateClass([
6037
6509
  state3()
6038
6510
  ], DawEditorElement.prototype, "_dragOver", 2);
6039
6511
  __decorateClass([
6040
- property8({ attribute: false })
6512
+ property9({ attribute: false })
6041
6513
  ], DawEditorElement.prototype, "adapter", 1);
6042
6514
  __decorateClass([
6043
- property8({ attribute: "eager-resume" })
6515
+ property9({ attribute: "eager-resume" })
6044
6516
  ], DawEditorElement.prototype, "eagerResume", 2);
6045
6517
  DawEditorElement = __decorateClass([
6046
- customElement12("daw-editor")
6518
+ customElement13("daw-editor")
6047
6519
  ], DawEditorElement);
6048
6520
 
6049
- // src/elements/daw-ruler.ts
6050
- import { LitElement as LitElement11, html as html10, css as css10 } from "lit";
6051
- import { customElement as customElement13, property as property9 } from "lit/decorators.js";
6052
-
6053
- // src/utils/time-format.ts
6054
- function formatTime(milliseconds) {
6055
- const seconds = Math.floor(milliseconds / 1e3);
6056
- const s = seconds % 60;
6057
- const m = (seconds - s) / 60;
6058
- return `${m}:${String(s).padStart(2, "0")}`;
6059
- }
6060
-
6061
- // src/utils/smart-scale.ts
6062
- var timeinfo = /* @__PURE__ */ new Map([
6063
- [700, { marker: 1e3, bigStep: 500, smallStep: 100 }],
6064
- [1500, { marker: 2e3, bigStep: 1e3, smallStep: 200 }],
6065
- [2500, { marker: 2e3, bigStep: 1e3, smallStep: 500 }],
6066
- [5e3, { marker: 5e3, bigStep: 1e3, smallStep: 500 }],
6067
- [1e4, { marker: 1e4, bigStep: 5e3, smallStep: 1e3 }],
6068
- [12e3, { marker: 15e3, bigStep: 5e3, smallStep: 1e3 }],
6069
- [Infinity, { marker: 3e4, bigStep: 1e4, smallStep: 5e3 }]
6070
- ]);
6071
- function getScaleInfo(samplesPerPixel) {
6072
- for (const [resolution, config] of timeinfo) {
6073
- if (samplesPerPixel < resolution) {
6074
- return config;
6075
- }
6076
- }
6077
- return { marker: 3e4, bigStep: 1e4, smallStep: 5e3 };
6078
- }
6079
- function computeTemporalTicks(samplesPerPixel, sampleRate, duration, rulerHeight) {
6080
- const widthX = Math.ceil(duration * sampleRate / samplesPerPixel);
6081
- const config = getScaleInfo(samplesPerPixel);
6082
- const { marker, bigStep, smallStep } = config;
6083
- const canvasInfo = /* @__PURE__ */ new Map();
6084
- const labels = [];
6085
- const pixPerSec = sampleRate / samplesPerPixel;
6086
- for (let counter = 0; ; counter += smallStep) {
6087
- const pix = Math.floor(counter / 1e3 * pixPerSec);
6088
- if (pix >= widthX) break;
6089
- if (counter % marker === 0) {
6090
- canvasInfo.set(pix, rulerHeight);
6091
- labels.push({ pix, text: formatTime(counter) });
6092
- } else if (counter % bigStep === 0) {
6093
- canvasInfo.set(pix, Math.floor(rulerHeight / 2));
6094
- } else if (counter % smallStep === 0) {
6095
- canvasInfo.set(pix, Math.floor(rulerHeight / 5));
6096
- }
6097
- }
6098
- return { widthX, canvasInfo, labels };
6099
- }
6100
-
6101
- // src/elements/daw-ruler.ts
6102
- var MAX_CANVAS_WIDTH4 = 1e3;
6103
- var DawRulerElement = class extends LitElement11 {
6104
- constructor() {
6105
- super(...arguments);
6106
- this.samplesPerPixel = 1024;
6107
- this.sampleRate = 48e3;
6108
- this.duration = 0;
6109
- this.rulerHeight = 30;
6110
- this.scaleMode = "temporal";
6111
- this.ticksPerPixel = 4;
6112
- this.meterEntries = [
6113
- { tick: 0, numerator: 4, denominator: 4 }
6114
- ];
6115
- this.ppqn = 960;
6116
- this.totalWidth = 0;
6117
- this._tickData = null;
6118
- this._musicalTickData = null;
6119
- }
6120
- willUpdate() {
6121
- if (this.scaleMode === "beats" && this.totalWidth > 0) {
6122
- this._musicalTickData = getCachedMusicalTicks({
6123
- meterEntries: this.meterEntries,
6124
- ticksPerPixel: this.ticksPerPixel,
6125
- startPixel: 0,
6126
- endPixel: this.totalWidth,
6127
- ppqn: this.ppqn
6128
- });
6129
- this._tickData = null;
6130
- } else if (this.duration > 0 || this.totalWidth > 0) {
6131
- const widthDerivedDuration = this.totalWidth * this.samplesPerPixel / this.sampleRate;
6132
- const effectiveDuration = Math.max(this.duration, widthDerivedDuration);
6133
- this._musicalTickData = null;
6134
- this._tickData = computeTemporalTicks(
6135
- this.samplesPerPixel,
6136
- this.sampleRate,
6137
- effectiveDuration,
6138
- this.rulerHeight
6139
- );
6140
- } else {
6141
- this._musicalTickData = null;
6142
- this._tickData = null;
6143
- }
6144
- }
6145
- render() {
6146
- const widthX = this.scaleMode === "beats" ? this.totalWidth : this._tickData?.widthX ?? 0;
6147
- if (widthX <= 0) return html10``;
6148
- const totalChunks = Math.ceil(widthX / MAX_CANVAS_WIDTH4);
6149
- const indices = Array.from({ length: totalChunks }, (_, i) => i);
6150
- const dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
6151
- const beatsLabels = this.scaleMode === "beats" ? this._musicalTickData?.ticks.filter((t) => t.label) ?? [] : [];
6152
- const temporalLabels = this.scaleMode !== "beats" ? this._tickData?.labels ?? [] : [];
6153
- return html10`
6154
- <div class="container" style="width: ${widthX}px; height: ${this.rulerHeight}px;">
6155
- ${indices.map((i) => {
6156
- const width = Math.min(MAX_CANVAS_WIDTH4, widthX - i * MAX_CANVAS_WIDTH4);
6157
- return html10`
6158
- <canvas
6159
- data-index=${i}
6160
- width=${width * dpr}
6161
- height=${this.rulerHeight * dpr}
6162
- style="left: ${i * MAX_CANVAS_WIDTH4}px; width: ${width}px; height: ${this.rulerHeight}px;"
6163
- ></canvas>
6164
- `;
6165
- })}
6166
- ${this.scaleMode === "beats" ? beatsLabels.map(
6167
- (t) => html10`<span
6168
- class="label ${t.pixel > 0 ? "centered" : ""}"
6169
- style="left: ${t.pixel > 0 ? t.pixel : t.pixel + 4}px;"
6170
- >${t.label}</span
6171
- >`
6172
- ) : temporalLabels.map(
6173
- ({ pix, text }) => html10`<span class="label" style="left: ${pix + 4}px;">${text}</span>`
6174
- )}
6175
- </div>
6176
- `;
6177
- }
6178
- updated() {
6179
- this._drawTicks();
6180
- }
6181
- _drawTicks() {
6182
- const canvases = this.shadowRoot?.querySelectorAll("canvas");
6183
- if (!canvases) return;
6184
- const dpr = typeof devicePixelRatio !== "undefined" ? devicePixelRatio : 1;
6185
- const rulerColor = getComputedStyle(this).getPropertyValue("--daw-ruler-color").trim() || "#c49a6c";
6186
- const widthX = this.scaleMode === "beats" ? this.totalWidth : this._tickData?.widthX ?? 0;
6187
- for (const canvas of canvases) {
6188
- const idx = Number(canvas.dataset.index);
6189
- const ctx = canvas.getContext("2d");
6190
- if (!ctx) continue;
6191
- const canvasWidth = Math.min(MAX_CANVAS_WIDTH4, widthX - idx * MAX_CANVAS_WIDTH4);
6192
- const globalOffset = idx * MAX_CANVAS_WIDTH4;
6193
- ctx.resetTransform();
6194
- ctx.clearRect(0, 0, canvas.width, canvas.height);
6195
- ctx.scale(dpr, dpr);
6196
- ctx.strokeStyle = rulerColor;
6197
- ctx.lineWidth = 1;
6198
- if (this.scaleMode === "beats" && this._musicalTickData) {
6199
- const h = this.rulerHeight;
6200
- for (const tick of this._musicalTickData.ticks) {
6201
- const localX = tick.pixel - globalOffset;
6202
- if (localX < 0 || localX >= canvasWidth) continue;
6203
- const tickH = tick.type === "major" ? h * 0.6 : tick.type === "minor" ? h * 0.35 : h * 0.15;
6204
- ctx.globalAlpha = tick.type === "major" ? 1 : 0.5;
6205
- ctx.beginPath();
6206
- ctx.moveTo(localX + 0.5, h);
6207
- ctx.lineTo(localX + 0.5, h - tickH);
6208
- ctx.stroke();
6209
- }
6210
- ctx.globalAlpha = 1;
6211
- } else if (this._tickData) {
6212
- for (const [pix, height] of this._tickData.canvasInfo) {
6213
- const localX = pix - globalOffset;
6214
- if (localX < 0 || localX >= canvasWidth) continue;
6215
- ctx.beginPath();
6216
- ctx.moveTo(localX + 0.5, this.rulerHeight);
6217
- ctx.lineTo(localX + 0.5, this.rulerHeight - height);
6218
- ctx.stroke();
6219
- }
6220
- }
6221
- }
6222
- }
6223
- };
6224
- DawRulerElement.styles = css10`
6225
- :host {
6226
- display: block;
6227
- position: relative;
6228
- background: var(--daw-ruler-background, #0f0f1a);
6229
- }
6230
- .container {
6231
- position: relative;
6232
- }
6233
- canvas {
6234
- position: absolute;
6235
- top: 0;
6236
- }
6237
- .label {
6238
- position: absolute;
6239
- font-size: 0.7rem;
6240
- line-height: 1;
6241
- white-space: nowrap;
6242
- color: var(--daw-ruler-color, #c49a6c);
6243
- top: 1px;
6244
- }
6245
- .label.centered {
6246
- transform: translateX(-50%);
6247
- }
6248
- `;
6249
- __decorateClass([
6250
- property9({ type: Number, attribute: false })
6251
- ], DawRulerElement.prototype, "samplesPerPixel", 2);
6252
- __decorateClass([
6253
- property9({ type: Number, attribute: false })
6254
- ], DawRulerElement.prototype, "sampleRate", 2);
6255
- __decorateClass([
6256
- property9({ type: Number, attribute: false })
6257
- ], DawRulerElement.prototype, "duration", 2);
6258
- __decorateClass([
6259
- property9({ type: Number, attribute: false })
6260
- ], DawRulerElement.prototype, "rulerHeight", 2);
6261
- __decorateClass([
6262
- property9({ type: String, attribute: false })
6263
- ], DawRulerElement.prototype, "scaleMode", 2);
6264
- __decorateClass([
6265
- property9({ type: Number, attribute: false })
6266
- ], DawRulerElement.prototype, "ticksPerPixel", 2);
6267
- __decorateClass([
6268
- property9({ attribute: false })
6269
- ], DawRulerElement.prototype, "meterEntries", 2);
6270
- __decorateClass([
6271
- property9({ type: Number, attribute: false })
6272
- ], DawRulerElement.prototype, "ppqn", 2);
6273
- __decorateClass([
6274
- property9({ type: Number, attribute: false })
6275
- ], DawRulerElement.prototype, "totalWidth", 2);
6276
- DawRulerElement = __decorateClass([
6277
- customElement13("daw-ruler")
6278
- ], DawRulerElement);
6279
-
6280
6521
  // src/elements/daw-selection.ts
6281
6522
  import { LitElement as LitElement12, html as html11, css as css11 } from "lit";
6282
6523
  import { customElement as customElement14, property as property10 } from "lit/decorators.js";