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