@arraypress/waveform-player 1.7.1 → 1.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -229,6 +229,20 @@ Choose from 6 built-in styles:
229
229
  | `waveform` | array | `null` | Pre-generated waveform data |
230
230
  | `enableMediaSession` | boolean | `true` | Enable system media controls |
231
231
  | `audioMode` | string | `'self'` | `'self'` (own `<audio>`) or `'external'` (delegate) |
232
+ | `accessibleSeek` | boolean | `true` | Expose the waveform as a keyboard-operable ARIA slider |
233
+ | `seekLabel` | string | `null` | Accessible name for the seek slider (falls back to title) |
234
+
235
+ ### Accessibility
236
+
237
+ By default the waveform is exposed as a keyboard-operable [ARIA slider](https://www.w3.org/WAI/ARIA/apg/patterns/slider/): the `.waveform-container` gets `role="slider"`, is focusable in the tab order, and reports `aria-valuemin`/`aria-valuemax`/`aria-valuenow` plus a readable `aria-valuetext` (e.g. `"0:30 of 2:00"`). When focused it responds to the standard slider keys:
238
+
239
+ | Key | Action |
240
+ |---|---|
241
+ | <kbd>←</kbd> / <kbd>↓</kbd> · <kbd>→</kbd> / <kbd>↑</kbd> | Seek ∓5s |
242
+ | <kbd>Page Down</kbd> / <kbd>Page Up</kbd> | Seek ∓10s |
243
+ | <kbd>Home</kbd> / <kbd>End</kbd> | Start / end |
244
+
245
+ This works in both `self` and `external` audio modes (in external mode it dispatches `waveformplayer:request-seek`, exactly like click-to-seek). Set `accessibleSeek: false` to opt out and keep the prior markup, or `seekLabel` to localize the control's accessible name.
232
246
 
233
247
  ### External audio mode
234
248
 
@@ -1 +1 @@
1
- .waveform-player{font-family:inherit;color:inherit;line-height:1.4}.waveform-player *{box-sizing:border-box}.waveform-body{display:flex;flex-direction:column;gap:8px}.waveform-track{display:flex;align-items:center;gap:12px;position:relative}.waveform-btn{width:36px;height:36px;min-width:36px;border-radius:50%;border:2px solid currentColor;background:transparent;color:inherit;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s ease;padding:0;opacity:.9;flex-shrink:0}.waveform-btn:hover:not(:disabled){opacity:1;transform:scale(1.05)}.waveform-btn:disabled{cursor:not-allowed;opacity:.3}.waveform-btn>*{display:flex;align-items:center;justify-content:center;width:100%;height:100%}.waveform-btn svg{width:16px;height:16px;fill:currentColor;display:block}.waveform-icon-play svg{margin-left:1px}.waveform-container{flex:1;position:relative;min-height:60px;cursor:pointer;min-width:0;width:100%}.waveform-container canvas{display:block;width:100%;height:100%;max-width:100%;transition:opacity .3s ease;position:relative;z-index:1}.waveform-info{display:flex;align-items:center;gap:8px;font-size:13px;min-height:20px}.waveform-text{flex:1;display:flex;flex-direction:column;gap:2px;min-width:0}.waveform-title{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:500}.waveform-subtitle{font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.waveform-time{font-size:11px;white-space:nowrap;flex-shrink:0}.waveform-bpm{font-size:11px;white-space:nowrap;flex-shrink:0;display:inline-flex;align-items:center;gap:4px}.waveform-loading{position:absolute;inset:0;background:#0000001a;z-index:10}.waveform-error{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:#0003;z-index:10}.waveform-error-text{font-size:12px;opacity:.7;text-align:center;padding:0 20px}.waveform-markers{position:absolute;inset:0;pointer-events:none;z-index:5}.waveform-marker{position:absolute;top:0;width:2px;height:100%;background:#ffffff80;border:none;padding:0;cursor:pointer;pointer-events:all;transition:all .2s}.waveform-marker:hover{width:4px;z-index:20}.waveform-marker-tooltip{position:absolute;bottom:calc(100% + 4px);left:50%;transform:translate(-50%);background:#000000e6;color:#fff;padding:4px 8px;border-radius:4px;font-size:11px;white-space:nowrap;pointer-events:none;opacity:0;transition:opacity .2s;z-index:1000}.waveform-marker:hover .waveform-marker-tooltip{opacity:1}.waveform-btn:focus-visible{outline:2px solid currentColor;outline-offset:2px}.waveform-marker:focus-visible{outline:2px solid currentColor;outline-offset:1px;width:4px}.waveform-speed{position:relative;flex-shrink:0}.speed-btn{background:transparent;border:1px solid rgba(255,255,255,.2);border-radius:4px;padding:4px 8px;color:inherit;font-size:11px;cursor:pointer;transition:all .2s;min-width:40px}.speed-btn:hover{background:#ffffff0d;border-color:#ffffff4d}.speed-value{font-weight:600}.speed-menu{position:absolute;bottom:100%;right:0;margin-bottom:4px;background:#000000f2;border:1px solid rgba(255,255,255,.2);border-radius:6px;padding:4px;z-index:100;min-width:60px}.speed-option{display:block;width:100%;background:transparent;border:none;color:#ffffffb3;padding:6px 12px;font-size:12px;cursor:pointer;transition:all .2s;text-align:left;border-radius:4px}.speed-option:hover{background:#ffffff1a;color:#fff}.speed-option.active{background:#a855f733;color:#a855f7;font-weight:600}.waveform-player.waveform-focused{outline:2px solid rgba(168,85,247,.5);outline-offset:2px;border-radius:4px}.waveform-player:focus{outline:none}.waveform-player:focus-visible{outline:1px solid rgba(168,85,247,.3);outline-offset:1px}.waveform-player.waveform-focused{outline:none}.waveform-track.waveform-align-top{align-items:flex-start}.waveform-track.waveform-align-top .waveform-btn{margin-top:5px}.waveform-track.waveform-align-center{align-items:center}.waveform-track.waveform-align-bottom{align-items:flex-end}.waveform-track.waveform-align-bottom .waveform-btn{margin-bottom:5px}@media (max-width: 480px){.waveform-btn{width:32px;height:32px;min-width:32px}.waveform-container{min-height:50px}.waveform-info{font-size:12px}.waveform-subtitle,.waveform-time,.waveform-bpm{font-size:10px}}
1
+ .waveform-player{font-family:inherit;color:inherit;line-height:1.4}.waveform-player *{box-sizing:border-box}.waveform-body{display:flex;flex-direction:column;gap:8px}.waveform-track{display:flex;align-items:center;gap:12px;position:relative}.waveform-btn{width:36px;height:36px;min-width:36px;border-radius:50%;border:2px solid currentColor;background:transparent;color:inherit;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s ease;padding:0;opacity:.9;flex-shrink:0}.waveform-btn:hover:not(:disabled){opacity:1;transform:scale(1.05)}.waveform-btn:disabled{cursor:not-allowed;opacity:.3}.waveform-btn>*{display:flex;align-items:center;justify-content:center;width:100%;height:100%}.waveform-btn svg{width:16px;height:16px;fill:currentColor;display:block}.waveform-icon-play svg{margin-left:1px}.waveform-container{flex:1;position:relative;min-height:60px;cursor:pointer;min-width:0;width:100%}.waveform-container:focus-visible{outline:2px solid currentColor}.waveform-container canvas{display:block;width:100%;height:100%;max-width:100%;transition:opacity .3s ease;position:relative;z-index:1}.waveform-info{display:flex;align-items:center;gap:8px;font-size:13px;min-height:20px}.waveform-text{flex:1;display:flex;flex-direction:column;gap:2px;min-width:0}.waveform-title{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:500}.waveform-subtitle{font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.waveform-time{font-size:11px;white-space:nowrap;flex-shrink:0}.waveform-bpm{font-size:11px;white-space:nowrap;flex-shrink:0;display:inline-flex;align-items:center;gap:4px}.waveform-loading{position:absolute;inset:0;background:#0000001a;z-index:10}.waveform-error{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:#0003;z-index:10}.waveform-error-text{font-size:12px;opacity:.7;text-align:center;padding:0 20px}.waveform-markers{position:absolute;inset:0;pointer-events:none;z-index:5}.waveform-marker{position:absolute;top:0;width:2px;height:100%;background:#ffffff80;border:none;padding:0;cursor:pointer;pointer-events:all;transition:all .2s}.waveform-marker:hover{width:4px;z-index:20}.waveform-marker-tooltip{position:absolute;bottom:calc(100% + 4px);left:50%;transform:translate(-50%);background:#000000e6;color:#fff;padding:4px 8px;border-radius:4px;font-size:11px;white-space:nowrap;pointer-events:none;opacity:0;transition:opacity .2s;z-index:1000}.waveform-marker:hover .waveform-marker-tooltip{opacity:1}.waveform-btn:focus-visible{outline:2px solid currentColor;outline-offset:2px}.waveform-marker:focus-visible{outline:2px solid currentColor;outline-offset:1px;width:4px}.waveform-speed{position:relative;flex-shrink:0}.speed-btn{background:transparent;border:1px solid rgba(255,255,255,.2);border-radius:4px;padding:4px 8px;color:inherit;font-size:11px;cursor:pointer;transition:all .2s;min-width:40px}.speed-btn:hover{background:#ffffff0d;border-color:#ffffff4d}.speed-value{font-weight:600}.speed-menu{position:absolute;bottom:100%;right:0;margin-bottom:4px;background:#000000f2;border:1px solid rgba(255,255,255,.2);border-radius:6px;padding:4px;z-index:100;min-width:60px}.speed-option{display:block;width:100%;background:transparent;border:none;color:#ffffffb3;padding:6px 12px;font-size:12px;cursor:pointer;transition:all .2s;text-align:left;border-radius:4px}.speed-option:hover{background:#ffffff1a;color:#fff}.speed-option.active{background:#a855f733;color:#a855f7;font-weight:600}.waveform-player.waveform-focused{outline:2px solid rgba(168,85,247,.5);outline-offset:2px;border-radius:4px}.waveform-player:focus{outline:none}.waveform-player:focus-visible{outline:1px solid rgba(168,85,247,.3);outline-offset:1px}.waveform-player.waveform-focused{outline:none}.waveform-track.waveform-align-top{align-items:flex-start}.waveform-track.waveform-align-top .waveform-btn{margin-top:5px}.waveform-track.waveform-align-center{align-items:center}.waveform-track.waveform-align-bottom{align-items:flex-end}.waveform-track.waveform-align-bottom .waveform-btn{margin-bottom:5px}@media (max-width: 480px){.waveform-btn{width:32px;height:32px;min-width:32px}.waveform-container{min-height:50px}.waveform-info{font-size:12px}.waveform-subtitle,.waveform-time,.waveform-bpm{font-size:10px}}
@@ -1,4 +1,4 @@
1
- function A(t){let e={};if(t.dataset.url&&(e.url=t.dataset.url),t.dataset.height&&(e.height=parseInt(t.dataset.height)),t.dataset.samples&&(e.samples=parseInt(t.dataset.samples)),t.dataset.preload&&(e.preload=t.dataset.preload),t.dataset.waveformStyle&&(e.waveformStyle=t.dataset.waveformStyle),t.dataset.barWidth&&(e.barWidth=parseInt(t.dataset.barWidth)),t.dataset.barSpacing&&(e.barSpacing=parseInt(t.dataset.barSpacing)),t.dataset.buttonAlign&&(e.buttonAlign=t.dataset.buttonAlign),t.dataset.colorPreset&&(e.colorPreset=t.dataset.colorPreset),t.dataset.waveformColor&&(e.waveformColor=t.dataset.waveformColor),t.dataset.progressColor&&(e.progressColor=t.dataset.progressColor),t.dataset.buttonColor&&(e.buttonColor=t.dataset.buttonColor),t.dataset.buttonHoverColor&&(e.buttonHoverColor=t.dataset.buttonHoverColor),t.dataset.textColor&&(e.textColor=t.dataset.textColor),t.dataset.textSecondaryColor&&(e.textSecondaryColor=t.dataset.textSecondaryColor),t.dataset.backgroundColor&&(e.backgroundColor=t.dataset.backgroundColor),t.dataset.borderColor&&(e.borderColor=t.dataset.borderColor),t.dataset.color&&(e.waveformColor=t.dataset.color),t.dataset.theme&&(e.colorPreset=t.dataset.theme),t.dataset.autoplay&&(e.autoplay=t.dataset.autoplay==="true"),t.dataset.showControls!==void 0&&(e.showControls=t.dataset.showControls==="true"),t.dataset.showInfo!==void 0&&(e.showInfo=t.dataset.showInfo==="true"),t.dataset.showTime&&(e.showTime=t.dataset.showTime==="true"),t.dataset.showHoverTime&&(e.showHoverTime=t.dataset.showHoverTime==="true"),t.dataset.showBpm&&(e.showBPM=t.dataset.showBpm==="true"),t.dataset.singlePlay&&(e.singlePlay=t.dataset.singlePlay==="true"),t.dataset.playOnSeek&&(e.playOnSeek=t.dataset.playOnSeek==="true"),t.dataset.title&&(e.title=t.dataset.title),t.dataset.subtitle&&(e.subtitle=t.dataset.subtitle),t.dataset.album&&(e.album=t.dataset.album),t.dataset.artwork&&(e.artwork=t.dataset.artwork),t.dataset.waveform&&(e.waveform=t.dataset.waveform),t.dataset.markers)try{e.markers=JSON.parse(t.dataset.markers)}catch(i){console.warn("Invalid markers JSON:",i)}if(t.dataset.playbackRate&&(e.playbackRate=parseFloat(t.dataset.playbackRate)),t.dataset.showPlaybackSpeed!==void 0&&(e.showPlaybackSpeed=t.dataset.showPlaybackSpeed==="true"),t.dataset.playbackRates)try{e.playbackRates=JSON.parse(t.dataset.playbackRates)}catch(i){console.warn("Invalid playbackRates JSON:",i)}return t.dataset.enableMediaSession!==void 0&&(e.enableMediaSession=t.dataset.enableMediaSession==="true"),e}function S(t){if(!t||isNaN(t))return"0:00";let e=Math.floor(t/60),i=Math.floor(t%60);return`${e}:${i.toString().padStart(2,"0")}`}function x(t){let e=t||Math.random().toString();return btoa(e.substring(0,10)).replace(/[^a-zA-Z0-9]/g,"")}function B(t){if(!t)return"Audio";let e=t.split("/");return e[e.length-1].split(".")[0].replace(/[-_]/g," ").replace(/\b\w/g,s=>s.toUpperCase())}function C(...t){let e={};for(let i of t)for(let a in i)i[a]!==null&&i[a]!==void 0&&(e[a]=i[a]);return e}function R(t,e){let i;return function(...s){let o=()=>{clearTimeout(i),t(...s)};clearTimeout(i),i=setTimeout(o,e)}}function P(t,e){if(t.length===e)return t;if(t.length===0||e===0)return[];let i=[];if(e>t.length){let a=(t.length-1)/(e-1);for(let s=0;s<e;s++){let o=s*a,n=Math.floor(o),r=Math.ceil(o),h=o-n;if(r>=t.length)i.push(t[t.length-1]);else if(n===r)i.push(t[n]);else{let l=t[n]*(1-h)+t[r]*h;i.push(l)}}}else{let a=t.length/e;for(let s=0;s<e;s++){let o=Math.floor(s*a),n=Math.floor((s+1)*a),r=0,h=0;for(let l=o;l<=n&&l<t.length;l++)t[l]>r&&(r=t[l]),h++;if(h===0){let l=Math.min(Math.round(s*a),t.length-1);r=t[l]}i.push(r)}}return i}function E(t,e,i,a,s){let o=window.devicePixelRatio||1,n=s.barWidth*o,r=s.barSpacing*o,h=Math.floor(e.width/(n+r)),l=P(i,h),d=e.height,p=a*e.width;t.clearRect(0,0,e.width,e.height);for(let y=0;y<l.length;y++){let f=y*(n+r);if(f+n>e.width)break;let c=l[y]*d*.9,m=d-c;t.fillStyle=s.color,t.fillRect(f,m,n,c)}t.save(),t.beginPath(),t.rect(0,0,p,d),t.clip();for(let y=0;y<l.length;y++){let f=y*(n+r);if(f>p)break;let c=l[y]*d*.9,m=d-c;t.fillStyle=s.progressColor,t.fillRect(f,m,n,c)}t.restore()}function U(t,e,i,a,s){let o=window.devicePixelRatio||1,n=s.barWidth*o,r=s.barSpacing*o,h=Math.floor(e.width/(n+r)),l=P(i,h),d=e.height,p=d/2,y=a*e.width;t.clearRect(0,0,e.width,e.height);for(let f=0;f<l.length;f++){let c=f*(n+r);if(c+n>e.width)break;let m=l[f]*d*.45;t.fillStyle=s.color,t.fillRect(c,p-m,n,m),t.fillRect(c,p,n,m)}t.save(),t.beginPath(),t.rect(0,0,y,d),t.clip();for(let f=0;f<l.length;f++){let c=f*(n+r);if(c>y)break;let m=l[f]*d*.45;t.fillStyle=s.progressColor,t.fillRect(c,p-m,n,m),t.fillRect(c,p,n,m)}t.restore()}function F(t,e,i,a,s){let o=e.width,n=e.height,r=n/2,h=n*.35;t.clearRect(0,0,o,n);let l=(d,p,y=1,f=!1)=>{f&&(t.shadowBlur=12,t.shadowColor=d),t.strokeStyle=d,t.lineWidth=p,t.lineCap="round",t.lineJoin="round",t.beginPath(),t.moveTo(0,r);let c=[],m=Math.floor(i.length*y);for(let u=0;u<m;u++){let v=u/(i.length-1)*o,k=i[u],b=Math.sin(u*.1)*k,w=r+b*h;c.push({x:v,y:w})}for(let u=0;u<c.length-1;u++){let v=c[u].x+(c[u+1].x-c[u].x)*.5,k=c[u].y,b=c[u+1].x-(c[u+1].x-c[u].x)*.5,w=c[u+1].y;t.bezierCurveTo(v,k,b,w,c[u+1].x,c[u+1].y)}t.stroke(),f&&(t.shadowBlur=0)};t.strokeStyle="rgba(255, 255, 255, 0.03)",t.lineWidth=.5,t.beginPath(),t.moveTo(0,r),t.lineTo(o,r),t.stroke();for(let d=0;d<=10;d++){let p=o/10*d;t.beginPath(),t.moveTo(p,0),t.lineTo(p,n),t.stroke()}l(s.color,2,1,!1),a>0&&l(s.progressColor,3,a,!0)}function I(t,e,i,a,s){let o=window.devicePixelRatio||1,n=(s.barWidth||3)*o,r=(s.barSpacing||1)*o,h=Math.floor(e.width/(n+r)),l=P(i,h),d=e.height,p=4*o,y=2*o,f=a*e.width,c=d/2;t.clearRect(0,0,e.width,e.height);for(let m=0;m<l.length;m++){let u=m*(n+r);if(u+n>e.width)break;let v=l[m]*d*.9,k=Math.floor(v/(p+y));t.fillStyle=u<f?s.progressColor:s.color;for(let b=0;b<k;b++){let w=b*(p+y);t.fillRect(u,c-w-p,n,p),b>0&&t.fillRect(u,c+w,n,p)}}}function D(t,e,i,a,s){let o=window.devicePixelRatio||1,n=(s.barWidth||2)*o,r=(s.barSpacing||3)*o,h=Math.floor(e.width/(n+r)),l=P(i,h),d=e.height,p=Math.max(1.5*o,n/2),y=a*e.width,f=d/2;t.clearRect(0,0,e.width,e.height);for(let c=0;c<l.length;c++){let m=c*(n+r)+n/2;if(m>e.width)break;let u=l[c]*d*.9;t.fillStyle=m<y?s.progressColor:s.color,t.beginPath(),t.arc(m,f-u/2,p,0,Math.PI*2),t.fill(),t.beginPath(),t.arc(m,f+u/2,p,0,Math.PI*2),t.fill()}}function N(t,e,i,a,s){let o=e.width,n=e.height,r=n/2,h=4,l=h/2;if(t.clearRect(0,0,o,n),t.fillStyle=s.color||"rgba(255, 255, 255, 0.2)",t.beginPath(),t.moveTo(l,r-h/2),t.lineTo(o-l,r-h/2),t.arc(o-l,r,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,r+h/2),t.arc(l,r,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),a>0){let d=Math.max(l*2,a*o);t.shadowBlur=8,t.shadowColor=s.progressColor,t.fillStyle=s.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.moveTo(l,r-h/2),t.lineTo(d-l,r-h/2),t.arc(d-l,r,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,r+h/2),t.arc(l,r,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),t.shadowBlur=0;let p=8,y=d;t.shadowBlur=4,t.shadowColor="rgba(0, 0, 0, 0.3)",t.shadowOffsetY=2,t.fillStyle="#ffffff",t.beginPath(),t.arc(y,r,p,0,Math.PI*2),t.fill(),t.shadowBlur=0,t.shadowOffsetY=0,t.fillStyle=s.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.arc(y,r,p*.4,0,Math.PI*2),t.fill()}}var Y={bars:E,bar:E,mirror:U,line:F,blocks:I,block:I,dots:D,dot:D,seekbar:N};function W(t,e,i,a,s){(Y[s.waveformStyle]||E)(t,e,i,a,s)}function z(t){try{let e=t.getChannelData(0),i=t.sampleRate,a=j(e,i);if(a.length<2)return 120;let s=[];for(let h=1;h<a.length;h++)s.push((a[h]-a[h-1])/i);let o={};s.forEach(h=>{let l=60/h,d=Math.round(l/3)*3;d>60&&d<200&&(o[d]=(o[d]||0)+1)});let n=0,r=120;for(let[h,l]of Object.entries(o))l>n&&(n=l,r=parseInt(h));return r<70&&o[r*2]?r*=2:r>160&&o[Math.round(r/2)]&&(r=Math.round(r/2)),r-1}catch(e){return console.warn("BPM detection failed:",e),null}}function j(t,e){let s=[],o=0;for(let n=0;n<t.length-2048;n+=1024){let r=0;for(let d=n;d<n+2048;d++)r+=t[d]*t[d];r=r/2048;let h=r-o,l=o*1.8+.01;if(h>l&&r>.01){let d=s[s.length-1]||0,p=e*.15;n-d>p&&s.push(n)}o=r*.8+o*.2}return s}function V(t,e=200){let i=t.length/e,a=~~(i/10)||1,s=t.numberOfChannels,o=[];for(let r=0;r<s;r++){let h=t.getChannelData(r);for(let l=0;l<e;l++){let d=~~(l*i),p=~~(d+i),y=0,f=0;for(let m=d;m<p;m+=a){let u=h[m];u>f&&(f=u),u<y&&(y=u)}let c=Math.max(Math.abs(f),Math.abs(y));(r===0||c>o[l])&&(o[l]=c)}}let n=Math.max(...o);return n>0?o.map(r=>r/n):o}async function M(t,e=200,i=!1){try{let a=new(window.AudioContext||window.webkitAudioContext),o=await(await fetch(t)).arrayBuffer(),n=await a.decodeAudioData(o),r=V(n,e);r=J(r);let h=null;return i&&(h=await z(n)),a.close(),{peaks:r,bpm:h}}catch(a){throw console.error("Failed to generate waveform:",a),a}}function O(t=200){let e=[];for(let i=0;i<t;i++){let a=Math.random()*.5+.3,s=Math.sin(i/t*Math.PI*4)*.2;e.push(Math.max(.1,Math.min(1,a+s)))}return e}function J(t,e=.95){let i=Math.max(...t);if(i===0||i>e)return t;let a=e/i;return t.map(s=>s*a)}function _(){let t=document.documentElement,e=document.body;if(t.classList.contains("dark")||t.classList.contains("dark-mode")||t.classList.contains("theme-dark")||t.getAttribute("data-theme")==="dark"||t.getAttribute("data-color-scheme")==="dark"||e.classList.contains("dark")||e.classList.contains("dark-mode")||e.getAttribute("data-theme")==="dark")return"dark";if(t.classList.contains("light")||t.classList.contains("light-mode")||t.classList.contains("theme-light")||t.getAttribute("data-theme")==="light"||t.getAttribute("data-color-scheme")==="light"||e.classList.contains("light")||e.classList.contains("light-mode")||e.getAttribute("data-theme")==="light")return"light";try{let a=getComputedStyle(document.body).backgroundColor.match(/\d+/g);if(a&&a.length>=3){let[s,o,n]=a.map(Number),r=(s*299+o*587+n*114)/1e3;if(r>128)return"light";if(r<128)return"dark"}}catch{}if(window.matchMedia){if(window.matchMedia("(prefers-color-scheme: dark)").matches)return"dark";if(window.matchMedia("(prefers-color-scheme: light)").matches)return"light"}return"dark"}var T={dark:{waveformColor:"rgba(255, 255, 255, 0.3)",progressColor:"rgba(255, 255, 255, 0.9)",buttonColor:"rgba(255, 255, 255, 0.9)",buttonHoverColor:"rgba(255, 255, 255, 1)",textColor:"#ffffff",textSecondaryColor:"rgba(255, 255, 255, 0.6)",backgroundColor:"rgba(255, 255, 255, 0.03)",borderColor:"rgba(255, 255, 255, 0.1)"},light:{waveformColor:"rgba(0, 0, 0, 0.2)",progressColor:"rgba(0, 0, 0, 0.8)",buttonColor:"rgba(0, 0, 0, 0.8)",buttonHoverColor:"rgba(0, 0, 0, 0.9)",textColor:"#333333",textSecondaryColor:"rgba(0, 0, 0, 0.6)",backgroundColor:"rgba(0, 0, 0, 0.02)",borderColor:"rgba(0, 0, 0, 0.1)"}};function H(t){if(t&&T[t])return T[t];let e=_();return T[e]}var q={url:"",height:60,samples:200,preload:"metadata",audioMode:"self",playbackRate:1,showPlaybackSpeed:!1,playbackRates:[.5,.75,1,1.25,1.5,1.75,2],buttonAlign:"auto",waveformStyle:"mirror",barWidth:2,barSpacing:0,colorPreset:null,waveformColor:null,progressColor:null,buttonColor:null,buttonHoverColor:null,textColor:null,textSecondaryColor:null,backgroundColor:null,borderColor:null,autoplay:!1,showControls:!0,showInfo:!0,showTime:!0,showHoverTime:!1,showBPM:!1,singlePlay:!0,playOnSeek:!0,enableMediaSession:!0,markers:[],showMarkers:!0,title:null,subtitle:null,artwork:null,album:"",playIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M8 5v14l11-7z"/></svg>',pauseIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M6 4h4v16H6zM14 4h4v16h-4z"/></svg>',onLoad:null,onPlay:null,onPause:null,onEnd:null,onError:null,onTimeUpdate:null},$={bars:{barWidth:3,barSpacing:1},mirror:{barWidth:2,barSpacing:0},line:{barWidth:2,barSpacing:0},blocks:{barWidth:4,barSpacing:2},dots:{barWidth:3,barSpacing:3},seekbar:{barWidth:1,barSpacing:0}};var g=class t{static instances=new Map;static currentlyPlaying=null;constructor(e,i={}){if(this.container=typeof e=="string"?document.querySelector(e):e,!this.container)throw new Error("WaveformPlayer: Container element not found");let a=A(this.container);this.options=C(q,a,i);let s=H(this.options.colorPreset);for(let[n,r]of Object.entries(s))(this.options[n]===null||this.options[n]===void 0)&&(this.options[n]=r);let o=$[this.options.waveformStyle];o&&(a.barWidth===void 0&&i.barWidth===void 0&&(this.options.barWidth=o.barWidth),a.barSpacing===void 0&&i.barSpacing===void 0&&(this.options.barSpacing=o.barSpacing)),this.audio=null,this.canvas=null,this.ctx=null,this.waveformData=[],this.progress=0,this.isPlaying=!1,this.isLoading=!1,this.hasError=!1,this.updateTimer=null,this.resizeObserver=null,this.id=this.container.id||x(this.options.url),t.instances.set(this.id,this),this.init(),setTimeout(()=>{this.container.dispatchEvent(new CustomEvent("waveformplayer:ready",{bubbles:!0,detail:{player:this,url:this.options.url}}))},100)}init(){this.createDOM(),this.createAudio(),this.initPlaybackSpeed(),this.initKeyboardControls(),this.bindEvents(),this.setupResizeObserver(),requestAnimationFrame(()=>{this.resizeCanvas(),this.options.url&&this.load(this.options.url).then(()=>{this.options.autoplay&&this.play()}).catch(e=>{console.error("Failed to load audio:",e)})})}createDOM(){this.container.innerHTML="",this.container.className="waveform-player";let e=this.options.buttonAlign;e==="auto"&&(this.options.waveformStyle==="bars"?e="bottom":e="center");let i=this.options.showControls?`
1
+ function L(t){let e={};if(t.dataset.url&&(e.url=t.dataset.url),t.dataset.height&&(e.height=parseInt(t.dataset.height)),t.dataset.samples&&(e.samples=parseInt(t.dataset.samples)),t.dataset.preload&&(e.preload=t.dataset.preload),t.dataset.waveformStyle&&(e.waveformStyle=t.dataset.waveformStyle),t.dataset.barWidth&&(e.barWidth=parseInt(t.dataset.barWidth)),t.dataset.barSpacing&&(e.barSpacing=parseInt(t.dataset.barSpacing)),t.dataset.buttonAlign&&(e.buttonAlign=t.dataset.buttonAlign),t.dataset.colorPreset&&(e.colorPreset=t.dataset.colorPreset),t.dataset.waveformColor&&(e.waveformColor=t.dataset.waveformColor),t.dataset.progressColor&&(e.progressColor=t.dataset.progressColor),t.dataset.buttonColor&&(e.buttonColor=t.dataset.buttonColor),t.dataset.buttonHoverColor&&(e.buttonHoverColor=t.dataset.buttonHoverColor),t.dataset.textColor&&(e.textColor=t.dataset.textColor),t.dataset.textSecondaryColor&&(e.textSecondaryColor=t.dataset.textSecondaryColor),t.dataset.backgroundColor&&(e.backgroundColor=t.dataset.backgroundColor),t.dataset.borderColor&&(e.borderColor=t.dataset.borderColor),t.dataset.color&&(e.waveformColor=t.dataset.color),t.dataset.theme&&(e.colorPreset=t.dataset.theme),t.dataset.autoplay&&(e.autoplay=t.dataset.autoplay==="true"),t.dataset.showControls!==void 0&&(e.showControls=t.dataset.showControls==="true"),t.dataset.showInfo!==void 0&&(e.showInfo=t.dataset.showInfo==="true"),t.dataset.showTime&&(e.showTime=t.dataset.showTime==="true"),t.dataset.showHoverTime&&(e.showHoverTime=t.dataset.showHoverTime==="true"),t.dataset.showBpm&&(e.showBPM=t.dataset.showBpm==="true"),t.dataset.singlePlay&&(e.singlePlay=t.dataset.singlePlay==="true"),t.dataset.playOnSeek&&(e.playOnSeek=t.dataset.playOnSeek==="true"),t.dataset.title&&(e.title=t.dataset.title),t.dataset.subtitle&&(e.subtitle=t.dataset.subtitle),t.dataset.album&&(e.album=t.dataset.album),t.dataset.artwork&&(e.artwork=t.dataset.artwork),t.dataset.waveform&&(e.waveform=t.dataset.waveform),t.dataset.markers)try{e.markers=JSON.parse(t.dataset.markers)}catch(i){console.warn("Invalid markers JSON:",i)}if(t.dataset.playbackRate&&(e.playbackRate=parseFloat(t.dataset.playbackRate)),t.dataset.showPlaybackSpeed!==void 0&&(e.showPlaybackSpeed=t.dataset.showPlaybackSpeed==="true"),t.dataset.playbackRates)try{e.playbackRates=JSON.parse(t.dataset.playbackRates)}catch(i){console.warn("Invalid playbackRates JSON:",i)}return t.dataset.enableMediaSession!==void 0&&(e.enableMediaSession=t.dataset.enableMediaSession==="true"),e}function g(t){if(!t||isNaN(t))return"0:00";let e=Math.floor(t/60),i=Math.floor(t%60);return`${e}:${i.toString().padStart(2,"0")}`}function x(t){let e=t||Math.random().toString();return btoa(e.substring(0,10)).replace(/[^a-zA-Z0-9]/g,"")}function D(t){if(!t)return"Audio";let e=t.split("/");return e[e.length-1].split(".")[0].replace(/[-_]/g," ").replace(/\b\w/g,s=>s.toUpperCase())}function C(...t){let e={};for(let i of t)for(let a in i)i[a]!==null&&i[a]!==void 0&&(e[a]=i[a]);return e}function B(t,e){let i;return function(...s){let o=()=>{clearTimeout(i),t(...s)};clearTimeout(i),i=setTimeout(o,e)}}function P(t,e){if(t.length===e)return t;if(t.length===0||e===0)return[];let i=[];if(e>t.length){let a=(t.length-1)/(e-1);for(let s=0;s<e;s++){let o=s*a,n=Math.floor(o),r=Math.ceil(o),h=o-n;if(r>=t.length)i.push(t[t.length-1]);else if(n===r)i.push(t[n]);else{let l=t[n]*(1-h)+t[r]*h;i.push(l)}}}else{let a=t.length/e;for(let s=0;s<e;s++){let o=Math.floor(s*a),n=Math.floor((s+1)*a),r=0,h=0;for(let l=o;l<=n&&l<t.length;l++)t[l]>r&&(r=t[l]),h++;if(h===0){let l=Math.min(Math.round(s*a),t.length-1);r=t[l]}i.push(r)}}return i}function E(t,e,i,a,s){let o=window.devicePixelRatio||1,n=s.barWidth*o,r=s.barSpacing*o,h=Math.floor(e.width/(n+r)),l=P(i,h),d=e.height,p=a*e.width;t.clearRect(0,0,e.width,e.height);for(let b=0;b<l.length;b++){let f=b*(n+r);if(f+n>e.width)break;let c=l[b]*d*.9,m=d-c;t.fillStyle=s.color,t.fillRect(f,m,n,c)}t.save(),t.beginPath(),t.rect(0,0,p,d),t.clip();for(let b=0;b<l.length;b++){let f=b*(n+r);if(f>p)break;let c=l[b]*d*.9,m=d-c;t.fillStyle=s.progressColor,t.fillRect(f,m,n,c)}t.restore()}function N(t,e,i,a,s){let o=window.devicePixelRatio||1,n=s.barWidth*o,r=s.barSpacing*o,h=Math.floor(e.width/(n+r)),l=P(i,h),d=e.height,p=d/2,b=a*e.width;t.clearRect(0,0,e.width,e.height);for(let f=0;f<l.length;f++){let c=f*(n+r);if(c+n>e.width)break;let m=l[f]*d*.45;t.fillStyle=s.color,t.fillRect(c,p-m,n,m),t.fillRect(c,p,n,m)}t.save(),t.beginPath(),t.rect(0,0,b,d),t.clip();for(let f=0;f<l.length;f++){let c=f*(n+r);if(c>b)break;let m=l[f]*d*.45;t.fillStyle=s.progressColor,t.fillRect(c,p-m,n,m),t.fillRect(c,p,n,m)}t.restore()}function Y(t,e,i,a,s){let o=e.width,n=e.height,r=n/2,h=n*.35;t.clearRect(0,0,o,n);let l=(d,p,b=1,f=!1)=>{f&&(t.shadowBlur=12,t.shadowColor=d),t.strokeStyle=d,t.lineWidth=p,t.lineCap="round",t.lineJoin="round",t.beginPath(),t.moveTo(0,r);let c=[],m=Math.floor(i.length*b);for(let u=0;u<m;u++){let k=u/(i.length-1)*o,S=i[u],y=Math.sin(u*.1)*S,v=r+y*h;c.push({x:k,y:v})}for(let u=0;u<c.length-1;u++){let k=c[u].x+(c[u+1].x-c[u].x)*.5,S=c[u].y,y=c[u+1].x-(c[u+1].x-c[u].x)*.5,v=c[u+1].y;t.bezierCurveTo(k,S,y,v,c[u+1].x,c[u+1].y)}t.stroke(),f&&(t.shadowBlur=0)};t.strokeStyle="rgba(255, 255, 255, 0.03)",t.lineWidth=.5,t.beginPath(),t.moveTo(0,r),t.lineTo(o,r),t.stroke();for(let d=0;d<=10;d++){let p=o/10*d;t.beginPath(),t.moveTo(p,0),t.lineTo(p,n),t.stroke()}l(s.color,2,1,!1),a>0&&l(s.progressColor,3,a,!0)}function R(t,e,i,a,s){let o=window.devicePixelRatio||1,n=(s.barWidth||3)*o,r=(s.barSpacing||1)*o,h=Math.floor(e.width/(n+r)),l=P(i,h),d=e.height,p=4*o,b=2*o,f=a*e.width,c=d/2;t.clearRect(0,0,e.width,e.height);for(let m=0;m<l.length;m++){let u=m*(n+r);if(u+n>e.width)break;let k=l[m]*d*.9,S=Math.floor(k/(p+b));t.fillStyle=u<f?s.progressColor:s.color;for(let y=0;y<S;y++){let v=y*(p+b);t.fillRect(u,c-v-p,n,p),y>0&&t.fillRect(u,c+v,n,p)}}}function I(t,e,i,a,s){let o=window.devicePixelRatio||1,n=(s.barWidth||2)*o,r=(s.barSpacing||3)*o,h=Math.floor(e.width/(n+r)),l=P(i,h),d=e.height,p=Math.max(1.5*o,n/2),b=a*e.width,f=d/2;t.clearRect(0,0,e.width,e.height);for(let c=0;c<l.length;c++){let m=c*(n+r)+n/2;if(m>e.width)break;let u=l[c]*d*.9;t.fillStyle=m<b?s.progressColor:s.color,t.beginPath(),t.arc(m,f-u/2,p,0,Math.PI*2),t.fill(),t.beginPath(),t.arc(m,f+u/2,p,0,Math.PI*2),t.fill()}}function _(t,e,i,a,s){let o=e.width,n=e.height,r=n/2,h=4,l=h/2;if(t.clearRect(0,0,o,n),t.fillStyle=s.color||"rgba(255, 255, 255, 0.2)",t.beginPath(),t.moveTo(l,r-h/2),t.lineTo(o-l,r-h/2),t.arc(o-l,r,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,r+h/2),t.arc(l,r,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),a>0){let d=Math.max(l*2,a*o);t.shadowBlur=8,t.shadowColor=s.progressColor,t.fillStyle=s.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.moveTo(l,r-h/2),t.lineTo(d-l,r-h/2),t.arc(d-l,r,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,r+h/2),t.arc(l,r,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),t.shadowBlur=0;let p=8,b=d;t.shadowBlur=4,t.shadowColor="rgba(0, 0, 0, 0.3)",t.shadowOffsetY=2,t.fillStyle="#ffffff",t.beginPath(),t.arc(b,r,p,0,Math.PI*2),t.fill(),t.shadowBlur=0,t.shadowOffsetY=0,t.fillStyle=s.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.arc(b,r,p*.4,0,Math.PI*2),t.fill()}}var j={bars:E,bar:E,mirror:N,line:Y,blocks:R,block:R,dots:I,dot:I,seekbar:_};function W(t,e,i,a,s){(j[s.waveformStyle]||E)(t,e,i,a,s)}function z(t){try{let e=t.getChannelData(0),i=t.sampleRate,a=V(e,i);if(a.length<2)return 120;let s=[];for(let h=1;h<a.length;h++)s.push((a[h]-a[h-1])/i);let o={};s.forEach(h=>{let l=60/h,d=Math.round(l/3)*3;d>60&&d<200&&(o[d]=(o[d]||0)+1)});let n=0,r=120;for(let[h,l]of Object.entries(o))l>n&&(n=l,r=parseInt(h));return r<70&&o[r*2]?r*=2:r>160&&o[Math.round(r/2)]&&(r=Math.round(r/2)),r-1}catch(e){return console.warn("BPM detection failed:",e),null}}function V(t,e){let s=[],o=0;for(let n=0;n<t.length-2048;n+=1024){let r=0;for(let d=n;d<n+2048;d++)r+=t[d]*t[d];r=r/2048;let h=r-o,l=o*1.8+.01;if(h>l&&r>.01){let d=s[s.length-1]||0,p=e*.15;n-d>p&&s.push(n)}o=r*.8+o*.2}return s}function J(t,e=200){let i=t.length/e,a=~~(i/10)||1,s=t.numberOfChannels,o=[];for(let r=0;r<s;r++){let h=t.getChannelData(r);for(let l=0;l<e;l++){let d=~~(l*i),p=~~(d+i),b=0,f=0;for(let m=d;m<p;m+=a){let u=h[m];u>f&&(f=u),u<b&&(b=u)}let c=Math.max(Math.abs(f),Math.abs(b));(r===0||c>o[l])&&(o[l]=c)}}let n=Math.max(...o);return n>0?o.map(r=>r/n):o}async function M(t,e=200,i=!1){try{let a=new(window.AudioContext||window.webkitAudioContext),o=await(await fetch(t)).arrayBuffer(),n=await a.decodeAudioData(o),r=J(n,e);r=K(r);let h=null;return i&&(h=await z(n)),a.close(),{peaks:r,bpm:h}}catch(a){throw console.error("Failed to generate waveform:",a),a}}function O(t=200){let e=[];for(let i=0;i<t;i++){let a=Math.random()*.5+.3,s=Math.sin(i/t*Math.PI*4)*.2;e.push(Math.max(.1,Math.min(1,a+s)))}return e}function K(t,e=.95){let i=Math.max(...t);if(i===0||i>e)return t;let a=e/i;return t.map(s=>s*a)}function G(){let t=document.documentElement,e=document.body;if(t.classList.contains("dark")||t.classList.contains("dark-mode")||t.classList.contains("theme-dark")||t.getAttribute("data-theme")==="dark"||t.getAttribute("data-color-scheme")==="dark"||e.classList.contains("dark")||e.classList.contains("dark-mode")||e.getAttribute("data-theme")==="dark")return"dark";if(t.classList.contains("light")||t.classList.contains("light-mode")||t.classList.contains("theme-light")||t.getAttribute("data-theme")==="light"||t.getAttribute("data-color-scheme")==="light"||e.classList.contains("light")||e.classList.contains("light-mode")||e.getAttribute("data-theme")==="light")return"light";try{let a=getComputedStyle(document.body).backgroundColor.match(/\d+/g);if(a&&a.length>=3){let[s,o,n]=a.map(Number),r=(s*299+o*587+n*114)/1e3;if(r>128)return"light";if(r<128)return"dark"}}catch{}if(window.matchMedia){if(window.matchMedia("(prefers-color-scheme: dark)").matches)return"dark";if(window.matchMedia("(prefers-color-scheme: light)").matches)return"light"}return"dark"}var T={dark:{waveformColor:"rgba(255, 255, 255, 0.3)",progressColor:"rgba(255, 255, 255, 0.9)",buttonColor:"rgba(255, 255, 255, 0.9)",buttonHoverColor:"rgba(255, 255, 255, 1)",textColor:"#ffffff",textSecondaryColor:"rgba(255, 255, 255, 0.6)",backgroundColor:"rgba(255, 255, 255, 0.03)",borderColor:"rgba(255, 255, 255, 0.1)"},light:{waveformColor:"rgba(0, 0, 0, 0.2)",progressColor:"rgba(0, 0, 0, 0.8)",buttonColor:"rgba(0, 0, 0, 0.8)",buttonHoverColor:"rgba(0, 0, 0, 0.9)",textColor:"#333333",textSecondaryColor:"rgba(0, 0, 0, 0.6)",backgroundColor:"rgba(0, 0, 0, 0.02)",borderColor:"rgba(0, 0, 0, 0.1)"}};function H(t){if(t&&T[t])return T[t];let e=G();return T[e]}var q={url:"",height:60,samples:200,preload:"metadata",audioMode:"self",playbackRate:1,showPlaybackSpeed:!1,playbackRates:[.5,.75,1,1.25,1.5,1.75,2],buttonAlign:"auto",waveformStyle:"mirror",barWidth:2,barSpacing:0,colorPreset:null,waveformColor:null,progressColor:null,buttonColor:null,buttonHoverColor:null,textColor:null,textSecondaryColor:null,backgroundColor:null,borderColor:null,autoplay:!1,showControls:!0,showInfo:!0,showTime:!0,showHoverTime:!1,showBPM:!1,singlePlay:!0,playOnSeek:!0,enableMediaSession:!0,markers:[],showMarkers:!0,accessibleSeek:!0,seekLabel:null,title:null,subtitle:null,artwork:null,album:"",playIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M8 5v14l11-7z"/></svg>',pauseIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M6 4h4v16H6zM14 4h4v16h-4z"/></svg>',onLoad:null,onPlay:null,onPause:null,onEnd:null,onError:null,onTimeUpdate:null},$={bars:{barWidth:3,barSpacing:1},mirror:{barWidth:2,barSpacing:0},line:{barWidth:2,barSpacing:0},blocks:{barWidth:4,barSpacing:2},dots:{barWidth:3,barSpacing:3},seekbar:{barWidth:1,barSpacing:0}};var U=5,F=10,w=class t{static instances=new Map;static currentlyPlaying=null;constructor(e,i={}){if(this.container=typeof e=="string"?document.querySelector(e):e,!this.container)throw new Error("WaveformPlayer: Container element not found");let a=L(this.container);this.options=C(q,a,i);let s=H(this.options.colorPreset);for(let[n,r]of Object.entries(s))(this.options[n]===null||this.options[n]===void 0)&&(this.options[n]=r);let o=$[this.options.waveformStyle];o&&(a.barWidth===void 0&&i.barWidth===void 0&&(this.options.barWidth=o.barWidth),a.barSpacing===void 0&&i.barSpacing===void 0&&(this.options.barSpacing=o.barSpacing)),this.audio=null,this.canvas=null,this.ctx=null,this.waveformData=[],this.progress=0,this.isPlaying=!1,this.isLoading=!1,this.hasError=!1,this.updateTimer=null,this.resizeObserver=null,this.id=this.container.id||x(this.options.url),t.instances.set(this.id,this),this.init(),setTimeout(()=>{this.container.dispatchEvent(new CustomEvent("waveformplayer:ready",{bubbles:!0,detail:{player:this,url:this.options.url}}))},100)}init(){this.createDOM(),this.createAudio(),this.initPlaybackSpeed(),this.initKeyboardControls(),this.initSeekControl(),this.bindEvents(),this.setupResizeObserver(),requestAnimationFrame(()=>{this.resizeCanvas(),this.options.url&&this.load(this.options.url).then(()=>{this.options.autoplay&&this.play()}).catch(e=>{console.error("Failed to load audio:",e)})})}createDOM(){this.container.innerHTML="",this.container.className="waveform-player";let e=this.options.buttonAlign;e==="auto"&&(this.options.waveformStyle==="bars"?e="bottom":e="center");let i=this.options.showControls?`
2
2
  <button class="waveform-btn" aria-label="Play/Pause" style="
3
3
  border-color: ${this.options.buttonColor};
4
4
  color: ${this.options.buttonColor};
@@ -63,4 +63,4 @@ function A(t){let e={};if(t.dataset.url&&(e.url=t.dataset.url),t.dataset.height&
63
63
  ${a}
64
64
  </div>
65
65
  </div>
66
- `,this.playBtn=this.container.querySelector(".waveform-btn"),this.canvas=this.container.querySelector("canvas"),this.ctx=this.canvas.getContext("2d"),this.titleEl=this.container.querySelector(".waveform-title"),this.subtitleEl=this.container.querySelector(".waveform-subtitle"),this.artworkEl=this.container.querySelector(".waveform-artwork"),this.currentTimeEl=this.container.querySelector(".time-current"),this.totalTimeEl=this.container.querySelector(".time-total"),this.bpmEl=this.container.querySelector(".waveform-bpm"),this.bpmValueEl=this.container.querySelector(".bpm-value"),this.loadingEl=this.container.querySelector(".waveform-loading"),this.errorEl=this.container.querySelector(".waveform-error"),this.markersContainer=this.container.querySelector(".waveform-markers"),this.speedBtn=this.container.querySelector(".speed-btn"),this.speedMenu=this.container.querySelector(".speed-menu"),this.resizeCanvas()}createAudio(){if(this.options.audioMode==="external"){this.audio=null;return}this.audio=new Audio,this.audio.preload=this.options.preload||"metadata",this.audio.crossOrigin="anonymous"}initPlaybackSpeed(){this.audio&&this.options.playbackRate&&this.options.playbackRate!==1&&(this.audio.playbackRate=this.options.playbackRate),this.options.showPlaybackSpeed&&this.initSpeedControls()}initSpeedControls(){let e=this.container.querySelector(".speed-btn"),i=this.container.querySelector(".speed-menu");!e||!i||(e.addEventListener("click",a=>{a.stopPropagation(),i.style.display=i.style.display==="none"?"block":"none"}),document.addEventListener("click",()=>{i.style.display="none"}),i.addEventListener("click",a=>{if(a.stopPropagation(),a.target.classList.contains("speed-option")){let s=parseFloat(a.target.dataset.rate);this.setPlaybackRate(s),i.style.display="none"}}),this.updateSpeedUI())}initKeyboardControls(){this.container.setAttribute("tabindex","-1"),this.container.addEventListener("click",()=>{t.getAllInstances().forEach(e=>{e!==this&&e.container.setAttribute("tabindex","-1")}),this.container.setAttribute("tabindex","0"),this.container.focus()}),this.container.addEventListener("keydown",e=>{if(document.activeElement!==this.container)return;let i=e.key,a=!!this.audio,s=a?this.audio.currentTime:0;if(a&&i>="0"&&i<="9"){e.preventDefault(),this.seekToPercent(parseInt(i)/10);return}let o={" ":()=>this.togglePlay()};a&&(o.ArrowLeft=()=>this.seekTo(Math.max(0,s-5)),o.ArrowRight=()=>this.seekTo(Math.min(this.audio.duration,s+5)),o.ArrowUp=()=>this.setVolume(Math.min(1,this.audio.volume+.1)),o.ArrowDown=()=>this.setVolume(Math.max(0,this.audio.volume-.1)),o.m=o.M=()=>this.audio.muted=!this.audio.muted),o[i]&&(e.preventDefault(),o[i]())})}initMediaSession(){!("mediaSession"in navigator)||!this.options.enableMediaSession||this.audio&&(navigator.mediaSession.metadata=new MediaMetadata({title:this.options.title||"Unknown Track",artist:this.options.subtitle||"",album:this.options.album||"",artwork:this.options.artwork?[{src:this.options.artwork,sizes:"512x512",type:"image/jpeg"}]:[]}),navigator.mediaSession.setActionHandler("play",()=>this.play()),navigator.mediaSession.setActionHandler("pause",()=>this.pause()),navigator.mediaSession.setActionHandler("seekbackward",()=>{this.seekTo(Math.max(0,this.audio.currentTime-10))}),navigator.mediaSession.setActionHandler("seekforward",()=>{this.seekTo(Math.min(this.audio.duration,this.audio.currentTime+10))}),navigator.mediaSession.setActionHandler("seekto",e=>{e.seekTime!==null&&this.seekTo(e.seekTime)}))}bindEvents(){this.playBtn&&this.playBtn.addEventListener("click",()=>this.togglePlay()),this.audio&&(this.audio.addEventListener("loadstart",()=>this.setLoading(!0)),this.audio.addEventListener("loadedmetadata",()=>this.onMetadataLoaded()),this.audio.addEventListener("canplay",()=>this.setLoading(!1)),this.audio.addEventListener("play",()=>this.onPlay()),this.audio.addEventListener("pause",()=>this.onPause()),this.audio.addEventListener("ended",()=>this.onEnded()),this.audio.addEventListener("error",e=>this.onError(e))),this.canvas.addEventListener("click",e=>this.handleCanvasClick(e)),this.resizeHandler=R(()=>this.resizeCanvas(),100),window.addEventListener("resize",this.resizeHandler)}setupResizeObserver(){"ResizeObserver"in window&&(this.resizeObserver=new ResizeObserver(()=>{this.resizeCanvas()}),this.canvas?.parentElement&&this.resizeObserver.observe(this.canvas.parentElement))}async load(e){try{this.setLoading(!0),this.progress=0,this.hasError=!1,this.audio&&(this.audio.src=e,await new Promise((a,s)=>{let o=()=>{this.audio.removeEventListener("loadedmetadata",o),this.audio.removeEventListener("error",n),a()},n=r=>{this.audio.removeEventListener("loadedmetadata",o),this.audio.removeEventListener("error",n),s(r)};this.audio.addEventListener("loadedmetadata",o),this.audio.addEventListener("error",n)}));let i=this.options.title||B(e);if(this.titleEl&&(this.titleEl.textContent=i),this.options.waveform)this.setWaveformData(this.options.waveform);else try{let a=await M(e,this.options.samples,this.options.showBPM);this.waveformData=a.peaks,a.bpm&&(this.detectedBPM=a.bpm,this.updateBPMDisplay())}catch(a){console.warn("Using placeholder waveform:",a),this.waveformData=O(this.options.samples)}this.drawWaveform(),this.renderMarkers(),this.initMediaSession(),this.options.onLoad&&this.options.onLoad(this)}catch(i){console.error("Failed to load audio:",i),this.onError(i)}finally{this.setLoading(!1)}}async loadTrack(e,i=null,a=null,s={}){this.isPlaying&&this.pause(),this.audio&&(this.audio.src="",this.audio.load()),this.hasError=!1,this.errorEl&&(this.errorEl.style.display="none"),this.canvas&&(this.canvas.style.opacity="1"),this.playBtn&&(this.playBtn.disabled=!1),this.progress=0,this.waveformData=[],this.options=C(this.options,{url:e,title:i||this.options.title,subtitle:a||this.options.subtitle,...s}),s.preload&&this.audio&&(this.audio.preload=s.preload),this.subtitleEl&&(a?(this.subtitleEl.textContent=a,this.subtitleEl.style.display=""):a===""&&(this.subtitleEl.style.display="none")),s.artwork&&this.artworkEl&&(this.artworkEl.src=s.artwork),this.options.markers=s.markers||[],await this.load(e),this.play().catch(()=>{})}setWaveformData(e){if(typeof e=="string"&&e.trim().endsWith(".json")){fetch(e.trim()).then(i=>i.json()).then(i=>{this.waveformData=Array.isArray(i)?i:i.peaks||[],i.markers&&!this.options.markers?.length&&(this.options.markers=i.markers,this.renderMarkers()),this.drawWaveform()}).catch(()=>{});return}if(typeof e=="string")try{let i=JSON.parse(e);this.waveformData=Array.isArray(i)?i:[]}catch{this.waveformData=e.split(",").map(Number)}else this.waveformData=Array.isArray(e)?e:[];this.drawWaveform()}drawWaveform(){!this.ctx||this.waveformData.length===0||W(this.ctx,this.canvas,this.waveformData,this.progress,{...this.options,waveformStyle:this.options.waveformStyle||"bars",color:this.options.waveformColor,progressColor:this.options.progressColor})}resizeCanvas(){if(!this.canvas||this.isDestroying)return;let e=window.devicePixelRatio||1,i=this.canvas.parentElement.getBoundingClientRect();this.canvas.width=i.width*e,this.canvas.height=this.options.height*e,this.canvas.parentElement.style.height=this.options.height+"px",this.drawWaveform()}renderMarkers(){this.markersContainer&&(this.markersContainer.innerHTML="",!(!this.options.showMarkers||!this.options.markers?.length)&&(!this.audio||!this.audio.duration||this.audio.duration===0||this.options.markers.forEach((e,i)=>{if(e.time>this.audio.duration){console.warn(`Marker "${e.label}" at ${e.time}s exceeds audio duration of ${this.audio.duration}s`);return}let a=e.time/this.audio.duration*100,s=document.createElement("button");s.className="waveform-marker",s.style.left=`${a}%`,s.style.backgroundColor=e.color||"rgba(255, 255, 255, 0.5)",s.setAttribute("aria-label",e.label),s.setAttribute("data-time",e.time);let o=document.createElement("span");o.className="waveform-marker-tooltip",o.textContent=e.label,s.appendChild(o),s.addEventListener("click",n=>{n.stopPropagation(),this.seekTo(e.time),this.options.playOnSeek&&!this.isPlaying&&this.play()}),this.markersContainer.appendChild(s)})))}handleCanvasClick(e){let i=this.canvas.getBoundingClientRect(),a=e.clientX-i.left,s=Math.max(0,Math.min(1,a/i.width));if(this.options.audioMode==="external"){let o=new CustomEvent("waveformplayer:request-seek",{bubbles:!0,cancelable:!0,detail:{...this._buildTrackDetail(),percent:s}});this.container.dispatchEvent(o),o.defaultPrevented||(this.progress=s,this.drawWaveform?.());return}!this.audio||!this.audio.duration||this.seekToPercent(s)}setLoading(e){this.isLoading=e,this.loadingEl&&(this.loadingEl.style.display=e?"block":"none")}onMetadataLoaded(){this.isDestroying||(this.totalTimeEl&&(this.totalTimeEl.textContent=S(this.audio.duration)),this.renderMarkers())}onPlay(){if(!this.isDestroying){if(this.isPlaying=!0,this.playBtn){this.playBtn.classList.add("playing");let e=this.playBtn.querySelector(".waveform-icon-play"),i=this.playBtn.querySelector(".waveform-icon-pause");e&&(e.style.display="none"),i&&(i.style.display="flex")}this.startSmoothUpdate(),this.container.dispatchEvent(new CustomEvent("waveformplayer:play",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPlay&&this.options.onPlay(this)}}onPause(){if(!this.isDestroying){if(this.isPlaying=!1,this.playBtn){this.playBtn.classList.remove("playing");let e=this.playBtn.querySelector(".waveform-icon-play"),i=this.playBtn.querySelector(".waveform-icon-pause");e&&(e.style.display="flex"),i&&(i.style.display="none")}this.stopSmoothUpdate(),this.container.dispatchEvent(new CustomEvent("waveformplayer:pause",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPause&&this.options.onPause(this)}}onEnded(){this.isDestroying||(this.progress=0,this.audio.currentTime=0,this.drawWaveform(),this.currentTimeEl&&(this.currentTimeEl.textContent="0:00"),this.container.dispatchEvent(new CustomEvent("waveformplayer:ended",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.onPause(),this.options.onEnd&&this.options.onEnd(this))}onError(e){this.isDestroying||(console.error("Audio error:",e),this.hasError=!0,this.setLoading(!1),this.errorEl&&(this.errorEl.style.display="flex"),this.canvas&&(this.canvas.style.opacity="0.2"),this.playBtn&&(this.playBtn.disabled=!0),this.options.onError&&this.options.onError(e,this))}startSmoothUpdate(){this.stopSmoothUpdate();let e=()=>{this.isPlaying&&this.audio&&this.audio.duration&&(this.updateProgress(),this.updateTimer=requestAnimationFrame(e))};this.updateTimer=requestAnimationFrame(e)}stopSmoothUpdate(){this.updateTimer&&(cancelAnimationFrame(this.updateTimer),this.updateTimer=null)}updateProgress(){if(!this.audio||!this.audio.duration)return;let e=this.audio.currentTime/this.audio.duration;Math.abs(e-this.progress)>.001&&(this.progress=e,this.drawWaveform()),this.currentTimeEl&&(this.currentTimeEl.textContent=S(this.audio.currentTime)),this.container.dispatchEvent(new CustomEvent("waveformplayer:timeupdate",{bubbles:!0,detail:{player:this,currentTime:this.audio.currentTime,duration:this.audio.duration,url:this.options.url}})),this.options.onTimeUpdate&&this.options.onTimeUpdate(this.audio.currentTime,this.audio.duration,this)}updateBPMDisplay(){this.bpmEl&&this.bpmValueEl&&this.detectedBPM&&(this.bpmValueEl.textContent=Math.round(this.detectedBPM),this.bpmEl.style.display="inline-flex")}updateSpeedUI(){let e=this.container.querySelector(".speed-value");if(e){let i=this.audio.playbackRate;e.textContent=i===1?"1x":`${i}x`}this.container.querySelectorAll(".speed-option").forEach(i=>{i.classList.toggle("active",parseFloat(i.dataset.rate)===this.audio.playbackRate)})}play(){if(this.options.singlePlay&&t.currentlyPlaying&&t.currentlyPlaying!==this&&t.currentlyPlaying.pause(),this.options.audioMode==="external"){let e=new CustomEvent("waveformplayer:request-play",{bubbles:!0,cancelable:!0,detail:this._buildTrackDetail()});this.container.dispatchEvent(e),e.defaultPrevented||(t.currentlyPlaying=this);return}return t.currentlyPlaying=this,this.audio.play()}pause(){if(t.currentlyPlaying===this&&(t.currentlyPlaying=null),this.options.audioMode==="external"){this.container.dispatchEvent(new CustomEvent("waveformplayer:request-pause",{bubbles:!0,cancelable:!0,detail:this._buildTrackDetail()}));return}this.audio.pause()}_buildTrackDetail(){return{url:this.options.url,title:this.options.title,subtitle:this.options.subtitle,artist:this.options.artist,artwork:this.options.artwork,id:this.id,player:this}}setPlayingState(e){let i=this.isPlaying;if(this.isPlaying=!!e,this.playBtn){this.playBtn.classList.toggle("playing",this.isPlaying);let a=this.playBtn.querySelector(".waveform-icon-play"),s=this.playBtn.querySelector(".waveform-icon-pause");a&&(a.style.display=this.isPlaying?"none":"flex"),s&&(s.style.display=this.isPlaying?"flex":"none")}this.isPlaying&&!i?(this.startSmoothUpdate?.(),this.container.dispatchEvent(new CustomEvent("waveformplayer:play",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPlay&&this.options.onPlay(this)):!this.isPlaying&&i&&(this.stopSmoothUpdate?.(),this.container.dispatchEvent(new CustomEvent("waveformplayer:pause",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPause&&this.options.onPause(this))}setProgress(e,i){!i||i<=0||(this.progress=Math.max(0,Math.min(1,e/i)),this.currentTimeEl&&(this.currentTimeEl.textContent=S(e)),this.totalTimeEl&&(!this.totalTimeEl.dataset._extSet||this._extDuration!==i)&&(this.totalTimeEl.textContent=S(i),this.totalTimeEl.dataset._extSet="1",this._extDuration=i),this.drawWaveform?.(),this.container.dispatchEvent(new CustomEvent("waveformplayer:timeupdate",{bubbles:!0,detail:{player:this,currentTime:e,duration:i,progress:this.progress}})),this.options.onTimeUpdate&&this.options.onTimeUpdate(this,e,i))}togglePlay(){this.isPlaying?this.pause():this.play()}seekTo(e){this.audio&&this.audio.duration&&(this.audio.currentTime=Math.max(0,Math.min(e,this.audio.duration)),this.updateProgress())}seekToPercent(e){this.audio&&this.audio.duration&&(this.audio.currentTime=this.audio.duration*Math.max(0,Math.min(1,e)),this.updateProgress())}setVolume(e){this.audio&&(this.audio.volume=Math.max(0,Math.min(1,e)))}setPlaybackRate(e){if(!this.audio)return;let i=Math.max(.5,Math.min(2,e));this.audio.playbackRate=i,this.options.playbackRate=i,this.updateSpeedUI()}destroy(){this.isDestroying=!0,this.pause(),this.stopSmoothUpdate(),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.resizeHandler&&(window.removeEventListener("resize",this.resizeHandler),this.resizeHandler=null),t.instances.delete(this.id),t.currentlyPlaying===this&&(t.currentlyPlaying=null),this.audio&&(this.audio.pause(),this.audio.src="",this.audio.load(),this.audio=null),this.container.innerHTML="",this.canvas=null,this.ctx=null,this.playBtn=null,this.waveformData=[]}static getInstance(e){if(typeof e=="string"){let i=this.instances.get(e);if(i)return i;let a=document.getElementById(e);if(a)return Array.from(this.instances.values()).find(s=>s.container===a)}if(e instanceof HTMLElement)return Array.from(this.instances.values()).find(i=>i.container===e)}static getAllInstances(){return Array.from(this.instances.values())}static destroyAll(){this.instances.forEach(e=>e.destroy()),this.instances.clear()}static async generateWaveformData(e,i=200){try{return(await M(e,i)).peaks}catch(a){throw console.error("Failed to generate waveform:",a),a}}static getPeaksUrl(e){if(!e)return;let i=e.replace(/\.(mp3|wav|ogg|flac|m4a|aac)(\?[^#]*)?(#.*)?$/i,".json$2$3");return i===e?void 0:i}};function L(){if(typeof document>"u")return;document.querySelectorAll("[data-waveform-player]").forEach(e=>{if(e.dataset.waveformInitialized!=="true")try{new g(e),e.dataset.waveformInitialized="true"}catch(i){console.error("Failed to initialize WaveformPlayer:",i,e)}})}typeof document<"u"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",L):L());g.init=L;typeof window<"u"&&(window.WaveformPlayer=g);var lt=g;export{g as WaveformPlayer,lt as default};
66
+ `,this.playBtn=this.container.querySelector(".waveform-btn"),this.canvas=this.container.querySelector("canvas"),this.ctx=this.canvas.getContext("2d"),this.titleEl=this.container.querySelector(".waveform-title"),this.subtitleEl=this.container.querySelector(".waveform-subtitle"),this.artworkEl=this.container.querySelector(".waveform-artwork"),this.currentTimeEl=this.container.querySelector(".time-current"),this.totalTimeEl=this.container.querySelector(".time-total"),this.bpmEl=this.container.querySelector(".waveform-bpm"),this.bpmValueEl=this.container.querySelector(".bpm-value"),this.loadingEl=this.container.querySelector(".waveform-loading"),this.errorEl=this.container.querySelector(".waveform-error"),this.markersContainer=this.container.querySelector(".waveform-markers"),this.speedBtn=this.container.querySelector(".speed-btn"),this.speedMenu=this.container.querySelector(".speed-menu"),this.resizeCanvas()}createAudio(){if(this.options.audioMode==="external"){this.audio=null;return}this.audio=new Audio,this.audio.preload=this.options.preload||"metadata",this.audio.crossOrigin="anonymous"}initPlaybackSpeed(){this.audio&&this.options.playbackRate&&this.options.playbackRate!==1&&(this.audio.playbackRate=this.options.playbackRate),this.options.showPlaybackSpeed&&this.initSpeedControls()}initSpeedControls(){let e=this.container.querySelector(".speed-btn"),i=this.container.querySelector(".speed-menu");!e||!i||(e.addEventListener("click",a=>{a.stopPropagation(),i.style.display=i.style.display==="none"?"block":"none"}),document.addEventListener("click",()=>{i.style.display="none"}),i.addEventListener("click",a=>{if(a.stopPropagation(),a.target.classList.contains("speed-option")){let s=parseFloat(a.target.dataset.rate);this.setPlaybackRate(s),i.style.display="none"}}),this.updateSpeedUI())}initKeyboardControls(){this.container.setAttribute("tabindex","-1"),this.container.addEventListener("click",()=>{t.getAllInstances().forEach(e=>{e!==this&&e.container.setAttribute("tabindex","-1")}),this.container.setAttribute("tabindex","0"),this.container.focus()}),this.container.addEventListener("keydown",e=>{if(document.activeElement!==this.container)return;let i=e.key,a=!!this.audio,s=a?this.audio.currentTime:0;if(a&&i>="0"&&i<="9"){e.preventDefault(),this.seekToPercent(parseInt(i)/10);return}let o={" ":()=>this.togglePlay()};a&&(o.ArrowLeft=()=>this.seekTo(Math.max(0,s-5)),o.ArrowRight=()=>this.seekTo(Math.min(this.audio.duration,s+5)),o.ArrowUp=()=>this.setVolume(Math.min(1,this.audio.volume+.1)),o.ArrowDown=()=>this.setVolume(Math.max(0,this.audio.volume-.1)),o.m=o.M=()=>this.audio.muted=!this.audio.muted),o[i]&&(e.preventDefault(),o[i]())})}initSeekControl(){this.options.accessibleSeek&&(this.seekEl=this.container.querySelector(".waveform-container"),this.seekEl&&(this.seekEl.setAttribute("role","slider"),this.seekEl.setAttribute("tabindex","0"),this.seekEl.setAttribute("aria-valuemin","0"),this.applySeekLabel(),this.updateSeekAccessibility(),this.seekEl.addEventListener("keydown",e=>{let i=this.getSeekDuration();if(!i)return;let a=this.getSeekCurrentTime(),s;switch(e.key){case"ArrowLeft":case"ArrowDown":s=a-U;break;case"ArrowRight":case"ArrowUp":s=a+U;break;case"PageDown":s=a-F;break;case"PageUp":s=a+F;break;case"Home":s=0;break;case"End":s=i;break;default:return}e.preventDefault(),e.stopPropagation(),this.seekToSeconds(s)})))}getSeekDuration(){return this.options.audioMode==="external"?this._extDuration||0:this.audio&&Number.isFinite(this.audio.duration)?this.audio.duration:0}getSeekCurrentTime(){return this.options.audioMode==="external"?this.progress*(this._extDuration||0):this.audio&&Number.isFinite(this.audio.currentTime)?this.audio.currentTime:0}seekToSeconds(e){let i=this.getSeekDuration();if(!i)return;let a=Math.max(0,Math.min(e,i));if(this.options.audioMode==="external"){let s=a/i,o=new CustomEvent("waveformplayer:request-seek",{bubbles:!0,cancelable:!0,detail:{...this._buildTrackDetail(),percent:s}});this.container.dispatchEvent(o),o.defaultPrevented||(this.progress=s,this.drawWaveform?.()),this.updateSeekAccessibility();return}this.seekTo(a)}applySeekLabel(e=this.options.title){if(!this.seekEl)return;let i=this.options.seekLabel||e||"Seek";this.seekEl.setAttribute("aria-label",i)}updateSeekAccessibility(){if(!this.seekEl)return;let e=this.getSeekDuration(),i=Math.min(this.getSeekCurrentTime(),e);this.seekEl.setAttribute("aria-valuemax",String(Math.round(e))),this.seekEl.setAttribute("aria-valuenow",String(Math.round(i))),this.seekEl.setAttribute("aria-valuetext",`${g(i)} of ${g(e)}`)}initMediaSession(){!("mediaSession"in navigator)||!this.options.enableMediaSession||this.audio&&(navigator.mediaSession.metadata=new MediaMetadata({title:this.options.title||"Unknown Track",artist:this.options.subtitle||"",album:this.options.album||"",artwork:this.options.artwork?[{src:this.options.artwork,sizes:"512x512",type:"image/jpeg"}]:[]}),navigator.mediaSession.setActionHandler("play",()=>this.play()),navigator.mediaSession.setActionHandler("pause",()=>this.pause()),navigator.mediaSession.setActionHandler("seekbackward",()=>{this.seekTo(Math.max(0,this.audio.currentTime-10))}),navigator.mediaSession.setActionHandler("seekforward",()=>{this.seekTo(Math.min(this.audio.duration,this.audio.currentTime+10))}),navigator.mediaSession.setActionHandler("seekto",e=>{e.seekTime!==null&&this.seekTo(e.seekTime)}))}bindEvents(){this.playBtn&&this.playBtn.addEventListener("click",()=>this.togglePlay()),this.audio&&(this.audio.addEventListener("loadstart",()=>this.setLoading(!0)),this.audio.addEventListener("loadedmetadata",()=>this.onMetadataLoaded()),this.audio.addEventListener("canplay",()=>this.setLoading(!1)),this.audio.addEventListener("play",()=>this.onPlay()),this.audio.addEventListener("pause",()=>this.onPause()),this.audio.addEventListener("ended",()=>this.onEnded()),this.audio.addEventListener("error",e=>this.onError(e))),this.canvas.addEventListener("click",e=>this.handleCanvasClick(e)),this.resizeHandler=B(()=>this.resizeCanvas(),100),window.addEventListener("resize",this.resizeHandler)}setupResizeObserver(){"ResizeObserver"in window&&(this.resizeObserver=new ResizeObserver(()=>{this.resizeCanvas()}),this.canvas?.parentElement&&this.resizeObserver.observe(this.canvas.parentElement))}async load(e){try{this.setLoading(!0),this.progress=0,this.hasError=!1,this.audio&&(this.audio.src=e,await new Promise((a,s)=>{let o=()=>{this.audio.removeEventListener("loadedmetadata",o),this.audio.removeEventListener("error",n),a()},n=r=>{this.audio.removeEventListener("loadedmetadata",o),this.audio.removeEventListener("error",n),s(r)};this.audio.addEventListener("loadedmetadata",o),this.audio.addEventListener("error",n)}));let i=this.options.title||D(e);if(this.titleEl&&(this.titleEl.textContent=i),this.applySeekLabel(i),this.options.waveform)this.setWaveformData(this.options.waveform);else try{let a=await M(e,this.options.samples,this.options.showBPM);this.waveformData=a.peaks,a.bpm&&(this.detectedBPM=a.bpm,this.updateBPMDisplay())}catch(a){console.warn("Using placeholder waveform:",a),this.waveformData=O(this.options.samples)}this.drawWaveform(),this.renderMarkers(),this.initMediaSession(),this.options.onLoad&&this.options.onLoad(this)}catch(i){console.error("Failed to load audio:",i),this.onError(i)}finally{this.setLoading(!1)}}async loadTrack(e,i=null,a=null,s={}){this.isPlaying&&this.pause(),this.audio&&(this.audio.src="",this.audio.load()),this.hasError=!1,this.errorEl&&(this.errorEl.style.display="none"),this.canvas&&(this.canvas.style.opacity="1"),this.playBtn&&(this.playBtn.disabled=!1),this.progress=0,this.waveformData=[],this.options=C(this.options,{url:e,title:i||this.options.title,subtitle:a||this.options.subtitle,...s}),s.preload&&this.audio&&(this.audio.preload=s.preload),this.subtitleEl&&(a?(this.subtitleEl.textContent=a,this.subtitleEl.style.display=""):a===""&&(this.subtitleEl.style.display="none")),s.artwork&&this.artworkEl&&(this.artworkEl.src=s.artwork),this.options.markers=s.markers||[],await this.load(e),this.play().catch(()=>{})}setWaveformData(e){if(typeof e=="string"&&e.trim().endsWith(".json")){fetch(e.trim()).then(i=>i.json()).then(i=>{this.waveformData=Array.isArray(i)?i:i.peaks||[],i.markers&&!this.options.markers?.length&&(this.options.markers=i.markers,this.renderMarkers()),this.drawWaveform()}).catch(()=>{});return}if(typeof e=="string")try{let i=JSON.parse(e);this.waveformData=Array.isArray(i)?i:[]}catch{this.waveformData=e.split(",").map(Number)}else this.waveformData=Array.isArray(e)?e:[];this.drawWaveform()}drawWaveform(){!this.ctx||this.waveformData.length===0||W(this.ctx,this.canvas,this.waveformData,this.progress,{...this.options,waveformStyle:this.options.waveformStyle||"bars",color:this.options.waveformColor,progressColor:this.options.progressColor})}resizeCanvas(){if(!this.canvas||this.isDestroying)return;let e=window.devicePixelRatio||1,i=this.canvas.parentElement.getBoundingClientRect();this.canvas.width=i.width*e,this.canvas.height=this.options.height*e,this.canvas.parentElement.style.height=this.options.height+"px",this.drawWaveform()}renderMarkers(){this.markersContainer&&(this.markersContainer.innerHTML="",!(!this.options.showMarkers||!this.options.markers?.length)&&(!this.audio||!this.audio.duration||this.audio.duration===0||this.options.markers.forEach((e,i)=>{if(e.time>this.audio.duration){console.warn(`Marker "${e.label}" at ${e.time}s exceeds audio duration of ${this.audio.duration}s`);return}let a=e.time/this.audio.duration*100,s=document.createElement("button");s.className="waveform-marker",s.style.left=`${a}%`,s.style.backgroundColor=e.color||"rgba(255, 255, 255, 0.5)",s.setAttribute("aria-label",e.label),s.setAttribute("data-time",e.time);let o=document.createElement("span");o.className="waveform-marker-tooltip",o.textContent=e.label,s.appendChild(o),s.addEventListener("click",n=>{n.stopPropagation(),this.seekTo(e.time),this.options.playOnSeek&&!this.isPlaying&&this.play()}),this.markersContainer.appendChild(s)})))}handleCanvasClick(e){let i=this.canvas.getBoundingClientRect(),a=e.clientX-i.left,s=Math.max(0,Math.min(1,a/i.width));if(this.options.audioMode==="external"){let o=new CustomEvent("waveformplayer:request-seek",{bubbles:!0,cancelable:!0,detail:{...this._buildTrackDetail(),percent:s}});this.container.dispatchEvent(o),o.defaultPrevented||(this.progress=s,this.drawWaveform?.());return}!this.audio||!this.audio.duration||this.seekToPercent(s)}setLoading(e){this.isLoading=e,this.loadingEl&&(this.loadingEl.style.display=e?"block":"none")}onMetadataLoaded(){this.isDestroying||(this.totalTimeEl&&(this.totalTimeEl.textContent=g(this.audio.duration)),this.renderMarkers(),this.updateSeekAccessibility())}onPlay(){if(!this.isDestroying){if(this.isPlaying=!0,this.playBtn){this.playBtn.classList.add("playing");let e=this.playBtn.querySelector(".waveform-icon-play"),i=this.playBtn.querySelector(".waveform-icon-pause");e&&(e.style.display="none"),i&&(i.style.display="flex")}this.startSmoothUpdate(),this.container.dispatchEvent(new CustomEvent("waveformplayer:play",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPlay&&this.options.onPlay(this)}}onPause(){if(!this.isDestroying){if(this.isPlaying=!1,this.playBtn){this.playBtn.classList.remove("playing");let e=this.playBtn.querySelector(".waveform-icon-play"),i=this.playBtn.querySelector(".waveform-icon-pause");e&&(e.style.display="flex"),i&&(i.style.display="none")}this.stopSmoothUpdate(),this.container.dispatchEvent(new CustomEvent("waveformplayer:pause",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPause&&this.options.onPause(this)}}onEnded(){this.isDestroying||(this.progress=0,this.audio.currentTime=0,this.drawWaveform(),this.currentTimeEl&&(this.currentTimeEl.textContent="0:00"),this.container.dispatchEvent(new CustomEvent("waveformplayer:ended",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.onPause(),this.options.onEnd&&this.options.onEnd(this))}onError(e){this.isDestroying||(console.error("Audio error:",e),this.hasError=!0,this.setLoading(!1),this.errorEl&&(this.errorEl.style.display="flex"),this.canvas&&(this.canvas.style.opacity="0.2"),this.playBtn&&(this.playBtn.disabled=!0),this.options.onError&&this.options.onError(e,this))}startSmoothUpdate(){this.stopSmoothUpdate();let e=()=>{this.isPlaying&&this.audio&&this.audio.duration&&(this.updateProgress(),this.updateTimer=requestAnimationFrame(e))};this.updateTimer=requestAnimationFrame(e)}stopSmoothUpdate(){this.updateTimer&&(cancelAnimationFrame(this.updateTimer),this.updateTimer=null)}updateProgress(){if(!this.audio||!this.audio.duration)return;let e=this.audio.currentTime/this.audio.duration;Math.abs(e-this.progress)>.001&&(this.progress=e,this.drawWaveform()),this.currentTimeEl&&(this.currentTimeEl.textContent=g(this.audio.currentTime)),this.container.dispatchEvent(new CustomEvent("waveformplayer:timeupdate",{bubbles:!0,detail:{player:this,currentTime:this.audio.currentTime,duration:this.audio.duration,url:this.options.url}})),this.options.onTimeUpdate&&this.options.onTimeUpdate(this.audio.currentTime,this.audio.duration,this),this.updateSeekAccessibility()}updateBPMDisplay(){this.bpmEl&&this.bpmValueEl&&this.detectedBPM&&(this.bpmValueEl.textContent=Math.round(this.detectedBPM),this.bpmEl.style.display="inline-flex")}updateSpeedUI(){let e=this.container.querySelector(".speed-value");if(e){let i=this.audio.playbackRate;e.textContent=i===1?"1x":`${i}x`}this.container.querySelectorAll(".speed-option").forEach(i=>{i.classList.toggle("active",parseFloat(i.dataset.rate)===this.audio.playbackRate)})}play(){if(this.options.singlePlay&&t.currentlyPlaying&&t.currentlyPlaying!==this&&t.currentlyPlaying.pause(),this.options.audioMode==="external"){let e=new CustomEvent("waveformplayer:request-play",{bubbles:!0,cancelable:!0,detail:this._buildTrackDetail()});this.container.dispatchEvent(e),e.defaultPrevented||(t.currentlyPlaying=this);return}return t.currentlyPlaying=this,this.audio.play()}pause(){if(t.currentlyPlaying===this&&(t.currentlyPlaying=null),this.options.audioMode==="external"){this.container.dispatchEvent(new CustomEvent("waveformplayer:request-pause",{bubbles:!0,cancelable:!0,detail:this._buildTrackDetail()}));return}this.audio.pause()}_buildTrackDetail(){return{url:this.options.url,title:this.options.title,subtitle:this.options.subtitle,artist:this.options.artist,artwork:this.options.artwork,id:this.id,player:this}}setPlayingState(e){let i=this.isPlaying;if(this.isPlaying=!!e,this.playBtn){this.playBtn.classList.toggle("playing",this.isPlaying);let a=this.playBtn.querySelector(".waveform-icon-play"),s=this.playBtn.querySelector(".waveform-icon-pause");a&&(a.style.display=this.isPlaying?"none":"flex"),s&&(s.style.display=this.isPlaying?"flex":"none")}this.isPlaying&&!i?(this.startSmoothUpdate?.(),this.container.dispatchEvent(new CustomEvent("waveformplayer:play",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPlay&&this.options.onPlay(this)):!this.isPlaying&&i&&(this.stopSmoothUpdate?.(),this.container.dispatchEvent(new CustomEvent("waveformplayer:pause",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPause&&this.options.onPause(this))}setProgress(e,i){!i||i<=0||(this.progress=Math.max(0,Math.min(1,e/i)),this.currentTimeEl&&(this.currentTimeEl.textContent=g(e)),this.totalTimeEl&&(!this.totalTimeEl.dataset._extSet||this._extDuration!==i)&&(this.totalTimeEl.textContent=g(i),this.totalTimeEl.dataset._extSet="1",this._extDuration=i),this.drawWaveform?.(),this.container.dispatchEvent(new CustomEvent("waveformplayer:timeupdate",{bubbles:!0,detail:{player:this,currentTime:e,duration:i,progress:this.progress}})),this.options.onTimeUpdate&&this.options.onTimeUpdate(this,e,i),this.updateSeekAccessibility())}togglePlay(){this.isPlaying?this.pause():this.play()}seekTo(e){this.audio&&this.audio.duration&&(this.audio.currentTime=Math.max(0,Math.min(e,this.audio.duration)),this.updateProgress())}seekToPercent(e){this.audio&&this.audio.duration&&(this.audio.currentTime=this.audio.duration*Math.max(0,Math.min(1,e)),this.updateProgress())}setVolume(e){this.audio&&(this.audio.volume=Math.max(0,Math.min(1,e)))}setPlaybackRate(e){if(!this.audio)return;let i=Math.max(.5,Math.min(2,e));this.audio.playbackRate=i,this.options.playbackRate=i,this.updateSpeedUI()}destroy(){this.isDestroying=!0,this.pause(),this.stopSmoothUpdate(),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.resizeHandler&&(window.removeEventListener("resize",this.resizeHandler),this.resizeHandler=null),t.instances.delete(this.id),t.currentlyPlaying===this&&(t.currentlyPlaying=null),this.audio&&(this.audio.pause(),this.audio.src="",this.audio.load(),this.audio=null),this.container.innerHTML="",this.canvas=null,this.ctx=null,this.playBtn=null,this.waveformData=[]}static getInstance(e){if(typeof e=="string"){let i=this.instances.get(e);if(i)return i;let a=document.getElementById(e);if(a)return Array.from(this.instances.values()).find(s=>s.container===a)}if(e instanceof HTMLElement)return Array.from(this.instances.values()).find(i=>i.container===e)}static getAllInstances(){return Array.from(this.instances.values())}static destroyAll(){this.instances.forEach(e=>e.destroy()),this.instances.clear()}static async generateWaveformData(e,i=200){try{return(await M(e,i)).peaks}catch(a){throw console.error("Failed to generate waveform:",a),a}}static getPeaksUrl(e){if(!e)return;let i=e.replace(/\.(mp3|wav|ogg|flac|m4a|aac)(\?[^#]*)?(#.*)?$/i,".json$2$3");return i===e?void 0:i}};function A(){if(typeof document>"u")return;document.querySelectorAll("[data-waveform-player]").forEach(e=>{if(e.dataset.waveformInitialized!=="true")try{new w(e),e.dataset.waveformInitialized="true"}catch(i){console.error("Failed to initialize WaveformPlayer:",i,e)}})}typeof document<"u"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",A):A());w.init=A;typeof window<"u"&&(window.WaveformPlayer=w);var dt=w;export{w as WaveformPlayer,dt as default};
@@ -617,6 +617,12 @@
617
617
  // Markers
618
618
  markers: [],
619
619
  showMarkers: true,
620
+ // Accessibility — expose the waveform as a keyboard-operable slider
621
+ // (role="slider" + ARIA value attributes + arrow/page/home/end seeking).
622
+ // seekLabel sets the slider's accessible name; when null it falls back
623
+ // to the track title, then 'Seek'.
624
+ accessibleSeek: true,
625
+ seekLabel: null,
620
626
  // Content
621
627
  title: null,
622
628
  subtitle: null,
@@ -643,6 +649,8 @@
643
649
  };
644
650
 
645
651
  // src/js/core.js
652
+ var SEEK_STEP_SECONDS = 5;
653
+ var SEEK_PAGE_SECONDS = 10;
646
654
  var WaveformPlayer = class _WaveformPlayer {
647
655
  /** @type {Map<string, WaveformPlayer>} */
648
656
  static instances = /* @__PURE__ */ new Map();
@@ -707,6 +715,7 @@
707
715
  this.createAudio();
708
716
  this.initPlaybackSpeed();
709
717
  this.initKeyboardControls();
718
+ this.initSeekControl();
710
719
  this.bindEvents();
711
720
  this.setupResizeObserver();
712
721
  requestAnimationFrame(() => {
@@ -925,6 +934,132 @@
925
934
  }
926
935
  });
927
936
  }
937
+ /**
938
+ * Expose the waveform as an accessible, keyboard-operable slider.
939
+ *
940
+ * Adds role="slider" + ARIA value attributes to the waveform surface,
941
+ * makes it focusable in the tab order, and handles the standard slider
942
+ * keys (arrows, Page Up/Down, Home/End) to seek. Works in both self and
943
+ * external audio modes. Opt out with `accessibleSeek: false`.
944
+ * @private
945
+ */
946
+ initSeekControl() {
947
+ if (!this.options.accessibleSeek) return;
948
+ this.seekEl = this.container.querySelector(".waveform-container");
949
+ if (!this.seekEl) return;
950
+ this.seekEl.setAttribute("role", "slider");
951
+ this.seekEl.setAttribute("tabindex", "0");
952
+ this.seekEl.setAttribute("aria-valuemin", "0");
953
+ this.applySeekLabel();
954
+ this.updateSeekAccessibility();
955
+ this.seekEl.addEventListener("keydown", (e) => {
956
+ const duration = this.getSeekDuration();
957
+ if (!duration) return;
958
+ const current = this.getSeekCurrentTime();
959
+ let target;
960
+ switch (e.key) {
961
+ case "ArrowLeft":
962
+ case "ArrowDown":
963
+ target = current - SEEK_STEP_SECONDS;
964
+ break;
965
+ case "ArrowRight":
966
+ case "ArrowUp":
967
+ target = current + SEEK_STEP_SECONDS;
968
+ break;
969
+ case "PageDown":
970
+ target = current - SEEK_PAGE_SECONDS;
971
+ break;
972
+ case "PageUp":
973
+ target = current + SEEK_PAGE_SECONDS;
974
+ break;
975
+ case "Home":
976
+ target = 0;
977
+ break;
978
+ case "End":
979
+ target = duration;
980
+ break;
981
+ default:
982
+ return;
983
+ }
984
+ e.preventDefault();
985
+ e.stopPropagation();
986
+ this.seekToSeconds(target);
987
+ });
988
+ }
989
+ /**
990
+ * Total seekable duration in seconds, regardless of audio mode.
991
+ * @returns {number}
992
+ * @private
993
+ */
994
+ getSeekDuration() {
995
+ if (this.options.audioMode === "external") {
996
+ return this._extDuration || 0;
997
+ }
998
+ return this.audio && Number.isFinite(this.audio.duration) ? this.audio.duration : 0;
999
+ }
1000
+ /**
1001
+ * Current playback position in seconds, regardless of audio mode.
1002
+ * @returns {number}
1003
+ * @private
1004
+ */
1005
+ getSeekCurrentTime() {
1006
+ if (this.options.audioMode === "external") {
1007
+ return this.progress * (this._extDuration || 0);
1008
+ }
1009
+ return this.audio && Number.isFinite(this.audio.currentTime) ? this.audio.currentTime : 0;
1010
+ }
1011
+ /**
1012
+ * Seek the slider to an absolute time, clamped to the track length.
1013
+ * Routes through the external controller in external mode.
1014
+ * @param {number} seconds - Target time in seconds.
1015
+ * @private
1016
+ */
1017
+ seekToSeconds(seconds) {
1018
+ const duration = this.getSeekDuration();
1019
+ if (!duration) return;
1020
+ const clamped = Math.max(0, Math.min(seconds, duration));
1021
+ if (this.options.audioMode === "external") {
1022
+ const percent = clamped / duration;
1023
+ const evt = new CustomEvent("waveformplayer:request-seek", {
1024
+ bubbles: true,
1025
+ cancelable: true,
1026
+ detail: { ...this._buildTrackDetail(), percent }
1027
+ });
1028
+ this.container.dispatchEvent(evt);
1029
+ if (!evt.defaultPrevented) {
1030
+ this.progress = percent;
1031
+ this.drawWaveform?.();
1032
+ }
1033
+ this.updateSeekAccessibility();
1034
+ return;
1035
+ }
1036
+ this.seekTo(clamped);
1037
+ }
1038
+ /**
1039
+ * Set the slider's accessible name from `seekLabel`, falling back to the
1040
+ * track title, then a generic 'Seek'.
1041
+ * @private
1042
+ */
1043
+ applySeekLabel(title = this.options.title) {
1044
+ if (!this.seekEl) return;
1045
+ const label = this.options.seekLabel || title || "Seek";
1046
+ this.seekEl.setAttribute("aria-label", label);
1047
+ }
1048
+ /**
1049
+ * Keep the slider's ARIA value attributes in sync with playback.
1050
+ * @private
1051
+ */
1052
+ updateSeekAccessibility() {
1053
+ if (!this.seekEl) return;
1054
+ const duration = this.getSeekDuration();
1055
+ const current = Math.min(this.getSeekCurrentTime(), duration);
1056
+ this.seekEl.setAttribute("aria-valuemax", String(Math.round(duration)));
1057
+ this.seekEl.setAttribute("aria-valuenow", String(Math.round(current)));
1058
+ this.seekEl.setAttribute(
1059
+ "aria-valuetext",
1060
+ `${formatTime(current)} of ${formatTime(duration)}`
1061
+ );
1062
+ }
928
1063
  /**
929
1064
  * Initialize Media Session API for system media controls
930
1065
  * @private
@@ -1026,6 +1161,7 @@
1026
1161
  if (this.titleEl) {
1027
1162
  this.titleEl.textContent = title;
1028
1163
  }
1164
+ this.applySeekLabel(title);
1029
1165
  if (this.options.waveform) {
1030
1166
  this.setWaveformData(this.options.waveform);
1031
1167
  } else {
@@ -1251,6 +1387,7 @@
1251
1387
  this.totalTimeEl.textContent = formatTime(this.audio.duration);
1252
1388
  }
1253
1389
  this.renderMarkers();
1390
+ this.updateSeekAccessibility();
1254
1391
  }
1255
1392
  /**
1256
1393
  * Handle play event
@@ -1394,6 +1531,7 @@
1394
1531
  if (this.options.onTimeUpdate) {
1395
1532
  this.options.onTimeUpdate(this.audio.currentTime, this.audio.duration, this);
1396
1533
  }
1534
+ this.updateSeekAccessibility();
1397
1535
  }
1398
1536
  // ============================================
1399
1537
  // UI Updates
@@ -1556,6 +1694,7 @@
1556
1694
  detail: { player: this, currentTime, duration, progress: this.progress }
1557
1695
  }));
1558
1696
  if (this.options.onTimeUpdate) this.options.onTimeUpdate(this, currentTime, duration);
1697
+ this.updateSeekAccessibility();
1559
1698
  }
1560
1699
  /**
1561
1700
  * Toggle play/pause
@@ -1,4 +1,4 @@
1
- (()=>{function A(t){let e={};if(t.dataset.url&&(e.url=t.dataset.url),t.dataset.height&&(e.height=parseInt(t.dataset.height)),t.dataset.samples&&(e.samples=parseInt(t.dataset.samples)),t.dataset.preload&&(e.preload=t.dataset.preload),t.dataset.waveformStyle&&(e.waveformStyle=t.dataset.waveformStyle),t.dataset.barWidth&&(e.barWidth=parseInt(t.dataset.barWidth)),t.dataset.barSpacing&&(e.barSpacing=parseInt(t.dataset.barSpacing)),t.dataset.buttonAlign&&(e.buttonAlign=t.dataset.buttonAlign),t.dataset.colorPreset&&(e.colorPreset=t.dataset.colorPreset),t.dataset.waveformColor&&(e.waveformColor=t.dataset.waveformColor),t.dataset.progressColor&&(e.progressColor=t.dataset.progressColor),t.dataset.buttonColor&&(e.buttonColor=t.dataset.buttonColor),t.dataset.buttonHoverColor&&(e.buttonHoverColor=t.dataset.buttonHoverColor),t.dataset.textColor&&(e.textColor=t.dataset.textColor),t.dataset.textSecondaryColor&&(e.textSecondaryColor=t.dataset.textSecondaryColor),t.dataset.backgroundColor&&(e.backgroundColor=t.dataset.backgroundColor),t.dataset.borderColor&&(e.borderColor=t.dataset.borderColor),t.dataset.color&&(e.waveformColor=t.dataset.color),t.dataset.theme&&(e.colorPreset=t.dataset.theme),t.dataset.autoplay&&(e.autoplay=t.dataset.autoplay==="true"),t.dataset.showControls!==void 0&&(e.showControls=t.dataset.showControls==="true"),t.dataset.showInfo!==void 0&&(e.showInfo=t.dataset.showInfo==="true"),t.dataset.showTime&&(e.showTime=t.dataset.showTime==="true"),t.dataset.showHoverTime&&(e.showHoverTime=t.dataset.showHoverTime==="true"),t.dataset.showBpm&&(e.showBPM=t.dataset.showBpm==="true"),t.dataset.singlePlay&&(e.singlePlay=t.dataset.singlePlay==="true"),t.dataset.playOnSeek&&(e.playOnSeek=t.dataset.playOnSeek==="true"),t.dataset.title&&(e.title=t.dataset.title),t.dataset.subtitle&&(e.subtitle=t.dataset.subtitle),t.dataset.album&&(e.album=t.dataset.album),t.dataset.artwork&&(e.artwork=t.dataset.artwork),t.dataset.waveform&&(e.waveform=t.dataset.waveform),t.dataset.markers)try{e.markers=JSON.parse(t.dataset.markers)}catch(i){console.warn("Invalid markers JSON:",i)}if(t.dataset.playbackRate&&(e.playbackRate=parseFloat(t.dataset.playbackRate)),t.dataset.showPlaybackSpeed!==void 0&&(e.showPlaybackSpeed=t.dataset.showPlaybackSpeed==="true"),t.dataset.playbackRates)try{e.playbackRates=JSON.parse(t.dataset.playbackRates)}catch(i){console.warn("Invalid playbackRates JSON:",i)}return t.dataset.enableMediaSession!==void 0&&(e.enableMediaSession=t.dataset.enableMediaSession==="true"),e}function S(t){if(!t||isNaN(t))return"0:00";let e=Math.floor(t/60),i=Math.floor(t%60);return`${e}:${i.toString().padStart(2,"0")}`}function x(t){let e=t||Math.random().toString();return btoa(e.substring(0,10)).replace(/[^a-zA-Z0-9]/g,"")}function B(t){if(!t)return"Audio";let e=t.split("/");return e[e.length-1].split(".")[0].replace(/[-_]/g," ").replace(/\b\w/g,s=>s.toUpperCase())}function C(...t){let e={};for(let i of t)for(let a in i)i[a]!==null&&i[a]!==void 0&&(e[a]=i[a]);return e}function R(t,e){let i;return function(...s){let o=()=>{clearTimeout(i),t(...s)};clearTimeout(i),i=setTimeout(o,e)}}function P(t,e){if(t.length===e)return t;if(t.length===0||e===0)return[];let i=[];if(e>t.length){let a=(t.length-1)/(e-1);for(let s=0;s<e;s++){let o=s*a,n=Math.floor(o),r=Math.ceil(o),h=o-n;if(r>=t.length)i.push(t[t.length-1]);else if(n===r)i.push(t[n]);else{let l=t[n]*(1-h)+t[r]*h;i.push(l)}}}else{let a=t.length/e;for(let s=0;s<e;s++){let o=Math.floor(s*a),n=Math.floor((s+1)*a),r=0,h=0;for(let l=o;l<=n&&l<t.length;l++)t[l]>r&&(r=t[l]),h++;if(h===0){let l=Math.min(Math.round(s*a),t.length-1);r=t[l]}i.push(r)}}return i}function E(t,e,i,a,s){let o=window.devicePixelRatio||1,n=s.barWidth*o,r=s.barSpacing*o,h=Math.floor(e.width/(n+r)),l=P(i,h),d=e.height,p=a*e.width;t.clearRect(0,0,e.width,e.height);for(let y=0;y<l.length;y++){let f=y*(n+r);if(f+n>e.width)break;let c=l[y]*d*.9,m=d-c;t.fillStyle=s.color,t.fillRect(f,m,n,c)}t.save(),t.beginPath(),t.rect(0,0,p,d),t.clip();for(let y=0;y<l.length;y++){let f=y*(n+r);if(f>p)break;let c=l[y]*d*.9,m=d-c;t.fillStyle=s.progressColor,t.fillRect(f,m,n,c)}t.restore()}function U(t,e,i,a,s){let o=window.devicePixelRatio||1,n=s.barWidth*o,r=s.barSpacing*o,h=Math.floor(e.width/(n+r)),l=P(i,h),d=e.height,p=d/2,y=a*e.width;t.clearRect(0,0,e.width,e.height);for(let f=0;f<l.length;f++){let c=f*(n+r);if(c+n>e.width)break;let m=l[f]*d*.45;t.fillStyle=s.color,t.fillRect(c,p-m,n,m),t.fillRect(c,p,n,m)}t.save(),t.beginPath(),t.rect(0,0,y,d),t.clip();for(let f=0;f<l.length;f++){let c=f*(n+r);if(c>y)break;let m=l[f]*d*.45;t.fillStyle=s.progressColor,t.fillRect(c,p-m,n,m),t.fillRect(c,p,n,m)}t.restore()}function F(t,e,i,a,s){let o=e.width,n=e.height,r=n/2,h=n*.35;t.clearRect(0,0,o,n);let l=(d,p,y=1,f=!1)=>{f&&(t.shadowBlur=12,t.shadowColor=d),t.strokeStyle=d,t.lineWidth=p,t.lineCap="round",t.lineJoin="round",t.beginPath(),t.moveTo(0,r);let c=[],m=Math.floor(i.length*y);for(let u=0;u<m;u++){let v=u/(i.length-1)*o,k=i[u],b=Math.sin(u*.1)*k,w=r+b*h;c.push({x:v,y:w})}for(let u=0;u<c.length-1;u++){let v=c[u].x+(c[u+1].x-c[u].x)*.5,k=c[u].y,b=c[u+1].x-(c[u+1].x-c[u].x)*.5,w=c[u+1].y;t.bezierCurveTo(v,k,b,w,c[u+1].x,c[u+1].y)}t.stroke(),f&&(t.shadowBlur=0)};t.strokeStyle="rgba(255, 255, 255, 0.03)",t.lineWidth=.5,t.beginPath(),t.moveTo(0,r),t.lineTo(o,r),t.stroke();for(let d=0;d<=10;d++){let p=o/10*d;t.beginPath(),t.moveTo(p,0),t.lineTo(p,n),t.stroke()}l(s.color,2,1,!1),a>0&&l(s.progressColor,3,a,!0)}function I(t,e,i,a,s){let o=window.devicePixelRatio||1,n=(s.barWidth||3)*o,r=(s.barSpacing||1)*o,h=Math.floor(e.width/(n+r)),l=P(i,h),d=e.height,p=4*o,y=2*o,f=a*e.width,c=d/2;t.clearRect(0,0,e.width,e.height);for(let m=0;m<l.length;m++){let u=m*(n+r);if(u+n>e.width)break;let v=l[m]*d*.9,k=Math.floor(v/(p+y));t.fillStyle=u<f?s.progressColor:s.color;for(let b=0;b<k;b++){let w=b*(p+y);t.fillRect(u,c-w-p,n,p),b>0&&t.fillRect(u,c+w,n,p)}}}function D(t,e,i,a,s){let o=window.devicePixelRatio||1,n=(s.barWidth||2)*o,r=(s.barSpacing||3)*o,h=Math.floor(e.width/(n+r)),l=P(i,h),d=e.height,p=Math.max(1.5*o,n/2),y=a*e.width,f=d/2;t.clearRect(0,0,e.width,e.height);for(let c=0;c<l.length;c++){let m=c*(n+r)+n/2;if(m>e.width)break;let u=l[c]*d*.9;t.fillStyle=m<y?s.progressColor:s.color,t.beginPath(),t.arc(m,f-u/2,p,0,Math.PI*2),t.fill(),t.beginPath(),t.arc(m,f+u/2,p,0,Math.PI*2),t.fill()}}function N(t,e,i,a,s){let o=e.width,n=e.height,r=n/2,h=4,l=h/2;if(t.clearRect(0,0,o,n),t.fillStyle=s.color||"rgba(255, 255, 255, 0.2)",t.beginPath(),t.moveTo(l,r-h/2),t.lineTo(o-l,r-h/2),t.arc(o-l,r,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,r+h/2),t.arc(l,r,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),a>0){let d=Math.max(l*2,a*o);t.shadowBlur=8,t.shadowColor=s.progressColor,t.fillStyle=s.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.moveTo(l,r-h/2),t.lineTo(d-l,r-h/2),t.arc(d-l,r,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,r+h/2),t.arc(l,r,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),t.shadowBlur=0;let p=8,y=d;t.shadowBlur=4,t.shadowColor="rgba(0, 0, 0, 0.3)",t.shadowOffsetY=2,t.fillStyle="#ffffff",t.beginPath(),t.arc(y,r,p,0,Math.PI*2),t.fill(),t.shadowBlur=0,t.shadowOffsetY=0,t.fillStyle=s.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.arc(y,r,p*.4,0,Math.PI*2),t.fill()}}var Y={bars:E,bar:E,mirror:U,line:F,blocks:I,block:I,dots:D,dot:D,seekbar:N};function W(t,e,i,a,s){(Y[s.waveformStyle]||E)(t,e,i,a,s)}function z(t){try{let e=t.getChannelData(0),i=t.sampleRate,a=j(e,i);if(a.length<2)return 120;let s=[];for(let h=1;h<a.length;h++)s.push((a[h]-a[h-1])/i);let o={};s.forEach(h=>{let l=60/h,d=Math.round(l/3)*3;d>60&&d<200&&(o[d]=(o[d]||0)+1)});let n=0,r=120;for(let[h,l]of Object.entries(o))l>n&&(n=l,r=parseInt(h));return r<70&&o[r*2]?r*=2:r>160&&o[Math.round(r/2)]&&(r=Math.round(r/2)),r-1}catch(e){return console.warn("BPM detection failed:",e),null}}function j(t,e){let s=[],o=0;for(let n=0;n<t.length-2048;n+=1024){let r=0;for(let d=n;d<n+2048;d++)r+=t[d]*t[d];r=r/2048;let h=r-o,l=o*1.8+.01;if(h>l&&r>.01){let d=s[s.length-1]||0,p=e*.15;n-d>p&&s.push(n)}o=r*.8+o*.2}return s}function V(t,e=200){let i=t.length/e,a=~~(i/10)||1,s=t.numberOfChannels,o=[];for(let r=0;r<s;r++){let h=t.getChannelData(r);for(let l=0;l<e;l++){let d=~~(l*i),p=~~(d+i),y=0,f=0;for(let m=d;m<p;m+=a){let u=h[m];u>f&&(f=u),u<y&&(y=u)}let c=Math.max(Math.abs(f),Math.abs(y));(r===0||c>o[l])&&(o[l]=c)}}let n=Math.max(...o);return n>0?o.map(r=>r/n):o}async function M(t,e=200,i=!1){try{let a=new(window.AudioContext||window.webkitAudioContext),o=await(await fetch(t)).arrayBuffer(),n=await a.decodeAudioData(o),r=V(n,e);r=J(r);let h=null;return i&&(h=await z(n)),a.close(),{peaks:r,bpm:h}}catch(a){throw console.error("Failed to generate waveform:",a),a}}function O(t=200){let e=[];for(let i=0;i<t;i++){let a=Math.random()*.5+.3,s=Math.sin(i/t*Math.PI*4)*.2;e.push(Math.max(.1,Math.min(1,a+s)))}return e}function J(t,e=.95){let i=Math.max(...t);if(i===0||i>e)return t;let a=e/i;return t.map(s=>s*a)}function _(){let t=document.documentElement,e=document.body;if(t.classList.contains("dark")||t.classList.contains("dark-mode")||t.classList.contains("theme-dark")||t.getAttribute("data-theme")==="dark"||t.getAttribute("data-color-scheme")==="dark"||e.classList.contains("dark")||e.classList.contains("dark-mode")||e.getAttribute("data-theme")==="dark")return"dark";if(t.classList.contains("light")||t.classList.contains("light-mode")||t.classList.contains("theme-light")||t.getAttribute("data-theme")==="light"||t.getAttribute("data-color-scheme")==="light"||e.classList.contains("light")||e.classList.contains("light-mode")||e.getAttribute("data-theme")==="light")return"light";try{let a=getComputedStyle(document.body).backgroundColor.match(/\d+/g);if(a&&a.length>=3){let[s,o,n]=a.map(Number),r=(s*299+o*587+n*114)/1e3;if(r>128)return"light";if(r<128)return"dark"}}catch{}if(window.matchMedia){if(window.matchMedia("(prefers-color-scheme: dark)").matches)return"dark";if(window.matchMedia("(prefers-color-scheme: light)").matches)return"light"}return"dark"}var T={dark:{waveformColor:"rgba(255, 255, 255, 0.3)",progressColor:"rgba(255, 255, 255, 0.9)",buttonColor:"rgba(255, 255, 255, 0.9)",buttonHoverColor:"rgba(255, 255, 255, 1)",textColor:"#ffffff",textSecondaryColor:"rgba(255, 255, 255, 0.6)",backgroundColor:"rgba(255, 255, 255, 0.03)",borderColor:"rgba(255, 255, 255, 0.1)"},light:{waveformColor:"rgba(0, 0, 0, 0.2)",progressColor:"rgba(0, 0, 0, 0.8)",buttonColor:"rgba(0, 0, 0, 0.8)",buttonHoverColor:"rgba(0, 0, 0, 0.9)",textColor:"#333333",textSecondaryColor:"rgba(0, 0, 0, 0.6)",backgroundColor:"rgba(0, 0, 0, 0.02)",borderColor:"rgba(0, 0, 0, 0.1)"}};function H(t){if(t&&T[t])return T[t];let e=_();return T[e]}var q={url:"",height:60,samples:200,preload:"metadata",audioMode:"self",playbackRate:1,showPlaybackSpeed:!1,playbackRates:[.5,.75,1,1.25,1.5,1.75,2],buttonAlign:"auto",waveformStyle:"mirror",barWidth:2,barSpacing:0,colorPreset:null,waveformColor:null,progressColor:null,buttonColor:null,buttonHoverColor:null,textColor:null,textSecondaryColor:null,backgroundColor:null,borderColor:null,autoplay:!1,showControls:!0,showInfo:!0,showTime:!0,showHoverTime:!1,showBPM:!1,singlePlay:!0,playOnSeek:!0,enableMediaSession:!0,markers:[],showMarkers:!0,title:null,subtitle:null,artwork:null,album:"",playIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M8 5v14l11-7z"/></svg>',pauseIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M6 4h4v16H6zM14 4h4v16h-4z"/></svg>',onLoad:null,onPlay:null,onPause:null,onEnd:null,onError:null,onTimeUpdate:null},$={bars:{barWidth:3,barSpacing:1},mirror:{barWidth:2,barSpacing:0},line:{barWidth:2,barSpacing:0},blocks:{barWidth:4,barSpacing:2},dots:{barWidth:3,barSpacing:3},seekbar:{barWidth:1,barSpacing:0}};var g=class t{static instances=new Map;static currentlyPlaying=null;constructor(e,i={}){if(this.container=typeof e=="string"?document.querySelector(e):e,!this.container)throw new Error("WaveformPlayer: Container element not found");let a=A(this.container);this.options=C(q,a,i);let s=H(this.options.colorPreset);for(let[n,r]of Object.entries(s))(this.options[n]===null||this.options[n]===void 0)&&(this.options[n]=r);let o=$[this.options.waveformStyle];o&&(a.barWidth===void 0&&i.barWidth===void 0&&(this.options.barWidth=o.barWidth),a.barSpacing===void 0&&i.barSpacing===void 0&&(this.options.barSpacing=o.barSpacing)),this.audio=null,this.canvas=null,this.ctx=null,this.waveformData=[],this.progress=0,this.isPlaying=!1,this.isLoading=!1,this.hasError=!1,this.updateTimer=null,this.resizeObserver=null,this.id=this.container.id||x(this.options.url),t.instances.set(this.id,this),this.init(),setTimeout(()=>{this.container.dispatchEvent(new CustomEvent("waveformplayer:ready",{bubbles:!0,detail:{player:this,url:this.options.url}}))},100)}init(){this.createDOM(),this.createAudio(),this.initPlaybackSpeed(),this.initKeyboardControls(),this.bindEvents(),this.setupResizeObserver(),requestAnimationFrame(()=>{this.resizeCanvas(),this.options.url&&this.load(this.options.url).then(()=>{this.options.autoplay&&this.play()}).catch(e=>{console.error("Failed to load audio:",e)})})}createDOM(){this.container.innerHTML="",this.container.className="waveform-player";let e=this.options.buttonAlign;e==="auto"&&(this.options.waveformStyle==="bars"?e="bottom":e="center");let i=this.options.showControls?`
1
+ (()=>{function L(t){let e={};if(t.dataset.url&&(e.url=t.dataset.url),t.dataset.height&&(e.height=parseInt(t.dataset.height)),t.dataset.samples&&(e.samples=parseInt(t.dataset.samples)),t.dataset.preload&&(e.preload=t.dataset.preload),t.dataset.waveformStyle&&(e.waveformStyle=t.dataset.waveformStyle),t.dataset.barWidth&&(e.barWidth=parseInt(t.dataset.barWidth)),t.dataset.barSpacing&&(e.barSpacing=parseInt(t.dataset.barSpacing)),t.dataset.buttonAlign&&(e.buttonAlign=t.dataset.buttonAlign),t.dataset.colorPreset&&(e.colorPreset=t.dataset.colorPreset),t.dataset.waveformColor&&(e.waveformColor=t.dataset.waveformColor),t.dataset.progressColor&&(e.progressColor=t.dataset.progressColor),t.dataset.buttonColor&&(e.buttonColor=t.dataset.buttonColor),t.dataset.buttonHoverColor&&(e.buttonHoverColor=t.dataset.buttonHoverColor),t.dataset.textColor&&(e.textColor=t.dataset.textColor),t.dataset.textSecondaryColor&&(e.textSecondaryColor=t.dataset.textSecondaryColor),t.dataset.backgroundColor&&(e.backgroundColor=t.dataset.backgroundColor),t.dataset.borderColor&&(e.borderColor=t.dataset.borderColor),t.dataset.color&&(e.waveformColor=t.dataset.color),t.dataset.theme&&(e.colorPreset=t.dataset.theme),t.dataset.autoplay&&(e.autoplay=t.dataset.autoplay==="true"),t.dataset.showControls!==void 0&&(e.showControls=t.dataset.showControls==="true"),t.dataset.showInfo!==void 0&&(e.showInfo=t.dataset.showInfo==="true"),t.dataset.showTime&&(e.showTime=t.dataset.showTime==="true"),t.dataset.showHoverTime&&(e.showHoverTime=t.dataset.showHoverTime==="true"),t.dataset.showBpm&&(e.showBPM=t.dataset.showBpm==="true"),t.dataset.singlePlay&&(e.singlePlay=t.dataset.singlePlay==="true"),t.dataset.playOnSeek&&(e.playOnSeek=t.dataset.playOnSeek==="true"),t.dataset.title&&(e.title=t.dataset.title),t.dataset.subtitle&&(e.subtitle=t.dataset.subtitle),t.dataset.album&&(e.album=t.dataset.album),t.dataset.artwork&&(e.artwork=t.dataset.artwork),t.dataset.waveform&&(e.waveform=t.dataset.waveform),t.dataset.markers)try{e.markers=JSON.parse(t.dataset.markers)}catch(i){console.warn("Invalid markers JSON:",i)}if(t.dataset.playbackRate&&(e.playbackRate=parseFloat(t.dataset.playbackRate)),t.dataset.showPlaybackSpeed!==void 0&&(e.showPlaybackSpeed=t.dataset.showPlaybackSpeed==="true"),t.dataset.playbackRates)try{e.playbackRates=JSON.parse(t.dataset.playbackRates)}catch(i){console.warn("Invalid playbackRates JSON:",i)}return t.dataset.enableMediaSession!==void 0&&(e.enableMediaSession=t.dataset.enableMediaSession==="true"),e}function g(t){if(!t||isNaN(t))return"0:00";let e=Math.floor(t/60),i=Math.floor(t%60);return`${e}:${i.toString().padStart(2,"0")}`}function x(t){let e=t||Math.random().toString();return btoa(e.substring(0,10)).replace(/[^a-zA-Z0-9]/g,"")}function D(t){if(!t)return"Audio";let e=t.split("/");return e[e.length-1].split(".")[0].replace(/[-_]/g," ").replace(/\b\w/g,s=>s.toUpperCase())}function C(...t){let e={};for(let i of t)for(let a in i)i[a]!==null&&i[a]!==void 0&&(e[a]=i[a]);return e}function B(t,e){let i;return function(...s){let o=()=>{clearTimeout(i),t(...s)};clearTimeout(i),i=setTimeout(o,e)}}function P(t,e){if(t.length===e)return t;if(t.length===0||e===0)return[];let i=[];if(e>t.length){let a=(t.length-1)/(e-1);for(let s=0;s<e;s++){let o=s*a,n=Math.floor(o),r=Math.ceil(o),h=o-n;if(r>=t.length)i.push(t[t.length-1]);else if(n===r)i.push(t[n]);else{let l=t[n]*(1-h)+t[r]*h;i.push(l)}}}else{let a=t.length/e;for(let s=0;s<e;s++){let o=Math.floor(s*a),n=Math.floor((s+1)*a),r=0,h=0;for(let l=o;l<=n&&l<t.length;l++)t[l]>r&&(r=t[l]),h++;if(h===0){let l=Math.min(Math.round(s*a),t.length-1);r=t[l]}i.push(r)}}return i}function E(t,e,i,a,s){let o=window.devicePixelRatio||1,n=s.barWidth*o,r=s.barSpacing*o,h=Math.floor(e.width/(n+r)),l=P(i,h),d=e.height,p=a*e.width;t.clearRect(0,0,e.width,e.height);for(let b=0;b<l.length;b++){let f=b*(n+r);if(f+n>e.width)break;let c=l[b]*d*.9,m=d-c;t.fillStyle=s.color,t.fillRect(f,m,n,c)}t.save(),t.beginPath(),t.rect(0,0,p,d),t.clip();for(let b=0;b<l.length;b++){let f=b*(n+r);if(f>p)break;let c=l[b]*d*.9,m=d-c;t.fillStyle=s.progressColor,t.fillRect(f,m,n,c)}t.restore()}function N(t,e,i,a,s){let o=window.devicePixelRatio||1,n=s.barWidth*o,r=s.barSpacing*o,h=Math.floor(e.width/(n+r)),l=P(i,h),d=e.height,p=d/2,b=a*e.width;t.clearRect(0,0,e.width,e.height);for(let f=0;f<l.length;f++){let c=f*(n+r);if(c+n>e.width)break;let m=l[f]*d*.45;t.fillStyle=s.color,t.fillRect(c,p-m,n,m),t.fillRect(c,p,n,m)}t.save(),t.beginPath(),t.rect(0,0,b,d),t.clip();for(let f=0;f<l.length;f++){let c=f*(n+r);if(c>b)break;let m=l[f]*d*.45;t.fillStyle=s.progressColor,t.fillRect(c,p-m,n,m),t.fillRect(c,p,n,m)}t.restore()}function Y(t,e,i,a,s){let o=e.width,n=e.height,r=n/2,h=n*.35;t.clearRect(0,0,o,n);let l=(d,p,b=1,f=!1)=>{f&&(t.shadowBlur=12,t.shadowColor=d),t.strokeStyle=d,t.lineWidth=p,t.lineCap="round",t.lineJoin="round",t.beginPath(),t.moveTo(0,r);let c=[],m=Math.floor(i.length*b);for(let u=0;u<m;u++){let k=u/(i.length-1)*o,S=i[u],y=Math.sin(u*.1)*S,v=r+y*h;c.push({x:k,y:v})}for(let u=0;u<c.length-1;u++){let k=c[u].x+(c[u+1].x-c[u].x)*.5,S=c[u].y,y=c[u+1].x-(c[u+1].x-c[u].x)*.5,v=c[u+1].y;t.bezierCurveTo(k,S,y,v,c[u+1].x,c[u+1].y)}t.stroke(),f&&(t.shadowBlur=0)};t.strokeStyle="rgba(255, 255, 255, 0.03)",t.lineWidth=.5,t.beginPath(),t.moveTo(0,r),t.lineTo(o,r),t.stroke();for(let d=0;d<=10;d++){let p=o/10*d;t.beginPath(),t.moveTo(p,0),t.lineTo(p,n),t.stroke()}l(s.color,2,1,!1),a>0&&l(s.progressColor,3,a,!0)}function R(t,e,i,a,s){let o=window.devicePixelRatio||1,n=(s.barWidth||3)*o,r=(s.barSpacing||1)*o,h=Math.floor(e.width/(n+r)),l=P(i,h),d=e.height,p=4*o,b=2*o,f=a*e.width,c=d/2;t.clearRect(0,0,e.width,e.height);for(let m=0;m<l.length;m++){let u=m*(n+r);if(u+n>e.width)break;let k=l[m]*d*.9,S=Math.floor(k/(p+b));t.fillStyle=u<f?s.progressColor:s.color;for(let y=0;y<S;y++){let v=y*(p+b);t.fillRect(u,c-v-p,n,p),y>0&&t.fillRect(u,c+v,n,p)}}}function I(t,e,i,a,s){let o=window.devicePixelRatio||1,n=(s.barWidth||2)*o,r=(s.barSpacing||3)*o,h=Math.floor(e.width/(n+r)),l=P(i,h),d=e.height,p=Math.max(1.5*o,n/2),b=a*e.width,f=d/2;t.clearRect(0,0,e.width,e.height);for(let c=0;c<l.length;c++){let m=c*(n+r)+n/2;if(m>e.width)break;let u=l[c]*d*.9;t.fillStyle=m<b?s.progressColor:s.color,t.beginPath(),t.arc(m,f-u/2,p,0,Math.PI*2),t.fill(),t.beginPath(),t.arc(m,f+u/2,p,0,Math.PI*2),t.fill()}}function _(t,e,i,a,s){let o=e.width,n=e.height,r=n/2,h=4,l=h/2;if(t.clearRect(0,0,o,n),t.fillStyle=s.color||"rgba(255, 255, 255, 0.2)",t.beginPath(),t.moveTo(l,r-h/2),t.lineTo(o-l,r-h/2),t.arc(o-l,r,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,r+h/2),t.arc(l,r,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),a>0){let d=Math.max(l*2,a*o);t.shadowBlur=8,t.shadowColor=s.progressColor,t.fillStyle=s.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.moveTo(l,r-h/2),t.lineTo(d-l,r-h/2),t.arc(d-l,r,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,r+h/2),t.arc(l,r,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),t.shadowBlur=0;let p=8,b=d;t.shadowBlur=4,t.shadowColor="rgba(0, 0, 0, 0.3)",t.shadowOffsetY=2,t.fillStyle="#ffffff",t.beginPath(),t.arc(b,r,p,0,Math.PI*2),t.fill(),t.shadowBlur=0,t.shadowOffsetY=0,t.fillStyle=s.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.arc(b,r,p*.4,0,Math.PI*2),t.fill()}}var j={bars:E,bar:E,mirror:N,line:Y,blocks:R,block:R,dots:I,dot:I,seekbar:_};function W(t,e,i,a,s){(j[s.waveformStyle]||E)(t,e,i,a,s)}function z(t){try{let e=t.getChannelData(0),i=t.sampleRate,a=V(e,i);if(a.length<2)return 120;let s=[];for(let h=1;h<a.length;h++)s.push((a[h]-a[h-1])/i);let o={};s.forEach(h=>{let l=60/h,d=Math.round(l/3)*3;d>60&&d<200&&(o[d]=(o[d]||0)+1)});let n=0,r=120;for(let[h,l]of Object.entries(o))l>n&&(n=l,r=parseInt(h));return r<70&&o[r*2]?r*=2:r>160&&o[Math.round(r/2)]&&(r=Math.round(r/2)),r-1}catch(e){return console.warn("BPM detection failed:",e),null}}function V(t,e){let s=[],o=0;for(let n=0;n<t.length-2048;n+=1024){let r=0;for(let d=n;d<n+2048;d++)r+=t[d]*t[d];r=r/2048;let h=r-o,l=o*1.8+.01;if(h>l&&r>.01){let d=s[s.length-1]||0,p=e*.15;n-d>p&&s.push(n)}o=r*.8+o*.2}return s}function J(t,e=200){let i=t.length/e,a=~~(i/10)||1,s=t.numberOfChannels,o=[];for(let r=0;r<s;r++){let h=t.getChannelData(r);for(let l=0;l<e;l++){let d=~~(l*i),p=~~(d+i),b=0,f=0;for(let m=d;m<p;m+=a){let u=h[m];u>f&&(f=u),u<b&&(b=u)}let c=Math.max(Math.abs(f),Math.abs(b));(r===0||c>o[l])&&(o[l]=c)}}let n=Math.max(...o);return n>0?o.map(r=>r/n):o}async function M(t,e=200,i=!1){try{let a=new(window.AudioContext||window.webkitAudioContext),o=await(await fetch(t)).arrayBuffer(),n=await a.decodeAudioData(o),r=J(n,e);r=K(r);let h=null;return i&&(h=await z(n)),a.close(),{peaks:r,bpm:h}}catch(a){throw console.error("Failed to generate waveform:",a),a}}function O(t=200){let e=[];for(let i=0;i<t;i++){let a=Math.random()*.5+.3,s=Math.sin(i/t*Math.PI*4)*.2;e.push(Math.max(.1,Math.min(1,a+s)))}return e}function K(t,e=.95){let i=Math.max(...t);if(i===0||i>e)return t;let a=e/i;return t.map(s=>s*a)}function G(){let t=document.documentElement,e=document.body;if(t.classList.contains("dark")||t.classList.contains("dark-mode")||t.classList.contains("theme-dark")||t.getAttribute("data-theme")==="dark"||t.getAttribute("data-color-scheme")==="dark"||e.classList.contains("dark")||e.classList.contains("dark-mode")||e.getAttribute("data-theme")==="dark")return"dark";if(t.classList.contains("light")||t.classList.contains("light-mode")||t.classList.contains("theme-light")||t.getAttribute("data-theme")==="light"||t.getAttribute("data-color-scheme")==="light"||e.classList.contains("light")||e.classList.contains("light-mode")||e.getAttribute("data-theme")==="light")return"light";try{let a=getComputedStyle(document.body).backgroundColor.match(/\d+/g);if(a&&a.length>=3){let[s,o,n]=a.map(Number),r=(s*299+o*587+n*114)/1e3;if(r>128)return"light";if(r<128)return"dark"}}catch{}if(window.matchMedia){if(window.matchMedia("(prefers-color-scheme: dark)").matches)return"dark";if(window.matchMedia("(prefers-color-scheme: light)").matches)return"light"}return"dark"}var T={dark:{waveformColor:"rgba(255, 255, 255, 0.3)",progressColor:"rgba(255, 255, 255, 0.9)",buttonColor:"rgba(255, 255, 255, 0.9)",buttonHoverColor:"rgba(255, 255, 255, 1)",textColor:"#ffffff",textSecondaryColor:"rgba(255, 255, 255, 0.6)",backgroundColor:"rgba(255, 255, 255, 0.03)",borderColor:"rgba(255, 255, 255, 0.1)"},light:{waveformColor:"rgba(0, 0, 0, 0.2)",progressColor:"rgba(0, 0, 0, 0.8)",buttonColor:"rgba(0, 0, 0, 0.8)",buttonHoverColor:"rgba(0, 0, 0, 0.9)",textColor:"#333333",textSecondaryColor:"rgba(0, 0, 0, 0.6)",backgroundColor:"rgba(0, 0, 0, 0.02)",borderColor:"rgba(0, 0, 0, 0.1)"}};function H(t){if(t&&T[t])return T[t];let e=G();return T[e]}var q={url:"",height:60,samples:200,preload:"metadata",audioMode:"self",playbackRate:1,showPlaybackSpeed:!1,playbackRates:[.5,.75,1,1.25,1.5,1.75,2],buttonAlign:"auto",waveformStyle:"mirror",barWidth:2,barSpacing:0,colorPreset:null,waveformColor:null,progressColor:null,buttonColor:null,buttonHoverColor:null,textColor:null,textSecondaryColor:null,backgroundColor:null,borderColor:null,autoplay:!1,showControls:!0,showInfo:!0,showTime:!0,showHoverTime:!1,showBPM:!1,singlePlay:!0,playOnSeek:!0,enableMediaSession:!0,markers:[],showMarkers:!0,accessibleSeek:!0,seekLabel:null,title:null,subtitle:null,artwork:null,album:"",playIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M8 5v14l11-7z"/></svg>',pauseIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M6 4h4v16H6zM14 4h4v16h-4z"/></svg>',onLoad:null,onPlay:null,onPause:null,onEnd:null,onError:null,onTimeUpdate:null},$={bars:{barWidth:3,barSpacing:1},mirror:{barWidth:2,barSpacing:0},line:{barWidth:2,barSpacing:0},blocks:{barWidth:4,barSpacing:2},dots:{barWidth:3,barSpacing:3},seekbar:{barWidth:1,barSpacing:0}};var U=5,F=10,w=class t{static instances=new Map;static currentlyPlaying=null;constructor(e,i={}){if(this.container=typeof e=="string"?document.querySelector(e):e,!this.container)throw new Error("WaveformPlayer: Container element not found");let a=L(this.container);this.options=C(q,a,i);let s=H(this.options.colorPreset);for(let[n,r]of Object.entries(s))(this.options[n]===null||this.options[n]===void 0)&&(this.options[n]=r);let o=$[this.options.waveformStyle];o&&(a.barWidth===void 0&&i.barWidth===void 0&&(this.options.barWidth=o.barWidth),a.barSpacing===void 0&&i.barSpacing===void 0&&(this.options.barSpacing=o.barSpacing)),this.audio=null,this.canvas=null,this.ctx=null,this.waveformData=[],this.progress=0,this.isPlaying=!1,this.isLoading=!1,this.hasError=!1,this.updateTimer=null,this.resizeObserver=null,this.id=this.container.id||x(this.options.url),t.instances.set(this.id,this),this.init(),setTimeout(()=>{this.container.dispatchEvent(new CustomEvent("waveformplayer:ready",{bubbles:!0,detail:{player:this,url:this.options.url}}))},100)}init(){this.createDOM(),this.createAudio(),this.initPlaybackSpeed(),this.initKeyboardControls(),this.initSeekControl(),this.bindEvents(),this.setupResizeObserver(),requestAnimationFrame(()=>{this.resizeCanvas(),this.options.url&&this.load(this.options.url).then(()=>{this.options.autoplay&&this.play()}).catch(e=>{console.error("Failed to load audio:",e)})})}createDOM(){this.container.innerHTML="",this.container.className="waveform-player";let e=this.options.buttonAlign;e==="auto"&&(this.options.waveformStyle==="bars"?e="bottom":e="center");let i=this.options.showControls?`
2
2
  <button class="waveform-btn" aria-label="Play/Pause" style="
3
3
  border-color: ${this.options.buttonColor};
4
4
  color: ${this.options.buttonColor};
@@ -63,4 +63,4 @@
63
63
  ${a}
64
64
  </div>
65
65
  </div>
66
- `,this.playBtn=this.container.querySelector(".waveform-btn"),this.canvas=this.container.querySelector("canvas"),this.ctx=this.canvas.getContext("2d"),this.titleEl=this.container.querySelector(".waveform-title"),this.subtitleEl=this.container.querySelector(".waveform-subtitle"),this.artworkEl=this.container.querySelector(".waveform-artwork"),this.currentTimeEl=this.container.querySelector(".time-current"),this.totalTimeEl=this.container.querySelector(".time-total"),this.bpmEl=this.container.querySelector(".waveform-bpm"),this.bpmValueEl=this.container.querySelector(".bpm-value"),this.loadingEl=this.container.querySelector(".waveform-loading"),this.errorEl=this.container.querySelector(".waveform-error"),this.markersContainer=this.container.querySelector(".waveform-markers"),this.speedBtn=this.container.querySelector(".speed-btn"),this.speedMenu=this.container.querySelector(".speed-menu"),this.resizeCanvas()}createAudio(){if(this.options.audioMode==="external"){this.audio=null;return}this.audio=new Audio,this.audio.preload=this.options.preload||"metadata",this.audio.crossOrigin="anonymous"}initPlaybackSpeed(){this.audio&&this.options.playbackRate&&this.options.playbackRate!==1&&(this.audio.playbackRate=this.options.playbackRate),this.options.showPlaybackSpeed&&this.initSpeedControls()}initSpeedControls(){let e=this.container.querySelector(".speed-btn"),i=this.container.querySelector(".speed-menu");!e||!i||(e.addEventListener("click",a=>{a.stopPropagation(),i.style.display=i.style.display==="none"?"block":"none"}),document.addEventListener("click",()=>{i.style.display="none"}),i.addEventListener("click",a=>{if(a.stopPropagation(),a.target.classList.contains("speed-option")){let s=parseFloat(a.target.dataset.rate);this.setPlaybackRate(s),i.style.display="none"}}),this.updateSpeedUI())}initKeyboardControls(){this.container.setAttribute("tabindex","-1"),this.container.addEventListener("click",()=>{t.getAllInstances().forEach(e=>{e!==this&&e.container.setAttribute("tabindex","-1")}),this.container.setAttribute("tabindex","0"),this.container.focus()}),this.container.addEventListener("keydown",e=>{if(document.activeElement!==this.container)return;let i=e.key,a=!!this.audio,s=a?this.audio.currentTime:0;if(a&&i>="0"&&i<="9"){e.preventDefault(),this.seekToPercent(parseInt(i)/10);return}let o={" ":()=>this.togglePlay()};a&&(o.ArrowLeft=()=>this.seekTo(Math.max(0,s-5)),o.ArrowRight=()=>this.seekTo(Math.min(this.audio.duration,s+5)),o.ArrowUp=()=>this.setVolume(Math.min(1,this.audio.volume+.1)),o.ArrowDown=()=>this.setVolume(Math.max(0,this.audio.volume-.1)),o.m=o.M=()=>this.audio.muted=!this.audio.muted),o[i]&&(e.preventDefault(),o[i]())})}initMediaSession(){!("mediaSession"in navigator)||!this.options.enableMediaSession||this.audio&&(navigator.mediaSession.metadata=new MediaMetadata({title:this.options.title||"Unknown Track",artist:this.options.subtitle||"",album:this.options.album||"",artwork:this.options.artwork?[{src:this.options.artwork,sizes:"512x512",type:"image/jpeg"}]:[]}),navigator.mediaSession.setActionHandler("play",()=>this.play()),navigator.mediaSession.setActionHandler("pause",()=>this.pause()),navigator.mediaSession.setActionHandler("seekbackward",()=>{this.seekTo(Math.max(0,this.audio.currentTime-10))}),navigator.mediaSession.setActionHandler("seekforward",()=>{this.seekTo(Math.min(this.audio.duration,this.audio.currentTime+10))}),navigator.mediaSession.setActionHandler("seekto",e=>{e.seekTime!==null&&this.seekTo(e.seekTime)}))}bindEvents(){this.playBtn&&this.playBtn.addEventListener("click",()=>this.togglePlay()),this.audio&&(this.audio.addEventListener("loadstart",()=>this.setLoading(!0)),this.audio.addEventListener("loadedmetadata",()=>this.onMetadataLoaded()),this.audio.addEventListener("canplay",()=>this.setLoading(!1)),this.audio.addEventListener("play",()=>this.onPlay()),this.audio.addEventListener("pause",()=>this.onPause()),this.audio.addEventListener("ended",()=>this.onEnded()),this.audio.addEventListener("error",e=>this.onError(e))),this.canvas.addEventListener("click",e=>this.handleCanvasClick(e)),this.resizeHandler=R(()=>this.resizeCanvas(),100),window.addEventListener("resize",this.resizeHandler)}setupResizeObserver(){"ResizeObserver"in window&&(this.resizeObserver=new ResizeObserver(()=>{this.resizeCanvas()}),this.canvas?.parentElement&&this.resizeObserver.observe(this.canvas.parentElement))}async load(e){try{this.setLoading(!0),this.progress=0,this.hasError=!1,this.audio&&(this.audio.src=e,await new Promise((a,s)=>{let o=()=>{this.audio.removeEventListener("loadedmetadata",o),this.audio.removeEventListener("error",n),a()},n=r=>{this.audio.removeEventListener("loadedmetadata",o),this.audio.removeEventListener("error",n),s(r)};this.audio.addEventListener("loadedmetadata",o),this.audio.addEventListener("error",n)}));let i=this.options.title||B(e);if(this.titleEl&&(this.titleEl.textContent=i),this.options.waveform)this.setWaveformData(this.options.waveform);else try{let a=await M(e,this.options.samples,this.options.showBPM);this.waveformData=a.peaks,a.bpm&&(this.detectedBPM=a.bpm,this.updateBPMDisplay())}catch(a){console.warn("Using placeholder waveform:",a),this.waveformData=O(this.options.samples)}this.drawWaveform(),this.renderMarkers(),this.initMediaSession(),this.options.onLoad&&this.options.onLoad(this)}catch(i){console.error("Failed to load audio:",i),this.onError(i)}finally{this.setLoading(!1)}}async loadTrack(e,i=null,a=null,s={}){this.isPlaying&&this.pause(),this.audio&&(this.audio.src="",this.audio.load()),this.hasError=!1,this.errorEl&&(this.errorEl.style.display="none"),this.canvas&&(this.canvas.style.opacity="1"),this.playBtn&&(this.playBtn.disabled=!1),this.progress=0,this.waveformData=[],this.options=C(this.options,{url:e,title:i||this.options.title,subtitle:a||this.options.subtitle,...s}),s.preload&&this.audio&&(this.audio.preload=s.preload),this.subtitleEl&&(a?(this.subtitleEl.textContent=a,this.subtitleEl.style.display=""):a===""&&(this.subtitleEl.style.display="none")),s.artwork&&this.artworkEl&&(this.artworkEl.src=s.artwork),this.options.markers=s.markers||[],await this.load(e),this.play().catch(()=>{})}setWaveformData(e){if(typeof e=="string"&&e.trim().endsWith(".json")){fetch(e.trim()).then(i=>i.json()).then(i=>{this.waveformData=Array.isArray(i)?i:i.peaks||[],i.markers&&!this.options.markers?.length&&(this.options.markers=i.markers,this.renderMarkers()),this.drawWaveform()}).catch(()=>{});return}if(typeof e=="string")try{let i=JSON.parse(e);this.waveformData=Array.isArray(i)?i:[]}catch{this.waveformData=e.split(",").map(Number)}else this.waveformData=Array.isArray(e)?e:[];this.drawWaveform()}drawWaveform(){!this.ctx||this.waveformData.length===0||W(this.ctx,this.canvas,this.waveformData,this.progress,{...this.options,waveformStyle:this.options.waveformStyle||"bars",color:this.options.waveformColor,progressColor:this.options.progressColor})}resizeCanvas(){if(!this.canvas||this.isDestroying)return;let e=window.devicePixelRatio||1,i=this.canvas.parentElement.getBoundingClientRect();this.canvas.width=i.width*e,this.canvas.height=this.options.height*e,this.canvas.parentElement.style.height=this.options.height+"px",this.drawWaveform()}renderMarkers(){this.markersContainer&&(this.markersContainer.innerHTML="",!(!this.options.showMarkers||!this.options.markers?.length)&&(!this.audio||!this.audio.duration||this.audio.duration===0||this.options.markers.forEach((e,i)=>{if(e.time>this.audio.duration){console.warn(`Marker "${e.label}" at ${e.time}s exceeds audio duration of ${this.audio.duration}s`);return}let a=e.time/this.audio.duration*100,s=document.createElement("button");s.className="waveform-marker",s.style.left=`${a}%`,s.style.backgroundColor=e.color||"rgba(255, 255, 255, 0.5)",s.setAttribute("aria-label",e.label),s.setAttribute("data-time",e.time);let o=document.createElement("span");o.className="waveform-marker-tooltip",o.textContent=e.label,s.appendChild(o),s.addEventListener("click",n=>{n.stopPropagation(),this.seekTo(e.time),this.options.playOnSeek&&!this.isPlaying&&this.play()}),this.markersContainer.appendChild(s)})))}handleCanvasClick(e){let i=this.canvas.getBoundingClientRect(),a=e.clientX-i.left,s=Math.max(0,Math.min(1,a/i.width));if(this.options.audioMode==="external"){let o=new CustomEvent("waveformplayer:request-seek",{bubbles:!0,cancelable:!0,detail:{...this._buildTrackDetail(),percent:s}});this.container.dispatchEvent(o),o.defaultPrevented||(this.progress=s,this.drawWaveform?.());return}!this.audio||!this.audio.duration||this.seekToPercent(s)}setLoading(e){this.isLoading=e,this.loadingEl&&(this.loadingEl.style.display=e?"block":"none")}onMetadataLoaded(){this.isDestroying||(this.totalTimeEl&&(this.totalTimeEl.textContent=S(this.audio.duration)),this.renderMarkers())}onPlay(){if(!this.isDestroying){if(this.isPlaying=!0,this.playBtn){this.playBtn.classList.add("playing");let e=this.playBtn.querySelector(".waveform-icon-play"),i=this.playBtn.querySelector(".waveform-icon-pause");e&&(e.style.display="none"),i&&(i.style.display="flex")}this.startSmoothUpdate(),this.container.dispatchEvent(new CustomEvent("waveformplayer:play",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPlay&&this.options.onPlay(this)}}onPause(){if(!this.isDestroying){if(this.isPlaying=!1,this.playBtn){this.playBtn.classList.remove("playing");let e=this.playBtn.querySelector(".waveform-icon-play"),i=this.playBtn.querySelector(".waveform-icon-pause");e&&(e.style.display="flex"),i&&(i.style.display="none")}this.stopSmoothUpdate(),this.container.dispatchEvent(new CustomEvent("waveformplayer:pause",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPause&&this.options.onPause(this)}}onEnded(){this.isDestroying||(this.progress=0,this.audio.currentTime=0,this.drawWaveform(),this.currentTimeEl&&(this.currentTimeEl.textContent="0:00"),this.container.dispatchEvent(new CustomEvent("waveformplayer:ended",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.onPause(),this.options.onEnd&&this.options.onEnd(this))}onError(e){this.isDestroying||(console.error("Audio error:",e),this.hasError=!0,this.setLoading(!1),this.errorEl&&(this.errorEl.style.display="flex"),this.canvas&&(this.canvas.style.opacity="0.2"),this.playBtn&&(this.playBtn.disabled=!0),this.options.onError&&this.options.onError(e,this))}startSmoothUpdate(){this.stopSmoothUpdate();let e=()=>{this.isPlaying&&this.audio&&this.audio.duration&&(this.updateProgress(),this.updateTimer=requestAnimationFrame(e))};this.updateTimer=requestAnimationFrame(e)}stopSmoothUpdate(){this.updateTimer&&(cancelAnimationFrame(this.updateTimer),this.updateTimer=null)}updateProgress(){if(!this.audio||!this.audio.duration)return;let e=this.audio.currentTime/this.audio.duration;Math.abs(e-this.progress)>.001&&(this.progress=e,this.drawWaveform()),this.currentTimeEl&&(this.currentTimeEl.textContent=S(this.audio.currentTime)),this.container.dispatchEvent(new CustomEvent("waveformplayer:timeupdate",{bubbles:!0,detail:{player:this,currentTime:this.audio.currentTime,duration:this.audio.duration,url:this.options.url}})),this.options.onTimeUpdate&&this.options.onTimeUpdate(this.audio.currentTime,this.audio.duration,this)}updateBPMDisplay(){this.bpmEl&&this.bpmValueEl&&this.detectedBPM&&(this.bpmValueEl.textContent=Math.round(this.detectedBPM),this.bpmEl.style.display="inline-flex")}updateSpeedUI(){let e=this.container.querySelector(".speed-value");if(e){let i=this.audio.playbackRate;e.textContent=i===1?"1x":`${i}x`}this.container.querySelectorAll(".speed-option").forEach(i=>{i.classList.toggle("active",parseFloat(i.dataset.rate)===this.audio.playbackRate)})}play(){if(this.options.singlePlay&&t.currentlyPlaying&&t.currentlyPlaying!==this&&t.currentlyPlaying.pause(),this.options.audioMode==="external"){let e=new CustomEvent("waveformplayer:request-play",{bubbles:!0,cancelable:!0,detail:this._buildTrackDetail()});this.container.dispatchEvent(e),e.defaultPrevented||(t.currentlyPlaying=this);return}return t.currentlyPlaying=this,this.audio.play()}pause(){if(t.currentlyPlaying===this&&(t.currentlyPlaying=null),this.options.audioMode==="external"){this.container.dispatchEvent(new CustomEvent("waveformplayer:request-pause",{bubbles:!0,cancelable:!0,detail:this._buildTrackDetail()}));return}this.audio.pause()}_buildTrackDetail(){return{url:this.options.url,title:this.options.title,subtitle:this.options.subtitle,artist:this.options.artist,artwork:this.options.artwork,id:this.id,player:this}}setPlayingState(e){let i=this.isPlaying;if(this.isPlaying=!!e,this.playBtn){this.playBtn.classList.toggle("playing",this.isPlaying);let a=this.playBtn.querySelector(".waveform-icon-play"),s=this.playBtn.querySelector(".waveform-icon-pause");a&&(a.style.display=this.isPlaying?"none":"flex"),s&&(s.style.display=this.isPlaying?"flex":"none")}this.isPlaying&&!i?(this.startSmoothUpdate?.(),this.container.dispatchEvent(new CustomEvent("waveformplayer:play",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPlay&&this.options.onPlay(this)):!this.isPlaying&&i&&(this.stopSmoothUpdate?.(),this.container.dispatchEvent(new CustomEvent("waveformplayer:pause",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPause&&this.options.onPause(this))}setProgress(e,i){!i||i<=0||(this.progress=Math.max(0,Math.min(1,e/i)),this.currentTimeEl&&(this.currentTimeEl.textContent=S(e)),this.totalTimeEl&&(!this.totalTimeEl.dataset._extSet||this._extDuration!==i)&&(this.totalTimeEl.textContent=S(i),this.totalTimeEl.dataset._extSet="1",this._extDuration=i),this.drawWaveform?.(),this.container.dispatchEvent(new CustomEvent("waveformplayer:timeupdate",{bubbles:!0,detail:{player:this,currentTime:e,duration:i,progress:this.progress}})),this.options.onTimeUpdate&&this.options.onTimeUpdate(this,e,i))}togglePlay(){this.isPlaying?this.pause():this.play()}seekTo(e){this.audio&&this.audio.duration&&(this.audio.currentTime=Math.max(0,Math.min(e,this.audio.duration)),this.updateProgress())}seekToPercent(e){this.audio&&this.audio.duration&&(this.audio.currentTime=this.audio.duration*Math.max(0,Math.min(1,e)),this.updateProgress())}setVolume(e){this.audio&&(this.audio.volume=Math.max(0,Math.min(1,e)))}setPlaybackRate(e){if(!this.audio)return;let i=Math.max(.5,Math.min(2,e));this.audio.playbackRate=i,this.options.playbackRate=i,this.updateSpeedUI()}destroy(){this.isDestroying=!0,this.pause(),this.stopSmoothUpdate(),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.resizeHandler&&(window.removeEventListener("resize",this.resizeHandler),this.resizeHandler=null),t.instances.delete(this.id),t.currentlyPlaying===this&&(t.currentlyPlaying=null),this.audio&&(this.audio.pause(),this.audio.src="",this.audio.load(),this.audio=null),this.container.innerHTML="",this.canvas=null,this.ctx=null,this.playBtn=null,this.waveformData=[]}static getInstance(e){if(typeof e=="string"){let i=this.instances.get(e);if(i)return i;let a=document.getElementById(e);if(a)return Array.from(this.instances.values()).find(s=>s.container===a)}if(e instanceof HTMLElement)return Array.from(this.instances.values()).find(i=>i.container===e)}static getAllInstances(){return Array.from(this.instances.values())}static destroyAll(){this.instances.forEach(e=>e.destroy()),this.instances.clear()}static async generateWaveformData(e,i=200){try{return(await M(e,i)).peaks}catch(a){throw console.error("Failed to generate waveform:",a),a}}static getPeaksUrl(e){if(!e)return;let i=e.replace(/\.(mp3|wav|ogg|flac|m4a|aac)(\?[^#]*)?(#.*)?$/i,".json$2$3");return i===e?void 0:i}};function L(){if(typeof document>"u")return;document.querySelectorAll("[data-waveform-player]").forEach(e=>{if(e.dataset.waveformInitialized!=="true")try{new g(e),e.dataset.waveformInitialized="true"}catch(i){console.error("Failed to initialize WaveformPlayer:",i,e)}})}typeof document<"u"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",L):L());g.init=L;typeof window<"u"&&(window.WaveformPlayer=g);var lt=g;})();
66
+ `,this.playBtn=this.container.querySelector(".waveform-btn"),this.canvas=this.container.querySelector("canvas"),this.ctx=this.canvas.getContext("2d"),this.titleEl=this.container.querySelector(".waveform-title"),this.subtitleEl=this.container.querySelector(".waveform-subtitle"),this.artworkEl=this.container.querySelector(".waveform-artwork"),this.currentTimeEl=this.container.querySelector(".time-current"),this.totalTimeEl=this.container.querySelector(".time-total"),this.bpmEl=this.container.querySelector(".waveform-bpm"),this.bpmValueEl=this.container.querySelector(".bpm-value"),this.loadingEl=this.container.querySelector(".waveform-loading"),this.errorEl=this.container.querySelector(".waveform-error"),this.markersContainer=this.container.querySelector(".waveform-markers"),this.speedBtn=this.container.querySelector(".speed-btn"),this.speedMenu=this.container.querySelector(".speed-menu"),this.resizeCanvas()}createAudio(){if(this.options.audioMode==="external"){this.audio=null;return}this.audio=new Audio,this.audio.preload=this.options.preload||"metadata",this.audio.crossOrigin="anonymous"}initPlaybackSpeed(){this.audio&&this.options.playbackRate&&this.options.playbackRate!==1&&(this.audio.playbackRate=this.options.playbackRate),this.options.showPlaybackSpeed&&this.initSpeedControls()}initSpeedControls(){let e=this.container.querySelector(".speed-btn"),i=this.container.querySelector(".speed-menu");!e||!i||(e.addEventListener("click",a=>{a.stopPropagation(),i.style.display=i.style.display==="none"?"block":"none"}),document.addEventListener("click",()=>{i.style.display="none"}),i.addEventListener("click",a=>{if(a.stopPropagation(),a.target.classList.contains("speed-option")){let s=parseFloat(a.target.dataset.rate);this.setPlaybackRate(s),i.style.display="none"}}),this.updateSpeedUI())}initKeyboardControls(){this.container.setAttribute("tabindex","-1"),this.container.addEventListener("click",()=>{t.getAllInstances().forEach(e=>{e!==this&&e.container.setAttribute("tabindex","-1")}),this.container.setAttribute("tabindex","0"),this.container.focus()}),this.container.addEventListener("keydown",e=>{if(document.activeElement!==this.container)return;let i=e.key,a=!!this.audio,s=a?this.audio.currentTime:0;if(a&&i>="0"&&i<="9"){e.preventDefault(),this.seekToPercent(parseInt(i)/10);return}let o={" ":()=>this.togglePlay()};a&&(o.ArrowLeft=()=>this.seekTo(Math.max(0,s-5)),o.ArrowRight=()=>this.seekTo(Math.min(this.audio.duration,s+5)),o.ArrowUp=()=>this.setVolume(Math.min(1,this.audio.volume+.1)),o.ArrowDown=()=>this.setVolume(Math.max(0,this.audio.volume-.1)),o.m=o.M=()=>this.audio.muted=!this.audio.muted),o[i]&&(e.preventDefault(),o[i]())})}initSeekControl(){this.options.accessibleSeek&&(this.seekEl=this.container.querySelector(".waveform-container"),this.seekEl&&(this.seekEl.setAttribute("role","slider"),this.seekEl.setAttribute("tabindex","0"),this.seekEl.setAttribute("aria-valuemin","0"),this.applySeekLabel(),this.updateSeekAccessibility(),this.seekEl.addEventListener("keydown",e=>{let i=this.getSeekDuration();if(!i)return;let a=this.getSeekCurrentTime(),s;switch(e.key){case"ArrowLeft":case"ArrowDown":s=a-U;break;case"ArrowRight":case"ArrowUp":s=a+U;break;case"PageDown":s=a-F;break;case"PageUp":s=a+F;break;case"Home":s=0;break;case"End":s=i;break;default:return}e.preventDefault(),e.stopPropagation(),this.seekToSeconds(s)})))}getSeekDuration(){return this.options.audioMode==="external"?this._extDuration||0:this.audio&&Number.isFinite(this.audio.duration)?this.audio.duration:0}getSeekCurrentTime(){return this.options.audioMode==="external"?this.progress*(this._extDuration||0):this.audio&&Number.isFinite(this.audio.currentTime)?this.audio.currentTime:0}seekToSeconds(e){let i=this.getSeekDuration();if(!i)return;let a=Math.max(0,Math.min(e,i));if(this.options.audioMode==="external"){let s=a/i,o=new CustomEvent("waveformplayer:request-seek",{bubbles:!0,cancelable:!0,detail:{...this._buildTrackDetail(),percent:s}});this.container.dispatchEvent(o),o.defaultPrevented||(this.progress=s,this.drawWaveform?.()),this.updateSeekAccessibility();return}this.seekTo(a)}applySeekLabel(e=this.options.title){if(!this.seekEl)return;let i=this.options.seekLabel||e||"Seek";this.seekEl.setAttribute("aria-label",i)}updateSeekAccessibility(){if(!this.seekEl)return;let e=this.getSeekDuration(),i=Math.min(this.getSeekCurrentTime(),e);this.seekEl.setAttribute("aria-valuemax",String(Math.round(e))),this.seekEl.setAttribute("aria-valuenow",String(Math.round(i))),this.seekEl.setAttribute("aria-valuetext",`${g(i)} of ${g(e)}`)}initMediaSession(){!("mediaSession"in navigator)||!this.options.enableMediaSession||this.audio&&(navigator.mediaSession.metadata=new MediaMetadata({title:this.options.title||"Unknown Track",artist:this.options.subtitle||"",album:this.options.album||"",artwork:this.options.artwork?[{src:this.options.artwork,sizes:"512x512",type:"image/jpeg"}]:[]}),navigator.mediaSession.setActionHandler("play",()=>this.play()),navigator.mediaSession.setActionHandler("pause",()=>this.pause()),navigator.mediaSession.setActionHandler("seekbackward",()=>{this.seekTo(Math.max(0,this.audio.currentTime-10))}),navigator.mediaSession.setActionHandler("seekforward",()=>{this.seekTo(Math.min(this.audio.duration,this.audio.currentTime+10))}),navigator.mediaSession.setActionHandler("seekto",e=>{e.seekTime!==null&&this.seekTo(e.seekTime)}))}bindEvents(){this.playBtn&&this.playBtn.addEventListener("click",()=>this.togglePlay()),this.audio&&(this.audio.addEventListener("loadstart",()=>this.setLoading(!0)),this.audio.addEventListener("loadedmetadata",()=>this.onMetadataLoaded()),this.audio.addEventListener("canplay",()=>this.setLoading(!1)),this.audio.addEventListener("play",()=>this.onPlay()),this.audio.addEventListener("pause",()=>this.onPause()),this.audio.addEventListener("ended",()=>this.onEnded()),this.audio.addEventListener("error",e=>this.onError(e))),this.canvas.addEventListener("click",e=>this.handleCanvasClick(e)),this.resizeHandler=B(()=>this.resizeCanvas(),100),window.addEventListener("resize",this.resizeHandler)}setupResizeObserver(){"ResizeObserver"in window&&(this.resizeObserver=new ResizeObserver(()=>{this.resizeCanvas()}),this.canvas?.parentElement&&this.resizeObserver.observe(this.canvas.parentElement))}async load(e){try{this.setLoading(!0),this.progress=0,this.hasError=!1,this.audio&&(this.audio.src=e,await new Promise((a,s)=>{let o=()=>{this.audio.removeEventListener("loadedmetadata",o),this.audio.removeEventListener("error",n),a()},n=r=>{this.audio.removeEventListener("loadedmetadata",o),this.audio.removeEventListener("error",n),s(r)};this.audio.addEventListener("loadedmetadata",o),this.audio.addEventListener("error",n)}));let i=this.options.title||D(e);if(this.titleEl&&(this.titleEl.textContent=i),this.applySeekLabel(i),this.options.waveform)this.setWaveformData(this.options.waveform);else try{let a=await M(e,this.options.samples,this.options.showBPM);this.waveformData=a.peaks,a.bpm&&(this.detectedBPM=a.bpm,this.updateBPMDisplay())}catch(a){console.warn("Using placeholder waveform:",a),this.waveformData=O(this.options.samples)}this.drawWaveform(),this.renderMarkers(),this.initMediaSession(),this.options.onLoad&&this.options.onLoad(this)}catch(i){console.error("Failed to load audio:",i),this.onError(i)}finally{this.setLoading(!1)}}async loadTrack(e,i=null,a=null,s={}){this.isPlaying&&this.pause(),this.audio&&(this.audio.src="",this.audio.load()),this.hasError=!1,this.errorEl&&(this.errorEl.style.display="none"),this.canvas&&(this.canvas.style.opacity="1"),this.playBtn&&(this.playBtn.disabled=!1),this.progress=0,this.waveformData=[],this.options=C(this.options,{url:e,title:i||this.options.title,subtitle:a||this.options.subtitle,...s}),s.preload&&this.audio&&(this.audio.preload=s.preload),this.subtitleEl&&(a?(this.subtitleEl.textContent=a,this.subtitleEl.style.display=""):a===""&&(this.subtitleEl.style.display="none")),s.artwork&&this.artworkEl&&(this.artworkEl.src=s.artwork),this.options.markers=s.markers||[],await this.load(e),this.play().catch(()=>{})}setWaveformData(e){if(typeof e=="string"&&e.trim().endsWith(".json")){fetch(e.trim()).then(i=>i.json()).then(i=>{this.waveformData=Array.isArray(i)?i:i.peaks||[],i.markers&&!this.options.markers?.length&&(this.options.markers=i.markers,this.renderMarkers()),this.drawWaveform()}).catch(()=>{});return}if(typeof e=="string")try{let i=JSON.parse(e);this.waveformData=Array.isArray(i)?i:[]}catch{this.waveformData=e.split(",").map(Number)}else this.waveformData=Array.isArray(e)?e:[];this.drawWaveform()}drawWaveform(){!this.ctx||this.waveformData.length===0||W(this.ctx,this.canvas,this.waveformData,this.progress,{...this.options,waveformStyle:this.options.waveformStyle||"bars",color:this.options.waveformColor,progressColor:this.options.progressColor})}resizeCanvas(){if(!this.canvas||this.isDestroying)return;let e=window.devicePixelRatio||1,i=this.canvas.parentElement.getBoundingClientRect();this.canvas.width=i.width*e,this.canvas.height=this.options.height*e,this.canvas.parentElement.style.height=this.options.height+"px",this.drawWaveform()}renderMarkers(){this.markersContainer&&(this.markersContainer.innerHTML="",!(!this.options.showMarkers||!this.options.markers?.length)&&(!this.audio||!this.audio.duration||this.audio.duration===0||this.options.markers.forEach((e,i)=>{if(e.time>this.audio.duration){console.warn(`Marker "${e.label}" at ${e.time}s exceeds audio duration of ${this.audio.duration}s`);return}let a=e.time/this.audio.duration*100,s=document.createElement("button");s.className="waveform-marker",s.style.left=`${a}%`,s.style.backgroundColor=e.color||"rgba(255, 255, 255, 0.5)",s.setAttribute("aria-label",e.label),s.setAttribute("data-time",e.time);let o=document.createElement("span");o.className="waveform-marker-tooltip",o.textContent=e.label,s.appendChild(o),s.addEventListener("click",n=>{n.stopPropagation(),this.seekTo(e.time),this.options.playOnSeek&&!this.isPlaying&&this.play()}),this.markersContainer.appendChild(s)})))}handleCanvasClick(e){let i=this.canvas.getBoundingClientRect(),a=e.clientX-i.left,s=Math.max(0,Math.min(1,a/i.width));if(this.options.audioMode==="external"){let o=new CustomEvent("waveformplayer:request-seek",{bubbles:!0,cancelable:!0,detail:{...this._buildTrackDetail(),percent:s}});this.container.dispatchEvent(o),o.defaultPrevented||(this.progress=s,this.drawWaveform?.());return}!this.audio||!this.audio.duration||this.seekToPercent(s)}setLoading(e){this.isLoading=e,this.loadingEl&&(this.loadingEl.style.display=e?"block":"none")}onMetadataLoaded(){this.isDestroying||(this.totalTimeEl&&(this.totalTimeEl.textContent=g(this.audio.duration)),this.renderMarkers(),this.updateSeekAccessibility())}onPlay(){if(!this.isDestroying){if(this.isPlaying=!0,this.playBtn){this.playBtn.classList.add("playing");let e=this.playBtn.querySelector(".waveform-icon-play"),i=this.playBtn.querySelector(".waveform-icon-pause");e&&(e.style.display="none"),i&&(i.style.display="flex")}this.startSmoothUpdate(),this.container.dispatchEvent(new CustomEvent("waveformplayer:play",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPlay&&this.options.onPlay(this)}}onPause(){if(!this.isDestroying){if(this.isPlaying=!1,this.playBtn){this.playBtn.classList.remove("playing");let e=this.playBtn.querySelector(".waveform-icon-play"),i=this.playBtn.querySelector(".waveform-icon-pause");e&&(e.style.display="flex"),i&&(i.style.display="none")}this.stopSmoothUpdate(),this.container.dispatchEvent(new CustomEvent("waveformplayer:pause",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPause&&this.options.onPause(this)}}onEnded(){this.isDestroying||(this.progress=0,this.audio.currentTime=0,this.drawWaveform(),this.currentTimeEl&&(this.currentTimeEl.textContent="0:00"),this.container.dispatchEvent(new CustomEvent("waveformplayer:ended",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.onPause(),this.options.onEnd&&this.options.onEnd(this))}onError(e){this.isDestroying||(console.error("Audio error:",e),this.hasError=!0,this.setLoading(!1),this.errorEl&&(this.errorEl.style.display="flex"),this.canvas&&(this.canvas.style.opacity="0.2"),this.playBtn&&(this.playBtn.disabled=!0),this.options.onError&&this.options.onError(e,this))}startSmoothUpdate(){this.stopSmoothUpdate();let e=()=>{this.isPlaying&&this.audio&&this.audio.duration&&(this.updateProgress(),this.updateTimer=requestAnimationFrame(e))};this.updateTimer=requestAnimationFrame(e)}stopSmoothUpdate(){this.updateTimer&&(cancelAnimationFrame(this.updateTimer),this.updateTimer=null)}updateProgress(){if(!this.audio||!this.audio.duration)return;let e=this.audio.currentTime/this.audio.duration;Math.abs(e-this.progress)>.001&&(this.progress=e,this.drawWaveform()),this.currentTimeEl&&(this.currentTimeEl.textContent=g(this.audio.currentTime)),this.container.dispatchEvent(new CustomEvent("waveformplayer:timeupdate",{bubbles:!0,detail:{player:this,currentTime:this.audio.currentTime,duration:this.audio.duration,url:this.options.url}})),this.options.onTimeUpdate&&this.options.onTimeUpdate(this.audio.currentTime,this.audio.duration,this),this.updateSeekAccessibility()}updateBPMDisplay(){this.bpmEl&&this.bpmValueEl&&this.detectedBPM&&(this.bpmValueEl.textContent=Math.round(this.detectedBPM),this.bpmEl.style.display="inline-flex")}updateSpeedUI(){let e=this.container.querySelector(".speed-value");if(e){let i=this.audio.playbackRate;e.textContent=i===1?"1x":`${i}x`}this.container.querySelectorAll(".speed-option").forEach(i=>{i.classList.toggle("active",parseFloat(i.dataset.rate)===this.audio.playbackRate)})}play(){if(this.options.singlePlay&&t.currentlyPlaying&&t.currentlyPlaying!==this&&t.currentlyPlaying.pause(),this.options.audioMode==="external"){let e=new CustomEvent("waveformplayer:request-play",{bubbles:!0,cancelable:!0,detail:this._buildTrackDetail()});this.container.dispatchEvent(e),e.defaultPrevented||(t.currentlyPlaying=this);return}return t.currentlyPlaying=this,this.audio.play()}pause(){if(t.currentlyPlaying===this&&(t.currentlyPlaying=null),this.options.audioMode==="external"){this.container.dispatchEvent(new CustomEvent("waveformplayer:request-pause",{bubbles:!0,cancelable:!0,detail:this._buildTrackDetail()}));return}this.audio.pause()}_buildTrackDetail(){return{url:this.options.url,title:this.options.title,subtitle:this.options.subtitle,artist:this.options.artist,artwork:this.options.artwork,id:this.id,player:this}}setPlayingState(e){let i=this.isPlaying;if(this.isPlaying=!!e,this.playBtn){this.playBtn.classList.toggle("playing",this.isPlaying);let a=this.playBtn.querySelector(".waveform-icon-play"),s=this.playBtn.querySelector(".waveform-icon-pause");a&&(a.style.display=this.isPlaying?"none":"flex"),s&&(s.style.display=this.isPlaying?"flex":"none")}this.isPlaying&&!i?(this.startSmoothUpdate?.(),this.container.dispatchEvent(new CustomEvent("waveformplayer:play",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPlay&&this.options.onPlay(this)):!this.isPlaying&&i&&(this.stopSmoothUpdate?.(),this.container.dispatchEvent(new CustomEvent("waveformplayer:pause",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPause&&this.options.onPause(this))}setProgress(e,i){!i||i<=0||(this.progress=Math.max(0,Math.min(1,e/i)),this.currentTimeEl&&(this.currentTimeEl.textContent=g(e)),this.totalTimeEl&&(!this.totalTimeEl.dataset._extSet||this._extDuration!==i)&&(this.totalTimeEl.textContent=g(i),this.totalTimeEl.dataset._extSet="1",this._extDuration=i),this.drawWaveform?.(),this.container.dispatchEvent(new CustomEvent("waveformplayer:timeupdate",{bubbles:!0,detail:{player:this,currentTime:e,duration:i,progress:this.progress}})),this.options.onTimeUpdate&&this.options.onTimeUpdate(this,e,i),this.updateSeekAccessibility())}togglePlay(){this.isPlaying?this.pause():this.play()}seekTo(e){this.audio&&this.audio.duration&&(this.audio.currentTime=Math.max(0,Math.min(e,this.audio.duration)),this.updateProgress())}seekToPercent(e){this.audio&&this.audio.duration&&(this.audio.currentTime=this.audio.duration*Math.max(0,Math.min(1,e)),this.updateProgress())}setVolume(e){this.audio&&(this.audio.volume=Math.max(0,Math.min(1,e)))}setPlaybackRate(e){if(!this.audio)return;let i=Math.max(.5,Math.min(2,e));this.audio.playbackRate=i,this.options.playbackRate=i,this.updateSpeedUI()}destroy(){this.isDestroying=!0,this.pause(),this.stopSmoothUpdate(),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.resizeHandler&&(window.removeEventListener("resize",this.resizeHandler),this.resizeHandler=null),t.instances.delete(this.id),t.currentlyPlaying===this&&(t.currentlyPlaying=null),this.audio&&(this.audio.pause(),this.audio.src="",this.audio.load(),this.audio=null),this.container.innerHTML="",this.canvas=null,this.ctx=null,this.playBtn=null,this.waveformData=[]}static getInstance(e){if(typeof e=="string"){let i=this.instances.get(e);if(i)return i;let a=document.getElementById(e);if(a)return Array.from(this.instances.values()).find(s=>s.container===a)}if(e instanceof HTMLElement)return Array.from(this.instances.values()).find(i=>i.container===e)}static getAllInstances(){return Array.from(this.instances.values())}static destroyAll(){this.instances.forEach(e=>e.destroy()),this.instances.clear()}static async generateWaveformData(e,i=200){try{return(await M(e,i)).peaks}catch(a){throw console.error("Failed to generate waveform:",a),a}}static getPeaksUrl(e){if(!e)return;let i=e.replace(/\.(mp3|wav|ogg|flac|m4a|aac)(\?[^#]*)?(#.*)?$/i,".json$2$3");return i===e?void 0:i}};function A(){if(typeof document>"u")return;document.querySelectorAll("[data-waveform-player]").forEach(e=>{if(e.dataset.waveformInitialized!=="true")try{new w(e),e.dataset.waveformInitialized="true"}catch(i){console.error("Failed to initialize WaveformPlayer:",i,e)}})}typeof document<"u"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",A):A());w.init=A;typeof window<"u"&&(window.WaveformPlayer=w);var dt=w;})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arraypress/waveform-player",
3
- "version": "1.7.1",
3
+ "version": "1.7.2",
4
4
  "description": "Lightweight, customizable audio player with waveform visualization",
5
5
  "type": "module",
6
6
  "main": "dist/waveform-player.esm.js",
@@ -83,6 +83,11 @@
83
83
  width: 100%;
84
84
  }
85
85
 
86
+ /* Keyboard focus ring for the accessible seek slider */
87
+ .waveform-container:focus-visible {
88
+ outline: 2px solid currentColor;
89
+ }
90
+
86
91
  .waveform-container canvas {
87
92
  display: block;
88
93
  width: 100%;
package/src/js/core.js CHANGED
@@ -16,6 +16,10 @@ import {
16
16
 
17
17
  import {DEFAULT_OPTIONS, STYLE_DEFAULTS, getColorPreset} from './themes.js';
18
18
 
19
+ // Keyboard seek steps (seconds) for the accessible slider.
20
+ const SEEK_STEP_SECONDS = 5;
21
+ const SEEK_PAGE_SECONDS = 10;
22
+
19
23
  /**
20
24
  * WaveformPlayer - Modern audio player with waveform visualization
21
25
  * @class
@@ -112,6 +116,7 @@ export class WaveformPlayer {
112
116
  this.createAudio();
113
117
  this.initPlaybackSpeed();
114
118
  this.initKeyboardControls();
119
+ this.initSeekControl();
115
120
  this.bindEvents();
116
121
  this.setupResizeObserver();
117
122
 
@@ -389,6 +394,156 @@ export class WaveformPlayer {
389
394
  });
390
395
  }
391
396
 
397
+ /**
398
+ * Expose the waveform as an accessible, keyboard-operable slider.
399
+ *
400
+ * Adds role="slider" + ARIA value attributes to the waveform surface,
401
+ * makes it focusable in the tab order, and handles the standard slider
402
+ * keys (arrows, Page Up/Down, Home/End) to seek. Works in both self and
403
+ * external audio modes. Opt out with `accessibleSeek: false`.
404
+ * @private
405
+ */
406
+ initSeekControl() {
407
+ if (!this.options.accessibleSeek) return;
408
+
409
+ this.seekEl = this.container.querySelector('.waveform-container');
410
+ if (!this.seekEl) return;
411
+
412
+ this.seekEl.setAttribute('role', 'slider');
413
+ this.seekEl.setAttribute('tabindex', '0');
414
+ this.seekEl.setAttribute('aria-valuemin', '0');
415
+ this.applySeekLabel();
416
+ this.updateSeekAccessibility();
417
+
418
+ this.seekEl.addEventListener('keydown', (e) => {
419
+ const duration = this.getSeekDuration();
420
+ if (!duration) return;
421
+
422
+ const current = this.getSeekCurrentTime();
423
+ let target;
424
+ switch (e.key) {
425
+ case 'ArrowLeft':
426
+ case 'ArrowDown':
427
+ target = current - SEEK_STEP_SECONDS;
428
+ break;
429
+ case 'ArrowRight':
430
+ case 'ArrowUp':
431
+ target = current + SEEK_STEP_SECONDS;
432
+ break;
433
+ case 'PageDown':
434
+ target = current - SEEK_PAGE_SECONDS;
435
+ break;
436
+ case 'PageUp':
437
+ target = current + SEEK_PAGE_SECONDS;
438
+ break;
439
+ case 'Home':
440
+ target = 0;
441
+ break;
442
+ case 'End':
443
+ target = duration;
444
+ break;
445
+ default:
446
+ return;
447
+ }
448
+
449
+ // Prevent page scroll and stop the container-level keydown
450
+ // handler from also seeking (it would double-fire / change
451
+ // volume on the vertical arrows).
452
+ e.preventDefault();
453
+ e.stopPropagation();
454
+ this.seekToSeconds(target);
455
+ });
456
+ }
457
+
458
+ /**
459
+ * Total seekable duration in seconds, regardless of audio mode.
460
+ * @returns {number}
461
+ * @private
462
+ */
463
+ getSeekDuration() {
464
+ if (this.options.audioMode === 'external') {
465
+ return this._extDuration || 0;
466
+ }
467
+ return this.audio && Number.isFinite(this.audio.duration)
468
+ ? this.audio.duration
469
+ : 0;
470
+ }
471
+
472
+ /**
473
+ * Current playback position in seconds, regardless of audio mode.
474
+ * @returns {number}
475
+ * @private
476
+ */
477
+ getSeekCurrentTime() {
478
+ if (this.options.audioMode === 'external') {
479
+ return this.progress * (this._extDuration || 0);
480
+ }
481
+ return this.audio && Number.isFinite(this.audio.currentTime)
482
+ ? this.audio.currentTime
483
+ : 0;
484
+ }
485
+
486
+ /**
487
+ * Seek the slider to an absolute time, clamped to the track length.
488
+ * Routes through the external controller in external mode.
489
+ * @param {number} seconds - Target time in seconds.
490
+ * @private
491
+ */
492
+ seekToSeconds(seconds) {
493
+ const duration = this.getSeekDuration();
494
+ if (!duration) return;
495
+
496
+ const clamped = Math.max(0, Math.min(seconds, duration));
497
+
498
+ if (this.options.audioMode === 'external') {
499
+ const percent = clamped / duration;
500
+ const evt = new CustomEvent('waveformplayer:request-seek', {
501
+ bubbles: true,
502
+ cancelable: true,
503
+ detail: { ...this._buildTrackDetail(), percent }
504
+ });
505
+ this.container.dispatchEvent(evt);
506
+ if (!evt.defaultPrevented) {
507
+ this.progress = percent;
508
+ this.drawWaveform?.();
509
+ }
510
+ this.updateSeekAccessibility();
511
+ return;
512
+ }
513
+
514
+ // seekTo() calls updateProgress(), which refreshes the ARIA values.
515
+ this.seekTo(clamped);
516
+ }
517
+
518
+ /**
519
+ * Set the slider's accessible name from `seekLabel`, falling back to the
520
+ * track title, then a generic 'Seek'.
521
+ * @private
522
+ */
523
+ applySeekLabel(title = this.options.title) {
524
+ if (!this.seekEl) return;
525
+ const label = this.options.seekLabel || title || 'Seek';
526
+ this.seekEl.setAttribute('aria-label', label);
527
+ }
528
+
529
+ /**
530
+ * Keep the slider's ARIA value attributes in sync with playback.
531
+ * @private
532
+ */
533
+ updateSeekAccessibility() {
534
+ if (!this.seekEl) return;
535
+
536
+ const duration = this.getSeekDuration();
537
+ const current = Math.min(this.getSeekCurrentTime(), duration);
538
+
539
+ this.seekEl.setAttribute('aria-valuemax', String(Math.round(duration)));
540
+ this.seekEl.setAttribute('aria-valuenow', String(Math.round(current)));
541
+ this.seekEl.setAttribute(
542
+ 'aria-valuetext',
543
+ `${formatTime(current)} of ${formatTime(duration)}`
544
+ );
545
+ }
546
+
392
547
  /**
393
548
  * Initialize Media Session API for system media controls
394
549
  * @private
@@ -528,6 +683,8 @@ export class WaveformPlayer {
528
683
  if (this.titleEl) {
529
684
  this.titleEl.textContent = title;
530
685
  }
686
+ // Keep the seek slider's accessible name in sync with the track.
687
+ this.applySeekLabel(title);
531
688
 
532
689
  // Load or generate waveform
533
690
  if (this.options.waveform) {
@@ -826,6 +983,8 @@ export class WaveformPlayer {
826
983
  }
827
984
  // Re-render markers when duration is known
828
985
  this.renderMarkers();
986
+ // Duration is now known — publish it to the accessible slider.
987
+ this.updateSeekAccessibility();
829
988
  }
830
989
 
831
990
  /**
@@ -1020,6 +1179,8 @@ export class WaveformPlayer {
1020
1179
  if (this.options.onTimeUpdate) {
1021
1180
  this.options.onTimeUpdate(this.audio.currentTime, this.audio.duration, this);
1022
1181
  }
1182
+
1183
+ this.updateSeekAccessibility();
1023
1184
  }
1024
1185
 
1025
1186
  // ============================================
@@ -1200,6 +1361,8 @@ export class WaveformPlayer {
1200
1361
  detail: {player: this, currentTime, duration, progress: this.progress}
1201
1362
  }));
1202
1363
  if (this.options.onTimeUpdate) this.options.onTimeUpdate(this, currentTime, duration);
1364
+
1365
+ this.updateSeekAccessibility();
1203
1366
  }
1204
1367
 
1205
1368
  /**
package/src/js/themes.js CHANGED
@@ -173,6 +173,13 @@ export const DEFAULT_OPTIONS = {
173
173
  markers: [],
174
174
  showMarkers: true,
175
175
 
176
+ // Accessibility — expose the waveform as a keyboard-operable slider
177
+ // (role="slider" + ARIA value attributes + arrow/page/home/end seeking).
178
+ // seekLabel sets the slider's accessible name; when null it falls back
179
+ // to the track title, then 'Seek'.
180
+ accessibleSeek: true,
181
+ seekLabel: null,
182
+
176
183
  // Content
177
184
  title: null,
178
185
  subtitle: null,