sceyt-chat-react-uikit 1.8.0-beta.6 → 1.8.0-beta.7
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/index.js +566 -1239
- package/index.modern.js +566 -1239
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -15,6 +15,8 @@ var LinkifyIt = _interopDefault(require('linkify-it'));
|
|
|
15
15
|
var Cropper = _interopDefault(require('react-easy-crop'));
|
|
16
16
|
var Carousel = _interopDefault(require('react-elastic-carousel'));
|
|
17
17
|
var reactCircularProgressbar = require('react-circular-progressbar');
|
|
18
|
+
var ffmpeg = require('@ffmpeg/ffmpeg');
|
|
19
|
+
var util = require('@ffmpeg/util');
|
|
18
20
|
var lexical = require('lexical');
|
|
19
21
|
var LexicalComposerContext = require('@lexical/react/LexicalComposerContext');
|
|
20
22
|
var LexicalComposer = require('@lexical/react/LexicalComposer');
|
|
@@ -32188,16 +32190,15 @@ class EventEmitter {
|
|
|
32188
32190
|
if (!this.listeners[event]) {
|
|
32189
32191
|
this.listeners[event] = new Set();
|
|
32190
32192
|
}
|
|
32193
|
+
this.listeners[event].add(listener);
|
|
32191
32194
|
if (options === null || options === void 0 ? void 0 : options.once) {
|
|
32192
|
-
|
|
32193
|
-
|
|
32194
|
-
this.un(event,
|
|
32195
|
-
listener(...args);
|
|
32195
|
+
const unsubscribeOnce = () => {
|
|
32196
|
+
this.un(event, unsubscribeOnce);
|
|
32197
|
+
this.un(event, listener);
|
|
32196
32198
|
};
|
|
32197
|
-
this.
|
|
32198
|
-
return
|
|
32199
|
+
this.on(event, unsubscribeOnce);
|
|
32200
|
+
return unsubscribeOnce;
|
|
32199
32201
|
}
|
|
32200
|
-
this.listeners[event].add(listener);
|
|
32201
32202
|
return () => this.un(event, listener);
|
|
32202
32203
|
}
|
|
32203
32204
|
/** Unsubscribe from an event */
|
|
@@ -32267,13 +32268,8 @@ var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _argume
|
|
|
32267
32268
|
function decode(audioData, sampleRate) {
|
|
32268
32269
|
return __awaiter(this, void 0, void 0, function* () {
|
|
32269
32270
|
const audioCtx = new AudioContext({ sampleRate });
|
|
32270
|
-
|
|
32271
|
-
|
|
32272
|
-
}
|
|
32273
|
-
finally {
|
|
32274
|
-
// Ensure AudioContext is always closed, even on synchronous errors
|
|
32275
|
-
audioCtx.close();
|
|
32276
|
-
}
|
|
32271
|
+
const decode = audioCtx.decodeAudioData(audioData);
|
|
32272
|
+
return decode.finally(() => audioCtx.close());
|
|
32277
32273
|
});
|
|
32278
32274
|
}
|
|
32279
32275
|
/** Normalize peaks to -1..1 */
|
|
@@ -32297,36 +32293,17 @@ function normalize(channelData) {
|
|
|
32297
32293
|
}
|
|
32298
32294
|
/** Create an audio buffer from pre-decoded audio data */
|
|
32299
32295
|
function createBuffer(channelData, duration) {
|
|
32300
|
-
// Validate inputs
|
|
32301
|
-
if (!channelData || channelData.length === 0) {
|
|
32302
|
-
throw new Error('channelData must be a non-empty array');
|
|
32303
|
-
}
|
|
32304
|
-
if (duration <= 0) {
|
|
32305
|
-
throw new Error('duration must be greater than 0');
|
|
32306
|
-
}
|
|
32307
32296
|
// If a single array of numbers is passed, make it an array of arrays
|
|
32308
32297
|
if (typeof channelData[0] === 'number')
|
|
32309
32298
|
channelData = [channelData];
|
|
32310
|
-
// Validate channel data after conversion
|
|
32311
|
-
if (!channelData[0] || channelData[0].length === 0) {
|
|
32312
|
-
throw new Error('channelData must contain non-empty channel arrays');
|
|
32313
|
-
}
|
|
32314
32299
|
// Normalize to -1..1
|
|
32315
32300
|
normalize(channelData);
|
|
32316
|
-
// Convert to Float32Array for consistency
|
|
32317
|
-
const float32Channels = channelData.map((channel) => channel instanceof Float32Array ? channel : Float32Array.from(channel));
|
|
32318
32301
|
return {
|
|
32319
32302
|
duration,
|
|
32320
|
-
length:
|
|
32321
|
-
sampleRate:
|
|
32322
|
-
numberOfChannels:
|
|
32323
|
-
getChannelData: (i) =>
|
|
32324
|
-
const channel = float32Channels[i];
|
|
32325
|
-
if (!channel) {
|
|
32326
|
-
throw new Error(`Channel ${i} not found`);
|
|
32327
|
-
}
|
|
32328
|
-
return channel;
|
|
32329
|
-
},
|
|
32303
|
+
length: channelData[0].length,
|
|
32304
|
+
sampleRate: channelData[0].length / duration,
|
|
32305
|
+
numberOfChannels: channelData.length,
|
|
32306
|
+
getChannelData: (i) => channelData === null || channelData === void 0 ? void 0 : channelData[i],
|
|
32330
32307
|
copyFromChannel: AudioBuffer.prototype.copyFromChannel,
|
|
32331
32308
|
copyToChannel: AudioBuffer.prototype.copyToChannel,
|
|
32332
32309
|
};
|
|
@@ -32395,26 +32372,28 @@ function watchProgress(response, progressCallback) {
|
|
|
32395
32372
|
const contentLength = Number(response.headers.get('Content-Length')) || 0;
|
|
32396
32373
|
let receivedLength = 0;
|
|
32397
32374
|
// Process the data
|
|
32398
|
-
const processChunk = (value) => {
|
|
32375
|
+
const processChunk = (value) => __awaiter$1(this, void 0, void 0, function* () {
|
|
32399
32376
|
// Add to the received length
|
|
32400
32377
|
receivedLength += (value === null || value === void 0 ? void 0 : value.length) || 0;
|
|
32401
32378
|
const percentage = Math.round((receivedLength / contentLength) * 100);
|
|
32402
32379
|
progressCallback(percentage);
|
|
32403
|
-
};
|
|
32404
|
-
|
|
32405
|
-
|
|
32406
|
-
|
|
32407
|
-
|
|
32408
|
-
|
|
32409
|
-
|
|
32410
|
-
|
|
32380
|
+
});
|
|
32381
|
+
const read = () => __awaiter$1(this, void 0, void 0, function* () {
|
|
32382
|
+
let data;
|
|
32383
|
+
try {
|
|
32384
|
+
data = yield reader.read();
|
|
32385
|
+
}
|
|
32386
|
+
catch (_a) {
|
|
32387
|
+
// Ignore errors because we can only handle the main response
|
|
32388
|
+
return;
|
|
32389
|
+
}
|
|
32390
|
+
// Continue reading data until done
|
|
32391
|
+
if (!data.done) {
|
|
32411
32392
|
processChunk(data.value);
|
|
32393
|
+
yield read();
|
|
32412
32394
|
}
|
|
32413
|
-
}
|
|
32414
|
-
|
|
32415
|
-
// Ignore errors because we can only handle the main response
|
|
32416
|
-
console.warn('Progress tracking error:', err);
|
|
32417
|
-
}
|
|
32395
|
+
});
|
|
32396
|
+
read();
|
|
32418
32397
|
});
|
|
32419
32398
|
}
|
|
32420
32399
|
function fetchBlob(url, progressCallback, requestInit) {
|
|
@@ -32433,121 +32412,6 @@ const Fetcher = {
|
|
|
32433
32412
|
fetchBlob,
|
|
32434
32413
|
};
|
|
32435
32414
|
|
|
32436
|
-
/**
|
|
32437
|
-
* Reactive primitives for managing state in WaveSurfer
|
|
32438
|
-
*
|
|
32439
|
-
* This module provides signal-based reactivity similar to SolidJS signals.
|
|
32440
|
-
* Signals are reactive values that notify subscribers when they change.
|
|
32441
|
-
*/
|
|
32442
|
-
/**
|
|
32443
|
-
* Create a reactive signal that notifies subscribers when its value changes
|
|
32444
|
-
*
|
|
32445
|
-
* @example
|
|
32446
|
-
* ```typescript
|
|
32447
|
-
* const count = signal(0)
|
|
32448
|
-
* count.subscribe(val => console.log('Count:', val))
|
|
32449
|
-
* count.set(5) // Logs: Count: 5
|
|
32450
|
-
* ```
|
|
32451
|
-
*/
|
|
32452
|
-
function signal(initialValue) {
|
|
32453
|
-
let _value = initialValue;
|
|
32454
|
-
const subscribers = new Set();
|
|
32455
|
-
return {
|
|
32456
|
-
get value() {
|
|
32457
|
-
return _value;
|
|
32458
|
-
},
|
|
32459
|
-
set(newValue) {
|
|
32460
|
-
// Only update and notify if value actually changed
|
|
32461
|
-
if (!Object.is(_value, newValue)) {
|
|
32462
|
-
_value = newValue;
|
|
32463
|
-
subscribers.forEach((fn) => fn(_value));
|
|
32464
|
-
}
|
|
32465
|
-
},
|
|
32466
|
-
update(fn) {
|
|
32467
|
-
this.set(fn(_value));
|
|
32468
|
-
},
|
|
32469
|
-
subscribe(callback) {
|
|
32470
|
-
subscribers.add(callback);
|
|
32471
|
-
return () => subscribers.delete(callback);
|
|
32472
|
-
},
|
|
32473
|
-
};
|
|
32474
|
-
}
|
|
32475
|
-
/**
|
|
32476
|
-
* Create a computed value that automatically updates when its dependencies change
|
|
32477
|
-
*
|
|
32478
|
-
* @example
|
|
32479
|
-
* ```typescript
|
|
32480
|
-
* const count = signal(0)
|
|
32481
|
-
* const doubled = computed(() => count.value * 2, [count])
|
|
32482
|
-
* console.log(doubled.value) // 0
|
|
32483
|
-
* count.set(5)
|
|
32484
|
-
* console.log(doubled.value) // 10
|
|
32485
|
-
* ```
|
|
32486
|
-
*/
|
|
32487
|
-
function computed(fn, dependencies) {
|
|
32488
|
-
const result = signal(fn());
|
|
32489
|
-
// Subscribe to all dependencies immediately
|
|
32490
|
-
// This ensures the computed value stays in sync even if no one is subscribed to it
|
|
32491
|
-
dependencies.forEach((dep) => dep.subscribe(() => {
|
|
32492
|
-
const newValue = fn();
|
|
32493
|
-
// Update the result signal, which will notify our subscribers if value changed
|
|
32494
|
-
if (!Object.is(result.value, newValue)) {
|
|
32495
|
-
result.set(newValue);
|
|
32496
|
-
}
|
|
32497
|
-
}));
|
|
32498
|
-
// Return a read-only signal that proxies the result
|
|
32499
|
-
return {
|
|
32500
|
-
get value() {
|
|
32501
|
-
return result.value;
|
|
32502
|
-
},
|
|
32503
|
-
subscribe(callback) {
|
|
32504
|
-
// Just subscribe to result changes
|
|
32505
|
-
return result.subscribe(callback);
|
|
32506
|
-
},
|
|
32507
|
-
};
|
|
32508
|
-
}
|
|
32509
|
-
/**
|
|
32510
|
-
* Run a side effect automatically when dependencies change
|
|
32511
|
-
*
|
|
32512
|
-
* @param fn - Effect function. Can return a cleanup function.
|
|
32513
|
-
* @param dependencies - Signals that trigger the effect when they change
|
|
32514
|
-
* @returns Unsubscribe function that stops the effect and runs cleanup
|
|
32515
|
-
*
|
|
32516
|
-
* @example
|
|
32517
|
-
* ```typescript
|
|
32518
|
-
* const count = signal(0)
|
|
32519
|
-
* effect(() => {
|
|
32520
|
-
* console.log('Count is:', count.value)
|
|
32521
|
-
* return () => console.log('Cleanup')
|
|
32522
|
-
* }, [count])
|
|
32523
|
-
* count.set(5) // Logs: Cleanup, Count is: 5
|
|
32524
|
-
* ```
|
|
32525
|
-
*/
|
|
32526
|
-
function effect(fn, dependencies) {
|
|
32527
|
-
let cleanup;
|
|
32528
|
-
const run = () => {
|
|
32529
|
-
// Run cleanup from previous execution
|
|
32530
|
-
if (cleanup) {
|
|
32531
|
-
cleanup();
|
|
32532
|
-
cleanup = undefined;
|
|
32533
|
-
}
|
|
32534
|
-
// Run effect and capture new cleanup
|
|
32535
|
-
cleanup = fn();
|
|
32536
|
-
};
|
|
32537
|
-
// Subscribe to all dependencies
|
|
32538
|
-
const unsubscribes = dependencies.map((dep) => dep.subscribe(run));
|
|
32539
|
-
// Run effect immediately
|
|
32540
|
-
run();
|
|
32541
|
-
// Return function that unsubscribes and runs cleanup
|
|
32542
|
-
return () => {
|
|
32543
|
-
if (cleanup) {
|
|
32544
|
-
cleanup();
|
|
32545
|
-
cleanup = undefined;
|
|
32546
|
-
}
|
|
32547
|
-
unsubscribes.forEach((unsub) => unsub());
|
|
32548
|
-
};
|
|
32549
|
-
}
|
|
32550
|
-
|
|
32551
32415
|
var __awaiter$2 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
32552
32416
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
32553
32417
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -32558,33 +32422,9 @@ var __awaiter$2 = (undefined && undefined.__awaiter) || function (thisArg, _argu
|
|
|
32558
32422
|
});
|
|
32559
32423
|
};
|
|
32560
32424
|
class Player extends EventEmitter {
|
|
32561
|
-
// Expose reactive state as writable signals
|
|
32562
|
-
// These are writable to allow WaveSurfer to compose them into centralized state
|
|
32563
|
-
get isPlayingSignal() {
|
|
32564
|
-
return this._isPlaying;
|
|
32565
|
-
}
|
|
32566
|
-
get currentTimeSignal() {
|
|
32567
|
-
return this._currentTime;
|
|
32568
|
-
}
|
|
32569
|
-
get durationSignal() {
|
|
32570
|
-
return this._duration;
|
|
32571
|
-
}
|
|
32572
|
-
get volumeSignal() {
|
|
32573
|
-
return this._volume;
|
|
32574
|
-
}
|
|
32575
|
-
get mutedSignal() {
|
|
32576
|
-
return this._muted;
|
|
32577
|
-
}
|
|
32578
|
-
get playbackRateSignal() {
|
|
32579
|
-
return this._playbackRate;
|
|
32580
|
-
}
|
|
32581
|
-
get seekingSignal() {
|
|
32582
|
-
return this._seeking;
|
|
32583
|
-
}
|
|
32584
32425
|
constructor(options) {
|
|
32585
32426
|
super();
|
|
32586
32427
|
this.isExternalMedia = false;
|
|
32587
|
-
this.reactiveMediaEventCleanups = [];
|
|
32588
32428
|
if (options.media) {
|
|
32589
32429
|
this.media = options.media;
|
|
32590
32430
|
this.isExternalMedia = true;
|
|
@@ -32592,16 +32432,6 @@ class Player extends EventEmitter {
|
|
|
32592
32432
|
else {
|
|
32593
32433
|
this.media = document.createElement('audio');
|
|
32594
32434
|
}
|
|
32595
|
-
// Initialize reactive state
|
|
32596
|
-
this._isPlaying = signal(false);
|
|
32597
|
-
this._currentTime = signal(0);
|
|
32598
|
-
this._duration = signal(0);
|
|
32599
|
-
this._volume = signal(this.media.volume);
|
|
32600
|
-
this._muted = signal(this.media.muted);
|
|
32601
|
-
this._playbackRate = signal(this.media.playbackRate || 1);
|
|
32602
|
-
this._seeking = signal(false);
|
|
32603
|
-
// Setup reactive media event handlers
|
|
32604
|
-
this.setupReactiveMediaEvents();
|
|
32605
32435
|
// Controls
|
|
32606
32436
|
if (options.mediaControls) {
|
|
32607
32437
|
this.media.controls = true;
|
|
@@ -32619,48 +32449,6 @@ class Player extends EventEmitter {
|
|
|
32619
32449
|
}, { once: true });
|
|
32620
32450
|
}
|
|
32621
32451
|
}
|
|
32622
|
-
/**
|
|
32623
|
-
* Setup reactive media event handlers that update signals
|
|
32624
|
-
* This bridges the imperative HTMLMediaElement API to reactive state
|
|
32625
|
-
*/
|
|
32626
|
-
setupReactiveMediaEvents() {
|
|
32627
|
-
// Playing state
|
|
32628
|
-
this.reactiveMediaEventCleanups.push(this.onMediaEvent('play', () => {
|
|
32629
|
-
this._isPlaying.set(true);
|
|
32630
|
-
}));
|
|
32631
|
-
this.reactiveMediaEventCleanups.push(this.onMediaEvent('pause', () => {
|
|
32632
|
-
this._isPlaying.set(false);
|
|
32633
|
-
}));
|
|
32634
|
-
this.reactiveMediaEventCleanups.push(this.onMediaEvent('ended', () => {
|
|
32635
|
-
this._isPlaying.set(false);
|
|
32636
|
-
}));
|
|
32637
|
-
// Time tracking
|
|
32638
|
-
this.reactiveMediaEventCleanups.push(this.onMediaEvent('timeupdate', () => {
|
|
32639
|
-
this._currentTime.set(this.media.currentTime);
|
|
32640
|
-
}));
|
|
32641
|
-
this.reactiveMediaEventCleanups.push(this.onMediaEvent('durationchange', () => {
|
|
32642
|
-
this._duration.set(this.media.duration || 0);
|
|
32643
|
-
}));
|
|
32644
|
-
this.reactiveMediaEventCleanups.push(this.onMediaEvent('loadedmetadata', () => {
|
|
32645
|
-
this._duration.set(this.media.duration || 0);
|
|
32646
|
-
}));
|
|
32647
|
-
// Seeking state
|
|
32648
|
-
this.reactiveMediaEventCleanups.push(this.onMediaEvent('seeking', () => {
|
|
32649
|
-
this._seeking.set(true);
|
|
32650
|
-
}));
|
|
32651
|
-
this.reactiveMediaEventCleanups.push(this.onMediaEvent('seeked', () => {
|
|
32652
|
-
this._seeking.set(false);
|
|
32653
|
-
}));
|
|
32654
|
-
// Volume and muted
|
|
32655
|
-
this.reactiveMediaEventCleanups.push(this.onMediaEvent('volumechange', () => {
|
|
32656
|
-
this._volume.set(this.media.volume);
|
|
32657
|
-
this._muted.set(this.media.muted);
|
|
32658
|
-
}));
|
|
32659
|
-
// Playback rate
|
|
32660
|
-
this.reactiveMediaEventCleanups.push(this.onMediaEvent('ratechange', () => {
|
|
32661
|
-
this._playbackRate.set(this.media.playbackRate);
|
|
32662
|
-
}));
|
|
32663
|
-
}
|
|
32664
32452
|
onMediaEvent(event, callback, options) {
|
|
32665
32453
|
this.media.addEventListener(event, callback, options);
|
|
32666
32454
|
return () => this.media.removeEventListener(event, callback, options);
|
|
@@ -32697,27 +32485,17 @@ class Player extends EventEmitter {
|
|
|
32697
32485
|
}
|
|
32698
32486
|
}
|
|
32699
32487
|
destroy() {
|
|
32700
|
-
// Cleanup reactive media event listeners
|
|
32701
|
-
this.reactiveMediaEventCleanups.forEach((cleanup) => cleanup());
|
|
32702
|
-
this.reactiveMediaEventCleanups = [];
|
|
32703
32488
|
if (this.isExternalMedia)
|
|
32704
32489
|
return;
|
|
32705
32490
|
this.media.pause();
|
|
32491
|
+
this.media.remove();
|
|
32706
32492
|
this.revokeSrc();
|
|
32707
32493
|
this.media.removeAttribute('src');
|
|
32708
32494
|
// Load resets the media element to its initial state
|
|
32709
32495
|
this.media.load();
|
|
32710
|
-
// Remove from DOM after cleanup
|
|
32711
|
-
this.media.remove();
|
|
32712
32496
|
}
|
|
32713
32497
|
setMediaElement(element) {
|
|
32714
|
-
// Cleanup reactive event listeners from old media element
|
|
32715
|
-
this.reactiveMediaEventCleanups.forEach((cleanup) => cleanup());
|
|
32716
|
-
this.reactiveMediaEventCleanups = [];
|
|
32717
|
-
// Set new media element
|
|
32718
32498
|
this.media = element;
|
|
32719
|
-
// Reinitialize reactive event listeners on new media element
|
|
32720
|
-
this.setupReactiveMediaEvents();
|
|
32721
32499
|
}
|
|
32722
32500
|
/** Start playing the audio */
|
|
32723
32501
|
play() {
|
|
@@ -32797,312 +32575,23 @@ class Player extends EventEmitter {
|
|
|
32797
32575
|
}
|
|
32798
32576
|
}
|
|
32799
32577
|
|
|
32800
|
-
|
|
32801
|
-
|
|
32802
|
-
|
|
32803
|
-
function clampToUnit(value) {
|
|
32804
|
-
if (value < 0)
|
|
32805
|
-
return 0;
|
|
32806
|
-
if (value > 1)
|
|
32807
|
-
return 1;
|
|
32808
|
-
return value;
|
|
32809
|
-
}
|
|
32810
|
-
function calculateBarRenderConfig({ width, height, length, options, pixelRatio, }) {
|
|
32811
|
-
const halfHeight = height / 2;
|
|
32812
|
-
const barWidth = options.barWidth ? options.barWidth * pixelRatio : 1;
|
|
32813
|
-
const barGap = options.barGap ? options.barGap * pixelRatio : options.barWidth ? barWidth / 2 : 0;
|
|
32814
|
-
const barRadius = options.barRadius || 0;
|
|
32815
|
-
const barMinHeight = options.barMinHeight ? options.barMinHeight * pixelRatio : 0;
|
|
32816
|
-
const spacing = barWidth + barGap || 1;
|
|
32817
|
-
const barIndexScale = length > 0 ? width / spacing / length : 0;
|
|
32818
|
-
return {
|
|
32819
|
-
halfHeight,
|
|
32820
|
-
barWidth,
|
|
32821
|
-
barGap,
|
|
32822
|
-
barRadius,
|
|
32823
|
-
barMinHeight,
|
|
32824
|
-
barIndexScale,
|
|
32825
|
-
barSpacing: spacing,
|
|
32826
|
-
};
|
|
32827
|
-
}
|
|
32828
|
-
function calculateBarHeights({ maxTop, maxBottom, halfHeight, vScale, barMinHeight = 0, barAlign, }) {
|
|
32829
|
-
let topHeight = Math.round(maxTop * halfHeight * vScale);
|
|
32830
|
-
const bottomHeight = Math.round(maxBottom * halfHeight * vScale);
|
|
32831
|
-
let totalHeight = topHeight + bottomHeight || 1;
|
|
32832
|
-
if (totalHeight < barMinHeight) {
|
|
32833
|
-
totalHeight = barMinHeight;
|
|
32834
|
-
if (!barAlign) {
|
|
32835
|
-
topHeight = totalHeight / 2;
|
|
32836
|
-
}
|
|
32837
|
-
}
|
|
32838
|
-
return { topHeight, totalHeight };
|
|
32839
|
-
}
|
|
32840
|
-
function resolveBarYPosition({ barAlign, halfHeight, topHeight, totalHeight, canvasHeight, }) {
|
|
32841
|
-
if (barAlign === 'top')
|
|
32842
|
-
return 0;
|
|
32843
|
-
if (barAlign === 'bottom')
|
|
32844
|
-
return canvasHeight - totalHeight;
|
|
32845
|
-
return halfHeight - topHeight;
|
|
32846
|
-
}
|
|
32847
|
-
function calculateBarSegments({ channelData, barIndexScale, barSpacing, barWidth, halfHeight, vScale, canvasHeight, barAlign, barMinHeight, }) {
|
|
32848
|
-
const topChannel = channelData[0] || [];
|
|
32849
|
-
const bottomChannel = channelData[1] || topChannel;
|
|
32850
|
-
const length = topChannel.length;
|
|
32851
|
-
const segments = [];
|
|
32852
|
-
let prevX = 0;
|
|
32853
|
-
let maxTop = 0;
|
|
32854
|
-
let maxBottom = 0;
|
|
32855
|
-
for (let i = 0; i <= length; i++) {
|
|
32856
|
-
const x = Math.round(i * barIndexScale);
|
|
32857
|
-
if (x > prevX) {
|
|
32858
|
-
const { topHeight, totalHeight } = calculateBarHeights({
|
|
32859
|
-
maxTop,
|
|
32860
|
-
maxBottom,
|
|
32861
|
-
halfHeight,
|
|
32862
|
-
vScale,
|
|
32863
|
-
barMinHeight,
|
|
32864
|
-
barAlign,
|
|
32865
|
-
});
|
|
32866
|
-
const y = resolveBarYPosition({
|
|
32867
|
-
barAlign,
|
|
32868
|
-
halfHeight,
|
|
32869
|
-
topHeight,
|
|
32870
|
-
totalHeight,
|
|
32871
|
-
canvasHeight,
|
|
32872
|
-
});
|
|
32873
|
-
segments.push({
|
|
32874
|
-
x: prevX * barSpacing,
|
|
32875
|
-
y,
|
|
32876
|
-
width: barWidth,
|
|
32877
|
-
height: totalHeight,
|
|
32878
|
-
});
|
|
32879
|
-
prevX = x;
|
|
32880
|
-
maxTop = 0;
|
|
32881
|
-
maxBottom = 0;
|
|
32882
|
-
}
|
|
32883
|
-
const magnitudeTop = Math.abs(topChannel[i] || 0);
|
|
32884
|
-
const magnitudeBottom = Math.abs(bottomChannel[i] || 0);
|
|
32885
|
-
if (magnitudeTop > maxTop)
|
|
32886
|
-
maxTop = magnitudeTop;
|
|
32887
|
-
if (magnitudeBottom > maxBottom)
|
|
32888
|
-
maxBottom = magnitudeBottom;
|
|
32889
|
-
}
|
|
32890
|
-
return segments;
|
|
32891
|
-
}
|
|
32892
|
-
function getRelativePointerPosition(rect, clientX, clientY) {
|
|
32893
|
-
const x = clientX - rect.left;
|
|
32894
|
-
const y = clientY - rect.top;
|
|
32895
|
-
const relativeX = x / rect.width;
|
|
32896
|
-
const relativeY = y / rect.height;
|
|
32897
|
-
return [relativeX, relativeY];
|
|
32898
|
-
}
|
|
32899
|
-
function resolveChannelHeight({ optionsHeight, optionsSplitChannels, parentHeight, numberOfChannels, defaultHeight = DEFAULT_HEIGHT, }) {
|
|
32900
|
-
if (optionsHeight == null)
|
|
32901
|
-
return defaultHeight;
|
|
32902
|
-
const numericHeight = Number(optionsHeight);
|
|
32903
|
-
if (!isNaN(numericHeight))
|
|
32904
|
-
return numericHeight;
|
|
32905
|
-
if (optionsHeight === 'auto') {
|
|
32906
|
-
const height = parentHeight || defaultHeight;
|
|
32907
|
-
if (optionsSplitChannels === null || optionsSplitChannels === void 0 ? void 0 : optionsSplitChannels.every((channel) => !channel.overlay)) {
|
|
32908
|
-
return height / numberOfChannels;
|
|
32909
|
-
}
|
|
32910
|
-
return height;
|
|
32911
|
-
}
|
|
32912
|
-
return defaultHeight;
|
|
32913
|
-
}
|
|
32914
|
-
function getPixelRatio(devicePixelRatio) {
|
|
32915
|
-
return Math.max(1, devicePixelRatio || 1);
|
|
32916
|
-
}
|
|
32917
|
-
function shouldRenderBars(options) {
|
|
32918
|
-
return Boolean(options.barWidth || options.barGap || options.barAlign);
|
|
32919
|
-
}
|
|
32920
|
-
function resolveColorValue(color, devicePixelRatio, canvasHeight) {
|
|
32921
|
-
if (!Array.isArray(color))
|
|
32922
|
-
return color || '';
|
|
32923
|
-
if (color.length === 0)
|
|
32924
|
-
return '#999';
|
|
32925
|
-
if (color.length < 2)
|
|
32926
|
-
return color[0] || '';
|
|
32927
|
-
const canvasElement = document.createElement('canvas');
|
|
32928
|
-
const ctx = canvasElement.getContext('2d');
|
|
32929
|
-
const gradientHeight = canvasHeight !== null && canvasHeight !== void 0 ? canvasHeight : canvasElement.height * devicePixelRatio;
|
|
32930
|
-
const gradient = ctx.createLinearGradient(0, 0, 0, gradientHeight || devicePixelRatio);
|
|
32931
|
-
const colorStopPercentage = 1 / (color.length - 1);
|
|
32932
|
-
color.forEach((value, index) => {
|
|
32933
|
-
gradient.addColorStop(index * colorStopPercentage, value);
|
|
32934
|
-
});
|
|
32935
|
-
return gradient;
|
|
32936
|
-
}
|
|
32937
|
-
function calculateWaveformLayout({ duration, minPxPerSec = 0, parentWidth, fillParent, pixelRatio, }) {
|
|
32938
|
-
const scrollWidth = Math.ceil(duration * minPxPerSec);
|
|
32939
|
-
const isScrollable = scrollWidth > parentWidth;
|
|
32940
|
-
const useParentWidth = Boolean(fillParent && !isScrollable);
|
|
32941
|
-
const width = (useParentWidth ? parentWidth : scrollWidth) * pixelRatio;
|
|
32942
|
-
return {
|
|
32943
|
-
scrollWidth,
|
|
32944
|
-
isScrollable,
|
|
32945
|
-
useParentWidth,
|
|
32946
|
-
width,
|
|
32947
|
-
};
|
|
32948
|
-
}
|
|
32949
|
-
function clampWidthToBarGrid(width, options) {
|
|
32950
|
-
if (!shouldRenderBars(options))
|
|
32951
|
-
return width;
|
|
32952
|
-
const barWidth = options.barWidth || 0.5;
|
|
32953
|
-
const barGap = options.barGap || barWidth / 2;
|
|
32954
|
-
const totalBarWidth = barWidth + barGap;
|
|
32955
|
-
if (totalBarWidth === 0)
|
|
32956
|
-
return width;
|
|
32957
|
-
return Math.floor(width / totalBarWidth) * totalBarWidth;
|
|
32958
|
-
}
|
|
32959
|
-
function calculateSingleCanvasWidth({ clientWidth, totalWidth, options, }) {
|
|
32960
|
-
const baseWidth = Math.min(MAX_CANVAS_WIDTH, clientWidth, totalWidth);
|
|
32961
|
-
return clampWidthToBarGrid(baseWidth, options);
|
|
32962
|
-
}
|
|
32963
|
-
function sliceChannelData({ channelData, offset, clampedWidth, totalWidth, }) {
|
|
32964
|
-
return channelData.map((channel) => {
|
|
32965
|
-
const start = Math.floor((offset / totalWidth) * channel.length);
|
|
32966
|
-
const end = Math.floor(((offset + clampedWidth) / totalWidth) * channel.length);
|
|
32967
|
-
return channel.slice(start, end);
|
|
32968
|
-
});
|
|
32969
|
-
}
|
|
32970
|
-
function shouldClearCanvases(currentNodeCount) {
|
|
32971
|
-
return currentNodeCount > MAX_NODES;
|
|
32972
|
-
}
|
|
32973
|
-
function getLazyRenderRange({ scrollLeft, totalWidth, numCanvases, }) {
|
|
32974
|
-
if (totalWidth === 0)
|
|
32975
|
-
return [0];
|
|
32976
|
-
const viewPosition = scrollLeft / totalWidth;
|
|
32977
|
-
const startCanvas = Math.floor(viewPosition * numCanvases);
|
|
32978
|
-
return [startCanvas - 1, startCanvas, startCanvas + 1];
|
|
32979
|
-
}
|
|
32980
|
-
function calculateVerticalScale({ channelData, barHeight, normalize, maxPeak, }) {
|
|
32981
|
-
var _a;
|
|
32982
|
-
const baseScale = barHeight || 1;
|
|
32983
|
-
if (!normalize)
|
|
32984
|
-
return baseScale;
|
|
32985
|
-
const firstChannel = channelData[0];
|
|
32986
|
-
if (!firstChannel || firstChannel.length === 0)
|
|
32987
|
-
return baseScale;
|
|
32988
|
-
// Use fixed max peak if provided, otherwise calculate from data
|
|
32989
|
-
let max = maxPeak !== null && maxPeak !== void 0 ? maxPeak : 0;
|
|
32990
|
-
if (!maxPeak) {
|
|
32991
|
-
for (let i = 0; i < firstChannel.length; i++) {
|
|
32992
|
-
const value = (_a = firstChannel[i]) !== null && _a !== void 0 ? _a : 0;
|
|
32993
|
-
const magnitude = Math.abs(value);
|
|
32994
|
-
if (magnitude > max)
|
|
32995
|
-
max = magnitude;
|
|
32996
|
-
}
|
|
32997
|
-
}
|
|
32998
|
-
if (!max)
|
|
32999
|
-
return baseScale;
|
|
33000
|
-
return baseScale / max;
|
|
33001
|
-
}
|
|
33002
|
-
function calculateLinePaths({ channelData, width, height, vScale, }) {
|
|
33003
|
-
const halfHeight = height / 2;
|
|
33004
|
-
const primaryChannel = channelData[0] || [];
|
|
33005
|
-
const secondaryChannel = channelData[1] || primaryChannel;
|
|
33006
|
-
const channels = [primaryChannel, secondaryChannel];
|
|
33007
|
-
return channels.map((channel, index) => {
|
|
33008
|
-
const length = channel.length;
|
|
33009
|
-
const hScale = length ? width / length : 0;
|
|
33010
|
-
const baseY = halfHeight;
|
|
33011
|
-
const direction = index === 0 ? -1 : 1;
|
|
33012
|
-
const path = [{ x: 0, y: baseY }];
|
|
33013
|
-
let prevX = 0;
|
|
33014
|
-
let max = 0;
|
|
33015
|
-
for (let i = 0; i <= length; i++) {
|
|
33016
|
-
const x = Math.round(i * hScale);
|
|
33017
|
-
if (x > prevX) {
|
|
33018
|
-
const heightDelta = Math.round(max * halfHeight * vScale) || 1;
|
|
33019
|
-
const y = baseY + heightDelta * direction;
|
|
33020
|
-
path.push({ x: prevX, y });
|
|
33021
|
-
prevX = x;
|
|
33022
|
-
max = 0;
|
|
33023
|
-
}
|
|
33024
|
-
const value = Math.abs(channel[i] || 0);
|
|
33025
|
-
if (value > max)
|
|
33026
|
-
max = value;
|
|
33027
|
-
}
|
|
33028
|
-
path.push({ x: prevX, y: baseY });
|
|
33029
|
-
return path;
|
|
33030
|
-
});
|
|
33031
|
-
}
|
|
33032
|
-
function roundToHalfAwayFromZero(value) {
|
|
33033
|
-
const scaled = value * 2;
|
|
33034
|
-
const rounded = scaled < 0 ? Math.floor(scaled) : Math.ceil(scaled);
|
|
33035
|
-
return rounded / 2;
|
|
33036
|
-
}
|
|
33037
|
-
|
|
33038
|
-
/**
|
|
33039
|
-
* Event stream utilities for converting DOM events to reactive signals
|
|
33040
|
-
*
|
|
33041
|
-
* These utilities allow composing event handling using reactive primitives.
|
|
33042
|
-
*/
|
|
33043
|
-
/**
|
|
33044
|
-
* Cleanup a stream created with event stream utilities
|
|
33045
|
-
*
|
|
33046
|
-
* This removes event listeners and unsubscribes from sources.
|
|
33047
|
-
*/
|
|
33048
|
-
function cleanup(stream) {
|
|
33049
|
-
const cleanupFn = stream._cleanup;
|
|
33050
|
-
if (typeof cleanupFn === 'function') {
|
|
33051
|
-
cleanupFn();
|
|
33052
|
-
}
|
|
33053
|
-
}
|
|
33054
|
-
|
|
33055
|
-
/**
|
|
33056
|
-
* Reactive drag stream utilities
|
|
33057
|
-
*
|
|
33058
|
-
* Provides declarative drag handling using reactive streams.
|
|
33059
|
-
* Automatically handles mouseup cleanup and supports constraints.
|
|
33060
|
-
*/
|
|
33061
|
-
/**
|
|
33062
|
-
* Create a reactive drag stream from an element
|
|
33063
|
-
*
|
|
33064
|
-
* Emits drag events (start, move, end) as the user drags the element.
|
|
33065
|
-
* Automatically handles pointer capture, multi-touch prevention, and cleanup.
|
|
33066
|
-
*
|
|
33067
|
-
* @example
|
|
33068
|
-
* ```typescript
|
|
33069
|
-
* const dragSignal = createDragStream(element)
|
|
33070
|
-
*
|
|
33071
|
-
* effect(() => {
|
|
33072
|
-
* const drag = dragSignal.value
|
|
33073
|
-
* if (drag?.type === 'move') {
|
|
33074
|
-
* console.log('Dragging:', drag.deltaX, drag.deltaY)
|
|
33075
|
-
* }
|
|
33076
|
-
* }, [dragSignal])
|
|
33077
|
-
* ```
|
|
33078
|
-
*
|
|
33079
|
-
* @param element - Element to make draggable
|
|
33080
|
-
* @param options - Drag configuration options
|
|
33081
|
-
* @returns Signal emitting drag events and cleanup function
|
|
33082
|
-
*/
|
|
33083
|
-
function createDragStream(element, options = {}) {
|
|
33084
|
-
const { threshold = 3, mouseButton = 0, touchDelay = 100 } = options;
|
|
33085
|
-
const dragSignal = signal(null);
|
|
33086
|
-
const activePointers = new Map();
|
|
32578
|
+
function makeDraggable(element, onDrag, onStart, onEnd, threshold = 3, mouseButton = 0, touchDelay = 100) {
|
|
32579
|
+
if (!element)
|
|
32580
|
+
return () => void 0;
|
|
33087
32581
|
const isTouchDevice = matchMedia('(pointer: coarse)').matches;
|
|
33088
32582
|
let unsubscribeDocument = () => void 0;
|
|
33089
32583
|
const onPointerDown = (event) => {
|
|
33090
32584
|
if (event.button !== mouseButton)
|
|
33091
32585
|
return;
|
|
33092
|
-
|
|
33093
|
-
|
|
33094
|
-
return;
|
|
33095
|
-
}
|
|
32586
|
+
event.preventDefault();
|
|
32587
|
+
event.stopPropagation();
|
|
33096
32588
|
let startX = event.clientX;
|
|
33097
32589
|
let startY = event.clientY;
|
|
33098
32590
|
let isDragging = false;
|
|
33099
32591
|
const touchStartTime = Date.now();
|
|
33100
|
-
const rect = element.getBoundingClientRect();
|
|
33101
|
-
const { left, top } = rect;
|
|
33102
32592
|
const onPointerMove = (event) => {
|
|
33103
|
-
|
|
33104
|
-
|
|
33105
|
-
}
|
|
32593
|
+
event.preventDefault();
|
|
32594
|
+
event.stopPropagation();
|
|
33106
32595
|
if (isTouchDevice && Date.now() - touchStartTime < touchDelay)
|
|
33107
32596
|
return;
|
|
33108
32597
|
const x = event.clientX;
|
|
@@ -33110,45 +32599,29 @@ function createDragStream(element, options = {}) {
|
|
|
33110
32599
|
const dx = x - startX;
|
|
33111
32600
|
const dy = y - startY;
|
|
33112
32601
|
if (isDragging || Math.abs(dx) > threshold || Math.abs(dy) > threshold) {
|
|
33113
|
-
|
|
33114
|
-
|
|
32602
|
+
const rect = element.getBoundingClientRect();
|
|
32603
|
+
const { left, top } = rect;
|
|
33115
32604
|
if (!isDragging) {
|
|
33116
|
-
|
|
33117
|
-
dragSignal.set({
|
|
33118
|
-
type: 'start',
|
|
33119
|
-
x: startX - left,
|
|
33120
|
-
y: startY - top,
|
|
33121
|
-
});
|
|
32605
|
+
onStart === null || onStart === void 0 ? void 0 : onStart(startX - left, startY - top);
|
|
33122
32606
|
isDragging = true;
|
|
33123
32607
|
}
|
|
33124
|
-
|
|
33125
|
-
dragSignal.set({
|
|
33126
|
-
type: 'move',
|
|
33127
|
-
x: x - left,
|
|
33128
|
-
y: y - top,
|
|
33129
|
-
deltaX: dx,
|
|
33130
|
-
deltaY: dy,
|
|
33131
|
-
});
|
|
32608
|
+
onDrag(dx, dy, x - left, y - top);
|
|
33132
32609
|
startX = x;
|
|
33133
32610
|
startY = y;
|
|
33134
32611
|
}
|
|
33135
32612
|
};
|
|
33136
32613
|
const onPointerUp = (event) => {
|
|
33137
|
-
activePointers.delete(event.pointerId);
|
|
33138
32614
|
if (isDragging) {
|
|
33139
32615
|
const x = event.clientX;
|
|
33140
32616
|
const y = event.clientY;
|
|
33141
|
-
|
|
33142
|
-
|
|
33143
|
-
|
|
33144
|
-
x: x - left,
|
|
33145
|
-
y: y - top,
|
|
33146
|
-
});
|
|
32617
|
+
const rect = element.getBoundingClientRect();
|
|
32618
|
+
const { left, top } = rect;
|
|
32619
|
+
onEnd === null || onEnd === void 0 ? void 0 : onEnd(x - left, y - top);
|
|
33147
32620
|
}
|
|
33148
32621
|
unsubscribeDocument();
|
|
33149
32622
|
};
|
|
33150
32623
|
const onPointerLeave = (e) => {
|
|
33151
|
-
|
|
32624
|
+
// Listen to events only on the document and not on inner elements
|
|
33152
32625
|
if (!e.relatedTarget || e.relatedTarget === document.documentElement) {
|
|
33153
32626
|
onPointerUp(e);
|
|
33154
32627
|
}
|
|
@@ -33160,9 +32633,6 @@ function createDragStream(element, options = {}) {
|
|
|
33160
32633
|
}
|
|
33161
32634
|
};
|
|
33162
32635
|
const onTouchMove = (event) => {
|
|
33163
|
-
if (event.defaultPrevented || activePointers.size > 1) {
|
|
33164
|
-
return;
|
|
33165
|
-
}
|
|
33166
32636
|
if (isDragging) {
|
|
33167
32637
|
event.preventDefault();
|
|
33168
32638
|
}
|
|
@@ -33185,114 +32655,9 @@ function createDragStream(element, options = {}) {
|
|
|
33185
32655
|
};
|
|
33186
32656
|
};
|
|
33187
32657
|
element.addEventListener('pointerdown', onPointerDown);
|
|
33188
|
-
|
|
32658
|
+
return () => {
|
|
33189
32659
|
unsubscribeDocument();
|
|
33190
32660
|
element.removeEventListener('pointerdown', onPointerDown);
|
|
33191
|
-
activePointers.clear();
|
|
33192
|
-
cleanup(dragSignal);
|
|
33193
|
-
};
|
|
33194
|
-
return {
|
|
33195
|
-
signal: dragSignal,
|
|
33196
|
-
cleanup: cleanupFn,
|
|
33197
|
-
};
|
|
33198
|
-
}
|
|
33199
|
-
|
|
33200
|
-
/**
|
|
33201
|
-
* Reactive scroll stream utilities
|
|
33202
|
-
*
|
|
33203
|
-
* Provides declarative scroll handling using reactive streams.
|
|
33204
|
-
* Automatically handles scroll event optimization and cleanup.
|
|
33205
|
-
*/
|
|
33206
|
-
// ============================================================================
|
|
33207
|
-
// Pure Scroll Calculation Functions
|
|
33208
|
-
// ============================================================================
|
|
33209
|
-
/**
|
|
33210
|
-
* Calculate visible percentages from scroll data
|
|
33211
|
-
* Pure function - no side effects
|
|
33212
|
-
*
|
|
33213
|
-
* @param scrollData - Current scroll dimensions
|
|
33214
|
-
* @returns Start and end positions as percentages (0-1)
|
|
33215
|
-
*/
|
|
33216
|
-
function calculateScrollPercentages(scrollData) {
|
|
33217
|
-
const { scrollLeft, scrollWidth, clientWidth } = scrollData;
|
|
33218
|
-
if (scrollWidth === 0) {
|
|
33219
|
-
return { startX: 0, endX: 1 };
|
|
33220
|
-
}
|
|
33221
|
-
const startX = scrollLeft / scrollWidth;
|
|
33222
|
-
const endX = (scrollLeft + clientWidth) / scrollWidth;
|
|
33223
|
-
return {
|
|
33224
|
-
startX: Math.max(0, Math.min(1, startX)),
|
|
33225
|
-
endX: Math.max(0, Math.min(1, endX)),
|
|
33226
|
-
};
|
|
33227
|
-
}
|
|
33228
|
-
/**
|
|
33229
|
-
* Calculate scroll bounds in pixels
|
|
33230
|
-
* Pure function - no side effects
|
|
33231
|
-
*
|
|
33232
|
-
* @param scrollData - Current scroll dimensions
|
|
33233
|
-
* @returns Left and right scroll bounds in pixels
|
|
33234
|
-
*/
|
|
33235
|
-
function calculateScrollBounds(scrollData) {
|
|
33236
|
-
return {
|
|
33237
|
-
left: scrollData.scrollLeft,
|
|
33238
|
-
right: scrollData.scrollLeft + scrollData.clientWidth,
|
|
33239
|
-
};
|
|
33240
|
-
}
|
|
33241
|
-
/**
|
|
33242
|
-
* Create a reactive scroll stream from an element
|
|
33243
|
-
*
|
|
33244
|
-
* Emits scroll data as the user scrolls the element.
|
|
33245
|
-
* Automatically computes derived values (percentages, bounds).
|
|
33246
|
-
*
|
|
33247
|
-
* @example
|
|
33248
|
-
* ```typescript
|
|
33249
|
-
* const scrollStream = createScrollStream(container)
|
|
33250
|
-
*
|
|
33251
|
-
* effect(() => {
|
|
33252
|
-
* const { startX, endX } = scrollStream.percentages.value
|
|
33253
|
-
* console.log('Visible:', startX, 'to', endX)
|
|
33254
|
-
* }, [scrollStream.percentages])
|
|
33255
|
-
*
|
|
33256
|
-
* scrollStream.cleanup()
|
|
33257
|
-
* ```
|
|
33258
|
-
*
|
|
33259
|
-
* @param element - Scrollable element
|
|
33260
|
-
* @returns Scroll stream with signals and cleanup
|
|
33261
|
-
*/
|
|
33262
|
-
function createScrollStream(element) {
|
|
33263
|
-
// Create signals
|
|
33264
|
-
const scrollData = signal({
|
|
33265
|
-
scrollLeft: element.scrollLeft,
|
|
33266
|
-
scrollWidth: element.scrollWidth,
|
|
33267
|
-
clientWidth: element.clientWidth,
|
|
33268
|
-
});
|
|
33269
|
-
// Computed derived values
|
|
33270
|
-
const percentages = computed(() => {
|
|
33271
|
-
return calculateScrollPercentages(scrollData.value);
|
|
33272
|
-
}, [scrollData]);
|
|
33273
|
-
const bounds = computed(() => {
|
|
33274
|
-
return calculateScrollBounds(scrollData.value);
|
|
33275
|
-
}, [scrollData]);
|
|
33276
|
-
// Update scroll data on scroll event
|
|
33277
|
-
const onScroll = () => {
|
|
33278
|
-
scrollData.set({
|
|
33279
|
-
scrollLeft: element.scrollLeft,
|
|
33280
|
-
scrollWidth: element.scrollWidth,
|
|
33281
|
-
clientWidth: element.clientWidth,
|
|
33282
|
-
});
|
|
33283
|
-
};
|
|
33284
|
-
// Attach scroll listener
|
|
33285
|
-
element.addEventListener('scroll', onScroll, { passive: true });
|
|
33286
|
-
// Cleanup function
|
|
33287
|
-
const cleanupFn = () => {
|
|
33288
|
-
element.removeEventListener('scroll', onScroll);
|
|
33289
|
-
cleanup(scrollData);
|
|
33290
|
-
};
|
|
33291
|
-
return {
|
|
33292
|
-
scrollData,
|
|
33293
|
-
percentages,
|
|
33294
|
-
bounds,
|
|
33295
|
-
cleanup: cleanupFn,
|
|
33296
32661
|
};
|
|
33297
32662
|
}
|
|
33298
32663
|
|
|
@@ -33327,8 +32692,6 @@ class Renderer extends EventEmitter {
|
|
|
33327
32692
|
this.isDragging = false;
|
|
33328
32693
|
this.subscriptions = [];
|
|
33329
32694
|
this.unsubscribeOnScroll = [];
|
|
33330
|
-
this.dragStream = null;
|
|
33331
|
-
this.scrollStream = null;
|
|
33332
32695
|
this.subscriptions = [];
|
|
33333
32696
|
this.options = options;
|
|
33334
32697
|
const parent = this.parentFromOptionsContainer(options.container);
|
|
@@ -33360,30 +32723,35 @@ class Renderer extends EventEmitter {
|
|
|
33360
32723
|
return parent;
|
|
33361
32724
|
}
|
|
33362
32725
|
initEvents() {
|
|
32726
|
+
const getClickPosition = (e) => {
|
|
32727
|
+
const rect = this.wrapper.getBoundingClientRect();
|
|
32728
|
+
const x = e.clientX - rect.left;
|
|
32729
|
+
const y = e.clientY - rect.top;
|
|
32730
|
+
const relativeX = x / rect.width;
|
|
32731
|
+
const relativeY = y / rect.height;
|
|
32732
|
+
return [relativeX, relativeY];
|
|
32733
|
+
};
|
|
33363
32734
|
// Add a click listener
|
|
33364
32735
|
this.wrapper.addEventListener('click', (e) => {
|
|
33365
|
-
const
|
|
33366
|
-
const [x, y] = getRelativePointerPosition(rect, e.clientX, e.clientY);
|
|
32736
|
+
const [x, y] = getClickPosition(e);
|
|
33367
32737
|
this.emit('click', x, y);
|
|
33368
32738
|
});
|
|
33369
32739
|
// Add a double click listener
|
|
33370
32740
|
this.wrapper.addEventListener('dblclick', (e) => {
|
|
33371
|
-
const
|
|
33372
|
-
const [x, y] = getRelativePointerPosition(rect, e.clientX, e.clientY);
|
|
32741
|
+
const [x, y] = getClickPosition(e);
|
|
33373
32742
|
this.emit('dblclick', x, y);
|
|
33374
32743
|
});
|
|
33375
32744
|
// Drag
|
|
33376
32745
|
if (this.options.dragToSeek === true || typeof this.options.dragToSeek === 'object') {
|
|
33377
32746
|
this.initDrag();
|
|
33378
32747
|
}
|
|
33379
|
-
// Add a scroll listener
|
|
33380
|
-
this.
|
|
33381
|
-
|
|
33382
|
-
const
|
|
33383
|
-
const
|
|
33384
|
-
this.emit('scroll', startX, endX,
|
|
33385
|
-
}
|
|
33386
|
-
this.subscriptions.push(unsubscribeScroll);
|
|
32748
|
+
// Add a scroll listener
|
|
32749
|
+
this.scrollContainer.addEventListener('scroll', () => {
|
|
32750
|
+
const { scrollLeft, scrollWidth, clientWidth } = this.scrollContainer;
|
|
32751
|
+
const startX = scrollLeft / scrollWidth;
|
|
32752
|
+
const endX = (scrollLeft + clientWidth) / scrollWidth;
|
|
32753
|
+
this.emit('scroll', startX, endX, scrollLeft, scrollLeft + clientWidth);
|
|
32754
|
+
});
|
|
33387
32755
|
// Re-render the waveform on container resize
|
|
33388
32756
|
if (typeof ResizeObserver === 'function') {
|
|
33389
32757
|
const delay = this.createDelay(100);
|
|
@@ -33401,32 +32769,39 @@ class Renderer extends EventEmitter {
|
|
|
33401
32769
|
return;
|
|
33402
32770
|
this.lastContainerWidth = width;
|
|
33403
32771
|
this.reRender();
|
|
33404
|
-
this.emit('resize');
|
|
33405
32772
|
}
|
|
33406
32773
|
initDrag() {
|
|
33407
|
-
|
|
33408
|
-
|
|
33409
|
-
|
|
33410
|
-
|
|
33411
|
-
|
|
33412
|
-
|
|
33413
|
-
|
|
33414
|
-
|
|
33415
|
-
|
|
33416
|
-
|
|
33417
|
-
|
|
33418
|
-
|
|
33419
|
-
|
|
33420
|
-
|
|
33421
|
-
|
|
33422
|
-
|
|
33423
|
-
|
|
33424
|
-
|
|
33425
|
-
|
|
33426
|
-
|
|
33427
|
-
|
|
33428
|
-
|
|
33429
|
-
|
|
32774
|
+
this.subscriptions.push(makeDraggable(this.wrapper,
|
|
32775
|
+
// On drag
|
|
32776
|
+
(_, __, x) => {
|
|
32777
|
+
this.emit('drag', Math.max(0, Math.min(1, x / this.wrapper.getBoundingClientRect().width)));
|
|
32778
|
+
},
|
|
32779
|
+
// On start drag
|
|
32780
|
+
(x) => {
|
|
32781
|
+
this.isDragging = true;
|
|
32782
|
+
this.emit('dragstart', Math.max(0, Math.min(1, x / this.wrapper.getBoundingClientRect().width)));
|
|
32783
|
+
},
|
|
32784
|
+
// On end drag
|
|
32785
|
+
(x) => {
|
|
32786
|
+
this.isDragging = false;
|
|
32787
|
+
this.emit('dragend', Math.max(0, Math.min(1, x / this.wrapper.getBoundingClientRect().width)));
|
|
32788
|
+
}));
|
|
32789
|
+
}
|
|
32790
|
+
getHeight(optionsHeight, optionsSplitChannel) {
|
|
32791
|
+
var _a;
|
|
32792
|
+
const defaultHeight = 128;
|
|
32793
|
+
const numberOfChannels = ((_a = this.audioData) === null || _a === void 0 ? void 0 : _a.numberOfChannels) || 1;
|
|
32794
|
+
if (optionsHeight == null)
|
|
32795
|
+
return defaultHeight;
|
|
32796
|
+
if (!isNaN(Number(optionsHeight)))
|
|
32797
|
+
return Number(optionsHeight);
|
|
32798
|
+
if (optionsHeight === 'auto') {
|
|
32799
|
+
const height = this.parent.clientHeight || defaultHeight;
|
|
32800
|
+
if (optionsSplitChannel === null || optionsSplitChannel === void 0 ? void 0 : optionsSplitChannel.every((channel) => !channel.overlay))
|
|
32801
|
+
return height / numberOfChannels;
|
|
32802
|
+
return height;
|
|
32803
|
+
}
|
|
32804
|
+
return defaultHeight;
|
|
33430
32805
|
}
|
|
33431
32806
|
initHtml() {
|
|
33432
32807
|
const div = document.createElement('div');
|
|
@@ -33463,7 +32838,6 @@ class Renderer extends EventEmitter {
|
|
|
33463
32838
|
}
|
|
33464
32839
|
:host .canvases {
|
|
33465
32840
|
min-height: ${this.getHeight(this.options.height, this.options.splitChannels)}px;
|
|
33466
|
-
pointer-events: none;
|
|
33467
32841
|
}
|
|
33468
32842
|
:host .canvases > div {
|
|
33469
32843
|
position: relative;
|
|
@@ -33540,134 +32914,150 @@ class Renderer extends EventEmitter {
|
|
|
33540
32914
|
this.setScroll(scrollStart);
|
|
33541
32915
|
}
|
|
33542
32916
|
destroy() {
|
|
33543
|
-
var _a;
|
|
32917
|
+
var _a, _b;
|
|
33544
32918
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
33545
32919
|
this.container.remove();
|
|
33546
|
-
|
|
33547
|
-
|
|
33548
|
-
this.resizeObserver = null;
|
|
33549
|
-
}
|
|
33550
|
-
(_a = this.unsubscribeOnScroll) === null || _a === void 0 ? void 0 : _a.forEach((unsubscribe) => unsubscribe());
|
|
32920
|
+
(_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
32921
|
+
(_b = this.unsubscribeOnScroll) === null || _b === void 0 ? void 0 : _b.forEach((unsubscribe) => unsubscribe());
|
|
33551
32922
|
this.unsubscribeOnScroll = [];
|
|
33552
|
-
if (this.dragStream) {
|
|
33553
|
-
this.dragStream.cleanup();
|
|
33554
|
-
this.dragStream = null;
|
|
33555
|
-
}
|
|
33556
|
-
if (this.scrollStream) {
|
|
33557
|
-
this.scrollStream.cleanup();
|
|
33558
|
-
this.scrollStream = null;
|
|
33559
|
-
}
|
|
33560
32923
|
}
|
|
33561
32924
|
createDelay(delayMs = 10) {
|
|
33562
32925
|
let timeout;
|
|
33563
|
-
let
|
|
32926
|
+
let reject;
|
|
33564
32927
|
const onClear = () => {
|
|
33565
|
-
if (timeout)
|
|
32928
|
+
if (timeout)
|
|
33566
32929
|
clearTimeout(timeout);
|
|
33567
|
-
|
|
33568
|
-
|
|
33569
|
-
if (rejectFn) {
|
|
33570
|
-
rejectFn();
|
|
33571
|
-
rejectFn = undefined;
|
|
33572
|
-
}
|
|
32930
|
+
if (reject)
|
|
32931
|
+
reject();
|
|
33573
32932
|
};
|
|
33574
32933
|
this.timeouts.push(onClear);
|
|
33575
32934
|
return () => {
|
|
33576
|
-
return new Promise((
|
|
33577
|
-
// Clear any pending delay
|
|
32935
|
+
return new Promise((resolveFn, rejectFn) => {
|
|
33578
32936
|
onClear();
|
|
33579
|
-
|
|
33580
|
-
rejectFn = reject;
|
|
33581
|
-
// Set new timeout
|
|
32937
|
+
reject = rejectFn;
|
|
33582
32938
|
timeout = setTimeout(() => {
|
|
33583
32939
|
timeout = undefined;
|
|
33584
|
-
|
|
33585
|
-
|
|
32940
|
+
reject = undefined;
|
|
32941
|
+
resolveFn();
|
|
33586
32942
|
}, delayMs);
|
|
33587
32943
|
});
|
|
33588
32944
|
};
|
|
33589
32945
|
}
|
|
33590
|
-
|
|
33591
|
-
|
|
33592
|
-
|
|
33593
|
-
|
|
33594
|
-
|
|
33595
|
-
|
|
33596
|
-
|
|
33597
|
-
|
|
33598
|
-
|
|
32946
|
+
// Convert array of color values to linear gradient
|
|
32947
|
+
convertColorValues(color) {
|
|
32948
|
+
if (!Array.isArray(color))
|
|
32949
|
+
return color || '';
|
|
32950
|
+
if (color.length < 2)
|
|
32951
|
+
return color[0] || '';
|
|
32952
|
+
const canvasElement = document.createElement('canvas');
|
|
32953
|
+
const ctx = canvasElement.getContext('2d');
|
|
32954
|
+
const gradientHeight = canvasElement.height * (window.devicePixelRatio || 1);
|
|
32955
|
+
const gradient = ctx.createLinearGradient(0, 0, 0, gradientHeight);
|
|
32956
|
+
const colorStopPercentage = 1 / (color.length - 1);
|
|
32957
|
+
color.forEach((color, index) => {
|
|
32958
|
+
const offset = index * colorStopPercentage;
|
|
32959
|
+
gradient.addColorStop(offset, color);
|
|
33599
32960
|
});
|
|
33600
|
-
|
|
33601
|
-
convertColorValues(color, ctx) {
|
|
33602
|
-
return resolveColorValue(color, this.getPixelRatio(), ctx === null || ctx === void 0 ? void 0 : ctx.canvas.height);
|
|
32961
|
+
return gradient;
|
|
33603
32962
|
}
|
|
33604
32963
|
getPixelRatio() {
|
|
33605
|
-
return
|
|
32964
|
+
return Math.max(1, window.devicePixelRatio || 1);
|
|
33606
32965
|
}
|
|
33607
32966
|
renderBarWaveform(channelData, options, ctx, vScale) {
|
|
32967
|
+
const topChannel = channelData[0];
|
|
32968
|
+
const bottomChannel = channelData[1] || channelData[0];
|
|
32969
|
+
const length = topChannel.length;
|
|
33608
32970
|
const { width, height } = ctx.canvas;
|
|
33609
|
-
const
|
|
33610
|
-
|
|
33611
|
-
|
|
33612
|
-
|
|
33613
|
-
|
|
33614
|
-
|
|
33615
|
-
|
|
33616
|
-
const segments = calculateBarSegments({
|
|
33617
|
-
channelData,
|
|
33618
|
-
barIndexScale,
|
|
33619
|
-
barSpacing,
|
|
33620
|
-
barWidth,
|
|
33621
|
-
halfHeight,
|
|
33622
|
-
vScale,
|
|
33623
|
-
canvasHeight: height,
|
|
33624
|
-
barAlign: options.barAlign,
|
|
33625
|
-
barMinHeight,
|
|
33626
|
-
});
|
|
32971
|
+
const halfHeight = height / 2;
|
|
32972
|
+
const pixelRatio = this.getPixelRatio();
|
|
32973
|
+
const barWidth = options.barWidth ? options.barWidth * pixelRatio : 1;
|
|
32974
|
+
const barGap = options.barGap ? options.barGap * pixelRatio : options.barWidth ? barWidth / 2 : 0;
|
|
32975
|
+
const barRadius = options.barRadius || 0;
|
|
32976
|
+
const barIndexScale = width / (barWidth + barGap) / length;
|
|
32977
|
+
const rectFn = barRadius && 'roundRect' in ctx ? 'roundRect' : 'rect';
|
|
33627
32978
|
ctx.beginPath();
|
|
33628
|
-
|
|
33629
|
-
|
|
33630
|
-
|
|
33631
|
-
|
|
33632
|
-
|
|
33633
|
-
|
|
32979
|
+
let prevX = 0;
|
|
32980
|
+
let maxTop = 0;
|
|
32981
|
+
let maxBottom = 0;
|
|
32982
|
+
for (let i = 0; i <= length; i++) {
|
|
32983
|
+
const x = Math.round(i * barIndexScale);
|
|
32984
|
+
if (x > prevX) {
|
|
32985
|
+
const topBarHeight = Math.round(maxTop * halfHeight * vScale);
|
|
32986
|
+
const bottomBarHeight = Math.round(maxBottom * halfHeight * vScale);
|
|
32987
|
+
const barHeight = topBarHeight + bottomBarHeight || 1;
|
|
32988
|
+
// Vertical alignment
|
|
32989
|
+
let y = halfHeight - topBarHeight;
|
|
32990
|
+
if (options.barAlign === 'top') {
|
|
32991
|
+
y = 0;
|
|
32992
|
+
}
|
|
32993
|
+
else if (options.barAlign === 'bottom') {
|
|
32994
|
+
y = height - barHeight;
|
|
32995
|
+
}
|
|
32996
|
+
ctx[rectFn](prevX * (barWidth + barGap), y, barWidth, barHeight, barRadius);
|
|
32997
|
+
prevX = x;
|
|
32998
|
+
maxTop = 0;
|
|
32999
|
+
maxBottom = 0;
|
|
33634
33000
|
}
|
|
33001
|
+
const magnitudeTop = Math.abs(topChannel[i] || 0);
|
|
33002
|
+
const magnitudeBottom = Math.abs(bottomChannel[i] || 0);
|
|
33003
|
+
if (magnitudeTop > maxTop)
|
|
33004
|
+
maxTop = magnitudeTop;
|
|
33005
|
+
if (magnitudeBottom > maxBottom)
|
|
33006
|
+
maxBottom = magnitudeBottom;
|
|
33635
33007
|
}
|
|
33636
33008
|
ctx.fill();
|
|
33637
33009
|
ctx.closePath();
|
|
33638
33010
|
}
|
|
33639
33011
|
renderLineWaveform(channelData, _options, ctx, vScale) {
|
|
33640
|
-
const
|
|
33641
|
-
|
|
33642
|
-
|
|
33643
|
-
|
|
33644
|
-
|
|
33645
|
-
|
|
33646
|
-
ctx.moveTo(
|
|
33647
|
-
|
|
33648
|
-
|
|
33649
|
-
|
|
33012
|
+
const drawChannel = (index) => {
|
|
33013
|
+
const channel = channelData[index] || channelData[0];
|
|
33014
|
+
const length = channel.length;
|
|
33015
|
+
const { height } = ctx.canvas;
|
|
33016
|
+
const halfHeight = height / 2;
|
|
33017
|
+
const hScale = ctx.canvas.width / length;
|
|
33018
|
+
ctx.moveTo(0, halfHeight);
|
|
33019
|
+
let prevX = 0;
|
|
33020
|
+
let max = 0;
|
|
33021
|
+
for (let i = 0; i <= length; i++) {
|
|
33022
|
+
const x = Math.round(i * hScale);
|
|
33023
|
+
if (x > prevX) {
|
|
33024
|
+
const h = Math.round(max * halfHeight * vScale) || 1;
|
|
33025
|
+
const y = halfHeight + h * (index === 0 ? -1 : 1);
|
|
33026
|
+
ctx.lineTo(prevX, y);
|
|
33027
|
+
prevX = x;
|
|
33028
|
+
max = 0;
|
|
33029
|
+
}
|
|
33030
|
+
const value = Math.abs(channel[i] || 0);
|
|
33031
|
+
if (value > max)
|
|
33032
|
+
max = value;
|
|
33650
33033
|
}
|
|
33651
|
-
|
|
33034
|
+
ctx.lineTo(prevX, halfHeight);
|
|
33035
|
+
};
|
|
33036
|
+
ctx.beginPath();
|
|
33037
|
+
drawChannel(0);
|
|
33038
|
+
drawChannel(1);
|
|
33652
33039
|
ctx.fill();
|
|
33653
33040
|
ctx.closePath();
|
|
33654
33041
|
}
|
|
33655
33042
|
renderWaveform(channelData, options, ctx) {
|
|
33656
|
-
ctx.fillStyle = this.convertColorValues(options.waveColor
|
|
33043
|
+
ctx.fillStyle = this.convertColorValues(options.waveColor);
|
|
33044
|
+
// Custom rendering function
|
|
33657
33045
|
if (options.renderFunction) {
|
|
33658
33046
|
options.renderFunction(channelData, ctx);
|
|
33659
33047
|
return;
|
|
33660
33048
|
}
|
|
33661
|
-
|
|
33662
|
-
|
|
33663
|
-
|
|
33664
|
-
|
|
33665
|
-
|
|
33666
|
-
}
|
|
33667
|
-
|
|
33049
|
+
// Vertical scaling
|
|
33050
|
+
let vScale = options.barHeight || 1;
|
|
33051
|
+
if (options.normalize) {
|
|
33052
|
+
const max = Array.from(channelData[0]).reduce((max, value) => Math.max(max, Math.abs(value)), 0);
|
|
33053
|
+
vScale = max ? 1 / max : 1;
|
|
33054
|
+
}
|
|
33055
|
+
// Render waveform as bars
|
|
33056
|
+
if (options.barWidth || options.barGap || options.barAlign) {
|
|
33668
33057
|
this.renderBarWaveform(channelData, options, ctx, vScale);
|
|
33669
33058
|
return;
|
|
33670
33059
|
}
|
|
33060
|
+
// Render waveform as a polyline
|
|
33671
33061
|
this.renderLineWaveform(channelData, options, ctx, vScale);
|
|
33672
33062
|
}
|
|
33673
33063
|
renderSingleCanvas(data, options, width, height, offset, canvasContainer, progressContainer) {
|
|
@@ -33680,13 +33070,7 @@ class Renderer extends EventEmitter {
|
|
|
33680
33070
|
canvas.style.left = `${Math.round(offset)}px`;
|
|
33681
33071
|
canvasContainer.appendChild(canvas);
|
|
33682
33072
|
const ctx = canvas.getContext('2d');
|
|
33683
|
-
|
|
33684
|
-
ctx.fillStyle = this.convertColorValues(options.waveColor, ctx);
|
|
33685
|
-
options.renderFunction(data, ctx);
|
|
33686
|
-
}
|
|
33687
|
-
else {
|
|
33688
|
-
this.renderWaveform(data, options, ctx);
|
|
33689
|
-
}
|
|
33073
|
+
this.renderWaveform(data, options, ctx);
|
|
33690
33074
|
// Draw a progress canvas
|
|
33691
33075
|
if (canvas.width > 0 && canvas.height > 0) {
|
|
33692
33076
|
const progressCanvas = canvas.cloneNode();
|
|
@@ -33694,7 +33078,7 @@ class Renderer extends EventEmitter {
|
|
|
33694
33078
|
progressCtx.drawImage(canvas, 0, 0);
|
|
33695
33079
|
// Set the composition method to draw only where the waveform is drawn
|
|
33696
33080
|
progressCtx.globalCompositeOperation = 'source-in';
|
|
33697
|
-
progressCtx.fillStyle = this.convertColorValues(options.progressColor
|
|
33081
|
+
progressCtx.fillStyle = this.convertColorValues(options.progressColor);
|
|
33698
33082
|
// This rectangle acts as a mask thanks to the composition method
|
|
33699
33083
|
progressCtx.fillRect(0, 0, canvas.width, canvas.height);
|
|
33700
33084
|
progressContainer.appendChild(progressCanvas);
|
|
@@ -33704,8 +33088,17 @@ class Renderer extends EventEmitter {
|
|
|
33704
33088
|
const pixelRatio = this.getPixelRatio();
|
|
33705
33089
|
const { clientWidth } = this.scrollContainer;
|
|
33706
33090
|
const totalWidth = width / pixelRatio;
|
|
33707
|
-
|
|
33091
|
+
let singleCanvasWidth = Math.min(Renderer.MAX_CANVAS_WIDTH, clientWidth, totalWidth);
|
|
33708
33092
|
let drawnIndexes = {};
|
|
33093
|
+
// Adjust width to avoid gaps between canvases when using bars
|
|
33094
|
+
if (options.barWidth || options.barGap) {
|
|
33095
|
+
const barWidth = options.barWidth || 0.5;
|
|
33096
|
+
const barGap = options.barGap || barWidth / 2;
|
|
33097
|
+
const totalBarWidth = barWidth + barGap;
|
|
33098
|
+
if (singleCanvasWidth % totalBarWidth !== 0) {
|
|
33099
|
+
singleCanvasWidth = Math.floor(singleCanvasWidth / totalBarWidth) * totalBarWidth;
|
|
33100
|
+
}
|
|
33101
|
+
}
|
|
33709
33102
|
// Nothing to render
|
|
33710
33103
|
if (singleCanvasWidth === 0)
|
|
33711
33104
|
return;
|
|
@@ -33719,15 +33112,24 @@ class Renderer extends EventEmitter {
|
|
|
33719
33112
|
const offset = index * singleCanvasWidth;
|
|
33720
33113
|
let clampedWidth = Math.min(totalWidth - offset, singleCanvasWidth);
|
|
33721
33114
|
// Clamp the width to the bar grid to avoid empty canvases at the end
|
|
33722
|
-
|
|
33115
|
+
if (options.barWidth || options.barGap) {
|
|
33116
|
+
const barWidth = options.barWidth || 0.5;
|
|
33117
|
+
const barGap = options.barGap || barWidth / 2;
|
|
33118
|
+
const totalBarWidth = barWidth + barGap;
|
|
33119
|
+
clampedWidth = Math.floor(clampedWidth / totalBarWidth) * totalBarWidth;
|
|
33120
|
+
}
|
|
33723
33121
|
if (clampedWidth <= 0)
|
|
33724
33122
|
return;
|
|
33725
|
-
const data =
|
|
33123
|
+
const data = channelData.map((channel) => {
|
|
33124
|
+
const start = Math.floor((offset / totalWidth) * channel.length);
|
|
33125
|
+
const end = Math.floor(((offset + clampedWidth) / totalWidth) * channel.length);
|
|
33126
|
+
return channel.slice(start, end);
|
|
33127
|
+
});
|
|
33726
33128
|
this.renderSingleCanvas(data, options, clampedWidth, height, offset, canvasContainer, progressContainer);
|
|
33727
33129
|
};
|
|
33728
33130
|
// Clear canvases to avoid too many DOM nodes
|
|
33729
33131
|
const clearCanvases = () => {
|
|
33730
|
-
if (
|
|
33132
|
+
if (Object.keys(drawnIndexes).length > Renderer.MAX_NODES) {
|
|
33731
33133
|
canvasContainer.innerHTML = '';
|
|
33732
33134
|
progressContainer.innerHTML = '';
|
|
33733
33135
|
drawnIndexes = {};
|
|
@@ -33743,18 +33145,21 @@ class Renderer extends EventEmitter {
|
|
|
33743
33145
|
return;
|
|
33744
33146
|
}
|
|
33745
33147
|
// Lazy rendering
|
|
33746
|
-
const
|
|
33747
|
-
|
|
33748
|
-
|
|
33749
|
-
|
|
33750
|
-
|
|
33751
|
-
|
|
33148
|
+
const viewPosition = this.scrollContainer.scrollLeft / totalWidth;
|
|
33149
|
+
const startCanvas = Math.floor(viewPosition * numCanvases);
|
|
33150
|
+
// Draw the canvases in the viewport first
|
|
33151
|
+
draw(startCanvas - 1);
|
|
33152
|
+
draw(startCanvas);
|
|
33153
|
+
draw(startCanvas + 1);
|
|
33752
33154
|
// Subscribe to the scroll event to draw additional canvases
|
|
33753
33155
|
if (numCanvases > 1) {
|
|
33754
33156
|
const unsubscribe = this.on('scroll', () => {
|
|
33755
33157
|
const { scrollLeft } = this.scrollContainer;
|
|
33158
|
+
const canvasIndex = Math.floor((scrollLeft / totalWidth) * numCanvases);
|
|
33756
33159
|
clearCanvases();
|
|
33757
|
-
|
|
33160
|
+
draw(canvasIndex - 1);
|
|
33161
|
+
draw(canvasIndex);
|
|
33162
|
+
draw(canvasIndex + 1);
|
|
33758
33163
|
});
|
|
33759
33164
|
this.unsubscribeOnScroll.push(unsubscribe);
|
|
33760
33165
|
}
|
|
@@ -33793,15 +33198,12 @@ class Renderer extends EventEmitter {
|
|
|
33793
33198
|
// Determine the width of the waveform
|
|
33794
33199
|
const pixelRatio = this.getPixelRatio();
|
|
33795
33200
|
const parentWidth = this.scrollContainer.clientWidth;
|
|
33796
|
-
const
|
|
33797
|
-
duration: audioData.duration,
|
|
33798
|
-
minPxPerSec: this.options.minPxPerSec || 0,
|
|
33799
|
-
parentWidth,
|
|
33800
|
-
fillParent: this.options.fillParent,
|
|
33801
|
-
pixelRatio,
|
|
33802
|
-
});
|
|
33201
|
+
const scrollWidth = Math.ceil(audioData.duration * (this.options.minPxPerSec || 0));
|
|
33803
33202
|
// Whether the container should scroll
|
|
33804
|
-
this.isScrollable =
|
|
33203
|
+
this.isScrollable = scrollWidth > parentWidth;
|
|
33204
|
+
const useParentWidth = this.options.fillParent && !this.isScrollable;
|
|
33205
|
+
// Width of the waveform in pixels
|
|
33206
|
+
const width = (useParentWidth ? parentWidth : scrollWidth) * pixelRatio;
|
|
33805
33207
|
// Set the width of the wrapper
|
|
33806
33208
|
this.wrapper.style.width = useParentWidth ? '100%' : `${scrollWidth}px`;
|
|
33807
33209
|
// Set additional styles
|
|
@@ -33844,7 +33246,12 @@ class Renderer extends EventEmitter {
|
|
|
33844
33246
|
// Adjust the scroll position so that the cursor stays in the same place
|
|
33845
33247
|
if (this.isScrollable && scrollWidth !== this.scrollContainer.scrollWidth) {
|
|
33846
33248
|
const { right: after } = this.progressWrapper.getBoundingClientRect();
|
|
33847
|
-
|
|
33249
|
+
let delta = after - before;
|
|
33250
|
+
// to limit compounding floating-point drift
|
|
33251
|
+
// we need to round to the half px furthest from 0
|
|
33252
|
+
delta *= 2;
|
|
33253
|
+
delta = delta < 0 ? Math.floor(delta) : Math.ceil(delta);
|
|
33254
|
+
delta /= 2;
|
|
33848
33255
|
this.scrollContainer.scrollLeft += delta;
|
|
33849
33256
|
}
|
|
33850
33257
|
}
|
|
@@ -33875,9 +33282,16 @@ class Renderer extends EventEmitter {
|
|
|
33875
33282
|
// Keep the cursor centered when playing
|
|
33876
33283
|
const center = progressWidth - scrollLeft - middle;
|
|
33877
33284
|
if (isPlaying && this.options.autoCenter && center > 0) {
|
|
33878
|
-
this.scrollContainer.scrollLeft += center;
|
|
33285
|
+
this.scrollContainer.scrollLeft += Math.min(center, 10);
|
|
33879
33286
|
}
|
|
33880
33287
|
}
|
|
33288
|
+
// Emit the scroll event
|
|
33289
|
+
{
|
|
33290
|
+
const newScroll = this.scrollContainer.scrollLeft;
|
|
33291
|
+
const startX = newScroll / scrollWidth;
|
|
33292
|
+
const endX = (newScroll + clientWidth) / scrollWidth;
|
|
33293
|
+
this.emit('scroll', startX, endX, newScroll, newScroll + clientWidth);
|
|
33294
|
+
}
|
|
33881
33295
|
}
|
|
33882
33296
|
renderProgress(progress, isPlaying) {
|
|
33883
33297
|
if (isNaN(progress))
|
|
@@ -33889,8 +33303,7 @@ class Renderer extends EventEmitter {
|
|
|
33889
33303
|
this.cursor.style.transform = this.options.cursorWidth
|
|
33890
33304
|
? `translateX(-${progress * this.options.cursorWidth}px)`
|
|
33891
33305
|
: '';
|
|
33892
|
-
|
|
33893
|
-
if (this.isScrollable && this.options.autoScroll && this.audioData && this.audioData.duration > 0) {
|
|
33306
|
+
if (this.isScrollable && this.options.autoScroll) {
|
|
33894
33307
|
this.scrollIntoView(progress, isPlaying);
|
|
33895
33308
|
}
|
|
33896
33309
|
}
|
|
@@ -33921,39 +33334,27 @@ class Renderer extends EventEmitter {
|
|
|
33921
33334
|
});
|
|
33922
33335
|
}
|
|
33923
33336
|
}
|
|
33337
|
+
Renderer.MAX_CANVAS_WIDTH = 8000;
|
|
33338
|
+
Renderer.MAX_NODES = 10;
|
|
33924
33339
|
|
|
33925
33340
|
class Timer extends EventEmitter {
|
|
33926
33341
|
constructor() {
|
|
33927
33342
|
super(...arguments);
|
|
33928
|
-
this.
|
|
33929
|
-
this.isRunning = false;
|
|
33343
|
+
this.unsubscribe = () => undefined;
|
|
33930
33344
|
}
|
|
33931
33345
|
start() {
|
|
33932
|
-
|
|
33933
|
-
|
|
33934
|
-
|
|
33935
|
-
|
|
33936
|
-
|
|
33937
|
-
|
|
33938
|
-
if (!this.isRunning)
|
|
33939
|
-
return;
|
|
33940
|
-
this.emit('tick');
|
|
33941
|
-
// Schedule next frame
|
|
33942
|
-
this.animationFrameId = requestAnimationFrame(tick);
|
|
33943
|
-
};
|
|
33944
|
-
// Start the loop
|
|
33945
|
-
tick();
|
|
33346
|
+
this.unsubscribe = this.on('tick', () => {
|
|
33347
|
+
requestAnimationFrame(() => {
|
|
33348
|
+
this.emit('tick');
|
|
33349
|
+
});
|
|
33350
|
+
});
|
|
33351
|
+
this.emit('tick');
|
|
33946
33352
|
}
|
|
33947
33353
|
stop() {
|
|
33948
|
-
this.
|
|
33949
|
-
// Cancel any pending animation frame
|
|
33950
|
-
if (this.animationFrameId !== null) {
|
|
33951
|
-
cancelAnimationFrame(this.animationFrameId);
|
|
33952
|
-
this.animationFrameId = null;
|
|
33953
|
-
}
|
|
33354
|
+
this.unsubscribe();
|
|
33954
33355
|
}
|
|
33955
33356
|
destroy() {
|
|
33956
|
-
this.
|
|
33357
|
+
this.unsubscribe();
|
|
33957
33358
|
}
|
|
33958
33359
|
}
|
|
33959
33360
|
|
|
@@ -33968,10 +33369,6 @@ var __awaiter$4 = (undefined && undefined.__awaiter) || function (thisArg, _argu
|
|
|
33968
33369
|
};
|
|
33969
33370
|
/**
|
|
33970
33371
|
* A Web Audio buffer player emulating the behavior of an HTML5 Audio element.
|
|
33971
|
-
*
|
|
33972
|
-
* Note: This class does not manage blob: URLs. If you pass a blob: URL to setSrc(),
|
|
33973
|
-
* you are responsible for revoking it when done. The Player class (player.ts) handles
|
|
33974
|
-
* blob URL lifecycle management automatically.
|
|
33975
33372
|
*/
|
|
33976
33373
|
class WebAudioPlayer extends EventEmitter {
|
|
33977
33374
|
constructor(audioContext = new AudioContext()) {
|
|
@@ -34032,21 +33429,14 @@ class WebAudioPlayer extends EventEmitter {
|
|
|
34032
33429
|
this.emit('canplay');
|
|
34033
33430
|
if (this.autoplay)
|
|
34034
33431
|
this.play();
|
|
34035
|
-
})
|
|
34036
|
-
.catch((err) => {
|
|
34037
|
-
// Emit error for proper error handling
|
|
34038
|
-
console.error('WebAudioPlayer load error:', err);
|
|
34039
33432
|
});
|
|
34040
33433
|
}
|
|
34041
33434
|
_play() {
|
|
33435
|
+
var _a;
|
|
34042
33436
|
if (!this.paused)
|
|
34043
33437
|
return;
|
|
34044
33438
|
this.paused = false;
|
|
34045
|
-
|
|
34046
|
-
if (this.bufferNode) {
|
|
34047
|
-
this.bufferNode.onended = null;
|
|
34048
|
-
this.bufferNode.disconnect();
|
|
34049
|
-
}
|
|
33439
|
+
(_a = this.bufferNode) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
34050
33440
|
this.bufferNode = this.audioContext.createBufferSource();
|
|
34051
33441
|
if (this.buffer) {
|
|
34052
33442
|
this.bufferNode.buffer = this.buffer;
|
|
@@ -34202,239 +33592,6 @@ class WebAudioPlayer extends EventEmitter {
|
|
|
34202
33592
|
}
|
|
34203
33593
|
}
|
|
34204
33594
|
|
|
34205
|
-
/**
|
|
34206
|
-
* Centralized reactive state for WaveSurfer
|
|
34207
|
-
*
|
|
34208
|
-
* This module provides a single source of truth for all WaveSurfer state.
|
|
34209
|
-
* State is managed using reactive signals that automatically notify subscribers.
|
|
34210
|
-
*/
|
|
34211
|
-
/**
|
|
34212
|
-
* Create a new WaveSurfer state instance
|
|
34213
|
-
*
|
|
34214
|
-
* @param playerSignals - Optional signals from Player to compose with WaveSurfer state
|
|
34215
|
-
*
|
|
34216
|
-
* @example
|
|
34217
|
-
* ```typescript
|
|
34218
|
-
* // Without Player signals (standalone)
|
|
34219
|
-
* const { state, actions } = createWaveSurferState()
|
|
34220
|
-
*
|
|
34221
|
-
* // With Player signals (composed)
|
|
34222
|
-
* const { state, actions } = createWaveSurferState({
|
|
34223
|
-
* isPlaying: player.isPlayingSignal,
|
|
34224
|
-
* currentTime: player.currentTimeSignal,
|
|
34225
|
-
* // ...
|
|
34226
|
-
* })
|
|
34227
|
-
*
|
|
34228
|
-
* // Read state
|
|
34229
|
-
* console.log(state.isPlaying.value)
|
|
34230
|
-
*
|
|
34231
|
-
* // Update state
|
|
34232
|
-
* actions.setPlaying(true)
|
|
34233
|
-
*
|
|
34234
|
-
* // Subscribe to changes
|
|
34235
|
-
* state.isPlaying.subscribe(playing => {
|
|
34236
|
-
* console.log('Playing:', playing)
|
|
34237
|
-
* })
|
|
34238
|
-
* ```
|
|
34239
|
-
*/
|
|
34240
|
-
function createWaveSurferState(playerSignals) {
|
|
34241
|
-
var _a, _b, _c, _d, _e, _f;
|
|
34242
|
-
// Use Player signals if provided, otherwise create new ones
|
|
34243
|
-
const currentTime = (_a = playerSignals === null || playerSignals === void 0 ? void 0 : playerSignals.currentTime) !== null && _a !== void 0 ? _a : signal(0);
|
|
34244
|
-
const duration = (_b = playerSignals === null || playerSignals === void 0 ? void 0 : playerSignals.duration) !== null && _b !== void 0 ? _b : signal(0);
|
|
34245
|
-
const isPlaying = (_c = playerSignals === null || playerSignals === void 0 ? void 0 : playerSignals.isPlaying) !== null && _c !== void 0 ? _c : signal(false);
|
|
34246
|
-
const isSeeking = (_d = playerSignals === null || playerSignals === void 0 ? void 0 : playerSignals.isSeeking) !== null && _d !== void 0 ? _d : signal(false);
|
|
34247
|
-
const volume = (_e = playerSignals === null || playerSignals === void 0 ? void 0 : playerSignals.volume) !== null && _e !== void 0 ? _e : signal(1);
|
|
34248
|
-
const playbackRate = (_f = playerSignals === null || playerSignals === void 0 ? void 0 : playerSignals.playbackRate) !== null && _f !== void 0 ? _f : signal(1);
|
|
34249
|
-
// WaveSurfer-specific signals (not in Player)
|
|
34250
|
-
const audioBuffer = signal(null);
|
|
34251
|
-
const peaks = signal(null);
|
|
34252
|
-
const url = signal('');
|
|
34253
|
-
const zoom = signal(0);
|
|
34254
|
-
const scrollPosition = signal(0);
|
|
34255
|
-
// Computed values (derived state)
|
|
34256
|
-
const isPaused = computed(() => !isPlaying.value, [isPlaying]);
|
|
34257
|
-
const canPlay = computed(() => audioBuffer.value !== null, [audioBuffer]);
|
|
34258
|
-
const isReady = computed(() => {
|
|
34259
|
-
return canPlay.value && duration.value > 0;
|
|
34260
|
-
}, [canPlay, duration]);
|
|
34261
|
-
const progress = computed(() => currentTime.value, [currentTime]);
|
|
34262
|
-
const progressPercent = computed(() => {
|
|
34263
|
-
return duration.value > 0 ? currentTime.value / duration.value : 0;
|
|
34264
|
-
}, [currentTime, duration]);
|
|
34265
|
-
// Public read-only state
|
|
34266
|
-
const state = {
|
|
34267
|
-
currentTime,
|
|
34268
|
-
duration,
|
|
34269
|
-
isPlaying,
|
|
34270
|
-
isPaused,
|
|
34271
|
-
isSeeking,
|
|
34272
|
-
volume,
|
|
34273
|
-
playbackRate,
|
|
34274
|
-
audioBuffer,
|
|
34275
|
-
peaks,
|
|
34276
|
-
url,
|
|
34277
|
-
zoom,
|
|
34278
|
-
scrollPosition,
|
|
34279
|
-
canPlay,
|
|
34280
|
-
isReady,
|
|
34281
|
-
progress,
|
|
34282
|
-
progressPercent,
|
|
34283
|
-
};
|
|
34284
|
-
// Actions that modify state
|
|
34285
|
-
const actions = {
|
|
34286
|
-
setCurrentTime: (time) => {
|
|
34287
|
-
const clampedTime = Math.max(0, Math.min(duration.value || Infinity, time));
|
|
34288
|
-
currentTime.set(clampedTime);
|
|
34289
|
-
},
|
|
34290
|
-
setDuration: (d) => {
|
|
34291
|
-
duration.set(Math.max(0, d));
|
|
34292
|
-
},
|
|
34293
|
-
setPlaying: (playing) => {
|
|
34294
|
-
isPlaying.set(playing);
|
|
34295
|
-
},
|
|
34296
|
-
setSeeking: (seeking) => {
|
|
34297
|
-
isSeeking.set(seeking);
|
|
34298
|
-
},
|
|
34299
|
-
setVolume: (v) => {
|
|
34300
|
-
const clampedVolume = Math.max(0, Math.min(1, v));
|
|
34301
|
-
volume.set(clampedVolume);
|
|
34302
|
-
},
|
|
34303
|
-
setPlaybackRate: (rate) => {
|
|
34304
|
-
const clampedRate = Math.max(0.1, Math.min(16, rate));
|
|
34305
|
-
playbackRate.set(clampedRate);
|
|
34306
|
-
},
|
|
34307
|
-
setAudioBuffer: (buffer) => {
|
|
34308
|
-
audioBuffer.set(buffer);
|
|
34309
|
-
if (buffer) {
|
|
34310
|
-
duration.set(buffer.duration);
|
|
34311
|
-
}
|
|
34312
|
-
},
|
|
34313
|
-
setPeaks: (p) => {
|
|
34314
|
-
peaks.set(p);
|
|
34315
|
-
},
|
|
34316
|
-
setUrl: (u) => {
|
|
34317
|
-
url.set(u);
|
|
34318
|
-
},
|
|
34319
|
-
setZoom: (z) => {
|
|
34320
|
-
zoom.set(Math.max(0, z));
|
|
34321
|
-
},
|
|
34322
|
-
setScrollPosition: (pos) => {
|
|
34323
|
-
scrollPosition.set(Math.max(0, pos));
|
|
34324
|
-
},
|
|
34325
|
-
};
|
|
34326
|
-
return { state, actions };
|
|
34327
|
-
}
|
|
34328
|
-
|
|
34329
|
-
/**
|
|
34330
|
-
* State-driven event emission utilities
|
|
34331
|
-
*
|
|
34332
|
-
* Automatically emit events when reactive state changes.
|
|
34333
|
-
* Ensures events are always in sync with state and removes manual emit() calls.
|
|
34334
|
-
*/
|
|
34335
|
-
/**
|
|
34336
|
-
* Setup automatic event emission from state changes
|
|
34337
|
-
*
|
|
34338
|
-
* This function subscribes to all relevant state signals and automatically
|
|
34339
|
-
* emits corresponding events when state changes. This ensures:
|
|
34340
|
-
* - Events are always in sync with state
|
|
34341
|
-
* - No manual emit() calls needed
|
|
34342
|
-
* - Can't forget to emit an event
|
|
34343
|
-
* - Clear event sources (state changes)
|
|
34344
|
-
*
|
|
34345
|
-
* @example
|
|
34346
|
-
* ```typescript
|
|
34347
|
-
* const { state } = createWaveSurferState()
|
|
34348
|
-
* const wavesurfer = new WaveSurfer()
|
|
34349
|
-
*
|
|
34350
|
-
* const cleanup = setupStateEventEmission(state, wavesurfer)
|
|
34351
|
-
*
|
|
34352
|
-
* // Now state changes automatically emit events
|
|
34353
|
-
* state.isPlaying.set(true) // → wavesurfer.emit('play')
|
|
34354
|
-
* ```
|
|
34355
|
-
*
|
|
34356
|
-
* @param state - Reactive state to observe
|
|
34357
|
-
* @param emitter - Event emitter to emit events on
|
|
34358
|
-
* @returns Cleanup function that removes all subscriptions
|
|
34359
|
-
*/
|
|
34360
|
-
function setupStateEventEmission(state, emitter) {
|
|
34361
|
-
const cleanups = [];
|
|
34362
|
-
// ============================================================================
|
|
34363
|
-
// Play/Pause Events
|
|
34364
|
-
// ============================================================================
|
|
34365
|
-
// Emit play/pause events when playing state changes
|
|
34366
|
-
cleanups.push(effect(() => {
|
|
34367
|
-
const isPlaying = state.isPlaying.value;
|
|
34368
|
-
emitter.emit(isPlaying ? 'play' : 'pause');
|
|
34369
|
-
}, [state.isPlaying]));
|
|
34370
|
-
// ============================================================================
|
|
34371
|
-
// Time Update Events
|
|
34372
|
-
// ============================================================================
|
|
34373
|
-
// Emit timeupdate when current time changes
|
|
34374
|
-
cleanups.push(effect(() => {
|
|
34375
|
-
const currentTime = state.currentTime.value;
|
|
34376
|
-
emitter.emit('timeupdate', currentTime);
|
|
34377
|
-
// Also emit audioprocess when playing
|
|
34378
|
-
if (state.isPlaying.value) {
|
|
34379
|
-
emitter.emit('audioprocess', currentTime);
|
|
34380
|
-
}
|
|
34381
|
-
}, [state.currentTime, state.isPlaying]));
|
|
34382
|
-
// ============================================================================
|
|
34383
|
-
// Seeking Events
|
|
34384
|
-
// ============================================================================
|
|
34385
|
-
// Emit seeking event when seeking state changes to true
|
|
34386
|
-
cleanups.push(effect(() => {
|
|
34387
|
-
const isSeeking = state.isSeeking.value;
|
|
34388
|
-
if (isSeeking) {
|
|
34389
|
-
emitter.emit('seeking', state.currentTime.value);
|
|
34390
|
-
}
|
|
34391
|
-
}, [state.isSeeking, state.currentTime]));
|
|
34392
|
-
// ============================================================================
|
|
34393
|
-
// Ready Event
|
|
34394
|
-
// ============================================================================
|
|
34395
|
-
// Emit ready when state becomes ready
|
|
34396
|
-
let wasReady = false;
|
|
34397
|
-
cleanups.push(effect(() => {
|
|
34398
|
-
const isReady = state.isReady.value;
|
|
34399
|
-
if (isReady && !wasReady) {
|
|
34400
|
-
wasReady = true;
|
|
34401
|
-
emitter.emit('ready', state.duration.value);
|
|
34402
|
-
}
|
|
34403
|
-
}, [state.isReady, state.duration]));
|
|
34404
|
-
// ============================================================================
|
|
34405
|
-
// Finish Event
|
|
34406
|
-
// ============================================================================
|
|
34407
|
-
// Emit finish when playback ends (reached duration and stopped)
|
|
34408
|
-
let wasPlayingAtEnd = false;
|
|
34409
|
-
cleanups.push(effect(() => {
|
|
34410
|
-
const isPlaying = state.isPlaying.value;
|
|
34411
|
-
const currentTime = state.currentTime.value;
|
|
34412
|
-
const duration = state.duration.value;
|
|
34413
|
-
// Check if we're at the end
|
|
34414
|
-
const isAtEnd = duration > 0 && currentTime >= duration;
|
|
34415
|
-
// Emit finish when we were playing at end and now stopped
|
|
34416
|
-
if (wasPlayingAtEnd && !isPlaying && isAtEnd) {
|
|
34417
|
-
emitter.emit('finish');
|
|
34418
|
-
}
|
|
34419
|
-
// Track if we're playing at the end
|
|
34420
|
-
wasPlayingAtEnd = isPlaying && isAtEnd;
|
|
34421
|
-
}, [state.isPlaying, state.currentTime, state.duration]));
|
|
34422
|
-
// ============================================================================
|
|
34423
|
-
// Zoom Events
|
|
34424
|
-
// ============================================================================
|
|
34425
|
-
// Emit zoom when zoom level changes
|
|
34426
|
-
cleanups.push(effect(() => {
|
|
34427
|
-
const zoom = state.zoom.value;
|
|
34428
|
-
if (zoom > 0) {
|
|
34429
|
-
emitter.emit('zoom', zoom);
|
|
34430
|
-
}
|
|
34431
|
-
}, [state.zoom]));
|
|
34432
|
-
// Return cleanup function
|
|
34433
|
-
return () => {
|
|
34434
|
-
cleanups.forEach((cleanup) => cleanup());
|
|
34435
|
-
};
|
|
34436
|
-
}
|
|
34437
|
-
|
|
34438
33595
|
var __awaiter$5 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
34439
33596
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
34440
33597
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -34461,14 +33618,6 @@ class WaveSurfer extends Player {
|
|
|
34461
33618
|
static create(options) {
|
|
34462
33619
|
return new WaveSurfer(options);
|
|
34463
33620
|
}
|
|
34464
|
-
/** Get the reactive state for advanced use cases */
|
|
34465
|
-
getState() {
|
|
34466
|
-
return this.wavesurferState;
|
|
34467
|
-
}
|
|
34468
|
-
/** Get the renderer instance for plugin access to reactive streams */
|
|
34469
|
-
getRenderer() {
|
|
34470
|
-
return this.renderer;
|
|
34471
|
-
}
|
|
34472
33621
|
/** Create a new WaveSurfer instance */
|
|
34473
33622
|
constructor(options) {
|
|
34474
33623
|
const media = options.media ||
|
|
@@ -34485,27 +33634,13 @@ class WaveSurfer extends Player {
|
|
|
34485
33634
|
this.subscriptions = [];
|
|
34486
33635
|
this.mediaSubscriptions = [];
|
|
34487
33636
|
this.abortController = null;
|
|
34488
|
-
this.reactiveCleanups = [];
|
|
34489
33637
|
this.options = Object.assign({}, defaultOptions, options);
|
|
34490
|
-
// Initialize reactive state
|
|
34491
|
-
// Pass Player signals to compose them into WaveSurferState
|
|
34492
|
-
const { state, actions } = createWaveSurferState({
|
|
34493
|
-
isPlaying: this.isPlayingSignal,
|
|
34494
|
-
currentTime: this.currentTimeSignal,
|
|
34495
|
-
duration: this.durationSignal,
|
|
34496
|
-
volume: this.volumeSignal,
|
|
34497
|
-
playbackRate: this.playbackRateSignal,
|
|
34498
|
-
isSeeking: this.seekingSignal,
|
|
34499
|
-
});
|
|
34500
|
-
this.wavesurferState = state;
|
|
34501
|
-
this.wavesurferActions = actions;
|
|
34502
33638
|
this.timer = new Timer();
|
|
34503
33639
|
const audioElement = media ? undefined : this.getMediaElement();
|
|
34504
33640
|
this.renderer = new Renderer(this.options, audioElement);
|
|
34505
33641
|
this.initPlayerEvents();
|
|
34506
33642
|
this.initRendererEvents();
|
|
34507
33643
|
this.initTimerEvents();
|
|
34508
|
-
this.initReactiveState();
|
|
34509
33644
|
this.initPlugins();
|
|
34510
33645
|
// Read the initial URL before load has been called
|
|
34511
33646
|
const initialUrl = this.options.url || this.getSrc() || '';
|
|
@@ -34518,10 +33653,7 @@ class WaveSurfer extends Player {
|
|
|
34518
33653
|
if (initialUrl || (peaks && duration)) {
|
|
34519
33654
|
// Swallow async errors because they cannot be caught from a constructor call.
|
|
34520
33655
|
// Subscribe to the wavesurfer's error event to handle them.
|
|
34521
|
-
this.load(initialUrl, peaks, duration).catch((
|
|
34522
|
-
// Emit error event for proper error handling
|
|
34523
|
-
this.emit('error', err instanceof Error ? err : new Error(String(err)));
|
|
34524
|
-
});
|
|
33656
|
+
this.load(initialUrl, peaks, duration).catch(() => null);
|
|
34525
33657
|
}
|
|
34526
33658
|
});
|
|
34527
33659
|
}
|
|
@@ -34543,12 +33675,6 @@ class WaveSurfer extends Player {
|
|
|
34543
33675
|
}
|
|
34544
33676
|
}));
|
|
34545
33677
|
}
|
|
34546
|
-
initReactiveState() {
|
|
34547
|
-
// Bridge reactive state to EventEmitter for backwards compatibility
|
|
34548
|
-
this.reactiveCleanups.push(setupStateEventEmission(this.wavesurferState, {
|
|
34549
|
-
emit: this.emit.bind(this),
|
|
34550
|
-
}));
|
|
34551
|
-
}
|
|
34552
33678
|
initPlayerEvents() {
|
|
34553
33679
|
if (this.isPlaying()) {
|
|
34554
33680
|
this.emit('play');
|
|
@@ -34613,44 +33739,33 @@ class WaveSurfer extends Player {
|
|
|
34613
33739
|
// DragEnd
|
|
34614
33740
|
this.renderer.on('dragend', (relativeX) => {
|
|
34615
33741
|
this.emit('dragend', relativeX);
|
|
34616
|
-
}),
|
|
34617
|
-
// Resize
|
|
34618
|
-
this.renderer.on('resize', () => {
|
|
34619
|
-
this.emit('resize');
|
|
34620
33742
|
}));
|
|
34621
33743
|
// Drag
|
|
34622
33744
|
{
|
|
34623
33745
|
let debounce;
|
|
34624
|
-
|
|
34625
|
-
var _a;
|
|
33746
|
+
this.subscriptions.push(this.renderer.on('drag', (relativeX) => {
|
|
34626
33747
|
if (!this.options.interact)
|
|
34627
33748
|
return;
|
|
34628
33749
|
// Update the visual position
|
|
34629
33750
|
this.renderer.renderProgress(relativeX);
|
|
34630
33751
|
// Set the audio position with a debounce
|
|
34631
33752
|
clearTimeout(debounce);
|
|
34632
|
-
let debounceTime
|
|
34633
|
-
const dragToSeek = this.options.dragToSeek;
|
|
33753
|
+
let debounceTime;
|
|
34634
33754
|
if (this.isPlaying()) {
|
|
34635
33755
|
debounceTime = 0;
|
|
34636
33756
|
}
|
|
34637
|
-
else if (dragToSeek === true) {
|
|
33757
|
+
else if (this.options.dragToSeek === true) {
|
|
34638
33758
|
debounceTime = 200;
|
|
34639
33759
|
}
|
|
34640
|
-
else if (
|
|
34641
|
-
debounceTime =
|
|
33760
|
+
else if (typeof this.options.dragToSeek === 'object' && this.options.dragToSeek !== undefined) {
|
|
33761
|
+
debounceTime = this.options.dragToSeek['debounceTime'];
|
|
34642
33762
|
}
|
|
34643
33763
|
debounce = setTimeout(() => {
|
|
34644
33764
|
this.seekTo(relativeX);
|
|
34645
33765
|
}, debounceTime);
|
|
34646
33766
|
this.emit('interaction', relativeX * this.getDuration());
|
|
34647
33767
|
this.emit('drag', relativeX);
|
|
34648
|
-
});
|
|
34649
|
-
// Clear debounce timeout on destroy
|
|
34650
|
-
this.subscriptions.push(() => {
|
|
34651
|
-
clearTimeout(debounce);
|
|
34652
|
-
unsubscribeDrag();
|
|
34653
|
-
});
|
|
33768
|
+
}));
|
|
34654
33769
|
}
|
|
34655
33770
|
}
|
|
34656
33771
|
initPlugins() {
|
|
@@ -34737,15 +33852,12 @@ class WaveSurfer extends Player {
|
|
|
34737
33852
|
this.pause();
|
|
34738
33853
|
this.decodedData = null;
|
|
34739
33854
|
this.stopAtPosition = null;
|
|
34740
|
-
// Abort any ongoing fetch before starting a new one
|
|
34741
|
-
(_a = this.abortController) === null || _a === void 0 ? void 0 : _a.abort();
|
|
34742
|
-
this.abortController = null;
|
|
34743
33855
|
// Fetch the entire audio as a blob if pre-decoded data is not provided
|
|
34744
33856
|
if (!blob && !channelData) {
|
|
34745
33857
|
const fetchParams = this.options.fetchParams || {};
|
|
34746
33858
|
if (window.AbortController && !fetchParams.signal) {
|
|
34747
33859
|
this.abortController = new AbortController();
|
|
34748
|
-
fetchParams.signal = this.abortController.signal;
|
|
33860
|
+
fetchParams.signal = (_a = this.abortController) === null || _a === void 0 ? void 0 : _a.signal;
|
|
34749
33861
|
}
|
|
34750
33862
|
const onProgress = (percentage) => this.emit('loading', percentage);
|
|
34751
33863
|
blob = yield Fetcher.fetchBlob(url, onProgress, fetchParams);
|
|
@@ -34835,8 +33947,8 @@ class WaveSurfer extends Player {
|
|
|
34835
33947
|
const channel = this.decodedData.getChannelData(i);
|
|
34836
33948
|
const data = [];
|
|
34837
33949
|
const sampleSize = channel.length / maxLength;
|
|
34838
|
-
for (let
|
|
34839
|
-
const sample = channel.slice(Math.floor(
|
|
33950
|
+
for (let i = 0; i < maxLength; i++) {
|
|
33951
|
+
const sample = channel.slice(Math.floor(i * sampleSize), Math.ceil((i + 1) * sampleSize));
|
|
34840
33952
|
let max = 0;
|
|
34841
33953
|
for (let x = 0; x < sample.length; x++) {
|
|
34842
33954
|
const n = sample[x];
|
|
@@ -34869,7 +33981,7 @@ class WaveSurfer extends Player {
|
|
|
34869
33981
|
this.updateProgress(time);
|
|
34870
33982
|
this.emit('timeupdate', time);
|
|
34871
33983
|
}
|
|
34872
|
-
/** Seek to a
|
|
33984
|
+
/** Seek to a percentage of audio as [0..1] (0 = beginning, 1 = end) */
|
|
34873
33985
|
seekTo(progress) {
|
|
34874
33986
|
const time = this.getDuration() * progress;
|
|
34875
33987
|
this.setTime(time);
|
|
@@ -34933,8 +34045,6 @@ class WaveSurfer extends Player {
|
|
|
34933
34045
|
this.plugins.forEach((plugin) => plugin.destroy());
|
|
34934
34046
|
this.subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
34935
34047
|
this.unsubscribePlayerEvents();
|
|
34936
|
-
this.reactiveCleanups.forEach((cleanup) => cleanup());
|
|
34937
|
-
this.reactiveCleanups = [];
|
|
34938
34048
|
this.timer.destroy();
|
|
34939
34049
|
this.renderer.destroy();
|
|
34940
34050
|
super.destroy();
|
|
@@ -35007,9 +34117,170 @@ var Bar = styled__default.div(_templateObject2$q || (_templateObject2$q = _tagge
|
|
|
35007
34117
|
return props.progressColor;
|
|
35008
34118
|
});
|
|
35009
34119
|
|
|
34120
|
+
var ffmpegInstance = null;
|
|
34121
|
+
var isFFmpegLoading = false;
|
|
34122
|
+
var ffmpegLoadPromise = null;
|
|
34123
|
+
var isSafari = function isSafari() {
|
|
34124
|
+
if (typeof window === 'undefined' || !window.navigator) {
|
|
34125
|
+
return false;
|
|
34126
|
+
}
|
|
34127
|
+
var userAgent = window.navigator.userAgent;
|
|
34128
|
+
return /^((?!chrome|android).)*safari/i.test(userAgent);
|
|
34129
|
+
};
|
|
34130
|
+
var initFFmpeg = function initFFmpeg() {
|
|
34131
|
+
try {
|
|
34132
|
+
if (ffmpegInstance) {
|
|
34133
|
+
return Promise.resolve(ffmpegInstance);
|
|
34134
|
+
}
|
|
34135
|
+
if (isFFmpegLoading && ffmpegLoadPromise) {
|
|
34136
|
+
return Promise.resolve(ffmpegLoadPromise.then(function () {
|
|
34137
|
+
if (!ffmpegInstance) {
|
|
34138
|
+
throw new Error('FFmpeg failed to initialize');
|
|
34139
|
+
}
|
|
34140
|
+
return ffmpegInstance;
|
|
34141
|
+
}));
|
|
34142
|
+
}
|
|
34143
|
+
isFFmpegLoading = true;
|
|
34144
|
+
ffmpegLoadPromise = function () {
|
|
34145
|
+
try {
|
|
34146
|
+
return Promise.resolve(_catch(function () {
|
|
34147
|
+
var ffmpeg$1 = new ffmpeg.FFmpeg();
|
|
34148
|
+
var baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/umd';
|
|
34149
|
+
var _load = ffmpeg$1.load;
|
|
34150
|
+
return Promise.resolve(util.toBlobURL(baseURL + "/ffmpeg-core.js", 'text/javascript')).then(function (_toBlobURL) {
|
|
34151
|
+
return Promise.resolve(util.toBlobURL(baseURL + "/ffmpeg-core.wasm", 'application/wasm')).then(function (_toBlobURL2) {
|
|
34152
|
+
return Promise.resolve(_load.call(ffmpeg$1, {
|
|
34153
|
+
coreURL: _toBlobURL,
|
|
34154
|
+
wasmURL: _toBlobURL2
|
|
34155
|
+
})).then(function () {
|
|
34156
|
+
ffmpegInstance = ffmpeg$1;
|
|
34157
|
+
isFFmpegLoading = false;
|
|
34158
|
+
});
|
|
34159
|
+
});
|
|
34160
|
+
});
|
|
34161
|
+
}, function (error) {
|
|
34162
|
+
isFFmpegLoading = false;
|
|
34163
|
+
ffmpegLoadPromise = null;
|
|
34164
|
+
ffmpegInstance = null;
|
|
34165
|
+
log.error('Failed to load FFmpeg:', error);
|
|
34166
|
+
throw error;
|
|
34167
|
+
}));
|
|
34168
|
+
} catch (e) {
|
|
34169
|
+
return Promise.reject(e);
|
|
34170
|
+
}
|
|
34171
|
+
}();
|
|
34172
|
+
return Promise.resolve(ffmpegLoadPromise).then(function () {
|
|
34173
|
+
if (!ffmpegInstance) {
|
|
34174
|
+
throw new Error('FFmpeg instance is null after initialization');
|
|
34175
|
+
}
|
|
34176
|
+
return ffmpegInstance;
|
|
34177
|
+
});
|
|
34178
|
+
} catch (e) {
|
|
34179
|
+
return Promise.reject(e);
|
|
34180
|
+
}
|
|
34181
|
+
};
|
|
34182
|
+
var convertMp3ToAac = function convertMp3ToAac(file, messageId) {
|
|
34183
|
+
try {
|
|
34184
|
+
return Promise.resolve(_catch(function () {
|
|
34185
|
+
var maxSize = 50 * 1024 * 1024;
|
|
34186
|
+
if (file.size > maxSize) {
|
|
34187
|
+
throw new Error("File size (" + (file.size / 1024 / 1024).toFixed(2) + "MB) exceeds maximum allowed size (50MB)");
|
|
34188
|
+
}
|
|
34189
|
+
if (file.size === 0) {
|
|
34190
|
+
throw new Error('File is empty');
|
|
34191
|
+
}
|
|
34192
|
+
return Promise.resolve(initFFmpeg()).then(function (ffmpeg) {
|
|
34193
|
+
function _temp8() {
|
|
34194
|
+
function _temp6() {
|
|
34195
|
+
return Promise.resolve(util.fetchFile(file)).then(function (inputData) {
|
|
34196
|
+
return Promise.resolve(ffmpeg.writeFile(messageId + "_input.mp3", inputData)).then(function () {
|
|
34197
|
+
return Promise.resolve(ffmpeg.exec(['-i', messageId + "_input.mp3", '-c:a', 'aac', '-b:a', '128k', '-movflags', '+faststart', messageId + "_output.m4a"])).then(function () {
|
|
34198
|
+
return Promise.resolve(ffmpeg.readFile(messageId + "_output.m4a")).then(function (data) {
|
|
34199
|
+
function _temp4() {
|
|
34200
|
+
function _temp2() {
|
|
34201
|
+
var dataArray;
|
|
34202
|
+
if (data instanceof Uint8Array) {
|
|
34203
|
+
dataArray = data;
|
|
34204
|
+
} else if (typeof data === 'string') {
|
|
34205
|
+
var binaryString = atob(data);
|
|
34206
|
+
var bytes = new Uint8Array(binaryString.length);
|
|
34207
|
+
for (var i = 0; i < binaryString.length; i++) {
|
|
34208
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
34209
|
+
}
|
|
34210
|
+
dataArray = bytes;
|
|
34211
|
+
} else {
|
|
34212
|
+
dataArray = new Uint8Array(data);
|
|
34213
|
+
}
|
|
34214
|
+
var arrayBuffer = dataArray.buffer.slice(dataArray.byteOffset, dataArray.byteOffset + dataArray.byteLength);
|
|
34215
|
+
var blob = new Blob([arrayBuffer], {
|
|
34216
|
+
type: 'audio/mp4'
|
|
34217
|
+
});
|
|
34218
|
+
var convertedFile = new File([blob], messageId + "_" + file.name.replace('.mp3', '.m4a'), {
|
|
34219
|
+
type: 'audio/mp4',
|
|
34220
|
+
lastModified: file.lastModified
|
|
34221
|
+
});
|
|
34222
|
+
return convertedFile;
|
|
34223
|
+
}
|
|
34224
|
+
var _temp = _catch(function () {
|
|
34225
|
+
return Promise.resolve(ffmpeg.deleteFile(messageId + "_output.m4a")).then(function () {});
|
|
34226
|
+
}, function () {});
|
|
34227
|
+
return _temp && _temp.then ? _temp.then(_temp2) : _temp2(_temp);
|
|
34228
|
+
}
|
|
34229
|
+
var _temp3 = _catch(function () {
|
|
34230
|
+
return Promise.resolve(ffmpeg.deleteFile(messageId + "_input.mp3")).then(function () {});
|
|
34231
|
+
}, function () {});
|
|
34232
|
+
return _temp3 && _temp3.then ? _temp3.then(_temp4) : _temp4(_temp3);
|
|
34233
|
+
});
|
|
34234
|
+
});
|
|
34235
|
+
});
|
|
34236
|
+
});
|
|
34237
|
+
}
|
|
34238
|
+
var _temp5 = _catch(function () {
|
|
34239
|
+
return Promise.resolve(ffmpeg.deleteFile(messageId + "_output.m4a")).then(function () {});
|
|
34240
|
+
}, function () {});
|
|
34241
|
+
return _temp5 && _temp5.then ? _temp5.then(_temp6) : _temp6(_temp5);
|
|
34242
|
+
}
|
|
34243
|
+
var _temp7 = _catch(function () {
|
|
34244
|
+
return Promise.resolve(ffmpeg.deleteFile(messageId + "_input.mp3")).then(function () {});
|
|
34245
|
+
}, function () {});
|
|
34246
|
+
return _temp7 && _temp7.then ? _temp7.then(_temp8) : _temp8(_temp7);
|
|
34247
|
+
});
|
|
34248
|
+
}, function (error) {
|
|
34249
|
+
log.error('Failed to convert MP3 to AAC:', error);
|
|
34250
|
+
throw error;
|
|
34251
|
+
}));
|
|
34252
|
+
} catch (e) {
|
|
34253
|
+
return Promise.reject(e);
|
|
34254
|
+
}
|
|
34255
|
+
};
|
|
34256
|
+
var convertAudioForSafari = function convertAudioForSafari(file, messageId) {
|
|
34257
|
+
try {
|
|
34258
|
+
var _exit = false;
|
|
34259
|
+
var _temp9 = function () {
|
|
34260
|
+
if (isSafari() && file.type === 'audio/mpeg' && file.name.endsWith('.mp3')) {
|
|
34261
|
+
return _catch(function () {
|
|
34262
|
+
return Promise.resolve(convertMp3ToAac(file, messageId)).then(function (_await$convertMp3ToAa) {
|
|
34263
|
+
_exit = true;
|
|
34264
|
+
return _await$convertMp3ToAa;
|
|
34265
|
+
});
|
|
34266
|
+
}, function (error) {
|
|
34267
|
+
log.warn('Audio conversion failed, using original file:', error);
|
|
34268
|
+
_exit = true;
|
|
34269
|
+
return file;
|
|
34270
|
+
});
|
|
34271
|
+
}
|
|
34272
|
+
}();
|
|
34273
|
+
return Promise.resolve(_temp9 && _temp9.then ? _temp9.then(function (_result2) {
|
|
34274
|
+
return _exit ? _result2 : file;
|
|
34275
|
+
}) : _exit ? _temp9 : file);
|
|
34276
|
+
} catch (e) {
|
|
34277
|
+
return Promise.reject(e);
|
|
34278
|
+
}
|
|
34279
|
+
};
|
|
34280
|
+
|
|
35010
34281
|
var _templateObject$v, _templateObject2$r, _templateObject3$l, _templateObject4$h, _templateObject5$e, _templateObject6$c, _templateObject7$b, _templateObject8$b;
|
|
35011
34282
|
var AudioPlayer = function AudioPlayer(_ref) {
|
|
35012
|
-
var _file$
|
|
34283
|
+
var _file$metadata5, _file$metadata6, _file$metadata7;
|
|
35013
34284
|
var url = _ref.url,
|
|
35014
34285
|
file = _ref.file,
|
|
35015
34286
|
messagePlayed = _ref.messagePlayed,
|
|
@@ -35060,6 +34331,7 @@ var AudioPlayer = function AudioPlayer(_ref) {
|
|
|
35060
34331
|
var wavesurfer = React.useRef(null);
|
|
35061
34332
|
var wavesurferContainer = React.useRef(null);
|
|
35062
34333
|
var intervalRef = React.useRef(null);
|
|
34334
|
+
var convertedUrlRef = React.useRef(null);
|
|
35063
34335
|
var handleSetAudioRate = function handleSetAudioRate() {
|
|
35064
34336
|
if (wavesurfer.current) {
|
|
35065
34337
|
if (audioRate === 1) {
|
|
@@ -35143,84 +34415,131 @@ var AudioPlayer = function AudioPlayer(_ref) {
|
|
|
35143
34415
|
}, [recording.initRecording]);
|
|
35144
34416
|
React.useEffect(function () {
|
|
35145
34417
|
if (url) {
|
|
34418
|
+
if (convertedUrlRef.current) {
|
|
34419
|
+
URL.revokeObjectURL(convertedUrlRef.current);
|
|
34420
|
+
convertedUrlRef.current = null;
|
|
34421
|
+
}
|
|
35146
34422
|
if (url !== '_' && !isRendered && wavesurfer && wavesurfer.current) {
|
|
35147
34423
|
wavesurfer.current.destroy();
|
|
35148
34424
|
}
|
|
35149
34425
|
var initWaveSurfer = function initWaveSurfer() {
|
|
35150
34426
|
try {
|
|
35151
|
-
|
|
35152
|
-
|
|
35153
|
-
|
|
35154
|
-
|
|
35155
|
-
|
|
35156
|
-
|
|
35157
|
-
|
|
35158
|
-
|
|
35159
|
-
|
|
35160
|
-
|
|
35161
|
-
|
|
35162
|
-
|
|
35163
|
-
|
|
35164
|
-
|
|
35165
|
-
|
|
35166
|
-
|
|
35167
|
-
|
|
35168
|
-
|
|
35169
|
-
|
|
34427
|
+
var _exit = false;
|
|
34428
|
+
return Promise.resolve(_catch(function () {
|
|
34429
|
+
var _file$name;
|
|
34430
|
+
function _temp2(_result2) {
|
|
34431
|
+
if (_exit) return _result2;
|
|
34432
|
+
wavesurfer.current = WaveSurfer.create({
|
|
34433
|
+
container: wavesurferContainer.current,
|
|
34434
|
+
waveColor: 'transparent',
|
|
34435
|
+
progressColor: 'transparent',
|
|
34436
|
+
audioRate: audioRate,
|
|
34437
|
+
barWidth: 1,
|
|
34438
|
+
barHeight: 1,
|
|
34439
|
+
hideScrollbar: true,
|
|
34440
|
+
barRadius: 1.5,
|
|
34441
|
+
cursorWidth: 0,
|
|
34442
|
+
barGap: 2,
|
|
34443
|
+
height: 20
|
|
34444
|
+
});
|
|
34445
|
+
var peaks;
|
|
34446
|
+
if (file.metadata) {
|
|
34447
|
+
if (file.metadata.dur) {
|
|
34448
|
+
setDuration(file.metadata.dur);
|
|
34449
|
+
setCurrentTime(formatAudioVideoTime(file.metadata.dur));
|
|
34450
|
+
}
|
|
34451
|
+
if (file.metadata.tmb) {
|
|
34452
|
+
var maxVal = Array.isArray(file.metadata.tmb) && file.metadata.tmb.length > 0 ? file.metadata.tmb.reduce(function (acc, n) {
|
|
34453
|
+
return n > acc ? n : acc;
|
|
34454
|
+
}, -Infinity) : 0;
|
|
34455
|
+
var dec = maxVal / 100;
|
|
34456
|
+
peaks = file.metadata.tmb.map(function (peak) {
|
|
34457
|
+
return peak / dec / 100;
|
|
34458
|
+
});
|
|
34459
|
+
}
|
|
35170
34460
|
}
|
|
35171
|
-
|
|
35172
|
-
|
|
35173
|
-
|
|
35174
|
-
|
|
35175
|
-
|
|
35176
|
-
|
|
35177
|
-
|
|
35178
|
-
|
|
34461
|
+
wavesurfer.current.load(audioUrl, peaks);
|
|
34462
|
+
wavesurfer.current.on('ready', function () {
|
|
34463
|
+
var _file$metadata, _file$metadata2;
|
|
34464
|
+
var audioDuration = wavesurfer.current.getDuration();
|
|
34465
|
+
setDuration((file === null || file === void 0 ? void 0 : (_file$metadata = file.metadata) === null || _file$metadata === void 0 ? void 0 : _file$metadata.dur) || audioDuration);
|
|
34466
|
+
setCurrentTime(formatAudioVideoTime((file === null || file === void 0 ? void 0 : (_file$metadata2 = file.metadata) === null || _file$metadata2 === void 0 ? void 0 : _file$metadata2.dur) || audioDuration));
|
|
34467
|
+
wavesurfer.current.drawBuffer = function (d) {
|
|
34468
|
+
log.info('filters --- ', d);
|
|
34469
|
+
};
|
|
34470
|
+
});
|
|
34471
|
+
wavesurfer.current.on('finish', function () {
|
|
34472
|
+
var _file$metadata3, _file$metadata4;
|
|
34473
|
+
setPlayAudio(false);
|
|
34474
|
+
wavesurfer.current.seekTo(0);
|
|
34475
|
+
var audioDuration = wavesurfer.current.getDuration();
|
|
34476
|
+
setDuration((file === null || file === void 0 ? void 0 : (_file$metadata3 = file.metadata) === null || _file$metadata3 === void 0 ? void 0 : _file$metadata3.dur) || audioDuration);
|
|
34477
|
+
setCurrentTime(formatAudioVideoTime((file === null || file === void 0 ? void 0 : (_file$metadata4 = file.metadata) === null || _file$metadata4 === void 0 ? void 0 : _file$metadata4.dur) || audioDuration));
|
|
34478
|
+
setCurrentTimeSeconds(0);
|
|
34479
|
+
if (playingAudioId === file.id) {
|
|
34480
|
+
dispatch(setPlayingAudioIdAC(null));
|
|
34481
|
+
}
|
|
34482
|
+
clearInterval(intervalRef.current);
|
|
34483
|
+
if (onClose) {
|
|
34484
|
+
onClose();
|
|
34485
|
+
}
|
|
34486
|
+
});
|
|
34487
|
+
wavesurfer.current.on('pause', function () {
|
|
34488
|
+
setPlayAudio(false);
|
|
34489
|
+
if (playingAudioId === file.id) {
|
|
34490
|
+
dispatch(setPlayingAudioIdAC(null));
|
|
34491
|
+
}
|
|
34492
|
+
clearInterval(intervalRef.current);
|
|
34493
|
+
});
|
|
34494
|
+
wavesurfer.current.on('interaction', function () {
|
|
34495
|
+
var currentTime = wavesurfer.current.getCurrentTime();
|
|
34496
|
+
setCurrentTime(formatAudioVideoTime(currentTime));
|
|
34497
|
+
setCurrentTimeSeconds(currentTime);
|
|
34498
|
+
});
|
|
34499
|
+
if (url !== '_') {
|
|
34500
|
+
setIsRendered(true);
|
|
35179
34501
|
}
|
|
35180
34502
|
}
|
|
35181
|
-
|
|
35182
|
-
|
|
35183
|
-
|
|
35184
|
-
|
|
35185
|
-
|
|
35186
|
-
|
|
35187
|
-
|
|
35188
|
-
|
|
35189
|
-
|
|
35190
|
-
|
|
35191
|
-
|
|
35192
|
-
|
|
35193
|
-
|
|
35194
|
-
|
|
35195
|
-
|
|
35196
|
-
|
|
35197
|
-
|
|
35198
|
-
|
|
35199
|
-
|
|
35200
|
-
|
|
35201
|
-
|
|
35202
|
-
|
|
35203
|
-
|
|
35204
|
-
|
|
35205
|
-
|
|
35206
|
-
|
|
35207
|
-
|
|
35208
|
-
|
|
34503
|
+
var audioUrl = url;
|
|
34504
|
+
var needsConversion = isSafari() && url && url !== '_' && (url.endsWith('.mp3') || ((_file$name = file.name) === null || _file$name === void 0 ? void 0 : _file$name.endsWith('.mp3')) || file.type === 'audio/mpeg');
|
|
34505
|
+
var _temp = function () {
|
|
34506
|
+
if (needsConversion) {
|
|
34507
|
+
return _catch(function () {
|
|
34508
|
+
if (convertedUrlRef.current) {
|
|
34509
|
+
URL.revokeObjectURL(convertedUrlRef.current);
|
|
34510
|
+
convertedUrlRef.current = null;
|
|
34511
|
+
}
|
|
34512
|
+
var cacheKey = file.id || url;
|
|
34513
|
+
return Promise.resolve(fetch(url, {
|
|
34514
|
+
cache: 'no-store'
|
|
34515
|
+
})).then(function (response) {
|
|
34516
|
+
if (!response.ok) {
|
|
34517
|
+
throw new Error("Failed to fetch audio: " + response.statusText);
|
|
34518
|
+
}
|
|
34519
|
+
return Promise.resolve(response.blob()).then(function (blob) {
|
|
34520
|
+
var uniqueFileName = (file.id || Date.now()) + "_" + (file.name || 'audio.mp3');
|
|
34521
|
+
var audioFile = new File([blob], uniqueFileName, {
|
|
34522
|
+
type: blob.type || 'audio/mpeg',
|
|
34523
|
+
lastModified: Date.now()
|
|
34524
|
+
});
|
|
34525
|
+
return Promise.resolve(convertAudioForSafari(audioFile, file === null || file === void 0 ? void 0 : file.messageId)).then(function (convertedFile) {
|
|
34526
|
+
var convertedBlobUrl = URL.createObjectURL(convertedFile);
|
|
34527
|
+
audioUrl = convertedBlobUrl;
|
|
34528
|
+
convertedUrlRef.current = convertedBlobUrl;
|
|
34529
|
+
log.info("Converted audio for Safari: " + cacheKey + " -> " + convertedBlobUrl);
|
|
34530
|
+
});
|
|
34531
|
+
});
|
|
34532
|
+
});
|
|
34533
|
+
}, function (conversionError) {
|
|
34534
|
+
log.warn('Failed to convert audio for Safari, using original:', conversionError);
|
|
34535
|
+
audioUrl = url;
|
|
34536
|
+
});
|
|
35209
34537
|
}
|
|
35210
|
-
|
|
35211
|
-
|
|
35212
|
-
|
|
35213
|
-
var currentTime = wavesurfer.current.getCurrentTime();
|
|
35214
|
-
setCurrentTime(formatAudioVideoTime(currentTime));
|
|
35215
|
-
setCurrentTimeSeconds(currentTime);
|
|
35216
|
-
});
|
|
35217
|
-
if (url !== '_') {
|
|
35218
|
-
setIsRendered(true);
|
|
35219
|
-
}
|
|
35220
|
-
} catch (e) {
|
|
34538
|
+
}();
|
|
34539
|
+
return _temp && _temp.then ? _temp.then(_temp2) : _temp2(_temp);
|
|
34540
|
+
}, function (e) {
|
|
35221
34541
|
log.error('Failed to init wavesurfer', e);
|
|
35222
|
-
}
|
|
35223
|
-
return Promise.resolve();
|
|
34542
|
+
}));
|
|
35224
34543
|
} catch (e) {
|
|
35225
34544
|
return Promise.reject(e);
|
|
35226
34545
|
}
|
|
@@ -35229,8 +34548,16 @@ var AudioPlayer = function AudioPlayer(_ref) {
|
|
|
35229
34548
|
}
|
|
35230
34549
|
return function () {
|
|
35231
34550
|
clearInterval(intervalRef.current);
|
|
34551
|
+
if (convertedUrlRef.current) {
|
|
34552
|
+
URL.revokeObjectURL(convertedUrlRef.current);
|
|
34553
|
+
convertedUrlRef.current = null;
|
|
34554
|
+
}
|
|
34555
|
+
if (wavesurfer.current) {
|
|
34556
|
+
wavesurfer.current.destroy();
|
|
34557
|
+
wavesurfer.current = null;
|
|
34558
|
+
}
|
|
35232
34559
|
};
|
|
35233
|
-
}, [url]);
|
|
34560
|
+
}, [url, file.id]);
|
|
35234
34561
|
React.useEffect(function () {
|
|
35235
34562
|
if (playAudio && playingAudioId && playingAudioId !== "player_" + file.id && wavesurfer.current) {
|
|
35236
34563
|
setPlayAudio(false);
|
|
@@ -35248,8 +34575,8 @@ var AudioPlayer = function AudioPlayer(_ref) {
|
|
|
35248
34575
|
iconColor: accentColor
|
|
35249
34576
|
}))), /*#__PURE__*/React__default.createElement(WaveContainer, null, /*#__PURE__*/React__default.createElement(VisualizationWrapper, null, /*#__PURE__*/React__default.createElement(AudioVisualizationPlaceholder, {
|
|
35250
34577
|
ref: wavesurferContainer,
|
|
35251
|
-
hidden: !!((_file$
|
|
35252
|
-
}), ((_file$
|
|
34578
|
+
hidden: !!((_file$metadata5 = file.metadata) !== null && _file$metadata5 !== void 0 && _file$metadata5.tmb && Array.isArray(file.metadata.tmb))
|
|
34579
|
+
}), ((_file$metadata6 = file.metadata) === null || _file$metadata6 === void 0 ? void 0 : _file$metadata6.tmb) && Array.isArray(file.metadata.tmb) && (/*#__PURE__*/React__default.createElement(AudioVisualization, {
|
|
35253
34580
|
tmb: file.metadata.tmb,
|
|
35254
34581
|
duration: duration || file.metadata.dur || 0,
|
|
35255
34582
|
currentTime: currentTimeSeconds,
|
|
@@ -35265,7 +34592,7 @@ var AudioPlayer = function AudioPlayer(_ref) {
|
|
|
35265
34592
|
backgroundColor: backgroundSections
|
|
35266
34593
|
}, audioRate, /*#__PURE__*/React__default.createElement("span", null, "X"))), /*#__PURE__*/React__default.createElement(Timer$1, {
|
|
35267
34594
|
color: textSecondary
|
|
35268
|
-
}, currentTime || formatAudioVideoTime(((_file$
|
|
34595
|
+
}, currentTime || formatAudioVideoTime(((_file$metadata7 = file.metadata) === null || _file$metadata7 === void 0 ? void 0 : _file$metadata7.dur) || 0)));
|
|
35269
34596
|
};
|
|
35270
34597
|
var Container$f = styled__default.div(_templateObject$v || (_templateObject$v = _taggedTemplateLiteralLoose(["\n position: relative;\n display: flex;\n align-items: flex-start;\n width: 230px;\n padding: 8px 12px;\n ", "\n ", "\n"])), function (props) {
|
|
35271
34598
|
return props.backgroundColor && "background-color: " + props.backgroundColor + ";";
|
|
@@ -46317,7 +45644,7 @@ var SendMessageInput = function SendMessageInput(_ref3) {
|
|
|
46317
45644
|
if (activeChannel.id) {
|
|
46318
45645
|
prevActiveChannelId = activeChannel.id;
|
|
46319
45646
|
}
|
|
46320
|
-
if (activeChannel.id && membersHasNext === undefined) {
|
|
45647
|
+
if (activeChannel.id && membersHasNext === undefined && !(activeChannel.type === DEFAULT_CHANNEL_TYPE.DIRECT && activeChannel.memberCount === 2)) {
|
|
46321
45648
|
dispatch(getMembersAC(activeChannel.id));
|
|
46322
45649
|
}
|
|
46323
45650
|
setMentionedUsers([]);
|
|
@@ -49950,7 +49277,7 @@ var Members = function Members(_ref) {
|
|
|
49950
49277
|
if (getFromContacts) {
|
|
49951
49278
|
dispatch(getContactsAC());
|
|
49952
49279
|
}
|
|
49953
|
-
if (channel !== null && channel !== void 0 && channel.id) {
|
|
49280
|
+
if (channel !== null && channel !== void 0 && channel.id && !(channel.type === DEFAULT_CHANNEL_TYPE.DIRECT && channel.memberCount === 2)) {
|
|
49954
49281
|
dispatch(getMembersAC(channel.id));
|
|
49955
49282
|
}
|
|
49956
49283
|
}
|