@arraypress/waveform-player 1.0.0 → 1.0.1
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 +127 -30
- package/dist/waveform-player.css +1 -185
- package/dist/waveform-player.js +7 -7
- package/package.json +13 -8
- package/src/css/waveform-player.css +180 -0
- /package/src/{audio.js → js/audio.js} +0 -0
- /package/src/{bpm.js → js/bpm.js} +0 -0
- /package/src/{core.js → js/core.js} +0 -0
- /package/src/{drawing.js → js/drawing.js} +0 -0
- /package/src/{index.js → js/index.js} +0 -0
- /package/src/{themes.js → js/themes.js} +0 -0
- /package/src/{utils.js → js/utils.js} +0 -0
package/README.md
CHANGED
|
@@ -2,67 +2,122 @@
|
|
|
2
2
|
|
|
3
3
|
A lightweight, customizable audio player with waveform visualization. Under 6KB gzipped.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-

|
|
7
|
-

|
|
5
|
+
**[Live Demo](https://waveformplayer.com)** | **[Documentation](https://waveformplayer.com/#docs)** | **[NPM Package](https://www.npmjs.com/package/@arraypress/waveform-player)**
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+

|
|
8
|
+

|
|
9
|
+

|
|
10
|
+

|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
## Why WaveformPlayer?
|
|
15
|
+
|
|
16
|
+
- **Zero Config** - Just add `data-waveform-player` to any div. No JavaScript required.
|
|
17
|
+
- **Tiny** - 6KB gzipped vs 40KB+ for alternatives
|
|
18
|
+
- **Real Waveforms** - Actual audio analysis, not fake waves
|
|
19
|
+
- **No Dependencies** - No jQuery, no bloat, pure vanilla JS
|
|
20
|
+
- **Works Everywhere** - WordPress, Shopify, React, Vue, or plain HTML
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
Simplest possible usage:
|
|
25
|
+
|
|
26
|
+
```html
|
|
27
|
+
<!-- Just this. That's it. -->
|
|
28
|
+
<div data-waveform-player data-url="song.mp3"></div>
|
|
29
|
+
```
|
|
19
30
|
|
|
20
31
|
## Installation
|
|
21
32
|
|
|
22
33
|
### NPM
|
|
23
34
|
```bash
|
|
24
|
-
npm install waveform-player
|
|
35
|
+
npm install @arraypress/waveform-player
|
|
25
36
|
```
|
|
26
37
|
|
|
27
38
|
### CDN
|
|
28
39
|
```html
|
|
29
|
-
<link rel="stylesheet" href="https://unpkg.com/waveform-player/dist/waveform-player.css">
|
|
30
|
-
<script src="https://unpkg.com/waveform-player/dist/waveform-player.min.js"></script>
|
|
40
|
+
<link rel="stylesheet" href="https://unpkg.com/@arraypress/waveform-player@latest/dist/waveform-player.css">
|
|
41
|
+
<script src="https://unpkg.com/@arraypress/waveform-player@latest/dist/waveform-player.min.js"></script>
|
|
31
42
|
```
|
|
32
43
|
|
|
33
|
-
|
|
44
|
+
### Download
|
|
45
|
+
```html
|
|
46
|
+
<link rel="stylesheet" href="waveform-player.css">
|
|
47
|
+
<script src="waveform-player.js"></script>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Features
|
|
34
51
|
|
|
35
|
-
|
|
52
|
+
- 🎨 **6 Visual Styles** - Bars, mirror, line, blocks, dots, seekbar
|
|
53
|
+
- 🎯 **Tiny Footprint** - Under 6KB gzipped
|
|
54
|
+
- ⚡ **Zero Dependencies** - Pure JavaScript
|
|
55
|
+
- 🎭 **Fully Customizable** - Colors, sizes, styles
|
|
56
|
+
- 📱 **Responsive** - Works on all devices
|
|
57
|
+
- 🎵 **BPM Detection** - Automatic tempo detection (optional)
|
|
58
|
+
- 💾 **Waveform Caching** - Pre-generate waveforms for performance
|
|
59
|
+
- 🌐 **Framework Agnostic** - Works with React, Vue, Angular, or vanilla JS
|
|
60
|
+
|
|
61
|
+
## Comparison
|
|
62
|
+
|
|
63
|
+
| Feature | WaveformPlayer | WaveSurfer.js | Amplitude.js |
|
|
64
|
+
|---------|---------------|---------------|--------------|
|
|
65
|
+
| Size (gzipped) | 6KB | 40KB+ | 35KB+ |
|
|
66
|
+
| Zero Config | ✅ | ❌ | ❌ |
|
|
67
|
+
| Dependencies | None | None | None |
|
|
68
|
+
| Waveform Styles | 6 | 3 | N/A |
|
|
69
|
+
| Setup Time | 30 seconds | 5+ minutes | 5+ minutes |
|
|
70
|
+
| Real Waveforms | ✅ | ✅ | ❌ |
|
|
71
|
+
|
|
72
|
+
## Usage
|
|
73
|
+
|
|
74
|
+
### HTML (Zero JavaScript)
|
|
36
75
|
```html
|
|
37
76
|
<div data-waveform-player
|
|
38
77
|
data-url="audio.mp3"
|
|
39
|
-
data-title="My Song"
|
|
78
|
+
data-title="My Song"
|
|
79
|
+
data-subtitle="Artist Name"
|
|
80
|
+
data-waveform-style="mirror">
|
|
40
81
|
</div>
|
|
41
82
|
```
|
|
42
83
|
|
|
43
|
-
### JavaScript
|
|
84
|
+
### JavaScript API
|
|
44
85
|
```javascript
|
|
45
|
-
import WaveformPlayer from 'waveform-player';
|
|
86
|
+
import WaveformPlayer from '@arraypress/waveform-player';
|
|
46
87
|
|
|
47
88
|
const player = new WaveformPlayer('#player', {
|
|
48
89
|
url: 'audio.mp3',
|
|
49
90
|
waveformStyle: 'mirror',
|
|
50
|
-
height: 80
|
|
91
|
+
height: 80,
|
|
92
|
+
barWidth: 2,
|
|
93
|
+
barSpacing: 1
|
|
51
94
|
});
|
|
52
95
|
```
|
|
53
96
|
|
|
97
|
+
## Visual Styles
|
|
98
|
+
|
|
99
|
+
Choose from 6 built-in styles:
|
|
100
|
+
|
|
101
|
+
- **bars** - Classic waveform bars
|
|
102
|
+
- **mirror** - SoundCloud-style mirrored waveform
|
|
103
|
+
- **line** - Smooth oscilloscope line
|
|
104
|
+
- **blocks** - LED meter blocks
|
|
105
|
+
- **dots** - Circular dots
|
|
106
|
+
- **seekbar** - Minimal progress bar
|
|
107
|
+
|
|
54
108
|
## Options
|
|
55
109
|
|
|
56
110
|
| Option | Type | Default | Description |
|
|
57
111
|
|--------|------|---------|-------------|
|
|
58
112
|
| `url` | string | `''` | Audio file URL |
|
|
59
|
-
| `waveformStyle` | string | `'bars'` | Visual style: bars, mirror, line, blocks, dots |
|
|
113
|
+
| `waveformStyle` | string | `'bars'` | Visual style: bars, mirror, line, blocks, dots, seekbar |
|
|
60
114
|
| `height` | number | `60` | Waveform height in pixels |
|
|
61
115
|
| `barWidth` | number | `3` | Width of waveform bars |
|
|
62
116
|
| `barSpacing` | number | `1` | Space between bars |
|
|
63
117
|
| `samples` | number | `200` | Number of waveform samples |
|
|
64
118
|
| `waveformColor` | string | `'rgba(255,255,255,0.3)'` | Waveform color |
|
|
65
119
|
| `progressColor` | string | `'rgba(255,255,255,0.9)'` | Progress color |
|
|
120
|
+
| `buttonColor` | string | `'rgba(255,255,255,0.9)'` | Play button color |
|
|
66
121
|
| `showTime` | boolean | `true` | Show time display |
|
|
67
122
|
| `showBPM` | boolean | `false` | Enable BPM detection |
|
|
68
123
|
| `autoplay` | boolean | `false` | Autoplay on load |
|
|
@@ -110,10 +165,10 @@ new WaveformPlayer('#player', {
|
|
|
110
165
|
For better performance, generate waveform data server-side:
|
|
111
166
|
|
|
112
167
|
```javascript
|
|
113
|
-
// Generate waveform data
|
|
168
|
+
// Generate waveform data once
|
|
114
169
|
const waveformData = await WaveformPlayer.generateWaveformData('audio.mp3');
|
|
115
170
|
|
|
116
|
-
// Use pre-generated data
|
|
171
|
+
// Use pre-generated data for instant display
|
|
117
172
|
new WaveformPlayer('#player', {
|
|
118
173
|
url: 'audio.mp3',
|
|
119
174
|
waveform: waveformData // Bypass client-side processing
|
|
@@ -146,20 +201,62 @@ const player = WaveformPlayer.getInstance('my-player');
|
|
|
146
201
|
}
|
|
147
202
|
```
|
|
148
203
|
|
|
204
|
+
## Framework Integration
|
|
205
|
+
|
|
206
|
+
### React
|
|
207
|
+
```jsx
|
|
208
|
+
import { useEffect, useRef } from 'react';
|
|
209
|
+
import WaveformPlayer from '@arraypress/waveform-player';
|
|
210
|
+
|
|
211
|
+
function AudioPlayer({ url }) {
|
|
212
|
+
const playerRef = useRef();
|
|
213
|
+
|
|
214
|
+
useEffect(() => {
|
|
215
|
+
const player = new WaveformPlayer(playerRef.current, { url });
|
|
216
|
+
return () => player.destroy();
|
|
217
|
+
}, [url]);
|
|
218
|
+
|
|
219
|
+
return <div ref={playerRef} />;
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Vue
|
|
224
|
+
```vue
|
|
225
|
+
<template>
|
|
226
|
+
<div ref="player"></div>
|
|
227
|
+
</template>
|
|
228
|
+
|
|
229
|
+
<script>
|
|
230
|
+
import WaveformPlayer from '@arraypress/waveform-player';
|
|
231
|
+
|
|
232
|
+
export default {
|
|
233
|
+
mounted() {
|
|
234
|
+
this.player = new WaveformPlayer(this.$refs.player, {
|
|
235
|
+
url: this.audioUrl
|
|
236
|
+
});
|
|
237
|
+
},
|
|
238
|
+
beforeDestroy() {
|
|
239
|
+
this.player?.destroy();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
</script>
|
|
243
|
+
```
|
|
244
|
+
|
|
149
245
|
## Browser Support
|
|
150
246
|
|
|
151
247
|
- Chrome/Edge 90+
|
|
152
248
|
- Firefox 88+
|
|
153
249
|
- Safari 14+
|
|
154
|
-
- Mobile browsers
|
|
250
|
+
- Mobile browsers (iOS Safari, Chrome Android)
|
|
155
251
|
|
|
156
252
|
## Examples
|
|
157
253
|
|
|
158
|
-
|
|
159
|
-
-
|
|
160
|
-
-
|
|
161
|
-
- Custom styling
|
|
254
|
+
See the [live demo](https://waveformplayer.com) for:
|
|
255
|
+
- All visual styles
|
|
256
|
+
- Custom styling examples
|
|
162
257
|
- Event handling
|
|
258
|
+
- Player builder
|
|
259
|
+
- BPM detection
|
|
163
260
|
- Pre-generated waveforms
|
|
164
261
|
|
|
165
262
|
## Development
|
|
@@ -180,7 +277,7 @@ npm run size
|
|
|
180
277
|
|
|
181
278
|
## License
|
|
182
279
|
|
|
183
|
-
MIT ©
|
|
280
|
+
MIT © [ArrayPress](https://github.com/arraypress)
|
|
184
281
|
|
|
185
282
|
## Credits
|
|
186
283
|
|
package/dist/waveform-player.css
CHANGED
|
@@ -1,185 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* WaveformPlayer.css
|
|
3
|
-
* Ultra-minimal styles
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/* Base structure */
|
|
7
|
-
.waveform-player {
|
|
8
|
-
font-family: inherit;
|
|
9
|
-
color: inherit;
|
|
10
|
-
line-height: 1.4;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
.waveform-player * {
|
|
14
|
-
box-sizing: border-box;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
.waveform-player-inner {
|
|
18
|
-
padding: 12px;
|
|
19
|
-
border-radius: 4px;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/* Body container */
|
|
23
|
-
.waveform-body {
|
|
24
|
-
display: flex;
|
|
25
|
-
flex-direction: column;
|
|
26
|
-
gap: 8px;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/* Track row */
|
|
30
|
-
.waveform-track {
|
|
31
|
-
display: flex;
|
|
32
|
-
align-items: center;
|
|
33
|
-
gap: 12px;
|
|
34
|
-
position: relative;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/* Play button */
|
|
38
|
-
.waveform-btn {
|
|
39
|
-
width: 36px;
|
|
40
|
-
height: 36px;
|
|
41
|
-
min-width: 36px;
|
|
42
|
-
border-radius: 50%;
|
|
43
|
-
border: 2px solid currentColor;
|
|
44
|
-
background: transparent;
|
|
45
|
-
color: inherit;
|
|
46
|
-
cursor: pointer;
|
|
47
|
-
display: flex;
|
|
48
|
-
align-items: center;
|
|
49
|
-
justify-content: center;
|
|
50
|
-
transition: all 0.2s ease;
|
|
51
|
-
padding: 0;
|
|
52
|
-
opacity: 0.9;
|
|
53
|
-
flex-shrink: 0;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
.waveform-btn:hover:not(:disabled) {
|
|
57
|
-
opacity: 1;
|
|
58
|
-
transform: scale(1.05);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
.waveform-btn:disabled {
|
|
62
|
-
cursor: not-allowed;
|
|
63
|
-
opacity: 0.3;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/* Icons */
|
|
67
|
-
.waveform-btn > * {
|
|
68
|
-
display: flex;
|
|
69
|
-
align-items: center;
|
|
70
|
-
justify-content: center;
|
|
71
|
-
width: 100%;
|
|
72
|
-
height: 100%;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
.waveform-btn svg {
|
|
76
|
-
width: 16px;
|
|
77
|
-
height: 16px;
|
|
78
|
-
fill: currentColor;
|
|
79
|
-
display: block;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
.waveform-icon-play svg {
|
|
83
|
-
margin-left: 1px;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/* Waveform container */
|
|
87
|
-
.waveform-container {
|
|
88
|
-
flex: 1;
|
|
89
|
-
position: relative;
|
|
90
|
-
min-height: 60px;
|
|
91
|
-
cursor: pointer;
|
|
92
|
-
overflow: hidden;
|
|
93
|
-
min-width: 0;
|
|
94
|
-
width: 100%;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
.waveform-container canvas {
|
|
98
|
-
display: block;
|
|
99
|
-
width: 100%;
|
|
100
|
-
height: 100%;
|
|
101
|
-
max-width: 100%;
|
|
102
|
-
transition: opacity 0.3s ease;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/* Info section */
|
|
106
|
-
.waveform-info {
|
|
107
|
-
display: flex;
|
|
108
|
-
align-items: center;
|
|
109
|
-
gap: 8px;
|
|
110
|
-
font-size: 13px;
|
|
111
|
-
min-height: 20px;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
.waveform-text {
|
|
115
|
-
flex: 1;
|
|
116
|
-
display: flex;
|
|
117
|
-
flex-direction: column;
|
|
118
|
-
gap: 2px;
|
|
119
|
-
min-width: 0;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
.waveform-title {
|
|
123
|
-
white-space: nowrap;
|
|
124
|
-
overflow: hidden;
|
|
125
|
-
text-overflow: ellipsis;
|
|
126
|
-
font-weight: 500;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
.waveform-subtitle {
|
|
130
|
-
font-size: 11px;
|
|
131
|
-
white-space: nowrap;
|
|
132
|
-
overflow: hidden;
|
|
133
|
-
text-overflow: ellipsis;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
.waveform-time {
|
|
137
|
-
font-size: 11px;
|
|
138
|
-
white-space: nowrap;
|
|
139
|
-
flex-shrink: 0;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/* Loading state - simplified */
|
|
143
|
-
.waveform-loading {
|
|
144
|
-
position: absolute;
|
|
145
|
-
inset: 0;
|
|
146
|
-
background: rgba(0, 0, 0, 0.1);
|
|
147
|
-
z-index: 1;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/* Error state */
|
|
151
|
-
.waveform-error {
|
|
152
|
-
position: absolute;
|
|
153
|
-
inset: 0;
|
|
154
|
-
display: flex;
|
|
155
|
-
align-items: center;
|
|
156
|
-
justify-content: center;
|
|
157
|
-
background: rgba(0, 0, 0, 0.2);
|
|
158
|
-
z-index: 1;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
.waveform-error-text {
|
|
162
|
-
font-size: 12px;
|
|
163
|
-
opacity: 0.7;
|
|
164
|
-
text-align: center;
|
|
165
|
-
padding: 0 20px;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/* Minimal responsive */
|
|
169
|
-
@media (max-width: 480px) {
|
|
170
|
-
.waveform-btn {
|
|
171
|
-
width: 32px;
|
|
172
|
-
height: 32px;
|
|
173
|
-
min-width: 32px;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
.waveform-container {
|
|
177
|
-
min-height: 50px;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/* Accessibility */
|
|
182
|
-
.waveform-btn:focus-visible {
|
|
183
|
-
outline: 2px solid currentColor;
|
|
184
|
-
outline-offset: 2px;
|
|
185
|
-
}
|
|
1
|
+
.waveform-player{font-family:inherit;color:inherit;line-height:1.4}.waveform-player *{box-sizing:border-box}.waveform-player-inner{padding:12px;border-radius:4px}.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;overflow:hidden;min-width:0;width:100%}.waveform-container canvas{display:block;width:100%;height:100%;max-width:100%;transition:opacity .3s ease}.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-loading{position:absolute;inset:0;background:#0000001a;z-index:1}.waveform-error{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:#0003;z-index:1}.waveform-error-text{font-size:12px;opacity:.7;text-align:center;padding:0 20px}@media (max-width: 480px){.waveform-btn{width:32px;height:32px;min-width:32px}.waveform-container{min-height:50px}}.waveform-btn:focus-visible{outline:2px solid currentColor;outline-offset:2px}
|
package/dist/waveform-player.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
(() => {
|
|
2
|
-
// src/utils.js
|
|
2
|
+
// src/js/utils.js
|
|
3
3
|
function parseDataAttributes(element) {
|
|
4
4
|
const options = {};
|
|
5
5
|
if (element.dataset.url) options.url = element.dataset.url;
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
return result;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
// src/drawing.js
|
|
115
|
+
// src/js/drawing.js
|
|
116
116
|
function drawBars(ctx, canvas, peaks, progress, options) {
|
|
117
117
|
const dpr = window.devicePixelRatio || 1;
|
|
118
118
|
const barWidth = options.barWidth * dpr;
|
|
@@ -349,7 +349,7 @@
|
|
|
349
349
|
drawFunc(ctx, canvas, peaks, progress, options);
|
|
350
350
|
}
|
|
351
351
|
|
|
352
|
-
// src/bpm.js
|
|
352
|
+
// src/js/bpm.js
|
|
353
353
|
function detectBPM(buffer) {
|
|
354
354
|
try {
|
|
355
355
|
const channelData = buffer.getChannelData(0);
|
|
@@ -412,7 +412,7 @@
|
|
|
412
412
|
return onsets;
|
|
413
413
|
}
|
|
414
414
|
|
|
415
|
-
// src/audio.js
|
|
415
|
+
// src/js/audio.js
|
|
416
416
|
function extractPeaks(buffer, samples = 200) {
|
|
417
417
|
const sampleSize = buffer.length / samples;
|
|
418
418
|
const sampleStep = ~~(sampleSize / 10) || 1;
|
|
@@ -469,7 +469,7 @@
|
|
|
469
469
|
return data;
|
|
470
470
|
}
|
|
471
471
|
|
|
472
|
-
// src/themes.js
|
|
472
|
+
// src/js/themes.js
|
|
473
473
|
var DEFAULT_OPTIONS = {
|
|
474
474
|
// Core settings
|
|
475
475
|
url: "",
|
|
@@ -520,7 +520,7 @@
|
|
|
520
520
|
seekbar: { barWidth: 1, barSpacing: 0 }
|
|
521
521
|
};
|
|
522
522
|
|
|
523
|
-
// src/core.js
|
|
523
|
+
// src/js/core.js
|
|
524
524
|
var WaveformPlayer = class _WaveformPlayer {
|
|
525
525
|
/** @type {Map<string, WaveformPlayer>} */
|
|
526
526
|
static instances = /* @__PURE__ */ new Map();
|
|
@@ -1058,7 +1058,7 @@
|
|
|
1058
1058
|
}
|
|
1059
1059
|
};
|
|
1060
1060
|
|
|
1061
|
-
// src/index.js
|
|
1061
|
+
// src/js/index.js
|
|
1062
1062
|
function autoInit() {
|
|
1063
1063
|
if (typeof document === "undefined") return;
|
|
1064
1064
|
const elements = document.querySelectorAll("[data-waveform-player]");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arraypress/waveform-player",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Lightweight, customizable audio player with waveform visualization",
|
|
5
5
|
"main": "dist/waveform-player.js",
|
|
6
6
|
"module": "dist/waveform-player.esm.js",
|
|
@@ -12,12 +12,16 @@
|
|
|
12
12
|
"LICENSE"
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
|
-
"build": "npm run build:iife && npm run build:esm && npm run build:min",
|
|
16
|
-
"build:
|
|
17
|
-
"build:
|
|
18
|
-
"build:
|
|
19
|
-
"
|
|
20
|
-
"
|
|
15
|
+
"build": "npm run build:css && npm run build:iife && npm run build:esm && npm run build:min && npm run build:demo",
|
|
16
|
+
"build:css": "esbuild src/css/waveform-player.css --minify --outfile=dist/waveform-player.css",
|
|
17
|
+
"build:iife": "esbuild src/js/index.js --bundle --format=iife --outfile=dist/waveform-player.js",
|
|
18
|
+
"build:min": "esbuild src/js/index.js --bundle --format=iife --outfile=dist/waveform-player.min.js --minify",
|
|
19
|
+
"build:esm": "esbuild src/js/index.js --bundle --format=esm --outfile=dist/waveform-player.esm.js --minify",
|
|
20
|
+
"build:demo": "mkdir -p demo/dist && cp dist/*.js demo/dist/ && cp dist/*.css demo/dist/",
|
|
21
|
+
"dev": "npm run build:css && esbuild src/js/index.js --bundle --format=iife --outfile=dist/waveform-player.js --watch",
|
|
22
|
+
"dev:css": "esbuild src/css/waveform-player.css --outfile=dist/waveform-player.css --watch",
|
|
23
|
+
"dev:all": "npm run build:css && npm run build:demo && concurrently \"npm run dev\" \"npm run dev:css\"",
|
|
24
|
+
"size": "npm run build:min && npm run build:css && echo 'JS:' && gzip -c dist/waveform-player.min.js | wc -c && echo 'CSS:' && gzip -c dist/waveform-player.css | wc -c",
|
|
21
25
|
"prepublishOnly": "npm run build"
|
|
22
26
|
},
|
|
23
27
|
"keywords": [
|
|
@@ -41,6 +45,7 @@
|
|
|
41
45
|
},
|
|
42
46
|
"homepage": "https://github.com/arraypress/waveform-player#readme",
|
|
43
47
|
"devDependencies": {
|
|
48
|
+
"concurrently": "^7.6.0",
|
|
44
49
|
"esbuild": "^0.25.0"
|
|
45
50
|
}
|
|
46
|
-
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/* Base structure */
|
|
2
|
+
.waveform-player {
|
|
3
|
+
font-family: inherit;
|
|
4
|
+
color: inherit;
|
|
5
|
+
line-height: 1.4;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.waveform-player * {
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.waveform-player-inner {
|
|
13
|
+
padding: 12px;
|
|
14
|
+
border-radius: 4px;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* Body container */
|
|
18
|
+
.waveform-body {
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
gap: 8px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* Track row */
|
|
25
|
+
.waveform-track {
|
|
26
|
+
display: flex;
|
|
27
|
+
align-items: center;
|
|
28
|
+
gap: 12px;
|
|
29
|
+
position: relative;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Play button */
|
|
33
|
+
.waveform-btn {
|
|
34
|
+
width: 36px;
|
|
35
|
+
height: 36px;
|
|
36
|
+
min-width: 36px;
|
|
37
|
+
border-radius: 50%;
|
|
38
|
+
border: 2px solid currentColor;
|
|
39
|
+
background: transparent;
|
|
40
|
+
color: inherit;
|
|
41
|
+
cursor: pointer;
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
justify-content: center;
|
|
45
|
+
transition: all 0.2s ease;
|
|
46
|
+
padding: 0;
|
|
47
|
+
opacity: 0.9;
|
|
48
|
+
flex-shrink: 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.waveform-btn:hover:not(:disabled) {
|
|
52
|
+
opacity: 1;
|
|
53
|
+
transform: scale(1.05);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.waveform-btn:disabled {
|
|
57
|
+
cursor: not-allowed;
|
|
58
|
+
opacity: 0.3;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* Icons */
|
|
62
|
+
.waveform-btn > * {
|
|
63
|
+
display: flex;
|
|
64
|
+
align-items: center;
|
|
65
|
+
justify-content: center;
|
|
66
|
+
width: 100%;
|
|
67
|
+
height: 100%;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.waveform-btn svg {
|
|
71
|
+
width: 16px;
|
|
72
|
+
height: 16px;
|
|
73
|
+
fill: currentColor;
|
|
74
|
+
display: block;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.waveform-icon-play svg {
|
|
78
|
+
margin-left: 1px;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* Waveform container */
|
|
82
|
+
.waveform-container {
|
|
83
|
+
flex: 1;
|
|
84
|
+
position: relative;
|
|
85
|
+
min-height: 60px;
|
|
86
|
+
cursor: pointer;
|
|
87
|
+
overflow: hidden;
|
|
88
|
+
min-width: 0;
|
|
89
|
+
width: 100%;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.waveform-container canvas {
|
|
93
|
+
display: block;
|
|
94
|
+
width: 100%;
|
|
95
|
+
height: 100%;
|
|
96
|
+
max-width: 100%;
|
|
97
|
+
transition: opacity 0.3s ease;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* Info section */
|
|
101
|
+
.waveform-info {
|
|
102
|
+
display: flex;
|
|
103
|
+
align-items: center;
|
|
104
|
+
gap: 8px;
|
|
105
|
+
font-size: 13px;
|
|
106
|
+
min-height: 20px;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.waveform-text {
|
|
110
|
+
flex: 1;
|
|
111
|
+
display: flex;
|
|
112
|
+
flex-direction: column;
|
|
113
|
+
gap: 2px;
|
|
114
|
+
min-width: 0;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.waveform-title {
|
|
118
|
+
white-space: nowrap;
|
|
119
|
+
overflow: hidden;
|
|
120
|
+
text-overflow: ellipsis;
|
|
121
|
+
font-weight: 500;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.waveform-subtitle {
|
|
125
|
+
font-size: 11px;
|
|
126
|
+
white-space: nowrap;
|
|
127
|
+
overflow: hidden;
|
|
128
|
+
text-overflow: ellipsis;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.waveform-time {
|
|
132
|
+
font-size: 11px;
|
|
133
|
+
white-space: nowrap;
|
|
134
|
+
flex-shrink: 0;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/* Loading state - simplified */
|
|
138
|
+
.waveform-loading {
|
|
139
|
+
position: absolute;
|
|
140
|
+
inset: 0;
|
|
141
|
+
background: rgba(0, 0, 0, 0.1);
|
|
142
|
+
z-index: 1;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* Error state */
|
|
146
|
+
.waveform-error {
|
|
147
|
+
position: absolute;
|
|
148
|
+
inset: 0;
|
|
149
|
+
display: flex;
|
|
150
|
+
align-items: center;
|
|
151
|
+
justify-content: center;
|
|
152
|
+
background: rgba(0, 0, 0, 0.2);
|
|
153
|
+
z-index: 1;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.waveform-error-text {
|
|
157
|
+
font-size: 12px;
|
|
158
|
+
opacity: 0.7;
|
|
159
|
+
text-align: center;
|
|
160
|
+
padding: 0 20px;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* Minimal responsive */
|
|
164
|
+
@media (max-width: 480px) {
|
|
165
|
+
.waveform-btn {
|
|
166
|
+
width: 32px;
|
|
167
|
+
height: 32px;
|
|
168
|
+
min-width: 32px;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.waveform-container {
|
|
172
|
+
min-height: 50px;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* Accessibility */
|
|
177
|
+
.waveform-btn:focus-visible {
|
|
178
|
+
outline: 2px solid currentColor;
|
|
179
|
+
outline-offset: 2px;
|
|
180
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|