@arraypress/waveform-player 1.12.1 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/waveform-player.cjs +71 -0
- package/dist/waveform-player.cjs.map +2 -2
- package/dist/waveform-player.esm.js +3 -3
- package/dist/waveform-player.esm.js.map +3 -3
- package/dist/waveform-player.js +71 -0
- package/dist/waveform-player.min.js +3 -3
- package/dist/waveform-player.min.js.map +3 -3
- package/package.json +1 -1
- package/src/js/core.js +77 -1
package/package.json
CHANGED
package/src/js/core.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
escapeHtml
|
|
17
17
|
} from './utils.js';
|
|
18
18
|
|
|
19
|
-
import {DEFAULT_OPTIONS, STYLE_DEFAULTS, getColorPreset} from './themes.js';
|
|
19
|
+
import {DEFAULT_OPTIONS, STYLE_DEFAULTS, getColorPreset, COLOR_PRESETS} from './themes.js';
|
|
20
20
|
|
|
21
21
|
// Keyboard seek steps (seconds) for the accessible slider.
|
|
22
22
|
const SEEK_STEP_SECONDS = 5;
|
|
@@ -75,10 +75,17 @@ export class WaveformPlayer {
|
|
|
75
75
|
// Apply color preset (auto-detect if not specified)
|
|
76
76
|
const preset = getColorPreset(this.options.colorPreset);
|
|
77
77
|
|
|
78
|
+
// Track auto-theme state so the player can re-detect on a runtime light/
|
|
79
|
+
// dark switch. Only colours the user LEFT UNSET follow the preset; an
|
|
80
|
+
// explicit colorPreset or hand-set colour is never overridden.
|
|
81
|
+
this._autoTheme = this.options.colorPreset == null || !COLOR_PRESETS[this.options.colorPreset];
|
|
82
|
+
this._presetKeys = [];
|
|
83
|
+
|
|
78
84
|
// Apply preset colors only if individual colors aren't explicitly set
|
|
79
85
|
for (const [key, value] of Object.entries(preset)) {
|
|
80
86
|
if (this.options[key] === null || this.options[key] === undefined) {
|
|
81
87
|
this.options[key] = value;
|
|
88
|
+
this._presetKeys.push(key);
|
|
82
89
|
}
|
|
83
90
|
}
|
|
84
91
|
|
|
@@ -116,6 +123,9 @@ export class WaveformPlayer {
|
|
|
116
123
|
// Add to instances
|
|
117
124
|
WaveformPlayer.instances.set(this.id, this);
|
|
118
125
|
|
|
126
|
+
// Re-detect the theme on runtime light/dark switches (shared watcher).
|
|
127
|
+
WaveformPlayer._watchTheme();
|
|
128
|
+
|
|
119
129
|
// Initialize
|
|
120
130
|
this.init();
|
|
121
131
|
|
|
@@ -1388,6 +1398,72 @@ export class WaveformPlayer {
|
|
|
1388
1398
|
}
|
|
1389
1399
|
}
|
|
1390
1400
|
|
|
1401
|
+
/**
|
|
1402
|
+
* Re-detect the page theme and re-apply auto colours + redraw, so an
|
|
1403
|
+
* auto-themed player adapts to a runtime light/dark switch (not just the
|
|
1404
|
+
* value present on load). No-op when the player uses an explicit
|
|
1405
|
+
* `colorPreset` or hand-set colours. Driven by the shared theme watcher.
|
|
1406
|
+
* @public
|
|
1407
|
+
*/
|
|
1408
|
+
refreshTheme() {
|
|
1409
|
+
if (!this._autoTheme || !this._presetKeys || !this._presetKeys.length) return;
|
|
1410
|
+
const preset = getColorPreset(this.options.colorPreset);
|
|
1411
|
+
let changed = false;
|
|
1412
|
+
for (const key of this._presetKeys) {
|
|
1413
|
+
if (this.options[key] !== preset[key]) {
|
|
1414
|
+
this.options[key] = preset[key];
|
|
1415
|
+
changed = true;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
if (changed) this._applyThemeColors();
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
/**
|
|
1422
|
+
* Push the current resolved colours onto the live DOM (button border/icon,
|
|
1423
|
+
* title, subtitle, time, BPM) and redraw the waveform.
|
|
1424
|
+
* @private
|
|
1425
|
+
*/
|
|
1426
|
+
_applyThemeColors() {
|
|
1427
|
+
const o = this.options;
|
|
1428
|
+
if (this.playBtn) {
|
|
1429
|
+
this.playBtn.style.borderColor = o.buttonColor;
|
|
1430
|
+
this.playBtn.style.color = o.buttonColor;
|
|
1431
|
+
}
|
|
1432
|
+
if (this.titleEl) this.titleEl.style.color = o.textColor;
|
|
1433
|
+
if (this.subtitleEl) this.subtitleEl.style.color = o.textSecondaryColor;
|
|
1434
|
+
this.container.querySelectorAll('.waveform-time, .waveform-bpm').forEach((el) => {
|
|
1435
|
+
el.style.color = o.textSecondaryColor;
|
|
1436
|
+
});
|
|
1437
|
+
if (this.canvas) this.drawWaveform();
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
/**
|
|
1441
|
+
* Lazily install ONE shared watcher that re-detects the theme for every
|
|
1442
|
+
* auto-themed instance when the document theme changes — a class/attribute
|
|
1443
|
+
* flip on `<html>`/`<body>` (Tailwind `dark`, `data-theme`,
|
|
1444
|
+
* `data-color-scheme`) or an OS `prefers-color-scheme` change. Event-driven
|
|
1445
|
+
* (MutationObserver + matchMedia), never a timer.
|
|
1446
|
+
* @private
|
|
1447
|
+
*/
|
|
1448
|
+
static _watchTheme() {
|
|
1449
|
+
if (WaveformPlayer._themeWatch || typeof document === 'undefined') return;
|
|
1450
|
+
const refresh = () => requestAnimationFrame(() => {
|
|
1451
|
+
WaveformPlayer.instances.forEach((p) => {
|
|
1452
|
+
try { p.refreshTheme(); } catch (e) { /* ignore */ }
|
|
1453
|
+
});
|
|
1454
|
+
});
|
|
1455
|
+
const opts = {attributes: true, attributeFilter: ['class', 'data-theme', 'data-color-scheme', 'style']};
|
|
1456
|
+
const obs = new MutationObserver(refresh);
|
|
1457
|
+
obs.observe(document.documentElement, opts);
|
|
1458
|
+
if (document.body) obs.observe(document.body, opts);
|
|
1459
|
+
let mq = null;
|
|
1460
|
+
try {
|
|
1461
|
+
mq = window.matchMedia('(prefers-color-scheme: dark)');
|
|
1462
|
+
mq.addEventListener('change', refresh);
|
|
1463
|
+
} catch (e) { /* no matchMedia */ }
|
|
1464
|
+
WaveformPlayer._themeWatch = {obs, mq, refresh};
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1391
1467
|
/**
|
|
1392
1468
|
* Sync the speed control's label and the menu's active-option highlight to
|
|
1393
1469
|
* the audio element's current `playbackRate`. No-op in external mode (no
|